6428204 CDC ACM class driver and devices with modem function need to be supported
authoryq193411
Tue, 12 Dec 2006 23:36:51 -0800
changeset 3227 9602d8b74112
parent 3226 955c53c12aab
child 3228 8df08477df6f
6428204 CDC ACM class driver and devices with modem function need to be supported
usr/src/cmd/netadm/iu.ap.sh
usr/src/pkgdefs/Makefile
usr/src/pkgdefs/SUNWuacm/Makefile
usr/src/pkgdefs/SUNWuacm/depend
usr/src/pkgdefs/SUNWuacm/pkginfo.tmpl
usr/src/pkgdefs/SUNWuacm/postinstall
usr/src/pkgdefs/SUNWuacm/preremove
usr/src/pkgdefs/SUNWuacm/prototype_com
usr/src/pkgdefs/SUNWuacm/prototype_i386
usr/src/pkgdefs/SUNWuacm/prototype_sparc
usr/src/pkgdefs/SUNWusbu/prototype_com
usr/src/pkgdefs/common_files/i.iuap
usr/src/uts/common/Makefile.files
usr/src/uts/common/Makefile.rules
usr/src/uts/common/io/usb/clients/usbser/usbsacm/usbsacm.c
usr/src/uts/common/io/usb/inc.flg
usr/src/uts/common/io/warlock/usbsacm.wlcmd
usr/src/uts/common/io/warlock/usbsacm_with_usba.wlcmd
usr/src/uts/common/sys/Makefile
usr/src/uts/common/sys/Makefile.syshdrs
usr/src/uts/common/sys/usb/clients/usbcdc/usb_cdc.h
usr/src/uts/common/sys/usb/clients/usbser/usbsacm/usbsacm.h
usr/src/uts/intel/Makefile.intel.shared
usr/src/uts/intel/usbsacm/Makefile
usr/src/uts/intel/warlock/Makefile
usr/src/uts/sparc/Makefile.sparc.shared
usr/src/uts/sparc/usbsacm/Makefile
usr/src/uts/sparc/warlock/Makefile
--- a/usr/src/cmd/netadm/iu.ap.sh	Tue Dec 12 23:13:49 2006 -0800
+++ b/usr/src/cmd/netadm/iu.ap.sh	Tue Dec 12 23:36:51 2006 -0800
@@ -38,6 +38,7 @@
 	wc	0	0	ldterm ttcompat
 	asy	-1	0	ldterm ttcompat
 	ptsl	0	47	ldterm ttcompat
+	usbsacm -1	0	ldterm ttcompat
 	rts	-1	0	rts [anchor]
 	ipsecesp -1	0	ipsecesp
 	ipsecah	-1	0	ipsecah
@@ -65,6 +66,7 @@
 	su	16385	0	ldterm ttcompat
 	su	131072	131073	ldterm ttcompat
 	usbser_edge -1	0	ldterm ttcompat
+	usbsacm -1	0	ldterm ttcompat
 	rts	-1	0	rts [anchor]
 	ttymux	-1	0	ldterm ttcompat
 	ipsecesp -1	0	ipsecesp
--- a/usr/src/pkgdefs/Makefile	Tue Dec 12 23:13:49 2006 -0800
+++ b/usr/src/pkgdefs/Makefile	Tue Dec 12 23:36:51 2006 -0800
@@ -359,6 +359,7 @@
 	SUNWugen \
 	SUNWugenu \
 	SUNWuprl \
+	SUNWuacm \
 	SUNWusb \
 	SUNWusbs \
 	SUNWusbu \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWuacm/Makefile	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+ 
+include ../Makefile.com
+
+.KEEP_STATE:
+
+all: $(FILES) depend postinstall preremove
+install: all pkg
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWuacm/depend	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,53 @@
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# This package information file defines software dependencies associated
+# with the pkg.  You can define three types of pkg dependencies with this file:
+#	 P indicates a prerequisite for installation
+#	 I indicates an incompatible package
+#	 R indicates a reverse dependency
+# <pkg.abbr> see pkginfo(4), PKG parameter
+# <name> see pkginfo(4), NAME parameter
+# <version> see pkginfo(4), VERSION parameter
+# <arch> see pkginfo(4), ARCH parameter
+# <type> <pkg.abbr> <name>
+# 	(<arch>)<version>
+# 	(<arch>)<version>
+# 	...
+# <type> <pkg.abbr> <name>
+# ...
+#
+
+P SUNWcar	Core Architecture, (Root)
+P SUNWcakr	Core Solaris Kernel Architecture (Root)
+P SUNWkvm	Core Architecture, (Kvm)
+P SUNWcsr	Core Solaris, (Root)
+P SUNWckr	Core Solaris Kernel (Root)
+P SUNWcnetr	Core Solaris Network Infrastructure (Root)
+P SUNWcsu	Core Solaris, (Usr)
+P SUNWcsd	Core Solaris Devices
+P SUNWcsl	Core Solaris Libraries
+P SUNWusb	USB Device Drivers
+P SUNWusbs	USB generic serial module 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWuacm/pkginfo.tmpl	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,48 @@
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWuacm"
+NAME="CDC ACM USB-to-serial driver"
+ARCH="ISA"
+CATEGORY="system"
+BASEDIR=/
+SUNW_PKGTYPE="root"
+CLASSES="none"
+DESC="CDC ACM USB-to-serial driver"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+VERSION="ONVERS,REV=0.0.0"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+MAXINST="1000"
+SUNW_PKGVERS="1.0"
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="true"
+SUNW_PKG_THISZONE="false"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWuacm/postinstall	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+ 
+PATH="/usr/bin:/usr/sbin:${PATH}"
+export PATH
+
+if [ "${BASEDIR:=/}" != "/" ] 
+then
+	BASEDIR_OPT="-b $BASEDIR" 
+fi
+
+installed() {
+	driver=$1
+	grep "^${driver} " $BASEDIR/etc/name_to_major > /dev/null 2>&1
+
+	return $?
+}
+
+EXIT=0
+
+#"usbc88,17da"	EVDO card of Kyocera KPC650
+#"usb1410,1110"	EVDO card of Novatel Merlin V620
+USBSACM_ALIASES="\
+	\"usb,class2.2.0\" \
+	\"usbif,class2.2\" \
+	\"usbc88,17da\" \
+	\"usb1410,1110\" \
+	"
+
+if installed usbsacm ; then
+	echo "usbsacm already installed" || EXIT=1
+
+else
+	add_drv ${BASEDIR_OPT} -m '* 0666 root sys' \
+	    -i "${USBSACM_ALIASES}" -n usbsacm || \
+	    EXIT=1
+fi
+
+exit $EXIT
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWuacm/preremove	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,39 @@
+#! /bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+ 
+PATH="/usr/bin:/usr/sbin:${PATH}"
+export PATH
+
+EXIT=0
+
+if grep -w usbsacm ${BASEDIR}/etc/name_to_major > /dev/null 2>&1
+then
+	rem_drv -b ${BASEDIR} usbsacm || EXIT=1
+fi
+
+exit $EXIT
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWuacm/prototype_com	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#ident	"%Z%%M%	%I%	%E% SMI"
+
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# SUNWuacm
+#
+i copyright
+i depend
+i pkginfo
+i postinstall
+i preremove
+d none kernel 0755 root sys
+d none kernel/drv 0755 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWuacm/prototype_i386	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,52 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+# List files which are i386 specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWuacm
+#
+f none kernel/drv/usbsacm 0755 root sys
+d none kernel/drv/amd64 0755 root sys
+f none kernel/drv/amd64/usbsacm 0755 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWuacm/prototype_sparc	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,52 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#ident	"%Z%%M%	%I%	%E% SMI"
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWuacm
+d none kernel/drv/sparcv9 0755 root sys
+f none kernel/drv/sparcv9/usbsacm 0755 root sys
--- a/usr/src/pkgdefs/SUNWusbu/prototype_com	Tue Dec 12 23:13:49 2006 -0800
+++ b/usr/src/pkgdefs/SUNWusbu/prototype_com	Tue Dec 12 23:36:51 2006 -0800
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -20,8 +19,8 @@
 # CDDL HEADER END
 #
 #
-# Copyright (c) 1998-2001 by Sun Microsystems, Inc.
-# All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
 #
 
 # ident	"%Z%%M%	%I%	%E% SMI"
@@ -57,6 +56,7 @@
 d none usr/include/sys/usb/clients/hid 755 root bin
 d none usr/include/sys/usb/clients/mass_storage 755 root bin
 d none usr/include/sys/usb/clients/printer 755 root bin
+d none usr/include/sys/usb/clients/usbcdc 755 root bin
 f none usr/include/sys/usb/usbai.h 644 root bin
 f none usr/include/sys/usb/usba.h 644 root bin
 f none usr/include/sys/usb/hubd/hub.h 644 root bin
@@ -65,3 +65,4 @@
 f none usr/include/sys/usb/clients/mass_storage/usb_bulkonly.h 644 root bin
 f none usr/include/sys/usb/clients/mass_storage/usb_cbi.h 644 root bin
 f none usr/include/sys/usb/clients/printer/usb_printer.h 644 root bin
+f none usr/include/sys/usb/clients/usbcdc/usbcdc.h 644 root bin
--- a/usr/src/pkgdefs/common_files/i.iuap	Tue Dec 12 23:13:49 2006 -0800
+++ b/usr/src/pkgdefs/common_files/i.iuap	Tue Dec 12 23:36:51 2006 -0800
@@ -3,9 +3,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -23,7 +22,7 @@
 #
 #ident	"%Z%%M%	%I%	%E% SMI"
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -71,6 +70,7 @@
 		add_driver_replace se
 		add_driver_replace su
 		add_driver usbser_edge
+		add_driver usbsacm
 		add_driver_replace rts
 		add_driver ipsecah
 		add_driver ipsecesp
--- a/usr/src/uts/common/Makefile.files	Tue Dec 12 23:13:49 2006 -0800
+++ b/usr/src/uts/common/Makefile.files	Tue Dec 12 23:36:51 2006 -0800
@@ -608,6 +608,8 @@
 
 USBSER_OBJS += usbser.o usbser_rseq.o
 
+USBSACM_OBJS += usbsacm.o
+
 USBSER_KEYSPAN_OBJS += usbser_keyspan.o keyspan_dsd.o keyspan_pipe.o
 
 USBS49_FW_OBJS += keyspan_49fw.o
--- a/usr/src/uts/common/Makefile.rules	Tue Dec 12 23:13:49 2006 -0800
+++ b/usr/src/uts/common/Makefile.rules	Tue Dec 12 23:36:51 2006 -0800
@@ -691,6 +691,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/usb/clients/usbser/usbsacm/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/usb/clients/usbser/usbser_keyspan/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -1379,6 +1383,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/usb/clients/usbser/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/usb/clients/usbser/usbsacm/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/usb/clients/usbser/usbser_keyspan/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/clients/usbser/usbsacm/usbsacm.c	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,3258 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * USB Serial CDC ACM driver
+ *
+ * 1. General Concepts
+ * -------------------
+ *
+ * 1.1 Overview
+ * ------------
+ * This driver supports devices that comply with the USB Communication
+ * Device Class Abstract Control Model (USB CDC ACM) specification,
+ * which is available at http://www.usb.org. Given the broad nature
+ * of communication equipment, this driver supports the following
+ * types of devices:
+ * 	+ Telecommunications devices: analog modems, mobile phones;
+ * 	+ Networking devices: cable modems;
+ * Except the above mentioned acm devices, this driver also supports
+ * some devices which provide modem-like function and have pairs of
+ * bulk in/out pipes.
+ *
+ * There are three classes that make up the definition for communication
+ * devices: the Communication Device Class, the Communication Interface
+ * Class and the Data Interface Class. The Communication Device Class
+ * is a device level definition and is used by the host to properly
+ * identify a communication device that may present several different
+ * types of interfaces. The Communication Interface Class defines a
+ * general-purpose mechanism that can be used to enable all types of
+ * communication services on the Universal Serial Bus (USB). The Data
+ * Interface Class defines a general-purpose mechanism to enable bulk
+ * transfer on the USB when the data does not meet the requirements
+ * for any other class.
+ *
+ * 1.2 Interface Definitions
+ * -------------------------
+ * Communication Class Interface is used for device management and,
+ * optionally, call management. Device management includes the requests
+ * that manage the operational state of a device, the device responses,
+ * and event notifications. In Abstract Control Model, the device can
+ * provide an internal implementation of call management over the Data
+ * Class interface or the Communication Class interface.
+ *
+ * The Data Class defines a data interface as an interface with a class
+ * type of Data Class. Data transmission on a communication device is
+ * not restricted to interfaces using the Data Class. Rather, a data
+ * interface is used to transmit and/or receive data that is not
+ * defined by any other class. The data could be:
+ * 	+ Some form of raw data from a communication line.
+ * 	+ Legacy modem data.
+ * 	+ Data using a proprietary format.
+ *
+ * 1.3 Endpoint Requirements
+ * -------------------------
+ * The Communication Class interface requires one endpoint, the management
+ * element. Optionally, it can have an additional endpoint, the notification
+ * element. The management element uses the default endpoint for all
+ * standard and Communication Class-specific requests. The notification
+ * element normally uses an interrupt endpoint.
+ *
+ * The type of endpoints belonging to a Data Class interface are restricted
+ * to bulk, and are expected to exist in pairs of the same type (one In and
+ * one Out).
+ *
+ * 1.4 ACM Function Characteristics
+ * --------------------------------
+ * With Abstract Control Model, the USB device understands standard
+ * V.25ter (AT) commands. The device contains a Datapump and micro-
+ * controller that handles the AT commands and relay controls. The
+ * device uses both a Data Class interface and a Communication Class.
+ * interface.
+ *
+ * A Communication Class interface of type Abstract Control Model will
+ * consist of a minimum of two pipes; one is used to implement the
+ * management element and the other to implement a notification element.
+ * In addition, the device can use two pipes to implement channels over
+ * which to carry unspecified data, typically over a Data Class interface.
+ *
+ * 1.5 ACM Serial Emulation
+ * ------------------------
+ * The Abstract Control Model can bridge the gap between legacy modem
+ * devices and USB devices. To support certain types of legacy applications,
+ * two problems need to be addressed. The first is supporting specific
+ * legacy control signals and state variables which are addressed
+ * directly by the various carrier modulation standards. To support these
+ * requirement, additional requests and notifications have been created.
+ * Please refer to macro, beginning with USB_CDC_REQ_* and
+ * USB_CDC_NOTIFICATION_*.
+ *
+ * The second significant item which is needed to bridge the gap between
+ * legacy modem designs and the Abstract Control Model is a means to
+ * multiplex call control (AT commands) on the Data Class interface.
+ * Legacy modem designs are limited by only supporting one channel for
+ * both "AT" commands and the actual data. To allow this type of
+ * functionality, the device must have a means to specify this limitation
+ * to the host.
+ *
+ * When describing this type of device, the Communication Class interface
+ * would still specify a Abstract Control Model, but call control would
+ * actually occur over the Data Class interface. To describe this
+ * particular characteristic, the Call Management Functional Descriptor
+ * would have bit D1 of bmCapabilities set.
+ *
+ * 1.6 Other Bulk In/Out Devices
+ * -----------------------------
+ * Some devices don't conform to USB CDC specification, but they provide
+ * modem-like function and have pairs of bulk in/out pipes. This driver
+ * supports this kind of device and exports term nodes by their pipes.
+ *
+ * 2. Implementation
+ * -----------------
+ *
+ * 2.1 Overview
+ * ------------
+ * It is a device-specific driver (DSD) working with USB generic serial
+ * driver (GSD). It implements the USB-to-serial device-specific driver
+ * interface (DSDI) which is offered by GSD. The interface is defined
+ * by ds_ops_t structure.
+ *
+ * 2.2 Port States
+ * ---------------
+ * For USB CDC ACM devices, this driver is attached to its interface,
+ * and exports one port for each interface. For other modem-like devices,
+ * this driver can dynamically find the ports in the current device,
+ * and export one port for each pair bulk in/out pipes. Each port can
+ * be operated independently.
+ *
+ * port_state:
+ *
+ *		attach_ports
+ *		    |
+ *		    |
+ *		    |
+ *		    v
+ *	    USBSACM_PORT_CLOSED
+ *		|	    ^
+ *		|	    |
+ *		V	    |
+ *	   open_port	close_port
+ *		|	    ^
+ *		|	    |
+ *		V	    |
+ *	      USBSACM_PORT_OPEN
+ *
+ *
+ * 2.3 Pipe States
+ * ---------------
+ * Each port has its own bulk in/out pipes and some ports could also have
+ * its own interrupt pipes (traced by usbsacm_port structure), which are
+ * opened during attach. The pipe status is as following:
+ *
+ * pipe_state:
+ *
+ *		usbsacm_init_alloc_ports  usbsacm_free_ports
+ *				|		^
+ *				v		|
+ *	          |---->------ USBSACM_PORT_CLOSED ------>------+
+ *		  ^						|
+ *		  |				reconnect/resume/open_port
+ *		  |						|
+ *    disconnect/suspend/close_port				|
+ *		  |						v
+ *		  +------<------ USBSACM_PIPE_IDLE ------<------|
+ *				    |		|
+ *				    V		^
+ *				    |		|
+ *		  +-----------------+		+-----------+
+ *		  |					    |
+ *		  V					    ^
+ *		  |					    |
+ *	rx_start/tx_start----->------failed------->---------|
+ *		  |					    |
+ *		  |				bulkin_cb/bulkout_cb
+ *		  V					    |
+ *		  |					    ^
+ *		  |					    |
+ *		  +----->----- USBSACM_PIPE_BUSY ---->------+
+ *
+ *
+ * To get its status in a timely way, acm driver can get the status
+ * of the device by polling the interrupt pipe.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/termio.h>
+#include <sys/termiox.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/byteorder.h>
+#define	USBDRV_MAJOR_VER	2
+#define	USBDRV_MINOR_VER	0
+#include <sys/usb/usba.h>
+#include <sys/usb/usba/usba_types.h>
+#include <sys/usb/clients/usbser/usbser.h>
+#include <sys/usb/clients/usbser/usbser_dsdi.h>
+#include <sys/usb/clients/usbcdc/usb_cdc.h>
+#include <sys/usb/clients/usbser/usbsacm/usbsacm.h>
+
+/* devops entry points */
+static int	usbsacm_attach(dev_info_t *, ddi_attach_cmd_t);
+static int	usbsacm_detach(dev_info_t *, ddi_detach_cmd_t);
+static int 	usbsacm_getinfo(dev_info_t *, ddi_info_cmd_t, void *,
+		void **);
+static int	usbsacm_open(queue_t *, dev_t *, int, int, cred_t *);
+
+/* DSD operations */
+static int	usbsacm_ds_attach(ds_attach_info_t *);
+static void	usbsacm_ds_detach(ds_hdl_t);
+static int	usbsacm_ds_register_cb(ds_hdl_t, uint_t, ds_cb_t *);
+static void	usbsacm_ds_unregister_cb(ds_hdl_t, uint_t);
+static int	usbsacm_ds_open_port(ds_hdl_t, uint_t);
+static int	usbsacm_ds_close_port(ds_hdl_t, uint_t);
+
+/* standard UART operations */
+static int	usbsacm_ds_set_port_params(ds_hdl_t, uint_t,
+		ds_port_params_t *);
+static int	usbsacm_ds_set_modem_ctl(ds_hdl_t, uint_t, int, int);
+static int	usbsacm_ds_get_modem_ctl(ds_hdl_t, uint_t, int, int *);
+static int	usbsacm_ds_break_ctl(ds_hdl_t, uint_t, int);
+
+/* data xfer */
+static int	usbsacm_ds_tx(ds_hdl_t, uint_t, mblk_t *);
+static mblk_t	*usbsacm_ds_rx(ds_hdl_t, uint_t);
+static void	usbsacm_ds_stop(ds_hdl_t, uint_t, int);
+static void	usbsacm_ds_start(ds_hdl_t, uint_t, int);
+
+/* fifo operations */
+static int	usbsacm_ds_fifo_flush(ds_hdl_t, uint_t, int);
+static int	usbsacm_ds_fifo_drain(ds_hdl_t, uint_t, int);
+static int	usbsacm_wait_tx_drain(usbsacm_port_t *, int);
+static int	usbsacm_fifo_flush_locked(usbsacm_state_t *, uint_t, int);
+
+/* power management and CPR */
+static int	usbsacm_ds_suspend(ds_hdl_t);
+static int	usbsacm_ds_resume(ds_hdl_t);
+static int	usbsacm_ds_disconnect(ds_hdl_t);
+static int	usbsacm_ds_reconnect(ds_hdl_t);
+static int	usbsacm_ds_usb_power(ds_hdl_t, int, int, int *);
+static int	usbsacm_create_pm_components(usbsacm_state_t *);
+static void	usbsacm_destroy_pm_components(usbsacm_state_t *);
+static void	usbsacm_pm_set_busy(usbsacm_state_t *);
+static void	usbsacm_pm_set_idle(usbsacm_state_t *);
+static int	usbsacm_pwrlvl0(usbsacm_state_t *);
+static int	usbsacm_pwrlvl1(usbsacm_state_t *);
+static int	usbsacm_pwrlvl2(usbsacm_state_t *);
+static int	usbsacm_pwrlvl3(usbsacm_state_t *);
+
+/* event handling */
+/* pipe callbacks */
+static void	usbsacm_bulkin_cb(usb_pipe_handle_t, usb_bulk_req_t *);
+static void	usbsacm_bulkout_cb(usb_pipe_handle_t, usb_bulk_req_t *);
+
+/* interrupt pipe */
+static void	usbsacm_pipe_start_polling(usbsacm_port_t *acmp);
+static void	usbsacm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
+static void	usbsacm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req);
+static void	usbsacm_parse_intr_data(usbsacm_port_t *acmp, mblk_t *data);
+
+/* Utility functions */
+/* data transfer routines */
+static int	usbsacm_rx_start(usbsacm_port_t *);
+static void	usbsacm_tx_start(usbsacm_port_t *);
+static int	usbsacm_send_data(usbsacm_port_t *, mblk_t *);
+
+/* Initialize or release resources */
+static int	usbsacm_init_alloc_ports(usbsacm_state_t *);
+static void	usbsacm_free_ports(usbsacm_state_t *);
+static void	usbsacm_cleanup(usbsacm_state_t *);
+
+/* analysis functional descriptors */
+static int	usbsacm_get_descriptors(usbsacm_state_t *);
+
+/* hotplug */
+static int	usbsacm_restore_device_state(usbsacm_state_t *);
+static int	usbsacm_restore_port_state(usbsacm_state_t *);
+
+/* pipe operations */
+static int	usbsacm_open_port_pipes(usbsacm_port_t *);
+static void	usbsacm_close_port_pipes(usbsacm_port_t *);
+static void	usbsacm_close_pipes(usbsacm_state_t *);
+static void	usbsacm_disconnect_pipes(usbsacm_state_t *);
+static int	usbsacm_reconnect_pipes(usbsacm_state_t *);
+
+/* vendor-specific commands */
+static int	usbsacm_req_write(usbsacm_port_t *, uchar_t, uint16_t,
+		mblk_t **);
+static int	usbsacm_set_line_coding(usbsacm_port_t *,
+		usb_cdc_line_coding_t *);
+static void	usbsacm_mctl2reg(int mask, int val, uint8_t *);
+static int	usbsacm_reg2mctl(uint8_t);
+
+/* misc */
+static void	usbsacm_put_tail(mblk_t **, mblk_t *);
+static void	usbsacm_put_head(mblk_t **, mblk_t *);
+
+
+/*
+ * Standard STREAMS driver definitions
+ */
+struct module_info usbsacm_modinfo = {
+	0,			/* module id */
+	"usbsacm",		/* module name */
+	USBSER_MIN_PKTSZ,	/* min pkt size */
+	USBSER_MAX_PKTSZ,	/* max pkt size */
+	USBSER_HIWAT,		/* hi watermark */
+	USBSER_LOWAT		/* low watermark */
+};
+
+static struct qinit usbsacm_rinit = {
+	NULL,
+	usbser_rsrv,
+	usbsacm_open,
+	usbser_close,
+	NULL,
+	&usbsacm_modinfo,
+	NULL
+};
+
+static struct qinit usbsacm_winit = {
+	usbser_wput,
+	usbser_wsrv,
+	NULL,
+	NULL,
+	NULL,
+	&usbsacm_modinfo,
+	NULL
+};
+
+
+struct streamtab usbsacm_str_info = {
+	&usbsacm_rinit, &usbsacm_winit, NULL, NULL
+};
+
+/* cb_ops structure */
+static struct cb_ops usbsacm_cb_ops = {
+	nodev,			/* cb_open */
+	nodev,			/* cb_close */
+	nodev,			/* cb_strategy */
+	nodev,			/* cb_print */
+	nodev,			/* cb_dump */
+	nodev,			/* cb_read */
+	nodev,			/* cb_write */
+	nodev,			/* cb_ioctl */
+	nodev,			/* cb_devmap */
+	nodev,			/* cb_mmap */
+	nodev,			/* cb_segmap */
+	nochpoll,		/* cb_chpoll */
+	ddi_prop_op,		/* cb_prop_op */
+	&usbsacm_str_info,	/* cb_stream */
+	(int)(D_64BIT | D_NEW | D_MP | D_HOTPLUG)	/* cb_flag */
+};
+
+/* dev_ops structure */
+struct dev_ops usbsacm_ops = {
+	DEVO_REV,		/* devo_rev */
+	0,			/* devo_refcnt */
+	usbsacm_getinfo,	/* devo_getinfo */
+	nulldev,		/* devo_identify */
+	nulldev,		/* devo_probe */
+	usbsacm_attach,		/* devo_attach */
+	usbsacm_detach,		/* devo_detach */
+	nodev,			/* devo_reset */
+	&usbsacm_cb_ops,	/* devo_cb_ops */
+	(struct bus_ops *)NULL,	/* devo_bus_ops */
+	usbser_power		/* devo_power */
+};
+
+extern struct mod_ops mod_driverops;
+/* modldrv structure */
+static struct modldrv modldrv = {
+	&mod_driverops,		/* type of module - driver */
+	"USB Serial CDC ACM driver %I%",
+	&usbsacm_ops,
+};
+
+/* modlinkage structure */
+static struct modlinkage modlinkage = {
+	MODREV_1,
+	&modldrv,
+	NULL
+};
+
+static void	*usbsacm_statep;	/* soft state */
+
+/*
+ * DSD definitions
+ */
+ds_ops_t ds_ops = {
+	DS_OPS_VERSION,
+	usbsacm_ds_attach,
+	usbsacm_ds_detach,
+	usbsacm_ds_register_cb,
+	usbsacm_ds_unregister_cb,
+	usbsacm_ds_open_port,
+	usbsacm_ds_close_port,
+	usbsacm_ds_usb_power,
+	usbsacm_ds_suspend,
+	usbsacm_ds_resume,
+	usbsacm_ds_disconnect,
+	usbsacm_ds_reconnect,
+	usbsacm_ds_set_port_params,
+	usbsacm_ds_set_modem_ctl,
+	usbsacm_ds_get_modem_ctl,
+	usbsacm_ds_break_ctl,
+	NULL,			/* NULL if h/w doesn't support loopback */
+	usbsacm_ds_tx,
+	usbsacm_ds_rx,
+	usbsacm_ds_stop,
+	usbsacm_ds_start,
+	usbsacm_ds_fifo_flush,
+	usbsacm_ds_fifo_drain
+};
+
+/*
+ * baud code -> baud rate (0 means unsupported rate)
+ */
+static int usbsacm_speedtab[] = {
+	0,	/* B0 */
+	50,	/* B50 */
+	75,	/* B75 */
+	110,	/* B110 */
+	134,	/* B134 */
+	150,	/* B150 */
+	200,	/* B200 */
+	300,	/* B300 */
+	600,	/* B600 */
+	1200,	/* B1200 */
+	1800,	/* B1800 */
+	2400,	/* B2400 */
+	4800,	/* B4800 */
+	9600,	/* B9600 */
+	19200,	/* B19200 */
+	38400,	/* B38400 */
+	57600,	/* B57600 */
+	76800,	/* B76800 */
+	115200,	/* B115200 */
+	153600,	/* B153600 */
+	230400,	/* B230400 */
+	307200,	/* B307200 */
+	460800	/* B460800 */
+};
+
+
+static uint_t	usbsacm_errlevel = USB_LOG_L4;
+static uint_t	usbsacm_errmask = 0xffffffff;
+static uint_t	usbsacm_instance_debug = (uint_t)-1;
+
+
+/*
+ * usbsacm driver's entry points
+ * -----------------------------
+ */
+/*
+ * Module-wide initialization routine.
+ */
+int
+_init(void)
+{
+	int    error;
+
+	if ((error = mod_install(&modlinkage)) == 0) {
+
+		error = ddi_soft_state_init(&usbsacm_statep,
+		    usbser_soft_state_size(), 1);
+	}
+
+	return (error);
+}
+
+
+/*
+ * Module-wide tear-down routine.
+ */
+int
+_fini(void)
+{
+	int    error;
+
+	if ((error = mod_remove(&modlinkage)) == 0) {
+		ddi_soft_state_fini(&usbsacm_statep);
+	}
+
+	return (error);
+}
+
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+
+/*
+ * Device configuration entry points
+ */
+static int
+usbsacm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	return (usbser_attach(dip, cmd, usbsacm_statep, &ds_ops));
+}
+
+
+static int
+usbsacm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	return (usbser_detach(dip, cmd, usbsacm_statep));
+}
+
+
+int
+usbsacm_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
+		void **result)
+{
+	return (usbser_getinfo(dip, infocmd, arg, result, usbsacm_statep));
+}
+
+
+static int
+usbsacm_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
+{
+	return (usbser_open(rq, dev, flag, sflag, cr, usbsacm_statep));
+}
+
+/*
+ * usbsacm_ds_detach:
+ *	attach device instance, called from GSD attach
+ *	initialize state and device, including:
+ *		state variables, locks, device node
+ *		device registration with system
+ *		power management
+ */
+static int
+usbsacm_ds_attach(ds_attach_info_t *aip)
+{
+	usbsacm_state_t	*acmp;
+
+	acmp = (usbsacm_state_t *)kmem_zalloc(sizeof (usbsacm_state_t),
+	    KM_SLEEP);
+	acmp->acm_dip = aip->ai_dip;
+	acmp->acm_usb_events = aip->ai_usb_events;
+	acmp->acm_ports = NULL;
+	*aip->ai_hdl = (ds_hdl_t)acmp;
+
+	/* registers usbsacm with the USBA framework */
+	if (usb_client_attach(acmp->acm_dip, USBDRV_VERSION,
+	    0) != USB_SUCCESS) {
+
+		goto fail;
+	}
+
+	/* Get the configuration information of device */
+	if (usb_get_dev_data(acmp->acm_dip, &acmp->acm_dev_data,
+	    USB_PARSE_LVL_CFG, 0) != USB_SUCCESS) {
+
+		goto fail;
+	}
+	acmp->acm_def_ph = acmp->acm_dev_data->dev_default_ph;
+	acmp->acm_dev_state = USB_DEV_ONLINE;
+	mutex_init(&acmp->acm_mutex, NULL, MUTEX_DRIVER,
+	    acmp->acm_dev_data->dev_iblock_cookie);
+
+	acmp->acm_lh = usb_alloc_log_hdl(acmp->acm_dip, "usbsacm",
+	    &usbsacm_errlevel, &usbsacm_errmask, &usbsacm_instance_debug, 0);
+
+	/* Create power management components */
+	if (usbsacm_create_pm_components(acmp) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_ds_attach: create pm components failed.");
+
+		goto fail;
+	}
+
+	/* Register to get callbacks for USB events */
+	if (usb_register_event_cbs(acmp->acm_dip, acmp->acm_usb_events, 0)
+	    != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_ds_attach: register event callback failed.");
+
+		goto fail;
+	}
+
+	/*
+	 * If devices conform to acm spec, driver will attach using class id;
+	 * if not, using device id.
+	 */
+	if ((strcmp(DEVI(acmp->acm_dip)->devi_binding_name,
+	    "usbif,class2.2") == 0) ||
+	    ((strcmp(DEVI(acmp->acm_dip)->devi_binding_name,
+	    "usb,class2.2.0") == 0))) {
+
+		acmp->acm_compatibility = B_TRUE;
+	} else {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_ds_attach: A nonstandard device is attaching to "
+		    "usbsacm driver. This device doesn't conform to "
+		    "usb cdc spec.");
+
+		acmp->acm_compatibility = B_FALSE;
+	}
+
+	/* initialize state variables */
+	if (usbsacm_init_alloc_ports(acmp) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_ds_attach: initialize port structure failed.");
+
+		goto fail;
+	}
+	*aip->ai_port_cnt = acmp->acm_port_cnt;
+
+	/* Get max data size of bulk transfer */
+	if (usb_pipe_get_max_bulk_transfer_size(acmp->acm_dip,
+	    &acmp->acm_xfer_sz) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_ds_attach: get max size of transfer failed.");
+
+		goto fail;
+	}
+
+	return (USB_SUCCESS);
+fail:
+	usbsacm_cleanup(acmp);
+
+	return (USB_FAILURE);
+}
+
+
+/*
+ * usbsacm_ds_detach:
+ *	detach device instance, called from GSD detach
+ */
+static void
+usbsacm_ds_detach(ds_hdl_t hdl)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
+	    "usbsacm_ds_detach:");
+
+	usbsacm_close_pipes(acmp);
+	usbsacm_cleanup(acmp);
+}
+
+
+/*
+ * usbsacm_ds_register_cb:
+ *	GSD routine call ds_register_cb to register interrupt callbacks
+ *	for the given port
+ */
+/*ARGSUSED*/
+static int
+usbsacm_ds_register_cb(ds_hdl_t hdl, uint_t port_num, ds_cb_t *cb)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	usbsacm_port_t	*acm_port;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
+	    "usbsacm_ds_register_cb: acmp = 0x%p port_num = %d",
+	    acmp, port_num);
+
+	/* Check if port number is greater than actual port number. */
+	if (port_num >= acmp->acm_port_cnt) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_ds_register_cb: port number is wrong.");
+
+		return (USB_FAILURE);
+	}
+	acm_port = &acmp->acm_ports[port_num];
+	acm_port->acm_cb = *cb;
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * usbsacm_ds_unregister_cb:
+ *	GSD routine call ds_unregister_cb to unregister
+ *	interrupt callbacks for the given port
+ */
+/*ARGSUSED*/
+static void
+usbsacm_ds_unregister_cb(ds_hdl_t hdl, uint_t port_num)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	usbsacm_port_t	*acm_port;
+
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
+	    "usbsacm_ds_unregister_cb: ");
+
+	if (port_num < acmp->acm_port_cnt) {
+		/* Release callback function */
+		acm_port = &acmp->acm_ports[port_num];
+		bzero(&acm_port->acm_cb, sizeof (acm_port->acm_cb));
+	}
+}
+
+
+/*
+ * usbsacm_ds_open_port:
+ *	GSD routine call ds_open_port
+ *	to open the given port
+ */
+/*ARGSUSED*/
+static int
+usbsacm_ds_open_port(ds_hdl_t hdl, uint_t port_num)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
+
+	USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
+	    "usbsacm_ds_open_port: port_num = %d", port_num);
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	/* Check the status of the given port and device */
+	if ((acmp->acm_dev_state == USB_DEV_DISCONNECTED) ||
+	    (acm_port->acm_port_state != USBSACM_PORT_CLOSED)) {
+		mutex_exit(&acm_port->acm_port_mutex);
+
+		return (USB_FAILURE);
+	}
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	usbsacm_pm_set_busy(acmp);
+
+	/* open pipes of port */
+	if (usbsacm_open_port_pipes(acm_port) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
+		    "usbsacm_ds_open_port: open pipes failed.");
+
+		return (USB_FAILURE);
+	}
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	/* data receipt */
+	if (usbsacm_rx_start(acm_port) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
+		    "usbsacm_ds_open_port: start receive data failed.");
+		mutex_exit(&acm_port->acm_port_mutex);
+
+		return (USB_FAILURE);
+	}
+	acm_port->acm_port_state = USBSACM_PORT_OPEN;
+
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * usbsacm_ds_close_port:
+ *	GSD routine call ds_close_port
+ *	to close the given port
+ */
+/*ARGSUSED*/
+static int
+usbsacm_ds_close_port(ds_hdl_t hdl, uint_t port_num)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
+	int		rval = USB_SUCCESS;
+
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
+	    "usbsacm_ds_close_port: acmp = 0x%p", acmp);
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	acm_port->acm_port_state = USBSACM_PORT_CLOSED;
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	usbsacm_close_port_pipes(acm_port);
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	rval = usbsacm_fifo_flush_locked(acmp, port_num, DS_TX | DS_RX);
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	usbsacm_pm_set_idle(acmp);
+
+	return (rval);
+}
+
+
+/*
+ * usbsacm_ds_usb_power:
+ *	GSD routine call ds_usb_power
+ *	to set power level of the component
+ */
+/*ARGSUSED*/
+static int
+usbsacm_ds_usb_power(ds_hdl_t hdl, int comp, int level, int *new_state)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	usbsacm_pm_t	*pm = acmp->acm_pm;
+	int		rval = USB_SUCCESS;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
+	    "usbsacm_ds_usb_power: ");
+
+	/* check if pm is NULL */
+	if (pm == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
+		    "usbsacm_ds_usb_power: pm is NULL.");
+
+		return (USB_FAILURE);
+	}
+
+	mutex_enter(&acmp->acm_mutex);
+	/*
+	 * check if we are transitioning to a legal power level
+	 */
+	if (USB_DEV_PWRSTATE_OK(pm->pm_pwr_states, level)) {
+		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
+		    "usbsacm_ds_usb_power: "
+		    "illegal power level %d, pwr_states=%x",
+		    level, pm->pm_pwr_states);
+		mutex_exit(&acmp->acm_mutex);
+
+		return (USB_FAILURE);
+	}
+
+	/*
+	 * if we are about to raise power and asked to lower power, fail
+	 */
+	if (pm->pm_raise_power && (level < (int)pm->pm_cur_power)) {
+		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
+		    "usbsacm_ds_usb_power: wrong condition.");
+		mutex_exit(&acmp->acm_mutex);
+
+		return (USB_FAILURE);
+	}
+
+	/*
+	 * Set the power status of device by request level.
+	 */
+	switch (level) {
+	case USB_DEV_OS_PWR_OFF:
+		rval = usbsacm_pwrlvl0(acmp);
+
+		break;
+	case USB_DEV_OS_PWR_1:
+		rval = usbsacm_pwrlvl1(acmp);
+
+		break;
+	case USB_DEV_OS_PWR_2:
+		rval = usbsacm_pwrlvl2(acmp);
+
+		break;
+	case USB_DEV_OS_FULL_PWR:
+		rval = usbsacm_pwrlvl3(acmp);
+
+		break;
+	}
+
+	*new_state = acmp->acm_dev_state;
+	mutex_exit(&acmp->acm_mutex);
+
+	return (rval);
+}
+
+
+/*
+ * usbsacm_ds_suspend:
+ *	GSD routine call ds_suspend
+ *	during CPR suspend
+ */
+static int
+usbsacm_ds_suspend(ds_hdl_t hdl)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	int		state;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
+	    "usbsacm_ds_suspend: ");
+
+	mutex_enter(&acmp->acm_mutex);
+	/* set device status to suspend */
+	state = acmp->acm_dev_state = USB_DEV_SUSPENDED;
+	mutex_exit(&acmp->acm_mutex);
+
+	usbsacm_disconnect_pipes(acmp);
+
+	return (state);
+}
+
+/*
+ * usbsacm_ds_resume:
+ *	GSD routine call ds_resume
+ *	during CPR resume
+ */
+/*ARGSUSED*/
+static int
+usbsacm_ds_resume(ds_hdl_t hdl)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	int		current_state;
+	int		ret;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
+	    "usbsacm_ds_resume: ");
+
+	mutex_enter(&acmp->acm_mutex);
+	current_state = acmp->acm_dev_state;
+	mutex_exit(&acmp->acm_mutex);
+
+	/* restore the status of device */
+	if (current_state != USB_DEV_ONLINE) {
+		ret = usbsacm_restore_device_state(acmp);
+	} else {
+		ret = USB_DEV_ONLINE;
+	}
+
+	return (ret);
+}
+
+/*
+ * usbsacm_ds_disconnect:
+ *	GSD routine call ds_disconnect
+ *	to disconnect USB device
+ */
+static int
+usbsacm_ds_disconnect(ds_hdl_t hdl)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	int		state;
+
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
+	    "usbsacm_ds_disconnect: ");
+
+	mutex_enter(&acmp->acm_mutex);
+	/* set device status to disconnected */
+	state = acmp->acm_dev_state = USB_DEV_DISCONNECTED;
+	mutex_exit(&acmp->acm_mutex);
+
+	usbsacm_disconnect_pipes(acmp);
+
+	return (state);
+}
+
+
+/*
+ * usbsacm_ds_reconnect:
+ *	GSD routine call ds_reconnect
+ *	to reconnect USB device
+ */
+/*ARGSUSED*/
+static int
+usbsacm_ds_reconnect(ds_hdl_t hdl)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
+	    "usbsacm_ds_reconnect: ");
+
+	return (usbsacm_restore_device_state(acmp));
+}
+
+
+/*
+ * usbsacm_ds_set_port_params:
+ *	GSD routine call ds_set_port_params
+ *	to set one or more port parameters
+ */
+/*ARGSUSED*/
+static int
+usbsacm_ds_set_port_params(ds_hdl_t hdl, uint_t port_num, ds_port_params_t *tp)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
+	int		i;
+	uint_t		ui;
+	ds_port_param_entry_t *pe;
+	usb_cdc_line_coding_t lc;
+	int		ret;
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
+	    "usbsacm_ds_set_port_params: acmp = 0x%p", acmp);
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	/*
+	 * If device conform to acm spec, check if it support to set port param.
+	 */
+	if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SERIAL_LINE) == 0 &&
+	    acmp->acm_compatibility == B_TRUE) {
+
+		mutex_exit(&acm_port->acm_port_mutex);
+		USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
+		    "usbsacm_ds_set_port_params: "
+		    "don't support Set_Line_Coding.");
+
+		return (USB_FAILURE);
+	}
+
+	lc = acm_port->acm_line_coding;
+	mutex_exit(&acm_port->acm_port_mutex);
+	pe = tp->tp_entries;
+	/* Get parameter information from ds_port_params_t */
+	for (i = 0; i < tp->tp_cnt; i++, pe++) {
+		switch (pe->param) {
+		case DS_PARAM_BAUD:
+			/* Data terminal rate, in bits per second. */
+			ui = pe->val.ui;
+
+			/* if we don't support this speed, return USB_FAILURE */
+			if ((ui >= NELEM(usbsacm_speedtab)) ||
+			    ((ui > 0) && (usbsacm_speedtab[ui] == 0))) {
+				USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
+				    "usbsacm_ds_set_port_params: "
+				    " error baud rate");
+
+				return (USB_FAILURE);
+			}
+			lc.dwDTERate = LE_32(usbsacm_speedtab[ui]);
+
+			break;
+		case DS_PARAM_PARITY:
+			/* Parity Type */
+			if (pe->val.ui & PARENB) {
+				if (pe->val.ui & PARODD) {
+					lc.bParityType = USB_CDC_PARITY_ODD;
+				} else {
+					lc.bParityType = USB_CDC_PARITY_EVEN;
+				}
+			} else {
+				lc.bParityType = USB_CDC_PARITY_NO;
+			}
+
+			break;
+		case DS_PARAM_STOPB:
+			/* Stop bit */
+			if (pe->val.ui & CSTOPB) {
+				lc.bCharFormat = USB_CDC_STOP_BITS_2;
+			} else {
+				lc.bCharFormat = USB_CDC_STOP_BITS_1;
+			}
+
+			break;
+		case DS_PARAM_CHARSZ:
+			/* Data Bits */
+			switch (pe->val.ui) {
+			case CS5:
+				lc.bDataBits = 5;
+				break;
+			case CS6:
+				lc.bDataBits = 6;
+				break;
+			case CS7:
+				lc.bDataBits = 7;
+				break;
+			case CS8:
+			default:
+				lc.bDataBits = 8;
+				break;
+			}
+
+			break;
+		default:
+			USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
+			    "usbsacm_ds_set_port_params: "
+			    "parameter 0x%x isn't supported",
+			    pe->param);
+
+			break;
+		}
+	}
+
+	if ((ret = usbsacm_set_line_coding(acm_port, &lc)) == USB_SUCCESS) {
+		mutex_enter(&acm_port->acm_port_mutex);
+		acm_port->acm_line_coding = lc;
+		mutex_exit(&acm_port->acm_port_mutex);
+	}
+
+	/*
+	 * If device don't conform to acm spec, return success directly.
+	 */
+	if (acmp->acm_compatibility != B_TRUE) {
+		ret = USB_SUCCESS;
+	}
+
+	return (ret);
+}
+
+
+/*
+ * usbsacm_ds_set_modem_ctl:
+ *	GSD routine call ds_set_modem_ctl
+ *	to set modem control of the given port
+ */
+/*ARGSUSED*/
+static int
+usbsacm_ds_set_modem_ctl(ds_hdl_t hdl, uint_t port_num, int mask, int val)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
+	uint8_t		new_mctl;
+	int		ret;
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
+	    "usbsacm_ds_set_modem_ctl: mask = 0x%x val = 0x%x",
+	    mask, val);
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	/*
+	 * If device conform to acm spec, check if it support to set modem
+	 * controls.
+	 */
+	if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SERIAL_LINE) == 0 &&
+		acmp->acm_compatibility == B_TRUE) {
+
+		mutex_exit(&acm_port->acm_port_mutex);
+		USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
+		    "usbsacm_ds_set_modem_ctl: "
+		    "don't support Set_Control_Line_State.");
+
+		return (USB_FAILURE);
+	}
+
+	new_mctl = acm_port->acm_mctlout;
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	usbsacm_mctl2reg(mask, val, &new_mctl);
+
+	if ((acmp->acm_compatibility == B_FALSE) || ((ret =
+	    usbsacm_req_write(acm_port, USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+	    new_mctl, NULL)) == USB_SUCCESS)) {
+		mutex_enter(&acm_port->acm_port_mutex);
+		acm_port->acm_mctlout = new_mctl;
+		mutex_exit(&acm_port->acm_port_mutex);
+	}
+
+	/*
+	 * If device don't conform to acm spec, return success directly.
+	 */
+	if (acmp->acm_compatibility != B_TRUE) {
+		ret = USB_SUCCESS;
+	}
+
+	return (ret);
+}
+
+
+/*
+ * usbsacm_ds_get_modem_ctl:
+ *	GSD routine call ds_get_modem_ctl
+ *	to get modem control/status of the given port
+ */
+/*ARGSUSED*/
+static int
+usbsacm_ds_get_modem_ctl(ds_hdl_t hdl, uint_t port_num, int mask, int *valp)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	*valp = usbsacm_reg2mctl(acm_port->acm_mctlout) & mask;
+	/*
+	 * If device conform to acm spec, polling function can modify the value
+	 * of acm_mctlin; else set to default value.
+	 */
+	if (acmp->acm_compatibility) {
+		*valp |= usbsacm_reg2mctl(acm_port->acm_mctlin) & mask;
+		*valp |= (mask & (TIOCM_CD | TIOCM_CTS));
+	} else {
+		*valp |= (mask & (TIOCM_CD | TIOCM_CTS | TIOCM_DSR | TIOCM_RI));
+	}
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
+	    "usbsacm_ds_get_modem_ctl: val = 0x%x", *valp);
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * usbsacm_ds_tx:
+ *	GSD routine call ds_break_ctl
+ *	to set/clear break
+ */
+/*ARGSUSED*/
+static int
+usbsacm_ds_break_ctl(ds_hdl_t hdl, uint_t port_num, int ctl)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
+	    "usbsacm_ds_break_ctl: ");
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	/*
+	 * If device conform to acm spec, check if it support to send break.
+	 */
+	if ((acm_port->acm_cap & USB_CDC_ACM_CAP_SEND_BREAK) == 0 &&
+		acmp->acm_compatibility == B_TRUE) {
+
+		mutex_exit(&acm_port->acm_port_mutex);
+		USB_DPRINTF_L2(PRINT_MASK_ALL, acmp->acm_lh,
+		    "usbsacm_ds_break_ctl: don't support send break.");
+
+		return (USB_FAILURE);
+	}
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	return (usbsacm_req_write(acm_port, USB_CDC_REQ_SEND_BREAK,
+		((ctl == DS_ON) ? 0xffff : 0), NULL));
+}
+
+
+/*
+ * usbsacm_ds_tx:
+ *	GSD routine call ds_tx
+ *	to data transmit
+ */
+/*ARGSUSED*/
+static int
+usbsacm_ds_tx(ds_hdl_t hdl, uint_t port_num, mblk_t *mp)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
+	    "usbsacm_ds_tx: mp = 0x%p acmp = 0x%p", mp, acmp);
+
+	/* sanity checks */
+	if (mp == NULL) {
+
+		return (USB_SUCCESS);
+	}
+	if (MBLKL(mp) <= 0) {
+		freemsg(mp);
+
+		return (USB_SUCCESS);
+	}
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	/* put mblk to tail of mblk chain */
+	usbsacm_put_tail(&acm_port->acm_tx_mp, mp);
+	usbsacm_tx_start(acm_port);
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * usbsacm_ds_rx:
+ *	GSD routine call ds_rx;
+ *	to data receipt
+ */
+/*ARGSUSED*/
+static mblk_t *
+usbsacm_ds_rx(ds_hdl_t hdl, uint_t port_num)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
+	mblk_t		*mp;
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
+	    "usbsacm_ds_rx: acmp = 0x%p", acmp);
+
+	mutex_enter(&acm_port->acm_port_mutex);
+
+	mp = acm_port->acm_rx_mp;
+	acm_port->acm_rx_mp = NULL;
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	return (mp);
+}
+
+
+/*
+ * usbsacm_ds_stop:
+ *	GSD routine call ds_stop;
+ *	but acm spec don't define this function
+ */
+/*ARGSUSED*/
+static void
+usbsacm_ds_stop(ds_hdl_t hdl, uint_t port_num, int dir)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+
+	USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
+	    "usbsacm_ds_stop: don't support!");
+}
+
+
+/*
+ * usbsacm_ds_start:
+ *	GSD routine call ds_start;
+ *	but acm spec don't define this function
+ */
+/*ARGSUSED*/
+static void
+usbsacm_ds_start(ds_hdl_t hdl, uint_t port_num, int dir)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+
+	USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
+	    "usbsacm_ds_start: don't support!");
+}
+
+
+/*
+ * usbsacm_ds_fifo_flush:
+ *	GSD routine call ds_fifo_flush
+ *	to flush FIFOs
+ */
+/*ARGSUSED*/
+static int
+usbsacm_ds_fifo_flush(ds_hdl_t hdl, uint_t port_num, int dir)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
+	int		ret = USB_SUCCESS;
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
+	    "usbsacm_ds_fifo_flush: ");
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	ret = usbsacm_fifo_flush_locked(acmp, port_num, dir);
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	return (ret);
+}
+
+
+/*
+ * usbsacm_ds_fifo_drain:
+ *	GSD routine call ds_fifo_drain
+ *	to wait until empty output FIFO
+ */
+/*ARGSUSED*/
+static int
+usbsacm_ds_fifo_drain(ds_hdl_t hdl, uint_t port_num, int timeout)
+{
+	usbsacm_state_t	*acmp = (usbsacm_state_t *)hdl;
+	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
+	int		rval = USB_SUCCESS;
+
+	USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
+	    "usbsacm_ds_fifo_drain: ");
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	ASSERT(acm_port->acm_port_state == USBSACM_PORT_OPEN);
+
+	if (usbsacm_wait_tx_drain(acm_port, timeout) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
+		    "usbsacm_ds_fifo_drain: fifo drain failed.");
+		mutex_exit(&acm_port->acm_port_mutex);
+
+		return (USB_FAILURE);
+	}
+
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	return (rval);
+}
+
+
+/*
+ * usbsacm_fifo_flush_locked:
+ *	flush FIFOs of the given ports
+ */
+/*ARGSUSED*/
+static int
+usbsacm_fifo_flush_locked(usbsacm_state_t *acmp, uint_t port_num, int dir)
+{
+	usbsacm_port_t	*acm_port = &acmp->acm_ports[port_num];
+
+	USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
+	    "usbsacm_fifo_flush_locked: ");
+
+	/* flush transmit FIFO if DS_TX is set */
+	if ((dir & DS_TX) && acm_port->acm_tx_mp) {
+		freemsg(acm_port->acm_tx_mp);
+		acm_port->acm_tx_mp = NULL;
+	}
+	/* flush received FIFO if DS_RX is set */
+	if ((dir & DS_RX) && acm_port->acm_rx_mp) {
+		freemsg(acm_port->acm_rx_mp);
+		acm_port->acm_rx_mp = NULL;
+	}
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * usbsacm_get_bulk_pipe_number:
+ *	Calculate the number of bulk in or out pipes in current device.
+ */
+static int
+usbsacm_get_bulk_pipe_number(usbsacm_state_t *acmp, uint_t dir)
+{
+	int		count = 0;
+	int		i, skip;
+	usb_if_data_t	*cur_if;
+	int		ep_num;
+	int		if_num;
+	int		if_no;
+
+	USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
+	    "usbsacm_get_bulk_pipe_number: ");
+
+	cur_if = acmp->acm_dev_data->dev_curr_cfg->cfg_if;
+	if_num = acmp->acm_dev_data->dev_curr_cfg->cfg_n_if;
+	if_no = acmp->acm_dev_data->dev_curr_if;
+
+	/* search each interface which have bulk endpoint */
+	for (i = 0; i < if_num; i++) {
+		ep_num = cur_if->if_alt->altif_n_ep;
+
+		/*
+		 * search endpoints in current interface,
+		 * which type is input parameter 'dir'
+		 */
+		for (skip = 0; skip < ep_num; skip++) {
+		    if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
+			if_no + i, 0, skip, USB_EP_ATTR_BULK, dir) == NULL) {
+
+			/*
+			 * If not found, skip the internal loop and search
+			 * the next interface.
+			 */
+			break;
+		    }
+		    count++;
+		}
+
+		cur_if++;
+	}
+
+	return (count);
+}
+
+
+/*
+ * port management
+ * ---------------
+ *	initialize, release port.
+ *
+ *
+ * usbsacm_init_ports_status:
+ *	Initialize the port status for the current device.
+ */
+static int
+usbsacm_init_ports_status(usbsacm_state_t *acmp)
+{
+	usbsacm_port_t	*cur_port;
+	int		i, skip;
+	int		if_num;
+	int		intr_if_no = 0;
+	int		ep_num;
+	usb_if_data_t	*cur_if;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
+	    "usbsacm_init_ports_status: acmp = 0x%p", acmp);
+
+	/* Initialize the port status to default value */
+	for (i = 0; i < acmp->acm_port_cnt; i++) {
+		cur_port = &acmp->acm_ports[i];
+
+		cv_init(&cur_port->acm_tx_cv, NULL, CV_DRIVER, NULL);
+
+		cur_port->acm_port_state = USBSACM_PORT_CLOSED;
+
+		cur_port->acm_line_coding.dwDTERate = LE_32((uint32_t)9600);
+		cur_port->acm_line_coding.bCharFormat = 0;
+		cur_port->acm_line_coding.bParityType = USB_CDC_PARITY_NO;
+		cur_port->acm_line_coding.bDataBits = 8;
+		cur_port->acm_device = acmp;
+		mutex_init(&cur_port->acm_port_mutex, NULL, MUTEX_DRIVER,
+		    acmp->acm_dev_data->dev_iblock_cookie);
+	}
+
+	/*
+	 * If device conform to cdc acm spec, parse function descriptors.
+	 */
+	if (acmp->acm_compatibility == B_TRUE) {
+
+		if (usbsacm_get_descriptors(acmp) != USB_SUCCESS) {
+
+			return (USB_FAILURE);
+		}
+
+		return (USB_SUCCESS);
+	}
+
+	/*
+	 * If device don't conform to spec, search pairs of bulk in/out
+	 * endpoints and fill port structure.
+	 */
+	cur_if = acmp->acm_dev_data->dev_curr_cfg->cfg_if;
+	if_num = acmp->acm_dev_data->dev_curr_cfg->cfg_n_if;
+	cur_port = acmp->acm_ports;
+
+	/* search each interface which have bulk in and out */
+	for (i = 0; i < if_num; i++) {
+	    ep_num = cur_if->if_alt->altif_n_ep;
+
+	    for (skip = 0; skip < ep_num; skip++) {
+
+		/* search interrupt pipe. */
+		if ((usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
+		    i, 0, skip, USB_EP_ATTR_INTR, USB_EP_DIR_IN) != NULL)) {
+
+			intr_if_no = i;
+		}
+
+		/* search pair of bulk in/out endpoints. */
+		if ((usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
+		    i, 0, skip, USB_EP_ATTR_BULK, USB_EP_DIR_IN) == NULL) ||
+		    (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
+		    i, 0, skip, USB_EP_ATTR_BULK, USB_EP_DIR_OUT) == NULL)) {
+
+			continue;
+		}
+
+		cur_port->acm_data_if_no = i;
+		cur_port->acm_ctrl_if_no = intr_if_no;
+		cur_port->acm_data_port_no = skip;
+		cur_port++;
+		intr_if_no = 0;
+	    }
+
+	    cur_if++;
+	}
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * usbsacm_init_alloc_ports:
+ *	Allocate memory and initialize the port state for the current device.
+ */
+static int
+usbsacm_init_alloc_ports(usbsacm_state_t *acmp)
+{
+	int 		rval = USB_SUCCESS;
+	int		count_in = 0, count_out = 0;
+
+	if (acmp->acm_compatibility) {
+		acmp->acm_port_cnt = 1;
+	} else {
+		/* Calculate the number of the bulk in/out endpoints */
+		count_in = usbsacm_get_bulk_pipe_number(acmp, USB_EP_DIR_IN);
+		count_out = usbsacm_get_bulk_pipe_number(acmp, USB_EP_DIR_OUT);
+
+		USB_DPRINTF_L3(PRINT_MASK_OPEN, acmp->acm_lh,
+		    "usbsacm_init_alloc_ports: count_in = %d, count_out = %d",
+		    count_in, count_out);
+
+		acmp->acm_port_cnt = min(count_in, count_out);
+	}
+
+	/* return if not found any pair of bulk in/out endpoint. */
+	if (acmp->acm_port_cnt == 0) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
+		    "usbsacm_init_alloc_ports: port count is zero.");
+
+		return (USB_FAILURE);
+	}
+
+	/* allocate memory for ports */
+	acmp->acm_ports = (usbsacm_port_t *)kmem_zalloc(acmp->acm_port_cnt *
+	    sizeof (usbsacm_port_t), KM_SLEEP);
+	if (acmp->acm_ports == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
+		    "usbsacm_init_alloc_ports: allocate memory failed.");
+
+		return (USB_FAILURE);
+	}
+
+	/* fill the status of port structure. */
+	rval = usbsacm_init_ports_status(acmp);
+	if (rval != USB_SUCCESS) {
+		usbsacm_free_ports(acmp);
+	}
+
+	return (rval);
+}
+
+
+/*
+ * usbsacm_free_ports:
+ *	Release ports and deallocate memory.
+ */
+static void
+usbsacm_free_ports(usbsacm_state_t *acmp)
+{
+	int		i;
+
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
+	    "usbsacm_free_ports: ");
+
+	/* Release memory and data structure for each port */
+	for (i = 0; i < acmp->acm_port_cnt; i++) {
+		cv_destroy(&acmp->acm_ports[i].acm_tx_cv);
+		mutex_destroy(&acmp->acm_ports[i].acm_port_mutex);
+	}
+	kmem_free((caddr_t)acmp->acm_ports, sizeof (usbsacm_port_t) *
+	    acmp->acm_port_cnt);
+	acmp->acm_ports = NULL;
+}
+
+
+/*
+ * usbsacm_get_descriptors:
+ *	analysis functional descriptors of acm device
+ */
+static int
+usbsacm_get_descriptors(usbsacm_state_t *acmp)
+{
+	int			i;
+	usb_cfg_data_t		*cfg;
+	usb_alt_if_data_t	*altif;
+	usb_cvs_data_t		*cvs;
+	int			mgmt_cap = 0;
+	int			master_if = -1, slave_if = -1;
+	usbsacm_port_t		*acm_port = acmp->acm_ports;
+
+	USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
+	    "usbsacm_get_descriptors: ");
+
+	cfg = acmp->acm_dev_data->dev_curr_cfg;
+	/* set default control and data interface */
+	acm_port->acm_ctrl_if_no = acm_port->acm_data_if_no = 0;
+
+	/* get current interfaces */
+	acm_port->acm_ctrl_if_no = acmp->acm_dev_data->dev_curr_if;
+	if (cfg->cfg_if[acm_port->acm_ctrl_if_no].if_n_alt == 0) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_get_descriptors: elements in if_alt is %d",
+		    cfg->cfg_if[acm_port->acm_ctrl_if_no].if_n_alt);
+
+		return (USB_FAILURE);
+	}
+
+	altif = &cfg->cfg_if[acm_port->acm_ctrl_if_no].if_alt[0];
+
+	/*
+	 * Based on CDC specification, ACM devices usually include the
+	 * following function descriptors: Header, ACM, Union and Call
+	 * Management function descriptors. This loop search tree data
+	 * structure for each acm class descriptor.
+	 */
+	for (i = 0; i < altif->altif_n_cvs; i++) {
+
+		cvs = &altif->altif_cvs[i];
+
+		if ((cvs->cvs_buf == NULL) ||
+		    (cvs->cvs_buf[1] != USB_CDC_CS_INTERFACE)) {
+			continue;
+		}
+
+		switch (cvs->cvs_buf[2]) {
+		case USB_CDC_DESCR_TYPE_CALL_MANAGEMENT:
+			/* parse call management functional descriptor. */
+			if (cvs->cvs_buf_len >= 5) {
+				mgmt_cap = cvs->cvs_buf[3];
+				acm_port->acm_data_if_no = cvs->cvs_buf[4];
+			}
+			break;
+		case USB_CDC_DESCR_TYPE_ACM:
+			/* parse ACM functional descriptor. */
+			if (cvs->cvs_buf_len >= 4) {
+				acm_port->acm_cap = cvs->cvs_buf[3];
+			}
+			break;
+		case USB_CDC_DESCR_TYPE_UNION:
+			/* parse Union functional descriptor. */
+			if (cvs->cvs_buf_len >= 5) {
+				master_if = cvs->cvs_buf[3];
+				slave_if = cvs->cvs_buf[4];
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* For usb acm devices, it must satisfy the following options. */
+	if (cfg->cfg_n_if < 2) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_get_descriptors: # of interfaces %d < 2",
+		    cfg->cfg_n_if);
+
+		return (USB_FAILURE);
+	}
+
+	if (acm_port->acm_data_if_no == 0 &&
+		slave_if != acm_port->acm_data_if_no) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_get_descriptors: Device hasn't call management "
+		    "descriptor and use Union Descriptor.");
+
+		acm_port->acm_data_if_no = slave_if;
+	}
+
+	if ((master_if != acm_port->acm_ctrl_if_no) ||
+	    (slave_if != acm_port->acm_data_if_no)) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_get_descriptors: control interface or "
+		    "data interface don't match.");
+
+		return (USB_FAILURE);
+	}
+
+	/*
+	 * We usually need both call and data capabilities, but
+	 * some devices, such as Nokia mobile phones, don't provide
+	 * call management descriptor, so we just give a warning
+	 * message.
+	 */
+	if (((mgmt_cap & USB_CDC_CALL_MGMT_CAP_CALL_MGMT) == 0) ||
+	    ((mgmt_cap & USB_CDC_CALL_MGMT_CAP_DATA_INTERFACE) == 0)) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_get_descriptors: "
+		    "insufficient mgmt capabilities %x",
+		    mgmt_cap);
+	}
+
+	if ((acm_port->acm_ctrl_if_no >= cfg->cfg_n_if) ||
+	    (acm_port->acm_data_if_no >= cfg->cfg_n_if)) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_get_descriptors: control interface %d or "
+		    "data interface %d out of range.",
+		    acm_port->acm_ctrl_if_no, acm_port->acm_data_if_no);
+
+		return (USB_FAILURE);
+	}
+
+	/* control interface must have interrupt endpoint */
+	if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
+	    acm_port->acm_ctrl_if_no, 0, 0, USB_EP_ATTR_INTR,
+	    USB_EP_DIR_IN) == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_get_descriptors: "
+		    "ctrl interface %d has no interrupt endpoint",
+		    acm_port->acm_data_if_no);
+
+		return (USB_FAILURE);
+	}
+
+	/* data interface must have bulk in and out */
+	if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
+	    acm_port->acm_data_if_no, 0, 0, USB_EP_ATTR_BULK,
+	    USB_EP_DIR_IN) == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_get_descriptors: "
+		    "data interface %d has no bulk in endpoint",
+		    acm_port->acm_data_if_no);
+
+		return (USB_FAILURE);
+	}
+	if (usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
+	    acm_port->acm_data_if_no, 0, 0, USB_EP_ATTR_BULK,
+	    USB_EP_DIR_OUT) == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_get_descriptors: "
+		    "data interface %d has no bulk out endpoint",
+		    acm_port->acm_data_if_no);
+
+		return (USB_FAILURE);
+	}
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * usbsacm_cleanup:
+ *	Release resources of current device during detach.
+ */
+static void
+usbsacm_cleanup(usbsacm_state_t *acmp)
+{
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
+	    "usbsacm_cleanup: ");
+
+	if (acmp != NULL) {
+		/* free ports */
+		if (acmp->acm_ports != NULL) {
+			usbsacm_free_ports(acmp);
+		}
+
+		/* unregister callback function */
+		if (acmp->acm_usb_events != NULL) {
+			usb_unregister_event_cbs(acmp->acm_dip,
+				acmp->acm_usb_events);
+		}
+
+		/* destroy power management components */
+		if (acmp->acm_pm != NULL) {
+			usbsacm_destroy_pm_components(acmp);
+		}
+
+		/* free description of device tree. */
+		if (acmp->acm_def_ph != NULL) {
+			mutex_destroy(&acmp->acm_mutex);
+
+			usb_free_descr_tree(acmp->acm_dip, acmp->acm_dev_data);
+			acmp->acm_def_ph = NULL;
+		}
+
+		if (acmp->acm_lh != NULL) {
+			usb_free_log_hdl(acmp->acm_lh);
+			acmp->acm_lh = NULL;
+		}
+
+		/* detach client device */
+		if (acmp->acm_dev_data != NULL) {
+			usb_client_detach(acmp->acm_dip, acmp->acm_dev_data);
+		}
+
+		kmem_free((caddr_t)acmp, sizeof (usbsacm_state_t));
+	}
+}
+
+
+/*
+ * usbsacm_restore_device_state:
+ *	restore device state after CPR resume or reconnect
+ */
+static int
+usbsacm_restore_device_state(usbsacm_state_t *acmp)
+{
+	int	state;
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
+	    "usbsacm_restore_device_state: ");
+
+	mutex_enter(&acmp->acm_mutex);
+	state = acmp->acm_dev_state;
+	mutex_exit(&acmp->acm_mutex);
+
+	/* Check device status */
+	if ((state != USB_DEV_DISCONNECTED) && (state != USB_DEV_SUSPENDED)) {
+
+		return (state);
+	}
+
+	/* Check if we are talking to the same device */
+	if (usb_check_same_device(acmp->acm_dip, acmp->acm_lh, USB_LOG_L0,
+	    -1, USB_CHK_ALL, NULL) != USB_SUCCESS) {
+		mutex_enter(&acmp->acm_mutex);
+		state = acmp->acm_dev_state = USB_DEV_DISCONNECTED;
+		mutex_exit(&acmp->acm_mutex);
+
+		return (state);
+	}
+
+	if (state == USB_DEV_DISCONNECTED) {
+		USB_DPRINTF_L1(PRINT_MASK_ALL, acmp->acm_lh,
+		    "usbsacm_restore_device_state: Device has been reconnected "
+		    "but data may have been lost");
+	}
+
+	/* reconnect pipes */
+	if (usbsacm_reconnect_pipes(acmp) != USB_SUCCESS) {
+
+		return (state);
+	}
+
+	/*
+	 * init device state
+	 */
+	mutex_enter(&acmp->acm_mutex);
+	state = acmp->acm_dev_state = USB_DEV_ONLINE;
+	mutex_exit(&acmp->acm_mutex);
+
+	if ((usbsacm_restore_port_state(acmp) != USB_SUCCESS)) {
+		USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+		    "usbsacm_restore_device_state: failed");
+	}
+
+	return (state);
+}
+
+
+/*
+ * usbsacm_restore_port_state:
+ *	restore ports state after CPR resume or reconnect
+ */
+static int
+usbsacm_restore_port_state(usbsacm_state_t *acmp)
+{
+	int		i, ret = USB_SUCCESS;
+	usbsacm_port_t	*cur_port;
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
+	    "usbsacm_restore_port_state: ");
+
+	/* restore status of all ports */
+	for (i = 0; i < acmp->acm_port_cnt; i++) {
+		cur_port = &acmp->acm_ports[i];
+		mutex_enter(&cur_port->acm_port_mutex);
+		if (cur_port->acm_port_state != USBSACM_PORT_OPEN) {
+			mutex_exit(&cur_port->acm_port_mutex);
+
+			continue;
+		}
+		mutex_exit(&cur_port->acm_port_mutex);
+
+		if ((ret = usbsacm_set_line_coding(cur_port,
+		    &cur_port->acm_line_coding)) != USB_SUCCESS) {
+			USB_DPRINTF_L2(PRINT_MASK_ATTA, acmp->acm_lh,
+			    "usbsacm_restore_port_state: failed.");
+		}
+	}
+
+	return (ret);
+}
+
+
+/*
+ * pipe management
+ * ---------------
+ *
+ *
+ * usbsacm_open_port_pipes:
+ *	Open pipes of one port and set port structure;
+ *	Each port includes three pipes: bulk in, bulk out and interrupt.
+ */
+static int
+usbsacm_open_port_pipes(usbsacm_port_t *acm_port)
+{
+	int		rval = USB_SUCCESS;
+	usbsacm_state_t	*acmp = acm_port->acm_device;
+	usb_ep_data_t	*in_data, *out_data, *intr_pipe;
+	usb_pipe_policy_t policy;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
+	    "usbsacm_open_port_pipes: acmp = 0x%p", acmp);
+
+	/* Get bulk and interrupt endpoint data */
+	intr_pipe = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
+	    acm_port->acm_ctrl_if_no, 0, 0,
+	    USB_EP_ATTR_INTR, USB_EP_DIR_IN);
+	in_data = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
+	    acm_port->acm_data_if_no, 0, acm_port->acm_data_port_no,
+	    USB_EP_ATTR_BULK, USB_EP_DIR_IN);
+	out_data = usb_lookup_ep_data(acmp->acm_dip, acmp->acm_dev_data,
+	    acm_port->acm_data_if_no, 0, acm_port->acm_data_port_no,
+	    USB_EP_ATTR_BULK, USB_EP_DIR_OUT);
+
+	/* Bulk in and out must exist meanwhile. */
+	if ((in_data == NULL) || (out_data == NULL)) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
+		    "usbsacm_open_port_pipes: look up bulk pipe failed in "
+		    "interface %d port %d",
+		    acm_port->acm_data_if_no, acm_port->acm_data_port_no);
+
+		return (USB_FAILURE);
+	}
+
+	/*
+	 * If device conform to acm spec, it must have an interrupt pipe
+	 * for this port.
+	 */
+	if (acmp->acm_compatibility == B_TRUE && intr_pipe == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
+		    "usbsacm_open_port_pipes: look up interrupt pipe failed in "
+		    "interface %d", acm_port->acm_ctrl_if_no);
+
+		return (USB_FAILURE);
+	}
+
+	policy.pp_max_async_reqs = 2;
+
+	/* Open bulk in endpoint */
+	if (usb_pipe_open(acmp->acm_dip, &in_data->ep_descr, &policy,
+	    USB_FLAGS_SLEEP, &acm_port->acm_bulkin_ph) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
+		    "usbsacm_open_port_pipes: open bulkin pipe failed!");
+
+		return (USB_FAILURE);
+	}
+
+	/* Open bulk out endpoint */
+	if (usb_pipe_open(acmp->acm_dip, &out_data->ep_descr, &policy,
+	    USB_FLAGS_SLEEP, &acm_port->acm_bulkout_ph) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
+		    "usbsacm_open_port_pipes: open bulkout pipe failed!");
+
+		usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
+		    USB_FLAGS_SLEEP, NULL, NULL);
+
+		return (USB_FAILURE);
+	}
+
+	/* Open interrupt endpoint if found. */
+	if (intr_pipe != NULL) {
+
+		if (usb_pipe_open(acmp->acm_dip, &intr_pipe->ep_descr, &policy,
+		    USB_FLAGS_SLEEP, &acm_port->acm_intr_ph) != USB_SUCCESS) {
+			USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
+			    "usbsacm_open_port_pipes: "
+			    "open control pipe failed");
+
+			usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
+			    USB_FLAGS_SLEEP, NULL, NULL);
+			usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkout_ph,
+			    USB_FLAGS_SLEEP, NULL, NULL);
+
+			return (USB_FAILURE);
+		}
+	}
+
+	/* initialize the port structure. */
+	mutex_enter(&acm_port->acm_port_mutex);
+	acm_port->acm_bulkin_size = in_data->ep_descr.wMaxPacketSize;
+	acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
+	acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
+	if (acm_port->acm_intr_ph != NULL) {
+		acm_port->acm_intr_state = USBSACM_PIPE_IDLE;
+		acm_port->acm_intr_ep_descr = intr_pipe->ep_descr;
+	}
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	if (acm_port->acm_intr_ph != NULL) {
+
+		usbsacm_pipe_start_polling(acm_port);
+	}
+
+	return (rval);
+}
+
+
+/*
+ * usbsacm_close_port_pipes:
+ *	Close pipes of one port and reset port structure to closed;
+ *	Each port includes three pipes: bulk in, bulk out and interrupt.
+ */
+static void
+usbsacm_close_port_pipes(usbsacm_port_t	*acm_port)
+{
+	usbsacm_state_t	*acmp = acm_port->acm_device;
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
+	    "usbsacm_close_port_pipes: acm_bulkin_state = %d",
+	    acm_port->acm_bulkin_state);
+
+	/*
+	 * Check the status of the given port. If port is closing or closed,
+	 * return directly.
+	 */
+	if ((acm_port->acm_bulkin_state == USBSACM_PIPE_CLOSED) ||
+	    (acm_port->acm_bulkin_state == USBSACM_PIPE_CLOSING)) {
+		USB_DPRINTF_L2(PRINT_MASK_CLOSE, acmp->acm_lh,
+		    "usbsacm_close_port_pipes: port is closing or has closed");
+		mutex_exit(&acm_port->acm_port_mutex);
+
+		return;
+	}
+
+	acm_port->acm_bulkin_state = USBSACM_PIPE_CLOSING;
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	/* Close pipes */
+	usb_pipe_reset(acmp->acm_dip, acm_port->acm_bulkin_ph,
+	    USB_FLAGS_SLEEP, 0, 0);
+	usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkin_ph,
+	    USB_FLAGS_SLEEP, 0, 0);
+	usb_pipe_close(acmp->acm_dip, acm_port->acm_bulkout_ph,
+	    USB_FLAGS_SLEEP, 0, 0);
+	if (acm_port->acm_intr_ph != NULL) {
+		usb_pipe_stop_intr_polling(acm_port->acm_intr_ph,
+		    USB_FLAGS_SLEEP);
+		usb_pipe_close(acmp->acm_dip, acm_port->acm_intr_ph,
+		    USB_FLAGS_SLEEP, 0, 0);
+	}
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	/* Reset the status of pipes to closed */
+	acm_port->acm_bulkin_state = USBSACM_PIPE_CLOSED;
+	acm_port->acm_bulkin_ph = NULL;
+	acm_port->acm_bulkout_state = USBSACM_PIPE_CLOSED;
+	acm_port->acm_bulkout_ph = NULL;
+	if (acm_port->acm_intr_ph != NULL) {
+		acm_port->acm_intr_state = USBSACM_PIPE_CLOSED;
+		acm_port->acm_intr_ph = NULL;
+	}
+
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
+	    "usbsacm_close_port_pipes: port has been closed.");
+}
+
+
+/*
+ * usbsacm_close_pipes:
+ *	close all opened pipes of current devices.
+ */
+static void
+usbsacm_close_pipes(usbsacm_state_t *acmp)
+{
+	int		i;
+
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
+	    "usbsacm_close_pipes: ");
+
+	/* Close all ports */
+	for (i = 0; i < acmp->acm_port_cnt; i++) {
+		usbsacm_close_port_pipes(&acmp->acm_ports[i]);
+	}
+}
+
+
+/*
+ * usbsacm_disconnect_pipes:
+ *	this function just call usbsacm_close_pipes.
+ */
+static void
+usbsacm_disconnect_pipes(usbsacm_state_t *acmp)
+{
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
+	    "usbsacm_disconnect_pipes: ");
+
+	usbsacm_close_pipes(acmp);
+}
+
+
+/*
+ * usbsacm_reconnect_pipes:
+ *	reconnect pipes in CPR resume or reconnect
+ */
+static int
+usbsacm_reconnect_pipes(usbsacm_state_t *acmp)
+{
+	usbsacm_port_t	*cur_port = acmp->acm_ports;
+	int		i;
+
+	USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
+	    "usbsacm_reconnect_pipes: ");
+
+	/* reopen all ports of current device. */
+	for (i = 0; i < acmp->acm_port_cnt; i++) {
+		cur_port = &acmp->acm_ports[i];
+
+		mutex_enter(&cur_port->acm_port_mutex);
+		/*
+		 * If port status is open, reopen it;
+		 * else retain the current status.
+		 */
+		if (cur_port->acm_port_state == USBSACM_PORT_OPEN) {
+
+			mutex_exit(&cur_port->acm_port_mutex);
+			if (usbsacm_open_port_pipes(cur_port) != USB_SUCCESS) {
+				USB_DPRINTF_L4(PRINT_MASK_OPEN, acmp->acm_lh,
+				    "usbsacm_reconnect_pipes: "
+				    "open port %d failed.", i);
+
+				return (USB_FAILURE);
+			}
+			mutex_enter(&cur_port->acm_port_mutex);
+		}
+		mutex_exit(&cur_port->acm_port_mutex);
+	}
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * usbsacm_bulkin_cb:
+ *	Bulk In regular and exeception callback;
+ *	USBA framework will call this callback
+ *	after deal with bulkin request.
+ */
+/*ARGSUSED*/
+static void
+usbsacm_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
+{
+	usbsacm_port_t	*acm_port = (usbsacm_port_t *)req->bulk_client_private;
+	usbsacm_state_t	*acmp = acm_port->acm_device;
+	mblk_t		*data;
+	int		data_len;
+
+	data = req->bulk_data;
+	data_len = (data) ? MBLKL(data) : 0;
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
+	    "usbsacm_bulkin_cb: "
+	    "acm_bulkin_state = %d acm_port_state = %d data_len = %d",
+	    acm_port->acm_bulkin_state, acm_port->acm_port_state, data_len);
+
+	if ((acm_port->acm_port_state == USBSACM_PORT_OPEN) && (data_len) &&
+	    (req->bulk_completion_reason == USB_CR_OK)) {
+		mutex_exit(&acm_port->acm_port_mutex);
+		/* prevent USBA from freeing data along with the request */
+		req->bulk_data = NULL;
+
+		/* save data on the receive list */
+		usbsacm_put_tail(&acm_port->acm_rx_mp, data);
+
+		/* invoke GSD receive callback */
+		if (acm_port->acm_cb.cb_rx) {
+			acm_port->acm_cb.cb_rx(acm_port->acm_cb.cb_arg);
+		}
+		mutex_enter(&acm_port->acm_port_mutex);
+	}
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	usb_free_bulk_req(req);
+
+	/* receive more */
+	mutex_enter(&acm_port->acm_port_mutex);
+	if (((acm_port->acm_bulkin_state == USBSACM_PIPE_BUSY) ||
+	    (acm_port->acm_bulkin_state == USBSACM_PIPE_IDLE)) &&
+	    (acm_port->acm_port_state == USBSACM_PORT_OPEN) &&
+	    (acmp->acm_dev_state == USB_DEV_ONLINE)) {
+		if (usbsacm_rx_start(acm_port) != USB_SUCCESS) {
+			USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
+			    "usbsacm_bulkin_cb: restart rx fail "
+			    "acm_port_state = %d", acm_port->acm_port_state);
+		}
+	} else if (acm_port->acm_bulkin_state == USBSACM_PIPE_BUSY) {
+		acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
+	}
+	mutex_exit(&acm_port->acm_port_mutex);
+}
+
+
+/*
+ * usbsacm_bulkout_cb:
+ *	Bulk Out regular and exeception callback;
+ *	USBA framework will call this callback function
+ *	after deal with bulkout request.
+ */
+/*ARGSUSED*/
+static void
+usbsacm_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
+{
+	usbsacm_port_t	*acm_port = (usbsacm_port_t *)req->bulk_client_private;
+	usbsacm_state_t	*acmp = acm_port->acm_device;
+	int		data_len;
+	mblk_t		*data = req->bulk_data;
+
+	USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
+	    "usbsacm_bulkout_cb: acmp = 0x%p", acmp);
+
+	data_len = (req->bulk_data) ? MBLKL(req->bulk_data) : 0;
+
+	/* put untransferred residue back on the transfer list */
+	if (req->bulk_completion_reason && (data_len > 0)) {
+		usbsacm_put_head(&acm_port->acm_tx_mp, data);
+		req->bulk_data = NULL;
+	}
+
+	usb_free_bulk_req(req);
+
+	/* invoke GSD transmit callback */
+	if (acm_port->acm_cb.cb_tx) {
+		acm_port->acm_cb.cb_tx(acm_port->acm_cb.cb_arg);
+	}
+
+	/* send more */
+	mutex_enter(&acm_port->acm_port_mutex);
+	acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
+	if (acm_port->acm_tx_mp == NULL) {
+		cv_broadcast(&acm_port->acm_tx_cv);
+	} else {
+		usbsacm_tx_start(acm_port);
+	}
+	mutex_exit(&acm_port->acm_port_mutex);
+}
+
+
+/*
+ * usbsacm_rx_start:
+ *	start data receipt
+ */
+static int
+usbsacm_rx_start(usbsacm_port_t *acm_port)
+{
+	usbsacm_state_t	*acmp = acm_port->acm_device;
+	usb_bulk_req_t	*br;
+	int		rval = USB_FAILURE;
+	int		data_len;
+
+	USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
+	    "usbsacm_rx_start: acm_xfer_sz = 0x%x acm_bulkin_size = 0x%x",
+	    acmp->acm_xfer_sz, acm_port->acm_bulkin_size);
+
+	acm_port->acm_bulkin_state = USBSACM_PIPE_BUSY;
+	/*
+	 * Qualcomm CDMA card won't response the first request,
+	 * if the following code don't multiply by 2.
+	 */
+	data_len = min(acmp->acm_xfer_sz, acm_port->acm_bulkin_size * 2);
+	mutex_exit(&acm_port->acm_port_mutex);
+
+	br = usb_alloc_bulk_req(acmp->acm_dip, data_len, USB_FLAGS_SLEEP);
+	if (br == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
+		    "usbsacm_rx_start: allocate bulk request failed");
+
+		mutex_enter(&acm_port->acm_port_mutex);
+
+		return (USB_FAILURE);
+	}
+	/* initialize bulk in request. */
+	br->bulk_len = data_len;
+	br->bulk_timeout = USBSACM_BULKIN_TIMEOUT;
+	br->bulk_cb = usbsacm_bulkin_cb;
+	br->bulk_exc_cb = usbsacm_bulkin_cb;
+	br->bulk_client_private = (usb_opaque_t)acm_port;
+	br->bulk_attributes = USB_ATTRS_AUTOCLEARING
+	    | USB_ATTRS_SHORT_XFER_OK;
+
+	rval = usb_pipe_bulk_xfer(acm_port->acm_bulkin_ph, br, 0);
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	if (rval != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
+		    "usbsacm_rx_start: bulk transfer failed %d", rval);
+		usb_free_bulk_req(br);
+		acm_port->acm_bulkin_state = USBSACM_PIPE_IDLE;
+	}
+
+	return (rval);
+}
+
+
+/*
+ * usbsacm_tx_start:
+ *	start data transmit
+ */
+static void
+usbsacm_tx_start(usbsacm_port_t *acm_port)
+{
+	int		len;		/* bytes we can transmit */
+	mblk_t		*data;		/* data to be transmitted */
+	int		data_len;	/* bytes in 'data' */
+	mblk_t		*mp;		/* current msgblk */
+	int		copylen;	/* bytes copy from 'mp' to 'data' */
+	int		rval;
+	usbsacm_state_t	*acmp = acm_port->acm_device;
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
+	    "usbsacm_tx_start: ");
+
+	/* check the transmitted data. */
+	if (acm_port->acm_tx_mp == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
+		    "usbsacm_tx_start: acm_tx_mp is NULL");
+
+		return;
+	}
+
+	/* check pipe status */
+	if (acm_port->acm_bulkout_state != USBSACM_PIPE_IDLE) {
+
+		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
+		    "usbsacm_tx_start: error state in bulkout endpoint");
+
+		return;
+	}
+	ASSERT(MBLKL(acm_port->acm_tx_mp) > 0);
+
+	/* send as much data as port can receive */
+	len = min(msgdsize(acm_port->acm_tx_mp), acmp->acm_xfer_sz);
+
+	if (len == 0) {
+		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
+		    "usbsacm_tx_start: data len is 0");
+
+		return;
+	}
+
+	/* allocate memory for sending data. */
+	if ((data = allocb(len, BPRI_LO)) == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
+		    "usbsacm_tx_start: failure in allocate memory");
+
+		return;
+	}
+
+	/*
+	 * copy no more than 'len' bytes from mblk chain to transmit mblk 'data'
+	 */
+	data_len = 0;
+	while ((data_len < len) && acm_port->acm_tx_mp) {
+		/* Get the first mblk from chain. */
+		mp = acm_port->acm_tx_mp;
+		copylen = min(MBLKL(mp), len - data_len);
+		bcopy(mp->b_rptr, data->b_wptr, copylen);
+		mp->b_rptr += copylen;
+		data->b_wptr += copylen;
+		data_len += copylen;
+
+		if (MBLKL(mp) <= 0) {
+			acm_port->acm_tx_mp = unlinkb(mp);
+			freeb(mp);
+		} else {
+			ASSERT(data_len == len);
+		}
+	}
+
+	if (data_len <= 0) {
+		freeb(data);
+
+		return;
+	}
+
+	acm_port->acm_bulkout_state = USBSACM_PIPE_BUSY;
+
+	mutex_exit(&acm_port->acm_port_mutex);
+	/* send request. */
+	rval = usbsacm_send_data(acm_port, data);
+	mutex_enter(&acm_port->acm_port_mutex);
+
+	/*
+	 * If send failed, retransmit data when acm_tx_mp is null.
+	 */
+	if (rval != USB_SUCCESS) {
+		acm_port->acm_bulkout_state = USBSACM_PIPE_IDLE;
+		if (acm_port->acm_tx_mp == NULL) {
+			usbsacm_put_head(&acm_port->acm_tx_mp, data);
+		}
+	}
+}
+
+
+/*
+ * usbsacm_send_data:
+ *	data transfer
+ */
+static int
+usbsacm_send_data(usbsacm_port_t *acm_port, mblk_t *data)
+{
+	usbsacm_state_t	*acmp = acm_port->acm_device;
+	usb_bulk_req_t	*br;
+	int		rval;
+	int		data_len = MBLKL(data);
+
+	USB_DPRINTF_L4(PRINT_MASK_EVENTS, acmp->acm_lh,
+	    "usbsacm_send_data: data address is 0x%p, length = %d",
+	    data, data_len);
+
+	br = usb_alloc_bulk_req(acmp->acm_dip, 0, USB_FLAGS_SLEEP);
+	if (br == NULL) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
+		    "usbsacm_send_data: alloc req failed.");
+
+		return (USB_FAILURE);
+	}
+
+	/* initialize the bulk out request */
+	br->bulk_data = data;
+	br->bulk_len = data_len;
+	br->bulk_timeout = USBSACM_BULKOUT_TIMEOUT;
+	br->bulk_cb = usbsacm_bulkout_cb;
+	br->bulk_exc_cb = usbsacm_bulkout_cb;
+	br->bulk_client_private = (usb_opaque_t)acm_port;
+	br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
+
+	rval = usb_pipe_bulk_xfer(acm_port->acm_bulkout_ph, br, 0);
+
+	if (rval != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_EVENTS, acmp->acm_lh,
+		    "usbsacm_send_data: Send Data failed.");
+
+		/*
+		 * Don't free it in usb_free_bulk_req because it will
+		 * be linked in usbsacm_put_head
+		 */
+		br->bulk_data = NULL;
+
+		usb_free_bulk_req(br);
+	}
+
+	return (rval);
+}
+
+/*
+ * usbsacm_wait_tx_drain:
+ *	wait until local tx buffer drains.
+ *	'timeout' is in seconds, zero means wait forever
+ */
+static int
+usbsacm_wait_tx_drain(usbsacm_port_t *acm_port, int timeout)
+{
+	clock_t		until;
+	int		over = 0;
+
+	until = ddi_get_lbolt() + drv_usectohz(1000 * 1000 * timeout);
+
+	while (acm_port->acm_tx_mp && !over) {
+		if (timeout > 0) {
+			over = (cv_timedwait_sig(&acm_port->acm_tx_cv,
+			    &acm_port->acm_port_mutex, until) <= 0);
+		} else {
+			over = (cv_wait_sig(&acm_port->acm_tx_cv,
+			    &acm_port->acm_port_mutex) == 0);
+		}
+	}
+
+	return ((acm_port->acm_tx_mp == NULL) ? USB_SUCCESS : USB_FAILURE);
+}
+
+
+/*
+ * usbsacm_req_write:
+ *	send command over control pipe
+ */
+static int
+usbsacm_req_write(usbsacm_port_t *acm_port, uchar_t request, uint16_t value,
+    mblk_t **data)
+{
+	usbsacm_state_t	*acmp = acm_port->acm_device;
+	usb_ctrl_setup_t setup;
+	usb_cb_flags_t	cb_flags;
+	usb_cr_t	cr;
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
+	    "usbsacm_req_write: ");
+
+	/* initialize the control request. */
+	setup.bmRequestType = USBSACM_REQ_WRITE_IF;
+	setup.bRequest = request;
+	setup.wValue = value;
+	setup.wIndex = acm_port->acm_ctrl_if_no;
+	setup.wLength = ((data != NULL) && (*data != NULL)) ? MBLKL(*data) : 0;
+	setup.attrs = 0;
+
+	return (usb_pipe_ctrl_xfer_wait(acmp->acm_def_ph, &setup, data,
+	    &cr, &cb_flags, 0));
+}
+
+
+/*
+ * usbsacm_set_line_coding:
+ *	Send USB_CDC_REQ_SET_LINE_CODING request
+ */
+static int
+usbsacm_set_line_coding(usbsacm_port_t *acm_port, usb_cdc_line_coding_t *lc)
+{
+	mblk_t		*bp;
+	int		ret;
+
+	/* allocate mblk and copy supplied structure into it */
+	if ((bp = allocb(USB_CDC_LINE_CODING_LEN, BPRI_HI)) == NULL) {
+
+		return (USB_NO_RESOURCES);
+	}
+
+#ifndef __lock_lint /* warlock gets confused here */
+	*((usb_cdc_line_coding_t *)bp->b_wptr) = *lc;
+	bp->b_wptr += USB_CDC_LINE_CODING_LEN;
+#endif
+
+	ret = usbsacm_req_write(acm_port, USB_CDC_REQ_SET_LINE_CODING, 0, &bp);
+
+	if (bp != NULL) {
+		freeb(bp);
+	}
+
+	return (ret);
+}
+
+
+
+/*
+ * usbsacm_mctl2reg:
+ *	Set Modem control status
+ */
+static void
+usbsacm_mctl2reg(int mask, int val, uint8_t *line_ctl)
+{
+	if (mask & TIOCM_RTS) {
+		if (val & TIOCM_RTS) {
+			*line_ctl |= USB_CDC_ACM_CONTROL_RTS;
+		} else {
+			*line_ctl &= ~USB_CDC_ACM_CONTROL_RTS;
+		}
+	}
+	if (mask & TIOCM_DTR) {
+		if (val & TIOCM_DTR) {
+			*line_ctl |= USB_CDC_ACM_CONTROL_DTR;
+		} else {
+			*line_ctl &= ~USB_CDC_ACM_CONTROL_DTR;
+		}
+	}
+}
+
+
+/*
+ * usbsacm_reg2mctl:
+ *	Get Modem control status
+ */
+static int
+usbsacm_reg2mctl(uint8_t line_ctl)
+{
+	int	val = 0;
+
+	if (line_ctl & USB_CDC_ACM_CONTROL_RTS) {
+		val |= TIOCM_RTS;
+	}
+	if (line_ctl & USB_CDC_ACM_CONTROL_DTR) {
+		val |= TIOCM_DTR;
+	}
+	if (line_ctl & USB_CDC_ACM_CONTROL_DSR) {
+		val |= TIOCM_DSR;
+	}
+	if (line_ctl & USB_CDC_ACM_CONTROL_RNG) {
+		val |= TIOCM_RI;
+	}
+
+	return (val);
+}
+
+
+/*
+ * misc routines
+ * -------------
+ *
+ */
+
+/*
+ * usbsacm_put_tail:
+ *	link a message block to tail of message
+ *	account for the case when message is null
+ */
+static void
+usbsacm_put_tail(mblk_t **mpp, mblk_t *bp)
+{
+	if (*mpp) {
+		linkb(*mpp, bp);
+	} else {
+		*mpp = bp;
+	}
+}
+
+
+/*
+ * usbsacm_put_head:
+ *	put a message block at the head of the message
+ *	account for the case when message is null
+ */
+static void
+usbsacm_put_head(mblk_t **mpp, mblk_t *bp)
+{
+	if (*mpp) {
+		linkb(bp, *mpp);
+	}
+	*mpp = bp;
+}
+
+
+/*
+ * power management
+ * ----------------
+ *
+ * usbsacm_create_pm_components:
+ *	create PM components
+ */
+static int
+usbsacm_create_pm_components(usbsacm_state_t *acmp)
+{
+	dev_info_t	*dip = acmp->acm_dip;
+	usbsacm_pm_t	*pm;
+	uint_t		pwr_states;
+	usb_dev_descr_t *dev_descr;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
+	    "usbsacm_create_pm_components: ");
+
+	if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
+		    "usbsacm_create_pm_components: failed");
+
+		return (USB_SUCCESS);
+	}
+
+	pm = acmp->acm_pm =
+	    (usbsacm_pm_t *)kmem_zalloc(sizeof (usbsacm_pm_t), KM_SLEEP);
+
+	pm->pm_pwr_states = (uint8_t)pwr_states;
+	pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
+	/*
+	 * Qualcomm CDMA card won't response the following control commands
+	 * after receive USB_REMOTE_WAKEUP_ENABLE. So we just set
+	 * pm_wakeup_enable to 0 for this specific device.
+	 */
+	dev_descr = acmp->acm_dev_data->dev_descr;
+	if (dev_descr->idVendor == 0x5c6 && dev_descr->idProduct == 0x3100) {
+		pm->pm_wakeup_enabled = 0;
+	} else {
+		pm->pm_wakeup_enabled = (usb_handle_remote_wakeup(dip,
+		    USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS);
+	}
+
+	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * usbsacm_destroy_pm_components:
+ *	destroy PM components
+ */
+static void
+usbsacm_destroy_pm_components(usbsacm_state_t *acmp)
+{
+	usbsacm_pm_t	*pm = acmp->acm_pm;
+	dev_info_t	*dip = acmp->acm_dip;
+	int		rval;
+
+	USB_DPRINTF_L4(PRINT_MASK_CLOSE, acmp->acm_lh,
+	    "usbsacm_destroy_pm_components: ");
+
+	if (acmp->acm_dev_state != USB_DEV_DISCONNECTED) {
+		if (pm->pm_wakeup_enabled) {
+			rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
+			if (rval != DDI_SUCCESS) {
+				USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
+				    "usbsacm_destroy_pm_components: "
+				    "raising power failed (%d)", rval);
+			}
+
+			rval = usb_handle_remote_wakeup(dip,
+			    USB_REMOTE_WAKEUP_DISABLE);
+			if (rval != USB_SUCCESS) {
+				USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
+				    "usbsacm_destroy_pm_components: "
+				    "disable remote wakeup failed (%d)", rval);
+			}
+		}
+
+		(void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
+	}
+	kmem_free((caddr_t)pm, sizeof (usbsacm_pm_t));
+	acmp->acm_pm = NULL;
+}
+
+
+/*
+ * usbsacm_pm_set_busy:
+ *	mark device busy and raise power
+ */
+static void
+usbsacm_pm_set_busy(usbsacm_state_t *acmp)
+{
+	usbsacm_pm_t	*pm = acmp->acm_pm;
+	dev_info_t	*dip = acmp->acm_dip;
+	int		rval;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
+	    "usbsacm_pm_set_busy: pm = 0x%p", pm);
+
+	if (pm == NULL) {
+
+		return;
+	}
+
+	mutex_enter(&acmp->acm_mutex);
+	/* if already marked busy, just increment the counter */
+	if (pm->pm_busy_cnt++ > 0) {
+		mutex_exit(&acmp->acm_mutex);
+
+		return;
+	}
+
+	(void) pm_busy_component(dip, 0);
+
+	if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) {
+		mutex_exit(&acmp->acm_mutex);
+
+		return;
+	}
+
+	/* need to raise power	*/
+	pm->pm_raise_power = B_TRUE;
+	mutex_exit(&acmp->acm_mutex);
+
+	rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
+	if (rval != DDI_SUCCESS) {
+		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
+		    "usbsacm_pm_set_busy: raising power failed");
+	}
+
+	mutex_enter(&acmp->acm_mutex);
+	pm->pm_raise_power = B_FALSE;
+	mutex_exit(&acmp->acm_mutex);
+}
+
+
+/*
+ * usbsacm_pm_set_idle:
+ *	mark device idle
+ */
+static void
+usbsacm_pm_set_idle(usbsacm_state_t *acmp)
+{
+	usbsacm_pm_t	*pm = acmp->acm_pm;
+	dev_info_t	*dip = acmp->acm_dip;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
+	    "usbsacm_pm_set_idle: ");
+
+	if (pm == NULL) {
+
+		return;
+	}
+
+	/*
+	 * if more ports use the device, do not mark as yet
+	 */
+	mutex_enter(&acmp->acm_mutex);
+	if (--pm->pm_busy_cnt > 0) {
+		mutex_exit(&acmp->acm_mutex);
+
+		return;
+	}
+
+	if (pm) {
+		(void) pm_idle_component(dip, 0);
+	}
+	mutex_exit(&acmp->acm_mutex);
+}
+
+
+/*
+ * usbsacm_pwrlvl0:
+ *	Functions to handle power transition for OS levels 0 -> 3
+ *	The same level as OS state, different from USB state
+ */
+static int
+usbsacm_pwrlvl0(usbsacm_state_t *acmp)
+{
+	int		rval;
+	int		i;
+	usbsacm_port_t	*cur_port = acmp->acm_ports;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
+	    "usbsacm_pwrlvl0: ");
+
+	switch (acmp->acm_dev_state) {
+	case USB_DEV_ONLINE:
+		/* issue USB D3 command to the device */
+		rval = usb_set_device_pwrlvl3(acmp->acm_dip);
+		ASSERT(rval == USB_SUCCESS);
+
+		if (cur_port != NULL) {
+		    for (i = 0; i < acmp->acm_port_cnt; i++) {
+			cur_port = &acmp->acm_ports[i];
+			if (cur_port->acm_intr_ph != NULL &&
+			    cur_port->acm_port_state != USBSACM_PORT_CLOSED) {
+
+			    mutex_exit(&acmp->acm_mutex);
+			    usb_pipe_stop_intr_polling(cur_port->acm_intr_ph,
+				USB_FLAGS_SLEEP);
+			    mutex_enter(&acmp->acm_mutex);
+
+			    mutex_enter(&cur_port->acm_port_mutex);
+			    cur_port->acm_intr_state = USBSACM_PIPE_IDLE;
+			    mutex_exit(&cur_port->acm_port_mutex);
+			}
+		    }
+		}
+
+		acmp->acm_dev_state = USB_DEV_PWRED_DOWN;
+		acmp->acm_pm->pm_cur_power = USB_DEV_OS_PWR_OFF;
+
+		/* FALLTHRU */
+	case USB_DEV_DISCONNECTED:
+	case USB_DEV_SUSPENDED:
+		/* allow a disconnect/cpr'ed device to go to lower power */
+
+		return (USB_SUCCESS);
+	case USB_DEV_PWRED_DOWN:
+	default:
+		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
+		    "usbsacm_pwrlvl0: illegal device state");
+
+		return (USB_FAILURE);
+	}
+}
+
+
+/*
+ * usbsacm_pwrlvl1:
+ *	Functions to handle power transition for OS levels 1 -> 2
+ */
+static int
+usbsacm_pwrlvl1(usbsacm_state_t *acmp)
+{
+	/* issue USB D2 command to the device */
+	(void) usb_set_device_pwrlvl2(acmp->acm_dip);
+
+	return (USB_FAILURE);
+}
+
+
+/*
+ * usbsacm_pwrlvl2:
+ *	Functions to handle power transition for OS levels 2 -> 1
+ */
+static int
+usbsacm_pwrlvl2(usbsacm_state_t *acmp)
+{
+	/* issue USB D1 command to the device */
+	(void) usb_set_device_pwrlvl1(acmp->acm_dip);
+
+	return (USB_FAILURE);
+}
+
+
+/*
+ * usbsacm_pwrlvl3:
+ *	Functions to handle power transition for OS levels 3 -> 0
+ *	The same level as OS state, different from USB state
+ */
+static int
+usbsacm_pwrlvl3(usbsacm_state_t *acmp)
+{
+	int		rval;
+	int		i;
+	usbsacm_port_t	*cur_port = acmp->acm_ports;
+
+	USB_DPRINTF_L4(PRINT_MASK_PM, acmp->acm_lh,
+	    "usbsacm_pwrlvl3: ");
+
+	switch (acmp->acm_dev_state) {
+	case USB_DEV_PWRED_DOWN:
+		/* Issue USB D0 command to the device here */
+		rval = usb_set_device_pwrlvl0(acmp->acm_dip);
+		ASSERT(rval == USB_SUCCESS);
+
+		if (cur_port != NULL) {
+		    for (i = 0; i < acmp->acm_port_cnt; i++) {
+			cur_port = &acmp->acm_ports[i];
+			if (cur_port->acm_intr_ph != NULL &&
+			    cur_port->acm_port_state != USBSACM_PORT_CLOSED) {
+
+			    mutex_exit(&acmp->acm_mutex);
+			    usbsacm_pipe_start_polling(cur_port);
+			    mutex_enter(&acmp->acm_mutex);
+			}
+		    }
+		}
+
+		acmp->acm_dev_state = USB_DEV_ONLINE;
+		acmp->acm_pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
+
+		/* FALLTHRU */
+	case USB_DEV_ONLINE:
+		/* we are already in full power */
+
+		/* FALLTHRU */
+	case USB_DEV_DISCONNECTED:
+	case USB_DEV_SUSPENDED:
+
+		return (USB_SUCCESS);
+	default:
+		USB_DPRINTF_L2(PRINT_MASK_PM, acmp->acm_lh,
+		    "usbsacm_pwrlvl3: illegal device state");
+
+		return (USB_FAILURE);
+	}
+}
+
+
+/*
+ * usbsacm_pipe_start_polling:
+ *	start polling on the interrupt pipe
+ */
+static void
+usbsacm_pipe_start_polling(usbsacm_port_t *acm_port)
+{
+	usb_intr_req_t	*intr;
+	int		rval;
+	usbsacm_state_t	*acmp = acm_port->acm_device;
+
+	USB_DPRINTF_L4(PRINT_MASK_ATTA, acmp->acm_lh,
+	    "usbsacm_pipe_start_polling: ");
+
+	if (acm_port->acm_intr_ph == NULL) {
+
+		return;
+	}
+
+	intr = usb_alloc_intr_req(acmp->acm_dip, 0, USB_FLAGS_SLEEP);
+
+	/*
+	 * If it is in interrupt context, usb_alloc_intr_req will return NULL if
+	 * called with SLEEP flag.
+	 */
+	if (!intr) {
+		USB_DPRINTF_L2(PRINT_MASK_OPEN, acmp->acm_lh,
+		    "usbsacm_pipe_start_polling: alloc req failed.");
+
+		return;
+	}
+
+	/* initialize the interrupt request. */
+	intr->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
+		USB_ATTRS_AUTOCLEARING;
+	mutex_enter(&acm_port->acm_port_mutex);
+	intr->intr_len = acm_port->acm_intr_ep_descr.wMaxPacketSize;
+	mutex_exit(&acm_port->acm_port_mutex);
+	intr->intr_client_private = (usb_opaque_t)acm_port;
+	intr->intr_cb = usbsacm_intr_cb;
+	intr->intr_exc_cb = usbsacm_intr_ex_cb;
+
+	rval = usb_pipe_intr_xfer(acm_port->acm_intr_ph, intr, USB_FLAGS_SLEEP);
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	if (rval == USB_SUCCESS) {
+		acm_port->acm_intr_state = USBSACM_PIPE_BUSY;
+	} else {
+		usb_free_intr_req(intr);
+		acm_port->acm_intr_state = USBSACM_PIPE_IDLE;
+		USB_DPRINTF_L3(PRINT_MASK_OPEN, acmp->acm_lh,
+		    "usbsacm_pipe_start_polling: failed (%d)", rval);
+	}
+	mutex_exit(&acm_port->acm_port_mutex);
+}
+
+
+/*
+ * usbsacm_intr_cb:
+ *	interrupt pipe normal callback
+ */
+/*ARGSUSED*/
+static void
+usbsacm_intr_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
+{
+	usbsacm_port_t	*acm_port = (usbsacm_port_t *)req->intr_client_private;
+	usbsacm_state_t	*acmp = acm_port->acm_device;
+	mblk_t		*data = req->intr_data;
+	int		data_len;
+
+	USB_DPRINTF_L4(PRINT_MASK_CB, acmp->acm_lh,
+	    "usbsacm_intr_cb: ");
+
+	data_len = (data) ? MBLKL(data) : 0;
+
+	/* check data length */
+	if (data_len < 8) {
+		USB_DPRINTF_L2(PRINT_MASK_CB, acmp->acm_lh,
+		    "usbsacm_intr_cb: %d packet too short", data_len);
+		usb_free_intr_req(req);
+
+		return;
+	}
+	req->intr_data = NULL;
+	usb_free_intr_req(req);
+
+	mutex_enter(&acm_port->acm_port_mutex);
+	/* parse interrupt data. */
+	usbsacm_parse_intr_data(acm_port, data);
+	mutex_exit(&acm_port->acm_port_mutex);
+}
+
+
+/*
+ * usbsacm_intr_ex_cb:
+ *	interrupt pipe exception callback
+ */
+/*ARGSUSED*/
+static void
+usbsacm_intr_ex_cb(usb_pipe_handle_t ph, usb_intr_req_t *req)
+{
+	usbsacm_port_t	*acm_port = (usbsacm_port_t *)req->intr_client_private;
+	usbsacm_state_t	*acmp = acm_port->acm_device;
+	usb_cr_t	cr = req->intr_completion_reason;
+
+	USB_DPRINTF_L4(PRINT_MASK_CB, acmp->acm_lh,
+	    "usbsacm_intr_ex_cb: ");
+
+	usb_free_intr_req(req);
+
+	/*
+	 * If completion reason isn't USB_CR_PIPE_CLOSING and
+	 * USB_CR_STOPPED_POLLING, restart polling.
+	 */
+	if ((cr != USB_CR_PIPE_CLOSING) && (cr != USB_CR_STOPPED_POLLING)) {
+		mutex_enter(&acmp->acm_mutex);
+
+		if (acmp->acm_dev_state != USB_DEV_ONLINE) {
+
+			USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
+			    "usbsacm_intr_ex_cb: state = %d",
+			    acmp->acm_dev_state);
+
+			mutex_exit(&acmp->acm_mutex);
+
+			return;
+		}
+		mutex_exit(&acmp->acm_mutex);
+
+		usbsacm_pipe_start_polling(acm_port);
+	}
+}
+
+
+/*
+ * usbsacm_parse_intr_data:
+ *	Parse data received from interrupt callback
+ */
+static void
+usbsacm_parse_intr_data(usbsacm_port_t *acm_port, mblk_t *data)
+{
+	usbsacm_state_t	*acmp = acm_port->acm_device;
+	uint8_t		bmRequestType;
+	uint8_t		bNotification;
+	uint16_t	wValue;
+	uint16_t	wLength;
+	uint16_t	wData;
+
+	USB_DPRINTF_L4(PRINT_MASK_ALL, acmp->acm_lh,
+	    "usbsacm_parse_intr_data: ");
+
+	bmRequestType = data->b_rptr[0];
+	bNotification = data->b_rptr[1];
+	/*
+	 * If Notification type is NETWORK_CONNECTION, wValue is 0 or 1,
+	 * mLength is 0. If Notification type is SERIAL_TYPE, mValue is 0,
+	 * mLength is 2. So we directly get the value from the byte.
+	 */
+	wValue = data->b_rptr[2];
+	wLength = data->b_rptr[6];
+
+	if (bmRequestType != USB_CDC_NOTIFICATION_REQUEST_TYPE) {
+		USB_DPRINTF_L2(PRINT_MASK_CB, acmp->acm_lh,
+		    "usbsacm_parse_intr_data: unknown request type - 0x%x",
+		    bmRequestType);
+
+		return;
+	}
+
+	/*
+	 * Check the return value of device
+	 */
+	switch (bNotification) {
+	case USB_CDC_NOTIFICATION_NETWORK_CONNECTION:
+		USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
+		    "usbsacm_parse_intr_data: %s network!",
+		    wValue ? "connected to" :"disconnected from");
+
+		break;
+	case USB_CDC_NOTIFICATION_RESPONSE_AVAILABLE:
+		USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
+		    "usbsacm_parse_intr_data: A response is a available.");
+
+		break;
+	case USB_CDC_NOTIFICATION_SERIAL_STATE:
+		/* check the parameter's length. */
+		if (wLength != 2) {
+
+			USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
+			    "usbsacm_parse_intr_data: error data length.");
+		} else {
+			/*
+			 * The Data field is a bitmapped value that contains
+			 * the current state of carrier detect, transmission
+			 * carrier, break, ring signal and device overrun
+			 * error.
+			 */
+			wData = data->b_rptr[8];
+			/*
+			 * Check the serial state of the current port.
+			 */
+			if (wData & USB_CDC_ACM_CONTROL_DCD) {
+
+				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
+				    "usbsacm_parse_intr_data: "
+				    "receiver carrier is set.");
+			}
+			if (wData & USB_CDC_ACM_CONTROL_DSR) {
+
+				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
+				    "usbsacm_parse_intr_data: "
+				    "transmission carrier is set.");
+
+				acm_port->acm_mctlin |= USB_CDC_ACM_CONTROL_DSR;
+			}
+			if (wData & USB_CDC_ACM_CONTROL_BREAK) {
+
+				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
+				    "usbsacm_parse_intr_data: "
+				    "break detection mechanism is set.");
+			}
+			if (wData & USB_CDC_ACM_CONTROL_RNG) {
+
+				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
+				    "usbsacm_parse_intr_data: "
+				    "ring signal detection is set.");
+
+				acm_port->acm_mctlin |= USB_CDC_ACM_CONTROL_RNG;
+			}
+			if (wData & USB_CDC_ACM_CONTROL_FRAMING) {
+
+				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
+				    "usbsacm_parse_intr_data: "
+				    "A framing error has occurred.");
+			}
+			if (wData & USB_CDC_ACM_CONTROL_PARITY) {
+
+				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
+				    "usbsacm_parse_intr_data: "
+				    "A parity error has occurred.");
+			}
+			if (wData & USB_CDC_ACM_CONTROL_OVERRUN) {
+
+				USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
+				    "usbsacm_parse_intr_data: "
+				    "Received data has been discarded "
+				    "due to overrun.");
+			}
+		}
+
+		break;
+	default:
+		USB_DPRINTF_L3(PRINT_MASK_CB, acmp->acm_lh,
+		    "usbsacm_parse_intr_data: unknown notification - 0x%x!",
+		    bNotification);
+
+		break;
+	}
+
+	freemsg(data);
+}
--- a/usr/src/uts/common/io/usb/inc.flg	Tue Dec 12 23:13:49 2006 -0800
+++ b/usr/src/uts/common/io/usb/inc.flg	Tue Dec 12 23:36:51 2006 -0800
@@ -3,9 +3,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -21,7 +20,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -76,6 +75,7 @@
 	usr/src/uts/sparc/usbser \
 	usr/src/uts/sparc/usbsksp \
 	usr/src/uts/sparc/usbsprl \
+	usr/src/uts/sparc/usbsacm \
 	usr/src/uts/sparc/usbskel \
 	usr/src/uts/sparc/warlock \
 	usr/src/uts/intel/hid	\
@@ -97,6 +97,7 @@
 	usr/src/uts/intel/usbser \
 	usr/src/uts/intel/usbsksp \
 	usr/src/uts/intel/usbsprl \
+	usr/src/uts/intel/usbsacm \
 	usr/src/uts/intel/ia32 \
 	usr/src/uts/intel/amd64 \
 	usr/src/uts/intel/asm 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/warlock/usbsacm.wlcmd	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,92 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+
+one usbser_state
+one usbsacm_state
+
+### specify the root functions
+
+root usbser_first_device
+root usbser_putchar
+root usbser_getchar
+root usbser_ischar
+root usbser_polledio_enter
+root usbser_polledio_exit
+root usbser_soft_state_size
+root usbsacm_open
+root usbser_close
+root usbser_wput
+root usbser_wsrv
+root usbser_rsrv
+root usbser_tx_cb
+root usbser_rx_cb
+root usbser_status_cb
+root usbser_wq_thread
+root usbser_rq_thread
+root usbser_disconnect_cb
+root usbser_reconnect_cb
+root usbser_cpr_suspend
+root usbser_cpr_resume
+
+root usbsacm_bulkin_cb
+root usbsacm_bulkout_cb
+root usbsacm_intr_cb
+root usbsacm_intr_ex_cb
+
+### specify usbsacm function pointers
+
+add ds_ops::ds_attach targets usbsacm_ds_attach
+add ds_ops::ds_detach targets usbsacm_ds_detach
+add ds_ops::ds_register_cb targets usbsacm_ds_register_cb
+add ds_ops::ds_unregister_cb targets usbsacm_ds_unregister_cb
+add ds_ops::ds_open_port targets usbsacm_ds_open_port
+add ds_ops::ds_close_port targets usbsacm_ds_close_port
+add ds_ops::ds_usb_power targets usbsacm_ds_usb_power
+add ds_ops::ds_suspend targets usbsacm_ds_suspend
+add ds_ops::ds_resume targets usbsacm_ds_resume
+add ds_ops::ds_disconnect targets usbsacm_ds_disconnect
+add ds_ops::ds_reconnect targets usbsacm_ds_reconnect
+add ds_ops::ds_set_port_params targets usbsacm_ds_set_port_params
+add ds_ops::ds_set_modem_ctl targets usbsacm_ds_set_modem_ctl
+add ds_ops::ds_get_modem_ctl targets usbsacm_ds_get_modem_ctl
+add ds_ops::ds_break_ctl targets usbsacm_ds_break_ctl
+add ds_ops::ds_tx targets usbsacm_ds_tx
+add ds_ops::ds_rx targets usbsacm_ds_rx
+add ds_ops::ds_stop targets usbsacm_ds_stop
+add ds_ops::ds_start targets usbsacm_ds_start
+add ds_ops::ds_fifo_flush targets usbsacm_ds_fifo_flush
+add ds_ops::ds_fifo_drain targets usbsacm_ds_fifo_drain
+add ds_ops::ds_in_pipe targets warlock_dummy
+add ds_ops::ds_out_pipe targets warlock_dummy
+
+add usbsacm_port::acm_cb.cb_tx targets usbser_tx_cb
+add usbsacm_port::acm_cb.cb_rx targets usbser_rx_cb
+
+add bus_ops::bus_add_eventcall targets warlock_dummy
+add bus_ops::bus_get_eventcookie targets warlock_dummy
+add bus_ops::bus_post_event targets warlock_dummy
+add bus_ops::bus_remove_eventcall targets warlock_dummy
+add bus_ops::bus_intr_ctl targets warlock_dummy
+add bus_ops::bus_config targets warlock_dummy
+add bus_ops::bus_unconfig targets warlock_dummy
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/warlock/usbsacm_with_usba.wlcmd	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,213 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+
+one ohci_state
+one ehci_state
+one uhci_state
+one usbser_state
+one usbsacm_state
+one usbsacm_port
+
+### specify the root functions
+
+root usbser_first_device
+root usbser_putchar
+root usbser_getchar
+root usbser_ischar
+root usbser_polledio_enter
+root usbser_polledio_exit
+root usb_console_output_fini
+root usb_console_input_enter
+root usb_console_input_exit
+root usb_console_input_fini
+root usb_console_input_init
+root usb_console_read
+root usb_get_dev_descr
+root usb_get_if_number
+root usb_parse_CV_cfg_descr
+root usb_parse_CV_ep_descr
+root usb_parse_CV_if_descr
+root usb_pipe_get_private
+root usb_get_current_frame_number
+root usb_get_max_isoc_pkts
+root usb_pipe_set_private
+root usba_ready_interface_node
+root usba_free_hcdi_ops
+root usba_async_req_raise_power
+root usba_async_req_lower_power
+root usb_req_raise_power
+root usb_req_lower_power
+root usb_set_device_pwrlvl0
+root usb_set_device_pwrlvl1
+root usb_set_device_pwrlvl2
+root usb_set_device_pwrlvl3
+root usb_is_pm_enabled
+root usb_async_req
+root usb_pipe_bulk_transfer_size
+root usb_get_ep_data
+root usba_pipe_get_policy
+root usb_pipe_ctrl_xfer_wait
+root usb_pipe_drain_reqs
+root usb_try_serialize_access
+root usb_fini_serialization
+root usb_init_serialization
+root usb_release_access
+root usb_serialize_access
+root usb_clr_feature
+root usb_clear_feature
+root usb_get_alt_if
+root usb_get_ep_descr
+root usb_get_if_descr
+root usb_log
+root usb_pipe_isoc_xfer
+root usb_pipe_stop_isoc_polling
+root usb_set_alt_if
+root usb_set_cfg
+root usb_get_cfg
+root usb_ep_num
+root usb_get_status
+root usb_pipe_reset
+root usb_log_descr_tree
+root usb_print_descr_tree
+root usb_rval2errno
+root usb_register_hotplug_cbs
+root usb_get_current_cfgidx
+root usb_register_client
+
+root usb_ugen_attach
+root usb_ugen_close
+root usb_ugen_detach
+root usb_ugen_disconnect_ev_cb
+root usb_ugen_get_hdl
+root usb_ugen_open
+root usb_ugen_poll
+root usb_ugen_power
+root usb_ugen_read
+root usb_ugen_reconnect_ev_cb
+root usb_ugen_write
+
+root hcdi_autoclearing
+root hcdi_cb_thread
+root hcdi_shared_cb_thread
+
+root hubd_restore_state_cb
+root hubd_disconnect_event_cb
+root hubd_post_resume_event_cb
+root hubd_pre_suspend_event_cb
+root hubd_reconnect_event_cb
+root hubd_hotplug_thread
+root hubd_cpr_post_user_callb
+root hubd_root_hub_cleanup_thread
+root hubd_bus_power
+
+root usba_pipe_do_async_func_thread
+root usba_pipe_sync_reset
+root usba_get_hc_dma_attr
+root usba_hcdi_get_req_private
+root usba_hcdi_set_req_private
+root usba_dbuf_tail
+root usba_hubdi_power
+root usba_hubdi_root_hub_power
+root usba_get_hotplug_stats
+root usba_reset_hotplug_stats
+root usba_ascii_string_descr
+root usba_move_list
+root usba_taskq_destroy
+root usba_mk_mctl
+root usb_fail_checkpoint
+
+root ohci_intr
+root ehci_intr
+
+### specify the usbsacm root functions
+
+root usbser_soft_state_size
+root usbsacm_open
+root usbser_close
+root usbser_wput
+root usbser_wsrv
+root usbser_rsrv
+root usbser_tx_cb
+root usbser_rx_cb
+root usbser_status_cb
+root usbser_wq_thread
+root usbser_rq_thread
+root usbser_disconnect_cb
+root usbser_reconnect_cb
+root usbser_cpr_suspend
+root usbser_cpr_resume
+
+root usbsacm_bulkin_cb
+root usbsacm_bulkout_cb
+
+### specify usbsacm function pointers
+
+add ds_ops::ds_attach targets usbsacm_ds_attach
+add ds_ops::ds_detach targets usbsacm_ds_detach
+add ds_ops::ds_register_cb targets usbsacm_ds_register_cb
+add ds_ops::ds_unregister_cb targets usbsacm_ds_unregister_cb
+add ds_ops::ds_open_port targets usbsacm_ds_open_port
+add ds_ops::ds_close_port targets usbsacm_ds_close_port
+add ds_ops::ds_usb_power targets usbsacm_ds_usb_power
+add ds_ops::ds_suspend targets usbsacm_ds_suspend
+add ds_ops::ds_resume targets usbsacm_ds_resume
+add ds_ops::ds_disconnect targets usbsacm_ds_disconnect
+add ds_ops::ds_reconnect targets usbsacm_ds_reconnect
+add ds_ops::ds_set_port_params targets usbsacm_ds_set_port_params
+add ds_ops::ds_set_modem_ctl targets usbsacm_ds_set_modem_ctl
+add ds_ops::ds_get_modem_ctl targets usbsacm_ds_get_modem_ctl
+add ds_ops::ds_break_ctl targets usbsacm_ds_break_ctl
+add ds_ops::ds_tx targets usbsacm_ds_tx
+add ds_ops::ds_rx targets usbsacm_ds_rx
+add ds_ops::ds_stop targets usbsacm_ds_stop
+add ds_ops::ds_start targets usbsacm_ds_start
+add ds_ops::ds_fifo_flush targets usbsacm_ds_fifo_flush
+add ds_ops::ds_fifo_drain targets usbsacm_ds_fifo_drain
+add ds_ops::ds_out_pipe targets warlock_dummy
+add ds_ops::ds_in_pipe targets warlock_dummy
+
+add usbsacm_port::acm_cb.cb_tx targets usbser_tx_cb
+add usbsacm_port::acm_cb.cb_rx targets usbser_rx_cb
+
+add usb_ctrl_req::ctrl_cb	targets usbsacm_intr_cb
+add usb_ctrl_req::ctrl_exc_cb	targets usbsacm_intr_ex_cb
+add usb_bulk_req::bulk_cb	targets usbsacm_bulkin_cb
+add usb_bulk_req::bulk_exc_cb	targets usbsacm_bulkin_cb
+add usb_bulk_req::bulk_cb	targets usbsacm_bulkout_cb
+add usb_bulk_req::bulk_exc_cb	targets usbsacm_bulkout_cb
+
+add usb_isoc_req::isoc_cb	targets warlock_dummy
+add usb_isoc_req::isoc_exc_cb	targets warlock_dummy
+add usba_pipe_async_req::callback	targets warlock_dummy
+add usba_pipe_async_req::sync_func	targets warlock_dummy
+add usba_pm_req::cb			targets warlock_dummy
+
+add ohci_trans_wrapper::tw_handle_td targets ohci_handle_ctrl_td
+add ohci_trans_wrapper::tw_handle_td targets ohci_handle_bulk_td
+add ohci_trans_wrapper::tw_handle_td targets ohci_handle_intr_td
+add ohci_trans_wrapper::tw_handle_td targets ohci_handle_isoc_td
+
+add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_bulk_qtd
+add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_intr_qtd
+add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_ctrl_qtd
--- a/usr/src/uts/common/sys/Makefile	Tue Dec 12 23:13:49 2006 -0800
+++ b/usr/src/uts/common/sys/Makefile	Tue Dec 12 23:36:51 2006 -0800
@@ -842,6 +842,9 @@
 USBPRNHDRS=           \
 	usb_printer.h
 
+USBCDCHDRS=		\
+	usbcdc.h
+
 UGENHDRS=           \
 	usb_ugen.h
 
@@ -964,6 +967,7 @@
 	$(USBHIDHDRS:%.h=usb/clients/hid/%.check)	\
 	$(USBMSHDRS:%.h=usb/clients/mass_storage/%.check)	\
 	$(USBPRNHDRS:%.h=usb/clients/printer/%.check)   \
+	$(USBCDCHDRS:%.h=usb/clients/usbcdc/%.check)	\
 	$(UGENHDRS:%.h=usb/clients/ugen/%.check)   \
 	$(USBHDRS:%.h=usb/%.check)                      \
 	$(I1394HDRS:%.h=1394/%.check)			\
@@ -1008,6 +1012,7 @@
 	$(ROOTUSBHIDHDRS)	\
 	$(ROOTUSBMSHDRS)	\
 	$(ROOTUSBPRNHDRS)       \
+	$(ROOTUSBCDCHDRS)       \
 	$(ROOTUGENHDRS)		\
 	$(ROOT1394HDRS)         \
 	$(ROOTHOTPLUGHDRS)	\
@@ -1054,6 +1059,7 @@
 	$(ROOTUSBHIDHDRS)	\
 	$(ROOTUSBMSHDRS)	\
 	$(ROOTUSBPRNHDRS)       \
+	$(ROOTUSBCDCHDRS)       \
 	$(ROOTUGENHDRS)		\
 	$(ROOT1394HDRS)         \
 	$(ROOTHOTPLUGHDRS)	\
--- a/usr/src/uts/common/sys/Makefile.syshdrs	Tue Dec 12 23:13:49 2006 -0800
+++ b/usr/src/uts/common/sys/Makefile.syshdrs	Tue Dec 12 23:36:51 2006 -0800
@@ -105,6 +105,9 @@
 usb/clients/printer/%.check:  usb/clients/printer/%.h
 	$(DOT_H_CHECK)
 
+usb/clients/usbcdc/%.check:	usb/clients/usbcdc/%.h
+	$(DOT_H_CHECK)
+
 1394/%.check:         1394/%.h
 	$(DOT_H_CHECK)
 
@@ -162,6 +165,7 @@
 	$(ROOTDIR)/usb/clients/hid \
 	$(ROOTDIR)/usb/clients/mass_storage	\
 	$(ROOTDIR)/usb/clients/printer  \
+	$(ROOTDIR)/usb/clients/usbcdc  \
 	$(ROOTDIR)/usb/clients/ugen  \
 	$(ROOTDIR)/1394         \
 	$(ROOTDIR)/rsm		\
@@ -225,6 +229,7 @@
 ROOTUSBHIDHDRS = $(USBHIDHDRS:%=$(ROOTDIR)/usb/clients/hid/%)
 ROOTUSBMSHDRS= $(USBMSHDRS:%=$(ROOTDIR)/usb/clients/mass_storage/%)
 ROOTUSBPRNHDRS= $(USBPRNHDRS:%=$(ROOTDIR)/usb/clients/printer/%)
+ROOTUSBCDCHDRS= $(USBCDCHDRS:%=$(ROOTDIR)/usb/clients/usbcdc/%)
 ROOTUGENHDRS= $(UGENHDRS:%=$(ROOTDIR)/usb/clients/ugen/%)
 
 ROOT1394HDRS= $(I1394HDRS:%=$(ROOTDIR)/1394/%)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/usb/clients/usbcdc/usb_cdc.h	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,164 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_USB_USB_CDC_H
+#define	_SYS_USB_USB_CDC_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+#include <sys/dditypes.h>
+#include <sys/usb/clients/usbser/usbser_dsdi.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * USB Communications Device Class
+ */
+
+/*
+ * Class-specific descriptors
+ */
+#define	USB_CDC_CS_INTERFACE			0x24
+#define	USB_CDC_CS_ENDPOINT			0x25
+
+#define	USB_CDC_DESCR_TYPE_HEADER		0x00
+#define	USB_CDC_DESCR_TYPE_CALL_MANAGEMENT	0x01
+#define	USB_CDC_DESCR_TYPE_ACM			0x02
+#define	USB_CDC_DESCR_TYPE_UNION		0x06
+#define	USB_CDC_DESCR_TYPE_COUNTRY		0x07
+#define	USB_CDC_DESCR_TYPE_NETWORK_TERMINAL	0x0a
+#define	USB_CDC_DESCR_TYPE_ETHERNET		0x0f
+
+/* Header Functional Descriptor */
+typedef struct usb_cdc_header_descr {
+	uint8_t		bFunctionalLength;
+	uint8_t		bDescriptorType;
+	uint8_t		bDescriptorSubtype;
+	uint16_t	bcdCDC;
+} usb_cdc_header_descr_t;
+
+/* Call Management Descriptor */
+typedef struct usb_cdc_call_mgmt_descr {
+	uint8_t		bFunctionalLength;
+	uint8_t		bDescriptorType;
+	uint8_t		bDescriptorSubtype;
+	uint8_t		bmCapabilities;
+	uint8_t		bDataInterface;
+} usb_cdc_call_mgmt_descr_t;
+
+#define	USB_CDC_CALL_MGMT_CAP_CALL_MGMT		0x01
+#define	USB_CDC_CALL_MGMT_CAP_DATA_INTERFACE	0x02
+
+/* Abstract Control Management Descriptor */
+typedef struct usb_cdc_acm_descr {
+	uint8_t		bFunctionalLength;
+	uint8_t		bDescriptorType;
+	uint8_t		bDescriptorSubtype;
+	uint8_t		bmCapabilities;
+} usb_cdc_acm_descr_t;
+
+#define	USB_CDC_ACM_CAP_COMM_FEATURE		0x01
+#define	USB_CDC_ACM_CAP_SERIAL_LINE		0x02
+#define	USB_CDC_ACM_CAP_SEND_BREAK		0x04
+#define	USB_CDC_ACM_CAP_NETWORK_CONNECTION	0x08
+
+/* Union Functional Descriptor */
+typedef struct usb_cdc_union_descr {
+	uint8_t		bFunctionalLength;
+	uint8_t		bDescriptorType;
+	uint8_t		bDescriptorSubtype;
+	uint8_t		bMasterInterface0;
+	uint8_t		bSlaveInterface0;
+	/* more slave interafce may follow */
+} usb_cdc_union_descr_t;
+
+/*
+ * Class-specific requests
+ */
+#define	USB_CDC_REQ_SEND_ENCAPSULATED_COMMAND	0x00
+#define	USB_CDC_REQ_GET_ENCAPSULATED_RESPONSE	0x01
+#define	USB_CDC_REQ_SET_LINE_CODING		0x20
+#define	USB_CDC_REQ_GET_LINE_CODING		0x21
+#define	USB_CDC_REQ_SET_CONTROL_LINE_STATE	0x22
+#define	USB_CDC_REQ_SEND_BREAK			0x23
+
+/* Line Coding */
+typedef struct usb_cdc_line_coding {
+	uint32_t	dwDTERate;
+	uint8_t		bCharFormat;
+	uint8_t		bParityType;
+	uint8_t		bDataBits;
+} usb_cdc_line_coding_t;
+
+#define	USB_CDC_LINE_CODING_LEN			7
+
+#define	USB_CDC_STOP_BITS_1			0
+#define	USB_CDC_STOP_BITS_1_5			1
+#define	USB_CDC_STOP_BITS_2			2
+
+#define	USB_CDC_PARITY_NO			0
+#define	USB_CDC_PARITY_ODD			1
+#define	USB_CDC_PARITY_EVEN			2
+#define	USB_CDC_PARITY_MARK			3
+#define	USB_CDC_PARITY_SPACE			4
+
+#define	USB_CDC_ACM_CONTROL_DTR			0x01
+#define	USB_CDC_ACM_CONTROL_RTS			0x02
+
+#define	USB_CDC_NOTIFICATION_REQUEST_TYPE	0xa1
+/*
+ * Class-specific notifications
+ */
+#define	USB_CDC_NOTIFICATION_NETWORK_CONNECTION	0x00
+#define	USB_CDC_NOTIFICATION_RESPONSE_AVAILABLE	0x01
+#define	USB_CDC_NOTIFICATION_SERIAL_STATE	0x20
+#define	USB_CDC_NOTIFICATION_SPEED_CHANGE	0x2a
+
+typedef struct usb_cdc_notification {
+	uint8_t		bmRequestType;
+	uint8_t		bNotificationType;
+	uint16_t	wValue;
+	uint16_t	wIndex;
+	uint16_t	wLength;
+} usb_cdc_notification_t;
+
+/* Serial State */
+#define	USB_CDC_ACM_CONTROL_DCD			0x01
+#define	USB_CDC_ACM_CONTROL_DSR			0x02
+#define	USB_CDC_ACM_CONTROL_BREAK		0x04
+#define	USB_CDC_ACM_CONTROL_RNG			0x08
+#define	USB_CDC_ACM_CONTROL_FRAMING		0x10
+#define	USB_CDC_ACM_CONTROL_PARITY		0x20
+#define	USB_CDC_ACM_CONTROL_OVERRUN		0x40
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_USB_USB_CDC_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/usb/clients/usbser/usbsacm/usbsacm.h	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,176 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_USB_USBSACM_H
+#define	_SYS_USB_USBSACM_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+#include <sys/dditypes.h>
+#include <sys/note.h>
+
+#include <sys/usb/clients/usbser/usbser_dsdi.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+
+typedef struct usbsacm_port usbsacm_port_t;
+typedef struct usbsacm_state usbsacm_state_t;
+
+
+/*
+ * PM support
+ */
+typedef struct usbsacm_power {
+	uint8_t		pm_wakeup_enabled;	/* remote wakeup enabled */
+	uint8_t		pm_pwr_states;	/* bit mask of power states */
+	boolean_t	pm_raise_power;	/* driver is about to raise power */
+	uint8_t		pm_cur_power;	/* current power level */
+	uint_t		pm_busy_cnt;	/* number of set_busy requests */
+} usbsacm_pm_t;
+
+
+/*
+ * per bulk in/out structure
+ */
+struct usbsacm_port {
+	kmutex_t		acm_port_mutex;		/* structure lock */
+	usbsacm_state_t		*acm_device;		/* back pointer */
+	usb_pipe_handle_t	acm_bulkin_ph;		/* in pipe hdl */
+	int			acm_bulkin_state;	/* in pipe state */
+	usb_pipe_handle_t	acm_bulkout_ph;		/* out pipe hdl */
+	int			acm_bulkout_state;	/* out pipe state */
+	usb_pipe_handle_t	acm_intr_ph;		/* intr pipe hdl */
+	int			acm_intr_state;		/* intr pipe state */
+	usb_ep_descr_t		acm_intr_ep_descr;	/* ep descriptor */
+	int			acm_ctrl_if_no;		/* control interface */
+	int			acm_data_if_no;		/* data interface */
+	int			acm_data_port_no;	/* which data port */
+	ds_cb_t			acm_cb;			/* DSD callbacks */
+	mblk_t			*acm_rx_mp;		/* rx data */
+	mblk_t			*acm_tx_mp;		/* tx data */
+	kcondvar_t		acm_tx_cv;		/* tx completion */
+	uint8_t			acm_mctlout;		/* controls out */
+	uint8_t			acm_mctlin;		/* controls in */
+	int			acm_cap;		/* port capabilities */
+	usb_cdc_line_coding_t	acm_line_coding;	/* port line coding */
+	int			acm_port_state;		/* port state */
+	size_t			acm_bulkin_size;	/* bulkin xfer size */
+};
+
+_NOTE(MUTEX_PROTECTS_DATA(usbsacm_port::acm_port_mutex, usbsacm_port))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(usbsacm_port::{
+	acm_device
+	acm_cb.cb_rx
+	acm_cb.cb_tx
+	acm_cb.cb_arg
+	acm_bulkin_ph
+	acm_bulkout_ph
+	acm_intr_ph
+	acm_ctrl_if_no
+	acm_data_if_no
+	acm_data_port_no
+	acm_port_state
+}))
+
+struct usbsacm_state {
+	kmutex_t		acm_mutex;		/* structure lock */
+	dev_info_t		*acm_dip;		/* device info */
+	usb_client_dev_data_t	*acm_dev_data;		/* registration data */
+	usb_event_t		*acm_usb_events;	/* usb events */
+	usb_pipe_handle_t	acm_def_ph;		/* default pipe hdl */
+	usb_log_handle_t	acm_lh;			/* USBA log handle */
+	int			acm_dev_state;		/* USB device state */
+	size_t			acm_xfer_sz;		/* bulk xfer size */
+	boolean_t		acm_compatibility;	/* if conform to spec */
+	usbsacm_port_t		*acm_ports;		/* per port structs */
+	int			acm_port_cnt;		/* port number */
+	usbsacm_pm_t		*acm_pm;		/* PM support */
+};
+
+_NOTE(MUTEX_PROTECTS_DATA(usbsacm_state::acm_mutex, usbsacm_state))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(usbsacm_state::{
+	acm_dip
+	acm_dev_data
+	acm_usb_events
+	acm_def_ph
+	acm_lh
+	acm_dev_state
+	acm_xfer_sz
+	acm_compatibility
+	acm_ports
+	acm_port_cnt
+	acm_pm
+}))
+
+/* port state */
+enum {
+	USBSACM_PORT_CLOSED,			/* port is closed */
+	USBSACM_PORT_OPEN,			/* port is open */
+	USBSACM_PORT_CLOSING
+};
+
+/* pipe state */
+enum {
+	USBSACM_PIPE_CLOSED,			/* pipe is closed */
+	USBSACM_PIPE_IDLE,			/* open but no requests */
+	USBSACM_PIPE_BUSY,			/* servicing request */
+	USBSACM_PIPE_CLOSING			/* pipe is closing */
+};
+
+/* various tunables */
+enum {
+	USBSACM_BULKOUT_TIMEOUT		= 15,	/* bulkout timeout */
+	USBSACM_BULKIN_TIMEOUT		= 0	/* bulkin timeout */
+};
+
+/* hardware definitions */
+enum {
+	USBSACM_REQ_OUT	= USB_DEV_REQ_TYPE_CLASS| USB_DEV_REQ_HOST_TO_DEV,
+	USBSACM_REQ_IN	= USB_DEV_REQ_TYPE_CLASS | USB_DEV_REQ_DEV_TO_HOST,
+	USBSACM_REQ_WRITE_IF		= USBSACM_REQ_OUT | USB_DEV_REQ_RCPT_IF,
+	USBSACM_REQ_READ_IF		= USBSACM_REQ_IN | USB_DEV_REQ_RCPT_IF
+};
+
+#define	PRINT_MASK_ATTA		0x00000001
+#define	PRINT_MASK_CLOSE	0x00000002
+#define	PRINT_MASK_OPEN		0x00000004
+#define	PRINT_MASK_EVENTS	0x00000008
+#define	PRINT_MASK_PM		0x00000010
+#define	PRINT_MASK_CB		0x00000020
+#define	PRINT_MASK_ALL		0xFFFFFFFF
+
+
+#define	NELEM(a)	(sizeof (a) / sizeof (*(a)))
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_USB_USBSACM_H */
--- a/usr/src/uts/intel/Makefile.intel.shared	Tue Dec 12 23:13:49 2006 -0800
+++ b/usr/src/uts/intel/Makefile.intel.shared	Tue Dec 12 23:36:51 2006 -0800
@@ -375,6 +375,7 @@
 DRV_KMODS	+= usbprn
 DRV_KMODS	+= ugen
 DRV_KMODS	+= usbser
