1d3cf9c7dSdf157793 /*
2d3cf9c7dSdf157793 * CDDL HEADER START
3d3cf9c7dSdf157793 *
4d3cf9c7dSdf157793 * The contents of this file are subject to the terms of the
57cb42c7eSrameshc * Common Development and Distribution License (the "License").
67cb42c7eSrameshc * You may not use this file except in compliance with the License.
7d3cf9c7dSdf157793 *
8d3cf9c7dSdf157793 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d3cf9c7dSdf157793 * or http://www.opensolaris.org/os/licensing.
10d3cf9c7dSdf157793 * See the License for the specific language governing permissions
11d3cf9c7dSdf157793 * and limitations under the License.
12d3cf9c7dSdf157793 *
13d3cf9c7dSdf157793 * When distributing Covered Code, include this CDDL HEADER in each
14d3cf9c7dSdf157793 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d3cf9c7dSdf157793 * If applicable, add the following below this CDDL HEADER, with the
16d3cf9c7dSdf157793 * fields enclosed by brackets "[]" replaced with your own identifying
17d3cf9c7dSdf157793 * information: Portions Copyright [yyyy] [name of copyright owner]
18d3cf9c7dSdf157793 *
19d3cf9c7dSdf157793 * CDDL HEADER END
20d3cf9c7dSdf157793 */
21d3cf9c7dSdf157793 /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
22d3cf9c7dSdf157793 /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
23d3cf9c7dSdf157793 /* All Rights Reserved */
24d3cf9c7dSdf157793
25d3cf9c7dSdf157793 /*
264d0b1b0dSAn Bui * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
27d3cf9c7dSdf157793 * Use is subject to license terms.
2848bbca81SDaniel Hoffman * Copyright (c) 2016 by Delphix. All rights reserved.
29730a650aSPeter Tribble * Copyright (c) 2019 Peter Tribble.
30d3cf9c7dSdf157793 */
31d3cf9c7dSdf157793
32d3cf9c7dSdf157793
33d3cf9c7dSdf157793 /*
34f63f7506Sanovick * Serial I/O driver for 82510/8250/16450/16550AF/16C554D chips.
35d3cf9c7dSdf157793 * Modified as sparc keyboard/mouse driver.
36d3cf9c7dSdf157793 */
37d3cf9c7dSdf157793 #define SU_REGISTER_FILE_NO 0
38d3cf9c7dSdf157793 #define SU_REGOFFSET 0
39d3cf9c7dSdf157793 #define SU_REGISTER_LEN 8
40d3cf9c7dSdf157793
41d3cf9c7dSdf157793 #include <sys/param.h>
42d3cf9c7dSdf157793 #include <sys/types.h>
43d3cf9c7dSdf157793 #include <sys/signal.h>
44d3cf9c7dSdf157793 #include <sys/stream.h>
45d3cf9c7dSdf157793 #include <sys/termio.h>
46d3cf9c7dSdf157793 #include <sys/errno.h>
47d3cf9c7dSdf157793 #include <sys/file.h>
48d3cf9c7dSdf157793 #include <sys/cmn_err.h>
49d3cf9c7dSdf157793 #include <sys/stropts.h>
50d3cf9c7dSdf157793 #include <sys/strsubr.h>
51d3cf9c7dSdf157793 #include <sys/strsun.h>
52d3cf9c7dSdf157793 #include <sys/strtty.h>
53d3cf9c7dSdf157793 #include <sys/debug.h>
54d3cf9c7dSdf157793 #include <sys/kbio.h>
55d3cf9c7dSdf157793 #include <sys/cred.h>
56d3cf9c7dSdf157793 #include <sys/modctl.h>
57d3cf9c7dSdf157793 #include <sys/stat.h>
58d3cf9c7dSdf157793 #include <sys/consdev.h>
59d3cf9c7dSdf157793 #include <sys/mkdev.h>
60d3cf9c7dSdf157793 #include <sys/kmem.h>
61d3cf9c7dSdf157793 #include <sys/cred.h>
62d3cf9c7dSdf157793 #ifdef DEBUG
63d3cf9c7dSdf157793 #include <sys/promif.h>
64d3cf9c7dSdf157793 #endif
65d3cf9c7dSdf157793 #include <sys/ddi.h>
66d3cf9c7dSdf157793 #include <sys/sunddi.h>
67d3cf9c7dSdf157793 #include <sys/sudev.h>
68d3cf9c7dSdf157793 #include <sys/note.h>
69d3cf9c7dSdf157793 #include <sys/timex.h>
70d3cf9c7dSdf157793 #include <sys/policy.h>
71d3cf9c7dSdf157793
72d3cf9c7dSdf157793 #define async_stopc async_ttycommon.t_stopc
73d3cf9c7dSdf157793 #define async_startc async_ttycommon.t_startc
74d3cf9c7dSdf157793
75d3cf9c7dSdf157793 #define ASY_INIT 1
76d3cf9c7dSdf157793 #define ASY_NOINIT 0
77d3cf9c7dSdf157793
78d3cf9c7dSdf157793 #ifdef DEBUG
79d3cf9c7dSdf157793 #define ASY_DEBUG_INIT 0x001
80d3cf9c7dSdf157793 #define ASY_DEBUG_INPUT 0x002
81d3cf9c7dSdf157793 #define ASY_DEBUG_EOT 0x004
82d3cf9c7dSdf157793 #define ASY_DEBUG_CLOSE 0x008
83d3cf9c7dSdf157793 #define ASY_DEBUG_HFLOW 0x010
84d3cf9c7dSdf157793 #define ASY_DEBUG_PROCS 0x020
85d3cf9c7dSdf157793 #define ASY_DEBUG_STATE 0x040
86d3cf9c7dSdf157793 #define ASY_DEBUG_INTR 0x080
87d3cf9c7dSdf157793 static int asydebug = 0;
88d3cf9c7dSdf157793 #endif
89d3cf9c7dSdf157793 static int su_log = 0;
90d3cf9c7dSdf157793
91d3cf9c7dSdf157793 int su_drain_check = 15000000; /* tunable: exit drain check time */
92d3cf9c7dSdf157793
93d3cf9c7dSdf157793 static struct ppsclockev asy_ppsev;
94d3cf9c7dSdf157793
95d3cf9c7dSdf157793 static int max_asy_instance = -1;
96d3cf9c7dSdf157793 static void *su_asycom; /* soft state asycom pointer */
97d3cf9c7dSdf157793 static void *su_asyncline; /* soft state asyncline pointer */
98d3cf9c7dSdf157793 static boolean_t abort_charseq_recognize(uchar_t ch);
99d3cf9c7dSdf157793
100d3cf9c7dSdf157793 static uint_t asysoftintr(caddr_t intarg);
101d3cf9c7dSdf157793 static uint_t asyintr(caddr_t argasy);
102d3cf9c7dSdf157793
103d3cf9c7dSdf157793 /* The async interrupt entry points */
104d3cf9c7dSdf157793 static void async_txint(struct asycom *asy, uchar_t lsr);
105d3cf9c7dSdf157793 static void async_rxint(struct asycom *asy, uchar_t lsr);
106d3cf9c7dSdf157793 static void async_msint(struct asycom *asy);
107d3cf9c7dSdf157793 static int async_softint(struct asycom *asy);
108d3cf9c7dSdf157793
109d3cf9c7dSdf157793 static void async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp,
110d3cf9c7dSdf157793 boolean_t iswput);
111d3cf9c7dSdf157793 static void async_reioctl(void *);
112d3cf9c7dSdf157793 static void async_iocdata(queue_t *q, mblk_t *mp);
113d3cf9c7dSdf157793 static void async_restart(void *);
114d3cf9c7dSdf157793 static void async_start(struct asyncline *async);
115d3cf9c7dSdf157793 static void async_nstart(struct asyncline *async, int mode);
116d3cf9c7dSdf157793 static void async_resume(struct asyncline *async);
117d3cf9c7dSdf157793 static int asy_program(struct asycom *asy, int mode);
118d3cf9c7dSdf157793
1190280efdcSzk194757 /* Polled mode functions */
1200280efdcSzk194757 static void asyputchar(cons_polledio_arg_t, uchar_t c);
1210280efdcSzk194757 static int asygetchar(cons_polledio_arg_t);
1220280efdcSzk194757 static boolean_t asyischar(cons_polledio_arg_t);
1230280efdcSzk194757 static void asy_polled_enter(cons_polledio_arg_t);
1240280efdcSzk194757 static void asy_polled_exit(cons_polledio_arg_t);
1250280efdcSzk194757
126d3cf9c7dSdf157793 static int asymctl(struct asycom *, int, int);
127d3cf9c7dSdf157793 static int asytodm(int, int);
128d3cf9c7dSdf157793 static int dmtoasy(int);
129d3cf9c7dSdf157793 static void asycheckflowcontrol_hw(struct asycom *asy);
130d3cf9c7dSdf157793 static boolean_t asycheckflowcontrol_sw(struct asycom *asy);
131d3cf9c7dSdf157793 static void asy_ppsevent(struct asycom *asy, int msr);
132d3cf9c7dSdf157793
133d3cf9c7dSdf157793 extern kcondvar_t lbolt_cv;
134d3cf9c7dSdf157793 extern int ddi_create_internal_pathname(dev_info_t *dip, char *name,
135d3cf9c7dSdf157793 int spec_type, minor_t minor_num);
136d3cf9c7dSdf157793
137d3cf9c7dSdf157793
138d3cf9c7dSdf157793 /*
139d3cf9c7dSdf157793 * Baud rate table. Indexed by #defines found in sys/termios.h
140d3cf9c7dSdf157793 */
141d3cf9c7dSdf157793 ushort_t asyspdtab[] = {
142d3cf9c7dSdf157793 0, /* 0 baud rate */
143d3cf9c7dSdf157793 0x900, /* 50 baud rate */
144d3cf9c7dSdf157793 0x600, /* 75 baud rate */
145d3cf9c7dSdf157793 0x417, /* 110 baud rate (%0.026) */
146d3cf9c7dSdf157793 0x359, /* 134 baud rate (%0.058) */
147d3cf9c7dSdf157793 0x300, /* 150 baud rate */
148d3cf9c7dSdf157793 0x240, /* 200 baud rate */
149d3cf9c7dSdf157793 0x180, /* 300 baud rate */
150d3cf9c7dSdf157793 0x0c0, /* 600 baud rate */
151d3cf9c7dSdf157793 0x060, /* 1200 baud rate */
152d3cf9c7dSdf157793 0x040, /* 1800 baud rate */
153d3cf9c7dSdf157793 0x030, /* 2400 baud rate */
154d3cf9c7dSdf157793 0x018, /* 4800 baud rate */
155d3cf9c7dSdf157793 0x00c, /* 9600 baud rate */
156d3cf9c7dSdf157793 0x006, /* 19200 baud rate */
157d3cf9c7dSdf157793 0x003, /* 38400 baud rate */
158d3cf9c7dSdf157793 0x002, /* 57600 baud rate */
159d3cf9c7dSdf157793 0, /* 76800 baud rate - not supported */
160d3cf9c7dSdf157793 0x001, /* 115200 baud rate */
161d3cf9c7dSdf157793 0, /* 153600 baud rate - not supported */
162d3cf9c7dSdf157793 0x8002, /* 230400 baud rate - supported on specific platforms */
163d3cf9c7dSdf157793 0, /* 307200 baud rate - not supported */
164d3cf9c7dSdf157793 0x8001 /* 460800 baud rate - supported on specific platforms */
165d3cf9c7dSdf157793 };
166d3cf9c7dSdf157793
167d3cf9c7dSdf157793 /*
168d3cf9c7dSdf157793 * Number of speeds supported is the number of entries in
169d3cf9c7dSdf157793 * the above table.
170d3cf9c7dSdf157793 */
171d3cf9c7dSdf157793 #define N_SU_SPEEDS (sizeof (asyspdtab)/sizeof (ushort_t))
172d3cf9c7dSdf157793
173d3cf9c7dSdf157793 /*
174d3cf9c7dSdf157793 * Human-readable baud rate table.
175d3cf9c7dSdf157793 * Indexed by #defines found in sys/termios.h
176d3cf9c7dSdf157793 */
177d3cf9c7dSdf157793 int baudtable[] = {
178d3cf9c7dSdf157793 0, /* 0 baud rate */
179d3cf9c7dSdf157793 50, /* 50 baud rate */
180d3cf9c7dSdf157793 75, /* 75 baud rate */
181d3cf9c7dSdf157793 110, /* 110 baud rate */
182d3cf9c7dSdf157793 134, /* 134 baud rate */
183d3cf9c7dSdf157793 150, /* 150 baud rate */
184d3cf9c7dSdf157793 200, /* 200 baud rate */
185d3cf9c7dSdf157793 300, /* 300 baud rate */
186d3cf9c7dSdf157793 600, /* 600 baud rate */
187d3cf9c7dSdf157793 1200, /* 1200 baud rate */
188d3cf9c7dSdf157793 1800, /* 1800 baud rate */
189d3cf9c7dSdf157793 2400, /* 2400 baud rate */
190d3cf9c7dSdf157793 4800, /* 4800 baud rate */
191d3cf9c7dSdf157793 9600, /* 9600 baud rate */
192d3cf9c7dSdf157793 19200, /* 19200 baud rate */
193d3cf9c7dSdf157793 38400, /* 38400 baud rate */
194d3cf9c7dSdf157793 57600, /* 57600 baud rate */
195d3cf9c7dSdf157793 76800, /* 76800 baud rate */
196d3cf9c7dSdf157793 115200, /* 115200 baud rate */
197d3cf9c7dSdf157793 153600, /* 153600 baud rate */
198d3cf9c7dSdf157793 230400, /* 230400 baud rate */
199d3cf9c7dSdf157793 307200, /* 307200 baud rate */
200d3cf9c7dSdf157793 460800 /* 460800 baud rate */
201d3cf9c7dSdf157793 };
202d3cf9c7dSdf157793
203d3cf9c7dSdf157793 static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr);
204730a650aSPeter Tribble static int asyclose(queue_t *q, int flag, cred_t *cr);
205*e476cc14SToomas Soome static int asywput(queue_t *q, mblk_t *mp);
206*e476cc14SToomas Soome static int asyrsrv(queue_t *q);
207d3cf9c7dSdf157793
208d3cf9c7dSdf157793 struct module_info asy_info = {
209d3cf9c7dSdf157793 0,
210d3cf9c7dSdf157793 "su",
211d3cf9c7dSdf157793 0,
212d3cf9c7dSdf157793 INFPSZ,
213d3cf9c7dSdf157793 32*4096,
214d3cf9c7dSdf157793 4096
215d3cf9c7dSdf157793 };
216d3cf9c7dSdf157793
217d3cf9c7dSdf157793 static struct qinit asy_rint = {
218d3cf9c7dSdf157793 putq,
219*e476cc14SToomas Soome asyrsrv,
220d3cf9c7dSdf157793 asyopen,
221d3cf9c7dSdf157793 asyclose,
222d3cf9c7dSdf157793 NULL,
223d3cf9c7dSdf157793 &asy_info,
224d3cf9c7dSdf157793 NULL
225d3cf9c7dSdf157793 };
226d3cf9c7dSdf157793
227d3cf9c7dSdf157793 static struct qinit asy_wint = {
228*e476cc14SToomas Soome asywput,
229d3cf9c7dSdf157793 NULL,
230d3cf9c7dSdf157793 NULL,
231d3cf9c7dSdf157793 NULL,
232d3cf9c7dSdf157793 NULL,
233d3cf9c7dSdf157793 &asy_info,
234d3cf9c7dSdf157793 NULL
235d3cf9c7dSdf157793 };
236d3cf9c7dSdf157793
237d3cf9c7dSdf157793 struct streamtab asy_str_info = {
238d3cf9c7dSdf157793 &asy_rint,
239d3cf9c7dSdf157793 &asy_wint,
240d3cf9c7dSdf157793 NULL,
241d3cf9c7dSdf157793 NULL
242d3cf9c7dSdf157793 };
243d3cf9c7dSdf157793
244d3cf9c7dSdf157793 static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
245d3cf9c7dSdf157793 void **result);
246d3cf9c7dSdf157793 static int asyprobe(dev_info_t *);
247d3cf9c7dSdf157793 static int asyattach(dev_info_t *, ddi_attach_cmd_t);
248d3cf9c7dSdf157793 static int asydetach(dev_info_t *, ddi_detach_cmd_t);
249d3cf9c7dSdf157793
250d3cf9c7dSdf157793 static struct cb_ops cb_asy_ops = {
251d3cf9c7dSdf157793 nodev, /* cb_open */
252d3cf9c7dSdf157793 nodev, /* cb_close */
253d3cf9c7dSdf157793 nodev, /* cb_strategy */
254d3cf9c7dSdf157793 nodev, /* cb_print */
255d3cf9c7dSdf157793 nodev, /* cb_dump */
256d3cf9c7dSdf157793 nodev, /* cb_read */
257d3cf9c7dSdf157793 nodev, /* cb_write */
258d3cf9c7dSdf157793 nodev, /* cb_ioctl */
259d3cf9c7dSdf157793 nodev, /* cb_devmap */
260d3cf9c7dSdf157793 nodev, /* cb_mmap */
261d3cf9c7dSdf157793 nodev, /* cb_segmap */
262d3cf9c7dSdf157793 nochpoll, /* cb_chpoll */
263d3cf9c7dSdf157793 ddi_prop_op, /* cb_prop_op */
264d3cf9c7dSdf157793 &asy_str_info, /* cb_stream */
265d3cf9c7dSdf157793 D_MP /* cb_flag */
266d3cf9c7dSdf157793 };
267d3cf9c7dSdf157793
268d3cf9c7dSdf157793 struct dev_ops asy_ops = {
269d3cf9c7dSdf157793 DEVO_REV, /* devo_rev */
270d3cf9c7dSdf157793 0, /* devo_refcnt */
271d3cf9c7dSdf157793 asyinfo, /* devo_getinfo */
272d3cf9c7dSdf157793 nulldev, /* devo_identify */
273d3cf9c7dSdf157793 asyprobe, /* devo_probe */
274d3cf9c7dSdf157793 asyattach, /* devo_attach */
275d3cf9c7dSdf157793 asydetach, /* devo_detach */
276d3cf9c7dSdf157793 nodev, /* devo_reset */
277d3cf9c7dSdf157793 &cb_asy_ops, /* devo_cb_ops */
27819397407SSherry Moore NULL, /* devo_bus_ops */
27919397407SSherry Moore NULL, /* devo_power */
28019397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */
281d3cf9c7dSdf157793 };
282d3cf9c7dSdf157793
283d3cf9c7dSdf157793 /*
284d3cf9c7dSdf157793 * Module linkage information for the kernel.
285d3cf9c7dSdf157793 */
286d3cf9c7dSdf157793
287d3cf9c7dSdf157793 static struct modldrv modldrv = {
288d3cf9c7dSdf157793 &mod_driverops, /* Type of module. This one is a driver */
28919397407SSherry Moore "su driver",
290d3cf9c7dSdf157793 &asy_ops, /* driver ops */
291d3cf9c7dSdf157793 };
292d3cf9c7dSdf157793
293d3cf9c7dSdf157793 static struct modlinkage modlinkage = {
294d3cf9c7dSdf157793 MODREV_1,
295d3cf9c7dSdf157793 &modldrv,
296d3cf9c7dSdf157793 NULL
297d3cf9c7dSdf157793 };
298d3cf9c7dSdf157793
299d3cf9c7dSdf157793 int
_init(void)300d3cf9c7dSdf157793 _init(void)
301d3cf9c7dSdf157793 {
302d3cf9c7dSdf157793 int status;
303d3cf9c7dSdf157793
304d3cf9c7dSdf157793 status = ddi_soft_state_init(&su_asycom, sizeof (struct asycom),
305d3cf9c7dSdf157793 SU_INITIAL_SOFT_ITEMS);
306d3cf9c7dSdf157793 if (status != 0)
307d3cf9c7dSdf157793 return (status);
308d3cf9c7dSdf157793 status = ddi_soft_state_init(&su_asyncline, sizeof (struct asyncline),
309d3cf9c7dSdf157793 SU_INITIAL_SOFT_ITEMS);
310d3cf9c7dSdf157793 if (status != 0) {
311d3cf9c7dSdf157793 ddi_soft_state_fini(&su_asycom);
312d3cf9c7dSdf157793 return (status);
313d3cf9c7dSdf157793 }
314d3cf9c7dSdf157793
315d3cf9c7dSdf157793 if ((status = mod_install(&modlinkage)) != 0) {
316d3cf9c7dSdf157793 ddi_soft_state_fini(&su_asycom);
317d3cf9c7dSdf157793 ddi_soft_state_fini(&su_asyncline);
318d3cf9c7dSdf157793 }
319d3cf9c7dSdf157793
320d3cf9c7dSdf157793 return (status);
321d3cf9c7dSdf157793 }
322d3cf9c7dSdf157793
323d3cf9c7dSdf157793 int
_fini(void)324d3cf9c7dSdf157793 _fini(void)
325d3cf9c7dSdf157793 {
326d3cf9c7dSdf157793 int i;
327d3cf9c7dSdf157793
328d3cf9c7dSdf157793 i = mod_remove(&modlinkage);
329d3cf9c7dSdf157793 if (i == 0) {
330d3cf9c7dSdf157793 ddi_soft_state_fini(&su_asycom);
331d3cf9c7dSdf157793 ddi_soft_state_fini(&su_asyncline);
332d3cf9c7dSdf157793 }
333d3cf9c7dSdf157793
334d3cf9c7dSdf157793 return (i);
335d3cf9c7dSdf157793 }
336d3cf9c7dSdf157793
337d3cf9c7dSdf157793 int
_info(struct modinfo * modinfop)338d3cf9c7dSdf157793 _info(struct modinfo *modinfop)
339d3cf9c7dSdf157793 {
340d3cf9c7dSdf157793 return (mod_info(&modlinkage, modinfop));
341d3cf9c7dSdf157793 }
342d3cf9c7dSdf157793
343d3cf9c7dSdf157793 static int
asyprobe(dev_info_t * devi)344d3cf9c7dSdf157793 asyprobe(dev_info_t *devi)
345d3cf9c7dSdf157793 {
346d3cf9c7dSdf157793 int instance;
347d3cf9c7dSdf157793 ddi_acc_handle_t handle;
348d3cf9c7dSdf157793 uchar_t *addr;
349d3cf9c7dSdf157793 ddi_device_acc_attr_t attr;
350d3cf9c7dSdf157793
351d3cf9c7dSdf157793 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
352d3cf9c7dSdf157793 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
353d3cf9c7dSdf157793 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
354d3cf9c7dSdf157793 if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, (caddr_t *)&addr,
355d3cf9c7dSdf157793 SU_REGOFFSET, SU_REGISTER_LEN, &attr, &handle) != DDI_SUCCESS) {
356d3cf9c7dSdf157793 cmn_err(CE_WARN, "asyprobe regs map setup failed");
357d3cf9c7dSdf157793 return (DDI_PROBE_FAILURE);
358d3cf9c7dSdf157793 }
359d3cf9c7dSdf157793 #ifdef DEBUG
360d3cf9c7dSdf157793 if (asydebug)
361d3cf9c7dSdf157793 printf("Probe address mapped %p\n", (void *)addr);
362d3cf9c7dSdf157793 #endif
363d3cf9c7dSdf157793
364d3cf9c7dSdf157793 /*
365d3cf9c7dSdf157793 * Probe for the device:
366d3cf9c7dSdf157793 * Ser. int. uses bits 0,1,2; FIFO uses 3,6,7; 4,5 wired low.
367d3cf9c7dSdf157793 * If bit 4 or 5 appears on inb() ISR, board is not there.
368d3cf9c7dSdf157793 */
3692d526643Sjesusm if (ddi_get8(handle, addr+ISR) & 0x30) {
3702d526643Sjesusm ddi_regs_map_free(&handle);
371d3cf9c7dSdf157793 return (DDI_PROBE_FAILURE);
3722d526643Sjesusm }
3732d526643Sjesusm
374d3cf9c7dSdf157793 instance = ddi_get_instance(devi);
375d3cf9c7dSdf157793 if (max_asy_instance < instance)
376d3cf9c7dSdf157793 max_asy_instance = instance;
377d3cf9c7dSdf157793 ddi_regs_map_free(&handle);
378d3cf9c7dSdf157793
379d3cf9c7dSdf157793 return (DDI_PROBE_SUCCESS); /* hw is present */
380d3cf9c7dSdf157793 }
381d3cf9c7dSdf157793
382d3cf9c7dSdf157793 static int
asydetach(dev_info_t * devi,ddi_detach_cmd_t cmd)383d3cf9c7dSdf157793 asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
384d3cf9c7dSdf157793 {
385*e476cc14SToomas Soome int instance;
386d3cf9c7dSdf157793 struct asycom *asy;
387d3cf9c7dSdf157793 struct asyncline *async;
388d3cf9c7dSdf157793 char name[16];
389d3cf9c7dSdf157793
390d3cf9c7dSdf157793 instance = ddi_get_instance(devi); /* find out which unit */
391d3cf9c7dSdf157793
392d3cf9c7dSdf157793 asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance);
393d3cf9c7dSdf157793 async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance);
394d3cf9c7dSdf157793
395d3cf9c7dSdf157793 switch (cmd) {
396d3cf9c7dSdf157793 case DDI_DETACH:
397d3cf9c7dSdf157793 break;
398d3cf9c7dSdf157793 case DDI_SUSPEND:
399d3cf9c7dSdf157793 /* grab both mutex locks */
400d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
401d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
402d3cf9c7dSdf157793 if (asy->suspended) {
403d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
404d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
405d3cf9c7dSdf157793 return (DDI_SUCCESS);
406d3cf9c7dSdf157793 }
407d3cf9c7dSdf157793 asy->suspended = B_TRUE;
40817ea09c7Sanovick
40917ea09c7Sanovick /*
4100280efdcSzk194757 * The quad UART ST16C554D, version D2 (made by EXAR)
4110280efdcSzk194757 * has an anomaly of generating spurious interrupts
4120280efdcSzk194757 * when the ICR is loaded with zero. The workaround
4130280efdcSzk194757 * would be to read/write any register with DATA1 bit
4140280efdcSzk194757 * set to 0 before such write.
41517ea09c7Sanovick */
416f63f7506Sanovick if (asy->asy_hwtype == ASY16C554D)
417f63f7506Sanovick OUTB(SPR, 0);
41817ea09c7Sanovick
419d3cf9c7dSdf157793 /* Disable further interrupts */
420d3cf9c7dSdf157793 OUTB(ICR, 0);
421d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
422d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
423d3cf9c7dSdf157793 return (DDI_SUCCESS);
424d3cf9c7dSdf157793
425d3cf9c7dSdf157793 default:
426d3cf9c7dSdf157793 return (DDI_FAILURE);
427d3cf9c7dSdf157793 }
428d3cf9c7dSdf157793
429d3cf9c7dSdf157793 #ifdef DEBUG
430d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_INIT)
431d3cf9c7dSdf157793 cmn_err(CE_NOTE, "su%d: ASY%s shutdown.", instance,
432d3cf9c7dSdf157793 asy->asy_hwtype == ASY82510 ? "82510" :
433d3cf9c7dSdf157793 asy->asy_hwtype == ASY16550AF ? "16550AF" :
434f63f7506Sanovick asy->asy_hwtype == ASY16C554D ? "16C554D" :
435d3cf9c7dSdf157793 "8250");
436d3cf9c7dSdf157793 #endif
437d3cf9c7dSdf157793 /*
438d3cf9c7dSdf157793 * Before removing interrupts it is always better to disable
439d3cf9c7dSdf157793 * interrupts if the chip gives a provision to disable the
440d3cf9c7dSdf157793 * serial port interrupts.
441d3cf9c7dSdf157793 */
442d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
443d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
44417ea09c7Sanovick /* disable interrupts, see EXAR bug */
445f63f7506Sanovick if (asy->asy_hwtype == ASY16C554D)
446f63f7506Sanovick OUTB(SPR, 0);
44717ea09c7Sanovick OUTB(ICR, 0);
448d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
449d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
450d3cf9c7dSdf157793
451d3cf9c7dSdf157793 /* remove minor device node(s) for this device */
452d3cf9c7dSdf157793 (void) sprintf(name, "%c", (instance+'a')); /* serial-port */
453d3cf9c7dSdf157793 ddi_remove_minor_node(devi, name);
454d3cf9c7dSdf157793 (void) sprintf(name, "%c,cu", (instance+'a')); /* serial-port:dailout */
455d3cf9c7dSdf157793 ddi_remove_minor_node(devi, name);
456d3cf9c7dSdf157793
457d3cf9c7dSdf157793 mutex_destroy(asy->asy_excl);
458d3cf9c7dSdf157793 mutex_destroy(asy->asy_excl_hi);
459d3cf9c7dSdf157793 kmem_free(asy->asy_excl, sizeof (kmutex_t));
460d3cf9c7dSdf157793 kmem_free(asy->asy_excl_hi, sizeof (kmutex_t));
461d3cf9c7dSdf157793 cv_destroy(&async->async_flags_cv);
462d3cf9c7dSdf157793 kstat_delete(asy->sukstat);
463d3cf9c7dSdf157793 ddi_remove_intr(devi, 0, asy->asy_iblock);
464d3cf9c7dSdf157793 ddi_regs_map_free(&asy->asy_handle);
465d3cf9c7dSdf157793 ddi_remove_softintr(asy->asy_softintr_id);
466d3cf9c7dSdf157793 mutex_destroy(asy->asy_soft_lock);
467d3cf9c7dSdf157793 kmem_free(asy->asy_soft_lock, sizeof (kmutex_t));
468d3cf9c7dSdf157793 ddi_soft_state_free(su_asycom, instance);
469d3cf9c7dSdf157793 ddi_soft_state_free(su_asyncline, instance);
470d3cf9c7dSdf157793 return (DDI_SUCCESS);
471d3cf9c7dSdf157793 }
472d3cf9c7dSdf157793
473d3cf9c7dSdf157793 static int
asyattach(dev_info_t * devi,ddi_attach_cmd_t cmd)474d3cf9c7dSdf157793 asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
475d3cf9c7dSdf157793 {
476*e476cc14SToomas Soome int instance;
477d3cf9c7dSdf157793 struct asycom *asy;
478d3cf9c7dSdf157793 struct asyncline *async;
479d3cf9c7dSdf157793 char name[40];
480d3cf9c7dSdf157793 ddi_device_acc_attr_t attr;
481d3cf9c7dSdf157793 enum states { EMPTY, SOFTSTATE, REGSMAP, MUTEXES, ADDINTR,
482d3cf9c7dSdf157793 SOFTINTR, ASYINIT, KSTAT, MINORNODE };
483d3cf9c7dSdf157793 enum states state = EMPTY;
484f63f7506Sanovick char *hwtype;
485d3cf9c7dSdf157793
486d3cf9c7dSdf157793 instance = ddi_get_instance(devi); /* find out which unit */
487d3cf9c7dSdf157793
488d3cf9c7dSdf157793 /* cannot attach a device that has not been probed first */
489d3cf9c7dSdf157793 if (instance > max_asy_instance)
490d3cf9c7dSdf157793 return (DDI_FAILURE);
491d3cf9c7dSdf157793
492d3cf9c7dSdf157793 if (cmd != DDI_RESUME) {
493d3cf9c7dSdf157793 /* Allocate soft state space */
494d3cf9c7dSdf157793 if (ddi_soft_state_zalloc(su_asycom, instance) != DDI_SUCCESS) {
495d3cf9c7dSdf157793 cmn_err(CE_WARN, "su%d: cannot allocate soft state",
496d3cf9c7dSdf157793 instance);
497d3cf9c7dSdf157793 goto error;
498d3cf9c7dSdf157793 }
499d3cf9c7dSdf157793 }
500d3cf9c7dSdf157793 state = SOFTSTATE;
501d3cf9c7dSdf157793
502d3cf9c7dSdf157793 asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance);
503d3cf9c7dSdf157793
504d3cf9c7dSdf157793 if (asy == NULL) {
505d3cf9c7dSdf157793 cmn_err(CE_WARN, "su%d: cannot get soft state", instance);
506d3cf9c7dSdf157793 goto error;
507d3cf9c7dSdf157793 }
508d3cf9c7dSdf157793
509d3cf9c7dSdf157793 switch (cmd) {
510d3cf9c7dSdf157793 case DDI_ATTACH:
511d3cf9c7dSdf157793 break;
512d3cf9c7dSdf157793 case DDI_RESUME: {
513d3cf9c7dSdf157793 struct asyncline *async;
514d3cf9c7dSdf157793
515d3cf9c7dSdf157793 /* grab both mutex locks */
516d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
517d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
518d3cf9c7dSdf157793 if (!asy->suspended) {
519d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
520d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
521d3cf9c7dSdf157793 return (DDI_SUCCESS);
522d3cf9c7dSdf157793 }
5230280efdcSzk194757 /*
5240280efdcSzk194757 * re-setup all the registers and enable interrupts if
5250280efdcSzk194757 * needed
5260280efdcSzk194757 */
527d3cf9c7dSdf157793 async = (struct asyncline *)asy->asy_priv;
528d3cf9c7dSdf157793 if ((async) && (async->async_flags & ASYNC_ISOPEN))
529d3cf9c7dSdf157793 (void) asy_program(asy, ASY_INIT);
530d3cf9c7dSdf157793 asy->suspended = B_FALSE;
531d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
532d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
533d3cf9c7dSdf157793 return (DDI_SUCCESS);
534d3cf9c7dSdf157793 }
535d3cf9c7dSdf157793 default:
536d3cf9c7dSdf157793 goto error;
537d3cf9c7dSdf157793 }
538d3cf9c7dSdf157793
539d3cf9c7dSdf157793 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
540d3cf9c7dSdf157793 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
541d3cf9c7dSdf157793 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
542d3cf9c7dSdf157793
543d3cf9c7dSdf157793 if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO,
544d3cf9c7dSdf157793 (caddr_t *)&asy->asy_ioaddr, SU_REGOFFSET, SU_REGISTER_LEN,
545d3cf9c7dSdf157793 &attr, &asy->asy_handle) != DDI_SUCCESS) {
546d3cf9c7dSdf157793 cmn_err(CE_WARN, "asyprobe regs map setup failed");
547d3cf9c7dSdf157793 goto error;
548d3cf9c7dSdf157793 }
549d3cf9c7dSdf157793 state = REGSMAP;
550d3cf9c7dSdf157793
551d3cf9c7dSdf157793 #ifdef DEBUG
552d3cf9c7dSdf157793 if (asydebug)
553d3cf9c7dSdf157793 printf("su attach mapped %p\n", (void *)asy->asy_ioaddr);
554d3cf9c7dSdf157793 #endif
555d3cf9c7dSdf157793
556d3cf9c7dSdf157793 /*
557d3cf9c7dSdf157793 * Initialize the port with default settings.
558d3cf9c7dSdf157793 */
559d3cf9c7dSdf157793 asy->asy_fifo_buf = 1;
560d3cf9c7dSdf157793 asy->asy_use_fifo = FIFO_OFF;
561d3cf9c7dSdf157793
562d3cf9c7dSdf157793 /*
563d3cf9c7dSdf157793 * Check for baudrate generator's "baud-divisor-factor" property setup
564d3cf9c7dSdf157793 * by OBP, since different UART chips might have different baudrate
565d3cf9c7dSdf157793 * generator divisor. e.g., in case of NSPG's Sputnik platform, the
566d3cf9c7dSdf157793 * baud-divisor-factor is 13, it uses dedicated 16552 "DUART" chip
567d3cf9c7dSdf157793 * instead of SuperIO. Since the baud-divisor-factor must be a positive
568d3cf9c7dSdf157793 * integer, the divisors will always be at least as large as the values
569d3cf9c7dSdf157793 * in asyspdtab[]. Make the default factor 1.
570d3cf9c7dSdf157793 */
571d3cf9c7dSdf157793 asy->asy_baud_divisor_factor = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
572d3cf9c7dSdf157793 DDI_PROP_DONTPASS, "baud-divisor-factor", 1);
573d3cf9c7dSdf157793
574d3cf9c7dSdf157793 /* set speed cap */
575d3cf9c7dSdf157793 asy->asy_speed_cap = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
576d3cf9c7dSdf157793 DDI_PROP_DONTPASS, "serial-speed-cap", 115200);
577d3cf9c7dSdf157793
578d3cf9c7dSdf157793 /* check for ASY82510 chip */
579d3cf9c7dSdf157793 OUTB(ISR, 0x20);
580d3cf9c7dSdf157793 if (INB(ISR) & 0x20) { /* 82510 chip is present */
581d3cf9c7dSdf157793 /*
582d3cf9c7dSdf157793 * Since most of the general operation of the 82510 chip
583d3cf9c7dSdf157793 * can be done from BANK 0 (8250A/16450 compatable mode)
584d3cf9c7dSdf157793 * we will default to BANK 0.
585d3cf9c7dSdf157793 */
586d3cf9c7dSdf157793 asy->asy_hwtype = ASY82510;
587d3cf9c7dSdf157793 OUTB(DAT+7, 0x04); /* clear status */
588d3cf9c7dSdf157793 OUTB(ISR, 0x40); /* set to bank 2 */
589d3cf9c7dSdf157793 OUTB(MCR, 0x08); /* IMD */
590d3cf9c7dSdf157793 OUTB(DAT, 0x21); /* FMD */
591d3cf9c7dSdf157793 OUTB(ISR, 0x00); /* set to bank 0 */
592d3cf9c7dSdf157793 asy->asy_trig_level = 0;
593d3cf9c7dSdf157793 } else { /* Set the UART in FIFO mode if it has FIFO buffers */
594d3cf9c7dSdf157793 asy->asy_hwtype = ASY16550AF;
595d3cf9c7dSdf157793 OUTB(FIFOR, 0x00); /* clear fifo register */
596d3cf9c7dSdf157793 asy->asy_trig_level = 0x00; /* sets the fifo Threshold to 1 */
597d3cf9c7dSdf157793
598d3cf9c7dSdf157793 /* set/Enable FIFO */
599d3cf9c7dSdf157793 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | FIFORXFLSH |
600d3cf9c7dSdf157793 (asy->asy_trig_level & 0xff));
601d3cf9c7dSdf157793
602d3cf9c7dSdf157793 if ((INB(ISR) & 0xc0) == 0xc0)
603d3cf9c7dSdf157793 asy->asy_use_fifo = FIFO_ON;
604d3cf9c7dSdf157793 else {
605d3cf9c7dSdf157793 asy->asy_hwtype = ASY8250;
606d3cf9c7dSdf157793 OUTB(FIFOR, 0x00); /* NO FIFOs */
607d3cf9c7dSdf157793 asy->asy_trig_level = 0;
608d3cf9c7dSdf157793 }
609d3cf9c7dSdf157793 }
610d3cf9c7dSdf157793
611f63f7506Sanovick /* check for ST16C554D chip */
612f63f7506Sanovick if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, DDI_PROP_NOTPROM |
613f63f7506Sanovick DDI_PROP_DONTPASS, "hwtype", &hwtype)) == DDI_PROP_SUCCESS) {
614f63f7506Sanovick if (strcmp(hwtype, "ST16C554D") == 0)
615f63f7506Sanovick asy->asy_hwtype = ASY16C554D;
616f63f7506Sanovick ddi_prop_free(hwtype);
617f63f7506Sanovick }
618f63f7506Sanovick
61917ea09c7Sanovick /* disable interrupts, see EXAR bug */
620f63f7506Sanovick if (asy->asy_hwtype == ASY16C554D)
621f63f7506Sanovick OUTB(SPR, 0);
62217ea09c7Sanovick OUTB(ICR, 0);
623d3cf9c7dSdf157793 OUTB(LCR, DLAB); /* select baud rate generator */
624d3cf9c7dSdf157793 /* Set the baud rate to 9600 */
625d3cf9c7dSdf157793 OUTB(DAT+DLL, (ASY9600*asy->asy_baud_divisor_factor) & 0xff);
626d3cf9c7dSdf157793 OUTB(DAT+DLH, ((ASY9600*asy->asy_baud_divisor_factor) >> 8) & 0xff);
627d3cf9c7dSdf157793 OUTB(LCR, STOP1|BITS8);
628d3cf9c7dSdf157793 OUTB(MCR, (DTR | RTS| OUT2));
629d3cf9c7dSdf157793
630d3cf9c7dSdf157793 /*
631d3cf9c7dSdf157793 * Set up the other components of the asycom structure for this port.
632d3cf9c7dSdf157793 */
633d3cf9c7dSdf157793 asy->asy_excl = (kmutex_t *)
634d3cf9c7dSdf157793 kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
635d3cf9c7dSdf157793 asy->asy_excl_hi = (kmutex_t *)
636d3cf9c7dSdf157793 kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
637d3cf9c7dSdf157793 asy->asy_soft_lock = (kmutex_t *)
638d3cf9c7dSdf157793 kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
639d3cf9c7dSdf157793 asy->asy_unit = instance;
640d3cf9c7dSdf157793 asy->asy_dip = devi;
641d3cf9c7dSdf157793
642d3cf9c7dSdf157793 if (ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) != DDI_SUCCESS) {
643d3cf9c7dSdf157793 cmn_err(CE_NOTE,
644d3cf9c7dSdf157793 "Get iblock_cookie failed-Device interrupt%x\n", instance);
645d3cf9c7dSdf157793 goto error;
646d3cf9c7dSdf157793 }
647d3cf9c7dSdf157793
648d3cf9c7dSdf157793 if (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_HIGH,
649d3cf9c7dSdf157793 &asy->asy_soft_iblock) != DDI_SUCCESS) {
650d3cf9c7dSdf157793 cmn_err(CE_NOTE, "Get iblock_cookie failed -soft interrupt%x\n",
651d3cf9c7dSdf157793 instance);
652d3cf9c7dSdf157793 goto error;
653d3cf9c7dSdf157793 }
654d3cf9c7dSdf157793
655d3cf9c7dSdf157793 mutex_init(asy->asy_soft_lock, NULL, MUTEX_DRIVER,
656d3cf9c7dSdf157793 (void *)asy->asy_soft_iblock);
657d3cf9c7dSdf157793 mutex_init(asy->asy_excl, NULL, MUTEX_DRIVER, NULL);
658d3cf9c7dSdf157793 mutex_init(asy->asy_excl_hi, NULL, MUTEX_DRIVER,
659d3cf9c7dSdf157793 (void *)asy->asy_iblock);
660d3cf9c7dSdf157793 state = MUTEXES;
661d3cf9c7dSdf157793
662d3cf9c7dSdf157793 /*
663d3cf9c7dSdf157793 * Install interrupt handlers for this device.
664d3cf9c7dSdf157793 */
665d3cf9c7dSdf157793 if (ddi_add_intr(devi, 0, &(asy->asy_iblock), 0, asyintr,
666d3cf9c7dSdf157793 (caddr_t)asy) != DDI_SUCCESS) {
667d3cf9c7dSdf157793 cmn_err(CE_CONT,
668d3cf9c7dSdf157793 "Cannot set device interrupt for su driver\n");
669d3cf9c7dSdf157793 goto error;
670d3cf9c7dSdf157793 }
671d3cf9c7dSdf157793 state = ADDINTR;
672d3cf9c7dSdf157793
673d3cf9c7dSdf157793 if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &(asy->asy_softintr_id),
674d3cf9c7dSdf157793 &asy->asy_soft_iblock, 0, asysoftintr, (caddr_t)asy)
675d3cf9c7dSdf157793 != DDI_SUCCESS) {
676d3cf9c7dSdf157793 cmn_err(CE_CONT, "Cannot set soft interrupt for su driver\n");
677d3cf9c7dSdf157793 goto error;
678d3cf9c7dSdf157793 }
679d3cf9c7dSdf157793 state = SOFTINTR;
680d3cf9c7dSdf157793
681d3cf9c7dSdf157793 /* initialize the asyncline structure */
682d3cf9c7dSdf157793 if (ddi_soft_state_zalloc(su_asyncline, instance) != DDI_SUCCESS) {
683d3cf9c7dSdf157793 cmn_err(CE_CONT, "su%d: cannot allocate soft state", instance);
684d3cf9c7dSdf157793 goto error;
685d3cf9c7dSdf157793 }
686d3cf9c7dSdf157793 state = ASYINIT;
687d3cf9c7dSdf157793
688d3cf9c7dSdf157793 async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance);
689d3cf9c7dSdf157793
690d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
691d3cf9c7dSdf157793 async->async_common = asy;
692d3cf9c7dSdf157793 cv_init(&async->async_flags_cv, NULL, CV_DEFAULT, NULL);
693d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
694d3cf9c7dSdf157793
695d3cf9c7dSdf157793 if ((asy->sukstat = kstat_create("su", instance, "serialstat",
696d3cf9c7dSdf157793 "misc", KSTAT_TYPE_NAMED, 2, KSTAT_FLAG_VIRTUAL)) != NULL) {
697d3cf9c7dSdf157793 asy->sukstat->ks_data = &asy->kstats;
698d3cf9c7dSdf157793 kstat_named_init(&asy->kstats.ringover, "ring buffer overflow",
699d3cf9c7dSdf157793 KSTAT_DATA_UINT64);
700d3cf9c7dSdf157793 kstat_named_init(&asy->kstats.siloover, "silo overflow",
701d3cf9c7dSdf157793 KSTAT_DATA_UINT64);
702d3cf9c7dSdf157793 kstat_install(asy->sukstat);
703d3cf9c7dSdf157793 }
704d3cf9c7dSdf157793 state = KSTAT;
705d3cf9c7dSdf157793
706d3cf9c7dSdf157793 if (strcmp(ddi_node_name(devi), "rsc-console") == 0) {
707d3cf9c7dSdf157793 /*
708d3cf9c7dSdf157793 * If the device is configured as the 'rsc-console'
709d3cf9c7dSdf157793 * create the minor device for this node.
710d3cf9c7dSdf157793 */
711d3cf9c7dSdf157793 if (ddi_create_minor_node(devi, "ssp", S_IFCHR,
712574493ffSToomas Soome asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, 0) == DDI_FAILURE) {
713d3cf9c7dSdf157793 cmn_err(CE_WARN,
714d3cf9c7dSdf157793 "%s%d: Failed to create node rsc-console",
715d3cf9c7dSdf157793 ddi_get_name(devi), ddi_get_instance(devi));
716d3cf9c7dSdf157793 goto error;
717d3cf9c7dSdf157793 }
718d3cf9c7dSdf157793
719d3cf9c7dSdf157793 asy->asy_lom_console = 0;
720d3cf9c7dSdf157793 asy->asy_rsc_console = 1;
721d3cf9c7dSdf157793 asy->asy_rsc_control = 0;
722d3cf9c7dSdf157793 asy->asy_device_type = ASY_SERIAL;
723d3cf9c7dSdf157793 asy->asy_flags |= ASY_IGNORE_CD;
724d3cf9c7dSdf157793
725d3cf9c7dSdf157793 } else if (strcmp(ddi_node_name(devi), "lom-console") == 0) {
726d3cf9c7dSdf157793 /*
727d3cf9c7dSdf157793 * If the device is configured as the 'lom-console'
728d3cf9c7dSdf157793 * create the minor device for this node.
729d3cf9c7dSdf157793 * Do not create a dialout device.
730d3cf9c7dSdf157793 * Use the same minor numbers as would be used for standard
731d3cf9c7dSdf157793 * serial instances.
732d3cf9c7dSdf157793 */
733d3cf9c7dSdf157793 if (ddi_create_minor_node(devi, "lom-console", S_IFCHR,
734574493ffSToomas Soome instance, DDI_NT_SERIAL_LOMCON, 0) == DDI_FAILURE) {
735d3cf9c7dSdf157793 cmn_err(CE_WARN,
736d3cf9c7dSdf157793 "%s%d: Failed to create node lom-console",
737d3cf9c7dSdf157793 ddi_get_name(devi), ddi_get_instance(devi));
738d3cf9c7dSdf157793 goto error;
739d3cf9c7dSdf157793 }
740d3cf9c7dSdf157793 asy->asy_lom_console = 1;
741d3cf9c7dSdf157793 asy->asy_rsc_console = 0;
742d3cf9c7dSdf157793 asy->asy_rsc_control = 0;
743d3cf9c7dSdf157793 asy->asy_device_type = ASY_SERIAL;
744d3cf9c7dSdf157793 asy->asy_flags |= ASY_IGNORE_CD;
745d3cf9c7dSdf157793
746d3cf9c7dSdf157793 } else if (strcmp(ddi_node_name(devi), "rsc-control") == 0) {
747d3cf9c7dSdf157793 /*
748d3cf9c7dSdf157793 * If the device is configured as the 'rsc-control'
749d3cf9c7dSdf157793 * create the minor device for this node.
750d3cf9c7dSdf157793 */
751d3cf9c7dSdf157793 if (ddi_create_minor_node(devi, "sspctl", S_IFCHR,
752574493ffSToomas Soome asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, 0) == DDI_FAILURE) {
753d3cf9c7dSdf157793 cmn_err(CE_WARN, "%s%d: Failed to create rsc-control",
754d3cf9c7dSdf157793 ddi_get_name(devi), ddi_get_instance(devi));
755d3cf9c7dSdf157793 goto error;
756d3cf9c7dSdf157793 }
757d3cf9c7dSdf157793
758d3cf9c7dSdf157793 asy->asy_lom_console = 0;
759d3cf9c7dSdf157793 asy->asy_rsc_console = 0;
760d3cf9c7dSdf157793 asy->asy_rsc_control = 1;
761d3cf9c7dSdf157793 asy->asy_device_type = ASY_SERIAL;
762d3cf9c7dSdf157793 asy->asy_flags |= ASY_IGNORE_CD;
763d3cf9c7dSdf157793
764d3cf9c7dSdf157793 } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
765d3cf9c7dSdf157793 "keyboard", 0)) {
766d3cf9c7dSdf157793 /*
767d3cf9c7dSdf157793 * If the device is a keyboard, then create an internal
768d3cf9c7dSdf157793 * pathname so that the dacf code will link the node into
769d3cf9c7dSdf157793 * the keyboard console stream. See dacf.conf.
770d3cf9c7dSdf157793 */
771d3cf9c7dSdf157793 if (ddi_create_internal_pathname(devi, "keyboard",
772d3cf9c7dSdf157793 S_IFCHR, instance) == DDI_FAILURE) {
773d3cf9c7dSdf157793 goto error;
774d3cf9c7dSdf157793 }
775d3cf9c7dSdf157793 asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */
776d3cf9c7dSdf157793 asy->asy_device_type = ASY_KEYBOARD; /* Device type */
777d3cf9c7dSdf157793 } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
778d3cf9c7dSdf157793 "mouse", 0)) {
779d3cf9c7dSdf157793 /*
780d3cf9c7dSdf157793 * If the device is a mouse, then create an internal
781d3cf9c7dSdf157793 * pathname so that the dacf code will link the node into
782d3cf9c7dSdf157793 * the mouse stream. See dacf.conf.
783d3cf9c7dSdf157793 */
784d3cf9c7dSdf157793 if (ddi_create_internal_pathname(devi, "mouse", S_IFCHR,
785d3cf9c7dSdf157793 instance) == DDI_FAILURE) {
786d3cf9c7dSdf157793 goto error;
787d3cf9c7dSdf157793 }
788d3cf9c7dSdf157793 asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */
789d3cf9c7dSdf157793 asy->asy_device_type = ASY_MOUSE;
790d3cf9c7dSdf157793 } else {
791d3cf9c7dSdf157793 /*
792d3cf9c7dSdf157793 * If not used for keyboard/mouse, create minor devices nodes
793d3cf9c7dSdf157793 * for this device
794d3cf9c7dSdf157793 */
795d3cf9c7dSdf157793 /* serial-port */
796d3cf9c7dSdf157793 (void) sprintf(name, "%c", (instance+'a'));
797d3cf9c7dSdf157793 if (ddi_create_minor_node(devi, name, S_IFCHR, instance,
798574493ffSToomas Soome DDI_NT_SERIAL_MB, 0) == DDI_FAILURE) {
799d3cf9c7dSdf157793 goto error;
800d3cf9c7dSdf157793 }
801d3cf9c7dSdf157793 state = MINORNODE;
802d3cf9c7dSdf157793 /* serial-port:dailout */
803d3cf9c7dSdf157793 (void) sprintf(name, "%c,cu", (instance+'a'));
804d3cf9c7dSdf157793 if (ddi_create_minor_node(devi, name, S_IFCHR, instance|OUTLINE,
805574493ffSToomas Soome DDI_NT_SERIAL_MB_DO, 0) == DDI_FAILURE) {
806d3cf9c7dSdf157793 goto error;
807d3cf9c7dSdf157793 }
808d3cf9c7dSdf157793 /* Property for ignoring DCD */
809d3cf9c7dSdf157793 if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
810d3cf9c7dSdf157793 "ignore-cd", 0)) {
811d3cf9c7dSdf157793 asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */
812d3cf9c7dSdf157793 } else {
813d3cf9c7dSdf157793 asy->asy_flags &= ~ASY_IGNORE_CD;
814d3cf9c7dSdf157793 /*
815d3cf9c7dSdf157793 * if ignore-cd is not available it could be
816d3cf9c7dSdf157793 * some old legacy platform, try to see
817d3cf9c7dSdf157793 * whether the old legacy property exists
818d3cf9c7dSdf157793 */
819d3cf9c7dSdf157793 (void) sprintf(name,
820d3cf9c7dSdf157793 "port-%c-ignore-cd", (instance+ 'a'));
821d3cf9c7dSdf157793 if (ddi_getprop(DDI_DEV_T_ANY, devi,
822d3cf9c7dSdf157793 DDI_PROP_DONTPASS, name, 0))
823d3cf9c7dSdf157793 asy->asy_flags |= ASY_IGNORE_CD;
824d3cf9c7dSdf157793 }
825d3cf9c7dSdf157793 asy->asy_device_type = ASY_SERIAL;
826d3cf9c7dSdf157793 }
8270280efdcSzk194757
8280280efdcSzk194757 /*
8290280efdcSzk194757 * Fill in the polled I/O structure
8300280efdcSzk194757 */
8310280efdcSzk194757 asy->polledio.cons_polledio_version = CONSPOLLEDIO_V0;
8320280efdcSzk194757 asy->polledio.cons_polledio_argument = (cons_polledio_arg_t)asy;
8330280efdcSzk194757 asy->polledio.cons_polledio_putchar = asyputchar;
8340280efdcSzk194757 asy->polledio.cons_polledio_getchar = asygetchar;
8350280efdcSzk194757 asy->polledio.cons_polledio_ischar = asyischar;
8360280efdcSzk194757 asy->polledio.cons_polledio_enter = asy_polled_enter;
8370280efdcSzk194757 asy->polledio.cons_polledio_exit = asy_polled_exit;
8380280efdcSzk194757
8390280efdcSzk194757 /* Initialize saved ICR and polled_enter */
8400280efdcSzk194757 asy->polled_icr = 0;
8410280efdcSzk194757 asy->polled_enter = B_FALSE;
8420280efdcSzk194757
843d3cf9c7dSdf157793 ddi_report_dev(devi);
844d3cf9c7dSdf157793 return (DDI_SUCCESS);
845d3cf9c7dSdf157793
846d3cf9c7dSdf157793 error:
847d3cf9c7dSdf157793 if (state == MINORNODE) {
848d3cf9c7dSdf157793 (void) sprintf(name, "%c", (instance+'a'));
849d3cf9c7dSdf157793 ddi_remove_minor_node(devi, name);
850d3cf9c7dSdf157793 }
851d3cf9c7dSdf157793 if (state >= KSTAT)
852d3cf9c7dSdf157793 kstat_delete(asy->sukstat);
853d3cf9c7dSdf157793 if (state >= ASYINIT) {
854d3cf9c7dSdf157793 cv_destroy(&async->async_flags_cv);
855d3cf9c7dSdf157793 ddi_soft_state_free(su_asyncline, instance);
856d3cf9c7dSdf157793 }
857d3cf9c7dSdf157793 if (state >= SOFTINTR)
858d3cf9c7dSdf157793 ddi_remove_softintr(asy->asy_softintr_id);
859d3cf9c7dSdf157793 if (state >= ADDINTR)
860d3cf9c7dSdf157793 ddi_remove_intr(devi, 0, asy->asy_iblock);
861d3cf9c7dSdf157793 if (state >= MUTEXES) {
862d3cf9c7dSdf157793 mutex_destroy(asy->asy_excl_hi);
863d3cf9c7dSdf157793 mutex_destroy(asy->asy_excl);
864d3cf9c7dSdf157793 mutex_destroy(asy->asy_soft_lock);
865d3cf9c7dSdf157793 kmem_free(asy->asy_excl_hi, sizeof (kmutex_t));
866d3cf9c7dSdf157793 kmem_free(asy->asy_excl, sizeof (kmutex_t));
867d3cf9c7dSdf157793 kmem_free(asy->asy_soft_lock, sizeof (kmutex_t));
868d3cf9c7dSdf157793 }
869d3cf9c7dSdf157793 if (state >= REGSMAP)
870d3cf9c7dSdf157793 ddi_regs_map_free(&asy->asy_handle);
871d3cf9c7dSdf157793 if (state >= SOFTSTATE)
872d3cf9c7dSdf157793 ddi_soft_state_free(su_asycom, instance);
873d3cf9c7dSdf157793 /* no action for EMPTY state */
874d3cf9c7dSdf157793 return (DDI_FAILURE);
875d3cf9c7dSdf157793 }
876d3cf9c7dSdf157793
877d3cf9c7dSdf157793 static int
asyinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)878d3cf9c7dSdf157793 asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
879d3cf9c7dSdf157793 void **result)
880d3cf9c7dSdf157793 {
881d3cf9c7dSdf157793 _NOTE(ARGUNUSED(dip))
882*e476cc14SToomas Soome dev_t dev = (dev_t)arg;
883*e476cc14SToomas Soome int instance, error;
884d3cf9c7dSdf157793 struct asycom *asy;
885d3cf9c7dSdf157793
886d3cf9c7dSdf157793 if ((instance = UNIT(dev)) > max_asy_instance)
887d3cf9c7dSdf157793 return (DDI_FAILURE);
888d3cf9c7dSdf157793
889d3cf9c7dSdf157793 switch (infocmd) {
890d3cf9c7dSdf157793 case DDI_INFO_DEVT2DEVINFO:
8910280efdcSzk194757 asy = (struct asycom *)ddi_get_soft_state(su_asycom,
8920280efdcSzk194757 instance);
893d3cf9c7dSdf157793 if (asy->asy_dip == NULL)
894d3cf9c7dSdf157793 error = DDI_FAILURE;
895d3cf9c7dSdf157793 else {
896d3cf9c7dSdf157793 *result = (void *) asy->asy_dip;
897d3cf9c7dSdf157793 error = DDI_SUCCESS;
898d3cf9c7dSdf157793 }
899d3cf9c7dSdf157793 break;
900d3cf9c7dSdf157793 case DDI_INFO_DEVT2INSTANCE:
901360e6f5eSmathue *result = (void *)(uintptr_t)instance;
902d3cf9c7dSdf157793 error = DDI_SUCCESS;
903d3cf9c7dSdf157793 break;
904d3cf9c7dSdf157793 default:
905d3cf9c7dSdf157793 error = DDI_FAILURE;
906d3cf9c7dSdf157793 }
907d3cf9c7dSdf157793 return (error);
908d3cf9c7dSdf157793 }
909d3cf9c7dSdf157793
910d3cf9c7dSdf157793 static int
asyopen(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr)911d3cf9c7dSdf157793 asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
912d3cf9c7dSdf157793 {
913d3cf9c7dSdf157793 _NOTE(ARGUNUSED(sflag))
914d3cf9c7dSdf157793 struct asycom *asy;
915d3cf9c7dSdf157793 struct asyncline *async;
916d3cf9c7dSdf157793 int mcr;
917d3cf9c7dSdf157793 int unit;
918d3cf9c7dSdf157793 int len;
919d3cf9c7dSdf157793 struct termios *termiosp;
920d3cf9c7dSdf157793
921d3cf9c7dSdf157793 #ifdef DEBUG
922d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_CLOSE)
923d3cf9c7dSdf157793 printf("open\n");
924d3cf9c7dSdf157793 #endif
925d3cf9c7dSdf157793 unit = UNIT(*dev);
926d3cf9c7dSdf157793 if (unit > max_asy_instance)
927d3cf9c7dSdf157793 return (ENXIO); /* unit not configured */
928d3cf9c7dSdf157793
929d3cf9c7dSdf157793 async = (struct asyncline *)ddi_get_soft_state(su_asyncline, unit);
930d3cf9c7dSdf157793 if (async == NULL)
931d3cf9c7dSdf157793 return (ENXIO);
932d3cf9c7dSdf157793
933d3cf9c7dSdf157793 asy = async->async_common;
934d3cf9c7dSdf157793 if (asy == NULL)
935d3cf9c7dSdf157793 return (ENXIO); /* device not found by autoconfig */
936d3cf9c7dSdf157793
937d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
938d3cf9c7dSdf157793 asy->asy_priv = (caddr_t)async;
939d3cf9c7dSdf157793
940d3cf9c7dSdf157793 again:
941d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
942d3cf9c7dSdf157793 /*
943d3cf9c7dSdf157793 * Block waiting for carrier to come up, unless this is a no-delay open.
944d3cf9c7dSdf157793 */
945d3cf9c7dSdf157793 if (!(async->async_flags & ASYNC_ISOPEN)) {
946d3cf9c7dSdf157793 /*
947d3cf9c7dSdf157793 * If this port is for a RSC console or control
948d3cf9c7dSdf157793 * use the following termio info
949d3cf9c7dSdf157793 */
950d3cf9c7dSdf157793 if (asy->asy_rsc_console || asy->asy_rsc_control) {
951d3cf9c7dSdf157793 async->async_ttycommon.t_cflag = CIBAUDEXT | CBAUDEXT |
952d3cf9c7dSdf157793 (B115200 & CBAUD);
953d3cf9c7dSdf157793 async->async_ttycommon.t_cflag |= ((B115200 << IBSHIFT)
954d3cf9c7dSdf157793 & CIBAUD);
955d3cf9c7dSdf157793 async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL;
956d3cf9c7dSdf157793 } else if (asy->asy_lom_console) {
957d3cf9c7dSdf157793 async->async_ttycommon.t_cflag = B9600 & CBAUD;
958d3cf9c7dSdf157793 async->async_ttycommon.t_cflag |= ((B9600 << IBSHIFT)
959d3cf9c7dSdf157793 & CIBAUD);
960d3cf9c7dSdf157793 async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL;
961d3cf9c7dSdf157793 } else {
962d3cf9c7dSdf157793
963d3cf9c7dSdf157793 /*
964d3cf9c7dSdf157793 * Set the default termios settings (cflag).
965d3cf9c7dSdf157793 * Others are set in ldterm. Release the spin
966d3cf9c7dSdf157793 * mutex as we can block here, reaquire before
967d3cf9c7dSdf157793 * calling asy_program.
968d3cf9c7dSdf157793 */
969d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
970d3cf9c7dSdf157793 if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(),
971d3cf9c7dSdf157793 0, "ttymodes", (caddr_t)&termiosp, &len)
972d3cf9c7dSdf157793 == DDI_PROP_SUCCESS &&
973d3cf9c7dSdf157793 len == sizeof (struct termios)) {
974d3cf9c7dSdf157793 async->async_ttycommon.t_cflag =
975d3cf9c7dSdf157793 termiosp->c_cflag;
976d3cf9c7dSdf157793 kmem_free(termiosp, len);
977d3cf9c7dSdf157793 } else {
978d3cf9c7dSdf157793 cmn_err(CE_WARN,
979d3cf9c7dSdf157793 "su: couldn't get ttymodes property!");
980d3cf9c7dSdf157793 }
981d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
982d3cf9c7dSdf157793 }
983d3cf9c7dSdf157793 async->async_ttycommon.t_iflag = 0;
984d3cf9c7dSdf157793 async->async_ttycommon.t_iocpending = NULL;
985d3cf9c7dSdf157793 async->async_ttycommon.t_size.ws_row = 0;
986d3cf9c7dSdf157793 async->async_ttycommon.t_size.ws_col = 0;
987d3cf9c7dSdf157793 async->async_ttycommon.t_size.ws_xpixel = 0;
988d3cf9c7dSdf157793 async->async_ttycommon.t_size.ws_ypixel = 0;
989d3cf9c7dSdf157793 async->async_dev = *dev;
990d3cf9c7dSdf157793 async->async_wbufcid = 0;
991d3cf9c7dSdf157793
992d3cf9c7dSdf157793 async->async_startc = CSTART;
993d3cf9c7dSdf157793 async->async_stopc = CSTOP;
994d3cf9c7dSdf157793 (void) asy_program(asy, ASY_INIT);
995d3cf9c7dSdf157793 } else if ((async->async_ttycommon.t_flags & TS_XCLUDE) &&
996d3cf9c7dSdf157793 secpolicy_excl_open(cr) != 0) {
997d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
998d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
999d3cf9c7dSdf157793 return (EBUSY);
1000d3cf9c7dSdf157793 } else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) {
1001d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
1002d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
1003d3cf9c7dSdf157793 return (EBUSY);
1004d3cf9c7dSdf157793 }
1005d3cf9c7dSdf157793
1006d3cf9c7dSdf157793 if (*dev & OUTLINE)
1007d3cf9c7dSdf157793 async->async_flags |= ASYNC_OUT;
1008d3cf9c7dSdf157793
1009d3cf9c7dSdf157793 /* Raise DTR on every open */
1010d3cf9c7dSdf157793 mcr = INB(MCR);
1011d3cf9c7dSdf157793 OUTB(MCR, mcr|DTR);
1012d3cf9c7dSdf157793
1013d3cf9c7dSdf157793 /*
1014d3cf9c7dSdf157793 * Check carrier.
1015d3cf9c7dSdf157793 */
1016d3cf9c7dSdf157793 if (asy->asy_flags & ASY_IGNORE_CD)
1017d3cf9c7dSdf157793 async->async_ttycommon.t_flags |= TS_SOFTCAR;
1018d3cf9c7dSdf157793 if ((async->async_ttycommon.t_flags & TS_SOFTCAR) ||
1019d3cf9c7dSdf157793 (INB(MSR) & DCD))
1020d3cf9c7dSdf157793 async->async_flags |= ASYNC_CARR_ON;
1021d3cf9c7dSdf157793 else
1022d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_CARR_ON;
1023d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
1024d3cf9c7dSdf157793
1025d3cf9c7dSdf157793 /*
1026d3cf9c7dSdf157793 * If FNDELAY and FNONBLOCK are clear, block until carrier up.
1027d3cf9c7dSdf157793 * Quit on interrupt.
1028d3cf9c7dSdf157793 */
1029d3cf9c7dSdf157793 if (!(flag & (FNDELAY|FNONBLOCK)) &&
1030d3cf9c7dSdf157793 !(async->async_ttycommon.t_cflag & CLOCAL)) {
1031d3cf9c7dSdf157793 if (!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) ||
1032d3cf9c7dSdf157793 ((async->async_flags & ASYNC_OUT) &&
1033d3cf9c7dSdf157793 !(*dev & OUTLINE))) {
1034d3cf9c7dSdf157793 async->async_flags |= ASYNC_WOPEN;
1035d3cf9c7dSdf157793 if (cv_wait_sig(&async->async_flags_cv,
1036d3cf9c7dSdf157793 asy->asy_excl) == 0) {
1037d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_WOPEN;
1038d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
1039d3cf9c7dSdf157793 return (EINTR);
1040d3cf9c7dSdf157793 }
1041d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_WOPEN;
1042d3cf9c7dSdf157793 goto again;
1043d3cf9c7dSdf157793 }
1044d3cf9c7dSdf157793 } else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) {
1045d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
1046d3cf9c7dSdf157793 return (EBUSY);
1047d3cf9c7dSdf157793 }
1048d3cf9c7dSdf157793
1049d3cf9c7dSdf157793 if (asy->suspended) {
1050d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
1051d3cf9c7dSdf157793 (void) ddi_dev_is_needed(asy->asy_dip, 0, 1);
1052d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
1053d3cf9c7dSdf157793 }
1054d3cf9c7dSdf157793
1055d3cf9c7dSdf157793 async->async_ttycommon.t_readq = rq;
1056d3cf9c7dSdf157793 async->async_ttycommon.t_writeq = WR(rq);
1057d3cf9c7dSdf157793 rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async;
1058d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
1059d3cf9c7dSdf157793 qprocson(rq);
1060d3cf9c7dSdf157793 async->async_flags |= ASYNC_ISOPEN;
1061d3cf9c7dSdf157793 async->async_polltid = 0;
1062d3cf9c7dSdf157793 return (0);
1063d3cf9c7dSdf157793 }
1064d3cf9c7dSdf157793
1065d3cf9c7dSdf157793 static void
async_progress_check(void * arg)1066d3cf9c7dSdf157793 async_progress_check(void *arg)
1067d3cf9c7dSdf157793 {
1068d3cf9c7dSdf157793 struct asyncline *async = arg;
1069d3cf9c7dSdf157793 struct asycom *asy = async->async_common;
1070d3cf9c7dSdf157793 mblk_t *bp;
1071d3cf9c7dSdf157793
1072d3cf9c7dSdf157793 /*
1073d3cf9c7dSdf157793 * We define "progress" as either waiting on a timed break or delay, or
1074d3cf9c7dSdf157793 * having had at least one transmitter interrupt. If none of these are
1075d3cf9c7dSdf157793 * true, then just terminate the output and wake up that close thread.
1076d3cf9c7dSdf157793 */
1077d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
1078d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
1079d3cf9c7dSdf157793 if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) {
1080d3cf9c7dSdf157793 async->async_ocnt = 0;
1081d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_BUSY;
1082d3cf9c7dSdf157793 async->async_timer = 0;
1083d3cf9c7dSdf157793 bp = async->async_xmitblk;
1084d3cf9c7dSdf157793 async->async_xmitblk = NULL;
1085d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
1086d3cf9c7dSdf157793 if (bp != NULL)
1087d3cf9c7dSdf157793 freeb(bp);
1088d3cf9c7dSdf157793 /*
1089d3cf9c7dSdf157793 * Since this timer is running, we know that we're in exit(2).
1090d3cf9c7dSdf157793 * That means that the user can't possibly be waiting on any
1091d3cf9c7dSdf157793 * valid ioctl(2) completion anymore, and we should just flush
1092d3cf9c7dSdf157793 * everything.
1093d3cf9c7dSdf157793 */
1094d3cf9c7dSdf157793 flushq(async->async_ttycommon.t_writeq, FLUSHALL);
1095d3cf9c7dSdf157793 cv_broadcast(&async->async_flags_cv);
1096d3cf9c7dSdf157793 } else {
1097d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_PROGRESS;
1098d3cf9c7dSdf157793 async->async_timer = timeout(async_progress_check, async,
1099d3cf9c7dSdf157793 drv_usectohz(su_drain_check));
1100d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
1101d3cf9c7dSdf157793 }
1102d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
1103d3cf9c7dSdf157793 }
1104d3cf9c7dSdf157793
1105d3cf9c7dSdf157793 /*
1106d3cf9c7dSdf157793 * Close routine.
1107d3cf9c7dSdf157793 */
1108d3cf9c7dSdf157793 static int
asyclose(queue_t * q,int flag,cred_t * cr __unused)1109730a650aSPeter Tribble asyclose(queue_t *q, int flag, cred_t *cr __unused)
1110d3cf9c7dSdf157793 {
1111d3cf9c7dSdf157793 struct asyncline *async;
1112d3cf9c7dSdf157793 struct asycom *asy;
1113d3cf9c7dSdf157793 int icr, lcr;
1114d3cf9c7dSdf157793 int nohupcl;
1115d3cf9c7dSdf157793
1116d3cf9c7dSdf157793
1117d3cf9c7dSdf157793 #ifdef DEBUG
1118d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_CLOSE)
1119d3cf9c7dSdf157793 printf("close\n");
1120d3cf9c7dSdf157793 #endif
1121d3cf9c7dSdf157793 async = q->q_ptr;
1122d3cf9c7dSdf157793 ASSERT(async != NULL);
1123d3cf9c7dSdf157793 asy = async->async_common;
1124d3cf9c7dSdf157793
1125d3cf9c7dSdf157793 /* get the nohupcl OBP property of this device */
1126d3cf9c7dSdf157793 nohupcl = ddi_getprop(DDI_DEV_T_ANY, asy->asy_dip, DDI_PROP_DONTPASS,
1127d3cf9c7dSdf157793 "nohupcl", 0);
1128d3cf9c7dSdf157793
1129d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
1130d3cf9c7dSdf157793 async->async_flags |= ASYNC_CLOSING;
1131d3cf9c7dSdf157793
1132d3cf9c7dSdf157793 /*
1133d3cf9c7dSdf157793 * Turn off PPS handling early to avoid events occuring during
1134d3cf9c7dSdf157793 * close. Also reset the DCD edge monitoring bit.
1135d3cf9c7dSdf157793 */
1136d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
1137d3cf9c7dSdf157793 asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE);
1138d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
1139d3cf9c7dSdf157793
1140d3cf9c7dSdf157793 /*
1141d3cf9c7dSdf157793 * There are two flavors of break -- timed (M_BREAK or TCSBRK) and
1142d3cf9c7dSdf157793 * untimed (TIOCSBRK). For the timed case, these are enqueued on our
1143d3cf9c7dSdf157793 * write queue and there's a timer running, so we don't have to worry
1144d3cf9c7dSdf157793 * about them. For the untimed case, though, the user obviously made a
1145d3cf9c7dSdf157793 * mistake, because these are handled immediately. We'll terminate the
114648bbca81SDaniel Hoffman * break now and honor their implicit request by discarding the rest of
1147d3cf9c7dSdf157793 * the data.
1148d3cf9c7dSdf157793 */
1149d3cf9c7dSdf157793 if (!(async->async_flags & ASYNC_BREAK)) {
1150d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
1151d3cf9c7dSdf157793 lcr = INB(LCR);
1152d3cf9c7dSdf157793 if (lcr & SETBREAK) {
1153d3cf9c7dSdf157793 OUTB(LCR, (lcr & ~SETBREAK));
1154d3cf9c7dSdf157793 }
1155d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
1156d3cf9c7dSdf157793 if (lcr & SETBREAK)
1157d3cf9c7dSdf157793 goto nodrain;
1158d3cf9c7dSdf157793 }
1159d3cf9c7dSdf157793
1160d3cf9c7dSdf157793 /*
1161d3cf9c7dSdf157793 * If the user told us not to delay the close ("non-blocking"), then
1162d3cf9c7dSdf157793 * don't bother trying to drain.
1163d3cf9c7dSdf157793 *
1164d3cf9c7dSdf157793 * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever
1165d3cf9c7dSdf157793 * getting an M_START (since these messages aren't enqueued), and the
1166d3cf9c7dSdf157793 * only other way to clear the stop condition is by loss of DCD, which
1167d3cf9c7dSdf157793 * would discard the queue data. Thus, we drop the output data if
1168d3cf9c7dSdf157793 * ASYNC_STOPPED is set.
1169d3cf9c7dSdf157793 */
1170d3cf9c7dSdf157793 if ((flag & (FNDELAY|FNONBLOCK)) ||
1171d3cf9c7dSdf157793 (async->async_flags & ASYNC_STOPPED)) {
1172d3cf9c7dSdf157793 goto nodrain;
1173d3cf9c7dSdf157793 }
1174d3cf9c7dSdf157793
1175d3cf9c7dSdf157793 /*
1176d3cf9c7dSdf157793 * If there's any pending output, then we have to try to drain it.
1177d3cf9c7dSdf157793 * There are two main cases to be handled:
1178d3cf9c7dSdf157793 * - called by close(2): need to drain until done or until
1179d3cf9c7dSdf157793 * a signal is received. No timeout.
1180d3cf9c7dSdf157793 * - called by exit(2): need to drain while making progress
1181d3cf9c7dSdf157793 * or until a timeout occurs. No signals.
1182d3cf9c7dSdf157793 *
1183d3cf9c7dSdf157793 * If we can't rely on receiving a signal to get us out of a hung
1184d3cf9c7dSdf157793 * session, then we have to use a timer. In this case, we set a timer
1185d3cf9c7dSdf157793 * to check for progress in sending the output data -- all that we ask
1186d3cf9c7dSdf157793 * (at each interval) is that there's been some progress made. Since
1187d3cf9c7dSdf157793 * the interrupt routine grabs buffers from the write queue, we can't
1188d3cf9c7dSdf157793 * trust async_ocnt. Instead, we use a flag.
1189d3cf9c7dSdf157793 *
1190d3cf9c7dSdf157793 * Note that loss of carrier will cause the output queue to be flushed,
1191d3cf9c7dSdf157793 * and we'll wake up again and finish normally.
1192d3cf9c7dSdf157793 */
1193d3cf9c7dSdf157793 if (!ddi_can_receive_sig() && su_drain_check != 0) {
1194d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_PROGRESS;
1195d3cf9c7dSdf157793 async->async_timer = timeout(async_progress_check, async,
1196d3cf9c7dSdf157793 drv_usectohz(su_drain_check));
1197d3cf9c7dSdf157793 }
1198d3cf9c7dSdf157793
1199d3cf9c7dSdf157793 while (async->async_ocnt > 0 ||
1200d3cf9c7dSdf157793 async->async_ttycommon.t_writeq->q_first != NULL ||
1201d3cf9c7dSdf157793 (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) {
1202d3cf9c7dSdf157793 if (cv_wait_sig(&async->async_flags_cv, asy->asy_excl) == 0)
1203d3cf9c7dSdf157793 break;
1204d3cf9c7dSdf157793 }
1205d3cf9c7dSdf157793 if (async->async_timer != 0) {
1206d3cf9c7dSdf157793 (void) untimeout(async->async_timer);
1207d3cf9c7dSdf157793 async->async_timer = 0;
1208d3cf9c7dSdf157793 }
1209d3cf9c7dSdf157793
1210d3cf9c7dSdf157793 nodrain:
1211d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
1212d3cf9c7dSdf157793
1213d3cf9c7dSdf157793 /* turn off the loopback mode */
1214d3cf9c7dSdf157793 if ((async->async_dev != rconsdev) &&
1215d3cf9c7dSdf157793 (async->async_dev != kbddev) &&
1216d3cf9c7dSdf157793 (async->async_dev != stdindev)) {
1217d3cf9c7dSdf157793 OUTB(MCR, INB(MCR) & ~ ASY_LOOP);
1218d3cf9c7dSdf157793 }
1219d3cf9c7dSdf157793
1220d3cf9c7dSdf157793 async->async_ocnt = 0;
1221d3cf9c7dSdf157793 if (async->async_xmitblk != NULL)
1222d3cf9c7dSdf157793 freeb(async->async_xmitblk);
1223d3cf9c7dSdf157793 async->async_xmitblk = NULL;
1224d3cf9c7dSdf157793
1225d3cf9c7dSdf157793 /*
1226d3cf9c7dSdf157793 * If the "nohupcl" OBP property is set for this device, do
1227d3cf9c7dSdf157793 * not turn off DTR and RTS no matter what. Otherwise, if the
1228d3cf9c7dSdf157793 * line has HUPCL set or is incompletely opened, turn off DTR
1229d3cf9c7dSdf157793 * and RTS to fix the modem line.
1230d3cf9c7dSdf157793 */
1231d3cf9c7dSdf157793 if (!nohupcl && ((async->async_ttycommon.t_cflag & HUPCL) ||
1232d3cf9c7dSdf157793 (async->async_flags & ASYNC_WOPEN))) {
1233d3cf9c7dSdf157793 /* turn off DTR, RTS but NOT interrupt to 386 */
1234d3cf9c7dSdf157793 OUTB(MCR, OUT2);
1235d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
1236d3cf9c7dSdf157793 /*
1237d3cf9c7dSdf157793 * Don't let an interrupt in the middle of close
1238d3cf9c7dSdf157793 * bounce us back to the top; just continue closing
1239d3cf9c7dSdf157793 * as if nothing had happened.
1240d3cf9c7dSdf157793 */
1241d3cf9c7dSdf157793 if (cv_wait_sig(&lbolt_cv, asy->asy_excl) == 0)
1242d3cf9c7dSdf157793 goto out;
1243d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
1244d3cf9c7dSdf157793 }
1245d3cf9c7dSdf157793
1246d3cf9c7dSdf157793 /*
1247d3cf9c7dSdf157793 * If nobody's using it now, turn off receiver interrupts.
1248d3cf9c7dSdf157793 */
1249d3cf9c7dSdf157793 if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) {
1250d3cf9c7dSdf157793 icr = INB(ICR);
1251d3cf9c7dSdf157793 OUTB(ICR, (icr & ~RIEN));
1252d3cf9c7dSdf157793 }
1253d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
1254d3cf9c7dSdf157793 out:
1255d3cf9c7dSdf157793 /*
1256d3cf9c7dSdf157793 * Clear out device state.
1257d3cf9c7dSdf157793 */
1258d3cf9c7dSdf157793 async->async_flags = 0;
1259d3cf9c7dSdf157793 ttycommon_close(&async->async_ttycommon);
1260d3cf9c7dSdf157793 cv_broadcast(&async->async_flags_cv);
1261d3cf9c7dSdf157793
1262d3cf9c7dSdf157793 /*
1263d3cf9c7dSdf157793 * Clear ASY_DOINGSOFT and ASY_NEEDSOFT in case we were in
1264d3cf9c7dSdf157793 * async_softint or an interrupt was pending when the process
1265d3cf9c7dSdf157793 * using the port exited.
1266d3cf9c7dSdf157793 */
1267d3cf9c7dSdf157793 asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT;
1268d3cf9c7dSdf157793
1269d3cf9c7dSdf157793 /*
1270d3cf9c7dSdf157793 * Cancel outstanding "bufcall" request.
1271d3cf9c7dSdf157793 */
1272d3cf9c7dSdf157793 if (async->async_wbufcid) {
1273d3cf9c7dSdf157793 unbufcall(async->async_wbufcid);
1274d3cf9c7dSdf157793 async->async_wbufcid = 0;
1275d3cf9c7dSdf157793 }
1276d3cf9c7dSdf157793
1277d3cf9c7dSdf157793 /*
1278d3cf9c7dSdf157793 * If inperim is true, it means the port is closing while there's
1279d3cf9c7dSdf157793 * a pending software interrupt. async_flags has been zeroed out,
1280d3cf9c7dSdf157793 * so this instance of leaveq() needs to be called before we call
1281d3cf9c7dSdf157793 * qprocsoff() to disable services on the q. If inperim is false,
1282d3cf9c7dSdf157793 * leaveq() has already been called or we're not in a perimeter.
1283d3cf9c7dSdf157793 */
1284d3cf9c7dSdf157793 if (asy->inperim == B_TRUE) {
1285d3cf9c7dSdf157793 asy->inperim = B_FALSE;
1286d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
1287d3cf9c7dSdf157793 leaveq(q);
1288d3cf9c7dSdf157793 } else {
1289d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
1290d3cf9c7dSdf157793 }
1291d3cf9c7dSdf157793
1292d3cf9c7dSdf157793 /* Note that qprocsoff can't be done until after interrupts are off */
1293d3cf9c7dSdf157793 qprocsoff(q);
1294d3cf9c7dSdf157793 q->q_ptr = WR(q)->q_ptr = NULL;
1295d3cf9c7dSdf157793 async->async_ttycommon.t_readq = NULL;
1296d3cf9c7dSdf157793 async->async_ttycommon.t_writeq = NULL;
1297d3cf9c7dSdf157793
1298d3cf9c7dSdf157793 return (0);
1299d3cf9c7dSdf157793 }
1300d3cf9c7dSdf157793
1301d3cf9c7dSdf157793 /*
1302d3cf9c7dSdf157793 * Checks to see if the serial port is still transmitting
1303d3cf9c7dSdf157793 * characters. It returns true when there are characters
1304d3cf9c7dSdf157793 * queued to transmit, when the holding register contains
1305d3cf9c7dSdf157793 * a byte, or when the shifting register still contains
1306d3cf9c7dSdf157793 * data to send.
1307d3cf9c7dSdf157793 *
1308d3cf9c7dSdf157793 */
1309d3cf9c7dSdf157793 static boolean_t
asy_isbusy(struct asycom * asy)1310d3cf9c7dSdf157793 asy_isbusy(struct asycom *asy)
1311d3cf9c7dSdf157793 {
1312d3cf9c7dSdf157793 struct asyncline *async;
1313d3cf9c7dSdf157793
1314d3cf9c7dSdf157793 #ifdef DEBUG
1315d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_EOT)
1316d3cf9c7dSdf157793 printf("isbusy\n");
1317d3cf9c7dSdf157793 #endif
1318d3cf9c7dSdf157793 async = (struct asyncline *)asy->asy_priv;
1319d3cf9c7dSdf157793 ASSERT(mutex_owned(asy->asy_excl));
1320d3cf9c7dSdf157793 ASSERT(mutex_owned(asy->asy_excl_hi));
1321d3cf9c7dSdf157793 return ((async->async_ocnt > 0) ||
1322d3cf9c7dSdf157793 ((INB(LSR) & XSRE) == 0));
1323d3cf9c7dSdf157793 }
1324d3cf9c7dSdf157793
1325d3cf9c7dSdf157793 /*
1326d3cf9c7dSdf157793 * Program the ASY port. Most of the async operation is based on the values
1327d3cf9c7dSdf157793 * of 'c_iflag' and 'c_cflag'.
1328d3cf9c7dSdf157793 */
1329d3cf9c7dSdf157793 static int
asy_program(struct asycom * asy,int mode)1330d3cf9c7dSdf157793 asy_program(struct asycom *asy, int mode)
1331d3cf9c7dSdf157793 {
1332d3cf9c7dSdf157793 struct asyncline *async;
1333d3cf9c7dSdf157793 int baudrate, c_flag;
1334d3cf9c7dSdf157793 int icr, lcr;
1335d3cf9c7dSdf157793 int ocflags;
1336d3cf9c7dSdf157793 int error = 0;
1337d3cf9c7dSdf157793
1338d3cf9c7dSdf157793 ASSERT(mutex_owned(asy->asy_excl));
1339d3cf9c7dSdf157793 ASSERT(mutex_owned(asy->asy_excl_hi));
1340d3cf9c7dSdf157793
1341d3cf9c7dSdf157793 #ifdef DEBUG
1342d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_PROCS)
1343d3cf9c7dSdf157793 printf("program\n");
1344d3cf9c7dSdf157793 #endif
1345d3cf9c7dSdf157793 async = (struct asyncline *)asy->asy_priv;
1346d3cf9c7dSdf157793
1347d3cf9c7dSdf157793 baudrate = async->async_ttycommon.t_cflag & CBAUD;
1348d3cf9c7dSdf157793 if (async->async_ttycommon.t_cflag & CBAUDEXT)
1349d3cf9c7dSdf157793 baudrate += 16;
1350d3cf9c7dSdf157793
1351d3cf9c7dSdf157793 /* Limit baudrate so it can't index out of baudtable */
1352d3cf9c7dSdf157793 if (baudrate >= N_SU_SPEEDS) baudrate = B9600;
1353d3cf9c7dSdf157793
1354d3cf9c7dSdf157793 /*
1355d3cf9c7dSdf157793 * If baud rate requested is greater than the speed cap
1356d3cf9c7dSdf157793 * or is an unsupported baud rate then reset t_cflag baud
1357d3cf9c7dSdf157793 * to the last valid baud rate. If this is the initial
1358d3cf9c7dSdf157793 * pass through asy_program then set it to 9600.
1359d3cf9c7dSdf157793 */
1360d3cf9c7dSdf157793 if (((baudrate > 0) && (asyspdtab[baudrate] == 0)) ||
1361d3cf9c7dSdf157793 (baudtable[baudrate] > asy->asy_speed_cap)) {
1362d3cf9c7dSdf157793 async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT &
1363d3cf9c7dSdf157793 ~CIBAUD & ~CIBAUDEXT;
1364d3cf9c7dSdf157793 if (mode == ASY_INIT) {
1365d3cf9c7dSdf157793 async->async_ttycommon.t_cflag |= B9600;
1366b4a51ac1Skc28005 async->async_ttycommon.t_cflag |= B9600 << IBSHIFT;
1367d3cf9c7dSdf157793 baudrate = B9600;
1368d3cf9c7dSdf157793 } else {
1369d3cf9c7dSdf157793 async->async_ttycommon.t_cflag |=
1370d3cf9c7dSdf157793 (asy->asy_ocflags & (CBAUD | CBAUDEXT |
1371d3cf9c7dSdf157793 CIBAUD | CIBAUDEXT));
1372d3cf9c7dSdf157793 error = EINVAL;
1373d3cf9c7dSdf157793 goto end;
1374d3cf9c7dSdf157793 }
1375b4a51ac1Skc28005 }
1376d3cf9c7dSdf157793
1377b4a51ac1Skc28005 /*
1378b4a51ac1Skc28005 * If CIBAUD and CIBAUDEXT are zero then we should set them to
1379b4a51ac1Skc28005 * the equivelant output baud bits. Else, if CIBAUD and CIBAUDEXT
1380b4a51ac1Skc28005 * don't match CBAUD and CBAUDEXT respectively then we should
1381b4a51ac1Skc28005 * notify the requestor that we do not support split speeds.
1382b4a51ac1Skc28005 */
1383b4a51ac1Skc28005 if ((async->async_ttycommon.t_cflag & (CIBAUD|CIBAUDEXT)) == 0) {
1384b4a51ac1Skc28005 async->async_ttycommon.t_cflag |=
1385b4a51ac1Skc28005 (async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT;
1386b4a51ac1Skc28005 if (async->async_ttycommon.t_cflag & CBAUDEXT)
1387d3cf9c7dSdf157793 async->async_ttycommon.t_cflag |= CIBAUDEXT;
1388d3cf9c7dSdf157793 } else {
1389b4a51ac1Skc28005 if ((((async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT) !=
1390b4a51ac1Skc28005 (async->async_ttycommon.t_cflag & CIBAUD)) ||
1391b4a51ac1Skc28005 !(((async->async_ttycommon.t_cflag & (CBAUDEXT |
1392b4a51ac1Skc28005 CIBAUDEXT)) == (CBAUDEXT | CIBAUDEXT)) ||
1393b4a51ac1Skc28005 ((async->async_ttycommon.t_cflag & (CBAUDEXT |
1394b4a51ac1Skc28005 CIBAUDEXT)) == 0))) {
1395b4a51ac1Skc28005 async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT &
1396b4a51ac1Skc28005 ~CIBAUD & ~CIBAUDEXT;
1397d3cf9c7dSdf157793 async->async_ttycommon.t_cflag |=
1398b4a51ac1Skc28005 (asy->asy_ocflags & (CBAUD | CBAUDEXT |
1399b4a51ac1Skc28005 CIBAUD | CIBAUDEXT));
1400b4a51ac1Skc28005 error = EINVAL;
1401b4a51ac1Skc28005 goto end;
1402d3cf9c7dSdf157793 }
1403d3cf9c7dSdf157793 }
1404d3cf9c7dSdf157793
1405d3cf9c7dSdf157793 c_flag = async->async_ttycommon.t_cflag &
1406d3cf9c7dSdf157793 (CLOCAL | CREAD | CSTOPB | CSIZE | PARENB | PARODD | CBAUD |
1407d3cf9c7dSdf157793 CBAUDEXT | CIBAUD | CIBAUDEXT);
140817ea09c7Sanovick
140917ea09c7Sanovick /* disable interrupts, see EXAR bug */
1410f63f7506Sanovick if (asy->asy_hwtype == ASY16C554D)
1411f63f7506Sanovick OUTB(SPR, 0);
141217ea09c7Sanovick OUTB(ICR, 0);
1413d3cf9c7dSdf157793
1414d3cf9c7dSdf157793 ocflags = asy->asy_ocflags;
1415d3cf9c7dSdf157793
1416d3cf9c7dSdf157793 /* flush/reset the status registers */
1417d3cf9c7dSdf157793 if (mode == ASY_INIT) {
1418d3cf9c7dSdf157793 (void) INB(DAT);
1419d3cf9c7dSdf157793 (void) INB(ISR);
1420d3cf9c7dSdf157793 (void) INB(LSR);
1421d3cf9c7dSdf157793 (void) INB(MSR);
1422d3cf9c7dSdf157793 }
1423d3cf9c7dSdf157793
1424d3cf9c7dSdf157793 if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) {
1425d3cf9c7dSdf157793 /* Set line control */
1426d3cf9c7dSdf157793 lcr = INB(LCR);
1427d3cf9c7dSdf157793 lcr &= ~(WLS0|WLS1|STB|PEN|EPS);
1428d3cf9c7dSdf157793
1429d3cf9c7dSdf157793 if (c_flag & CSTOPB)
1430d3cf9c7dSdf157793 lcr |= STB; /* 2 stop bits */
1431d3cf9c7dSdf157793
1432d3cf9c7dSdf157793 if (c_flag & PARENB)
1433d3cf9c7dSdf157793 lcr |= PEN;
1434d3cf9c7dSdf157793
1435d3cf9c7dSdf157793 if ((c_flag & PARODD) == 0)
1436d3cf9c7dSdf157793 lcr |= EPS;
1437d3cf9c7dSdf157793
1438d3cf9c7dSdf157793 switch (c_flag & CSIZE) {
1439d3cf9c7dSdf157793 case CS5:
1440d3cf9c7dSdf157793 lcr |= BITS5;
1441d3cf9c7dSdf157793 break;
1442d3cf9c7dSdf157793 case CS6:
1443d3cf9c7dSdf157793 lcr |= BITS6;
1444d3cf9c7dSdf157793 break;
1445d3cf9c7dSdf157793 case CS7:
1446d3cf9c7dSdf157793 lcr |= BITS7;
1447d3cf9c7dSdf157793 break;
1448d3cf9c7dSdf157793 case CS8:
1449d3cf9c7dSdf157793 lcr |= BITS8;
1450d3cf9c7dSdf157793 break;
1451d3cf9c7dSdf157793 }
1452d3cf9c7dSdf157793
1453d3cf9c7dSdf157793 /* set the baud rate when the rate is NOT B0 */
1454d3cf9c7dSdf157793 if (baudrate != 0) {
1455d3cf9c7dSdf157793 OUTB(LCR, DLAB);
1456d3cf9c7dSdf157793 OUTB(DAT, (asyspdtab[baudrate] *
1457d3cf9c7dSdf157793 asy->asy_baud_divisor_factor) & 0xff);
1458d3cf9c7dSdf157793 OUTB(ICR, ((asyspdtab[baudrate] *
1459d3cf9c7dSdf157793 asy->asy_baud_divisor_factor) >> 8) & 0xff);
1460d3cf9c7dSdf157793 }
1461d3cf9c7dSdf157793 /* set the line control modes */
1462d3cf9c7dSdf157793 OUTB(LCR, lcr);
1463d3cf9c7dSdf157793
1464d3cf9c7dSdf157793 /*
1465d3cf9c7dSdf157793 * if transitioning from CREAD off to CREAD on,
1466d3cf9c7dSdf157793 * flush the FIFO buffer if we have one.
1467d3cf9c7dSdf157793 */
1468d3cf9c7dSdf157793 if ((ocflags & CREAD) == 0 && (c_flag & CREAD)) {
1469d3cf9c7dSdf157793 if (asy->asy_use_fifo == FIFO_ON) {
1470d3cf9c7dSdf157793 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH |
1471d3cf9c7dSdf157793 (asy->asy_trig_level & 0xff));
1472d3cf9c7dSdf157793 }
1473d3cf9c7dSdf157793 }
1474d3cf9c7dSdf157793
1475d3cf9c7dSdf157793 /* remember the new cflags */
1476d3cf9c7dSdf157793 asy->asy_ocflags = c_flag & ~CLOCAL;
1477d3cf9c7dSdf157793 }
1478d3cf9c7dSdf157793
1479d3cf9c7dSdf157793 /* whether or not CLOCAL is set, modify the modem control lines */
1480d3cf9c7dSdf157793 if (baudrate == 0)
1481d3cf9c7dSdf157793 /* B0 has been issued, lower DTR */
1482d3cf9c7dSdf157793 OUTB(MCR, RTS|OUT2);
1483d3cf9c7dSdf157793 else
1484d3cf9c7dSdf157793 /* raise DTR */
1485d3cf9c7dSdf157793 OUTB(MCR, DTR|RTS|OUT2);
1486d3cf9c7dSdf157793
1487d3cf9c7dSdf157793 /*
1488d3cf9c7dSdf157793 * Call the modem status interrupt handler to check for the carrier
1489d3cf9c7dSdf157793 * in case CLOCAL was turned off after the carrier came on.
1490d3cf9c7dSdf157793 * (Note: Modem status interrupt is not enabled if CLOCAL is ON.)
1491d3cf9c7dSdf157793 */
1492d3cf9c7dSdf157793 async_msint(asy);
1493d3cf9c7dSdf157793
1494d3cf9c7dSdf157793 /* Set interrupt control */
1495d3cf9c7dSdf157793 if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS))
1496d3cf9c7dSdf157793 /*
1497d3cf9c7dSdf157793 * direct-wired line ignores DCD, so we don't enable modem
1498d3cf9c7dSdf157793 * status interrupts.
1499d3cf9c7dSdf157793 */
1500d3cf9c7dSdf157793 icr = (TIEN | SIEN);
1501d3cf9c7dSdf157793 else
1502d3cf9c7dSdf157793 icr = (TIEN | SIEN | MIEN);
1503d3cf9c7dSdf157793
1504d3cf9c7dSdf157793 if (c_flag & CREAD)
1505d3cf9c7dSdf157793 icr |= RIEN;
1506d3cf9c7dSdf157793
1507d3cf9c7dSdf157793 OUTB(ICR, icr);
1508d3cf9c7dSdf157793 end:
1509d3cf9c7dSdf157793 return (error);
1510d3cf9c7dSdf157793 }
1511d3cf9c7dSdf157793
1512d3cf9c7dSdf157793 /*
15130280efdcSzk194757 * Polled mode support -- all functions called with interrupts
15140280efdcSzk194757 * disabled.
15150280efdcSzk194757 */
15160280efdcSzk194757
15170280efdcSzk194757 static void
asyputchar(cons_polledio_arg_t arg,uchar_t c)15180280efdcSzk194757 asyputchar(cons_polledio_arg_t arg, uchar_t c)
15190280efdcSzk194757 {
15200280efdcSzk194757 struct asycom *asy = (struct asycom *)arg;
15210280efdcSzk194757
15220280efdcSzk194757 /*
15230280efdcSzk194757 * If we see a line feed make sure to also
15240280efdcSzk194757 * put out a carriage return.
15250280efdcSzk194757 */
15260280efdcSzk194757 if (c == '\n')
15270280efdcSzk194757 asyputchar(arg, '\r');
15280280efdcSzk194757
15290280efdcSzk194757 while ((INB(LSR) & XHRE) == 0) {
15300280efdcSzk194757 /* wait for the transmission to complete */
15310280efdcSzk194757 drv_usecwait(10);
15320280efdcSzk194757 }
15330280efdcSzk194757
15340280efdcSzk194757 /* ouput the character */
15350280efdcSzk194757 OUTB(DAT, c);
15360280efdcSzk194757 }
15370280efdcSzk194757
15380280efdcSzk194757 /*
15390280efdcSzk194757 * Determines if there is a character avaialable for
15400280efdcSzk194757 * reading.
15410280efdcSzk194757 */
15420280efdcSzk194757 static boolean_t
asyischar(cons_polledio_arg_t arg)15430280efdcSzk194757 asyischar(cons_polledio_arg_t arg)
15440280efdcSzk194757 {
15450280efdcSzk194757 struct asycom *asy = (struct asycom *)arg;
15460280efdcSzk194757 return ((INB(LSR) & RCA) != 0);
15470280efdcSzk194757 }
15480280efdcSzk194757
15490280efdcSzk194757 static int
asygetchar(cons_polledio_arg_t arg)15500280efdcSzk194757 asygetchar(cons_polledio_arg_t arg)
15510280efdcSzk194757 {
15520280efdcSzk194757 struct asycom *asy = (struct asycom *)arg;
15530280efdcSzk194757
15540280efdcSzk194757 /*
15550280efdcSzk194757 * Spin waiting for a character to be
15560280efdcSzk194757 * available to read.
15570280efdcSzk194757 */
15580280efdcSzk194757 while (!asyischar(arg))
15590280efdcSzk194757 drv_usecwait(10);
15600280efdcSzk194757
15610280efdcSzk194757 return (INB(DAT));
15620280efdcSzk194757 }
15630280efdcSzk194757
15640280efdcSzk194757 /*
15650280efdcSzk194757 * Called when machine is transitioning to polled mode
15660280efdcSzk194757 */
15670280efdcSzk194757 static void
asy_polled_enter(cons_polledio_arg_t arg)15680280efdcSzk194757 asy_polled_enter(cons_polledio_arg_t arg)
15690280efdcSzk194757 {
15700280efdcSzk194757 struct asycom *asy = (struct asycom *)arg;
15710280efdcSzk194757
15720280efdcSzk194757 mutex_enter(asy->asy_excl);
15730280efdcSzk194757 mutex_enter(asy->asy_excl_hi);
15740280efdcSzk194757
15750280efdcSzk194757 /*
15760280efdcSzk194757 * If this is the first time that asy_polled_enter()
15770280efdcSzk194757 * has been called, during this transition request,
15780280efdcSzk194757 * save the ICR. Clear the software interrupt
15790280efdcSzk194757 * flags since we won't be able to handle these when
15800280efdcSzk194757 * we are in polled mode.
15810280efdcSzk194757 */
15820280efdcSzk194757 if (!asy->polled_enter) {
15830280efdcSzk194757 asy->polled_enter = B_TRUE;
15840280efdcSzk194757 asy->polled_icr = INB(ICR);
15850280efdcSzk194757
15860280efdcSzk194757 /* Disable HW interrupts */
15870280efdcSzk194757 if (asy->asy_hwtype == ASY16C554D)
15880280efdcSzk194757 OUTB(SPR, 0);
15890280efdcSzk194757 OUTB(ICR, 0);
15900280efdcSzk194757
15910280efdcSzk194757 asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT;
15920280efdcSzk194757 }
15930280efdcSzk194757 mutex_exit(asy->asy_excl_hi);
15940280efdcSzk194757 mutex_exit(asy->asy_excl);
15950280efdcSzk194757 }
15960280efdcSzk194757
15970280efdcSzk194757 /*
15980280efdcSzk194757 * Called when machine is transitioning from polled mode.
15990280efdcSzk194757 */
16000280efdcSzk194757 static void
asy_polled_exit(cons_polledio_arg_t arg)16010280efdcSzk194757 asy_polled_exit(cons_polledio_arg_t arg)
16020280efdcSzk194757 {
16030280efdcSzk194757 struct asycom *asy = (struct asycom *)arg;
16040280efdcSzk194757
16050280efdcSzk194757 mutex_enter(asy->asy_excl);
16060280efdcSzk194757 mutex_enter(asy->asy_excl_hi);
16070280efdcSzk194757
16080280efdcSzk194757 /* Restore the ICR */
16090280efdcSzk194757 OUTB(ICR, asy->polled_icr);
16100280efdcSzk194757
16110280efdcSzk194757 /*
16120280efdcSzk194757 * We have finished this polled IO transition.
16130280efdcSzk194757 * Set polled_enter to B_FALSE to note this.
16140280efdcSzk194757 */
16150280efdcSzk194757 asy->polled_enter = B_FALSE;
16160280efdcSzk194757 mutex_exit(asy->asy_excl_hi);
16170280efdcSzk194757 mutex_exit(asy->asy_excl);
16180280efdcSzk194757 }
16190280efdcSzk194757
16200280efdcSzk194757 /*
1621d3cf9c7dSdf157793 * asyintr() is the High Level Interrupt Handler.
1622d3cf9c7dSdf157793 *
1623d3cf9c7dSdf157793 * There are four different interrupt types indexed by ISR register values:
1624d3cf9c7dSdf157793 * 0: modem
1625d3cf9c7dSdf157793 * 1: Tx holding register is empty, ready for next char
1626d3cf9c7dSdf157793 * 2: Rx register now holds a char to be picked up
1627d3cf9c7dSdf157793 * 3: error or break on line
1628d3cf9c7dSdf157793 * This routine checks the Bit 0 (interrupt-not-pending) to determine if
1629d3cf9c7dSdf157793 * the interrupt is from this port.
1630d3cf9c7dSdf157793 */
1631d3cf9c7dSdf157793 uint_t
asyintr(caddr_t argasy)1632d3cf9c7dSdf157793 asyintr(caddr_t argasy)
1633d3cf9c7dSdf157793 {
1634d3cf9c7dSdf157793 struct asycom *asy = (struct asycom *)argasy;
1635d3cf9c7dSdf157793 struct asyncline *async;
1636d3cf9c7dSdf157793 int ret_status = DDI_INTR_UNCLAIMED;
1637d3cf9c7dSdf157793 uchar_t interrupt_id, lsr;
1638d3cf9c7dSdf157793
1639d3cf9c7dSdf157793 interrupt_id = INB(ISR) & 0x0F;
1640d3cf9c7dSdf157793 async = (struct asyncline *)asy->asy_priv;
1641d3cf9c7dSdf157793 if ((async == NULL) ||
1642d3cf9c7dSdf157793 !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) {
1643d3cf9c7dSdf157793 if (interrupt_id & NOINTERRUPT) {
1644d3cf9c7dSdf157793 return (DDI_INTR_UNCLAIMED);
1645d3cf9c7dSdf157793 } else {
1646d3cf9c7dSdf157793 lsr = INB(LSR);
1647d3cf9c7dSdf157793 if ((lsr & BRKDET) &&
1648d3cf9c7dSdf157793 ((abort_enable == KIOCABORTENABLE) &&
1649d3cf9c7dSdf157793 (async->async_dev == rconsdev)))
1650d3cf9c7dSdf157793 abort_sequence_enter((char *)NULL);
1651d3cf9c7dSdf157793 else {
1652d3cf9c7dSdf157793 /* reset line status */
1653d3cf9c7dSdf157793 (void) INB(LSR);
1654d3cf9c7dSdf157793 /* discard any data */
1655d3cf9c7dSdf157793 (void) INB(DAT);
1656d3cf9c7dSdf157793 /* reset modem status */
1657d3cf9c7dSdf157793 (void) INB(MSR);
1658d3cf9c7dSdf157793 return (DDI_INTR_CLAIMED);
1659d3cf9c7dSdf157793 }
1660d3cf9c7dSdf157793 }
1661d3cf9c7dSdf157793 }
1662d3cf9c7dSdf157793 /*
1663d3cf9c7dSdf157793 * Spurious interrupts happen in this driver
1664d3cf9c7dSdf157793 * because of the transmission on serial port not handled
1665d3cf9c7dSdf157793 * properly.
1666d3cf9c7dSdf157793 *
1667d3cf9c7dSdf157793 * The reasons for Spurious interrupts are:
1668d3cf9c7dSdf157793 * 1. There is a path in async_nstart which transmits
1669d3cf9c7dSdf157793 * characters without going through interrupt services routine
1670d3cf9c7dSdf157793 * which causes spurious interrupts to happen.
1671d3cf9c7dSdf157793 * 2. In the async_txint more than one character is sent
1672d3cf9c7dSdf157793 * in one interrupt service.
1673d3cf9c7dSdf157793 * 3. In async_rxint more than one characters are received in
1674d3cf9c7dSdf157793 * in one interrupt service.
1675d3cf9c7dSdf157793 *
1676d3cf9c7dSdf157793 * Hence we have flags to indicate that such scenerio has happened.
1677d3cf9c7dSdf157793 * and claim only such interrupts and others we donot claim it
1678d3cf9c7dSdf157793 * as it could be a indicator of some hardware problem.
1679d3cf9c7dSdf157793 *
1680d3cf9c7dSdf157793 */
1681d3cf9c7dSdf157793 if (interrupt_id & NOINTERRUPT) {
1682d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
1683d3cf9c7dSdf157793 if ((asy->asy_xmit_count > 1) ||
1684d3cf9c7dSdf157793 (asy->asy_out_of_band_xmit > 0) ||
1685d3cf9c7dSdf157793 (asy->asy_rx_count > 1)) {
1686d3cf9c7dSdf157793 asy->asy_xmit_count = 0;
1687d3cf9c7dSdf157793 asy->asy_out_of_band_xmit = 0;
1688d3cf9c7dSdf157793 asy->asy_rx_count = 0;
1689d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
1690d3cf9c7dSdf157793 return (DDI_INTR_CLAIMED);
1691d3cf9c7dSdf157793 } else {
1692d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
1693d3cf9c7dSdf157793 return (DDI_INTR_UNCLAIMED);
1694d3cf9c7dSdf157793 }
1695d3cf9c7dSdf157793 }
1696d3cf9c7dSdf157793 ret_status = DDI_INTR_CLAIMED;
1697d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
1698d3cf9c7dSdf157793 if (asy->asy_hwtype == ASY82510)
1699d3cf9c7dSdf157793 OUTB(ISR, 0x00); /* set bank 0 */
1700d3cf9c7dSdf157793
1701d3cf9c7dSdf157793 #ifdef DEBUG
1702d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_INTR)
1703d3cf9c7dSdf157793 prom_printf("l");
1704d3cf9c7dSdf157793 #endif
1705d3cf9c7dSdf157793 lsr = INB(LSR);
1706d3cf9c7dSdf157793 switch (interrupt_id) {
1707d3cf9c7dSdf157793 case RxRDY:
1708d3cf9c7dSdf157793 case RSTATUS:
1709d3cf9c7dSdf157793 case FFTMOUT:
1710d3cf9c7dSdf157793 /* receiver interrupt or receiver errors */
1711d3cf9c7dSdf157793 async_rxint(asy, lsr);
1712d3cf9c7dSdf157793 break;
1713d3cf9c7dSdf157793 case TxRDY:
1714d3cf9c7dSdf157793 /* transmit interrupt */
1715d3cf9c7dSdf157793 async_txint(asy, lsr);
1716d3cf9c7dSdf157793 break;
1717d3cf9c7dSdf157793 case MSTATUS:
1718d3cf9c7dSdf157793 /* modem status interrupt */
1719d3cf9c7dSdf157793 async_msint(asy);
1720d3cf9c7dSdf157793 break;
1721d3cf9c7dSdf157793 }
1722d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
1723d3cf9c7dSdf157793 return (ret_status);
1724d3cf9c7dSdf157793 }
1725d3cf9c7dSdf157793
1726d3cf9c7dSdf157793 /*
1727d3cf9c7dSdf157793 * Transmitter interrupt service routine.
1728d3cf9c7dSdf157793 * If there is more data to transmit in the current pseudo-DMA block,
1729d3cf9c7dSdf157793 * send the next character if output is not stopped or draining.
1730d3cf9c7dSdf157793 * Otherwise, queue up a soft interrupt.
1731d3cf9c7dSdf157793 *
1732d3cf9c7dSdf157793 * XXX - Needs review for HW FIFOs.
1733d3cf9c7dSdf157793 */
1734d3cf9c7dSdf157793 static void
async_txint(struct asycom * asy,uchar_t lsr)1735d3cf9c7dSdf157793 async_txint(struct asycom *asy, uchar_t lsr)
1736d3cf9c7dSdf157793 {
1737d3cf9c7dSdf157793 struct asyncline *async = (struct asyncline *)asy->asy_priv;
1738d3cf9c7dSdf157793 int fifo_len;
1739d3cf9c7dSdf157793 int xmit_progress;
1740d3cf9c7dSdf157793
1741d3cf9c7dSdf157793 asycheckflowcontrol_hw(asy);
1742d3cf9c7dSdf157793
1743d3cf9c7dSdf157793 /*
1744d3cf9c7dSdf157793 * If ASYNC_BREAK has been set, return to asyintr()'s context to
1745d3cf9c7dSdf157793 * claim the interrupt without performing any action.
1746d3cf9c7dSdf157793 */
1747d3cf9c7dSdf157793 if (async->async_flags & ASYNC_BREAK)
1748d3cf9c7dSdf157793 return;
1749d3cf9c7dSdf157793
1750d3cf9c7dSdf157793 fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
1751d3cf9c7dSdf157793
1752d3cf9c7dSdf157793 /*
1753d3cf9c7dSdf157793 * Check for flow control and do the needed action.
1754d3cf9c7dSdf157793 */
1755d3cf9c7dSdf157793 if (asycheckflowcontrol_sw(asy)) {
1756d3cf9c7dSdf157793 return;
1757d3cf9c7dSdf157793 }
1758d3cf9c7dSdf157793
1759d3cf9c7dSdf157793 if (async->async_ocnt > 0 &&
1760d3cf9c7dSdf157793 !(async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED))) {
1761d3cf9c7dSdf157793 xmit_progress = 0;
1762d3cf9c7dSdf157793 while (fifo_len > 0 && async->async_ocnt > 0) {
1763d3cf9c7dSdf157793 if (lsr & XHRE) {
1764d3cf9c7dSdf157793 OUTB(DAT, *async->async_optr++);
1765d3cf9c7dSdf157793 fifo_len--;
1766d3cf9c7dSdf157793 async->async_ocnt--;
1767d3cf9c7dSdf157793 xmit_progress++;
1768d3cf9c7dSdf157793 }
1769d3cf9c7dSdf157793 /*
1770d3cf9c7dSdf157793 * Reading the lsr, (moved reading at the end of
1771d3cf9c7dSdf157793 * while loop) as already we have read once at
1772d3cf9c7dSdf157793 * the beginning of interrupt service
1773d3cf9c7dSdf157793 */
1774d3cf9c7dSdf157793 lsr = INB(LSR);
1775d3cf9c7dSdf157793 }
1776d3cf9c7dSdf157793 asy->asy_xmit_count = xmit_progress;
1777d3cf9c7dSdf157793 if (xmit_progress > 0)
1778d3cf9c7dSdf157793 async->async_flags |= ASYNC_PROGRESS;
1779d3cf9c7dSdf157793 }
1780d3cf9c7dSdf157793
1781d3cf9c7dSdf157793 if (fifo_len == 0) {
1782d3cf9c7dSdf157793 return;
1783d3cf9c7dSdf157793 }
1784d3cf9c7dSdf157793
1785d3cf9c7dSdf157793
1786d3cf9c7dSdf157793 ASYSETSOFT(asy);
1787d3cf9c7dSdf157793 }
1788d3cf9c7dSdf157793
1789d3cf9c7dSdf157793 /*
1790d3cf9c7dSdf157793 * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive
1791d3cf9c7dSdf157793 * error interrupt.
1792d3cf9c7dSdf157793 * Try to put the character into the circular buffer for this line; if it
1793d3cf9c7dSdf157793 * overflows, indicate a circular buffer overrun. If this port is always
1794d3cf9c7dSdf157793 * to be serviced immediately, or the character is a STOP character, or
1795d3cf9c7dSdf157793 * more than 15 characters have arrived, queue up a soft interrupt to
1796d3cf9c7dSdf157793 * drain the circular buffer.
1797d3cf9c7dSdf157793 * XXX - needs review for hw FIFOs support.
1798d3cf9c7dSdf157793 */
1799d3cf9c7dSdf157793
1800d3cf9c7dSdf157793 static void
async_rxint(struct asycom * asy,uchar_t lsr)1801d3cf9c7dSdf157793 async_rxint(struct asycom *asy, uchar_t lsr)
1802d3cf9c7dSdf157793 {
1803d3cf9c7dSdf157793 struct asyncline *async = (struct asyncline *)asy->asy_priv;
1804d3cf9c7dSdf157793 uchar_t c = 0;
1805d3cf9c7dSdf157793 uint_t s = 0, needsoft = 0;
1806d3cf9c7dSdf157793 register tty_common_t *tp;
1807d3cf9c7dSdf157793
1808d3cf9c7dSdf157793 tp = &async->async_ttycommon;
1809d3cf9c7dSdf157793 if (!(tp->t_cflag & CREAD)) {
1810d3cf9c7dSdf157793 if (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
1811d3cf9c7dSdf157793 (void) (INB(DAT) & 0xff);
1812d3cf9c7dSdf157793 }
1813d3cf9c7dSdf157793 return; /* line is not open for read? */
1814d3cf9c7dSdf157793 }
1815d3cf9c7dSdf157793 asy->asy_rx_count = 0;
1816d3cf9c7dSdf157793 while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
1817d3cf9c7dSdf157793 c = 0;
1818d3cf9c7dSdf157793 s = 0;
1819d3cf9c7dSdf157793 asy->asy_rx_count++;
1820d3cf9c7dSdf157793 if (lsr & RCA) {
1821d3cf9c7dSdf157793 c = INB(DAT) & 0xff;
1822d3cf9c7dSdf157793 /*
1823d3cf9c7dSdf157793 * Even a single character is received
1824d3cf9c7dSdf157793 * we need Soft interrupt to pass it to
1825d3cf9c7dSdf157793 * higher layers.
1826d3cf9c7dSdf157793 */
1827d3cf9c7dSdf157793 needsoft = 1;
1828d3cf9c7dSdf157793 }
1829d3cf9c7dSdf157793
1830d3cf9c7dSdf157793 /* Check for character break sequence */
1831d3cf9c7dSdf157793 if ((abort_enable == KIOCABORTALTERNATE) &&
1832d3cf9c7dSdf157793 (async->async_dev == rconsdev)) {
1833d3cf9c7dSdf157793 if (abort_charseq_recognize(c))
1834d3cf9c7dSdf157793 abort_sequence_enter((char *)NULL);
1835d3cf9c7dSdf157793 }
1836d3cf9c7dSdf157793
1837d3cf9c7dSdf157793 /* Handle framing errors */
1838d3cf9c7dSdf157793 if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) {
1839d3cf9c7dSdf157793 if (lsr & PARERR) {
1840d3cf9c7dSdf157793 if (tp->t_iflag & INPCK) /* parity enabled */
1841d3cf9c7dSdf157793 s |= PERROR;
1842d3cf9c7dSdf157793 }
1843d3cf9c7dSdf157793 if (lsr & (FRMERR|BRKDET))
1844d3cf9c7dSdf157793 s |= FRERROR;
1845d3cf9c7dSdf157793 if (lsr & OVRRUN) {
1846d3cf9c7dSdf157793 async->async_hw_overrun = 1;
1847d3cf9c7dSdf157793 s |= OVERRUN;
1848d3cf9c7dSdf157793 }
1849d3cf9c7dSdf157793 }
1850d3cf9c7dSdf157793
1851d3cf9c7dSdf157793 if (s == 0)
1852d3cf9c7dSdf157793 if ((tp->t_iflag & PARMRK) &&
1853d3cf9c7dSdf157793 !(tp->t_iflag & (IGNPAR|ISTRIP)) &&
1854d3cf9c7dSdf157793 (c == 0377))
1855d3cf9c7dSdf157793 if (RING_POK(async, 2)) {
1856d3cf9c7dSdf157793 RING_PUT(async, 0377);
1857d3cf9c7dSdf157793 RING_PUT(async, c);
1858d3cf9c7dSdf157793 } else
1859d3cf9c7dSdf157793 async->async_sw_overrun = 1;
1860d3cf9c7dSdf157793 else
1861d3cf9c7dSdf157793 if (RING_POK(async, 1))
1862d3cf9c7dSdf157793 RING_PUT(async, c);
1863d3cf9c7dSdf157793 else
1864d3cf9c7dSdf157793 async->async_sw_overrun = 1;
1865d3cf9c7dSdf157793 else
1866d3cf9c7dSdf157793 if (s & FRERROR) { /* Handle framing errors */
1867d3cf9c7dSdf157793 if (c == 0) {
1868d3cf9c7dSdf157793 /* Look for break on kbd, stdin, or rconsdev */
1869d3cf9c7dSdf157793 if ((async->async_dev == kbddev) ||
1870d3cf9c7dSdf157793 ((async->async_dev == rconsdev) ||
1871d3cf9c7dSdf157793 (async->async_dev == stdindev)) &&
1872d3cf9c7dSdf157793 (abort_enable !=
1873d3cf9c7dSdf157793 KIOCABORTALTERNATE))
1874d3cf9c7dSdf157793 abort_sequence_enter((char *)0);
1875d3cf9c7dSdf157793 else
1876d3cf9c7dSdf157793 async->async_break++;
1877d3cf9c7dSdf157793 } else {
1878d3cf9c7dSdf157793 if (RING_POK(async, 1))
1879d3cf9c7dSdf157793 RING_MARK(async, c, s);
1880d3cf9c7dSdf157793 else
1881d3cf9c7dSdf157793 async->async_sw_overrun = 1;
1882d3cf9c7dSdf157793 }
1883d3cf9c7dSdf157793 } else { /* Parity errors handled by ldterm */
1884d3cf9c7dSdf157793 if (RING_POK(async, 1))
1885d3cf9c7dSdf157793 RING_MARK(async, c, s);
1886d3cf9c7dSdf157793 else
1887d3cf9c7dSdf157793 async->async_sw_overrun = 1;
1888d3cf9c7dSdf157793 }
1889d3cf9c7dSdf157793 lsr = INB(LSR);
1890d3cf9c7dSdf157793 if (asy->asy_rx_count > 16) break;
1891d3cf9c7dSdf157793 }
1892d3cf9c7dSdf157793 /* Check whether there is a request for hw/sw inbound/input flow ctrl */
1893d3cf9c7dSdf157793 if ((async->async_ttycommon.t_cflag & CRTSXOFF) ||
1894d3cf9c7dSdf157793 (async->async_ttycommon.t_iflag & IXOFF))
1895d3cf9c7dSdf157793 if ((int)(RING_CNT(async)) > (RINGSIZE * 3)/4) {
1896d3cf9c7dSdf157793 #ifdef DEBUG
1897d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_HFLOW)
1898d3cf9c7dSdf157793 printf("asy%d: hardware flow stop input.\n",
1899d3cf9c7dSdf157793 UNIT(async->async_dev));
1900d3cf9c7dSdf157793 #endif
1901d3cf9c7dSdf157793 async->async_flags |= ASYNC_HW_IN_FLOW;
1902d3cf9c7dSdf157793 async->async_flowc = async->async_stopc;
1903d3cf9c7dSdf157793 async->async_ringbuf_overflow = 1;
1904d3cf9c7dSdf157793 }
1905d3cf9c7dSdf157793
1906d3cf9c7dSdf157793 if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft ||
1907d3cf9c7dSdf157793 (RING_FRAC(async)) || (async->async_polltid == 0))
1908d3cf9c7dSdf157793 ASYSETSOFT(asy); /* need a soft interrupt */
1909d3cf9c7dSdf157793 }
1910d3cf9c7dSdf157793
1911d3cf9c7dSdf157793 /*
1912d3cf9c7dSdf157793 * Interrupt on port: handle PPS event. This function is only called
1913d3cf9c7dSdf157793 * for a port on which PPS event handling has been enabled.
1914d3cf9c7dSdf157793 */
1915d3cf9c7dSdf157793 static void
asy_ppsevent(struct asycom * asy,int msr)1916d3cf9c7dSdf157793 asy_ppsevent(struct asycom *asy, int msr)
1917d3cf9c7dSdf157793 {
1918d3cf9c7dSdf157793 if (asy->asy_flags & ASY_PPS_EDGE) {
1919d3cf9c7dSdf157793 /* Have seen leading edge, now look for and record drop */
1920d3cf9c7dSdf157793 if ((msr & DCD) == 0)
1921d3cf9c7dSdf157793 asy->asy_flags &= ~ASY_PPS_EDGE;
1922d3cf9c7dSdf157793 /*
1923d3cf9c7dSdf157793 * Waiting for leading edge, look for rise; stamp event and
1924d3cf9c7dSdf157793 * calibrate kernel clock.
1925d3cf9c7dSdf157793 */
1926d3cf9c7dSdf157793 } else if (msr & DCD) {
1927d3cf9c7dSdf157793 /*
1928d3cf9c7dSdf157793 * This code captures a timestamp at the designated
1929d3cf9c7dSdf157793 * transition of the PPS signal (DCD asserted). The
1930d3cf9c7dSdf157793 * code provides a pointer to the timestamp, as well
1931d3cf9c7dSdf157793 * as the hardware counter value at the capture.
1932d3cf9c7dSdf157793 *
1933d3cf9c7dSdf157793 * Note: the kernel has nano based time values while
1934d3cf9c7dSdf157793 * NTP requires micro based, an in-line fast algorithm
1935d3cf9c7dSdf157793 * to convert nsec to usec is used here -- see hrt2ts()
1936d3cf9c7dSdf157793 * in common/os/timers.c for a full description.
1937d3cf9c7dSdf157793 */
1938d3cf9c7dSdf157793 struct timeval *tvp = &asy_ppsev.tv;
1939d3cf9c7dSdf157793 timestruc_t ts;
1940d3cf9c7dSdf157793 long nsec, usec;
1941d3cf9c7dSdf157793
1942d3cf9c7dSdf157793 asy->asy_flags |= ASY_PPS_EDGE;
1943d3cf9c7dSdf157793 gethrestime(&ts);
1944d3cf9c7dSdf157793 nsec = ts.tv_nsec;
1945d3cf9c7dSdf157793 usec = nsec + (nsec >> 2);
1946d3cf9c7dSdf157793 usec = nsec + (usec >> 1);
1947d3cf9c7dSdf157793 usec = nsec + (usec >> 2);
1948d3cf9c7dSdf157793 usec = nsec + (usec >> 4);
1949d3cf9c7dSdf157793 usec = nsec - (usec >> 3);
1950d3cf9c7dSdf157793 usec = nsec + (usec >> 2);
1951d3cf9c7dSdf157793 usec = nsec + (usec >> 3);
1952d3cf9c7dSdf157793 usec = nsec + (usec >> 4);
1953d3cf9c7dSdf157793 usec = nsec + (usec >> 1);
1954d3cf9c7dSdf157793 usec = nsec + (usec >> 6);
1955d3cf9c7dSdf157793 tvp->tv_usec = usec >> 10;
1956d3cf9c7dSdf157793 tvp->tv_sec = ts.tv_sec;
1957d3cf9c7dSdf157793
1958d3cf9c7dSdf157793 ++asy_ppsev.serial;
1959d3cf9c7dSdf157793
1960d3cf9c7dSdf157793 /*
1961d3cf9c7dSdf157793 * Because the kernel keeps a high-resolution time,
1962d3cf9c7dSdf157793 * pass the current highres timestamp in tvp and zero
1963d3cf9c7dSdf157793 * in usec.
1964d3cf9c7dSdf157793 */
1965d3cf9c7dSdf157793 ddi_hardpps(tvp, 0);
1966d3cf9c7dSdf157793 }
1967d3cf9c7dSdf157793 }
1968d3cf9c7dSdf157793
1969d3cf9c7dSdf157793 /*
1970d3cf9c7dSdf157793 * Modem status interrupt.
1971d3cf9c7dSdf157793 *
1972d3cf9c7dSdf157793 * (Note: It is assumed that the MSR hasn't been read by asyintr().)
1973d3cf9c7dSdf157793 */
1974d3cf9c7dSdf157793
1975d3cf9c7dSdf157793 static void
async_msint(struct asycom * asy)1976d3cf9c7dSdf157793 async_msint(struct asycom *asy)
1977d3cf9c7dSdf157793 {
1978d3cf9c7dSdf157793 struct asyncline *async = (struct asyncline *)asy->asy_priv;
1979d3cf9c7dSdf157793 int msr;
1980d3cf9c7dSdf157793
1981d3cf9c7dSdf157793 msr = INB(MSR); /* this resets the interrupt */
1982d3cf9c7dSdf157793 asy->asy_cached_msr = msr;
1983d3cf9c7dSdf157793 #ifdef DEBUG
1984d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_STATE) {
1985d3cf9c7dSdf157793 printf(" transition: %3s %3s %3s %3s\n"
1986d3cf9c7dSdf157793 "current state: %3s %3s %3s %3s\n",
1987d3cf9c7dSdf157793 (msr & DCTS) ? "CTS" : " ",
1988d3cf9c7dSdf157793 (msr & DDSR) ? "DSR" : " ",
1989d3cf9c7dSdf157793 (msr & DRI) ? "RI " : " ",
1990d3cf9c7dSdf157793 (msr & DDCD) ? "DCD" : " ",
1991d3cf9c7dSdf157793 (msr & CTS) ? "CTS" : " ",
1992d3cf9c7dSdf157793 (msr & DSR) ? "DSR" : " ",
1993d3cf9c7dSdf157793 (msr & RI) ? "RI " : " ",
1994d3cf9c7dSdf157793 (msr & DCD) ? "DCD" : " ");
1995d3cf9c7dSdf157793 }
1996d3cf9c7dSdf157793 #endif
1997d3cf9c7dSdf157793 if (async->async_ttycommon.t_cflag & CRTSCTS && !(msr & CTS)) {
1998d3cf9c7dSdf157793 #ifdef DEBUG
1999d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_HFLOW)
2000d3cf9c7dSdf157793 printf("asy%d: hflow start\n",
2001d3cf9c7dSdf157793 UNIT(async->async_dev));
2002d3cf9c7dSdf157793 #endif
2003d3cf9c7dSdf157793 async->async_flags |= ASYNC_HW_OUT_FLW;
2004d3cf9c7dSdf157793 }
2005d3cf9c7dSdf157793 if (asy->asy_hwtype == ASY82510)
2006d3cf9c7dSdf157793 OUTB(MSR, (msr & 0xF0));
2007d3cf9c7dSdf157793
2008d3cf9c7dSdf157793 /* Handle PPS event */
2009d3cf9c7dSdf157793 if (asy->asy_flags & ASY_PPS)
2010d3cf9c7dSdf157793 asy_ppsevent(asy, msr);
2011d3cf9c7dSdf157793
2012d3cf9c7dSdf157793 async->async_ext++;
2013d3cf9c7dSdf157793 ASYSETSOFT(asy);
2014d3cf9c7dSdf157793 }
2015d3cf9c7dSdf157793
2016d3cf9c7dSdf157793 /*
2017d3cf9c7dSdf157793 * Handle a second-stage interrupt.
2018d3cf9c7dSdf157793 */
2019d3cf9c7dSdf157793 uint_t
asysoftintr(caddr_t intarg)2020d3cf9c7dSdf157793 asysoftintr(caddr_t intarg)
2021d3cf9c7dSdf157793 {
2022d3cf9c7dSdf157793 struct asycom *asy = (struct asycom *)intarg;
2023d3cf9c7dSdf157793 struct asyncline *async;
2024d3cf9c7dSdf157793 int rv;
2025d3cf9c7dSdf157793 int cc;
2026d3cf9c7dSdf157793 /*
2027d3cf9c7dSdf157793 * Test and clear soft interrupt.
2028d3cf9c7dSdf157793 */
2029d3cf9c7dSdf157793 mutex_enter(asy->asy_soft_lock);
2030d3cf9c7dSdf157793 #ifdef DEBUG
2031d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_PROCS)
2032d3cf9c7dSdf157793 printf("softintr\n");
2033d3cf9c7dSdf157793 #endif
2034d3cf9c7dSdf157793 rv = asy->asysoftpend;
2035d3cf9c7dSdf157793 if (rv != 0)
2036d3cf9c7dSdf157793 asy->asysoftpend = 0;
2037d3cf9c7dSdf157793 mutex_exit(asy->asy_soft_lock);
2038d3cf9c7dSdf157793
2039d3cf9c7dSdf157793 if (rv) {
2040d3cf9c7dSdf157793 if (asy->asy_priv == NULL)
2041d3cf9c7dSdf157793 return (rv);
2042d3cf9c7dSdf157793 async = (struct asyncline *)asy->asy_priv;
2043d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2044d3cf9c7dSdf157793 if (asy->asy_flags & ASY_NEEDSOFT) {
2045d3cf9c7dSdf157793 asy->asy_flags &= ~ASY_NEEDSOFT;
2046d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2047d3cf9c7dSdf157793 (void) async_softint(asy);
2048d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2049d3cf9c7dSdf157793 }
2050d3cf9c7dSdf157793 /*
2051d3cf9c7dSdf157793 * There are some instances where the softintr is not
2052d3cf9c7dSdf157793 * scheduled and hence not called. It so happened that makes
2053d3cf9c7dSdf157793 * the last few characters to be stuck in ringbuffer.
2054d3cf9c7dSdf157793 * Hence, call once again the handler so that the last few
2055d3cf9c7dSdf157793 * characters are cleared.
2056d3cf9c7dSdf157793 */
2057d3cf9c7dSdf157793 cc = RING_CNT(async);
2058d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2059d3cf9c7dSdf157793 if (cc > 0) {
2060d3cf9c7dSdf157793 (void) async_softint(asy);
2061d3cf9c7dSdf157793 }
2062d3cf9c7dSdf157793 }
2063d3cf9c7dSdf157793 return (rv);
2064d3cf9c7dSdf157793 }
2065d3cf9c7dSdf157793
2066d3cf9c7dSdf157793 /*
2067d3cf9c7dSdf157793 * Handle a software interrupt.
2068d3cf9c7dSdf157793 */
2069d3cf9c7dSdf157793 static int
async_softint(struct asycom * asy)2070d3cf9c7dSdf157793 async_softint(struct asycom *asy)
2071d3cf9c7dSdf157793 {
2072d3cf9c7dSdf157793 struct asyncline *async = (struct asyncline *)asy->asy_priv;
2073d3cf9c7dSdf157793 uint_t cc;
2074d3cf9c7dSdf157793 mblk_t *bp;
2075d3cf9c7dSdf157793 queue_t *q;
2076d3cf9c7dSdf157793 uchar_t val;
2077d3cf9c7dSdf157793 uchar_t c;
2078d3cf9c7dSdf157793 tty_common_t *tp;
2079d3cf9c7dSdf157793
2080d3cf9c7dSdf157793 #ifdef DEBUG
2081d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_PROCS)
2082d3cf9c7dSdf157793 printf("process\n");
2083d3cf9c7dSdf157793 #endif
2084d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2085d3cf9c7dSdf157793 if (asy->asy_flags & ASY_DOINGSOFT) {
2086d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2087d3cf9c7dSdf157793 return (0);
2088d3cf9c7dSdf157793 }
2089d3cf9c7dSdf157793 tp = &async->async_ttycommon;
2090d3cf9c7dSdf157793 q = tp->t_readq;
2091d3cf9c7dSdf157793 if (q != NULL) {
2092d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2093d3cf9c7dSdf157793 enterq(q);
2094d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2095d3cf9c7dSdf157793 }
2096d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2097d3cf9c7dSdf157793 asy->asy_flags |= ASY_DOINGSOFT;
2098d3cf9c7dSdf157793
2099d3cf9c7dSdf157793 if (INB(ICR) & MIEN)
2100d3cf9c7dSdf157793 val = asy->asy_cached_msr & 0xFF;
2101d3cf9c7dSdf157793 else
2102d3cf9c7dSdf157793 val = INB(MSR) & 0xFF;
2103d3cf9c7dSdf157793
2104d3cf9c7dSdf157793 if (async->async_ttycommon.t_cflag & CRTSCTS) {
2105d3cf9c7dSdf157793 if ((val & CTS) && (async->async_flags & ASYNC_HW_OUT_FLW)) {
2106d3cf9c7dSdf157793 #ifdef DEBUG
2107d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_HFLOW)
2108d3cf9c7dSdf157793 printf("asy%d: hflow start\n",
2109d3cf9c7dSdf157793 UNIT(async->async_dev));
2110d3cf9c7dSdf157793 #endif
2111d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_HW_OUT_FLW;
2112d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2113d3cf9c7dSdf157793 if (async->async_ocnt > 0) {
2114d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2115d3cf9c7dSdf157793 async_resume(async);
2116d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2117d3cf9c7dSdf157793 } else {
2118d3cf9c7dSdf157793 async_start(async);
2119d3cf9c7dSdf157793 }
2120d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2121d3cf9c7dSdf157793 }
2122d3cf9c7dSdf157793 }
2123d3cf9c7dSdf157793 if (async->async_ext) {
2124d3cf9c7dSdf157793 async->async_ext = 0;
2125d3cf9c7dSdf157793 /* check for carrier up */
2126d3cf9c7dSdf157793 if ((val & DCD) || (tp->t_flags & TS_SOFTCAR)) {
2127d3cf9c7dSdf157793 /* carrier present */
2128d3cf9c7dSdf157793 if ((async->async_flags & ASYNC_CARR_ON) == 0) {
2129d3cf9c7dSdf157793 async->async_flags |= ASYNC_CARR_ON;
2130d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2131d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2132d3cf9c7dSdf157793 if (async->async_flags & ASYNC_ISOPEN)
2133d3cf9c7dSdf157793 (void) putctl(q, M_UNHANGUP);
2134d3cf9c7dSdf157793 cv_broadcast(&async->async_flags_cv);
2135d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2136d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2137d3cf9c7dSdf157793 }
2138d3cf9c7dSdf157793 } else {
2139d3cf9c7dSdf157793 if ((async->async_flags & ASYNC_CARR_ON) &&
2140d3cf9c7dSdf157793 !(tp->t_cflag & CLOCAL)) {
2141d3cf9c7dSdf157793 int flushflag;
2142d3cf9c7dSdf157793
2143d3cf9c7dSdf157793 /*
2144d3cf9c7dSdf157793 * Carrier went away.
2145d3cf9c7dSdf157793 * Drop DTR, abort any output in
2146d3cf9c7dSdf157793 * progress, indicate that output is
2147d3cf9c7dSdf157793 * not stopped, and send a hangup
2148d3cf9c7dSdf157793 * notification upstream.
2149d3cf9c7dSdf157793 *
2150d3cf9c7dSdf157793 * If we're in the midst of close, then flush
2151d3cf9c7dSdf157793 * everything. Don't leave stale ioctls lying
2152d3cf9c7dSdf157793 * about.
2153d3cf9c7dSdf157793 */
2154d3cf9c7dSdf157793 val = INB(MCR);
2155d3cf9c7dSdf157793 OUTB(MCR, (val & ~DTR));
2156d3cf9c7dSdf157793 flushflag = (async->async_flags &
2157d3cf9c7dSdf157793 ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA;
21584d0b1b0dSAn Bui if (tp->t_writeq != NULL) {
2159d3cf9c7dSdf157793 flushq(tp->t_writeq, flushflag);
21604d0b1b0dSAn Bui }
2161d3cf9c7dSdf157793 if (async->async_xmitblk != NULL) {
2162d3cf9c7dSdf157793 freeb(async->async_xmitblk);
2163d3cf9c7dSdf157793 async->async_xmitblk = NULL;
2164d3cf9c7dSdf157793 }
2165d3cf9c7dSdf157793 if (async->async_flags & ASYNC_BUSY) {
2166d3cf9c7dSdf157793 async->async_ocnt = 0;
2167d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_BUSY;
2168d3cf9c7dSdf157793 }
2169d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_STOPPED;
2170d3cf9c7dSdf157793 if (async->async_flags & ASYNC_ISOPEN) {
2171d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2172d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2173d3cf9c7dSdf157793 (void) putctl(q, M_HANGUP);
2174d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2175d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2176d3cf9c7dSdf157793 }
2177d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_CARR_ON;
2178d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2179d3cf9c7dSdf157793 cv_broadcast(&async->async_flags_cv);
2180d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2181d3cf9c7dSdf157793 }
2182d3cf9c7dSdf157793 }
21837cb42c7eSrameshc }
2184d3cf9c7dSdf157793
2185d3cf9c7dSdf157793 /*
2186d3cf9c7dSdf157793 * If data has been added to the circular buffer, remove
2187d3cf9c7dSdf157793 * it from the buffer, and send it up the stream if there's
2188d3cf9c7dSdf157793 * somebody listening. Try to do it 16 bytes at a time. If we
2189d3cf9c7dSdf157793 * have more than 16 bytes to move, move 16 byte chunks and
2190d3cf9c7dSdf157793 * leave the rest for next time around (maybe it will grow).
2191d3cf9c7dSdf157793 */
2192d3cf9c7dSdf157793 if (!(async->async_flags & ASYNC_ISOPEN)) {
2193d3cf9c7dSdf157793 RING_INIT(async);
2194d3cf9c7dSdf157793 goto rv;
2195d3cf9c7dSdf157793 }
2196d3cf9c7dSdf157793 if ((cc = RING_CNT(async)) == 0) {
2197d3cf9c7dSdf157793 goto rv;
2198d3cf9c7dSdf157793 }
2199d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2200d3cf9c7dSdf157793
2201d3cf9c7dSdf157793 if (!canput(q)) {
2202d3cf9c7dSdf157793 if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) {
2203d3cf9c7dSdf157793 #ifdef DEBUG
2204d3cf9c7dSdf157793 if (!(asydebug & ASY_DEBUG_HFLOW)) {
2205d3cf9c7dSdf157793 printf("asy%d: hflow stop input.\n",
2206d3cf9c7dSdf157793 UNIT(async->async_dev));
2207d3cf9c7dSdf157793 if (canputnext(q))
2208d3cf9c7dSdf157793 printf("asy%d: next queue is "
2209d3cf9c7dSdf157793 "ready\n",
2210d3cf9c7dSdf157793 UNIT(async->async_dev));
2211d3cf9c7dSdf157793 }
2212d3cf9c7dSdf157793 #endif
2213d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2214d3cf9c7dSdf157793 async->async_flags |= ASYNC_HW_IN_FLOW;
2215d3cf9c7dSdf157793 async->async_flowc = async->async_stopc;
2216d3cf9c7dSdf157793 } else mutex_enter(asy->asy_excl_hi);
2217d3cf9c7dSdf157793 goto rv;
2218d3cf9c7dSdf157793 }
2219d3cf9c7dSdf157793
2220d3cf9c7dSdf157793 if (async->async_ringbuf_overflow) {
2221d3cf9c7dSdf157793 if ((async->async_flags & ASYNC_HW_IN_FLOW) &&
2222d3cf9c7dSdf157793 ((int)(RING_CNT(async)) < (RINGSIZE/4))) {
2223d3cf9c7dSdf157793 #ifdef DEBUG
2224d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_HFLOW)
2225d3cf9c7dSdf157793 printf("asy%d: hflow start input.\n",
2226d3cf9c7dSdf157793 UNIT(async->async_dev));
2227d3cf9c7dSdf157793 #endif
2228d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2229d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_HW_IN_FLOW;
2230d3cf9c7dSdf157793 async->async_flowc = async->async_startc;
2231d3cf9c7dSdf157793 async->async_ringbuf_overflow = 0;
2232d3cf9c7dSdf157793 goto rv;
2233d3cf9c7dSdf157793 }
2234d3cf9c7dSdf157793 }
2235d3cf9c7dSdf157793 #ifdef DEBUG
2236d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_INPUT)
2237d3cf9c7dSdf157793 printf("asy%d: %d char(s) in queue.\n",
2238d3cf9c7dSdf157793 UNIT(async->async_dev), cc);
2239d3cf9c7dSdf157793 #endif
2240d3cf9c7dSdf157793 /*
2241d3cf9c7dSdf157793 * Before you pull the characters from the RING BUF
2242d3cf9c7dSdf157793 * Check whether you can put into the queue again
2243d3cf9c7dSdf157793 */
2244d3cf9c7dSdf157793 if ((!canputnext(q)) || (!canput(q))) {
2245d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2246d3cf9c7dSdf157793 if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) {
2247d3cf9c7dSdf157793 async->async_flags |= ASYNC_HW_IN_FLOW;
2248d3cf9c7dSdf157793 async->async_flowc = async->async_stopc;
2249d3cf9c7dSdf157793 async->async_queue_full = 1;
2250d3cf9c7dSdf157793 }
2251d3cf9c7dSdf157793 goto rv;
2252d3cf9c7dSdf157793 }
2253d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2254d3cf9c7dSdf157793 if (async->async_queue_full) {
2255d3cf9c7dSdf157793 /*
2256d3cf9c7dSdf157793 * Last time the Stream queue didnot allow
2257d3cf9c7dSdf157793 * now it allows so, relax, the flow control
2258d3cf9c7dSdf157793 */
2259d3cf9c7dSdf157793 if (async->async_flags & ASYNC_HW_IN_FLOW) {
2260d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_HW_IN_FLOW;
2261d3cf9c7dSdf157793 async->async_queue_full = 0;
2262d3cf9c7dSdf157793 async->async_flowc = async->async_startc;
2263d3cf9c7dSdf157793 goto rv;
2264d3cf9c7dSdf157793 } else
2265d3cf9c7dSdf157793 async->async_queue_full = 0;
2266d3cf9c7dSdf157793 }
2267d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2268d3cf9c7dSdf157793 if (!(bp = allocb(cc, BPRI_MED))) {
2269d3cf9c7dSdf157793 ttycommon_qfull(&async->async_ttycommon, q);
2270d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2271d3cf9c7dSdf157793 goto rv;
2272d3cf9c7dSdf157793 }
2273d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2274d3cf9c7dSdf157793 do {
2275d3cf9c7dSdf157793 if (RING_ERR(async, S_ERRORS)) {
2276d3cf9c7dSdf157793 RING_UNMARK(async);
2277d3cf9c7dSdf157793 c = RING_GET(async);
2278d3cf9c7dSdf157793 break;
2279d3cf9c7dSdf157793 } else {
2280d3cf9c7dSdf157793 *bp->b_wptr++ = RING_GET(async);
2281d3cf9c7dSdf157793 }
2282d3cf9c7dSdf157793 } while (--cc);
2283d3cf9c7dSdf157793
2284d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2285d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2286d3cf9c7dSdf157793 if (bp->b_wptr > bp->b_rptr) {
2287d3cf9c7dSdf157793 if (!canputnext(q)) {
2288d3cf9c7dSdf157793 if (!canput(q)) {
2289d3cf9c7dSdf157793 /*
2290d3cf9c7dSdf157793 * Even after taking all precautions that
2291d3cf9c7dSdf157793 * Still we are unable to queue, then we
2292d3cf9c7dSdf157793 * cannot do anything, just drop the block
2293d3cf9c7dSdf157793 */
2294d3cf9c7dSdf157793 cmn_err(CE_NOTE,
2295d3cf9c7dSdf157793 "su%d: local queue full\n",
2296d3cf9c7dSdf157793 UNIT(async->async_dev));
2297d3cf9c7dSdf157793 freemsg(bp);
2298d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2299d3cf9c7dSdf157793 if ((async->async_flags &
2300d3cf9c7dSdf157793 ASYNC_HW_IN_FLOW) == 0) {
2301d3cf9c7dSdf157793 async->async_flags |=
2302d3cf9c7dSdf157793 ASYNC_HW_IN_FLOW;
2303d3cf9c7dSdf157793 async->async_flowc =
2304d3cf9c7dSdf157793 async->async_stopc;
2305d3cf9c7dSdf157793 async->async_queue_full = 1;
2306d3cf9c7dSdf157793 }
2307d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2308d3cf9c7dSdf157793 } else {
2309d3cf9c7dSdf157793 (void) putq(q, bp);
2310d3cf9c7dSdf157793 }
2311d3cf9c7dSdf157793 } else {
2312d3cf9c7dSdf157793 putnext(q, bp);
2313d3cf9c7dSdf157793 }
2314d3cf9c7dSdf157793 } else {
2315d3cf9c7dSdf157793 freemsg(bp);
2316d3cf9c7dSdf157793 }
2317d3cf9c7dSdf157793 /*
2318d3cf9c7dSdf157793 * If we have a parity error, then send
2319d3cf9c7dSdf157793 * up an M_BREAK with the "bad"
2320d3cf9c7dSdf157793 * character as an argument. Let ldterm
2321d3cf9c7dSdf157793 * figure out what to do with the error.
2322d3cf9c7dSdf157793 */
2323d3cf9c7dSdf157793 if (cc)
2324d3cf9c7dSdf157793 (void) putctl1(q, M_BREAK, c);
2325d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2326d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2327d3cf9c7dSdf157793 rv:
2328d3cf9c7dSdf157793 /*
2329d3cf9c7dSdf157793 * If a transmission has finished, indicate that it's finished,
2330d3cf9c7dSdf157793 * and start that line up again.
2331d3cf9c7dSdf157793 */
2332d3cf9c7dSdf157793 if (async->async_break) {
2333d3cf9c7dSdf157793 async->async_break = 0;
2334d3cf9c7dSdf157793 if (async->async_flags & ASYNC_ISOPEN) {
2335d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2336d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2337d3cf9c7dSdf157793 (void) putctl(q, M_BREAK);
2338d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2339d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2340d3cf9c7dSdf157793 }
2341d3cf9c7dSdf157793 }
2342d3cf9c7dSdf157793 if ((async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) ||
234319cdc281Skc28005 (async->async_flowc != '\0')) {
2344d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_BUSY;
2345d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2346d3cf9c7dSdf157793 if (async->async_xmitblk)
2347d3cf9c7dSdf157793 freeb(async->async_xmitblk);
2348d3cf9c7dSdf157793 async->async_xmitblk = NULL;
2349d3cf9c7dSdf157793 if (async->async_flags & ASYNC_ISOPEN) {
2350d3cf9c7dSdf157793 asy->inperim = B_TRUE;
2351d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2352d3cf9c7dSdf157793 enterq(async->async_ttycommon.t_writeq);
2353d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2354d3cf9c7dSdf157793 }
2355d3cf9c7dSdf157793 async_start(async);
2356d3cf9c7dSdf157793 /*
2357d3cf9c7dSdf157793 * We need to check for inperim and ISOPEN due to
2358d3cf9c7dSdf157793 * multi-threading implications; it's possible to close the
2359d3cf9c7dSdf157793 * port and nullify async_flags while completing the software
2360d3cf9c7dSdf157793 * interrupt. If the port is closed, leaveq() will have already
2361d3cf9c7dSdf157793 * been called. We don't want to call it twice.
2362d3cf9c7dSdf157793 */
2363d3cf9c7dSdf157793 if ((asy->inperim) && (async->async_flags & ASYNC_ISOPEN)) {
2364d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2365d3cf9c7dSdf157793 leaveq(async->async_ttycommon.t_writeq);
2366d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2367d3cf9c7dSdf157793 asy->inperim = B_FALSE;
2368d3cf9c7dSdf157793 }
2369d3cf9c7dSdf157793 if (!(async->async_flags & ASYNC_BUSY))
2370d3cf9c7dSdf157793 cv_broadcast(&async->async_flags_cv);
2371d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2372d3cf9c7dSdf157793 }
2373d3cf9c7dSdf157793 /*
2374d3cf9c7dSdf157793 * A note about these overrun bits: all they do is *tell* someone
2375d3cf9c7dSdf157793 * about an error- They do not track multiple errors. In fact,
2376d3cf9c7dSdf157793 * you could consider them latched register bits if you like.
2377d3cf9c7dSdf157793 * We are only interested in printing the error message once for
23784cad604cSMarcel Telka * any cluster of overrun errors.
2379d3cf9c7dSdf157793 */
2380d3cf9c7dSdf157793 if (async->async_hw_overrun) {
2381d3cf9c7dSdf157793 if (async->async_flags & ASYNC_ISOPEN) {
2382d3cf9c7dSdf157793 if (su_log > 0) {
2383d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2384d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2385d3cf9c7dSdf157793 cmn_err(CE_NOTE, "su%d: silo overflow\n",
2386d3cf9c7dSdf157793 UNIT(async->async_dev));
2387d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2388d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2389d3cf9c7dSdf157793 }
2390d3cf9c7dSdf157793 INC64_KSTAT(asy, siloover);
2391d3cf9c7dSdf157793 }
2392d3cf9c7dSdf157793 async->async_hw_overrun = 0;
2393d3cf9c7dSdf157793 }
2394d3cf9c7dSdf157793 if (async->async_sw_overrun) {
2395d3cf9c7dSdf157793 if (async->async_flags & ASYNC_ISOPEN) {
2396d3cf9c7dSdf157793 if (su_log > 0) {
2397d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2398d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2399d3cf9c7dSdf157793 cmn_err(CE_NOTE, "su%d: ring buffer overflow\n",
2400d3cf9c7dSdf157793 UNIT(async->async_dev));
2401d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2402d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2403d3cf9c7dSdf157793 }
2404d3cf9c7dSdf157793 INC64_KSTAT(asy, ringover);
2405d3cf9c7dSdf157793 }
2406d3cf9c7dSdf157793 async->async_sw_overrun = 0;
2407d3cf9c7dSdf157793 }
2408d3cf9c7dSdf157793 asy->asy_flags &= ~ASY_DOINGSOFT;
2409d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2410d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2411d3cf9c7dSdf157793 if (q != NULL)
2412d3cf9c7dSdf157793 leaveq(q);
2413d3cf9c7dSdf157793 return (0);
2414d3cf9c7dSdf157793 }
2415d3cf9c7dSdf157793
2416d3cf9c7dSdf157793 /*
2417d3cf9c7dSdf157793 * Restart output on a line after a delay or break timer expired.
2418d3cf9c7dSdf157793 */
2419d3cf9c7dSdf157793 static void
async_restart(void * arg)2420d3cf9c7dSdf157793 async_restart(void *arg)
2421d3cf9c7dSdf157793 {
2422d3cf9c7dSdf157793 struct asyncline *async = arg;
2423d3cf9c7dSdf157793 struct asycom *asy = async->async_common;
2424d3cf9c7dSdf157793 queue_t *q;
2425d3cf9c7dSdf157793 uchar_t lcr;
2426d3cf9c7dSdf157793
2427d3cf9c7dSdf157793 /*
2428d3cf9c7dSdf157793 * If break timer expired, turn off the break bit.
2429d3cf9c7dSdf157793 */
2430d3cf9c7dSdf157793 #ifdef DEBUG
2431d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_PROCS)
2432d3cf9c7dSdf157793 printf("restart\n");
2433d3cf9c7dSdf157793 #endif
2434d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2435d3cf9c7dSdf157793 if (async->async_flags & ASYNC_BREAK) {
2436587bcfd8Skc28005 unsigned int rate;
2437587bcfd8Skc28005
2438d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2439d3cf9c7dSdf157793 lcr = INB(LCR);
2440d3cf9c7dSdf157793 OUTB(LCR, (lcr & ~SETBREAK));
2441587bcfd8Skc28005
2442587bcfd8Skc28005 /*
2443587bcfd8Skc28005 * Go to sleep for the time it takes for at least one
2444587bcfd8Skc28005 * stop bit to be received by the device at the other
2445587bcfd8Skc28005 * end of the line as stated in the RS-232 specification.
2446587bcfd8Skc28005 * The wait period is equal to:
2447587bcfd8Skc28005 * 2 clock cycles * (1 MICROSEC / baud rate)
2448587bcfd8Skc28005 */
2449587bcfd8Skc28005 rate = async->async_ttycommon.t_cflag & CBAUD;
2450587bcfd8Skc28005 if (async->async_ttycommon.t_cflag & CBAUDEXT)
2451587bcfd8Skc28005 rate += 16;
2452587bcfd8Skc28005 if (rate >= N_SU_SPEEDS || rate == B0) {
2453587bcfd8Skc28005 rate = B9600;
2454587bcfd8Skc28005 }
2455587bcfd8Skc28005
2456d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2457587bcfd8Skc28005 mutex_exit(asy->asy_excl);
2458587bcfd8Skc28005 drv_usecwait(2 * MICROSEC / baudtable[rate]);
2459587bcfd8Skc28005 mutex_enter(asy->asy_excl);
2460d3cf9c7dSdf157793 }
2461d3cf9c7dSdf157793 async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK|ASYNC_DRAINING);
2462d3cf9c7dSdf157793 if ((q = async->async_ttycommon.t_writeq) != NULL) {
2463d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2464d3cf9c7dSdf157793 enterq(q);
2465d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2466d3cf9c7dSdf157793 }
2467d3cf9c7dSdf157793 async_start(async);
2468d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2469d3cf9c7dSdf157793 if (q != NULL)
2470d3cf9c7dSdf157793 leaveq(q);
2471d3cf9c7dSdf157793
2472d3cf9c7dSdf157793 /* cleared break or delay flag; may have made some output progress */
2473d3cf9c7dSdf157793 cv_broadcast(&async->async_flags_cv);
2474d3cf9c7dSdf157793 }
2475d3cf9c7dSdf157793
2476d3cf9c7dSdf157793 static void
async_start(struct asyncline * async)2477d3cf9c7dSdf157793 async_start(struct asyncline *async)
2478d3cf9c7dSdf157793 {
2479d3cf9c7dSdf157793 async_nstart(async, 0);
2480d3cf9c7dSdf157793 }
2481d3cf9c7dSdf157793
2482d3cf9c7dSdf157793 /*
2483d3cf9c7dSdf157793 * Start output on a line, unless it's busy, frozen, or otherwise.
2484d3cf9c7dSdf157793 */
2485d3cf9c7dSdf157793 static void
async_nstart(struct asyncline * async,int mode)2486d3cf9c7dSdf157793 async_nstart(struct asyncline *async, int mode)
2487d3cf9c7dSdf157793 {
2488d3cf9c7dSdf157793 register struct asycom *asy = async->async_common;
2489d3cf9c7dSdf157793 register int cc;
2490d3cf9c7dSdf157793 register queue_t *q;
2491d3cf9c7dSdf157793 mblk_t *bp, *nbp;
2492d3cf9c7dSdf157793 uchar_t *xmit_addr;
2493d3cf9c7dSdf157793 uchar_t val;
2494d3cf9c7dSdf157793 int fifo_len = 1;
2495d3cf9c7dSdf157793 int xmit_progress;
2496d3cf9c7dSdf157793
2497d3cf9c7dSdf157793 #ifdef DEBUG
2498d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_PROCS)
2499d3cf9c7dSdf157793 printf("start\n");
2500d3cf9c7dSdf157793 #endif
2501d3cf9c7dSdf157793 if (asy->asy_use_fifo == FIFO_ON)
2502d3cf9c7dSdf157793 fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
2503d3cf9c7dSdf157793
2504d3cf9c7dSdf157793 ASSERT(mutex_owned(asy->asy_excl));
2505d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2506d3cf9c7dSdf157793 asycheckflowcontrol_hw(asy);
2507d3cf9c7dSdf157793
2508d3cf9c7dSdf157793 /*
2509d3cf9c7dSdf157793 * If the chip is busy (i.e., we're waiting for a break timeout
2510d3cf9c7dSdf157793 * to expire, or for the current transmission to finish, or for
2511d3cf9c7dSdf157793 * output to finish draining from chip), don't grab anything new.
2512d3cf9c7dSdf157793 */
2513d3cf9c7dSdf157793 if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY|ASYNC_DRAINING)) {
2514d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2515d3cf9c7dSdf157793 #ifdef DEBUG
2516d3cf9c7dSdf157793 if (mode && asydebug & ASY_DEBUG_CLOSE)
2517d3cf9c7dSdf157793 printf("asy%d: start %s.\n",
2518d3cf9c7dSdf157793 UNIT(async->async_dev),
2519d3cf9c7dSdf157793 async->async_flags & ASYNC_BREAK
2520d3cf9c7dSdf157793 ? "break" : "busy");
2521d3cf9c7dSdf157793 #endif
2522d3cf9c7dSdf157793 return;
2523d3cf9c7dSdf157793 }
2524d3cf9c7dSdf157793
2525d3cf9c7dSdf157793 /*
2526d3cf9c7dSdf157793 * If we have a flow-control character to transmit, do it now.
2527d3cf9c7dSdf157793 */
2528d3cf9c7dSdf157793 if (asycheckflowcontrol_sw(asy)) {
2529d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2530d3cf9c7dSdf157793 return;
2531d3cf9c7dSdf157793 }
2532d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2533d3cf9c7dSdf157793 /*
2534d3cf9c7dSdf157793 * If we're waiting for a delay timeout to expire, don't grab
2535d3cf9c7dSdf157793 * anything new.
2536d3cf9c7dSdf157793 */
2537d3cf9c7dSdf157793 if (async->async_flags & ASYNC_DELAY) {
2538d3cf9c7dSdf157793 #ifdef DEBUG
2539d3cf9c7dSdf157793 if (mode && asydebug & ASY_DEBUG_CLOSE)
2540d3cf9c7dSdf157793 printf("asy%d: start ASYNC_DELAY.\n",
2541d3cf9c7dSdf157793 UNIT(async->async_dev));
2542d3cf9c7dSdf157793 #endif
2543d3cf9c7dSdf157793 return;
2544d3cf9c7dSdf157793 }
2545d3cf9c7dSdf157793
2546d3cf9c7dSdf157793 if ((q = async->async_ttycommon.t_writeq) == NULL) {
2547d3cf9c7dSdf157793 #ifdef DEBUG
2548d3cf9c7dSdf157793 if (mode && asydebug & ASY_DEBUG_CLOSE)
2549d3cf9c7dSdf157793 printf("asy%d: start writeq is null.\n",
2550d3cf9c7dSdf157793 UNIT(async->async_dev));
2551d3cf9c7dSdf157793 #endif
2552d3cf9c7dSdf157793 return; /* not attached to a stream */
2553d3cf9c7dSdf157793 }
2554d3cf9c7dSdf157793
2555d3cf9c7dSdf157793 for (;;) {
2556d3cf9c7dSdf157793 if ((bp = getq(q)) == NULL)
2557d3cf9c7dSdf157793 return; /* no data to transmit */
2558d3cf9c7dSdf157793
2559d3cf9c7dSdf157793 /*
2560d3cf9c7dSdf157793 * We have a message block to work on.
2561d3cf9c7dSdf157793 * Check whether it's a break, a delay, or an ioctl (the latter
2562d3cf9c7dSdf157793 * occurs if the ioctl in question was waiting for the output
2563d3cf9c7dSdf157793 * to drain). If it's one of those, process it immediately.
2564d3cf9c7dSdf157793 */
2565d3cf9c7dSdf157793 switch (bp->b_datap->db_type) {
2566d3cf9c7dSdf157793
2567d3cf9c7dSdf157793 case M_BREAK:
2568d3cf9c7dSdf157793 /*
2569d3cf9c7dSdf157793 * Set the break bit, and arrange for "async_restart"
2570d3cf9c7dSdf157793 * to be called in 1/4 second; it will turn the
2571d3cf9c7dSdf157793 * break bit off, and call "async_start" to grab
2572d3cf9c7dSdf157793 * the next message.
2573d3cf9c7dSdf157793 */
2574d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2575d3cf9c7dSdf157793 val = INB(LCR);
2576d3cf9c7dSdf157793 OUTB(LCR, (val | SETBREAK));
2577d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2578d3cf9c7dSdf157793 async->async_flags |= ASYNC_BREAK;
2579d3cf9c7dSdf157793 (void) timeout(async_restart, async, hz / 4);
2580d3cf9c7dSdf157793 freemsg(bp);
2581d3cf9c7dSdf157793 return; /* wait for this to finish */
2582d3cf9c7dSdf157793
2583d3cf9c7dSdf157793 case M_DELAY:
2584d3cf9c7dSdf157793 /*
2585d3cf9c7dSdf157793 * Arrange for "async_restart" to be called when the
2586d3cf9c7dSdf157793 * delay expires; it will turn ASYNC_DELAY off,
2587d3cf9c7dSdf157793 * and call "async_start" to grab the next message.
2588d3cf9c7dSdf157793 */
2589d3cf9c7dSdf157793 (void) timeout(async_restart, async,
2590d3cf9c7dSdf157793 (clock_t)(*(unsigned char *)bp->b_rptr + 6));
2591d3cf9c7dSdf157793 async->async_flags |= ASYNC_DELAY;
2592d3cf9c7dSdf157793 freemsg(bp);
2593d3cf9c7dSdf157793 return; /* wait for this to finish */
2594d3cf9c7dSdf157793
2595d3cf9c7dSdf157793 case M_IOCTL:
2596d3cf9c7dSdf157793 /*
2597d3cf9c7dSdf157793 * This ioctl needs to wait for the output ahead of
2598d3cf9c7dSdf157793 * it to drain. Try to do it, and then either
2599d3cf9c7dSdf157793 * redo the ioctl at a later time or grab the next
2600d3cf9c7dSdf157793 * message after it.
2601d3cf9c7dSdf157793 */
2602d3cf9c7dSdf157793
2603d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2604d3cf9c7dSdf157793 if (asy_isbusy(asy)) {
2605d3cf9c7dSdf157793 /*
2606d3cf9c7dSdf157793 * Get the divisor by calculating the rate
2607d3cf9c7dSdf157793 */
2608d3cf9c7dSdf157793 unsigned int rate;
2609d3cf9c7dSdf157793
2610d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2611d3cf9c7dSdf157793 rate = async->async_ttycommon.t_cflag & CBAUD;
2612d3cf9c7dSdf157793 if (async->async_ttycommon.t_cflag & CBAUDEXT)
2613d3cf9c7dSdf157793 rate += 16;
2614d3cf9c7dSdf157793 if (rate >= N_SU_SPEEDS || rate == B0) {
2615d3cf9c7dSdf157793 rate = B9600;
2616d3cf9c7dSdf157793 }
2617d3cf9c7dSdf157793
2618d3cf9c7dSdf157793 /*
2619d3cf9c7dSdf157793 * We need to do a callback as the port will
2620d3cf9c7dSdf157793 * be set to drain
2621d3cf9c7dSdf157793 */
2622d3cf9c7dSdf157793 async->async_flags |= ASYNC_DRAINING;
2623d3cf9c7dSdf157793
2624d3cf9c7dSdf157793 /*
2625d3cf9c7dSdf157793 * Put the message we just processed back onto
2626d3cf9c7dSdf157793 * the end of the queue
2627d3cf9c7dSdf157793 */
2628d3cf9c7dSdf157793 if (putq(q, bp) == 0)
2629d3cf9c7dSdf157793 freemsg(bp);
2630d3cf9c7dSdf157793
2631d3cf9c7dSdf157793 /*
2632d3cf9c7dSdf157793 * We need to delay until the TSR and THR
2633d3cf9c7dSdf157793 * have been exhausted. We base the delay on
2634d3cf9c7dSdf157793 * the amount of time it takes to transmit
2635d3cf9c7dSdf157793 * 2 chars at the current baud rate in
2636d3cf9c7dSdf157793 * microseconds.
2637d3cf9c7dSdf157793 *
2638d3cf9c7dSdf157793 * Therefore, the wait period is:
2639d3cf9c7dSdf157793 *
2640d3cf9c7dSdf157793 * (#TSR bits + #THR bits) *
2641d3cf9c7dSdf157793 * 1 MICROSEC / baud rate
2642d3cf9c7dSdf157793 */
2643d3cf9c7dSdf157793 (void) timeout(async_restart, async,
2644d3cf9c7dSdf157793 drv_usectohz(16 * MICROSEC /
2645d3cf9c7dSdf157793 baudtable[rate]));
2646d3cf9c7dSdf157793 return;
2647d3cf9c7dSdf157793 }
2648d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2649d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2650d3cf9c7dSdf157793 async_ioctl(async, q, bp, B_FALSE);
2651d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2652d3cf9c7dSdf157793 continue;
2653d3cf9c7dSdf157793 }
2654d3cf9c7dSdf157793
2655d3cf9c7dSdf157793 while (bp != NULL && (cc = bp->b_wptr - bp->b_rptr) == 0) {
2656d3cf9c7dSdf157793 nbp = bp->b_cont;
2657d3cf9c7dSdf157793 freeb(bp);
2658d3cf9c7dSdf157793 bp = nbp;
2659d3cf9c7dSdf157793 }
2660d3cf9c7dSdf157793 if (bp != NULL)
2661d3cf9c7dSdf157793 break;
2662d3cf9c7dSdf157793 }
2663d3cf9c7dSdf157793
2664d3cf9c7dSdf157793 /*
2665d3cf9c7dSdf157793 * We have data to transmit. If output is stopped, put
2666d3cf9c7dSdf157793 * it back and try again later.
2667d3cf9c7dSdf157793 */
2668d3cf9c7dSdf157793 if (async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED)) {
2669d3cf9c7dSdf157793 #ifdef DEBUG
2670d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_HFLOW &&
2671d3cf9c7dSdf157793 async->async_flags & ASYNC_HW_OUT_FLW)
2672d3cf9c7dSdf157793 printf("asy%d: output hflow in effect.\n",
2673d3cf9c7dSdf157793 UNIT(async->async_dev));
2674d3cf9c7dSdf157793 #endif
2675d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2676d3cf9c7dSdf157793 (void) putbq(q, bp);
2677d3cf9c7dSdf157793 /*
2678d3cf9c7dSdf157793 * We entered the routine owning the lock, we need to
2679d3cf9c7dSdf157793 * exit the routine owning the lock.
2680d3cf9c7dSdf157793 */
2681d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2682d3cf9c7dSdf157793 return;
2683d3cf9c7dSdf157793 }
2684d3cf9c7dSdf157793
2685d3cf9c7dSdf157793 async->async_xmitblk = bp;
2686d3cf9c7dSdf157793 xmit_addr = bp->b_rptr;
2687d3cf9c7dSdf157793 bp = bp->b_cont;
2688d3cf9c7dSdf157793 if (bp != NULL) {
2689d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2690d3cf9c7dSdf157793 (void) putbq(q, bp); /* not done with this message yet */
2691d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2692d3cf9c7dSdf157793 }
2693d3cf9c7dSdf157793
2694d3cf9c7dSdf157793 /*
2695d3cf9c7dSdf157793 * In 5-bit mode, the high order bits are used
2696d3cf9c7dSdf157793 * to indicate character sizes less than five,
2697d3cf9c7dSdf157793 * so we need to explicitly mask before transmitting
2698d3cf9c7dSdf157793 */
2699d3cf9c7dSdf157793 if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) {
2700d3cf9c7dSdf157793 register unsigned char *p = xmit_addr;
2701d3cf9c7dSdf157793 register int cnt = cc;
2702d3cf9c7dSdf157793
2703d3cf9c7dSdf157793 while (cnt--)
2704d3cf9c7dSdf157793 *p++ &= (unsigned char) 0x1f;
2705d3cf9c7dSdf157793 }
2706d3cf9c7dSdf157793
2707d3cf9c7dSdf157793 /*
2708d3cf9c7dSdf157793 * Set up this block for pseudo-DMA.
2709d3cf9c7dSdf157793 */
2710d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2711d3cf9c7dSdf157793 async->async_optr = xmit_addr;
2712d3cf9c7dSdf157793 async->async_ocnt = cc;
2713d3cf9c7dSdf157793 /*
2714d3cf9c7dSdf157793 * If the transmitter is ready, shove some
2715d3cf9c7dSdf157793 * characters out.
2716d3cf9c7dSdf157793 */
2717d3cf9c7dSdf157793 xmit_progress = 0;
2718d3cf9c7dSdf157793 while (fifo_len-- && async->async_ocnt) {
2719d3cf9c7dSdf157793 if (INB(LSR) & XHRE) {
2720d3cf9c7dSdf157793 OUTB(DAT, *async->async_optr++);
2721d3cf9c7dSdf157793 async->async_ocnt--;
2722d3cf9c7dSdf157793 xmit_progress++;
2723d3cf9c7dSdf157793 }
2724d3cf9c7dSdf157793 }
2725d3cf9c7dSdf157793 asy->asy_out_of_band_xmit = xmit_progress;
2726d3cf9c7dSdf157793 if (xmit_progress > 0)
2727d3cf9c7dSdf157793 async->async_flags |= ASYNC_PROGRESS;
2728d3cf9c7dSdf157793 async->async_flags |= ASYNC_BUSY;
2729d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2730d3cf9c7dSdf157793 }
2731d3cf9c7dSdf157793
2732d3cf9c7dSdf157793 /*
2733d3cf9c7dSdf157793 * Resume output by poking the transmitter.
2734d3cf9c7dSdf157793 */
2735d3cf9c7dSdf157793 static void
async_resume(struct asyncline * async)2736d3cf9c7dSdf157793 async_resume(struct asyncline *async)
2737d3cf9c7dSdf157793 {
2738d3cf9c7dSdf157793 register struct asycom *asy = async->async_common;
2739d3cf9c7dSdf157793
2740d3cf9c7dSdf157793 ASSERT(mutex_owned(asy->asy_excl_hi));
2741d3cf9c7dSdf157793 #ifdef DEBUG
2742d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_PROCS)
2743d3cf9c7dSdf157793 printf("resume\n");
2744d3cf9c7dSdf157793 #endif
2745d3cf9c7dSdf157793
2746d3cf9c7dSdf157793 asycheckflowcontrol_hw(asy);
2747d3cf9c7dSdf157793
2748d3cf9c7dSdf157793 if (INB(LSR) & XHRE) {
2749d3cf9c7dSdf157793 if (asycheckflowcontrol_sw(asy)) {
2750d3cf9c7dSdf157793 return;
2751d3cf9c7dSdf157793 } else if (async->async_ocnt > 0) {
2752d3cf9c7dSdf157793 OUTB(DAT, *async->async_optr++);
2753d3cf9c7dSdf157793 async->async_ocnt--;
2754d3cf9c7dSdf157793 async->async_flags |= ASYNC_PROGRESS;
2755d3cf9c7dSdf157793 }
2756d3cf9c7dSdf157793 }
2757d3cf9c7dSdf157793 }
2758d3cf9c7dSdf157793
2759d3cf9c7dSdf157793 /*
2760d3cf9c7dSdf157793 * Process an "ioctl" message sent down to us.
2761d3cf9c7dSdf157793 * Note that we don't need to get any locks until we are ready to access
2762d3cf9c7dSdf157793 * the hardware. Nothing we access until then is going to be altered
2763d3cf9c7dSdf157793 * outside of the STREAMS framework, so we should be safe.
2764d3cf9c7dSdf157793 */
2765d3cf9c7dSdf157793 static void
async_ioctl(struct asyncline * async,queue_t * wq,mblk_t * mp,boolean_t iswput)2766d3cf9c7dSdf157793 async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp, boolean_t iswput)
2767d3cf9c7dSdf157793 {
2768d3cf9c7dSdf157793 register struct asycom *asy = async->async_common;
2769d3cf9c7dSdf157793 register tty_common_t *tp = &async->async_ttycommon;
2770d3cf9c7dSdf157793 register struct iocblk *iocp;
2771d3cf9c7dSdf157793 register unsigned datasize;
277290a71dbdSzk194757 size_t ioc_count;
2773d3cf9c7dSdf157793 mblk_t *datamp;
2774d3cf9c7dSdf157793 int error = 0;
2775d3cf9c7dSdf157793 uchar_t val, icr;
2776d3cf9c7dSdf157793 #ifdef DEBUG
2777d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_PROCS)
2778d3cf9c7dSdf157793 printf("ioctl\n");
2779d3cf9c7dSdf157793 #endif
2780d3cf9c7dSdf157793
2781d3cf9c7dSdf157793 if (tp->t_iocpending != NULL) {
2782d3cf9c7dSdf157793 /*
2783d3cf9c7dSdf157793 * We were holding an "ioctl" response pending the
2784d3cf9c7dSdf157793 * availability of an "mblk" to hold data to be passed up;
2785d3cf9c7dSdf157793 * another "ioctl" came through, which means that "ioctl"
2786d3cf9c7dSdf157793 * must have timed out or been aborted.
2787d3cf9c7dSdf157793 */
2788d3cf9c7dSdf157793 freemsg(async->async_ttycommon.t_iocpending);
2789d3cf9c7dSdf157793 async->async_ttycommon.t_iocpending = NULL;
2790d3cf9c7dSdf157793 }
2791d3cf9c7dSdf157793
2792d3cf9c7dSdf157793 iocp = (struct iocblk *)mp->b_rptr;
2793d3cf9c7dSdf157793
2794d3cf9c7dSdf157793 /*
279590a71dbdSzk194757 * Save off the ioc count in case we need to restore it
279690a71dbdSzk194757 * because we are queuing a message block.
279790a71dbdSzk194757 */
279890a71dbdSzk194757 ioc_count = iocp->ioc_count;
279990a71dbdSzk194757
280090a71dbdSzk194757 /*
2801d3cf9c7dSdf157793 * For TIOCMGET, TIOCMBIC, TIOCMBIS, TIOCMSET, and PPS, do NOT call
2802d3cf9c7dSdf157793 * ttycommon_ioctl() because this function frees up the message block
2803d3cf9c7dSdf157793 * (mp->b_cont) that contains the address of the user variable where
2804d3cf9c7dSdf157793 * we need to pass back the bit array.
28050280efdcSzk194757 *
28060280efdcSzk194757 * Similarly, ttycommon_ioctl() does not know about CONSOPENPOLLEDIO
28070280efdcSzk194757 * and CONSCLOSEPOLLEDIO, so don't let ttycommon_ioctl() touch them.
2808d3cf9c7dSdf157793 */
2809d3cf9c7dSdf157793 if (iocp->ioc_cmd == TIOCMGET ||
2810d3cf9c7dSdf157793 iocp->ioc_cmd == TIOCMBIC ||
2811d3cf9c7dSdf157793 iocp->ioc_cmd == TIOCMBIS ||
2812d3cf9c7dSdf157793 iocp->ioc_cmd == TIOCMSET ||
2813d3cf9c7dSdf157793 iocp->ioc_cmd == TIOCGPPS ||
2814d3cf9c7dSdf157793 iocp->ioc_cmd == TIOCSPPS ||
28150280efdcSzk194757 iocp->ioc_cmd == TIOCGPPSEV ||
28160280efdcSzk194757 iocp->ioc_cmd == CONSOPENPOLLEDIO ||
28170280efdcSzk194757 iocp->ioc_cmd == CONSCLOSEPOLLEDIO)
2818d3cf9c7dSdf157793 error = -1; /* Do Nothing */
2819d3cf9c7dSdf157793 else
2820d3cf9c7dSdf157793
2821d3cf9c7dSdf157793 /*
2822d3cf9c7dSdf157793 * The only way in which "ttycommon_ioctl" can fail is if the "ioctl"
2823d3cf9c7dSdf157793 * requires a response containing data to be returned to the user,
2824d3cf9c7dSdf157793 * and no mblk could be allocated for the data.
2825d3cf9c7dSdf157793 * No such "ioctl" alters our state. Thus, we always go ahead and
2826d3cf9c7dSdf157793 * do any state-changes the "ioctl" calls for. If we couldn't allocate
2827d3cf9c7dSdf157793 * the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so
2828d3cf9c7dSdf157793 * we just call "bufcall" to request that we be called back when we
2829d3cf9c7dSdf157793 * stand a better chance of allocating the data.
2830d3cf9c7dSdf157793 */
2831d3cf9c7dSdf157793 if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) {
2832d3cf9c7dSdf157793 if (async->async_wbufcid)
2833d3cf9c7dSdf157793 unbufcall(async->async_wbufcid);
2834d3cf9c7dSdf157793 async->async_wbufcid = bufcall(datasize, BPRI_HI, async_reioctl,
2835d3cf9c7dSdf157793 async);
2836d3cf9c7dSdf157793 return;
2837d3cf9c7dSdf157793 }
2838d3cf9c7dSdf157793
2839d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
2840d3cf9c7dSdf157793
2841d3cf9c7dSdf157793 if (error == 0) {
2842d3cf9c7dSdf157793 /*
2843d3cf9c7dSdf157793 * "ttycommon_ioctl" did most of the work; we just use the
2844d3cf9c7dSdf157793 * data it set up.
2845d3cf9c7dSdf157793 */
2846d3cf9c7dSdf157793 switch (iocp->ioc_cmd) {
2847d3cf9c7dSdf157793
2848d3cf9c7dSdf157793 case TCSETS:
2849d3cf9c7dSdf157793 if (!(asy->asy_rsc_console || asy->asy_rsc_control ||
2850d3cf9c7dSdf157793 asy->asy_lom_console)) {
2851d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2852d3cf9c7dSdf157793 error = asy_program(asy, ASY_NOINIT);
2853d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2854d3cf9c7dSdf157793 }
2855d3cf9c7dSdf157793 break;
2856d3cf9c7dSdf157793 case TCSETSF:
2857d3cf9c7dSdf157793 case TCSETSW:
2858d3cf9c7dSdf157793 case TCSETA:
2859d3cf9c7dSdf157793 case TCSETAW:
2860d3cf9c7dSdf157793 case TCSETAF:
2861d3cf9c7dSdf157793 if (!(asy->asy_rsc_console || asy->asy_rsc_control ||
2862d3cf9c7dSdf157793 asy->asy_lom_console)) {
2863d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2864d3cf9c7dSdf157793 if (iswput && asy_isbusy(asy)) {
286590a71dbdSzk194757 /*
286690a71dbdSzk194757 * ttycommon_ioctl sets the db_type to
286790a71dbdSzk194757 * M_IOCACK and ioc_count to zero
286890a71dbdSzk194757 * we need to undo this when we
286990a71dbdSzk194757 * queue a control message. This will
287090a71dbdSzk194757 * allow the control messages to be
287190a71dbdSzk194757 * processed again when the chip
287290a71dbdSzk194757 * becomes available.
287390a71dbdSzk194757 */
287490a71dbdSzk194757 mp->b_datap->db_type = M_IOCTL;
287590a71dbdSzk194757 iocp->ioc_count = ioc_count;
287690a71dbdSzk194757
2877d3cf9c7dSdf157793 if (putq(wq, mp) == 0)
2878d3cf9c7dSdf157793 freemsg(mp);
2879d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2880d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
2881d3cf9c7dSdf157793 return;
2882d3cf9c7dSdf157793 }
28830a450c21Szk194757
28840a450c21Szk194757 /*
28850a450c21Szk194757 * TCSETA, TCSETAW, and TCSETAF make use of
28860a450c21Szk194757 * the termio structure and therefore have
28870a450c21Szk194757 * no concept of any speed except what can
28880a450c21Szk194757 * be represented by CBAUD. This is because
28890a450c21Szk194757 * of legacy SVR4 code. Therefore, if we see
28900a450c21Szk194757 * one of the aforementioned IOCTL commands
28910a450c21Szk194757 * we should zero out CBAUDEXT, CIBAUD, and
28920a450c21Szk194757 * CIBAUDEXT as to not break legacy
28930a450c21Szk194757 * functionality. This is because CBAUDEXT,
28940a450c21Szk194757 * CIBAUD, and CIBAUDEXT can't be stored in
28950a450c21Szk194757 * an unsigned short. By zeroing out CBAUDEXT,
28960a450c21Szk194757 * CIBAUD, and CIBAUDEXT in the t_cflag of the
28970a450c21Szk194757 * termios structure asy_program() will set the
28980a450c21Szk194757 * input baud rate to the output baud rate.
28990a450c21Szk194757 */
29000a450c21Szk194757 if (iocp->ioc_cmd == TCSETA ||
29010a450c21Szk194757 iocp->ioc_cmd == TCSETAW ||
29020a450c21Szk194757 iocp->ioc_cmd == TCSETAF)
29030a450c21Szk194757 tp->t_cflag &= ~(CIBAUD |
29040a450c21Szk194757 CIBAUDEXT | CBAUDEXT);
29050a450c21Szk194757
2906d3cf9c7dSdf157793 error = asy_program(asy, ASY_NOINIT);
2907d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2908d3cf9c7dSdf157793 }
2909d3cf9c7dSdf157793 break;
2910d3cf9c7dSdf157793 case TIOCSSOFTCAR:
2911d3cf9c7dSdf157793 /* Set the driver state appropriately */
2912d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2913d3cf9c7dSdf157793 if (tp->t_flags & TS_SOFTCAR)
2914d3cf9c7dSdf157793 asy->asy_flags |= ASY_IGNORE_CD;
2915d3cf9c7dSdf157793 else
2916d3cf9c7dSdf157793 asy->asy_flags &= ~ASY_IGNORE_CD;
2917d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2918d3cf9c7dSdf157793 break;
2919d3cf9c7dSdf157793 }
2920d3cf9c7dSdf157793 } else if (error < 0) {
2921d3cf9c7dSdf157793 /*
2922d3cf9c7dSdf157793 * "ttycommon_ioctl" didn't do anything; we process it here.
2923d3cf9c7dSdf157793 */
2924d3cf9c7dSdf157793 error = 0;
2925d3cf9c7dSdf157793 switch (iocp->ioc_cmd) {
2926d3cf9c7dSdf157793
2927d3cf9c7dSdf157793 case TIOCGPPS:
2928d3cf9c7dSdf157793 /*
2929d3cf9c7dSdf157793 * Get PPS on/off.
2930d3cf9c7dSdf157793 */
2931d3cf9c7dSdf157793 if (mp->b_cont != NULL)
2932d3cf9c7dSdf157793 freemsg(mp->b_cont);
2933d3cf9c7dSdf157793
2934d3cf9c7dSdf157793 mp->b_cont = allocb(sizeof (int), BPRI_HI);
2935d3cf9c7dSdf157793 if (mp->b_cont == NULL) {
2936d3cf9c7dSdf157793 error = ENOMEM;
2937d3cf9c7dSdf157793 break;
2938d3cf9c7dSdf157793 }
2939d3cf9c7dSdf157793 if (asy->asy_flags & ASY_PPS)
2940d3cf9c7dSdf157793 *(int *)mp->b_cont->b_wptr = 1;
2941d3cf9c7dSdf157793 else
2942d3cf9c7dSdf157793 *(int *)mp->b_cont->b_wptr = 0;
2943d3cf9c7dSdf157793 mp->b_cont->b_wptr += sizeof (int);
2944d3cf9c7dSdf157793 mp->b_datap->db_type = M_IOCACK;
2945d3cf9c7dSdf157793 iocp->ioc_count = sizeof (int);
2946d3cf9c7dSdf157793 break;
2947d3cf9c7dSdf157793
2948d3cf9c7dSdf157793 case TIOCSPPS:
2949d3cf9c7dSdf157793 /*
2950d3cf9c7dSdf157793 * Set PPS on/off.
2951d3cf9c7dSdf157793 */
2952d3cf9c7dSdf157793 error = miocpullup(mp, sizeof (int));
2953d3cf9c7dSdf157793 if (error != 0)
2954d3cf9c7dSdf157793 break;
2955d3cf9c7dSdf157793
2956d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2957d3cf9c7dSdf157793 if (*(int *)mp->b_cont->b_rptr)
2958d3cf9c7dSdf157793 asy->asy_flags |= ASY_PPS;
2959d3cf9c7dSdf157793 else
2960d3cf9c7dSdf157793 asy->asy_flags &= ~ASY_PPS;
2961d3cf9c7dSdf157793 /* Reset edge sense */
2962d3cf9c7dSdf157793 asy->asy_flags &= ~ASY_PPS_EDGE;
2963d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2964d3cf9c7dSdf157793 mp->b_datap->db_type = M_IOCACK;
2965d3cf9c7dSdf157793 break;
2966d3cf9c7dSdf157793
2967d3cf9c7dSdf157793 case TIOCGPPSEV: {
2968d3cf9c7dSdf157793 /*
2969d3cf9c7dSdf157793 * Get PPS event data.
2970d3cf9c7dSdf157793 */
2971d3cf9c7dSdf157793 mblk_t *bp;
2972d3cf9c7dSdf157793 void *buf;
2973d3cf9c7dSdf157793 #ifdef _SYSCALL32_IMPL
2974d3cf9c7dSdf157793 struct ppsclockev32 p32;
2975d3cf9c7dSdf157793 #endif
2976d3cf9c7dSdf157793 struct ppsclockev ppsclockev;
2977d3cf9c7dSdf157793
2978d3cf9c7dSdf157793 if (mp->b_cont != NULL) {
2979d3cf9c7dSdf157793 freemsg(mp->b_cont);
2980d3cf9c7dSdf157793 mp->b_cont = NULL;
2981d3cf9c7dSdf157793 }
2982d3cf9c7dSdf157793
2983d3cf9c7dSdf157793 if ((asy->asy_flags & ASY_PPS) == 0) {
2984d3cf9c7dSdf157793 error = ENXIO;
2985d3cf9c7dSdf157793 break;
2986d3cf9c7dSdf157793 }
2987d3cf9c7dSdf157793
2988d3cf9c7dSdf157793 /* Protect from incomplete asy_ppsev */
2989d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
2990d3cf9c7dSdf157793 ppsclockev = asy_ppsev;
2991d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
2992d3cf9c7dSdf157793
2993d3cf9c7dSdf157793 #ifdef _SYSCALL32_IMPL
2994d3cf9c7dSdf157793 if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
2995d3cf9c7dSdf157793 TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv);
2996d3cf9c7dSdf157793 p32.serial = ppsclockev.serial;
2997d3cf9c7dSdf157793 buf = &p32;
2998d3cf9c7dSdf157793 iocp->ioc_count = sizeof (struct ppsclockev32);
2999d3cf9c7dSdf157793 } else
3000d3cf9c7dSdf157793 #endif
3001d3cf9c7dSdf157793 {
3002d3cf9c7dSdf157793 buf = &ppsclockev;
3003d3cf9c7dSdf157793 iocp->ioc_count = sizeof (struct ppsclockev);
3004d3cf9c7dSdf157793 }
3005d3cf9c7dSdf157793
3006d3cf9c7dSdf157793 if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) {
3007d3cf9c7dSdf157793 error = ENOMEM;
3008d3cf9c7dSdf157793 break;
3009d3cf9c7dSdf157793 }
3010d3cf9c7dSdf157793 mp->b_cont = bp;
3011d3cf9c7dSdf157793
3012d3cf9c7dSdf157793 bcopy(buf, bp->b_wptr, iocp->ioc_count);
3013d3cf9c7dSdf157793 bp->b_wptr += iocp->ioc_count;
3014d3cf9c7dSdf157793 mp->b_datap->db_type = M_IOCACK;
3015d3cf9c7dSdf157793 break;
3016d3cf9c7dSdf157793 }
3017d3cf9c7dSdf157793
3018d3cf9c7dSdf157793 case TCSBRK:
3019d3cf9c7dSdf157793 error = miocpullup(mp, sizeof (int));
3020d3cf9c7dSdf157793 if (error != 0)
3021d3cf9c7dSdf157793 break;
3022d3cf9c7dSdf157793
3023d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
3024d3cf9c7dSdf157793 if (*(int *)mp->b_cont->b_rptr == 0) {
3025d3cf9c7dSdf157793 /*
3026d3cf9c7dSdf157793 * Get the divisor by calculating the rate
3027d3cf9c7dSdf157793 */
3028d3cf9c7dSdf157793 unsigned int rate, divisor;
3029d3cf9c7dSdf157793 rate = async->async_ttycommon.t_cflag & CBAUD;
3030d3cf9c7dSdf157793 if (async->async_ttycommon.t_cflag & CBAUDEXT)
3031d3cf9c7dSdf157793 rate += 16;
3032d3cf9c7dSdf157793 if (rate >= N_SU_SPEEDS) rate = B9600;
3033d3cf9c7dSdf157793 divisor = asyspdtab[rate] & 0xfff;
3034d3cf9c7dSdf157793
3035d3cf9c7dSdf157793 /*
3036d3cf9c7dSdf157793 * To ensure that erroneous characters are
3037d3cf9c7dSdf157793 * not sent out when the break is set, SB
3038d3cf9c7dSdf157793 * recommends three steps:
3039d3cf9c7dSdf157793 *
3040d3cf9c7dSdf157793 * 1) pad the TSR with 0 bits
3041d3cf9c7dSdf157793 * 2) When the TSR is full, set break
3042d3cf9c7dSdf157793 * 3) When the TSR has been flushed, unset
3043d3cf9c7dSdf157793 * the break when transmission must be
3044d3cf9c7dSdf157793 * restored.
3045d3cf9c7dSdf157793 *
3046d3cf9c7dSdf157793 * We loop until the TSR is empty and then
3047d3cf9c7dSdf157793 * set the break. ASYNC_BREAK has been set
3048d3cf9c7dSdf157793 * to ensure that no characters are
3049d3cf9c7dSdf157793 * transmitted while the TSR is being
3050d3cf9c7dSdf157793 * flushed and SOUT is being used for the
3051d3cf9c7dSdf157793 * break signal.
3052d3cf9c7dSdf157793 *
3053d3cf9c7dSdf157793 * The wait period is equal to
3054d3cf9c7dSdf157793 * clock / (baud * 16) * 16 * 2.
3055d3cf9c7dSdf157793 */
3056d3cf9c7dSdf157793 async->async_flags |= ASYNC_BREAK;
3057d3cf9c7dSdf157793 while ((INB(LSR) & XSRE) == 0) {
3058d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3059d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3060d3cf9c7dSdf157793 drv_usecwait(32*divisor);
3061d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
3062d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
3063d3cf9c7dSdf157793 }
3064d3cf9c7dSdf157793
3065d3cf9c7dSdf157793 /*
3066d3cf9c7dSdf157793 * Set the break bit, and arrange for
3067d3cf9c7dSdf157793 * "async_restart" to be called in 1/4 second;
3068d3cf9c7dSdf157793 * it will turn the break bit off, and call
3069d3cf9c7dSdf157793 * "async_start" to grab the next message.
3070d3cf9c7dSdf157793 */
3071d3cf9c7dSdf157793 val = INB(LCR);
3072d3cf9c7dSdf157793 OUTB(LCR, (val | SETBREAK));
3073d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3074d3cf9c7dSdf157793 (void) timeout(async_restart, async, hz / 4);
3075d3cf9c7dSdf157793 } else {
3076d3cf9c7dSdf157793 #ifdef DEBUG
3077d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_CLOSE)
3078d3cf9c7dSdf157793 printf("asy%d: wait for flush.\n",
3079d3cf9c7dSdf157793 UNIT(async->async_dev));
3080d3cf9c7dSdf157793 #endif
3081d3cf9c7dSdf157793 if (iswput && asy_isbusy(asy)) {
3082d3cf9c7dSdf157793 if (putq(wq, mp) == 0)
3083d3cf9c7dSdf157793 freemsg(mp);
3084d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3085d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3086d3cf9c7dSdf157793 return;
3087d3cf9c7dSdf157793 }
3088d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3089d3cf9c7dSdf157793 #ifdef DEBUG
3090d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_CLOSE)
3091d3cf9c7dSdf157793 printf("asy%d: ldterm satisfied.\n",
3092d3cf9c7dSdf157793 UNIT(async->async_dev));
3093d3cf9c7dSdf157793 #endif
3094d3cf9c7dSdf157793 }
3095d3cf9c7dSdf157793 break;
3096d3cf9c7dSdf157793
3097d3cf9c7dSdf157793 case TIOCSBRK:
3098d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
3099d3cf9c7dSdf157793 val = INB(LCR);
3100d3cf9c7dSdf157793 OUTB(LCR, (val | SETBREAK));
3101d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3102d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3103d3cf9c7dSdf157793 miocack(wq, mp, 0, 0);
3104d3cf9c7dSdf157793 return;
3105d3cf9c7dSdf157793
3106d3cf9c7dSdf157793 case TIOCCBRK:
3107d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
3108d3cf9c7dSdf157793 val = INB(LCR);
3109d3cf9c7dSdf157793 OUTB(LCR, (val & ~SETBREAK));
3110d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3111d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3112d3cf9c7dSdf157793 miocack(wq, mp, 0, 0);
3113d3cf9c7dSdf157793 return;
3114d3cf9c7dSdf157793
3115d3cf9c7dSdf157793 case TIOCMSET:
3116d3cf9c7dSdf157793 case TIOCMBIS:
3117d3cf9c7dSdf157793 case TIOCMBIC:
3118d3cf9c7dSdf157793 if (iocp->ioc_count == TRANSPARENT)
3119d3cf9c7dSdf157793 mcopyin(mp, NULL, sizeof (int), NULL);
3120d3cf9c7dSdf157793 else {
3121d3cf9c7dSdf157793 error = miocpullup(mp, sizeof (int));
3122d3cf9c7dSdf157793 if (error != 0)
3123d3cf9c7dSdf157793 break;
3124d3cf9c7dSdf157793
3125d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
3126d3cf9c7dSdf157793
3127d3cf9c7dSdf157793 (void) asymctl(asy,
3128d3cf9c7dSdf157793 dmtoasy(*(int *)mp->b_cont->b_rptr),
3129d3cf9c7dSdf157793 iocp->ioc_cmd);
3130d3cf9c7dSdf157793
3131d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3132d3cf9c7dSdf157793 iocp->ioc_error = 0;
3133d3cf9c7dSdf157793 mp->b_datap->db_type = M_IOCACK;
3134d3cf9c7dSdf157793 }
3135d3cf9c7dSdf157793 break;
3136d3cf9c7dSdf157793
3137d3cf9c7dSdf157793 case TIOCSILOOP:
3138d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
3139d3cf9c7dSdf157793 /*
3140d3cf9c7dSdf157793 * If somebody misues this Ioctl when used for
3141d3cf9c7dSdf157793 * driving keyboard and mouse indicate not supported
3142d3cf9c7dSdf157793 */
3143d3cf9c7dSdf157793 if ((asy->asy_device_type == ASY_KEYBOARD) ||
3144d3cf9c7dSdf157793 (asy->asy_device_type == ASY_MOUSE)) {
3145d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3146d3cf9c7dSdf157793 error = ENOTTY;
3147d3cf9c7dSdf157793 break;
3148d3cf9c7dSdf157793 }
3149d3cf9c7dSdf157793
3150d3cf9c7dSdf157793 /* should not use when we're the console */
3151d3cf9c7dSdf157793 if ((async->async_dev == kbddev) ||
3152d3cf9c7dSdf157793 (async->async_dev == rconsdev) ||
3153d3cf9c7dSdf157793 (async->async_dev == stdindev)) {
3154d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3155d3cf9c7dSdf157793 error = EINVAL;
3156d3cf9c7dSdf157793 break;
3157d3cf9c7dSdf157793 }
3158d3cf9c7dSdf157793
3159d3cf9c7dSdf157793 val = INB(MCR);
3160d3cf9c7dSdf157793 icr = INB(ICR);
3161d3cf9c7dSdf157793 /*
3162d3cf9c7dSdf157793 * Disable the Modem Status Interrupt
3163d3cf9c7dSdf157793 * The reason for disabling is the status of
3164d3cf9c7dSdf157793 * modem signal are in the higher 4 bits instead of
3165d3cf9c7dSdf157793 * lower four bits when in loopback mode,
3166d3cf9c7dSdf157793 * so, donot worry about Modem interrupt when
3167d3cf9c7dSdf157793 * you are planning to set
3168d3cf9c7dSdf157793 * this in loopback mode until it is cleared by
3169d3cf9c7dSdf157793 * another ioctl to get out of the loopback mode
3170d3cf9c7dSdf157793 */
3171d3cf9c7dSdf157793 OUTB(ICR, icr & ~ MIEN);
3172d3cf9c7dSdf157793 OUTB(MCR, val | ASY_LOOP);
3173d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3174d3cf9c7dSdf157793 iocp->ioc_error = 0;
3175d3cf9c7dSdf157793 mp->b_datap->db_type = M_IOCACK;
3176d3cf9c7dSdf157793 break;
3177d3cf9c7dSdf157793
3178d3cf9c7dSdf157793 case TIOCMGET:
3179d3cf9c7dSdf157793 datamp = allocb(sizeof (int), BPRI_MED);
3180d3cf9c7dSdf157793 if (datamp == NULL) {
3181d3cf9c7dSdf157793 error = EAGAIN;
3182d3cf9c7dSdf157793 break;
3183d3cf9c7dSdf157793 }
3184d3cf9c7dSdf157793
3185d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
3186d3cf9c7dSdf157793 *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET);
3187d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3188d3cf9c7dSdf157793
3189d3cf9c7dSdf157793 if (iocp->ioc_count == TRANSPARENT) {
3190d3cf9c7dSdf157793 mcopyout(mp, NULL, sizeof (int), NULL, datamp);
3191d3cf9c7dSdf157793 } else {
3192d3cf9c7dSdf157793 if (mp->b_cont != NULL)
3193d3cf9c7dSdf157793 freemsg(mp->b_cont);
3194d3cf9c7dSdf157793 mp->b_cont = datamp;
3195d3cf9c7dSdf157793 mp->b_cont->b_wptr += sizeof (int);
3196d3cf9c7dSdf157793 mp->b_datap->db_type = M_IOCACK;
3197d3cf9c7dSdf157793 iocp->ioc_count = sizeof (int);
3198d3cf9c7dSdf157793 }
3199d3cf9c7dSdf157793 break;
3200d3cf9c7dSdf157793
32010280efdcSzk194757 case CONSOPENPOLLEDIO:
32020280efdcSzk194757 /*
32030280efdcSzk194757 * If we are driving a keyboard there is nothing
32040280efdcSzk194757 * upstream to translate the scan codes. Therefore,
32050280efdcSzk194757 * set the error code to ENOTSUP and NAK the request
32060280efdcSzk194757 */
32070280efdcSzk194757 if (asy->asy_device_type == ASY_KEYBOARD) {
32080280efdcSzk194757 error = ENOTSUP;
32090280efdcSzk194757 break;
32100280efdcSzk194757 }
32110280efdcSzk194757
32120280efdcSzk194757 error = miocpullup(mp, sizeof (struct cons_polledio *));
32130280efdcSzk194757 if (error != 0)
32140280efdcSzk194757 break;
32150280efdcSzk194757
32160280efdcSzk194757 /*
32170280efdcSzk194757 * send up a message block containing the
32180280efdcSzk194757 * cons_polledio structure. This provides
32190280efdcSzk194757 * handles to the putchar, getchar, ischar,
32200280efdcSzk194757 * polledio_enter and polledio_exit functions.
32210280efdcSzk194757 */
32220280efdcSzk194757 *(struct cons_polledio **)mp->b_cont->b_rptr =
32230280efdcSzk194757 &asy->polledio;
32240280efdcSzk194757
32250280efdcSzk194757 mp->b_datap->db_type = M_IOCACK;
32260280efdcSzk194757 break;
32270280efdcSzk194757
32280280efdcSzk194757 case CONSCLOSEPOLLEDIO:
32290280efdcSzk194757 /*
32300280efdcSzk194757 * If we are driving a keyboard we never successfully
32310280efdcSzk194757 * called CONSOPENPOLLEDIO so set the error to
32320280efdcSzk194757 * ENOTSUP and NAK the request.
32330280efdcSzk194757 */
32340280efdcSzk194757 if (asy->asy_device_type == ASY_KEYBOARD) {
32350280efdcSzk194757 error = ENOTSUP;
32360280efdcSzk194757 break;
32370280efdcSzk194757 }
32380280efdcSzk194757
32390280efdcSzk194757 mp->b_datap->db_type = M_IOCACK;
32400280efdcSzk194757 iocp->ioc_error = 0;
32410280efdcSzk194757 iocp->ioc_rval = 0;
32420280efdcSzk194757 break;
32430280efdcSzk194757
3244d3cf9c7dSdf157793 default: /* unexpected ioctl type */
3245d3cf9c7dSdf157793 /*
3246d3cf9c7dSdf157793 * If we don't understand it, it's an error. NAK it.
3247d3cf9c7dSdf157793 */
3248d3cf9c7dSdf157793 error = EINVAL;
3249d3cf9c7dSdf157793 break;
3250d3cf9c7dSdf157793 }
3251d3cf9c7dSdf157793 }
3252d3cf9c7dSdf157793 if (error != 0) {
3253d3cf9c7dSdf157793 iocp->ioc_error = error;
3254d3cf9c7dSdf157793 mp->b_datap->db_type = M_IOCNAK;
3255d3cf9c7dSdf157793 }
3256d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3257d3cf9c7dSdf157793 qreply(wq, mp);
3258d3cf9c7dSdf157793 }
3259d3cf9c7dSdf157793
3260*e476cc14SToomas Soome static int
asyrsrv(queue_t * q)3261d3cf9c7dSdf157793 asyrsrv(queue_t *q)
3262d3cf9c7dSdf157793 {
3263d3cf9c7dSdf157793 mblk_t *bp;
3264d3cf9c7dSdf157793 struct asyncline *async;
3265d3cf9c7dSdf157793
3266d3cf9c7dSdf157793 async = (struct asyncline *)q->q_ptr;
3267d3cf9c7dSdf157793
3268d3cf9c7dSdf157793 while (canputnext(q) && (bp = getq(q)))
3269d3cf9c7dSdf157793 putnext(q, bp);
3270d3cf9c7dSdf157793 ASYSETSOFT(async->async_common);
3271d3cf9c7dSdf157793 async->async_polltid = 0;
3272*e476cc14SToomas Soome return (0);
3273d3cf9c7dSdf157793 }
3274d3cf9c7dSdf157793
3275d3cf9c7dSdf157793 /*
3276d3cf9c7dSdf157793 * Put procedure for write queue.
3277d3cf9c7dSdf157793 * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
3278d3cf9c7dSdf157793 * set the flow control character for M_STOPI and M_STARTI messages;
3279d3cf9c7dSdf157793 * queue up M_BREAK, M_DELAY, and M_DATA messages for processing
3280d3cf9c7dSdf157793 * by the start routine, and then call the start routine; discard
3281d3cf9c7dSdf157793 * everything else. Note that this driver does not incorporate any
3282d3cf9c7dSdf157793 * mechanism to negotiate to handle the canonicalization process.
3283d3cf9c7dSdf157793 * It expects that these functions are handled in upper module(s),
3284d3cf9c7dSdf157793 * as we do in ldterm.
3285d3cf9c7dSdf157793 */
3286*e476cc14SToomas Soome static int
asywput(queue_t * q,mblk_t * mp)3287d3cf9c7dSdf157793 asywput(queue_t *q, mblk_t *mp)
3288d3cf9c7dSdf157793 {
3289d3cf9c7dSdf157793 register struct asyncline *async;
3290d3cf9c7dSdf157793 register struct asycom *asy;
3291d3cf9c7dSdf157793 int error;
3292d3cf9c7dSdf157793
3293d3cf9c7dSdf157793 async = (struct asyncline *)q->q_ptr;
3294d3cf9c7dSdf157793 asy = async->async_common;
3295d3cf9c7dSdf157793
3296d3cf9c7dSdf157793 switch (mp->b_datap->db_type) {
3297d3cf9c7dSdf157793
3298d3cf9c7dSdf157793 case M_STOP:
3299d3cf9c7dSdf157793 /*
3300d3cf9c7dSdf157793 * Since we don't do real DMA, we can just let the
3301d3cf9c7dSdf157793 * chip coast to a stop after applying the brakes.
3302d3cf9c7dSdf157793 */
3303d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
3304d3cf9c7dSdf157793 async->async_flags |= ASYNC_STOPPED;
3305d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3306d3cf9c7dSdf157793 freemsg(mp);
3307d3cf9c7dSdf157793 break;
3308d3cf9c7dSdf157793
3309d3cf9c7dSdf157793 case M_START:
3310d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
3311d3cf9c7dSdf157793 if (async->async_flags & ASYNC_STOPPED) {
3312d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_STOPPED;
3313d3cf9c7dSdf157793 /*
3314d3cf9c7dSdf157793 * If an output operation is in progress,
3315d3cf9c7dSdf157793 * resume it. Otherwise, prod the start
3316d3cf9c7dSdf157793 * routine.
3317d3cf9c7dSdf157793 */
3318d3cf9c7dSdf157793 if (async->async_ocnt > 0) {
3319d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
3320d3cf9c7dSdf157793 async_resume(async);
3321d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3322d3cf9c7dSdf157793 } else {
3323d3cf9c7dSdf157793 async_start(async);
3324d3cf9c7dSdf157793 }
3325d3cf9c7dSdf157793 }
3326d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3327d3cf9c7dSdf157793 freemsg(mp);
3328d3cf9c7dSdf157793 break;
3329d3cf9c7dSdf157793
3330d3cf9c7dSdf157793 case M_IOCTL:
3331d3cf9c7dSdf157793 switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
3332d3cf9c7dSdf157793
3333d3cf9c7dSdf157793 case TCSBRK:
3334d3cf9c7dSdf157793 error = miocpullup(mp, sizeof (int));
3335d3cf9c7dSdf157793 if (error != 0) {
3336d3cf9c7dSdf157793 miocnak(q, mp, 0, error);
3337*e476cc14SToomas Soome return (0);
3338d3cf9c7dSdf157793 }
3339d3cf9c7dSdf157793
3340d3cf9c7dSdf157793 if (*(int *)mp->b_cont->b_rptr != 0) {
3341d3cf9c7dSdf157793 #ifdef DEBUG
3342d3cf9c7dSdf157793 if (asydebug & ASY_DEBUG_CLOSE)
3343d3cf9c7dSdf157793 printf("asy%d: flush request.\n",
3344d3cf9c7dSdf157793 UNIT(async->async_dev));
3345d3cf9c7dSdf157793 #endif
3346d3cf9c7dSdf157793 (void) putq(q, mp);
3347d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
3348d3cf9c7dSdf157793 async_nstart(async, 1);
3349d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3350d3cf9c7dSdf157793 break;
3351d3cf9c7dSdf157793 }
3352d3cf9c7dSdf157793 /*FALLTHROUGH*/
3353d3cf9c7dSdf157793 case TCSETSW:
3354d3cf9c7dSdf157793 case TCSETSF:
3355d3cf9c7dSdf157793 case TCSETAW:
3356d3cf9c7dSdf157793 case TCSETAF:
3357d3cf9c7dSdf157793 /*
3358d3cf9c7dSdf157793 * The changes do not take effect until all
3359d3cf9c7dSdf157793 * output queued before them is drained.
3360d3cf9c7dSdf157793 * Put this message on the queue, so that
3361d3cf9c7dSdf157793 * "async_start" will see it when it's done
3362d3cf9c7dSdf157793 * with the output before it. Poke the
3363d3cf9c7dSdf157793 * start routine, just in case.
3364d3cf9c7dSdf157793 */
3365d3cf9c7dSdf157793 (void) putq(q, mp);
3366d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
3367d3cf9c7dSdf157793 async_start(async);
3368d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3369d3cf9c7dSdf157793 break;
3370d3cf9c7dSdf157793
3371d3cf9c7dSdf157793 default:
3372d3cf9c7dSdf157793 /*
3373d3cf9c7dSdf157793 * Do it now.
3374d3cf9c7dSdf157793 */
3375d3cf9c7dSdf157793 async_ioctl(async, q, mp, B_TRUE);
3376d3cf9c7dSdf157793 break;
3377d3cf9c7dSdf157793 }
3378d3cf9c7dSdf157793 break;
3379d3cf9c7dSdf157793
3380d3cf9c7dSdf157793 case M_FLUSH:
3381d3cf9c7dSdf157793 if (*mp->b_rptr & FLUSHW) {
3382d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
3383d3cf9c7dSdf157793
3384d3cf9c7dSdf157793 /*
3385d3cf9c7dSdf157793 * Abort any output in progress.
3386d3cf9c7dSdf157793 */
3387d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
3388d3cf9c7dSdf157793 if (async->async_flags & ASYNC_BUSY) {
3389d3cf9c7dSdf157793 async->async_ocnt = 0;
3390d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_BUSY;
3391d3cf9c7dSdf157793 }
3392d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3393d3cf9c7dSdf157793
3394d3cf9c7dSdf157793 /* Flush FIFO buffers */
3395d3cf9c7dSdf157793 if (asy->asy_use_fifo == FIFO_ON) {
3396d3cf9c7dSdf157793 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH |
3397d3cf9c7dSdf157793 (asy->asy_trig_level & 0xff));
3398d3cf9c7dSdf157793 }
3399d3cf9c7dSdf157793
3400d3cf9c7dSdf157793 /*
3401d3cf9c7dSdf157793 * Flush our write queue.
3402d3cf9c7dSdf157793 */
3403d3cf9c7dSdf157793 flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */
3404d3cf9c7dSdf157793 if (async->async_xmitblk != NULL) {
3405d3cf9c7dSdf157793 freeb(async->async_xmitblk);
3406d3cf9c7dSdf157793 async->async_xmitblk = NULL;
3407d3cf9c7dSdf157793 }
3408d3cf9c7dSdf157793
3409d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3410d3cf9c7dSdf157793 *mp->b_rptr &= ~FLUSHW; /* it has been flushed */
3411d3cf9c7dSdf157793 }
3412d3cf9c7dSdf157793 if (*mp->b_rptr & FLUSHR) {
3413d3cf9c7dSdf157793 /* Flush FIFO buffers */
3414d3cf9c7dSdf157793 if (asy->asy_use_fifo == FIFO_ON) {
3415d3cf9c7dSdf157793 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH |
3416d3cf9c7dSdf157793 (asy->asy_trig_level & 0xff));
3417d3cf9c7dSdf157793 }
3418d3cf9c7dSdf157793 flushq(RD(q), FLUSHDATA);
3419d3cf9c7dSdf157793 qreply(q, mp); /* give the read queues a crack at it */
3420d3cf9c7dSdf157793 } else {
3421d3cf9c7dSdf157793 freemsg(mp);
3422d3cf9c7dSdf157793 }
3423d3cf9c7dSdf157793
3424d3cf9c7dSdf157793 /*
3425d3cf9c7dSdf157793 * We must make sure we process messages that survive the
3426d3cf9c7dSdf157793 * write-side flush. Without this call, the close protocol
3427d3cf9c7dSdf157793 * with ldterm can hang forever. (ldterm will have sent us a
3428d3cf9c7dSdf157793 * TCSBRK ioctl that it expects a response to.)
3429d3cf9c7dSdf157793 */
3430d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
3431d3cf9c7dSdf157793 async_start(async);
3432d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3433d3cf9c7dSdf157793 break;
3434d3cf9c7dSdf157793 case M_BREAK:
3435d3cf9c7dSdf157793 case M_DELAY:
3436d3cf9c7dSdf157793 case M_DATA:
3437d3cf9c7dSdf157793 /*
3438d3cf9c7dSdf157793 * Queue the message up to be transmitted,
3439d3cf9c7dSdf157793 * and poke the start routine.
3440d3cf9c7dSdf157793 */
3441d3cf9c7dSdf157793 (void) putq(q, mp);
3442d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
3443d3cf9c7dSdf157793 async_start(async);
3444d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3445d3cf9c7dSdf157793 break;
3446d3cf9c7dSdf157793
3447d3cf9c7dSdf157793 case M_STOPI:
3448d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
3449d3cf9c7dSdf157793 async->async_flowc = async->async_stopc;
3450d3cf9c7dSdf157793 async_start(async); /* poke the start routine */
3451d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3452d3cf9c7dSdf157793 freemsg(mp);
3453d3cf9c7dSdf157793 break;
3454d3cf9c7dSdf157793
3455d3cf9c7dSdf157793 case M_STARTI:
3456d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
3457d3cf9c7dSdf157793 async->async_flowc = async->async_startc;
3458d3cf9c7dSdf157793 async_start(async); /* poke the start routine */
3459d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3460d3cf9c7dSdf157793 freemsg(mp);
3461d3cf9c7dSdf157793 break;
3462d3cf9c7dSdf157793
3463d3cf9c7dSdf157793 case M_CTL:
3464d3cf9c7dSdf157793 if (MBLKL(mp) >= sizeof (struct iocblk) &&
3465d3cf9c7dSdf157793 ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) {
3466d3cf9c7dSdf157793 ((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX;
3467d3cf9c7dSdf157793 qreply(q, mp);
3468d3cf9c7dSdf157793 } else {
3469d3cf9c7dSdf157793 /*
3470d3cf9c7dSdf157793 * These MC_SERVICE type messages are used by upper
3471d3cf9c7dSdf157793 * modules to tell this driver to send input up
3472d3cf9c7dSdf157793 * immediately, or that it can wait for normal
3473d3cf9c7dSdf157793 * processing that may or may not be done. Sun
3474d3cf9c7dSdf157793 * requires these for the mouse module.
3475d3cf9c7dSdf157793 * (XXX - for x86?)
3476d3cf9c7dSdf157793 */
3477d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
3478d3cf9c7dSdf157793 switch (*mp->b_rptr) {
3479d3cf9c7dSdf157793
3480d3cf9c7dSdf157793 case MC_SERVICEIMM:
3481d3cf9c7dSdf157793 async->async_flags |= ASYNC_SERVICEIMM;
3482d3cf9c7dSdf157793 break;
3483d3cf9c7dSdf157793
3484d3cf9c7dSdf157793 case MC_SERVICEDEF:
3485d3cf9c7dSdf157793 async->async_flags &= ~ASYNC_SERVICEIMM;
3486d3cf9c7dSdf157793 break;
3487d3cf9c7dSdf157793 }
3488d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3489d3cf9c7dSdf157793 freemsg(mp);
3490d3cf9c7dSdf157793 }
3491d3cf9c7dSdf157793 break;
3492d3cf9c7dSdf157793
3493d3cf9c7dSdf157793 case M_IOCDATA:
3494d3cf9c7dSdf157793 async_iocdata(q, mp);
3495d3cf9c7dSdf157793 break;
3496d3cf9c7dSdf157793
3497d3cf9c7dSdf157793 default:
3498d3cf9c7dSdf157793 freemsg(mp);
3499d3cf9c7dSdf157793 break;
3500d3cf9c7dSdf157793 }
3501*e476cc14SToomas Soome return (0);
3502d3cf9c7dSdf157793 }
3503d3cf9c7dSdf157793
3504d3cf9c7dSdf157793 /*
3505d3cf9c7dSdf157793 * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
3506d3cf9c7dSdf157793 * the buffer we need.
3507d3cf9c7dSdf157793 */
3508d3cf9c7dSdf157793 static void
async_reioctl(void * arg)3509d3cf9c7dSdf157793 async_reioctl(void *arg)
3510d3cf9c7dSdf157793 {
3511d3cf9c7dSdf157793 struct asyncline *async = arg;
3512d3cf9c7dSdf157793 struct asycom *asy = async->async_common;
3513d3cf9c7dSdf157793 queue_t *q;
3514d3cf9c7dSdf157793 mblk_t *mp;
3515d3cf9c7dSdf157793
3516d3cf9c7dSdf157793 /*
3517d3cf9c7dSdf157793 * The bufcall is no longer pending.
3518d3cf9c7dSdf157793 */
3519d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
3520d3cf9c7dSdf157793 async->async_wbufcid = 0;
3521d3cf9c7dSdf157793 if ((q = async->async_ttycommon.t_writeq) == NULL) {
3522d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3523d3cf9c7dSdf157793 return;
3524d3cf9c7dSdf157793 }
3525d3cf9c7dSdf157793 if ((mp = async->async_ttycommon.t_iocpending) != NULL) {
3526d3cf9c7dSdf157793 /* not pending any more */
3527d3cf9c7dSdf157793 async->async_ttycommon.t_iocpending = NULL;
3528d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3529d3cf9c7dSdf157793 /* not in STREAMS queue; we no longer know if we're in wput */
3530d3cf9c7dSdf157793 async_ioctl(async, q, mp, B_TRUE);
3531d3cf9c7dSdf157793 } else
3532d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3533d3cf9c7dSdf157793 }
3534d3cf9c7dSdf157793
3535d3cf9c7dSdf157793 static void
async_iocdata(queue_t * q,mblk_t * mp)3536d3cf9c7dSdf157793 async_iocdata(queue_t *q, mblk_t *mp)
3537d3cf9c7dSdf157793 {
3538d3cf9c7dSdf157793 struct asyncline *async = (struct asyncline *)q->q_ptr;
3539d3cf9c7dSdf157793 struct asycom *asy;
3540d3cf9c7dSdf157793 struct copyresp *csp;
3541d3cf9c7dSdf157793
3542d3cf9c7dSdf157793 asy = async->async_common;
3543d3cf9c7dSdf157793 csp = (struct copyresp *)mp->b_rptr;
3544d3cf9c7dSdf157793
3545d3cf9c7dSdf157793 if (csp->cp_rval != 0) {
3546d3cf9c7dSdf157793 freemsg(mp);
3547d3cf9c7dSdf157793 return;
3548d3cf9c7dSdf157793 }
3549d3cf9c7dSdf157793
3550d3cf9c7dSdf157793 mutex_enter(asy->asy_excl);
3551d3cf9c7dSdf157793
3552d3cf9c7dSdf157793 switch (csp->cp_cmd) {
3553d3cf9c7dSdf157793 case TIOCMSET:
3554d3cf9c7dSdf157793 case TIOCMBIS:
3555d3cf9c7dSdf157793 case TIOCMBIC:
3556d3cf9c7dSdf157793 if (mp->b_cont == NULL) {
3557d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3558d3cf9c7dSdf157793 miocnak(q, mp, 0, EINVAL);
3559d3cf9c7dSdf157793 break;
3560d3cf9c7dSdf157793 }
3561d3cf9c7dSdf157793
3562d3cf9c7dSdf157793 mutex_enter(asy->asy_excl_hi);
3563d3cf9c7dSdf157793 (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr),
3564d3cf9c7dSdf157793 csp->cp_cmd);
3565d3cf9c7dSdf157793 mutex_exit(asy->asy_excl_hi);
3566d3cf9c7dSdf157793
3567d3cf9c7dSdf157793 freemsg(mp->b_cont);
3568d3cf9c7dSdf157793 mp->b_cont = NULL;
3569d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3570d3cf9c7dSdf157793 miocack(q, mp, 0, 0);
3571d3cf9c7dSdf157793 break;
3572d3cf9c7dSdf157793
3573d3cf9c7dSdf157793 case TIOCMGET:
3574d3cf9c7dSdf157793 if (mp->b_cont != NULL) {
3575d3cf9c7dSdf157793 freemsg(mp->b_cont);
3576d3cf9c7dSdf157793 mp->b_cont = NULL;
3577d3cf9c7dSdf157793 }
3578d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3579d3cf9c7dSdf157793 miocack(q, mp, 0, 0);
3580d3cf9c7dSdf157793 break;
3581d3cf9c7dSdf157793
3582d3cf9c7dSdf157793 default:
3583d3cf9c7dSdf157793 mutex_exit(asy->asy_excl);
3584d3cf9c7dSdf157793 miocnak(q, mp, 0, EINVAL);
3585d3cf9c7dSdf157793 break;
3586d3cf9c7dSdf157793 }
3587d3cf9c7dSdf157793 }
3588d3cf9c7dSdf157793
3589d3cf9c7dSdf157793
3590d3cf9c7dSdf157793 /*
3591d3cf9c7dSdf157793 * Set or get the modem control status.
3592d3cf9c7dSdf157793 */
3593d3cf9c7dSdf157793 static int
asymctl(struct asycom * asy,int bits,int how)3594d3cf9c7dSdf157793 asymctl(struct asycom *asy, int bits, int how)
3595d3cf9c7dSdf157793 {
3596d3cf9c7dSdf157793 register int mcr_r, msr_r;
3597d3cf9c7dSdf157793
3598d3cf9c7dSdf157793 ASSERT(mutex_owned(asy->asy_excl_hi));
3599d3cf9c7dSdf157793 ASSERT(mutex_owned(asy->asy_excl));
3600d3cf9c7dSdf157793
3601d3cf9c7dSdf157793 /* Read Modem Control Registers */
3602d3cf9c7dSdf157793 mcr_r = INB(MCR);
3603d3cf9c7dSdf157793
3604d3cf9c7dSdf157793 switch (how) {
3605d3cf9c7dSdf157793
3606d3cf9c7dSdf157793 case TIOCMSET:
3607d3cf9c7dSdf157793 mcr_r = bits;
3608d3cf9c7dSdf157793 break;
3609d3cf9c7dSdf157793
3610d3cf9c7dSdf157793 case TIOCMBIS:
3611d3cf9c7dSdf157793 mcr_r |= bits; /* Set bits from input */
3612d3cf9c7dSdf157793 break;
3613d3cf9c7dSdf157793
3614d3cf9c7dSdf157793 case TIOCMBIC:
3615d3cf9c7dSdf157793 mcr_r &= ~bits; /* Set ~bits from input */
3616d3cf9c7dSdf157793 break;
3617d3cf9c7dSdf157793
3618d3cf9c7dSdf157793 case TIOCMGET:
3619d3cf9c7dSdf157793 /* Read Modem Status Registers */
3620d3cf9c7dSdf157793 if (INB(ICR) & MIEN)
3621d3cf9c7dSdf157793 msr_r = asy->asy_cached_msr;
3622d3cf9c7dSdf157793 else
3623d3cf9c7dSdf157793 msr_r = INB(MSR);
3624d3cf9c7dSdf157793 return (asytodm(mcr_r, msr_r));
3625d3cf9c7dSdf157793 }
3626d3cf9c7dSdf157793
3627d3cf9c7dSdf157793 OUTB(MCR, mcr_r);
3628d3cf9c7dSdf157793
3629d3cf9c7dSdf157793 return (mcr_r);
3630d3cf9c7dSdf157793 }
3631d3cf9c7dSdf157793
3632d3cf9c7dSdf157793 static int
asytodm(int mcr_r,int msr_r)3633d3cf9c7dSdf157793 asytodm(int mcr_r, int msr_r)
3634d3cf9c7dSdf157793 {
3635d3cf9c7dSdf157793 register int b = 0;
3636d3cf9c7dSdf157793
3637d3cf9c7dSdf157793
3638d3cf9c7dSdf157793 /* MCR registers */
3639d3cf9c7dSdf157793 if (mcr_r & RTS)
3640d3cf9c7dSdf157793 b |= TIOCM_RTS;
3641d3cf9c7dSdf157793
3642d3cf9c7dSdf157793 if (mcr_r & DTR)
3643d3cf9c7dSdf157793 b |= TIOCM_DTR;
3644d3cf9c7dSdf157793
3645d3cf9c7dSdf157793 /* MSR registers */
3646d3cf9c7dSdf157793 if (msr_r & DCD)
3647d3cf9c7dSdf157793 b |= TIOCM_CAR;
3648d3cf9c7dSdf157793
3649d3cf9c7dSdf157793 if (msr_r & CTS)
3650d3cf9c7dSdf157793 b |= TIOCM_CTS;
3651d3cf9c7dSdf157793
3652d3cf9c7dSdf157793 if (msr_r & DSR)
3653d3cf9c7dSdf157793 b |= TIOCM_DSR;
3654d3cf9c7dSdf157793
3655d3cf9c7dSdf157793 if (msr_r & RI)
3656d3cf9c7dSdf157793 b |= TIOCM_RNG;
3657d3cf9c7dSdf157793
3658d3cf9c7dSdf157793 return (b);
3659d3cf9c7dSdf157793 }
3660d3cf9c7dSdf157793
3661d3cf9c7dSdf157793 static int
dmtoasy(int bits)3662d3cf9c7dSdf157793 dmtoasy(int bits)
3663d3cf9c7dSdf157793 {
3664d3cf9c7dSdf157793 register int b = 0;
3665d3cf9c7dSdf157793
3666d3cf9c7dSdf157793 #ifdef CAN_NOT_SET /* only DTR and RTS can be set */
3667d3cf9c7dSdf157793 if (bits & TIOCM_CAR)
3668d3cf9c7dSdf157793 b |= DCD;
3669d3cf9c7dSdf157793 if (bits & TIOCM_CTS)
3670d3cf9c7dSdf157793 b |= CTS;
3671d3cf9c7dSdf157793 if (bits & TIOCM_DSR)
3672d3cf9c7dSdf157793 b |= DSR;
3673d3cf9c7dSdf157793 if (bits & TIOCM_RNG)
3674d3cf9c7dSdf157793 b |= RI;
3675d3cf9c7dSdf157793 #endif
3676d3cf9c7dSdf157793
3677d3cf9c7dSdf157793 if (bits & TIOCM_RTS)
3678d3cf9c7dSdf157793 b |= RTS;
3679d3cf9c7dSdf157793 if (bits & TIOCM_DTR)
3680d3cf9c7dSdf157793 b |= DTR;
3681d3cf9c7dSdf157793
3682d3cf9c7dSdf157793 return (b);
3683d3cf9c7dSdf157793 }
3684d3cf9c7dSdf157793
3685d3cf9c7dSdf157793 static void
asycheckflowcontrol_hw(struct asycom * asy)3686d3cf9c7dSdf157793 asycheckflowcontrol_hw(struct asycom *asy)
3687d3cf9c7dSdf157793 {
3688d3cf9c7dSdf157793 struct asyncline *async;
3689d3cf9c7dSdf157793 uchar_t mcr, flag;
3690d3cf9c7dSdf157793
3691d3cf9c7dSdf157793 ASSERT(mutex_owned(asy->asy_excl_hi));
3692d3cf9c7dSdf157793
3693d3cf9c7dSdf157793 async = (struct asyncline *)asy->asy_priv;
3694d3cf9c7dSdf157793 ASSERT(async != NULL);
3695d3cf9c7dSdf157793
3696d3cf9c7dSdf157793 if (async->async_ttycommon.t_cflag & CRTSXOFF) {
3697d3cf9c7dSdf157793 mcr = INB(MCR);
3698d3cf9c7dSdf157793 flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS;
3699d3cf9c7dSdf157793 if (((mcr ^ flag) & RTS) != 0) {
3700d3cf9c7dSdf157793 OUTB(MCR, (mcr ^ RTS));
3701d3cf9c7dSdf157793 }
3702d3cf9c7dSdf157793 }
3703d3cf9c7dSdf157793 }
3704d3cf9c7dSdf157793
3705d3cf9c7dSdf157793 static boolean_t
asycheckflowcontrol_sw(struct asycom * asy)3706d3cf9c7dSdf157793 asycheckflowcontrol_sw(struct asycom *asy)
3707d3cf9c7dSdf157793 {
3708d3cf9c7dSdf157793 uchar_t ss;
3709d3cf9c7dSdf157793 struct asyncline *async;
3710d3cf9c7dSdf157793 int rval = B_FALSE;
3711d3cf9c7dSdf157793
3712d3cf9c7dSdf157793 ASSERT(mutex_owned(asy->asy_excl_hi));
3713d3cf9c7dSdf157793
3714d3cf9c7dSdf157793 async = (struct asyncline *)asy->asy_priv;
3715d3cf9c7dSdf157793 ASSERT(async != NULL);
3716d3cf9c7dSdf157793
3717d3cf9c7dSdf157793 if ((ss = async->async_flowc) != '\0' && (INB(LSR) & XHRE)) {
3718d3cf9c7dSdf157793 /*
3719d3cf9c7dSdf157793 * If we get this far, then we know that flowc is non-zero and
3720d3cf9c7dSdf157793 * that there's transmit room available. We've "handled" the
3721d3cf9c7dSdf157793 * request now, so clear it. If the user didn't ask for IXOFF,
3722d3cf9c7dSdf157793 * then don't actually send anything, but wait for the next
3723d3cf9c7dSdf157793 * opportunity.
3724d3cf9c7dSdf157793 */
3725d3cf9c7dSdf157793 async->async_flowc = '\0';
3726d3cf9c7dSdf157793 if (async->async_ttycommon.t_iflag & IXOFF) {
3727d3cf9c7dSdf157793 async->async_flags |= ASYNC_BUSY;
3728d3cf9c7dSdf157793 OUTB(DAT, ss);
3729d3cf9c7dSdf157793 rval = B_TRUE;
3730d3cf9c7dSdf157793 }
3731d3cf9c7dSdf157793 }
3732d3cf9c7dSdf157793
3733d3cf9c7dSdf157793 return (rval);
3734d3cf9c7dSdf157793 }
3735d3cf9c7dSdf157793
3736d3cf9c7dSdf157793 /*
3737d3cf9c7dSdf157793 * Check for abort character sequence
3738d3cf9c7dSdf157793 */
3739d3cf9c7dSdf157793 static boolean_t
abort_charseq_recognize(uchar_t ch)3740d3cf9c7dSdf157793 abort_charseq_recognize(uchar_t ch)
3741d3cf9c7dSdf157793 {
3742d3cf9c7dSdf157793 static int state = 0;
3743d3cf9c7dSdf157793 #define CNTRL(c) ((c)&037)
3744d3cf9c7dSdf157793 static char sequence[] = { '\r', '~', CNTRL('b') };
3745d3cf9c7dSdf157793
3746d3cf9c7dSdf157793 if (ch == sequence[state]) {
3747d3cf9c7dSdf157793 if (++state >= sizeof (sequence)) {
3748d3cf9c7dSdf157793 state = 0;
3749d3cf9c7dSdf157793 return (B_TRUE);
3750d3cf9c7dSdf157793 }
3751d3cf9c7dSdf157793 } else {
3752d3cf9c7dSdf157793 state = (ch == sequence[0]) ? 1 : 0;
3753d3cf9c7dSdf157793 }
3754d3cf9c7dSdf157793 return (B_FALSE);
3755d3cf9c7dSdf157793 }
3756