changeset 901:51e564a5263e

6314455 Support for Keyspan USB serial adapters
author yz147069
date Mon, 14 Nov 2005 01:27:09 -0800
parents 7a6a7342ba44
children eda97dc69bb7
files usr/src/pkgdefs/Makefile usr/src/pkgdefs/SUNWuksp/Makefile usr/src/pkgdefs/SUNWuksp/depend usr/src/pkgdefs/SUNWuksp/pkginfo.tmpl usr/src/pkgdefs/SUNWuksp/postinstall usr/src/pkgdefs/SUNWuksp/preremove usr/src/pkgdefs/SUNWuksp/prototype_com usr/src/pkgdefs/SUNWuksp/prototype_i386 usr/src/pkgdefs/SUNWuksp/prototype_sparc usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/usb/clients/usbser/usbser_keyspan/keyspan_dsd.c usr/src/uts/common/io/usb/clients/usbser/usbser_keyspan/keyspan_pipe.c usr/src/uts/common/io/usb/clients/usbser/usbser_keyspan/usbser_keyspan.c usr/src/uts/common/sys/usb/clients/usbser/usbser_keyspan/keyspan_pipe.h usr/src/uts/common/sys/usb/clients/usbser/usbser_keyspan/keyspan_var.h usr/src/uts/common/sys/usb/clients/usbser/usbser_keyspan/usa90msg.h usr/src/uts/intel/Makefile.intel usr/src/uts/intel/usbsksp/Makefile usr/src/uts/sparc/Makefile.sparc usr/src/uts/sparc/usbsksp/Makefile usr/src/uts/sparc/usbsksp/usbser_keyspan.wlcmd usr/src/uts/sparc/usbsksp/usbser_keyspan_with_usba.wlcmd usr/src/uts/sparc/warlock/Makefile
diffstat 24 files changed, 6416 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/pkgdefs/Makefile	Sun Nov 13 16:47:18 2005 -0800
+++ b/usr/src/pkgdefs/Makefile	Mon Nov 14 01:27:09 2005 -0800
@@ -307,6 +307,7 @@
 	SUNWudaplr \
 	SUNWudaplu \
 	SUNWuedg \
+	SUNWuksp \
 	SUNWugen \
 	SUNWugenu \
 	SUNWusb \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWuksp/Makefile	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,35 @@
+#
+# 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.
+#
+# 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 2005 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/SUNWuksp/depend	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,54 @@
+#
+# Copyright 2005 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, Version 1.0 only
+# (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/SUNWuksp/pkginfo.tmpl	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,49 @@
+#
+# Copyright 2005 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, Version 1.0 only
+# (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="SUNWuksp"
+NAME="USB Keyspan serial driver"
+ARCH="ISA"
+CATEGORY="system"
+BASEDIR=/
+SUNW_PKGTYPE="root"
+CLASSES="none"
+DESC="USB Keyspan 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"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWuksp/postinstall	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,51 @@
+#!/bin/sh
+#
+# 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.
+#
+# 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 2005 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
+
+not_installed() {
+	driver=$1
+	grep "^${driver} " $BASEDIR/etc/name_to_major > /dev/null 2>&1
+
+	return $?
+}
+
+EXIT=0
+
+#"usb6cd,121"	Keyspan USA19HS serial adapter.
+USBSKSP_ALIASES="\
+	\"usb6cd,121\" \
+	"
+
+not_installed usbsksp || add_drv -b ${BASEDIR} -m '* 0666 root sys' \
+	-i "${USBSKSP_ALIASES}" -n usbsksp || EXIT=1
+
+exit $EXIT
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWuksp/preremove	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,40 @@
+#! /bin/sh
+#
+# 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.
+#
+# 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 2005 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 usbsksp ${BASEDIR}/etc/name_to_major > /dev/null 2>&1
+then
+	rem_drv -b ${BASEDIR} usbsksp || EXIT=1
+fi
+
+exit $EXIT
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWuksp/prototype_com	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+# 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 2005 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
+
+#
+# SUNWuksp
+#
+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/SUNWuksp/prototype_i386	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,53 @@
+#
+# 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.
+#
+# 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 2005 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
+#
+#
+# SUNWuksp
+#
+f none kernel/drv/usbsksp 0755 root sys
+d none kernel/drv/amd64 0755 root sys
+f none kernel/drv/amd64/usbsksp 0755 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWuksp/prototype_sparc	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,53 @@
+#
+# 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.
+#
+# 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 2005 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
+#
+#
+# SUNWuksp
+d none kernel/drv/sparcv9 0755 root sys
+f none kernel/drv/sparcv9/usbsksp 0755 root sys
--- a/usr/src/uts/common/Makefile.files	Sun Nov 13 16:47:18 2005 -0800
+++ b/usr/src/uts/common/Makefile.files	Mon Nov 14 01:27:09 2005 -0800
@@ -586,6 +586,8 @@
 		edge_pipe.o edge_subr.o	\
 		edge_fw_down.o edge_fw_down_g2.o edge_fw_down_ti.o
 
+USBSER_KEYSPAN_OBJS += usbser_keyspan.o keyspan_dsd.o keyspan_pipe.o
+
 WC_OBJS += wscons.o
 
 SCSI_OBJS +=	scsi_capabilities.o	scsi_control.o	scsi_watch.o \
--- a/usr/src/uts/common/Makefile.rules	Sun Nov 13 16:47:18 2005 -0800
+++ b/usr/src/uts/common/Makefile.rules	Mon Nov 14 01:27:09 2005 -0800
@@ -684,6 +684,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/usb/clients/usbser/usbser_keyspan/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/usb/hcd/openhci/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -1384,6 +1388,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/usb/clients/usbser/usbser_edge/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/usb/clients/usbser/usbser_keyspan/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/usb/hcd/openhci/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/clients/usbser/usbser_keyspan/keyspan_dsd.c	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,3028 @@
+/*
+ * 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.
+ *
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ *
+ * DSD code for keyspan usb2serial adapters
+ *
+ */
+#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>
+
+#define	USBDRV_MAJOR_VER	2
+#define	USBDRV_MINOR_VER	0
+
+#include <sys/usb/usba.h>
+
+#include <sys/usb/clients/usbser/usbser_dsdi.h>
+#include <sys/usb/clients/usbser/usbser_keyspan/keyspan_var.h>
+#include <sys/usb/clients/usbser/usbser_keyspan/keyspan_pipe.h>
+
+#include <sys/usb/clients/usbser/usbser_keyspan/usa90msg.h>
+
+#ifdef	KEYSPAN_USA49WLC
+#include <sys/usb/clients/usbser/usbser_keyspan/usa49msg.h>
+#endif	/* If KEYSPAN_USA49WLC defined */
+
+/*
+ * DSD operations which are filled in ds_ops structure.
+ */
+static int	keyspan_attach(ds_attach_info_t *);
+static void	keyspan_detach(ds_hdl_t);
+static int	keyspan_register_cb(ds_hdl_t, uint_t, ds_cb_t *);
+static void	keyspan_unregister_cb(ds_hdl_t, uint_t);
+static int	keyspan_open_port(ds_hdl_t, uint_t);
+static int	keyspan_close_port(ds_hdl_t, uint_t);
+
+/* power management */
+static int	keyspan_usb_power(ds_hdl_t, int, int, int *);
+static int	keyspan_suspend(ds_hdl_t);
+static int	keyspan_resume(ds_hdl_t);
+
+/* hotplug */
+static int	keyspan_disconnect(ds_hdl_t);
+static int	keyspan_reconnect(ds_hdl_t);
+
+/* standard UART operations */
+static int	keyspan_set_port_params(ds_hdl_t, uint_t, ds_port_params_t *);
+static int	keyspan_set_modem_ctl(ds_hdl_t, uint_t, int, int);
+static int	keyspan_get_modem_ctl(ds_hdl_t, uint_t, int, int *);
+static int	keyspan_break_ctl(ds_hdl_t, uint_t, int);
+static int	keyspan_loopback(ds_hdl_t, uint_t, int);
+
+/* data xfer */
+static int	keyspan_tx(ds_hdl_t, uint_t, mblk_t *);
+static mblk_t	*keyspan_rx(ds_hdl_t, uint_t);
+static void	keyspan_stop(ds_hdl_t, uint_t, int);
+static void	keyspan_start(ds_hdl_t, uint_t, int);
+static int	keyspan_fifo_flush(ds_hdl_t, uint_t, int);
+static int	keyspan_fifo_drain(ds_hdl_t, uint_t, int);
+
+/*
+ * Sub-routines
+ */
+
+/* configuration routines */
+static void	keyspan_free_soft_state(keyspan_state_t *);
+static void	keyspan_init_sync_objs(keyspan_state_t *);
+static void	keyspan_fini_sync_objs(keyspan_state_t *);
+static int	keyspan_usb_register(keyspan_state_t *);
+static void	keyspan_usb_unregister(keyspan_state_t *);
+static int	keyspan_attach_dev(keyspan_state_t *);
+static void	keyspan_attach_ports(keyspan_state_t *);
+static void	keyspan_detach_ports(keyspan_state_t *);
+static void	keyspan_init_port_params(keyspan_state_t *);
+static void	keyspan_free_descr_tree(keyspan_state_t *);
+static int	keyspan_register_events(keyspan_state_t *);
+static void	keyspan_unregister_events(keyspan_state_t *);
+static void	keyspan_set_dev_state_online(keyspan_state_t *);
+
+/* hotplug */
+static int	keyspan_restore_device_state(keyspan_state_t *);
+static int	keyspan_restore_ports_state(keyspan_state_t *);
+
+/* power management */
+static int	keyspan_create_pm_components(keyspan_state_t *);
+static void	keyspan_destroy_pm_components(keyspan_state_t *);
+static int	keyspan_pm_set_busy(keyspan_state_t *);
+static void	keyspan_pm_set_idle(keyspan_state_t *);
+static int	keyspan_pwrlvl0(keyspan_state_t *);
+static int	keyspan_pwrlvl1(keyspan_state_t *);
+static int	keyspan_pwrlvl2(keyspan_state_t *);
+static int	keyspan_pwrlvl3(keyspan_state_t *);
+
+/* pipe operations */
+static int	keyspan_attach_pipes(keyspan_state_t *);
+static void	keyspan_detach_pipes(keyspan_state_t *);
+static void	keyspan_disconnect_pipes(keyspan_state_t *);
+static int	keyspan_reconnect_pipes(keyspan_state_t *);
+
+/* data transfer routines */
+static int	keyspan_wait_tx_drain(keyspan_port_t *, int);
+
+/* misc */
+static void	keyspan_default_port_params(keyspan_port_t *);
+static void	keyspan_build_cmd_msg(keyspan_port_t *, ds_port_params_t *);
+static void	keyspan_save_port_params(keyspan_port_t	*);
+
+/*
+ * Model specific functions.
+ */
+
+/* usa19hs specific functions */
+static void	keyspan_build_cmd_msg_usa19hs(keyspan_port_t *,
+    ds_port_params_t *);
+static void	keyspan_default_port_params_usa19hs(keyspan_port_t *);
+static void	keyspan_save_port_params_usa19hs(keyspan_port_t	*);
+
+#ifdef	KEYSPAN_USA49WLC
+/* usa49 specific functions */
+static void	keyspan_build_cmd_msg_usa49(keyspan_port_t *,
+    ds_port_params_t *);
+static void	keyspan_default_port_params_usa49(keyspan_port_t *);
+static void	keyspan_save_port_params_usa49(keyspan_port_t	*);
+#endif	/* If KEYSPAN_USA49WLC defined */
+
+/*
+ * DSD ops structure
+ */
+ds_ops_t ds_ops = {
+	DS_OPS_VERSION,
+	keyspan_attach,
+	keyspan_detach,
+	keyspan_register_cb,
+	keyspan_unregister_cb,
+	keyspan_open_port,
+	keyspan_close_port,
+	keyspan_usb_power,
+	keyspan_suspend,
+	keyspan_resume,
+	keyspan_disconnect,
+	keyspan_reconnect,
+	keyspan_set_port_params,
+	keyspan_set_modem_ctl,
+	keyspan_get_modem_ctl,
+	keyspan_break_ctl,
+	keyspan_loopback,
+	keyspan_tx,
+	keyspan_rx,
+	keyspan_stop,
+	keyspan_start,
+	keyspan_fifo_flush,
+	keyspan_fifo_drain
+};
+
+/*
+ *  For USA19HS baud speed, precalculated using the following algorithm:
+ *
+ *	speed = (uint16_t)(14769231L / baud);
+ */
+static uint16_t	keyspan_speedtab_usa19hs[] = {
+	0x0,	/* B0 */
+	0x481d,	/* B50 */
+	0x3013,	/* B75 */
+	0x20c7,	/* B110 */
+	0x1ae8,	/* B134 */
+	0x1809,	/* B150 */
+	0x1207,	/* B200 */
+	0xc04,	/* B300 */
+	0x602,	/* B600 */
+	0x301,	/* B1200 */
+	0x200,	/* B1800 */
+	0x180,	/* B2400 */
+	0xc0,	/* B4800 */
+	0x60,	/* B9600 */
+	0x30,	/* B19200 */
+	0x18,	/* B38400 */
+	0x10,	/* B57600 */
+	0xc,	/* B76800 */
+	0x8,	/* B115200 */
+	0x6,	/* B153600 */
+	0x4,	/* B230400 */
+};
+#ifdef	KEYSPAN_USA49WLC
+/*
+ *  For USA49WLC baud speed, precalculated.
+ */
+static uint16_t	keyspan_speedtab_usa49[] = {
+	0x0,	/* B0 */
+	0x7530,	/* B50 */
+	0x4e20,	/* B75 */
+	0x3544,	/* B110 */
+	0x2bba,	/* B134 */
+	0x2710,	/* B150 */
+	0x1d4c,	/* B200 */
+	0x1388,	/* B300 */
+	0x9c4,	/* B600 */
+	0x4e2,	/* B1200 */
+	0x25e,	/* B1800 */
+	0x271,	/* B2400 */
+	0xfa,	/* B4800 */
+	0x7d,	/* B9600 */
+	0x19,	/* B19200 */
+	0x27,	/* B38400 */
+	0x1a,	/* B57600 */
+	0xd,	/* B76800 */
+	0xd,	/* B115200 */
+	0x6,	/* B153600 */
+	0x4,	/* B230400 */
+};
+
+/*
+ *  For USA49WLC prescaler, precalculated.
+ */
+static uint8_t	keyspan_prescaler_49wlc[] = {
+	0x0,	/* B0 */
+	0x8,	/* B50 */
+	0x8,	/* B75 */
+	0x8,	/* B110 */
+	0x8,	/* B134 */
+	0x8,	/* B150 */
+	0x8,	/* B200 */
+	0x8,	/* B300 */
+	0x8,	/* B600 */
+	0x8,	/* B1200 */
+	0xb,	/* B1800 */
+	0x8,	/* B2400 */
+	0xa,	/* B4800 */
+	0xa,	/* B9600 */
+	0x19,	/* B19200 */
+	0x8,	/* B38400 */
+	0x8,	/* B57600 */
+	0xc,	/* B76800 */
+	0x8,	/* B115200 */
+	0xd,	/* B153600 */
+	0xd,	/* B230400 */
+};
+#endif	/* If KEYSPAN_USA49WLC defined */
+
+/* convert baud code into baud rate */
+static int keyspan_speed2baud[] = {
+	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 */
+};
+
+
+/* debug support */
+static uint_t	keyspan_errlevel = USB_LOG_L4;
+static uint_t	keyspan_errmask = DPRINT_MASK_ALL;
+static uint_t	keyspan_instance_debug = (uint_t)-1;
+
+static int
+keyspan_attach(ds_attach_info_t *aip)
+{
+	keyspan_state_t	*ksp;
+
+	ksp = (keyspan_state_t *)kmem_zalloc(sizeof (keyspan_state_t),
+	    KM_SLEEP);
+	ksp->ks_dip = aip->ai_dip;
+	ksp->ks_usb_events = aip->ai_usb_events;
+	*aip->ai_hdl = (ds_hdl_t)ksp;
+
+	if (keyspan_usb_register(ksp) != USB_SUCCESS) {
+
+		goto fail_register;
+	}
+
+	/* init mutex and semaphore */
+	keyspan_init_sync_objs(ksp);
+
+	/* get device specific parameters */
+	if (keyspan_attach_dev(ksp) != USB_SUCCESS) {
+		USB_DPRINTF_L1(DPRINT_ATTACH, ksp->ks_lh, "fail attach dev ");
+
+		goto fail_attach_dev;
+	}
+
+	keyspan_attach_ports(ksp);
+
+	if (keyspan_init_pipes(ksp) != USB_SUCCESS) {
+		USB_DPRINTF_L1(DPRINT_ATTACH, ksp->ks_lh,
+		    "keyspan_init_pipes: failed.");
+
+		goto fail_init_pipes;
+	}
+
+	keyspan_init_port_params(ksp);
+	keyspan_free_descr_tree(ksp);
+	keyspan_set_dev_state_online(ksp);
+
+	if (keyspan_create_pm_components(ksp) != USB_SUCCESS) {
+		USB_DPRINTF_L1(DPRINT_ATTACH, ksp->ks_lh,
+		    "keyspan_create_pm_components: failed.");
+
+		goto fail_pm;
+	}
+
+	if (keyspan_register_events(ksp) != USB_SUCCESS) {
+
+		goto fail_events;
+	}
+
+	/* open the global pipes */
+	if (keyspan_attach_pipes(ksp) != USB_SUCCESS) {
+		USB_DPRINTF_L1(DPRINT_ATTACH, ksp->ks_lh,
+		    "keyspan_attach_pipes: failed.");
+
+		goto fail_attach_pipes;
+	}
+
+	*aip->ai_port_cnt = ksp->ks_dev_spec.port_cnt;
+
+	return (USB_SUCCESS);
+
+fail_attach_pipes:
+	keyspan_unregister_events(ksp);
+fail_events:
+	keyspan_destroy_pm_components(ksp);
+fail_pm:
+	keyspan_fini_pipes(ksp);
+fail_init_pipes:
+	keyspan_detach_ports(ksp);
+fail_attach_dev:
+	keyspan_fini_sync_objs(ksp);
+	keyspan_usb_unregister(ksp);
+fail_register:
+	keyspan_free_soft_state(ksp);
+
+	return (USB_FAILURE);
+}
+
+
+/*
+ * ds_detach
+ */
+static void
+keyspan_detach(ds_hdl_t hdl)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+
+	keyspan_detach_pipes(ksp);
+	keyspan_unregister_events(ksp);
+	keyspan_destroy_pm_components(ksp);
+	keyspan_fini_pipes(ksp);
+	keyspan_detach_ports(ksp);
+	keyspan_fini_sync_objs(ksp);
+	keyspan_usb_unregister(ksp);
+	keyspan_free_soft_state(ksp);
+}
+
+/*
+ * ds_register_cb
+ */
+static int
+keyspan_register_cb(ds_hdl_t hdl, uint_t port_num, ds_cb_t *cb)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp;
+
+	if (port_num >= ksp->ks_dev_spec.port_cnt) {
+
+		return (USB_FAILURE);
+	}
+	kp = &ksp->ks_ports[port_num];
+	kp->kp_cb = *cb;
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * ds_unregister_cb
+ */
+static void
+keyspan_unregister_cb(ds_hdl_t hdl, uint_t port_num)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp;
+
+	if (port_num < ksp->ks_dev_spec.port_cnt) {
+		kp = &ksp->ks_ports[port_num];
+		bzero(&kp->kp_cb, sizeof (kp->kp_cb));
+	}
+}
+
+/*
+ * initialize hardware serial port
+ *
+ * 'open_pipes' specifies whether to open USB pipes or not
+ */
+int
+keyspan_open_hw_port(keyspan_port_t *kp, boolean_t open_pipes)
+{
+	int		rval;
+
+	USB_DPRINTF_L4(DPRINT_OPEN, kp->kp_lh,
+	    "keyspan_open_hw_port: [%d]", kp->kp_port_num);
+
+	if (open_pipes) {
+
+		/* open r/w pipes for this port */
+		if ((rval = keyspan_open_port_pipes(kp)) != USB_SUCCESS) {
+
+			return (rval);
+		}
+	}
+
+	mutex_enter(&kp->kp_mutex);
+	kp->kp_state = KEYSPAN_PORT_OPEN;
+	mutex_exit(&kp->kp_mutex);
+
+	if ((rval = keyspan_receive_data(&kp->kp_datain_pipe,
+	    kp->kp_read_len, kp)) != USB_SUCCESS) {
+
+		goto fail;
+	}
+
+	/* set the default port parameters and send cmd msg to enable port */
+	mutex_enter(&kp->kp_mutex);
+	keyspan_default_port_params(kp);
+	mutex_exit(&kp->kp_mutex);
+
+	(void) keyspan_send_cmd(kp);
+
+	USB_DPRINTF_L4(DPRINT_OPEN, kp->kp_lh,
+	    "keyspan_open_hw_port: [%d] finished", kp->kp_port_num);
+
+	return (rval);
+
+fail:
+
+	mutex_enter(&kp->kp_mutex);
+	kp->kp_state = KEYSPAN_PORT_CLOSED;
+	mutex_exit(&kp->kp_mutex);
+
+	if (open_pipes) {
+
+		/* close all ports' data pipes */
+		keyspan_close_port_pipes(kp);
+	}
+
+	USB_DPRINTF_L1(DPRINT_OPEN, kp->kp_lh,
+	    "keyspan_open_hw_port: failed. This port can't be used.");
+
+	return (rval);
+}
+
+/*
+ * ds_open_port
+ */
+static int
+keyspan_open_port(ds_hdl_t hdl, uint_t port_num)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp = &ksp->ks_ports[port_num];
+	int		rval;
+
+	if (port_num >= ksp->ks_dev_spec.port_cnt) {
+
+		return (USB_FAILURE);
+	}
+	USB_DPRINTF_L4(DPRINT_OPEN, kp->kp_lh, "keyspan_open_port");
+
+	mutex_enter(&ksp->ks_mutex);
+	if (ksp->ks_dev_state == USB_DEV_DISCONNECTED) {
+		mutex_exit(&ksp->ks_mutex);
+
+		return (USB_FAILURE);
+	}
+	mutex_exit(&ksp->ks_mutex);
+
+	if (keyspan_pm_set_busy(ksp) != USB_SUCCESS) {
+
+		return (USB_FAILURE);
+	}
+
+	/*
+	 * initialize state
+	 */
+	mutex_enter(&kp->kp_mutex);
+	ASSERT(kp->kp_state == KEYSPAN_PORT_CLOSED);
+	ASSERT((kp->kp_rx_mp == NULL) && (kp->kp_tx_mp == NULL));
+
+	kp->kp_state = KEYSPAN_PORT_OPENING;
+	kp->kp_flags = 0;
+	mutex_exit(&kp->kp_mutex);
+
+	/*
+	 * initialize hardware serial port, B_TRUE means open pipes
+	 */
+	sema_p(&ksp->ks_pipes_sema);
+	rval = keyspan_open_hw_port(kp, B_TRUE);
+	if (rval != USB_SUCCESS) {
+		keyspan_pm_set_idle(ksp);
+	}
+	sema_v(&ksp->ks_pipes_sema);
+
+	return (rval);
+}
+
+
+/*
+ * close hardware serial port
+ */
+void
+keyspan_close_hw_port(keyspan_port_t *kp)
+{
+	keyspan_state_t	*ksp = kp->kp_ksp;
+
+	ASSERT(!mutex_owned(&kp->kp_mutex));
+
+	USB_DPRINTF_L4(DPRINT_CLOSE, kp->kp_lh,
+	    "keyspan_close_hw_port");
+
+	/*
+	 * The bulk IN/OUT pipes might have got closed due to
+	 * a device disconnect event. So its required to check the
+	 * pipe handle and proceed if it is not NULL
+	 */
+
+	mutex_enter(&kp->kp_mutex);
+	if ((kp->kp_datain_pipe.pipe_handle == NULL) &&
+	    (kp->kp_dataout_pipe.pipe_handle == NULL)) {
+		mutex_exit(&kp->kp_mutex);
+
+		return;
+	}
+
+	switch (ksp->ks_dev_spec.id_product) {
+	case KEYSPAN_USA19HS_PID:
+		keyspan_build_cmd_msg_usa19hs(kp, NULL);
+		kp->kp_ctrl_msg.usa19hs.portEnabled = 0;
+		kp->kp_ctrl_msg.usa19hs.rxFlush = 0;
+		kp->kp_ctrl_msg.usa19hs.txFlush = 0;
+		kp->kp_ctrl_msg.usa19hs.returnStatus = 0;
+		kp->kp_ctrl_msg.usa19hs.setRts = 1;
+		kp->kp_ctrl_msg.usa19hs.rts = 0;
+		kp->kp_ctrl_msg.usa19hs.setDtr = 1;
+		kp->kp_ctrl_msg.usa19hs.dtr = 0;
+		kp->kp_ctrl_msg.usa19hs.setTxFlowControl = 1;
+		kp->kp_ctrl_msg.usa19hs.txFlowControl = 0;
+		kp->kp_ctrl_msg.usa19hs.setRxFlowControl = 1;
+		kp->kp_ctrl_msg.usa19hs.rxFlowControl = 0;
+		kp->kp_ctrl_msg.usa19hs.rxForwardingTimeout = 0;
+		kp->kp_ctrl_msg.usa19hs.rxForwardingLength = 0;
+
+		break;
+
+#ifdef	KEYSPAN_USA49WLC
+	case KEYSPAN_USA49WLC_PID:
+		keyspan_build_cmd_msg_usa49(kp, NULL);
+		kp->kp_ctrl_msg.usa49._txOn = 0;
+		kp->kp_ctrl_msg.usa49._txOff = 1;
+		kp->kp_ctrl_msg.usa49.txFlush = 0;
+		kp->kp_ctrl_msg.usa49.txBreak = 0;
+		kp->kp_ctrl_msg.usa49.rxOn = 0;
+		kp->kp_ctrl_msg.usa49.rxOff = 1;
+		kp->kp_ctrl_msg.usa49.rxFlush = 0;
+		kp->kp_ctrl_msg.usa49.rxForward = 0;
+		kp->kp_ctrl_msg.usa49.returnStatus = 0;
+		kp->kp_ctrl_msg.usa49.resetDataToggle = 0;
+		kp->kp_ctrl_msg.usa49.enablePort = 0;
+		kp->kp_ctrl_msg.usa49.disablePort = 1;
+
+		break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+	default:
+		USB_DPRINTF_L2(DPRINT_ATTACH, ksp->ks_lh,
+		    "keyspan_close_hw_port:"
+		    "the device's product id can't be recognized");
+		mutex_exit(&kp->kp_mutex);
+
+		return;
+	}
+
+	mutex_exit(&kp->kp_mutex);
+
+	/* send close port cmd to this port */
+	if (keyspan_send_cmd(kp) != USB_SUCCESS) {
+		USB_DPRINTF_L2(DPRINT_CTLOP, kp->kp_lh,
+		    "keyspan_close_hw_port: closing hw port, send cmd FAILED");
+	}
+
+	/* blow away bulkin requests or pipe close will wait until timeout */
+	usb_pipe_reset(ksp->ks_dip, kp->kp_datain_pipe.pipe_handle,
+	    USB_FLAGS_SLEEP, NULL, NULL);
+
+	(void) keyspan_close_port_pipes(kp);
+}
+
+/*
+ * ds_close_port
+ */
+static int
+keyspan_close_port(ds_hdl_t hdl, uint_t port_num)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp = &ksp->ks_ports[port_num];
+
+	if (port_num >= ksp->ks_dev_spec.port_cnt) {
+
+		return (USB_FAILURE);
+	}
+	USB_DPRINTF_L4(DPRINT_CLOSE, kp->kp_lh, "keyspan_close_port");
+
+	sema_p(&ksp->ks_pipes_sema);
+	mutex_enter(&kp->kp_mutex);
+	kp->kp_no_more_reads = B_TRUE;
+
+	/* close hardware serial port */
+	mutex_exit(&kp->kp_mutex);
+
+	keyspan_close_hw_port(kp);
+	mutex_enter(&kp->kp_mutex);
+
+	/*
+	 * free resources and finalize state
+	 */
+	if (kp->kp_rx_mp) {
+		freemsg(kp->kp_rx_mp);
+		kp->kp_rx_mp = NULL;
+	}
+	if (kp->kp_tx_mp) {
+		freemsg(kp->kp_tx_mp);
+		kp->kp_tx_mp = NULL;
+	}
+
+	kp->kp_no_more_reads = B_FALSE;
+	kp->kp_state = KEYSPAN_PORT_CLOSED;
+	mutex_exit(&kp->kp_mutex);
+
+	keyspan_pm_set_idle(ksp);
+
+	sema_v(&ksp->ks_pipes_sema);
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * power management
+ *
+ * ds_usb_power
+ */
+/*ARGSUSED*/
+static int
+keyspan_usb_power(ds_hdl_t hdl, int comp, int level, int *new_state)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_pm_t	*pm = ksp->ks_pm;
+	int		rval;
+
+	USB_DPRINTF_L4(DPRINT_PM, ksp->ks_lh, "keyspan_usb_power");
+
+	mutex_enter(&ksp->ks_mutex);
+
+	/*
+	 * check if we are transitioning to a legal power level
+	 */
+	if (USB_DEV_PWRSTATE_OK(pm->pm_pwr_states, level)) {
+		USB_DPRINTF_L2(DPRINT_PM, ksp->ks_lh, "keyspan_usb_power:"
+		    "illegal power level %d, pwr_states=%x",
+		    level, pm->pm_pwr_states);
+		mutex_exit(&ksp->ks_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)) {
+		mutex_exit(&ksp->ks_mutex);
+
+		return (USB_FAILURE);
+	}
+
+	switch (level) {
+	case USB_DEV_OS_PWR_OFF:
+		rval = keyspan_pwrlvl0(ksp);
+
+		break;
+	case USB_DEV_OS_PWR_1:
+		rval = keyspan_pwrlvl1(ksp);
+
+		break;
+	case USB_DEV_OS_PWR_2:
+		rval = keyspan_pwrlvl2(ksp);
+
+		break;
+	case USB_DEV_OS_FULL_PWR:
+		rval = keyspan_pwrlvl3(ksp);
+
+		break;
+	default:
+		ASSERT(0);	/* cannot happen */
+	}
+
+	*new_state = ksp->ks_dev_state;
+	mutex_exit(&ksp->ks_mutex);
+
+	return (rval);
+}
+
+
+/*
+ * ds_suspend
+ */
+static int
+keyspan_suspend(ds_hdl_t hdl)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	int		state;
+
+	USB_DPRINTF_L4(DPRINT_PM, ksp->ks_lh, "keyspan_suspend");
+
+	mutex_enter(&ksp->ks_mutex);
+	state = ksp->ks_dev_state = USB_DEV_SUSPENDED;
+	mutex_exit(&ksp->ks_mutex);
+
+	keyspan_disconnect_pipes(ksp);
+
+	return (state);
+}
+
+
+/*
+ * ds_resume
+ */
+static int
+keyspan_resume(ds_hdl_t hdl)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	int		current_state;
+	int		rval;
+
+	USB_DPRINTF_L4(DPRINT_PM, ksp->ks_lh, "keyspan_resume");
+
+	/* needed as power up state of dev is "unknown" to system */
+	(void) pm_busy_component(ksp->ks_dip, 0);
+	(void) pm_raise_power(ksp->ks_dip, 0, USB_DEV_OS_FULL_PWR);
+
+	mutex_enter(&ksp->ks_mutex);
+	current_state = ksp->ks_dev_state;
+	mutex_exit(&ksp->ks_mutex);
+
+	if (current_state != USB_DEV_ONLINE) {
+		rval = keyspan_restore_device_state(ksp);
+	} else {
+		rval = USB_SUCCESS;
+	}
+
+	(void) pm_idle_component(ksp->ks_dip, 0);
+
+	return (rval);
+}
+
+
+/*
+ * ds_disconnect
+ */
+static int
+keyspan_disconnect(ds_hdl_t hdl)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	int		state;
+
+	USB_DPRINTF_L4(DPRINT_HOTPLUG, ksp->ks_lh, "keyspan_disconnect");
+
+	mutex_enter(&ksp->ks_mutex);
+	state = ksp->ks_dev_state = USB_DEV_DISCONNECTED;
+	mutex_exit(&ksp->ks_mutex);
+
+	keyspan_disconnect_pipes(ksp);
+
+	return (state);
+}
+
+
+/*
+ * ds_reconnect
+ */
+static int
+keyspan_reconnect(ds_hdl_t hdl)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+
+	USB_DPRINTF_L4(DPRINT_HOTPLUG, ksp->ks_lh, "keyspan_reconnect");
+
+	return (keyspan_restore_device_state(ksp));
+}
+
+/*
+ * ds_set_port_params
+ */
+static int
+keyspan_set_port_params(ds_hdl_t hdl, uint_t port_num, ds_port_params_t *tp)
+{
+	int		cnt = tp->tp_cnt;
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp = &ksp->ks_ports[port_num];
+
+	ASSERT(port_num < ksp->ks_dev_spec.port_cnt);
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh,
+	    "keyspan_set_port_params: port: %d params", cnt);
+
+	if (cnt <= 0) {
+
+		return (USB_SUCCESS);
+	}
+
+	mutex_enter(&kp->kp_mutex);
+	ASSERT((kp->kp_state == KEYSPAN_PORT_OPENING) ||
+	    (kp->kp_state == KEYSPAN_PORT_OPEN));
+	keyspan_build_cmd_msg(kp, tp);
+	mutex_exit(&kp->kp_mutex);
+
+	if (keyspan_send_cmd(kp) != USB_SUCCESS) {
+		USB_DPRINTF_L1(DPRINT_CTLOP, kp->kp_lh,
+		    "keyspan_send_cmd() FAILED");
+
+		return (USB_FAILURE);
+	}
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * ds_set_modem_ctl
+ */
+static int
+keyspan_set_modem_ctl(ds_hdl_t hdl, uint_t port_num, int mask, int val)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp = &ksp->ks_ports[port_num];
+
+	ASSERT(port_num < ksp->ks_dev_spec.port_cnt);
+
+	mutex_enter(&kp->kp_mutex);
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh, "keyspan_set_modem_ctl: "
+	    "mask=%x, val=%x", mask, val);
+
+	keyspan_build_cmd_msg(kp, NULL);
+
+	switch (ksp->ks_dev_spec.id_product) {
+	case KEYSPAN_USA19HS_PID:
+		if (mask & TIOCM_RTS) {
+
+			kp->kp_ctrl_msg.usa19hs.setRts = 0x01;
+
+			if (val & TIOCM_RTS) {
+				kp->kp_ctrl_msg.usa19hs.rts = 0x1;
+			} else {
+				kp->kp_ctrl_msg.usa19hs.rts = 0x0;
+			}
+
+		} else {
+			kp->kp_ctrl_msg.usa19hs.setRts = 0x0;
+		}
+
+		if (mask & TIOCM_DTR) {
+			kp->kp_ctrl_msg.usa19hs.setDtr = 0x01;
+
+			if (val & TIOCM_DTR) {
+				kp->kp_ctrl_msg.usa19hs.dtr = 0x1;
+			} else {
+				kp->kp_ctrl_msg.usa19hs.dtr = 0x0;
+			}
+
+		} else {
+			kp->kp_ctrl_msg.usa19hs.setDtr = 0x0;
+		}
+
+		break;
+
+#ifdef	KEYSPAN_USA49WLC
+	case KEYSPAN_USA49WLC_PID:
+		if (mask & TIOCM_RTS) {
+
+			kp->kp_ctrl_msg.usa49.setRts = 0x1;
+
+			if (val & TIOCM_RTS) {
+				kp->kp_ctrl_msg.usa49.rts = 0x1;
+			} else {
+				kp->kp_ctrl_msg.usa49.rts = 0x0;
+			}
+
+		} else {
+			kp->kp_ctrl_msg.usa49.setRts = 0x0;
+		}
+
+		if (mask & TIOCM_DTR) {
+			kp->kp_ctrl_msg.usa49.setDtr = 0x1;
+
+			if (val & TIOCM_DTR) {
+				kp->kp_ctrl_msg.usa49.dtr = 0x1;
+			} else {
+				kp->kp_ctrl_msg.usa49.dtr = 0x0;
+			}
+
+		} else {
+			kp->kp_ctrl_msg.usa49.setDtr = 0x0;
+		}
+
+		break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+	default:
+		USB_DPRINTF_L2(DPRINT_CTLOP, kp->kp_lh,
+		    "keyspan_get_modem_ctl:"
+		    "the device's product id can't be recognized");
+		mutex_exit(&kp->kp_mutex);
+
+		return (USB_FAILURE);
+	}
+
+	mutex_exit(&kp->kp_mutex);
+
+	if (keyspan_send_cmd(kp) != USB_SUCCESS) {
+		USB_DPRINTF_L2(DPRINT_CTLOP, kp->kp_lh,
+		    "keyspan_send_cmd() FAILED");
+
+		return (USB_FAILURE);
+	}
+
+	/* busy wait 10ms */
+	drv_usecwait(10000);
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * ds_get_modem_ctl
+ */
+static int
+keyspan_get_modem_ctl(ds_hdl_t hdl, uint_t port_num, int mask, int *valp)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp = &ksp->ks_ports[port_num];
+	int	val = 0;
+
+	ASSERT(port_num < ksp->ks_dev_spec.port_cnt);
+
+	mutex_enter(&kp->kp_mutex);
+
+	/*
+	 * rts and dtr are not in status_msg, but we can get it from
+	 * status_flag since it represents what we set the device last time.
+	 */
+	if (kp->kp_status_flag & KEYSPAN_PORT_RTS) {
+		val |= TIOCM_RTS;
+	}
+	if (kp->kp_status_flag & KEYSPAN_PORT_DTR) {
+		val |= TIOCM_DTR;
+	}
+
+	/* usbser don't deal with TIOCM_RI status */
+	switch (ksp->ks_dev_spec.id_product) {
+	case KEYSPAN_USA19HS_PID:
+		if (kp->kp_status_msg.usa19hs.dcd) {
+			val |= TIOCM_CD;
+		}
+		if (kp->kp_status_msg.usa19hs.cts) {
+			val |= TIOCM_CTS;
+		}
+		if (kp->kp_status_msg.usa19hs.dsr) {
+			val |= TIOCM_DSR;
+		}
+		break;
+
+#ifdef	KEYSPAN_USA49WLC
+	case KEYSPAN_USA49WLC_PID:
+		if (kp->kp_status_msg.usa49.dcd) {
+			val |= TIOCM_CD;
+		}
+		if (kp->kp_status_msg.usa49.cts) {
+			val |= TIOCM_CTS;
+		}
+		if (kp->kp_status_msg.usa49.dsr) {
+			val |= TIOCM_DSR;
+		}
+		break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+	default:
+		USB_DPRINTF_L2(DPRINT_ATTACH, ksp->ks_lh,
+		    "keyspan_get_modem_ctl:"
+		    "the device's product id can't be recognized");
+		mutex_exit(&kp->kp_mutex);
+
+		return (USB_FAILURE);
+	}
+
+	*valp = val & mask;
+
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh, "keyspan_get_modem_ctl:"
+	    "success. status_flag = %x, val=0%o",
+	    kp->kp_status_flag, *valp);
+
+	mutex_exit(&kp->kp_mutex);
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * ds_break_ctl
+ */
+static int
+keyspan_break_ctl(ds_hdl_t hdl, uint_t port_num, int ctl)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp = &ksp->ks_ports[port_num];
+	int		is_break;
+	int		rval = USB_SUCCESS;
+
+	ASSERT(port_num < ksp->ks_dev_spec.port_cnt);
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh,
+	    "keyspan_break_ctl: ctl = %s", (ctl == DS_ON) ? "on" : "off");
+
+	mutex_enter(&kp->kp_mutex);
+	ASSERT(kp->kp_state == KEYSPAN_PORT_OPEN);
+	ASSERT(ctl == DS_ON || ctl == DS_OFF);
+
+	is_break = kp->kp_status_flag & KEYSPAN_PORT_TXBREAK;
+
+	if ((ctl == DS_ON) && !is_break) {
+
+		keyspan_build_cmd_msg(kp, NULL);
+
+		switch (ksp->ks_dev_spec.id_product) {
+		case KEYSPAN_USA19HS_PID:
+			kp->kp_ctrl_msg.usa19hs.txBreak = 1;
+
+			break;
+
+#ifdef	KEYSPAN_USA49WLC
+		case KEYSPAN_USA49WLC_PID:
+			kp->kp_ctrl_msg.usa49.txBreak = 1;
+
+			break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+		default:
+			mutex_exit(&kp->kp_mutex);
+			USB_DPRINTF_L2(DPRINT_ATTACH, ksp->ks_lh,
+			    "keyspan_break_ctl:"
+			    "the device's product id can't be recognized");
+
+			return (USB_FAILURE);
+		}
+
+		mutex_exit(&kp->kp_mutex);
+
+		rval = keyspan_send_cmd(kp);
+
+		return (rval);
+	}
+
+	if ((ctl == DS_OFF) && is_break) {
+		keyspan_build_cmd_msg(kp, NULL);
+
+		switch (ksp->ks_dev_spec.id_product) {
+		case KEYSPAN_USA19HS_PID:
+			kp->kp_ctrl_msg.usa19hs.txBreak = 0;
+
+			break;
+#ifdef	KEYSPAN_USA49WLC
+		case KEYSPAN_USA49WLC_PID:
+			kp->kp_ctrl_msg.usa49._txOn = 1;
+			kp->kp_ctrl_msg.usa49.txBreak = 0;
+
+			break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+		default:
+			mutex_exit(&kp->kp_mutex);
+			USB_DPRINTF_L2(DPRINT_ATTACH, ksp->ks_lh,
+			    "keyspan_break_ctl:"
+			    "the device's product id can't be recognized");
+
+			return (USB_FAILURE);
+		}
+
+		mutex_exit(&kp->kp_mutex);
+
+		rval = keyspan_send_cmd(kp);
+
+		if (rval == USB_SUCCESS) {
+			mutex_enter(&kp->kp_mutex);
+
+			/* resume transmit */
+			keyspan_tx_start(kp, NULL);
+			mutex_exit(&kp->kp_mutex);
+		}
+
+		return (rval);
+	}
+
+	mutex_exit(&kp->kp_mutex);
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh,
+	    "keyspan_break_ctl: not necessary to set break, is_break = %d",
+	    is_break);
+
+	return (rval);
+}
+
+
+/*
+ * ds_loopback
+ */
+static int
+keyspan_loopback(ds_hdl_t hdl, uint_t port_num, int ctl)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp = &ksp->ks_ports[port_num];
+	int		is_loop;
+	int		rval = USB_SUCCESS;
+
+	ASSERT(port_num < ksp->ks_dev_spec.port_cnt);
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh,
+	    "keyspan_loopback: %s", (ctl == DS_ON) ? "on" : "off");
+
+	mutex_enter(&kp->kp_mutex);
+	ASSERT(kp->kp_state == KEYSPAN_PORT_OPEN);
+	ASSERT(ctl == DS_ON || ctl == DS_OFF);
+
+	/* check bit indicating internal loopback state */
+	is_loop = kp->kp_status_flag & KEYSPAN_PORT_LOOPBACK;
+
+	if ((ctl == DS_ON) && !is_loop) {
+
+		keyspan_build_cmd_msg(kp, NULL);
+		switch (ksp->ks_dev_spec.id_product) {
+		case KEYSPAN_USA19HS_PID:
+			kp->kp_ctrl_msg.usa19hs.loopbackMode = 0;
+
+			break;
+
+#ifdef	KEYSPAN_USA49WLC
+		case KEYSPAN_USA49WLC_PID:
+			kp->kp_ctrl_msg.usa49.loopbackMode = 0;
+
+			break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+		default:
+			mutex_exit(&kp->kp_mutex);
+			USB_DPRINTF_L2(DPRINT_ATTACH, ksp->ks_lh,
+			    "keyspan_loopback:"
+			    "the device's product id can't be recognized");
+
+			return (USB_FAILURE);
+		}
+		mutex_exit(&kp->kp_mutex);
+		rval = keyspan_send_cmd(kp);
+	} else if ((ctl == DS_OFF) && is_loop) {
+
+		keyspan_build_cmd_msg(kp, NULL);
+		switch (ksp->ks_dev_spec.id_product) {
+		case KEYSPAN_USA19HS_PID:
+			kp->kp_ctrl_msg.usa19hs.loopbackMode = 1;
+
+			break;
+
+#ifdef	KEYSPAN_USA49WLC
+		case KEYSPAN_USA49WLC_PID:
+			kp->kp_ctrl_msg.usa49.loopbackMode = 1;
+
+			break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+		default:
+			mutex_exit(&kp->kp_mutex);
+			USB_DPRINTF_L2(DPRINT_ATTACH, ksp->ks_lh,
+			    "keyspan_loopback:"
+			    "the device's product id can't be recognized");
+
+			return (USB_FAILURE);
+		}
+		mutex_exit(&kp->kp_mutex);
+		rval = keyspan_send_cmd(kp);
+	} else {
+		mutex_exit(&kp->kp_mutex);
+		USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh,
+		    "keyspan_loopback: not necessary to set loopback,"
+		    "is_loop = %d", is_loop);
+	}
+
+	return (rval);
+}
+
+
+/*
+ * ds_tx
+ */
+static int
+keyspan_tx(ds_hdl_t hdl, uint_t port_num, mblk_t *mp)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp = &ksp->ks_ports[port_num];
+	int		xferd;
+
+	ASSERT(port_num < ksp->ks_dev_spec.port_cnt);
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh, "keyspan_tx");
+
+	/*
+	 * sanity checks
+	 */
+	if (mp == NULL) {
+		USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh, "keyspan_tx: mp=NULL");
+
+		return (USB_SUCCESS);
+	}
+	if (MBLKL(mp) <= 0) {
+		USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh, "keyspan_tx: len<=0");
+		freemsg(mp);
+
+		return (USB_SUCCESS);
+	}
+
+	kp = &ksp->ks_ports[port_num];
+
+	mutex_enter(&kp->kp_mutex);
+
+	keyspan_put_tail(&kp->kp_tx_mp, mp);	/* add to the chain */
+
+	keyspan_tx_start(kp, &xferd);		/* go! */
+
+	mutex_exit(&kp->kp_mutex);
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * ds_rx. the real data receiving is in keyspan_open_hw_port
+ */
+static mblk_t *
+keyspan_rx(ds_hdl_t hdl, uint_t port_num)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp = &ksp->ks_ports[port_num];
+	mblk_t		*mp;
+
+	ASSERT(port_num < ksp->ks_dev_spec.port_cnt);
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh, "keyspan_rx");
+
+	mutex_enter(&kp->kp_mutex);
+	mp = kp->kp_rx_mp;
+	kp->kp_rx_mp = NULL;
+	mutex_exit(&kp->kp_mutex);
+
+	return (mp);
+}
+
+
+/*
+ * ds_stop
+ */
+static void
+keyspan_stop(ds_hdl_t hdl, uint_t port_num, int dir)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp = &ksp->ks_ports[port_num];
+
+	ASSERT(port_num < ksp->ks_dev_spec.port_cnt);
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh, "keyspan_stop");
+
+	if (dir & DS_TX) {
+		mutex_enter(&kp->kp_mutex);
+		kp->kp_flags |= KEYSPAN_PORT_TX_STOPPED;
+		mutex_exit(&kp->kp_mutex);
+	}
+}
+
+
+/*
+ * ds_start
+ */
+static void
+keyspan_start(ds_hdl_t hdl, uint_t port_num, int dir)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp = &ksp->ks_ports[port_num];
+
+	ASSERT(port_num < ksp->ks_dev_spec.port_cnt);
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh, "keyspan_start");
+
+	if (dir & DS_TX) {
+		mutex_enter(&kp->kp_mutex);
+		if (kp->kp_flags & KEYSPAN_PORT_TX_STOPPED) {
+			kp->kp_flags &= ~KEYSPAN_PORT_TX_STOPPED;
+			keyspan_tx_start(kp, NULL);
+		}
+		mutex_exit(&kp->kp_mutex);
+	}
+}
+
+
+/*
+ * ds_fifo_flush
+ * send flush cmd and wait for completion, then turn off the flush.
+ */
+static int
+keyspan_fifo_flush(ds_hdl_t hdl, uint_t port_num, int dir)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp = &ksp->ks_ports[port_num];
+
+	ASSERT(port_num < ksp->ks_dev_spec.port_cnt);
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh,
+	    "keyspan_fifo_flush: dir=%x", dir);
+
+	mutex_enter(&kp->kp_mutex);
+	ASSERT(kp->kp_state == KEYSPAN_PORT_OPEN);
+
+	/* discard the data in DSD buffers */
+	if ((dir & DS_TX) && kp->kp_tx_mp) {
+		freemsg(kp->kp_tx_mp);
+		kp->kp_tx_mp = NULL;
+	}
+	if ((dir & DS_RX) && kp->kp_rx_mp) {
+		freemsg(kp->kp_rx_mp);
+		kp->kp_rx_mp = NULL;
+	}
+
+	mutex_exit(&kp->kp_mutex);
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * ds_fifo_drain
+ *
+ * it is the caller's responsibility to cease submitting new tx data
+ * while this function executes
+ */
+static int
+keyspan_fifo_drain(ds_hdl_t hdl, uint_t port_num, int timeout)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)hdl;
+	keyspan_port_t	*kp = &ksp->ks_ports[port_num];
+	int		rval = USB_SUCCESS;
+
+	ASSERT(port_num < ksp->ks_dev_spec.port_cnt);
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh,
+	    "keyspan_fifo_drain, timeout = %d", timeout);
+
+	mutex_enter(&kp->kp_mutex);
+	ASSERT(kp->kp_state == KEYSPAN_PORT_OPEN);
+
+	/* wait until local data drains */
+	if (keyspan_wait_tx_drain(kp, 0) != USB_SUCCESS) {
+		mutex_exit(&kp->kp_mutex);
+
+		return (USB_FAILURE);
+	}
+	mutex_exit(&kp->kp_mutex);
+
+	/* wait until hw fifo drains */
+	delay(drv_usectohz(500*1000));
+
+	return (rval);
+}
+
+
+/*
+ * configuration routines
+ * ----------------------
+ *
+ */
+
+/*
+ * free state structure
+ */
+static void
+keyspan_free_soft_state(keyspan_state_t *ksp)
+{
+	kmem_free(ksp, sizeof (keyspan_state_t));
+}
+
+
+/*
+ * register/unregister USBA client
+ */
+static int
+keyspan_usb_register(keyspan_state_t *ksp)
+{
+	int	rval;
+
+	rval = usb_client_attach(ksp->ks_dip, USBDRV_VERSION, 0);
+	if (rval == USB_SUCCESS) {
+		rval = usb_get_dev_data(ksp->ks_dip, &ksp->ks_dev_data,
+		    USB_PARSE_LVL_IF, 0);
+		if (rval == USB_SUCCESS) {
+			ksp->ks_lh =
+			    usb_alloc_log_hdl(ksp->ks_dip, "keyspan[*].",
+			    &keyspan_errlevel, &keyspan_errmask,
+			    &keyspan_instance_debug, 0);
+
+			ksp->ks_def_pipe.pipe_handle =
+			    ksp->ks_dev_data->dev_default_ph;
+			ksp->ks_def_pipe.pipe_ksp = ksp;
+			ksp->ks_def_pipe.pipe_lh = ksp->ks_lh;
+		}
+	}
+
+	return (rval);
+}
+
+
+static void
+keyspan_usb_unregister(keyspan_state_t *ksp)
+{
+	usb_free_log_hdl(ksp->ks_lh);
+	ksp->ks_lh = NULL;
+	usb_client_detach(ksp->ks_dip, ksp->ks_dev_data);
+	ksp->ks_def_pipe.pipe_handle = NULL;
+	ksp->ks_dev_data = NULL;
+}
+
+
+/*
+ * init/fini soft state during attach
+ */
+static void
+keyspan_init_sync_objs(keyspan_state_t *ksp)
+{
+	mutex_init(&ksp->ks_mutex, NULL, MUTEX_DRIVER,
+			ksp->ks_dev_data->dev_iblock_cookie);
+	sema_init(&ksp->ks_pipes_sema, 1, NULL, SEMA_DRIVER, NULL);
+}
+
+
+static void
+keyspan_fini_sync_objs(keyspan_state_t *ksp)
+{
+	mutex_destroy(&ksp->ks_mutex);
+	sema_destroy(&ksp->ks_pipes_sema);
+}
+
+
+/*
+ * device specific attributes
+ */
+static int
+keyspan_attach_dev(keyspan_state_t *ksp)
+{
+
+	mutex_enter(&ksp->ks_mutex);
+	switch (ksp->ks_dev_data->dev_descr->idProduct) {
+	case KEYSPAN_USA19HS_PID:
+		ksp->ks_dev_spec.id_product = KEYSPAN_USA19HS_PID;
+		ksp->ks_dev_spec.port_cnt = 1;
+		ksp->ks_dev_spec.ctrl_ep_addr = 0x02;
+		ksp->ks_dev_spec.stat_ep_addr = 0x82;
+		ksp->ks_dev_spec.dataout_ep_addr[0] = 0x01;
+		ksp->ks_dev_spec.datain_ep_addr[0] = 0x81;
+
+		break;
+
+#ifdef	KEYSPAN_USA49WLC
+	case KEYSPAN_USA49WLC_PID:
+		ksp->ks_dev_spec.id_product = KEYSPAN_USA49WLC_PID;
+		ksp->ks_dev_spec.port_cnt = 4;
+		ksp->ks_dev_spec.ctrl_ep_addr = 0x07;
+		ksp->ks_dev_spec.stat_ep_addr = 0x87;
+		ksp->ks_dev_spec.dataout_ep_addr[0] = 0x01;
+		ksp->ks_dev_spec.dataout_ep_addr[1] = 0x02;
+		ksp->ks_dev_spec.dataout_ep_addr[2] = 0x03;
+		ksp->ks_dev_spec.dataout_ep_addr[3] = 0x04;
+		ksp->ks_dev_spec.datain_ep_addr[0] = 0x81;
+		ksp->ks_dev_spec.datain_ep_addr[1] = 0x82;
+		ksp->ks_dev_spec.datain_ep_addr[2] = 0x83;
+		ksp->ks_dev_spec.datain_ep_addr[3] = 0x84;
+
+		break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+	default:
+		mutex_exit(&ksp->ks_mutex);
+		USB_DPRINTF_L1(DPRINT_ATTACH, ksp->ks_lh,
+		    "keyspan_attach_dev:"
+		    "the device's product id can't be recognized");
+
+		return (USB_FAILURE);
+	}
+
+	mutex_exit(&ksp->ks_mutex);
+
+	return (USB_SUCCESS);
+}
+
+/*
+ * allocate and initialize per port resources.
+ */
+static void
+keyspan_attach_ports(keyspan_state_t *ksp)
+{
+	int		i;
+	keyspan_port_t	*kp;
+
+	ksp->ks_ports = kmem_zalloc(ksp->ks_dev_spec.port_cnt *
+					sizeof (keyspan_port_t), KM_SLEEP);
+
+	for (i = 0; i < ksp->ks_dev_spec.port_cnt; i++) {
+		kp = &ksp->ks_ports[i];
+		kp->kp_port_num = i;
+		kp->kp_ksp = ksp;
+
+		(void) sprintf(kp->kp_lh_name, "keyspan[%d].", i);
+		kp->kp_lh = usb_alloc_log_hdl(ksp->ks_dip, kp->kp_lh_name,
+			&keyspan_errlevel, &keyspan_errmask,
+			&keyspan_instance_debug, 0);
+
+		kp->kp_state = KEYSPAN_PORT_CLOSED;
+		mutex_init(&kp->kp_mutex, NULL, MUTEX_DRIVER,
+				ksp->ks_dev_data->dev_iblock_cookie);
+		cv_init(&kp->kp_tx_cv, NULL, CV_DRIVER, NULL);
+	}
+}
+
+
+/*
+ * free per port resources
+ */
+static void
+keyspan_detach_ports(keyspan_state_t *ksp)
+{
+	int		i;
+	keyspan_port_t	*kp;
+
+	for (i = 0; i < ksp->ks_dev_spec.port_cnt; i++) {
+		kp = &ksp->ks_ports[i];
+		if (kp->kp_state != KEYSPAN_PORT_NOT_INIT) {
+			ASSERT(kp->kp_state == KEYSPAN_PORT_CLOSED);
+
+			mutex_destroy(&kp->kp_mutex);
+			cv_destroy(&kp->kp_tx_cv);
+			usb_free_log_hdl(kp->kp_lh);
+		}
+	}
+	kmem_free(ksp->ks_ports,
+	    ksp->ks_dev_spec.port_cnt * sizeof (keyspan_port_t));
+}
+
+static void
+keyspan_init_port_params(keyspan_state_t *ksp)
+{
+	int		i;
+	size_t		sz;
+
+	for (i = 0; i < ksp->ks_dev_spec.port_cnt; i++) {
+
+		/* lengths for bulk requests */
+		if (usb_pipe_get_max_bulk_transfer_size(ksp->ks_dip, &sz) ==
+		    USB_SUCCESS) {
+			ksp->ks_ports[i].kp_read_len = min(sz,
+			    KEYSPAN_BULKIN_MAX_LEN);
+		} else {
+			ksp->ks_ports[i].kp_read_len = KEYSPAN_BULKIN_MAX_LEN;
+		}
+
+		/* the max data len of every bulk out req. */
+		ksp->ks_ports[i].kp_write_len = KEYSPAN_BULKOUT_MAX_LEN;
+	}
+}
+
+
+/*
+ * free descriptor tree
+ */
+static void
+keyspan_free_descr_tree(keyspan_state_t *ksp)
+{
+	usb_free_descr_tree(ksp->ks_dip, ksp->ks_dev_data);
+
+}
+
+
+/*
+ * register/unregister USB event callbacks
+ */
+static int
+keyspan_register_events(keyspan_state_t *ksp)
+{
+	return (usb_register_event_cbs(ksp->ks_dip, ksp->ks_usb_events, 0));
+}
+
+
+static void
+keyspan_unregister_events(keyspan_state_t *ksp)
+{
+	usb_unregister_event_cbs(ksp->ks_dip, ksp->ks_usb_events);
+}
+
+
+static void
+keyspan_set_dev_state_online(keyspan_state_t *ksp)
+{
+	ksp->ks_dev_state = USB_DEV_ONLINE;
+}
+
+/*
+ * send command to the port and save the params after its completion
+ */
+int
+keyspan_send_cmd(keyspan_port_t *kp)
+{
+	keyspan_state_t	*ksp = kp->kp_ksp;
+	mblk_t		*mp;
+	int		rval = USB_SUCCESS;
+	int	size;
+	usb_bulk_req_t	*br;
+
+	ASSERT(!mutex_owned(&kp->kp_mutex));
+	USB_DPRINTF_L4(DPRINT_OUT_PIPE, kp->kp_lh, "keyspan_send_cmd");
+
+	switch (ksp->ks_dev_spec.id_product) {
+	case KEYSPAN_USA19HS_PID:
+		size = sizeof (keyspan_usa19hs_port_ctrl_msg_t);
+
+		break;
+
+#ifdef	KEYSPAN_USA49WLC
+	case KEYSPAN_USA49WLC_PID:
+		size = sizeof (keyspan_usa49_port_ctrl_msg_t);
+
+		break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+	default:
+		USB_DPRINTF_L2(DPRINT_ATTACH, ksp->ks_lh, "keyspan_break_ctl:"
+		    "the device's product id can't be recognized");
+		return (USB_FAILURE);
+	}
+
+	if ((mp = allocb(size, BPRI_LO)) == NULL) {
+
+		return (USB_FAILURE);
+	}
+	bcopy(&kp->kp_ctrl_msg, mp->b_rptr, size);
+
+	br = usb_alloc_bulk_req(ksp->ks_dip, 0, USB_FLAGS_SLEEP);
+	br->bulk_len = size;
+	br->bulk_data = mp;
+	br->bulk_timeout = KEYSPAN_BULK_TIMEOUT;
+	br->bulk_client_private = (void *)kp;
+	br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
+
+	rval = usb_pipe_bulk_xfer(ksp->ks_ctrlout_pipe.pipe_handle, br,
+	    USB_FLAGS_SLEEP);
+	if (rval == USB_SUCCESS) {
+
+		/* busy wait 5ms for the cmd to complete */
+		drv_usecwait(5000);
+
+		mutex_enter(&kp->kp_mutex);
+		keyspan_save_port_params(kp);
+		mutex_exit(&kp->kp_mutex);
+	} else {
+		USB_DPRINTF_L2(DPRINT_CTLOP, kp->kp_lh, "keyspan_send_cmd:"
+		    "failure, rval=%d", rval);
+	}
+
+	usb_free_bulk_req(br);
+
+	return (rval);
+}
+
+/*
+ * hotplug
+ * -------
+ *
+ * restore device state after CPR resume or reconnect
+ */
+static int
+keyspan_restore_device_state(keyspan_state_t *ksp)
+{
+	int	state;
+
+	mutex_enter(&ksp->ks_mutex);
+	state = ksp->ks_dev_state;
+	mutex_exit(&ksp->ks_mutex);
+
+	if ((state != USB_DEV_DISCONNECTED) && (state != USB_DEV_SUSPENDED)) {
+
+		return (state);
+	}
+
+	if (usb_check_same_device(ksp->ks_dip, ksp->ks_lh, USB_LOG_L2,
+	    DPRINT_MASK_ALL, USB_CHK_ALL, NULL) != USB_SUCCESS) {
+		mutex_enter(&ksp->ks_mutex);
+		state = ksp->ks_dev_state = USB_DEV_DISCONNECTED;
+		mutex_exit(&ksp->ks_mutex);
+
+		return (state);
+	}
+
+	if (state == USB_DEV_DISCONNECTED) {
+		USB_DPRINTF_L0(DPRINT_HOTPLUG, ksp->ks_lh,
+		    "device has been reconnected but data may have been lost");
+	}
+
+	if (keyspan_reconnect_pipes(ksp) != USB_SUCCESS) {
+
+		return (state);
+	}
+
+	/*
+	 * init device state
+	 */
+	mutex_enter(&ksp->ks_mutex);
+	state = ksp->ks_dev_state = USB_DEV_ONLINE;
+	mutex_exit(&ksp->ks_mutex);
+
+	/*
+	 * now restore each open port
+	 */
+	(void) keyspan_restore_ports_state(ksp);
+
+	return (state);
+}
+
+
+/*
+ * restore ports state after CPR resume or reconnect
+ */
+static int
+keyspan_restore_ports_state(keyspan_state_t *ksp)
+{
+	keyspan_port_t	*kp;
+	int		rval = USB_SUCCESS;
+	int		err;
+	int		i;
+
+	for (i = 0; i < ksp->ks_dev_spec.port_cnt; i++) {
+		kp = &ksp->ks_ports[i];
+		/*
+		 * only care about open ports
+		 */
+		mutex_enter(&kp->kp_mutex);
+		if (kp->kp_state != KEYSPAN_PORT_OPEN) {
+			mutex_exit(&kp->kp_mutex);
+			continue;
+		}
+		mutex_exit(&kp->kp_mutex);
+
+		sema_p(&ksp->ks_pipes_sema);
+		/* open hardware serial port */
+		err = keyspan_open_hw_port(kp, B_FALSE);
+		sema_v(&ksp->ks_pipes_sema);
+
+		if (err != USB_SUCCESS) {
+			USB_DPRINTF_L2(DPRINT_HOTPLUG, kp->kp_lh,
+			    "keyspan_restore_ports_state: failed");
+			rval = err;
+		}
+	}
+
+	return (rval);
+}
+
+
+/*
+ * power management
+ * ----------------
+ *
+ *
+ * create PM components
+ */
+static int
+keyspan_create_pm_components(keyspan_state_t *ksp)
+{
+	dev_info_t	*dip = ksp->ks_dip;
+	keyspan_pm_t	*pm;
+	uint_t		pwr_states;
+
+	pm = ksp->ks_pm = kmem_zalloc(sizeof (keyspan_pm_t), KM_SLEEP);
+	pm->pm_cur_power = USB_DEV_OS_FULL_PWR;
+
+	if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) {
+		USB_DPRINTF_L2(DPRINT_PM, ksp->ks_lh,
+		    "keyspan_create_pm_components: failed");
+
+		return (USB_SUCCESS);
+	}
+
+	pm->pm_wakeup_enabled = (usb_handle_remote_wakeup(dip,
+				USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS);
+	pm->pm_pwr_states = (uint8_t)pwr_states;
+
+	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * destroy PM components
+ */
+static void
+keyspan_destroy_pm_components(keyspan_state_t *ksp)
+{
+	keyspan_pm_t	*pm = ksp->ks_pm;
+	dev_info_t	*dip = ksp->ks_dip;
+	int		rval;
+
+	if (ksp->ks_dev_state != USB_DEV_DISCONNECTED) {
+		if (pm->pm_wakeup_enabled) {
+			(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
+
+			rval = usb_handle_remote_wakeup(dip,
+			    USB_REMOTE_WAKEUP_DISABLE);
+			if (rval != USB_SUCCESS) {
+				USB_DPRINTF_L2(DPRINT_PM, ksp->ks_lh,
+				    "keyspan_destroy_pm_components: disable "
+				    "remote wakeup failed, rval=%d", rval);
+			}
+		}
+
+		(void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
+	}
+	kmem_free(pm, sizeof (keyspan_pm_t));
+	ksp->ks_pm = NULL;
+}
+
+
+/*
+ * mark device busy and raise power
+ */
+static int
+keyspan_pm_set_busy(keyspan_state_t *ksp)
+{
+	keyspan_pm_t	*pm = ksp->ks_pm;
+	dev_info_t	*dip = ksp->ks_dip;
+
+	USB_DPRINTF_L4(DPRINT_PM, ksp->ks_lh, "keyspan_pm_set_busy");
+
+	mutex_enter(&ksp->ks_mutex);
+	/* if already marked busy, just increment the counter */
+	if (pm->pm_busy_cnt++ > 0) {
+		USB_DPRINTF_L3(DPRINT_PM, ksp->ks_lh, "keyspan_pm_set_busy:"
+		    "already busy, busy_cnt = %d", pm->pm_busy_cnt);
+		mutex_exit(&ksp->ks_mutex);
+
+		return (USB_SUCCESS);
+	}
+
+	(void) pm_busy_component(dip, 0);
+
+	if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) {
+		mutex_exit(&ksp->ks_mutex);
+
+		return (USB_SUCCESS);
+	}
+
+	/* need to raise power	*/
+	pm->pm_raise_power = B_TRUE;
+	mutex_exit(&ksp->ks_mutex);
+
+	USB_DPRINTF_L3(DPRINT_PM, ksp->ks_lh,
+	    "keyspan_pm_set_busy: raise power");
+	(void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
+
+	mutex_enter(&ksp->ks_mutex);
+	pm->pm_raise_power = B_FALSE;
+	mutex_exit(&ksp->ks_mutex);
+
+	return (USB_SUCCESS);
+}
+
+
+/*
+ * mark device idle
+ */
+static void
+keyspan_pm_set_idle(keyspan_state_t *ksp)
+{
+	keyspan_pm_t	*pm = ksp->ks_pm;
+	dev_info_t	*dip = ksp->ks_dip;
+
+	USB_DPRINTF_L4(DPRINT_PM, ksp->ks_lh, "keyspan_pm_set_idle");
+
+	/*
+	 * if more ports use the device, do not mark as yet
+	 */
+	mutex_enter(&ksp->ks_mutex);
+	if (--pm->pm_busy_cnt > 0) {
+		mutex_exit(&ksp->ks_mutex);
+
+		return;
+	}
+
+	USB_DPRINTF_L4(DPRINT_PM, ksp->ks_lh, "keyspan_pm_set_idle: set idle");
+	(void) pm_idle_component(dip, 0);
+
+	mutex_exit(&ksp->ks_mutex);
+}
+
+
+/*
+ * Functions to handle power transition for OS levels 0 -> 3
+ */
+static int
+keyspan_pwrlvl0(keyspan_state_t *ksp)
+{
+	int	rval;
+
+	USB_DPRINTF_L4(DPRINT_PM, ksp->ks_lh, "keyspan_pwrlvl0");
+
+	switch (ksp->ks_dev_state) {
+	case USB_DEV_ONLINE:
+		/* issue USB D3 command to the device */
+		rval = usb_set_device_pwrlvl3(ksp->ks_dip);
+		ASSERT(rval == USB_SUCCESS);
+		ksp->ks_dev_state = USB_DEV_PWRED_DOWN;
+		ksp->ks_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(DPRINT_PM, ksp->ks_lh,
+		    "keyspan_pwrlvl0: illegal device state");
+
+		return (USB_FAILURE);
+	}
+}
+
+
+static int
+keyspan_pwrlvl1(keyspan_state_t *ksp)
+{
+	USB_DPRINTF_L4(DPRINT_PM, ksp->ks_lh, "keyspan_pwrlvl1");
+
+	/* issue USB D2 command to the device */
+	(void) usb_set_device_pwrlvl2(ksp->ks_dip);
+
+	return (USB_FAILURE);
+}
+
+
+static int
+keyspan_pwrlvl2(keyspan_state_t *ksp)
+{
+	USB_DPRINTF_L4(DPRINT_PM, ksp->ks_lh, "keyspan_pwrlvl2");
+
+	/* issue USB D1 command to the device */
+	(void) usb_set_device_pwrlvl1(ksp->ks_dip);
+
+	return (USB_FAILURE);
+}
+
+
+static int
+keyspan_pwrlvl3(keyspan_state_t *ksp)
+{
+	int	rval;
+
+	USB_DPRINTF_L4(DPRINT_PM, ksp->ks_lh, "keyspan_pwrlvl3");
+
+	switch (ksp->ks_dev_state) {
+	case USB_DEV_PWRED_DOWN:
+		/* Issue USB D0 command to the device here */
+		rval = usb_set_device_pwrlvl0(ksp->ks_dip);
+		ASSERT(rval == USB_SUCCESS);
+		ksp->ks_dev_state = USB_DEV_ONLINE;
+		ksp->ks_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(DPRINT_PM, ksp->ks_lh,
+		    "keyspan_pwrlvl3: illegal device state");
+
+		return (USB_FAILURE);
+	}
+}
+
+
+/*
+ * pipe operations
+ * ---------------
+ *
+ * XXX keyspan seem to malfunction after the pipes are closed
+ * and reopened again (does not respond to OPEN_PORT command).
+ * so we open them once in attach
+ */
+static int
+keyspan_attach_pipes(keyspan_state_t *ksp)
+{
+
+	return (keyspan_open_dev_pipes(ksp));
+}
+
+
+void
+keyspan_detach_pipes(keyspan_state_t *ksp)
+{
+
+	/*
+	 * Blow away status bulk in requests or
+	 * pipe close will wait until timeout.
+	 */
+	if (ksp->ks_statin_pipe.pipe_handle) {
+
+		usb_pipe_reset(ksp->ks_dip, ksp->ks_statin_pipe.pipe_handle,
+		    USB_FLAGS_SLEEP, NULL, NULL);
+	}
+
+	/* Close the globle pipes */
+	keyspan_close_dev_pipes(ksp);
+}
+
+
+/*
+ * during device disconnect/suspend, close pipes if they are open.
+ */
+static void
+keyspan_disconnect_pipes(keyspan_state_t *ksp)
+{
+	sema_p(&ksp->ks_pipes_sema);
+	keyspan_close_pipes(ksp);
+	sema_v(&ksp->ks_pipes_sema);
+}
+
+
+/*
+ * during device reconnect/resume, reopen pipes if they were open.
+ */
+static int
+keyspan_reconnect_pipes(keyspan_state_t *ksp)
+{
+	int	rval = USB_SUCCESS;
+
+	sema_p(&ksp->ks_pipes_sema);
+	rval = keyspan_reopen_pipes(ksp);
+	sema_v(&ksp->ks_pipes_sema);
+
+	return (rval);
+}
+
+/*
+ * data transfer routines
+ * ----------------------
+ *
+ *
+ * start data transmit
+ */
+void
+keyspan_tx_start(keyspan_port_t *kp, int *xferd)
+{
+	keyspan_state_t	*ksp = kp->kp_ksp;
+	int		len;		/* # of bytes we can transmit */
+	mblk_t		*data;		/* data to be transmitted */
+	int		data_len;	/* # of bytes in 'data' */
+	int		rval;
+
+	ASSERT(!mutex_owned(&ksp->ks_mutex));
+	ASSERT(mutex_owned(&kp->kp_mutex));
+	ASSERT(kp->kp_state != KEYSPAN_PORT_CLOSED);
+
+	USB_DPRINTF_L4(DPRINT_OUT_PIPE, kp->kp_lh, "keyspan_tx_start");
+
+	if (xferd) {
+		*xferd = 0;
+	}
+	if ((kp->kp_flags & KEYSPAN_PORT_TX_STOPPED) ||
+	    (kp->kp_tx_mp == NULL)) {
+
+		return;
+	}
+	ASSERT(MBLKL(kp->kp_tx_mp) > 0);
+
+	len = min(msgdsize(kp->kp_tx_mp), kp->kp_write_len);
+	USB_DPRINTF_L4(DPRINT_OUT_PIPE, kp->kp_lh, "keyspan_tx_start:"
+	    "len = %d, tx_mp_len = %d", len, (int)msgdsize(kp->kp_tx_mp));
+	if (len == 0) {
+
+		return;
+	}
+
+	mutex_exit(&kp->kp_mutex);
+
+	/*
+	 * Some keyspan adapters, such as usa49wlc,
+	 * need use the first byte as flag.
+	 */
+	switch (ksp->ks_dev_spec.id_product) {
+	case KEYSPAN_USA19HS_PID:
+
+		if ((data = allocb(len, BPRI_LO)) == NULL) {
+			mutex_enter(&kp->kp_mutex);
+
+			return;
+		}
+		mutex_enter(&kp->kp_mutex);
+
+		break;
+
+#ifdef	KEYSPAN_USA49WLC
+	case KEYSPAN_USA49WLC_PID:
+		if (len == 64) {
+			if ((data = allocb(len, BPRI_LO)) == NULL) {
+				mutex_enter(&kp->kp_mutex);
+
+				return;
+			}
+			mutex_enter(&kp->kp_mutex);
+			len = 63;
+		} else {
+
+			if ((data = allocb(len + 1, BPRI_LO)) == NULL) {
+				mutex_enter(&kp->kp_mutex);
+
+				return;
+			}
+			mutex_enter(&kp->kp_mutex);
+		}
+		*(data->b_rptr) = 0; /* zero the first byte */
+		data->b_wptr++;
+
+		break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+	default:
+
+		mutex_enter(&kp->kp_mutex);
+		USB_DPRINTF_L2(DPRINT_OUT_PIPE, ksp->ks_lh, "keyspan_tx_start:"
+		    "the device's product id can't be recognized");
+
+		return;
+	}
+
+	/* copy at most 'len' bytes from mblk chain for transmission */
+	data_len = keyspan_tx_copy_data(kp, data, len);
+
+	if (data_len <= 0) {
+		USB_DPRINTF_L3(DPRINT_OUT_PIPE, kp->kp_lh, "keyspan_tx_start:"
+		    "keyspan_tx_copy_data copied zero bytes");
+		freeb(data);
+
+		return;
+	}
+	mutex_exit(&kp->kp_mutex);
+	rval = keyspan_send_data(&kp->kp_dataout_pipe, &data, kp);
+	mutex_enter(&kp->kp_mutex);
+
+	/*
+	 * if send failed, put data back
+	 */
+	if (rval != USB_SUCCESS) {
+		ASSERT(data);
+		keyspan_put_head(&kp->kp_tx_mp, data, kp);
+	} else if (xferd) {
+		*xferd = data_len;
+	}
+
+	USB_DPRINTF_L4(DPRINT_OUT_PIPE, kp->kp_lh, "keyspan_tx_start[%d]: over"
+	    "(%d) rval=%d", kp->kp_port_num, data_len, rval);
+
+}
+
+
+/*
+ * copy no more than 'len' bytes from mblk chain to transmit mblk 'data'.
+ * return number of bytes copied
+ */
+int
+keyspan_tx_copy_data(keyspan_port_t *kp, mblk_t *data, int len)
+{
+	mblk_t		*mp;	/* current msgblk */
+	int		copylen; /* # of bytes to copy from 'mp' to 'data' */
+	int		data_len = 0;
+
+	ASSERT(mutex_owned(&kp->kp_mutex));
+
+	while ((data_len < len) && kp->kp_tx_mp) {
+		mp = kp->kp_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) {
+			kp->kp_tx_mp = unlinkb(mp);
+			freeb(mp);
+		} else {
+			ASSERT(data_len == len);
+		}
+	}
+	USB_DPRINTF_L3(DPRINT_OUT_DATA, kp->kp_lh, "keyspan_tx_copy_data:"
+	    "copied data_len = %d", data_len);
+
+	return (data_len);
+}
+
+
+/*
+ * wait until local tx buffer drains.
+ * 'timeout' is in seconds, zero means wait forever
+ */
+static int
+keyspan_wait_tx_drain(keyspan_port_t *kp, int timeout)
+{
+	clock_t	until;
+	int	over = 0;
+
+	USB_DPRINTF_L4(DPRINT_OUT_DATA, kp->kp_lh, "keyspan_wait_tx_drain:"
+	    "timeout = %d", timeout);
+	until = ddi_get_lbolt() + drv_usectohz(1000000 * timeout);
+
+	while (kp->kp_tx_mp && !over) {
+		if (timeout > 0) {
+			over = (cv_timedwait_sig(&kp->kp_tx_cv,
+					&kp->kp_mutex, until) <= 0);
+		} else {
+			over = (cv_wait_sig(&kp->kp_tx_cv, &kp->kp_mutex) == 0);
+		}
+	}
+
+	return ((kp->kp_tx_mp == NULL) ? USB_SUCCESS : USB_FAILURE);
+}
+
+/*
+ * returns 0 if device is not online, != 0 otherwise
+ */
+int
+keyspan_dev_is_online(keyspan_state_t *ksp)
+{
+	int	rval;
+
+	mutex_enter(&ksp->ks_mutex);
+	rval = (ksp->ks_dev_state == USB_DEV_ONLINE);
+	mutex_exit(&ksp->ks_mutex);
+
+	return (rval);
+}
+
+/*
+ * link a message block to tail of message
+ * account for the case when message is null
+ */
+void
+keyspan_put_tail(mblk_t **mpp, mblk_t *bp)
+{
+	if (*mpp) {
+		linkb(*mpp, bp);
+	} else {
+		*mpp = bp;
+	}
+}
+
+/*
+ * put a message block at the head of the message
+ * account for the case when message is null
+ */
+void
+keyspan_put_head(mblk_t **mpp, mblk_t *bp, keyspan_port_t *kp)
+{
+	switch (kp->kp_ksp->ks_dev_spec.id_product) {
+	case KEYSPAN_USA19HS_PID:
+		if (*mpp) {
+			linkb(bp, *mpp);
+		}
+		*mpp = bp;
+
+		break;
+
+#ifdef	KEYSPAN_USA49WLC
+	case KEYSPAN_USA49WLC_PID:
+
+		/* get rid of the first byte of the msg data which is a flag */
+		if (*mpp) {
+			linkb(bp, *mpp);
+		}
+		bp->b_rptr = bp->b_datap->db_base + 1;
+		*mpp = bp;
+
+		break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+	default:
+		USB_DPRINTF_L2(DPRINT_OUT_DATA, kp->kp_lh, "keyspan_put_head:"
+		    "the device's product id can't be recognized");
+
+		return;
+	}
+
+}
+
+/*
+ * Set the port parameters to default values
+ */
+static void
+keyspan_default_port_params(keyspan_port_t *kp)
+{
+	keyspan_state_t	*ksp = kp->kp_ksp;
+
+	ASSERT(mutex_owned(&kp->kp_mutex));
+
+	switch (ksp->ks_dev_spec.id_product) {
+	case KEYSPAN_USA19HS_PID:
+		keyspan_default_port_params_usa19hs(kp);
+
+		break;
+
+#ifdef	KEYSPAN_USA49WLC
+	case KEYSPAN_USA49WLC_PID:
+		keyspan_default_port_params_usa49(kp);
+
+		break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+	default:
+		USB_DPRINTF_L2(DPRINT_ATTACH, ksp->ks_lh,
+		    "keyspan_default_port_params:"
+		    "the device's product id can't be recognized");
+	}
+
+	USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+	    "keyspan_default_port_params: setted.");
+}
+
+/*
+ * Build the command message according to the params from usbser.
+ * The message will then be sent to deivce by keyspan_send_cmd.
+ */
+static void
+keyspan_build_cmd_msg(keyspan_port_t *kp, ds_port_params_t *tp)
+{
+	keyspan_state_t	*ksp = kp->kp_ksp;
+
+	switch (ksp->ks_dev_spec.id_product) {
+	case KEYSPAN_USA19HS_PID:
+		keyspan_build_cmd_msg_usa19hs(kp, tp);
+
+		break;
+
+#ifdef	KEYSPAN_USA49WLC
+	case KEYSPAN_USA49WLC_PID:
+		keyspan_build_cmd_msg_usa49(kp, tp);
+
+		break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+	default:
+		USB_DPRINTF_L2(DPRINT_ATTACH, ksp->ks_lh,
+		    "keyspan_build_cmd_msg:"
+		    "the device's product id can't be recognized");
+	}
+}
+
+/* save the port params after send cmd successfully */
+static void
+keyspan_save_port_params(keyspan_port_t	*kp)
+{
+	keyspan_state_t	*ksp = kp->kp_ksp;
+
+	ASSERT(mutex_owned(&kp->kp_mutex));
+
+	switch (ksp->ks_dev_spec.id_product) {
+	case KEYSPAN_USA19HS_PID:
+		keyspan_save_port_params_usa19hs(kp);
+
+		break;
+
+#ifdef	KEYSPAN_USA49WLC
+	case KEYSPAN_USA49WLC_PID:
+		keyspan_save_port_params_usa49(kp);
+
+		break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+	default:
+		USB_DPRINTF_L2(DPRINT_ATTACH, ksp->ks_lh,
+		    "keyspan_save_port_params:"
+		    "the device's product id can't be recognized");
+	}
+
+	USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+	    "keyspan_save_port_params: baud = %x, lcr = %x,"
+	    "status_flag = %x", kp->kp_baud, kp->kp_lcr, kp->kp_status_flag);
+
+}
+
+/* save the port params after send cmd successfully */
+static void
+keyspan_save_port_params_usa19hs(keyspan_port_t	*kp)
+{
+	keyspan_usa19hs_port_ctrl_msg_t	*ctrl_msg = &(kp->kp_ctrl_msg.usa19hs);
+
+	ASSERT(mutex_owned(&kp->kp_mutex));
+
+	if (ctrl_msg->setClocking) {
+		kp->kp_baud = ctrl_msg->baudHi;
+		kp->kp_baud = (kp->kp_baud << 8);
+		kp->kp_baud |= ctrl_msg->baudLo;
+	}
+	if (ctrl_msg->setLcr) {
+		kp->kp_lcr = ctrl_msg->lcr;
+	}
+	if (ctrl_msg->setRts) {
+		if (ctrl_msg->rts) {
+			kp->kp_status_flag |= KEYSPAN_PORT_RTS;
+		} else {
+			kp->kp_status_flag &= ~KEYSPAN_PORT_RTS;
+		}
+	}
+	if (ctrl_msg->setDtr) {
+		if (ctrl_msg->dtr) {
+			kp->kp_status_flag |= KEYSPAN_PORT_DTR;
+		} else {
+			kp->kp_status_flag &= ~KEYSPAN_PORT_DTR;
+		}
+	}
+
+	if (ctrl_msg->portEnabled) {
+		kp->kp_status_flag |= KEYSPAN_PORT_ENABLE;
+	} else {
+		kp->kp_status_flag &= ~KEYSPAN_PORT_ENABLE;
+	}
+
+	USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+	    "keyspan_save_port_params: baud = %x, lcr = %x,"
+	    "status_flag = %x", kp->kp_baud, kp->kp_lcr, kp->kp_status_flag);
+
+}
+
+/*
+ * Set the port parameters to default values
+ */
+static void
+keyspan_default_port_params_usa19hs(keyspan_port_t *kp)
+{
+	keyspan_usa19hs_port_ctrl_msg_t	*ctrl_msg = &(kp->kp_ctrl_msg.usa19hs);
+	ASSERT(mutex_owned(&kp->kp_mutex));
+
+	keyspan_build_cmd_msg(kp, NULL);
+
+	ctrl_msg->setRts = 0x01;
+	ctrl_msg->rts = 0x1;
+	ctrl_msg->setDtr = 0x01;
+	ctrl_msg->dtr = 0x1;
+
+	ctrl_msg->setClocking = 1;
+	ctrl_msg->setRxMode = 1;
+	ctrl_msg->setTxMode = 1;
+
+	/* set baud rate to 9600 */
+	ctrl_msg->baudLo = keyspan_speedtab_usa19hs[13] & 0xff;
+	ctrl_msg->baudHi = (keyspan_speedtab_usa19hs[13] >> 8) & 0xff;
+	ctrl_msg->rxMode = RXMODE_BYHAND;
+	ctrl_msg->txMode = TXMODE_BYHAND;
+
+	ctrl_msg->lcr = 0x3;
+	ctrl_msg->setLcr = 0x1;
+
+	ctrl_msg->xonChar = CSTART;
+	ctrl_msg->xoffChar = CSTOP;
+	ctrl_msg->setTxFlowControl = 1;
+	ctrl_msg->txFlowControl = TXFLOW_CTS;
+	ctrl_msg->setRxFlowControl = 1;
+	ctrl_msg->rxFlowControl = RXFLOW_RTS;
+	ctrl_msg->rxFlush = 0;
+
+}
+
+/*
+ * Build the command message according to the params from usbser.
+ * The message will then be sent to deivce by keyspan_send_cmd.
+ */
+static void
+keyspan_build_cmd_msg_usa19hs(keyspan_port_t *kp, ds_port_params_t *tp)
+{
+	int		cnt, i;
+	uint_t		ui;
+	ds_port_param_entry_t *pe;
+	keyspan_usa19hs_port_ctrl_msg_t	*ctrl_msg = &(kp->kp_ctrl_msg.usa19hs);
+
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh,
+	    "keyspan_build_cmd_msg_usa19hs: tp = %p", (void *)tp);
+
+	ASSERT(mutex_owned(&kp->kp_mutex));
+	ASSERT(kp->kp_state == KEYSPAN_PORT_OPEN ||
+	    kp->kp_state == KEYSPAN_PORT_OPENING);
+
+	/* bzero all elements */
+	bzero(ctrl_msg, sizeof (keyspan_usa19hs_port_ctrl_msg_t));
+
+	/* it is usaually 16, according to Keyspan spec */
+	ctrl_msg->rxForwardingLength = 16;
+	/* from 1ms to 31ms, according to Keyspan spec. */
+	ctrl_msg->rxForwardingTimeout = 16;
+
+	ctrl_msg->portEnabled = 1;
+	ctrl_msg->returnStatus = 1;
+
+	if (tp == NULL) {
+
+		return;
+	}
+
+	cnt = tp->tp_cnt;
+	pe = tp->tp_entries;
+
+	/* translate tp parameters into cmd_msg elements */
+	for (i = 0; i < cnt; i++, pe++) {
+		switch (pe->param) {
+		case DS_PARAM_BAUD:
+			ui = pe->val.ui;
+
+			/*
+			 * if we don't support this speed,
+			 * then return failure.
+			 */
+			if ((ui >= NELEM(keyspan_speedtab_usa19hs)) ||
+			    ((ui > 0) && (keyspan_speedtab_usa19hs[ui] == 0))) {
+
+				USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+				    "keyspan_build_cmd_msg_usa19hs:"
+				    " bad baud %d", ui);
+
+				break;
+			}
+
+			/* if the same as the old rate, need not set the rate */
+			if (kp->kp_baud == keyspan_speedtab_usa19hs[ui]) {
+
+				USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+				    "keyspan_build_cmd_msg_usa19hs:"
+				    " same as old baud setting, baud = %d",
+				    keyspan_speed2baud[ui]);
+
+				break;
+			}
+			ctrl_msg->setClocking = 1; /* enable the setting */
+			ctrl_msg->setRxMode = 1;
+			ctrl_msg->setTxMode = 1;
+
+			ctrl_msg->baudLo = keyspan_speedtab_usa19hs[ui] & 0xff;
+			ctrl_msg->baudHi = (keyspan_speedtab_usa19hs[ui] >> 8)
+			    & 0xff;
+
+			ctrl_msg->rxMode = RXMODE_BYHAND;
+			ctrl_msg->txMode = TXMODE_BYHAND;
+
+			USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa19hs: baud=%d",
+			    keyspan_speed2baud[ui]);
+
+			break;
+		case DS_PARAM_PARITY:
+			if (pe->val.ui & PARENB) {
+
+				/*
+				 * Since USA_PARITY_NONE == 0, it's not
+				 * necessary to or it in here.
+				 */
+				if (pe->val.ui & PARODD) {
+					ctrl_msg->lcr |= USA_PARITY_ODD;
+				} else {
+					ctrl_msg->lcr |= USA_PARITY_EVEN;
+				}
+			}
+			USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa19hs: parity=%x,lcr = %x",
+			    pe->val.ui, ctrl_msg->lcr);
+
+			break;
+		case DS_PARAM_STOPB:
+			if (pe->val.ui & CSTOPB) {
+				ctrl_msg->lcr |= STOPBITS_678_2;
+			} else {
+
+				/*
+				 * STOPBITS_5678_1 equals zero,
+				 * so it's not necessary to or it in.
+				 */
+				USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+				    "keyspan_build_cmd_msg_usa19hs:"
+				    " STOPBITS_5678_1");
+			}
+
+			USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa19hs: stopb=%x, lcr = %x",
+			    pe->val.ui, ctrl_msg->lcr);
+
+			break;
+		case DS_PARAM_CHARSZ:
+			switch (pe->val.ui) {
+			case CS5:
+
+				/*
+				 * USA_DATABITS_5 equals zero,
+				 * not necessary to or it in.
+				 */
+				USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+				    "keyspan_build_cmd_msg_usa19hs:"
+				    " USA_DATABITS_5");
+
+				break;
+			case CS6:
+				ctrl_msg->lcr |= USA_DATABITS_6;
+
+				break;
+			case CS7:
+				ctrl_msg->lcr |= USA_DATABITS_7;
+
+				break;
+			case CS8:
+			default:
+				/*
+				 * The default value is USA_DATABITS_8. It is
+				 * safe to set to the default one here.
+				 */
+				ctrl_msg->lcr |= USA_DATABITS_8;
+
+				break;
+			}
+
+			USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa19hs: cs=%x, lcr = %x",
+			    pe->val.ui, ctrl_msg->lcr);
+
+			break;
+		case DS_PARAM_XON_XOFF:
+			ctrl_msg->xonChar = pe->val.uc[0]; /* init to CSTART */
+			ctrl_msg->xoffChar = pe->val.uc[1]; /* init to CSTOP */
+
+			USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa19hs: xonChar=%x, "
+			    "xoffChar = %x", ctrl_msg->xonChar,
+			    ctrl_msg->xoffChar);
+
+			break;
+		case DS_PARAM_FLOW_CTL:
+			if (pe->val.ui & CTSXON) {
+				ctrl_msg->txFlowControl = TXFLOW_CTS;
+				ctrl_msg->setTxFlowControl = 1;
+			} else {
+				/* Clear the tx flow control setting */
+				ctrl_msg->txFlowControl = 0;
+				ctrl_msg->setTxFlowControl = 1;
+			}
+			if (pe->val.ui & RTSXOFF) {
+				ctrl_msg->rxFlowControl = RXFLOW_RTS;
+				ctrl_msg->setRxFlowControl = 1;
+			} else {
+				/* Clear the rx flow control setting */
+				ctrl_msg->rxFlowControl = 0;
+				ctrl_msg->setRxFlowControl = 1;
+			}
+
+			USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa19hs: txFlowControl = %x,"
+			    "rxFlowControl = %x", ctrl_msg->txFlowControl,
+			    ctrl_msg->rxFlowControl);
+
+			break;
+		default:
+			USB_DPRINTF_L2(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa19hs: bad param %d",
+			    pe->param);
+
+			break;
+		}
+
+	}
+
+	/*
+	 * Enable the lcr settings only if they are different
+	 * with the existing settings.
+	 */
+	ctrl_msg->setLcr =  (ctrl_msg->lcr == kp->kp_lcr) ? 0 : 1;
+
+}
+
+#ifdef	KEYSPAN_USA49WLC
+/*
+ * Build the command message according to the params from usbser.
+ * The message will then be sent to deivce by keyspan_send_cmd.
+ */
+static void
+keyspan_build_cmd_msg_usa49(keyspan_port_t *kp, ds_port_params_t *tp)
+{
+	int		cnt, i;
+	uint_t		ui;
+	ds_port_param_entry_t *pe;
+	keyspan_usa49_port_ctrl_msg_t	*ctrl_msg = &(kp->kp_ctrl_msg.usa49);
+
+	USB_DPRINTF_L4(DPRINT_CTLOP, kp->kp_lh,
+	    "keyspan_build_cmd_msg_usa49: tp = %p", (void *)tp);
+
+	ASSERT(mutex_owned(&kp->kp_mutex));
+	ASSERT(kp->kp_state == KEYSPAN_PORT_OPEN ||
+	    kp->kp_state == KEYSPAN_PORT_OPENING);
+
+	/* bzero all elements */
+	bzero(ctrl_msg, sizeof (keyspan_usa49_port_ctrl_msg_t));
+
+	ctrl_msg->portNumber = kp->kp_port_num;
+
+	/* it is usaually 16, according to Keyspan spec */
+	ctrl_msg->forwardingLength = 16;
+
+	ctrl_msg->enablePort = 1;
+	ctrl_msg->returnStatus = 1;
+
+	if (tp == NULL) {
+
+		return;
+	}
+
+	cnt = tp->tp_cnt;
+	pe = tp->tp_entries;
+
+	/* translate tp parameters into cmd_msg elements */
+	for (i = 0; i < cnt; i++, pe++) {
+		switch (pe->param) {
+		case DS_PARAM_BAUD:
+			ui = pe->val.ui;
+
+			/*
+			 * If we don't support this speed,
+			 * then return failure.
+			 */
+			if ((ui >= NELEM(keyspan_speedtab_usa49)) ||
+			    ((ui > 0) && (keyspan_speedtab_usa49[ui] == 0))) {
+
+				USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+				    "keyspan_build_cmd_msg_usa49:"
+				    " bad baud %d", ui);
+
+				break;
+			}
+
+			/* if the same as the old rate, need not set the rate */
+			if (kp->kp_baud == keyspan_speedtab_usa49[ui]) {
+
+				USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+					"keyspan_build_cmd_msg_usa49: "
+					"same as old baud setting, baud = %d",
+					keyspan_speed2baud[ui]);
+
+				break;
+			}
+			ctrl_msg->setClocking = 0xff; /* enable the setting */
+			ctrl_msg->baudLo = keyspan_speedtab_usa49[ui] & 0xff;
+			ctrl_msg->baudHi = (keyspan_speedtab_usa49[ui] >> 8)
+			    & 0xff;
+			ctrl_msg->prescaler = keyspan_prescaler_49wlc[ui];
+
+			USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa49: baud=%d",
+			    keyspan_speed2baud[ui]);
+
+			break;
+		case DS_PARAM_PARITY:
+			if (pe->val.ui & PARENB) {
+
+				/*
+				 * Since USA_PARITY_NONE == 0,
+				 * it's not necessary to or it in here.
+				 */
+				if (pe->val.ui & PARODD) {
+					ctrl_msg->lcr |= USA_PARITY_ODD;
+				} else {
+					ctrl_msg->lcr |= USA_PARITY_EVEN;
+				}
+			}
+			USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa49: parity=%x, lcr = %x",
+			    pe->val.ui, ctrl_msg->lcr);
+
+			break;
+		case DS_PARAM_STOPB:
+			if (pe->val.ui & CSTOPB) {
+				ctrl_msg->lcr |= STOPBITS_678_2;
+			} else {
+
+				/*
+				 * STOPBITS_5678_1 equals zero,
+				 * not necessary to or it in.
+				 */
+				USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+				    "keyspan_build_cmd_msg_usa49: "
+				    "STOPBITS_5678_1");
+			}
+
+			USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa49: stopb=%x, lcr = %x",
+			    pe->val.ui, ctrl_msg->lcr);
+
+			break;
+		case DS_PARAM_CHARSZ:
+			switch (pe->val.ui) {
+			case CS5:
+
+				/*
+				 * USA_DATABITS_5 equals zero,
+				 * not necessary to or it in.
+				 */
+				USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+				    "keyspan_build_cmd_msg_usa49:"
+				    " USA_DATABITS_5");
+
+				break;
+			case CS6:
+				ctrl_msg->lcr |= USA_DATABITS_6;
+
+				break;
+			case CS7:
+				ctrl_msg->lcr |= USA_DATABITS_7;
+
+				break;
+			case CS8:
+			default:
+				ctrl_msg->lcr |= USA_DATABITS_8;
+
+				break;
+			}
+
+			USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa49: cs=%x, lcr = %x",
+			    pe->val.ui, ctrl_msg->lcr);
+
+			break;
+		case DS_PARAM_XON_XOFF:
+			ctrl_msg->xonChar = pe->val.uc[0]; /* init to CSTART */
+			ctrl_msg->xoffChar = pe->val.uc[1]; /* init to CSTOP */
+
+			USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa49: xonChar=%x, "
+			    "xoffChar = %x", ctrl_msg->xonChar,
+			    ctrl_msg->xoffChar);
+
+			break;
+		case DS_PARAM_FLOW_CTL:
+			if (pe->val.ui & CTSXON) {
+				ctrl_msg->ctsFlowControl = 1;
+				ctrl_msg->setFlowControl = 1;
+			} else {
+				ctrl_msg->ctsFlowControl = 0;
+				ctrl_msg->setFlowControl = 1;
+			}
+			if (pe->val.ui & RTSXOFF) {
+				USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+				    "keyspan_build_cmd_msg_usa49: "
+				    "pe->val.ui = %x, flow_ctl: RTSXOFF, "
+				    "no hardware support", pe->val.ui);
+			}
+
+			USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa49: ctsFlowControl = %x,"
+			    "dsrFlowControl = %x", ctrl_msg->ctsFlowControl,
+			    ctrl_msg->dsrFlowControl);
+
+			break;
+		default:
+			USB_DPRINTF_L2(DPRINT_CTLOP, kp->kp_lh,
+			    "keyspan_build_cmd_msg_usa49: bad param %d",
+			    pe->param);
+
+			break;
+		}
+	}
+
+	/*
+	 * enable the lcr settings only if they are different
+	 * with the existing settings.
+	 */
+	ctrl_msg->setLcr =  (ctrl_msg->lcr == kp->kp_lcr) ? 0 : 1;
+
+}
+
+
+/*
+ * Set the port parameters to default values
+ */
+static void
+keyspan_default_port_params_usa49(keyspan_port_t *kp)
+{
+	keyspan_usa49_port_ctrl_msg_t	*ctrl_msg = &(kp->kp_ctrl_msg.usa49);
+	ASSERT(mutex_owned(&kp->kp_mutex));
+
+	keyspan_build_cmd_msg(kp, NULL);
+
+	ctrl_msg->setRts = 1;
+	ctrl_msg->rts = 1;
+	ctrl_msg->setDtr = 1;
+	ctrl_msg->dtr = 1;
+
+	ctrl_msg->_txOn = 1;
+	ctrl_msg->_txOff = 0;
+	ctrl_msg->txFlush = 0;
+	ctrl_msg->txBreak = 0;
+	ctrl_msg->rxOn = 1;
+	ctrl_msg->rxOff = 0;
+	ctrl_msg->rxFlush = 0;
+	ctrl_msg->rxForward = 0;
+	ctrl_msg->returnStatus = 1;
+	ctrl_msg->resetDataToggle = 0;
+	ctrl_msg->enablePort = 1;
+	ctrl_msg->disablePort = 0;
+
+	/* set baud rate to 9600 */
+	ctrl_msg->setClocking = 1;
+	ctrl_msg->baudLo = keyspan_speedtab_usa49[13] & 0xff;
+	ctrl_msg->baudHi = (keyspan_speedtab_usa49[13] >> 8) & 0xff;
+	ctrl_msg->prescaler = keyspan_prescaler_49wlc[13];
+
+	ctrl_msg->lcr = 0x3;
+	ctrl_msg->setLcr = 1;
+
+	ctrl_msg->xonChar = CSTART;
+	ctrl_msg->xoffChar = CSTOP;
+	ctrl_msg->ctsFlowControl = 1;
+	ctrl_msg->setFlowControl = 1;
+
+}
+
+
+/* save the port params after send cmd successfully */
+static void
+keyspan_save_port_params_usa49(keyspan_port_t	*kp)
+{
+	keyspan_usa49_port_ctrl_msg_t	*ctrl_msg = &(kp->kp_ctrl_msg.usa49);
+
+	ASSERT(mutex_owned(&kp->kp_mutex));
+
+	if (ctrl_msg->setClocking) {
+		kp->kp_baud = ctrl_msg->baudHi;
+		kp->kp_baud = (kp->kp_baud << 8);
+		kp->kp_baud |= ctrl_msg->baudLo;
+	}
+	if (ctrl_msg->setLcr) {
+		kp->kp_lcr = ctrl_msg->lcr;
+	}
+	if (ctrl_msg->setRts) {
+		if (ctrl_msg->rts) {
+			kp->kp_status_flag |= KEYSPAN_PORT_RTS;
+		} else {
+			kp->kp_status_flag &= ~KEYSPAN_PORT_RTS;
+		}
+	}
+	if (ctrl_msg->setDtr) {
+		if (ctrl_msg->dtr) {
+			kp->kp_status_flag |= KEYSPAN_PORT_DTR;
+		} else {
+			kp->kp_status_flag &= ~KEYSPAN_PORT_DTR;
+		}
+	}
+
+	if (ctrl_msg->enablePort) {
+		kp->kp_status_flag |= KEYSPAN_PORT_ENABLE;
+	} else {
+		kp->kp_status_flag &= ~KEYSPAN_PORT_ENABLE;
+	}
+
+	/*
+	 * There are no flags in status msg (49wlc) can indicate the
+	 * break status, so we make use of ctrl_msg->txBreak here.
+	 */
+	if (ctrl_msg->txBreak) {
+		kp->kp_status_flag |= KEYSPAN_PORT_TXBREAK;
+	} else {
+		kp->kp_status_flag &= ~KEYSPAN_PORT_TXBREAK;
+	}
+
+	USB_DPRINTF_L3(DPRINT_CTLOP, kp->kp_lh,
+	    "keyspan_save_port_params: baud = %x, lcr = %x,"
+	    "status_flag = %x", kp->kp_baud, kp->kp_lcr, kp->kp_status_flag);
+
+}
+#endif	/* If KEYSPAN_USA49WLC defined */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/clients/usbser/usbser_keyspan/keyspan_pipe.c	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,1163 @@
+/*
+ * 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.
+ *
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ *
+ * keyspanport pipe routines (mostly device-neutral)
+ *
+ */
+#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/ddi.h>
+#include <sys/sunddi.h>
+
+#include <sys/usb/usba.h>
+#include <sys/usb/clients/usbser/usbser_keyspan/keyspan_var.h>
+#include <sys/usb/clients/usbser/usbser_keyspan/keyspan_pipe.h>
+
+/*
+ * initialize pipe structure with the given parameters
+ */
+static void
+keyspan_init_one_pipe(keyspan_state_t *ksp, keyspan_port_t *kp,
+    keyspan_pipe_t *pipe)
+{
+	usb_pipe_policy_t	*policy;
+
+	USB_DPRINTF_L4(DPRINT_OPEN, ksp->ks_lh, "keyspan_init_one_pipe: "
+	    "pipe = %p, pipe_stat %x", (void *)pipe, pipe->pipe_state);
+
+	/* init sync primitives */
+	mutex_init(&pipe->pipe_mutex, NULL, MUTEX_DRIVER, (void *)NULL);
+
+	/* init pipe policy */
+	policy = &pipe->pipe_policy;
+	policy->pp_max_async_reqs = 2;
+
+	pipe->pipe_ksp = ksp;
+	if (kp == NULL) {
+		/* globle pipes should have device log handle */
+		pipe->pipe_lh = ksp->ks_lh;
+	} else {
+		/* port pipes should have port log handle */
+		pipe->pipe_lh = kp->kp_lh;
+	}
+
+	pipe->pipe_state = KEYSPAN_PIPE_CLOSED;
+}
+
+
+static void
+keyspan_fini_one_pipe(keyspan_pipe_t *pipe)
+{
+	USB_DPRINTF_L4(DPRINT_OPEN, pipe->pipe_ksp->ks_lh,
+	    "keyspan_fini_one_pipe: pipe_stat %x", pipe->pipe_state);
+
+	if (pipe->pipe_state != KEYSPAN_PIPE_NOT_INIT) {
+		mutex_destroy(&pipe->pipe_mutex);
+		pipe->pipe_state = KEYSPAN_PIPE_NOT_INIT;
+	}
+}
+
+/*
+ * Lookup the endpoints defined in the spec;
+ * Allocate resources, initialize pipe structures.
+ * All are bulk pipes, including data in/out, cmd/status pipes.
+ */
+int
+keyspan_init_pipes(keyspan_state_t *ksp)
+{
+	usb_client_dev_data_t *dev_data = ksp->ks_dev_data;
+	int		ifc, alt, i, j, k = 0;
+	uint8_t		port_cnt = ksp->ks_dev_spec.port_cnt;
+	uint8_t		ep_addr, ep_cnt;
+	usb_ep_data_t	*dataout[KEYSPAN_MAX_PORT_NUM],
+			*datain[KEYSPAN_MAX_PORT_NUM],
+			*status = NULL, *ctrl = NULL, *tmp_ep;
+	usb_alt_if_data_t *alt_data;
+	usb_if_data_t *if_data;
+
+
+	ifc = dev_data->dev_curr_if;
+	alt = 0;
+	if_data = &dev_data->dev_curr_cfg->cfg_if[ifc];
+	alt_data = &if_data->if_alt[alt];
+
+	/*
+	 * The actual EP number (indicated by bNumEndpoints) is more than
+	 * those defined in spec. We have to match those we need according
+	 * to EP addresses. And we'll lookup In EPs and Out EPs separately.
+	 */
+	ep_cnt = (alt_data->altif_descr.bNumEndpoints + 1) / 2;
+
+	/*
+	 * get DIR_IN EP descriptors, and then match with EP addresses.
+	 * Different keyspan devices may has different EP addresses.
+	 */
+	for (i = 0; i < ep_cnt; i++) {
+		tmp_ep = usb_lookup_ep_data(ksp->ks_dip, dev_data, ifc, alt, i,
+			    USB_EP_ATTR_BULK, USB_EP_DIR_IN);
+		if (tmp_ep == NULL) {
+			USB_DPRINTF_L3(DPRINT_ATTACH, ksp->ks_lh,
+			    "keyspan_init_pipes: can't find bulk in ep, i=%d,"
+			    "ep_cnt=%d", i, ep_cnt);
+
+			continue;
+		}
+		ep_addr = tmp_ep->ep_descr.bEndpointAddress;
+
+		USB_DPRINTF_L3(DPRINT_ATTACH, ksp->ks_lh, "keyspan_init_pipes: "
+		    "ep_addr =%x, stat_ep_addr=%x, i=%d", ep_addr,
+		    ksp->ks_dev_spec.stat_ep_addr, i);
+
+		/* match the status EP */
+		if (ep_addr == ksp->ks_dev_spec.stat_ep_addr) {
+			status = tmp_ep;
+
+			continue;
+		}
+
+		/* match the EPs of the ports */
+		for (j = 0; j < port_cnt; j++) {
+			USB_DPRINTF_L3(DPRINT_ATTACH, ksp->ks_lh,
+			    "keyspan_init_pipes: try to match bulk in data ep,"
+			    " j=%d", j);
+			if (ep_addr == ksp->ks_dev_spec.datain_ep_addr[j]) {
+				datain[j] = tmp_ep;
+				k++;
+				USB_DPRINTF_L3(DPRINT_ATTACH, ksp->ks_lh,
+				    "keyspan_init_pipes: matched a bulk in"
+				    " data ep");
+
+				break;
+			}
+		}
+
+		/* if have matched all the necessary endpoints, break out */
+		if (k >= port_cnt && status != NULL) {
+
+			break;
+		}
+
+		USB_DPRINTF_L4(DPRINT_ATTACH, ksp->ks_lh, "keyspan_init_pipes: "
+		    "try to match bulk in data ep, j=%d", j);
+
+		if (j == port_cnt) {
+			/* this ep can't be matched by any addr */
+			USB_DPRINTF_L4(DPRINT_ATTACH, ksp->ks_lh,
+			    "keyspan_init_pipes: can't match bulk in ep,"
+			    " addr =%x,", ep_addr);
+		}
+	}
+
+	if (k != port_cnt || status == NULL) {
+
+		/* Some of the necessary IN endpoints are not matched */
+		USB_DPRINTF_L2(DPRINT_ATTACH, ksp->ks_lh,
+		    "keyspan_init_pipes: matched %d data in endpoints,"
+		    " not enough", k);
+
+		return (USB_FAILURE);
+	}
+
+	k = 0;
+
+	/*
+	 * get DIR_OUT EP descriptors, and then match with ep addrs.
+	 * different keyspan devices may has different ep addresses.
+	 */
+	for (i = 0; i < ep_cnt; i++) {
+		tmp_ep = usb_lookup_ep_data(ksp->ks_dip, dev_data, ifc, alt, i,
+			    USB_EP_ATTR_BULK, USB_EP_DIR_OUT);
+		if (tmp_ep == NULL) {
+			USB_DPRINTF_L3(DPRINT_ATTACH, ksp->ks_lh,
+			    "keyspan_init_pipes: can't find bulk out ep, i=%d,"
+			    "ep_cnt=%d", i, ep_cnt);
+
+			continue;
+		}
+		ep_addr = tmp_ep->ep_descr.bEndpointAddress;
+
+		/* match the status ep */
+		if (ep_addr == ksp->ks_dev_spec.ctrl_ep_addr) {
+			ctrl = tmp_ep;
+
+			continue;
+		}
+
+		/* match the ep of the ports */
+		for (j = 0; j < port_cnt; j++) {
+			if (ep_addr == ksp->ks_dev_spec.dataout_ep_addr[j]) {
+				dataout[j] = tmp_ep;
+				k++;
+
+				break;
+			}
+		}
+		/* if have matched all the necessary endpoints, break out */
+		if (k >= port_cnt && ctrl != NULL) {
+
+			break;
+		}
+
+		if (j == port_cnt) {
+
+			/* this ep can't be matched by any addr */
+			USB_DPRINTF_L4(DPRINT_ATTACH, ksp->ks_lh,
+			    "keyspan_init_pipes: can't match bulk out ep,"
+			    " ep_addr =%x", ep_addr);
+
+		}
+	}
+
+	if (k != port_cnt || ctrl == NULL) {
+		/* Not all the necessary OUT endpoints are matched */
+		USB_DPRINTF_L2(DPRINT_ATTACH, ksp->ks_lh,
+		    "keyspan_init_pipes: matched %d data in endpoints,"
+		    " not enough", k);
+
+		return (USB_FAILURE);
+	}
+
+	mutex_enter(&ksp->ks_mutex);
+
+	/*
+	 * Device globle pipes: a bulk in pipe for status and a bulk out
+	 * pipe for controle cmd.
+	 */
+	ksp->ks_statin_pipe.pipe_ep_descr = status->ep_descr;
+	keyspan_init_one_pipe(ksp, NULL, &ksp->ks_statin_pipe);
+
+	ksp->ks_ctrlout_pipe.pipe_ep_descr = ctrl->ep_descr;
+	keyspan_init_one_pipe(ksp, NULL, &ksp->ks_ctrlout_pipe);
+
+	/* for data in/out pipes of each port */
+	for (i = 0; i < port_cnt; i++) {
+
+		ksp->ks_ports[i].kp_datain_pipe.pipe_ep_descr =
+		    datain[i]->ep_descr;
+		keyspan_init_one_pipe(ksp, &ksp->ks_ports[i],
+		    &ksp->ks_ports[i].kp_datain_pipe);
+
+		ksp->ks_ports[i].kp_dataout_pipe.pipe_ep_descr =
+		    dataout[i]->ep_descr;
+		keyspan_init_one_pipe(ksp, &ksp->ks_ports[i],
+		    &ksp->ks_ports[i].kp_dataout_pipe);
+	}
+
+	mutex_exit(&ksp->ks_mutex);
+
+	return (USB_SUCCESS);
+}
+
+void
+keyspan_fini_pipes(keyspan_state_t *ksp)
+{
+	keyspan_port_t	*kp;
+	int		i;
+
+	for (i = 0; i < ksp->ks_dev_spec.port_cnt; i++) {
+		kp = &ksp->ks_ports[i];
+		keyspan_fini_one_pipe(&kp->kp_datain_pipe);
+		keyspan_fini_one_pipe(&kp->kp_dataout_pipe);
+	}
+
+	/* fini global pipes */
+	keyspan_fini_one_pipe(&ksp->ks_statin_pipe);
+	keyspan_fini_one_pipe(&ksp->ks_ctrlout_pipe);
+}
+
+
+static int
+keyspan_open_one_pipe(keyspan_state_t *ksp, keyspan_pipe_t *pipe)
+{
+	int	rval;
+
+	/* don't open for the second time */
+	mutex_enter(&pipe->pipe_mutex);
+	ASSERT(pipe->pipe_state != KEYSPAN_PIPE_NOT_INIT);
+	if (pipe->pipe_state != KEYSPAN_PIPE_CLOSED) {
+		mutex_exit(&pipe->pipe_mutex);
+
+		return (USB_SUCCESS);
+	}
+	mutex_exit(&pipe->pipe_mutex);
+
+	rval = usb_pipe_open(ksp->ks_dip, &pipe->pipe_ep_descr,
+	    &pipe->pipe_policy, USB_FLAGS_SLEEP, &pipe->pipe_handle);
+
+	if (rval == USB_SUCCESS) {
+		mutex_enter(&pipe->pipe_mutex);
+		pipe->pipe_state = KEYSPAN_PIPE_OPEN;
+		mutex_exit(&pipe->pipe_mutex);
+	}
+
+	return (rval);
+}
+
+
+/*
+ * close one pipe if open
+ */
+static void
+keyspan_close_one_pipe(keyspan_pipe_t *pipe)
+{
+	/*
+	 * pipe may already be closed, e.g. if device has been physically
+	 * disconnected and the driver immediately detached
+	 */
+	if (pipe->pipe_handle != NULL) {
+		usb_pipe_close(pipe->pipe_ksp->ks_dip, pipe->pipe_handle,
+				USB_FLAGS_SLEEP, NULL, NULL);
+		mutex_enter(&pipe->pipe_mutex);
+		pipe->pipe_handle = NULL;
+		pipe->pipe_state = KEYSPAN_PIPE_CLOSED;
+		mutex_exit(&pipe->pipe_mutex);
+	}
+}
+
+/*
+ * Open global pipes, a status pipe and a control pipe
+ */
+int
+keyspan_open_dev_pipes(keyspan_state_t *ksp)
+{
+	int		rval;
+
+	USB_DPRINTF_L4(DPRINT_OPEN, ksp->ks_lh, "keyspan_open_dev_pipes");
+
+	rval = keyspan_open_one_pipe(ksp, &ksp->ks_ctrlout_pipe);
+	if (rval != USB_SUCCESS) {
+		USB_DPRINTF_L2(DPRINT_OPEN, ksp->ks_lh,
+		    "keyspan_open_dev_pipes: open ctrl pipe failed %d", rval);
+
+		return (rval);
+	}
+
+	rval = keyspan_open_one_pipe(ksp, &ksp->ks_statin_pipe);
+	if (rval != USB_SUCCESS) {
+		USB_DPRINTF_L2(DPRINT_OPEN, ksp->ks_lh,
+		    "keyspan_open_dev_pipes: open status pipe failed %d", rval);
+
+		/* close the first opened pipe here */
+		keyspan_close_one_pipe(&ksp->ks_ctrlout_pipe);
+
+		return (rval);
+	}
+
+	/* start receive device status */
+	rval = keyspan_receive_status(ksp);
+	if (rval != USB_SUCCESS) {
+		USB_DPRINTF_L2(DPRINT_OPEN, ksp->ks_lh,
+		    "keyspan_open_dev_pipes: receive device status failed %d",
+		    rval);
+
+		/* close opened pipes here */
+		keyspan_close_one_pipe(&ksp->ks_statin_pipe);
+		keyspan_close_one_pipe(&ksp->ks_ctrlout_pipe);
+
+		return (rval);
+	}
+
+	return (rval);
+}
+
+
+/*
+ * Reopen all pipes if the port had them open
+ */
+int
+keyspan_reopen_pipes(keyspan_state_t *ksp)
+{
+	keyspan_port_t	*kp;
+	int		i;
+
+	USB_DPRINTF_L4(DPRINT_OPEN, ksp->ks_lh, "keyspan_reopen_pipes");
+
+	if (keyspan_open_dev_pipes(ksp) != USB_SUCCESS) {
+
+		return (USB_FAILURE);
+	}
+
+	for (i = 0; i < ksp->ks_dev_spec.port_cnt; i++) {
+		kp = &ksp->ks_ports[i];
+		mutex_enter(&kp->kp_mutex);
+		if (kp->kp_state == KEYSPAN_PORT_OPEN) {
+			USB_DPRINTF_L4(DPRINT_OPEN, ksp->ks_lh,
+			    "keyspan_reopen_pipes() reopen pipe #%d", i);
+			mutex_exit(&kp->kp_mutex);
+			if (keyspan_open_port_pipes(kp) != USB_SUCCESS) {
+
+				return (USB_FAILURE);
+			}
+			mutex_enter(&kp->kp_mutex);
+			kp->kp_no_more_reads = B_FALSE;
+		}
+		mutex_exit(&kp->kp_mutex);
+	}
+
+	return (USB_SUCCESS);
+}
+
+void
+keyspan_close_port_pipes(keyspan_port_t *kp)
+{
+	USB_DPRINTF_L4(DPRINT_CLOSE, kp->kp_lh, "keyspan_close_port_pipes");
+
+	keyspan_close_one_pipe(&kp->kp_dataout_pipe);
+	keyspan_close_one_pipe(&kp->kp_datain_pipe);
+}
+
+/*
+ * Close IN and OUT bulk pipes of all ports
+ */
+void
+keyspan_close_open_pipes(keyspan_state_t *ksp)
+{
+	keyspan_port_t	*kp;
+	int		i;
+
+	USB_DPRINTF_L4(DPRINT_CLOSE, ksp->ks_lh, "keyspan_close_open_pipes");
+
+	for (i = 0; i < ksp->ks_dev_spec.port_cnt; i++) {
+		kp = &ksp->ks_ports[i];
+		mutex_enter(&kp->kp_mutex);
+		if (kp->kp_state == KEYSPAN_PORT_OPEN) {
+			kp->kp_no_more_reads = B_TRUE;
+			mutex_exit(&kp->kp_mutex);
+			usb_pipe_reset(ksp->ks_dip,
+			    kp->kp_datain_pipe.pipe_handle, USB_FLAGS_SLEEP,
+			    NULL, NULL);
+			keyspan_close_port_pipes(kp);
+		} else {
+			mutex_exit(&kp->kp_mutex);
+		}
+	}
+}
+
+
+/*
+ * Close global pipes
+ */
+void
+keyspan_close_dev_pipes(keyspan_state_t *ksp)
+{
+	USB_DPRINTF_L4(DPRINT_CLOSE, ksp->ks_lh, "keyspan_close_dev_pipes");
+
+	keyspan_close_one_pipe(&ksp->ks_statin_pipe);
+	keyspan_close_one_pipe(&ksp->ks_ctrlout_pipe);
+}
+
+
+/*
+ * Open bulk data IN and data OUT pipes for one port.
+ * The status and control pipes are opened in attach because they are global.
+ */
+int
+keyspan_open_port_pipes(keyspan_port_t *kp)
+{
+	keyspan_state_t	*ksp = kp->kp_ksp;
+	int		rval;
+
+	USB_DPRINTF_L4(DPRINT_OPEN, kp->kp_lh, "keyspan_open_port_pipes");
+
+	rval = keyspan_open_one_pipe(ksp, &kp->kp_datain_pipe);
+	if (rval != USB_SUCCESS) {
+
+		goto fail;
+	}
+
+	rval = keyspan_open_one_pipe(ksp, &kp->kp_dataout_pipe);
+	if (rval != USB_SUCCESS) {
+
+		goto fail;
+	}
+
+	return (rval);
+
+fail:
+	USB_DPRINTF_L2(DPRINT_OPEN, kp->kp_lh,
+	    "keyspan_open_port_pipes: failed %d", rval);
+	keyspan_close_port_pipes(kp);
+
+	return (rval);
+}
+
+void
+keyspan_close_pipes(keyspan_state_t *ksp)
+{
+	USB_DPRINTF_L4(DPRINT_OPEN, ksp->ks_lh, "keyspan_close_pipes");
+
+	/* close all ports' pipes first, and then device ctrl/status pipes. */
+	keyspan_close_open_pipes(ksp);
+	keyspan_close_dev_pipes(ksp);
+
+}
+
+
+/*
+ * bulk out common callback
+ */
+/*ARGSUSED*/
+void
+keyspan_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
+{
+	keyspan_port_t	*kp = (keyspan_port_t *)req->bulk_client_private;
+	keyspan_pipe_t	*bulkout = &kp->kp_dataout_pipe;
+	mblk_t		*data = req->bulk_data;
+	int		data_len;
+
+	data_len = (data) ? MBLKL(data) : 0;
+
+	USB_DPRINTF_L4(DPRINT_OUT_PIPE, bulkout->pipe_lh,
+	    "keyspan_bulkout_cb: len=%d cr=%d cb_flags=%x",
+	    data_len, req->bulk_completion_reason, req->bulk_cb_flags);
+
+	if (req->bulk_completion_reason && (data_len > 0)) {
+
+		/*
+		 * Data wasn't transfered successfully.
+		 * Put data back on the queue.
+		 */
+		keyspan_put_head(&kp->kp_tx_mp, data, kp);
+
+		/* don't release mem in usb_free_bulk_req */
+		req->bulk_data = NULL;
+	}
+
+	usb_free_bulk_req(req);
+
+	/* if more data available, kick off another transmit */
+	mutex_enter(&kp->kp_mutex);
+	if (kp->kp_tx_mp == NULL) {
+
+			/* no more data, notify waiters */
+			cv_broadcast(&kp->kp_tx_cv);
+			mutex_exit(&kp->kp_mutex);
+
+			/* tx callback for this port */
+			kp->kp_cb.cb_tx(kp->kp_cb.cb_arg);
+	} else {
+		keyspan_tx_start(kp, NULL);
+		mutex_exit(&kp->kp_mutex);
+	}
+}
+
+/*
+ * pipe callbacks
+ * --------------
+ *
+ * bulk in common callback for usa19hs model
+ */
+/*ARGSUSED*/
+int
+keyspan_bulkin_cb_usa19hs(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
+{
+	keyspan_port_t	*kp = (keyspan_port_t *)req->bulk_client_private;
+	keyspan_pipe_t	*bulkin = &kp->kp_datain_pipe;
+	mblk_t		*data = req->bulk_data;
+	uint_t		cr = req->bulk_completion_reason;
+	int		data_len;
+
+	ASSERT(mutex_owned(&kp->kp_mutex));
+
+	data_len = (data) ? MBLKL(data) : 0;
+
+	USB_DPRINTF_L4(DPRINT_IN_PIPE, bulkin->pipe_lh,
+	    "keyspan_bulkin_cb_usa19hs: len=%d"
+	    " cr=%d flags=%x baud=%x",
+	    data_len, cr, req->bulk_cb_flags, kp->kp_baud);
+
+	/* put data on the read queue */
+	if ((data_len > 0) && (kp->kp_state != KEYSPAN_PORT_CLOSED) &&
+	    (cr == USB_CR_OK)) {
+
+		/*
+		 * According to Keyspan spec, if 0x80 bit set, the data
+		 * buf contains alternate status and data bytes;
+		 * if 0x80 bit is clear, then there are no status bytes,
+		 * so we put tail to send up data.
+		 */
+		if ((data->b_rptr[0] & 0x80) == 0) {
+			USB_DPRINTF_L4(DPRINT_IN_PIPE, bulkin->pipe_lh,
+			    "keyspan_bulkin_cb_usa19hs: len=%d",
+			    data_len);
+
+			data->b_rptr++;
+			data_len--;
+			if (data_len > 0) {
+				keyspan_put_tail(&kp->kp_rx_mp, data);
+
+				/*
+				 * the data will not be freed and
+				 * will be sent up later.
+				 */
+				req->bulk_data = NULL;
+			}
+		} else {  /* there might be some errs in the data */
+
+			USB_DPRINTF_L4(DPRINT_IN_PIPE, bulkin->pipe_lh,
+			    "keyspan_bulkin_cb_usa19hs:"
+			    " err in the data, len=%d",
+			    data_len);
+
+			if (data_len > 1) {
+				int i = 0;
+				int j = 1;
+
+				/* get rid of status bytes. */
+				for (j = 1; j < data_len; j += 2) {
+					data->b_rptr[i] = data->b_rptr[j];
+					i++;
+				}
+				data->b_wptr = data->b_rptr + i;
+				keyspan_put_tail(&kp->kp_rx_mp, data);
+
+				/*
+				 * The data will not be freed and
+				 * will be sent up later.
+				 */
+				req->bulk_data = NULL;
+			} else {
+				/*
+				 * When zero len returned, no data will
+				 * be sent up and the data buf will be
+				 * just freed.
+				 */
+				data_len = 0;
+			}
+		}
+
+	} else {
+
+		/* usb error happened, so don't send up data */
+		USB_DPRINTF_L4(DPRINT_IN_PIPE, bulkin->pipe_lh,
+		    "keyspan_bulkin_cb_usa19hs: error happened, len=%d, "
+		    "cr=0x%x, cb_flags=0x%x", data_len, cr, req->bulk_cb_flags);
+
+		data_len = 0;
+		if (kp->kp_state != KEYSPAN_PORT_OPEN) {
+			kp->kp_no_more_reads = B_TRUE;
+		}
+	}
+
+	return (data_len);
+}
+
+#ifdef	KEYSPAN_USA49WLC
+/*
+ * pipe callbacks
+ * --------------
+ *
+ * bulk in common callback for usa49 model
+ */
+/*ARGSUSED*/
+int
+keyspan_bulkin_cb_usa49(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
+{
+	keyspan_port_t	*kp = (keyspan_port_t *)req->bulk_client_private;
+	keyspan_pipe_t	*bulkin = &kp->kp_datain_pipe;
+	mblk_t		*data = req->bulk_data;
+	uint_t		cr = req->bulk_completion_reason;
+	int		data_len;
+
+	ASSERT(mutex_owned(&kp->kp_mutex));
+
+	data_len = (data) ? MBLKL(data) : 0;
+
+	USB_DPRINTF_L4(DPRINT_IN_PIPE, bulkin->pipe_lh,
+	    "keyspan_bulkin_cb_usa49: len=%d"
+	    " cr=%d flags=%x", data_len, cr, req->bulk_cb_flags);
+
+	/* put data on the read queue */
+	if ((data_len > 0) && (kp->kp_state != KEYSPAN_PORT_CLOSED) &&
+	    (cr == USB_CR_OK)) {
+
+		/*
+		 * According to Keyspan spec, if 0x80 bit set, the data
+		 * buf contains alternate status and data bytes;
+		 * if 0x80 bit is clear, then there are no status bytes,
+		 * so we put tail to send up data.
+		 */
+		if ((data->b_rptr[0] & 0x80) == 0) {
+			USB_DPRINTF_L4(DPRINT_IN_PIPE, bulkin->pipe_lh,
+			    "keyspan_bulkin_cb_usa49: len=%d"
+			    "cr=%d", data_len, cr);
+
+			data->b_rptr++;
+			data_len--;
+			if (data_len > 0) {
+				keyspan_put_tail(&kp->kp_rx_mp, data);
+
+				/*
+				 * The data will not be freed and
+				 * will be sent up later.
+				 */
+				req->bulk_data = NULL;
+			}
+		} else {
+			if (data_len > 1) {
+				int i = 0;
+				int j = 1;
+
+				/* get rid of the status bytes in the data. */
+				for (j = 1; j < data_len; j += 2) {
+					data->b_rptr[i] = data->b_rptr[j];
+					i++;
+				}
+				data->b_wptr = data->b_rptr + i;
+				keyspan_put_tail(&kp->kp_rx_mp, data);
+
+				/*
+				 * The data will not be freed and
+				 * will be sent up later.
+				 */
+				req->bulk_data = NULL;
+			} else {
+				/*
+				 * When zero len returned, no data will be sent
+				 * up and the data buf will be just freed.
+				 */
+				data_len = 0;
+			}
+		}
+	} else {
+
+		/* usb error happened, so don't send up data */
+		data_len = 0;
+		USB_DPRINTF_L2(DPRINT_IN_PIPE, bulkin->pipe_lh,
+		    "keyspan_bulkin_cb_usa49: port_state=%d"
+		    " b_rptr[0]=%c", kp->kp_state, data->b_rptr[0]);
+
+		if (kp->kp_state != KEYSPAN_PORT_OPEN) {
+			kp->kp_no_more_reads = B_TRUE;
+		}
+	}
+
+	return (data_len);
+}
+#endif	/* If KEYSPAN_USA49WLC defined */
+
+/*
+ * pipe callbacks
+ * --------------
+ *
+ * bulk in common callback
+ */
+/*ARGSUSED*/
+void
+keyspan_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
+{
+	keyspan_port_t	*kp = (keyspan_port_t *)req->bulk_client_private;
+	keyspan_state_t	*ksp = kp->kp_ksp;
+	int		data_len;
+	boolean_t	no_more_reads = B_FALSE;
+
+	USB_DPRINTF_L4(DPRINT_IN_PIPE, (&kp->kp_datain_pipe)->pipe_lh,
+	    "keyspan_bulkin_cb");
+
+	mutex_enter(&kp->kp_mutex);
+
+	/* put data on the read queue */
+	switch (ksp->ks_dev_spec.id_product) {
+	case KEYSPAN_USA19HS_PID:
+		data_len = keyspan_bulkin_cb_usa19hs(pipe, req);
+
+		break;
+
+#ifdef	KEYSPAN_USA49WLC
+	case KEYSPAN_USA49WLC_PID:
+		data_len = keyspan_bulkin_cb_usa49(pipe, req);
+
+		break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+	default:
+		USB_DPRINTF_L2(DPRINT_IN_PIPE, (&kp->kp_datain_pipe)->pipe_lh,
+		    "keyspan_bulkin_cb:"
+		    "the device's product id can't be recognized");
+		mutex_exit(&kp->kp_mutex);
+
+		return;
+	}
+
+	no_more_reads = kp->kp_no_more_reads;
+
+	mutex_exit(&kp->kp_mutex);
+
+	usb_free_bulk_req(req);
+
+	/* kick off another read unless indicated otherwise */
+	if (!no_more_reads) {
+		(void) keyspan_receive_data(&kp->kp_datain_pipe,
+		    kp->kp_read_len, kp);
+	}
+
+	/* setup rx callback for this port */
+	if (data_len > 0)  {
+		kp->kp_cb.cb_rx(kp->kp_cb.cb_arg);
+	}
+}
+
+/*
+ * pipe callbacks
+ * --------------
+ *
+ * bulk in status callback for usa19hs model
+ */
+/*ARGSUSED*/
+void
+keyspan_status_cb_usa19hs(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)req->bulk_client_private;
+	keyspan_pipe_t	*bulkin = &ksp->ks_statin_pipe;
+	mblk_t		*data = req->bulk_data;
+	usb_cr_t	cr = req->bulk_completion_reason;
+	int		data_len;
+
+	data_len = (data) ? MBLKL(data) : 0;
+
+	USB_DPRINTF_L4(DPRINT_IN_PIPE, bulkin->pipe_lh,
+	    "keyspan_status_cb_usa19hs: len=%d"
+	    " cr=%d flags=%x", data_len, cr, req->bulk_cb_flags);
+
+	/* put data on the read queue */
+	if ((data_len == 14) && (cr == USB_CR_OK)) {
+		keyspan_port_t	*kp = &ksp->ks_ports[0];
+		keyspan_usa19hs_port_status_msg_t *status_msg =
+		    &(kp->kp_status_msg.usa19hs);
+
+		mutex_enter(&kp->kp_mutex);
+		bcopy(data->b_rptr, status_msg, data_len);
+
+		if (status_msg->controlResponse) {
+			kp->kp_status_flag |= KEYSPAN_PORT_CTRLRESP;
+		} else {
+			kp->kp_status_flag &= ~KEYSPAN_PORT_CTRLRESP;
+		}
+
+		if (status_msg->portState & PORTSTATE_ENABLED) {
+			kp->kp_status_flag |= KEYSPAN_PORT_ENABLE;
+		} else {
+			kp->kp_status_flag &= ~KEYSPAN_PORT_ENABLE;
+		}
+
+		if (status_msg->portState & PORTSTATE_TXBREAK) {
+			kp->kp_status_flag |= KEYSPAN_PORT_TXBREAK;
+		} else {
+			kp->kp_status_flag &= ~KEYSPAN_PORT_TXBREAK;
+		}
+
+		if (status_msg->rxBreak) {
+			kp->kp_status_flag |= KEYSPAN_PORT_RXBREAK;
+		} else {
+			kp->kp_status_flag &= ~KEYSPAN_PORT_RXBREAK;
+		}
+
+		if (status_msg->portState & PORTSTATE_LOOPBACK) {
+			kp->kp_status_flag |= KEYSPAN_PORT_LOOPBACK;
+		} else {
+			kp->kp_status_flag &= ~KEYSPAN_PORT_LOOPBACK;
+		}
+
+		/* if msr status changed, then invoke status callback */
+		if (status_msg->msr & USA_MSR_dCTS ||
+		    status_msg->msr & USA_MSR_dDSR ||
+		    status_msg->msr & USA_MSR_dRI ||
+		    status_msg->msr & USA_MSR_dDCD) {
+
+			mutex_exit(&kp->kp_mutex);
+			kp->kp_cb.cb_status(kp->kp_cb.cb_arg);
+		} else {
+			mutex_exit(&kp->kp_mutex);
+		}
+	} else {
+
+		USB_DPRINTF_L2(DPRINT_IN_PIPE, bulkin->pipe_lh,
+		    "keyspan_status_cb_usa19hs: get status failed, cr=%d"
+		    " data_len=%d", cr, data_len);
+	}
+}
+
+#ifdef	KEYSPAN_USA49WLC
+/*
+ * pipe callbacks
+ * --------------
+ *
+ * bulk in status callback for usa49 model
+ */
+/*ARGSUSED*/
+void
+keyspan_status_cb_usa49(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)req->bulk_client_private;
+	keyspan_pipe_t	*bulkin = &ksp->ks_statin_pipe;
+	mblk_t		*data = req->bulk_data;
+	uint_t		cr = req->bulk_completion_reason;
+	int		data_len;
+
+	data_len = (data) ? MBLKL(data) : 0;
+
+	USB_DPRINTF_L4(DPRINT_IN_PIPE, bulkin->pipe_lh,
+	    "keyspan_status_cb_usa49: len=%d"
+	    " cr=%d flags=%x", data_len, cr, req->bulk_cb_flags);
+
+	/* put data on the read queue */
+	if ((data_len == 11) && (cr == USB_CR_OK)) {
+		keyspan_usa49_port_status_msg_t status_msg;
+		keyspan_port_t *cur_kp;
+		keyspan_usa49_port_status_msg_t *kp_status_msg;
+		boolean_t need_cb = B_FALSE;
+
+		bcopy(data->b_rptr, &status_msg, data_len);
+		if (status_msg.portNumber >= ksp->ks_dev_spec.port_cnt) {
+
+			return;
+		}
+		cur_kp = &ksp->ks_ports[status_msg.portNumber];
+		kp_status_msg = &(cur_kp->kp_status_msg.usa49);
+
+		mutex_enter(&cur_kp->kp_mutex);
+
+		/* if msr status changed, then need invoke status callback */
+		if (status_msg.cts !=  kp_status_msg->cts ||
+		    status_msg.dsr != kp_status_msg->dsr ||
+		    status_msg.ri != kp_status_msg->ri ||
+		    status_msg.dcd != kp_status_msg->dcd) {
+
+			need_cb = B_TRUE;
+		}
+
+		bcopy(&status_msg, kp_status_msg, data_len);
+
+		if (kp_status_msg->controlResponse) {
+			cur_kp->kp_status_flag |= KEYSPAN_PORT_CTRLRESP;
+		} else {
+			cur_kp->kp_status_flag &= ~KEYSPAN_PORT_CTRLRESP;
+		}
+
+		if (!kp_status_msg->rxEnabled) {
+			cur_kp->kp_status_flag |= KEYSPAN_PORT_RXBREAK;
+		} else {
+			cur_kp->kp_status_flag &= ~KEYSPAN_PORT_RXBREAK;
+		}
+
+		mutex_exit(&cur_kp->kp_mutex);
+
+		if (need_cb) {
+
+			cur_kp->kp_cb.cb_status(cur_kp->kp_cb.cb_arg);
+		}
+	} else {
+
+		USB_DPRINTF_L2(DPRINT_IN_PIPE, bulkin->pipe_lh,
+		    "keyspan_status_cb_usa49: get status failed, cr=%d"
+		    " data_len=%d", cr, data_len);
+	}
+}
+#endif	/* If KEYSPAN_USA49WLC defined */
+
+/*
+ * pipe callbacks
+ * --------------
+ *
+ * bulk in callback for status receiving
+ */
+/*ARGSUSED*/
+void
+keyspan_status_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req)
+{
+	keyspan_state_t	*ksp = (keyspan_state_t *)req->bulk_client_private;
+	usb_cr_t	cr = req->bulk_completion_reason;
+
+	USB_DPRINTF_L4(DPRINT_IN_PIPE, (&ksp->ks_statin_pipe)->pipe_lh,
+	    "keyspan_status_cb");
+
+	/* put data on the read queue */
+	switch (ksp->ks_dev_spec.id_product) {
+	case KEYSPAN_USA19HS_PID:
+		keyspan_status_cb_usa19hs(pipe, req);
+
+		break;
+
+#ifdef	KEYSPAN_USA49WLC
+	case KEYSPAN_USA49WLC_PID:
+		keyspan_status_cb_usa49(pipe, req);
+
+		break;
+#endif	/* If KEYSPAN_USA49WLC defined */
+	default:
+		USB_DPRINTF_L2(DPRINT_IN_PIPE,
+		    (&ksp->ks_statin_pipe)->pipe_lh, "keyspan_status_cb:"
+		    "the device's product id can't be recognized");
+
+		return;
+	}
+
+	usb_free_bulk_req(req);
+
+	/* kick off another read to receive status */
+	if ((cr != USB_CR_FLUSHED) && (cr != USB_CR_DEV_NOT_RESP) &&
+	    keyspan_dev_is_online(ksp)) {
+		if (keyspan_receive_status(ksp) != USB_SUCCESS) {
+			USB_DPRINTF_L2(DPRINT_IN_PIPE,
+			    (&ksp->ks_statin_pipe)->pipe_lh,
+			    "keyspan_status_cb:"
+			    "receive status can't be restarted.");
+		}
+	} else {
+		USB_DPRINTF_L2(DPRINT_IN_PIPE,
+		    (&ksp->ks_statin_pipe)->pipe_lh, "keyspan_status_cb:"
+		    "get status failed: cr=%d", cr);
+	}
+}
+
+/*
+ * Submit data read request (asynchronous). If this function returns
+ * USB_SUCCESS, pipe is acquired and request is sent, otherwise req is free.
+ */
+int
+keyspan_receive_data(keyspan_pipe_t *bulkin, int len, void *cb_arg)
+{
+	keyspan_state_t	*ksp = bulkin->pipe_ksp;
+	usb_bulk_req_t	*br;
+	int		rval;
+
+	USB_DPRINTF_L4(DPRINT_IN_PIPE, bulkin->pipe_lh, "keyspan_receive_data:"
+	    "len=%d", len);
+
+	ASSERT(!mutex_owned(&bulkin->pipe_mutex));
+
+	br = usb_alloc_bulk_req(ksp->ks_dip, len, USB_FLAGS_SLEEP);
+	br->bulk_len = len;
+
+	/* No timeout, just wait for data */
+	br->bulk_timeout = 0;
+	br->bulk_client_private = cb_arg;
+	br->bulk_attributes = USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING;
+	br->bulk_cb = keyspan_bulkin_cb;
+	br->bulk_exc_cb = keyspan_bulkin_cb;
+
+	rval = usb_pipe_bulk_xfer(bulkin->pipe_handle, br, 0);
+	if (rval != USB_SUCCESS) {
+		usb_free_bulk_req(br);
+	}
+	USB_DPRINTF_L4(DPRINT_IN_PIPE, bulkin->pipe_lh,
+	    "keyspan_receive_data: rval = %d", rval);
+	return (rval);
+}
+
+/*
+ * submit device status read request (asynchronous).
+ */
+int
+keyspan_receive_status(keyspan_state_t	*ksp)
+{
+	keyspan_pipe_t *bulkin = &ksp->ks_statin_pipe;
+	usb_bulk_req_t	*br;
+	int		rval;
+
+	USB_DPRINTF_L4(DPRINT_IN_PIPE, bulkin->pipe_lh,
+	    "keyspan_receive_status");
+
+	ASSERT(!mutex_owned(&bulkin->pipe_mutex));
+
+	br = usb_alloc_bulk_req(ksp->ks_dip, 32, USB_FLAGS_SLEEP);
+	br->bulk_len = KEYSPAN_STATIN_MAX_LEN;
+
+	/* No timeout, just wait for data */
+	br->bulk_timeout = 0;
+	br->bulk_client_private = (void *)ksp;
+	br->bulk_attributes = USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING;
+	br->bulk_cb = keyspan_status_cb;
+	br->bulk_exc_cb = keyspan_status_cb;
+
+	rval = usb_pipe_bulk_xfer(bulkin->pipe_handle, br, 0);
+	if (rval != USB_SUCCESS) {
+		usb_free_bulk_req(br);
+	}
+	USB_DPRINTF_L4(DPRINT_IN_PIPE, bulkin->pipe_lh,
+	    "keyspan_receive_status: rval = %d", rval);
+	return (rval);
+}
+
+/*
+ * submit data for transfer (asynchronous)
+ *
+ * if data was sent successfully, 'mpp' will be nulled to indicate
+ * that mblk is consumed by USBA and no longer belongs to the caller.
+ *
+ * if this function returns USB_SUCCESS, pipe is acquired and request
+ * is sent, otherwise pipe is free.
+ */
+int
+keyspan_send_data(keyspan_pipe_t *bulkout, mblk_t **mpp, void *cb_arg)
+{
+	keyspan_state_t	*ksp = bulkout->pipe_ksp;
+	usb_bulk_req_t	*br;
+	int		rval;
+
+	ASSERT(!mutex_owned(&bulkout->pipe_mutex));
+	USB_DPRINTF_L4(DPRINT_OUT_PIPE, bulkout->pipe_lh,
+	    "keyspan_send_data");
+
+	br = usb_alloc_bulk_req(ksp->ks_dip, 0, USB_FLAGS_SLEEP);
+	br->bulk_len = MBLKL(*mpp);
+	br->bulk_data = *mpp;
+	br->bulk_timeout = KEYSPAN_BULK_TIMEOUT;
+	br->bulk_client_private = cb_arg;
+	br->bulk_attributes = USB_ATTRS_AUTOCLEARING;
+	br->bulk_cb = keyspan_bulkout_cb;
+	br->bulk_exc_cb = keyspan_bulkout_cb;
+
+	USB_DPRINTF_L3(DPRINT_OUT_PIPE, bulkout->pipe_lh, "keyspan_send_data:"
+	    "bulk_len = %d", br->bulk_len);
+
+	rval = usb_pipe_bulk_xfer(bulkout->pipe_handle, br, 0);
+	if (rval == USB_SUCCESS) {
+
+		/* data consumed. The mem will be released in bulkout_cb */
+		*mpp = NULL;
+	} else {
+
+		/*
+		 * Don't free it in usb_free_bulk_req because it will
+		 * be linked in keyspan_put_head
+		 */
+		br->bulk_data = NULL;
+
+		usb_free_bulk_req(br);
+	}
+	USB_DPRINTF_L4(DPRINT_OUT_PIPE, bulkout->pipe_lh,
+	    "keyspan_send_data: rval = %d", rval);
+
+	return (rval);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/usb/clients/usbser/usbser_keyspan/usbser_keyspan.c	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,626 @@
+/*
+ * 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.
+ *
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * This driver includes code for Keyspan USA49WLC/USA19HS adapters. 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.
+ *
+ * For USA49WLC, it's necessary to download firmware every time the device is
+ * plugged. Before the firmware is downloaded, we say that the device is in
+ * "firmware mode", and the attach routin is keyspan_pre_attach(). After
+ * downloading, the device's product id will change to 0x12a. Then the device
+ * will be enumerated again and another attach for the new product id will
+ * begin. No firmware is included in the driver. The functions of USA49WLC is
+ * disabled.
+ *
+ * For USA19HS, no need to download firmware since it can be kept in the
+ * device's memory.
+ *
+ * For both models, it's necessary to check and switch their configrations at
+ * the beginning of attach, since each of them has two configrations. This
+ * driver uses the one whose endpoints are all bulk.
+ *
+ * Some of Keyspan adapters have only one port, some have two or four ports.
+ * This driver supports up to four ports. Each port has its own states (traced
+ * by keyspan_port structure) and can be operated independently.
+ *
+ * port_state:
+ *
+ *   KEYSPAN_PORT_NOT_INIT
+ *	    |
+ *	    |
+ *     attach_ports
+ *	    |
+ *	    |
+ *	    |
+ *	    v
+ *   KEYSPAN_PORT_CLOSED <-----close-------<---- +
+ *	|					 |
+ *	|					 |
+ *	|					 |
+ *  open_port					 |
+ *	|					 |
+ *	|					 |
+ *	v					 |
+ * KEYSPAN_PORT_OPENING ---open_hw_port---> USBSER_PORT_OPEN
+ *
+ * Each port has its own data in/out pipes and each pipe also has its own states
+ * (traced by keyspan_pipe structure). The pipe states is as following:
+ *
+ * pipe_state:
+ *
+ *	  KEYSPAN_PIPE_NOT_INIT
+ *		|	^
+ *		|	|
+ * keyspan_init_pipes  keyspan_fini_pipes
+ *		|	|
+ *		v	|
+ *	   KEYSPAN_PIPE_CLOSED ------------->-----------+
+ *		  ^					|
+ *		  |			  reconnect/resume/open_port
+ *		  |					|
+ *    disconnect/suspend/close_port			|
+ *		  |					v
+ *		  +---------<------------------ KEYSPAN_PIPE_OPEN
+ *
+ * To control the device and get its status in a timely way, this driver makes
+ * use of two global bulk endpoints for cmd and status on the device. The pipes
+ * for cmd/status will be opened during attach. For multi-port devices, one of
+ * the cmd/status message fields will designate which port this message is for.
+ *
+ * This driver can be easily extended to support more Keyspan adapter models.
+ * You need the following steps to reach the aim:
+ * 1. Add the model specific data structures, like cmd/status message structure.
+ * 2. If the device need firmware downloaded, add the firmware code as a header
+ * file, and add code to keyspan_pre_attach() as what were done for USA49WLC.
+ * 3. Add several model specific functions, like keyspan_build_cmd_msg_*,
+ * keyspan_default_port_params_*, keyspan_save_port_params_*, etc. The functions
+ * for USA19HS and USA49WLC can be taken as examples.
+ * 4. Add model specific code to the "switch (id_product) {...}" sentences.
+ */
+
+/*
+ *
+ * keyspan driver glue code
+ *
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stream.h>
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+#define	USBDRV_MAJOR_VER	2
+#define	USBDRV_MINOR_VER	0
+
+#include <sys/usb/usba.h>
+
+#include <sys/usb/clients/usbser/usbser.h>
+#include <sys/usb/clients/usbser/usbser_keyspan/keyspan_var.h>
+
+#ifdef	KEYSPAN_USA49WLC
+#include <sys/usb/clients/usbser/usbser_keyspan/usa49wlc_fw.h>
+#endif	/* If KEYSPAN_USA49WLC defined */
+
+#include <sys/byteorder.h>
+#include <sys/strsun.h>
+
+
+/* configuration entry points */
+static int	usbser_keyspan_getinfo(dev_info_t *, ddi_info_cmd_t, void *,
+		void **);
+static int	usbser_keyspan_attach(dev_info_t *, ddi_attach_cmd_t);
+static int	usbser_keyspan_detach(dev_info_t *, ddi_detach_cmd_t);
+static int	usbser_keyspan_open(queue_t *, dev_t *, int, int, cred_t *);
+
+/* functions related with set config or firmware download */
+static int	keyspan_pre_attach(dev_info_t *, ddi_attach_cmd_t, void *);
+static int	keyspan_set_cfg(dev_info_t *);
+
+#ifdef	KEYSPAN_USA49WLC
+
+static int	keyspan_pre_detach(dev_info_t *, ddi_detach_cmd_t, void *);
+static boolean_t keyspan_need_fw(usb_client_dev_data_t *);
+static int	keyspan_set_reg(keyspan_pipe_t *, uchar_t);
+static int	keyspan_write_memory(keyspan_pipe_t *, uint16_t, uchar_t *,
+		uint16_t, uint8_t);
+
+#endif	/* If KEYSPAN_USA49WLC defined */
+
+static void    *usbser_keyspan_statep;	/* soft state */
+
+extern ds_ops_t ds_ops;		/* DSD operations */
+
+/*
+ * STREAMS structures
+ */
+struct module_info usbser_keyspan_modinfo = {
+	0,			/* module id */
+	"usbsksp",		/* 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 usbser_keyspan_rinit = {
+	putq,
+	usbser_rsrv,
+	usbser_keyspan_open,
+	usbser_close,
+	NULL,
+	&usbser_keyspan_modinfo,
+	NULL
+};
+
+static struct qinit usbser_keyspan_winit = {
+	usbser_wput,
+	usbser_wsrv,
+	NULL,
+	NULL,
+	NULL,
+	&usbser_keyspan_modinfo,
+	NULL
+};
+
+struct streamtab usbser_keyspan_str_info = {
+	&usbser_keyspan_rinit, &usbser_keyspan_winit, NULL, NULL
+};
+
+static struct cb_ops usbser_keyspan_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 */
+	&usbser_keyspan_str_info,	/* cb_stream */
+	(int)(D_64BIT | D_NEW | D_MP | D_HOTPLUG)	/* cb_flag */
+};
+
+/*
+ * auto configuration ops
+ */
+struct dev_ops usbser_keyspan_ops = {
+	DEVO_REV,		/* devo_rev */
+	0,			/* devo_refcnt */
+	usbser_keyspan_getinfo,	/* devo_getinfo */
+	nulldev,		/* devo_identify */
+	nulldev,		/* devo_probe */
+	usbser_keyspan_attach,	/* devo_attach */
+	usbser_keyspan_detach,	/* devo_detach */
+	nodev,			/* devo_reset */
+	&usbser_keyspan_cb_ops,	/* devo_cb_ops */
+	(struct bus_ops *)NULL,	/* devo_bus_ops */
+	usbser_power		/* devo_power */
+};
+
+extern struct mod_ops mod_driverops;
+
+static struct modldrv modldrv = {
+	&mod_driverops,		/* type of module - driver */
+	"USB keyspan usb2serial driver 1.0",
+	&usbser_keyspan_ops,
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, &modldrv, 0
+};
+
+
+/*
+ * configuration entry points
+ * --------------------------
+ */
+int
+_init(void)
+{
+	int    error;
+
+	if ((error = mod_install(&modlinkage)) == 0) {
+		error = ddi_soft_state_init(&usbser_keyspan_statep,
+		    max(usbser_soft_state_size(),
+		    sizeof (keyspan_pre_state_t)), 1);
+	}
+
+	return (error);
+}
+
+
+int
+_fini(void)
+{
+	int    error;
+
+	if ((error = mod_remove(&modlinkage)) == 0) {
+		ddi_soft_state_fini(&usbser_keyspan_statep);
+	}
+
+	return (error);
+}
+
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+
+/*ARGSUSED*/
+int
+usbser_keyspan_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
+		void **result)
+{
+	return (usbser_getinfo(dip, infocmd, arg, result,
+	    usbser_keyspan_statep));
+}
+
+
+static int
+usbser_keyspan_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	int	rval;
+
+	/*
+	 * Once the device is plugged, we need set its cfg. And need download
+	 * firmware for some of them.
+	 */
+	rval = keyspan_pre_attach(dip, cmd, usbser_keyspan_statep);
+
+	/*
+	 * After the cfg is set, and the firmware is downloaded,
+	 * do the real attach.
+	 */
+	if (rval == DDI_ECONTEXT) {
+
+		return (usbser_attach(dip, cmd, usbser_keyspan_statep,
+		    &ds_ops));
+	} else {
+
+		return (rval);
+	}
+}
+
+
+static int
+usbser_keyspan_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+#ifdef	KEYSPAN_USA49WLC
+	if (ddi_get_driver_private(dip) == NULL) {
+
+		return (keyspan_pre_detach(dip, cmd, usbser_keyspan_statep));
+	} else {
+#endif	/* If KEYSPAN_USA49WLC defined */
+
+		return (usbser_detach(dip, cmd, usbser_keyspan_statep));
+
+#ifdef	KEYSPAN_USA49WLC
+	}
+#endif	/* If KEYSPAN_USA49WLC defined */
+}
+
+
+static int
+usbser_keyspan_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
+{
+	return (usbser_open(rq, dev, flag, sflag, cr, usbser_keyspan_statep));
+}
+
+/*
+ * Switch config or download firmware
+ * -----------------------------
+ */
+/*ARGSUSED*/
+static int
+keyspan_pre_attach(dev_info_t *dip, ddi_attach_cmd_t cmd, void *statep)
+{
+
+#ifdef	KEYSPAN_USA49WLC
+	int			instance;
+	keyspan_pre_state_t		*kbp;
+	struct fw_record *record;
+#endif	/* If KEYSPAN_USA49WLC defined */
+
+	usb_client_dev_data_t	*dev_data;
+
+#ifdef	KEYSPAN_USA49WLC
+
+	instance = ddi_get_instance(dip);
+
+	switch (cmd) {
+	case DDI_ATTACH:
+
+		break;
+	case DDI_RESUME:
+
+		return (DDI_SUCCESS);
+	default:
+
+		return (DDI_FAILURE);
+	}
+
+#endif	/* If KEYSPAN_USA49WLC defined */
+
+	/* attach driver to USBA */
+	if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
+
+		return (DDI_FAILURE);
+	}
+
+	if (usb_get_dev_data(dip, &dev_data, USB_PARSE_LVL_IF, 0) !=
+	    USB_SUCCESS) {
+
+		return (DDI_FAILURE);
+	}
+
+#ifndef	KEYSPAN_USA49WLC
+
+	/*
+	 * Disable USA49WLC.
+	 */
+	if (dev_data->dev_descr->idProduct == KEYSPAN_USA49WLC_PID) {
+
+		goto fail_unreg;
+	}
+
+#endif	/* If no KEYSPAN_USA49WLC defined */
+
+	/*
+	 * If 19HS, needn't download firmware, but need check the current cfg.
+	 * If 49WLC, need check the current cfg before download fw. And after
+	 * download, the product id will change, not be KEYSPAN_USA49WLC_PID.
+	 */
+	if (dev_data->dev_descr->idProduct == KEYSPAN_USA19HS_PID ||
+	    dev_data->dev_descr->idProduct == KEYSPAN_USA49WLC_PID) {
+		if (keyspan_set_cfg(dip) != USB_SUCCESS) {
+
+			goto fail_unreg;
+		}
+		usb_client_detach(dip, dev_data);
+
+		/* Go to keyspan_attach() by return DDI_ECONTEXT. */
+		return (DDI_ECONTEXT);
+	}
+
+#ifdef	KEYSPAN_USA49WLC
+
+	/*
+	 * By checking KEYSPAN_FW_FLAG,  we can know whether the firmware
+	 * is downloaded. If firmware is already there, then do normal attach.
+	 */
+	if (!keyspan_need_fw(dev_data)) {
+		usb_client_detach(dip, dev_data);
+
+		/* Go to keyspan_attach() by return DDI_ECONTEXT. */
+		return (DDI_ECONTEXT);
+	}
+
+	/* Go on to download firmware. */
+
+	if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS) {
+
+		goto fail_unreg;
+	}
+
+	if ((kbp = ddi_get_soft_state(statep, instance)) == NULL) {
+
+		goto fail_unreg;
+	}
+
+	kbp->kb_dip = dip;
+	kbp->kb_instance = instance;
+	kbp->kb_dev_data = dev_data;
+	kbp->kb_def_pipe.pipe_handle = kbp->kb_dev_data->dev_default_ph;
+	kbp->kb_def_pipe.pipe_lh = kbp->kb_lh;
+
+	/* open control pipe for firmware download */
+	record = &keyspan_usa49wlc_firmware[0];
+
+	/* Set bit 1 before downloading firmware. */
+	if (keyspan_set_reg(&kbp->kb_def_pipe, 1) != USB_SUCCESS) {
+
+		goto fail_state;
+	}
+
+	/* Write until the last record of the firmware */
+	while (record->address != 0xffff) {
+		if (keyspan_write_memory(&kbp->kb_def_pipe,
+		    record->address, (uchar_t *)record->data,
+		    record->data_size, KEYSPAN_REQ_SET) != USB_SUCCESS) {
+
+			goto fail_state;
+		}
+		record++;
+	}
+
+	/*
+	 * Set bit 0, device will be enumerated again after a while,
+	 * and then go to keyspan_attach()
+	 */
+	if (keyspan_set_reg(&kbp->kb_def_pipe, 0) != USB_SUCCESS) {
+
+		goto fail_state;
+	}
+
+	/* keyspan download firmware done. */
+	return (DDI_SUCCESS);
+
+fail_state:
+	ddi_soft_state_free(statep, instance);
+
+#endif	/* If KEYSPAN_USA49WLC defined */
+
+fail_unreg:
+	usb_client_detach(dip, dev_data);
+
+	return (DDI_FAILURE);
+}
+
+
+#ifdef	KEYSPAN_USA49WLC
+
+static int
+keyspan_pre_detach(dev_info_t *dip, ddi_detach_cmd_t cmd, void *statep)
+{
+	int		instance = ddi_get_instance(dip);
+	keyspan_pre_state_t	*kbp;
+
+	kbp = ddi_get_soft_state(statep, instance);
+
+	switch (cmd) {
+	case DDI_DETACH:
+
+		break;
+	case DDI_SUSPEND:
+
+		return (DDI_SUCCESS);
+	default:
+
+		return (DDI_FAILURE);
+	}
+
+	usb_client_detach(dip, kbp->kb_dev_data);
+	ddi_soft_state_free(statep, instance);
+
+	return (DDI_SUCCESS);
+}
+
+#endif	/* If KEYSPAN_USA49WLC defined */
+
+/* Set cfg for the device which has more than one cfg */
+static int
+keyspan_set_cfg(dev_info_t *dip)
+{
+
+	if (usb_set_cfg(dip, 1, USB_FLAGS_SLEEP, NULL, NULL) != USB_SUCCESS) {
+
+		return (USB_FAILURE);
+	}
+
+	return (USB_SUCCESS);
+}
+
+#ifdef	KEYSPAN_USA49WLC
+
+/* Return TRUE if need download firmware to the device. */
+static boolean_t
+keyspan_need_fw(usb_client_dev_data_t *dev_data)
+{
+	uint16_t	bcd_descr;
+	uint16_t	bcd_descr_change;
+
+	/* need to convert to Little-Endian */
+	bcd_descr = dev_data->dev_descr->bcdDevice;
+
+	/*
+	 * According to Keyspan's interface spec, this flag indicates
+	 * if need download fw.
+	 */
+	bcd_descr_change = bcd_descr & KEYSPAN_FW_FLAG;
+
+	return (bcd_descr_change == KEYSPAN_FW_FLAG);
+}
+
+/* Set the device's register. */
+static int
+keyspan_set_reg(keyspan_pipe_t *pipe, uchar_t bit)
+{
+	int	rval;
+
+	/*
+	 * (0x7f92) is the reg addr we want to set.
+	 * We set this reg before/after downloading firmware.
+	 */
+	rval = keyspan_write_memory(pipe, 0x7f92, &bit, 1, KEYSPAN_REQ_SET);
+
+	return (rval);
+}
+
+/*
+ * Download firmware or set register to the device by default ctrl pipe
+ */
+static int
+keyspan_write_memory(keyspan_pipe_t *pipe, uint16_t addr, uchar_t *buf,
+    uint16_t len, uint8_t bRequest)
+{
+	mblk_t *data;
+	usb_ctrl_setup_t setup;
+
+	usb_cb_flags_t	cb_flags;
+	usb_cr_t	cr;
+	uint8_t		retry = 0;
+
+	/* reuse previous mblk if possible */
+	if ((data = allocb(len, BPRI_HI)) == NULL) {
+
+		return (USB_FAILURE);
+	}
+
+	bcopy(buf, data->b_rptr, len);
+
+	setup.bmRequestType = USB_DEV_REQ_TYPE_VENDOR;
+
+	/* This is a req defined by hardware vendor. */
+	setup.bRequest = bRequest;
+	setup.wValue = addr;
+	setup.wIndex = 0;
+	setup.wLength = len;
+	setup.attrs = 0;
+
+	while (usb_pipe_ctrl_xfer_wait(pipe->pipe_handle, &setup, &data,
+			&cr, &cb_flags, 0) != USB_SUCCESS) {
+
+		/* KEYSPAN_RETRY */
+		if (++retry > 3) {
+			if (data) {
+				freemsg(data);
+			}
+
+			return (USB_FAILURE);
+		}
+	}
+
+	if (data) {
+		freemsg(data);
+	}
+
+	return (USB_SUCCESS);
+}
+#endif	/* If KEYSPAN_USA49WLC defined */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/usb/clients/usbser/usbser_keyspan/keyspan_pipe.h	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_USB_USBSER_KEYSPAN_PIPE_H
+#define	_SYS_USB_USBSER_KEYSPAN_PIPE_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * USB pipe management (mostly device-neutral)
+ */
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * pipe structure
+ */
+typedef struct keyspan_pipe {
+	kmutex_t		pipe_mutex;	/* structure lock */
+	keyspan_state_t		*pipe_ksp;	/* backpointer to state */
+	usb_pipe_handle_t	pipe_handle;	/* pipe handle */
+	usb_ep_descr_t		pipe_ep_descr;	/* endpoint descriptor */
+	usb_pipe_policy_t	pipe_policy;	/* pipe policy */
+	int			pipe_state;	/* pipe state */
+	usb_log_handle_t	pipe_lh;	/* log handle */
+} keyspan_pipe_t;
+
+_NOTE(MUTEX_PROTECTS_DATA(keyspan_pipe::pipe_mutex, keyspan_pipe))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(keyspan_pipe::{
+	pipe_ksp
+	pipe_handle
+	pipe_lh
+	pipe_ep_descr
+	pipe_policy
+}))
+
+/* pipe states */
+enum {
+	KEYSPAN_PIPE_NOT_INIT = 0,
+	KEYSPAN_PIPE_CLOSED,
+	KEYSPAN_PIPE_OPEN
+};
+
+
+int	keyspan_init_pipes(keyspan_state_t *);
+void	keyspan_fini_pipes(keyspan_state_t *);
+int	keyspansp_open_pipes(keyspan_state_t *);
+void	keyspansp_close_pipes(keyspan_state_t *);
+int	keyspan_open_dev_pipes(keyspan_state_t *);
+void	keyspan_close_dev_pipes(keyspan_state_t *);
+int	keyspan_open_port_pipes(keyspan_port_t *);
+void	keyspan_close_port_pipes(keyspan_port_t *);
+int	keyspan_reopen_pipes(keyspan_state_t *);
+void	keyspan_close_pipes(keyspan_state_t *);
+void	keyspan_close_open_pipes(keyspan_state_t *esp);
+
+int	keyspan_receive_data(keyspan_pipe_t *, int, void *);
+int	keyspan_send_data(keyspan_pipe_t *, mblk_t **, void *);
+
+int	keyspan_receive_status(keyspan_state_t	*);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_USB_USBSER_KEYSPAN_PIPE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/usb/clients/usbser/usbser_keyspan/keyspan_var.h	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ *
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_USB_USBSER_KEYSPAN_VAR_H
+#define	_SYS_USB_USBSER_KEYSPAN_VAR_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * keyspan implementation definitions
+ */
+
+#include <sys/types.h>
+#include <sys/dditypes.h>
+#include <sys/note.h>
+
+#include <sys/usb/clients/usbser/usbser_dsdi.h>
+
+#include <sys/usb/clients/usbser/usbser_keyspan/usa90msg.h>
+#ifdef	KEYSPAN_USA49WLC
+#include <sys/usb/clients/usbser/usbser_keyspan/usa49msg.h>
+#endif	/* If KEYSPAN_USA49WLC defined */
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/* product id */
+#define	KEYSPAN_USA19HS_PID		0x121
+#define	KEYSPAN_USA49WLC_PID		0x12a
+
+#define	KEYSPAN_MAX_PORT_NUM		4
+
+/*
+ * forward typedefs needed to resolve recursive header dependencies
+ */
+typedef struct keyspan_pre_state keyspan_pre_state_t;
+typedef struct keyspan_state keyspan_state_t;
+typedef struct keyspan_port keyspan_port_t;
+
+#include <sys/usb/clients/usbser/usbser_keyspan/keyspan_pipe.h>
+
+/*
+ * temporary soft state for pre_attach
+ */
+struct keyspan_pre_state {
+	dev_info_t		*kb_dip;	/* device info */
+	int			kb_instance;	/* instance */
+	usb_client_dev_data_t	*kb_dev_data;	/* registration data */
+	usb_log_handle_t	kb_lh;		/* USBA log handle */
+	keyspan_pipe_t		kb_def_pipe;	/* default pipe */
+};
+
+/*
+ * PM support
+ */
+typedef struct keyspan_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 */
+} keyspan_pm_t;
+
+/*
+ * device specific info structure
+ */
+typedef struct keyspan_dev_spec {
+
+	uint16_t	id_product;	/* product ID value */
+	uint8_t		port_cnt;	/* How many ports on the device */
+	uint8_t	ctrl_ep_addr;	/* Endpoint used to control the device */
+	uint8_t	stat_ep_addr;	/* Endpoint used to get device status */
+	uint8_t	dataout_ep_addr[4];	/* Endpoint used to send data */
+
+	/* Endpoint used to get data from device */
+	uint8_t	datain_ep_addr[4];
+} keyspan_dev_spec_t;
+
+/*
+ * To support different keyspan adapters, use union type
+ * for different cmd msg format.
+ */
+typedef union keyspan_port_ctrl_msg {
+	keyspan_usa19hs_port_ctrl_msg_t	usa19hs;
+#ifdef	KEYSPAN_USA49WLC
+	keyspan_usa49_port_ctrl_msg_t usa49;
+#endif	/* If KEYSPAN_USA49WLC defined */
+} keyspan_port_ctrl_msg_t;
+
+/*
+ * To support different keyspan adapters, use union type
+ * for different status msg format.
+ */
+typedef union keyspan_port_status_msg {
+	keyspan_usa19hs_port_status_msg_t	usa19hs;
+#ifdef	KEYSPAN_USA49WLC
+	keyspan_usa49_port_status_msg_t usa49;
+#endif	/* If KEYSPAN_USA49WLC defined */
+} keyspan_port_status_msg_t;
+
+/*
+ * per device state structure
+ */
+struct keyspan_state {
+	kmutex_t		ks_mutex;	/* structure lock */
+	dev_info_t		*ks_dip;	/* device info */
+	keyspan_port_t		*ks_ports;	/* per port structs */
+	keyspan_dev_spec_t	ks_dev_spec;	/* device specific info */
+
+	/*
+	 * we use semaphore to serialize pipe open/close by different ports.
+	 * mutex could be used too, but it causes trouble when warlocking
+	 * with USBA: some functions inside usb_pipe_close() wait on cv
+	 *
+	 * since semaphore is only used for serialization during
+	 * open/close and suspend/resume, there is no deadlock hazard
+	 */
+	ksema_t			ks_pipes_sema;
+
+	/*
+	 * USBA related
+	 */
+	usb_client_dev_data_t	*ks_dev_data;	/* registration data */
+	usb_event_t		*ks_usb_events;	/* usb events */
+
+	keyspan_pipe_t		ks_def_pipe;	/* default pipe */
+
+	/* bulk in pipe for getting device status */
+	keyspan_pipe_t		ks_statin_pipe;
+
+	/* bulk out pipe for sending control cmd to device */
+	keyspan_pipe_t		ks_ctrlout_pipe;
+
+	usb_log_handle_t	ks_lh;		/* USBA log handle */
+	int			ks_dev_state;	/* USB device state */
+	keyspan_pm_t		*ks_pm;		/* PM support */
+
+};
+
+_NOTE(MUTEX_PROTECTS_DATA(keyspan_state::ks_mutex, keyspan_state))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(keyspan_state::{
+	ks_dip
+	ks_dev_data
+	ks_usb_events
+	ks_dev_spec
+	ks_ports
+	ks_def_pipe
+	ks_ctrlout_pipe.pipe_handle
+	ks_statin_pipe.pipe_handle
+	ks_lh
+	ks_pm
+}))
+
+/*
+ * per port structure
+ */
+struct keyspan_port {
+	kmutex_t	kp_mutex;	/* structure lock */
+	keyspan_state_t	*kp_ksp;	/* back pointer to the state */
+	char		kp_lh_name[16];	/* log handle name */
+	usb_log_handle_t kp_lh;		/* log handle */
+	uint_t		kp_port_num;	/* port number */
+	int		kp_state;	/* port state */
+	int		kp_flags;	/* port flags */
+	ds_cb_t		kp_cb;		/* DSD callbacks */
+	kcondvar_t	kp_tx_cv;	/* cv to wait for tx completion */
+	/*
+	 * data receipt and transmit
+	 */
+	mblk_t		*kp_rx_mp;	/* received data */
+	mblk_t		*kp_tx_mp;	/* data to transmit */
+	boolean_t	kp_no_more_reads; /* disable reads */
+
+	/* The control cmd sent to the port */
+	keyspan_port_ctrl_msg_t	kp_ctrl_msg;
+
+	/* status msg of the port */
+	keyspan_port_status_msg_t	kp_status_msg;
+
+	uint_t kp_baud;	/* the current baud speed code */
+	uint8_t	kp_lcr;	/* the current lcr value */
+	/*
+	 * the current port status, including: rts, dtr,
+	 * break, loopback, enable.
+	 */
+	uint8_t	kp_status_flag;
+
+	keyspan_pipe_t		kp_datain_pipe;	/* bulk in data pipe */
+	keyspan_pipe_t		kp_dataout_pipe; /* bulk out data pipe */
+
+	uint_t		kp_read_len;	/* max length of bulkin request */
+	uint_t		kp_write_len;	/* max length of bulkout request */
+};
+
+_NOTE(MUTEX_PROTECTS_DATA(keyspan_port::kp_mutex, keyspan_port))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(keyspan_port::{
+	kp_ksp
+	kp_lh
+	kp_port_num
+	kp_read_len
+	kp_write_len
+	kp_cb
+	kp_datain_pipe.pipe_handle
+	kp_datain_pipe.pipe_ep_descr
+}))
+
+/* lock relationships */
+_NOTE(LOCK_ORDER(keyspan_state::ks_mutex keyspan_port::kp_mutex))
+_NOTE(LOCK_ORDER(keyspan_port::kp_mutex keyspan_pipe::pipe_mutex))
+
+/* port status flags */
+enum {
+	KEYSPAN_PORT_ENABLE = 0x0001,		/* port is enabled */
+	KEYSPAN_PORT_RTS = 0x0002,		/* port's rts is set */
+	KEYSPAN_PORT_DTR = 0x0004,		/* port's dtr is set */
+	KEYSPAN_PORT_TXBREAK = 0x0008,		/* port is in TX break mod */
+	KEYSPAN_PORT_LOOPBACK = 0x0010,		/* port is in loopback mod */
+
+	/* the ctrl cmd sent to this port is responded */
+	KEYSPAN_PORT_CTRLRESP = 0x0020,
+	KEYSPAN_PORT_RXBREAK = 0x0040		/* port is in RX break mod */
+};
+
+/* port state */
+enum {
+	KEYSPAN_PORT_NOT_INIT = 0,	/* port is not initialized */
+	KEYSPAN_PORT_CLOSED,		/* port is closed */
+	KEYSPAN_PORT_OPENING,		/* port is being opened */
+	KEYSPAN_PORT_OPEN		/* port is open */
+};
+
+/* port flags */
+enum {
+	KEYSPAN_PORT_TX_STOPPED	= 0x0001	/* transmit not allowed */
+};
+
+/* various tunables */
+enum {
+	KEYSPAN_BULK_TIMEOUT		= 3,	/* transfer timeout */
+	KEYSPAN_BULKIN_MAX_LEN		= 64,	/* bulk in max length */
+	KEYSPAN_BULKOUT_MAX_LEN		= 64,	/* bulk out max length */
+	KEYSPAN_STATIN_MAX_LEN		= 16	/* status in max length */
+};
+
+/* This flag indicates if the firmware already downloaded to the device */
+#define	KEYSPAN_FW_FLAG 0x8000
+
+/* Vendor specific ctrl req, used to set/download bytes in the device memory */
+#define	KEYSPAN_REQ_SET 0xa0
+
+/*
+ * debug printing masks
+ */
+#define	DPRINT_ATTACH		0x00000001
+#define	DPRINT_OPEN		0x00000002
+#define	DPRINT_CLOSE		0x00000004
+#define	DPRINT_DEF_PIPE		0x00000010
+#define	DPRINT_IN_PIPE		0x00000020
+#define	DPRINT_OUT_PIPE		0x00000040
+#define	DPRINT_INTR_PIPE	0x00000080
+#define	DPRINT_PIPE_RESET	0x00000100
+#define	DPRINT_IN_DATA		0x00000200
+#define	DPRINT_OUT_DATA		0x00000400
+#define	DPRINT_CTLOP		0x00000800
+#define	DPRINT_HOTPLUG		0x00001000
+#define	DPRINT_PM		0x00002000
+#define	DPRINT_MASK_ALL		0xFFFFFFFF
+
+/*
+ * misc macros
+ */
+#define	NELEM(a)	(sizeof (a) / sizeof (*(a)))
+
+/* common DSD functions */
+int	keyspan_tx_copy_data(keyspan_port_t *, mblk_t *, int);
+void	keyspan_tx_start(keyspan_port_t *, int *);
+void	keyspan_put_tail(mblk_t **, mblk_t *);
+void	keyspan_put_head(mblk_t **, mblk_t *, keyspan_port_t *);
+
+void	keyspan_bulkin_cb(usb_pipe_handle_t, usb_bulk_req_t *);
+void	keyspan_bulkout_cb(usb_pipe_handle_t, usb_bulk_req_t *);
+
+int	keyspan_restore_device(keyspan_state_t *);
+int	keyspan_send_cmd(keyspan_port_t *);
+
+int	keyspan_dev_is_online(keyspan_state_t *);
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_USB_USBSER_KEYSPAN_VAR_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/usb/clients/usbser/usbser_keyspan/usa90msg.h	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,254 @@
+/*
+ * 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.
+ *
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_USB_USBSER_KEYSPAN_USA90MSG_H
+#define	_SYS_USB_USBSER_KEYSPAN_USA90MSG_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef struct keyspan_usa19hs_port_ctrl_msg keyspan_usa19hs_port_ctrl_msg_t;
+typedef struct keyspan_usa19hs_port_status_msg
+    keyspan_usa19hs_port_status_msg_t;
+
+/*
+ * usa90msg.h
+ *
+ * Copyright (c) 1998-2003 InnoSys Incorporated.  All Rights Reserved
+ * This file is available under a BSD-style copyright
+ *
+ * Keyspan USB Async Firmware to run on xxxxx
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain this licence text
+ * without modification, this list of conditions, and the following
+ * disclaimer.	The following copyright notice must appear immediately at
+ * the beginning of all source files:
+ *
+ *	Copyright (c) 1998-2000 InnoSys Incorporated.  All Rights Reserved
+ *
+ *	This file is available under a BSD-style copyright
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. The name of InnoSys Incorprated may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Revisions:
+ *
+ * 2003feb14		add setTxMode/txMode  and cancelRxXoff to portControl
+ *
+ */
+
+struct keyspan_usa19hs_port_ctrl_msg {
+	/*
+	 * there are three types of "commands" sent in the control message:
+	 *
+	 * configuration changes which must be requested by setting
+	 * the corresponding "set" flag (and should only be requested
+	 * when necessary, to reduce overhead on the device):
+	 */
+
+	uint8_t setClocking;	/* host requests baud rate be set */
+	uint8_t baudLo;		/* host does baud divisor calculation */
+	uint8_t baudHi;		/* host does baud divisor calculation */
+
+	uint8_t setLcr;		/* host requests lcr be set */
+	uint8_t lcr;		/* use PARITY, STOPBITS, DATABITS below */
+
+	uint8_t setRxMode;	/* set receive mode */
+	uint8_t rxMode;		/* RXMODE_DMA or RXMODE_BYHAND */
+
+	uint8_t setTxMode;	/* set transmit mode */
+	uint8_t txMode;		/* TXMODE_DMA or TXMODE_BYHAND */
+
+	/* host requests tx flow control be set */
+	uint8_t setTxFlowControl;
+	uint8_t txFlowControl;	/* use TX_FLOW... bits below */
+
+	/* host requests rx flow control be set */
+	uint8_t setRxFlowControl;
+	uint8_t rxFlowControl;	/* use RX_FLOW... bits below */
+	uint8_t sendXoff;	/* host requests XOFF transmitted immediately */
+	uint8_t sendXon;	/* host requests XON char transmitted */
+	uint8_t xonChar;	/* specified in current character format */
+	uint8_t xoffChar;	/* specified in current character format */
+
+	uint8_t sendChar;	/* host requests char transmitted immediately */
+	uint8_t txChar;		/* character to send */
+
+	uint8_t setRts;		/* host requests RTS output be set */
+	uint8_t rts;		/* 1=on, 0=off */
+	uint8_t setDtr;		/* host requests DTR output be set */
+	uint8_t dtr;		/* 1=on, 0=off */
+
+	/*
+	 * configuration data which is simply used as is
+	 * and must be specified correctly in every host message.
+	 */
+
+	/* forward when this number of chars available */
+	uint8_t rxForwardingLength;
+	uint8_t rxForwardingTimeout;	/* (1-31 in ms) */
+	uint8_t txAckSetting;	/* 0=don't ack, 1=normal, 2-255 TBD... */
+	/*
+	 * Firmware states which cause actions if they change
+	 * and must be specified correctly in every host message.
+	 */
+
+	uint8_t portEnabled;	/* 0=disabled, 1=enabled */
+	uint8_t txFlush;	/* 0=normal, 1=toss outbound data */
+	uint8_t txBreak;	/* 0=break off, 1=break on */
+	uint8_t loopbackMode;	/* 0=no loopback, 1=loopback enabled */
+
+	/*
+	 * commands which are flags only; these are processed in order
+	 * (so that, e.g., if rxFlush and rxForward flags are set, the
+	 * port will have no data to forward); any non-zero value
+	 * is respected
+	 */
+
+	uint8_t rxFlush;	/* toss inbound data */
+
+	/* forward all inbound data, NOW (as if fwdLen==1) */
+	uint8_t rxForward;
+	uint8_t cancelRxXoff;	/* cancel any receive XOFF state (_txXoff) */
+	uint8_t returnStatus;	/* return current status NOW */
+};
+
+/* defines for bits in lcr */
+#define		USA_DATABITS_5		0x00
+#define		USA_DATABITS_6		0x01
+#define		USA_DATABITS_7		0x02
+#define		USA_DATABITS_8		0x03
+#define		STOPBITS_5678_1		0x00 /* 1 stop bit for all byte sizes */
+#define		STOPBITS_5_1p5		0x04 /* 1.5 stop bits for 5-bit byte */
+#define		STOPBITS_678_2		0x04 /* 2 stop bits for 6-8 bit byte */
+#define		USA_PARITY_NONE		0x00
+#define		USA_PARITY_ODD		0x08
+#define		USA_PARITY_EVEN		0x18
+#define		PARITY_MARK_1		0x28 /* force parity MARK */
+#define		PARITY_SPACE_0		0x38 /* force parity SPACE */
+
+#define		TXFLOW_CTS			0x04
+#define		TXFLOW_DSR			0x08
+#define		TXFLOW_XOFF			0x01
+#define		TXFLOW_XOFF_ANY		0x02
+#define		TXFLOW_XOFF_BITS	(TXFLOW_XOFF | TXFLOW_XOFF_ANY)
+
+#define		RXFLOW_XOFF			0x10
+#define		RXFLOW_RTS			0x20
+#define		RXFLOW_DTR			0x40
+#define		RXFLOW_DSR_SENSITIVITY	0x80
+
+#define		RXMODE_BYHAND		0x00
+#define		RXMODE_DMA			0x02
+
+#define		TXMODE_BYHAND		0x00
+#define		TXMODE_DMA			0x02
+
+/* all things called "StatusMessage" are sent on the status endpoint */
+
+struct keyspan_usa19hs_port_status_msg {
+	uint8_t msr;		/* reports the actual MSR register */
+	uint8_t cts;		/* reports CTS pin */
+	uint8_t dcd;		/* reports DCD pin */
+	uint8_t dsr;		/* reports DSR pin */
+	uint8_t ri;		/* reports RI pin */
+	uint8_t _txXoff;	/* port is in XOFF state (we received XOFF) */
+	uint8_t rxBreak;	/* reports break state */
+
+	/* count of overrun errors (since last reported) */
+	uint8_t rxOverrun;
+
+	/* count of parity errors (since last reported) */
+	uint8_t rxParity;
+
+	/* count of frame errors (since last reported) */
+	uint8_t rxFrame;
+	uint8_t portState;	/* PORTSTATE_xxx bits (useful for debugging) */
+	uint8_t messageAck;	/* message acknowledgement */
+	uint8_t charAck;	/* character acknowledgement */
+
+	/* (value = returnStatus) a control message has been processed */
+	uint8_t controlResponse;
+};
+
+/* bits in RX data message when STAT byte is included */
+
+#define	RXERROR_OVERRUN		0x02
+#define	RXERROR_PARITY		0x04
+#define	RXERROR_FRAMING		0x08
+#define	RXERROR_BREAK		0x10
+
+#define	PORTSTATE_ENABLED	0x80
+#define	PORTSTATE_TXFLUSH	0x01
+#define	PORTSTATE_TXBREAK	0x02
+#define	PORTSTATE_LOOPBACK	0x04
+
+/* MSR bits */
+
+/* CTS has changed since last report */
+#define	USA_MSR_dCTS			0x01
+#define	USA_MSR_dDSR			0x02
+#define	USA_MSR_dRI			0x04
+#define	USA_MSR_dDCD			0x08
+
+#define	USA_MSR_CTS			0x10	/* current state of CTS */
+#define	USA_MSR_DSR			0x20
+#define	USA_USA_MSR_RI			0x40
+#define	MSR_DCD				0x80
+
+/* ie: the maximum length of an endpoint buffer */
+#define		MAX_DATA_LEN			64
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_USB_USBSER_KEYSPAN_USA90MSG_H */
--- a/usr/src/uts/intel/Makefile.intel	Sun Nov 13 16:47:18 2005 -0800
+++ b/usr/src/uts/intel/Makefile.intel	Mon Nov 14 01:27:09 2005 -0800
@@ -359,6 +359,7 @@
 DRV_KMODS	+= ugen
 DRV_KMODS	+= usbser
 DRV_KMODS	+= usbser_edge