+DRV_KMODS	+= usbsacm
 DRV_KMODS	+= usbsksp
 DRV_KMODS	+= usbsprl
 DRV_KMODS	+= usb_ac
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/usbsacm/Makefile	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,152 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# uts/intel/usbsacm/Makefile
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives the production of Abstract Control Model of
+#	USB Communication Devices Class dirver.
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= usbsacm
+OBJECTS		= $(USBSACM_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(USBSACM_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+WARLOCK_OUT	= $(USBSACM_OBJS:%.o=%.ll)
+WARLOCK_OK	= $(MODULE).ok
+WLCMD_DIR	= $(UTSBASE)/common/io/warlock
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+LDFLAGS         += -dy -Nmisc/usba -Nmisc/usbser
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS	+= -erroff=E_PTRDIFF_OVERFLOW
+LINTTAGS	+= -erroff=E_BAD_PTR_CAST_ALIGN
+
+.KEEP_STATE:
+
+all:		$(ALL_DEPS)
+
+def:		$(DEF_DEPS)
+
+clean:		$(CLEAN_DEPS)
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+clobber:	$(CLOBBER_DEPS)
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+#
+#	Defines for local commands.
+#
+WLCC		= wlcc
+TOUCH		= touch
+WARLOCK		= warlock
+SCCS		= sccs
+TEST		= test
+
+#
+#	warlock
+#
+WARLOCK_CMD	= $(MODULE).wlcmd
+
+USBSER_FILES = $(USBSER_OBJS:%.o=../usbser/%.ll)
+USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
+OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
+EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
+
+warlock: $(WARLOCK_OK)
+
+%.wlcmd:
+	cd $(WLCMD_DIR); $(TEST) -f $@ || $(SCCS) get $@
+
+$(WARLOCK_OK): warlock_with_usbser warlock_with_usba
+	$(TOUCH) $@
+
+%.ll: $(UTSBASE)/common/io/usb/clients/usbser/usbsacm/%.c
+	$(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $<
+
+warlock_with_usbser: $(WARLOCK_OUT) usbser_files warlock_ddi.files \
+	$(WARLOCK_CMD)
+	$(WARLOCK) -c $(WLCMD_DIR)/$(WARLOCK_CMD) $(WARLOCK_OUT) \
+		$(USBSER_FILES) -l ../warlock/ddi_dki_impl.ll
+
+warlock_with_usba: usbsacm_with_usba.wlcmd $(WARLOCK_OUT) usbser_files \
+	usba_files ohci_files uhci_files ehci_files warlock_ddi.files
+	$(WARLOCK) -c $(WLCMD_DIR)/usbsacm_with_usba.wlcmd \
+		$(USBA_FILES) $(OHCI_FILES) $(EHCI_FILES) $(UHCI_FILES) \
+		$(USBSER_FILES) \
+		$(WARLOCK_OUT) -l ../warlock/ddi_dki_impl.ll
+
+usbser_files:
+	@cd ../usbser; pwd; $(MAKE) warlock
+
+usba_files:
+	@cd ../usba;pwd; $(MAKE) warlock
+
+uhci_files:
+	@cd ../uhci;pwd; $(MAKE) warlock
+
+ohci_files:
+	@cd ../ohci;pwd; $(MAKE) warlock
+
+ehci_files:
+	@cd ../ehci;pwd; $(MAKE) warlock
+
+warlock_ddi.files:
+	cd ../warlock; pwd; $(MAKE) warlock
--- a/usr/src/uts/intel/warlock/Makefile	Tue Dec 12 23:13:49 2006 -0800
+++ b/usr/src/uts/intel/warlock/Makefile	Tue Dec 12 23:36:51 2006 -0800
@@ -82,6 +82,7 @@
 	@cd $(CLOSED)/uts/intel/usbser_edge; make clean; $(MAKE) warlock
 	@cd ../usbsksp; make clean; $(MAKE) warlock
 	@cd ../usbsprl; make clean; $(MAKE) warlock
+	@cd ../usbsacm; make clean; $(MAKE) warlock
 	@cd ../usbskel; make clean; $(MAKE) warlock
 
 warlock.scsi:
--- a/usr/src/uts/sparc/Makefile.sparc.shared	Tue Dec 12 23:13:49 2006 -0800
+++ b/usr/src/uts/sparc/Makefile.sparc.shared	Tue Dec 12 23:36:51 2006 -0800
@@ -267,7 +267,7 @@
 DRV_KMODS	+= ssd
 DRV_KMODS	+= ecpp
 DRV_KMODS	+= hid hubd ehci ohci uhci usb_mid scsa2usb usbprn ugen
-DRV_KMODS	+= usbser usbsksp usbsprl
+DRV_KMODS	+= usbser usbsacm usbsksp usbsprl
 DRV_KMODS	+= usb_as usb_ac
 DRV_KMODS	+= usbskel
 DRV_KMODS	+= hci1394 av1394 scsa1394 dcam1394
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/usbsacm/Makefile	Tue Dec 12 23:36:51 2006 -0800
@@ -0,0 +1,158 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# uts/sparc/usbsacm/Makefile
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives the production of Abstract Control Model of
+#	USB Communication Devices Class dirver.
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= usbsacm
+OBJECTS		= $(USBSACM_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(USBSACM_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+WARLOCK_OUT	= $(USBSACM_OBJS:%.o=%.ll)
+WARLOCK_OK	= $(MODULE).ok
+WLCMD_DIR	= $(UTSBASE)/common/io/warlock
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+# 
+# lint pass one enforcement 
+# 
+CFLAGS += $(CCVERBOSE)
+
+LDFLAGS         += -dy -Nmisc/usba -Nmisc/usbser
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS	+= -erroff=E_PTRDIFF_OVERFLOW
+LINTTAGS	+= -erroff=E_BAD_PTR_CAST_ALIGN
+
+.KEEP_STATE:
+
+all:		$(ALL_DEPS)
+
+def:		$(DEF_DEPS)
+
+clean:		$(CLEAN_DEPS)
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+clobber:	$(CLOBBER_DEPS)
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
+
+#
+#	Defines for local commands.
+#
+WLCC		= wlcc
+TOUCH		= touch
+WARLOCK		= warlock
+SCCS		= sccs
+TEST		= test
+
+#
+#	warlock
+#
+WARLOCK_CMD	= $(MODULE).wlcmd
+
+USBSER_FILES = $(USBSER_OBJS:%.o=../usbser/%.ll)
+USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
+OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
+EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
+
+warlock: $(WARLOCK_OK)
+
+%.wlcmd:
+	cd $(WLCMD_DIR); $(TEST) -f $@ || $(SCCS) get $@
+
+$(WARLOCK_OK): warlock_with_usbser warlock_with_usba
+	$(TOUCH) $@
+
+%.ll: $(UTSBASE)/common/io/usb/clients/usbser/usbsacm/%.c
+	$(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $<
+
+warlock_with_usbser: $(WARLOCK_OUT) usbser_files warlock_ddi.files \
+	$(WARLOCK_CMD)
+	$(WARLOCK) -c $(WLCMD_DIR)/$(WARLOCK_CMD) $(WARLOCK_OUT) \
+		$(USBSER_FILES) -l ../warlock/ddi_dki_impl.ll
+
+warlock_with_usba: usbsacm_with_usba.wlcmd $(WARLOCK_OUT) usbser_files \
+	usba_files ohci_files uhci_files ehci_files warlock_ddi.files
+	$(WARLOCK) -c $(WLCMD_DIR)/usbsacm_with_usba.wlcmd \
+		$(USBA_FILES) $(OHCI_FILES) $(EHCI_FILES) $(UHCI_FILES) \
+		$(USBSER_FILES) \
+		$(WARLOCK_OUT) -l ../warlock/ddi_dki_impl.ll
+
+usbser_files:
+	@cd ../usbser; pwd; $(MAKE) warlock
+
+usba_files:
+	@cd ../usba;pwd; $(MAKE) warlock
+
+uhci_files:
+	@cd ../uhci;pwd; $(MAKE) warlock
+
+ohci_files:
+	@cd ../ohci;pwd; $(MAKE) warlock
+
+ehci_files:
+	@cd ../ehci;pwd; $(MAKE) warlock
+
+warlock_ddi.files:
+	cd ../warlock; pwd; $(MAKE) warlock
--- a/usr/src/uts/sparc/warlock/Makefile	Tue Dec 12 23:13:49 2006 -0800
+++ b/usr/src/uts/sparc/warlock/Makefile	Tue Dec 12 23:36:51 2006 -0800
@@ -82,6 +82,7 @@
 	@cd $(CLOSED)/uts/sparc/usbser_edge; make clean; $(MAKE) warlock
 	@cd ../usbsksp; make clean; $(MAKE) warlock
 	@cd ../usbsprl; make clean; $(MAKE) warlock
+	@cd ../usbsacm; make clean; $(MAKE) warlock
 	@cd ../usbskel; make clean; $(MAKE) warlock
 
 warlock.scsi: