17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
528cdc3d7Sszhou  * Common Development and Distribution License (the "License").
628cdc3d7Sszhou  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22de81e71eSTim Marsland  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  *
297c478bd9Sstevel@tonic-gate  * USB generic serial driver (GSD)
307c478bd9Sstevel@tonic-gate  *
317c478bd9Sstevel@tonic-gate  */
327c478bd9Sstevel@tonic-gate #include <sys/types.h>
337c478bd9Sstevel@tonic-gate #include <sys/param.h>
347c478bd9Sstevel@tonic-gate #include <sys/stream.h>
357c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
367c478bd9Sstevel@tonic-gate #include <sys/errno.h>
377c478bd9Sstevel@tonic-gate #include <sys/cred.h>
387c478bd9Sstevel@tonic-gate #include <sys/conf.h>
397c478bd9Sstevel@tonic-gate #include <sys/stat.h>
407c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
417c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
427c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
4328cdc3d7Sszhou #include <sys/sunndi.h>
447c478bd9Sstevel@tonic-gate #include <sys/termio.h>
457c478bd9Sstevel@tonic-gate #include <sys/termiox.h>
467c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
477c478bd9Sstevel@tonic-gate #include <sys/stream.h>
487c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
497c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
507c478bd9Sstevel@tonic-gate #include <sys/strtty.h>
517c478bd9Sstevel@tonic-gate #include <sys/policy.h>
5228cdc3d7Sszhou #include <sys/consdev.h>
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h>
557c478bd9Sstevel@tonic-gate #include <sys/usb/clients/usbser/usbser_var.h>
567c478bd9Sstevel@tonic-gate #include <sys/usb/clients/usbser/usbser_dsdi.h>
577c478bd9Sstevel@tonic-gate #include <sys/usb/clients/usbser/usbser_rseq.h>
5828cdc3d7Sszhou #include <sys/usb/usba/genconsole.h>
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate /* autoconfiguration subroutines */
617c478bd9Sstevel@tonic-gate static int	usbser_rseq_do_cb(rseq_t *, int, uintptr_t);
627c478bd9Sstevel@tonic-gate static int	usbser_free_soft_state(usbser_state_t *);
637c478bd9Sstevel@tonic-gate static int	usbser_init_soft_state(usbser_state_t *);
647c478bd9Sstevel@tonic-gate static int	usbser_fini_soft_state(usbser_state_t *);
657c478bd9Sstevel@tonic-gate static int	usbser_attach_dev(usbser_state_t *);
667c478bd9Sstevel@tonic-gate static void	usbser_detach_dev(usbser_state_t *);
677c478bd9Sstevel@tonic-gate static int	usbser_attach_ports(usbser_state_t *);
687c478bd9Sstevel@tonic-gate static int	usbser_create_port_minor_nodes(usbser_state_t *, int);
697c478bd9Sstevel@tonic-gate static void	usbser_detach_ports(usbser_state_t *);
707c478bd9Sstevel@tonic-gate static int	usbser_create_taskq(usbser_state_t *);
717c478bd9Sstevel@tonic-gate static void	usbser_destroy_taskq(usbser_state_t *);
727c478bd9Sstevel@tonic-gate static void	usbser_set_dev_state_init(usbser_state_t *);
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate /* hotplugging and power management */
757c478bd9Sstevel@tonic-gate static int	usbser_disconnect_cb(dev_info_t *);
767c478bd9Sstevel@tonic-gate static int	usbser_reconnect_cb(dev_info_t *);
777c478bd9Sstevel@tonic-gate static void	usbser_disconnect_ports(usbser_state_t *);
787c478bd9Sstevel@tonic-gate static int	usbser_cpr_suspend(dev_info_t *);
797c478bd9Sstevel@tonic-gate static int	usbser_suspend_ports(usbser_state_t *);
807c478bd9Sstevel@tonic-gate static void	usbser_cpr_resume(dev_info_t *);
817c478bd9Sstevel@tonic-gate static int	usbser_restore_device_state(usbser_state_t *);
827c478bd9Sstevel@tonic-gate static void	usbser_restore_ports_state(usbser_state_t *);
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate /* STREAMS subroutines */
857c478bd9Sstevel@tonic-gate static int	usbser_open_setup(queue_t *, usbser_port_t *, int, int,
867c478bd9Sstevel@tonic-gate 		cred_t *);
877c478bd9Sstevel@tonic-gate static int	usbser_open_init(usbser_port_t *, int);
887c478bd9Sstevel@tonic-gate static void	usbser_check_port_props(usbser_port_t *);
897c478bd9Sstevel@tonic-gate static void	usbser_open_fini(usbser_port_t *);
907c478bd9Sstevel@tonic-gate static int	usbser_open_line_setup(usbser_port_t *, int, int);
917c478bd9Sstevel@tonic-gate static int	usbser_open_carrier_check(usbser_port_t *, int, int);
927c478bd9Sstevel@tonic-gate static void	usbser_open_queues_init(usbser_port_t *, queue_t *);
937c478bd9Sstevel@tonic-gate static void	usbser_open_queues_fini(usbser_port_t *);
947c478bd9Sstevel@tonic-gate static void	usbser_close_drain(usbser_port_t *);
957c478bd9Sstevel@tonic-gate static void	usbser_close_cancel_break(usbser_port_t *);
967c478bd9Sstevel@tonic-gate static void	usbser_close_hangup(usbser_port_t *);
977c478bd9Sstevel@tonic-gate static void	usbser_close_cleanup(usbser_port_t *);
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate /* threads */
1007c478bd9Sstevel@tonic-gate static void	usbser_thr_dispatch(usbser_thread_t *);
1017c478bd9Sstevel@tonic-gate static void	usbser_thr_cancel(usbser_thread_t *);
1027c478bd9Sstevel@tonic-gate static void	usbser_thr_wake(usbser_thread_t *);
1037c478bd9Sstevel@tonic-gate static void	usbser_wq_thread(void *);
1047c478bd9Sstevel@tonic-gate static void	usbser_rq_thread(void *);
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate /* DSD callbacks */
1077c478bd9Sstevel@tonic-gate static void	usbser_tx_cb(caddr_t);
1087c478bd9Sstevel@tonic-gate static void	usbser_rx_cb(caddr_t);
1097c478bd9Sstevel@tonic-gate static void	usbser_rx_massage_data(usbser_port_t *, mblk_t *);
1107c478bd9Sstevel@tonic-gate static void	usbser_rx_massage_mbreak(usbser_port_t *, mblk_t *);
1117c478bd9Sstevel@tonic-gate static void	usbser_rx_cb_put(usbser_port_t *, queue_t *, queue_t *,
1127c478bd9Sstevel@tonic-gate 		mblk_t *);
1137c478bd9Sstevel@tonic-gate static void	usbser_status_cb(caddr_t);
1147c478bd9Sstevel@tonic-gate static void	usbser_status_proc_cb(usbser_port_t *);
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate /* serial support */
1177c478bd9Sstevel@tonic-gate static void	usbser_wmsg(usbser_port_t *);
1187c478bd9Sstevel@tonic-gate static int	usbser_data(usbser_port_t *, mblk_t *);
1197c478bd9Sstevel@tonic-gate static int	usbser_ioctl(usbser_port_t *, mblk_t *);
1207c478bd9Sstevel@tonic-gate static void	usbser_iocdata(usbser_port_t *, mblk_t *);
1217c478bd9Sstevel@tonic-gate static void	usbser_stop(usbser_port_t *, mblk_t *);
1227c478bd9Sstevel@tonic-gate static void	usbser_start(usbser_port_t *, mblk_t *);
1237c478bd9Sstevel@tonic-gate static void	usbser_stopi(usbser_port_t *, mblk_t *);
1247c478bd9Sstevel@tonic-gate static void	usbser_starti(usbser_port_t *, mblk_t *);
1257c478bd9Sstevel@tonic-gate static void	usbser_flush(usbser_port_t *, mblk_t *);
1267c478bd9Sstevel@tonic-gate static void	usbser_break(usbser_port_t *, mblk_t *);
1277c478bd9Sstevel@tonic-gate static void	usbser_delay(usbser_port_t *, mblk_t *);
1287c478bd9Sstevel@tonic-gate static void	usbser_restart(void *);
1297c478bd9Sstevel@tonic-gate static int	usbser_port_program(usbser_port_t *);
1307c478bd9Sstevel@tonic-gate static void	usbser_inbound_flow_ctl(usbser_port_t *);
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate /* misc */
1337c478bd9Sstevel@tonic-gate static int	usbser_dev_is_online(usbser_state_t *);
1347c478bd9Sstevel@tonic-gate static void	usbser_serialize_port_act(usbser_port_t *, int);
1357c478bd9Sstevel@tonic-gate static void	usbser_release_port_act(usbser_port_t *, int);
136d235eb05SRichard PALO #ifdef DEBUG
1377c478bd9Sstevel@tonic-gate static char	*usbser_msgtype2str(int);
1387c478bd9Sstevel@tonic-gate static char	*usbser_ioctl2str(int);
139d235eb05SRichard PALO #endif
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate /* USBA events */
1427c478bd9Sstevel@tonic-gate usb_event_t usbser_usb_events = {
1437c478bd9Sstevel@tonic-gate 	usbser_disconnect_cb,	/* disconnect */
1447c478bd9Sstevel@tonic-gate 	usbser_reconnect_cb,	/* reconnect */
1457c478bd9Sstevel@tonic-gate 	NULL,			/* pre-suspend */
1467c478bd9Sstevel@tonic-gate 	NULL,			/* pre-resume */
1477c478bd9Sstevel@tonic-gate };
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate /* debug support */
150112116d8Sfb uint_t	 usbser_errlevel = USB_LOG_L4;
151112116d8Sfb uint_t	 usbser_errmask = DPRINT_MASK_ALL;
152112116d8Sfb uint_t	 usbser_instance_debug = (uint_t)-1;
1537c478bd9Sstevel@tonic-gate 
15428cdc3d7Sszhou /* usb serial console */
15528cdc3d7Sszhou static struct usbser_state *usbser_list;
15628cdc3d7Sszhou static kmutex_t usbser_lock;
15728cdc3d7Sszhou static int usbser_console_abort;
15828cdc3d7Sszhou static usb_console_info_t console_input, console_output;
15928cdc3d7Sszhou static uchar_t *console_input_buf;
16028cdc3d7Sszhou static uchar_t *console_input_start, *console_input_end;
16128cdc3d7Sszhou 
162fef1e07eSsl _NOTE(SCHEME_PROTECTS_DATA("unshared", usbser_console_abort))
163fef1e07eSsl _NOTE(SCHEME_PROTECTS_DATA("unshared", console_input))
164fef1e07eSsl _NOTE(SCHEME_PROTECTS_DATA("unshared", console_output))
165fef1e07eSsl _NOTE(SCHEME_PROTECTS_DATA("unshared", console_input_start))
166fef1e07eSsl _NOTE(SCHEME_PROTECTS_DATA("unshared", console_input_end))
167fef1e07eSsl 
16828cdc3d7Sszhou static void usbser_putchar(cons_polledio_arg_t, uchar_t);
16928cdc3d7Sszhou static int usbser_getchar(cons_polledio_arg_t);
17028cdc3d7Sszhou static boolean_t usbser_ischar(cons_polledio_arg_t);
17128cdc3d7Sszhou static void usbser_polledio_enter(cons_polledio_arg_t);
17228cdc3d7Sszhou static void usbser_polledio_exit(cons_polledio_arg_t);
17328cdc3d7Sszhou static int usbser_polledio_init(usbser_port_t *);
17428cdc3d7Sszhou static void usbser_polledio_fini(usbser_port_t *);
17528cdc3d7Sszhou 
17628cdc3d7Sszhou static struct cons_polledio usbser_polledio = {
17728cdc3d7Sszhou 	CONSPOLLEDIO_V1,
17828cdc3d7Sszhou 	NULL,	/* to be set later */
17928cdc3d7Sszhou 	usbser_putchar,
18028cdc3d7Sszhou 	usbser_getchar,
18128cdc3d7Sszhou 	usbser_ischar,
18228cdc3d7Sszhou 	usbser_polledio_enter,
18328cdc3d7Sszhou 	usbser_polledio_exit
18428cdc3d7Sszhou };
18528cdc3d7Sszhou 
1867c478bd9Sstevel@tonic-gate /* various statistics. TODO: replace with kstats */
1877c478bd9Sstevel@tonic-gate static int usbser_st_tx_data_loss = 0;
1887c478bd9Sstevel@tonic-gate static int usbser_st_rx_data_loss = 0;
1897c478bd9Sstevel@tonic-gate static int usbser_st_put_stopi = 0;
1907c478bd9Sstevel@tonic-gate static int usbser_st_mstop = 0;
1917c478bd9Sstevel@tonic-gate static int usbser_st_mstart = 0;
1927c478bd9Sstevel@tonic-gate static int usbser_st_mstopi = 0;
1937c478bd9Sstevel@tonic-gate static int usbser_st_mstarti = 0;
1947c478bd9Sstevel@tonic-gate static int usbser_st_rsrv = 0;
1957c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("monotonic stats", usbser_st_{
1967c478bd9Sstevel@tonic-gate 	tx_data_loss rx_data_loss put_stopi mstop mstart mstopi mstarti rsrv}))
1977c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unshared", usb_bulk_req_t))
1987c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unshared", usb_intr_req_t))
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate /* taskq parameter */
2017c478bd9Sstevel@tonic-gate extern pri_t minclsyspri;
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate /*
2047c478bd9Sstevel@tonic-gate  * tell warlock not to worry about STREAMS structures
2057c478bd9Sstevel@tonic-gate  */
2067c478bd9Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", iocblk datab msgb queue copyreq))
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate /*
2097c478bd9Sstevel@tonic-gate  * modload support
2107c478bd9Sstevel@tonic-gate  */
2117c478bd9Sstevel@tonic-gate extern struct mod_ops mod_miscops;
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate static struct modlmisc modlmisc = {
2147c478bd9Sstevel@tonic-gate 	&mod_miscops,	/* Type of module */
2152172f489Sgd 	"USB generic serial module"
2167c478bd9Sstevel@tonic-gate };
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
2197c478bd9Sstevel@tonic-gate 	MODREV_1, (void *)&modlmisc, NULL
2207c478bd9Sstevel@tonic-gate };
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate #define	RSEQ(f1, f2) RSEQE(f1, usbser_rseq_do_cb, f2, NULL)
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate /*
2277c478bd9Sstevel@tonic-gate  * loadable module entry points
2287c478bd9Sstevel@tonic-gate  * ----------------------------
2297c478bd9Sstevel@tonic-gate  */
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate int
_init(void)2327c478bd9Sstevel@tonic-gate _init(void)
2337c478bd9Sstevel@tonic-gate {
23428cdc3d7Sszhou 	int err;
23528cdc3d7Sszhou 
23628cdc3d7Sszhou 	mutex_init(&usbser_lock, NULL, MUTEX_DRIVER, (void *)NULL);
237d235eb05SRichard PALO 
238d235eb05SRichard PALO 	if ((err = mod_install(&modlinkage)) != 0)
23928cdc3d7Sszhou 		mutex_destroy(&usbser_lock);
24028cdc3d7Sszhou 
24128cdc3d7Sszhou 	return (err);
2427c478bd9Sstevel@tonic-gate }
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate int
_fini(void)2467c478bd9Sstevel@tonic-gate _fini(void)
2477c478bd9Sstevel@tonic-gate {
24828cdc3d7Sszhou 	int err;
24928cdc3d7Sszhou 
250d235eb05SRichard PALO 	if ((err = mod_remove(&modlinkage)) != 0)
25128cdc3d7Sszhou 		return (err);
25228cdc3d7Sszhou 
25328cdc3d7Sszhou 	mutex_destroy(&usbser_lock);
25428cdc3d7Sszhou 
25528cdc3d7Sszhou 	return (0);
2567c478bd9Sstevel@tonic-gate }
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2607c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2617c478bd9Sstevel@tonic-gate {
2627c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2637c478bd9Sstevel@tonic-gate }
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate /*
2677c478bd9Sstevel@tonic-gate  * soft state size
2687c478bd9Sstevel@tonic-gate  */
2697c478bd9Sstevel@tonic-gate int
usbser_soft_state_size()2707c478bd9Sstevel@tonic-gate usbser_soft_state_size()
2717c478bd9Sstevel@tonic-gate {
2727c478bd9Sstevel@tonic-gate 	return (sizeof (usbser_state_t));
2737c478bd9Sstevel@tonic-gate }
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate /*
2777c478bd9Sstevel@tonic-gate  * autoconfiguration entry points
2787c478bd9Sstevel@tonic-gate  * ------------------------------
2797c478bd9Sstevel@tonic-gate  */
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2827c478bd9Sstevel@tonic-gate int
usbser_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result,void * statep)2837c478bd9Sstevel@tonic-gate usbser_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
284*be128d9bSToomas Soome     void **result, void *statep)
2857c478bd9Sstevel@tonic-gate {
2867c478bd9Sstevel@tonic-gate 	int		instance;
2877c478bd9Sstevel@tonic-gate 	int		ret = DDI_FAILURE;
2887c478bd9Sstevel@tonic-gate 	usbser_state_t	*usbserp;
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 	instance = USBSER_MINOR2INST(getminor((dev_t)arg));
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 	switch (infocmd) {
2937c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
2947c478bd9Sstevel@tonic-gate 		*result = NULL;
2957c478bd9Sstevel@tonic-gate 		usbserp = ddi_get_soft_state(statep, instance);
2967c478bd9Sstevel@tonic-gate 		if (usbserp != NULL) {
2977c478bd9Sstevel@tonic-gate 			*result = usbserp->us_dip;
2987c478bd9Sstevel@tonic-gate 			if (*result != NULL) {
2997c478bd9Sstevel@tonic-gate 				ret = DDI_SUCCESS;
3007c478bd9Sstevel@tonic-gate 			}
3017c478bd9Sstevel@tonic-gate 		}
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 		break;
3047c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
3057c478bd9Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
3067c478bd9Sstevel@tonic-gate 		ret = DDI_SUCCESS;
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 		break;
3097c478bd9Sstevel@tonic-gate 	default:
3107c478bd9Sstevel@tonic-gate 		break;
3117c478bd9Sstevel@tonic-gate 	}
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	return (ret);
3147c478bd9Sstevel@tonic-gate }
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate /*
3177c478bd9Sstevel@tonic-gate  * device attach
3187c478bd9Sstevel@tonic-gate  */
3197c478bd9Sstevel@tonic-gate static rseq_t rseq_att[] = {
3207c478bd9Sstevel@tonic-gate 	RSEQ(NULL,			usbser_free_soft_state),
3217c478bd9Sstevel@tonic-gate 	RSEQ(usbser_init_soft_state,	usbser_fini_soft_state),
3227c478bd9Sstevel@tonic-gate 	RSEQ(usbser_attach_dev,		usbser_detach_dev),
3237c478bd9Sstevel@tonic-gate 	RSEQ(usbser_attach_ports,	usbser_detach_ports),
3247c478bd9Sstevel@tonic-gate 	RSEQ(usbser_create_taskq,	usbser_destroy_taskq),
3257c478bd9Sstevel@tonic-gate 	RSEQ(NULL,			usbser_set_dev_state_init)
3267c478bd9Sstevel@tonic-gate };
3277c478bd9Sstevel@tonic-gate 
32828cdc3d7Sszhou static void
usbser_insert(struct usbser_state * usp)32928cdc3d7Sszhou usbser_insert(struct usbser_state *usp)
33028cdc3d7Sszhou {
33128cdc3d7Sszhou 	struct usbser_state *tmp;
33228cdc3d7Sszhou 
33328cdc3d7Sszhou 	mutex_enter(&usbser_lock);
33428cdc3d7Sszhou 	tmp = usbser_list;
33528cdc3d7Sszhou 	if (tmp == NULL)
33628cdc3d7Sszhou 		usbser_list = usp;
33728cdc3d7Sszhou 	else {
33828cdc3d7Sszhou 		while (tmp->us_next)
33928cdc3d7Sszhou 			tmp = tmp->us_next;
34028cdc3d7Sszhou 		tmp->us_next = usp;
34128cdc3d7Sszhou 	}
34228cdc3d7Sszhou 	mutex_exit(&usbser_lock);
34328cdc3d7Sszhou }
34428cdc3d7Sszhou 
34528cdc3d7Sszhou static void
usbser_remove(struct usbser_state * usp)34628cdc3d7Sszhou usbser_remove(struct usbser_state *usp)
34728cdc3d7Sszhou {
34828cdc3d7Sszhou 	struct usbser_state *tmp, *prev = NULL;
34928cdc3d7Sszhou 
35028cdc3d7Sszhou 	mutex_enter(&usbser_lock);
35128cdc3d7Sszhou 	tmp = usbser_list;
35228cdc3d7Sszhou 	while (tmp != usp) {
35328cdc3d7Sszhou 		prev = tmp;
35428cdc3d7Sszhou 		tmp = tmp->us_next;
35528cdc3d7Sszhou 	}
35628cdc3d7Sszhou 	ASSERT(tmp == usp);	/* must exist, else attach/detach wrong */
35728cdc3d7Sszhou 	if (prev)
35828cdc3d7Sszhou 		prev->us_next = usp->us_next;
35928cdc3d7Sszhou 	else
36028cdc3d7Sszhou 		usbser_list = usp->us_next;
36128cdc3d7Sszhou 	usp->us_next = NULL;
36228cdc3d7Sszhou 	mutex_exit(&usbser_lock);
36328cdc3d7Sszhou }
36428cdc3d7Sszhou 
36528cdc3d7Sszhou /*
36628cdc3d7Sszhou  * Return the first serial device, with dip held. This is called
36728cdc3d7Sszhou  * from the console subsystem to place console on usb serial device.
36828cdc3d7Sszhou  */
36928cdc3d7Sszhou dev_info_t *
usbser_first_device(void)37028cdc3d7Sszhou usbser_first_device(void)
37128cdc3d7Sszhou {
37228cdc3d7Sszhou 	dev_info_t *dip = NULL;
37328cdc3d7Sszhou 
37428cdc3d7Sszhou 	mutex_enter(&usbser_lock);
37528cdc3d7Sszhou 	if (usbser_list) {
37628cdc3d7Sszhou 		dip = usbser_list->us_dip;
37728cdc3d7Sszhou 		ndi_hold_devi(dip);
37828cdc3d7Sszhou 	}
37928cdc3d7Sszhou 	mutex_exit(&usbser_lock);
38028cdc3d7Sszhou 
38128cdc3d7Sszhou 	return (dip);
38228cdc3d7Sszhou }
38328cdc3d7Sszhou 
3847c478bd9Sstevel@tonic-gate int
usbser_attach(dev_info_t * dip,ddi_attach_cmd_t cmd,void * statep,ds_ops_t * ds_ops)3857c478bd9Sstevel@tonic-gate usbser_attach(dev_info_t *dip, ddi_attach_cmd_t cmd,
386*be128d9bSToomas Soome     void *statep, ds_ops_t *ds_ops)
3877c478bd9Sstevel@tonic-gate {
3887c478bd9Sstevel@tonic-gate 	int		instance;
3897c478bd9Sstevel@tonic-gate 	usbser_state_t	*usp;
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 	switch (cmd) {
3947c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 		break;
3977c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
3987c478bd9Sstevel@tonic-gate 		usbser_cpr_resume(dip);
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
4017c478bd9Sstevel@tonic-gate 	default:
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4047c478bd9Sstevel@tonic-gate 	}
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	/* allocate and get soft state */
4077c478bd9Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS) {
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4107c478bd9Sstevel@tonic-gate 	}
4117c478bd9Sstevel@tonic-gate 	if ((usp = ddi_get_soft_state(statep, instance)) == NULL) {
4127c478bd9Sstevel@tonic-gate 		ddi_soft_state_free(statep, instance);
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4157c478bd9Sstevel@tonic-gate 	}
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 	usp->us_statep = statep;
4187c478bd9Sstevel@tonic-gate 	usp->us_dip = dip;
4197c478bd9Sstevel@tonic-gate 	usp->us_instance = instance;
4207c478bd9Sstevel@tonic-gate 	usp->us_ds_ops = ds_ops;
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 	if (rseq_do(rseq_att, NELEM(rseq_att), (uintptr_t)usp, 0) == RSEQ_OK) {
4237c478bd9Sstevel@tonic-gate 		ddi_report_dev(dip);
42428cdc3d7Sszhou 		usbser_insert(usp);
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
4277c478bd9Sstevel@tonic-gate 	} else {
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4307c478bd9Sstevel@tonic-gate 	}
4317c478bd9Sstevel@tonic-gate }
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate /*
4347c478bd9Sstevel@tonic-gate  * device detach
4357c478bd9Sstevel@tonic-gate  */
4367c478bd9Sstevel@tonic-gate int
usbser_detach(dev_info_t * dip,ddi_detach_cmd_t cmd,void * statep)4377c478bd9Sstevel@tonic-gate usbser_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, void *statep)
4387c478bd9Sstevel@tonic-gate {
4397c478bd9Sstevel@tonic-gate 	int		instance = ddi_get_instance(dip);
4407c478bd9Sstevel@tonic-gate 	usbser_state_t	*usp;
4417c478bd9Sstevel@tonic-gate 	int		rval;
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, instance);
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 	switch (cmd) {
4467c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
4477c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_DETACH, usp->us_lh, "usbser_detach");
44828cdc3d7Sszhou 		usbser_remove(usp);
4497c478bd9Sstevel@tonic-gate 		(void) rseq_undo(rseq_att, NELEM(rseq_att), (uintptr_t)usp, 0);
4507c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_DETACH, NULL,
4514ee52f77Slg 		    "usbser_detach.%d: end", instance);
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
4547c478bd9Sstevel@tonic-gate 	case DDI_SUSPEND:
4557c478bd9Sstevel@tonic-gate 		rval = usbser_cpr_suspend(dip);
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 		return ((rval == USB_SUCCESS)? DDI_SUCCESS : DDI_FAILURE);
4587c478bd9Sstevel@tonic-gate 	default:
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4617c478bd9Sstevel@tonic-gate 	}
4627c478bd9Sstevel@tonic-gate }
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate /*
4657c478bd9Sstevel@tonic-gate  * STREAMS entry points
4667c478bd9Sstevel@tonic-gate  * --------------------
4677c478bd9Sstevel@tonic-gate  *
4687c478bd9Sstevel@tonic-gate  *
4697c478bd9Sstevel@tonic-gate  * port open
4707c478bd9Sstevel@tonic-gate  */
4717c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4727c478bd9Sstevel@tonic-gate int
usbser_open(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr,void * statep)4737c478bd9Sstevel@tonic-gate usbser_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr,
474*be128d9bSToomas Soome     void *statep)
4757c478bd9Sstevel@tonic-gate {
4767c478bd9Sstevel@tonic-gate 	usbser_state_t	*usp;
4777c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp;
4787c478bd9Sstevel@tonic-gate 	int		minor = getminor(*dev);
4797c478bd9Sstevel@tonic-gate 	int		instance;
4807c478bd9Sstevel@tonic-gate 	uint_t		port_num;
4817c478bd9Sstevel@tonic-gate 	int		rval;
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate 	instance = USBSER_MINOR2INST(minor);
4847c478bd9Sstevel@tonic-gate 	if (instance < 0) {
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate 		return (ENXIO);
4877c478bd9Sstevel@tonic-gate 	}
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, instance);
4907c478bd9Sstevel@tonic-gate 	if (usp == NULL) {
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 		return (ENXIO);
4937c478bd9Sstevel@tonic-gate 	}
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 	/* don't allow to open disconnected device */
4967c478bd9Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
4977c478bd9Sstevel@tonic-gate 	if (usp->us_dev_state == USB_DEV_DISCONNECTED) {
4987c478bd9Sstevel@tonic-gate 		mutex_exit(&usp->us_mutex);
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate 		return (ENXIO);
5017c478bd9Sstevel@tonic-gate 	}
5027c478bd9Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	/* get port soft state */
5057c478bd9Sstevel@tonic-gate 	port_num = USBSER_MINOR2PORT(minor);
5067c478bd9Sstevel@tonic-gate 	if (port_num >= usp->us_port_cnt) {
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 		return (ENXIO);
5097c478bd9Sstevel@tonic-gate 	}
5107c478bd9Sstevel@tonic-gate 	pp = &usp->us_ports[port_num];
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	/* set up everything for open */
5137c478bd9Sstevel@tonic-gate 	rval = usbser_open_setup(rq, pp, minor, flag, cr);
5147c478bd9Sstevel@tonic-gate 
5157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_OPEN, pp->port_lh, "usbser_open: rval=%d", rval);
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate 	return (rval);
5187c478bd9Sstevel@tonic-gate }
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate /*
5227c478bd9Sstevel@tonic-gate  * port close
5237c478bd9Sstevel@tonic-gate  *
5247c478bd9Sstevel@tonic-gate  * some things driver should do when the last app closes the line:
5257c478bd9Sstevel@tonic-gate  *
5267c478bd9Sstevel@tonic-gate  *	drain data;
5277c478bd9Sstevel@tonic-gate  *	cancel break/delay;
5287c478bd9Sstevel@tonic-gate  *	hangup line (if necessary);
5297c478bd9Sstevel@tonic-gate  *	DSD close;
5307c478bd9Sstevel@tonic-gate  *	cleanup soft state;
5317c478bd9Sstevel@tonic-gate  */
5327c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5337c478bd9Sstevel@tonic-gate int
usbser_close(queue_t * rq,int flag,cred_t * cr)5347c478bd9Sstevel@tonic-gate usbser_close(queue_t *rq, int flag, cred_t *cr)
5357c478bd9Sstevel@tonic-gate {
5367c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)rq->q_ptr;
5377c478bd9Sstevel@tonic-gate 	int		online;
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 	if (pp == NULL) {
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 		return (ENXIO);
5427c478bd9Sstevel@tonic-gate 	}
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	online = usbser_dev_is_online(pp->port_usp);
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	/*
5477c478bd9Sstevel@tonic-gate 	 * in the closing state new activities will not be initiated
5487c478bd9Sstevel@tonic-gate 	 */
5497c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
5507c478bd9Sstevel@tonic-gate 	pp->port_state = USBSER_PORT_CLOSING;
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate 	if (online) {
5537c478bd9Sstevel@tonic-gate 		/* drain the data */
5547c478bd9Sstevel@tonic-gate 		usbser_close_drain(pp);
5557c478bd9Sstevel@tonic-gate 	}
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 	/* stop break/delay */
5587c478bd9Sstevel@tonic-gate 	usbser_close_cancel_break(pp);
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 	if (online) {
5617c478bd9Sstevel@tonic-gate 		/* hangup line */
5627c478bd9Sstevel@tonic-gate 		usbser_close_hangup(pp);
5637c478bd9Sstevel@tonic-gate 	}
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 	/*
5667c478bd9Sstevel@tonic-gate 	 * close DSD, cleanup state and transition to 'closed' state
5677c478bd9Sstevel@tonic-gate 	 */
5687c478bd9Sstevel@tonic-gate 	usbser_close_cleanup(pp);
5697c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_CLOSE, pp->port_lh, "usbser_close: end");
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 	return (0);
5747c478bd9Sstevel@tonic-gate }
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate /*
5787c478bd9Sstevel@tonic-gate  * read side service routine: send as much as possible messages upstream
5797c478bd9Sstevel@tonic-gate  * and if there is still place on the queue, enable receive (if not already)
5807c478bd9Sstevel@tonic-gate  */
5817c478bd9Sstevel@tonic-gate int
usbser_rsrv(queue_t * q)5827c478bd9Sstevel@tonic-gate usbser_rsrv(queue_t *q)
5837c478bd9Sstevel@tonic-gate {
5847c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)q->q_ptr;
5857c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 	usbser_st_rsrv++;
5887c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_RQ, pp->port_lh, "usbser_rsrv");
5897c478bd9Sstevel@tonic-gate 
5907c478bd9Sstevel@tonic-gate 	while (canputnext(q) && (mp = getq(q))) {
5917c478bd9Sstevel@tonic-gate 		putnext(q, mp);
5927c478bd9Sstevel@tonic-gate 	}
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 	if (canputnext(q)) {
5957c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
5967c478bd9Sstevel@tonic-gate 		ASSERT(pp->port_state != USBSER_PORT_CLOSED);
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 		if (USBSER_PORT_ACCESS_OK(pp)) {
5997c478bd9Sstevel@tonic-gate 			usbser_thr_wake(&pp->port_rq_thread);
6007c478bd9Sstevel@tonic-gate 		}
6017c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
6027c478bd9Sstevel@tonic-gate 	}
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 	return (0);
6057c478bd9Sstevel@tonic-gate }
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate /*
6097c478bd9Sstevel@tonic-gate  * wput: put message on the queue and wake wq thread
6107c478bd9Sstevel@tonic-gate  */
6117c478bd9Sstevel@tonic-gate int
usbser_wput(queue_t * q,mblk_t * mp)6127c478bd9Sstevel@tonic-gate usbser_wput(queue_t *q, mblk_t *mp)
6137c478bd9Sstevel@tonic-gate {
6147c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)q->q_ptr;
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wput");
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
6197c478bd9Sstevel@tonic-gate 	ASSERT(pp->port_state != USBSER_PORT_CLOSED);
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	/* ignore new messages if port is already closing */
6227c478bd9Sstevel@tonic-gate 	if (pp->port_state == USBSER_PORT_CLOSING) {
6237c478bd9Sstevel@tonic-gate 		freemsg(mp);
6247c478bd9Sstevel@tonic-gate 	} else if (putq(q, mp)) {
6257c478bd9Sstevel@tonic-gate 		/*
6267c478bd9Sstevel@tonic-gate 		 * this counter represents amount of tx data on the wq.
6277c478bd9Sstevel@tonic-gate 		 * each time the data is passed to DSD for transmission,
6287c478bd9Sstevel@tonic-gate 		 * the counter is decremented accordingly
6297c478bd9Sstevel@tonic-gate 		 */
6307c478bd9Sstevel@tonic-gate 		pp->port_wq_data_cnt += msgdsize(mp);
6317c478bd9Sstevel@tonic-gate 	} else {
6327c478bd9Sstevel@tonic-gate 		usbser_st_tx_data_loss++;
6337c478bd9Sstevel@tonic-gate 	}
6347c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 	return (0);
6377c478bd9Sstevel@tonic-gate }
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate /*
6417c478bd9Sstevel@tonic-gate  * we need wsrv() routine to take advantage of STREAMS flow control:
6427c478bd9Sstevel@tonic-gate  * without it the framework will consider we are always able to process msgs
6437c478bd9Sstevel@tonic-gate  */
6447c478bd9Sstevel@tonic-gate int
usbser_wsrv(queue_t * q)6457c478bd9Sstevel@tonic-gate usbser_wsrv(queue_t *q)
6467c478bd9Sstevel@tonic-gate {
6477c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)q->q_ptr;
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wsrv");
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
6527c478bd9Sstevel@tonic-gate 	ASSERT(pp->port_state != USBSER_PORT_CLOSED);
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 	if (USBSER_PORT_ACCESS_OK(pp)) {
6557c478bd9Sstevel@tonic-gate 		usbser_thr_wake(&pp->port_wq_thread);
6567c478bd9Sstevel@tonic-gate 	}
6577c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 	return (0);
6607c478bd9Sstevel@tonic-gate }
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate /*
6647c478bd9Sstevel@tonic-gate  * power entry point
6657c478bd9Sstevel@tonic-gate  */
6667c478bd9Sstevel@tonic-gate int
usbser_power(dev_info_t * dip,int comp,int level)6677c478bd9Sstevel@tonic-gate usbser_power(dev_info_t *dip, int comp, int level)
6687c478bd9Sstevel@tonic-gate {
6697c478bd9Sstevel@tonic-gate 	void		*statep;
6707c478bd9Sstevel@tonic-gate 	usbser_state_t	*usp;
6717c478bd9Sstevel@tonic-gate 	int		new_state;
6727c478bd9Sstevel@tonic-gate 	int		rval;
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 	statep = ddi_get_driver_private(dip);
6757c478bd9Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
6767c478bd9Sstevel@tonic-gate 
6777c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
678112116d8Sfb 	    "usbser_power: dip=0x%p, comp=%d, level=%d",
679112116d8Sfb 	    (void *)dip, comp, level);
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
6827c478bd9Sstevel@tonic-gate 	new_state = usp->us_dev_state;
6837c478bd9Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate 	/* let DSD do the job */
6867c478bd9Sstevel@tonic-gate 	rval = USBSER_DS_USB_POWER(usp, comp, level, &new_state);
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 	/* stay in sync with DSD */
6897c478bd9Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
6907c478bd9Sstevel@tonic-gate 	usp->us_dev_state = new_state;
6917c478bd9Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate 	return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
6947c478bd9Sstevel@tonic-gate }
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate /*
6987c478bd9Sstevel@tonic-gate  *
6997c478bd9Sstevel@tonic-gate  * configuration entry point subroutines
7007c478bd9Sstevel@tonic-gate  * -------------------------------------
7017c478bd9Sstevel@tonic-gate  *
7027c478bd9Sstevel@tonic-gate  * rseq callback
7037c478bd9Sstevel@tonic-gate  */
7047c478bd9Sstevel@tonic-gate static int
usbser_rseq_do_cb(rseq_t * rseq,int num,uintptr_t arg)7057c478bd9Sstevel@tonic-gate usbser_rseq_do_cb(rseq_t *rseq, int num, uintptr_t arg)
7067c478bd9Sstevel@tonic-gate {
7077c478bd9Sstevel@tonic-gate 	usbser_state_t *usp = (usbser_state_t *)arg;
7087c478bd9Sstevel@tonic-gate 	int	rval = rseq[num].r_do.s_rval;
7097c478bd9Sstevel@tonic-gate 	char	*name = rseq[num].r_do.s_name;
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate 	if (rval != DDI_SUCCESS) {
7127c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_ATTACH, usp->us_lh,
7137c478bd9Sstevel@tonic-gate 		    "do %s failed (%d)", name, rval);
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate 		return (RSEQ_UNDO);
7167c478bd9Sstevel@tonic-gate 	} else {
7177c478bd9Sstevel@tonic-gate 
7187c478bd9Sstevel@tonic-gate 		return (RSEQ_OK);
7197c478bd9Sstevel@tonic-gate 	}
7207c478bd9Sstevel@tonic-gate }
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate 
7237c478bd9Sstevel@tonic-gate /*
7247c478bd9Sstevel@tonic-gate  * free soft state
7257c478bd9Sstevel@tonic-gate  */
7267c478bd9Sstevel@tonic-gate static int
usbser_free_soft_state(usbser_state_t * usp)7277c478bd9Sstevel@tonic-gate usbser_free_soft_state(usbser_state_t *usp)
7287c478bd9Sstevel@tonic-gate {
7297c478bd9Sstevel@tonic-gate 	ddi_soft_state_free(usp->us_statep, usp->us_instance);
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
7327c478bd9Sstevel@tonic-gate }
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate /*
7357c478bd9Sstevel@tonic-gate  * init instance soft state
7367c478bd9Sstevel@tonic-gate  */
7377c478bd9Sstevel@tonic-gate static int
usbser_init_soft_state(usbser_state_t * usp)7387c478bd9Sstevel@tonic-gate usbser_init_soft_state(usbser_state_t *usp)
7397c478bd9Sstevel@tonic-gate {
7407c478bd9Sstevel@tonic-gate 	usp->us_lh = usb_alloc_log_hdl(usp->us_dip, "usbs[*].",
7414ee52f77Slg 	    &usbser_errlevel, &usbser_errmask, &usbser_instance_debug,
7424ee52f77Slg 	    0);
7437c478bd9Sstevel@tonic-gate 	mutex_init(&usp->us_mutex, NULL, MUTEX_DRIVER, (void *)NULL);
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate 	/* save state pointer for use in event callbacks */
7467c478bd9Sstevel@tonic-gate 	ddi_set_driver_private(usp->us_dip, usp->us_statep);
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 	usp->us_dev_state = USBSER_DEV_INIT;
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
7517c478bd9Sstevel@tonic-gate }
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate /*
7547c478bd9Sstevel@tonic-gate  * fini instance soft state
7557c478bd9Sstevel@tonic-gate  */
7567c478bd9Sstevel@tonic-gate static int
usbser_fini_soft_state(usbser_state_t * usp)7577c478bd9Sstevel@tonic-gate usbser_fini_soft_state(usbser_state_t *usp)
7587c478bd9Sstevel@tonic-gate {
7597c478bd9Sstevel@tonic-gate 	usb_free_log_hdl(usp->us_lh);
7607c478bd9Sstevel@tonic-gate 	mutex_destroy(&usp->us_mutex);
7617c478bd9Sstevel@tonic-gate 	ddi_set_driver_private(usp->us_dip, NULL);
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
7647c478bd9Sstevel@tonic-gate }
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate /*
7677c478bd9Sstevel@tonic-gate  * attach entire device
7687c478bd9Sstevel@tonic-gate  */
7697c478bd9Sstevel@tonic-gate static int
usbser_attach_dev(usbser_state_t * usp)7707c478bd9Sstevel@tonic-gate usbser_attach_dev(usbser_state_t *usp)
7717c478bd9Sstevel@tonic-gate {
7727c478bd9Sstevel@tonic-gate 	ds_attach_info_t ai;
7737c478bd9Sstevel@tonic-gate 	int		rval;
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 	usp->us_dev_state = USB_DEV_ONLINE;
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate 	ai.ai_dip = usp->us_dip;
7787c478bd9Sstevel@tonic-gate 	ai.ai_usb_events = &usbser_usb_events;
7797c478bd9Sstevel@tonic-gate 	ai.ai_hdl = &usp->us_ds_hdl;
7807c478bd9Sstevel@tonic-gate 	ai.ai_port_cnt = &usp->us_port_cnt;
7817c478bd9Sstevel@tonic-gate 
7827c478bd9Sstevel@tonic-gate 	rval = USBSER_DS_ATTACH(usp, &ai);
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate 	if ((rval != USB_SUCCESS) || (usp->us_ds_hdl == NULL) ||
7857c478bd9Sstevel@tonic-gate 	    (usp->us_port_cnt == 0)) {
7867c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_ATTACH, usp->us_lh, "usbser_attach_dev: "
7877c478bd9Sstevel@tonic-gate 		    "failed %d %p %d", rval, usp->us_ds_hdl, usp->us_port_cnt);
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
7907c478bd9Sstevel@tonic-gate 	}
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_ATTACH, usp->us_lh,
7937c478bd9Sstevel@tonic-gate 	    "usbser_attach_dev: port_cnt = %d", usp->us_port_cnt);
7947c478bd9Sstevel@tonic-gate 
7957c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
7967c478bd9Sstevel@tonic-gate }
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate /*
8007c478bd9Sstevel@tonic-gate  * detach entire device
8017c478bd9Sstevel@tonic-gate  */
8027c478bd9Sstevel@tonic-gate static void
usbser_detach_dev(usbser_state_t * usp)8037c478bd9Sstevel@tonic-gate usbser_detach_dev(usbser_state_t *usp)
8047c478bd9Sstevel@tonic-gate {
8057c478bd9Sstevel@tonic-gate 	USBSER_DS_DETACH(usp);
8067c478bd9Sstevel@tonic-gate }
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate 
8097c478bd9Sstevel@tonic-gate /*
8107c478bd9Sstevel@tonic-gate  * attach each individual port
8117c478bd9Sstevel@tonic-gate  */
8127c478bd9Sstevel@tonic-gate static int
usbser_attach_ports(usbser_state_t * usp)8137c478bd9Sstevel@tonic-gate usbser_attach_ports(usbser_state_t *usp)
8147c478bd9Sstevel@tonic-gate {
8157c478bd9Sstevel@tonic-gate 	int		i;
8167c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp;
8177c478bd9Sstevel@tonic-gate 	ds_cb_t		ds_cb;
8187c478bd9Sstevel@tonic-gate 
8197c478bd9Sstevel@tonic-gate 	/*
8207c478bd9Sstevel@tonic-gate 	 * allocate port array
8217c478bd9Sstevel@tonic-gate 	 */
8227c478bd9Sstevel@tonic-gate 	usp->us_ports = kmem_zalloc(usp->us_port_cnt *
8234ee52f77Slg 	    sizeof (usbser_port_t), KM_SLEEP);
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate 	/* callback handlers */
8267c478bd9Sstevel@tonic-gate 	ds_cb.cb_tx = usbser_tx_cb;
8277c478bd9Sstevel@tonic-gate 	ds_cb.cb_rx = usbser_rx_cb;
8287c478bd9Sstevel@tonic-gate 	ds_cb.cb_status = usbser_status_cb;
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 	/*
8317c478bd9Sstevel@tonic-gate 	 * initialize each port
8327c478bd9Sstevel@tonic-gate 	 */
8337c478bd9Sstevel@tonic-gate 	for (i = 0; i < usp->us_port_cnt; i++) {
8347c478bd9Sstevel@tonic-gate 		pp = &usp->us_ports[i];
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate 		/*
8377c478bd9Sstevel@tonic-gate 		 * initialize data
8387c478bd9Sstevel@tonic-gate 		 */
8397c478bd9Sstevel@tonic-gate 		pp->port_num = i;
8407c478bd9Sstevel@tonic-gate 		pp->port_usp = usp;
8417c478bd9Sstevel@tonic-gate 		pp->port_ds_ops = usp->us_ds_ops;
8427c478bd9Sstevel@tonic-gate 		pp->port_ds_hdl = usp->us_ds_hdl;
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 		/* allocate log handle */
8457c478bd9Sstevel@tonic-gate 		(void) sprintf(pp->port_lh_name, "usbs[%d].", i);
8467c478bd9Sstevel@tonic-gate 		pp->port_lh = usb_alloc_log_hdl(usp->us_dip,
8474ee52f77Slg 		    pp->port_lh_name, &usbser_errlevel, &usbser_errmask,
8484ee52f77Slg 		    &usbser_instance_debug, 0);
8497c478bd9Sstevel@tonic-gate 
8507c478bd9Sstevel@tonic-gate 		mutex_init(&pp->port_mutex, NULL, MUTEX_DRIVER, (void *)NULL);
8517c478bd9Sstevel@tonic-gate 		cv_init(&pp->port_state_cv, NULL, CV_DEFAULT, NULL);
8527c478bd9Sstevel@tonic-gate 		cv_init(&pp->port_act_cv, NULL, CV_DEFAULT, NULL);
8537c478bd9Sstevel@tonic-gate 		cv_init(&pp->port_car_cv, NULL, CV_DEFAULT, NULL);
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 		/*
8567c478bd9Sstevel@tonic-gate 		 * init threads
8577c478bd9Sstevel@tonic-gate 		 */
8587c478bd9Sstevel@tonic-gate 		pp->port_wq_thread.thr_port = pp;
8597c478bd9Sstevel@tonic-gate 		pp->port_wq_thread.thr_func = usbser_wq_thread;
8607c478bd9Sstevel@tonic-gate 		pp->port_wq_thread.thr_arg = (void *)&pp->port_wq_thread;
8617c478bd9Sstevel@tonic-gate 		cv_init(&pp->port_wq_thread.thr_cv, NULL, CV_DEFAULT, NULL);
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate 		pp->port_rq_thread.thr_port = pp;
8647c478bd9Sstevel@tonic-gate 		pp->port_rq_thread.thr_func = usbser_rq_thread;
8657c478bd9Sstevel@tonic-gate 		pp->port_rq_thread.thr_arg = (void *)&pp->port_rq_thread;
8667c478bd9Sstevel@tonic-gate 		cv_init(&pp->port_rq_thread.thr_cv, NULL, CV_DEFAULT, NULL);
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 		/*
8697c478bd9Sstevel@tonic-gate 		 * register callbacks
8707c478bd9Sstevel@tonic-gate 		 */
8717c478bd9Sstevel@tonic-gate 		ds_cb.cb_arg = (caddr_t)pp;
8727c478bd9Sstevel@tonic-gate 		USBSER_DS_REGISTER_CB(usp, i, &ds_cb);
8737c478bd9Sstevel@tonic-gate 
8747c478bd9Sstevel@tonic-gate 		pp->port_state = USBSER_PORT_CLOSED;
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 		if (usbser_create_port_minor_nodes(usp, i) != USB_SUCCESS) {
8777c478bd9Sstevel@tonic-gate 			usbser_detach_ports(usp);
8787c478bd9Sstevel@tonic-gate 
8797c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
8807c478bd9Sstevel@tonic-gate 		}
8817c478bd9Sstevel@tonic-gate 	}
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
8847c478bd9Sstevel@tonic-gate }
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate /*
8887c478bd9Sstevel@tonic-gate  * create a pair of minor nodes for the port
8897c478bd9Sstevel@tonic-gate  */
8907c478bd9Sstevel@tonic-gate static int
usbser_create_port_minor_nodes(usbser_state_t * usp,int port_num)8917c478bd9Sstevel@tonic-gate usbser_create_port_minor_nodes(usbser_state_t *usp, int port_num)
8927c478bd9Sstevel@tonic-gate {
8937c478bd9Sstevel@tonic-gate 	int	instance = usp->us_instance;
8947c478bd9Sstevel@tonic-gate 	minor_t	minor;
8957c478bd9Sstevel@tonic-gate 	char	name[16];
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate 	/*
8987c478bd9Sstevel@tonic-gate 	 * tty node
8997c478bd9Sstevel@tonic-gate 	 */
9007c478bd9Sstevel@tonic-gate 	(void) sprintf(name, "%d", port_num);
9017c478bd9Sstevel@tonic-gate 	minor = USBSER_MAKEMINOR(instance, port_num, 0);
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(usp->us_dip, name,
904*be128d9bSToomas Soome 	    S_IFCHR, minor, DDI_NT_SERIAL, 0) != DDI_SUCCESS) {
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
9077c478bd9Sstevel@tonic-gate 	}
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 	/*
9107c478bd9Sstevel@tonic-gate 	 * dial-out node
9117c478bd9Sstevel@tonic-gate 	 */
9127c478bd9Sstevel@tonic-gate 	(void) sprintf(name, "%d,cu", port_num);
9137c478bd9Sstevel@tonic-gate 	minor = USBSER_MAKEMINOR(instance, port_num, OUTLINE);
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(usp->us_dip, name,
916*be128d9bSToomas Soome 	    S_IFCHR, minor, DDI_NT_SERIAL_DO, 0) != DDI_SUCCESS) {
9177c478bd9Sstevel@tonic-gate 
9187c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
9197c478bd9Sstevel@tonic-gate 	}
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
9227c478bd9Sstevel@tonic-gate }
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate 
9257c478bd9Sstevel@tonic-gate /*
9267c478bd9Sstevel@tonic-gate  * detach each port individually
9277c478bd9Sstevel@tonic-gate  */
9287c478bd9Sstevel@tonic-gate static void
usbser_detach_ports(usbser_state_t * usp)9297c478bd9Sstevel@tonic-gate usbser_detach_ports(usbser_state_t *usp)
9307c478bd9Sstevel@tonic-gate {
9317c478bd9Sstevel@tonic-gate 	int		i;
9327c478bd9Sstevel@tonic-gate 	int		sz;
9337c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp;
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 	/*
9367c478bd9Sstevel@tonic-gate 	 * remove all minor nodes
9377c478bd9Sstevel@tonic-gate 	 */
9387c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(usp->us_dip, NULL);
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate 	for (i = 0; i < usp->us_port_cnt; i++) {
9417c478bd9Sstevel@tonic-gate 		pp = &usp->us_ports[i];
9427c478bd9Sstevel@tonic-gate 
9437c478bd9Sstevel@tonic-gate 		if (pp->port_state != USBSER_PORT_CLOSED) {
9447c478bd9Sstevel@tonic-gate 			ASSERT(pp->port_state == USBSER_PORT_NOT_INIT);
9457c478bd9Sstevel@tonic-gate 
9467c478bd9Sstevel@tonic-gate 			continue;
9477c478bd9Sstevel@tonic-gate 		}
9487c478bd9Sstevel@tonic-gate 
9497c478bd9Sstevel@tonic-gate 		USBSER_DS_UNREGISTER_CB(usp, i);
9507c478bd9Sstevel@tonic-gate 
9517c478bd9Sstevel@tonic-gate 		mutex_destroy(&pp->port_mutex);
9527c478bd9Sstevel@tonic-gate 		cv_destroy(&pp->port_state_cv);
9537c478bd9Sstevel@tonic-gate 		cv_destroy(&pp->port_act_cv);
9547c478bd9Sstevel@tonic-gate 		cv_destroy(&pp->port_car_cv);
9557c478bd9Sstevel@tonic-gate 
9567c478bd9Sstevel@tonic-gate 		cv_destroy(&pp->port_wq_thread.thr_cv);
9577c478bd9Sstevel@tonic-gate 		cv_destroy(&pp->port_rq_thread.thr_cv);
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate 		usb_free_log_hdl(pp->port_lh);
9607c478bd9Sstevel@tonic-gate 	}
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate 	/*
9637c478bd9Sstevel@tonic-gate 	 * free memory
9647c478bd9Sstevel@tonic-gate 	 */
9657c478bd9Sstevel@tonic-gate 	sz = usp->us_port_cnt * sizeof (usbser_port_t);
9667c478bd9Sstevel@tonic-gate 	kmem_free(usp->us_ports, sz);
9677c478bd9Sstevel@tonic-gate 	usp->us_ports = NULL;
9687c478bd9Sstevel@tonic-gate }
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 
9717c478bd9Sstevel@tonic-gate /*
9727c478bd9Sstevel@tonic-gate  * create a taskq with two threads per port (read and write sides)
9737c478bd9Sstevel@tonic-gate  */
9747c478bd9Sstevel@tonic-gate static int
usbser_create_taskq(usbser_state_t * usp)9757c478bd9Sstevel@tonic-gate usbser_create_taskq(usbser_state_t *usp)
9767c478bd9Sstevel@tonic-gate {
9777c478bd9Sstevel@tonic-gate 	int	nthr = usp->us_port_cnt * 2;
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 	usp->us_taskq = ddi_taskq_create(usp->us_dip, "usbser_taskq",
9807c478bd9Sstevel@tonic-gate 	    nthr, TASKQ_DEFAULTPRI, 0);
9817c478bd9Sstevel@tonic-gate 
9827c478bd9Sstevel@tonic-gate 	return ((usp->us_taskq == NULL) ? DDI_FAILURE : DDI_SUCCESS);
9837c478bd9Sstevel@tonic-gate }
9847c478bd9Sstevel@tonic-gate 
9857c478bd9Sstevel@tonic-gate 
9867c478bd9Sstevel@tonic-gate static void
usbser_destroy_taskq(usbser_state_t * usp)9877c478bd9Sstevel@tonic-gate usbser_destroy_taskq(usbser_state_t *usp)
9887c478bd9Sstevel@tonic-gate {
9897c478bd9Sstevel@tonic-gate 	ddi_taskq_destroy(usp->us_taskq);
9907c478bd9Sstevel@tonic-gate }
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 
9937c478bd9Sstevel@tonic-gate static void
usbser_set_dev_state_init(usbser_state_t * usp)9947c478bd9Sstevel@tonic-gate usbser_set_dev_state_init(usbser_state_t *usp)
9957c478bd9Sstevel@tonic-gate {
9967c478bd9Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
9977c478bd9Sstevel@tonic-gate 	usp->us_dev_state = USBSER_DEV_INIT;
9987c478bd9Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
9997c478bd9Sstevel@tonic-gate }
10007c478bd9Sstevel@tonic-gate 
10017c478bd9Sstevel@tonic-gate /*
10027c478bd9Sstevel@tonic-gate  * hotplugging and power management
10037c478bd9Sstevel@tonic-gate  * ---------------------------------
10047c478bd9Sstevel@tonic-gate  *
10057c478bd9Sstevel@tonic-gate  * disconnect event callback
10067c478bd9Sstevel@tonic-gate  */
10077c478bd9Sstevel@tonic-gate /*ARGSUSED*/
10087c478bd9Sstevel@tonic-gate static int
usbser_disconnect_cb(dev_info_t * dip)10097c478bd9Sstevel@tonic-gate usbser_disconnect_cb(dev_info_t *dip)
10107c478bd9Sstevel@tonic-gate {
10117c478bd9Sstevel@tonic-gate 	void		*statep;
10127c478bd9Sstevel@tonic-gate 	usbser_state_t	*usp;
10137c478bd9Sstevel@tonic-gate 
10147c478bd9Sstevel@tonic-gate 	statep = ddi_get_driver_private(dip);
10157c478bd9Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
10167c478bd9Sstevel@tonic-gate 
10177c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
1018112116d8Sfb 	    "usbser_disconnect_cb: dip=%p", (void *)dip);
10197c478bd9Sstevel@tonic-gate 
10207c478bd9Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
10214ee52f77Slg 	switch (usp->us_dev_state) {
10224ee52f77Slg 	case USB_DEV_ONLINE:
10234ee52f77Slg 	case USB_DEV_PWRED_DOWN:
10244ee52f77Slg 		/* prevent further activity */
10254ee52f77Slg 		usp->us_dev_state = USB_DEV_DISCONNECTED;
10267c478bd9Sstevel@tonic-gate 		mutex_exit(&usp->us_mutex);
10277c478bd9Sstevel@tonic-gate 
10284ee52f77Slg 		/* see if any of the ports are open and do necessary handling */
10294ee52f77Slg 		usbser_disconnect_ports(usp);
10307c478bd9Sstevel@tonic-gate 
10314ee52f77Slg 		/* call DSD to do any necessary work */
10324ee52f77Slg 		if (USBSER_DS_DISCONNECT(usp) != USB_DEV_DISCONNECTED) {
10334ee52f77Slg 			USB_DPRINTF_L2(DPRINT_EVENTS, usp->us_lh,
10344ee52f77Slg 			    "usbser_disconnect_cb: ds_disconnect failed");
10354ee52f77Slg 		}
10367c478bd9Sstevel@tonic-gate 
10374ee52f77Slg 		break;
10384ee52f77Slg 	case USB_DEV_SUSPENDED:
10394ee52f77Slg 		/* we remain suspended */
10404ee52f77Slg 	default:
10414ee52f77Slg 		mutex_exit(&usp->us_mutex);
10424ee52f77Slg 
10434ee52f77Slg 		break;
10447c478bd9Sstevel@tonic-gate 	}
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
10477c478bd9Sstevel@tonic-gate }
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate /*
10517c478bd9Sstevel@tonic-gate  * reconnect event callback
10527c478bd9Sstevel@tonic-gate  */
10537c478bd9Sstevel@tonic-gate /*ARGSUSED*/
10547c478bd9Sstevel@tonic-gate static int
usbser_reconnect_cb(dev_info_t * dip)10557c478bd9Sstevel@tonic-gate usbser_reconnect_cb(dev_info_t *dip)
10567c478bd9Sstevel@tonic-gate {
10577c478bd9Sstevel@tonic-gate 	void		*statep;
10587c478bd9Sstevel@tonic-gate 	usbser_state_t	*usp;
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 	statep = ddi_get_driver_private(dip);
10617c478bd9Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
1064112116d8Sfb 	    "usbser_reconnect_cb: dip=%p", (void *)dip);
10657c478bd9Sstevel@tonic-gate 
10667c478bd9Sstevel@tonic-gate 	(void) usbser_restore_device_state(usp);
10677c478bd9Sstevel@tonic-gate 
10687c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
10697c478bd9Sstevel@tonic-gate }
10707c478bd9Sstevel@tonic-gate 
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate /*
10737c478bd9Sstevel@tonic-gate  * if any of the ports is open during disconnect,
10747c478bd9Sstevel@tonic-gate  * send M_HANGUP message upstream and log a warning
10757c478bd9Sstevel@tonic-gate  */
10767c478bd9Sstevel@tonic-gate static void
usbser_disconnect_ports(usbser_state_t * usp)10777c478bd9Sstevel@tonic-gate usbser_disconnect_ports(usbser_state_t *usp)
10787c478bd9Sstevel@tonic-gate {
10797c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp;
10807c478bd9Sstevel@tonic-gate 	queue_t		*rq;
10817c478bd9Sstevel@tonic-gate 	int		complain = 0;
10827c478bd9Sstevel@tonic-gate 	int		hangup = 0;
10837c478bd9Sstevel@tonic-gate 	timeout_id_t	delay_id = 0;
10847c478bd9Sstevel@tonic-gate 	int		i;
10857c478bd9Sstevel@tonic-gate 
10867c478bd9Sstevel@tonic-gate 	if (usp->us_ports == NULL) {
10877c478bd9Sstevel@tonic-gate 		return;
10887c478bd9Sstevel@tonic-gate 	}
10897c478bd9Sstevel@tonic-gate 
10907c478bd9Sstevel@tonic-gate 	for (i = 0; i < usp->us_port_cnt; i++) {
10917c478bd9Sstevel@tonic-gate 		pp = &usp->us_ports[i];
10927c478bd9Sstevel@tonic-gate 
10937c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
10947c478bd9Sstevel@tonic-gate 		if (pp->port_state == USBSER_PORT_OPEN ||
10957c478bd9Sstevel@tonic-gate 		    USBSER_IS_OPENING(pp) ||
10967c478bd9Sstevel@tonic-gate 		    pp->port_state == USBSER_PORT_CLOSING) {
10977c478bd9Sstevel@tonic-gate 			complain = 1;
10987c478bd9Sstevel@tonic-gate 		}
10997c478bd9Sstevel@tonic-gate 
11007c478bd9Sstevel@tonic-gate 		if (pp->port_state == USBSER_PORT_OPEN) {
11017c478bd9Sstevel@tonic-gate 			rq = pp->port_ttycommon.t_readq;
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 			/*
11047c478bd9Sstevel@tonic-gate 			 * hangup the stream; will send actual
11057c478bd9Sstevel@tonic-gate 			 * M_HANGUP message after releasing mutex
11067c478bd9Sstevel@tonic-gate 			 */
11077c478bd9Sstevel@tonic-gate 			pp->port_flags |= USBSER_FL_HUNGUP;
11087c478bd9Sstevel@tonic-gate 			hangup = 1;
11097c478bd9Sstevel@tonic-gate 
11107c478bd9Sstevel@tonic-gate 			/*
11117c478bd9Sstevel@tonic-gate 			 * cancel all activities
11127c478bd9Sstevel@tonic-gate 			 */
11137c478bd9Sstevel@tonic-gate 			usbser_release_port_act(pp, USBSER_ACT_ALL);
11147c478bd9Sstevel@tonic-gate 
11157c478bd9Sstevel@tonic-gate 			delay_id = pp->port_delay_id;
11167c478bd9Sstevel@tonic-gate 			pp->port_delay_id = 0;
11177c478bd9Sstevel@tonic-gate 
11187c478bd9Sstevel@tonic-gate 			/* mark disconnected */
11197c478bd9Sstevel@tonic-gate 			pp->port_state = USBSER_PORT_DISCONNECTED;
11207c478bd9Sstevel@tonic-gate 			cv_broadcast(&pp->port_state_cv);
11217c478bd9Sstevel@tonic-gate 		}
11227c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
11237c478bd9Sstevel@tonic-gate 
11247c478bd9Sstevel@tonic-gate 		if (hangup) {
11257c478bd9Sstevel@tonic-gate 			(void) putnextctl(rq, M_HANGUP);
11267c478bd9Sstevel@tonic-gate 			hangup = 0;
11277c478bd9Sstevel@tonic-gate 		}
11287c478bd9Sstevel@tonic-gate 
11297c478bd9Sstevel@tonic-gate 		/*
11307c478bd9Sstevel@tonic-gate 		 * we couldn't untimeout while holding the mutex - do it now
11317c478bd9Sstevel@tonic-gate 		 */
11327c478bd9Sstevel@tonic-gate 		if (delay_id) {
11337c478bd9Sstevel@tonic-gate 			(void) untimeout(delay_id);
11347c478bd9Sstevel@tonic-gate 			delay_id = 0;
11357c478bd9Sstevel@tonic-gate 		}
11367c478bd9Sstevel@tonic-gate 	}
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate 	/*
11397c478bd9Sstevel@tonic-gate 	 * complain about disconnecting device while open
11407c478bd9Sstevel@tonic-gate 	 */
11417c478bd9Sstevel@tonic-gate 	if (complain) {
11427c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L0(DPRINT_EVENTS, usp->us_lh, "device was "
11437c478bd9Sstevel@tonic-gate 		    "disconnected while open. Data may have been lost");
11447c478bd9Sstevel@tonic-gate 	}
11457c478bd9Sstevel@tonic-gate }
11467c478bd9Sstevel@tonic-gate 
11477c478bd9Sstevel@tonic-gate 
11487c478bd9Sstevel@tonic-gate /*
11497c478bd9Sstevel@tonic-gate  * do CPR suspend
11507c478bd9Sstevel@tonic-gate  *
11517c478bd9Sstevel@tonic-gate  * We use a trivial CPR strategy - fail if any of the device's ports are open.
11527c478bd9Sstevel@tonic-gate  * The problem with more sophisticated strategies is that each open port uses
11537c478bd9Sstevel@tonic-gate  * two threads that sit in the loop until the port is closed, while CPR has to
11547c478bd9Sstevel@tonic-gate  * stop all kernel threads to succeed. Stopping port threads is a rather
11557c478bd9Sstevel@tonic-gate  * intrusive and delicate procedure; I leave it as an RFE for now.
11567c478bd9Sstevel@tonic-gate  *
11577c478bd9Sstevel@tonic-gate  */
11587c478bd9Sstevel@tonic-gate static int
usbser_cpr_suspend(dev_info_t * dip)11597c478bd9Sstevel@tonic-gate usbser_cpr_suspend(dev_info_t *dip)
11607c478bd9Sstevel@tonic-gate {
11617c478bd9Sstevel@tonic-gate 	void		*statep;
11627c478bd9Sstevel@tonic-gate 	usbser_state_t	*usp;
11637c478bd9Sstevel@tonic-gate 	int		new_state;
11647c478bd9Sstevel@tonic-gate 	int		rval;
11657c478bd9Sstevel@tonic-gate 
11667c478bd9Sstevel@tonic-gate 	statep = ddi_get_driver_private(dip);
11677c478bd9Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
11687c478bd9Sstevel@tonic-gate 
11697c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_EVENTS, usp->us_lh, "usbser_cpr_suspend");
11707c478bd9Sstevel@tonic-gate 
11717c478bd9Sstevel@tonic-gate 	/* suspend each port first */
11727c478bd9Sstevel@tonic-gate 	if (usbser_suspend_ports(usp) != USB_SUCCESS) {
11737c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
11747c478bd9Sstevel@tonic-gate 		    "usbser_cpr_suspend: GSD failure");
11757c478bd9Sstevel@tonic-gate 
11767c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
11777c478bd9Sstevel@tonic-gate 	}
11787c478bd9Sstevel@tonic-gate 
11797c478bd9Sstevel@tonic-gate 	new_state = USBSER_DS_SUSPEND(usp);	/* let DSD do its part */
11807c478bd9Sstevel@tonic-gate 
11817c478bd9Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
11827c478bd9Sstevel@tonic-gate 	if (new_state == USB_DEV_SUSPENDED) {
11837c478bd9Sstevel@tonic-gate 		rval = USB_SUCCESS;
11847c478bd9Sstevel@tonic-gate 	} else {
11857c478bd9Sstevel@tonic-gate 		ASSERT(new_state == USB_DEV_ONLINE);
11867c478bd9Sstevel@tonic-gate 		rval = USB_FAILURE;
11877c478bd9Sstevel@tonic-gate 	}
11887c478bd9Sstevel@tonic-gate 	usp->us_dev_state = new_state;
11897c478bd9Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
11907c478bd9Sstevel@tonic-gate 
11917c478bd9Sstevel@tonic-gate 	return (rval);
11927c478bd9Sstevel@tonic-gate }
11937c478bd9Sstevel@tonic-gate 
11947c478bd9Sstevel@tonic-gate 
11957c478bd9Sstevel@tonic-gate static int
usbser_suspend_ports(usbser_state_t * usp)11967c478bd9Sstevel@tonic-gate usbser_suspend_ports(usbser_state_t *usp)
11977c478bd9Sstevel@tonic-gate {
11987c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp;
11997c478bd9Sstevel@tonic-gate 	int		i;
12007c478bd9Sstevel@tonic-gate 
12017c478bd9Sstevel@tonic-gate 	for (i = 0; i < usp->us_port_cnt; i++) {
12027c478bd9Sstevel@tonic-gate 		pp = &usp->us_ports[i];
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
12057c478bd9Sstevel@tonic-gate 		if (pp->port_state != USBSER_PORT_CLOSED) {
12067c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->port_mutex);
12077c478bd9Sstevel@tonic-gate 
12087c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
12097c478bd9Sstevel@tonic-gate 		}
12107c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
12117c478bd9Sstevel@tonic-gate 	}
12127c478bd9Sstevel@tonic-gate 
12137c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
12147c478bd9Sstevel@tonic-gate }
12157c478bd9Sstevel@tonic-gate 
12167c478bd9Sstevel@tonic-gate 
12177c478bd9Sstevel@tonic-gate /*
12187c478bd9Sstevel@tonic-gate  * do CPR resume
12197c478bd9Sstevel@tonic-gate  *
12207c478bd9Sstevel@tonic-gate  * DSD will return USB_DEV_ONLINE in case of success
12217c478bd9Sstevel@tonic-gate  */
12227c478bd9Sstevel@tonic-gate static void
usbser_cpr_resume(dev_info_t * dip)12237c478bd9Sstevel@tonic-gate usbser_cpr_resume(dev_info_t *dip)
12247c478bd9Sstevel@tonic-gate {
12257c478bd9Sstevel@tonic-gate 	void		*statep;
12267c478bd9Sstevel@tonic-gate 	usbser_state_t	*usp;
12277c478bd9Sstevel@tonic-gate 
12287c478bd9Sstevel@tonic-gate 	statep = ddi_get_driver_private(dip);
12297c478bd9Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
12307c478bd9Sstevel@tonic-gate 
12317c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh, "usbser_cpr_resume");
12327c478bd9Sstevel@tonic-gate 
12337c478bd9Sstevel@tonic-gate 	(void) usbser_restore_device_state(usp);
12347c478bd9Sstevel@tonic-gate }
12357c478bd9Sstevel@tonic-gate 
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate /*
12387c478bd9Sstevel@tonic-gate  * restore device state after CPR resume or reconnect
12397c478bd9Sstevel@tonic-gate  */
12407c478bd9Sstevel@tonic-gate static int
usbser_restore_device_state(usbser_state_t * usp)12417c478bd9Sstevel@tonic-gate usbser_restore_device_state(usbser_state_t *usp)
12427c478bd9Sstevel@tonic-gate {
12437c478bd9Sstevel@tonic-gate 	int	new_state, current_state;
12447c478bd9Sstevel@tonic-gate 
12454ee52f77Slg 	/* needed as power up state of dev is "unknown" to system */
12464ee52f77Slg 	(void) pm_busy_component(usp->us_dip, 0);
12474ee52f77Slg 	(void) pm_raise_power(usp->us_dip, 0, USB_DEV_OS_FULL_PWR);
12484ee52f77Slg 
12497c478bd9Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
12507c478bd9Sstevel@tonic-gate 	current_state = usp->us_dev_state;
12517c478bd9Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
12527c478bd9Sstevel@tonic-gate 
12537c478bd9Sstevel@tonic-gate 	ASSERT((current_state == USB_DEV_DISCONNECTED) ||
12544ee52f77Slg 	    (current_state == USB_DEV_SUSPENDED));
12557c478bd9Sstevel@tonic-gate 
12567c478bd9Sstevel@tonic-gate 	/*
12577c478bd9Sstevel@tonic-gate 	 * call DSD to perform device-specific work
12587c478bd9Sstevel@tonic-gate 	 */
12597c478bd9Sstevel@tonic-gate 	if (current_state == USB_DEV_DISCONNECTED) {
12607c478bd9Sstevel@tonic-gate 		new_state = USBSER_DS_RECONNECT(usp);
12617c478bd9Sstevel@tonic-gate 	} else {
12627c478bd9Sstevel@tonic-gate 		new_state = USBSER_DS_RESUME(usp);
12637c478bd9Sstevel@tonic-gate 	}
12647c478bd9Sstevel@tonic-gate 
12657c478bd9Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
12667c478bd9Sstevel@tonic-gate 	usp->us_dev_state = new_state;
12677c478bd9Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 	if (new_state == USB_DEV_ONLINE) {
12707c478bd9Sstevel@tonic-gate 		/*
12717c478bd9Sstevel@tonic-gate 		 * restore ports state
12727c478bd9Sstevel@tonic-gate 		 */
12737c478bd9Sstevel@tonic-gate 		usbser_restore_ports_state(usp);
12747c478bd9Sstevel@tonic-gate 	}
12757c478bd9Sstevel@tonic-gate 
12764ee52f77Slg 	(void) pm_idle_component(usp->us_dip, 0);
12774ee52f77Slg 
12787c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
12797c478bd9Sstevel@tonic-gate }
12807c478bd9Sstevel@tonic-gate 
12817c478bd9Sstevel@tonic-gate 
12827c478bd9Sstevel@tonic-gate /*
12837c478bd9Sstevel@tonic-gate  * restore ports state after device reconnect/resume
12847c478bd9Sstevel@tonic-gate  */
12857c478bd9Sstevel@tonic-gate static void
usbser_restore_ports_state(usbser_state_t * usp)12867c478bd9Sstevel@tonic-gate usbser_restore_ports_state(usbser_state_t *usp)
12877c478bd9Sstevel@tonic-gate {
12887c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp;
12897c478bd9Sstevel@tonic-gate 	queue_t		*rq;
12907c478bd9Sstevel@tonic-gate 	int		i;
12917c478bd9Sstevel@tonic-gate 
12927c478bd9Sstevel@tonic-gate 	for (i = 0; i < usp->us_port_cnt; i++) {
12937c478bd9Sstevel@tonic-gate 		pp = &usp->us_ports[i];
12947c478bd9Sstevel@tonic-gate 
12957c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
12967c478bd9Sstevel@tonic-gate 		/*
12977c478bd9Sstevel@tonic-gate 		 * only care about ports that are open
12987c478bd9Sstevel@tonic-gate 		 */
12997c478bd9Sstevel@tonic-gate 		if ((pp->port_state != USBSER_PORT_SUSPENDED) &&
13007c478bd9Sstevel@tonic-gate 		    (pp->port_state != USBSER_PORT_DISCONNECTED)) {
13017c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->port_mutex);
13027c478bd9Sstevel@tonic-gate 
13037c478bd9Sstevel@tonic-gate 			continue;
13047c478bd9Sstevel@tonic-gate 		}
13057c478bd9Sstevel@tonic-gate 
13067c478bd9Sstevel@tonic-gate 		pp->port_state = USBSER_PORT_OPEN;
13077c478bd9Sstevel@tonic-gate 
13087c478bd9Sstevel@tonic-gate 		/*
13097c478bd9Sstevel@tonic-gate 		 * if the stream was hung up during disconnect, restore it
13107c478bd9Sstevel@tonic-gate 		 */
13117c478bd9Sstevel@tonic-gate 		if (pp->port_flags & USBSER_FL_HUNGUP) {
13127c478bd9Sstevel@tonic-gate 			pp->port_flags &= ~USBSER_FL_HUNGUP;
13137c478bd9Sstevel@tonic-gate 			rq = pp->port_ttycommon.t_readq;
13147c478bd9Sstevel@tonic-gate 
13157c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->port_mutex);
13167c478bd9Sstevel@tonic-gate 			(void) putnextctl(rq, M_UNHANGUP);
13177c478bd9Sstevel@tonic-gate 			mutex_enter(&pp->port_mutex);
13187c478bd9Sstevel@tonic-gate 		}
13197c478bd9Sstevel@tonic-gate 
13207c478bd9Sstevel@tonic-gate 		/*
13217c478bd9Sstevel@tonic-gate 		 * restore serial parameters
13227c478bd9Sstevel@tonic-gate 		 */
13237c478bd9Sstevel@tonic-gate 		(void) usbser_port_program(pp);
13247c478bd9Sstevel@tonic-gate 
13257c478bd9Sstevel@tonic-gate 		/*
13267c478bd9Sstevel@tonic-gate 		 * wake anything that might be sleeping
13277c478bd9Sstevel@tonic-gate 		 */
13287c478bd9Sstevel@tonic-gate 		cv_broadcast(&pp->port_state_cv);
13297c478bd9Sstevel@tonic-gate 		cv_broadcast(&pp->port_act_cv);
13307c478bd9Sstevel@tonic-gate 		usbser_thr_wake(&pp->port_wq_thread);
13317c478bd9Sstevel@tonic-gate 		usbser_thr_wake(&pp->port_rq_thread);
13327c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
13337c478bd9Sstevel@tonic-gate 	}
13347c478bd9Sstevel@tonic-gate }
13357c478bd9Sstevel@tonic-gate 
13367c478bd9Sstevel@tonic-gate 
13377c478bd9Sstevel@tonic-gate /*
13387c478bd9Sstevel@tonic-gate  * STREAMS subroutines
13397c478bd9Sstevel@tonic-gate  * -------------------
13407c478bd9Sstevel@tonic-gate  *
13417c478bd9Sstevel@tonic-gate  *
13427c478bd9Sstevel@tonic-gate  * port open state machine
13437c478bd9Sstevel@tonic-gate  *
13447c478bd9Sstevel@tonic-gate  * here's a list of things that the driver has to do while open;
13457c478bd9Sstevel@tonic-gate  * because device can be opened any number of times,
13467c478bd9Sstevel@tonic-gate  * initial open has additional responsibilities:
13477c478bd9Sstevel@tonic-gate  *
13487c478bd9Sstevel@tonic-gate  *	if (initial_open) {
13497c478bd9Sstevel@tonic-gate  *		initialize soft state;	\
13507c478bd9Sstevel@tonic-gate  *		DSD open;		- see usbser_open_init()
13517c478bd9Sstevel@tonic-gate  *		dispatch threads;	/
13527c478bd9Sstevel@tonic-gate  *	}
13537c478bd9Sstevel@tonic-gate  *	raise DTR;
13547c478bd9Sstevel@tonic-gate  *	wait for carrier (if necessary);
13557c478bd9Sstevel@tonic-gate  *
13567c478bd9Sstevel@tonic-gate  * we should also take into consideration that two threads can try to open
13577c478bd9Sstevel@tonic-gate  * the same physical port simultaneously (/dev/term/N and /dev/cua/N).
13587c478bd9Sstevel@tonic-gate  *
13597c478bd9Sstevel@tonic-gate  * return values:
13607c478bd9Sstevel@tonic-gate  *	0	- success;
13617c478bd9Sstevel@tonic-gate  *	>0	- fail with this error code;
13627c478bd9Sstevel@tonic-gate  */
13637c478bd9Sstevel@tonic-gate static int
usbser_open_setup(queue_t * rq,usbser_port_t * pp,int minor,int flag,cred_t * cr)13647c478bd9Sstevel@tonic-gate usbser_open_setup(queue_t *rq, usbser_port_t *pp, int minor, int flag,
1365*be128d9bSToomas Soome     cred_t *cr)
13667c478bd9Sstevel@tonic-gate {
13677c478bd9Sstevel@tonic-gate 	int	rval = USBSER_CONTINUE;
13687c478bd9Sstevel@tonic-gate 
13697c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
13707c478bd9Sstevel@tonic-gate 	/*
13717c478bd9Sstevel@tonic-gate 	 * refer to port state diagram in the header file
13727c478bd9Sstevel@tonic-gate 	 */
13737c478bd9Sstevel@tonic-gate loop:
13747c478bd9Sstevel@tonic-gate 	switch (pp->port_state) {
13757c478bd9Sstevel@tonic-gate 	case USBSER_PORT_CLOSED:
13767c478bd9Sstevel@tonic-gate 		/*
13777c478bd9Sstevel@tonic-gate 		 * initial open
13787c478bd9Sstevel@tonic-gate 		 */
13797c478bd9Sstevel@tonic-gate 		rval = usbser_open_init(pp, minor);
13807c478bd9Sstevel@tonic-gate 
13817c478bd9Sstevel@tonic-gate 		break;
13827c478bd9Sstevel@tonic-gate 	case USBSER_PORT_OPENING_TTY:
13837c478bd9Sstevel@tonic-gate 		/*
13847c478bd9Sstevel@tonic-gate 		 * dial-out thread can overtake the port
13857c478bd9Sstevel@tonic-gate 		 * if tty open thread is sleeping waiting for carrier
13867c478bd9Sstevel@tonic-gate 		 */
13877c478bd9Sstevel@tonic-gate 		if ((minor & OUTLINE) && (pp->port_flags & USBSER_FL_WOPEN)) {
13887c478bd9Sstevel@tonic-gate 			pp->port_state = USBSER_PORT_OPENING_OUT;
13897c478bd9Sstevel@tonic-gate 
13907c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_OPEN, pp->port_lh,
13917c478bd9Sstevel@tonic-gate 			    "usbser_open_state: overtake");
13927c478bd9Sstevel@tonic-gate 		}
13937c478bd9Sstevel@tonic-gate 
13947c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
13957c478bd9Sstevel@tonic-gate 	case USBSER_PORT_OPENING_OUT:
13967c478bd9Sstevel@tonic-gate 		/*
13977c478bd9Sstevel@tonic-gate 		 * if no other open in progress, setup the line
13987c478bd9Sstevel@tonic-gate 		 */
13997c478bd9Sstevel@tonic-gate 		if (USBSER_NO_OTHER_OPEN(pp, minor)) {
14007c478bd9Sstevel@tonic-gate 			rval = usbser_open_line_setup(pp, minor, flag);
14017c478bd9Sstevel@tonic-gate 
14027c478bd9Sstevel@tonic-gate 			break;
14037c478bd9Sstevel@tonic-gate 		}
14047c478bd9Sstevel@tonic-gate 
14057c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
14067c478bd9Sstevel@tonic-gate 	case USBSER_PORT_CLOSING:
14077c478bd9Sstevel@tonic-gate 		/*
14087c478bd9Sstevel@tonic-gate 		 * wait until close active phase ends
14097c478bd9Sstevel@tonic-gate 		 */
14107c478bd9Sstevel@tonic-gate 		if (cv_wait_sig(&pp->port_state_cv, &pp->port_mutex) == 0) {
14117c478bd9Sstevel@tonic-gate 			rval = EINTR;
14127c478bd9Sstevel@tonic-gate 		}
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate 		break;
14157c478bd9Sstevel@tonic-gate 	case USBSER_PORT_OPEN:
14167c478bd9Sstevel@tonic-gate 		if ((pp->port_ttycommon.t_flags & TS_XCLUDE) &&
14174ee52f77Slg 		    secpolicy_excl_open(cr) != 0) {
14187c478bd9Sstevel@tonic-gate 			/*
14197c478bd9Sstevel@tonic-gate 			 * exclusive use
14207c478bd9Sstevel@tonic-gate 			 */
14217c478bd9Sstevel@tonic-gate 			rval = EBUSY;
14227c478bd9Sstevel@tonic-gate 		} else if (USBSER_OPEN_IN_OTHER_MODE(pp, minor)) {
14237c478bd9Sstevel@tonic-gate 			/*
14247c478bd9Sstevel@tonic-gate 			 * tty and dial-out modes are mutually exclusive
14257c478bd9Sstevel@tonic-gate 			 */
14267c478bd9Sstevel@tonic-gate 			rval = EBUSY;
14277c478bd9Sstevel@tonic-gate 		} else {
14287c478bd9Sstevel@tonic-gate 			/*
14297c478bd9Sstevel@tonic-gate 			 * port is being re-open in the same mode
14307c478bd9Sstevel@tonic-gate 			 */
14317c478bd9Sstevel@tonic-gate 			rval = usbser_open_line_setup(pp, minor, flag);
14327c478bd9Sstevel@tonic-gate 		}
14337c478bd9Sstevel@tonic-gate 
14347c478bd9Sstevel@tonic-gate 		break;
14357c478bd9Sstevel@tonic-gate 	default:
14367c478bd9Sstevel@tonic-gate 		rval = ENXIO;
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 		break;
14397c478bd9Sstevel@tonic-gate 	}
14407c478bd9Sstevel@tonic-gate 
14417c478bd9Sstevel@tonic-gate 	if (rval == USBSER_CONTINUE) {
14427c478bd9Sstevel@tonic-gate 
14437c478bd9Sstevel@tonic-gate 		goto loop;
14447c478bd9Sstevel@tonic-gate 	}
14457c478bd9Sstevel@tonic-gate 
14467c478bd9Sstevel@tonic-gate 	/*
14477c478bd9Sstevel@tonic-gate 	 * initial open requires additional handling
14487c478bd9Sstevel@tonic-gate 	 */
14497c478bd9Sstevel@tonic-gate 	if (USBSER_IS_OPENING(pp)) {
14507c478bd9Sstevel@tonic-gate 		if (rval == USBSER_COMPLETE) {
14517c478bd9Sstevel@tonic-gate 			if (pp->port_state == USBSER_PORT_OPENING_OUT) {
14527c478bd9Sstevel@tonic-gate 				pp->port_flags |= USBSER_FL_OUT;
14537c478bd9Sstevel@tonic-gate 			}
14547c478bd9Sstevel@tonic-gate 			pp->port_state = USBSER_PORT_OPEN;
14557c478bd9Sstevel@tonic-gate 			cv_broadcast(&pp->port_state_cv);
14567c478bd9Sstevel@tonic-gate 
14577c478bd9Sstevel@tonic-gate 			usbser_open_queues_init(pp, rq);
14587c478bd9Sstevel@tonic-gate 		} else {
14597c478bd9Sstevel@tonic-gate 			usbser_open_fini(pp);
14607c478bd9Sstevel@tonic-gate 		}
14617c478bd9Sstevel@tonic-gate 	}
14627c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
14637c478bd9Sstevel@tonic-gate 
14647c478bd9Sstevel@tonic-gate 	return (rval);
14657c478bd9Sstevel@tonic-gate }
14667c478bd9Sstevel@tonic-gate 
14677c478bd9Sstevel@tonic-gate 
14687c478bd9Sstevel@tonic-gate /*
14697c478bd9Sstevel@tonic-gate  * initialize the port when opened for the first time
14707c478bd9Sstevel@tonic-gate  */
14717c478bd9Sstevel@tonic-gate static int
usbser_open_init(usbser_port_t * pp,int minor)14727c478bd9Sstevel@tonic-gate usbser_open_init(usbser_port_t *pp, int minor)
14737c478bd9Sstevel@tonic-gate {
14747c478bd9Sstevel@tonic-gate 	usbser_state_t	*usp = pp->port_usp;
14757c478bd9Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
14767c478bd9Sstevel@tonic-gate 	int		rval = ENXIO;
14777c478bd9Sstevel@tonic-gate 
14787c478bd9Sstevel@tonic-gate 	ASSERT(pp->port_state == USBSER_PORT_CLOSED);
14797c478bd9Sstevel@tonic-gate 
14807c478bd9Sstevel@tonic-gate 	/*
14817c478bd9Sstevel@tonic-gate 	 * init state
14827c478bd9Sstevel@tonic-gate 	 */
14837c478bd9Sstevel@tonic-gate 	pp->port_act = 0;
14847c478bd9Sstevel@tonic-gate 	pp->port_flags &= USBSER_FL_PRESERVE;
14857c478bd9Sstevel@tonic-gate 	pp->port_flowc = '\0';
14867c478bd9Sstevel@tonic-gate 	pp->port_wq_data_cnt = 0;
14877c478bd9Sstevel@tonic-gate 
14887c478bd9Sstevel@tonic-gate 	if (minor & OUTLINE) {
14897c478bd9Sstevel@tonic-gate 		pp->port_state = USBSER_PORT_OPENING_OUT;
14907c478bd9Sstevel@tonic-gate 	} else {
14917c478bd9Sstevel@tonic-gate 		pp->port_state = USBSER_PORT_OPENING_TTY;
14927c478bd9Sstevel@tonic-gate 	}
14937c478bd9Sstevel@tonic-gate 
14947c478bd9Sstevel@tonic-gate 	/*
14957c478bd9Sstevel@tonic-gate 	 * init termios settings
14967c478bd9Sstevel@tonic-gate 	 */
14977c478bd9Sstevel@tonic-gate 	tp->t_iflag = 0;
14987c478bd9Sstevel@tonic-gate 	tp->t_iocpending = NULL;
14997c478bd9Sstevel@tonic-gate 	tp->t_size.ws_row = tp->t_size.ws_col = 0;
15007c478bd9Sstevel@tonic-gate 	tp->t_size.ws_xpixel = tp->t_size.ws_ypixel = 0;
15017c478bd9Sstevel@tonic-gate 	tp->t_startc = CSTART;
15027c478bd9Sstevel@tonic-gate 	tp->t_stopc = CSTOP;
15037c478bd9Sstevel@tonic-gate 
15047c478bd9Sstevel@tonic-gate 	usbser_check_port_props(pp);
15057c478bd9Sstevel@tonic-gate 
15067c478bd9Sstevel@tonic-gate 	/*
15077c478bd9Sstevel@tonic-gate 	 * dispatch wq and rq threads:
15087c478bd9Sstevel@tonic-gate 	 * although queues are not enabled at this point,
15097c478bd9Sstevel@tonic-gate 	 * we will need wq to run status processing callback
15107c478bd9Sstevel@tonic-gate 	 */
15117c478bd9Sstevel@tonic-gate 	usbser_thr_dispatch(&pp->port_wq_thread);
15127c478bd9Sstevel@tonic-gate 	usbser_thr_dispatch(&pp->port_rq_thread);
15137c478bd9Sstevel@tonic-gate 
15147c478bd9Sstevel@tonic-gate 	/*
15157c478bd9Sstevel@tonic-gate 	 * open DSD port
15167c478bd9Sstevel@tonic-gate 	 */
15177c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
15187c478bd9Sstevel@tonic-gate 	rval = USBSER_DS_OPEN_PORT(usp, pp->port_num);
15197c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
15207c478bd9Sstevel@tonic-gate 
15217c478bd9Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
15227c478bd9Sstevel@tonic-gate 
15237c478bd9Sstevel@tonic-gate 		return (ENXIO);
15247c478bd9Sstevel@tonic-gate 	}
15257c478bd9Sstevel@tonic-gate 	pp->port_flags |= USBSER_FL_DSD_OPEN;
15267c478bd9Sstevel@tonic-gate 
15277c478bd9Sstevel@tonic-gate 	/*
15287c478bd9Sstevel@tonic-gate 	 * program port with default parameters
15297c478bd9Sstevel@tonic-gate 	 */
15307c478bd9Sstevel@tonic-gate 	if ((rval = usbser_port_program(pp)) != 0) {
15317c478bd9Sstevel@tonic-gate 
15327c478bd9Sstevel@tonic-gate 		return (ENXIO);
15337c478bd9Sstevel@tonic-gate 	}
15347c478bd9Sstevel@tonic-gate 
15357c478bd9Sstevel@tonic-gate 	return (USBSER_CONTINUE);
15367c478bd9Sstevel@tonic-gate }
15377c478bd9Sstevel@tonic-gate 
15387c478bd9Sstevel@tonic-gate 
15397c478bd9Sstevel@tonic-gate /*
15407c478bd9Sstevel@tonic-gate  * create a pair of minor nodes for the port
15417c478bd9Sstevel@tonic-gate  */
15427c478bd9Sstevel@tonic-gate static void
usbser_check_port_props(usbser_port_t * pp)15437c478bd9Sstevel@tonic-gate usbser_check_port_props(usbser_port_t *pp)
15447c478bd9Sstevel@tonic-gate {
15457c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = pp->port_usp->us_dip;
15467c478bd9Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
1547112116d8Sfb 	struct termios	*termiosp;
15487c478bd9Sstevel@tonic-gate 	uint_t		len;
15497c478bd9Sstevel@tonic-gate 	char		name[20];
15507c478bd9Sstevel@tonic-gate 
15517c478bd9Sstevel@tonic-gate 	/*
15527c478bd9Sstevel@tonic-gate 	 * take default modes from "ttymodes" property if it exists
15537c478bd9Sstevel@tonic-gate 	 */
15547c478bd9Sstevel@tonic-gate 	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ddi_root_node(), 0,
15557c478bd9Sstevel@tonic-gate 	    "ttymodes", (uchar_t **)&termiosp, &len) == DDI_PROP_SUCCESS) {
15567c478bd9Sstevel@tonic-gate 
15577c478bd9Sstevel@tonic-gate 		if (len == sizeof (struct termios)) {
15587c478bd9Sstevel@tonic-gate 			tp->t_cflag = termiosp->c_cflag;
15597c478bd9Sstevel@tonic-gate 
15607c478bd9Sstevel@tonic-gate 			if (termiosp->c_iflag & (IXON | IXANY)) {
15617c478bd9Sstevel@tonic-gate 				tp->t_iflag =
15624ee52f77Slg 				    termiosp->c_iflag & (IXON | IXANY);
15637c478bd9Sstevel@tonic-gate 				tp->t_startc = termiosp->c_cc[VSTART];
15647c478bd9Sstevel@tonic-gate 				tp->t_stopc = termiosp->c_cc[VSTOP];
15657c478bd9Sstevel@tonic-gate 			}
15667c478bd9Sstevel@tonic-gate 		}
15677c478bd9Sstevel@tonic-gate 		ddi_prop_free(termiosp);
15687c478bd9Sstevel@tonic-gate 	}
15697c478bd9Sstevel@tonic-gate 
15707c478bd9Sstevel@tonic-gate 	/*
15717c478bd9Sstevel@tonic-gate 	 * look for "ignore-cd" or "port-N-ignore-cd" property
15727c478bd9Sstevel@tonic-gate 	 */
15737c478bd9Sstevel@tonic-gate 	(void) sprintf(name, "port-%d-ignore-cd", pp->port_num);
15747c478bd9Sstevel@tonic-gate 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
15757c478bd9Sstevel@tonic-gate 	    "ignore-cd", 0) ||
15767c478bd9Sstevel@tonic-gate 	    ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, name, 0)) {
15777c478bd9Sstevel@tonic-gate 		pp->port_flags |= USBSER_FL_IGNORE_CD;
15787c478bd9Sstevel@tonic-gate 	} else {
15797c478bd9Sstevel@tonic-gate 		pp->port_flags &= ~USBSER_FL_IGNORE_CD;
15807c478bd9Sstevel@tonic-gate 	}
15817c478bd9Sstevel@tonic-gate }
15827c478bd9Sstevel@tonic-gate 
15837c478bd9Sstevel@tonic-gate 
15847c478bd9Sstevel@tonic-gate /*
15857c478bd9Sstevel@tonic-gate  * undo what was done in usbser_open_init()
15867c478bd9Sstevel@tonic-gate  */
15877c478bd9Sstevel@tonic-gate static void
usbser_open_fini(usbser_port_t * pp)15887c478bd9Sstevel@tonic-gate usbser_open_fini(usbser_port_t *pp)
15897c478bd9Sstevel@tonic-gate {
15907c478bd9Sstevel@tonic-gate 	uint_t		port_num = pp->port_num;
15917c478bd9Sstevel@tonic-gate 	usbser_state_t	*usp = pp->port_usp;
15927c478bd9Sstevel@tonic-gate 
15937c478bd9Sstevel@tonic-gate 	/*
15947c478bd9Sstevel@tonic-gate 	 * close DSD if it is open
15957c478bd9Sstevel@tonic-gate 	 */
15967c478bd9Sstevel@tonic-gate 	if (pp->port_flags & USBSER_FL_DSD_OPEN) {
15977c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
15987c478bd9Sstevel@tonic-gate 		if (USBSER_DS_CLOSE_PORT(usp, port_num) != USB_SUCCESS) {
15997c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_CLOSE, pp->port_lh,
16007c478bd9Sstevel@tonic-gate 			    "usbser_open_fini: CLOSE_PORT fail");
16017c478bd9Sstevel@tonic-gate 		}
16027c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
16037c478bd9Sstevel@tonic-gate 	}
16047c478bd9Sstevel@tonic-gate 
16057c478bd9Sstevel@tonic-gate 	/*
16067c478bd9Sstevel@tonic-gate 	 * cancel threads
16077c478bd9Sstevel@tonic-gate 	 */
16087c478bd9Sstevel@tonic-gate 	usbser_thr_cancel(&pp->port_wq_thread);
16097c478bd9Sstevel@tonic-gate 	usbser_thr_cancel(&pp->port_rq_thread);
16107c478bd9Sstevel@tonic-gate 
16117c478bd9Sstevel@tonic-gate 	/*
16127c478bd9Sstevel@tonic-gate 	 * unpdate soft state
16137c478bd9Sstevel@tonic-gate 	 */
16147c478bd9Sstevel@tonic-gate 	pp->port_state = USBSER_PORT_CLOSED;
16157c478bd9Sstevel@tonic-gate 	cv_broadcast(&pp->port_state_cv);
16167c478bd9Sstevel@tonic-gate 	cv_broadcast(&pp->port_car_cv);
16177c478bd9Sstevel@tonic-gate }
16187c478bd9Sstevel@tonic-gate 
16197c478bd9Sstevel@tonic-gate 
16207c478bd9Sstevel@tonic-gate /*
16217c478bd9Sstevel@tonic-gate  * setup serial line
16227c478bd9Sstevel@tonic-gate  */
16237c478bd9Sstevel@tonic-gate static int
usbser_open_line_setup(usbser_port_t * pp,int minor,int flag)16247c478bd9Sstevel@tonic-gate usbser_open_line_setup(usbser_port_t *pp, int minor, int flag)
16257c478bd9Sstevel@tonic-gate {
16267c478bd9Sstevel@tonic-gate 	int	rval;
16277c478bd9Sstevel@tonic-gate 
16287c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
16297c478bd9Sstevel@tonic-gate 	/*
16307c478bd9Sstevel@tonic-gate 	 * prevent opening a disconnected device
16317c478bd9Sstevel@tonic-gate 	 */
16327c478bd9Sstevel@tonic-gate 	if (!usbser_dev_is_online(pp->port_usp)) {
16337c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
16347c478bd9Sstevel@tonic-gate 
16357c478bd9Sstevel@tonic-gate 		return (ENXIO);
16367c478bd9Sstevel@tonic-gate 	}
16377c478bd9Sstevel@tonic-gate 
16387c478bd9Sstevel@tonic-gate 	/* raise DTR on every open */
16397c478bd9Sstevel@tonic-gate 	(void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR, TIOCM_DTR);
16407c478bd9Sstevel@tonic-gate 
16417c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
16427c478bd9Sstevel@tonic-gate 	/*
16437c478bd9Sstevel@tonic-gate 	 * check carrier
16447c478bd9Sstevel@tonic-gate 	 */
16457c478bd9Sstevel@tonic-gate 	rval = usbser_open_carrier_check(pp, minor, flag);
16467c478bd9Sstevel@tonic-gate 
16477c478bd9Sstevel@tonic-gate 	return (rval);
16487c478bd9Sstevel@tonic-gate }
16497c478bd9Sstevel@tonic-gate 
16507c478bd9Sstevel@tonic-gate 
16517c478bd9Sstevel@tonic-gate /*
16527c478bd9Sstevel@tonic-gate  * check carrier and wait if needed
16537c478bd9Sstevel@tonic-gate  */
16547c478bd9Sstevel@tonic-gate static int
usbser_open_carrier_check(usbser_port_t * pp,int minor,int flag)16557c478bd9Sstevel@tonic-gate usbser_open_carrier_check(usbser_port_t *pp, int minor, int flag)
16567c478bd9Sstevel@tonic-gate {
16577c478bd9Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
16587c478bd9Sstevel@tonic-gate 	int		val = 0;
16597c478bd9Sstevel@tonic-gate 	int		rval;
16607c478bd9Sstevel@tonic-gate 
16617c478bd9Sstevel@tonic-gate 	if (pp->port_flags & USBSER_FL_IGNORE_CD) {
16627c478bd9Sstevel@tonic-gate 		tp->t_flags |= TS_SOFTCAR;
16637c478bd9Sstevel@tonic-gate 	}
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate 	/*
16667c478bd9Sstevel@tonic-gate 	 * check carrier
16677c478bd9Sstevel@tonic-gate 	 */
16687c478bd9Sstevel@tonic-gate 	if (tp->t_flags & TS_SOFTCAR) {
16697c478bd9Sstevel@tonic-gate 		pp->port_flags |= USBSER_FL_CARR_ON;
16707c478bd9Sstevel@tonic-gate 	} else if (USBSER_DS_GET_MODEM_CTL(pp, TIOCM_CD, &val) != USB_SUCCESS) {
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate 		return (ENXIO);
16737c478bd9Sstevel@tonic-gate 	} else if (val & TIOCM_CD) {
16747c478bd9Sstevel@tonic-gate 		pp->port_flags |= USBSER_FL_CARR_ON;
16757c478bd9Sstevel@tonic-gate 	} else {
16767c478bd9Sstevel@tonic-gate 		pp->port_flags &= ~USBSER_FL_CARR_ON;
16777c478bd9Sstevel@tonic-gate 	}
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate 	/*
16807c478bd9Sstevel@tonic-gate 	 * don't block if 1) not allowed to, 2) this is a local device,
16817c478bd9Sstevel@tonic-gate 	 * 3) opening in dial-out mode, or 4) carrier is already on
16827c478bd9Sstevel@tonic-gate 	 */
16837c478bd9Sstevel@tonic-gate 	if ((flag & (FNDELAY | FNONBLOCK)) || (tp->t_cflag & CLOCAL) ||
16847c478bd9Sstevel@tonic-gate 	    (minor & OUTLINE) || (pp->port_flags & USBSER_FL_CARR_ON)) {
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate 		return (USBSER_COMPLETE);
16877c478bd9Sstevel@tonic-gate 	}
16887c478bd9Sstevel@tonic-gate 
16897c478bd9Sstevel@tonic-gate 	/*
16907c478bd9Sstevel@tonic-gate 	 * block until carrier up (only in tty mode)
16917c478bd9Sstevel@tonic-gate 	 */
16927c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_OPEN, pp->port_lh,
16937c478bd9Sstevel@tonic-gate 	    "usbser_open_carrier_check: waiting for carrier...");
16947c478bd9Sstevel@tonic-gate 
16957c478bd9Sstevel@tonic-gate 	pp->port_flags |= USBSER_FL_WOPEN;
16967c478bd9Sstevel@tonic-gate 
16977c478bd9Sstevel@tonic-gate 	rval = cv_wait_sig(&pp->port_car_cv, &pp->port_mutex);
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 	pp->port_flags &= ~USBSER_FL_WOPEN;
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate 	if (rval == 0) {
17027c478bd9Sstevel@tonic-gate 		/*
17037c478bd9Sstevel@tonic-gate 		 * interrupted with a signal
17047c478bd9Sstevel@tonic-gate 		 */
17057c478bd9Sstevel@tonic-gate 		return (EINTR);
17067c478bd9Sstevel@tonic-gate 	} else {
17077c478bd9Sstevel@tonic-gate 		/*
17087c478bd9Sstevel@tonic-gate 		 * try again
17097c478bd9Sstevel@tonic-gate 		 */
17107c478bd9Sstevel@tonic-gate 		return (USBSER_CONTINUE);
17117c478bd9Sstevel@tonic-gate 	}
17127c478bd9Sstevel@tonic-gate }
17137c478bd9Sstevel@tonic-gate 
17147c478bd9Sstevel@tonic-gate 
17157c478bd9Sstevel@tonic-gate /*
17167c478bd9Sstevel@tonic-gate  * during open, setup queues and message processing
17177c478bd9Sstevel@tonic-gate  */
17187c478bd9Sstevel@tonic-gate static void
usbser_open_queues_init(usbser_port_t * pp,queue_t * rq)17197c478bd9Sstevel@tonic-gate usbser_open_queues_init(usbser_port_t *pp, queue_t *rq)
17207c478bd9Sstevel@tonic-gate {
17217c478bd9Sstevel@tonic-gate 	pp->port_ttycommon.t_readq = rq;
17227c478bd9Sstevel@tonic-gate 	pp->port_ttycommon.t_writeq = WR(rq);
17237c478bd9Sstevel@tonic-gate 	rq->q_ptr = WR(rq)->q_ptr = (caddr_t)pp;
17247c478bd9Sstevel@tonic-gate 
17257c478bd9Sstevel@tonic-gate 	qprocson(rq);
17267c478bd9Sstevel@tonic-gate }
17277c478bd9Sstevel@tonic-gate 
17287c478bd9Sstevel@tonic-gate 
17297c478bd9Sstevel@tonic-gate /*
17307c478bd9Sstevel@tonic-gate  * clean up queues and message processing
17317c478bd9Sstevel@tonic-gate  */
17327c478bd9Sstevel@tonic-gate static void
usbser_open_queues_fini(usbser_port_t * pp)17337c478bd9Sstevel@tonic-gate usbser_open_queues_fini(usbser_port_t *pp)
17347c478bd9Sstevel@tonic-gate {
17357c478bd9Sstevel@tonic-gate 	queue_t	*rq = pp->port_ttycommon.t_readq;
17367c478bd9Sstevel@tonic-gate 
17377c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
17387c478bd9Sstevel@tonic-gate 	/*
17397c478bd9Sstevel@tonic-gate 	 * clean up queues
17407c478bd9Sstevel@tonic-gate 	 */
17417c478bd9Sstevel@tonic-gate 	qprocsoff(rq);
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate 	/*
17447c478bd9Sstevel@tonic-gate 	 * free unused messages
17457c478bd9Sstevel@tonic-gate 	 */
17467c478bd9Sstevel@tonic-gate 	flushq(rq, FLUSHALL);
17477c478bd9Sstevel@tonic-gate 	flushq(WR(rq), FLUSHALL);
17487c478bd9Sstevel@tonic-gate 
17497c478bd9Sstevel@tonic-gate 	rq->q_ptr = WR(rq)->q_ptr = NULL;
17507c478bd9Sstevel@tonic-gate 	ttycommon_close(&pp->port_ttycommon);
17517c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
17527c478bd9Sstevel@tonic-gate }
17537c478bd9Sstevel@tonic-gate 
17547c478bd9Sstevel@tonic-gate 
17557c478bd9Sstevel@tonic-gate /*
17567c478bd9Sstevel@tonic-gate  * during close, wait until pending data is gone or the signal is sent
17577c478bd9Sstevel@tonic-gate  */
17587c478bd9Sstevel@tonic-gate static void
usbser_close_drain(usbser_port_t * pp)17597c478bd9Sstevel@tonic-gate usbser_close_drain(usbser_port_t *pp)
17607c478bd9Sstevel@tonic-gate {
17617c478bd9Sstevel@tonic-gate 	int	need_drain;
17627c478bd9Sstevel@tonic-gate 	clock_t	until;
1763d235eb05SRichard PALO 	int	rval = USB_SUCCESS;
17647c478bd9Sstevel@tonic-gate 
17657c478bd9Sstevel@tonic-gate 	/*
17667c478bd9Sstevel@tonic-gate 	 * port_wq_data_cnt indicates amount of data on the write queue,
17677c478bd9Sstevel@tonic-gate 	 * which becomes zero when all data is submitted to DSD. But usbser
17687c478bd9Sstevel@tonic-gate 	 * stays busy until it gets tx callback from DSD, signalling that
17697c478bd9Sstevel@tonic-gate 	 * data has been sent over USB. To be continued in the next comment...
17707c478bd9Sstevel@tonic-gate 	 */
17717c478bd9Sstevel@tonic-gate 	until = ddi_get_lbolt() +
17724ee52f77Slg 	    drv_usectohz(USBSER_WQ_DRAIN_TIMEOUT * 1000000);
17737c478bd9Sstevel@tonic-gate 
17747c478bd9Sstevel@tonic-gate 	while ((pp->port_wq_data_cnt > 0) && USBSER_PORT_IS_BUSY(pp)) {
17757c478bd9Sstevel@tonic-gate 		if ((rval = cv_timedwait_sig(&pp->port_act_cv, &pp->port_mutex,
17767c478bd9Sstevel@tonic-gate 		    until)) <= 0) {
17777c478bd9Sstevel@tonic-gate 
17787c478bd9Sstevel@tonic-gate 			break;
17797c478bd9Sstevel@tonic-gate 		}
17807c478bd9Sstevel@tonic-gate 	}
17817c478bd9Sstevel@tonic-gate 
17827c478bd9Sstevel@tonic-gate 	/* don't drain if timed out or received a signal */
17837c478bd9Sstevel@tonic-gate 	need_drain = (pp->port_wq_data_cnt == 0) || !USBSER_PORT_IS_BUSY(pp) ||
1784d235eb05SRichard PALO 	    (rval != USB_SUCCESS);
17857c478bd9Sstevel@tonic-gate 
17867c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
17877c478bd9Sstevel@tonic-gate 	/*
17887c478bd9Sstevel@tonic-gate 	 * Once the data reaches USB serial box, it may still be stored in its
17897c478bd9Sstevel@tonic-gate 	 * internal output buffer (FIFO). We call DSD drain to ensure that all
17907c478bd9Sstevel@tonic-gate 	 * the data is transmitted transmitted over the serial line.
17917c478bd9Sstevel@tonic-gate 	 */
17927c478bd9Sstevel@tonic-gate 	if (need_drain) {
17937c478bd9Sstevel@tonic-gate 		rval = USBSER_DS_FIFO_DRAIN(pp, USBSER_TX_FIFO_DRAIN_TIMEOUT);
17947c478bd9Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
17957c478bd9Sstevel@tonic-gate 			(void) USBSER_DS_FIFO_FLUSH(pp, DS_TX);
17967c478bd9Sstevel@tonic-gate 		}
17977c478bd9Sstevel@tonic-gate 	} else {
17987c478bd9Sstevel@tonic-gate 		(void) USBSER_DS_FIFO_FLUSH(pp, DS_TX);
17997c478bd9Sstevel@tonic-gate 	}
18007c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
18017c478bd9Sstevel@tonic-gate }
18027c478bd9Sstevel@tonic-gate 
18037c478bd9Sstevel@tonic-gate 
18047c478bd9Sstevel@tonic-gate /*
18057c478bd9Sstevel@tonic-gate  * during close, cancel break/delay
18067c478bd9Sstevel@tonic-gate  */
18077c478bd9Sstevel@tonic-gate static void
usbser_close_cancel_break(usbser_port_t * pp)18087c478bd9Sstevel@tonic-gate usbser_close_cancel_break(usbser_port_t *pp)
18097c478bd9Sstevel@tonic-gate {
18107c478bd9Sstevel@tonic-gate 	timeout_id_t	delay_id;
18117c478bd9Sstevel@tonic-gate 
18127c478bd9Sstevel@tonic-gate 	if (pp->port_act & USBSER_ACT_BREAK) {
18137c478bd9Sstevel@tonic-gate 		delay_id = pp->port_delay_id;
18147c478bd9Sstevel@tonic-gate 		pp->port_delay_id = 0;
18157c478bd9Sstevel@tonic-gate 
18167c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
18177c478bd9Sstevel@tonic-gate 		(void) untimeout(delay_id);
18187c478bd9Sstevel@tonic-gate 		(void) USBSER_DS_BREAK_CTL(pp, DS_OFF);
18197c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
18207c478bd9Sstevel@tonic-gate 
18217c478bd9Sstevel@tonic-gate 		pp->port_act &= ~USBSER_ACT_BREAK;
18227c478bd9Sstevel@tonic-gate 	}
18237c478bd9Sstevel@tonic-gate }
18247c478bd9Sstevel@tonic-gate 
18257c478bd9Sstevel@tonic-gate 
18267c478bd9Sstevel@tonic-gate /*
18277c478bd9Sstevel@tonic-gate  * during close, drop RTS/DTR if necessary
18287c478bd9Sstevel@tonic-gate  */
18297c478bd9Sstevel@tonic-gate static void
usbser_close_hangup(usbser_port_t * pp)18307c478bd9Sstevel@tonic-gate usbser_close_hangup(usbser_port_t *pp)
18317c478bd9Sstevel@tonic-gate {
18327c478bd9Sstevel@tonic-gate 	/*
18337c478bd9Sstevel@tonic-gate 	 * drop DTR and RTS if HUPCL is set
18347c478bd9Sstevel@tonic-gate 	 */
18357c478bd9Sstevel@tonic-gate 	if (pp->port_ttycommon.t_cflag & HUPCL) {
18367c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
18377c478bd9Sstevel@tonic-gate 		(void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_RTS | TIOCM_DTR, 0);
18387c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
18397c478bd9Sstevel@tonic-gate 	}
18407c478bd9Sstevel@tonic-gate }
18417c478bd9Sstevel@tonic-gate 
18427c478bd9Sstevel@tonic-gate 
18437c478bd9Sstevel@tonic-gate /*
18447c478bd9Sstevel@tonic-gate  * state cleanup during close
18457c478bd9Sstevel@tonic-gate  */
18467c478bd9Sstevel@tonic-gate static void
usbser_close_cleanup(usbser_port_t * pp)18477c478bd9Sstevel@tonic-gate usbser_close_cleanup(usbser_port_t *pp)
18487c478bd9Sstevel@tonic-gate {
18497c478bd9Sstevel@tonic-gate 	usbser_open_queues_fini(pp);
18507c478bd9Sstevel@tonic-gate 
18517c478bd9Sstevel@tonic-gate 	usbser_open_fini(pp);
18527c478bd9Sstevel@tonic-gate }
18537c478bd9Sstevel@tonic-gate 
18547c478bd9Sstevel@tonic-gate 
18557c478bd9Sstevel@tonic-gate /*
18567c478bd9Sstevel@tonic-gate  *
18577c478bd9Sstevel@tonic-gate  * thread management
18587c478bd9Sstevel@tonic-gate  * -----------------
18597c478bd9Sstevel@tonic-gate  *
18607c478bd9Sstevel@tonic-gate  *
18617c478bd9Sstevel@tonic-gate  * dispatch a thread
18627c478bd9Sstevel@tonic-gate  */
18637c478bd9Sstevel@tonic-gate static void
usbser_thr_dispatch(usbser_thread_t * thr)18647c478bd9Sstevel@tonic-gate usbser_thr_dispatch(usbser_thread_t *thr)
18657c478bd9Sstevel@tonic-gate {
18667c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp = thr->thr_port;
18677c478bd9Sstevel@tonic-gate 	usbser_state_t	*usp = pp->port_usp;
18687c478bd9Sstevel@tonic-gate 	int		rval;
18697c478bd9Sstevel@tonic-gate 
18707c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->port_mutex));
18717c478bd9Sstevel@tonic-gate 	ASSERT((thr->thr_flags & USBSER_THR_RUNNING) == 0);
18727c478bd9Sstevel@tonic-gate 
18737c478bd9Sstevel@tonic-gate 	thr->thr_flags = USBSER_THR_RUNNING;
18747c478bd9Sstevel@tonic-gate 
18757c478bd9Sstevel@tonic-gate 	rval = ddi_taskq_dispatch(usp->us_taskq, thr->thr_func, thr->thr_arg,
18764ee52f77Slg 	    DDI_SLEEP);
18777c478bd9Sstevel@tonic-gate 	ASSERT(rval == DDI_SUCCESS);
18787c478bd9Sstevel@tonic-gate }
18797c478bd9Sstevel@tonic-gate 
18807c478bd9Sstevel@tonic-gate 
18817c478bd9Sstevel@tonic-gate /*
18827c478bd9Sstevel@tonic-gate  * cancel a thread
18837c478bd9Sstevel@tonic-gate  */
18847c478bd9Sstevel@tonic-gate static void
usbser_thr_cancel(usbser_thread_t * thr)18857c478bd9Sstevel@tonic-gate usbser_thr_cancel(usbser_thread_t *thr)
18867c478bd9Sstevel@tonic-gate {
18877c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp = thr->thr_port;
18887c478bd9Sstevel@tonic-gate 
18897c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->port_mutex));
18907c478bd9Sstevel@tonic-gate 
18917c478bd9Sstevel@tonic-gate 	thr->thr_flags &= ~USBSER_THR_RUNNING;
18927c478bd9Sstevel@tonic-gate 	cv_signal(&thr->thr_cv);
18937c478bd9Sstevel@tonic-gate 
18947c478bd9Sstevel@tonic-gate 	/* wait until the thread actually exits */
18957c478bd9Sstevel@tonic-gate 	do {
18966918308bSyz 		cv_wait(&thr->thr_cv, &pp->port_mutex);
18976918308bSyz 
18987c478bd9Sstevel@tonic-gate 	} while ((thr->thr_flags & USBSER_THR_EXITED) == 0);
18997c478bd9Sstevel@tonic-gate }
19007c478bd9Sstevel@tonic-gate 
19017c478bd9Sstevel@tonic-gate 
19027c478bd9Sstevel@tonic-gate /*
19037c478bd9Sstevel@tonic-gate  * wake thread
19047c478bd9Sstevel@tonic-gate  */
19057c478bd9Sstevel@tonic-gate static void
usbser_thr_wake(usbser_thread_t * thr)19067c478bd9Sstevel@tonic-gate usbser_thr_wake(usbser_thread_t *thr)
19077c478bd9Sstevel@tonic-gate {
1908d235eb05SRichard PALO 	ASSERT(mutex_owned(&thr->thr_port->port_mutex));
19097c478bd9Sstevel@tonic-gate 
19107c478bd9Sstevel@tonic-gate 	thr->thr_flags |= USBSER_THR_WAKE;
19117c478bd9Sstevel@tonic-gate 	cv_signal(&thr->thr_cv);
19127c478bd9Sstevel@tonic-gate }
19137c478bd9Sstevel@tonic-gate 
19147c478bd9Sstevel@tonic-gate 
19157c478bd9Sstevel@tonic-gate /*
19167c478bd9Sstevel@tonic-gate  * thread handling write queue requests
19177c478bd9Sstevel@tonic-gate  */
19187c478bd9Sstevel@tonic-gate static void
usbser_wq_thread(void * arg)19197c478bd9Sstevel@tonic-gate usbser_wq_thread(void *arg)
19207c478bd9Sstevel@tonic-gate {
19217c478bd9Sstevel@tonic-gate 	usbser_thread_t	*thr = (usbser_thread_t *)arg;
19227c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp = thr->thr_port;
19237c478bd9Sstevel@tonic-gate 
19247c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wq_thread: enter");
19257c478bd9Sstevel@tonic-gate 
19267c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
19277c478bd9Sstevel@tonic-gate 	while (thr->thr_flags & USBSER_THR_RUNNING) {
19287c478bd9Sstevel@tonic-gate 		/*
19297c478bd9Sstevel@tonic-gate 		 * when woken, see what we should do
19307c478bd9Sstevel@tonic-gate 		 */
19317c478bd9Sstevel@tonic-gate 		if (thr->thr_flags & USBSER_THR_WAKE) {
19327c478bd9Sstevel@tonic-gate 			thr->thr_flags &= ~USBSER_THR_WAKE;
19337c478bd9Sstevel@tonic-gate 
19347c478bd9Sstevel@tonic-gate 			/*
19357c478bd9Sstevel@tonic-gate 			 * status callback pending?
19367c478bd9Sstevel@tonic-gate 			 */
19377c478bd9Sstevel@tonic-gate 			if (pp->port_flags & USBSER_FL_STATUS_CB) {
19387c478bd9Sstevel@tonic-gate 				usbser_status_proc_cb(pp);
19397c478bd9Sstevel@tonic-gate 			}
19407c478bd9Sstevel@tonic-gate 
19417c478bd9Sstevel@tonic-gate 			usbser_wmsg(pp);
19427c478bd9Sstevel@tonic-gate 		} else {
19437c478bd9Sstevel@tonic-gate 			/*
19447c478bd9Sstevel@tonic-gate 			 * sleep until woken up to do some work, e.g:
19457c478bd9Sstevel@tonic-gate 			 * - new message arrives;
19467c478bd9Sstevel@tonic-gate 			 * - data transmit completes;
19477c478bd9Sstevel@tonic-gate 			 * - status callback pending;
19487c478bd9Sstevel@tonic-gate 			 * - wq thread is cancelled;
19497c478bd9Sstevel@tonic-gate 			 */
19507c478bd9Sstevel@tonic-gate 			cv_wait(&thr->thr_cv, &pp->port_mutex);
19517c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh,
19527c478bd9Sstevel@tonic-gate 			    "usbser_wq_thread: wakeup");
19537c478bd9Sstevel@tonic-gate 		}
19547c478bd9Sstevel@tonic-gate 	}
19557c478bd9Sstevel@tonic-gate 	thr->thr_flags |= USBSER_THR_EXITED;
19567c478bd9Sstevel@tonic-gate 	cv_signal(&thr->thr_cv);
19577c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wq_thread: exit");
19587c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
19597c478bd9Sstevel@tonic-gate }
19607c478bd9Sstevel@tonic-gate 
19617c478bd9Sstevel@tonic-gate 
19627c478bd9Sstevel@tonic-gate /*
19637c478bd9Sstevel@tonic-gate  * thread handling read queue requests
19647c478bd9Sstevel@tonic-gate  */
19657c478bd9Sstevel@tonic-gate static void
usbser_rq_thread(void * arg)19667c478bd9Sstevel@tonic-gate usbser_rq_thread(void *arg)
19677c478bd9Sstevel@tonic-gate {
19687c478bd9Sstevel@tonic-gate 	usbser_thread_t	*thr = (usbser_thread_t *)arg;
19697c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp = thr->thr_port;
19707c478bd9Sstevel@tonic-gate 
19717c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_rq_thread: enter");
19727c478bd9Sstevel@tonic-gate 
19737c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
19747c478bd9Sstevel@tonic-gate 	while (thr->thr_flags & USBSER_THR_RUNNING) {
19757c478bd9Sstevel@tonic-gate 		/*
19767c478bd9Sstevel@tonic-gate 		 * read service routine will wake us when
19777c478bd9Sstevel@tonic-gate 		 * more space is available on the read queue
19787c478bd9Sstevel@tonic-gate 		 */
19797c478bd9Sstevel@tonic-gate 		if (thr->thr_flags & USBSER_THR_WAKE) {
19807c478bd9Sstevel@tonic-gate 			thr->thr_flags &= ~USBSER_THR_WAKE;
19817c478bd9Sstevel@tonic-gate 
19827c478bd9Sstevel@tonic-gate 			/*
19837c478bd9Sstevel@tonic-gate 			 * don't process messages until queue is enabled
19847c478bd9Sstevel@tonic-gate 			 */
19857c478bd9Sstevel@tonic-gate 			if (!pp->port_ttycommon.t_readq) {
19867c478bd9Sstevel@tonic-gate 
19877c478bd9Sstevel@tonic-gate 				continue;
19887c478bd9Sstevel@tonic-gate 			}
19897c478bd9Sstevel@tonic-gate 
19907c478bd9Sstevel@tonic-gate 			/*
19917c478bd9Sstevel@tonic-gate 			 * check whether we need to resume receive
19927c478bd9Sstevel@tonic-gate 			 */
19937c478bd9Sstevel@tonic-gate 			if (pp->port_flags & USBSER_FL_RX_STOPPED) {
19947c478bd9Sstevel@tonic-gate 				pp->port_flowc = pp->port_ttycommon.t_startc;
19957c478bd9Sstevel@tonic-gate 				usbser_inbound_flow_ctl(pp);
19967c478bd9Sstevel@tonic-gate 			}
19977c478bd9Sstevel@tonic-gate 
19987c478bd9Sstevel@tonic-gate 			/*
19997c478bd9Sstevel@tonic-gate 			 * grab more data if available
20007c478bd9Sstevel@tonic-gate 			 */
20017c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->port_mutex);
20027c478bd9Sstevel@tonic-gate 			usbser_rx_cb((caddr_t)pp);
20037c478bd9Sstevel@tonic-gate 			mutex_enter(&pp->port_mutex);
20047c478bd9Sstevel@tonic-gate 		} else {
20057c478bd9Sstevel@tonic-gate 			cv_wait(&thr->thr_cv, &pp->port_mutex);
20067c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh,
20077c478bd9Sstevel@tonic-gate 			    "usbser_rq_thread: wakeup");
20087c478bd9Sstevel@tonic-gate 		}
20097c478bd9Sstevel@tonic-gate 	}
20107c478bd9Sstevel@tonic-gate 	thr->thr_flags |= USBSER_THR_EXITED;
20117c478bd9Sstevel@tonic-gate 	cv_signal(&thr->thr_cv);
20127c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_RQ, pp->port_lh, "usbser_rq_thread: exit");
20137c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
20147c478bd9Sstevel@tonic-gate }
20157c478bd9Sstevel@tonic-gate 
20167c478bd9Sstevel@tonic-gate 
20177c478bd9Sstevel@tonic-gate /*
20187c478bd9Sstevel@tonic-gate  * DSD callbacks
20197c478bd9Sstevel@tonic-gate  * -------------
20207c478bd9Sstevel@tonic-gate  *
20217c478bd9Sstevel@tonic-gate  * Note: to avoid deadlocks with DSD, these callbacks
20227c478bd9Sstevel@tonic-gate  * should not call DSD functions that can block.
20237c478bd9Sstevel@tonic-gate  *
20247c478bd9Sstevel@tonic-gate  *
20257c478bd9Sstevel@tonic-gate  * transmit callback
20267c478bd9Sstevel@tonic-gate  *
20277c478bd9Sstevel@tonic-gate  * invoked by DSD when the last byte of data is transmitted over USB
20287c478bd9Sstevel@tonic-gate  */
20297c478bd9Sstevel@tonic-gate static void
usbser_tx_cb(caddr_t arg)20307c478bd9Sstevel@tonic-gate usbser_tx_cb(caddr_t arg)
20317c478bd9Sstevel@tonic-gate {
20327c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)arg;
20337c478bd9Sstevel@tonic-gate 	int		online;
20347c478bd9Sstevel@tonic-gate 
20357c478bd9Sstevel@tonic-gate 	online = usbser_dev_is_online(pp->port_usp);
20367c478bd9Sstevel@tonic-gate 
20377c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
20387c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_TX_CB, pp->port_lh,
2039112116d8Sfb 	    "usbser_tx_cb: act=%x curthread=%p", pp->port_act,
2040112116d8Sfb 	    (void *)curthread);
20417c478bd9Sstevel@tonic-gate 
20427c478bd9Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_TX);
20437c478bd9Sstevel@tonic-gate 
204461491074SRaymond Chen 	/*
204561491074SRaymond Chen 	 * as long as port access is ok and the port is not busy on
204661491074SRaymond Chen 	 * TX, break, ctrl or delay, the wq_thread should be waken
204761491074SRaymond Chen 	 * to do further process for next message
204861491074SRaymond Chen 	 */
204961491074SRaymond Chen 	if (online && USBSER_PORT_ACCESS_OK(pp) &&
205061491074SRaymond Chen 	    !USBSER_PORT_IS_BUSY_NON_RX(pp)) {
20517c478bd9Sstevel@tonic-gate 		/*
20527c478bd9Sstevel@tonic-gate 		 * wake wq thread for further data/ioctl processing
20537c478bd9Sstevel@tonic-gate 		 */
20547c478bd9Sstevel@tonic-gate 		usbser_thr_wake(&pp->port_wq_thread);
20557c478bd9Sstevel@tonic-gate 	}
20567c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
20577c478bd9Sstevel@tonic-gate }
20587c478bd9Sstevel@tonic-gate 
20597c478bd9Sstevel@tonic-gate 
20607c478bd9Sstevel@tonic-gate /*
20617c478bd9Sstevel@tonic-gate  * receive callback
20627c478bd9Sstevel@tonic-gate  *
20637c478bd9Sstevel@tonic-gate  * invoked by DSD when there is more data for us to pick
20647c478bd9Sstevel@tonic-gate  */
20657c478bd9Sstevel@tonic-gate static void
usbser_rx_cb(caddr_t arg)20667c478bd9Sstevel@tonic-gate usbser_rx_cb(caddr_t arg)
20677c478bd9Sstevel@tonic-gate {
20687c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)arg;
20697c478bd9Sstevel@tonic-gate 	queue_t		*rq, *wq;
20707c478bd9Sstevel@tonic-gate 	mblk_t		*mp;		/* current mblk */
20717c478bd9Sstevel@tonic-gate 	mblk_t		*data, *data_tail; /* M_DATA mblk list and its tail */
20727c478bd9Sstevel@tonic-gate 	mblk_t		*emp;		/* error (M_BREAK) mblk */
20737c478bd9Sstevel@tonic-gate 
20747c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh, "usbser_rx_cb");
20757c478bd9Sstevel@tonic-gate 
20767c478bd9Sstevel@tonic-gate 	if (!usbser_dev_is_online(pp->port_usp)) {
20777c478bd9Sstevel@tonic-gate 
20787c478bd9Sstevel@tonic-gate 		return;
20797c478bd9Sstevel@tonic-gate 	}
20807c478bd9Sstevel@tonic-gate 
20817c478bd9Sstevel@tonic-gate 	/* get data from DSD */
20827c478bd9Sstevel@tonic-gate 	if ((mp = USBSER_DS_RX(pp)) == NULL) {
20837c478bd9Sstevel@tonic-gate 
20847c478bd9Sstevel@tonic-gate 		return;
20857c478bd9Sstevel@tonic-gate 	}
20867c478bd9Sstevel@tonic-gate 
20877c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
20887c478bd9Sstevel@tonic-gate 	if ((!USBSER_PORT_ACCESS_OK(pp)) ||
20897c478bd9Sstevel@tonic-gate 	    ((pp->port_ttycommon.t_cflag & CREAD) == 0)) {
20907c478bd9Sstevel@tonic-gate 		freemsg(mp);
20917c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
20927c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_RX_CB, pp->port_lh,
20937c478bd9Sstevel@tonic-gate 		    "usbser_rx_cb: access not ok or receiver disabled");
20947c478bd9Sstevel@tonic-gate 
20957c478bd9Sstevel@tonic-gate 		return;
20967c478bd9Sstevel@tonic-gate 	}
20977c478bd9Sstevel@tonic-gate 
20987c478bd9Sstevel@tonic-gate 	usbser_serialize_port_act(pp, USBSER_ACT_RX);
20997c478bd9Sstevel@tonic-gate 
21007c478bd9Sstevel@tonic-gate 	rq = pp->port_ttycommon.t_readq;
21017c478bd9Sstevel@tonic-gate 	wq = pp->port_ttycommon.t_writeq;
21027c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
21037c478bd9Sstevel@tonic-gate 
21047c478bd9Sstevel@tonic-gate 	/*
21057c478bd9Sstevel@tonic-gate 	 * DSD data is a b_cont-linked list of M_DATA and M_BREAK blocks.
21067c478bd9Sstevel@tonic-gate 	 * M_DATA is correctly received data.
21077c478bd9Sstevel@tonic-gate 	 * M_BREAK is a character with either framing or parity error.
21087c478bd9Sstevel@tonic-gate 	 *
21097c478bd9Sstevel@tonic-gate 	 * this loop runs through the list of mblks. when it meets an M_BREAK,
21107c478bd9Sstevel@tonic-gate 	 * it sends all leading M_DATA's in one shot, then sends M_BREAK.
21117c478bd9Sstevel@tonic-gate 	 * in the trivial case when list contains only M_DATA's, the loop
21127c478bd9Sstevel@tonic-gate 	 * does nothing but set data variable.
21137c478bd9Sstevel@tonic-gate 	 */
21147c478bd9Sstevel@tonic-gate 	data = data_tail = NULL;
21157c478bd9Sstevel@tonic-gate 	while (mp) {
21167c478bd9Sstevel@tonic-gate 		/*
21177c478bd9Sstevel@tonic-gate 		 * skip data until we meet M_BREAK or end of list
21187c478bd9Sstevel@tonic-gate 		 */
21197c478bd9Sstevel@tonic-gate 		if (DB_TYPE(mp) == M_DATA) {
21207c478bd9Sstevel@tonic-gate 			if (data == NULL) {
21217c478bd9Sstevel@tonic-gate 				data = mp;
21227c478bd9Sstevel@tonic-gate 			}
21237c478bd9Sstevel@tonic-gate 			data_tail = mp;
21247c478bd9Sstevel@tonic-gate 			mp = mp->b_cont;
21257c478bd9Sstevel@tonic-gate 
21267c478bd9Sstevel@tonic-gate 			continue;
21277c478bd9Sstevel@tonic-gate 		}
21287c478bd9Sstevel@tonic-gate 
21297c478bd9Sstevel@tonic-gate 		/* detach data list from mp */
21307c478bd9Sstevel@tonic-gate 		if (data_tail) {
21317c478bd9Sstevel@tonic-gate 			data_tail->b_cont = NULL;
21327c478bd9Sstevel@tonic-gate 		}
21337c478bd9Sstevel@tonic-gate 		/* detach emp from the list */
21347c478bd9Sstevel@tonic-gate 		emp = mp;
21357c478bd9Sstevel@tonic-gate 		mp = mp->b_cont;
21367c478bd9Sstevel@tonic-gate 		emp->b_cont = NULL;
21377c478bd9Sstevel@tonic-gate 
21387c478bd9Sstevel@tonic-gate 		/* DSD shouldn't send anything but M_DATA or M_BREAK */
21397c478bd9Sstevel@tonic-gate 		if ((DB_TYPE(emp) != M_BREAK) || (MBLKL(emp) != 2)) {
21407c478bd9Sstevel@tonic-gate 			freemsg(emp);
21417c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_RX_CB, pp->port_lh,
21427c478bd9Sstevel@tonic-gate 			    "usbser_rx_cb: bad message");
21437c478bd9Sstevel@tonic-gate 
21447c478bd9Sstevel@tonic-gate 			continue;
21457c478bd9Sstevel@tonic-gate 		}
21467c478bd9Sstevel@tonic-gate 
21477c478bd9Sstevel@tonic-gate 		/*
21487c478bd9Sstevel@tonic-gate 		 * first tweak and send M_DATA's
21497c478bd9Sstevel@tonic-gate 		 */
21507c478bd9Sstevel@tonic-gate 		if (data) {
21517c478bd9Sstevel@tonic-gate 			usbser_rx_massage_data(pp, data);
21527c478bd9Sstevel@tonic-gate 			usbser_rx_cb_put(pp, rq, wq, data);
21537c478bd9Sstevel@tonic-gate 			data = data_tail = NULL;
21547c478bd9Sstevel@tonic-gate 		}
21557c478bd9Sstevel@tonic-gate 
21567c478bd9Sstevel@tonic-gate 		/*
21577c478bd9Sstevel@tonic-gate 		 * now tweak and send M_BREAK
21587c478bd9Sstevel@tonic-gate 		 */
21597c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
21607c478bd9Sstevel@tonic-gate 		usbser_rx_massage_mbreak(pp, emp);
21617c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
21627c478bd9Sstevel@tonic-gate 		usbser_rx_cb_put(pp, rq, wq, emp);
21637c478bd9Sstevel@tonic-gate 	}
21647c478bd9Sstevel@tonic-gate 
21657c478bd9Sstevel@tonic-gate 	/* send the rest of the data, if any */
21667c478bd9Sstevel@tonic-gate 	if (data) {
21677c478bd9Sstevel@tonic-gate 		usbser_rx_massage_data(pp, data);
21687c478bd9Sstevel@tonic-gate 		usbser_rx_cb_put(pp, rq, wq, data);
21697c478bd9Sstevel@tonic-gate 	}
21707c478bd9Sstevel@tonic-gate 
21717c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
21727c478bd9Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_RX);
21737c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
21747c478bd9Sstevel@tonic-gate }
21757c478bd9Sstevel@tonic-gate 
21767c478bd9Sstevel@tonic-gate /*
21777c478bd9Sstevel@tonic-gate  * the joys of termio -- this is to accomodate Unix98 assertion:
21787c478bd9Sstevel@tonic-gate  *
21797c478bd9Sstevel@tonic-gate  *   If PARENB is supported and is set, when PARMRK is set, and CSIZE is
21807c478bd9Sstevel@tonic-gate  *   set to CS8, and IGNPAR is clear, and ISTRIP is clear, a valid
21817c478bd9Sstevel@tonic-gate  *   character of '\377' is read as '\377', '\377'.
21827c478bd9Sstevel@tonic-gate  *
21837c478bd9Sstevel@tonic-gate  *   Posix Ref: Assertion 7.1.2.2-16(C)
21847c478bd9Sstevel@tonic-gate  *
21857c478bd9Sstevel@tonic-gate  * this requires the driver to scan every incoming valid character
21867c478bd9Sstevel@tonic-gate  */
21877c478bd9Sstevel@tonic-gate static void
usbser_rx_massage_data(usbser_port_t * pp,mblk_t * mp)21887c478bd9Sstevel@tonic-gate usbser_rx_massage_data(usbser_port_t *pp, mblk_t *mp)
21897c478bd9Sstevel@tonic-gate {
21907c478bd9Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
21917c478bd9Sstevel@tonic-gate 	uchar_t		*p;
21927c478bd9Sstevel@tonic-gate 	mblk_t		*newmp;
21937c478bd9Sstevel@tonic-gate 	int		tailsz;
21947c478bd9Sstevel@tonic-gate 
21957c478bd9Sstevel@tonic-gate 	/* avoid scanning if possible */
21967c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
21977c478bd9Sstevel@tonic-gate 	if (!((tp->t_cflag & PARENB) && (tp->t_iflag & PARMRK) &&
21987c478bd9Sstevel@tonic-gate 	    ((tp->t_cflag & CSIZE) == CS8) &&
21997c478bd9Sstevel@tonic-gate 	    ((tp->t_iflag & (IGNPAR|ISTRIP)) == 0))) {
22007c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
22017c478bd9Sstevel@tonic-gate 
22027c478bd9Sstevel@tonic-gate 		return;
22037c478bd9Sstevel@tonic-gate 	}
22047c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
22057c478bd9Sstevel@tonic-gate 
22067c478bd9Sstevel@tonic-gate 	while (mp) {
22077c478bd9Sstevel@tonic-gate 		for (p = mp->b_rptr; p < mp->b_wptr; ) {
22087c478bd9Sstevel@tonic-gate 			if (*p++ != 0377) {
22097c478bd9Sstevel@tonic-gate 
22107c478bd9Sstevel@tonic-gate 				continue;
22117c478bd9Sstevel@tonic-gate 			}
22127c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh,
2213112116d8Sfb 			    "usbser_rx_massage_data: mp=%p off=%ld(%ld)",
2214d29f5a71Szhigang lu - Sun Microsystems - Beijing China 			    (void *)mp, _PTRDIFF(p,  mp->b_rptr) - 1,
2215112116d8Sfb 			    (long)MBLKL(mp));
22167c478bd9Sstevel@tonic-gate 
22177c478bd9Sstevel@tonic-gate 			/*
22187c478bd9Sstevel@tonic-gate 			 * insert another 0377 after this one. all data after
22197c478bd9Sstevel@tonic-gate 			 * the original 0377 have to be copied to the new mblk
22207c478bd9Sstevel@tonic-gate 			 */
2221d29f5a71Szhigang lu - Sun Microsystems - Beijing China 			tailsz = _PTRDIFF(mp->b_wptr, p);
22227c478bd9Sstevel@tonic-gate 			if ((newmp = allocb(tailsz + 1, BPRI_HI)) == NULL) {
22237c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_RX_CB, pp->port_lh,
22247c478bd9Sstevel@tonic-gate 				    "usbser_rx_massage_data: allocb failed");
22257c478bd9Sstevel@tonic-gate 
22267c478bd9Sstevel@tonic-gate 				continue;
22277c478bd9Sstevel@tonic-gate 			}
22287c478bd9Sstevel@tonic-gate 
22297c478bd9Sstevel@tonic-gate 			/* fill in the new mblk */
22307c478bd9Sstevel@tonic-gate 			*newmp->b_wptr++ = 0377;
22317c478bd9Sstevel@tonic-gate 			if (tailsz > 0) {
22327c478bd9Sstevel@tonic-gate 				bcopy(p, newmp->b_wptr, tailsz);
22337c478bd9Sstevel@tonic-gate 				newmp->b_wptr += tailsz;
22347c478bd9Sstevel@tonic-gate 			}
22357c478bd9Sstevel@tonic-gate 			/* shrink the original mblk */
22367c478bd9Sstevel@tonic-gate 			mp->b_wptr = p;
22377c478bd9Sstevel@tonic-gate 
22387c478bd9Sstevel@tonic-gate 			newmp->b_cont = mp->b_cont;
22397c478bd9Sstevel@tonic-gate 			mp->b_cont = newmp;
22407c478bd9Sstevel@tonic-gate 			p = newmp->b_rptr + 1;
22417c478bd9Sstevel@tonic-gate 			mp = newmp;
22427c478bd9Sstevel@tonic-gate 		}
22437c478bd9Sstevel@tonic-gate 		mp = mp->b_cont;
22447c478bd9Sstevel@tonic-gate 	}
22457c478bd9Sstevel@tonic-gate }
22467c478bd9Sstevel@tonic-gate 
22477c478bd9Sstevel@tonic-gate /*
22487c478bd9Sstevel@tonic-gate  * more joys of termio
22497c478bd9Sstevel@tonic-gate  */
22507c478bd9Sstevel@tonic-gate static void
usbser_rx_massage_mbreak(usbser_port_t * pp,mblk_t * mp)22517c478bd9Sstevel@tonic-gate usbser_rx_massage_mbreak(usbser_port_t *pp, mblk_t *mp)
22527c478bd9Sstevel@tonic-gate {
22537c478bd9Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
22547c478bd9Sstevel@tonic-gate 	uchar_t		err, c;
22557c478bd9Sstevel@tonic-gate 
22567c478bd9Sstevel@tonic-gate 	err = *mp->b_rptr;
22577c478bd9Sstevel@tonic-gate 	c = *(mp->b_rptr + 1);
22587c478bd9Sstevel@tonic-gate 
22597c478bd9Sstevel@tonic-gate 	if ((err & (DS_FRAMING_ERR | DS_BREAK_ERR)) && (c == 0)) {
22607c478bd9Sstevel@tonic-gate 		/* break */
22617c478bd9Sstevel@tonic-gate 		mp->b_rptr += 2;
22627c478bd9Sstevel@tonic-gate 	} else if (!(tp->t_iflag & INPCK) && (err & (DS_PARITY_ERR))) {
22637c478bd9Sstevel@tonic-gate 		/* Posix Ref: Assertion 7.1.2.2-20(C) */
22647c478bd9Sstevel@tonic-gate 		mp->b_rptr++;
22657c478bd9Sstevel@tonic-gate 		DB_TYPE(mp) = M_DATA;
22667c478bd9Sstevel@tonic-gate 	} else {
22677c478bd9Sstevel@tonic-gate 		/* for ldterm to handle */
22687c478bd9Sstevel@tonic-gate 		mp->b_rptr++;
22697c478bd9Sstevel@tonic-gate 	}
22707c478bd9Sstevel@tonic-gate 
22717c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh,
2272112116d8Sfb 	    "usbser_rx_massage_mbreak: type=%x len=%ld [0]=0%o",
2273112116d8Sfb 	    DB_TYPE(mp), (long)MBLKL(mp), (MBLKL(mp) > 0) ? *mp->b_rptr : 45);
22747c478bd9Sstevel@tonic-gate }
22757c478bd9Sstevel@tonic-gate 
22767c478bd9Sstevel@tonic-gate 
22777c478bd9Sstevel@tonic-gate /*
22787c478bd9Sstevel@tonic-gate  * in rx callback, try to send an mblk upstream
22797c478bd9Sstevel@tonic-gate  */
22807c478bd9Sstevel@tonic-gate static void
usbser_rx_cb_put(usbser_port_t * pp,queue_t * rq,queue_t * wq,mblk_t * mp)22817c478bd9Sstevel@tonic-gate usbser_rx_cb_put(usbser_port_t *pp, queue_t *rq, queue_t *wq, mblk_t *mp)
22827c478bd9Sstevel@tonic-gate {
22837c478bd9Sstevel@tonic-gate 	if (canputnext(rq)) {
22847c478bd9Sstevel@tonic-gate 		putnext(rq, mp);
22857c478bd9Sstevel@tonic-gate 	} else if (canput(rq) && putq(rq, mp)) {
22867c478bd9Sstevel@tonic-gate 		/*
22877c478bd9Sstevel@tonic-gate 		 * full queue indicates the need for inbound flow control
22887c478bd9Sstevel@tonic-gate 		 */
22897c478bd9Sstevel@tonic-gate 		(void) putctl(wq, M_STOPI);
22907c478bd9Sstevel@tonic-gate 		usbser_st_put_stopi++;
22917c478bd9Sstevel@tonic-gate 
22927c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_RX_CB, pp->port_lh,
22937c478bd9Sstevel@tonic-gate 		    "usbser_rx_cb: cannot putnext, flow ctl");
22947c478bd9Sstevel@tonic-gate 	} else {
22957c478bd9Sstevel@tonic-gate 		freemsg(mp);
22967c478bd9Sstevel@tonic-gate 		usbser_st_rx_data_loss++;
22977c478bd9Sstevel@tonic-gate 		(void) putctl(wq, M_STOPI);
22987c478bd9Sstevel@tonic-gate 		usbser_st_put_stopi++;
22997c478bd9Sstevel@tonic-gate 
23007c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L1(DPRINT_RX_CB, pp->port_lh,
2301d291d9f2Sfrits 		    "input overrun");
23027c478bd9Sstevel@tonic-gate 	}
23037c478bd9Sstevel@tonic-gate }
23047c478bd9Sstevel@tonic-gate 
23057c478bd9Sstevel@tonic-gate 
23067c478bd9Sstevel@tonic-gate /*
23077c478bd9Sstevel@tonic-gate  * modem status change callback
23087c478bd9Sstevel@tonic-gate  *
23097c478bd9Sstevel@tonic-gate  * each time external status lines are changed, DSD calls this routine
23107c478bd9Sstevel@tonic-gate  */
23117c478bd9Sstevel@tonic-gate static void
usbser_status_cb(caddr_t arg)23127c478bd9Sstevel@tonic-gate usbser_status_cb(caddr_t arg)
23137c478bd9Sstevel@tonic-gate {
23147c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)arg;
23157c478bd9Sstevel@tonic-gate 
23167c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh, "usbser_status_cb");
23177c478bd9Sstevel@tonic-gate 
23187c478bd9Sstevel@tonic-gate 	if (!usbser_dev_is_online(pp->port_usp)) {
23197c478bd9Sstevel@tonic-gate 
23207c478bd9Sstevel@tonic-gate 		return;
23217c478bd9Sstevel@tonic-gate 	}
23227c478bd9Sstevel@tonic-gate 
23237c478bd9Sstevel@tonic-gate 	/*
23247c478bd9Sstevel@tonic-gate 	 * actual processing will be done in usbser_status_proc_cb()
23257c478bd9Sstevel@tonic-gate 	 * running in wq thread
23267c478bd9Sstevel@tonic-gate 	 */
23277c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
23287c478bd9Sstevel@tonic-gate 	if (USBSER_PORT_ACCESS_OK(pp) || USBSER_IS_OPENING(pp)) {
23297c478bd9Sstevel@tonic-gate 		pp->port_flags |= USBSER_FL_STATUS_CB;
23307c478bd9Sstevel@tonic-gate 		usbser_thr_wake(&pp->port_wq_thread);
23317c478bd9Sstevel@tonic-gate 	}
23327c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
23337c478bd9Sstevel@tonic-gate }
23347c478bd9Sstevel@tonic-gate 
23357c478bd9Sstevel@tonic-gate 
23367c478bd9Sstevel@tonic-gate /*
23377c478bd9Sstevel@tonic-gate  * modem status change
23387c478bd9Sstevel@tonic-gate  */
23397c478bd9Sstevel@tonic-gate static void
usbser_status_proc_cb(usbser_port_t * pp)23407c478bd9Sstevel@tonic-gate usbser_status_proc_cb(usbser_port_t *pp)
23417c478bd9Sstevel@tonic-gate {
23427c478bd9Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
23437c478bd9Sstevel@tonic-gate 	queue_t		*rq, *wq;
23447c478bd9Sstevel@tonic-gate 	int		status;
23457c478bd9Sstevel@tonic-gate 	int		drop_dtr = 0;
23467c478bd9Sstevel@tonic-gate 	int		rq_msg = 0, wq_msg = 0;
23477c478bd9Sstevel@tonic-gate 
23487c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh, "usbser_status_proc_cb");
23497c478bd9Sstevel@tonic-gate 
23507c478bd9Sstevel@tonic-gate 	pp->port_flags &= ~USBSER_FL_STATUS_CB;
23517c478bd9Sstevel@tonic-gate 
23527c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
23537c478bd9Sstevel@tonic-gate 	if (!usbser_dev_is_online(pp->port_usp)) {
23547c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
23557c478bd9Sstevel@tonic-gate 
23567c478bd9Sstevel@tonic-gate 		return;
23577c478bd9Sstevel@tonic-gate 	}
23587c478bd9Sstevel@tonic-gate 
23597c478bd9Sstevel@tonic-gate 	/* get modem status */
23607c478bd9Sstevel@tonic-gate 	if (USBSER_DS_GET_MODEM_CTL(pp, -1, &status) != USB_SUCCESS) {
23617c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
23627c478bd9Sstevel@tonic-gate 
23637c478bd9Sstevel@tonic-gate 		return;
23647c478bd9Sstevel@tonic-gate 	}
23657c478bd9Sstevel@tonic-gate 
23667c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
23677c478bd9Sstevel@tonic-gate 	usbser_serialize_port_act(pp, USBSER_ACT_CTL);
23687c478bd9Sstevel@tonic-gate 
23697c478bd9Sstevel@tonic-gate 	rq = pp->port_ttycommon.t_readq;
23707c478bd9Sstevel@tonic-gate 	wq = pp->port_ttycommon.t_writeq;
23717c478bd9Sstevel@tonic-gate 
23727c478bd9Sstevel@tonic-gate 	/*
23737c478bd9Sstevel@tonic-gate 	 * outbound flow control
23747c478bd9Sstevel@tonic-gate 	 */
23757c478bd9Sstevel@tonic-gate 	if (tp->t_cflag & CRTSCTS) {
23767c478bd9Sstevel@tonic-gate 		if (!(status & TIOCM_CTS)) {
23777c478bd9Sstevel@tonic-gate 			/*
23787c478bd9Sstevel@tonic-gate 			 * CTS dropped, stop xmit
23797c478bd9Sstevel@tonic-gate 			 */
23807c478bd9Sstevel@tonic-gate 			if (!(pp->port_flags & USBSER_FL_TX_STOPPED)) {
23817c478bd9Sstevel@tonic-gate 				wq_msg = M_STOP;
23827c478bd9Sstevel@tonic-gate 			}
23837c478bd9Sstevel@tonic-gate 		} else if (pp->port_flags & USBSER_FL_TX_STOPPED) {
23847c478bd9Sstevel@tonic-gate 			/*
23857c478bd9Sstevel@tonic-gate 			 * CTS raised, resume xmit
23867c478bd9Sstevel@tonic-gate 			 */
23877c478bd9Sstevel@tonic-gate 			wq_msg = M_START;
23887c478bd9Sstevel@tonic-gate 		}
23897c478bd9Sstevel@tonic-gate 	}
23907c478bd9Sstevel@tonic-gate 
23917c478bd9Sstevel@tonic-gate 	/*
23927c478bd9Sstevel@tonic-gate 	 * check carrier
23937c478bd9Sstevel@tonic-gate 	 */
23947c478bd9Sstevel@tonic-gate 	if ((status & TIOCM_CD) || (tp->t_flags & TS_SOFTCAR)) {
23957c478bd9Sstevel@tonic-gate 		/*
23967c478bd9Sstevel@tonic-gate 		 * carrier present
23977c478bd9Sstevel@tonic-gate 		 */
23987c478bd9Sstevel@tonic-gate 		if ((pp->port_flags & USBSER_FL_CARR_ON) == 0) {
23997c478bd9Sstevel@tonic-gate 			pp->port_flags |= USBSER_FL_CARR_ON;
24007c478bd9Sstevel@tonic-gate 
24017c478bd9Sstevel@tonic-gate 			rq_msg = M_UNHANGUP;
24027c478bd9Sstevel@tonic-gate 			/*
24037c478bd9Sstevel@tonic-gate 			 * wake open
24047c478bd9Sstevel@tonic-gate 			 */
24057c478bd9Sstevel@tonic-gate 			if (pp->port_flags & USBSER_FL_WOPEN) {
24067c478bd9Sstevel@tonic-gate 				cv_broadcast(&pp->port_car_cv);
24077c478bd9Sstevel@tonic-gate 			}
24087c478bd9Sstevel@tonic-gate 
24097c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh,
24107c478bd9Sstevel@tonic-gate 			    "usbser_status_cb: carr on");
24117c478bd9Sstevel@tonic-gate 		}
24127c478bd9Sstevel@tonic-gate 	} else if (pp->port_flags & USBSER_FL_CARR_ON) {
24137c478bd9Sstevel@tonic-gate 		pp->port_flags &= ~USBSER_FL_CARR_ON;
24147c478bd9Sstevel@tonic-gate 		/*
24157c478bd9Sstevel@tonic-gate 		 * carrier went away: if not local line, drop DTR
24167c478bd9Sstevel@tonic-gate 		 */
24177c478bd9Sstevel@tonic-gate 		if (!(tp->t_cflag & CLOCAL)) {
24187c478bd9Sstevel@tonic-gate 			drop_dtr = 1;
24197c478bd9Sstevel@tonic-gate 			rq_msg = M_HANGUP;
24207c478bd9Sstevel@tonic-gate 		}
24217c478bd9Sstevel@tonic-gate 		if ((pp->port_flags & USBSER_FL_TX_STOPPED) && (wq_msg == 0)) {
24227c478bd9Sstevel@tonic-gate 			wq_msg = M_START;
24237c478bd9Sstevel@tonic-gate 		}
24247c478bd9Sstevel@tonic-gate 
24257c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh,
24267c478bd9Sstevel@tonic-gate 		    "usbser_status_cb: carr off");
24277c478bd9Sstevel@tonic-gate 	}
24287c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
24297c478bd9Sstevel@tonic-gate 
24307c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh,
24317c478bd9Sstevel@tonic-gate 	    "usbser_status_cb: rq_msg=%d wq_msg=%d", rq_msg, wq_msg);
24327c478bd9Sstevel@tonic-gate 
24337c478bd9Sstevel@tonic-gate 	/*
24347c478bd9Sstevel@tonic-gate 	 * commit postponed actions now
24357c478bd9Sstevel@tonic-gate 	 * do so only if port is fully open (queues are enabled)
24367c478bd9Sstevel@tonic-gate 	 */
24377c478bd9Sstevel@tonic-gate 	if (rq) {
24387c478bd9Sstevel@tonic-gate 		if (rq_msg) {
24397c478bd9Sstevel@tonic-gate 			(void) putnextctl(rq, rq_msg);
24407c478bd9Sstevel@tonic-gate 		}
24417c478bd9Sstevel@tonic-gate 		if (drop_dtr) {
24427c478bd9Sstevel@tonic-gate 			(void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR, 0);
24437c478bd9Sstevel@tonic-gate 		}
24447c478bd9Sstevel@tonic-gate 		if (wq_msg) {
24457c478bd9Sstevel@tonic-gate 			(void) putctl(wq, wq_msg);
24467c478bd9Sstevel@tonic-gate 		}
24477c478bd9Sstevel@tonic-gate 	}
24487c478bd9Sstevel@tonic-gate 
24497c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
24507c478bd9Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_CTL);
24517c478bd9Sstevel@tonic-gate }
24527c478bd9Sstevel@tonic-gate 
24537c478bd9Sstevel@tonic-gate 
24547c478bd9Sstevel@tonic-gate /*
24557c478bd9Sstevel@tonic-gate  * serial support
24567c478bd9Sstevel@tonic-gate  * --------------
24577c478bd9Sstevel@tonic-gate  *
24587c478bd9Sstevel@tonic-gate  *
24597c478bd9Sstevel@tonic-gate  * this routine is run by wq thread every time it's woken,
24607c478bd9Sstevel@tonic-gate  * i.e. when the queue contains messages to process
24617c478bd9Sstevel@tonic-gate  */
24627c478bd9Sstevel@tonic-gate static void
usbser_wmsg(usbser_port_t * pp)24637c478bd9Sstevel@tonic-gate usbser_wmsg(usbser_port_t *pp)
24647c478bd9Sstevel@tonic-gate {
24657c478bd9Sstevel@tonic-gate 	queue_t		*q = pp->port_ttycommon.t_writeq;
24667c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
24677c478bd9Sstevel@tonic-gate 	int		msgtype;
24687c478bd9Sstevel@tonic-gate 
24697c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->port_mutex));
24707c478bd9Sstevel@tonic-gate 
24717c478bd9Sstevel@tonic-gate 	if (q == NULL) {
24727c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_WQ, pp->port_lh, "usbser_wmsg: q=NULL");
24737c478bd9Sstevel@tonic-gate 
24747c478bd9Sstevel@tonic-gate 		return;
24757c478bd9Sstevel@tonic-gate 	}
24767c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wmsg: q=%p act=%x 0x%x",
2477112116d8Sfb 	    (void *)q, pp->port_act, q->q_first ? DB_TYPE(q->q_first) : 0xff);
24787c478bd9Sstevel@tonic-gate 
24797c478bd9Sstevel@tonic-gate 	while ((mp = getq(q)) != NULL) {
24807c478bd9Sstevel@tonic-gate 		msgtype = DB_TYPE(mp);
24817c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wmsg: "
24827c478bd9Sstevel@tonic-gate 		    "type=%s (0x%x)", usbser_msgtype2str(msgtype), msgtype);
24837c478bd9Sstevel@tonic-gate 
24847c478bd9Sstevel@tonic-gate 		switch (msgtype) {
24857c478bd9Sstevel@tonic-gate 		/*
24867c478bd9Sstevel@tonic-gate 		 * high-priority messages
24877c478bd9Sstevel@tonic-gate 		 */
24887c478bd9Sstevel@tonic-gate 		case M_STOP:
24897c478bd9Sstevel@tonic-gate 			usbser_stop(pp, mp);
24907c478bd9Sstevel@tonic-gate 
24917c478bd9Sstevel@tonic-gate 			break;
24927c478bd9Sstevel@tonic-gate 		case M_START:
24937c478bd9Sstevel@tonic-gate 			usbser_start(pp, mp);
24947c478bd9Sstevel@tonic-gate 
24957c478bd9Sstevel@tonic-gate 			break;
24967c478bd9Sstevel@tonic-gate 		case M_STOPI:
24977c478bd9Sstevel@tonic-gate 			usbser_stopi(pp, mp);
24987c478bd9Sstevel@tonic-gate 
24997c478bd9Sstevel@tonic-gate 			break;
25007c478bd9Sstevel@tonic-gate 		case M_STARTI:
25017c478bd9Sstevel@tonic-gate 			usbser_starti(pp, mp);
25027c478bd9Sstevel@tonic-gate 
25037c478bd9Sstevel@tonic-gate 			break;
25047c478bd9Sstevel@tonic-gate 		case M_IOCDATA:
25057c478bd9Sstevel@tonic-gate 			usbser_iocdata(pp, mp);
25067c478bd9Sstevel@tonic-gate 
25077c478bd9Sstevel@tonic-gate 			break;
25087c478bd9Sstevel@tonic-gate 		case M_FLUSH:
25097c478bd9Sstevel@tonic-gate 			usbser_flush(pp, mp);
25107c478bd9Sstevel@tonic-gate 
25117c478bd9Sstevel@tonic-gate 			break;
25127c478bd9Sstevel@tonic-gate 		/*
25137c478bd9Sstevel@tonic-gate 		 * normal-priority messages
25147c478bd9Sstevel@tonic-gate 		 */
25157c478bd9Sstevel@tonic-gate 		case M_BREAK:
25167c478bd9Sstevel@tonic-gate 			usbser_break(pp, mp);
25177c478bd9Sstevel@tonic-gate 
25187c478bd9Sstevel@tonic-gate 			break;
25197c478bd9Sstevel@tonic-gate 		case M_DELAY:
25207c478bd9Sstevel@tonic-gate 			usbser_delay(pp, mp);
25217c478bd9Sstevel@tonic-gate 
25227c478bd9Sstevel@tonic-gate 			break;
25237c478bd9Sstevel@tonic-gate 		case M_DATA:
25247c478bd9Sstevel@tonic-gate 			if (usbser_data(pp, mp) != USB_SUCCESS) {
25257c478bd9Sstevel@tonic-gate 				(void) putbq(q, mp);
25267c478bd9Sstevel@tonic-gate 
25277c478bd9Sstevel@tonic-gate 				return;
25287c478bd9Sstevel@tonic-gate 			}
25297c478bd9Sstevel@tonic-gate 
25307c478bd9Sstevel@tonic-gate 			break;
25317c478bd9Sstevel@tonic-gate 		case M_IOCTL:
25327c478bd9Sstevel@tonic-gate 			if (usbser_ioctl(pp, mp) != USB_SUCCESS) {
25337c478bd9Sstevel@tonic-gate 				(void) putbq(q, mp);
25347c478bd9Sstevel@tonic-gate 
25357c478bd9Sstevel@tonic-gate 				return;
25367c478bd9Sstevel@tonic-gate 			}
25377c478bd9Sstevel@tonic-gate 
25387c478bd9Sstevel@tonic-gate 			break;
25397c478bd9Sstevel@tonic-gate 		default:
25407c478bd9Sstevel@tonic-gate 			freemsg(mp);
25417c478bd9Sstevel@tonic-gate 
25427c478bd9Sstevel@tonic-gate 			break;
25437c478bd9Sstevel@tonic-gate 		}
25447c478bd9Sstevel@tonic-gate 	}
25457c478bd9Sstevel@tonic-gate }
25467c478bd9Sstevel@tonic-gate 
25477c478bd9Sstevel@tonic-gate 
25487c478bd9Sstevel@tonic-gate /*
25497c478bd9Sstevel@tonic-gate  * process M_DATA message
25507c478bd9Sstevel@tonic-gate  */
25517c478bd9Sstevel@tonic-gate static int
usbser_data(usbser_port_t * pp,mblk_t * mp)25527c478bd9Sstevel@tonic-gate usbser_data(usbser_port_t *pp, mblk_t *mp)
25537c478bd9Sstevel@tonic-gate {
25547c478bd9Sstevel@tonic-gate 	/* put off until current transfer ends or delay is over */
25557c478bd9Sstevel@tonic-gate 	if ((pp->port_act & USBSER_ACT_TX) ||
25567c478bd9Sstevel@tonic-gate 	    (pp->port_act & USBSER_ACT_DELAY)) {
25577c478bd9Sstevel@tonic-gate 
25587c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
25597c478bd9Sstevel@tonic-gate 	}
25602172f489Sgd 	if (MBLKL(mp) <= 0) {
25617c478bd9Sstevel@tonic-gate 		freemsg(mp);
25627c478bd9Sstevel@tonic-gate 
25637c478bd9Sstevel@tonic-gate 		return (USB_SUCCESS);
25647c478bd9Sstevel@tonic-gate 	}
25657c478bd9Sstevel@tonic-gate 
25667c478bd9Sstevel@tonic-gate 	pp->port_act |= USBSER_ACT_TX;
25677c478bd9Sstevel@tonic-gate 	pp->port_wq_data_cnt -= msgdsize(mp);
25687c478bd9Sstevel@tonic-gate 
25697c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
25707c478bd9Sstevel@tonic-gate 	/* DSD is required to accept data block in any case */
25717c478bd9Sstevel@tonic-gate 	(void) USBSER_DS_TX(pp, mp);
25727c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
25737c478bd9Sstevel@tonic-gate 
25747c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
25757c478bd9Sstevel@tonic-gate }
25767c478bd9Sstevel@tonic-gate 
25777c478bd9Sstevel@tonic-gate 
25787c478bd9Sstevel@tonic-gate /*
25797c478bd9Sstevel@tonic-gate  * process an M_IOCTL message
25807c478bd9Sstevel@tonic-gate  */
25817c478bd9Sstevel@tonic-gate static int
usbser_ioctl(usbser_port_t * pp,mblk_t * mp)25827c478bd9Sstevel@tonic-gate usbser_ioctl(usbser_port_t *pp, mblk_t *mp)
25837c478bd9Sstevel@tonic-gate {
25847c478bd9Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
25857c478bd9Sstevel@tonic-gate 	queue_t		*q = tp->t_writeq;
25867c478bd9Sstevel@tonic-gate 	struct iocblk	*iocp;
25877c478bd9Sstevel@tonic-gate 	int		cmd;
25887c478bd9Sstevel@tonic-gate 	mblk_t		*datamp;
2589d235eb05SRichard PALO 	int		error = 0, rval = USB_SUCCESS;
25907c478bd9Sstevel@tonic-gate 	int		val;
25917c478bd9Sstevel@tonic-gate 
25927c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->port_mutex));
25937c478bd9Sstevel@tonic-gate 	ASSERT(DB_TYPE(mp) == M_IOCTL);
25947c478bd9Sstevel@tonic-gate 
25957c478bd9Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
25967c478bd9Sstevel@tonic-gate 	cmd = iocp->ioc_cmd;
25977c478bd9Sstevel@tonic-gate 
25987c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_IOCTL, pp->port_lh, "usbser_ioctl: "
2599112116d8Sfb 	    "mp=%p %s (0x%x)", (void *)mp, usbser_ioctl2str(cmd), cmd);
26007c478bd9Sstevel@tonic-gate 
26017c478bd9Sstevel@tonic-gate 	if (tp->t_iocpending != NULL) {
26027c478bd9Sstevel@tonic-gate 		/*
26037c478bd9Sstevel@tonic-gate 		 * We were holding an ioctl response pending the
26047c478bd9Sstevel@tonic-gate 		 * availability of an mblk to hold data to be passed up;
26057c478bd9Sstevel@tonic-gate 		 * another ioctl came through, which means that ioctl
26067c478bd9Sstevel@tonic-gate 		 * must have timed out or been aborted.
26077c478bd9Sstevel@tonic-gate 		 */
26087c478bd9Sstevel@tonic-gate 		freemsg(tp->t_iocpending);
26097c478bd9Sstevel@tonic-gate 		tp->t_iocpending = NULL;
26107c478bd9Sstevel@tonic-gate 	}
26117c478bd9Sstevel@tonic-gate 
26127c478bd9Sstevel@tonic-gate 	switch (cmd) {
26137c478bd9Sstevel@tonic-gate 	case TIOCMGET:
26147c478bd9Sstevel@tonic-gate 	case TIOCMBIC:
26157c478bd9Sstevel@tonic-gate 	case TIOCMBIS:
26167c478bd9Sstevel@tonic-gate 	case TIOCMSET:
261728cdc3d7Sszhou 	case CONSOPENPOLLEDIO:
261828cdc3d7Sszhou 	case CONSCLOSEPOLLEDIO:
261928cdc3d7Sszhou 	case CONSSETABORTENABLE:
262028cdc3d7Sszhou 	case CONSGETABORTENABLE:
26217c478bd9Sstevel@tonic-gate 		/*
26227c478bd9Sstevel@tonic-gate 		 * For the above ioctls do not call ttycommon_ioctl() because
26237c478bd9Sstevel@tonic-gate 		 * this function frees up the message block (mp->b_cont) that
26247c478bd9Sstevel@tonic-gate 		 * contains the address of the user variable where we need to
26257c478bd9Sstevel@tonic-gate 		 * pass back the bit array.
26267c478bd9Sstevel@tonic-gate 		 */
26277c478bd9Sstevel@tonic-gate 		error = -1;
26287c478bd9Sstevel@tonic-gate 		usbser_serialize_port_act(pp, USBSER_ACT_CTL);
26297c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
26307c478bd9Sstevel@tonic-gate 		break;
2631de81e71eSTim Marsland 
26327c478bd9Sstevel@tonic-gate 	case TCSBRK:
26337c478bd9Sstevel@tonic-gate 		/* serialize breaks */
2634de81e71eSTim Marsland 		if (pp->port_act & USBSER_ACT_BREAK)
26357c478bd9Sstevel@tonic-gate 			return (USB_FAILURE);
2636de81e71eSTim Marsland 		/*FALLTHRU*/
26377c478bd9Sstevel@tonic-gate 	default:
26387c478bd9Sstevel@tonic-gate 		usbser_serialize_port_act(pp, USBSER_ACT_CTL);
26397c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
26407c478bd9Sstevel@tonic-gate 		(void) ttycommon_ioctl(tp, q, mp, &error);
2641de81e71eSTim Marsland 		break;
26427c478bd9Sstevel@tonic-gate 	}
26437c478bd9Sstevel@tonic-gate 
26447c478bd9Sstevel@tonic-gate 	if (error == 0) {
26457c478bd9Sstevel@tonic-gate 		/*
26467c478bd9Sstevel@tonic-gate 		 * ttycommon_ioctl() did most of the work
26477c478bd9Sstevel@tonic-gate 		 * we just use the data it set up
26487c478bd9Sstevel@tonic-gate 		 */
26497c478bd9Sstevel@tonic-gate 		switch (cmd) {
26507c478bd9Sstevel@tonic-gate 		case TCSETSF:
26517c478bd9Sstevel@tonic-gate 		case TCSETSW:
26527c478bd9Sstevel@tonic-gate 		case TCSETA:
26537c478bd9Sstevel@tonic-gate 		case TCSETAW:
26547c478bd9Sstevel@tonic-gate 		case TCSETAF:
26557c478bd9Sstevel@tonic-gate 			(void) USBSER_DS_FIFO_DRAIN(pp, DS_TX);
2656de81e71eSTim Marsland 			/*FALLTHRU*/
26577c478bd9Sstevel@tonic-gate 
26587c478bd9Sstevel@tonic-gate 		case TCSETS:
26597c478bd9Sstevel@tonic-gate 			mutex_enter(&pp->port_mutex);
26607c478bd9Sstevel@tonic-gate 			error = usbser_port_program(pp);
26617c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->port_mutex);
26627c478bd9Sstevel@tonic-gate 			break;
26637c478bd9Sstevel@tonic-gate 		}
26647c478bd9Sstevel@tonic-gate 		goto end;
2665de81e71eSTim Marsland 
26667c478bd9Sstevel@tonic-gate 	} else if (error > 0) {
26677c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_IOCTL, pp->port_lh, "usbser_ioctl: "
26687c478bd9Sstevel@tonic-gate 		    "ttycommon_ioctl returned %d", error);
26697c478bd9Sstevel@tonic-gate 		goto end;
26707c478bd9Sstevel@tonic-gate 	}
26717c478bd9Sstevel@tonic-gate 
26727c478bd9Sstevel@tonic-gate 	/*
26737c478bd9Sstevel@tonic-gate 	 * error < 0: ttycommon_ioctl() didn't do anything, we process it here
26747c478bd9Sstevel@tonic-gate 	 */
26757c478bd9Sstevel@tonic-gate 	error = 0;
26767c478bd9Sstevel@tonic-gate 	switch (cmd) {
26777c478bd9Sstevel@tonic-gate 	case TCSBRK:
2678de81e71eSTim Marsland 		if ((error = miocpullup(mp, sizeof (int))) != 0)
26797c478bd9Sstevel@tonic-gate 			break;
2680de81e71eSTim Marsland 
26817c478bd9Sstevel@tonic-gate 		/* drain output */
26827c478bd9Sstevel@tonic-gate 		(void) USBSER_DS_FIFO_DRAIN(pp, USBSER_TX_FIFO_DRAIN_TIMEOUT);
2683de81e71eSTim Marsland 
26847c478bd9Sstevel@tonic-gate 		/*
26857c478bd9Sstevel@tonic-gate 		 * if required, set break
26867c478bd9Sstevel@tonic-gate 		 */
26877c478bd9Sstevel@tonic-gate 		if (*(int *)mp->b_cont->b_rptr == 0) {
26887c478bd9Sstevel@tonic-gate 			if (USBSER_DS_BREAK_CTL(pp, DS_ON) != USB_SUCCESS) {
26897c478bd9Sstevel@tonic-gate 				error = EIO;
26907c478bd9Sstevel@tonic-gate 				break;
26917c478bd9Sstevel@tonic-gate 			}
2692de81e71eSTim Marsland 
26937c478bd9Sstevel@tonic-gate 			mutex_enter(&pp->port_mutex);
26947c478bd9Sstevel@tonic-gate 			pp->port_act |= USBSER_ACT_BREAK;
26957c478bd9Sstevel@tonic-gate 			pp->port_delay_id = timeout(usbser_restart, pp,
26964ee52f77Slg 			    drv_usectohz(250000));
26977c478bd9Sstevel@tonic-gate 			mutex_exit(&pp->port_mutex);
26987c478bd9Sstevel@tonic-gate 		}
2699de81e71eSTim Marsland 		mioc2ack(mp, NULL, 0, 0);
27007c478bd9Sstevel@tonic-gate 		break;
27017c478bd9Sstevel@tonic-gate 
2702de81e71eSTim Marsland 	case TIOCSBRK:	/* set break */
2703de81e71eSTim Marsland 		if (USBSER_DS_BREAK_CTL(pp, DS_ON) != USB_SUCCESS)
27047c478bd9Sstevel@tonic-gate 			error = EIO;
2705de81e71eSTim Marsland 		else
2706de81e71eSTim Marsland 			mioc2ack(mp, NULL, 0, 0);
2707de81e71eSTim Marsland 		break;
27087c478bd9Sstevel@tonic-gate 
2709de81e71eSTim Marsland 	case TIOCCBRK:	/* clear break */
2710de81e71eSTim Marsland 		if (USBSER_DS_BREAK_CTL(pp, DS_OFF) != USB_SUCCESS)
2711de81e71eSTim Marsland 			error = EIO;
2712de81e71eSTim Marsland 		else
2713de81e71eSTim Marsland 			mioc2ack(mp, NULL, 0, 0);
27147c478bd9Sstevel@tonic-gate 		break;
2715de81e71eSTim Marsland 
2716de81e71eSTim Marsland 	case TIOCMSET:	/* set all modem bits */
2717de81e71eSTim Marsland 	case TIOCMBIS:	/* bis modem bits */
2718de81e71eSTim Marsland 	case TIOCMBIC:	/* bic modem bits */
27197c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count == TRANSPARENT) {
27207c478bd9Sstevel@tonic-gate 			mcopyin(mp, NULL, sizeof (int), NULL);
27217c478bd9Sstevel@tonic-gate 			break;
27227c478bd9Sstevel@tonic-gate 		}
2723de81e71eSTim Marsland 		if ((error = miocpullup(mp, sizeof (int))) != 0)
27247c478bd9Sstevel@tonic-gate 			break;
27257c478bd9Sstevel@tonic-gate 
27267c478bd9Sstevel@tonic-gate 		val = *(int *)mp->b_cont->b_rptr;
27277c478bd9Sstevel@tonic-gate 		if (cmd == TIOCMSET) {
27287c478bd9Sstevel@tonic-gate 			rval = USBSER_DS_SET_MODEM_CTL(pp, -1, val);
27297c478bd9Sstevel@tonic-gate 		} else if (cmd == TIOCMBIS) {
27307c478bd9Sstevel@tonic-gate 			rval = USBSER_DS_SET_MODEM_CTL(pp, val, -1);
27317c478bd9Sstevel@tonic-gate 		} else if (cmd == TIOCMBIC) {
27327c478bd9Sstevel@tonic-gate 			rval = USBSER_DS_SET_MODEM_CTL(pp, val, 0);
27337c478bd9Sstevel@tonic-gate 		}
2734de81e71eSTim Marsland 		if (rval == USB_SUCCESS)
2735de81e71eSTim Marsland 			mioc2ack(mp, NULL, 0, 0);
2736de81e71eSTim Marsland 		else
27377c478bd9Sstevel@tonic-gate 			error = EIO;
27387c478bd9Sstevel@tonic-gate 		break;
2739de81e71eSTim Marsland 
2740de81e71eSTim Marsland 	case TIOCSILOOP:
27417c478bd9Sstevel@tonic-gate 		if (USBSER_DS_LOOPBACK_SUPPORTED(pp)) {
2742de81e71eSTim Marsland 			if (USBSER_DS_LOOPBACK(pp, DS_ON) == USB_SUCCESS)
2743de81e71eSTim Marsland 				mioc2ack(mp, NULL, 0, 0);
2744de81e71eSTim Marsland 			else
27457c478bd9Sstevel@tonic-gate 				error = EIO;
27467c478bd9Sstevel@tonic-gate 		} else {
27477c478bd9Sstevel@tonic-gate 			error = EINVAL;
27487c478bd9Sstevel@tonic-gate 		}
27497c478bd9Sstevel@tonic-gate 		break;
2750de81e71eSTim Marsland 
2751de81e71eSTim Marsland 	case TIOCCILOOP:
27527c478bd9Sstevel@tonic-gate 		if (USBSER_DS_LOOPBACK_SUPPORTED(pp)) {
2753de81e71eSTim Marsland 			if (USBSER_DS_LOOPBACK(pp, DS_OFF) == USB_SUCCESS)
2754de81e71eSTim Marsland 				mioc2ack(mp, NULL, 0, 0);
2755de81e71eSTim Marsland 			else
27567c478bd9Sstevel@tonic-gate 				error = EIO;
27577c478bd9Sstevel@tonic-gate 		} else {
27587c478bd9Sstevel@tonic-gate 			error = EINVAL;
27597c478bd9Sstevel@tonic-gate 		}
27607c478bd9Sstevel@tonic-gate 		break;
27617c478bd9Sstevel@tonic-gate 
2762de81e71eSTim Marsland 	case TIOCMGET:	/* get all modem bits */
2763de81e71eSTim Marsland 		if ((datamp = allocb(sizeof (int), BPRI_MED)) == NULL) {
2764de81e71eSTim Marsland 			error = EAGAIN;
27657c478bd9Sstevel@tonic-gate 			break;
27667c478bd9Sstevel@tonic-gate 		}
27677c478bd9Sstevel@tonic-gate 		rval = USBSER_DS_GET_MODEM_CTL(pp, -1, (int *)datamp->b_rptr);
27687c478bd9Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
27697c478bd9Sstevel@tonic-gate 			error = EIO;
27707c478bd9Sstevel@tonic-gate 			break;
27717c478bd9Sstevel@tonic-gate 		}
2772de81e71eSTim Marsland 		if (iocp->ioc_count == TRANSPARENT)
27737c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (int), NULL, datamp);
2774de81e71eSTim Marsland 		else
2775de81e71eSTim Marsland 			mioc2ack(mp, datamp, sizeof (int), 0);
277628cdc3d7Sszhou 		break;
2777de81e71eSTim Marsland 
277828cdc3d7Sszhou 	case CONSOPENPOLLEDIO:
277928cdc3d7Sszhou 		error = usbser_polledio_init(pp);
278028cdc3d7Sszhou 		if (error != 0)
278128cdc3d7Sszhou 			break;
278228cdc3d7Sszhou 
278328cdc3d7Sszhou 		error = miocpullup(mp, sizeof (struct cons_polledio *));
278428cdc3d7Sszhou 		if (error != 0)
278528cdc3d7Sszhou 			break;
278628cdc3d7Sszhou 
278728cdc3d7Sszhou 		*(struct cons_polledio **)mp->b_cont->b_rptr = &usbser_polledio;
27881fd2d8e7Sguoqing zhu - Sun Microsystems - Beijing China 
27891fd2d8e7Sguoqing zhu - Sun Microsystems - Beijing China 		mp->b_datap->db_type = M_IOCACK;
279028cdc3d7Sszhou 		break;
2791de81e71eSTim Marsland 
279228cdc3d7Sszhou 	case CONSCLOSEPOLLEDIO:
279328cdc3d7Sszhou 		usbser_polledio_fini(pp);
27941fd2d8e7Sguoqing zhu - Sun Microsystems - Beijing China 		mp->b_datap->db_type = M_IOCACK;
27951fd2d8e7Sguoqing zhu - Sun Microsystems - Beijing China 		iocp->ioc_error = 0;
27961fd2d8e7Sguoqing zhu - Sun Microsystems - Beijing China 		iocp->ioc_rval = 0;
279728cdc3d7Sszhou 		break;
2798de81e71eSTim Marsland 
279928cdc3d7Sszhou 	case CONSSETABORTENABLE:
280028cdc3d7Sszhou 		error = secpolicy_console(iocp->ioc_cr);
280128cdc3d7Sszhou 		if (error != 0)
280228cdc3d7Sszhou 			break;
280328cdc3d7Sszhou 
280428cdc3d7Sszhou 		if (iocp->ioc_count != TRANSPARENT) {
280528cdc3d7Sszhou 			error = EINVAL;
280628cdc3d7Sszhou 			break;
280728cdc3d7Sszhou 		}
280828cdc3d7Sszhou 
280928cdc3d7Sszhou 		/*
281028cdc3d7Sszhou 		 * To do: implement console abort support
281128cdc3d7Sszhou 		 * This involves adding a console flag to usbser
281228cdc3d7Sszhou 		 * state structure. If flag is set, parse input stream
281328cdc3d7Sszhou 		 * for abort sequence (see asy for example).
281428cdc3d7Sszhou 		 *
281528cdc3d7Sszhou 		 * For now, run mdb -K to get kmdb prompt.
281628cdc3d7Sszhou 		 */
281728cdc3d7Sszhou 		if (*(intptr_t *)mp->b_cont->b_rptr)
281828cdc3d7Sszhou 			usbser_console_abort = 1;
281928cdc3d7Sszhou 		else
282028cdc3d7Sszhou 			usbser_console_abort = 0;
28211fd2d8e7Sguoqing zhu - Sun Microsystems - Beijing China 
28221fd2d8e7Sguoqing zhu - Sun Microsystems - Beijing China 		mp->b_datap->db_type = M_IOCACK;
28231fd2d8e7Sguoqing zhu - Sun Microsystems - Beijing China 		iocp->ioc_error = 0;
28241fd2d8e7Sguoqing zhu - Sun Microsystems - Beijing China 		iocp->ioc_rval = 0;
282528cdc3d7Sszhou 		break;
2826de81e71eSTim Marsland 
282728cdc3d7Sszhou 	case CONSGETABORTENABLE:
282828cdc3d7Sszhou 		/*CONSTANTCONDITION*/
282928cdc3d7Sszhou 		ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *));
283028cdc3d7Sszhou 		/*
283128cdc3d7Sszhou 		 * Store the return value right in the payload
283228cdc3d7Sszhou 		 * we were passed.  Crude.
283328cdc3d7Sszhou 		 */
283428cdc3d7Sszhou 		mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL);
283528cdc3d7Sszhou 		*(boolean_t *)mp->b_cont->b_rptr = (usbser_console_abort != 0);
28367c478bd9Sstevel@tonic-gate 		break;
2837de81e71eSTim Marsland 
28387c478bd9Sstevel@tonic-gate 	default:
28397c478bd9Sstevel@tonic-gate 		error = EINVAL;
28407c478bd9Sstevel@tonic-gate 		break;
28417c478bd9Sstevel@tonic-gate 	}
28427c478bd9Sstevel@tonic-gate end:
2843de81e71eSTim Marsland 	if (error != 0)
2844de81e71eSTim Marsland 		miocnak(q, mp, 0, error);
2845de81e71eSTim Marsland 	else
2846de81e71eSTim Marsland 		qreply(q, mp);
28477c478bd9Sstevel@tonic-gate 
28487c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
28497c478bd9Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_CTL);
28507c478bd9Sstevel@tonic-gate 
28517c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
28527c478bd9Sstevel@tonic-gate }
28537c478bd9Sstevel@tonic-gate 
28547c478bd9Sstevel@tonic-gate 
28557c478bd9Sstevel@tonic-gate /*
28567c478bd9Sstevel@tonic-gate  * process M_IOCDATA message
28577c478bd9Sstevel@tonic-gate  */
28587c478bd9Sstevel@tonic-gate static void
usbser_iocdata(usbser_port_t * pp,mblk_t * mp)28597c478bd9Sstevel@tonic-gate usbser_iocdata(usbser_port_t *pp, mblk_t *mp)
28607c478bd9Sstevel@tonic-gate {
28617c478bd9Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
28627c478bd9Sstevel@tonic-gate 	queue_t		*q = tp->t_writeq;
28637c478bd9Sstevel@tonic-gate 	struct copyresp	*csp;
28647c478bd9Sstevel@tonic-gate 	int		cmd;
28657c478bd9Sstevel@tonic-gate 	int		val;
2866d235eb05SRichard PALO 	int		rval = USB_FAILURE;
28677c478bd9Sstevel@tonic-gate 
28687c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->port_mutex));
28697c478bd9Sstevel@tonic-gate 
28707c478bd9Sstevel@tonic-gate 	csp = (struct copyresp *)mp->b_rptr;
28717c478bd9Sstevel@tonic-gate 	cmd = csp->cp_cmd;
28727c478bd9Sstevel@tonic-gate 
28737c478bd9Sstevel@tonic-gate 	if (csp->cp_rval != 0) {
28747c478bd9Sstevel@tonic-gate 		freemsg(mp);
28757c478bd9Sstevel@tonic-gate 		return;
28767c478bd9Sstevel@tonic-gate 	}
28777c478bd9Sstevel@tonic-gate 
28787c478bd9Sstevel@tonic-gate 	switch (cmd) {
2879de81e71eSTim Marsland 	case TIOCMSET:	/* set all modem bits */
2880de81e71eSTim Marsland 	case TIOCMBIS:	/* bis modem bits */
2881de81e71eSTim Marsland 	case TIOCMBIC:	/* bic modem bits */
28827c478bd9Sstevel@tonic-gate 		if ((mp->b_cont == NULL) ||
28837c478bd9Sstevel@tonic-gate 		    (MBLKL(mp->b_cont) < sizeof (int))) {
28847c478bd9Sstevel@tonic-gate 			miocnak(q, mp, 0, EINVAL);
28857c478bd9Sstevel@tonic-gate 			break;
28867c478bd9Sstevel@tonic-gate 		}
28877c478bd9Sstevel@tonic-gate 		val = *(int *)mp->b_cont->b_rptr;
28887c478bd9Sstevel@tonic-gate 
28897c478bd9Sstevel@tonic-gate 		usbser_serialize_port_act(pp, USBSER_ACT_CTL);
28907c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
2891de81e71eSTim Marsland 
28927c478bd9Sstevel@tonic-gate 		if (cmd == TIOCMSET) {
28937c478bd9Sstevel@tonic-gate 			rval = USBSER_DS_SET_MODEM_CTL(pp, -1, val);
28947c478bd9Sstevel@tonic-gate 		} else if (cmd == TIOCMBIS) {
28957c478bd9Sstevel@tonic-gate 			rval = USBSER_DS_SET_MODEM_CTL(pp, val, -1);
28967c478bd9Sstevel@tonic-gate 		} else if (cmd == TIOCMBIC) {
28977c478bd9Sstevel@tonic-gate 			rval = USBSER_DS_SET_MODEM_CTL(pp, val, 0);
28987c478bd9Sstevel@tonic-gate 		}
28997c478bd9Sstevel@tonic-gate 
29007c478bd9Sstevel@tonic-gate 		if (mp->b_cont) {
29017c478bd9Sstevel@tonic-gate 			freemsg(mp->b_cont);
29027c478bd9Sstevel@tonic-gate 			mp->b_cont = NULL;
29037c478bd9Sstevel@tonic-gate 		}
2904de81e71eSTim Marsland 
2905de81e71eSTim Marsland 		if (rval == USB_SUCCESS)
29067c478bd9Sstevel@tonic-gate 			miocack(q, mp, 0, 0);
2907de81e71eSTim Marsland 		else
29087c478bd9Sstevel@tonic-gate 			miocnak(q, mp, 0, EIO);
29097c478bd9Sstevel@tonic-gate 
2910de81e71eSTim Marsland 		mutex_enter(&pp->port_mutex);
29117c478bd9Sstevel@tonic-gate 		usbser_release_port_act(pp, USBSER_ACT_CTL);
29127c478bd9Sstevel@tonic-gate 		break;
2913de81e71eSTim Marsland 
2914de81e71eSTim Marsland 	case TIOCMGET:	/* get all modem bits */
29157c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
29167c478bd9Sstevel@tonic-gate 		miocack(q, mp, 0, 0);
29177c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
29187c478bd9Sstevel@tonic-gate 		break;
2919de81e71eSTim Marsland 
29207c478bd9Sstevel@tonic-gate 	default:
29217c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
29227c478bd9Sstevel@tonic-gate 		miocnak(q, mp, 0, EINVAL);
29237c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
29247c478bd9Sstevel@tonic-gate 		break;
29257c478bd9Sstevel@tonic-gate 	}
29267c478bd9Sstevel@tonic-gate }
29277c478bd9Sstevel@tonic-gate 
29287c478bd9Sstevel@tonic-gate 
29297c478bd9Sstevel@tonic-gate /*
29307c478bd9Sstevel@tonic-gate  * handle M_START[I]/M_STOP[I] messages
29317c478bd9Sstevel@tonic-gate  */
29327c478bd9Sstevel@tonic-gate static void
usbser_stop(usbser_port_t * pp,mblk_t * mp)29337c478bd9Sstevel@tonic-gate usbser_stop(usbser_port_t *pp, mblk_t *mp)
29347c478bd9Sstevel@tonic-gate {
29357c478bd9Sstevel@tonic-gate 	usbser_st_mstop++;
29367c478bd9Sstevel@tonic-gate 	if (!(pp->port_flags & USBSER_FL_TX_STOPPED)) {
29377c478bd9Sstevel@tonic-gate 		usbser_serialize_port_act(pp, USBSER_ACT_CTL);
29387c478bd9Sstevel@tonic-gate 		pp->port_flags |= USBSER_FL_TX_STOPPED;
29397c478bd9Sstevel@tonic-gate 
29407c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
29417c478bd9Sstevel@tonic-gate 		USBSER_DS_STOP(pp, DS_TX);
29427c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
29437c478bd9Sstevel@tonic-gate 
29447c478bd9Sstevel@tonic-gate 		usbser_release_port_act(pp, USBSER_ACT_TX);
29457c478bd9Sstevel@tonic-gate 		usbser_release_port_act(pp, USBSER_ACT_CTL);
29467c478bd9Sstevel@tonic-gate 	}
29477c478bd9Sstevel@tonic-gate 	freemsg(mp);
29487c478bd9Sstevel@tonic-gate }
29497c478bd9Sstevel@tonic-gate 
29507c478bd9Sstevel@tonic-gate 
29517c478bd9Sstevel@tonic-gate static void
usbser_start(usbser_port_t * pp,mblk_t * mp)29527c478bd9Sstevel@tonic-gate usbser_start(usbser_port_t *pp, mblk_t *mp)
29537c478bd9Sstevel@tonic-gate {
29547c478bd9Sstevel@tonic-gate 	usbser_st_mstart++;
29557c478bd9Sstevel@tonic-gate 	if (pp->port_flags & USBSER_FL_TX_STOPPED) {
29567c478bd9Sstevel@tonic-gate 		usbser_serialize_port_act(pp, USBSER_ACT_CTL);
29577c478bd9Sstevel@tonic-gate 		pp->port_flags &= ~USBSER_FL_TX_STOPPED;
29587c478bd9Sstevel@tonic-gate 
29597c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
29607c478bd9Sstevel@tonic-gate 		USBSER_DS_START(pp, DS_TX);
29617c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
29627c478bd9Sstevel@tonic-gate 		usbser_release_port_act(pp, USBSER_ACT_CTL);
29637c478bd9Sstevel@tonic-gate 	}
29647c478bd9Sstevel@tonic-gate 	freemsg(mp);
29657c478bd9Sstevel@tonic-gate }
29667c478bd9Sstevel@tonic-gate 
29677c478bd9Sstevel@tonic-gate 
29687c478bd9Sstevel@tonic-gate static void
usbser_stopi(usbser_port_t * pp,mblk_t * mp)29697c478bd9Sstevel@tonic-gate usbser_stopi(usbser_port_t *pp, mblk_t *mp)
29707c478bd9Sstevel@tonic-gate {
29717c478bd9Sstevel@tonic-gate 	usbser_st_mstopi++;
29727c478bd9Sstevel@tonic-gate 	usbser_serialize_port_act(pp, USBSER_ACT_CTL);
29737c478bd9Sstevel@tonic-gate 	pp->port_flowc = pp->port_ttycommon.t_stopc;
29747c478bd9Sstevel@tonic-gate 	usbser_inbound_flow_ctl(pp);
29757c478bd9Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_CTL);
29767c478bd9Sstevel@tonic-gate 	freemsg(mp);
29777c478bd9Sstevel@tonic-gate }
29787c478bd9Sstevel@tonic-gate 
29797c478bd9Sstevel@tonic-gate static void
usbser_starti(usbser_port_t * pp,mblk_t * mp)29807c478bd9Sstevel@tonic-gate usbser_starti(usbser_port_t *pp, mblk_t *mp)
29817c478bd9Sstevel@tonic-gate {
29827c478bd9Sstevel@tonic-gate 	usbser_st_mstarti++;
29837c478bd9Sstevel@tonic-gate 	usbser_serialize_port_act(pp, USBSER_ACT_CTL);
29847c478bd9Sstevel@tonic-gate 	pp->port_flowc = pp->port_ttycommon.t_startc;
29857c478bd9Sstevel@tonic-gate 	usbser_inbound_flow_ctl(pp);
29867c478bd9Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_CTL);
29877c478bd9Sstevel@tonic-gate 	freemsg(mp);
29887c478bd9Sstevel@tonic-gate }
29897c478bd9Sstevel@tonic-gate 
29907c478bd9Sstevel@tonic-gate /*
29917c478bd9Sstevel@tonic-gate  * process M_FLUSH message
29927c478bd9Sstevel@tonic-gate  */
29937c478bd9Sstevel@tonic-gate static void
usbser_flush(usbser_port_t * pp,mblk_t * mp)29947c478bd9Sstevel@tonic-gate usbser_flush(usbser_port_t *pp, mblk_t *mp)
29957c478bd9Sstevel@tonic-gate {
29967c478bd9Sstevel@tonic-gate 	queue_t	*q = pp->port_ttycommon.t_writeq;
29977c478bd9Sstevel@tonic-gate 
29987c478bd9Sstevel@tonic-gate 	if (*mp->b_rptr & FLUSHW) {
29997c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
30007c478bd9Sstevel@tonic-gate 		(void) USBSER_DS_FIFO_FLUSH(pp, DS_TX);	/* flush FIFO buffers */
30017c478bd9Sstevel@tonic-gate 		flushq(q, FLUSHDATA);			/* flush write queue */
30027c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
30037c478bd9Sstevel@tonic-gate 
30047c478bd9Sstevel@tonic-gate 		usbser_release_port_act(pp, USBSER_ACT_TX);
30057c478bd9Sstevel@tonic-gate 
30067c478bd9Sstevel@tonic-gate 		*mp->b_rptr &= ~FLUSHW;
30077c478bd9Sstevel@tonic-gate 	}
30087c478bd9Sstevel@tonic-gate 	if (*mp->b_rptr & FLUSHR) {
30097c478bd9Sstevel@tonic-gate 		/*
30107c478bd9Sstevel@tonic-gate 		 * flush FIFO buffers
30117c478bd9Sstevel@tonic-gate 		 */
30127c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
30137c478bd9Sstevel@tonic-gate 		(void) USBSER_DS_FIFO_FLUSH(pp, DS_RX);
30147c478bd9Sstevel@tonic-gate 		flushq(RD(q), FLUSHDATA);
30157c478bd9Sstevel@tonic-gate 		qreply(q, mp);
30167c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
30177c478bd9Sstevel@tonic-gate 	} else {
30187c478bd9Sstevel@tonic-gate 		freemsg(mp);
30197c478bd9Sstevel@tonic-gate 	}
30207c478bd9Sstevel@tonic-gate }
30217c478bd9Sstevel@tonic-gate 
30227c478bd9Sstevel@tonic-gate /*
30237c478bd9Sstevel@tonic-gate  * process M_BREAK message
30247c478bd9Sstevel@tonic-gate  */
30257c478bd9Sstevel@tonic-gate static void
usbser_break(usbser_port_t * pp,mblk_t * mp)30267c478bd9Sstevel@tonic-gate usbser_break(usbser_port_t *pp, mblk_t *mp)
30277c478bd9Sstevel@tonic-gate {
30287c478bd9Sstevel@tonic-gate 	int	rval;
30297c478bd9Sstevel@tonic-gate 
30307c478bd9Sstevel@tonic-gate 	/*
30317c478bd9Sstevel@tonic-gate 	 * set the break and arrange for usbser_restart() to be called in 1/4 s
30327c478bd9Sstevel@tonic-gate 	 */
30337c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
30347c478bd9Sstevel@tonic-gate 	rval = USBSER_DS_BREAK_CTL(pp, DS_ON);
30357c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
30367c478bd9Sstevel@tonic-gate 
30377c478bd9Sstevel@tonic-gate 	if (rval == USB_SUCCESS) {
30387c478bd9Sstevel@tonic-gate 		pp->port_act |= USBSER_ACT_BREAK;
30397c478bd9Sstevel@tonic-gate 		pp->port_delay_id = timeout(usbser_restart, pp,
30404ee52f77Slg 		    drv_usectohz(250000));
30417c478bd9Sstevel@tonic-gate 	}
30427c478bd9Sstevel@tonic-gate 	freemsg(mp);
30437c478bd9Sstevel@tonic-gate }
30447c478bd9Sstevel@tonic-gate 
30457c478bd9Sstevel@tonic-gate 
30467c478bd9Sstevel@tonic-gate /*
30477c478bd9Sstevel@tonic-gate  * process M_DELAY message
30487c478bd9Sstevel@tonic-gate  */
30497c478bd9Sstevel@tonic-gate static void
usbser_delay(usbser_port_t * pp,mblk_t * mp)30507c478bd9Sstevel@tonic-gate usbser_delay(usbser_port_t *pp, mblk_t *mp)
30517c478bd9Sstevel@tonic-gate {
30527c478bd9Sstevel@tonic-gate 	/*
30537c478bd9Sstevel@tonic-gate 	 * arrange for usbser_restart() to be called when the delay expires
30547c478bd9Sstevel@tonic-gate 	 */
30557c478bd9Sstevel@tonic-gate 	pp->port_act |= USBSER_ACT_DELAY;
30567c478bd9Sstevel@tonic-gate 	pp->port_delay_id = timeout(usbser_restart, pp,
30574ee52f77Slg 	    (clock_t)(*(uchar_t *)mp->b_rptr + 6));
30587c478bd9Sstevel@tonic-gate 	freemsg(mp);
30597c478bd9Sstevel@tonic-gate }
30607c478bd9Sstevel@tonic-gate 
30617c478bd9Sstevel@tonic-gate 
30627c478bd9Sstevel@tonic-gate /*
30637c478bd9Sstevel@tonic-gate  * restart output on a line after a delay or break timer expired
30647c478bd9Sstevel@tonic-gate  */
30657c478bd9Sstevel@tonic-gate static void
usbser_restart(void * arg)30667c478bd9Sstevel@tonic-gate usbser_restart(void *arg)
30677c478bd9Sstevel@tonic-gate {
30687c478bd9Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)arg;
30697c478bd9Sstevel@tonic-gate 
30707c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_restart");
30717c478bd9Sstevel@tonic-gate 
30727c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
30737c478bd9Sstevel@tonic-gate 	/* if cancelled, return immediately */
30747c478bd9Sstevel@tonic-gate 	if (pp->port_delay_id == 0) {
30757c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
30767c478bd9Sstevel@tonic-gate 
30777c478bd9Sstevel@tonic-gate 		return;
30787c478bd9Sstevel@tonic-gate 	}
30797c478bd9Sstevel@tonic-gate 	pp->port_delay_id = 0;
30807c478bd9Sstevel@tonic-gate 
30817c478bd9Sstevel@tonic-gate 	/* clear break if necessary */
30827c478bd9Sstevel@tonic-gate 	if (pp->port_act & USBSER_ACT_BREAK) {
30837c478bd9Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
30847c478bd9Sstevel@tonic-gate 		(void) USBSER_DS_BREAK_CTL(pp, DS_OFF);
30857c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
30867c478bd9Sstevel@tonic-gate 	}
30877c478bd9Sstevel@tonic-gate 
30887c478bd9Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_BREAK | USBSER_ACT_DELAY);
30897c478bd9Sstevel@tonic-gate 
30907c478bd9Sstevel@tonic-gate 	/* wake wq thread to resume message processing */
30917c478bd9Sstevel@tonic-gate 	usbser_thr_wake(&pp->port_wq_thread);
30927c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
30937c478bd9Sstevel@tonic-gate }
30947c478bd9Sstevel@tonic-gate 
30957c478bd9Sstevel@tonic-gate 
30967c478bd9Sstevel@tonic-gate /*
30977c478bd9Sstevel@tonic-gate  * program port hardware with the chosen parameters
30987c478bd9Sstevel@tonic-gate  * most of the operation is based on the values of 'c_iflag' and 'c_cflag'
30997c478bd9Sstevel@tonic-gate  */
31007c478bd9Sstevel@tonic-gate static int
usbser_port_program(usbser_port_t * pp)31017c478bd9Sstevel@tonic-gate usbser_port_program(usbser_port_t *pp)
31027c478bd9Sstevel@tonic-gate {
31037c478bd9Sstevel@tonic-gate 	tty_common_t		*tp = &pp->port_ttycommon;
31047c478bd9Sstevel@tonic-gate 	int			baudrate;
31057c478bd9Sstevel@tonic-gate 	int			c_flag;
31067c478bd9Sstevel@tonic-gate 	ds_port_param_entry_t	pe[6];
31077c478bd9Sstevel@tonic-gate 	ds_port_params_t	params;
31087c478bd9Sstevel@tonic-gate 	int			flow_ctl, ctl_val;
31097c478bd9Sstevel@tonic-gate 	int			err = 0;
31107c478bd9Sstevel@tonic-gate 
31117c478bd9Sstevel@tonic-gate 	baudrate = tp->t_cflag & CBAUD;
31127c478bd9Sstevel@tonic-gate 	if (tp->t_cflag & CBAUDEXT) {
31137c478bd9Sstevel@tonic-gate 		baudrate += 16;
31147c478bd9Sstevel@tonic-gate 	}
31157c478bd9Sstevel@tonic-gate 
31167c478bd9Sstevel@tonic-gate 	/*
31177c478bd9Sstevel@tonic-gate 	 * set input speed same as output, as split speed not supported
31187c478bd9Sstevel@tonic-gate 	 */
31197c478bd9Sstevel@tonic-gate 	if (tp->t_cflag & (CIBAUD|CIBAUDEXT)) {
31207c478bd9Sstevel@tonic-gate 		tp->t_cflag &= ~(CIBAUD);
31217c478bd9Sstevel@tonic-gate 		if (baudrate > CBAUD) {
31227c478bd9Sstevel@tonic-gate 			tp->t_cflag |= CIBAUDEXT;
31237c478bd9Sstevel@tonic-gate 			tp->t_cflag |=
31244ee52f77Slg 			    (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD);
31257c478bd9Sstevel@tonic-gate 		} else {
31267c478bd9Sstevel@tonic-gate 			tp->t_cflag &= ~CIBAUDEXT;
31277c478bd9Sstevel@tonic-gate 			tp->t_cflag |= ((baudrate << IBSHIFT) & CIBAUD);
31287c478bd9Sstevel@tonic-gate 		}
31297c478bd9Sstevel@tonic-gate 	}
31307c478bd9Sstevel@tonic-gate 
31317c478bd9Sstevel@tonic-gate 	c_flag = tp->t_cflag;
31327c478bd9Sstevel@tonic-gate 
31337c478bd9Sstevel@tonic-gate 	/*
31347c478bd9Sstevel@tonic-gate 	 * flow control
31357c478bd9Sstevel@tonic-gate 	 */
31367c478bd9Sstevel@tonic-gate 	flow_ctl = tp->t_iflag & (IXON | IXANY | IXOFF);
31377c478bd9Sstevel@tonic-gate 	if (c_flag & CRTSCTS) {
31387c478bd9Sstevel@tonic-gate 		flow_ctl |= CTSXON;
31397c478bd9Sstevel@tonic-gate 	}
31407c478bd9Sstevel@tonic-gate 	if (c_flag & CRTSXOFF) {
31417c478bd9Sstevel@tonic-gate 		flow_ctl |= RTSXOFF;
31427c478bd9Sstevel@tonic-gate 	}
31437c478bd9Sstevel@tonic-gate 
31447c478bd9Sstevel@tonic-gate 	/*
31457c478bd9Sstevel@tonic-gate 	 * fill in port parameters we need to set:
31467c478bd9Sstevel@tonic-gate 	 *
31477c478bd9Sstevel@tonic-gate 	 * baud rate
31487c478bd9Sstevel@tonic-gate 	 */
31497c478bd9Sstevel@tonic-gate 	pe[0].param = DS_PARAM_BAUD;
31507c478bd9Sstevel@tonic-gate 	pe[0].val.ui = baudrate;
31517c478bd9Sstevel@tonic-gate 
31527c478bd9Sstevel@tonic-gate 	/* stop bits */
31537c478bd9Sstevel@tonic-gate 	pe[1].param = DS_PARAM_STOPB;
31547c478bd9Sstevel@tonic-gate 	pe[1].val.ui = c_flag & CSTOPB;
31557c478bd9Sstevel@tonic-gate 
31567c478bd9Sstevel@tonic-gate 	/* parity */
31577c478bd9Sstevel@tonic-gate 	pe[2].param = DS_PARAM_PARITY;
31587c478bd9Sstevel@tonic-gate 	pe[2].val.ui = c_flag & (PARENB | PARODD);
31597c478bd9Sstevel@tonic-gate 
31607c478bd9Sstevel@tonic-gate 	/* char size */
31617c478bd9Sstevel@tonic-gate 	pe[3].param = DS_PARAM_CHARSZ;
31627c478bd9Sstevel@tonic-gate 	pe[3].val.ui = c_flag & CSIZE;
31637c478bd9Sstevel@tonic-gate 
31647c478bd9Sstevel@tonic-gate 	/* start & stop chars */
31657c478bd9Sstevel@tonic-gate 	pe[4].param = DS_PARAM_XON_XOFF;
31667c478bd9Sstevel@tonic-gate 	pe[4].val.uc[0] = tp->t_startc;
31677c478bd9Sstevel@tonic-gate 	pe[4].val.uc[1] = tp->t_stopc;
31687c478bd9Sstevel@tonic-gate 
31697c478bd9Sstevel@tonic-gate 	/* flow control */
31707c478bd9Sstevel@tonic-gate 	pe[5].param = DS_PARAM_FLOW_CTL;
31717c478bd9Sstevel@tonic-gate 	pe[5].val.ui = flow_ctl;
31727c478bd9Sstevel@tonic-gate 
31737c478bd9Sstevel@tonic-gate 	params.tp_entries = &pe[0];
31747c478bd9Sstevel@tonic-gate 	params.tp_cnt = 6;
31757c478bd9Sstevel@tonic-gate 
31767c478bd9Sstevel@tonic-gate 	/* control signals */
31777c478bd9Sstevel@tonic-gate 	ctl_val = TIOCM_DTR | TIOCM_RTS;
31787c478bd9Sstevel@tonic-gate 	if (baudrate == 0) {
31797c478bd9Sstevel@tonic-gate 		ctl_val &= ~TIOCM_DTR;	/* zero baudrate means drop DTR */
31807c478bd9Sstevel@tonic-gate 	}
31817c478bd9Sstevel@tonic-gate 	if (pp->port_flags & USBSER_FL_RX_STOPPED) {
31827c478bd9Sstevel@tonic-gate 		ctl_val &= ~TIOCM_RTS;
31837c478bd9Sstevel@tonic-gate 	}
31847c478bd9Sstevel@tonic-gate 
31857c478bd9Sstevel@tonic-gate 	/* submit */
31867c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
31877c478bd9Sstevel@tonic-gate 	err = USBSER_DS_SET_PORT_PARAMS(pp, &params);
31887c478bd9Sstevel@tonic-gate 	if (err != USB_SUCCESS) {
31897c478bd9Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
31907c478bd9Sstevel@tonic-gate 
31917c478bd9Sstevel@tonic-gate 		return (EINVAL);
31927c478bd9Sstevel@tonic-gate 	}
31937c478bd9Sstevel@tonic-gate 
31947c478bd9Sstevel@tonic-gate 	err = USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR | TIOCM_RTS, ctl_val);
31957c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
31967c478bd9Sstevel@tonic-gate 
31977c478bd9Sstevel@tonic-gate 	return ((err == USB_SUCCESS) ? 0 : EIO);
31987c478bd9Sstevel@tonic-gate }
31997c478bd9Sstevel@tonic-gate 
32007c478bd9Sstevel@tonic-gate 
32017c478bd9Sstevel@tonic-gate /*
32027c478bd9Sstevel@tonic-gate  * check if any inbound flow control action needed
32037c478bd9Sstevel@tonic-gate  */
32047c478bd9Sstevel@tonic-gate static void
usbser_inbound_flow_ctl(usbser_port_t * pp)32057c478bd9Sstevel@tonic-gate usbser_inbound_flow_ctl(usbser_port_t *pp)
32067c478bd9Sstevel@tonic-gate {
32077c478bd9Sstevel@tonic-gate 	tcflag_t	need_hw;
32087c478bd9Sstevel@tonic-gate 	int		rts;
32097c478bd9Sstevel@tonic-gate 	char		c = pp->port_flowc;
32107c478bd9Sstevel@tonic-gate 	mblk_t		*mp = NULL;
32117c478bd9Sstevel@tonic-gate 
32127c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh,
32137c478bd9Sstevel@tonic-gate 	    "usbser_inbound_flow_ctl: c=%x cflag=%x port_flags=%x",
32147c478bd9Sstevel@tonic-gate 	    c, pp->port_ttycommon.t_cflag, pp->port_flags);
32157c478bd9Sstevel@tonic-gate 
32167c478bd9Sstevel@tonic-gate 	if (c == '\0') {
32177c478bd9Sstevel@tonic-gate 
32187c478bd9Sstevel@tonic-gate 		return;
32197c478bd9Sstevel@tonic-gate 	}
32207c478bd9Sstevel@tonic-gate 	pp->port_flowc = '\0';
32217c478bd9Sstevel@tonic-gate 
32227c478bd9Sstevel@tonic-gate 	/*
32237c478bd9Sstevel@tonic-gate 	 * if inbound hardware flow control enabled, we need to frob RTS
32247c478bd9Sstevel@tonic-gate 	 */
32257c478bd9Sstevel@tonic-gate 	need_hw = (pp->port_ttycommon.t_cflag & CRTSXOFF);
32267c478bd9Sstevel@tonic-gate 	if (c == pp->port_ttycommon.t_startc) {
32277c478bd9Sstevel@tonic-gate 		rts = TIOCM_RTS;
32287c478bd9Sstevel@tonic-gate 		pp->port_flags &= ~USBSER_FL_RX_STOPPED;
32297c478bd9Sstevel@tonic-gate 	} else {
32307c478bd9Sstevel@tonic-gate 		rts = 0;
32317c478bd9Sstevel@tonic-gate 		pp->port_flags |= USBSER_FL_RX_STOPPED;
32327c478bd9Sstevel@tonic-gate 	}
32337c478bd9Sstevel@tonic-gate 
32347c478bd9Sstevel@tonic-gate 	/*
32357c478bd9Sstevel@tonic-gate 	 * if character flow control active, transmit a start or stop char,
32367c478bd9Sstevel@tonic-gate 	 */
32377c478bd9Sstevel@tonic-gate 	if (pp->port_ttycommon.t_iflag & IXOFF) {
32387c478bd9Sstevel@tonic-gate 		if ((mp = allocb(1, BPRI_LO)) == NULL) {
32397c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_WQ, pp->port_lh,
32407c478bd9Sstevel@tonic-gate 			    "usbser_inbound_flow_ctl: allocb failed");
32417c478bd9Sstevel@tonic-gate 		} else {
32427c478bd9Sstevel@tonic-gate 			*mp->b_wptr++ = c;
32437c478bd9Sstevel@tonic-gate 			pp->port_flags |= USBSER_ACT_TX;
32447c478bd9Sstevel@tonic-gate 		}
32457c478bd9Sstevel@tonic-gate 	}
32467c478bd9Sstevel@tonic-gate 
32477c478bd9Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
32487c478bd9Sstevel@tonic-gate 	if (need_hw) {
32497c478bd9Sstevel@tonic-gate 		(void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_RTS, rts);
32507c478bd9Sstevel@tonic-gate 	}
32517c478bd9Sstevel@tonic-gate 	if (mp) {
32527c478bd9Sstevel@tonic-gate 		(void) USBSER_DS_TX(pp, mp);
32537c478bd9Sstevel@tonic-gate 	}
32547c478bd9Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
32557c478bd9Sstevel@tonic-gate }
32567c478bd9Sstevel@tonic-gate 
32577c478bd9Sstevel@tonic-gate 
32587c478bd9Sstevel@tonic-gate /*
32597c478bd9Sstevel@tonic-gate  * misc
32607c478bd9Sstevel@tonic-gate  * ----
32617c478bd9Sstevel@tonic-gate  *
32627c478bd9Sstevel@tonic-gate  *
3263de81e71eSTim Marsland  * returns != 0 if device is online, 0 otherwise
32647c478bd9Sstevel@tonic-gate  */
32657c478bd9Sstevel@tonic-gate static int
usbser_dev_is_online(usbser_state_t * usp)32667c478bd9Sstevel@tonic-gate usbser_dev_is_online(usbser_state_t *usp)
32677c478bd9Sstevel@tonic-gate {
32687c478bd9Sstevel@tonic-gate 	int	rval;
32697c478bd9Sstevel@tonic-gate 
32707c478bd9Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
32717c478bd9Sstevel@tonic-gate 	rval = (usp->us_dev_state == USB_DEV_ONLINE);
32727c478bd9Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
32737c478bd9Sstevel@tonic-gate 
32747c478bd9Sstevel@tonic-gate 	return (rval);
32757c478bd9Sstevel@tonic-gate }
32767c478bd9Sstevel@tonic-gate 
32777c478bd9Sstevel@tonic-gate /*
32787c478bd9Sstevel@tonic-gate  * serialize port activities defined by 'act' mask
32797c478bd9Sstevel@tonic-gate  */
32807c478bd9Sstevel@tonic-gate static void
usbser_serialize_port_act(usbser_port_t * pp,int act)32817c478bd9Sstevel@tonic-gate usbser_serialize_port_act(usbser_port_t *pp, int act)
32827c478bd9Sstevel@tonic-gate {
3283de81e71eSTim Marsland 	while (pp->port_act & act)
32847c478bd9Sstevel@tonic-gate 		cv_wait(&pp->port_act_cv, &pp->port_mutex);
32857c478bd9Sstevel@tonic-gate 	pp->port_act |= act;
32867c478bd9Sstevel@tonic-gate }
32877c478bd9Sstevel@tonic-gate 
32887c478bd9Sstevel@tonic-gate 
32897c478bd9Sstevel@tonic-gate /*
32907c478bd9Sstevel@tonic-gate  * indicate that port activity is finished
32917c478bd9Sstevel@tonic-gate  */
32927c478bd9Sstevel@tonic-gate static void
usbser_release_port_act(usbser_port_t * pp,int act)32937c478bd9Sstevel@tonic-gate usbser_release_port_act(usbser_port_t *pp, int act)
32947c478bd9Sstevel@tonic-gate {
32957c478bd9Sstevel@tonic-gate 	pp->port_act &= ~act;
32967c478bd9Sstevel@tonic-gate 	cv_broadcast(&pp->port_act_cv);
32977c478bd9Sstevel@tonic-gate }
32987c478bd9Sstevel@tonic-gate 
3299d235eb05SRichard PALO #ifdef DEBUG
33007c478bd9Sstevel@tonic-gate /*
33017c478bd9Sstevel@tonic-gate  * message type to string and back conversion.
33027c478bd9Sstevel@tonic-gate  *
33037c478bd9Sstevel@tonic-gate  * pardon breaks on the same line, but as long as cstyle doesn't
33047c478bd9Sstevel@tonic-gate  * complain, I'd like to keep this form for trivial cases like this.
33057c478bd9Sstevel@tonic-gate  * associative arrays in the kernel, anyone?
33067c478bd9Sstevel@tonic-gate  */
33077c478bd9Sstevel@tonic-gate static char *
usbser_msgtype2str(int type)33087c478bd9Sstevel@tonic-gate usbser_msgtype2str(int type)
33097c478bd9Sstevel@tonic-gate {
33107c478bd9Sstevel@tonic-gate 	char	*str;
33117c478bd9Sstevel@tonic-gate 
33127c478bd9Sstevel@tonic-gate 	switch (type) {
33137c478bd9Sstevel@tonic-gate 	case M_STOP:	str = "M_STOP";		break;
33147c478bd9Sstevel@tonic-gate 	case M_START:	str = "M_START";	break;
33157c478bd9Sstevel@tonic-gate 	case M_STOPI:	str = "M_STOPI";	break;
33167c478bd9Sstevel@tonic-gate 	case M_STARTI:	str = "M_STARTI";	break;
33177c478bd9Sstevel@tonic-gate 	case M_DATA:	str = "M_DATA";		break;
33187c478bd9Sstevel@tonic-gate 	case M_DELAY:	str = "M_DELAY";	break;
33197c478bd9Sstevel@tonic-gate 	case M_BREAK:	str = "M_BREAK";	break;
33207c478bd9Sstevel@tonic-gate 	case M_IOCTL:	str = "M_IOCTL";	break;
33217c478bd9Sstevel@tonic-gate 	case M_IOCDATA:	str = "M_IOCDATA";	break;
33227c478bd9Sstevel@tonic-gate 	case M_FLUSH:	str = "M_FLUSH";	break;
33237c478bd9Sstevel@tonic-gate 	case M_CTL:	str = "M_CTL";		break;
33247c478bd9Sstevel@tonic-gate 	case M_READ:	str = "M_READ";		break;
33257c478bd9Sstevel@tonic-gate 	default:	str = "unknown";	break;
33267c478bd9Sstevel@tonic-gate 	}
33277c478bd9Sstevel@tonic-gate 
33287c478bd9Sstevel@tonic-gate 	return (str);
33297c478bd9Sstevel@tonic-gate }
33307c478bd9Sstevel@tonic-gate 
33317c478bd9Sstevel@tonic-gate static char *
usbser_ioctl2str(int ioctl)33327c478bd9Sstevel@tonic-gate usbser_ioctl2str(int ioctl)
33337c478bd9Sstevel@tonic-gate {
33347c478bd9Sstevel@tonic-gate 	char	*str;
33357c478bd9Sstevel@tonic-gate 
33367c478bd9Sstevel@tonic-gate 	switch (ioctl) {
33377c478bd9Sstevel@tonic-gate 	case TCGETA:	str = "TCGETA";		break;
33387c478bd9Sstevel@tonic-gate 	case TCSETA:	str = "TCSETA";		break;
33397c478bd9Sstevel@tonic-gate 	case TCSETAF:	str = "TCSETAF";	break;
33407c478bd9Sstevel@tonic-gate 	case TCSETAW:	str = "TCSETAW";	break;
33417c478bd9Sstevel@tonic-gate 	case TCSBRK:	str = "TCSBRK";		break;
33427c478bd9Sstevel@tonic-gate 	case TCXONC:	str = "TCXONC";		break;
33437c478bd9Sstevel@tonic-gate 	case TCFLSH:	str = "TCFLSH";		break;
33447c478bd9Sstevel@tonic-gate 	case TCGETS:	str = "TCGETS";		break;
33457c478bd9Sstevel@tonic-gate 	case TCSETS:	str = "TCSETS";		break;
33467c478bd9Sstevel@tonic-gate 	case TCSETSF:	str = "TCSETSF";	break;
33477c478bd9Sstevel@tonic-gate 	case TCSETSW:	str = "TCSETSW";	break;
33487c478bd9Sstevel@tonic-gate 	case TIOCSBRK:	str = "TIOCSBRK";	break;
33497c478bd9Sstevel@tonic-gate 	case TIOCCBRK:	str = "TIOCCBRK";	break;
33507c478bd9Sstevel@tonic-gate 	case TIOCMSET:	str = "TIOCMSET";	break;
33517c478bd9Sstevel@tonic-gate 	case TIOCMBIS:	str = "TIOCMBIS";	break;
33527c478bd9Sstevel@tonic-gate 	case TIOCMBIC:	str = "TIOCMBIC";	break;
33537c478bd9Sstevel@tonic-gate 	case TIOCMGET:	str = "TIOCMGET";	break;
3354de81e71eSTim Marsland 	case TIOCSILOOP: str = "TIOCSILOOP";	break;
3355de81e71eSTim Marsland 	case TIOCCILOOP: str = "TIOCCILOOP";	break;
33567c478bd9Sstevel@tonic-gate 	case TCGETX:	str = "TCGETX";		break;
33577c478bd9Sstevel@tonic-gate 	case TCSETX:	str = "TCGETX";		break;
33587c478bd9Sstevel@tonic-gate 	case TCSETXW:	str = "TCGETX";		break;
33597c478bd9Sstevel@tonic-gate 	case TCSETXF:	str = "TCGETX";		break;
33607c478bd9Sstevel@tonic-gate 	default:	str = "unknown";	break;
33617c478bd9Sstevel@tonic-gate 	}
33627c478bd9Sstevel@tonic-gate 
33637c478bd9Sstevel@tonic-gate 	return (str);
33647c478bd9Sstevel@tonic-gate }
3365d235eb05SRichard PALO #endif
336628cdc3d7Sszhou /*
336728cdc3d7Sszhou  * Polled IO support
336828cdc3d7Sszhou  */
336928cdc3d7Sszhou 
3370112116d8Sfb /* called once	by consconfig() when polledio is opened */
337128cdc3d7Sszhou static int
usbser_polledio_init(usbser_port_t * pp)337228cdc3d7Sszhou usbser_polledio_init(usbser_port_t *pp)
337328cdc3d7Sszhou {
337428cdc3d7Sszhou 	int err;
337528cdc3d7Sszhou 	usb_pipe_handle_t hdl;
337628cdc3d7Sszhou 	ds_ops_t *ds_ops = pp->port_ds_ops;
337728cdc3d7Sszhou 
337828cdc3d7Sszhou 	/* only one serial line console supported */
337928cdc3d7Sszhou 	if (console_input != NULL)
338028cdc3d7Sszhou 		return (USB_FAILURE);
338128cdc3d7Sszhou 
338228cdc3d7Sszhou 	/* check if underlying driver supports polled io */
338328cdc3d7Sszhou 	if (ds_ops->ds_version < DS_OPS_VERSION_V1 ||
338428cdc3d7Sszhou 	    ds_ops->ds_out_pipe == NULL || ds_ops->ds_in_pipe == NULL)
338528cdc3d7Sszhou 		return (USB_FAILURE);
338628cdc3d7Sszhou 
338728cdc3d7Sszhou 	/* init polled input pipe */
338828cdc3d7Sszhou 	hdl = ds_ops->ds_in_pipe(pp->port_ds_hdl, pp->port_num);
338928cdc3d7Sszhou 	err = usb_console_input_init(pp->port_usp->us_dip, hdl,
339028cdc3d7Sszhou 	    &console_input_buf, &console_input);
339128cdc3d7Sszhou 	if (err)
339228cdc3d7Sszhou 		return (USB_FAILURE);
339328cdc3d7Sszhou 
339428cdc3d7Sszhou 	/* init polled output pipe */
339528cdc3d7Sszhou 	hdl = ds_ops->ds_out_pipe(pp->port_ds_hdl, pp->port_num);
339628cdc3d7Sszhou 	err = usb_console_output_init(pp->port_usp->us_dip, hdl,
339728cdc3d7Sszhou 	    &console_output);
339828cdc3d7Sszhou 	if (err) {
339928cdc3d7Sszhou 		(void) usb_console_input_fini(console_input);
340028cdc3d7Sszhou 		console_input = NULL;
340128cdc3d7Sszhou 		return (USB_FAILURE);
340228cdc3d7Sszhou 	}
340328cdc3d7Sszhou 
340428cdc3d7Sszhou 	return (USB_SUCCESS);
340528cdc3d7Sszhou }
340628cdc3d7Sszhou 
3407112116d8Sfb /* called once	by consconfig() when polledio is closed */
340828cdc3d7Sszhou /*ARGSUSED*/
usbser_polledio_fini(usbser_port_t * pp)340928cdc3d7Sszhou static void usbser_polledio_fini(usbser_port_t *pp)
341028cdc3d7Sszhou {
341128cdc3d7Sszhou 	/* Since we can't move the console, there is nothing to do. */
341228cdc3d7Sszhou }
341328cdc3d7Sszhou 
341428cdc3d7Sszhou /*ARGSUSED*/
341528cdc3d7Sszhou static void
usbser_polledio_enter(cons_polledio_arg_t arg)341628cdc3d7Sszhou usbser_polledio_enter(cons_polledio_arg_t arg)
341728cdc3d7Sszhou {
341828cdc3d7Sszhou 	(void) usb_console_input_enter(console_input);
341928cdc3d7Sszhou 	(void) usb_console_output_enter(console_output);
342028cdc3d7Sszhou }
342128cdc3d7Sszhou 
342228cdc3d7Sszhou /*ARGSUSED*/
342328cdc3d7Sszhou static void
usbser_polledio_exit(cons_polledio_arg_t arg)342428cdc3d7Sszhou usbser_polledio_exit(cons_polledio_arg_t arg)
342528cdc3d7Sszhou {
342628cdc3d7Sszhou 	(void) usb_console_output_exit(console_output);
342728cdc3d7Sszhou 	(void) usb_console_input_exit(console_input);
342828cdc3d7Sszhou }
342928cdc3d7Sszhou 
343028cdc3d7Sszhou /*ARGSUSED*/
343128cdc3d7Sszhou static void
usbser_putchar(cons_polledio_arg_t arg,uchar_t c)343228cdc3d7Sszhou usbser_putchar(cons_polledio_arg_t arg, uchar_t c)
343328cdc3d7Sszhou {
343428cdc3d7Sszhou 	static uchar_t cr[2] = {'\r', '\n'};
343528cdc3d7Sszhou 	uint_t nout;
343628cdc3d7Sszhou 
343728cdc3d7Sszhou 	if (c == '\n')
343828cdc3d7Sszhou 		(void) usb_console_write(console_output, cr, 2, &nout);
343928cdc3d7Sszhou 	else
344028cdc3d7Sszhou 		(void) usb_console_write(console_output, &c, 1, &nout);
344128cdc3d7Sszhou }
344228cdc3d7Sszhou 
344328cdc3d7Sszhou /*ARGSUSED*/
344428cdc3d7Sszhou static int
usbser_getchar(cons_polledio_arg_t arg)344528cdc3d7Sszhou usbser_getchar(cons_polledio_arg_t arg)
344628cdc3d7Sszhou {
344728cdc3d7Sszhou 	while (!usbser_ischar(arg))
344828cdc3d7Sszhou 		;
344928cdc3d7Sszhou 
345028cdc3d7Sszhou 	return (*console_input_start++);
345128cdc3d7Sszhou }
345228cdc3d7Sszhou 
345328cdc3d7Sszhou /*ARGSUSED*/
345428cdc3d7Sszhou static boolean_t
usbser_ischar(cons_polledio_arg_t arg)345528cdc3d7Sszhou usbser_ischar(cons_polledio_arg_t arg)
345628cdc3d7Sszhou {
345728cdc3d7Sszhou 	uint_t num_bytes;
345828cdc3d7Sszhou 
345928cdc3d7Sszhou 	if (console_input_start < console_input_end)
3460de81e71eSTim Marsland 		return (B_TRUE);
346128cdc3d7Sszhou 
346228cdc3d7Sszhou 	if (usb_console_read(console_input, &num_bytes) != USB_SUCCESS)
3463de81e71eSTim Marsland 		return (B_FALSE);
346428cdc3d7Sszhou 
346528cdc3d7Sszhou 	console_input_start = console_input_buf;
346628cdc3d7Sszhou 	console_input_end = console_input_buf + num_bytes;
346728cdc3d7Sszhou 
346828cdc3d7Sszhou 	return (num_bytes != 0);
346928cdc3d7Sszhou }
3470