+DRV_KMODS	+= usbsksp
 DRV_KMODS	+= usb_ac
 DRV_KMODS	+= usb_as
 DRV_KMODS	+= usbskel
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/usbsksp/Makefile	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,81 @@
+#
+# 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.
+#
+# 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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# uts/intel/usbsksp/Makefile
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives the production of the Keyspan USB Serial
+#	Adapter driver.
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= usbsksp
+OBJECTS		= $(USBSER_KEYSPAN_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(USBSER_KEYSPAN_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+#	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)
+
+.KEEP_STATE:
+
+all:		$(ALL_DEPS)
+
+def:		$(DEF_DEPS)
+
+clean:		$(CLEAN_DEPS);
+
+clobber:	$(CLOBBER_DEPS);
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
--- a/usr/src/uts/sparc/Makefile.sparc	Sun Nov 13 16:47:18 2005 -0800
+++ b/usr/src/uts/sparc/Makefile.sparc	Mon Nov 14 01:27:09 2005 -0800
@@ -249,7 +249,7 @@
 DRV_KMODS	+= ecpp
 DRV_KMODS	+= uata dad ifp
 DRV_KMODS	+= hid hubd ehci ohci uhci usb_mid scsa2usb usbprn ugen
-DRV_KMODS	+= usbser usbser_edge
+DRV_KMODS	+= usbser usbser_edge usbsksp
 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/usbsksp/Makefile	Mon Nov 14 01:27:09 2005 -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, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# uts/sparc/usbsksp/Makefile
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#
+#	This makefile drives the production of the Keyspan USB Serial
+#	Adapter driver.
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= usbsksp
+OBJECTS		= $(USBSER_KEYSPAN_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(USBSER_KEYSPAN_OBJS:%.o=$(LINTS_DIR)/%.ln)
+WARLOCK_OK	= $(MODULE).ok
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+#	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)
+
+.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_OUT	= usbser_keyspan.ll keyspan_dsd.ll \
+		  keyspan_pipe.ll
+WARLOCK_CMD	= usbser_keyspan.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) warlock_with_usba
+
+%.wlcmd:
+	$(TEST) -f $@ || $(SCCS) get $@
+
+$(WARLOCK_OK): warlock_with_usbser
+	$(TOUCH) $@
+
+%.ll: $(UTSBASE)/common/io/usb/clients/usbser/usbser_keyspan/%.c
+	$(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $<
+
+warlock_with_usbser: $(WARLOCK_OUT) usbser_files warlock_ddi.files
+	$(WARLOCK) -c $(WARLOCK_CMD) $(WARLOCK_OUT) \
+		$(USBSER_FILES) -l ../warlock/ddi_dki_impl.ll
+
+usbser_files:
+	@cd ../usbser; pwd; $(MAKE) usbser.ll
+
+warlock_with_usba: $(WARLOCK_CMD) $(WARLOCK_OUT) usbser_files \
+	usba_files ohci_files uhci_files ehci_files warlock_ddi.files
+	$(WARLOCK) -c usbser_keyspan_with_usba.wlcmd \
+		$(USBA_FILES) $(OHCI_FILES) $(EHCI_FILES) $(UHCI_FILES) \
+		$(USBSER_FILES) \
+		$(WARLOCK_OUT) -l ../warlock/ddi_dki_impl.ll
+
+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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/usbsksp/usbser_keyspan.wlcmd	Mon Nov 14 01:27:09 2005 -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, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+one usbser_state
+one keyspan_state
+one keyspan_pipe
+one keyspan_port
+
+### specify the root functions
+
+root usbser_soft_state_size
+root usbser_keyspan_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 keyspan_bulkin_cb
+root keyspan_bulkout_cb
+root keyspan_status_cb
+
+### specify keyspan function pointers
+
+add        ds_ops::ds_attach targets keyspan_attach
+add        ds_ops::ds_detach targets keyspan_detach
+add        ds_ops::ds_register_cb targets keyspan_register_cb
+add        ds_ops::ds_unregister_cb targets keyspan_unregister_cb
+add        ds_ops::ds_open_port targets keyspan_open_port
+add        ds_ops::ds_close_port targets keyspan_close_port
+add        ds_ops::ds_usb_power targets keyspan_usb_power
+add        ds_ops::ds_suspend targets keyspan_suspend
+add        ds_ops::ds_resume targets keyspan_resume
+add        ds_ops::ds_disconnect targets keyspan_disconnect
+add        ds_ops::ds_reconnect targets keyspan_reconnect
+add        ds_ops::ds_set_port_params targets keyspan_set_port_params
+add        ds_ops::ds_set_modem_ctl targets keyspan_set_modem_ctl
+add        ds_ops::ds_get_modem_ctl targets keyspan_get_modem_ctl
+add        ds_ops::ds_break_ctl targets keyspan_break_ctl
+add        ds_ops::ds_loopback targets keyspan_loopback
+add        ds_ops::ds_tx targets keyspan_tx
+add        ds_ops::ds_rx targets keyspan_rx
+add        ds_ops::ds_stop targets keyspan_stop
+add        ds_ops::ds_start targets keyspan_start
+add        ds_ops::ds_fifo_flush targets keyspan_fifo_flush
+add        ds_ops::ds_fifo_drain targets keyspan_fifo_drain
+
+add        keyspan_port::kp_cb.cb_tx targets usbser_tx_cb
+add        keyspan_port::kp_cb.cb_rx targets usbser_rx_cb
+add        keyspan_port::kp_cb.cb_status targets usbser_status_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/sparc/usbsksp/usbser_keyspan_with_usba.wlcmd	Mon Nov 14 01:27:09 2005 -0800
@@ -0,0 +1,215 @@
+#
+# 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.
+#
+# 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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+one ohci_state
+one ehci_state
+one uhci_state
+one usbser_state
+one keyspan_state
+one keyspan_pipe
+one keyspan_port
+
+### specify the root functions
+
+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 keyspan root functions
+root usbser_soft_state_size
+root usbser_keyspan_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 keyspan_bulkin_cb
+root keyspan_bulkout_cb
+root keyspan_status_cb
+
+### specify keyspan function pointers
+
+add        ds_ops::ds_attach targets keyspan_attach
+add        ds_ops::ds_detach targets keyspan_detach
+add        ds_ops::ds_register_cb targets keyspan_register_cb
+add        ds_ops::ds_unregister_cb targets keyspan_unregister_cb
+add        ds_ops::ds_open_port targets keyspan_open_port
+add        ds_ops::ds_close_port targets keyspan_close_port
+add        ds_ops::ds_usb_power targets keyspan_usb_power
+add        ds_ops::ds_suspend targets keyspan_suspend
+add        ds_ops::ds_resume targets keyspan_resume
+add        ds_ops::ds_disconnect targets keyspan_disconnect
+add        ds_ops::ds_reconnect targets keyspan_reconnect
+add        ds_ops::ds_set_port_params targets keyspan_set_port_params
+add        ds_ops::ds_set_modem_ctl targets keyspan_set_modem_ctl
+add        ds_ops::ds_get_modem_ctl targets keyspan_get_modem_ctl
+add        ds_ops::ds_break_ctl targets keyspan_break_ctl
+add        ds_ops::ds_loopback targets keyspan_loopback
+add        ds_ops::ds_tx targets keyspan_tx
+add        ds_ops::ds_rx targets keyspan_rx
+add        ds_ops::ds_stop targets keyspan_stop
+add        ds_ops::ds_start targets keyspan_start
+add        ds_ops::ds_fifo_flush targets keyspan_fifo_flush
+add        ds_ops::ds_fifo_drain targets keyspan_fifo_drain
+
+add        keyspan_port::kp_cb.cb_tx targets usbser_tx_cb
+add        keyspan_port::kp_cb.cb_rx targets usbser_rx_cb
+add        keyspan_port::kp_cb.cb_status targets usbser_status_cb
+
+add usb_ctrl_req::ctrl_cb	targets warlock_dummy
+add usb_ctrl_req::ctrl_exc_cb	targets warlock_dummy
+add usb_bulk_req::bulk_cb	targets keyspan_bulkin_cb
+add usb_bulk_req::bulk_exc_cb	targets keyspan_bulkin_cb
+add usb_bulk_req::bulk_cb	targets keyspan_bulkout_cb
+add usb_bulk_req::bulk_exc_cb	targets keyspan_bulkout_cb
+add usb_bulk_req::bulk_cb	targets keyspan_status_cb
+add usb_bulk_req::bulk_exc_cb	targets keyspan_status_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/sparc/warlock/Makefile	Sun Nov 13 16:47:18 2005 -0800
+++ b/usr/src/uts/sparc/warlock/Makefile	Mon Nov 14 01:27:09 2005 -0800
@@ -84,6 +84,7 @@
 	@cd ../usbprn; rm -f *.ll *.ok; $(MAKE) warlock
 	@cd ../usbser; rm -f *.ll *.ok; $(MAKE) warlock
 	@cd ../usbser_edge; rm -f *.ll *.ok; $(MAKE) warlock
+	@cd ../usbsksp; rm -f *.ll *.ok; $(MAKE) warlock
 	@cd ../usbskel; rm -f *.ll *.ok; $(MAKE) warlock
 
 warlock.scsi: