Mercurial > illumos > illumos-gate
changeset 901:51e564a5263e
6314455 Support for Keyspan USB serial adapters
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: