xref: /onnv-gate/usr/src/uts/common/io/usb/clients/usbser/usbsacm/usbsacm.c (revision 12024:2f4aad840b1f)
13227Syq193411 /*
23227Syq193411  * CDDL HEADER START
33227Syq193411  *
43227Syq193411  * The contents of this file are subject to the terms of the
53227Syq193411  * Common Development and Distribution License (the "License").
63227Syq193411  * You may not use this file except in compliance with the License.
73227Syq193411  *
83227Syq193411  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93227Syq193411  * or http://www.opensolaris.org/os/licensing.
103227Syq193411  * See the License for the specific language governing permissions
113227Syq193411  * and limitations under the License.
123227Syq193411  *
133227Syq193411  * When distributing Covered Code, include this CDDL HEADER in each
143227Syq193411  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153227Syq193411  * If applicable, add the following below this CDDL HEADER, with the
163227Syq193411  * fields enclosed by brackets "[]" replaced with your own identifying
173227Syq193411  * information: Portions Copyright [yyyy] [name of copyright owner]
183227Syq193411  *
193227Syq193411  * CDDL HEADER END
203227Syq193411  */
219354STim.Marsland@Sun.COM 
223227Syq193411 /*
23*12024SRaymond.Chen@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
243227Syq193411  * Use is subject to license terms.
253227Syq193411  */
263227Syq193411 
273227Syq193411 /*
283227Syq193411  * USB Serial CDC ACM driver
293227Syq193411  *
303227Syq193411  * 1. General Concepts
313227Syq193411  * -------------------
323227Syq193411  *
333227Syq193411  * 1.1 Overview
343227Syq193411  * ------------
353227Syq193411  * This driver supports devices that comply with the USB Communication
363227Syq193411  * Device Class Abstract Control Model (USB CDC ACM) specification,
373227Syq193411  * which is available at http://www.usb.org. Given the broad nature
383227Syq193411  * of communication equipment, this driver supports the following
393227Syq193411  * types of devices:
405541Slg150142  *	+ Telecommunications devices: analog modems, mobile phones;
415541Slg150142  *	+ Networking devices: cable modems;
423227Syq193411  * Except the above mentioned acm devices, this driver also supports
433227Syq193411  * some devices which provide modem-like function and have pairs of
443227Syq193411  * bulk in/out pipes.
453227Syq193411  *
463227Syq193411  * There are three classes that make up the definition for communication
473227Syq193411  * devices: the Communication Device Class, the Communication Interface
483227Syq193411  * Class and the Data Interface Class. The Communication Device Class
493227Syq193411  * is a device level definition and is used by the host to properly
503227Syq193411  * identify a communication device that may present several different
513227Syq193411  * types of interfaces. The Communication Interface Class defines a
523227Syq193411  * general-purpose mechanism that can be used to enable all types of
533227Syq193411  * communication services on the Universal Serial Bus (USB). The Data
543227Syq193411  * Interface Class defines a general-purpose mechanism to enable bulk
553227Syq193411  * transfer on the USB when the data does not meet the requirements
563227Syq193411  * for any other class.
573227Syq193411  *
583227Syq193411  * 1.2 Interface Definitions
593227Syq193411  * -------------------------
603227Syq193411  * Communication Class Interface is used for device management and,
613227Syq193411  * optionally, call management. Device management includes the requests
623227Syq193411  * that manage the operational state of a device, the device responses,
633227Syq193411  * and event notifications. In Abstract Control Model, the device can
643227Syq193411  * provide an internal implementation of call management over the Data
653227Syq193411  * Class interface or the Communication Class interface.
663227Syq193411  *
673227Syq193411  * The Data Class defines a data interface as an interface with a class
683227Syq193411  * type of Data Class. Data transmission on a communication device is
693227Syq193411  * not restricted to interfaces using the Data Class. Rather, a data
703227Syq193411  * interface is used to transmit and/or receive data that is not
713227Syq193411  * defined by any other class. The data could be:
725541Slg150142  *	+ Some form of raw data from a communication line.
735541Slg150142  *	+ Legacy modem data.
745541Slg150142  *	+ Data using a proprietary format.
753227Syq193411  *
763227Syq193411  * 1.3 Endpoint Requirements
773227Syq193411  * -------------------------
783227Syq193411  * The Communication Class interface requires one endpoint, the management
793227Syq193411  * element. Optionally, it can have an additional endpoint, the notification
803227Syq193411  * element. The management element uses the default endpoint for all
813227Syq193411  * standard and Communication Class-specific requests. The notification
823227Syq193411  * element normally uses an interrupt endpoint.
833227Syq193411  *
843227Syq193411  * The type of endpoints belonging to a Data Class interface are restricted
853227Syq193411  * to bulk, and are expected to exist in pairs of the same type (one In and
863227Syq193411  * one Out).
873227Syq193411  *
883227Syq193411  * 1.4 ACM Function Characteristics
893227Syq193411  * --------------------------------
903227Syq193411  * With Abstract Control Model, the USB device understands standard
913227Syq193411  * V.25ter (AT) commands. The device contains a Datapump and micro-
923227Syq193411  * controller that handles the AT commands and relay controls. The
933227Syq193411  * device uses both a Data Class interface and a Communication Class.
943227Syq193411  * interface.
953227Syq193411  *
963227Syq193411  * A Communication Class interface of type Abstract Control Model will
973227Syq193411  * consist of a minimum of two pipes; one is used to implement the
983227Syq193411  * management element and the other to implement a notification element.
993227Syq193411  * In addition, the device can use two pipes to implement channels over
1003227Syq193411  * which to carry unspecified data, typically over a Data Class interface.
1013227Syq193411  *
1023227Syq193411  * 1.5 ACM Serial Emulation
1033227Syq193411  * ------------------------
1043227Syq193411  * The Abstract Control Model can bridge the gap between legacy modem
1053227Syq193411  * devices and USB devices. To support certain types of legacy applications,
1063227Syq193411  * two problems need to be addressed. The first is supporting specific
1073227Syq193411  * legacy control signals and state variables which are addressed
1083227Syq193411  * directly by the various carrier modulation standards. To support these
1093227Syq193411  * requirement, additional requests and notifications have been created.
1103227Syq193411  * Please refer to macro, beginning with USB_CDC_REQ_* and
1113227Syq193411  * USB_CDC_NOTIFICATION_*.
1123227Syq193411  *
1133227Syq193411  * The second significant item which is needed to bridge the gap between
1143227Syq193411  * legacy modem designs and the Abstract Control Model is a means to
1153227Syq193411  * multiplex call control (AT commands) on the Data Class interface.
1163227Syq193411  * Legacy modem designs are limited by only supporting one channel for
1173227Syq193411  * both "AT" commands and the actual data. To allow this type of
1183227Syq193411  * functionality, the device must have a means to specify this limitation
1193227Syq193411  * to the host.
1203227Syq193411  *
1213227Syq193411  * When describing this type of device, the Communication Class interface
1223227Syq193411  * would still specify a Abstract Control Model, but call control would
1233227Syq193411  * actually occur over the Data Class interface. To describe this
1243227Syq193411  * particular characteristic, the Call Management Functional Descriptor
1253227Syq193411  * would have bit D1 of bmCapabilities set.
1263227Syq193411  *
1273227Syq193411  * 1.6 Other Bulk In/Out Devices
1283227Syq193411  * -----------------------------
1293227Syq193411  * Some devices don't conform to USB CDC specification, but they provide
1303227Syq193411  * modem-like function and have pairs of bulk in/out pipes. This driver
1313227Syq193411  * supports this kind of device and exports term nodes by their pipes.
1323227Syq193411  *
1333227Syq193411  * 2. Implementation
1343227Syq193411  * -----------------
1353227Syq193411  *
1363227Syq193411  * 2.1 Overview
1373227Syq193411  * ------------
1383227Syq193411  * It is a device-specific driver (DSD) working with USB generic serial
1393227Syq193411  * driver (GSD). It implements the USB-to-serial device-specific driver
1403227Syq193411  * interface (DSDI) which is offered by GSD. The interface is defined
1413227Syq193411  * by ds_ops_t structure.
1423227Syq193411  *
1433227Syq193411  * 2.2 Port States
1443227Syq193411  * ---------------
1453227Syq193411  * For USB CDC ACM devices, this driver is attached to its interface,
1463227Syq193411  * and exports one port for each interface. For other modem-like devices,
1473227Syq193411  * this driver can dynamically find the ports in the current device,
1483227Syq193411  * and export one port for each pair bulk in/out pipes. Each port can
1493227Syq193411  * be operated independently.
1503227Syq193411  *
1513227Syq193411  * port_state:
1523227Syq193411  *
1533227Syq193411  *		attach_ports
1543227Syq193411  *		    |
1553227Syq193411  *		    |
1563227Syq193411  *		    |
1573227Syq193411  *		    v
1583227Syq193411  *	    USBSACM_PORT_CLOSED
1593227Syq193411  *		|	    ^
1603227Syq193411  *		|	    |
1613227Syq193411  *		V	    |
1623227Syq193411  *	   open_port	close_port
1633227Syq193411  *		|	    ^
1643227Syq193411  *		|	    |
1653227Syq193411  *		V	    |
1663227Syq193411  *	      USBSACM_PORT_OPEN
1673227Syq193411  *
1683227Syq193411  *
1693227Syq193411  * 2.3 Pipe States
1703227Syq193411  * ---------------
1713227Syq193411  * Each port has its own bulk in/out pipes and some ports could also have
1723227Syq193411  * its own interrupt pipes (traced by usbsacm_port structure), which are
1733227Syq193411  * opened during attach. The pipe status is as following:
1743227Syq193411  *
1753227Syq193411  * pipe_state:
1763227Syq193411  *
1773227Syq193411  *		usbsacm_init_alloc_ports  usbsacm_free_ports
1783227Syq193411  *				|		^
1793227Syq193411  *				v		|
1805541Slg150142  *		  |---->------ USBSACM_PORT_CLOSED ------>------+
1813227Syq193411  *		  ^						|
1823227Syq193411  *		  |				reconnect/resume/open_port
1833227Syq193411  *		  |						|
1843227Syq193411  *    disconnect/suspend/close_port				|
1853227Syq193411  *		  |						v
1863227Syq193411  *		  +------<------ USBSACM_PIPE_IDLE ------<------|
1873227Syq193411  *				    |		|
1883227Syq193411  *				    V		^
1893227Syq193411  *				    |		|
1903227Syq193411  *		  +-----------------+		+-----------+
1913227Syq193411  *		  |					    |
1923227Syq193411  *		  V					    ^
1933227Syq193411  *		  |					    |
1943227Syq193411  *	rx_start/tx_start----->------failed------->---------|
1953227Syq193411  *		  |					    |
1963227Syq193411  *		  |				bulkin_cb/bulkout_cb
1973227Syq193411  *		  V					    |
1983227Syq193411  *		  |					    ^
1993227Syq193411  *		  |					    |
2003227Syq193411  *		  +----->----- USBSACM_PIPE_BUSY ---->------+
2013227Syq193411  *
2023227Syq193411  *
2033227Syq193411  * To get its status in a timely way, acm driver can get the status
2043227Syq193411  * of the device by polling the interrupt pipe.
2053227Syq193411  *
2063227Syq193411  */
2073227Syq193411 
2083227Syq193411 #include <sys/types.h>
2093227Syq193411 #include <sys/param.h>
2103227Syq193411 #include <sys/conf.h>
2113227Syq193411 #include <sys/stream.h>
2123227Syq193411 #include <sys/strsun.h>
2133227Syq193411 #include <sys/termio.h>
2143227Syq193411 #include <sys/termiox.h>
2153227Syq193411 #include <sys/ddi.h>
2163227Syq193411 #include <sys/sunddi.h>
2173227Syq193411 #include <sys/byteorder.h>
2183227Syq193411 #define	USBDRV_MAJOR_VER	2
2193227Syq193411 #define	USBDRV_MINOR_VER	0
2203227Syq193411 #include <sys/usb/usba.h>
2213227Syq193411 #include <sys/usb/usba/usba_types.h>
2223227Syq193411 #include <sys/usb/clients/usbser/usbser.h>
2233227Syq193411 #include <sys/usb/clients/usbser/usbser_dsdi.h>
2243227Syq193411 #include <sys/usb/clients/usbcdc/usb_cdc.h>
2253227Syq193411 #include <sys/usb/clients/usbser/usbsacm/usbsacm.h>
2263227Syq193411 
2273227Syq193411 /* devops entry points */
2283227Syq193411 static int	usbsacm_attach(dev_info_t *, ddi_attach_cmd_t);
2293227Syq193411 static int	usbsacm_detach(dev_info_t *, ddi_detach_cmd_t);
2305541Slg150142 static int	usbsacm_getinfo(dev_info_t *, ddi_info_cmd_t, void *,
2313227Syq193411 		void **);
2323227Syq193411 static int	usbsacm_open(queue_t *, dev_t *, int, int, cred_t *);
2333227Syq193411 
2343227Syq193411 /* DSD operations */
2353227Syq193411 static int	usbsacm_ds_attach(ds_attach_info_t *);
2363227Syq193411 static void	usbsacm_ds_detach(ds_hdl_t);
2373227Syq193411 static int	usbsacm_ds_register_cb(ds_hdl_t, uint_t, ds_cb_t *);
2383227Syq193411 static void	usbsacm_ds_unregister_cb(ds_hdl_t, uint_t);
2393227Syq193411 static int	usbsacm_ds_open_port(ds_hdl_t, uint_t);
2403227Syq193411 static int	usbsacm_ds_close_port(ds_hdl_t, uint_t);
2413227Syq193411 
2423227Syq193411 /* standard UART operations */
2433227Syq193411 static int	usbsacm_ds_set_port_params(ds_hdl_t, uint_t,
2443227Syq193411 		ds_port_params_t *);
2453227Syq193411 static int	usbsacm_ds_set_modem_ctl(ds_hdl_t, uint_t, int, int);
2463227Syq193411 static int	usbsacm_ds_get_modem_ctl(ds_hdl_t, uint_t, int, int *);
2473227Syq193411 static int	usbsacm_ds_break_ctl(ds_hdl_t, uint_t, int);
2483227Syq193411 
2493227Syq193411 /* data xfer */
2503227Syq193411 static int	usbsacm_ds_tx(ds_hdl_t, uint_t, mblk_t *);
2513227Syq193411 static mblk_t	*usbsacm_ds_rx(ds_hdl_t, uint_t);
2523227Syq193411 static void	usbsacm_ds_stop(ds_hdl_t, uint_t, int);
2533227Syq193411 static void	usbsacm_ds_start(ds_hdl_t, uint_t, int);
2543227Syq193411 
2553227Syq193411 /* fifo operations */
2563227Syq193411 static int	usbsacm_ds_fifo_flush(ds_hdl_t, uint_t, int);
2573227Syq193411 static int	usbsacm_ds_fifo_drain(ds_hdl_t, uint_t, int);
2583227Syq193411 static int	usbsacm_wait_tx_drain(usbsacm_port_t *, int);
2593227Syq193411 static int	usbsacm_fifo_flush_locked(usbsacm_state_t *, uint_t, int);
2603227Syq193411 
2613227Syq193411 /* power management and CPR */
2623227Syq193411 static int	usbsacm_ds_suspend(ds_hdl_t);
2633227Syq193411 static int	usbsacm_ds_resume(ds_hdl_t);
2643227Syq193411 static int	usbsacm_ds_disconnect(ds_hdl_t);
2653227Syq193411 static int	usbsacm_ds_reconnect(ds_hdl_t);
2663227Syq193411 static int	usbsacm_ds_usb_power(ds_hdl_t, int, int, int *);
2673227Syq193411 static int	usbsacm_create_pm_components(usbsacm_state_t *);
2683227Syq193411 static void	usbsacm_destroy_pm_components(usbsacm_state_t *);
2693227Syq193411 static void	usbsacm_pm_set_busy(usbsacm_state_t *);
2703227Syq193411 static void	usbsacm_pm_set_idle(usbsacm_state_t *);
2713227Syq193411 static int	usbsacm_pwrlvl0(usbsacm_state_t *);
2723227Syq193411 static int	usbsacm_pwrlvl1(usbsacm_state_t *);
2733227Syq193411 static int	usbsacm_pwrlvl2(usbsacm_state_t *);
2743227Syq193411 static int	usbsacm_pwrlvl3(usbsacm_state_t *);
2753227Syq193411 
2763227Syq193411 /* event handling */
2773227Syq193411 /* pipe callbacks */
2783227Syq193411 static void	usbsacm_bulkin_cb(usb_pipe_handle_t, usb_bulk_req_t *);
2793227Syq193411 static void	usbsacm_bulkout_cb(usb_pipe_handle_t, usb_bulk_req_t *);
2803227Syq193411 
2813227Syq193411 /* interrupt pipe */
2823227Syq193411 static void	usbsacm_pipe_start_polling(usbsacm_port_t *acmp);
2833227Syq193411 static void	usbsacm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
2843227Syq193411 static void	usbsacm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
2853227Syq193411 static void	usbsacm_parse_intr_data(usbsacm_port_t *acmp, mblk_t *data);
2863227Syq193411 
2873227Syq193411 /* Utility functions */
2883227Syq193411 /* data transfer routines */
2893227Syq193411 static int	usbsacm_rx_start(usbsacm_port_t *);
2903227Syq193411 static void	usbsacm_tx_start(usbsacm_port_t *);
2913227Syq193411 static int	usbsacm_send_data(usbsacm_port_t *, mblk_t *);
2923227Syq193411 
2933227Syq193411 /* Initialize or release resources */
2943227Syq193411 static int	usbsacm_init_alloc_ports(usbsacm_state_t *);
2953227Syq193411 static void	usbsacm_free_ports(usbsacm_state_t *);
2963227Syq193411 static void	usbsacm_cleanup(usbsacm_state_t *);
2973227Syq193411 
2983227Syq193411 /* analysis functional descriptors */
2993227Syq193411 static int	usbsacm_get_descriptors(usbsacm_state_t *);
3003227Syq193411 
3013227Syq193411 /* hotplug */
3023227Syq193411 static int	usbsacm_restore_device_state(usbsacm_state_t *);
3033227Syq193411 static int	usbsacm_restore_port_state(usbsacm_state_t *);
3043227Syq193411 
3053227Syq193411 /* pipe operations */
3063227Syq193411 static int	usbsacm_open_port_pipes(usbsacm_port_t *);
3073227Syq193411 static void	usbsacm_close_port_pipes(usbsacm_port_t *);
3083227Syq193411 static void	usbsacm_close_pipes(usbsacm_state_t *);
3093227Syq193411 static void	usbsacm_disconnect_pipes(usbsacm_state_t *);
3103227Syq193411 static int	usbsacm_reconnect_pipes(usbsacm_state_t *);
3113227Syq193411 
3123227Syq193411 /* vendor-specific commands */
3133227Syq193411 static int	usbsacm_req_write(usbsacm_port_t *, uchar_t, uint16_t,
3143227Syq193411 		mblk_t **);
3153227Syq193411 static int	usbsacm_set_line_coding(usbsacm_port_t *,
3163227Syq193411 		usb_cdc_line_coding_t *);
3173227Syq193411 static void	usbsacm_mctl2reg(int mask, int val, uint8_t *);
3183227Syq193411 static int	usbsacm_reg2mctl(uint8_t);
3193227Syq193411 
3203227Syq193411 /* misc */
3213227Syq193411 static void	usbsacm_put_tail(mblk_t **, mblk_t *);
3223227Syq193411 static void	usbsacm_put_head(mblk_t **, mblk_t *);
3233227Syq193411 
3243227Syq193411 
3253227Syq193411 /*
3263227Syq193411  * Standard STREAMS driver definitions
3273227Syq193411  */
3283227Syq193411 struct module_info usbsacm_modinfo = {
3293227Syq193411 	0,			/* module id */
3303227Syq193411 	"usbsacm",		/* module name */
3313227Syq193411 	USBSER_MIN_PKTSZ,	/* min pkt size */
3323227Syq193411 	USBSER_MAX_PKTSZ,	/* max pkt size */
3333227Syq193411 	USBSER_HIWAT,		/* hi watermark */
3343227Syq193411 	USBSER_LOWAT		/* low watermark */
3353227Syq193411 };
3363227Syq193411 
3373227Syq193411 static struct qinit usbsacm_rinit = {
3383227Syq193411 	NULL,
3393227Syq193411 	usbser_rsrv,
3403227Syq193411 	usbsacm_open,
3413227Syq193411 	usbser_close,
3423227Syq193411 	NULL,
3433227Syq193411 	&usbsacm_modinfo,
3443227Syq193411 	NULL
3453227Syq193411 };
3463227Syq193411 
3473227Syq193411 static struct qinit usbsacm_winit = {
3483227Syq193411 	usbser_wput,
3493227Syq193411 	usbser_wsrv,
3503227Syq193411 	NULL,
3513227Syq193411 	NULL,
3523227Syq193411 	NULL,
3533227Syq193411 	&usbsacm_modinfo,
3543227Syq193411 	NULL
3553227Syq193411 };
3563227Syq193411 
3573227Syq193411 
3583227Syq193411 struct streamtab usbsacm_str_info = {
3593227Syq193411 	&usbsacm_rinit, &usbsacm_winit, NULL, NULL
3603227Syq193411 };
3613227Syq193411 
3623227Syq193411 /* cb_ops structure */
3633227Syq193411 static struct cb_ops usbsacm_cb_ops = {
3643227Syq193411 	nodev,			/* cb_open */
3653227Syq193411 	nodev,			/* cb_close */
3663227Syq193411 	nodev,			/* cb_strategy */
3673227Syq193411 	nodev,			/* cb_print */
3683227Syq193411 	nodev,			/* cb_dump */
3693227Syq193411 	nodev,			/* cb_read */
3703227Syq193411 	nodev,			/* cb_write */
3713227Syq193411 	nodev,			/* cb_ioctl */
3723227Syq193411 	nodev,			/* cb_devmap */
3733227Syq193411 	nodev,			/* cb_mmap */
3743227Syq193411 	nodev,			/* cb_segmap */
3753227Syq193411 	nochpoll,		/* cb_chpoll */
3763227Syq193411 	ddi_prop_op,		/* cb_prop_op */
3773227Syq193411 	&usbsacm_str_info,	/* cb_stream */
3783227Syq193411 	(int)(D_64BIT | D_NEW | D_MP | D_HOTPLUG)	/* cb_flag */
3793227Syq193411 };
3803227Syq193411 
3813227Syq193411 /* dev_ops structure */
3823227Syq193411 struct dev_ops usbsacm_ops = {
3833227Syq193411 	DEVO_REV,		/* devo_rev */
3843227Syq193411 	0,			/* devo_refcnt */
3853227Syq193411 	usbsacm_getinfo,	/* devo_getinfo */
3863227Syq193411 	nulldev,		/* devo_identify */
3873227Syq193411 	nulldev,		/* devo_probe */
3883227Syq193411 	usbsacm_attach,		/* devo_attach */
3893227Syq193411 	usbsacm_detach,		/* devo_detach */
3903227Syq193411 	nodev,			/* devo_reset */
3913227Syq193411 	&usbsacm_cb_ops,	/* devo_cb_ops */
3923227Syq193411 	(struct bus_ops *)NULL,	/* devo_bus_ops */
3937656SSherry.Moore@Sun.COM 	usbser_power,		/* devo_power */
3948688SRaymond.Chen@Sun.COM 	ddi_quiesce_not_needed,	/* devo_quiesce */
3953227Syq193411 };
3963227Syq193411 
3973227Syq193411 extern struct mod_ops mod_driverops;
3983227Syq193411 /* modldrv structure */
3993227Syq193411 static struct modldrv modldrv = {
4003227Syq193411 	&mod_driverops,		/* type of module - driver */
4017425SGongtian.Zhao@Sun.COM 	"USB Serial CDC ACM driver",
4023227Syq193411 	&usbsacm_ops,
4033227Syq193411 };
4043227Syq193411 
4053227Syq193411 /* modlinkage structure */
4063227Syq193411 static struct modlinkage modlinkage = {
4073227Syq193411 	MODREV_1,
4083227Syq193411 	&modldrv,
4093227Syq193411 	NULL
4103227Syq193411 };
4113227Syq193411 
4123227Syq193411 static void	*usbsacm_statep;	/* soft state */
4133227Syq193411 
4143227Syq193411 /*
4153227Syq193411  * DSD definitions
4163227Syq193411  */
4177767SJohn.Beck@Sun.COM static ds_ops_t usbsacm_ds_ops = {
4183227Syq193411 	DS_OPS_VERSION,
4193227Syq193411 	usbsacm_ds_attach,
4203227Syq193411 	usbsacm_ds_detach,
4213227Syq193411 	usbsacm_ds_register_cb,
4223227Syq193411 	usbsacm_ds_unregister_cb,
4233227Syq193411 	usbsacm_ds_open_port,
4243227Syq193411 	usbsacm_ds_close_port,
4253227Syq193411 	usbsacm_ds_usb_power,
4263227Syq193411 	usbsacm_ds_suspend,
4273227Syq193411 	usbsacm_ds_resume,
4283227Syq193411 	usbsacm_ds_disconnect,
4293227Syq193411 	usbsacm_ds_reconnect,
4303227Syq193411 	usbsacm_ds_set_port_params,
4313227Syq193411 	usbsacm_ds_set_modem_ctl,
4323227Syq193411 	usbsacm_ds_get_modem_ctl,
4333227Syq193411 	usbsacm_ds_break_ctl,
4343227Syq193411 	NULL,			/* NULL if h/w doesn't support loopback */
4353227Syq193411 	usbsacm_ds_tx,
4363227Syq193411 	usbsacm_ds_rx,
4373227Syq193411 	usbsacm_ds_stop,
4383227Syq193411 	usbsacm_ds_start,
4393227Syq193411 	usbsacm_ds_fifo_flush,
4403227Syq193411 	usbsacm_ds_fifo_drain
4413227Syq193411 };
4423227Syq193411 
4433227Syq193411 /*
4443227Syq193411  * baud code -> baud rate (0 means unsupported rate)
4453227Syq193411  */
4463227Syq193411 static int usbsacm_speedtab[] = {
4473227Syq193411 	0,	/* B0 */
4483227Syq193411 	50,	/* B50 */
4493227Syq193411 	75,	/* B75 */
4503227Syq193411 	110,	/* B110 */
4513227Syq193411 	134,	/* B134 */
4523227Syq193411 	150,	/* B150 */
4533227Syq193411 	200,	/* B200 */
4543227Syq193411 	300,	/* B300 */
4553227Syq193411 	600,	/* B600 */
4563227Syq193411 	1200,	/* B1200 */
4573227Syq193411 	1800,	/* B1800 */
4583227Syq193411 	2400,	/* B2400 */
4593227Syq193411 	4800,	/* B4800 */
4603227Syq193411 	9600,	/* B9600 */
4613227Syq193411 	19200,	/* B19200 */
4623227Syq193411 	38400,	/* B38400 */
4633227Syq193411 	57600,	/* B57600 */
4643227Syq193411 	76800,	/* B76800 */
4653227Syq193411 	115200,	/* B115200 */
4663227Syq193411 	153600,	/* B153600 */
4673227Syq193411 	230400,	/* B230400 */
4683227Syq193411 	307200,	/* B307200 */
4699354STim.Marsland@Sun.COM 	460800,	/* B460800 */
4709354STim.Marsland@Sun.COM 	921600	/* B921600 */
4713227Syq193411 };
4723227Syq193411 
4733227Syq193411 
4743227Syq193411 static uint_t	usbsacm_errlevel = USB_LOG_L4;
4753227Syq193411 static uint_t	usbsacm_errmask = 0xffffffff;
4763227Syq193411 static uint_t	usbsacm_instance_debug = (uint_t)-1;
4773227Syq193411 
4783227Syq193411 
4793227Syq193411 /*
4803227Syq193411  * usbsacm driver's entry points
4813227Syq193411  * -----------------------------
4823227Syq193411  */
4833227Syq193411 /*
4843227Syq193411  * Module-wide initialization routine.
4853227Syq193411  */
4863227Syq193411 int
_init(void)4873227Syq193411 _init(void)
4883227Syq193411 {
4893227Syq193411 	int    error;
4903227Syq193411 
4913227Syq193411 	if ((error = mod_install(&modlinkage)) == 0) {
4923227Syq193411 
4933227Syq193411 		error = ddi_soft_state_init(&usbsacm_statep,
4943227Syq193411 		    usbser_soft_state_size(), 1);
4953227Syq193411 	}
4963227Syq193411 
4973227Syq193411 	return (error);
4983227Syq193411 }
4993227Syq193411 
5003227Syq193411 
5013227Syq193411 /*
5023227Syq193411  * Module-wide tear-down routine.
5033227Syq193411  */
5043227Syq193411 int
_fini(void)5053227Syq193411 _fini(void)
5063227Syq193411 {
5073227Syq193411 	int    error;
5083227Syq193411 
5093227Syq193411 	if ((error = mod_remove(&modlinkage)) == 0) {
5103227Syq193411 		ddi_soft_state_fini(&usbsacm_statep);
5113227Syq193411 	}
5123227Syq193411 
5133227Syq193411 	return (error);
5143227Syq193411 }
5153227Syq193411 
5163227Syq193411 
5173227Syq193411 int
_info(struct modinfo * modinfop)5183227Syq193411 _info(struct modinfo *modinfop)
5193227Syq193411 {
5203227Syq193411 	return (mod_info(&modlinkage, modinfop));
5213227Syq193411 }
5223227Syq193411 
5233227Syq193411 
5243227Syq193411 /*
5253227Syq193411  * Device configuration entry points
5263227Syq193411  */
5273227Syq193411 static int
usbsacm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)5283227Syq193411 usbsacm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
5293227Syq193411 {
5307767SJohn.Beck@Sun.COM 	return (usbser_attach(dip, cmd, usbsacm_statep, &usbsacm_ds_ops));
5313227Syq193411 }
5323227Syq193411 
5333227Syq193411 
5343227Syq193411 static int
usbsacm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)5353227Syq193411 usbsacm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
5363227Syq193411 {
5373227Syq193411 	return (usbser_detach(dip, cmd, usbsacm_statep));
5383227Syq193411 }
5393227Syq193411 
5403227Syq193411 
5413227Syq193411 int
usbsacm_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)5423227Syq193411 usbsacm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
5433227Syq193411 		void **result)
5443227Syq193411 {
5453227Syq193411 	return (usbser_getinfo(dip, infocmd, arg, result, usbsacm_statep));
5463227Syq193411 }
5473227Syq193411 
5483227Syq193411 
5493227Syq193411 static int
usbsacm_open(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr)5503227Syq193411 usbsacm_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
5513227Syq193411 {
5523227Syq193411 	return (usbser_open(rq, dev, flag, sflag, cr, usbsacm_statep));
5533227Syq193411 }
5543227Syq193411 
5553227Syq193411 /*
5563227Syq193411  * usbsacm_ds_detach:
5573227Syq193411  *	attach device instance, called from GSD attach
5583227Syq193411  *	initialize state and device, including:
5593227Syq193411  *		state variables, locks, device node
5603227Syq193411  *		device registration with system
5613227Syq193411  *		power management
5623227Syq193411  */
5633227Syq193411 static int
usbsacm_ds_attach(ds_attach_info_t * aip)5643227Syq193411 usbsacm_ds_attach(ds_attach_info_t *aip)
5653227Syq193411 {
5663227Syq193411 	usbsacm_state_t	*acmp;
5673227Syq193411 
5683227Syq193411 	acmp = (usbsacm_state_t *)kmem_zalloc(sizeof (usbsacm_state_t),
5693227Syq193411 	    KM_SLEEP);
5703227Syq193411 	acmp->acm_dip = aip->ai_dip;
5713227Syq193411 	acmp->acm_usb_events = aip->ai_usb_events;
5723227Syq193411 	acmp->acm_ports = NULL;
5733227Syq193411 	*aip->ai_hdl = (ds_hdl_t)acmp;
5743227Syq193411 
5753227Syq193411 	/* registers usbsacm with the USBA framework */
5763227Syq193411 	if (usb_client_attach(acmp->acm_dip, USBDRV_VERSION,
5773227Syq193411 	    0) != USB_SUCCESS) {
5783227Syq193411 
5793227Syq193411 		goto fail;
5803227Syq193411 	}
5813227Syq193411 
5823227Syq193411 	/* Get the configuration information of device */
5833227Syq193411 	if (usb_get_dev_data(acmp->acm_dip, &acmp->acm_dev_data,
5843227Syq193411 	    USB_PARSE_LVL_CFG, 0) != USB_SUCCESS) {
5853227Syq193411 
5863227Syq193411 		goto fail;
5873227Syq193411 	}
5883227Syq193411 	acmp->acm_def_ph = acmp->acm_dev_data->dev_default_ph;
5893227Syq193411 	acmp->acm_dev_state = USB_DEV_ONLINE;
5903227Syq193411 	mutex_init(&acmp->acm_mutex, NULL, MUTEX_DRIVER,
5913227Syq193411 	    acmp->acm_dev_data->dev_iblock_cookie);
5923227Syq193411 
5933227Syq193411 	acmp->acm_lh = usb_alloc_log_hdl(acmp->acm_dip, "usbsacm",
5943227Syq193411 	    &usbsacm_errlevel, &usbsacm_errmask, &usbsacm_instance_debug, 0);
5953227Syq193411 
5963227Syq193411 	/* Create power management components */
5973227Syq193411 	if (usbsacm_create_pm_components(acmp) != USB_SUCCESS) {
5983227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
5993227Syq193411 		    "usbsacm_ds_attach: create pm components failed.");
6003227Syq193411 
6013227Syq193411 		goto fail;
6023227Syq193411 	}
6033227Syq193411 
6043227Syq193411 	/* Register to get callbacks for USB events */
6053227Syq193411 	if (usb_register_event_cbs(acmp->acm_dip, acmp->acm_usb_events, 0)
6063227Syq193411 	    != USB_SUCCESS) {
6073227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
6083227Syq193411 		    "usbsacm_ds_attach: register event callback failed.");
6093227Syq193411 
6103227Syq193411 		goto fail;
6113227Syq193411 	}
6123227Syq193411 
6133227Syq193411 	/*
6143227Syq193411 	 * If devices conform to acm spec, driver will attach using class id;
6153227Syq193411 	 * if not, using device id.
6163227Syq193411 	 */
6173227Syq193411 	if ((strcmp(DEVI(acmp->acm_dip)->devi_binding_name,
6183227Syq193411 	    "usbif,class2.2") == 0) ||
6193227Syq193411 	    ((strcmp(DEVI(acmp->acm_dip)->devi_binding_name,
6203227Syq193411 	    "usb,class2.2.0") == 0))) {
6213227Syq193411 
6223227Syq193411 		acmp->acm_compatibility = B_TRUE;
6233227Syq193411 	} else {
6243227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
6253227Syq193411 		    "usbsacm_ds_attach: A nonstandard device is attaching to "
6263227Syq193411 		    "usbsacm driver. This device doesn't conform to "
6273227Syq193411 		    "usb cdc spec.");
6283227Syq193411 
6293227Syq193411 		acmp->acm_compatibility = B_FALSE;
6303227Syq193411 	}
6313227Syq193411 
6323227Syq193411 	/* initialize state variables */
6333227Syq193411 	if (usbsacm_init_alloc_ports(acmp) != USB_SUCCESS) {
6343227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
6353227Syq193411 		    "usbsacm_ds_attach: initialize port structure failed.");
6363227Syq193411 
6373227Syq193411 		goto fail;
6383227Syq193411 	}
6393227Syq193411 	*aip->ai_port_cnt = acmp->acm_port_cnt;
6403227Syq193411 
6413227Syq193411 	/* Get max data size of bulk transfer */
6423227Syq193411 	if (usb_pipe_get_max_bulk_transfer_size(acmp->acm_dip,
6433227Syq193411 	    &acmp->acm_xfer_sz) != USB_SUCCESS) {
6443227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
6453227Syq193411 		    "usbsacm_ds_attach: get max size of transfer failed.");
6463227Syq193411 
6473227Syq193411 		goto fail;
6483227Syq193411 	}
6493227Syq193411 
6503227Syq193411 	return (USB_SUCCESS);
6513227Syq193411 fail:
6523227Syq193411 	usbsacm_cleanup(acmp);
6533227Syq193411 
6543227Syq193411 	return (USB_FAILURE);
6553227Syq193411 }
6563227Syq193411 
6573227Syq193411 
6583227Syq193411 /*
6593227Syq193411  * usbsacm_ds_detach:
6603227Syq193411  *	detach device instance, called from GSD detach
6613227Syq193411  */
6623227Syq193411 static void
usbsacm_ds_detach(ds_hdl_t hdl)6633227Syq193411 usbsacm_ds_detach(ds_hdl_t hdl)
6643227Syq193411 {
6653227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
6663227Syq193411 
6673227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
6683227Syq193411 	    "usbsacm_ds_detach:");
6693227Syq193411 
6703227Syq193411 	usbsacm_close_pipes(acmp);
6713227Syq193411 	usbsacm_cleanup(acmp);
6723227Syq193411 }
6733227Syq193411 
6743227Syq193411 
6753227Syq193411 /*
6763227Syq193411  * usbsacm_ds_register_cb:
6773227Syq193411  *	GSD routine call ds_register_cb to register interrupt callbacks
6783227Syq193411  *	for the given port
6793227Syq193411  */
6803227Syq193411 /*ARGSUSED*/
6813227Syq193411 static int
usbsacm_ds_register_cb(ds_hdl_t hdl,uint_t port_num,ds_cb_t * cb)6823227Syq193411 usbsacm_ds_register_cb(ds_hdl_t hdl, uint_t port_num, ds_cb_t *cb)
6833227Syq193411 {
6843227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
6853227Syq193411 	usbsacm_port_t	*acm_port;
6863227Syq193411 
6873227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
6883227Syq193411 	    "usbsacm_ds_register_cb: acmp = 0x%p port_num = %d",
6896898Sfb209375 	    (void *)acmp, port_num);
6903227Syq193411 
6913227Syq193411 	/* Check if port number is greater than actual port number. */
6923227Syq193411 	if (port_num >= acmp->acm_port_cnt) {
6933227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
6943227Syq193411 		    "usbsacm_ds_register_cb: port number is wrong.");
6953227Syq193411 
6963227Syq193411 		return (USB_FAILURE);
6973227Syq193411 	}
6983227Syq193411 	acm_port = &acmp->acm_ports[port_num];
6993227Syq193411 	acm_port->acm_cb = *cb;
7003227Syq193411 
7013227Syq193411 	return (USB_SUCCESS);
7023227Syq193411 }
7033227Syq193411 
7043227Syq193411 
7053227Syq193411 /*
7063227Syq193411  * usbsacm_ds_unregister_cb:
7073227Syq193411  *	GSD routine call ds_unregister_cb to unregister
7083227Syq193411  *	interrupt callbacks for the given port
7093227Syq193411  */
7103227Syq193411 /*ARGSUSED*/
7113227Syq193411 static void
usbsacm_ds_unregister_cb(ds_hdl_t hdl,uint_t port_num)7123227Syq193411 usbsacm_ds_unregister_cb(ds_hdl_t hdl, uint_t port_num)
7133227Syq193411 {
7143227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
7153227Syq193411 	usbsacm_port_t	*acm_port;
7163227Syq193411 
7173227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
7183227Syq193411 	    "usbsacm_ds_unregister_cb: ");
7193227Syq193411 
7203227Syq193411 	if (port_num < acmp->acm_port_cnt) {
7213227Syq193411 		/* Release callback function */
7223227Syq193411 		acm_port = &acmp->acm_ports[port_num];
7233227Syq193411 		bzero(&acm_port->acm_cb, sizeof (acm_port->acm_cb));
7243227Syq193411 	}
7253227Syq193411 }
7263227Syq193411 
7273227Syq193411 
7283227Syq193411 /*
7293227Syq193411  * usbsacm_ds_open_port:
7303227Syq193411  *	GSD routine call ds_open_port
7313227Syq193411  *	to open the given port
7323227Syq193411  */
7333227Syq193411 /*ARGSUSED*/
7343227Syq193411 static int
usbsacm_ds_open_port(ds_hdl_t hdl,uint_t port_num)7353227Syq193411 usbsacm_ds_open_port(ds_hdl_t hdl, uint_t port_num)
7363227Syq193411 {
7373227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
7383227Syq193411 	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
7393227Syq193411 
7403227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
7413227Syq193411 	    "usbsacm_ds_open_port: port_num = %d", port_num);
7423227Syq193411 
7433227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
7443227Syq193411 	/* Check the status of the given port and device */
7453227Syq193411 	if ((acmp->acm_dev_state == USB_DEV_DISCONNECTED) ||
7463227Syq193411 	    (acm_port->acm_port_state != USBSACM_PORT_CLOSED)) {
7473227Syq193411 		mutex_exit(&acm_port->acm_port_mutex);
7483227Syq193411 
7493227Syq193411 		return (USB_FAILURE);
7503227Syq193411 	}
7513227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
7523227Syq193411 
7533227Syq193411 	usbsacm_pm_set_busy(acmp);
7543227Syq193411 
7553227Syq193411 	/* open pipes of port */
7563227Syq193411 	if (usbsacm_open_port_pipes(acm_port) != USB_SUCCESS) {
7573227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
7583227Syq193411 		    "usbsacm_ds_open_port: open pipes failed.");
7593227Syq193411 
7603227Syq193411 		return (USB_FAILURE);
7613227Syq193411 	}
7623227Syq193411 
7633227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
7643227Syq193411 	/* data receipt */
7653227Syq193411 	if (usbsacm_rx_start(acm_port) != USB_SUCCESS) {
7663227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
7673227Syq193411 		    "usbsacm_ds_open_port: start receive data failed.");
7683227Syq193411 		mutex_exit(&acm_port->acm_port_mutex);
7693227Syq193411 
7703227Syq193411 		return (USB_FAILURE);
7713227Syq193411 	}
7723227Syq193411 	acm_port->acm_port_state = USBSACM_PORT_OPEN;
7733227Syq193411 
7743227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
7753227Syq193411 
7763227Syq193411 	return (USB_SUCCESS);
7773227Syq193411 }
7783227Syq193411 
7793227Syq193411 
7803227Syq193411 /*
7813227Syq193411  * usbsacm_ds_close_port:
7823227Syq193411  *	GSD routine call ds_close_port
7833227Syq193411  *	to close the given port
7843227Syq193411  */
7853227Syq193411 /*ARGSUSED*/
7863227Syq193411 static int
usbsacm_ds_close_port(ds_hdl_t hdl,uint_t port_num)7873227Syq193411 usbsacm_ds_close_port(ds_hdl_t hdl, uint_t port_num)
7883227Syq193411 {
7893227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
7903227Syq193411 	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
7913227Syq193411 	int		rval = USB_SUCCESS;
7923227Syq193411 
7933227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
7946898Sfb209375 	    "usbsacm_ds_close_port: acmp = 0x%p", (void *)acmp);
7953227Syq193411 
7963227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
7973227Syq193411 	acm_port->acm_port_state = USBSACM_PORT_CLOSED;
7983227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
7993227Syq193411 
8003227Syq193411 	usbsacm_close_port_pipes(acm_port);
8013227Syq193411 
8023227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
8033227Syq193411 	rval = usbsacm_fifo_flush_locked(acmp, port_num, DS_TX | DS_RX);
8043227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
8053227Syq193411 
8063227Syq193411 	usbsacm_pm_set_idle(acmp);
8073227Syq193411 
8083227Syq193411 	return (rval);
8093227Syq193411 }
8103227Syq193411 
8113227Syq193411 
8123227Syq193411 /*
8133227Syq193411  * usbsacm_ds_usb_power:
8143227Syq193411  *	GSD routine call ds_usb_power
8153227Syq193411  *	to set power level of the component
8163227Syq193411  */
8173227Syq193411 /*ARGSUSED*/
8183227Syq193411 static int
usbsacm_ds_usb_power(ds_hdl_t hdl,int comp,int level,int * new_state)8193227Syq193411 usbsacm_ds_usb_power(ds_hdl_t hdl, int comp, int level, int *new_state)
8203227Syq193411 {
8213227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
8223227Syq193411 	usbsacm_pm_t	*pm = acmp->acm_pm;
8233227Syq193411 	int		rval = USB_SUCCESS;
8243227Syq193411 
8253227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
8263227Syq193411 	    "usbsacm_ds_usb_power: ");
8273227Syq193411 
8283227Syq193411 	/* check if pm is NULL */
8293227Syq193411 	if (pm == NULL) {
8303227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
8313227Syq193411 		    "usbsacm_ds_usb_power: pm is NULL.");
8323227Syq193411 
8333227Syq193411 		return (USB_FAILURE);
8343227Syq193411 	}
8353227Syq193411 
8363227Syq193411 	mutex_enter(&acmp->acm_mutex);
8373227Syq193411 	/*
8383227Syq193411 	 * check if we are transitioning to a legal power level
8393227Syq193411 	 */
8403227Syq193411 	if (USB_DEV_PWRSTATE_OK(pm->pm_pwr_states, level)) {
8413227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
8423227Syq193411 		    "usbsacm_ds_usb_power: "
8433227Syq193411 		    "illegal power level %d, pwr_states=%x",
8443227Syq193411 		    level, pm->pm_pwr_states);
8453227Syq193411 		mutex_exit(&acmp->acm_mutex);
8463227Syq193411 
8473227Syq193411 		return (USB_FAILURE);
8483227Syq193411 	}
8493227Syq193411 
8503227Syq193411 	/*
8513227Syq193411 	 * if we are about to raise power and asked to lower power, fail
8523227Syq193411 	 */
8533227Syq193411 	if (pm->pm_raise_power && (level < (int)pm->pm_cur_power)) {
8543227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
8553227Syq193411 		    "usbsacm_ds_usb_power: wrong condition.");
8563227Syq193411 		mutex_exit(&acmp->acm_mutex);
8573227Syq193411 
8583227Syq193411 		return (USB_FAILURE);
8593227Syq193411 	}
8603227Syq193411 
8613227Syq193411 	/*
8623227Syq193411 	 * Set the power status of device by request level.
8633227Syq193411 	 */
8643227Syq193411 	switch (level) {
8653227Syq193411 	case USB_DEV_OS_PWR_OFF:
8663227Syq193411 		rval = usbsacm_pwrlvl0(acmp);
8673227Syq193411 
8683227Syq193411 		break;
8693227Syq193411 	case USB_DEV_OS_PWR_1:
8703227Syq193411 		rval = usbsacm_pwrlvl1(acmp);
8713227Syq193411 
8723227Syq193411 		break;
8733227Syq193411 	case USB_DEV_OS_PWR_2:
8743227Syq193411 		rval = usbsacm_pwrlvl2(acmp);
8753227Syq193411 
8763227Syq193411 		break;
8773227Syq193411 	case USB_DEV_OS_FULL_PWR:
8783227Syq193411 		rval = usbsacm_pwrlvl3(acmp);
8795541Slg150142 		/*
8805541Slg150142 		 * If usbser dev_state is DISCONNECTED or SUSPENDED, it shows
8815541Slg150142 		 * that the usb serial device is disconnected/suspended while it
8825541Slg150142 		 * is under power down state, now the device is powered up
8835541Slg150142 		 * before it is reconnected/resumed. xxx_pwrlvl3() will set dev
8845541Slg150142 		 * state to ONLINE, we need to set the dev state back to
8855541Slg150142 		 * DISCONNECTED/SUSPENDED.
8865541Slg150142 		 */
8875541Slg150142 		if ((rval == USB_SUCCESS) &&
8885541Slg150142 		    ((*new_state == USB_DEV_DISCONNECTED) ||
8895541Slg150142 		    (*new_state == USB_DEV_SUSPENDED))) {
8905541Slg150142 			acmp->acm_dev_state = *new_state;
8915541Slg150142 		}
8923227Syq193411 
8933227Syq193411 		break;
8943227Syq193411 	}
8953227Syq193411 
8963227Syq193411 	*new_state = acmp->acm_dev_state;
8973227Syq193411 	mutex_exit(&acmp->acm_mutex);
8983227Syq193411 
8993227Syq193411 	return (rval);
9003227Syq193411 }
9013227Syq193411 
9023227Syq193411 
9033227Syq193411 /*
9043227Syq193411  * usbsacm_ds_suspend:
9053227Syq193411  *	GSD routine call ds_suspend
9063227Syq193411  *	during CPR suspend
9073227Syq193411  */
9083227Syq193411 static int
usbsacm_ds_suspend(ds_hdl_t hdl)9093227Syq193411 usbsacm_ds_suspend(ds_hdl_t hdl)
9103227Syq193411 {
9113227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
9125541Slg150142 	int		state = USB_DEV_SUSPENDED;
9133227Syq193411 
9143227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
9153227Syq193411 	    "usbsacm_ds_suspend: ");
9165541Slg150142 	/*
9175541Slg150142 	 * If the device is suspended while it is under PWRED_DOWN state, we
9185541Slg150142 	 * need to keep the PWRED_DOWN state so that it could be powered up
9195541Slg150142 	 * later. In the mean while, usbser dev state will be changed to
9205541Slg150142 	 * SUSPENDED state.
9215541Slg150142 	 */
9223227Syq193411 	mutex_enter(&acmp->acm_mutex);
9235541Slg150142 	if (acmp->acm_dev_state != USB_DEV_PWRED_DOWN) {
9245541Slg150142 		/* set device status to suspend */
9255541Slg150142 		acmp->acm_dev_state = USB_DEV_SUSPENDED;
9265541Slg150142 	}
9273227Syq193411 	mutex_exit(&acmp->acm_mutex);
9283227Syq193411 
9293227Syq193411 	usbsacm_disconnect_pipes(acmp);
9303227Syq193411 
9313227Syq193411 	return (state);
9323227Syq193411 }
9333227Syq193411 
9343227Syq193411 /*
9353227Syq193411  * usbsacm_ds_resume:
9363227Syq193411  *	GSD routine call ds_resume
9373227Syq193411  *	during CPR resume
9383227Syq193411  */
9393227Syq193411 /*ARGSUSED*/
9403227Syq193411 static int
usbsacm_ds_resume(ds_hdl_t hdl)9413227Syq193411 usbsacm_ds_resume(ds_hdl_t hdl)
9423227Syq193411 {
9433227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
9443227Syq193411 	int		current_state;
9453227Syq193411 	int		ret;
9463227Syq193411 
9473227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
9483227Syq193411 	    "usbsacm_ds_resume: ");
9493227Syq193411 
9503227Syq193411 	mutex_enter(&acmp->acm_mutex);
9513227Syq193411 	current_state = acmp->acm_dev_state;
9523227Syq193411 	mutex_exit(&acmp->acm_mutex);
9533227Syq193411 
9543227Syq193411 	/* restore the status of device */
9553227Syq193411 	if (current_state != USB_DEV_ONLINE) {
9563227Syq193411 		ret = usbsacm_restore_device_state(acmp);
9573227Syq193411 	} else {
9583227Syq193411 		ret = USB_DEV_ONLINE;
9593227Syq193411 	}
9603227Syq193411 
9613227Syq193411 	return (ret);
9623227Syq193411 }
9633227Syq193411 
9643227Syq193411 /*
9653227Syq193411  * usbsacm_ds_disconnect:
9663227Syq193411  *	GSD routine call ds_disconnect
9673227Syq193411  *	to disconnect USB device
9683227Syq193411  */
9693227Syq193411 static int
usbsacm_ds_disconnect(ds_hdl_t hdl)9703227Syq193411 usbsacm_ds_disconnect(ds_hdl_t hdl)
9713227Syq193411 {
9723227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
9735541Slg150142 	int		state = USB_DEV_DISCONNECTED;
9743227Syq193411 
9753227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
9763227Syq193411 	    "usbsacm_ds_disconnect: ");
9773227Syq193411 
9785541Slg150142 	/*
9795541Slg150142 	 * If the device is disconnected while it is under PWRED_DOWN state, we
9805541Slg150142 	 * need to keep the PWRED_DOWN state so that it could be powered up
9815541Slg150142 	 * later. In the mean while, usbser dev state will be changed to
9825541Slg150142 	 * DISCONNECTED state.
9835541Slg150142 	 */
9843227Syq193411 	mutex_enter(&acmp->acm_mutex);
9855541Slg150142 	if (acmp->acm_dev_state != USB_DEV_PWRED_DOWN) {
9865541Slg150142 		/* set device status to disconnected */
9875541Slg150142 		acmp->acm_dev_state = USB_DEV_DISCONNECTED;
9885541Slg150142 	}
9893227Syq193411 	mutex_exit(&acmp->acm_mutex);
9903227Syq193411 
9913227Syq193411 	usbsacm_disconnect_pipes(acmp);
9923227Syq193411 
9933227Syq193411 	return (state);
9943227Syq193411 }
9953227Syq193411 
9963227Syq193411 
9973227Syq193411 /*
9983227Syq193411  * usbsacm_ds_reconnect:
9993227Syq193411  *	GSD routine call ds_reconnect
10003227Syq193411  *	to reconnect USB device
10013227Syq193411  */
10023227Syq193411 /*ARGSUSED*/
10033227Syq193411 static int
usbsacm_ds_reconnect(ds_hdl_t hdl)10043227Syq193411 usbsacm_ds_reconnect(ds_hdl_t hdl)
10053227Syq193411 {
10063227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
10073227Syq193411 
10083227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
10093227Syq193411 	    "usbsacm_ds_reconnect: ");
10103227Syq193411 
10113227Syq193411 	return (usbsacm_restore_device_state(acmp));
10123227Syq193411 }
10133227Syq193411 
10143227Syq193411 
10153227Syq193411 /*
10163227Syq193411  * usbsacm_ds_set_port_params:
10173227Syq193411  *	GSD routine call ds_set_port_params
10183227Syq193411  *	to set one or more port parameters
10193227Syq193411  */
10203227Syq193411 /*ARGSUSED*/
10213227Syq193411 static int
usbsacm_ds_set_port_params(ds_hdl_t hdl,uint_t port_num,ds_port_params_t * tp)10223227Syq193411 usbsacm_ds_set_port_params(ds_hdl_t hdl, uint_t port_num, ds_port_params_t *tp)
10233227Syq193411 {
10243227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
10253227Syq193411 	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
10263227Syq193411 	int		i;
10273227Syq193411 	uint_t		ui;
10283227Syq193411 	ds_port_param_entry_t *pe;
10293227Syq193411 	usb_cdc_line_coding_t lc;
10303227Syq193411 	int		ret;
10313227Syq193411 
10323227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
10336898Sfb209375 	    "usbsacm_ds_set_port_params: acmp = 0x%p", (void *)acmp);
10343227Syq193411 
10353227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
10363227Syq193411 	/*
10373227Syq193411 	 * If device conform to acm spec, check if it support to set port param.
10383227Syq193411 	 */
10393227Syq193411 	if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SERIAL_LINE) == 0 &&
10403227Syq193411 	    acmp->acm_compatibility == B_TRUE) {
10413227Syq193411 
10423227Syq193411 		mutex_exit(&acm_port->acm_port_mutex);
10433227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
10443227Syq193411 		    "usbsacm_ds_set_port_params: "
10453227Syq193411 		    "don't support Set_Line_Coding.");
10463227Syq193411 
10473227Syq193411 		return (USB_FAILURE);
10483227Syq193411 	}
10493227Syq193411 
10503227Syq193411 	lc = acm_port->acm_line_coding;
10513227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
10523227Syq193411 	pe = tp->tp_entries;
10533227Syq193411 	/* Get parameter information from ds_port_params_t */
10543227Syq193411 	for (i = 0; i < tp->tp_cnt; i++, pe++) {
10553227Syq193411 		switch (pe->param) {
10563227Syq193411 		case DS_PARAM_BAUD:
10573227Syq193411 			/* Data terminal rate, in bits per second. */
10583227Syq193411 			ui = pe->val.ui;
10593227Syq193411 
10603227Syq193411 			/* if we don't support this speed, return USB_FAILURE */
10613227Syq193411 			if ((ui >= NELEM(usbsacm_speedtab)) ||
10623227Syq193411 			    ((ui > 0) && (usbsacm_speedtab[ui] == 0))) {
10633227Syq193411 				USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
10643227Syq193411 				    "usbsacm_ds_set_port_params: "
10653227Syq193411 				    " error baud rate");
10663227Syq193411 
10673227Syq193411 				return (USB_FAILURE);
10683227Syq193411 			}
10693227Syq193411 			lc.dwDTERate = LE_32(usbsacm_speedtab[ui]);
10703227Syq193411 
10713227Syq193411 			break;
10723227Syq193411 		case DS_PARAM_PARITY:
10733227Syq193411 			/* Parity Type */
10743227Syq193411 			if (pe->val.ui & PARENB) {
10753227Syq193411 				if (pe->val.ui & PARODD) {
10763227Syq193411 					lc.bParityType = USB_CDC_PARITY_ODD;
10773227Syq193411 				} else {
10783227Syq193411 					lc.bParityType = USB_CDC_PARITY_EVEN;
10793227Syq193411 				}
10803227Syq193411 			} else {
10813227Syq193411 				lc.bParityType = USB_CDC_PARITY_NO;
10823227Syq193411 			}
10833227Syq193411 
10843227Syq193411 			break;
10853227Syq193411 		case DS_PARAM_STOPB:
10863227Syq193411 			/* Stop bit */
10873227Syq193411 			if (pe->val.ui & CSTOPB) {
10883227Syq193411 				lc.bCharFormat = USB_CDC_STOP_BITS_2;
10893227Syq193411 			} else {
10903227Syq193411 				lc.bCharFormat = USB_CDC_STOP_BITS_1;
10913227Syq193411 			}
10923227Syq193411 
10933227Syq193411 			break;
10943227Syq193411 		case DS_PARAM_CHARSZ:
10953227Syq193411 			/* Data Bits */
10963227Syq193411 			switch (pe->val.ui) {
10973227Syq193411 			case CS5:
10983227Syq193411 				lc.bDataBits = 5;
10993227Syq193411 				break;
11003227Syq193411 			case CS6:
11013227Syq193411 				lc.bDataBits = 6;
11023227Syq193411 				break;
11033227Syq193411 			case CS7:
11043227Syq193411 				lc.bDataBits = 7;
11053227Syq193411 				break;
11063227Syq193411 			case CS8:
11073227Syq193411 			default:
11083227Syq193411 				lc.bDataBits = 8;
11093227Syq193411 				break;
11103227Syq193411 			}
11113227Syq193411 
11123227Syq193411 			break;
11133227Syq193411 		default:
11143227Syq193411 			USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
11153227Syq193411 			    "usbsacm_ds_set_port_params: "
11163227Syq193411 			    "parameter 0x%x isn't supported",
11173227Syq193411 			    pe->param);
11183227Syq193411 
11193227Syq193411 			break;
11203227Syq193411 		}
11213227Syq193411 	}
11223227Syq193411 
11233227Syq193411 	if ((ret = usbsacm_set_line_coding(acm_port, &lc)) == USB_SUCCESS) {
11243227Syq193411 		mutex_enter(&acm_port->acm_port_mutex);
11253227Syq193411 		acm_port->acm_line_coding = lc;
11263227Syq193411 		mutex_exit(&acm_port->acm_port_mutex);
11273227Syq193411 	}
11283227Syq193411 
11293227Syq193411 	/*
11303227Syq193411 	 * If device don't conform to acm spec, return success directly.
11313227Syq193411 	 */
11323227Syq193411 	if (acmp->acm_compatibility != B_TRUE) {
11333227Syq193411 		ret = USB_SUCCESS;
11343227Syq193411 	}
11353227Syq193411 
11363227Syq193411 	return (ret);
11373227Syq193411 }
11383227Syq193411 
11393227Syq193411 
11403227Syq193411 /*
11413227Syq193411  * usbsacm_ds_set_modem_ctl:
11423227Syq193411  *	GSD routine call ds_set_modem_ctl
11433227Syq193411  *	to set modem control of the given port
11443227Syq193411  */
11453227Syq193411 /*ARGSUSED*/
11463227Syq193411 static int
usbsacm_ds_set_modem_ctl(ds_hdl_t hdl,uint_t port_num,int mask,int val)11473227Syq193411 usbsacm_ds_set_modem_ctl(ds_hdl_t hdl, uint_t port_num, int mask, int val)
11483227Syq193411 {
11493227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
11503227Syq193411 	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
11513227Syq193411 	uint8_t		new_mctl;
11523227Syq193411 	int		ret;
11533227Syq193411 
11543227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
11553227Syq193411 	    "usbsacm_ds_set_modem_ctl: mask = 0x%x val = 0x%x",
11563227Syq193411 	    mask, val);
11573227Syq193411 
11583227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
11593227Syq193411 	/*
11603227Syq193411 	 * If device conform to acm spec, check if it support to set modem
11613227Syq193411 	 * controls.
11623227Syq193411 	 */
11633227Syq193411 	if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SERIAL_LINE) == 0 &&
11645541Slg150142 	    acmp->acm_compatibility == B_TRUE) {
11653227Syq193411 
11663227Syq193411 		mutex_exit(&acm_port->acm_port_mutex);
11673227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
11683227Syq193411 		    "usbsacm_ds_set_modem_ctl: "
11693227Syq193411 		    "don't support Set_Control_Line_State.");
11703227Syq193411 
11713227Syq193411 		return (USB_FAILURE);
11723227Syq193411 	}
11733227Syq193411 
11743227Syq193411 	new_mctl = acm_port->acm_mctlout;
11753227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
11763227Syq193411 
11773227Syq193411 	usbsacm_mctl2reg(mask, val, &new_mctl);
11783227Syq193411 
11793227Syq193411 	if ((acmp->acm_compatibility == B_FALSE) || ((ret =
11803227Syq193411 	    usbsacm_req_write(acm_port, USB_CDC_REQ_SET_CONTROL_LINE_STATE,
11813227Syq193411 	    new_mctl, NULL)) == USB_SUCCESS)) {
11823227Syq193411 		mutex_enter(&acm_port->acm_port_mutex);
11833227Syq193411 		acm_port->acm_mctlout = new_mctl;
11843227Syq193411 		mutex_exit(&acm_port->acm_port_mutex);
11853227Syq193411 	}
11863227Syq193411 
11873227Syq193411 	/*
11883227Syq193411 	 * If device don't conform to acm spec, return success directly.
11893227Syq193411 	 */
11903227Syq193411 	if (acmp->acm_compatibility != B_TRUE) {
11913227Syq193411 		ret = USB_SUCCESS;
11923227Syq193411 	}
11933227Syq193411 
11943227Syq193411 	return (ret);
11953227Syq193411 }
11963227Syq193411 
11973227Syq193411 
11983227Syq193411 /*
11993227Syq193411  * usbsacm_ds_get_modem_ctl:
12003227Syq193411  *	GSD routine call ds_get_modem_ctl
12013227Syq193411  *	to get modem control/status of the given port
12023227Syq193411  */
12033227Syq193411 /*ARGSUSED*/
12043227Syq193411 static int
usbsacm_ds_get_modem_ctl(ds_hdl_t hdl,uint_t port_num,int mask,int * valp)12053227Syq193411 usbsacm_ds_get_modem_ctl(ds_hdl_t hdl, uint_t port_num, int mask, int *valp)
12063227Syq193411 {
12073227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
12083227Syq193411 	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
12093227Syq193411 
12103227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
12113227Syq193411 	*valp = usbsacm_reg2mctl(acm_port->acm_mctlout) & mask;
12123227Syq193411 	/*
12133227Syq193411 	 * If device conform to acm spec, polling function can modify the value
12143227Syq193411 	 * of acm_mctlin; else set to default value.
12153227Syq193411 	 */
12163227Syq193411 	if (acmp->acm_compatibility) {
12173227Syq193411 		*valp |= usbsacm_reg2mctl(acm_port->acm_mctlin) & mask;
12183227Syq193411 		*valp |= (mask & (TIOCM_CD | TIOCM_CTS));
12193227Syq193411 	} else {
12203227Syq193411 		*valp |= (mask & (TIOCM_CD | TIOCM_CTS | TIOCM_DSR | TIOCM_RI));
12213227Syq193411 	}
12223227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
12233227Syq193411 
12243227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
12253227Syq193411 	    "usbsacm_ds_get_modem_ctl: val = 0x%x", *valp);
12263227Syq193411 
12273227Syq193411 	return (USB_SUCCESS);
12283227Syq193411 }
12293227Syq193411 
12303227Syq193411 
12313227Syq193411 /*
12323227Syq193411  * usbsacm_ds_tx:
12333227Syq193411  *	GSD routine call ds_break_ctl
12343227Syq193411  *	to set/clear break
12353227Syq193411  */
12363227Syq193411 /*ARGSUSED*/
12373227Syq193411 static int
usbsacm_ds_break_ctl(ds_hdl_t hdl,uint_t port_num,int ctl)12383227Syq193411 usbsacm_ds_break_ctl(ds_hdl_t hdl, uint_t port_num, int ctl)
12393227Syq193411 {
12403227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
12413227Syq193411 	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
12423227Syq193411 
12433227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
12443227Syq193411 	    "usbsacm_ds_break_ctl: ");
12453227Syq193411 
12463227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
12473227Syq193411 	/*
12483227Syq193411 	 * If device conform to acm spec, check if it support to send break.
12493227Syq193411 	 */
12503227Syq193411 	if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SEND_BREAK) == 0 &&
12515541Slg150142 	    acmp->acm_compatibility == B_TRUE) {
12523227Syq193411 
12533227Syq193411 		mutex_exit(&acm_port->acm_port_mutex);
12543227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
12553227Syq193411 		    "usbsacm_ds_break_ctl: don't support send break.");
12563227Syq193411 
12573227Syq193411 		return (USB_FAILURE);
12583227Syq193411 	}
12593227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
12603227Syq193411 
12613227Syq193411 	return (usbsacm_req_write(acm_port, USB_CDC_REQ_SEND_BREAK,
12625541Slg150142 	    ((ctl == DS_ON) ? 0xffff : 0), NULL));
12633227Syq193411 }
12643227Syq193411 
12653227Syq193411 
12663227Syq193411 /*
12673227Syq193411  * usbsacm_ds_tx:
12683227Syq193411  *	GSD routine call ds_tx
12693227Syq193411  *	to data transmit
12703227Syq193411  */
12713227Syq193411 /*ARGSUSED*/
12723227Syq193411 static int
usbsacm_ds_tx(ds_hdl_t hdl,uint_t port_num,mblk_t * mp)12733227Syq193411 usbsacm_ds_tx(ds_hdl_t hdl, uint_t port_num, mblk_t *mp)
12743227Syq193411 {
12753227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
12763227Syq193411 	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
12773227Syq193411 
12783227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
12796898Sfb209375 	    "usbsacm_ds_tx: mp = 0x%p acmp = 0x%p", (void *)mp, (void *)acmp);
12803227Syq193411 
12813227Syq193411 	/* sanity checks */
12823227Syq193411 	if (mp == NULL) {
12833227Syq193411 
12843227Syq193411 		return (USB_SUCCESS);
12853227Syq193411 	}
12866990Sgd78059 	if (MBLKL(mp) < 1) {
12873227Syq193411 		freemsg(mp);
12883227Syq193411 
12893227Syq193411 		return (USB_SUCCESS);
12903227Syq193411 	}
12913227Syq193411 
12923227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
12933227Syq193411 	/* put mblk to tail of mblk chain */
12943227Syq193411 	usbsacm_put_tail(&acm_port->acm_tx_mp, mp);
12953227Syq193411 	usbsacm_tx_start(acm_port);
12963227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
12973227Syq193411 
12983227Syq193411 	return (USB_SUCCESS);
12993227Syq193411 }
13003227Syq193411 
13013227Syq193411 
13023227Syq193411 /*
13033227Syq193411  * usbsacm_ds_rx:
13043227Syq193411  *	GSD routine call ds_rx;
13053227Syq193411  *	to data receipt
13063227Syq193411  */
13073227Syq193411 /*ARGSUSED*/
13083227Syq193411 static mblk_t *
usbsacm_ds_rx(ds_hdl_t hdl,uint_t port_num)13093227Syq193411 usbsacm_ds_rx(ds_hdl_t hdl, uint_t port_num)
13103227Syq193411 {
13113227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
13123227Syq193411 	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
13133227Syq193411 	mblk_t		*mp;
13143227Syq193411 
13153227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
13166898Sfb209375 	    "usbsacm_ds_rx: acmp = 0x%p", (void *)acmp);
13173227Syq193411 
13183227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
13193227Syq193411 
13203227Syq193411 	mp = acm_port->acm_rx_mp;
13213227Syq193411 	acm_port->acm_rx_mp = NULL;
13223227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
13233227Syq193411 
13243227Syq193411 	return (mp);
13253227Syq193411 }
13263227Syq193411 
13273227Syq193411 
13283227Syq193411 /*
13293227Syq193411  * usbsacm_ds_stop:
13303227Syq193411  *	GSD routine call ds_stop;
13313227Syq193411  *	but acm spec don't define this function
13323227Syq193411  */
13333227Syq193411 /*ARGSUSED*/
13343227Syq193411 static void
usbsacm_ds_stop(ds_hdl_t hdl,uint_t port_num,int dir)13353227Syq193411 usbsacm_ds_stop(ds_hdl_t hdl, uint_t port_num, int dir)
13363227Syq193411 {
13373227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
13383227Syq193411 
13393227Syq193411 	USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
13403227Syq193411 	    "usbsacm_ds_stop: don't support!");
13413227Syq193411 }
13423227Syq193411 
13433227Syq193411 
13443227Syq193411 /*
13453227Syq193411  * usbsacm_ds_start:
13463227Syq193411  *	GSD routine call ds_start;
13473227Syq193411  *	but acm spec don't define this function
13483227Syq193411  */
13493227Syq193411 /*ARGSUSED*/
13503227Syq193411 static void
usbsacm_ds_start(ds_hdl_t hdl,uint_t port_num,int dir)13513227Syq193411 usbsacm_ds_start(ds_hdl_t hdl, uint_t port_num, int dir)
13523227Syq193411 {
13533227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
13543227Syq193411 
13553227Syq193411 	USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
13563227Syq193411 	    "usbsacm_ds_start: don't support!");
13573227Syq193411 }
13583227Syq193411 
13593227Syq193411 
13603227Syq193411 /*
13613227Syq193411  * usbsacm_ds_fifo_flush:
13623227Syq193411  *	GSD routine call ds_fifo_flush
13633227Syq193411  *	to flush FIFOs
13643227Syq193411  */
13653227Syq193411 /*ARGSUSED*/
13663227Syq193411 static int
usbsacm_ds_fifo_flush(ds_hdl_t hdl,uint_t port_num,int dir)13673227Syq193411 usbsacm_ds_fifo_flush(ds_hdl_t hdl, uint_t port_num, int dir)
13683227Syq193411 {
13693227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
13703227Syq193411 	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
13713227Syq193411 	int		ret = USB_SUCCESS;
13723227Syq193411 
13733227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
13743227Syq193411 	    "usbsacm_ds_fifo_flush: ");
13753227Syq193411 
13763227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
13773227Syq193411 	ret = usbsacm_fifo_flush_locked(acmp, port_num, dir);
13783227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
13793227Syq193411 
13803227Syq193411 	return (ret);
13813227Syq193411 }
13823227Syq193411 
13833227Syq193411 
13843227Syq193411 /*
13853227Syq193411  * usbsacm_ds_fifo_drain:
13863227Syq193411  *	GSD routine call ds_fifo_drain
13873227Syq193411  *	to wait until empty output FIFO
13883227Syq193411  */
13893227Syq193411 /*ARGSUSED*/
13903227Syq193411 static int
usbsacm_ds_fifo_drain(ds_hdl_t hdl,uint_t port_num,int timeout)13913227Syq193411 usbsacm_ds_fifo_drain(ds_hdl_t hdl, uint_t port_num, int timeout)
13923227Syq193411 {
13933227Syq193411 	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
13943227Syq193411 	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
13953227Syq193411 	int		rval = USB_SUCCESS;
13963227Syq193411 
13973227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
13983227Syq193411 	    "usbsacm_ds_fifo_drain: ");
13993227Syq193411 
14003227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
14013227Syq193411 	ASSERT(acm_port->acm_port_state == USBSACM_PORT_OPEN);
14023227Syq193411 
14033227Syq193411 	if (usbsacm_wait_tx_drain(acm_port, timeout) != USB_SUCCESS) {
14043227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
14053227Syq193411 		    "usbsacm_ds_fifo_drain: fifo drain failed.");
14063227Syq193411 		mutex_exit(&acm_port->acm_port_mutex);
14073227Syq193411 
14083227Syq193411 		return (USB_FAILURE);
14093227Syq193411 	}
14103227Syq193411 
14113227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
14123227Syq193411 
14133227Syq193411 	return (rval);
14143227Syq193411 }
14153227Syq193411 
14163227Syq193411 
14173227Syq193411 /*
14183227Syq193411  * usbsacm_fifo_flush_locked:
14193227Syq193411  *	flush FIFOs of the given ports
14203227Syq193411  */
14213227Syq193411 /*ARGSUSED*/
14223227Syq193411 static int
usbsacm_fifo_flush_locked(usbsacm_state_t * acmp,uint_t port_num,int dir)14233227Syq193411 usbsacm_fifo_flush_locked(usbsacm_state_t *acmp, uint_t port_num, int dir)
14243227Syq193411 {
14253227Syq193411 	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
14263227Syq193411 
14273227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
14283227Syq193411 	    "usbsacm_fifo_flush_locked: ");
14293227Syq193411 
14303227Syq193411 	/* flush transmit FIFO if DS_TX is set */
14313227Syq193411 	if ((dir & DS_TX) && acm_port->acm_tx_mp) {
14323227Syq193411 		freemsg(acm_port->acm_tx_mp);
14333227Syq193411 		acm_port->acm_tx_mp = NULL;
14343227Syq193411 	}
14353227Syq193411 	/* flush received FIFO if DS_RX is set */
14363227Syq193411 	if ((dir & DS_RX) && acm_port->acm_rx_mp) {
14373227Syq193411 		freemsg(acm_port->acm_rx_mp);
14383227Syq193411 		acm_port->acm_rx_mp = NULL;
14393227Syq193411 	}
14403227Syq193411 
14413227Syq193411 	return (USB_SUCCESS);
14423227Syq193411 }
14433227Syq193411 
14443227Syq193411 
14453227Syq193411 /*
14463227Syq193411  * usbsacm_get_bulk_pipe_number:
14473227Syq193411  *	Calculate the number of bulk in or out pipes in current device.
14483227Syq193411  */
14493227Syq193411 static int
usbsacm_get_bulk_pipe_number(usbsacm_state_t * acmp,uint_t dir)14503227Syq193411 usbsacm_get_bulk_pipe_number(usbsacm_state_t *acmp, uint_t dir)
14513227Syq193411 {
14523227Syq193411 	int		count = 0;
14533227Syq193411 	int		i, skip;
14543227Syq193411 	usb_if_data_t	*cur_if;
14553227Syq193411 	int		ep_num;
14563227Syq193411 	int		if_num;
14573227Syq193411 
14583227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
14593227Syq193411 	    "usbsacm_get_bulk_pipe_number: ");
14603227Syq193411 
14613227Syq193411 	cur_if = acmp->acm_dev_data->dev_curr_cfg->cfg_if;
14623227Syq193411 	if_num = acmp->acm_dev_data->dev_curr_cfg->cfg_n_if;
14633227Syq193411 
14643227Syq193411 	/* search each interface which have bulk endpoint */
14653227Syq193411 	for (i = 0; i < if_num; i++) {
14663227Syq193411 		ep_num = cur_if->if_alt->altif_n_ep;
14673227Syq193411 
14683227Syq193411 		/*
14693227Syq193411 		 * search endpoints in current interface,
14703227Syq193411 		 * which type is input parameter 'dir'
14713227Syq193411 		 */
14723227Syq193411 		for (skip = 0; skip < ep_num; skip++) {
14735541Slg150142 			if (usb_lookup_ep_data(acmp->acm_dip,
1474*12024SRaymond.Chen@Sun.COM 			    acmp->acm_dev_data, i, 0, skip,
14755541Slg150142 			    USB_EP_ATTR_BULK, dir) == NULL) {
14765541Slg150142 
14775541Slg150142 				/*
14785541Slg150142 				 * If not found, skip the internal loop
14795541Slg150142 				 * and search the next interface.
14805541Slg150142 				 */
14815541Slg150142 				break;
14825541Slg150142 			}
14835541Slg150142 			count++;
14843227Syq193411 		}
14853227Syq193411 
14863227Syq193411 		cur_if++;
14873227Syq193411 	}
14883227Syq193411 
14893227Syq193411 	return (count);
14903227Syq193411 }
14913227Syq193411 
14923227Syq193411 
14933227Syq193411 /*
14943227Syq193411  * port management
14953227Syq193411  * ---------------
14963227Syq193411  *	initialize, release port.
14973227Syq193411  *
14983227Syq193411  *
14993227Syq193411  * usbsacm_init_ports_status:
15003227Syq193411  *	Initialize the port status for the current device.
15013227Syq193411  */
15023227Syq193411 static int
usbsacm_init_ports_status(usbsacm_state_t * acmp)15033227Syq193411 usbsacm_init_ports_status(usbsacm_state_t *acmp)
15043227Syq193411 {
15053227Syq193411 	usbsacm_port_t	*cur_port;
15063227Syq193411 	int		i, skip;
15073227Syq193411 	int		if_num;
15083227Syq193411 	int		intr_if_no = 0;
15093227Syq193411 	int		ep_num;
15103227Syq193411 	usb_if_data_t	*cur_if;
15113227Syq193411 
15123227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
15136898Sfb209375 	    "usbsacm_init_ports_status: acmp = 0x%p", (void *)acmp);
15143227Syq193411 
15153227Syq193411 	/* Initialize the port status to default value */
15163227Syq193411 	for (i = 0; i < acmp->acm_port_cnt; i++) {
15173227Syq193411 		cur_port = &acmp->acm_ports[i];
15183227Syq193411 
15193227Syq193411 		cv_init(&cur_port->acm_tx_cv, NULL, CV_DRIVER, NULL);
15203227Syq193411 
15213227Syq193411 		cur_port->acm_port_state = USBSACM_PORT_CLOSED;
15223227Syq193411 
15233227Syq193411 		cur_port->acm_line_coding.dwDTERate = LE_32((uint32_t)9600);
15243227Syq193411 		cur_port->acm_line_coding.bCharFormat = 0;
15253227Syq193411 		cur_port->acm_line_coding.bParityType = USB_CDC_PARITY_NO;
15263227Syq193411 		cur_port->acm_line_coding.bDataBits = 8;
15273227Syq193411 		cur_port->acm_device = acmp;
15283227Syq193411 		mutex_init(&cur_port->acm_port_mutex, NULL, MUTEX_DRIVER,
15293227Syq193411 		    acmp->acm_dev_data->dev_iblock_cookie);
15303227Syq193411 	}
15313227Syq193411 
15323227Syq193411 	/*
15333227Syq193411 	 * If device conform to cdc acm spec, parse function descriptors.
15343227Syq193411 	 */
15353227Syq193411 	if (acmp->acm_compatibility == B_TRUE) {
15363227Syq193411 
15373227Syq193411 		if (usbsacm_get_descriptors(acmp) != USB_SUCCESS) {
15383227Syq193411 
15393227Syq193411 			return (USB_FAILURE);
15403227Syq193411 		}
15413227Syq193411 
15423227Syq193411 		return (USB_SUCCESS);
15433227Syq193411 	}
15443227Syq193411 
15453227Syq193411 	/*
15463227Syq193411 	 * If device don't conform to spec, search pairs of bulk in/out
15473227Syq193411 	 * endpoints and fill port structure.
15483227Syq193411 	 */
15493227Syq193411 	cur_if = acmp->acm_dev_data->dev_curr_cfg->cfg_if;
15503227Syq193411 	if_num = acmp->acm_dev_data->dev_curr_cfg->cfg_n_if;
15513227Syq193411 	cur_port = acmp->acm_ports;
15523227Syq193411 
15533227Syq193411 	/* search each interface which have bulk in and out */
15543227Syq193411 	for (i = 0; i < if_num; i++) {
15555541Slg150142 		ep_num = cur_if->if_alt->altif_n_ep;
15565541Slg150142 
15575541Slg150142 		for (skip = 0; skip < ep_num; skip++) {
15583227Syq193411 
15593227Syq193411 		/* search interrupt pipe. */
15603227Syq193411 		if ((usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
15613227Syq193411 		    i, 0, skip, USB_EP_ATTR_INTR, USB_EP_DIR_IN) != NULL)) {
15623227Syq193411 
15633227Syq193411 			intr_if_no = i;
15643227Syq193411 		}
15653227Syq193411 
15663227Syq193411 		/* search pair of bulk in/out endpoints. */
15673227Syq193411 		if ((usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
15683227Syq193411 		    i, 0, skip, USB_EP_ATTR_BULK, USB_EP_DIR_IN) == NULL) ||
15693227Syq193411 		    (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
15703227Syq193411 		    i, 0, skip, USB_EP_ATTR_BULK, USB_EP_DIR_OUT) == NULL)) {
15713227Syq193411 
15723227Syq193411 			continue;
15733227Syq193411 		}
15743227Syq193411 
15753227Syq193411 		cur_port->acm_data_if_no = i;
15763227Syq193411 		cur_port->acm_ctrl_if_no = intr_if_no;
15773227Syq193411 		cur_port->acm_data_port_no = skip;
15783227Syq193411 		cur_port++;
15793227Syq193411 		intr_if_no = 0;
15805541Slg150142 		}
15815541Slg150142 
15825541Slg150142 		cur_if++;
15833227Syq193411 	}
15843227Syq193411 
15853227Syq193411 	return (USB_SUCCESS);
15863227Syq193411 }
15873227Syq193411 
15883227Syq193411 
15893227Syq193411 /*
15903227Syq193411  * usbsacm_init_alloc_ports:
15913227Syq193411  *	Allocate memory and initialize the port state for the current device.
15923227Syq193411  */
15933227Syq193411 static int
usbsacm_init_alloc_ports(usbsacm_state_t * acmp)15943227Syq193411 usbsacm_init_alloc_ports(usbsacm_state_t *acmp)
15953227Syq193411 {
15965541Slg150142 	int		rval = USB_SUCCESS;
15973227Syq193411 	int		count_in = 0, count_out = 0;
15983227Syq193411 
15993227Syq193411 	if (acmp->acm_compatibility) {
16003227Syq193411 		acmp->acm_port_cnt = 1;
16013227Syq193411 	} else {
16023227Syq193411 		/* Calculate the number of the bulk in/out endpoints */
16033227Syq193411 		count_in = usbsacm_get_bulk_pipe_number(acmp, USB_EP_DIR_IN);
16043227Syq193411 		count_out = usbsacm_get_bulk_pipe_number(acmp, USB_EP_DIR_OUT);
16053227Syq193411 
16063227Syq193411 		USB_DPRINTF_L3(PRINT_MASK_OPEN, acmp->acm_lh,
16073227Syq193411 		    "usbsacm_init_alloc_ports: count_in = %d, count_out = %d",
16083227Syq193411 		    count_in, count_out);
16093227Syq193411 
16103227Syq193411 		acmp->acm_port_cnt = min(count_in, count_out);
16113227Syq193411 	}
16123227Syq193411 
16133227Syq193411 	/* return if not found any pair of bulk in/out endpoint. */
16143227Syq193411 	if (acmp->acm_port_cnt == 0) {
16153227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
16163227Syq193411 		    "usbsacm_init_alloc_ports: port count is zero.");
16173227Syq193411 
16183227Syq193411 		return (USB_FAILURE);
16193227Syq193411 	}
16203227Syq193411 
16213227Syq193411 	/* allocate memory for ports */
16223227Syq193411 	acmp->acm_ports = (usbsacm_port_t *)kmem_zalloc(acmp->acm_port_cnt *
16233227Syq193411 	    sizeof (usbsacm_port_t), KM_SLEEP);
16243227Syq193411 	if (acmp->acm_ports == NULL) {
16253227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
16263227Syq193411 		    "usbsacm_init_alloc_ports: allocate memory failed.");
16273227Syq193411 
16283227Syq193411 		return (USB_FAILURE);
16293227Syq193411 	}
16303227Syq193411 
16313227Syq193411 	/* fill the status of port structure. */
16323227Syq193411 	rval = usbsacm_init_ports_status(acmp);
16333227Syq193411 	if (rval != USB_SUCCESS) {
16343227Syq193411 		usbsacm_free_ports(acmp);
16353227Syq193411 	}
16363227Syq193411 
16373227Syq193411 	return (rval);
16383227Syq193411 }
16393227Syq193411 
16403227Syq193411 
16413227Syq193411 /*
16423227Syq193411  * usbsacm_free_ports:
16433227Syq193411  *	Release ports and deallocate memory.
16443227Syq193411  */
16453227Syq193411 static void
usbsacm_free_ports(usbsacm_state_t * acmp)16463227Syq193411 usbsacm_free_ports(usbsacm_state_t *acmp)
16473227Syq193411 {
16483227Syq193411 	int		i;
16493227Syq193411 
16503227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
16513227Syq193411 	    "usbsacm_free_ports: ");
16523227Syq193411 
16533227Syq193411 	/* Release memory and data structure for each port */
16543227Syq193411 	for (i = 0; i < acmp->acm_port_cnt; i++) {
16553227Syq193411 		cv_destroy(&acmp->acm_ports[i].acm_tx_cv);
16563227Syq193411 		mutex_destroy(&acmp->acm_ports[i].acm_port_mutex);
16573227Syq193411 	}
16583227Syq193411 	kmem_free((caddr_t)acmp->acm_ports, sizeof (usbsacm_port_t) *
16593227Syq193411 	    acmp->acm_port_cnt);
16603227Syq193411 	acmp->acm_ports = NULL;
16613227Syq193411 }
16623227Syq193411 
16633227Syq193411 
16643227Syq193411 /*
16653227Syq193411  * usbsacm_get_descriptors:
16663227Syq193411  *	analysis functional descriptors of acm device
16673227Syq193411  */
16683227Syq193411 static int
usbsacm_get_descriptors(usbsacm_state_t * acmp)16693227Syq193411 usbsacm_get_descriptors(usbsacm_state_t *acmp)
16703227Syq193411 {
16713227Syq193411 	int			i;
16723227Syq193411 	usb_cfg_data_t		*cfg;
16733227Syq193411 	usb_alt_if_data_t	*altif;
16743227Syq193411 	usb_cvs_data_t		*cvs;
16753227Syq193411 	int			mgmt_cap = 0;
16763227Syq193411 	int			master_if = -1, slave_if = -1;
16773227Syq193411 	usbsacm_port_t		*acm_port = acmp->acm_ports;
16783227Syq193411 
16793227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
16803227Syq193411 	    "usbsacm_get_descriptors: ");
16813227Syq193411 
16823227Syq193411 	cfg = acmp->acm_dev_data->dev_curr_cfg;
16833227Syq193411 	/* set default control and data interface */
16843227Syq193411 	acm_port->acm_ctrl_if_no = acm_port->acm_data_if_no = 0;
16853227Syq193411 
16863227Syq193411 	/* get current interfaces */
16873227Syq193411 	acm_port->acm_ctrl_if_no = acmp->acm_dev_data->dev_curr_if;
16883227Syq193411 	if (cfg->cfg_if[acm_port->acm_ctrl_if_no].if_n_alt == 0) {
16893227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
16903227Syq193411 		    "usbsacm_get_descriptors: elements in if_alt is %d",
16913227Syq193411 		    cfg->cfg_if[acm_port->acm_ctrl_if_no].if_n_alt);
16923227Syq193411 
16933227Syq193411 		return (USB_FAILURE);
16943227Syq193411 	}
16953227Syq193411 
16963227Syq193411 	altif = &cfg->cfg_if[acm_port->acm_ctrl_if_no].if_alt[0];
16973227Syq193411 
16983227Syq193411 	/*
16993227Syq193411 	 * Based on CDC specification, ACM devices usually include the
17003227Syq193411 	 * following function descriptors: Header, ACM, Union and Call
17013227Syq193411 	 * Management function descriptors. This loop search tree data
17023227Syq193411 	 * structure for each acm class descriptor.
17033227Syq193411 	 */
17043227Syq193411 	for (i = 0; i < altif->altif_n_cvs; i++) {
17053227Syq193411 
17063227Syq193411 		cvs = &altif->altif_cvs[i];
17073227Syq193411 
17083227Syq193411 		if ((cvs->cvs_buf == NULL) ||
17093227Syq193411 		    (cvs->cvs_buf[1] != USB_CDC_CS_INTERFACE)) {
17103227Syq193411 			continue;
17113227Syq193411 		}
17123227Syq193411 
17133227Syq193411 		switch (cvs->cvs_buf[2]) {
17143227Syq193411 		case USB_CDC_DESCR_TYPE_CALL_MANAGEMENT:
17153227Syq193411 			/* parse call management functional descriptor. */
17163227Syq193411 			if (cvs->cvs_buf_len >= 5) {
17173227Syq193411 				mgmt_cap = cvs->cvs_buf[3];
17183227Syq193411 				acm_port->acm_data_if_no = cvs->cvs_buf[4];
17193227Syq193411 			}
17203227Syq193411 			break;
17213227Syq193411 		case USB_CDC_DESCR_TYPE_ACM:
17223227Syq193411 			/* parse ACM functional descriptor. */
17233227Syq193411 			if (cvs->cvs_buf_len >= 4) {
17243227Syq193411 				acm_port->acm_cap = cvs->cvs_buf[3];
17253227Syq193411 			}
17263227Syq193411 			break;
17273227Syq193411 		case USB_CDC_DESCR_TYPE_UNION:
17283227Syq193411 			/* parse Union functional descriptor. */
17293227Syq193411 			if (cvs->cvs_buf_len >= 5) {
17303227Syq193411 				master_if = cvs->cvs_buf[3];
17313227Syq193411 				slave_if = cvs->cvs_buf[4];
17323227Syq193411 			}
17333227Syq193411 			break;
17343227Syq193411 		default:
17353227Syq193411 			break;
17363227Syq193411 		}
17373227Syq193411 	}
17383227Syq193411 
17393227Syq193411 	/* For usb acm devices, it must satisfy the following options. */
17403227Syq193411 	if (cfg->cfg_n_if < 2) {
17413227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
17423227Syq193411 		    "usbsacm_get_descriptors: # of interfaces %d < 2",
17433227Syq193411 		    cfg->cfg_n_if);
17443227Syq193411 
17453227Syq193411 		return (USB_FAILURE);
17463227Syq193411 	}
17473227Syq193411 
17483227Syq193411 	if (acm_port->acm_data_if_no == 0 &&
17495541Slg150142 	    slave_if != acm_port->acm_data_if_no) {
17503227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
17513227Syq193411 		    "usbsacm_get_descriptors: Device hasn't call management "
17523227Syq193411 		    "descriptor and use Union Descriptor.");
17533227Syq193411 
17543227Syq193411 		acm_port->acm_data_if_no = slave_if;
17553227Syq193411 	}
17563227Syq193411 
17573227Syq193411 	if ((master_if != acm_port->acm_ctrl_if_no) ||
17583227Syq193411 	    (slave_if != acm_port->acm_data_if_no)) {
17593227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
17603227Syq193411 		    "usbsacm_get_descriptors: control interface or "
17613227Syq193411 		    "data interface don't match.");
17623227Syq193411 
17633227Syq193411 		return (USB_FAILURE);
17643227Syq193411 	}
17653227Syq193411 
17663227Syq193411 	/*
17673227Syq193411 	 * We usually need both call and data capabilities, but
17683227Syq193411 	 * some devices, such as Nokia mobile phones, don't provide
17693227Syq193411 	 * call management descriptor, so we just give a warning
17703227Syq193411 	 * message.
17713227Syq193411 	 */
17723227Syq193411 	if (((mgmt_cap & USB_CDC_CALL_MGMT_CAP_CALL_MGMT) == 0) ||
17733227Syq193411 	    ((mgmt_cap & USB_CDC_CALL_MGMT_CAP_DATA_INTERFACE) == 0)) {
17743227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
17753227Syq193411 		    "usbsacm_get_descriptors: "
17763227Syq193411 		    "insufficient mgmt capabilities %x",
17773227Syq193411 		    mgmt_cap);
17783227Syq193411 	}
17793227Syq193411 
17803227Syq193411 	if ((acm_port->acm_ctrl_if_no >= cfg->cfg_n_if) ||
17813227Syq193411 	    (acm_port->acm_data_if_no >= cfg->cfg_n_if)) {
17823227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
17833227Syq193411 		    "usbsacm_get_descriptors: control interface %d or "
17843227Syq193411 		    "data interface %d out of range.",
17853227Syq193411 		    acm_port->acm_ctrl_if_no, acm_port->acm_data_if_no);
17863227Syq193411 
17873227Syq193411 		return (USB_FAILURE);
17883227Syq193411 	}
17893227Syq193411 
17903227Syq193411 	/* control interface must have interrupt endpoint */
17913227Syq193411 	if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
17923227Syq193411 	    acm_port->acm_ctrl_if_no, 0, 0, USB_EP_ATTR_INTR,
17933227Syq193411 	    USB_EP_DIR_IN) == NULL) {
17943227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
17953227Syq193411 		    "usbsacm_get_descriptors: "
17963227Syq193411 		    "ctrl interface %d has no interrupt endpoint",
17973227Syq193411 		    acm_port->acm_data_if_no);
17983227Syq193411 
17993227Syq193411 		return (USB_FAILURE);
18003227Syq193411 	}
18013227Syq193411 
18023227Syq193411 	/* data interface must have bulk in and out */
18033227Syq193411 	if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
18043227Syq193411 	    acm_port->acm_data_if_no, 0, 0, USB_EP_ATTR_BULK,
18053227Syq193411 	    USB_EP_DIR_IN) == NULL) {
18063227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
18073227Syq193411 		    "usbsacm_get_descriptors: "
18083227Syq193411 		    "data interface %d has no bulk in endpoint",
18093227Syq193411 		    acm_port->acm_data_if_no);
18103227Syq193411 
18113227Syq193411 		return (USB_FAILURE);
18123227Syq193411 	}
18133227Syq193411 	if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
18143227Syq193411 	    acm_port->acm_data_if_no, 0, 0, USB_EP_ATTR_BULK,
18153227Syq193411 	    USB_EP_DIR_OUT) == NULL) {
18163227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
18173227Syq193411 		    "usbsacm_get_descriptors: "
18183227Syq193411 		    "data interface %d has no bulk out endpoint",
18193227Syq193411 		    acm_port->acm_data_if_no);
18203227Syq193411 
18213227Syq193411 		return (USB_FAILURE);
18223227Syq193411 	}
18233227Syq193411 
18243227Syq193411 	return (USB_SUCCESS);
18253227Syq193411 }
18263227Syq193411 
18273227Syq193411 
18283227Syq193411 /*
18293227Syq193411  * usbsacm_cleanup:
18303227Syq193411  *	Release resources of current device during detach.
18313227Syq193411  */
18323227Syq193411 static void
usbsacm_cleanup(usbsacm_state_t * acmp)18333227Syq193411 usbsacm_cleanup(usbsacm_state_t *acmp)
18343227Syq193411 {
18353227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
18363227Syq193411 	    "usbsacm_cleanup: ");
18373227Syq193411 
18383227Syq193411 	if (acmp != NULL) {
18393227Syq193411 		/* free ports */
18403227Syq193411 		if (acmp->acm_ports != NULL) {
18413227Syq193411 			usbsacm_free_ports(acmp);
18423227Syq193411 		}
18433227Syq193411 
18443227Syq193411 		/* unregister callback function */
18453227Syq193411 		if (acmp->acm_usb_events != NULL) {
18463227Syq193411 			usb_unregister_event_cbs(acmp->acm_dip,
18475541Slg150142 			    acmp->acm_usb_events);
18483227Syq193411 		}
18493227Syq193411 
18503227Syq193411 		/* destroy power management components */
18513227Syq193411 		if (acmp->acm_pm != NULL) {
18523227Syq193411 			usbsacm_destroy_pm_components(acmp);
18533227Syq193411 		}
18543227Syq193411 
18553227Syq193411 		/* free description of device tree. */
18563227Syq193411 		if (acmp->acm_def_ph != NULL) {
18573227Syq193411 			mutex_destroy(&acmp->acm_mutex);
18583227Syq193411 
18593227Syq193411 			usb_free_descr_tree(acmp->acm_dip, acmp->acm_dev_data);
18603227Syq193411 			acmp->acm_def_ph = NULL;
18613227Syq193411 		}
18623227Syq193411 
18633227Syq193411 		if (acmp->acm_lh != NULL) {
18643227Syq193411 			usb_free_log_hdl(acmp->acm_lh);
18653227Syq193411 			acmp->acm_lh = NULL;
18663227Syq193411 		}
18673227Syq193411 
18683227Syq193411 		/* detach client device */
18693227Syq193411 		if (acmp->acm_dev_data != NULL) {
18703227Syq193411 			usb_client_detach(acmp->acm_dip, acmp->acm_dev_data);
18713227Syq193411 		}
18723227Syq193411 
18733227Syq193411 		kmem_free((caddr_t)acmp, sizeof (usbsacm_state_t));
18743227Syq193411 	}
18753227Syq193411 }
18763227Syq193411 
18773227Syq193411 
18783227Syq193411 /*
18793227Syq193411  * usbsacm_restore_device_state:
18803227Syq193411  *	restore device state after CPR resume or reconnect
18813227Syq193411  */
18823227Syq193411 static int
usbsacm_restore_device_state(usbsacm_state_t * acmp)18833227Syq193411 usbsacm_restore_device_state(usbsacm_state_t *acmp)
18843227Syq193411 {
18853227Syq193411 	int	state;
18863227Syq193411 
18873227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
18883227Syq193411 	    "usbsacm_restore_device_state: ");
18893227Syq193411 
18903227Syq193411 	mutex_enter(&acmp->acm_mutex);
18913227Syq193411 	state = acmp->acm_dev_state;
18923227Syq193411 	mutex_exit(&acmp->acm_mutex);
18933227Syq193411 
18943227Syq193411 	/* Check device status */
18953227Syq193411 	if ((state != USB_DEV_DISCONNECTED) && (state != USB_DEV_SUSPENDED)) {
18963227Syq193411 
18973227Syq193411 		return (state);
18983227Syq193411 	}
18993227Syq193411 
19003227Syq193411 	/* Check if we are talking to the same device */
19013227Syq193411 	if (usb_check_same_device(acmp->acm_dip, acmp->acm_lh, USB_LOG_L0,
19023227Syq193411 	    -1, USB_CHK_ALL, NULL) != USB_SUCCESS) {
19033227Syq193411 		mutex_enter(&acmp->acm_mutex);
19043227Syq193411 		state = acmp->acm_dev_state = USB_DEV_DISCONNECTED;
19053227Syq193411 		mutex_exit(&acmp->acm_mutex);
19063227Syq193411 
19073227Syq193411 		return (state);
19083227Syq193411 	}
19093227Syq193411 
19103227Syq193411 	if (state == USB_DEV_DISCONNECTED) {
19113227Syq193411 		USB_DPRINTF_L1(PRINT_MASK_ALL, acmp->acm_lh,
19123227Syq193411 		    "usbsacm_restore_device_state: Device has been reconnected "
19133227Syq193411 		    "but data may have been lost");
19143227Syq193411 	}
19153227Syq193411 
19163227Syq193411 	/* reconnect pipes */
19173227Syq193411 	if (usbsacm_reconnect_pipes(acmp) != USB_SUCCESS) {
19183227Syq193411 
19193227Syq193411 		return (state);
19203227Syq193411 	}
19213227Syq193411 
19223227Syq193411 	/*
19233227Syq193411 	 * init device state
19243227Syq193411 	 */
19253227Syq193411 	mutex_enter(&acmp->acm_mutex);
19263227Syq193411 	state = acmp->acm_dev_state = USB_DEV_ONLINE;
19273227Syq193411 	mutex_exit(&acmp->acm_mutex);
19283227Syq193411 
19293227Syq193411 	if ((usbsacm_restore_port_state(acmp) != USB_SUCCESS)) {
19303227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
19313227Syq193411 		    "usbsacm_restore_device_state: failed");
19323227Syq193411 	}
19333227Syq193411 
19343227Syq193411 	return (state);
19353227Syq193411 }
19363227Syq193411 
19373227Syq193411 
19383227Syq193411 /*
19393227Syq193411  * usbsacm_restore_port_state:
19403227Syq193411  *	restore ports state after CPR resume or reconnect
19413227Syq193411  */
19423227Syq193411 static int
usbsacm_restore_port_state(usbsacm_state_t * acmp)19433227Syq193411 usbsacm_restore_port_state(usbsacm_state_t *acmp)
19443227Syq193411 {
19453227Syq193411 	int		i, ret = USB_SUCCESS;
19463227Syq193411 	usbsacm_port_t	*cur_port;
19473227Syq193411 
19483227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
19493227Syq193411 	    "usbsacm_restore_port_state: ");
19503227Syq193411 
19513227Syq193411 	/* restore status of all ports */
19523227Syq193411 	for (i = 0; i < acmp->acm_port_cnt; i++) {
19533227Syq193411 		cur_port = &acmp->acm_ports[i];
19543227Syq193411 		mutex_enter(&cur_port->acm_port_mutex);
19553227Syq193411 		if (cur_port->acm_port_state != USBSACM_PORT_OPEN) {
19563227Syq193411 			mutex_exit(&cur_port->acm_port_mutex);
19573227Syq193411 
19583227Syq193411 			continue;
19593227Syq193411 		}
19603227Syq193411 		mutex_exit(&cur_port->acm_port_mutex);
19613227Syq193411 
19623227Syq193411 		if ((ret = usbsacm_set_line_coding(cur_port,
19633227Syq193411 		    &cur_port->acm_line_coding)) != USB_SUCCESS) {
19643227Syq193411 			USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
19653227Syq193411 			    "usbsacm_restore_port_state: failed.");
19663227Syq193411 		}
19673227Syq193411 	}
19683227Syq193411 
19693227Syq193411 	return (ret);
19703227Syq193411 }
19713227Syq193411 
19723227Syq193411 
19733227Syq193411 /*
19743227Syq193411  * pipe management
19753227Syq193411  * ---------------
19763227Syq193411  *
19773227Syq193411  *
19783227Syq193411  * usbsacm_open_port_pipes:
19793227Syq193411  *	Open pipes of one port and set port structure;
19803227Syq193411  *	Each port includes three pipes: bulk in, bulk out and interrupt.
19813227Syq193411  */
19823227Syq193411 static int
usbsacm_open_port_pipes(usbsacm_port_t * acm_port)19833227Syq193411 usbsacm_open_port_pipes(usbsacm_port_t *acm_port)
19843227Syq193411 {
19853227Syq193411 	int		rval = USB_SUCCESS;
19863227Syq193411 	usbsacm_state_t	*acmp = acm_port->acm_device;
19873227Syq193411 	usb_ep_data_t	*in_data, *out_data, *intr_pipe;
19883227Syq193411 	usb_pipe_policy_t policy;
19893227Syq193411 
19903227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
19916898Sfb209375 	    "usbsacm_open_port_pipes: acmp = 0x%p", (void *)acmp);
19923227Syq193411 
19933227Syq193411 	/* Get bulk and interrupt endpoint data */
19943227Syq193411 	intr_pipe = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
19953227Syq193411 	    acm_port->acm_ctrl_if_no, 0, 0,
19963227Syq193411 	    USB_EP_ATTR_INTR, USB_EP_DIR_IN);
19973227Syq193411 	in_data = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
19983227Syq193411 	    acm_port->acm_data_if_no, 0, acm_port->acm_data_port_no,
19993227Syq193411 	    USB_EP_ATTR_BULK, USB_EP_DIR_IN);
20003227Syq193411 	out_data = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
20013227Syq193411 	    acm_port->acm_data_if_no, 0, acm_port->acm_data_port_no,
20023227Syq193411 	    USB_EP_ATTR_BULK, USB_EP_DIR_OUT);
20033227Syq193411 
20043227Syq193411 	/* Bulk in and out must exist meanwhile. */
20053227Syq193411 	if ((in_data == NULL) || (out_data == NULL)) {
20063227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
20073227Syq193411 		    "usbsacm_open_port_pipes: look up bulk pipe failed in "
20083227Syq193411 		    "interface %d port %d",
20093227Syq193411 		    acm_port->acm_data_if_no, acm_port->acm_data_port_no);
20103227Syq193411 
20113227Syq193411 		return (USB_FAILURE);
20123227Syq193411 	}
20133227Syq193411 
20143227Syq193411 	/*
20153227Syq193411 	 * If device conform to acm spec, it must have an interrupt pipe
20163227Syq193411 	 * for this port.
20173227Syq193411 	 */
20183227Syq193411 	if (acmp->acm_compatibility == B_TRUE && intr_pipe == NULL) {
20193227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
20203227Syq193411 		    "usbsacm_open_port_pipes: look up interrupt pipe failed in "
20213227Syq193411 		    "interface %d", acm_port->acm_ctrl_if_no);
20223227Syq193411 
20233227Syq193411 		return (USB_FAILURE);
20243227Syq193411 	}
20253227Syq193411 
20263227Syq193411 	policy.pp_max_async_reqs = 2;
20273227Syq193411 
20283227Syq193411 	/* Open bulk in endpoint */
20293227Syq193411 	if (usb_pipe_open(acmp->acm_dip, &in_data->ep_descr, &policy,
20303227Syq193411 	    USB_FLAGS_SLEEP, &acm_port->acm_bulkin_ph) != USB_SUCCESS) {
20313227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
20323227Syq193411 		    "usbsacm_open_port_pipes: open bulkin pipe failed!");
20333227Syq193411 
20343227Syq193411 		return (USB_FAILURE);
20353227Syq193411 	}
20363227Syq193411 
20373227Syq193411 	/* Open bulk out endpoint */
20383227Syq193411 	if (usb_pipe_open(acmp->acm_dip, &out_data->ep_descr, &policy,
20393227Syq193411 	    USB_FLAGS_SLEEP, &acm_port->acm_bulkout_ph) != USB_SUCCESS) {
20403227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
20413227Syq193411 		    "usbsacm_open_port_pipes: open bulkout pipe failed!");
20423227Syq193411 
20433227Syq193411 		usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
20443227Syq193411 		    USB_FLAGS_SLEEP, NULL, NULL);
20453227Syq193411 
20463227Syq193411 		return (USB_FAILURE);
20473227Syq193411 	}
20483227Syq193411 
20493227Syq193411 	/* Open interrupt endpoint if found. */
20503227Syq193411 	if (intr_pipe != NULL) {
20513227Syq193411 
20523227Syq193411 		if (usb_pipe_open(acmp->acm_dip, &intr_pipe->ep_descr, &policy,
20533227Syq193411 		    USB_FLAGS_SLEEP, &acm_port->acm_intr_ph) != USB_SUCCESS) {
20543227Syq193411 			USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
20553227Syq193411 			    "usbsacm_open_port_pipes: "
20563227Syq193411 			    "open control pipe failed");
20573227Syq193411 
20583227Syq193411 			usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
20593227Syq193411 			    USB_FLAGS_SLEEP, NULL, NULL);
20603227Syq193411 			usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkout_ph,
20613227Syq193411 			    USB_FLAGS_SLEEP, NULL, NULL);
20623227Syq193411 
20633227Syq193411 			return (USB_FAILURE);
20643227Syq193411 		}
20653227Syq193411 	}
20663227Syq193411 
20673227Syq193411 	/* initialize the port structure. */
20683227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
20693227Syq193411 	acm_port->acm_bulkin_size = in_data->ep_descr.wMaxPacketSize;
20703227Syq193411 	acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
20713227Syq193411 	acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
20723227Syq193411 	if (acm_port->acm_intr_ph != NULL) {
20733227Syq193411 		acm_port->acm_intr_state = USBSACM_PIPE_IDLE;
20743227Syq193411 		acm_port->acm_intr_ep_descr = intr_pipe->ep_descr;
20753227Syq193411 	}
20763227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
20773227Syq193411 
20783227Syq193411 	if (acm_port->acm_intr_ph != NULL) {
20793227Syq193411 
20803227Syq193411 		usbsacm_pipe_start_polling(acm_port);
20813227Syq193411 	}
20823227Syq193411 
20833227Syq193411 	return (rval);
20843227Syq193411 }
20853227Syq193411 
20863227Syq193411 
20873227Syq193411 /*
20883227Syq193411  * usbsacm_close_port_pipes:
20893227Syq193411  *	Close pipes of one port and reset port structure to closed;
20903227Syq193411  *	Each port includes three pipes: bulk in, bulk out and interrupt.
20913227Syq193411  */
20923227Syq193411 static void
usbsacm_close_port_pipes(usbsacm_port_t * acm_port)20933227Syq193411 usbsacm_close_port_pipes(usbsacm_port_t	*acm_port)
20943227Syq193411 {
20953227Syq193411 	usbsacm_state_t	*acmp = acm_port->acm_device;
20963227Syq193411 
20973227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
20983227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
20993227Syq193411 	    "usbsacm_close_port_pipes: acm_bulkin_state = %d",
21003227Syq193411 	    acm_port->acm_bulkin_state);
21013227Syq193411 
21023227Syq193411 	/*
21033227Syq193411 	 * Check the status of the given port. If port is closing or closed,
21043227Syq193411 	 * return directly.
21053227Syq193411 	 */
21063227Syq193411 	if ((acm_port->acm_bulkin_state == USBSACM_PIPE_CLOSED) ||
21073227Syq193411 	    (acm_port->acm_bulkin_state == USBSACM_PIPE_CLOSING)) {
21083227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_CLOSE, acmp->acm_lh,
21093227Syq193411 		    "usbsacm_close_port_pipes: port is closing or has closed");
21103227Syq193411 		mutex_exit(&acm_port->acm_port_mutex);
21113227Syq193411 
21123227Syq193411 		return;
21133227Syq193411 	}
21143227Syq193411 
21153227Syq193411 	acm_port->acm_bulkin_state = USBSACM_PIPE_CLOSING;
21163227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
21173227Syq193411 
21183227Syq193411 	/* Close pipes */
21193227Syq193411 	usb_pipe_reset(acmp->acm_dip, acm_port->acm_bulkin_ph,
21203227Syq193411 	    USB_FLAGS_SLEEP, 0, 0);
21213227Syq193411 	usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
21223227Syq193411 	    USB_FLAGS_SLEEP, 0, 0);
21233227Syq193411 	usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkout_ph,
21243227Syq193411 	    USB_FLAGS_SLEEP, 0, 0);
21253227Syq193411 	if (acm_port->acm_intr_ph != NULL) {
21263227Syq193411 		usb_pipe_stop_intr_polling(acm_port->acm_intr_ph,
21273227Syq193411 		    USB_FLAGS_SLEEP);
21283227Syq193411 		usb_pipe_close(acmp->acm_dip, acm_port->acm_intr_ph,
21293227Syq193411 		    USB_FLAGS_SLEEP, 0, 0);
21303227Syq193411 	}
21313227Syq193411 
21323227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
21333227Syq193411 	/* Reset the status of pipes to closed */
21343227Syq193411 	acm_port->acm_bulkin_state = USBSACM_PIPE_CLOSED;
21353227Syq193411 	acm_port->acm_bulkin_ph = NULL;
21363227Syq193411 	acm_port->acm_bulkout_state = USBSACM_PIPE_CLOSED;
21373227Syq193411 	acm_port->acm_bulkout_ph = NULL;
21383227Syq193411 	if (acm_port->acm_intr_ph != NULL) {
21393227Syq193411 		acm_port->acm_intr_state = USBSACM_PIPE_CLOSED;
21403227Syq193411 		acm_port->acm_intr_ph = NULL;
21413227Syq193411 	}
21423227Syq193411 
21433227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
21443227Syq193411 
21453227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
21463227Syq193411 	    "usbsacm_close_port_pipes: port has been closed.");
21473227Syq193411 }
21483227Syq193411 
21493227Syq193411 
21503227Syq193411 /*
21513227Syq193411  * usbsacm_close_pipes:
21523227Syq193411  *	close all opened pipes of current devices.
21533227Syq193411  */
21543227Syq193411 static void
usbsacm_close_pipes(usbsacm_state_t * acmp)21553227Syq193411 usbsacm_close_pipes(usbsacm_state_t *acmp)
21563227Syq193411 {
21573227Syq193411 	int		i;
21583227Syq193411 
21593227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
21603227Syq193411 	    "usbsacm_close_pipes: ");
21613227Syq193411 
21623227Syq193411 	/* Close all ports */
21633227Syq193411 	for (i = 0; i < acmp->acm_port_cnt; i++) {
21643227Syq193411 		usbsacm_close_port_pipes(&acmp->acm_ports[i]);
21653227Syq193411 	}
21663227Syq193411 }
21673227Syq193411 
21683227Syq193411 
21693227Syq193411 /*
21703227Syq193411  * usbsacm_disconnect_pipes:
21713227Syq193411  *	this function just call usbsacm_close_pipes.
21723227Syq193411  */
21733227Syq193411 static void
usbsacm_disconnect_pipes(usbsacm_state_t * acmp)21743227Syq193411 usbsacm_disconnect_pipes(usbsacm_state_t *acmp)
21753227Syq193411 {
21763227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
21773227Syq193411 	    "usbsacm_disconnect_pipes: ");
21783227Syq193411 
21793227Syq193411 	usbsacm_close_pipes(acmp);
21803227Syq193411 }
21813227Syq193411 
21823227Syq193411 
21833227Syq193411 /*
21843227Syq193411  * usbsacm_reconnect_pipes:
21853227Syq193411  *	reconnect pipes in CPR resume or reconnect
21863227Syq193411  */
21873227Syq193411 static int
usbsacm_reconnect_pipes(usbsacm_state_t * acmp)21883227Syq193411 usbsacm_reconnect_pipes(usbsacm_state_t *acmp)
21893227Syq193411 {
21903227Syq193411 	usbsacm_port_t	*cur_port = acmp->acm_ports;
21913227Syq193411 	int		i;
21923227Syq193411 
21933227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
21943227Syq193411 	    "usbsacm_reconnect_pipes: ");
21953227Syq193411 
21963227Syq193411 	/* reopen all ports of current device. */
21973227Syq193411 	for (i = 0; i < acmp->acm_port_cnt; i++) {
21983227Syq193411 		cur_port = &acmp->acm_ports[i];
21993227Syq193411 
22003227Syq193411 		mutex_enter(&cur_port->acm_port_mutex);
22013227Syq193411 		/*
22023227Syq193411 		 * If port status is open, reopen it;
22033227Syq193411 		 * else retain the current status.
22043227Syq193411 		 */
22053227Syq193411 		if (cur_port->acm_port_state == USBSACM_PORT_OPEN) {
22063227Syq193411 
22073227Syq193411 			mutex_exit(&cur_port->acm_port_mutex);
22083227Syq193411 			if (usbsacm_open_port_pipes(cur_port) != USB_SUCCESS) {
22093227Syq193411 				USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
22103227Syq193411 				    "usbsacm_reconnect_pipes: "
22113227Syq193411 				    "open port %d failed.", i);
22123227Syq193411 
22133227Syq193411 				return (USB_FAILURE);
22143227Syq193411 			}
22153227Syq193411 			mutex_enter(&cur_port->acm_port_mutex);
22163227Syq193411 		}
22173227Syq193411 		mutex_exit(&cur_port->acm_port_mutex);
22183227Syq193411 	}
22193227Syq193411 
22203227Syq193411 	return (USB_SUCCESS);
22213227Syq193411 }
22223227Syq193411 
22233227Syq193411 /*
22243227Syq193411  * usbsacm_bulkin_cb:
22253227Syq193411  *	Bulk In regular and exeception callback;
22263227Syq193411  *	USBA framework will call this callback
22273227Syq193411  *	after deal with bulkin request.
22283227Syq193411  */
22293227Syq193411 /*ARGSUSED*/
22303227Syq193411 static void
usbsacm_bulkin_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)22313227Syq193411 usbsacm_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
22323227Syq193411 {
22333227Syq193411 	usbsacm_port_t	*acm_port = (usbsacm_port_t *)req->bulk_client_private;
22343227Syq193411 	usbsacm_state_t	*acmp = acm_port->acm_device;
22353227Syq193411 	mblk_t		*data;
22363227Syq193411 	int		data_len;
22373227Syq193411 
22383227Syq193411 	data = req->bulk_data;
22393227Syq193411 	data_len = (data) ? MBLKL(data) : 0;
22403227Syq193411 
22413227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
22423227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
22433227Syq193411 	    "usbsacm_bulkin_cb: "
22443227Syq193411 	    "acm_bulkin_state = %d acm_port_state = %d data_len = %d",
22453227Syq193411 	    acm_port->acm_bulkin_state, acm_port->acm_port_state, data_len);
22463227Syq193411 
22473227Syq193411 	if ((acm_port->acm_port_state == USBSACM_PORT_OPEN) && (data_len) &&
22483227Syq193411 	    (req->bulk_completion_reason == USB_CR_OK)) {
22493227Syq193411 		mutex_exit(&acm_port->acm_port_mutex);
22503227Syq193411 		/* prevent USBA from freeing data along with the request */
22513227Syq193411 		req->bulk_data = NULL;
22523227Syq193411 
22533227Syq193411 		/* save data on the receive list */
22543227Syq193411 		usbsacm_put_tail(&acm_port->acm_rx_mp, data);
22553227Syq193411 
22563227Syq193411 		/* invoke GSD receive callback */
22573227Syq193411 		if (acm_port->acm_cb.cb_rx) {
22583227Syq193411 			acm_port->acm_cb.cb_rx(acm_port->acm_cb.cb_arg);
22593227Syq193411 		}
22603227Syq193411 		mutex_enter(&acm_port->acm_port_mutex);
22613227Syq193411 	}
22623227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
22633227Syq193411 
22643227Syq193411 	usb_free_bulk_req(req);
22653227Syq193411 
22663227Syq193411 	/* receive more */
22673227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
22683227Syq193411 	if (((acm_port->acm_bulkin_state == USBSACM_PIPE_BUSY) ||
22693227Syq193411 	    (acm_port->acm_bulkin_state == USBSACM_PIPE_IDLE)) &&
22703227Syq193411 	    (acm_port->acm_port_state == USBSACM_PORT_OPEN) &&
22713227Syq193411 	    (acmp->acm_dev_state == USB_DEV_ONLINE)) {
22723227Syq193411 		if (usbsacm_rx_start(acm_port) != USB_SUCCESS) {
22733227Syq193411 			USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
22743227Syq193411 			    "usbsacm_bulkin_cb: restart rx fail "
22753227Syq193411 			    "acm_port_state = %d", acm_port->acm_port_state);
22763227Syq193411 		}
22773227Syq193411 	} else if (acm_port->acm_bulkin_state == USBSACM_PIPE_BUSY) {
22783227Syq193411 		acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
22793227Syq193411 	}
22803227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
22813227Syq193411 }
22823227Syq193411 
22833227Syq193411 
22843227Syq193411 /*
22853227Syq193411  * usbsacm_bulkout_cb:
22863227Syq193411  *	Bulk Out regular and exeception callback;
22873227Syq193411  *	USBA framework will call this callback function
22883227Syq193411  *	after deal with bulkout request.
22893227Syq193411  */
22903227Syq193411 /*ARGSUSED*/
22913227Syq193411 static void
usbsacm_bulkout_cb(usb_pipe_handle_t pipe,usb_bulk_req_t * req)22923227Syq193411 usbsacm_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
22933227Syq193411 {
22943227Syq193411 	usbsacm_port_t	*acm_port = (usbsacm_port_t *)req->bulk_client_private;
22953227Syq193411 	usbsacm_state_t	*acmp = acm_port->acm_device;
22963227Syq193411 	int		data_len;
22973227Syq193411 	mblk_t		*data = req->bulk_data;
22983227Syq193411 
22993227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
23006898Sfb209375 	    "usbsacm_bulkout_cb: acmp = 0x%p", (void *)acmp);
23013227Syq193411 
23023227Syq193411 	data_len = (req->bulk_data) ? MBLKL(req->bulk_data) : 0;
23033227Syq193411 
23043227Syq193411 	/* put untransferred residue back on the transfer list */
23053227Syq193411 	if (req->bulk_completion_reason && (data_len > 0)) {
23063227Syq193411 		usbsacm_put_head(&acm_port->acm_tx_mp, data);
23073227Syq193411 		req->bulk_data = NULL;
23083227Syq193411 	}
23093227Syq193411 
23103227Syq193411 	usb_free_bulk_req(req);
23113227Syq193411 
23123227Syq193411 	/* invoke GSD transmit callback */
23133227Syq193411 	if (acm_port->acm_cb.cb_tx) {
23143227Syq193411 		acm_port->acm_cb.cb_tx(acm_port->acm_cb.cb_arg);
23153227Syq193411 	}
23163227Syq193411 
23173227Syq193411 	/* send more */
23183227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
23193227Syq193411 	acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
23203227Syq193411 	if (acm_port->acm_tx_mp == NULL) {
23213227Syq193411 		cv_broadcast(&acm_port->acm_tx_cv);
23223227Syq193411 	} else {
23233227Syq193411 		usbsacm_tx_start(acm_port);
23243227Syq193411 	}
23253227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
23263227Syq193411 }
23273227Syq193411 
23283227Syq193411 
23293227Syq193411 /*
23303227Syq193411  * usbsacm_rx_start:
23313227Syq193411  *	start data receipt
23323227Syq193411  */
23333227Syq193411 static int
usbsacm_rx_start(usbsacm_port_t * acm_port)23343227Syq193411 usbsacm_rx_start(usbsacm_port_t *acm_port)
23353227Syq193411 {
23363227Syq193411 	usbsacm_state_t	*acmp = acm_port->acm_device;
23373227Syq193411 	usb_bulk_req_t	*br;
23383227Syq193411 	int		rval = USB_FAILURE;
23393227Syq193411 	int		data_len;
23403227Syq193411 
23413227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
23426898Sfb209375 	    "usbsacm_rx_start: acm_xfer_sz = 0x%lx acm_bulkin_size = 0x%lx",
23433227Syq193411 	    acmp->acm_xfer_sz, acm_port->acm_bulkin_size);
23443227Syq193411 
23453227Syq193411 	acm_port->acm_bulkin_state = USBSACM_PIPE_BUSY;
23463227Syq193411 	/*
23473227Syq193411 	 * Qualcomm CDMA card won't response the first request,
23483227Syq193411 	 * if the following code don't multiply by 2.
23493227Syq193411 	 */
23503227Syq193411 	data_len = min(acmp->acm_xfer_sz, acm_port->acm_bulkin_size * 2);
23513227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
23523227Syq193411 
23533227Syq193411 	br = usb_alloc_bulk_req(acmp->acm_dip, data_len, USB_FLAGS_SLEEP);
23543227Syq193411 	if (br == NULL) {
23553227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
23563227Syq193411 		    "usbsacm_rx_start: allocate bulk request failed");
23573227Syq193411 
23583227Syq193411 		mutex_enter(&acm_port->acm_port_mutex);
23593227Syq193411 
23603227Syq193411 		return (USB_FAILURE);
23613227Syq193411 	}
23623227Syq193411 	/* initialize bulk in request. */
23633227Syq193411 	br->bulk_len = data_len;
23643227Syq193411 	br->bulk_timeout = USBSACM_BULKIN_TIMEOUT;
23653227Syq193411 	br->bulk_cb = usbsacm_bulkin_cb;
23663227Syq193411 	br->bulk_exc_cb = usbsacm_bulkin_cb;
23673227Syq193411 	br->bulk_client_private = (usb_opaque_t)acm_port;
23683227Syq193411 	br->bulk_attributes = USB_ATTRS_AUTOCLEARING
23693227Syq193411 	    | USB_ATTRS_SHORT_XFER_OK;
23703227Syq193411 
23713227Syq193411 	rval = usb_pipe_bulk_xfer(acm_port->acm_bulkin_ph, br, 0);
23723227Syq193411 
23733227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
23743227Syq193411 	if (rval != USB_SUCCESS) {
23753227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
23763227Syq193411 		    "usbsacm_rx_start: bulk transfer failed %d", rval);
23773227Syq193411 		usb_free_bulk_req(br);
23783227Syq193411 		acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
23793227Syq193411 	}
23803227Syq193411 
23813227Syq193411 	return (rval);
23823227Syq193411 }
23833227Syq193411 
23843227Syq193411 
23853227Syq193411 /*
23863227Syq193411  * usbsacm_tx_start:
23873227Syq193411  *	start data transmit
23883227Syq193411  */
23893227Syq193411 static void
usbsacm_tx_start(usbsacm_port_t * acm_port)23903227Syq193411 usbsacm_tx_start(usbsacm_port_t *acm_port)
23913227Syq193411 {
23923227Syq193411 	int		len;		/* bytes we can transmit */
23933227Syq193411 	mblk_t		*data;		/* data to be transmitted */
23943227Syq193411 	int		data_len;	/* bytes in 'data' */
23953227Syq193411 	mblk_t		*mp;		/* current msgblk */
23963227Syq193411 	int		copylen;	/* bytes copy from 'mp' to 'data' */
23973227Syq193411 	int		rval;
23983227Syq193411 	usbsacm_state_t	*acmp = acm_port->acm_device;
23993227Syq193411 
24003227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
24013227Syq193411 	    "usbsacm_tx_start: ");
24023227Syq193411 
24033227Syq193411 	/* check the transmitted data. */
24043227Syq193411 	if (acm_port->acm_tx_mp == NULL) {
24053227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
24063227Syq193411 		    "usbsacm_tx_start: acm_tx_mp is NULL");
24073227Syq193411 
24083227Syq193411 		return;
24093227Syq193411 	}
24103227Syq193411 
24113227Syq193411 	/* check pipe status */
24123227Syq193411 	if (acm_port->acm_bulkout_state != USBSACM_PIPE_IDLE) {
24133227Syq193411 
24143227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
24153227Syq193411 		    "usbsacm_tx_start: error state in bulkout endpoint");
24163227Syq193411 
24173227Syq193411 		return;
24183227Syq193411 	}
24193227Syq193411 	ASSERT(MBLKL(acm_port->acm_tx_mp) > 0);
24203227Syq193411 
24213227Syq193411 	/* send as much data as port can receive */
24223227Syq193411 	len = min(msgdsize(acm_port->acm_tx_mp), acmp->acm_xfer_sz);
24233227Syq193411 
24243227Syq193411 	if (len == 0) {
24253227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
24263227Syq193411 		    "usbsacm_tx_start: data len is 0");
24273227Syq193411 
24283227Syq193411 		return;
24293227Syq193411 	}
24303227Syq193411 
24313227Syq193411 	/* allocate memory for sending data. */
24323227Syq193411 	if ((data = allocb(len, BPRI_LO)) == NULL) {
24333227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
24343227Syq193411 		    "usbsacm_tx_start: failure in allocate memory");
24353227Syq193411 
24363227Syq193411 		return;
24373227Syq193411 	}
24383227Syq193411 
24393227Syq193411 	/*
24403227Syq193411 	 * copy no more than 'len' bytes from mblk chain to transmit mblk 'data'
24413227Syq193411 	 */
24423227Syq193411 	data_len = 0;
24433227Syq193411 	while ((data_len < len) && acm_port->acm_tx_mp) {
24443227Syq193411 		/* Get the first mblk from chain. */
24453227Syq193411 		mp = acm_port->acm_tx_mp;
24463227Syq193411 		copylen = min(MBLKL(mp), len - data_len);
24473227Syq193411 		bcopy(mp->b_rptr, data->b_wptr, copylen);
24483227Syq193411 		mp->b_rptr += copylen;
24493227Syq193411 		data->b_wptr += copylen;
24503227Syq193411 		data_len += copylen;
24513227Syq193411 
24526990Sgd78059 		if (MBLKL(mp) < 1) {
24533227Syq193411 			acm_port->acm_tx_mp = unlinkb(mp);
24543227Syq193411 			freeb(mp);
24553227Syq193411 		} else {
24563227Syq193411 			ASSERT(data_len == len);
24573227Syq193411 		}
24583227Syq193411 	}
24593227Syq193411 
24603227Syq193411 	if (data_len <= 0) {
24613227Syq193411 		freeb(data);
24623227Syq193411 
24633227Syq193411 		return;
24643227Syq193411 	}
24653227Syq193411 
24663227Syq193411 	acm_port->acm_bulkout_state = USBSACM_PIPE_BUSY;
24673227Syq193411 
24683227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
24693227Syq193411 	/* send request. */
24703227Syq193411 	rval = usbsacm_send_data(acm_port, data);
24713227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
24723227Syq193411 
24733227Syq193411 	/*
24743227Syq193411 	 * If send failed, retransmit data when acm_tx_mp is null.
24753227Syq193411 	 */
24763227Syq193411 	if (rval != USB_SUCCESS) {
24773227Syq193411 		acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
24783227Syq193411 		if (acm_port->acm_tx_mp == NULL) {
24793227Syq193411 			usbsacm_put_head(&acm_port->acm_tx_mp, data);
24803227Syq193411 		}
24813227Syq193411 	}
24823227Syq193411 }
24833227Syq193411 
24843227Syq193411 
24853227Syq193411 /*
24863227Syq193411  * usbsacm_send_data:
24873227Syq193411  *	data transfer
24883227Syq193411  */
24893227Syq193411 static int
usbsacm_send_data(usbsacm_port_t * acm_port,mblk_t * data)24903227Syq193411 usbsacm_send_data(usbsacm_port_t *acm_port, mblk_t *data)
24913227Syq193411 {
24923227Syq193411 	usbsacm_state_t	*acmp = acm_port->acm_device;
24933227Syq193411 	usb_bulk_req_t	*br;
24943227Syq193411 	int		rval;
24953227Syq193411 	int		data_len = MBLKL(data);
24963227Syq193411 
24973227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
24983227Syq193411 	    "usbsacm_send_data: data address is 0x%p, length = %d",
24996898Sfb209375 	    (void *)data, data_len);
25003227Syq193411 
25013227Syq193411 	br = usb_alloc_bulk_req(acmp->acm_dip, 0, USB_FLAGS_SLEEP);
25023227Syq193411 	if (br == NULL) {
25033227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
25043227Syq193411 		    "usbsacm_send_data: alloc req failed.");
25053227Syq193411 
25063227Syq193411 		return (USB_FAILURE);
25073227Syq193411 	}
25083227Syq193411 
25093227Syq193411 	/* initialize the bulk out request */
25103227Syq193411 	br->bulk_data = data;
25113227Syq193411 	br->bulk_len = data_len;
25123227Syq193411 	br->bulk_timeout = USBSACM_BULKOUT_TIMEOUT;
25133227Syq193411 	br->bulk_cb = usbsacm_bulkout_cb;
25143227Syq193411 	br->bulk_exc_cb = usbsacm_bulkout_cb;
25153227Syq193411 	br->bulk_client_private = (usb_opaque_t)acm_port;
25163227Syq193411 	br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
25173227Syq193411 
25183227Syq193411 	rval = usb_pipe_bulk_xfer(acm_port->acm_bulkout_ph, br, 0);
25193227Syq193411 
25203227Syq193411 	if (rval != USB_SUCCESS) {
25213227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
25223227Syq193411 		    "usbsacm_send_data: Send Data failed.");
25233227Syq193411 
25243227Syq193411 		/*
25253227Syq193411 		 * Don't free it in usb_free_bulk_req because it will
25263227Syq193411 		 * be linked in usbsacm_put_head
25273227Syq193411 		 */
25283227Syq193411 		br->bulk_data = NULL;
25293227Syq193411 
25303227Syq193411 		usb_free_bulk_req(br);
25313227Syq193411 	}
25323227Syq193411 
25333227Syq193411 	return (rval);
25343227Syq193411 }
25353227Syq193411 
25363227Syq193411 /*
25373227Syq193411  * usbsacm_wait_tx_drain:
25383227Syq193411  *	wait until local tx buffer drains.
25393227Syq193411  *	'timeout' is in seconds, zero means wait forever
25403227Syq193411  */
25413227Syq193411 static int
usbsacm_wait_tx_drain(usbsacm_port_t * acm_port,int timeout)25423227Syq193411 usbsacm_wait_tx_drain(usbsacm_port_t *acm_port, int timeout)
25433227Syq193411 {
25443227Syq193411 	clock_t		until;
25453227Syq193411 	int		over = 0;
25463227Syq193411 
25473227Syq193411 	until = ddi_get_lbolt() + drv_usectohz(1000 * 1000 * timeout);
25483227Syq193411 
25493227Syq193411 	while (acm_port->acm_tx_mp && !over) {
25503227Syq193411 		if (timeout > 0) {
25513227Syq193411 			over = (cv_timedwait_sig(&acm_port->acm_tx_cv,
25523227Syq193411 			    &acm_port->acm_port_mutex, until) <= 0);
25533227Syq193411 		} else {
25543227Syq193411 			over = (cv_wait_sig(&acm_port->acm_tx_cv,
25553227Syq193411 			    &acm_port->acm_port_mutex) == 0);
25563227Syq193411 		}
25573227Syq193411 	}
25583227Syq193411 
25593227Syq193411 	return ((acm_port->acm_tx_mp == NULL) ? USB_SUCCESS : USB_FAILURE);
25603227Syq193411 }
25613227Syq193411 
25623227Syq193411 
25633227Syq193411 /*
25643227Syq193411  * usbsacm_req_write:
25653227Syq193411  *	send command over control pipe
25663227Syq193411  */
25673227Syq193411 static int
usbsacm_req_write(usbsacm_port_t * acm_port,uchar_t request,uint16_t value,mblk_t ** data)25683227Syq193411 usbsacm_req_write(usbsacm_port_t *acm_port, uchar_t request, uint16_t value,
25693227Syq193411     mblk_t **data)
25703227Syq193411 {
25713227Syq193411 	usbsacm_state_t	*acmp = acm_port->acm_device;
25723227Syq193411 	usb_ctrl_setup_t setup;
25733227Syq193411 	usb_cb_flags_t	cb_flags;
25743227Syq193411 	usb_cr_t	cr;
25753227Syq193411 
25763227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
25773227Syq193411 	    "usbsacm_req_write: ");
25783227Syq193411 
25793227Syq193411 	/* initialize the control request. */
25803227Syq193411 	setup.bmRequestType = USBSACM_REQ_WRITE_IF;
25813227Syq193411 	setup.bRequest = request;
25823227Syq193411 	setup.wValue = value;
25833227Syq193411 	setup.wIndex = acm_port->acm_ctrl_if_no;
25843227Syq193411 	setup.wLength = ((data != NULL) && (*data != NULL)) ? MBLKL(*data) : 0;
25853227Syq193411 	setup.attrs = 0;
25863227Syq193411 
25873227Syq193411 	return (usb_pipe_ctrl_xfer_wait(acmp->acm_def_ph, &setup, data,
25883227Syq193411 	    &cr, &cb_flags, 0));
25893227Syq193411 }
25903227Syq193411 
25913227Syq193411 
25923227Syq193411 /*
25933227Syq193411  * usbsacm_set_line_coding:
25943227Syq193411  *	Send USB_CDC_REQ_SET_LINE_CODING request
25953227Syq193411  */
25963227Syq193411 static int
usbsacm_set_line_coding(usbsacm_port_t * acm_port,usb_cdc_line_coding_t * lc)25973227Syq193411 usbsacm_set_line_coding(usbsacm_port_t *acm_port, usb_cdc_line_coding_t *lc)
25983227Syq193411 {
25993227Syq193411 	mblk_t		*bp;
26003227Syq193411 	int		ret;
26013227Syq193411 
26023227Syq193411 	/* allocate mblk and copy supplied structure into it */
26033227Syq193411 	if ((bp = allocb(USB_CDC_LINE_CODING_LEN, BPRI_HI)) == NULL) {
26043227Syq193411 
26053227Syq193411 		return (USB_NO_RESOURCES);
26063227Syq193411 	}
26073227Syq193411 
26083227Syq193411 #ifndef __lock_lint /* warlock gets confused here */
26097492SZhigang.Lu@Sun.COM 	/* LINTED E_BAD_PTR_CAST_ALIGN */
26103227Syq193411 	*((usb_cdc_line_coding_t *)bp->b_wptr) = *lc;
26113227Syq193411 	bp->b_wptr += USB_CDC_LINE_CODING_LEN;
26123227Syq193411 #endif
26133227Syq193411 
26143227Syq193411 	ret = usbsacm_req_write(acm_port, USB_CDC_REQ_SET_LINE_CODING, 0, &bp);
26153227Syq193411 
26163227Syq193411 	if (bp != NULL) {
26173227Syq193411 		freeb(bp);
26183227Syq193411 	}
26193227Syq193411 
26203227Syq193411 	return (ret);
26213227Syq193411 }
26223227Syq193411 
26233227Syq193411 
26243227Syq193411 
26253227Syq193411 /*
26263227Syq193411  * usbsacm_mctl2reg:
26273227Syq193411  *	Set Modem control status
26283227Syq193411  */
26293227Syq193411 static void
usbsacm_mctl2reg(int mask,int val,uint8_t * line_ctl)26303227Syq193411 usbsacm_mctl2reg(int mask, int val, uint8_t *line_ctl)
26313227Syq193411 {
26323227Syq193411 	if (mask & TIOCM_RTS) {
26333227Syq193411 		if (val & TIOCM_RTS) {
26343227Syq193411 			*line_ctl |= USB_CDC_ACM_CONTROL_RTS;
26353227Syq193411 		} else {
26363227Syq193411 			*line_ctl &= ~USB_CDC_ACM_CONTROL_RTS;
26373227Syq193411 		}
26383227Syq193411 	}
26393227Syq193411 	if (mask & TIOCM_DTR) {
26403227Syq193411 		if (val & TIOCM_DTR) {
26413227Syq193411 			*line_ctl |= USB_CDC_ACM_CONTROL_DTR;
26423227Syq193411 		} else {
26433227Syq193411 			*line_ctl &= ~USB_CDC_ACM_CONTROL_DTR;
26443227Syq193411 		}
26453227Syq193411 	}
26463227Syq193411 }
26473227Syq193411 
26483227Syq193411 
26493227Syq193411 /*
26503227Syq193411  * usbsacm_reg2mctl:
26513227Syq193411  *	Get Modem control status
26523227Syq193411  */
26533227Syq193411 static int
usbsacm_reg2mctl(uint8_t line_ctl)26543227Syq193411 usbsacm_reg2mctl(uint8_t line_ctl)
26553227Syq193411 {
26563227Syq193411 	int	val = 0;
26573227Syq193411 
26583227Syq193411 	if (line_ctl & USB_CDC_ACM_CONTROL_RTS) {
26593227Syq193411 		val |= TIOCM_RTS;
26603227Syq193411 	}
26613227Syq193411 	if (line_ctl & USB_CDC_ACM_CONTROL_DTR) {
26623227Syq193411 		val |= TIOCM_DTR;
26633227Syq193411 	}
26643227Syq193411 	if (line_ctl & USB_CDC_ACM_CONTROL_DSR) {
26653227Syq193411 		val |= TIOCM_DSR;
26663227Syq193411 	}
26673227Syq193411 	if (line_ctl & USB_CDC_ACM_CONTROL_RNG) {
26683227Syq193411 		val |= TIOCM_RI;
26693227Syq193411 	}
26703227Syq193411 
26713227Syq193411 	return (val);
26723227Syq193411 }
26733227Syq193411 
26743227Syq193411 
26753227Syq193411 /*
26763227Syq193411  * misc routines
26773227Syq193411  * -------------
26783227Syq193411  *
26793227Syq193411  */
26803227Syq193411 
26813227Syq193411 /*
26823227Syq193411  * usbsacm_put_tail:
26833227Syq193411  *	link a message block to tail of message
26843227Syq193411  *	account for the case when message is null
26853227Syq193411  */
26863227Syq193411 static void
usbsacm_put_tail(mblk_t ** mpp,mblk_t * bp)26873227Syq193411 usbsacm_put_tail(mblk_t **mpp, mblk_t *bp)
26883227Syq193411 {
26893227Syq193411 	if (*mpp) {
26903227Syq193411 		linkb(*mpp, bp);
26913227Syq193411 	} else {
26923227Syq193411 		*mpp = bp;
26933227Syq193411 	}
26943227Syq193411 }
26953227Syq193411 
26963227Syq193411 
26973227Syq193411 /*
26983227Syq193411  * usbsacm_put_head:
26993227Syq193411  *	put a message block at the head of the message
27003227Syq193411  *	account for the case when message is null
27013227Syq193411  */
27023227Syq193411 static void
usbsacm_put_head(mblk_t ** mpp,mblk_t * bp)27033227Syq193411 usbsacm_put_head(mblk_t **mpp, mblk_t *bp)
27043227Syq193411 {
27053227Syq193411 	if (*mpp) {
27063227Syq193411 		linkb(bp, *mpp);
27073227Syq193411 	}
27083227Syq193411 	*mpp = bp;
27093227Syq193411 }
27103227Syq193411 
27113227Syq193411 
27123227Syq193411 /*
27133227Syq193411  * power management
27143227Syq193411  * ----------------
27153227Syq193411  *
27163227Syq193411  * usbsacm_create_pm_components:
27173227Syq193411  *	create PM components
27183227Syq193411  */
27193227Syq193411 static int
usbsacm_create_pm_components(usbsacm_state_t * acmp)27203227Syq193411 usbsacm_create_pm_components(usbsacm_state_t *acmp)
27213227Syq193411 {
27223227Syq193411 	dev_info_t	*dip = acmp->acm_dip;
27233227Syq193411 	usbsacm_pm_t	*pm;
27243227Syq193411 	uint_t		pwr_states;
27253227Syq193411 	usb_dev_descr_t *dev_descr;
27263227Syq193411 
27273227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
27283227Syq193411 	    "usbsacm_create_pm_components: ");
27293227Syq193411 
27303227Syq193411 	if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) {
27313227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
27323227Syq193411 		    "usbsacm_create_pm_components: failed");
27333227Syq193411 
27343227Syq193411 		return (USB_SUCCESS);
27353227Syq193411 	}
27363227Syq193411 
27373227Syq193411 	pm = acmp->acm_pm =
27383227Syq193411 	    (usbsacm_pm_t *)kmem_zalloc(sizeof (usbsacm_pm_t), KM_SLEEP);
27393227Syq193411 
27403227Syq193411 	pm->pm_pwr_states = (uint8_t)pwr_states;
27413227Syq193411 	pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
27423227Syq193411 	/*
27433227Syq193411 	 * Qualcomm CDMA card won't response the following control commands
27443227Syq193411 	 * after receive USB_REMOTE_WAKEUP_ENABLE. So we just set
27453227Syq193411 	 * pm_wakeup_enable to 0 for this specific device.
27463227Syq193411 	 */
27473227Syq193411 	dev_descr = acmp->acm_dev_data->dev_descr;
27483227Syq193411 	if (dev_descr->idVendor == 0x5c6 && dev_descr->idProduct == 0x3100) {
27493227Syq193411 		pm->pm_wakeup_enabled = 0;
27503227Syq193411 	} else {
27513227Syq193411 		pm->pm_wakeup_enabled = (usb_handle_remote_wakeup(dip,
27523227Syq193411 		    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS);
27533227Syq193411 	}
27543227Syq193411 
27553227Syq193411 	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
27563227Syq193411 
27573227Syq193411 	return (USB_SUCCESS);
27583227Syq193411 }
27593227Syq193411 
27603227Syq193411 
27613227Syq193411 /*
27623227Syq193411  * usbsacm_destroy_pm_components:
27633227Syq193411  *	destroy PM components
27643227Syq193411  */
27653227Syq193411 static void
usbsacm_destroy_pm_components(usbsacm_state_t * acmp)27663227Syq193411 usbsacm_destroy_pm_components(usbsacm_state_t *acmp)
27673227Syq193411 {
27683227Syq193411 	usbsacm_pm_t	*pm = acmp->acm_pm;
27693227Syq193411 	dev_info_t	*dip = acmp->acm_dip;
27703227Syq193411 	int		rval;
27713227Syq193411 
27723227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
27733227Syq193411 	    "usbsacm_destroy_pm_components: ");
27743227Syq193411 
27753227Syq193411 	if (acmp->acm_dev_state != USB_DEV_DISCONNECTED) {
27763227Syq193411 		if (pm->pm_wakeup_enabled) {
27773227Syq193411 			rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
27783227Syq193411 			if (rval != DDI_SUCCESS) {
27793227Syq193411 				USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
27803227Syq193411 				    "usbsacm_destroy_pm_components: "
27813227Syq193411 				    "raising power failed (%d)", rval);
27823227Syq193411 			}
27833227Syq193411 
27843227Syq193411 			rval = usb_handle_remote_wakeup(dip,
27853227Syq193411 			    USB_REMOTE_WAKEUP_DISABLE);
27863227Syq193411 			if (rval != USB_SUCCESS) {
27873227Syq193411 				USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
27883227Syq193411 				    "usbsacm_destroy_pm_components: "
27893227Syq193411 				    "disable remote wakeup failed (%d)", rval);
27903227Syq193411 			}
27913227Syq193411 		}
27923227Syq193411 
27933227Syq193411 		(void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
27943227Syq193411 	}
27953227Syq193411 	kmem_free((caddr_t)pm, sizeof (usbsacm_pm_t));
27963227Syq193411 	acmp->acm_pm = NULL;
27973227Syq193411 }
27983227Syq193411 
27993227Syq193411 
28003227Syq193411 /*
28013227Syq193411  * usbsacm_pm_set_busy:
28023227Syq193411  *	mark device busy and raise power
28033227Syq193411  */
28043227Syq193411 static void
usbsacm_pm_set_busy(usbsacm_state_t * acmp)28053227Syq193411 usbsacm_pm_set_busy(usbsacm_state_t *acmp)
28063227Syq193411 {
28073227Syq193411 	usbsacm_pm_t	*pm = acmp->acm_pm;
28083227Syq193411 	dev_info_t	*dip = acmp->acm_dip;
28093227Syq193411 	int		rval;
28103227Syq193411 
28113227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
28126898Sfb209375 	    "usbsacm_pm_set_busy: pm = 0x%p", (void *)pm);
28133227Syq193411 
28143227Syq193411 	if (pm == NULL) {
28153227Syq193411 
28163227Syq193411 		return;
28173227Syq193411 	}
28183227Syq193411 
28193227Syq193411 	mutex_enter(&acmp->acm_mutex);
28203227Syq193411 	/* if already marked busy, just increment the counter */
28213227Syq193411 	if (pm->pm_busy_cnt++ > 0) {
28223227Syq193411 		mutex_exit(&acmp->acm_mutex);
28233227Syq193411 
28243227Syq193411 		return;
28253227Syq193411 	}
28263227Syq193411 
28273227Syq193411 	(void) pm_busy_component(dip, 0);
28283227Syq193411 
28293227Syq193411 	if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) {
28303227Syq193411 		mutex_exit(&acmp->acm_mutex);
28313227Syq193411 
28323227Syq193411 		return;
28333227Syq193411 	}
28343227Syq193411 
28353227Syq193411 	/* need to raise power	*/
28363227Syq193411 	pm->pm_raise_power = B_TRUE;
28373227Syq193411 	mutex_exit(&acmp->acm_mutex);
28383227Syq193411 
28393227Syq193411 	rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
28403227Syq193411 	if (rval != DDI_SUCCESS) {
28413227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
28423227Syq193411 		    "usbsacm_pm_set_busy: raising power failed");
28433227Syq193411 	}
28443227Syq193411 
28453227Syq193411 	mutex_enter(&acmp->acm_mutex);
28463227Syq193411 	pm->pm_raise_power = B_FALSE;
28473227Syq193411 	mutex_exit(&acmp->acm_mutex);
28483227Syq193411 }
28493227Syq193411 
28503227Syq193411 
28513227Syq193411 /*
28523227Syq193411  * usbsacm_pm_set_idle:
28533227Syq193411  *	mark device idle
28543227Syq193411  */
28553227Syq193411 static void
usbsacm_pm_set_idle(usbsacm_state_t * acmp)28563227Syq193411 usbsacm_pm_set_idle(usbsacm_state_t *acmp)
28573227Syq193411 {
28583227Syq193411 	usbsacm_pm_t	*pm = acmp->acm_pm;
28593227Syq193411 	dev_info_t	*dip = acmp->acm_dip;
28603227Syq193411 
28613227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
28623227Syq193411 	    "usbsacm_pm_set_idle: ");
28633227Syq193411 
28643227Syq193411 	if (pm == NULL) {
28653227Syq193411 
28663227Syq193411 		return;
28673227Syq193411 	}
28683227Syq193411 
28693227Syq193411 	/*
28703227Syq193411 	 * if more ports use the device, do not mark as yet
28713227Syq193411 	 */
28723227Syq193411 	mutex_enter(&acmp->acm_mutex);
28733227Syq193411 	if (--pm->pm_busy_cnt > 0) {
28743227Syq193411 		mutex_exit(&acmp->acm_mutex);
28753227Syq193411 
28763227Syq193411 		return;
28773227Syq193411 	}
28783227Syq193411 
28793227Syq193411 	if (pm) {
28803227Syq193411 		(void) pm_idle_component(dip, 0);
28813227Syq193411 	}
28823227Syq193411 	mutex_exit(&acmp->acm_mutex);
28833227Syq193411 }
28843227Syq193411 
28853227Syq193411 
28863227Syq193411 /*
28873227Syq193411  * usbsacm_pwrlvl0:
28883227Syq193411  *	Functions to handle power transition for OS levels 0 -> 3
28893227Syq193411  *	The same level as OS state, different from USB state
28903227Syq193411  */
28913227Syq193411 static int
usbsacm_pwrlvl0(usbsacm_state_t * acmp)28923227Syq193411 usbsacm_pwrlvl0(usbsacm_state_t *acmp)
28933227Syq193411 {
28943227Syq193411 	int		rval;
28953227Syq193411 	int		i;
28963227Syq193411 	usbsacm_port_t	*cur_port = acmp->acm_ports;
28973227Syq193411 
28983227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
28993227Syq193411 	    "usbsacm_pwrlvl0: ");
29003227Syq193411 
29013227Syq193411 	switch (acmp->acm_dev_state) {
29023227Syq193411 	case USB_DEV_ONLINE:
29033227Syq193411 		/* issue USB D3 command to the device */
29043227Syq193411 		rval = usb_set_device_pwrlvl3(acmp->acm_dip);
29053227Syq193411 		ASSERT(rval == USB_SUCCESS);
29063227Syq193411 
29073227Syq193411 		if (cur_port != NULL) {
29085541Slg150142 			for (i = 0; i < acmp->acm_port_cnt; i++) {
29095541Slg150142 				cur_port = &acmp->acm_ports[i];
29105541Slg150142 				if (cur_port->acm_intr_ph != NULL &&
29115541Slg150142 				    cur_port->acm_port_state !=
29125541Slg150142 				    USBSACM_PORT_CLOSED) {
29135541Slg150142 
29145541Slg150142 					mutex_exit(&acmp->acm_mutex);
29155541Slg150142 					usb_pipe_stop_intr_polling(
29165541Slg150142 					    cur_port->acm_intr_ph,
29175541Slg150142 					    USB_FLAGS_SLEEP);
29185541Slg150142 					mutex_enter(&acmp->acm_mutex);
29195541Slg150142 
29205541Slg150142 					mutex_enter(&cur_port->acm_port_mutex);
29215541Slg150142 					cur_port->acm_intr_state =
29225541Slg150142 					    USBSACM_PIPE_IDLE;
29235541Slg150142 					mutex_exit(&cur_port->acm_port_mutex);
29245541Slg150142 				}
29253227Syq193411 			}
29263227Syq193411 		}
29273227Syq193411 
29283227Syq193411 		acmp->acm_dev_state = USB_DEV_PWRED_DOWN;
29293227Syq193411 		acmp->acm_pm->pm_cur_power = USB_DEV_OS_PWR_OFF;
29303227Syq193411 
29313227Syq193411 		/* FALLTHRU */
29323227Syq193411 	case USB_DEV_DISCONNECTED:
29333227Syq193411 	case USB_DEV_SUSPENDED:
29343227Syq193411 		/* allow a disconnect/cpr'ed device to go to lower power */
29353227Syq193411 
29363227Syq193411 		return (USB_SUCCESS);
29373227Syq193411 	case USB_DEV_PWRED_DOWN:
29383227Syq193411 	default:
29393227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
29403227Syq193411 		    "usbsacm_pwrlvl0: illegal device state");
29413227Syq193411 
29423227Syq193411 		return (USB_FAILURE);
29433227Syq193411 	}
29443227Syq193411 }
29453227Syq193411 
29463227Syq193411 
29473227Syq193411 /*
29483227Syq193411  * usbsacm_pwrlvl1:
29493227Syq193411  *	Functions to handle power transition for OS levels 1 -> 2
29503227Syq193411  */
29513227Syq193411 static int
usbsacm_pwrlvl1(usbsacm_state_t * acmp)29523227Syq193411 usbsacm_pwrlvl1(usbsacm_state_t *acmp)
29533227Syq193411 {
29543227Syq193411 	/* issue USB D2 command to the device */
29553227Syq193411 	(void) usb_set_device_pwrlvl2(acmp->acm_dip);
29563227Syq193411 
29573227Syq193411 	return (USB_FAILURE);
29583227Syq193411 }
29593227Syq193411 
29603227Syq193411 
29613227Syq193411 /*
29623227Syq193411  * usbsacm_pwrlvl2:
29633227Syq193411  *	Functions to handle power transition for OS levels 2 -> 1
29643227Syq193411  */
29653227Syq193411 static int
usbsacm_pwrlvl2(usbsacm_state_t * acmp)29663227Syq193411 usbsacm_pwrlvl2(usbsacm_state_t *acmp)
29673227Syq193411 {
29683227Syq193411 	/* issue USB D1 command to the device */
29693227Syq193411 	(void) usb_set_device_pwrlvl1(acmp->acm_dip);
29703227Syq193411 
29713227Syq193411 	return (USB_FAILURE);
29723227Syq193411 }
29733227Syq193411 
29743227Syq193411 
29753227Syq193411 /*
29763227Syq193411  * usbsacm_pwrlvl3:
29773227Syq193411  *	Functions to handle power transition for OS levels 3 -> 0
29783227Syq193411  *	The same level as OS state, different from USB state
29793227Syq193411  */
29803227Syq193411 static int
usbsacm_pwrlvl3(usbsacm_state_t * acmp)29813227Syq193411 usbsacm_pwrlvl3(usbsacm_state_t *acmp)
29823227Syq193411 {
29833227Syq193411 	int		rval;
29843227Syq193411 	int		i;
29853227Syq193411 	usbsacm_port_t	*cur_port = acmp->acm_ports;
29863227Syq193411 
29873227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
29883227Syq193411 	    "usbsacm_pwrlvl3: ");
29893227Syq193411 
29903227Syq193411 	switch (acmp->acm_dev_state) {
29913227Syq193411 	case USB_DEV_PWRED_DOWN:
29923227Syq193411 		/* Issue USB D0 command to the device here */
29933227Syq193411 		rval = usb_set_device_pwrlvl0(acmp->acm_dip);
29943227Syq193411 		ASSERT(rval == USB_SUCCESS);
29953227Syq193411 
29963227Syq193411 		if (cur_port != NULL) {
29975541Slg150142 			for (i = 0; i < acmp->acm_port_cnt; i++) {
29985541Slg150142 				cur_port = &acmp->acm_ports[i];
29995541Slg150142 				if (cur_port->acm_intr_ph != NULL &&
30005541Slg150142 				    cur_port->acm_port_state !=
30015541Slg150142 				    USBSACM_PORT_CLOSED) {
30025541Slg150142 
30035541Slg150142 					mutex_exit(&acmp->acm_mutex);
30045541Slg150142 					usbsacm_pipe_start_polling(cur_port);
30055541Slg150142 					mutex_enter(&acmp->acm_mutex);
30065541Slg150142 				}
30073227Syq193411 			}
30083227Syq193411 		}
30093227Syq193411 
30103227Syq193411 		acmp->acm_dev_state = USB_DEV_ONLINE;
30113227Syq193411 		acmp->acm_pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
30123227Syq193411 
30133227Syq193411 		/* FALLTHRU */
30143227Syq193411 	case USB_DEV_ONLINE:
30153227Syq193411 		/* we are already in full power */
30163227Syq193411 
30173227Syq193411 		/* FALLTHRU */
30183227Syq193411 	case USB_DEV_DISCONNECTED:
30193227Syq193411 	case USB_DEV_SUSPENDED:
30203227Syq193411 
30213227Syq193411 		return (USB_SUCCESS);
30223227Syq193411 	default:
30233227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
30243227Syq193411 		    "usbsacm_pwrlvl3: illegal device state");
30253227Syq193411 
30263227Syq193411 		return (USB_FAILURE);
30273227Syq193411 	}
30283227Syq193411 }
30293227Syq193411 
30303227Syq193411 
30313227Syq193411 /*
30323227Syq193411  * usbsacm_pipe_start_polling:
30333227Syq193411  *	start polling on the interrupt pipe
30343227Syq193411  */
30353227Syq193411 static void
usbsacm_pipe_start_polling(usbsacm_port_t * acm_port)30363227Syq193411 usbsacm_pipe_start_polling(usbsacm_port_t *acm_port)
30373227Syq193411 {
30383227Syq193411 	usb_intr_req_t	*intr;
30393227Syq193411 	int		rval;
30403227Syq193411 	usbsacm_state_t	*acmp = acm_port->acm_device;
30413227Syq193411 
30423227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
30433227Syq193411 	    "usbsacm_pipe_start_polling: ");
30443227Syq193411 
30453227Syq193411 	if (acm_port->acm_intr_ph == NULL) {
30463227Syq193411 
30473227Syq193411 		return;
30483227Syq193411 	}
30493227Syq193411 
30503227Syq193411 	intr = usb_alloc_intr_req(acmp->acm_dip, 0, USB_FLAGS_SLEEP);
30513227Syq193411 
30523227Syq193411 	/*
30533227Syq193411 	 * If it is in interrupt context, usb_alloc_intr_req will return NULL if
30543227Syq193411 	 * called with SLEEP flag.
30553227Syq193411 	 */
30563227Syq193411 	if (!intr) {
30573227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
30583227Syq193411 		    "usbsacm_pipe_start_polling: alloc req failed.");
30593227Syq193411 
30603227Syq193411 		return;
30613227Syq193411 	}
30623227Syq193411 
30633227Syq193411 	/* initialize the interrupt request. */
30643227Syq193411 	intr->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
30655541Slg150142 	    USB_ATTRS_AUTOCLEARING;
30663227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
30673227Syq193411 	intr->intr_len = acm_port->acm_intr_ep_descr.wMaxPacketSize;
30683227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
30693227Syq193411 	intr->intr_client_private = (usb_opaque_t)acm_port;
30703227Syq193411 	intr->intr_cb = usbsacm_intr_cb;
30713227Syq193411 	intr->intr_exc_cb = usbsacm_intr_ex_cb;
30723227Syq193411 
30733227Syq193411 	rval = usb_pipe_intr_xfer(acm_port->acm_intr_ph, intr, USB_FLAGS_SLEEP);
30743227Syq193411 
30753227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
30763227Syq193411 	if (rval == USB_SUCCESS) {
30773227Syq193411 		acm_port->acm_intr_state = USBSACM_PIPE_BUSY;
30783227Syq193411 	} else {
30793227Syq193411 		usb_free_intr_req(intr);
30803227Syq193411 		acm_port->acm_intr_state = USBSACM_PIPE_IDLE;
30813227Syq193411 		USB_DPRINTF_L3(PRINT_MASK_OPEN, acmp->acm_lh,
30823227Syq193411 		    "usbsacm_pipe_start_polling: failed (%d)", rval);
30833227Syq193411 	}
30843227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
30853227Syq193411 }
30863227Syq193411 
30873227Syq193411 
30883227Syq193411 /*
30893227Syq193411  * usbsacm_intr_cb:
30903227Syq193411  *	interrupt pipe normal callback
30913227Syq193411  */
30923227Syq193411 /*ARGSUSED*/
30933227Syq193411 static void
usbsacm_intr_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)30943227Syq193411 usbsacm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
30953227Syq193411 {
30963227Syq193411 	usbsacm_port_t	*acm_port = (usbsacm_port_t *)req->intr_client_private;
30973227Syq193411 	usbsacm_state_t	*acmp = acm_port->acm_device;
30983227Syq193411 	mblk_t		*data = req->intr_data;
30993227Syq193411 	int		data_len;
31003227Syq193411 
31013227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_CB, acmp->acm_lh,
31023227Syq193411 	    "usbsacm_intr_cb: ");
31033227Syq193411 
31043227Syq193411 	data_len = (data) ? MBLKL(data) : 0;
31053227Syq193411 
31063227Syq193411 	/* check data length */
31073227Syq193411 	if (data_len < 8) {
31083227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_CB, acmp->acm_lh,
31093227Syq193411 		    "usbsacm_intr_cb: %d packet too short", data_len);
31103227Syq193411 		usb_free_intr_req(req);
31113227Syq193411 
31123227Syq193411 		return;
31133227Syq193411 	}
31143227Syq193411 	req->intr_data = NULL;
31153227Syq193411 	usb_free_intr_req(req);
31163227Syq193411 
31173227Syq193411 	mutex_enter(&acm_port->acm_port_mutex);
31183227Syq193411 	/* parse interrupt data. */
31193227Syq193411 	usbsacm_parse_intr_data(acm_port, data);
31203227Syq193411 	mutex_exit(&acm_port->acm_port_mutex);
31213227Syq193411 }
31223227Syq193411 
31233227Syq193411 
31243227Syq193411 /*
31253227Syq193411  * usbsacm_intr_ex_cb:
31263227Syq193411  *	interrupt pipe exception callback
31273227Syq193411  */
31283227Syq193411 /*ARGSUSED*/
31293227Syq193411 static void
usbsacm_intr_ex_cb(usb_pipe_handle_t ph,usb_intr_req_t * req)31303227Syq193411 usbsacm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
31313227Syq193411 {
31323227Syq193411 	usbsacm_port_t	*acm_port = (usbsacm_port_t *)req->intr_client_private;
31333227Syq193411 	usbsacm_state_t	*acmp = acm_port->acm_device;
31343227Syq193411 	usb_cr_t	cr = req->intr_completion_reason;
31353227Syq193411 
31363227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_CB, acmp->acm_lh,
31373227Syq193411 	    "usbsacm_intr_ex_cb: ");
31383227Syq193411 
31393227Syq193411 	usb_free_intr_req(req);
31403227Syq193411 
31413227Syq193411 	/*
31423227Syq193411 	 * If completion reason isn't USB_CR_PIPE_CLOSING and
31433227Syq193411 	 * USB_CR_STOPPED_POLLING, restart polling.
31443227Syq193411 	 */
31453227Syq193411 	if ((cr != USB_CR_PIPE_CLOSING) && (cr != USB_CR_STOPPED_POLLING)) {
31463227Syq193411 		mutex_enter(&acmp->acm_mutex);
31473227Syq193411 
31483227Syq193411 		if (acmp->acm_dev_state != USB_DEV_ONLINE) {
31493227Syq193411 
31503227Syq193411 			USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
31513227Syq193411 			    "usbsacm_intr_ex_cb: state = %d",
31523227Syq193411 			    acmp->acm_dev_state);
31533227Syq193411 
31543227Syq193411 			mutex_exit(&acmp->acm_mutex);
31553227Syq193411 
31563227Syq193411 			return;
31573227Syq193411 		}
31583227Syq193411 		mutex_exit(&acmp->acm_mutex);
31593227Syq193411 
31603227Syq193411 		usbsacm_pipe_start_polling(acm_port);
31613227Syq193411 	}
31623227Syq193411 }
31633227Syq193411 
31643227Syq193411 
31653227Syq193411 /*
31663227Syq193411  * usbsacm_parse_intr_data:
31673227Syq193411  *	Parse data received from interrupt callback
31683227Syq193411  */
31693227Syq193411 static void
usbsacm_parse_intr_data(usbsacm_port_t * acm_port,mblk_t * data)31703227Syq193411 usbsacm_parse_intr_data(usbsacm_port_t *acm_port, mblk_t *data)
31713227Syq193411 {
31723227Syq193411 	usbsacm_state_t	*acmp = acm_port->acm_device;
31733227Syq193411 	uint8_t		bmRequestType;
31743227Syq193411 	uint8_t		bNotification;
31753227Syq193411 	uint16_t	wValue;
31763227Syq193411 	uint16_t	wLength;
31773227Syq193411 	uint16_t	wData;
31783227Syq193411 
31793227Syq193411 	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
31803227Syq193411 	    "usbsacm_parse_intr_data: ");
31813227Syq193411 
31823227Syq193411 	bmRequestType = data->b_rptr[0];
31833227Syq193411 	bNotification = data->b_rptr[1];
31843227Syq193411 	/*
31853227Syq193411 	 * If Notification type is NETWORK_CONNECTION, wValue is 0 or 1,
31863227Syq193411 	 * mLength is 0. If Notification type is SERIAL_TYPE, mValue is 0,
31873227Syq193411 	 * mLength is 2. So we directly get the value from the byte.
31883227Syq193411 	 */
31893227Syq193411 	wValue = data->b_rptr[2];
31903227Syq193411 	wLength = data->b_rptr[6];
31913227Syq193411 
31923227Syq193411 	if (bmRequestType != USB_CDC_NOTIFICATION_REQUEST_TYPE) {
31933227Syq193411 		USB_DPRINTF_L2(PRINT_MASK_CB, acmp->acm_lh,
31943227Syq193411 		    "usbsacm_parse_intr_data: unknown request type - 0x%x",
31953227Syq193411 		    bmRequestType);
31963227Syq193411 
3197*12024SRaymond.Chen@Sun.COM 		freemsg(data);
3198*12024SRaymond.Chen@Sun.COM 
31993227Syq193411 		return;
32003227Syq193411 	}
32013227Syq193411 
32023227Syq193411 	/*
32033227Syq193411 	 * Check the return value of device
32043227Syq193411 	 */
32053227Syq193411 	switch (bNotification) {
32063227Syq193411 	case USB_CDC_NOTIFICATION_NETWORK_CONNECTION:
32073227Syq193411 		USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32083227Syq193411 		    "usbsacm_parse_intr_data: %s network!",
32093227Syq193411 		    wValue ? "connected to" :"disconnected from");
32103227Syq193411 
32113227Syq193411 		break;
32123227Syq193411 	case USB_CDC_NOTIFICATION_RESPONSE_AVAILABLE:
32133227Syq193411 		USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32143227Syq193411 		    "usbsacm_parse_intr_data: A response is a available.");
32153227Syq193411 
32163227Syq193411 		break;
32173227Syq193411 	case USB_CDC_NOTIFICATION_SERIAL_STATE:
32183227Syq193411 		/* check the parameter's length. */
32193227Syq193411 		if (wLength != 2) {
32203227Syq193411 
32213227Syq193411 			USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32223227Syq193411 			    "usbsacm_parse_intr_data: error data length.");
32233227Syq193411 		} else {
32243227Syq193411 			/*
32253227Syq193411 			 * The Data field is a bitmapped value that contains
32263227Syq193411 			 * the current state of carrier detect, transmission
32273227Syq193411 			 * carrier, break, ring signal and device overrun
32283227Syq193411 			 * error.
32293227Syq193411 			 */
32303227Syq193411 			wData = data->b_rptr[8];
32313227Syq193411 			/*
32323227Syq193411 			 * Check the serial state of the current port.
32333227Syq193411 			 */
32343227Syq193411 			if (wData & USB_CDC_ACM_CONTROL_DCD) {
32353227Syq193411 
32363227Syq193411 				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32373227Syq193411 				    "usbsacm_parse_intr_data: "
32383227Syq193411 				    "receiver carrier is set.");
32393227Syq193411 			}
32403227Syq193411 			if (wData & USB_CDC_ACM_CONTROL_DSR) {
32413227Syq193411 
32423227Syq193411 				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32433227Syq193411 				    "usbsacm_parse_intr_data: "
32443227Syq193411 				    "transmission carrier is set.");
32453227Syq193411 
32463227Syq193411 				acm_port->acm_mctlin |= USB_CDC_ACM_CONTROL_DSR;
32473227Syq193411 			}
32483227Syq193411 			if (wData & USB_CDC_ACM_CONTROL_BREAK) {
32493227Syq193411 
32503227Syq193411 				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32513227Syq193411 				    "usbsacm_parse_intr_data: "
32523227Syq193411 				    "break detection mechanism is set.");
32533227Syq193411 			}
32543227Syq193411 			if (wData & USB_CDC_ACM_CONTROL_RNG) {
32553227Syq193411 
32563227Syq193411 				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32573227Syq193411 				    "usbsacm_parse_intr_data: "
32583227Syq193411 				    "ring signal detection is set.");
32593227Syq193411 
32603227Syq193411 				acm_port->acm_mctlin |= USB_CDC_ACM_CONTROL_RNG;
32613227Syq193411 			}
32623227Syq193411 			if (wData & USB_CDC_ACM_CONTROL_FRAMING) {
32633227Syq193411 
32643227Syq193411 				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32653227Syq193411 				    "usbsacm_parse_intr_data: "
32663227Syq193411 				    "A framing error has occurred.");
32673227Syq193411 			}
32683227Syq193411 			if (wData & USB_CDC_ACM_CONTROL_PARITY) {
32693227Syq193411 
32703227Syq193411 				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32713227Syq193411 				    "usbsacm_parse_intr_data: "
32723227Syq193411 				    "A parity error has occurred.");
32733227Syq193411 			}
32743227Syq193411 			if (wData & USB_CDC_ACM_CONTROL_OVERRUN) {
32753227Syq193411 
32763227Syq193411 				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32773227Syq193411 				    "usbsacm_parse_intr_data: "
32783227Syq193411 				    "Received data has been discarded "
32793227Syq193411 				    "due to overrun.");
32803227Syq193411 			}
32813227Syq193411 		}
32823227Syq193411 
32833227Syq193411 		break;
32843227Syq193411 	default:
32853227Syq193411 		USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
32863227Syq193411 		    "usbsacm_parse_intr_data: unknown notification - 0x%x!",
32873227Syq193411 		    bNotification);
32883227Syq193411 
32893227Syq193411 		break;
32903227Syq193411 	}
32913227Syq193411 
32923227Syq193411 	freemsg(data);
32933227Syq193411 }
3294