xref: /onnv-gate/usr/src/uts/common/io/usb/clients/usbser/usbser.c (revision 10481:5edad2f503c3)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52191Sszhou  * Common Development and Distribution License (the "License").
62191Sszhou  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
229354STim.Marsland@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate  *
290Sstevel@tonic-gate  * USB generic serial driver (GSD)
300Sstevel@tonic-gate  *
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/param.h>
340Sstevel@tonic-gate #include <sys/stream.h>
350Sstevel@tonic-gate #include <sys/stropts.h>
360Sstevel@tonic-gate #include <sys/errno.h>
370Sstevel@tonic-gate #include <sys/cred.h>
380Sstevel@tonic-gate #include <sys/conf.h>
390Sstevel@tonic-gate #include <sys/stat.h>
400Sstevel@tonic-gate #include <sys/modctl.h>
410Sstevel@tonic-gate #include <sys/ddi.h>
420Sstevel@tonic-gate #include <sys/sunddi.h>
432191Sszhou #include <sys/sunndi.h>
440Sstevel@tonic-gate #include <sys/termio.h>
450Sstevel@tonic-gate #include <sys/termiox.h>
460Sstevel@tonic-gate #include <sys/stropts.h>
470Sstevel@tonic-gate #include <sys/stream.h>
480Sstevel@tonic-gate #include <sys/strsubr.h>
490Sstevel@tonic-gate #include <sys/strsun.h>
500Sstevel@tonic-gate #include <sys/strtty.h>
510Sstevel@tonic-gate #include <sys/policy.h>
522191Sszhou #include <sys/consdev.h>
530Sstevel@tonic-gate 
540Sstevel@tonic-gate #include <sys/usb/usba.h>
550Sstevel@tonic-gate #include <sys/usb/clients/usbser/usbser_var.h>
560Sstevel@tonic-gate #include <sys/usb/clients/usbser/usbser_dsdi.h>
570Sstevel@tonic-gate #include <sys/usb/clients/usbser/usbser_rseq.h>
582191Sszhou #include <sys/usb/usba/genconsole.h>
590Sstevel@tonic-gate 
600Sstevel@tonic-gate /* autoconfiguration subroutines */
610Sstevel@tonic-gate static int	usbser_rseq_do_cb(rseq_t *, int, uintptr_t);
620Sstevel@tonic-gate static int	usbser_free_soft_state(usbser_state_t *);
630Sstevel@tonic-gate static int	usbser_init_soft_state(usbser_state_t *);
640Sstevel@tonic-gate static int	usbser_fini_soft_state(usbser_state_t *);
650Sstevel@tonic-gate static int	usbser_attach_dev(usbser_state_t *);
660Sstevel@tonic-gate static void	usbser_detach_dev(usbser_state_t *);
670Sstevel@tonic-gate static int	usbser_attach_ports(usbser_state_t *);
680Sstevel@tonic-gate static int	usbser_create_port_minor_nodes(usbser_state_t *, int);
690Sstevel@tonic-gate static void	usbser_detach_ports(usbser_state_t *);
700Sstevel@tonic-gate static int	usbser_create_taskq(usbser_state_t *);
710Sstevel@tonic-gate static void	usbser_destroy_taskq(usbser_state_t *);
720Sstevel@tonic-gate static void	usbser_set_dev_state_init(usbser_state_t *);
730Sstevel@tonic-gate 
740Sstevel@tonic-gate /* hotplugging and power management */
750Sstevel@tonic-gate static int	usbser_disconnect_cb(dev_info_t *);
760Sstevel@tonic-gate static int	usbser_reconnect_cb(dev_info_t *);
770Sstevel@tonic-gate static void	usbser_disconnect_ports(usbser_state_t *);
780Sstevel@tonic-gate static int	usbser_cpr_suspend(dev_info_t *);
790Sstevel@tonic-gate static int	usbser_suspend_ports(usbser_state_t *);
800Sstevel@tonic-gate static void	usbser_cpr_resume(dev_info_t *);
810Sstevel@tonic-gate static int	usbser_restore_device_state(usbser_state_t *);
820Sstevel@tonic-gate static void	usbser_restore_ports_state(usbser_state_t *);
830Sstevel@tonic-gate 
840Sstevel@tonic-gate /* STREAMS subroutines */
850Sstevel@tonic-gate static int	usbser_open_setup(queue_t *, usbser_port_t *, int, int,
860Sstevel@tonic-gate 		cred_t *);
870Sstevel@tonic-gate static int	usbser_open_init(usbser_port_t *, int);
880Sstevel@tonic-gate static void	usbser_check_port_props(usbser_port_t *);
890Sstevel@tonic-gate static void	usbser_open_fini(usbser_port_t *);
900Sstevel@tonic-gate static int	usbser_open_line_setup(usbser_port_t *, int, int);
910Sstevel@tonic-gate static int	usbser_open_carrier_check(usbser_port_t *, int, int);
920Sstevel@tonic-gate static void	usbser_open_queues_init(usbser_port_t *, queue_t *);
930Sstevel@tonic-gate static void	usbser_open_queues_fini(usbser_port_t *);
940Sstevel@tonic-gate static void	usbser_close_drain(usbser_port_t *);
950Sstevel@tonic-gate static void	usbser_close_cancel_break(usbser_port_t *);
960Sstevel@tonic-gate static void	usbser_close_hangup(usbser_port_t *);
970Sstevel@tonic-gate static void	usbser_close_cleanup(usbser_port_t *);
980Sstevel@tonic-gate 
990Sstevel@tonic-gate /* threads */
1000Sstevel@tonic-gate static void	usbser_thr_dispatch(usbser_thread_t *);
1010Sstevel@tonic-gate static void	usbser_thr_cancel(usbser_thread_t *);
1020Sstevel@tonic-gate static void	usbser_thr_wake(usbser_thread_t *);
1030Sstevel@tonic-gate static void	usbser_wq_thread(void *);
1040Sstevel@tonic-gate static void	usbser_rq_thread(void *);
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate /* DSD callbacks */
1070Sstevel@tonic-gate static void	usbser_tx_cb(caddr_t);
1080Sstevel@tonic-gate static void	usbser_rx_cb(caddr_t);
1090Sstevel@tonic-gate static void	usbser_rx_massage_data(usbser_port_t *, mblk_t *);
1100Sstevel@tonic-gate static void	usbser_rx_massage_mbreak(usbser_port_t *, mblk_t *);
1110Sstevel@tonic-gate static void	usbser_rx_cb_put(usbser_port_t *, queue_t *, queue_t *,
1120Sstevel@tonic-gate 		mblk_t *);
1130Sstevel@tonic-gate static void	usbser_status_cb(caddr_t);
1140Sstevel@tonic-gate static void	usbser_status_proc_cb(usbser_port_t *);
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate /* serial support */
1170Sstevel@tonic-gate static void	usbser_wmsg(usbser_port_t *);
1180Sstevel@tonic-gate static int	usbser_data(usbser_port_t *, mblk_t *);
1190Sstevel@tonic-gate static int	usbser_ioctl(usbser_port_t *, mblk_t *);
1200Sstevel@tonic-gate static void	usbser_iocdata(usbser_port_t *, mblk_t *);
1210Sstevel@tonic-gate static void	usbser_stop(usbser_port_t *, mblk_t *);
1220Sstevel@tonic-gate static void	usbser_start(usbser_port_t *, mblk_t *);
1230Sstevel@tonic-gate static void	usbser_stopi(usbser_port_t *, mblk_t *);
1240Sstevel@tonic-gate static void	usbser_starti(usbser_port_t *, mblk_t *);
1250Sstevel@tonic-gate static void	usbser_flush(usbser_port_t *, mblk_t *);
1260Sstevel@tonic-gate static void	usbser_break(usbser_port_t *, mblk_t *);
1270Sstevel@tonic-gate static void	usbser_delay(usbser_port_t *, mblk_t *);
1280Sstevel@tonic-gate static void	usbser_restart(void *);
1290Sstevel@tonic-gate static int	usbser_port_program(usbser_port_t *);
1300Sstevel@tonic-gate static void	usbser_inbound_flow_ctl(usbser_port_t *);
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate /* misc */
1330Sstevel@tonic-gate static int	usbser_dev_is_online(usbser_state_t *);
1340Sstevel@tonic-gate static void	usbser_serialize_port_act(usbser_port_t *, int);
1350Sstevel@tonic-gate static void	usbser_release_port_act(usbser_port_t *, int);
1360Sstevel@tonic-gate static char	*usbser_msgtype2str(int);
1370Sstevel@tonic-gate static char	*usbser_ioctl2str(int);
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate /* USBA events */
1410Sstevel@tonic-gate usb_event_t usbser_usb_events = {
1420Sstevel@tonic-gate 	usbser_disconnect_cb,	/* disconnect */
1430Sstevel@tonic-gate 	usbser_reconnect_cb,	/* reconnect */
1440Sstevel@tonic-gate 	NULL,			/* pre-suspend */
1450Sstevel@tonic-gate 	NULL,			/* pre-resume */
1460Sstevel@tonic-gate };
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate /* debug support */
1496898Sfb209375 uint_t	 usbser_errlevel = USB_LOG_L4;
1506898Sfb209375 uint_t	 usbser_errmask = DPRINT_MASK_ALL;
1516898Sfb209375 uint_t	 usbser_instance_debug = (uint_t)-1;
1520Sstevel@tonic-gate 
1532191Sszhou /* usb serial console */
1542191Sszhou static struct usbser_state *usbser_list;
1552191Sszhou static kmutex_t usbser_lock;
1562191Sszhou static int usbser_console_abort;
1572191Sszhou static usb_console_info_t console_input, console_output;
1582191Sszhou static uchar_t *console_input_buf;
1592191Sszhou static uchar_t *console_input_start, *console_input_end;
1602191Sszhou 
1612326Ssl147100 _NOTE(SCHEME_PROTECTS_DATA("unshared", usbser_console_abort))
1622326Ssl147100 _NOTE(SCHEME_PROTECTS_DATA("unshared", console_input))
1632326Ssl147100 _NOTE(SCHEME_PROTECTS_DATA("unshared", console_output))
1642326Ssl147100 _NOTE(SCHEME_PROTECTS_DATA("unshared", console_input_start))
1652326Ssl147100 _NOTE(SCHEME_PROTECTS_DATA("unshared", console_input_end))
1662326Ssl147100 
1672191Sszhou static void usbser_putchar(cons_polledio_arg_t, uchar_t);
1682191Sszhou static int usbser_getchar(cons_polledio_arg_t);
1692191Sszhou static boolean_t usbser_ischar(cons_polledio_arg_t);
1702191Sszhou static void usbser_polledio_enter(cons_polledio_arg_t);
1712191Sszhou static void usbser_polledio_exit(cons_polledio_arg_t);
1722191Sszhou static int usbser_polledio_init(usbser_port_t *);
1732191Sszhou static void usbser_polledio_fini(usbser_port_t *);
1742191Sszhou 
1752191Sszhou static struct cons_polledio usbser_polledio = {
1762191Sszhou 	CONSPOLLEDIO_V1,
1772191Sszhou 	NULL,	/* to be set later */
1782191Sszhou 	usbser_putchar,
1792191Sszhou 	usbser_getchar,
1802191Sszhou 	usbser_ischar,
1812191Sszhou 	usbser_polledio_enter,
1822191Sszhou 	usbser_polledio_exit
1832191Sszhou };
1842191Sszhou 
1850Sstevel@tonic-gate /* various statistics. TODO: replace with kstats */
1860Sstevel@tonic-gate static int usbser_st_tx_data_loss = 0;
1870Sstevel@tonic-gate static int usbser_st_rx_data_loss = 0;
1880Sstevel@tonic-gate static int usbser_st_put_stopi = 0;
1890Sstevel@tonic-gate static int usbser_st_mstop = 0;
1900Sstevel@tonic-gate static int usbser_st_mstart = 0;
1910Sstevel@tonic-gate static int usbser_st_mstopi = 0;
1920Sstevel@tonic-gate static int usbser_st_mstarti = 0;
1930Sstevel@tonic-gate static int usbser_st_rsrv = 0;
1940Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("monotonic stats", usbser_st_{
1950Sstevel@tonic-gate 	tx_data_loss rx_data_loss put_stopi mstop mstart mstopi mstarti rsrv}))
1960Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unshared", usb_bulk_req_t))
1970Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unshared", usb_intr_req_t))
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate /* taskq parameter */
2000Sstevel@tonic-gate extern pri_t minclsyspri;
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate /*
2030Sstevel@tonic-gate  * tell warlock not to worry about STREAMS structures
2040Sstevel@tonic-gate  */
2050Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique per call", iocblk datab msgb queue copyreq))
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate /*
2080Sstevel@tonic-gate  * modload support
2090Sstevel@tonic-gate  */
2100Sstevel@tonic-gate extern struct mod_ops mod_miscops;
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate static struct modlmisc modlmisc = {
2130Sstevel@tonic-gate 	&mod_miscops,	/* Type of module */
2147020Sgd78059 	"USB generic serial module"
2150Sstevel@tonic-gate };
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate static struct modlinkage modlinkage = {
2180Sstevel@tonic-gate 	MODREV_1, (void *)&modlmisc, NULL
2190Sstevel@tonic-gate };
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate #define	RSEQ(f1, f2) RSEQE(f1, usbser_rseq_do_cb, f2, NULL)
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate /*
2260Sstevel@tonic-gate  * loadable module entry points
2270Sstevel@tonic-gate  * ----------------------------
2280Sstevel@tonic-gate  */
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate int
_init(void)2310Sstevel@tonic-gate _init(void)
2320Sstevel@tonic-gate {
2332191Sszhou 	int err;
2342191Sszhou 
2352191Sszhou 	mutex_init(&usbser_lock, NULL, MUTEX_DRIVER, (void *)NULL);
2362191Sszhou 	if (err = mod_install(&modlinkage))
2372191Sszhou 		mutex_destroy(&usbser_lock);
2382191Sszhou 
2392191Sszhou 	return (err);
2400Sstevel@tonic-gate }
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate int
_fini(void)2440Sstevel@tonic-gate _fini(void)
2450Sstevel@tonic-gate {
2462191Sszhou 	int err;
2472191Sszhou 
2482191Sszhou 	if (err = mod_remove(&modlinkage))
2492191Sszhou 
2502191Sszhou 		return (err);
2512191Sszhou 
2522191Sszhou 	mutex_destroy(&usbser_lock);
2532191Sszhou 
2542191Sszhou 	return (0);
2550Sstevel@tonic-gate }
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2590Sstevel@tonic-gate _info(struct modinfo *modinfop)
2600Sstevel@tonic-gate {
2610Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2620Sstevel@tonic-gate }
2630Sstevel@tonic-gate 
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate /*
2660Sstevel@tonic-gate  * soft state size
2670Sstevel@tonic-gate  */
2680Sstevel@tonic-gate int
usbser_soft_state_size()2690Sstevel@tonic-gate usbser_soft_state_size()
2700Sstevel@tonic-gate {
2710Sstevel@tonic-gate 	return (sizeof (usbser_state_t));
2720Sstevel@tonic-gate }
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate /*
2760Sstevel@tonic-gate  * autoconfiguration entry points
2770Sstevel@tonic-gate  * ------------------------------
2780Sstevel@tonic-gate  */
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate /*ARGSUSED*/
2810Sstevel@tonic-gate int
usbser_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result,void * statep)2820Sstevel@tonic-gate usbser_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
2830Sstevel@tonic-gate 		void **result, void *statep)
2840Sstevel@tonic-gate {
2850Sstevel@tonic-gate 	int		instance;
2860Sstevel@tonic-gate 	int		ret = DDI_FAILURE;
2870Sstevel@tonic-gate 	usbser_state_t	*usbserp;
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	instance = USBSER_MINOR2INST(getminor((dev_t)arg));
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	switch (infocmd) {
2920Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
2930Sstevel@tonic-gate 		*result = NULL;
2940Sstevel@tonic-gate 		usbserp = ddi_get_soft_state(statep, instance);
2950Sstevel@tonic-gate 		if (usbserp != NULL) {
2960Sstevel@tonic-gate 			*result = usbserp->us_dip;
2970Sstevel@tonic-gate 			if (*result != NULL) {
2980Sstevel@tonic-gate 				ret = DDI_SUCCESS;
2990Sstevel@tonic-gate 			}
3000Sstevel@tonic-gate 		}
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 		break;
3030Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
3040Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
3050Sstevel@tonic-gate 		ret = DDI_SUCCESS;
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 		break;
3080Sstevel@tonic-gate 	default:
3090Sstevel@tonic-gate 		break;
3100Sstevel@tonic-gate 	}
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	return (ret);
3130Sstevel@tonic-gate }
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate /*
3160Sstevel@tonic-gate  * device attach
3170Sstevel@tonic-gate  */
3180Sstevel@tonic-gate static rseq_t rseq_att[] = {
3190Sstevel@tonic-gate 	RSEQ(NULL,			usbser_free_soft_state),
3200Sstevel@tonic-gate 	RSEQ(usbser_init_soft_state,	usbser_fini_soft_state),
3210Sstevel@tonic-gate 	RSEQ(usbser_attach_dev,		usbser_detach_dev),
3220Sstevel@tonic-gate 	RSEQ(usbser_attach_ports,	usbser_detach_ports),
3230Sstevel@tonic-gate 	RSEQ(usbser_create_taskq,	usbser_destroy_taskq),
3240Sstevel@tonic-gate 	RSEQ(NULL,			usbser_set_dev_state_init)
3250Sstevel@tonic-gate };
3260Sstevel@tonic-gate 
3272191Sszhou static void
usbser_insert(struct usbser_state * usp)3282191Sszhou usbser_insert(struct usbser_state *usp)
3292191Sszhou {
3302191Sszhou 	struct usbser_state *tmp;
3312191Sszhou 
3322191Sszhou 	mutex_enter(&usbser_lock);
3332191Sszhou 	tmp = usbser_list;
3342191Sszhou 	if (tmp == NULL)
3352191Sszhou 		usbser_list = usp;
3362191Sszhou 	else {
3372191Sszhou 		while (tmp->us_next)
3382191Sszhou 			tmp = tmp->us_next;
3392191Sszhou 		tmp->us_next = usp;
3402191Sszhou 	}
3412191Sszhou 	mutex_exit(&usbser_lock);
3422191Sszhou }
3432191Sszhou 
3442191Sszhou static void
usbser_remove(struct usbser_state * usp)3452191Sszhou usbser_remove(struct usbser_state *usp)
3462191Sszhou {
3472191Sszhou 	struct usbser_state *tmp, *prev = NULL;
3482191Sszhou 
3492191Sszhou 	mutex_enter(&usbser_lock);
3502191Sszhou 	tmp = usbser_list;
3512191Sszhou 	while (tmp != usp) {
3522191Sszhou 		prev = tmp;
3532191Sszhou 		tmp = tmp->us_next;
3542191Sszhou 	}
3552191Sszhou 	ASSERT(tmp == usp);	/* must exist, else attach/detach wrong */
3562191Sszhou 	if (prev)
3572191Sszhou 		prev->us_next = usp->us_next;
3582191Sszhou 	else
3592191Sszhou 		usbser_list = usp->us_next;
3602191Sszhou 	usp->us_next = NULL;
3612191Sszhou 	mutex_exit(&usbser_lock);
3622191Sszhou }
3632191Sszhou 
3642191Sszhou /*
3652191Sszhou  * Return the first serial device, with dip held. This is called
3662191Sszhou  * from the console subsystem to place console on usb serial device.
3672191Sszhou  */
3682191Sszhou dev_info_t *
usbser_first_device(void)3692191Sszhou usbser_first_device(void)
3702191Sszhou {
3712191Sszhou 	dev_info_t *dip = NULL;
3722191Sszhou 
3732191Sszhou 	mutex_enter(&usbser_lock);
3742191Sszhou 	if (usbser_list) {
3752191Sszhou 		dip = usbser_list->us_dip;
3762191Sszhou 		ndi_hold_devi(dip);
3772191Sszhou 	}
3782191Sszhou 	mutex_exit(&usbser_lock);
3792191Sszhou 
3802191Sszhou 	return (dip);
3812191Sszhou }
3822191Sszhou 
3830Sstevel@tonic-gate int
usbser_attach(dev_info_t * dip,ddi_attach_cmd_t cmd,void * statep,ds_ops_t * ds_ops)3840Sstevel@tonic-gate usbser_attach(dev_info_t *dip, ddi_attach_cmd_t cmd,
3850Sstevel@tonic-gate 		void *statep, ds_ops_t *ds_ops)
3860Sstevel@tonic-gate {
3870Sstevel@tonic-gate 	int		instance;
3880Sstevel@tonic-gate 	usbser_state_t	*usp;
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 	switch (cmd) {
3930Sstevel@tonic-gate 	case DDI_ATTACH:
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate 		break;
3960Sstevel@tonic-gate 	case DDI_RESUME:
3970Sstevel@tonic-gate 		usbser_cpr_resume(dip);
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 		return (DDI_SUCCESS);
4000Sstevel@tonic-gate 	default:
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 		return (DDI_FAILURE);
4030Sstevel@tonic-gate 	}
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 	/* allocate and get soft state */
4060Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS) {
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 		return (DDI_FAILURE);
4090Sstevel@tonic-gate 	}
4100Sstevel@tonic-gate 	if ((usp = ddi_get_soft_state(statep, instance)) == NULL) {
4110Sstevel@tonic-gate 		ddi_soft_state_free(statep, instance);
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 		return (DDI_FAILURE);
4140Sstevel@tonic-gate 	}
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 	usp->us_statep = statep;
4170Sstevel@tonic-gate 	usp->us_dip = dip;
4180Sstevel@tonic-gate 	usp->us_instance = instance;
4190Sstevel@tonic-gate 	usp->us_ds_ops = ds_ops;
4200Sstevel@tonic-gate 
4210Sstevel@tonic-gate 	if (rseq_do(rseq_att, NELEM(rseq_att), (uintptr_t)usp, 0) == RSEQ_OK) {
4220Sstevel@tonic-gate 		ddi_report_dev(dip);
4232191Sszhou 		usbser_insert(usp);
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 		return (DDI_SUCCESS);
4260Sstevel@tonic-gate 	} else {
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 		return (DDI_FAILURE);
4290Sstevel@tonic-gate 	}
4300Sstevel@tonic-gate }
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate /*
4330Sstevel@tonic-gate  * device detach
4340Sstevel@tonic-gate  */
4350Sstevel@tonic-gate int
usbser_detach(dev_info_t * dip,ddi_detach_cmd_t cmd,void * statep)4360Sstevel@tonic-gate usbser_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, void *statep)
4370Sstevel@tonic-gate {
4380Sstevel@tonic-gate 	int		instance = ddi_get_instance(dip);
4390Sstevel@tonic-gate 	usbser_state_t	*usp;
4400Sstevel@tonic-gate 	int		rval;
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, instance);
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 	switch (cmd) {
4450Sstevel@tonic-gate 	case DDI_DETACH:
4460Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_DETACH, usp->us_lh, "usbser_detach");
4472191Sszhou 		usbser_remove(usp);
4480Sstevel@tonic-gate 		(void) rseq_undo(rseq_att, NELEM(rseq_att), (uintptr_t)usp, 0);
4490Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_DETACH, NULL,
4505541Slg150142 		    "usbser_detach.%d: end", instance);
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 		return (DDI_SUCCESS);
4530Sstevel@tonic-gate 	case DDI_SUSPEND:
4540Sstevel@tonic-gate 		rval = usbser_cpr_suspend(dip);
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 		return ((rval == USB_SUCCESS)? DDI_SUCCESS : DDI_FAILURE);
4570Sstevel@tonic-gate 	default:
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 		return (DDI_FAILURE);
4600Sstevel@tonic-gate 	}
4610Sstevel@tonic-gate }
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate /*
4640Sstevel@tonic-gate  * STREAMS entry points
4650Sstevel@tonic-gate  * --------------------
4660Sstevel@tonic-gate  *
4670Sstevel@tonic-gate  *
4680Sstevel@tonic-gate  * port open
4690Sstevel@tonic-gate  */
4700Sstevel@tonic-gate /*ARGSUSED*/
4710Sstevel@tonic-gate int
usbser_open(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr,void * statep)4720Sstevel@tonic-gate usbser_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr,
4730Sstevel@tonic-gate 		void *statep)
4740Sstevel@tonic-gate {
4750Sstevel@tonic-gate 	usbser_state_t	*usp;
4760Sstevel@tonic-gate 	usbser_port_t	*pp;
4770Sstevel@tonic-gate 	int		minor = getminor(*dev);
4780Sstevel@tonic-gate 	int		instance;
4790Sstevel@tonic-gate 	uint_t		port_num;
4800Sstevel@tonic-gate 	int		rval;
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	instance = USBSER_MINOR2INST(minor);
4830Sstevel@tonic-gate 	if (instance < 0) {
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 		return (ENXIO);
4860Sstevel@tonic-gate 	}
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, instance);
4890Sstevel@tonic-gate 	if (usp == NULL) {
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 		return (ENXIO);
4920Sstevel@tonic-gate 	}
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate 	/* don't allow to open disconnected device */
4950Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
4960Sstevel@tonic-gate 	if (usp->us_dev_state == USB_DEV_DISCONNECTED) {
4970Sstevel@tonic-gate 		mutex_exit(&usp->us_mutex);
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 		return (ENXIO);
5000Sstevel@tonic-gate 	}
5010Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 	/* get port soft state */
5040Sstevel@tonic-gate 	port_num = USBSER_MINOR2PORT(minor);
5050Sstevel@tonic-gate 	if (port_num >= usp->us_port_cnt) {
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 		return (ENXIO);
5080Sstevel@tonic-gate 	}
5090Sstevel@tonic-gate 	pp = &usp->us_ports[port_num];
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 	/* set up everything for open */
5120Sstevel@tonic-gate 	rval = usbser_open_setup(rq, pp, minor, flag, cr);
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_OPEN, pp->port_lh, "usbser_open: rval=%d", rval);
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	return (rval);
5170Sstevel@tonic-gate }
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate /*
5210Sstevel@tonic-gate  * port close
5220Sstevel@tonic-gate  *
5230Sstevel@tonic-gate  * some things driver should do when the last app closes the line:
5240Sstevel@tonic-gate  *
5250Sstevel@tonic-gate  *	drain data;
5260Sstevel@tonic-gate  *	cancel break/delay;
5270Sstevel@tonic-gate  *	hangup line (if necessary);
5280Sstevel@tonic-gate  *	DSD close;
5290Sstevel@tonic-gate  *	cleanup soft state;
5300Sstevel@tonic-gate  */
5310Sstevel@tonic-gate /*ARGSUSED*/
5320Sstevel@tonic-gate int
usbser_close(queue_t * rq,int flag,cred_t * cr)5330Sstevel@tonic-gate usbser_close(queue_t *rq, int flag, cred_t *cr)
5340Sstevel@tonic-gate {
5350Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)rq->q_ptr;
5360Sstevel@tonic-gate 	int		online;
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 	if (pp == NULL) {
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 		return (ENXIO);
5410Sstevel@tonic-gate 	}
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 	online = usbser_dev_is_online(pp->port_usp);
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate 	/*
5460Sstevel@tonic-gate 	 * in the closing state new activities will not be initiated
5470Sstevel@tonic-gate 	 */
5480Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
5490Sstevel@tonic-gate 	pp->port_state = USBSER_PORT_CLOSING;
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate 	if (online) {
5520Sstevel@tonic-gate 		/* drain the data */
5530Sstevel@tonic-gate 		usbser_close_drain(pp);
5540Sstevel@tonic-gate 	}
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 	/* stop break/delay */
5570Sstevel@tonic-gate 	usbser_close_cancel_break(pp);
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate 	if (online) {
5600Sstevel@tonic-gate 		/* hangup line */
5610Sstevel@tonic-gate 		usbser_close_hangup(pp);
5620Sstevel@tonic-gate 	}
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 	/*
5650Sstevel@tonic-gate 	 * close DSD, cleanup state and transition to 'closed' state
5660Sstevel@tonic-gate 	 */
5670Sstevel@tonic-gate 	usbser_close_cleanup(pp);
5680Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_CLOSE, pp->port_lh, "usbser_close: end");
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate 	return (0);
5730Sstevel@tonic-gate }
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate /*
5770Sstevel@tonic-gate  * read side service routine: send as much as possible messages upstream
5780Sstevel@tonic-gate  * and if there is still place on the queue, enable receive (if not already)
5790Sstevel@tonic-gate  */
5800Sstevel@tonic-gate int
usbser_rsrv(queue_t * q)5810Sstevel@tonic-gate usbser_rsrv(queue_t *q)
5820Sstevel@tonic-gate {
5830Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)q->q_ptr;
5840Sstevel@tonic-gate 	mblk_t		*mp;
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 	usbser_st_rsrv++;
5870Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_RQ, pp->port_lh, "usbser_rsrv");
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	while (canputnext(q) && (mp = getq(q))) {
5900Sstevel@tonic-gate 		putnext(q, mp);
5910Sstevel@tonic-gate 	}
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 	if (canputnext(q)) {
5940Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
5950Sstevel@tonic-gate 		ASSERT(pp->port_state != USBSER_PORT_CLOSED);
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 		if (USBSER_PORT_ACCESS_OK(pp)) {
5980Sstevel@tonic-gate 			usbser_thr_wake(&pp->port_rq_thread);
5990Sstevel@tonic-gate 		}
6000Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
6010Sstevel@tonic-gate 	}
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 	return (0);
6040Sstevel@tonic-gate }
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate /*
6080Sstevel@tonic-gate  * wput: put message on the queue and wake wq thread
6090Sstevel@tonic-gate  */
6100Sstevel@tonic-gate int
usbser_wput(queue_t * q,mblk_t * mp)6110Sstevel@tonic-gate usbser_wput(queue_t *q, mblk_t *mp)
6120Sstevel@tonic-gate {
6130Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)q->q_ptr;
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wput");
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
6180Sstevel@tonic-gate 	ASSERT(pp->port_state != USBSER_PORT_CLOSED);
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 	/* ignore new messages if port is already closing */
6210Sstevel@tonic-gate 	if (pp->port_state == USBSER_PORT_CLOSING) {
6220Sstevel@tonic-gate 		freemsg(mp);
6230Sstevel@tonic-gate 	} else if (putq(q, mp)) {
6240Sstevel@tonic-gate 		/*
6250Sstevel@tonic-gate 		 * this counter represents amount of tx data on the wq.
6260Sstevel@tonic-gate 		 * each time the data is passed to DSD for transmission,
6270Sstevel@tonic-gate 		 * the counter is decremented accordingly
6280Sstevel@tonic-gate 		 */
6290Sstevel@tonic-gate 		pp->port_wq_data_cnt += msgdsize(mp);
6300Sstevel@tonic-gate 	} else {
6310Sstevel@tonic-gate 		usbser_st_tx_data_loss++;
6320Sstevel@tonic-gate 	}
6330Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	return (0);
6360Sstevel@tonic-gate }
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate /*
6400Sstevel@tonic-gate  * we need wsrv() routine to take advantage of STREAMS flow control:
6410Sstevel@tonic-gate  * without it the framework will consider we are always able to process msgs
6420Sstevel@tonic-gate  */
6430Sstevel@tonic-gate int
usbser_wsrv(queue_t * q)6440Sstevel@tonic-gate usbser_wsrv(queue_t *q)
6450Sstevel@tonic-gate {
6460Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)q->q_ptr;
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wsrv");
6490Sstevel@tonic-gate 
6500Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
6510Sstevel@tonic-gate 	ASSERT(pp->port_state != USBSER_PORT_CLOSED);
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 	if (USBSER_PORT_ACCESS_OK(pp)) {
6540Sstevel@tonic-gate 		usbser_thr_wake(&pp->port_wq_thread);
6550Sstevel@tonic-gate 	}
6560Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
6570Sstevel@tonic-gate 
6580Sstevel@tonic-gate 	return (0);
6590Sstevel@tonic-gate }
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate /*
6630Sstevel@tonic-gate  * power entry point
6640Sstevel@tonic-gate  */
6650Sstevel@tonic-gate int
usbser_power(dev_info_t * dip,int comp,int level)6660Sstevel@tonic-gate usbser_power(dev_info_t *dip, int comp, int level)
6670Sstevel@tonic-gate {
6680Sstevel@tonic-gate 	void		*statep;
6690Sstevel@tonic-gate 	usbser_state_t	*usp;
6700Sstevel@tonic-gate 	int		new_state;
6710Sstevel@tonic-gate 	int		rval;
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 	statep = ddi_get_driver_private(dip);
6740Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
6776898Sfb209375 	    "usbser_power: dip=0x%p, comp=%d, level=%d",
6786898Sfb209375 	    (void *)dip, comp, level);
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
6810Sstevel@tonic-gate 	new_state = usp->us_dev_state;
6820Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate 	/* let DSD do the job */
6850Sstevel@tonic-gate 	rval = USBSER_DS_USB_POWER(usp, comp, level, &new_state);
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 	/* stay in sync with DSD */
6880Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
6890Sstevel@tonic-gate 	usp->us_dev_state = new_state;
6900Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate 	return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
6930Sstevel@tonic-gate }
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate 
6960Sstevel@tonic-gate /*
6970Sstevel@tonic-gate  *
6980Sstevel@tonic-gate  * configuration entry point subroutines
6990Sstevel@tonic-gate  * -------------------------------------
7000Sstevel@tonic-gate  *
7010Sstevel@tonic-gate  * rseq callback
7020Sstevel@tonic-gate  */
7030Sstevel@tonic-gate static int
usbser_rseq_do_cb(rseq_t * rseq,int num,uintptr_t arg)7040Sstevel@tonic-gate usbser_rseq_do_cb(rseq_t *rseq, int num, uintptr_t arg)
7050Sstevel@tonic-gate {
7060Sstevel@tonic-gate 	usbser_state_t *usp = (usbser_state_t *)arg;
7070Sstevel@tonic-gate 	int	rval = rseq[num].r_do.s_rval;
7080Sstevel@tonic-gate 	char	*name = rseq[num].r_do.s_name;
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate 	if (rval != DDI_SUCCESS) {
7110Sstevel@tonic-gate 		USB_DPRINTF_L2(DPRINT_ATTACH, usp->us_lh,
7120Sstevel@tonic-gate 		    "do %s failed (%d)", name, rval);
7130Sstevel@tonic-gate 
7140Sstevel@tonic-gate 		return (RSEQ_UNDO);
7150Sstevel@tonic-gate 	} else {
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 		return (RSEQ_OK);
7180Sstevel@tonic-gate 	}
7190Sstevel@tonic-gate }
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate /*
7230Sstevel@tonic-gate  * free soft state
7240Sstevel@tonic-gate  */
7250Sstevel@tonic-gate static int
usbser_free_soft_state(usbser_state_t * usp)7260Sstevel@tonic-gate usbser_free_soft_state(usbser_state_t *usp)
7270Sstevel@tonic-gate {
7280Sstevel@tonic-gate 	ddi_soft_state_free(usp->us_statep, usp->us_instance);
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate 	return (USB_SUCCESS);
7310Sstevel@tonic-gate }
7320Sstevel@tonic-gate 
7330Sstevel@tonic-gate /*
7340Sstevel@tonic-gate  * init instance soft state
7350Sstevel@tonic-gate  */
7360Sstevel@tonic-gate static int
usbser_init_soft_state(usbser_state_t * usp)7370Sstevel@tonic-gate usbser_init_soft_state(usbser_state_t *usp)
7380Sstevel@tonic-gate {
7390Sstevel@tonic-gate 	usp->us_lh = usb_alloc_log_hdl(usp->us_dip, "usbs[*].",
7405541Slg150142 	    &usbser_errlevel, &usbser_errmask, &usbser_instance_debug,
7415541Slg150142 	    0);
7420Sstevel@tonic-gate 	mutex_init(&usp->us_mutex, NULL, MUTEX_DRIVER, (void *)NULL);
7430Sstevel@tonic-gate 
7440Sstevel@tonic-gate 	/* save state pointer for use in event callbacks */
7450Sstevel@tonic-gate 	ddi_set_driver_private(usp->us_dip, usp->us_statep);
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate 	usp->us_dev_state = USBSER_DEV_INIT;
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate 	return (DDI_SUCCESS);
7500Sstevel@tonic-gate }
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate /*
7530Sstevel@tonic-gate  * fini instance soft state
7540Sstevel@tonic-gate  */
7550Sstevel@tonic-gate static int
usbser_fini_soft_state(usbser_state_t * usp)7560Sstevel@tonic-gate usbser_fini_soft_state(usbser_state_t *usp)
7570Sstevel@tonic-gate {
7580Sstevel@tonic-gate 	usb_free_log_hdl(usp->us_lh);
7590Sstevel@tonic-gate 	mutex_destroy(&usp->us_mutex);
7600Sstevel@tonic-gate 	ddi_set_driver_private(usp->us_dip, NULL);
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate 	return (DDI_SUCCESS);
7630Sstevel@tonic-gate }
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate /*
7660Sstevel@tonic-gate  * attach entire device
7670Sstevel@tonic-gate  */
7680Sstevel@tonic-gate static int
usbser_attach_dev(usbser_state_t * usp)7690Sstevel@tonic-gate usbser_attach_dev(usbser_state_t *usp)
7700Sstevel@tonic-gate {
7710Sstevel@tonic-gate 	ds_attach_info_t ai;
7720Sstevel@tonic-gate 	int		rval;
7730Sstevel@tonic-gate 
7740Sstevel@tonic-gate 	usp->us_dev_state = USB_DEV_ONLINE;
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	ai.ai_dip = usp->us_dip;
7770Sstevel@tonic-gate 	ai.ai_usb_events = &usbser_usb_events;
7780Sstevel@tonic-gate 	ai.ai_hdl = &usp->us_ds_hdl;
7790Sstevel@tonic-gate 	ai.ai_port_cnt = &usp->us_port_cnt;
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 	rval = USBSER_DS_ATTACH(usp, &ai);
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 	if ((rval != USB_SUCCESS) || (usp->us_ds_hdl == NULL) ||
7840Sstevel@tonic-gate 	    (usp->us_port_cnt == 0)) {
7850Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_ATTACH, usp->us_lh, "usbser_attach_dev: "
7860Sstevel@tonic-gate 		    "failed %d %p %d", rval, usp->us_ds_hdl, usp->us_port_cnt);
7870Sstevel@tonic-gate 
7880Sstevel@tonic-gate 		return (DDI_FAILURE);
7890Sstevel@tonic-gate 	}
7900Sstevel@tonic-gate 
7910Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_ATTACH, usp->us_lh,
7920Sstevel@tonic-gate 	    "usbser_attach_dev: port_cnt = %d", usp->us_port_cnt);
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate 	return (DDI_SUCCESS);
7950Sstevel@tonic-gate }
7960Sstevel@tonic-gate 
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate /*
7990Sstevel@tonic-gate  * detach entire device
8000Sstevel@tonic-gate  */
8010Sstevel@tonic-gate static void
usbser_detach_dev(usbser_state_t * usp)8020Sstevel@tonic-gate usbser_detach_dev(usbser_state_t *usp)
8030Sstevel@tonic-gate {
8040Sstevel@tonic-gate 	USBSER_DS_DETACH(usp);
8050Sstevel@tonic-gate }
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate /*
8090Sstevel@tonic-gate  * attach each individual port
8100Sstevel@tonic-gate  */
8110Sstevel@tonic-gate static int
usbser_attach_ports(usbser_state_t * usp)8120Sstevel@tonic-gate usbser_attach_ports(usbser_state_t *usp)
8130Sstevel@tonic-gate {
8140Sstevel@tonic-gate 	int		i;
8150Sstevel@tonic-gate 	usbser_port_t	*pp;
8160Sstevel@tonic-gate 	ds_cb_t		ds_cb;
8170Sstevel@tonic-gate 
8180Sstevel@tonic-gate 	/*
8190Sstevel@tonic-gate 	 * allocate port array
8200Sstevel@tonic-gate 	 */
8210Sstevel@tonic-gate 	usp->us_ports = kmem_zalloc(usp->us_port_cnt *
8225541Slg150142 	    sizeof (usbser_port_t), KM_SLEEP);
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate 	/* callback handlers */
8250Sstevel@tonic-gate 	ds_cb.cb_tx = usbser_tx_cb;
8260Sstevel@tonic-gate 	ds_cb.cb_rx = usbser_rx_cb;
8270Sstevel@tonic-gate 	ds_cb.cb_status = usbser_status_cb;
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate 	/*
8300Sstevel@tonic-gate 	 * initialize each port
8310Sstevel@tonic-gate 	 */
8320Sstevel@tonic-gate 	for (i = 0; i < usp->us_port_cnt; i++) {
8330Sstevel@tonic-gate 		pp = &usp->us_ports[i];
8340Sstevel@tonic-gate 
8350Sstevel@tonic-gate 		/*
8360Sstevel@tonic-gate 		 * initialize data
8370Sstevel@tonic-gate 		 */
8380Sstevel@tonic-gate 		pp->port_num = i;
8390Sstevel@tonic-gate 		pp->port_usp = usp;
8400Sstevel@tonic-gate 		pp->port_ds_ops = usp->us_ds_ops;
8410Sstevel@tonic-gate 		pp->port_ds_hdl = usp->us_ds_hdl;
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate 		/* allocate log handle */
8440Sstevel@tonic-gate 		(void) sprintf(pp->port_lh_name, "usbs[%d].", i);
8450Sstevel@tonic-gate 		pp->port_lh = usb_alloc_log_hdl(usp->us_dip,
8465541Slg150142 		    pp->port_lh_name, &usbser_errlevel, &usbser_errmask,
8475541Slg150142 		    &usbser_instance_debug, 0);
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 		mutex_init(&pp->port_mutex, NULL, MUTEX_DRIVER, (void *)NULL);
8500Sstevel@tonic-gate 		cv_init(&pp->port_state_cv, NULL, CV_DEFAULT, NULL);
8510Sstevel@tonic-gate 		cv_init(&pp->port_act_cv, NULL, CV_DEFAULT, NULL);
8520Sstevel@tonic-gate 		cv_init(&pp->port_car_cv, NULL, CV_DEFAULT, NULL);
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 		/*
8550Sstevel@tonic-gate 		 * init threads
8560Sstevel@tonic-gate 		 */
8570Sstevel@tonic-gate 		pp->port_wq_thread.thr_port = pp;
8580Sstevel@tonic-gate 		pp->port_wq_thread.thr_func = usbser_wq_thread;
8590Sstevel@tonic-gate 		pp->port_wq_thread.thr_arg = (void *)&pp->port_wq_thread;
8600Sstevel@tonic-gate 		cv_init(&pp->port_wq_thread.thr_cv, NULL, CV_DEFAULT, NULL);
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 		pp->port_rq_thread.thr_port = pp;
8630Sstevel@tonic-gate 		pp->port_rq_thread.thr_func = usbser_rq_thread;
8640Sstevel@tonic-gate 		pp->port_rq_thread.thr_arg = (void *)&pp->port_rq_thread;
8650Sstevel@tonic-gate 		cv_init(&pp->port_rq_thread.thr_cv, NULL, CV_DEFAULT, NULL);
8660Sstevel@tonic-gate 
8670Sstevel@tonic-gate 		/*
8680Sstevel@tonic-gate 		 * register callbacks
8690Sstevel@tonic-gate 		 */
8700Sstevel@tonic-gate 		ds_cb.cb_arg = (caddr_t)pp;
8710Sstevel@tonic-gate 		USBSER_DS_REGISTER_CB(usp, i, &ds_cb);
8720Sstevel@tonic-gate 
8730Sstevel@tonic-gate 		pp->port_state = USBSER_PORT_CLOSED;
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 		if (usbser_create_port_minor_nodes(usp, i) != USB_SUCCESS) {
8760Sstevel@tonic-gate 			usbser_detach_ports(usp);
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 			return (DDI_FAILURE);
8790Sstevel@tonic-gate 		}
8800Sstevel@tonic-gate 	}
8810Sstevel@tonic-gate 
8820Sstevel@tonic-gate 	return (DDI_SUCCESS);
8830Sstevel@tonic-gate }
8840Sstevel@tonic-gate 
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate /*
8870Sstevel@tonic-gate  * create a pair of minor nodes for the port
8880Sstevel@tonic-gate  */
8890Sstevel@tonic-gate static int
usbser_create_port_minor_nodes(usbser_state_t * usp,int port_num)8900Sstevel@tonic-gate usbser_create_port_minor_nodes(usbser_state_t *usp, int port_num)
8910Sstevel@tonic-gate {
8920Sstevel@tonic-gate 	int	instance = usp->us_instance;
8930Sstevel@tonic-gate 	minor_t	minor;
8940Sstevel@tonic-gate 	char	name[16];
8950Sstevel@tonic-gate 
8960Sstevel@tonic-gate 	/*
8970Sstevel@tonic-gate 	 * tty node
8980Sstevel@tonic-gate 	 */
8990Sstevel@tonic-gate 	(void) sprintf(name, "%d", port_num);
9000Sstevel@tonic-gate 	minor = USBSER_MAKEMINOR(instance, port_num, 0);
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 	if (ddi_create_minor_node(usp->us_dip, name,
9030Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_SERIAL, NULL) != DDI_SUCCESS) {
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 		return (USB_FAILURE);
9060Sstevel@tonic-gate 	}
9070Sstevel@tonic-gate 
9080Sstevel@tonic-gate 	/*
9090Sstevel@tonic-gate 	 * dial-out node
9100Sstevel@tonic-gate 	 */
9110Sstevel@tonic-gate 	(void) sprintf(name, "%d,cu", port_num);
9120Sstevel@tonic-gate 	minor = USBSER_MAKEMINOR(instance, port_num, OUTLINE);
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate 	if (ddi_create_minor_node(usp->us_dip, name,
9150Sstevel@tonic-gate 	    S_IFCHR, minor, DDI_NT_SERIAL_DO, NULL) != DDI_SUCCESS) {
9160Sstevel@tonic-gate 
9170Sstevel@tonic-gate 		return (USB_FAILURE);
9180Sstevel@tonic-gate 	}
9190Sstevel@tonic-gate 
9200Sstevel@tonic-gate 	return (USB_SUCCESS);
9210Sstevel@tonic-gate }
9220Sstevel@tonic-gate 
9230Sstevel@tonic-gate 
9240Sstevel@tonic-gate /*
9250Sstevel@tonic-gate  * detach each port individually
9260Sstevel@tonic-gate  */
9270Sstevel@tonic-gate static void
usbser_detach_ports(usbser_state_t * usp)9280Sstevel@tonic-gate usbser_detach_ports(usbser_state_t *usp)
9290Sstevel@tonic-gate {
9300Sstevel@tonic-gate 	int		i;
9310Sstevel@tonic-gate 	int		sz;
9320Sstevel@tonic-gate 	usbser_port_t	*pp;
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate 	/*
9350Sstevel@tonic-gate 	 * remove all minor nodes
9360Sstevel@tonic-gate 	 */
9370Sstevel@tonic-gate 	ddi_remove_minor_node(usp->us_dip, NULL);
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate 	for (i = 0; i < usp->us_port_cnt; i++) {
9400Sstevel@tonic-gate 		pp = &usp->us_ports[i];
9410Sstevel@tonic-gate 
9420Sstevel@tonic-gate 		if (pp->port_state != USBSER_PORT_CLOSED) {
9430Sstevel@tonic-gate 			ASSERT(pp->port_state == USBSER_PORT_NOT_INIT);
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 			continue;
9460Sstevel@tonic-gate 		}
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 		USBSER_DS_UNREGISTER_CB(usp, i);
9490Sstevel@tonic-gate 
9500Sstevel@tonic-gate 		mutex_destroy(&pp->port_mutex);
9510Sstevel@tonic-gate 		cv_destroy(&pp->port_state_cv);
9520Sstevel@tonic-gate 		cv_destroy(&pp->port_act_cv);
9530Sstevel@tonic-gate 		cv_destroy(&pp->port_car_cv);
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate 		cv_destroy(&pp->port_wq_thread.thr_cv);
9560Sstevel@tonic-gate 		cv_destroy(&pp->port_rq_thread.thr_cv);
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 		usb_free_log_hdl(pp->port_lh);
9590Sstevel@tonic-gate 	}
9600Sstevel@tonic-gate 
9610Sstevel@tonic-gate 	/*
9620Sstevel@tonic-gate 	 * free memory
9630Sstevel@tonic-gate 	 */
9640Sstevel@tonic-gate 	sz = usp->us_port_cnt * sizeof (usbser_port_t);
9650Sstevel@tonic-gate 	kmem_free(usp->us_ports, sz);
9660Sstevel@tonic-gate 	usp->us_ports = NULL;
9670Sstevel@tonic-gate }
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate /*
9710Sstevel@tonic-gate  * create a taskq with two threads per port (read and write sides)
9720Sstevel@tonic-gate  */
9730Sstevel@tonic-gate static int
usbser_create_taskq(usbser_state_t * usp)9740Sstevel@tonic-gate usbser_create_taskq(usbser_state_t *usp)
9750Sstevel@tonic-gate {
9760Sstevel@tonic-gate 	int	nthr = usp->us_port_cnt * 2;
9770Sstevel@tonic-gate 
9780Sstevel@tonic-gate 	usp->us_taskq = ddi_taskq_create(usp->us_dip, "usbser_taskq",
9790Sstevel@tonic-gate 	    nthr, TASKQ_DEFAULTPRI, 0);
9800Sstevel@tonic-gate 
9810Sstevel@tonic-gate 	return ((usp->us_taskq == NULL) ? DDI_FAILURE : DDI_SUCCESS);
9820Sstevel@tonic-gate }
9830Sstevel@tonic-gate 
9840Sstevel@tonic-gate 
9850Sstevel@tonic-gate static void
usbser_destroy_taskq(usbser_state_t * usp)9860Sstevel@tonic-gate usbser_destroy_taskq(usbser_state_t *usp)
9870Sstevel@tonic-gate {
9880Sstevel@tonic-gate 	ddi_taskq_destroy(usp->us_taskq);
9890Sstevel@tonic-gate }
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 
9920Sstevel@tonic-gate static void
usbser_set_dev_state_init(usbser_state_t * usp)9930Sstevel@tonic-gate usbser_set_dev_state_init(usbser_state_t *usp)
9940Sstevel@tonic-gate {
9950Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
9960Sstevel@tonic-gate 	usp->us_dev_state = USBSER_DEV_INIT;
9970Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
9980Sstevel@tonic-gate }
9990Sstevel@tonic-gate 
10000Sstevel@tonic-gate /*
10010Sstevel@tonic-gate  * hotplugging and power management
10020Sstevel@tonic-gate  * ---------------------------------
10030Sstevel@tonic-gate  *
10040Sstevel@tonic-gate  * disconnect event callback
10050Sstevel@tonic-gate  */
10060Sstevel@tonic-gate /*ARGSUSED*/
10070Sstevel@tonic-gate static int
usbser_disconnect_cb(dev_info_t * dip)10080Sstevel@tonic-gate usbser_disconnect_cb(dev_info_t *dip)
10090Sstevel@tonic-gate {
10100Sstevel@tonic-gate 	void		*statep;
10110Sstevel@tonic-gate 	usbser_state_t	*usp;
10120Sstevel@tonic-gate 
10130Sstevel@tonic-gate 	statep = ddi_get_driver_private(dip);
10140Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
10150Sstevel@tonic-gate 
10160Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
10176898Sfb209375 	    "usbser_disconnect_cb: dip=%p", (void *)dip);
10180Sstevel@tonic-gate 
10190Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
10205541Slg150142 	switch (usp->us_dev_state) {
10215541Slg150142 	case USB_DEV_ONLINE:
10225541Slg150142 	case USB_DEV_PWRED_DOWN:
10235541Slg150142 		/* prevent further activity */
10245541Slg150142 		usp->us_dev_state = USB_DEV_DISCONNECTED;
10250Sstevel@tonic-gate 		mutex_exit(&usp->us_mutex);
10260Sstevel@tonic-gate 
10275541Slg150142 		/* see if any of the ports are open and do necessary handling */
10285541Slg150142 		usbser_disconnect_ports(usp);
10295541Slg150142 
10305541Slg150142 		/* call DSD to do any necessary work */
10315541Slg150142 		if (USBSER_DS_DISCONNECT(usp) != USB_DEV_DISCONNECTED) {
10325541Slg150142 			USB_DPRINTF_L2(DPRINT_EVENTS, usp->us_lh,
10335541Slg150142 			    "usbser_disconnect_cb: ds_disconnect failed");
10345541Slg150142 		}
10355541Slg150142 
10365541Slg150142 		break;
10375541Slg150142 	case USB_DEV_SUSPENDED:
10385541Slg150142 		/* we remain suspended */
10395541Slg150142 	default:
10405541Slg150142 		mutex_exit(&usp->us_mutex);
10415541Slg150142 
10425541Slg150142 		break;
10430Sstevel@tonic-gate 	}
10440Sstevel@tonic-gate 
10450Sstevel@tonic-gate 	return (USB_SUCCESS);
10460Sstevel@tonic-gate }
10470Sstevel@tonic-gate 
10480Sstevel@tonic-gate 
10490Sstevel@tonic-gate /*
10500Sstevel@tonic-gate  * reconnect event callback
10510Sstevel@tonic-gate  */
10520Sstevel@tonic-gate /*ARGSUSED*/
10530Sstevel@tonic-gate static int
usbser_reconnect_cb(dev_info_t * dip)10540Sstevel@tonic-gate usbser_reconnect_cb(dev_info_t *dip)
10550Sstevel@tonic-gate {
10560Sstevel@tonic-gate 	void		*statep;
10570Sstevel@tonic-gate 	usbser_state_t	*usp;
10580Sstevel@tonic-gate 
10590Sstevel@tonic-gate 	statep = ddi_get_driver_private(dip);
10600Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
10636898Sfb209375 	    "usbser_reconnect_cb: dip=%p", (void *)dip);
10640Sstevel@tonic-gate 
10650Sstevel@tonic-gate 	(void) usbser_restore_device_state(usp);
10660Sstevel@tonic-gate 
10670Sstevel@tonic-gate 	return (USB_SUCCESS);
10680Sstevel@tonic-gate }
10690Sstevel@tonic-gate 
10700Sstevel@tonic-gate 
10710Sstevel@tonic-gate /*
10720Sstevel@tonic-gate  * if any of the ports is open during disconnect,
10730Sstevel@tonic-gate  * send M_HANGUP message upstream and log a warning
10740Sstevel@tonic-gate  */
10750Sstevel@tonic-gate static void
usbser_disconnect_ports(usbser_state_t * usp)10760Sstevel@tonic-gate usbser_disconnect_ports(usbser_state_t *usp)
10770Sstevel@tonic-gate {
10780Sstevel@tonic-gate 	usbser_port_t	*pp;
10790Sstevel@tonic-gate 	queue_t		*rq;
10800Sstevel@tonic-gate 	int		complain = 0;
10810Sstevel@tonic-gate 	int		hangup = 0;
10820Sstevel@tonic-gate 	timeout_id_t	delay_id = 0;
10830Sstevel@tonic-gate 	int		i;
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate 	if (usp->us_ports == NULL) {
10860Sstevel@tonic-gate 		return;
10870Sstevel@tonic-gate 	}
10880Sstevel@tonic-gate 
10890Sstevel@tonic-gate 	for (i = 0; i < usp->us_port_cnt; i++) {
10900Sstevel@tonic-gate 		pp = &usp->us_ports[i];
10910Sstevel@tonic-gate 
10920Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
10930Sstevel@tonic-gate 		if (pp->port_state == USBSER_PORT_OPEN ||
10940Sstevel@tonic-gate 		    USBSER_IS_OPENING(pp) ||
10950Sstevel@tonic-gate 		    pp->port_state == USBSER_PORT_CLOSING) {
10960Sstevel@tonic-gate 			complain = 1;
10970Sstevel@tonic-gate 		}
10980Sstevel@tonic-gate 
10990Sstevel@tonic-gate 		if (pp->port_state == USBSER_PORT_OPEN) {
11000Sstevel@tonic-gate 			rq = pp->port_ttycommon.t_readq;
11010Sstevel@tonic-gate 
11020Sstevel@tonic-gate 			/*
11030Sstevel@tonic-gate 			 * hangup the stream; will send actual
11040Sstevel@tonic-gate 			 * M_HANGUP message after releasing mutex
11050Sstevel@tonic-gate 			 */
11060Sstevel@tonic-gate 			pp->port_flags |= USBSER_FL_HUNGUP;
11070Sstevel@tonic-gate 			hangup = 1;
11080Sstevel@tonic-gate 
11090Sstevel@tonic-gate 			/*
11100Sstevel@tonic-gate 			 * cancel all activities
11110Sstevel@tonic-gate 			 */
11120Sstevel@tonic-gate 			usbser_release_port_act(pp, USBSER_ACT_ALL);
11130Sstevel@tonic-gate 
11140Sstevel@tonic-gate 			delay_id = pp->port_delay_id;
11150Sstevel@tonic-gate 			pp->port_delay_id = 0;
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate 			/* mark disconnected */
11180Sstevel@tonic-gate 			pp->port_state = USBSER_PORT_DISCONNECTED;
11190Sstevel@tonic-gate 			cv_broadcast(&pp->port_state_cv);
11200Sstevel@tonic-gate 		}
11210Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
11220Sstevel@tonic-gate 
11230Sstevel@tonic-gate 		if (hangup) {
11240Sstevel@tonic-gate 			(void) putnextctl(rq, M_HANGUP);
11250Sstevel@tonic-gate 			hangup = 0;
11260Sstevel@tonic-gate 		}
11270Sstevel@tonic-gate 
11280Sstevel@tonic-gate 		/*
11290Sstevel@tonic-gate 		 * we couldn't untimeout while holding the mutex - do it now
11300Sstevel@tonic-gate 		 */
11310Sstevel@tonic-gate 		if (delay_id) {
11320Sstevel@tonic-gate 			(void) untimeout(delay_id);
11330Sstevel@tonic-gate 			delay_id = 0;
11340Sstevel@tonic-gate 		}
11350Sstevel@tonic-gate 	}
11360Sstevel@tonic-gate 
11370Sstevel@tonic-gate 	/*
11380Sstevel@tonic-gate 	 * complain about disconnecting device while open
11390Sstevel@tonic-gate 	 */
11400Sstevel@tonic-gate 	if (complain) {
11410Sstevel@tonic-gate 		USB_DPRINTF_L0(DPRINT_EVENTS, usp->us_lh, "device was "
11420Sstevel@tonic-gate 		    "disconnected while open. Data may have been lost");
11430Sstevel@tonic-gate 	}
11440Sstevel@tonic-gate }
11450Sstevel@tonic-gate 
11460Sstevel@tonic-gate 
11470Sstevel@tonic-gate /*
11480Sstevel@tonic-gate  * do CPR suspend
11490Sstevel@tonic-gate  *
11500Sstevel@tonic-gate  * We use a trivial CPR strategy - fail if any of the device's ports are open.
11510Sstevel@tonic-gate  * The problem with more sophisticated strategies is that each open port uses
11520Sstevel@tonic-gate  * two threads that sit in the loop until the port is closed, while CPR has to
11530Sstevel@tonic-gate  * stop all kernel threads to succeed. Stopping port threads is a rather
11540Sstevel@tonic-gate  * intrusive and delicate procedure; I leave it as an RFE for now.
11550Sstevel@tonic-gate  *
11560Sstevel@tonic-gate  */
11570Sstevel@tonic-gate static int
usbser_cpr_suspend(dev_info_t * dip)11580Sstevel@tonic-gate usbser_cpr_suspend(dev_info_t *dip)
11590Sstevel@tonic-gate {
11600Sstevel@tonic-gate 	void		*statep;
11610Sstevel@tonic-gate 	usbser_state_t	*usp;
11620Sstevel@tonic-gate 	int		new_state;
11630Sstevel@tonic-gate 	int		rval;
11640Sstevel@tonic-gate 
11650Sstevel@tonic-gate 	statep = ddi_get_driver_private(dip);
11660Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
11670Sstevel@tonic-gate 
11680Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_EVENTS, usp->us_lh, "usbser_cpr_suspend");
11690Sstevel@tonic-gate 
11700Sstevel@tonic-gate 	/* suspend each port first */
11710Sstevel@tonic-gate 	if (usbser_suspend_ports(usp) != USB_SUCCESS) {
11720Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh,
11730Sstevel@tonic-gate 		    "usbser_cpr_suspend: GSD failure");
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate 		return (USB_FAILURE);
11760Sstevel@tonic-gate 	}
11770Sstevel@tonic-gate 
11780Sstevel@tonic-gate 	new_state = USBSER_DS_SUSPEND(usp);	/* let DSD do its part */
11790Sstevel@tonic-gate 
11800Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
11810Sstevel@tonic-gate 	if (new_state == USB_DEV_SUSPENDED) {
11820Sstevel@tonic-gate 		rval = USB_SUCCESS;
11830Sstevel@tonic-gate 	} else {
11840Sstevel@tonic-gate 		ASSERT(new_state == USB_DEV_ONLINE);
11850Sstevel@tonic-gate 		rval = USB_FAILURE;
11860Sstevel@tonic-gate 	}
11870Sstevel@tonic-gate 	usp->us_dev_state = new_state;
11880Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
11890Sstevel@tonic-gate 
11900Sstevel@tonic-gate 	return (rval);
11910Sstevel@tonic-gate }
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate 
11940Sstevel@tonic-gate static int
usbser_suspend_ports(usbser_state_t * usp)11950Sstevel@tonic-gate usbser_suspend_ports(usbser_state_t *usp)
11960Sstevel@tonic-gate {
11970Sstevel@tonic-gate 	usbser_port_t	*pp;
11980Sstevel@tonic-gate 	int		i;
11990Sstevel@tonic-gate 
12000Sstevel@tonic-gate 	for (i = 0; i < usp->us_port_cnt; i++) {
12010Sstevel@tonic-gate 		pp = &usp->us_ports[i];
12020Sstevel@tonic-gate 
12030Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
12040Sstevel@tonic-gate 		if (pp->port_state != USBSER_PORT_CLOSED) {
12050Sstevel@tonic-gate 			mutex_exit(&pp->port_mutex);
12060Sstevel@tonic-gate 
12070Sstevel@tonic-gate 			return (USB_FAILURE);
12080Sstevel@tonic-gate 		}
12090Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
12100Sstevel@tonic-gate 	}
12110Sstevel@tonic-gate 
12120Sstevel@tonic-gate 	return (USB_SUCCESS);
12130Sstevel@tonic-gate }
12140Sstevel@tonic-gate 
12150Sstevel@tonic-gate 
12160Sstevel@tonic-gate /*
12170Sstevel@tonic-gate  * do CPR resume
12180Sstevel@tonic-gate  *
12190Sstevel@tonic-gate  * DSD will return USB_DEV_ONLINE in case of success
12200Sstevel@tonic-gate  */
12210Sstevel@tonic-gate static void
usbser_cpr_resume(dev_info_t * dip)12220Sstevel@tonic-gate usbser_cpr_resume(dev_info_t *dip)
12230Sstevel@tonic-gate {
12240Sstevel@tonic-gate 	void		*statep;
12250Sstevel@tonic-gate 	usbser_state_t	*usp;
12260Sstevel@tonic-gate 
12270Sstevel@tonic-gate 	statep = ddi_get_driver_private(dip);
12280Sstevel@tonic-gate 	usp = ddi_get_soft_state(statep, ddi_get_instance(dip));
12290Sstevel@tonic-gate 
12300Sstevel@tonic-gate 	USB_DPRINTF_L3(DPRINT_EVENTS, usp->us_lh, "usbser_cpr_resume");
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate 	(void) usbser_restore_device_state(usp);
12330Sstevel@tonic-gate }
12340Sstevel@tonic-gate 
12350Sstevel@tonic-gate 
12360Sstevel@tonic-gate /*
12370Sstevel@tonic-gate  * restore device state after CPR resume or reconnect
12380Sstevel@tonic-gate  */
12390Sstevel@tonic-gate static int
usbser_restore_device_state(usbser_state_t * usp)12400Sstevel@tonic-gate usbser_restore_device_state(usbser_state_t *usp)
12410Sstevel@tonic-gate {
12420Sstevel@tonic-gate 	int	new_state, current_state;
12430Sstevel@tonic-gate 
12445541Slg150142 	/* needed as power up state of dev is "unknown" to system */
12455541Slg150142 	(void) pm_busy_component(usp->us_dip, 0);
12465541Slg150142 	(void) pm_raise_power(usp->us_dip, 0, USB_DEV_OS_FULL_PWR);
12475541Slg150142 
12480Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
12490Sstevel@tonic-gate 	current_state = usp->us_dev_state;
12500Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
12510Sstevel@tonic-gate 
12520Sstevel@tonic-gate 	ASSERT((current_state == USB_DEV_DISCONNECTED) ||
12535541Slg150142 	    (current_state == USB_DEV_SUSPENDED));
12540Sstevel@tonic-gate 
12550Sstevel@tonic-gate 	/*
12560Sstevel@tonic-gate 	 * call DSD to perform device-specific work
12570Sstevel@tonic-gate 	 */
12580Sstevel@tonic-gate 	if (current_state == USB_DEV_DISCONNECTED) {
12590Sstevel@tonic-gate 		new_state = USBSER_DS_RECONNECT(usp);
12600Sstevel@tonic-gate 	} else {
12610Sstevel@tonic-gate 		new_state = USBSER_DS_RESUME(usp);
12620Sstevel@tonic-gate 	}
12630Sstevel@tonic-gate 
12640Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
12650Sstevel@tonic-gate 	usp->us_dev_state = new_state;
12660Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
12670Sstevel@tonic-gate 
12680Sstevel@tonic-gate 	if (new_state == USB_DEV_ONLINE) {
12690Sstevel@tonic-gate 		/*
12700Sstevel@tonic-gate 		 * restore ports state
12710Sstevel@tonic-gate 		 */
12720Sstevel@tonic-gate 		usbser_restore_ports_state(usp);
12730Sstevel@tonic-gate 	}
12740Sstevel@tonic-gate 
12755541Slg150142 	(void) pm_idle_component(usp->us_dip, 0);
12765541Slg150142 
12770Sstevel@tonic-gate 	return (USB_SUCCESS);
12780Sstevel@tonic-gate }
12790Sstevel@tonic-gate 
12800Sstevel@tonic-gate 
12810Sstevel@tonic-gate /*
12820Sstevel@tonic-gate  * restore ports state after device reconnect/resume
12830Sstevel@tonic-gate  */
12840Sstevel@tonic-gate static void
usbser_restore_ports_state(usbser_state_t * usp)12850Sstevel@tonic-gate usbser_restore_ports_state(usbser_state_t *usp)
12860Sstevel@tonic-gate {
12870Sstevel@tonic-gate 	usbser_port_t	*pp;
12880Sstevel@tonic-gate 	queue_t		*rq;
12890Sstevel@tonic-gate 	int		i;
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate 	for (i = 0; i < usp->us_port_cnt; i++) {
12920Sstevel@tonic-gate 		pp = &usp->us_ports[i];
12930Sstevel@tonic-gate 
12940Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
12950Sstevel@tonic-gate 		/*
12960Sstevel@tonic-gate 		 * only care about ports that are open
12970Sstevel@tonic-gate 		 */
12980Sstevel@tonic-gate 		if ((pp->port_state != USBSER_PORT_SUSPENDED) &&
12990Sstevel@tonic-gate 		    (pp->port_state != USBSER_PORT_DISCONNECTED)) {
13000Sstevel@tonic-gate 			mutex_exit(&pp->port_mutex);
13010Sstevel@tonic-gate 
13020Sstevel@tonic-gate 			continue;
13030Sstevel@tonic-gate 		}
13040Sstevel@tonic-gate 
13050Sstevel@tonic-gate 		pp->port_state = USBSER_PORT_OPEN;
13060Sstevel@tonic-gate 
13070Sstevel@tonic-gate 		/*
13080Sstevel@tonic-gate 		 * if the stream was hung up during disconnect, restore it
13090Sstevel@tonic-gate 		 */
13100Sstevel@tonic-gate 		if (pp->port_flags & USBSER_FL_HUNGUP) {
13110Sstevel@tonic-gate 			pp->port_flags &= ~USBSER_FL_HUNGUP;
13120Sstevel@tonic-gate 			rq = pp->port_ttycommon.t_readq;
13130Sstevel@tonic-gate 
13140Sstevel@tonic-gate 			mutex_exit(&pp->port_mutex);
13150Sstevel@tonic-gate 			(void) putnextctl(rq, M_UNHANGUP);
13160Sstevel@tonic-gate 			mutex_enter(&pp->port_mutex);
13170Sstevel@tonic-gate 		}
13180Sstevel@tonic-gate 
13190Sstevel@tonic-gate 		/*
13200Sstevel@tonic-gate 		 * restore serial parameters
13210Sstevel@tonic-gate 		 */
13220Sstevel@tonic-gate 		(void) usbser_port_program(pp);
13230Sstevel@tonic-gate 
13240Sstevel@tonic-gate 		/*
13250Sstevel@tonic-gate 		 * wake anything that might be sleeping
13260Sstevel@tonic-gate 		 */
13270Sstevel@tonic-gate 		cv_broadcast(&pp->port_state_cv);
13280Sstevel@tonic-gate 		cv_broadcast(&pp->port_act_cv);
13290Sstevel@tonic-gate 		usbser_thr_wake(&pp->port_wq_thread);
13300Sstevel@tonic-gate 		usbser_thr_wake(&pp->port_rq_thread);
13310Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
13320Sstevel@tonic-gate 	}
13330Sstevel@tonic-gate }
13340Sstevel@tonic-gate 
13350Sstevel@tonic-gate 
13360Sstevel@tonic-gate /*
13370Sstevel@tonic-gate  * STREAMS subroutines
13380Sstevel@tonic-gate  * -------------------
13390Sstevel@tonic-gate  *
13400Sstevel@tonic-gate  *
13410Sstevel@tonic-gate  * port open state machine
13420Sstevel@tonic-gate  *
13430Sstevel@tonic-gate  * here's a list of things that the driver has to do while open;
13440Sstevel@tonic-gate  * because device can be opened any number of times,
13450Sstevel@tonic-gate  * initial open has additional responsibilities:
13460Sstevel@tonic-gate  *
13470Sstevel@tonic-gate  *	if (initial_open) {
13480Sstevel@tonic-gate  *		initialize soft state;	\
13490Sstevel@tonic-gate  *		DSD open;		- see usbser_open_init()
13500Sstevel@tonic-gate  *		dispatch threads;	/
13510Sstevel@tonic-gate  *	}
13520Sstevel@tonic-gate  *	raise DTR;
13530Sstevel@tonic-gate  *	wait for carrier (if necessary);
13540Sstevel@tonic-gate  *
13550Sstevel@tonic-gate  * we should also take into consideration that two threads can try to open
13560Sstevel@tonic-gate  * the same physical port simultaneously (/dev/term/N and /dev/cua/N).
13570Sstevel@tonic-gate  *
13580Sstevel@tonic-gate  * return values:
13590Sstevel@tonic-gate  *	0	- success;
13600Sstevel@tonic-gate  *	>0	- fail with this error code;
13610Sstevel@tonic-gate  */
13620Sstevel@tonic-gate static int
usbser_open_setup(queue_t * rq,usbser_port_t * pp,int minor,int flag,cred_t * cr)13630Sstevel@tonic-gate usbser_open_setup(queue_t *rq, usbser_port_t *pp, int minor, int flag,
13640Sstevel@tonic-gate 		cred_t *cr)
13650Sstevel@tonic-gate {
13660Sstevel@tonic-gate 	int	rval = USBSER_CONTINUE;
13670Sstevel@tonic-gate 
13680Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
13690Sstevel@tonic-gate 	/*
13700Sstevel@tonic-gate 	 * refer to port state diagram in the header file
13710Sstevel@tonic-gate 	 */
13720Sstevel@tonic-gate loop:
13730Sstevel@tonic-gate 	switch (pp->port_state) {
13740Sstevel@tonic-gate 	case USBSER_PORT_CLOSED:
13750Sstevel@tonic-gate 		/*
13760Sstevel@tonic-gate 		 * initial open
13770Sstevel@tonic-gate 		 */
13780Sstevel@tonic-gate 		rval = usbser_open_init(pp, minor);
13790Sstevel@tonic-gate 
13800Sstevel@tonic-gate 		break;
13810Sstevel@tonic-gate 	case USBSER_PORT_OPENING_TTY:
13820Sstevel@tonic-gate 		/*
13830Sstevel@tonic-gate 		 * dial-out thread can overtake the port
13840Sstevel@tonic-gate 		 * if tty open thread is sleeping waiting for carrier
13850Sstevel@tonic-gate 		 */
13860Sstevel@tonic-gate 		if ((minor & OUTLINE) && (pp->port_flags & USBSER_FL_WOPEN)) {
13870Sstevel@tonic-gate 			pp->port_state = USBSER_PORT_OPENING_OUT;
13880Sstevel@tonic-gate 
13890Sstevel@tonic-gate 			USB_DPRINTF_L3(DPRINT_OPEN, pp->port_lh,
13900Sstevel@tonic-gate 			    "usbser_open_state: overtake");
13910Sstevel@tonic-gate 		}
13920Sstevel@tonic-gate 
13930Sstevel@tonic-gate 		/* FALLTHRU */
13940Sstevel@tonic-gate 	case USBSER_PORT_OPENING_OUT:
13950Sstevel@tonic-gate 		/*
13960Sstevel@tonic-gate 		 * if no other open in progress, setup the line
13970Sstevel@tonic-gate 		 */
13980Sstevel@tonic-gate 		if (USBSER_NO_OTHER_OPEN(pp, minor)) {
13990Sstevel@tonic-gate 			rval = usbser_open_line_setup(pp, minor, flag);
14000Sstevel@tonic-gate 
14010Sstevel@tonic-gate 			break;
14020Sstevel@tonic-gate 		}
14030Sstevel@tonic-gate 
14040Sstevel@tonic-gate 		/* FALLTHRU */
14050Sstevel@tonic-gate 	case USBSER_PORT_CLOSING:
14060Sstevel@tonic-gate 		/*
14070Sstevel@tonic-gate 		 * wait until close active phase ends
14080Sstevel@tonic-gate 		 */
14090Sstevel@tonic-gate 		if (cv_wait_sig(&pp->port_state_cv, &pp->port_mutex) == 0) {
14100Sstevel@tonic-gate 			rval = EINTR;
14110Sstevel@tonic-gate 		}
14120Sstevel@tonic-gate 
14130Sstevel@tonic-gate 		break;
14140Sstevel@tonic-gate 	case USBSER_PORT_OPEN:
14150Sstevel@tonic-gate 		if ((pp->port_ttycommon.t_flags & TS_XCLUDE) &&
14165541Slg150142 		    secpolicy_excl_open(cr) != 0) {
14170Sstevel@tonic-gate 			/*
14180Sstevel@tonic-gate 			 * exclusive use
14190Sstevel@tonic-gate 			 */
14200Sstevel@tonic-gate 			rval = EBUSY;
14210Sstevel@tonic-gate 		} else if (USBSER_OPEN_IN_OTHER_MODE(pp, minor)) {
14220Sstevel@tonic-gate 			/*
14230Sstevel@tonic-gate 			 * tty and dial-out modes are mutually exclusive
14240Sstevel@tonic-gate 			 */
14250Sstevel@tonic-gate 			rval = EBUSY;
14260Sstevel@tonic-gate 		} else {
14270Sstevel@tonic-gate 			/*
14280Sstevel@tonic-gate 			 * port is being re-open in the same mode
14290Sstevel@tonic-gate 			 */
14300Sstevel@tonic-gate 			rval = usbser_open_line_setup(pp, minor, flag);
14310Sstevel@tonic-gate 		}
14320Sstevel@tonic-gate 
14330Sstevel@tonic-gate 		break;
14340Sstevel@tonic-gate 	default:
14350Sstevel@tonic-gate 		rval = ENXIO;
14360Sstevel@tonic-gate 
14370Sstevel@tonic-gate 		break;
14380Sstevel@tonic-gate 	}
14390Sstevel@tonic-gate 
14400Sstevel@tonic-gate 	if (rval == USBSER_CONTINUE) {
14410Sstevel@tonic-gate 
14420Sstevel@tonic-gate 		goto loop;
14430Sstevel@tonic-gate 	}
14440Sstevel@tonic-gate 
14450Sstevel@tonic-gate 	/*
14460Sstevel@tonic-gate 	 * initial open requires additional handling
14470Sstevel@tonic-gate 	 */
14480Sstevel@tonic-gate 	if (USBSER_IS_OPENING(pp)) {
14490Sstevel@tonic-gate 		if (rval == USBSER_COMPLETE) {
14500Sstevel@tonic-gate 			if (pp->port_state == USBSER_PORT_OPENING_OUT) {
14510Sstevel@tonic-gate 				pp->port_flags |= USBSER_FL_OUT;
14520Sstevel@tonic-gate 			}
14530Sstevel@tonic-gate 			pp->port_state = USBSER_PORT_OPEN;
14540Sstevel@tonic-gate 			cv_broadcast(&pp->port_state_cv);
14550Sstevel@tonic-gate 
14560Sstevel@tonic-gate 			usbser_open_queues_init(pp, rq);
14570Sstevel@tonic-gate 		} else {
14580Sstevel@tonic-gate 			usbser_open_fini(pp);
14590Sstevel@tonic-gate 		}
14600Sstevel@tonic-gate 	}
14610Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
14620Sstevel@tonic-gate 
14630Sstevel@tonic-gate 	return (rval);
14640Sstevel@tonic-gate }
14650Sstevel@tonic-gate 
14660Sstevel@tonic-gate 
14670Sstevel@tonic-gate /*
14680Sstevel@tonic-gate  * initialize the port when opened for the first time
14690Sstevel@tonic-gate  */
14700Sstevel@tonic-gate static int
usbser_open_init(usbser_port_t * pp,int minor)14710Sstevel@tonic-gate usbser_open_init(usbser_port_t *pp, int minor)
14720Sstevel@tonic-gate {
14730Sstevel@tonic-gate 	usbser_state_t	*usp = pp->port_usp;
14740Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
14750Sstevel@tonic-gate 	int		rval = ENXIO;
14760Sstevel@tonic-gate 
14770Sstevel@tonic-gate 	ASSERT(pp->port_state == USBSER_PORT_CLOSED);
14780Sstevel@tonic-gate 
14790Sstevel@tonic-gate 	/*
14800Sstevel@tonic-gate 	 * init state
14810Sstevel@tonic-gate 	 */
14820Sstevel@tonic-gate 	pp->port_act = 0;
14830Sstevel@tonic-gate 	pp->port_flags &= USBSER_FL_PRESERVE;
14840Sstevel@tonic-gate 	pp->port_flowc = '\0';
14850Sstevel@tonic-gate 	pp->port_wq_data_cnt = 0;
14860Sstevel@tonic-gate 
14870Sstevel@tonic-gate 	if (minor & OUTLINE) {
14880Sstevel@tonic-gate 		pp->port_state = USBSER_PORT_OPENING_OUT;
14890Sstevel@tonic-gate 	} else {
14900Sstevel@tonic-gate 		pp->port_state = USBSER_PORT_OPENING_TTY;
14910Sstevel@tonic-gate 	}
14920Sstevel@tonic-gate 
14930Sstevel@tonic-gate 	/*
14940Sstevel@tonic-gate 	 * init termios settings
14950Sstevel@tonic-gate 	 */
14960Sstevel@tonic-gate 	tp->t_iflag = 0;
14970Sstevel@tonic-gate 	tp->t_iocpending = NULL;
14980Sstevel@tonic-gate 	tp->t_size.ws_row = tp->t_size.ws_col = 0;
14990Sstevel@tonic-gate 	tp->t_size.ws_xpixel = tp->t_size.ws_ypixel = 0;
15000Sstevel@tonic-gate 	tp->t_startc = CSTART;
15010Sstevel@tonic-gate 	tp->t_stopc = CSTOP;
15020Sstevel@tonic-gate 
15030Sstevel@tonic-gate 	usbser_check_port_props(pp);
15040Sstevel@tonic-gate 
15050Sstevel@tonic-gate 	/*
15060Sstevel@tonic-gate 	 * dispatch wq and rq threads:
15070Sstevel@tonic-gate 	 * although queues are not enabled at this point,
15080Sstevel@tonic-gate 	 * we will need wq to run status processing callback
15090Sstevel@tonic-gate 	 */
15100Sstevel@tonic-gate 	usbser_thr_dispatch(&pp->port_wq_thread);
15110Sstevel@tonic-gate 	usbser_thr_dispatch(&pp->port_rq_thread);
15120Sstevel@tonic-gate 
15130Sstevel@tonic-gate 	/*
15140Sstevel@tonic-gate 	 * open DSD port
15150Sstevel@tonic-gate 	 */
15160Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
15170Sstevel@tonic-gate 	rval = USBSER_DS_OPEN_PORT(usp, pp->port_num);
15180Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
15190Sstevel@tonic-gate 
15200Sstevel@tonic-gate 	if (rval != USB_SUCCESS) {
15210Sstevel@tonic-gate 
15220Sstevel@tonic-gate 		return (ENXIO);
15230Sstevel@tonic-gate 	}
15240Sstevel@tonic-gate 	pp->port_flags |= USBSER_FL_DSD_OPEN;
15250Sstevel@tonic-gate 
15260Sstevel@tonic-gate 	/*
15270Sstevel@tonic-gate 	 * program port with default parameters
15280Sstevel@tonic-gate 	 */
15290Sstevel@tonic-gate 	if ((rval = usbser_port_program(pp)) != 0) {
15300Sstevel@tonic-gate 
15310Sstevel@tonic-gate 		return (ENXIO);
15320Sstevel@tonic-gate 	}
15330Sstevel@tonic-gate 
15340Sstevel@tonic-gate 	return (USBSER_CONTINUE);
15350Sstevel@tonic-gate }
15360Sstevel@tonic-gate 
15370Sstevel@tonic-gate 
15380Sstevel@tonic-gate /*
15390Sstevel@tonic-gate  * create a pair of minor nodes for the port
15400Sstevel@tonic-gate  */
15410Sstevel@tonic-gate static void
usbser_check_port_props(usbser_port_t * pp)15420Sstevel@tonic-gate usbser_check_port_props(usbser_port_t *pp)
15430Sstevel@tonic-gate {
15440Sstevel@tonic-gate 	dev_info_t	*dip = pp->port_usp->us_dip;
15450Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
15466898Sfb209375 	struct termios	*termiosp;
15470Sstevel@tonic-gate 	uint_t		len;
15480Sstevel@tonic-gate 	char		name[20];
15490Sstevel@tonic-gate 
15500Sstevel@tonic-gate 	/*
15510Sstevel@tonic-gate 	 * take default modes from "ttymodes" property if it exists
15520Sstevel@tonic-gate 	 */
15530Sstevel@tonic-gate 	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ddi_root_node(), 0,
15540Sstevel@tonic-gate 	    "ttymodes", (uchar_t **)&termiosp, &len) == DDI_PROP_SUCCESS) {
15550Sstevel@tonic-gate 
15560Sstevel@tonic-gate 		if (len == sizeof (struct termios)) {
15570Sstevel@tonic-gate 			tp->t_cflag = termiosp->c_cflag;
15580Sstevel@tonic-gate 
15590Sstevel@tonic-gate 			if (termiosp->c_iflag & (IXON | IXANY)) {
15600Sstevel@tonic-gate 				tp->t_iflag =
15615541Slg150142 				    termiosp->c_iflag & (IXON | IXANY);
15620Sstevel@tonic-gate 				tp->t_startc = termiosp->c_cc[VSTART];
15630Sstevel@tonic-gate 				tp->t_stopc = termiosp->c_cc[VSTOP];
15640Sstevel@tonic-gate 			}
15650Sstevel@tonic-gate 		}
15660Sstevel@tonic-gate 		ddi_prop_free(termiosp);
15670Sstevel@tonic-gate 	}
15680Sstevel@tonic-gate 
15690Sstevel@tonic-gate 	/*
15700Sstevel@tonic-gate 	 * look for "ignore-cd" or "port-N-ignore-cd" property
15710Sstevel@tonic-gate 	 */
15720Sstevel@tonic-gate 	(void) sprintf(name, "port-%d-ignore-cd", pp->port_num);
15730Sstevel@tonic-gate 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
15740Sstevel@tonic-gate 	    "ignore-cd", 0) ||
15750Sstevel@tonic-gate 	    ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, name, 0)) {
15760Sstevel@tonic-gate 		pp->port_flags |= USBSER_FL_IGNORE_CD;
15770Sstevel@tonic-gate 	} else {
15780Sstevel@tonic-gate 		pp->port_flags &= ~USBSER_FL_IGNORE_CD;
15790Sstevel@tonic-gate 	}
15800Sstevel@tonic-gate }
15810Sstevel@tonic-gate 
15820Sstevel@tonic-gate 
15830Sstevel@tonic-gate /*
15840Sstevel@tonic-gate  * undo what was done in usbser_open_init()
15850Sstevel@tonic-gate  */
15860Sstevel@tonic-gate static void
usbser_open_fini(usbser_port_t * pp)15870Sstevel@tonic-gate usbser_open_fini(usbser_port_t *pp)
15880Sstevel@tonic-gate {
15890Sstevel@tonic-gate 	uint_t		port_num = pp->port_num;
15900Sstevel@tonic-gate 	usbser_state_t	*usp = pp->port_usp;
15910Sstevel@tonic-gate 
15920Sstevel@tonic-gate 	/*
15930Sstevel@tonic-gate 	 * close DSD if it is open
15940Sstevel@tonic-gate 	 */
15950Sstevel@tonic-gate 	if (pp->port_flags & USBSER_FL_DSD_OPEN) {
15960Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
15970Sstevel@tonic-gate 		if (USBSER_DS_CLOSE_PORT(usp, port_num) != USB_SUCCESS) {
15980Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_CLOSE, pp->port_lh,
15990Sstevel@tonic-gate 			    "usbser_open_fini: CLOSE_PORT fail");
16000Sstevel@tonic-gate 		}
16010Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
16020Sstevel@tonic-gate 	}
16030Sstevel@tonic-gate 
16040Sstevel@tonic-gate 	/*
16050Sstevel@tonic-gate 	 * cancel threads
16060Sstevel@tonic-gate 	 */
16070Sstevel@tonic-gate 	usbser_thr_cancel(&pp->port_wq_thread);
16080Sstevel@tonic-gate 	usbser_thr_cancel(&pp->port_rq_thread);
16090Sstevel@tonic-gate 
16100Sstevel@tonic-gate 	/*
16110Sstevel@tonic-gate 	 * unpdate soft state
16120Sstevel@tonic-gate 	 */
16130Sstevel@tonic-gate 	pp->port_state = USBSER_PORT_CLOSED;
16140Sstevel@tonic-gate 	cv_broadcast(&pp->port_state_cv);
16150Sstevel@tonic-gate 	cv_broadcast(&pp->port_car_cv);
16160Sstevel@tonic-gate }
16170Sstevel@tonic-gate 
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate /*
16200Sstevel@tonic-gate  * setup serial line
16210Sstevel@tonic-gate  */
16220Sstevel@tonic-gate static int
usbser_open_line_setup(usbser_port_t * pp,int minor,int flag)16230Sstevel@tonic-gate usbser_open_line_setup(usbser_port_t *pp, int minor, int flag)
16240Sstevel@tonic-gate {
16250Sstevel@tonic-gate 	int	rval;
16260Sstevel@tonic-gate 
16270Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
16280Sstevel@tonic-gate 	/*
16290Sstevel@tonic-gate 	 * prevent opening a disconnected device
16300Sstevel@tonic-gate 	 */
16310Sstevel@tonic-gate 	if (!usbser_dev_is_online(pp->port_usp)) {
16320Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
16330Sstevel@tonic-gate 
16340Sstevel@tonic-gate 		return (ENXIO);
16350Sstevel@tonic-gate 	}
16360Sstevel@tonic-gate 
16370Sstevel@tonic-gate 	/* raise DTR on every open */
16380Sstevel@tonic-gate 	(void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR, TIOCM_DTR);
16390Sstevel@tonic-gate 
16400Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
16410Sstevel@tonic-gate 	/*
16420Sstevel@tonic-gate 	 * check carrier
16430Sstevel@tonic-gate 	 */
16440Sstevel@tonic-gate 	rval = usbser_open_carrier_check(pp, minor, flag);
16450Sstevel@tonic-gate 
16460Sstevel@tonic-gate 	return (rval);
16470Sstevel@tonic-gate }
16480Sstevel@tonic-gate 
16490Sstevel@tonic-gate 
16500Sstevel@tonic-gate /*
16510Sstevel@tonic-gate  * check carrier and wait if needed
16520Sstevel@tonic-gate  */
16530Sstevel@tonic-gate static int
usbser_open_carrier_check(usbser_port_t * pp,int minor,int flag)16540Sstevel@tonic-gate usbser_open_carrier_check(usbser_port_t *pp, int minor, int flag)
16550Sstevel@tonic-gate {
16560Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
16570Sstevel@tonic-gate 	int		val = 0;
16580Sstevel@tonic-gate 	int		rval;
16590Sstevel@tonic-gate 
16600Sstevel@tonic-gate 	if (pp->port_flags & USBSER_FL_IGNORE_CD) {
16610Sstevel@tonic-gate 		tp->t_flags |= TS_SOFTCAR;
16620Sstevel@tonic-gate 	}
16630Sstevel@tonic-gate 
16640Sstevel@tonic-gate 	/*
16650Sstevel@tonic-gate 	 * check carrier
16660Sstevel@tonic-gate 	 */
16670Sstevel@tonic-gate 	if (tp->t_flags & TS_SOFTCAR) {
16680Sstevel@tonic-gate 		pp->port_flags |= USBSER_FL_CARR_ON;
16690Sstevel@tonic-gate 	} else if (USBSER_DS_GET_MODEM_CTL(pp, TIOCM_CD, &val) != USB_SUCCESS) {
16700Sstevel@tonic-gate 
16710Sstevel@tonic-gate 		return (ENXIO);
16720Sstevel@tonic-gate 	} else if (val & TIOCM_CD) {
16730Sstevel@tonic-gate 		pp->port_flags |= USBSER_FL_CARR_ON;
16740Sstevel@tonic-gate 	} else {
16750Sstevel@tonic-gate 		pp->port_flags &= ~USBSER_FL_CARR_ON;
16760Sstevel@tonic-gate 	}
16770Sstevel@tonic-gate 
16780Sstevel@tonic-gate 	/*
16790Sstevel@tonic-gate 	 * don't block if 1) not allowed to, 2) this is a local device,
16800Sstevel@tonic-gate 	 * 3) opening in dial-out mode, or 4) carrier is already on
16810Sstevel@tonic-gate 	 */
16820Sstevel@tonic-gate 	if ((flag & (FNDELAY | FNONBLOCK)) || (tp->t_cflag & CLOCAL) ||
16830Sstevel@tonic-gate 	    (minor & OUTLINE) || (pp->port_flags & USBSER_FL_CARR_ON)) {
16840Sstevel@tonic-gate 
16850Sstevel@tonic-gate 		return (USBSER_COMPLETE);
16860Sstevel@tonic-gate 	}
16870Sstevel@tonic-gate 
16880Sstevel@tonic-gate 	/*
16890Sstevel@tonic-gate 	 * block until carrier up (only in tty mode)
16900Sstevel@tonic-gate 	 */
16910Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_OPEN, pp->port_lh,
16920Sstevel@tonic-gate 	    "usbser_open_carrier_check: waiting for carrier...");
16930Sstevel@tonic-gate 
16940Sstevel@tonic-gate 	pp->port_flags |= USBSER_FL_WOPEN;
16950Sstevel@tonic-gate 
16960Sstevel@tonic-gate 	rval = cv_wait_sig(&pp->port_car_cv, &pp->port_mutex);
16970Sstevel@tonic-gate 
16980Sstevel@tonic-gate 	pp->port_flags &= ~USBSER_FL_WOPEN;
16990Sstevel@tonic-gate 
17000Sstevel@tonic-gate 	if (rval == 0) {
17010Sstevel@tonic-gate 		/*
17020Sstevel@tonic-gate 		 * interrupted with a signal
17030Sstevel@tonic-gate 		 */
17040Sstevel@tonic-gate 		return (EINTR);
17050Sstevel@tonic-gate 	} else {
17060Sstevel@tonic-gate 		/*
17070Sstevel@tonic-gate 		 * try again
17080Sstevel@tonic-gate 		 */
17090Sstevel@tonic-gate 		return (USBSER_CONTINUE);
17100Sstevel@tonic-gate 	}
17110Sstevel@tonic-gate }
17120Sstevel@tonic-gate 
17130Sstevel@tonic-gate 
17140Sstevel@tonic-gate /*
17150Sstevel@tonic-gate  * during open, setup queues and message processing
17160Sstevel@tonic-gate  */
17170Sstevel@tonic-gate static void
usbser_open_queues_init(usbser_port_t * pp,queue_t * rq)17180Sstevel@tonic-gate usbser_open_queues_init(usbser_port_t *pp, queue_t *rq)
17190Sstevel@tonic-gate {
17200Sstevel@tonic-gate 	pp->port_ttycommon.t_readq = rq;
17210Sstevel@tonic-gate 	pp->port_ttycommon.t_writeq = WR(rq);
17220Sstevel@tonic-gate 	rq->q_ptr = WR(rq)->q_ptr = (caddr_t)pp;
17230Sstevel@tonic-gate 
17240Sstevel@tonic-gate 	qprocson(rq);
17250Sstevel@tonic-gate }
17260Sstevel@tonic-gate 
17270Sstevel@tonic-gate 
17280Sstevel@tonic-gate /*
17290Sstevel@tonic-gate  * clean up queues and message processing
17300Sstevel@tonic-gate  */
17310Sstevel@tonic-gate static void
usbser_open_queues_fini(usbser_port_t * pp)17320Sstevel@tonic-gate usbser_open_queues_fini(usbser_port_t *pp)
17330Sstevel@tonic-gate {
17340Sstevel@tonic-gate 	queue_t	*rq = pp->port_ttycommon.t_readq;
17350Sstevel@tonic-gate 
17360Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
17370Sstevel@tonic-gate 	/*
17380Sstevel@tonic-gate 	 * clean up queues
17390Sstevel@tonic-gate 	 */
17400Sstevel@tonic-gate 	qprocsoff(rq);
17410Sstevel@tonic-gate 
17420Sstevel@tonic-gate 	/*
17430Sstevel@tonic-gate 	 * free unused messages
17440Sstevel@tonic-gate 	 */
17450Sstevel@tonic-gate 	flushq(rq, FLUSHALL);
17460Sstevel@tonic-gate 	flushq(WR(rq), FLUSHALL);
17470Sstevel@tonic-gate 
17480Sstevel@tonic-gate 	rq->q_ptr = WR(rq)->q_ptr = NULL;
17490Sstevel@tonic-gate 	ttycommon_close(&pp->port_ttycommon);
17500Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
17510Sstevel@tonic-gate }
17520Sstevel@tonic-gate 
17530Sstevel@tonic-gate 
17540Sstevel@tonic-gate /*
17550Sstevel@tonic-gate  * during close, wait until pending data is gone or the signal is sent
17560Sstevel@tonic-gate  */
17570Sstevel@tonic-gate static void
usbser_close_drain(usbser_port_t * pp)17580Sstevel@tonic-gate usbser_close_drain(usbser_port_t *pp)
17590Sstevel@tonic-gate {
17600Sstevel@tonic-gate 	int	need_drain;
17610Sstevel@tonic-gate 	clock_t	until;
17620Sstevel@tonic-gate 	int	rval;
17630Sstevel@tonic-gate 
17640Sstevel@tonic-gate 	/*
17650Sstevel@tonic-gate 	 * port_wq_data_cnt indicates amount of data on the write queue,
17660Sstevel@tonic-gate 	 * which becomes zero when all data is submitted to DSD. But usbser
17670Sstevel@tonic-gate 	 * stays busy until it gets tx callback from DSD, signalling that
17680Sstevel@tonic-gate 	 * data has been sent over USB. To be continued in the next comment...
17690Sstevel@tonic-gate 	 */
17700Sstevel@tonic-gate 	until = ddi_get_lbolt() +
17715541Slg150142 	    drv_usectohz(USBSER_WQ_DRAIN_TIMEOUT * 1000000);
17720Sstevel@tonic-gate 
17730Sstevel@tonic-gate 	while ((pp->port_wq_data_cnt > 0) && USBSER_PORT_IS_BUSY(pp)) {
17740Sstevel@tonic-gate 		if ((rval = cv_timedwait_sig(&pp->port_act_cv, &pp->port_mutex,
17750Sstevel@tonic-gate 		    until)) <= 0) {
17760Sstevel@tonic-gate 
17770Sstevel@tonic-gate 			break;
17780Sstevel@tonic-gate 		}
17790Sstevel@tonic-gate 	}
17800Sstevel@tonic-gate 
17810Sstevel@tonic-gate 	/* don't drain if timed out or received a signal */
17820Sstevel@tonic-gate 	need_drain = (pp->port_wq_data_cnt == 0) || !USBSER_PORT_IS_BUSY(pp) ||
17835541Slg150142 	    (rval != 0);
17840Sstevel@tonic-gate 
17850Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
17860Sstevel@tonic-gate 	/*
17870Sstevel@tonic-gate 	 * Once the data reaches USB serial box, it may still be stored in its
17880Sstevel@tonic-gate 	 * internal output buffer (FIFO). We call DSD drain to ensure that all
17890Sstevel@tonic-gate 	 * the data is transmitted transmitted over the serial line.
17900Sstevel@tonic-gate 	 */
17910Sstevel@tonic-gate 	if (need_drain) {
17920Sstevel@tonic-gate 		rval = USBSER_DS_FIFO_DRAIN(pp, USBSER_TX_FIFO_DRAIN_TIMEOUT);
17930Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
17940Sstevel@tonic-gate 			(void) USBSER_DS_FIFO_FLUSH(pp, DS_TX);
17950Sstevel@tonic-gate 		}
17960Sstevel@tonic-gate 	} else {
17970Sstevel@tonic-gate 		(void) USBSER_DS_FIFO_FLUSH(pp, DS_TX);
17980Sstevel@tonic-gate 	}
17990Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
18000Sstevel@tonic-gate }
18010Sstevel@tonic-gate 
18020Sstevel@tonic-gate 
18030Sstevel@tonic-gate /*
18040Sstevel@tonic-gate  * during close, cancel break/delay
18050Sstevel@tonic-gate  */
18060Sstevel@tonic-gate static void
usbser_close_cancel_break(usbser_port_t * pp)18070Sstevel@tonic-gate usbser_close_cancel_break(usbser_port_t *pp)
18080Sstevel@tonic-gate {
18090Sstevel@tonic-gate 	timeout_id_t	delay_id;
18100Sstevel@tonic-gate 
18110Sstevel@tonic-gate 	if (pp->port_act & USBSER_ACT_BREAK) {
18120Sstevel@tonic-gate 		delay_id = pp->port_delay_id;
18130Sstevel@tonic-gate 		pp->port_delay_id = 0;
18140Sstevel@tonic-gate 
18150Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
18160Sstevel@tonic-gate 		(void) untimeout(delay_id);
18170Sstevel@tonic-gate 		(void) USBSER_DS_BREAK_CTL(pp, DS_OFF);
18180Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
18190Sstevel@tonic-gate 
18200Sstevel@tonic-gate 		pp->port_act &= ~USBSER_ACT_BREAK;
18210Sstevel@tonic-gate 	}
18220Sstevel@tonic-gate }
18230Sstevel@tonic-gate 
18240Sstevel@tonic-gate 
18250Sstevel@tonic-gate /*
18260Sstevel@tonic-gate  * during close, drop RTS/DTR if necessary
18270Sstevel@tonic-gate  */
18280Sstevel@tonic-gate static void
usbser_close_hangup(usbser_port_t * pp)18290Sstevel@tonic-gate usbser_close_hangup(usbser_port_t *pp)
18300Sstevel@tonic-gate {
18310Sstevel@tonic-gate 	/*
18320Sstevel@tonic-gate 	 * drop DTR and RTS if HUPCL is set
18330Sstevel@tonic-gate 	 */
18340Sstevel@tonic-gate 	if (pp->port_ttycommon.t_cflag & HUPCL) {
18350Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
18360Sstevel@tonic-gate 		(void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_RTS | TIOCM_DTR, 0);
18370Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
18380Sstevel@tonic-gate 	}
18390Sstevel@tonic-gate }
18400Sstevel@tonic-gate 
18410Sstevel@tonic-gate 
18420Sstevel@tonic-gate /*
18430Sstevel@tonic-gate  * state cleanup during close
18440Sstevel@tonic-gate  */
18450Sstevel@tonic-gate static void
usbser_close_cleanup(usbser_port_t * pp)18460Sstevel@tonic-gate usbser_close_cleanup(usbser_port_t *pp)
18470Sstevel@tonic-gate {
18480Sstevel@tonic-gate 	usbser_open_queues_fini(pp);
18490Sstevel@tonic-gate 
18500Sstevel@tonic-gate 	usbser_open_fini(pp);
18510Sstevel@tonic-gate }
18520Sstevel@tonic-gate 
18530Sstevel@tonic-gate 
18540Sstevel@tonic-gate /*
18550Sstevel@tonic-gate  *
18560Sstevel@tonic-gate  * thread management
18570Sstevel@tonic-gate  * -----------------
18580Sstevel@tonic-gate  *
18590Sstevel@tonic-gate  *
18600Sstevel@tonic-gate  * dispatch a thread
18610Sstevel@tonic-gate  */
18620Sstevel@tonic-gate static void
usbser_thr_dispatch(usbser_thread_t * thr)18630Sstevel@tonic-gate usbser_thr_dispatch(usbser_thread_t *thr)
18640Sstevel@tonic-gate {
18650Sstevel@tonic-gate 	usbser_port_t	*pp = thr->thr_port;
18660Sstevel@tonic-gate 	usbser_state_t	*usp = pp->port_usp;
18670Sstevel@tonic-gate 	int		rval;
18680Sstevel@tonic-gate 
18690Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->port_mutex));
18700Sstevel@tonic-gate 	ASSERT((thr->thr_flags & USBSER_THR_RUNNING) == 0);
18710Sstevel@tonic-gate 
18720Sstevel@tonic-gate 	thr->thr_flags = USBSER_THR_RUNNING;
18730Sstevel@tonic-gate 
18740Sstevel@tonic-gate 	rval = ddi_taskq_dispatch(usp->us_taskq, thr->thr_func, thr->thr_arg,
18755541Slg150142 	    DDI_SLEEP);
18760Sstevel@tonic-gate 	ASSERT(rval == DDI_SUCCESS);
18770Sstevel@tonic-gate }
18780Sstevel@tonic-gate 
18790Sstevel@tonic-gate 
18800Sstevel@tonic-gate /*
18810Sstevel@tonic-gate  * cancel a thread
18820Sstevel@tonic-gate  */
18830Sstevel@tonic-gate static void
usbser_thr_cancel(usbser_thread_t * thr)18840Sstevel@tonic-gate usbser_thr_cancel(usbser_thread_t *thr)
18850Sstevel@tonic-gate {
18860Sstevel@tonic-gate 	usbser_port_t	*pp = thr->thr_port;
18870Sstevel@tonic-gate 
18880Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->port_mutex));
18890Sstevel@tonic-gate 
18900Sstevel@tonic-gate 	thr->thr_flags &= ~USBSER_THR_RUNNING;
18910Sstevel@tonic-gate 	cv_signal(&thr->thr_cv);
18920Sstevel@tonic-gate 
18930Sstevel@tonic-gate 	/* wait until the thread actually exits */
18940Sstevel@tonic-gate 	do {
18951150Syz147069 		cv_wait(&thr->thr_cv, &pp->port_mutex);
18961150Syz147069 
18970Sstevel@tonic-gate 	} while ((thr->thr_flags & USBSER_THR_EXITED) == 0);
18980Sstevel@tonic-gate }
18990Sstevel@tonic-gate 
19000Sstevel@tonic-gate 
19010Sstevel@tonic-gate /*
19020Sstevel@tonic-gate  * wake thread
19030Sstevel@tonic-gate  */
19040Sstevel@tonic-gate static void
usbser_thr_wake(usbser_thread_t * thr)19050Sstevel@tonic-gate usbser_thr_wake(usbser_thread_t *thr)
19060Sstevel@tonic-gate {
19070Sstevel@tonic-gate 	usbser_port_t	*pp = thr->thr_port;
19080Sstevel@tonic-gate 
19090Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->port_mutex));
19100Sstevel@tonic-gate 
19110Sstevel@tonic-gate 	thr->thr_flags |= USBSER_THR_WAKE;
19120Sstevel@tonic-gate 	cv_signal(&thr->thr_cv);
19130Sstevel@tonic-gate }
19140Sstevel@tonic-gate 
19150Sstevel@tonic-gate 
19160Sstevel@tonic-gate /*
19170Sstevel@tonic-gate  * thread handling write queue requests
19180Sstevel@tonic-gate  */
19190Sstevel@tonic-gate static void
usbser_wq_thread(void * arg)19200Sstevel@tonic-gate usbser_wq_thread(void *arg)
19210Sstevel@tonic-gate {
19220Sstevel@tonic-gate 	usbser_thread_t	*thr = (usbser_thread_t *)arg;
19230Sstevel@tonic-gate 	usbser_port_t	*pp = thr->thr_port;
19240Sstevel@tonic-gate 
19250Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wq_thread: enter");
19260Sstevel@tonic-gate 
19270Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
19280Sstevel@tonic-gate 	while (thr->thr_flags & USBSER_THR_RUNNING) {
19290Sstevel@tonic-gate 		/*
19300Sstevel@tonic-gate 		 * when woken, see what we should do
19310Sstevel@tonic-gate 		 */
19320Sstevel@tonic-gate 		if (thr->thr_flags & USBSER_THR_WAKE) {
19330Sstevel@tonic-gate 			thr->thr_flags &= ~USBSER_THR_WAKE;
19340Sstevel@tonic-gate 
19350Sstevel@tonic-gate 			/*
19360Sstevel@tonic-gate 			 * status callback pending?
19370Sstevel@tonic-gate 			 */
19380Sstevel@tonic-gate 			if (pp->port_flags & USBSER_FL_STATUS_CB) {
19390Sstevel@tonic-gate 				usbser_status_proc_cb(pp);
19400Sstevel@tonic-gate 			}
19410Sstevel@tonic-gate 
19420Sstevel@tonic-gate 			usbser_wmsg(pp);
19430Sstevel@tonic-gate 		} else {
19440Sstevel@tonic-gate 			/*
19450Sstevel@tonic-gate 			 * sleep until woken up to do some work, e.g:
19460Sstevel@tonic-gate 			 * - new message arrives;
19470Sstevel@tonic-gate 			 * - data transmit completes;
19480Sstevel@tonic-gate 			 * - status callback pending;
19490Sstevel@tonic-gate 			 * - wq thread is cancelled;
19500Sstevel@tonic-gate 			 */
19510Sstevel@tonic-gate 			cv_wait(&thr->thr_cv, &pp->port_mutex);
19520Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh,
19530Sstevel@tonic-gate 			    "usbser_wq_thread: wakeup");
19540Sstevel@tonic-gate 		}
19550Sstevel@tonic-gate 	}
19560Sstevel@tonic-gate 	thr->thr_flags |= USBSER_THR_EXITED;
19570Sstevel@tonic-gate 	cv_signal(&thr->thr_cv);
19580Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wq_thread: exit");
19590Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
19600Sstevel@tonic-gate }
19610Sstevel@tonic-gate 
19620Sstevel@tonic-gate 
19630Sstevel@tonic-gate /*
19640Sstevel@tonic-gate  * thread handling read queue requests
19650Sstevel@tonic-gate  */
19660Sstevel@tonic-gate static void
usbser_rq_thread(void * arg)19670Sstevel@tonic-gate usbser_rq_thread(void *arg)
19680Sstevel@tonic-gate {
19690Sstevel@tonic-gate 	usbser_thread_t	*thr = (usbser_thread_t *)arg;
19700Sstevel@tonic-gate 	usbser_port_t	*pp = thr->thr_port;
19710Sstevel@tonic-gate 
19720Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_rq_thread: enter");
19730Sstevel@tonic-gate 
19740Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
19750Sstevel@tonic-gate 	while (thr->thr_flags & USBSER_THR_RUNNING) {
19760Sstevel@tonic-gate 		/*
19770Sstevel@tonic-gate 		 * read service routine will wake us when
19780Sstevel@tonic-gate 		 * more space is available on the read queue
19790Sstevel@tonic-gate 		 */
19800Sstevel@tonic-gate 		if (thr->thr_flags & USBSER_THR_WAKE) {
19810Sstevel@tonic-gate 			thr->thr_flags &= ~USBSER_THR_WAKE;
19820Sstevel@tonic-gate 
19830Sstevel@tonic-gate 			/*
19840Sstevel@tonic-gate 			 * don't process messages until queue is enabled
19850Sstevel@tonic-gate 			 */
19860Sstevel@tonic-gate 			if (!pp->port_ttycommon.t_readq) {
19870Sstevel@tonic-gate 
19880Sstevel@tonic-gate 				continue;
19890Sstevel@tonic-gate 			}
19900Sstevel@tonic-gate 
19910Sstevel@tonic-gate 			/*
19920Sstevel@tonic-gate 			 * check whether we need to resume receive
19930Sstevel@tonic-gate 			 */
19940Sstevel@tonic-gate 			if (pp->port_flags & USBSER_FL_RX_STOPPED) {
19950Sstevel@tonic-gate 				pp->port_flowc = pp->port_ttycommon.t_startc;
19960Sstevel@tonic-gate 				usbser_inbound_flow_ctl(pp);
19970Sstevel@tonic-gate 			}
19980Sstevel@tonic-gate 
19990Sstevel@tonic-gate 			/*
20000Sstevel@tonic-gate 			 * grab more data if available
20010Sstevel@tonic-gate 			 */
20020Sstevel@tonic-gate 			mutex_exit(&pp->port_mutex);
20030Sstevel@tonic-gate 			usbser_rx_cb((caddr_t)pp);
20040Sstevel@tonic-gate 			mutex_enter(&pp->port_mutex);
20050Sstevel@tonic-gate 		} else {
20060Sstevel@tonic-gate 			cv_wait(&thr->thr_cv, &pp->port_mutex);
20070Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh,
20080Sstevel@tonic-gate 			    "usbser_rq_thread: wakeup");
20090Sstevel@tonic-gate 		}
20100Sstevel@tonic-gate 	}
20110Sstevel@tonic-gate 	thr->thr_flags |= USBSER_THR_EXITED;
20120Sstevel@tonic-gate 	cv_signal(&thr->thr_cv);
20130Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_RQ, pp->port_lh, "usbser_rq_thread: exit");
20140Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
20150Sstevel@tonic-gate }
20160Sstevel@tonic-gate 
20170Sstevel@tonic-gate 
20180Sstevel@tonic-gate /*
20190Sstevel@tonic-gate  * DSD callbacks
20200Sstevel@tonic-gate  * -------------
20210Sstevel@tonic-gate  *
20220Sstevel@tonic-gate  * Note: to avoid deadlocks with DSD, these callbacks
20230Sstevel@tonic-gate  * should not call DSD functions that can block.
20240Sstevel@tonic-gate  *
20250Sstevel@tonic-gate  *
20260Sstevel@tonic-gate  * transmit callback
20270Sstevel@tonic-gate  *
20280Sstevel@tonic-gate  * invoked by DSD when the last byte of data is transmitted over USB
20290Sstevel@tonic-gate  */
20300Sstevel@tonic-gate static void
usbser_tx_cb(caddr_t arg)20310Sstevel@tonic-gate usbser_tx_cb(caddr_t arg)
20320Sstevel@tonic-gate {
20330Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)arg;
20340Sstevel@tonic-gate 	int		online;
20350Sstevel@tonic-gate 
20360Sstevel@tonic-gate 	online = usbser_dev_is_online(pp->port_usp);
20370Sstevel@tonic-gate 
20380Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
20390Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_TX_CB, pp->port_lh,
20406898Sfb209375 	    "usbser_tx_cb: act=%x curthread=%p", pp->port_act,
20416898Sfb209375 	    (void *)curthread);
20420Sstevel@tonic-gate 
20430Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_TX);
20440Sstevel@tonic-gate 
204510061SRaymond.Chen@Sun.COM 	/*
204610061SRaymond.Chen@Sun.COM 	 * as long as port access is ok and the port is not busy on
204710061SRaymond.Chen@Sun.COM 	 * TX, break, ctrl or delay, the wq_thread should be waken
204810061SRaymond.Chen@Sun.COM 	 * to do further process for next message
204910061SRaymond.Chen@Sun.COM 	 */
205010061SRaymond.Chen@Sun.COM 	if (online && USBSER_PORT_ACCESS_OK(pp) &&
205110061SRaymond.Chen@Sun.COM 	    !USBSER_PORT_IS_BUSY_NON_RX(pp)) {
20520Sstevel@tonic-gate 		/*
20530Sstevel@tonic-gate 		 * wake wq thread for further data/ioctl processing
20540Sstevel@tonic-gate 		 */
20550Sstevel@tonic-gate 		usbser_thr_wake(&pp->port_wq_thread);
20560Sstevel@tonic-gate 	}
20570Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
20580Sstevel@tonic-gate }
20590Sstevel@tonic-gate 
20600Sstevel@tonic-gate 
20610Sstevel@tonic-gate /*
20620Sstevel@tonic-gate  * receive callback
20630Sstevel@tonic-gate  *
20640Sstevel@tonic-gate  * invoked by DSD when there is more data for us to pick
20650Sstevel@tonic-gate  */
20660Sstevel@tonic-gate static void
usbser_rx_cb(caddr_t arg)20670Sstevel@tonic-gate usbser_rx_cb(caddr_t arg)
20680Sstevel@tonic-gate {
20690Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)arg;
20700Sstevel@tonic-gate 	queue_t		*rq, *wq;
20710Sstevel@tonic-gate 	mblk_t		*mp;		/* current mblk */
20720Sstevel@tonic-gate 	mblk_t		*data, *data_tail; /* M_DATA mblk list and its tail */
20730Sstevel@tonic-gate 	mblk_t		*emp;		/* error (M_BREAK) mblk */
20740Sstevel@tonic-gate 
20750Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh, "usbser_rx_cb");
20760Sstevel@tonic-gate 
20770Sstevel@tonic-gate 	if (!usbser_dev_is_online(pp->port_usp)) {
20780Sstevel@tonic-gate 
20790Sstevel@tonic-gate 		return;
20800Sstevel@tonic-gate 	}
20810Sstevel@tonic-gate 
20820Sstevel@tonic-gate 	/* get data from DSD */
20830Sstevel@tonic-gate 	if ((mp = USBSER_DS_RX(pp)) == NULL) {
20840Sstevel@tonic-gate 
20850Sstevel@tonic-gate 		return;
20860Sstevel@tonic-gate 	}
20870Sstevel@tonic-gate 
20880Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
20890Sstevel@tonic-gate 	if ((!USBSER_PORT_ACCESS_OK(pp)) ||
20900Sstevel@tonic-gate 	    ((pp->port_ttycommon.t_cflag & CREAD) == 0)) {
20910Sstevel@tonic-gate 		freemsg(mp);
20920Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
20930Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_RX_CB, pp->port_lh,
20940Sstevel@tonic-gate 		    "usbser_rx_cb: access not ok or receiver disabled");
20950Sstevel@tonic-gate 
20960Sstevel@tonic-gate 		return;
20970Sstevel@tonic-gate 	}
20980Sstevel@tonic-gate 
20990Sstevel@tonic-gate 	usbser_serialize_port_act(pp, USBSER_ACT_RX);
21000Sstevel@tonic-gate 
21010Sstevel@tonic-gate 	rq = pp->port_ttycommon.t_readq;
21020Sstevel@tonic-gate 	wq = pp->port_ttycommon.t_writeq;
21030Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
21040Sstevel@tonic-gate 
21050Sstevel@tonic-gate 	/*
21060Sstevel@tonic-gate 	 * DSD data is a b_cont-linked list of M_DATA and M_BREAK blocks.
21070Sstevel@tonic-gate 	 * M_DATA is correctly received data.
21080Sstevel@tonic-gate 	 * M_BREAK is a character with either framing or parity error.
21090Sstevel@tonic-gate 	 *
21100Sstevel@tonic-gate 	 * this loop runs through the list of mblks. when it meets an M_BREAK,
21110Sstevel@tonic-gate 	 * it sends all leading M_DATA's in one shot, then sends M_BREAK.
21120Sstevel@tonic-gate 	 * in the trivial case when list contains only M_DATA's, the loop
21130Sstevel@tonic-gate 	 * does nothing but set data variable.
21140Sstevel@tonic-gate 	 */
21150Sstevel@tonic-gate 	data = data_tail = NULL;
21160Sstevel@tonic-gate 	while (mp) {
21170Sstevel@tonic-gate 		/*
21180Sstevel@tonic-gate 		 * skip data until we meet M_BREAK or end of list
21190Sstevel@tonic-gate 		 */
21200Sstevel@tonic-gate 		if (DB_TYPE(mp) == M_DATA) {
21210Sstevel@tonic-gate 			if (data == NULL) {
21220Sstevel@tonic-gate 				data = mp;
21230Sstevel@tonic-gate 			}
21240Sstevel@tonic-gate 			data_tail = mp;
21250Sstevel@tonic-gate 			mp = mp->b_cont;
21260Sstevel@tonic-gate 
21270Sstevel@tonic-gate 			continue;
21280Sstevel@tonic-gate 		}
21290Sstevel@tonic-gate 
21300Sstevel@tonic-gate 		/* detach data list from mp */
21310Sstevel@tonic-gate 		if (data_tail) {
21320Sstevel@tonic-gate 			data_tail->b_cont = NULL;
21330Sstevel@tonic-gate 		}
21340Sstevel@tonic-gate 		/* detach emp from the list */
21350Sstevel@tonic-gate 		emp = mp;
21360Sstevel@tonic-gate 		mp = mp->b_cont;
21370Sstevel@tonic-gate 		emp->b_cont = NULL;
21380Sstevel@tonic-gate 
21390Sstevel@tonic-gate 		/* DSD shouldn't send anything but M_DATA or M_BREAK */
21400Sstevel@tonic-gate 		if ((DB_TYPE(emp) != M_BREAK) || (MBLKL(emp) != 2)) {
21410Sstevel@tonic-gate 			freemsg(emp);
21420Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_RX_CB, pp->port_lh,
21430Sstevel@tonic-gate 			    "usbser_rx_cb: bad message");
21440Sstevel@tonic-gate 
21450Sstevel@tonic-gate 			continue;
21460Sstevel@tonic-gate 		}
21470Sstevel@tonic-gate 
21480Sstevel@tonic-gate 		/*
21490Sstevel@tonic-gate 		 * first tweak and send M_DATA's
21500Sstevel@tonic-gate 		 */
21510Sstevel@tonic-gate 		if (data) {
21520Sstevel@tonic-gate 			usbser_rx_massage_data(pp, data);
21530Sstevel@tonic-gate 			usbser_rx_cb_put(pp, rq, wq, data);
21540Sstevel@tonic-gate 			data = data_tail = NULL;
21550Sstevel@tonic-gate 		}
21560Sstevel@tonic-gate 
21570Sstevel@tonic-gate 		/*
21580Sstevel@tonic-gate 		 * now tweak and send M_BREAK
21590Sstevel@tonic-gate 		 */
21600Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
21610Sstevel@tonic-gate 		usbser_rx_massage_mbreak(pp, emp);
21620Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
21630Sstevel@tonic-gate 		usbser_rx_cb_put(pp, rq, wq, emp);
21640Sstevel@tonic-gate 	}
21650Sstevel@tonic-gate 
21660Sstevel@tonic-gate 	/* send the rest of the data, if any */
21670Sstevel@tonic-gate 	if (data) {
21680Sstevel@tonic-gate 		usbser_rx_massage_data(pp, data);
21690Sstevel@tonic-gate 		usbser_rx_cb_put(pp, rq, wq, data);
21700Sstevel@tonic-gate 	}
21710Sstevel@tonic-gate 
21720Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
21730Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_RX);
21740Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
21750Sstevel@tonic-gate }
21760Sstevel@tonic-gate 
21770Sstevel@tonic-gate /*
21780Sstevel@tonic-gate  * the joys of termio -- this is to accomodate Unix98 assertion:
21790Sstevel@tonic-gate  *
21800Sstevel@tonic-gate  *   If PARENB is supported and is set, when PARMRK is set, and CSIZE is
21810Sstevel@tonic-gate  *   set to CS8, and IGNPAR is clear, and ISTRIP is clear, a valid
21820Sstevel@tonic-gate  *   character of '\377' is read as '\377', '\377'.
21830Sstevel@tonic-gate  *
21840Sstevel@tonic-gate  *   Posix Ref: Assertion 7.1.2.2-16(C)
21850Sstevel@tonic-gate  *
21860Sstevel@tonic-gate  * this requires the driver to scan every incoming valid character
21870Sstevel@tonic-gate  */
21880Sstevel@tonic-gate static void
usbser_rx_massage_data(usbser_port_t * pp,mblk_t * mp)21890Sstevel@tonic-gate usbser_rx_massage_data(usbser_port_t *pp, mblk_t *mp)
21900Sstevel@tonic-gate {
21910Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
21920Sstevel@tonic-gate 	uchar_t		*p;
21930Sstevel@tonic-gate 	mblk_t		*newmp;
21940Sstevel@tonic-gate 	int		tailsz;
21950Sstevel@tonic-gate 
21960Sstevel@tonic-gate 	/* avoid scanning if possible */
21970Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
21980Sstevel@tonic-gate 	if (!((tp->t_cflag & PARENB) && (tp->t_iflag & PARMRK) &&
21990Sstevel@tonic-gate 	    ((tp->t_cflag & CSIZE) == CS8) &&
22000Sstevel@tonic-gate 	    ((tp->t_iflag & (IGNPAR|ISTRIP)) == 0))) {
22010Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
22020Sstevel@tonic-gate 
22030Sstevel@tonic-gate 		return;
22040Sstevel@tonic-gate 	}
22050Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
22060Sstevel@tonic-gate 
22070Sstevel@tonic-gate 	while (mp) {
22080Sstevel@tonic-gate 		for (p = mp->b_rptr; p < mp->b_wptr; ) {
22090Sstevel@tonic-gate 			if (*p++ != 0377) {
22100Sstevel@tonic-gate 
22110Sstevel@tonic-gate 				continue;
22120Sstevel@tonic-gate 			}
22130Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh,
22146898Sfb209375 			    "usbser_rx_massage_data: mp=%p off=%ld(%ld)",
22157492SZhigang.Lu@Sun.COM 			    (void *)mp, _PTRDIFF(p,  mp->b_rptr) - 1,
22166898Sfb209375 			    (long)MBLKL(mp));
22170Sstevel@tonic-gate 
22180Sstevel@tonic-gate 			/*
22190Sstevel@tonic-gate 			 * insert another 0377 after this one. all data after
22200Sstevel@tonic-gate 			 * the original 0377 have to be copied to the new mblk
22210Sstevel@tonic-gate 			 */
22227492SZhigang.Lu@Sun.COM 			tailsz = _PTRDIFF(mp->b_wptr, p);
22230Sstevel@tonic-gate 			if ((newmp = allocb(tailsz + 1, BPRI_HI)) == NULL) {
22240Sstevel@tonic-gate 				USB_DPRINTF_L2(DPRINT_RX_CB, pp->port_lh,
22250Sstevel@tonic-gate 				    "usbser_rx_massage_data: allocb failed");
22260Sstevel@tonic-gate 
22270Sstevel@tonic-gate 				continue;
22280Sstevel@tonic-gate 			}
22290Sstevel@tonic-gate 
22300Sstevel@tonic-gate 			/* fill in the new mblk */
22310Sstevel@tonic-gate 			*newmp->b_wptr++ = 0377;
22320Sstevel@tonic-gate 			if (tailsz > 0) {
22330Sstevel@tonic-gate 				bcopy(p, newmp->b_wptr, tailsz);
22340Sstevel@tonic-gate 				newmp->b_wptr += tailsz;
22350Sstevel@tonic-gate 			}
22360Sstevel@tonic-gate 			/* shrink the original mblk */
22370Sstevel@tonic-gate 			mp->b_wptr = p;
22380Sstevel@tonic-gate 
22390Sstevel@tonic-gate 			newmp->b_cont = mp->b_cont;
22400Sstevel@tonic-gate 			mp->b_cont = newmp;
22410Sstevel@tonic-gate 			p = newmp->b_rptr + 1;
22420Sstevel@tonic-gate 			mp = newmp;
22430Sstevel@tonic-gate 		}
22440Sstevel@tonic-gate 		mp = mp->b_cont;
22450Sstevel@tonic-gate 	}
22460Sstevel@tonic-gate }
22470Sstevel@tonic-gate 
22480Sstevel@tonic-gate /*
22490Sstevel@tonic-gate  * more joys of termio
22500Sstevel@tonic-gate  */
22510Sstevel@tonic-gate static void
usbser_rx_massage_mbreak(usbser_port_t * pp,mblk_t * mp)22520Sstevel@tonic-gate usbser_rx_massage_mbreak(usbser_port_t *pp, mblk_t *mp)
22530Sstevel@tonic-gate {
22540Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
22550Sstevel@tonic-gate 	uchar_t		err, c;
22560Sstevel@tonic-gate 
22570Sstevel@tonic-gate 	err = *mp->b_rptr;
22580Sstevel@tonic-gate 	c = *(mp->b_rptr + 1);
22590Sstevel@tonic-gate 
22600Sstevel@tonic-gate 	if ((err & (DS_FRAMING_ERR | DS_BREAK_ERR)) && (c == 0)) {
22610Sstevel@tonic-gate 		/* break */
22620Sstevel@tonic-gate 		mp->b_rptr += 2;
22630Sstevel@tonic-gate 	} else if (!(tp->t_iflag & INPCK) && (err & (DS_PARITY_ERR))) {
22640Sstevel@tonic-gate 		/* Posix Ref: Assertion 7.1.2.2-20(C) */
22650Sstevel@tonic-gate 		mp->b_rptr++;
22660Sstevel@tonic-gate 		DB_TYPE(mp) = M_DATA;
22670Sstevel@tonic-gate 	} else {
22680Sstevel@tonic-gate 		/* for ldterm to handle */
22690Sstevel@tonic-gate 		mp->b_rptr++;
22700Sstevel@tonic-gate 	}
22710Sstevel@tonic-gate 
22720Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_RX_CB, pp->port_lh,
22736898Sfb209375 	    "usbser_rx_massage_mbreak: type=%x len=%ld [0]=0%o",
22746898Sfb209375 	    DB_TYPE(mp), (long)MBLKL(mp), (MBLKL(mp) > 0) ? *mp->b_rptr : 45);
22750Sstevel@tonic-gate }
22760Sstevel@tonic-gate 
22770Sstevel@tonic-gate 
22780Sstevel@tonic-gate /*
22790Sstevel@tonic-gate  * in rx callback, try to send an mblk upstream
22800Sstevel@tonic-gate  */
22810Sstevel@tonic-gate static void
usbser_rx_cb_put(usbser_port_t * pp,queue_t * rq,queue_t * wq,mblk_t * mp)22820Sstevel@tonic-gate usbser_rx_cb_put(usbser_port_t *pp, queue_t *rq, queue_t *wq, mblk_t *mp)
22830Sstevel@tonic-gate {
22840Sstevel@tonic-gate 	if (canputnext(rq)) {
22850Sstevel@tonic-gate 		putnext(rq, mp);
22860Sstevel@tonic-gate 	} else if (canput(rq) && putq(rq, mp)) {
22870Sstevel@tonic-gate 		/*
22880Sstevel@tonic-gate 		 * full queue indicates the need for inbound flow control
22890Sstevel@tonic-gate 		 */
22900Sstevel@tonic-gate 		(void) putctl(wq, M_STOPI);
22910Sstevel@tonic-gate 		usbser_st_put_stopi++;
22920Sstevel@tonic-gate 
22930Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_RX_CB, pp->port_lh,
22940Sstevel@tonic-gate 		    "usbser_rx_cb: cannot putnext, flow ctl");
22950Sstevel@tonic-gate 	} else {
22960Sstevel@tonic-gate 		freemsg(mp);
22970Sstevel@tonic-gate 		usbser_st_rx_data_loss++;
22980Sstevel@tonic-gate 		(void) putctl(wq, M_STOPI);
22990Sstevel@tonic-gate 		usbser_st_put_stopi++;
23000Sstevel@tonic-gate 
23010Sstevel@tonic-gate 		USB_DPRINTF_L1(DPRINT_RX_CB, pp->port_lh,
2302978Sfrits 		    "input overrun");
23030Sstevel@tonic-gate 	}
23040Sstevel@tonic-gate }
23050Sstevel@tonic-gate 
23060Sstevel@tonic-gate 
23070Sstevel@tonic-gate /*
23080Sstevel@tonic-gate  * modem status change callback
23090Sstevel@tonic-gate  *
23100Sstevel@tonic-gate  * each time external status lines are changed, DSD calls this routine
23110Sstevel@tonic-gate  */
23120Sstevel@tonic-gate static void
usbser_status_cb(caddr_t arg)23130Sstevel@tonic-gate usbser_status_cb(caddr_t arg)
23140Sstevel@tonic-gate {
23150Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)arg;
23160Sstevel@tonic-gate 
23170Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh, "usbser_status_cb");
23180Sstevel@tonic-gate 
23190Sstevel@tonic-gate 	if (!usbser_dev_is_online(pp->port_usp)) {
23200Sstevel@tonic-gate 
23210Sstevel@tonic-gate 		return;
23220Sstevel@tonic-gate 	}
23230Sstevel@tonic-gate 
23240Sstevel@tonic-gate 	/*
23250Sstevel@tonic-gate 	 * actual processing will be done in usbser_status_proc_cb()
23260Sstevel@tonic-gate 	 * running in wq thread
23270Sstevel@tonic-gate 	 */
23280Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
23290Sstevel@tonic-gate 	if (USBSER_PORT_ACCESS_OK(pp) || USBSER_IS_OPENING(pp)) {
23300Sstevel@tonic-gate 		pp->port_flags |= USBSER_FL_STATUS_CB;
23310Sstevel@tonic-gate 		usbser_thr_wake(&pp->port_wq_thread);
23320Sstevel@tonic-gate 	}
23330Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
23340Sstevel@tonic-gate }
23350Sstevel@tonic-gate 
23360Sstevel@tonic-gate 
23370Sstevel@tonic-gate /*
23380Sstevel@tonic-gate  * modem status change
23390Sstevel@tonic-gate  */
23400Sstevel@tonic-gate static void
usbser_status_proc_cb(usbser_port_t * pp)23410Sstevel@tonic-gate usbser_status_proc_cb(usbser_port_t *pp)
23420Sstevel@tonic-gate {
23430Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
23440Sstevel@tonic-gate 	queue_t		*rq, *wq;
23450Sstevel@tonic-gate 	int		status;
23460Sstevel@tonic-gate 	int		drop_dtr = 0;
23470Sstevel@tonic-gate 	int		rq_msg = 0, wq_msg = 0;
23480Sstevel@tonic-gate 
23490Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh, "usbser_status_proc_cb");
23500Sstevel@tonic-gate 
23510Sstevel@tonic-gate 	pp->port_flags &= ~USBSER_FL_STATUS_CB;
23520Sstevel@tonic-gate 
23530Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
23540Sstevel@tonic-gate 	if (!usbser_dev_is_online(pp->port_usp)) {
23550Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
23560Sstevel@tonic-gate 
23570Sstevel@tonic-gate 		return;
23580Sstevel@tonic-gate 	}
23590Sstevel@tonic-gate 
23600Sstevel@tonic-gate 	/* get modem status */
23610Sstevel@tonic-gate 	if (USBSER_DS_GET_MODEM_CTL(pp, -1, &status) != USB_SUCCESS) {
23620Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
23630Sstevel@tonic-gate 
23640Sstevel@tonic-gate 		return;
23650Sstevel@tonic-gate 	}
23660Sstevel@tonic-gate 
23670Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
23680Sstevel@tonic-gate 	usbser_serialize_port_act(pp, USBSER_ACT_CTL);
23690Sstevel@tonic-gate 
23700Sstevel@tonic-gate 	rq = pp->port_ttycommon.t_readq;
23710Sstevel@tonic-gate 	wq = pp->port_ttycommon.t_writeq;
23720Sstevel@tonic-gate 
23730Sstevel@tonic-gate 	/*
23740Sstevel@tonic-gate 	 * outbound flow control
23750Sstevel@tonic-gate 	 */
23760Sstevel@tonic-gate 	if (tp->t_cflag & CRTSCTS) {
23770Sstevel@tonic-gate 		if (!(status & TIOCM_CTS)) {
23780Sstevel@tonic-gate 			/*
23790Sstevel@tonic-gate 			 * CTS dropped, stop xmit
23800Sstevel@tonic-gate 			 */
23810Sstevel@tonic-gate 			if (!(pp->port_flags & USBSER_FL_TX_STOPPED)) {
23820Sstevel@tonic-gate 				wq_msg = M_STOP;
23830Sstevel@tonic-gate 			}
23840Sstevel@tonic-gate 		} else if (pp->port_flags & USBSER_FL_TX_STOPPED) {
23850Sstevel@tonic-gate 			/*
23860Sstevel@tonic-gate 			 * CTS raised, resume xmit
23870Sstevel@tonic-gate 			 */
23880Sstevel@tonic-gate 			wq_msg = M_START;
23890Sstevel@tonic-gate 		}
23900Sstevel@tonic-gate 	}
23910Sstevel@tonic-gate 
23920Sstevel@tonic-gate 	/*
23930Sstevel@tonic-gate 	 * check carrier
23940Sstevel@tonic-gate 	 */
23950Sstevel@tonic-gate 	if ((status & TIOCM_CD) || (tp->t_flags & TS_SOFTCAR)) {
23960Sstevel@tonic-gate 		/*
23970Sstevel@tonic-gate 		 * carrier present
23980Sstevel@tonic-gate 		 */
23990Sstevel@tonic-gate 		if ((pp->port_flags & USBSER_FL_CARR_ON) == 0) {
24000Sstevel@tonic-gate 			pp->port_flags |= USBSER_FL_CARR_ON;
24010Sstevel@tonic-gate 
24020Sstevel@tonic-gate 			rq_msg = M_UNHANGUP;
24030Sstevel@tonic-gate 			/*
24040Sstevel@tonic-gate 			 * wake open
24050Sstevel@tonic-gate 			 */
24060Sstevel@tonic-gate 			if (pp->port_flags & USBSER_FL_WOPEN) {
24070Sstevel@tonic-gate 				cv_broadcast(&pp->port_car_cv);
24080Sstevel@tonic-gate 			}
24090Sstevel@tonic-gate 
24100Sstevel@tonic-gate 			USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh,
24110Sstevel@tonic-gate 			    "usbser_status_cb: carr on");
24120Sstevel@tonic-gate 		}
24130Sstevel@tonic-gate 	} else if (pp->port_flags & USBSER_FL_CARR_ON) {
24140Sstevel@tonic-gate 		pp->port_flags &= ~USBSER_FL_CARR_ON;
24150Sstevel@tonic-gate 		/*
24160Sstevel@tonic-gate 		 * carrier went away: if not local line, drop DTR
24170Sstevel@tonic-gate 		 */
24180Sstevel@tonic-gate 		if (!(tp->t_cflag & CLOCAL)) {
24190Sstevel@tonic-gate 			drop_dtr = 1;
24200Sstevel@tonic-gate 			rq_msg = M_HANGUP;
24210Sstevel@tonic-gate 		}
24220Sstevel@tonic-gate 		if ((pp->port_flags & USBSER_FL_TX_STOPPED) && (wq_msg == 0)) {
24230Sstevel@tonic-gate 			wq_msg = M_START;
24240Sstevel@tonic-gate 		}
24250Sstevel@tonic-gate 
24260Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh,
24270Sstevel@tonic-gate 		    "usbser_status_cb: carr off");
24280Sstevel@tonic-gate 	}
24290Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
24300Sstevel@tonic-gate 
24310Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_STATUS_CB, pp->port_lh,
24320Sstevel@tonic-gate 	    "usbser_status_cb: rq_msg=%d wq_msg=%d", rq_msg, wq_msg);
24330Sstevel@tonic-gate 
24340Sstevel@tonic-gate 	/*
24350Sstevel@tonic-gate 	 * commit postponed actions now
24360Sstevel@tonic-gate 	 * do so only if port is fully open (queues are enabled)
24370Sstevel@tonic-gate 	 */
24380Sstevel@tonic-gate 	if (rq) {
24390Sstevel@tonic-gate 		if (rq_msg) {
24400Sstevel@tonic-gate 			(void) putnextctl(rq, rq_msg);
24410Sstevel@tonic-gate 		}
24420Sstevel@tonic-gate 		if (drop_dtr) {
24430Sstevel@tonic-gate 			(void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR, 0);
24440Sstevel@tonic-gate 		}
24450Sstevel@tonic-gate 		if (wq_msg) {
24460Sstevel@tonic-gate 			(void) putctl(wq, wq_msg);
24470Sstevel@tonic-gate 		}
24480Sstevel@tonic-gate 	}
24490Sstevel@tonic-gate 
24500Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
24510Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_CTL);
24520Sstevel@tonic-gate }
24530Sstevel@tonic-gate 
24540Sstevel@tonic-gate 
24550Sstevel@tonic-gate /*
24560Sstevel@tonic-gate  * serial support
24570Sstevel@tonic-gate  * --------------
24580Sstevel@tonic-gate  *
24590Sstevel@tonic-gate  *
24600Sstevel@tonic-gate  * this routine is run by wq thread every time it's woken,
24610Sstevel@tonic-gate  * i.e. when the queue contains messages to process
24620Sstevel@tonic-gate  */
24630Sstevel@tonic-gate static void
usbser_wmsg(usbser_port_t * pp)24640Sstevel@tonic-gate usbser_wmsg(usbser_port_t *pp)
24650Sstevel@tonic-gate {
24660Sstevel@tonic-gate 	queue_t		*q = pp->port_ttycommon.t_writeq;
24670Sstevel@tonic-gate 	mblk_t		*mp;
24680Sstevel@tonic-gate 	int		msgtype;
24690Sstevel@tonic-gate 
24700Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->port_mutex));
24710Sstevel@tonic-gate 
24720Sstevel@tonic-gate 	if (q == NULL) {
24730Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_WQ, pp->port_lh, "usbser_wmsg: q=NULL");
24740Sstevel@tonic-gate 
24750Sstevel@tonic-gate 		return;
24760Sstevel@tonic-gate 	}
24770Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wmsg: q=%p act=%x 0x%x",
24786898Sfb209375 	    (void *)q, pp->port_act, q->q_first ? DB_TYPE(q->q_first) : 0xff);
24790Sstevel@tonic-gate 
24800Sstevel@tonic-gate 	while ((mp = getq(q)) != NULL) {
24810Sstevel@tonic-gate 		msgtype = DB_TYPE(mp);
24820Sstevel@tonic-gate 		USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_wmsg: "
24830Sstevel@tonic-gate 		    "type=%s (0x%x)", usbser_msgtype2str(msgtype), msgtype);
24840Sstevel@tonic-gate 
24850Sstevel@tonic-gate 		switch (msgtype) {
24860Sstevel@tonic-gate 		/*
24870Sstevel@tonic-gate 		 * high-priority messages
24880Sstevel@tonic-gate 		 */
24890Sstevel@tonic-gate 		case M_STOP:
24900Sstevel@tonic-gate 			usbser_stop(pp, mp);
24910Sstevel@tonic-gate 
24920Sstevel@tonic-gate 			break;
24930Sstevel@tonic-gate 		case M_START:
24940Sstevel@tonic-gate 			usbser_start(pp, mp);
24950Sstevel@tonic-gate 
24960Sstevel@tonic-gate 			break;
24970Sstevel@tonic-gate 		case M_STOPI:
24980Sstevel@tonic-gate 			usbser_stopi(pp, mp);
24990Sstevel@tonic-gate 
25000Sstevel@tonic-gate 			break;
25010Sstevel@tonic-gate 		case M_STARTI:
25020Sstevel@tonic-gate 			usbser_starti(pp, mp);
25030Sstevel@tonic-gate 
25040Sstevel@tonic-gate 			break;
25050Sstevel@tonic-gate 		case M_IOCDATA:
25060Sstevel@tonic-gate 			usbser_iocdata(pp, mp);
25070Sstevel@tonic-gate 
25080Sstevel@tonic-gate 			break;
25090Sstevel@tonic-gate 		case M_FLUSH:
25100Sstevel@tonic-gate 			usbser_flush(pp, mp);
25110Sstevel@tonic-gate 
25120Sstevel@tonic-gate 			break;
25130Sstevel@tonic-gate 		/*
25140Sstevel@tonic-gate 		 * normal-priority messages
25150Sstevel@tonic-gate 		 */
25160Sstevel@tonic-gate 		case M_BREAK:
25170Sstevel@tonic-gate 			usbser_break(pp, mp);
25180Sstevel@tonic-gate 
25190Sstevel@tonic-gate 			break;
25200Sstevel@tonic-gate 		case M_DELAY:
25210Sstevel@tonic-gate 			usbser_delay(pp, mp);
25220Sstevel@tonic-gate 
25230Sstevel@tonic-gate 			break;
25240Sstevel@tonic-gate 		case M_DATA:
25250Sstevel@tonic-gate 			if (usbser_data(pp, mp) != USB_SUCCESS) {
25260Sstevel@tonic-gate 				(void) putbq(q, mp);
25270Sstevel@tonic-gate 
25280Sstevel@tonic-gate 				return;
25290Sstevel@tonic-gate 			}
25300Sstevel@tonic-gate 
25310Sstevel@tonic-gate 			break;
25320Sstevel@tonic-gate 		case M_IOCTL:
25330Sstevel@tonic-gate 			if (usbser_ioctl(pp, mp) != USB_SUCCESS) {
25340Sstevel@tonic-gate 				(void) putbq(q, mp);
25350Sstevel@tonic-gate 
25360Sstevel@tonic-gate 				return;
25370Sstevel@tonic-gate 			}
25380Sstevel@tonic-gate 
25390Sstevel@tonic-gate 			break;
25400Sstevel@tonic-gate 		default:
25410Sstevel@tonic-gate 			freemsg(mp);
25420Sstevel@tonic-gate 
25430Sstevel@tonic-gate 			break;
25440Sstevel@tonic-gate 		}
25450Sstevel@tonic-gate 	}
25460Sstevel@tonic-gate }
25470Sstevel@tonic-gate 
25480Sstevel@tonic-gate 
25490Sstevel@tonic-gate /*
25500Sstevel@tonic-gate  * process M_DATA message
25510Sstevel@tonic-gate  */
25520Sstevel@tonic-gate static int
usbser_data(usbser_port_t * pp,mblk_t * mp)25530Sstevel@tonic-gate usbser_data(usbser_port_t *pp, mblk_t *mp)
25540Sstevel@tonic-gate {
25550Sstevel@tonic-gate 	/* put off until current transfer ends or delay is over */
25560Sstevel@tonic-gate 	if ((pp->port_act & USBSER_ACT_TX) ||
25570Sstevel@tonic-gate 	    (pp->port_act & USBSER_ACT_DELAY)) {
25580Sstevel@tonic-gate 
25590Sstevel@tonic-gate 		return (USB_FAILURE);
25600Sstevel@tonic-gate 	}
25617020Sgd78059 	if (MBLKL(mp) <= 0) {
25620Sstevel@tonic-gate 		freemsg(mp);
25630Sstevel@tonic-gate 
25640Sstevel@tonic-gate 		return (USB_SUCCESS);
25650Sstevel@tonic-gate 	}
25660Sstevel@tonic-gate 
25670Sstevel@tonic-gate 	pp->port_act |= USBSER_ACT_TX;
25680Sstevel@tonic-gate 	pp->port_wq_data_cnt -= msgdsize(mp);
25690Sstevel@tonic-gate 
25700Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
25710Sstevel@tonic-gate 	/* DSD is required to accept data block in any case */
25720Sstevel@tonic-gate 	(void) USBSER_DS_TX(pp, mp);
25730Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
25740Sstevel@tonic-gate 
25750Sstevel@tonic-gate 	return (USB_SUCCESS);
25760Sstevel@tonic-gate }
25770Sstevel@tonic-gate 
25780Sstevel@tonic-gate 
25790Sstevel@tonic-gate /*
25800Sstevel@tonic-gate  * process an M_IOCTL message
25810Sstevel@tonic-gate  */
25820Sstevel@tonic-gate static int
usbser_ioctl(usbser_port_t * pp,mblk_t * mp)25830Sstevel@tonic-gate usbser_ioctl(usbser_port_t *pp, mblk_t *mp)
25840Sstevel@tonic-gate {
25850Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
25860Sstevel@tonic-gate 	queue_t		*q = tp->t_writeq;
25870Sstevel@tonic-gate 	struct iocblk	*iocp;
25880Sstevel@tonic-gate 	int		cmd;
25890Sstevel@tonic-gate 	mblk_t		*datamp;
25900Sstevel@tonic-gate 	int		error = 0, rval;
25910Sstevel@tonic-gate 	int		val;
25920Sstevel@tonic-gate 
25930Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->port_mutex));
25940Sstevel@tonic-gate 	ASSERT(DB_TYPE(mp) == M_IOCTL);
25950Sstevel@tonic-gate 
25960Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
25970Sstevel@tonic-gate 	cmd = iocp->ioc_cmd;
25980Sstevel@tonic-gate 
25990Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_IOCTL, pp->port_lh, "usbser_ioctl: "
26006898Sfb209375 	    "mp=%p %s (0x%x)", (void *)mp, usbser_ioctl2str(cmd), cmd);
26010Sstevel@tonic-gate 
26020Sstevel@tonic-gate 	if (tp->t_iocpending != NULL) {
26030Sstevel@tonic-gate 		/*
26040Sstevel@tonic-gate 		 * We were holding an ioctl response pending the
26050Sstevel@tonic-gate 		 * availability of an mblk to hold data to be passed up;
26060Sstevel@tonic-gate 		 * another ioctl came through, which means that ioctl
26070Sstevel@tonic-gate 		 * must have timed out or been aborted.
26080Sstevel@tonic-gate 		 */
26090Sstevel@tonic-gate 		freemsg(tp->t_iocpending);
26100Sstevel@tonic-gate 		tp->t_iocpending = NULL;
26110Sstevel@tonic-gate 	}
26120Sstevel@tonic-gate 
26130Sstevel@tonic-gate 	switch (cmd) {
26140Sstevel@tonic-gate 	case TIOCMGET:
26150Sstevel@tonic-gate 	case TIOCMBIC:
26160Sstevel@tonic-gate 	case TIOCMBIS:
26170Sstevel@tonic-gate 	case TIOCMSET:
26182191Sszhou 	case CONSOPENPOLLEDIO:
26192191Sszhou 	case CONSCLOSEPOLLEDIO:
26202191Sszhou 	case CONSSETABORTENABLE:
26212191Sszhou 	case CONSGETABORTENABLE:
26220Sstevel@tonic-gate 		/*
26230Sstevel@tonic-gate 		 * For the above ioctls do not call ttycommon_ioctl() because
26240Sstevel@tonic-gate 		 * this function frees up the message block (mp->b_cont) that
26250Sstevel@tonic-gate 		 * contains the address of the user variable where we need to
26260Sstevel@tonic-gate 		 * pass back the bit array.
26270Sstevel@tonic-gate 		 */
26280Sstevel@tonic-gate 		error = -1;
26290Sstevel@tonic-gate 		usbser_serialize_port_act(pp, USBSER_ACT_CTL);
26300Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
26310Sstevel@tonic-gate 		break;
26329354STim.Marsland@Sun.COM 
26330Sstevel@tonic-gate 	case TCSBRK:
26340Sstevel@tonic-gate 		/* serialize breaks */
26359354STim.Marsland@Sun.COM 		if (pp->port_act & USBSER_ACT_BREAK)
26360Sstevel@tonic-gate 			return (USB_FAILURE);
26379354STim.Marsland@Sun.COM 		/*FALLTHRU*/
26380Sstevel@tonic-gate 	default:
26390Sstevel@tonic-gate 		usbser_serialize_port_act(pp, USBSER_ACT_CTL);
26400Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
26410Sstevel@tonic-gate 		(void) ttycommon_ioctl(tp, q, mp, &error);
26429354STim.Marsland@Sun.COM 		break;
26430Sstevel@tonic-gate 	}
26440Sstevel@tonic-gate 
26450Sstevel@tonic-gate 	if (error == 0) {
26460Sstevel@tonic-gate 		/*
26470Sstevel@tonic-gate 		 * ttycommon_ioctl() did most of the work
26480Sstevel@tonic-gate 		 * we just use the data it set up
26490Sstevel@tonic-gate 		 */
26500Sstevel@tonic-gate 		switch (cmd) {
26510Sstevel@tonic-gate 		case TCSETSF:
26520Sstevel@tonic-gate 		case TCSETSW:
26530Sstevel@tonic-gate 		case TCSETA:
26540Sstevel@tonic-gate 		case TCSETAW:
26550Sstevel@tonic-gate 		case TCSETAF:
26560Sstevel@tonic-gate 			(void) USBSER_DS_FIFO_DRAIN(pp, DS_TX);
26579354STim.Marsland@Sun.COM 			/*FALLTHRU*/
26589354STim.Marsland@Sun.COM 
26590Sstevel@tonic-gate 		case TCSETS:
26600Sstevel@tonic-gate 			mutex_enter(&pp->port_mutex);
26610Sstevel@tonic-gate 			error = usbser_port_program(pp);
26620Sstevel@tonic-gate 			mutex_exit(&pp->port_mutex);
26630Sstevel@tonic-gate 			break;
26640Sstevel@tonic-gate 		}
26650Sstevel@tonic-gate 		goto end;
26669354STim.Marsland@Sun.COM 
26670Sstevel@tonic-gate 	} else if (error > 0) {
26680Sstevel@tonic-gate 		USB_DPRINTF_L3(DPRINT_IOCTL, pp->port_lh, "usbser_ioctl: "
26690Sstevel@tonic-gate 		    "ttycommon_ioctl returned %d", error);
26700Sstevel@tonic-gate 		goto end;
26710Sstevel@tonic-gate 	}
26720Sstevel@tonic-gate 
26730Sstevel@tonic-gate 	/*
26740Sstevel@tonic-gate 	 * error < 0: ttycommon_ioctl() didn't do anything, we process it here
26750Sstevel@tonic-gate 	 */
26760Sstevel@tonic-gate 	error = 0;
26770Sstevel@tonic-gate 	switch (cmd) {
26780Sstevel@tonic-gate 	case TCSBRK:
26799354STim.Marsland@Sun.COM 		if ((error = miocpullup(mp, sizeof (int))) != 0)
26800Sstevel@tonic-gate 			break;
26819354STim.Marsland@Sun.COM 
26820Sstevel@tonic-gate 		/* drain output */
26830Sstevel@tonic-gate 		(void) USBSER_DS_FIFO_DRAIN(pp, USBSER_TX_FIFO_DRAIN_TIMEOUT);
26849354STim.Marsland@Sun.COM 
26850Sstevel@tonic-gate 		/*
26860Sstevel@tonic-gate 		 * if required, set break
26870Sstevel@tonic-gate 		 */
26880Sstevel@tonic-gate 		if (*(int *)mp->b_cont->b_rptr == 0) {
26890Sstevel@tonic-gate 			if (USBSER_DS_BREAK_CTL(pp, DS_ON) != USB_SUCCESS) {
26900Sstevel@tonic-gate 				error = EIO;
26910Sstevel@tonic-gate 				break;
26920Sstevel@tonic-gate 			}
26939354STim.Marsland@Sun.COM 
26940Sstevel@tonic-gate 			mutex_enter(&pp->port_mutex);
26950Sstevel@tonic-gate 			pp->port_act |= USBSER_ACT_BREAK;
26960Sstevel@tonic-gate 			pp->port_delay_id = timeout(usbser_restart, pp,
26975541Slg150142 			    drv_usectohz(250000));
26980Sstevel@tonic-gate 			mutex_exit(&pp->port_mutex);
26990Sstevel@tonic-gate 		}
27009354STim.Marsland@Sun.COM 		mioc2ack(mp, NULL, 0, 0);
27010Sstevel@tonic-gate 		break;
27029354STim.Marsland@Sun.COM 
27039354STim.Marsland@Sun.COM 	case TIOCSBRK:	/* set break */
27049354STim.Marsland@Sun.COM 		if (USBSER_DS_BREAK_CTL(pp, DS_ON) != USB_SUCCESS)
27050Sstevel@tonic-gate 			error = EIO;
27069354STim.Marsland@Sun.COM 		else
27079354STim.Marsland@Sun.COM 			mioc2ack(mp, NULL, 0, 0);
27080Sstevel@tonic-gate 		break;
27099354STim.Marsland@Sun.COM 
27109354STim.Marsland@Sun.COM 	case TIOCCBRK:	/* clear break */
27119354STim.Marsland@Sun.COM 		if (USBSER_DS_BREAK_CTL(pp, DS_OFF) != USB_SUCCESS)
27120Sstevel@tonic-gate 			error = EIO;
27139354STim.Marsland@Sun.COM 		else
27149354STim.Marsland@Sun.COM 			mioc2ack(mp, NULL, 0, 0);
27150Sstevel@tonic-gate 		break;
27169354STim.Marsland@Sun.COM 
27179354STim.Marsland@Sun.COM 	case TIOCMSET:	/* set all modem bits */
27189354STim.Marsland@Sun.COM 	case TIOCMBIS:	/* bis modem bits */
27199354STim.Marsland@Sun.COM 	case TIOCMBIC:	/* bic modem bits */
27200Sstevel@tonic-gate 		if (iocp->ioc_count == TRANSPARENT) {
27210Sstevel@tonic-gate 			mcopyin(mp, NULL, sizeof (int), NULL);
27220Sstevel@tonic-gate 			break;
27230Sstevel@tonic-gate 		}
27249354STim.Marsland@Sun.COM 		if ((error = miocpullup(mp, sizeof (int))) != 0)
27250Sstevel@tonic-gate 			break;
27260Sstevel@tonic-gate 
27270Sstevel@tonic-gate 		val = *(int *)mp->b_cont->b_rptr;
27280Sstevel@tonic-gate 		if (cmd == TIOCMSET) {
27290Sstevel@tonic-gate 			rval = USBSER_DS_SET_MODEM_CTL(pp, -1, val);
27300Sstevel@tonic-gate 		} else if (cmd == TIOCMBIS) {
27310Sstevel@tonic-gate 			rval = USBSER_DS_SET_MODEM_CTL(pp, val, -1);
27320Sstevel@tonic-gate 		} else if (cmd == TIOCMBIC) {
27330Sstevel@tonic-gate 			rval = USBSER_DS_SET_MODEM_CTL(pp, val, 0);
27340Sstevel@tonic-gate 		}
27359354STim.Marsland@Sun.COM 		if (rval == USB_SUCCESS)
27369354STim.Marsland@Sun.COM 			mioc2ack(mp, NULL, 0, 0);
27379354STim.Marsland@Sun.COM 		else
27380Sstevel@tonic-gate 			error = EIO;
27390Sstevel@tonic-gate 		break;
27409354STim.Marsland@Sun.COM 
27419354STim.Marsland@Sun.COM 	case TIOCSILOOP:
27420Sstevel@tonic-gate 		if (USBSER_DS_LOOPBACK_SUPPORTED(pp)) {
27439354STim.Marsland@Sun.COM 			if (USBSER_DS_LOOPBACK(pp, DS_ON) == USB_SUCCESS)
27449354STim.Marsland@Sun.COM 				mioc2ack(mp, NULL, 0, 0);
27459354STim.Marsland@Sun.COM 			else
27460Sstevel@tonic-gate 				error = EIO;
27470Sstevel@tonic-gate 		} else {
27480Sstevel@tonic-gate 			error = EINVAL;
27490Sstevel@tonic-gate 		}
27500Sstevel@tonic-gate 		break;
27519354STim.Marsland@Sun.COM 
27529354STim.Marsland@Sun.COM 	case TIOCCILOOP:
27539354STim.Marsland@Sun.COM 		if (USBSER_DS_LOOPBACK_SUPPORTED(pp)) {
27549354STim.Marsland@Sun.COM 			if (USBSER_DS_LOOPBACK(pp, DS_OFF) == USB_SUCCESS)
27559354STim.Marsland@Sun.COM 				mioc2ack(mp, NULL, 0, 0);
27569354STim.Marsland@Sun.COM 			else
27579354STim.Marsland@Sun.COM 				error = EIO;
27589354STim.Marsland@Sun.COM 		} else {
27599354STim.Marsland@Sun.COM 			error = EINVAL;
27609354STim.Marsland@Sun.COM 		}
27619354STim.Marsland@Sun.COM 		break;
27629354STim.Marsland@Sun.COM 
27639354STim.Marsland@Sun.COM 	case TIOCMGET:	/* get all modem bits */
27649354STim.Marsland@Sun.COM 		if ((datamp = allocb(sizeof (int), BPRI_MED)) == NULL) {
27650Sstevel@tonic-gate 			error = EAGAIN;
27660Sstevel@tonic-gate 			break;
27670Sstevel@tonic-gate 		}
27680Sstevel@tonic-gate 		rval = USBSER_DS_GET_MODEM_CTL(pp, -1, (int *)datamp->b_rptr);
27690Sstevel@tonic-gate 		if (rval != USB_SUCCESS) {
27700Sstevel@tonic-gate 			error = EIO;
27710Sstevel@tonic-gate 			break;
27720Sstevel@tonic-gate 		}
27739354STim.Marsland@Sun.COM 		if (iocp->ioc_count == TRANSPARENT)
27740Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (int), NULL, datamp);
27759354STim.Marsland@Sun.COM 		else
27769354STim.Marsland@Sun.COM 			mioc2ack(mp, datamp, sizeof (int), 0);
27770Sstevel@tonic-gate 		break;
27789354STim.Marsland@Sun.COM 
27792191Sszhou 	case CONSOPENPOLLEDIO:
27802191Sszhou 		error = usbser_polledio_init(pp);
27812191Sszhou 		if (error != 0)
27822191Sszhou 			break;
27832191Sszhou 
27842191Sszhou 		error = miocpullup(mp, sizeof (struct cons_polledio *));
27852191Sszhou 		if (error != 0)
27862191Sszhou 			break;
27872191Sszhou 
27882191Sszhou 		*(struct cons_polledio **)mp->b_cont->b_rptr = &usbser_polledio;
2789*10481SGuoqing.Zhu@Sun.COM 
2790*10481SGuoqing.Zhu@Sun.COM 		mp->b_datap->db_type = M_IOCACK;
27912191Sszhou 		break;
27929354STim.Marsland@Sun.COM 
27932191Sszhou 	case CONSCLOSEPOLLEDIO:
27942191Sszhou 		usbser_polledio_fini(pp);
2795*10481SGuoqing.Zhu@Sun.COM 		mp->b_datap->db_type = M_IOCACK;
2796*10481SGuoqing.Zhu@Sun.COM 		iocp->ioc_error = 0;
2797*10481SGuoqing.Zhu@Sun.COM 		iocp->ioc_rval = 0;
27982191Sszhou 		break;
27999354STim.Marsland@Sun.COM 
28002191Sszhou 	case CONSSETABORTENABLE:
28012191Sszhou 		error = secpolicy_console(iocp->ioc_cr);
28022191Sszhou 		if (error != 0)
28032191Sszhou 			break;
28042191Sszhou 
28052191Sszhou 		if (iocp->ioc_count != TRANSPARENT) {
28062191Sszhou 			error = EINVAL;
28072191Sszhou 			break;
28082191Sszhou 		}
28092191Sszhou 
28102191Sszhou 		/*
28112191Sszhou 		 * To do: implement console abort support
28122191Sszhou 		 * This involves adding a console flag to usbser
28132191Sszhou 		 * state structure. If flag is set, parse input stream
28142191Sszhou 		 * for abort sequence (see asy for example).
28152191Sszhou 		 *
28162191Sszhou 		 * For now, run mdb -K to get kmdb prompt.
28172191Sszhou 		 */
28182191Sszhou 		if (*(intptr_t *)mp->b_cont->b_rptr)
28192191Sszhou 			usbser_console_abort = 1;
28202191Sszhou 		else
28212191Sszhou 			usbser_console_abort = 0;
2822*10481SGuoqing.Zhu@Sun.COM 
2823*10481SGuoqing.Zhu@Sun.COM 		mp->b_datap->db_type = M_IOCACK;
2824*10481SGuoqing.Zhu@Sun.COM 		iocp->ioc_error = 0;
2825*10481SGuoqing.Zhu@Sun.COM 		iocp->ioc_rval = 0;
28262191Sszhou 		break;
28279354STim.Marsland@Sun.COM 
28282191Sszhou 	case CONSGETABORTENABLE:
28292191Sszhou 		/*CONSTANTCONDITION*/
28302191Sszhou 		ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *));
28312191Sszhou 		/*
28322191Sszhou 		 * Store the return value right in the payload
28332191Sszhou 		 * we were passed.  Crude.
28342191Sszhou 		 */
28352191Sszhou 		mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL);
28362191Sszhou 		*(boolean_t *)mp->b_cont->b_rptr = (usbser_console_abort != 0);
28372191Sszhou 		break;
28389354STim.Marsland@Sun.COM 
28390Sstevel@tonic-gate 	default:
28400Sstevel@tonic-gate 		error = EINVAL;
28410Sstevel@tonic-gate 		break;
28420Sstevel@tonic-gate 	}
28430Sstevel@tonic-gate end:
28449354STim.Marsland@Sun.COM 	if (error != 0)
28459354STim.Marsland@Sun.COM 		miocnak(q, mp, 0, error);
28469354STim.Marsland@Sun.COM 	else
28479354STim.Marsland@Sun.COM 		qreply(q, mp);
28480Sstevel@tonic-gate 
28490Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
28500Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_CTL);
28510Sstevel@tonic-gate 
28520Sstevel@tonic-gate 	return (USB_SUCCESS);
28530Sstevel@tonic-gate }
28540Sstevel@tonic-gate 
28550Sstevel@tonic-gate 
28560Sstevel@tonic-gate /*
28570Sstevel@tonic-gate  * process M_IOCDATA message
28580Sstevel@tonic-gate  */
28590Sstevel@tonic-gate static void
usbser_iocdata(usbser_port_t * pp,mblk_t * mp)28600Sstevel@tonic-gate usbser_iocdata(usbser_port_t *pp, mblk_t *mp)
28610Sstevel@tonic-gate {
28620Sstevel@tonic-gate 	tty_common_t	*tp = &pp->port_ttycommon;
28630Sstevel@tonic-gate 	queue_t		*q = tp->t_writeq;
28640Sstevel@tonic-gate 	struct copyresp	*csp;
28650Sstevel@tonic-gate 	int		cmd;
28660Sstevel@tonic-gate 	int		val;
28670Sstevel@tonic-gate 	int		rval;
28680Sstevel@tonic-gate 
28690Sstevel@tonic-gate 	ASSERT(mutex_owned(&pp->port_mutex));
28700Sstevel@tonic-gate 
28710Sstevel@tonic-gate 	csp = (struct copyresp *)mp->b_rptr;
28720Sstevel@tonic-gate 	cmd = csp->cp_cmd;
28730Sstevel@tonic-gate 
28740Sstevel@tonic-gate 	if (csp->cp_rval != 0) {
28750Sstevel@tonic-gate 		freemsg(mp);
28760Sstevel@tonic-gate 		return;
28770Sstevel@tonic-gate 	}
28780Sstevel@tonic-gate 
28790Sstevel@tonic-gate 	switch (cmd) {
28809354STim.Marsland@Sun.COM 	case TIOCMSET:	/* set all modem bits */
28819354STim.Marsland@Sun.COM 	case TIOCMBIS:	/* bis modem bits */
28829354STim.Marsland@Sun.COM 	case TIOCMBIC:	/* bic modem bits */
28830Sstevel@tonic-gate 		if ((mp->b_cont == NULL) ||
28840Sstevel@tonic-gate 		    (MBLKL(mp->b_cont) < sizeof (int))) {
28850Sstevel@tonic-gate 			miocnak(q, mp, 0, EINVAL);
28860Sstevel@tonic-gate 			break;
28870Sstevel@tonic-gate 		}
28880Sstevel@tonic-gate 		val = *(int *)mp->b_cont->b_rptr;
28890Sstevel@tonic-gate 
28900Sstevel@tonic-gate 		usbser_serialize_port_act(pp, USBSER_ACT_CTL);
28910Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
28929354STim.Marsland@Sun.COM 
28930Sstevel@tonic-gate 		if (cmd == TIOCMSET) {
28940Sstevel@tonic-gate 			rval = USBSER_DS_SET_MODEM_CTL(pp, -1, val);
28950Sstevel@tonic-gate 		} else if (cmd == TIOCMBIS) {
28960Sstevel@tonic-gate 			rval = USBSER_DS_SET_MODEM_CTL(pp, val, -1);
28970Sstevel@tonic-gate 		} else if (cmd == TIOCMBIC) {
28980Sstevel@tonic-gate 			rval = USBSER_DS_SET_MODEM_CTL(pp, val, 0);
28990Sstevel@tonic-gate 		}
29000Sstevel@tonic-gate 
29010Sstevel@tonic-gate 		if (mp->b_cont) {
29020Sstevel@tonic-gate 			freemsg(mp->b_cont);
29030Sstevel@tonic-gate 			mp->b_cont = NULL;
29040Sstevel@tonic-gate 		}
29059354STim.Marsland@Sun.COM 
29069354STim.Marsland@Sun.COM 		if (rval == USB_SUCCESS)
29070Sstevel@tonic-gate 			miocack(q, mp, 0, 0);
29089354STim.Marsland@Sun.COM 		else
29090Sstevel@tonic-gate 			miocnak(q, mp, 0, EIO);
29109354STim.Marsland@Sun.COM 
29110Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
29120Sstevel@tonic-gate 		usbser_release_port_act(pp, USBSER_ACT_CTL);
29130Sstevel@tonic-gate 		break;
29149354STim.Marsland@Sun.COM 
29159354STim.Marsland@Sun.COM 	case TIOCMGET:	/* get all modem bits */
29160Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
29170Sstevel@tonic-gate 		miocack(q, mp, 0, 0);
29180Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
29190Sstevel@tonic-gate 		break;
29209354STim.Marsland@Sun.COM 
29210Sstevel@tonic-gate 	default:
29220Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
29230Sstevel@tonic-gate 		miocnak(q, mp, 0, EINVAL);
29240Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
29250Sstevel@tonic-gate 		break;
29260Sstevel@tonic-gate 	}
29270Sstevel@tonic-gate }
29280Sstevel@tonic-gate 
29290Sstevel@tonic-gate 
29300Sstevel@tonic-gate /*
29310Sstevel@tonic-gate  * handle M_START[I]/M_STOP[I] messages
29320Sstevel@tonic-gate  */
29330Sstevel@tonic-gate static void
usbser_stop(usbser_port_t * pp,mblk_t * mp)29340Sstevel@tonic-gate usbser_stop(usbser_port_t *pp, mblk_t *mp)
29350Sstevel@tonic-gate {
29360Sstevel@tonic-gate 	usbser_st_mstop++;
29370Sstevel@tonic-gate 	if (!(pp->port_flags & USBSER_FL_TX_STOPPED)) {
29380Sstevel@tonic-gate 		usbser_serialize_port_act(pp, USBSER_ACT_CTL);
29390Sstevel@tonic-gate 		pp->port_flags |= USBSER_FL_TX_STOPPED;
29400Sstevel@tonic-gate 
29410Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
29420Sstevel@tonic-gate 		USBSER_DS_STOP(pp, DS_TX);
29430Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
29440Sstevel@tonic-gate 
29450Sstevel@tonic-gate 		usbser_release_port_act(pp, USBSER_ACT_TX);
29460Sstevel@tonic-gate 		usbser_release_port_act(pp, USBSER_ACT_CTL);
29470Sstevel@tonic-gate 	}
29480Sstevel@tonic-gate 	freemsg(mp);
29490Sstevel@tonic-gate }
29500Sstevel@tonic-gate 
29510Sstevel@tonic-gate 
29520Sstevel@tonic-gate static void
usbser_start(usbser_port_t * pp,mblk_t * mp)29530Sstevel@tonic-gate usbser_start(usbser_port_t *pp, mblk_t *mp)
29540Sstevel@tonic-gate {
29550Sstevel@tonic-gate 	usbser_st_mstart++;
29560Sstevel@tonic-gate 	if (pp->port_flags & USBSER_FL_TX_STOPPED) {
29570Sstevel@tonic-gate 		usbser_serialize_port_act(pp, USBSER_ACT_CTL);
29580Sstevel@tonic-gate 		pp->port_flags &= ~USBSER_FL_TX_STOPPED;
29590Sstevel@tonic-gate 
29600Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
29610Sstevel@tonic-gate 		USBSER_DS_START(pp, DS_TX);
29620Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
29630Sstevel@tonic-gate 		usbser_release_port_act(pp, USBSER_ACT_CTL);
29640Sstevel@tonic-gate 	}
29650Sstevel@tonic-gate 	freemsg(mp);
29660Sstevel@tonic-gate }
29670Sstevel@tonic-gate 
29680Sstevel@tonic-gate 
29690Sstevel@tonic-gate static void
usbser_stopi(usbser_port_t * pp,mblk_t * mp)29700Sstevel@tonic-gate usbser_stopi(usbser_port_t *pp, mblk_t *mp)
29710Sstevel@tonic-gate {
29720Sstevel@tonic-gate 	usbser_st_mstopi++;
29730Sstevel@tonic-gate 	usbser_serialize_port_act(pp, USBSER_ACT_CTL);
29740Sstevel@tonic-gate 	pp->port_flowc = pp->port_ttycommon.t_stopc;
29750Sstevel@tonic-gate 	usbser_inbound_flow_ctl(pp);
29760Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_CTL);
29770Sstevel@tonic-gate 	freemsg(mp);
29780Sstevel@tonic-gate }
29790Sstevel@tonic-gate 
29800Sstevel@tonic-gate static void
usbser_starti(usbser_port_t * pp,mblk_t * mp)29810Sstevel@tonic-gate usbser_starti(usbser_port_t *pp, mblk_t *mp)
29820Sstevel@tonic-gate {
29830Sstevel@tonic-gate 	usbser_st_mstarti++;
29840Sstevel@tonic-gate 	usbser_serialize_port_act(pp, USBSER_ACT_CTL);
29850Sstevel@tonic-gate 	pp->port_flowc = pp->port_ttycommon.t_startc;
29860Sstevel@tonic-gate 	usbser_inbound_flow_ctl(pp);
29870Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_CTL);
29880Sstevel@tonic-gate 	freemsg(mp);
29890Sstevel@tonic-gate }
29900Sstevel@tonic-gate 
29910Sstevel@tonic-gate /*
29920Sstevel@tonic-gate  * process M_FLUSH message
29930Sstevel@tonic-gate  */
29940Sstevel@tonic-gate static void
usbser_flush(usbser_port_t * pp,mblk_t * mp)29950Sstevel@tonic-gate usbser_flush(usbser_port_t *pp, mblk_t *mp)
29960Sstevel@tonic-gate {
29970Sstevel@tonic-gate 	queue_t	*q = pp->port_ttycommon.t_writeq;
29980Sstevel@tonic-gate 
29990Sstevel@tonic-gate 	if (*mp->b_rptr & FLUSHW) {
30000Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
30010Sstevel@tonic-gate 		(void) USBSER_DS_FIFO_FLUSH(pp, DS_TX);	/* flush FIFO buffers */
30020Sstevel@tonic-gate 		flushq(q, FLUSHDATA);			/* flush write queue */
30030Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
30040Sstevel@tonic-gate 
30050Sstevel@tonic-gate 		usbser_release_port_act(pp, USBSER_ACT_TX);
30060Sstevel@tonic-gate 
30070Sstevel@tonic-gate 		*mp->b_rptr &= ~FLUSHW;
30080Sstevel@tonic-gate 	}
30090Sstevel@tonic-gate 	if (*mp->b_rptr & FLUSHR) {
30100Sstevel@tonic-gate 		/*
30110Sstevel@tonic-gate 		 * flush FIFO buffers
30120Sstevel@tonic-gate 		 */
30130Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
30140Sstevel@tonic-gate 		(void) USBSER_DS_FIFO_FLUSH(pp, DS_RX);
30150Sstevel@tonic-gate 		flushq(RD(q), FLUSHDATA);
30160Sstevel@tonic-gate 		qreply(q, mp);
30170Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
30180Sstevel@tonic-gate 	} else {
30190Sstevel@tonic-gate 		freemsg(mp);
30200Sstevel@tonic-gate 	}
30210Sstevel@tonic-gate }
30220Sstevel@tonic-gate 
30230Sstevel@tonic-gate /*
30240Sstevel@tonic-gate  * process M_BREAK message
30250Sstevel@tonic-gate  */
30260Sstevel@tonic-gate static void
usbser_break(usbser_port_t * pp,mblk_t * mp)30270Sstevel@tonic-gate usbser_break(usbser_port_t *pp, mblk_t *mp)
30280Sstevel@tonic-gate {
30290Sstevel@tonic-gate 	int	rval;
30300Sstevel@tonic-gate 
30310Sstevel@tonic-gate 	/*
30320Sstevel@tonic-gate 	 * set the break and arrange for usbser_restart() to be called in 1/4 s
30330Sstevel@tonic-gate 	 */
30340Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
30350Sstevel@tonic-gate 	rval = USBSER_DS_BREAK_CTL(pp, DS_ON);
30360Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
30370Sstevel@tonic-gate 
30380Sstevel@tonic-gate 	if (rval == USB_SUCCESS) {
30390Sstevel@tonic-gate 		pp->port_act |= USBSER_ACT_BREAK;
30400Sstevel@tonic-gate 		pp->port_delay_id = timeout(usbser_restart, pp,
30415541Slg150142 		    drv_usectohz(250000));
30420Sstevel@tonic-gate 	}
30430Sstevel@tonic-gate 	freemsg(mp);
30440Sstevel@tonic-gate }
30450Sstevel@tonic-gate 
30460Sstevel@tonic-gate 
30470Sstevel@tonic-gate /*
30480Sstevel@tonic-gate  * process M_DELAY message
30490Sstevel@tonic-gate  */
30500Sstevel@tonic-gate static void
usbser_delay(usbser_port_t * pp,mblk_t * mp)30510Sstevel@tonic-gate usbser_delay(usbser_port_t *pp, mblk_t *mp)
30520Sstevel@tonic-gate {
30530Sstevel@tonic-gate 	/*
30540Sstevel@tonic-gate 	 * arrange for usbser_restart() to be called when the delay expires
30550Sstevel@tonic-gate 	 */
30560Sstevel@tonic-gate 	pp->port_act |= USBSER_ACT_DELAY;
30570Sstevel@tonic-gate 	pp->port_delay_id = timeout(usbser_restart, pp,
30585541Slg150142 	    (clock_t)(*(uchar_t *)mp->b_rptr + 6));
30590Sstevel@tonic-gate 	freemsg(mp);
30600Sstevel@tonic-gate }
30610Sstevel@tonic-gate 
30620Sstevel@tonic-gate 
30630Sstevel@tonic-gate /*
30640Sstevel@tonic-gate  * restart output on a line after a delay or break timer expired
30650Sstevel@tonic-gate  */
30660Sstevel@tonic-gate static void
usbser_restart(void * arg)30670Sstevel@tonic-gate usbser_restart(void *arg)
30680Sstevel@tonic-gate {
30690Sstevel@tonic-gate 	usbser_port_t	*pp = (usbser_port_t *)arg;
30700Sstevel@tonic-gate 
30710Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh, "usbser_restart");
30720Sstevel@tonic-gate 
30730Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
30740Sstevel@tonic-gate 	/* if cancelled, return immediately */
30750Sstevel@tonic-gate 	if (pp->port_delay_id == 0) {
30760Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
30770Sstevel@tonic-gate 
30780Sstevel@tonic-gate 		return;
30790Sstevel@tonic-gate 	}
30800Sstevel@tonic-gate 	pp->port_delay_id = 0;
30810Sstevel@tonic-gate 
30820Sstevel@tonic-gate 	/* clear break if necessary */
30830Sstevel@tonic-gate 	if (pp->port_act & USBSER_ACT_BREAK) {
30840Sstevel@tonic-gate 		mutex_exit(&pp->port_mutex);
30850Sstevel@tonic-gate 		(void) USBSER_DS_BREAK_CTL(pp, DS_OFF);
30860Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
30870Sstevel@tonic-gate 	}
30880Sstevel@tonic-gate 
30890Sstevel@tonic-gate 	usbser_release_port_act(pp, USBSER_ACT_BREAK | USBSER_ACT_DELAY);
30900Sstevel@tonic-gate 
30910Sstevel@tonic-gate 	/* wake wq thread to resume message processing */
30920Sstevel@tonic-gate 	usbser_thr_wake(&pp->port_wq_thread);
30930Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
30940Sstevel@tonic-gate }
30950Sstevel@tonic-gate 
30960Sstevel@tonic-gate 
30970Sstevel@tonic-gate /*
30980Sstevel@tonic-gate  * program port hardware with the chosen parameters
30990Sstevel@tonic-gate  * most of the operation is based on the values of 'c_iflag' and 'c_cflag'
31000Sstevel@tonic-gate  */
31010Sstevel@tonic-gate static int
usbser_port_program(usbser_port_t * pp)31020Sstevel@tonic-gate usbser_port_program(usbser_port_t *pp)
31030Sstevel@tonic-gate {
31040Sstevel@tonic-gate 	tty_common_t		*tp = &pp->port_ttycommon;
31050Sstevel@tonic-gate 	int			baudrate;
31060Sstevel@tonic-gate 	int			c_flag;
31070Sstevel@tonic-gate 	ds_port_param_entry_t	pe[6];
31080Sstevel@tonic-gate 	ds_port_params_t	params;
31090Sstevel@tonic-gate 	int			flow_ctl, ctl_val;
31100Sstevel@tonic-gate 	int			err = 0;
31110Sstevel@tonic-gate 
31120Sstevel@tonic-gate 	baudrate = tp->t_cflag & CBAUD;
31130Sstevel@tonic-gate 	if (tp->t_cflag & CBAUDEXT) {
31140Sstevel@tonic-gate 		baudrate += 16;
31150Sstevel@tonic-gate 	}
31160Sstevel@tonic-gate 
31170Sstevel@tonic-gate 	/*
31180Sstevel@tonic-gate 	 * set input speed same as output, as split speed not supported
31190Sstevel@tonic-gate 	 */
31200Sstevel@tonic-gate 	if (tp->t_cflag & (CIBAUD|CIBAUDEXT)) {
31210Sstevel@tonic-gate 		tp->t_cflag &= ~(CIBAUD);
31220Sstevel@tonic-gate 		if (baudrate > CBAUD) {
31230Sstevel@tonic-gate 			tp->t_cflag |= CIBAUDEXT;
31240Sstevel@tonic-gate 			tp->t_cflag |=
31255541Slg150142 			    (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD);
31260Sstevel@tonic-gate 		} else {
31270Sstevel@tonic-gate 			tp->t_cflag &= ~CIBAUDEXT;
31280Sstevel@tonic-gate 			tp->t_cflag |= ((baudrate << IBSHIFT) & CIBAUD);
31290Sstevel@tonic-gate 		}
31300Sstevel@tonic-gate 	}
31310Sstevel@tonic-gate 
31320Sstevel@tonic-gate 	c_flag = tp->t_cflag;
31330Sstevel@tonic-gate 
31340Sstevel@tonic-gate 	/*
31350Sstevel@tonic-gate 	 * flow control
31360Sstevel@tonic-gate 	 */
31370Sstevel@tonic-gate 	flow_ctl = tp->t_iflag & (IXON | IXANY | IXOFF);
31380Sstevel@tonic-gate 	if (c_flag & CRTSCTS) {
31390Sstevel@tonic-gate 		flow_ctl |= CTSXON;
31400Sstevel@tonic-gate 	}
31410Sstevel@tonic-gate 	if (c_flag & CRTSXOFF) {
31420Sstevel@tonic-gate 		flow_ctl |= RTSXOFF;
31430Sstevel@tonic-gate 	}
31440Sstevel@tonic-gate 
31450Sstevel@tonic-gate 	/*
31460Sstevel@tonic-gate 	 * fill in port parameters we need to set:
31470Sstevel@tonic-gate 	 *
31480Sstevel@tonic-gate 	 * baud rate
31490Sstevel@tonic-gate 	 */
31500Sstevel@tonic-gate 	pe[0].param = DS_PARAM_BAUD;
31510Sstevel@tonic-gate 	pe[0].val.ui = baudrate;
31520Sstevel@tonic-gate 
31530Sstevel@tonic-gate 	/* stop bits */
31540Sstevel@tonic-gate 	pe[1].param = DS_PARAM_STOPB;
31550Sstevel@tonic-gate 	pe[1].val.ui = c_flag & CSTOPB;
31560Sstevel@tonic-gate 
31570Sstevel@tonic-gate 	/* parity */
31580Sstevel@tonic-gate 	pe[2].param = DS_PARAM_PARITY;
31590Sstevel@tonic-gate 	pe[2].val.ui = c_flag & (PARENB | PARODD);
31600Sstevel@tonic-gate 
31610Sstevel@tonic-gate 	/* char size */
31620Sstevel@tonic-gate 	pe[3].param = DS_PARAM_CHARSZ;
31630Sstevel@tonic-gate 	pe[3].val.ui = c_flag & CSIZE;
31640Sstevel@tonic-gate 
31650Sstevel@tonic-gate 	/* start & stop chars */
31660Sstevel@tonic-gate 	pe[4].param = DS_PARAM_XON_XOFF;
31670Sstevel@tonic-gate 	pe[4].val.uc[0] = tp->t_startc;
31680Sstevel@tonic-gate 	pe[4].val.uc[1] = tp->t_stopc;
31690Sstevel@tonic-gate 
31700Sstevel@tonic-gate 	/* flow control */
31710Sstevel@tonic-gate 	pe[5].param = DS_PARAM_FLOW_CTL;
31720Sstevel@tonic-gate 	pe[5].val.ui = flow_ctl;
31730Sstevel@tonic-gate 
31740Sstevel@tonic-gate 	params.tp_entries = &pe[0];
31750Sstevel@tonic-gate 	params.tp_cnt = 6;
31760Sstevel@tonic-gate 
31770Sstevel@tonic-gate 	/* control signals */
31780Sstevel@tonic-gate 	ctl_val = TIOCM_DTR | TIOCM_RTS;
31790Sstevel@tonic-gate 	if (baudrate == 0) {
31800Sstevel@tonic-gate 		ctl_val &= ~TIOCM_DTR;	/* zero baudrate means drop DTR */
31810Sstevel@tonic-gate 	}
31820Sstevel@tonic-gate 	if (pp->port_flags & USBSER_FL_RX_STOPPED) {
31830Sstevel@tonic-gate 		ctl_val &= ~TIOCM_RTS;
31840Sstevel@tonic-gate 	}
31850Sstevel@tonic-gate 
31860Sstevel@tonic-gate 	/* submit */
31870Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
31880Sstevel@tonic-gate 	err = USBSER_DS_SET_PORT_PARAMS(pp, &params);
31890Sstevel@tonic-gate 	if (err != USB_SUCCESS) {
31900Sstevel@tonic-gate 		mutex_enter(&pp->port_mutex);
31910Sstevel@tonic-gate 
31920Sstevel@tonic-gate 		return (EINVAL);
31930Sstevel@tonic-gate 	}
31940Sstevel@tonic-gate 
31950Sstevel@tonic-gate 	err = USBSER_DS_SET_MODEM_CTL(pp, TIOCM_DTR | TIOCM_RTS, ctl_val);
31960Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
31970Sstevel@tonic-gate 
31980Sstevel@tonic-gate 	return ((err == USB_SUCCESS) ? 0 : EIO);
31990Sstevel@tonic-gate }
32000Sstevel@tonic-gate 
32010Sstevel@tonic-gate 
32020Sstevel@tonic-gate /*
32030Sstevel@tonic-gate  * check if any inbound flow control action needed
32040Sstevel@tonic-gate  */
32050Sstevel@tonic-gate static void
usbser_inbound_flow_ctl(usbser_port_t * pp)32060Sstevel@tonic-gate usbser_inbound_flow_ctl(usbser_port_t *pp)
32070Sstevel@tonic-gate {
32080Sstevel@tonic-gate 	tcflag_t	need_hw;
32090Sstevel@tonic-gate 	int		rts;
32100Sstevel@tonic-gate 	char		c = pp->port_flowc;
32110Sstevel@tonic-gate 	mblk_t		*mp = NULL;
32120Sstevel@tonic-gate 
32130Sstevel@tonic-gate 	USB_DPRINTF_L4(DPRINT_WQ, pp->port_lh,
32140Sstevel@tonic-gate 	    "usbser_inbound_flow_ctl: c=%x cflag=%x port_flags=%x",
32150Sstevel@tonic-gate 	    c, pp->port_ttycommon.t_cflag, pp->port_flags);
32160Sstevel@tonic-gate 
32170Sstevel@tonic-gate 	if (c == '\0') {
32180Sstevel@tonic-gate 
32190Sstevel@tonic-gate 		return;
32200Sstevel@tonic-gate 	}
32210Sstevel@tonic-gate 	pp->port_flowc = '\0';
32220Sstevel@tonic-gate 
32230Sstevel@tonic-gate 	/*
32240Sstevel@tonic-gate 	 * if inbound hardware flow control enabled, we need to frob RTS
32250Sstevel@tonic-gate 	 */
32260Sstevel@tonic-gate 	need_hw = (pp->port_ttycommon.t_cflag & CRTSXOFF);
32270Sstevel@tonic-gate 	if (c == pp->port_ttycommon.t_startc) {
32280Sstevel@tonic-gate 		rts = TIOCM_RTS;
32290Sstevel@tonic-gate 		pp->port_flags &= ~USBSER_FL_RX_STOPPED;
32300Sstevel@tonic-gate 	} else {
32310Sstevel@tonic-gate 		rts = 0;
32320Sstevel@tonic-gate 		pp->port_flags |= USBSER_FL_RX_STOPPED;
32330Sstevel@tonic-gate 	}
32340Sstevel@tonic-gate 
32350Sstevel@tonic-gate 	/*
32360Sstevel@tonic-gate 	 * if character flow control active, transmit a start or stop char,
32370Sstevel@tonic-gate 	 */
32380Sstevel@tonic-gate 	if (pp->port_ttycommon.t_iflag & IXOFF) {
32390Sstevel@tonic-gate 		if ((mp = allocb(1, BPRI_LO)) == NULL) {
32400Sstevel@tonic-gate 			USB_DPRINTF_L2(DPRINT_WQ, pp->port_lh,
32410Sstevel@tonic-gate 			    "usbser_inbound_flow_ctl: allocb failed");
32420Sstevel@tonic-gate 		} else {
32430Sstevel@tonic-gate 			*mp->b_wptr++ = c;
32440Sstevel@tonic-gate 			pp->port_flags |= USBSER_ACT_TX;
32450Sstevel@tonic-gate 		}
32460Sstevel@tonic-gate 	}
32470Sstevel@tonic-gate 
32480Sstevel@tonic-gate 	mutex_exit(&pp->port_mutex);
32490Sstevel@tonic-gate 	if (need_hw) {
32500Sstevel@tonic-gate 		(void) USBSER_DS_SET_MODEM_CTL(pp, TIOCM_RTS, rts);
32510Sstevel@tonic-gate 	}
32520Sstevel@tonic-gate 	if (mp) {
32530Sstevel@tonic-gate 		(void) USBSER_DS_TX(pp, mp);
32540Sstevel@tonic-gate 	}
32550Sstevel@tonic-gate 	mutex_enter(&pp->port_mutex);
32560Sstevel@tonic-gate }
32570Sstevel@tonic-gate 
32580Sstevel@tonic-gate 
32590Sstevel@tonic-gate /*
32600Sstevel@tonic-gate  * misc
32610Sstevel@tonic-gate  * ----
32620Sstevel@tonic-gate  *
32630Sstevel@tonic-gate  *
32649354STim.Marsland@Sun.COM  * returns != 0 if device is online, 0 otherwise
32650Sstevel@tonic-gate  */
32660Sstevel@tonic-gate static int
usbser_dev_is_online(usbser_state_t * usp)32670Sstevel@tonic-gate usbser_dev_is_online(usbser_state_t *usp)
32680Sstevel@tonic-gate {
32690Sstevel@tonic-gate 	int	rval;
32700Sstevel@tonic-gate 
32710Sstevel@tonic-gate 	mutex_enter(&usp->us_mutex);
32720Sstevel@tonic-gate 	rval = (usp->us_dev_state == USB_DEV_ONLINE);
32730Sstevel@tonic-gate 	mutex_exit(&usp->us_mutex);
32740Sstevel@tonic-gate 
32750Sstevel@tonic-gate 	return (rval);
32760Sstevel@tonic-gate }
32770Sstevel@tonic-gate 
32780Sstevel@tonic-gate /*
32790Sstevel@tonic-gate  * serialize port activities defined by 'act' mask
32800Sstevel@tonic-gate  */
32810Sstevel@tonic-gate static void
usbser_serialize_port_act(usbser_port_t * pp,int act)32820Sstevel@tonic-gate usbser_serialize_port_act(usbser_port_t *pp, int act)
32830Sstevel@tonic-gate {
32849354STim.Marsland@Sun.COM 	while (pp->port_act & act)
32850Sstevel@tonic-gate 		cv_wait(&pp->port_act_cv, &pp->port_mutex);
32860Sstevel@tonic-gate 	pp->port_act |= act;
32870Sstevel@tonic-gate }
32880Sstevel@tonic-gate 
32890Sstevel@tonic-gate 
32900Sstevel@tonic-gate /*
32910Sstevel@tonic-gate  * indicate that port activity is finished
32920Sstevel@tonic-gate  */
32930Sstevel@tonic-gate static void
usbser_release_port_act(usbser_port_t * pp,int act)32940Sstevel@tonic-gate usbser_release_port_act(usbser_port_t *pp, int act)
32950Sstevel@tonic-gate {
32960Sstevel@tonic-gate 	pp->port_act &= ~act;
32970Sstevel@tonic-gate 	cv_broadcast(&pp->port_act_cv);
32980Sstevel@tonic-gate }
32990Sstevel@tonic-gate 
33000Sstevel@tonic-gate 
33010Sstevel@tonic-gate /*
33020Sstevel@tonic-gate  * message type to string and back conversion.
33030Sstevel@tonic-gate  *
33040Sstevel@tonic-gate  * pardon breaks on the same line, but as long as cstyle doesn't
33050Sstevel@tonic-gate  * complain, I'd like to keep this form for trivial cases like this.
33060Sstevel@tonic-gate  * associative arrays in the kernel, anyone?
33070Sstevel@tonic-gate  */
33080Sstevel@tonic-gate static char *
usbser_msgtype2str(int type)33090Sstevel@tonic-gate usbser_msgtype2str(int type)
33100Sstevel@tonic-gate {
33110Sstevel@tonic-gate 	char	*str;
33120Sstevel@tonic-gate 
33130Sstevel@tonic-gate 	switch (type) {
33140Sstevel@tonic-gate 	case M_STOP:	str = "M_STOP";		break;
33150Sstevel@tonic-gate 	case M_START:	str = "M_START";	break;
33160Sstevel@tonic-gate 	case M_STOPI:	str = "M_STOPI";	break;
33170Sstevel@tonic-gate 	case M_STARTI:	str = "M_STARTI";	break;
33180Sstevel@tonic-gate 	case M_DATA:	str = "M_DATA";		break;
33190Sstevel@tonic-gate 	case M_DELAY:	str = "M_DELAY";	break;
33200Sstevel@tonic-gate 	case M_BREAK:	str = "M_BREAK";	break;
33210Sstevel@tonic-gate 	case M_IOCTL:	str = "M_IOCTL";	break;
33220Sstevel@tonic-gate 	case M_IOCDATA:	str = "M_IOCDATA";	break;
33230Sstevel@tonic-gate 	case M_FLUSH:	str = "M_FLUSH";	break;
33240Sstevel@tonic-gate 	case M_CTL:	str = "M_CTL";		break;
33250Sstevel@tonic-gate 	case M_READ:	str = "M_READ";		break;
33260Sstevel@tonic-gate 	default:	str = "unknown";	break;
33270Sstevel@tonic-gate 	}
33280Sstevel@tonic-gate 
33290Sstevel@tonic-gate 	return (str);
33300Sstevel@tonic-gate }
33310Sstevel@tonic-gate 
33320Sstevel@tonic-gate 
33330Sstevel@tonic-gate static char *
usbser_ioctl2str(int ioctl)33340Sstevel@tonic-gate usbser_ioctl2str(int ioctl)
33350Sstevel@tonic-gate {
33360Sstevel@tonic-gate 	char	*str;
33370Sstevel@tonic-gate 
33380Sstevel@tonic-gate 	switch (ioctl) {
33390Sstevel@tonic-gate 	case TCGETA:	str = "TCGETA";		break;
33400Sstevel@tonic-gate 	case TCSETA:	str = "TCSETA";		break;
33410Sstevel@tonic-gate 	case TCSETAF:	str = "TCSETAF";	break;
33420Sstevel@tonic-gate 	case TCSETAW:	str = "TCSETAW";	break;
33430Sstevel@tonic-gate 	case TCSBRK:	str = "TCSBRK";		break;
33440Sstevel@tonic-gate 	case TCXONC:	str = "TCXONC";		break;
33450Sstevel@tonic-gate 	case TCFLSH:	str = "TCFLSH";		break;
33460Sstevel@tonic-gate 	case TCGETS:	str = "TCGETS";		break;
33470Sstevel@tonic-gate 	case TCSETS:	str = "TCSETS";		break;
33480Sstevel@tonic-gate 	case TCSETSF:	str = "TCSETSF";	break;
33490Sstevel@tonic-gate 	case TCSETSW:	str = "TCSETSW";	break;
33500Sstevel@tonic-gate 	case TIOCSBRK:	str = "TIOCSBRK";	break;
33510Sstevel@tonic-gate 	case TIOCCBRK:	str = "TIOCCBRK";	break;
33520Sstevel@tonic-gate 	case TIOCMSET:	str = "TIOCMSET";	break;
33530Sstevel@tonic-gate 	case TIOCMBIS:	str = "TIOCMBIS";	break;
33540Sstevel@tonic-gate 	case TIOCMBIC:	str = "TIOCMBIC";	break;
33550Sstevel@tonic-gate 	case TIOCMGET:	str = "TIOCMGET";	break;
33569354STim.Marsland@Sun.COM 	case TIOCSILOOP: str = "TIOCSILOOP";	break;
33579354STim.Marsland@Sun.COM 	case TIOCCILOOP: str = "TIOCCILOOP";	break;
33580Sstevel@tonic-gate 	case TCGETX:	str = "TCGETX";		break;
33590Sstevel@tonic-gate 	case TCSETX:	str = "TCGETX";		break;
33600Sstevel@tonic-gate 	case TCSETXW:	str = "TCGETX";		break;
33610Sstevel@tonic-gate 	case TCSETXF:	str = "TCGETX";		break;
33620Sstevel@tonic-gate 	default:	str = "unknown";	break;
33630Sstevel@tonic-gate 	}
33640Sstevel@tonic-gate 
33650Sstevel@tonic-gate 	return (str);
33660Sstevel@tonic-gate }
33672191Sszhou 
33682191Sszhou /*
33692191Sszhou  * Polled IO support
33702191Sszhou  */
33712191Sszhou 
33726898Sfb209375 /* called once	by consconfig() when polledio is opened */
33732191Sszhou static int
usbser_polledio_init(usbser_port_t * pp)33742191Sszhou usbser_polledio_init(usbser_port_t *pp)
33752191Sszhou {
33762191Sszhou 	int err;
33772191Sszhou 	usb_pipe_handle_t hdl;
33782191Sszhou 	ds_ops_t *ds_ops = pp->port_ds_ops;
33792191Sszhou 
33802191Sszhou 	/* only one serial line console supported */
33812191Sszhou 	if (console_input != NULL)
33822191Sszhou 		return (USB_FAILURE);
33832191Sszhou 
33842191Sszhou 	/* check if underlying driver supports polled io */
33852191Sszhou 	if (ds_ops->ds_version < DS_OPS_VERSION_V1 ||
33862191Sszhou 	    ds_ops->ds_out_pipe == NULL || ds_ops->ds_in_pipe == NULL)
33872191Sszhou 		return (USB_FAILURE);
33882191Sszhou 
33892191Sszhou 	/* init polled input pipe */
33902191Sszhou 	hdl = ds_ops->ds_in_pipe(pp->port_ds_hdl, pp->port_num);
33912191Sszhou 	err = usb_console_input_init(pp->port_usp->us_dip, hdl,
33922191Sszhou 	    &console_input_buf, &console_input);
33932191Sszhou 	if (err)
33942191Sszhou 		return (USB_FAILURE);
33952191Sszhou 
33962191Sszhou 	/* init polled output pipe */
33972191Sszhou 	hdl = ds_ops->ds_out_pipe(pp->port_ds_hdl, pp->port_num);
33982191Sszhou 	err = usb_console_output_init(pp->port_usp->us_dip, hdl,
33992191Sszhou 	    &console_output);
34002191Sszhou 	if (err) {
34012191Sszhou 		(void) usb_console_input_fini(console_input);
34022191Sszhou 		console_input = NULL;
34032191Sszhou 		return (USB_FAILURE);
34042191Sszhou 	}
34052191Sszhou 
34062191Sszhou 	return (USB_SUCCESS);
34072191Sszhou }
34082191Sszhou 
34096898Sfb209375 /* called once	by consconfig() when polledio is closed */
34102191Sszhou /*ARGSUSED*/
usbser_polledio_fini(usbser_port_t * pp)34112191Sszhou static void usbser_polledio_fini(usbser_port_t *pp)
34122191Sszhou {
34132191Sszhou 	/* Since we can't move the console, there is nothing to do. */
34142191Sszhou }
34152191Sszhou 
34162191Sszhou /*ARGSUSED*/
34172191Sszhou static void
usbser_polledio_enter(cons_polledio_arg_t arg)34182191Sszhou usbser_polledio_enter(cons_polledio_arg_t arg)
34192191Sszhou {
34202191Sszhou 	(void) usb_console_input_enter(console_input);
34212191Sszhou 	(void) usb_console_output_enter(console_output);
34222191Sszhou }
34232191Sszhou 
34242191Sszhou /*ARGSUSED*/
34252191Sszhou static void
usbser_polledio_exit(cons_polledio_arg_t arg)34262191Sszhou usbser_polledio_exit(cons_polledio_arg_t arg)
34272191Sszhou {
34282191Sszhou 	(void) usb_console_output_exit(console_output);
34292191Sszhou 	(void) usb_console_input_exit(console_input);
34302191Sszhou }
34312191Sszhou 
34322191Sszhou /*ARGSUSED*/
34332191Sszhou static void
usbser_putchar(cons_polledio_arg_t arg,uchar_t c)34342191Sszhou usbser_putchar(cons_polledio_arg_t arg, uchar_t c)
34352191Sszhou {
34362191Sszhou 	static uchar_t cr[2] = {'\r', '\n'};
34372191Sszhou 	uint_t nout;
34382191Sszhou 
34392191Sszhou 	if (c == '\n')
34402191Sszhou 		(void) usb_console_write(console_output, cr, 2, &nout);
34412191Sszhou 	else
34422191Sszhou 		(void) usb_console_write(console_output, &c, 1, &nout);
34432191Sszhou }
34442191Sszhou 
34452191Sszhou /*ARGSUSED*/
34462191Sszhou static int
usbser_getchar(cons_polledio_arg_t arg)34472191Sszhou usbser_getchar(cons_polledio_arg_t arg)
34482191Sszhou {
34492191Sszhou 	while (!usbser_ischar(arg))
34502191Sszhou 		;
34512191Sszhou 
34522191Sszhou 	return (*console_input_start++);
34532191Sszhou }
34542191Sszhou 
34552191Sszhou /*ARGSUSED*/
34562191Sszhou static boolean_t
usbser_ischar(cons_polledio_arg_t arg)34572191Sszhou usbser_ischar(cons_polledio_arg_t arg)
34582191Sszhou {
34592191Sszhou 	uint_t num_bytes;
34602191Sszhou 
34612191Sszhou 	if (console_input_start < console_input_end)
34629354STim.Marsland@Sun.COM 		return (B_TRUE);
34632191Sszhou 
34642191Sszhou 	if (usb_console_read(console_input, &num_bytes) != USB_SUCCESS)
34659354STim.Marsland@Sun.COM 		return (B_FALSE);
34662191Sszhou 
34672191Sszhou 	console_input_start = console_input_buf;
34682191Sszhou 	console_input_end = console_input_buf + num_bytes;
34692191Sszhou 
34702191Sszhou 	return (num_bytes != 0);
34712191Sszhou }
3472