|
diff --git a/usr/src/pkg/manifests/driver-serial-uchcom.mf b/usr/src/pkg/manifests/driver-serial-uchcom.mf |
|
new file mode 100644 |
|
index 0000000000..e06c57bd0e |
|
--- /dev/null |
|
+++ b/usr/src/pkg/manifests/driver-serial-uchcom.mf |
|
@@ -0,0 +1,50 @@ |
|
+# |
|
+# CDDL HEADER START |
|
+# |
|
+# The contents of this file are subject to the terms of the |
|
+# Common Development and Distribution License (the "License"). |
|
+# You may not use this file except in compliance with the License. |
|
+# |
|
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
+# or http://www.opensolaris.org/os/licensing. |
|
+# See the License for the specific language governing permissions |
|
+# and limitations under the License. |
|
+# |
|
+# When distributing Covered Code, include this CDDL HEADER in each |
|
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
+# If applicable, add the following below this CDDL HEADER, with the |
|
+# fields enclosed by brackets "[]" replaced with your own identifying |
|
+# information: Portions Copyright [yyyy] [name of copyright owner] |
|
+# |
|
+# CDDL HEADER END |
|
+# |
|
+ |
|
+# |
|
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. |
|
+# |
|
+ |
|
+# |
|
+# The default for payload-bearing actions in this package is to appear in the |
|
+# global zone only. See the include file for greater detail, as well as |
|
+# information about overriding the defaults. |
|
+# |
|
+<include global_zone_only_component> |
|
+set name=pkg.fmri value=pkg:/driver/serial/uchcom@$(PKGVERS) |
|
+set name=pkg.description value="CH340/CH341 serial driver" |
|
+set name=pkg.summary value="UCHCOM" |
|
+set name=info.classification value=org.opensolaris.category.2008:Drivers/Ports |
|
+set name=variant.arch value=$(ARCH) |
|
+dir path=kernel group=sys |
|
+dir path=kernel/drv group=sys |
|
+dir path=kernel/drv/$(ARCH64) group=sys |
|
+dir path=usr/share/man |
|
+dir path=usr/share/man/man7d |
|
+driver name=usbftdi perms="* 0666 root sys" \ |
|
+ alias=usb1a86,5523 \ |
|
+ alias=usb1a86,7523 |
|
+file path=kernel/drv/$(ARCH64)/uchcom group=sys |
|
+file path=usr/share/man/man7d/uchcom.7d |
|
+legacy pkg=SUNWuftdi desc="CH340/CH341 serial driver" \ |
|
+ name="UCHCOM" |
|
+license cr_Sun license=cr_Sun |
|
+license lic_CDDL license=lic_CDDL |
|
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files |
|
index 6cc78d6fb3..1f3b51f2e2 100644 |
|
--- a/usr/src/uts/common/Makefile.files |
|
+++ b/usr/src/uts/common/Makefile.files |
|
@@ -829,6 +829,8 @@ USBSPRL_OBJS += usbser_pl2303.o pl2303_dsd.o |
|
|
|
USBFTDI_OBJS += usbser_uftdi.o uftdi_dsd.o |
|
|
|
+UCHCOM_OBJS += usbser_uchcom.o uchcom_dsd.o |
|
+ |
|
USBECM_OBJS += usbecm.o |
|
|
|
WC_OBJS += wscons.o vcons.o |
|
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules |
|
index c2502f9ebb..6a65927840 100644 |
|
--- a/usr/src/uts/common/Makefile.rules |
|
+++ b/usr/src/uts/common/Makefile.rules |
|
@@ -1248,6 +1248,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usb/clients/usbser/usbsprl/%.c |
|
$(COMPILE.c) -o $@ $< |
|
$(CTFCONVERT_O) |
|
|
|
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usb/clients/usbser/uchcom/%.c |
|
+ $(COMPILE.c) -o $@ $< |
|
+ $(CTFCONVERT_O) |
|
+ |
|
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/usb/clients/usbecm/%.c |
|
$(COMPILE.c) -o $@ $< |
|
$(CTFCONVERT_O) |
|
@@ -2541,6 +2545,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/clients/usbser/usbser_keyspan/%.c |
|
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/clients/usbser/usbsprl/%.c |
|
@($(LHEAD) $(LINT.c) $< $(LTAIL)) |
|
|
|
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/clients/usbser/uchcom/%.c |
|
+ @($(LHEAD) $(LINT.c) $< $(LTAIL)) |
|
+ |
|
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/usb/clients/usbecm/%.c |
|
@($(LHEAD) $(LINT.c) $< $(LTAIL)) |
|
|
|
diff --git a/usr/src/uts/common/io/usb/clients/usbser/uchcom/uchcom_dsd.c b/usr/src/uts/common/io/usb/clients/usbser/uchcom/uchcom_dsd.c |
|
new file mode 100644 |
|
index 0000000000..08dd34b49c |
|
--- /dev/null |
|
+++ b/usr/src/uts/common/io/usb/clients/usbser/uchcom/uchcom_dsd.c |
|
@@ -0,0 +1,1738 @@ |
|
+/* |
|
+ * CDDL HEADER START |
|
+ * |
|
+ * The contents of this file are subject to the terms of the |
|
+ * Common Development and Distribution License (the "License"). |
|
+ * You may not use this file except in compliance with the License. |
|
+ * |
|
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
+ * or http://www.opensolaris.org/os/licensing. |
|
+ * See the License for the specific language governing permissions |
|
+ * and limitations under the License. |
|
+ * |
|
+ * When distributing Covered Code, include this CDDL HEADER in each |
|
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
+ * If applicable, add the following below this CDDL HEADER, with the |
|
+ * fields enclosed by brackets "[]" replaced with your own identifying |
|
+ * information: Portions Copyright [yyyy] [name of copyright owner] |
|
+ * |
|
+ * CDDL HEADER END |
|
+ */ |
|
+ |
|
+/* |
|
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
|
+ * Use is subject to license terms. |
|
+ */ |
|
+ |
|
+/* |
|
+ * Copyright 2020 Jorge Schrauwen <[email protected]> |
|
+ */ |
|
+ |
|
+/* |
|
+ * DSD code for WinChipHead CH341/340 adapter |
|
+ */ |
|
+ |
|
+#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/usba/usba_types.h> |
|
+#include <sys/usb/usba/usba_impl.h> |
|
+ |
|
+#include <sys/usb/clients/usbser/usbser_dsdi.h> |
|
+#include <sys/usb/clients/usbser/uchcom/uchcom_var.h> |
|
+#include <sys/usb/clients/usbser/uchcom/uchcom_reg.h> |
|
+ |
|
+#include <sys/usb/usbdevs.h> |
|
+ |
|
+#include <sys/cmn_err.h> |
|
+ |
|
+/* |
|
+ * DSD operations |
|
+ */ |
|
+static int uchcom_attach(ds_attach_info_t *); |
|
+static void uchcom_detach(ds_hdl_t); |
|
+static int uchcom_register_cb(ds_hdl_t, uint_t, ds_cb_t *); |
|
+static void uchcom_unregister_cb(ds_hdl_t, uint_t); |
|
+static int uchcom_open_port(ds_hdl_t, uint_t); |
|
+static int uchcom_close_port(ds_hdl_t, uint_t); |
|
+ |
|
+/* power management */ |
|
+static int uchcom_usb_power(ds_hdl_t, int, int, int *); |
|
+static int uchcom_suspend(ds_hdl_t); |
|
+static int uchcom_resume(ds_hdl_t); |
|
+static int uchcom_disconnect(ds_hdl_t); |
|
+static int uchcom_reconnect(ds_hdl_t); |
|
+ |
|
+/* standard UART operations */ |
|
+static int uchcom_set_port_params(ds_hdl_t, uint_t, ds_port_params_t *); |
|
+static int uchcom_set_modem_ctl(ds_hdl_t, uint_t, int, int); |
|
+static int uchcom_get_modem_ctl(ds_hdl_t, uint_t, int, int *); |
|
+static int uchcom_break_ctl(ds_hdl_t, uint_t, int); |
|
+ |
|
+/* data xfer */ |
|
+static int uchcom_tx(ds_hdl_t, uint_t, mblk_t *); |
|
+static mblk_t *uchcom_rx(ds_hdl_t, uint_t); |
|
+static void uchcom_stop(ds_hdl_t, uint_t, int); |
|
+static void uchcom_start(ds_hdl_t, uint_t, int); |
|
+static int uchcom_fifo_flush(ds_hdl_t, uint_t, int); |
|
+static int uchcom_fifo_drain(ds_hdl_t, uint_t, int); |
|
+ |
|
+/* polled I/O support */ |
|
+static usb_pipe_handle_t uchcom_out_pipe(ds_hdl_t, uint_t); |
|
+static usb_pipe_handle_t uchcom_in_pipe(ds_hdl_t, uint_t); |
|
+ |
|
+/* |
|
+ * Sub-routines |
|
+ */ |
|
+ |
|
+/* configuration routines */ |
|
+static void uchcom_cleanup(uchcom_state_t *, int); |
|
+static int uchcom_dev_attach(uchcom_state_t *); |
|
+static int uchcom_open_hw_port(uchcom_state_t *); |
|
+ |
|
+/* hotplug */ |
|
+static int uchcom_restore_device_state(uchcom_state_t *); |
|
+static int uchcom_restore_port_state(uchcom_state_t *); |
|
+ |
|
+/* power management */ |
|
+static int uchcom_create_pm_components(uchcom_state_t *); |
|
+static void uchcom_destroy_pm_components(uchcom_state_t *); |
|
+static int uchcom_pm_set_busy(uchcom_state_t *); |
|
+static void uchcom_pm_set_idle(uchcom_state_t *); |
|
+static int uchcom_pwrlvl0(uchcom_state_t *); |
|
+static int uchcom_pwrlvl1(uchcom_state_t *); |
|
+static int uchcom_pwrlvl2(uchcom_state_t *); |
|
+static int uchcom_pwrlvl3(uchcom_state_t *); |
|
+ |
|
+/* pipe operations */ |
|
+static int uchcom_open_pipes(uchcom_state_t *); |
|
+static void uchcom_close_pipes(uchcom_state_t *); |
|
+static void uchcom_disconnect_pipes(uchcom_state_t *); |
|
+static int uchcom_reconnect_pipes(uchcom_state_t *); |
|
+ |
|
+/* pipe callbacks */ |
|
+static void uchcom_bulkin_cb(usb_pipe_handle_t, usb_bulk_req_t *); |
|
+static void uchcom_bulkout_cb(usb_pipe_handle_t, usb_bulk_req_t *); |
|
+ |
|
+/* data transfer routines */ |
|
+static int uchcom_rx_start(uchcom_state_t *); |
|
+static void uchcom_tx_start(uchcom_state_t *, int *); |
|
+static int uchcom_send_data(uchcom_state_t *, mblk_t *); |
|
+static int uchcom_wait_tx_drain(uchcom_state_t *, int); |
|
+ |
|
+/* vendor-specific commands */ |
|
+static int uchcom_cmd_vendor_write0(uchcom_state_t *, |
|
+ uint16_t, uint16_t, uint16_t); |
|
+static int uchcom_cmd_vendor_read0(uchcom_state_t *, |
|
+ uint16_t, uint16_t, uint16_t, void *, uint16_t); |
|
+ |
|
+/* misc */ |
|
+static void uchcom_put_tail(mblk_t **, mblk_t *); |
|
+static void uchcom_put_head(mblk_t **, mblk_t *); |
|
+ |
|
+ |
|
+/* |
|
+ * DSD ops structure |
|
+ */ |
|
+ds_ops_t uchcom_ds_ops = { |
|
+ DS_OPS_VERSION, |
|
+ uchcom_attach, |
|
+ uchcom_detach, |
|
+ uchcom_register_cb, |
|
+ uchcom_unregister_cb, |
|
+ uchcom_open_port, |
|
+ uchcom_close_port, |
|
+ uchcom_usb_power, |
|
+ uchcom_suspend, |
|
+ uchcom_resume, |
|
+ uchcom_disconnect, |
|
+ uchcom_reconnect, |
|
+ uchcom_set_port_params, |
|
+ uchcom_set_modem_ctl, |
|
+ uchcom_get_modem_ctl, |
|
+ uchcom_break_ctl, |
|
+ NULL, /* no loopback support */ |
|
+ uchcom_tx, |
|
+ uchcom_rx, |
|
+ uchcom_stop, |
|
+ uchcom_start, |
|
+ uchcom_fifo_flush, |
|
+ uchcom_fifo_drain, |
|
+ uchcom_out_pipe, |
|
+ uchcom_in_pipe |
|
+}; |
|
+ |
|
+/* debug support */ |
|
+static uint_t uchcom_errlevel = USB_LOG_L4; |
|
+static uint_t uchcom_errmask = DPRINT_MASK_ALL; |
|
+static uint_t uchcom_instance_debug = (uint_t)-1; |
|
+ |
|
+/* |
|
+ * ds_attach |
|
+ */ |
|
+static int |
|
+uchcom_attach(ds_attach_info_t *aip) |
|
+{ |
|
+ uchcom_state_t *uch; |
|
+ usb_dev_descr_t *dd; |
|
+ boolean_t recognized; |
|
+ |
|
+ uch = kmem_zalloc(sizeof (*uch), KM_SLEEP); |
|
+ uch->uch_dip = aip->ai_dip; |
|
+ uch->uch_usb_events = aip->ai_usb_events; |
|
+ *aip->ai_hdl = (ds_hdl_t)uch; |
|
+ |
|
+ /* only one port */ |
|
+ *aip->ai_port_cnt = 1; |
|
+ |
|
+ if (usb_client_attach(uch->uch_dip, USBDRV_VERSION, 0) != USB_SUCCESS) { |
|
+ uchcom_cleanup(uch, 1); |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ |
|
+ if (usb_get_dev_data(uch->uch_dip, |
|
+ &uch->uch_dev_data, USB_PARSE_LVL_IF, 0) != USB_SUCCESS) { |
|
+ uchcom_cleanup(uch, 2); |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ |
|
+ mutex_init(&uch->uch_lock, NULL, MUTEX_DRIVER, |
|
+ uch->uch_dev_data->dev_iblock_cookie); |
|
+ |
|
+ cv_init(&uch->uch_tx_cv, NULL, CV_DRIVER, NULL); |
|
+ |
|
+ uch->uch_lh = usb_alloc_log_hdl(uch->uch_dip, "uchcom", |
|
+ &uchcom_errlevel, &uchcom_errmask, &uchcom_instance_debug, 0); |
|
+ |
|
+ |
|
+ recognized = B_FALSE; |
|
+ dd = uch->uch_dev_data->dev_descr; |
|
+ if (dd->idVendor == UCHCOM_VENDOR) { |
|
+ switch (dd->idProduct) { |
|
+ case UCHCOM_CHIP_CH340: |
|
+ USB_DPRINTF_L3(DPRINT_ATTACH, uch->uch_lh, |
|
+ "Chip Type: CH340"); |
|
+ recognized = B_TRUE; |
|
+ break; |
|
+ case UCHCOM_CHIP_CH341: |
|
+ USB_DPRINTF_L3(DPRINT_ATTACH, uch->uch_lh, |
|
+ "Chip Type: CH341"); |
|
+ recognized = B_TRUE; |
|
+ break; |
|
+ } |
|
+ } |
|
+ |
|
+ if (!recognized) { |
|
+ uchcom_cleanup(uch, 3); |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ |
|
+ uch->uch_def_ph = uch->uch_dev_data->dev_default_ph; |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ uch->uch_dev_state = USB_DEV_ONLINE; |
|
+ uch->uch_port_state = UCHCOM_PORT_CLOSED; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ if (uchcom_create_pm_components(uch) != USB_SUCCESS) { |
|
+ cmn_err(CE_WARN, "uchcom: fail pmc"); |
|
+ uchcom_cleanup(uch, 3); |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ |
|
+ if (usb_register_event_cbs(uch->uch_dip, |
|
+ uch->uch_usb_events, 0) != USB_SUCCESS) { |
|
+ uchcom_cleanup(uch, 4); |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ |
|
+ if (uchcom_dev_attach(uch) != USB_SUCCESS) { |
|
+ uchcom_cleanup(uch, 5); |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_detach |
|
+ */ |
|
+static void |
|
+uchcom_detach(ds_hdl_t hdl) |
|
+{ |
|
+ uchcom_cleanup((uchcom_state_t *)hdl, UCHCOM_CLEANUP_LEVEL_MAX); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_register_cb |
|
+ */ |
|
+/*ARGSUSED*/ |
|
+static int |
|
+uchcom_register_cb(ds_hdl_t hdl, uint_t portno, ds_cb_t *cb) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ ASSERT(portno == 0); |
|
+ |
|
+ uch->uch_cb = *cb; |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_unregister_cb |
|
+ */ |
|
+/*ARGSUSED*/ |
|
+static void |
|
+uchcom_unregister_cb(ds_hdl_t hdl, uint_t portno) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ ASSERT(portno == 0); |
|
+ |
|
+ bzero(&uch->uch_cb, sizeof (uch->uch_cb)); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_open_port |
|
+ */ |
|
+/*ARGSUSED*/ |
|
+static int |
|
+uchcom_open_port(ds_hdl_t hdl, uint_t portno) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ int rval; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_OPEN, uch->uch_lh, "uchcom_open_port %d", portno); |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ if (uch->uch_dev_state == USB_DEV_DISCONNECTED || |
|
+ uch->uch_port_state != UCHCOM_PORT_CLOSED) { |
|
+ mutex_exit(&uch->uch_lock); |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ if ((rval = uchcom_pm_set_busy(uch)) != USB_SUCCESS) |
|
+ return (rval); |
|
+ |
|
+ /* initialize hardware serial port */ |
|
+ rval = uchcom_open_hw_port(uch); |
|
+ |
|
+ if (rval == USB_SUCCESS) { |
|
+ mutex_enter(&uch->uch_lock); |
|
+ |
|
+ /* start to receive data */ |
|
+ if (uchcom_rx_start(uch) != USB_SUCCESS) { |
|
+ mutex_exit(&uch->uch_lock); |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ uch->uch_port_state = UCHCOM_PORT_OPEN; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ } else |
|
+ uchcom_pm_set_idle(uch); |
|
+ |
|
+ return (rval); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_close_port |
|
+ */ |
|
+/*ARGSUSED*/ |
|
+static int |
|
+uchcom_close_port(ds_hdl_t hdl, uint_t portno) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_CLOSE, uch->uch_lh, "uchcom_close_port %d", portno); |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ |
|
+ /* free resources and finalize state */ |
|
+ freemsg(uch->uch_rx_mp); |
|
+ uch->uch_rx_mp = NULL; |
|
+ |
|
+ freemsg(uch->uch_tx_mp); |
|
+ uch->uch_tx_mp = NULL; |
|
+ |
|
+ uch->uch_port_state = UCHCOM_PORT_CLOSED; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ uchcom_pm_set_idle(uch); |
|
+ |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_usb_power |
|
+ */ |
|
+/*ARGSUSED*/ |
|
+static int |
|
+uchcom_usb_power(ds_hdl_t hdl, int comp, int level, int *new_state) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ uchcom_pm_t *pm = uch->uch_pm; |
|
+ int rval; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_PM, uch->uch_lh, "uchcom_usb_power"); |
|
+ |
|
+ if (!pm) |
|
+ return (USB_FAILURE); |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ |
|
+ /* |
|
+ * 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, uch->uch_lh, "uchcom_usb_power: " |
|
+ "illegal power level %d, pwr_states=0x%x", |
|
+ level, pm->pm_pwr_states); |
|
+ mutex_exit(&uch->uch_lock); |
|
+ 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(&uch->uch_lock); |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ |
|
+ switch (level) { |
|
+ case USB_DEV_OS_PWR_OFF: |
|
+ rval = uchcom_pwrlvl0(uch); |
|
+ break; |
|
+ case USB_DEV_OS_PWR_1: |
|
+ rval = uchcom_pwrlvl1(uch); |
|
+ break; |
|
+ case USB_DEV_OS_PWR_2: |
|
+ rval = uchcom_pwrlvl2(uch); |
|
+ break; |
|
+ case USB_DEV_OS_FULL_PWR: |
|
+ rval = uchcom_pwrlvl3(uch); |
|
+ /* |
|
+ * If usbser dev_state is DISCONNECTED or SUSPENDED, it shows |
|
+ * that the usb serial device is disconnected/suspended while it |
|
+ * is under power down state, now the device is powered up |
|
+ * before it is reconnected/resumed. xxx_pwrlvl3() will set dev |
|
+ * state to ONLINE, we need to set the dev state back to |
|
+ * DISCONNECTED/SUSPENDED. |
|
+ */ |
|
+ if (rval == USB_SUCCESS && |
|
+ (*new_state == USB_DEV_DISCONNECTED || |
|
+ *new_state == USB_DEV_SUSPENDED)) |
|
+ uch->uch_dev_state = *new_state; |
|
+ break; |
|
+ default: |
|
+ ASSERT(0); /* cannot happen */ |
|
+ } |
|
+ |
|
+ *new_state = uch->uch_dev_state; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ return (rval); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_suspend |
|
+ */ |
|
+static int |
|
+uchcom_suspend(ds_hdl_t hdl) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ int state = USB_DEV_SUSPENDED; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_PM, uch->uch_lh, "uchcom_suspend"); |
|
+ |
|
+ /* |
|
+ * If the device is suspended while it is under PWRED_DOWN state, we |
|
+ * need to keep the PWRED_DOWN state so that it could be powered up |
|
+ * later. In the mean while, usbser dev state will be changed to |
|
+ * SUSPENDED state. |
|
+ */ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ if (uch->uch_dev_state != USB_DEV_PWRED_DOWN) |
|
+ uch->uch_dev_state = USB_DEV_SUSPENDED; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ uchcom_disconnect_pipes(uch); |
|
+ return (state); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_resume |
|
+ */ |
|
+static int |
|
+uchcom_resume(ds_hdl_t hdl) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ int current_state; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_PM, uch->uch_lh, "uchcom_resume"); |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ current_state = uch->uch_dev_state; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ if (current_state == USB_DEV_ONLINE) |
|
+ return (USB_SUCCESS); |
|
+ |
|
+ return (uchcom_restore_device_state(uch)); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_disconnect |
|
+ */ |
|
+static int |
|
+uchcom_disconnect(ds_hdl_t hdl) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ int state = USB_DEV_DISCONNECTED; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_HOTPLUG, uch->uch_lh, "uchcom_disconnect"); |
|
+ |
|
+ /* |
|
+ * If the device is disconnected while it is under PWRED_DOWN state, we |
|
+ * need to keep the PWRED_DOWN state so that it could be powered up |
|
+ * later. In the mean while, usbser dev state will be changed to |
|
+ * DISCONNECTED state. |
|
+ */ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ if (uch->uch_dev_state != USB_DEV_PWRED_DOWN) |
|
+ uch->uch_dev_state = USB_DEV_DISCONNECTED; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ uchcom_disconnect_pipes(uch); |
|
+ |
|
+ return (state); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_reconnect |
|
+ */ |
|
+static int |
|
+uchcom_reconnect(ds_hdl_t hdl) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_HOTPLUG, uch->uch_lh, "uchcom_reconnect"); |
|
+ |
|
+ return (uchcom_restore_device_state(uch)); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_set_port_params |
|
+ */ |
|
+static int |
|
+uchcom_set_port_params(ds_hdl_t hdl, uint_t portno, ds_port_params_t *tp) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ int rval; |
|
+ int i; |
|
+ ds_port_param_entry_t *pe; |
|
+ |
|
+ if (tp == NULL) |
|
+ return (USB_FAILURE); |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_CTLOP, uch->uch_lh, "uchcom_set_port_params"); |
|
+ |
|
+ rval = uchcom_cmd_vendor_write0(uch, UCHCOM_REQ_RESET, 0, 0); |
|
+ if (rval != USB_SUCCESS) { |
|
+ USB_DPRINTF_L2(DPRINT_DEF_PIPE, uch->uch_lh, |
|
+ "uchcom_set_port_params: failed to reset!"); |
|
+ return (rval); |
|
+ } |
|
+ |
|
+ for (i = 0, pe = tp->tp_entries; i < tp->tp_cnt; i++, pe++) { |
|
+ switch (pe->param) { |
|
+ case DS_PARAM_BAUD: |
|
+ switch (pe->val.ui) { |
|
+ case B300: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params br 300"); |
|
+ break; |
|
+ case B600: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params br 600"); |
|
+ break; |
|
+ case B1200: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params br 1200"); |
|
+ break; |
|
+ case B2400: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params br 2400"); |
|
+ break; |
|
+ case B4800: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params br 4800"); |
|
+ break; |
|
+ case B9600: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params br 9400"); |
|
+ break; |
|
+ case B19200: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params br 19200"); |
|
+ break; |
|
+ case B38400: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params br 38400"); |
|
+ break; |
|
+ case B57600: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params br 57600"); |
|
+ break; |
|
+ case B115200: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params br 115200"); |
|
+ break; |
|
+ case B230400: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params br 230400"); |
|
+ break; |
|
+ case B460800: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params br 460800"); |
|
+ break; |
|
+ case B921600: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params br 921600"); |
|
+ break; |
|
+ default: |
|
+ USB_DPRINTF_L3(DPRINT_CTLOP, uch->uch_lh, |
|
+ "uchcom_set_port_params: bad baud %d", |
|
+ pe->val.ui); |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ break; |
|
+ |
|
+ case DS_PARAM_PARITY: |
|
+ if (pe->val.ui & PARENB) { |
|
+ if (pe->val.ui & PARODD) |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params par odd"); |
|
+ else |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params par even"); |
|
+ } else { |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params par none"); |
|
+ } |
|
+ break; |
|
+ |
|
+ case DS_PARAM_STOPB: |
|
+ if (pe->val.ui & CSTOPB) |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params stop bits2"); |
|
+ else { |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params stop bits1"); |
|
+ } |
|
+ break; |
|
+ |
|
+ case DS_PARAM_CHARSZ: |
|
+ switch (pe->val.ui) { |
|
+ case CS8: |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params csz cs8"); |
|
+ break; |
|
+ default: |
|
+ USB_DPRINTF_L3(DPRINT_CTLOP, uch->uch_lh, |
|
+ "uchcom_set_port_params: bad charsz"); |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ break; |
|
+ |
|
+ case DS_PARAM_XON_XOFF: /* Software flow control */ |
|
+ if ((pe->val.ui & IXON) || (pe->val.ui & IXOFF)) { |
|
+ uint8_t xonc = pe->val.uc[0]; |
|
+ uint8_t xoffc = pe->val.uc[1]; |
|
+ |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params soft flow ???"); |
|
+ } |
|
+ break; |
|
+ |
|
+ case DS_PARAM_FLOW_CTL: /* Hardware flow control */ |
|
+ if (pe->val.ui & (RTSXOFF | CTSXON)) { |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params hard flow RTS_CTS_HS"); |
|
+ } |
|
+ if (pe->val.ui & DTRXOFF) { |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_port_params hard flow DTR_DSR_HS"); |
|
+ } |
|
+ break; |
|
+ default: |
|
+ USB_DPRINTF_L2(DPRINT_CTLOP, uch->uch_lh, |
|
+ "uchcom_set_port_params: bad param %d", pe->param); |
|
+ break; |
|
+ } |
|
+ } |
|
+ |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_set_modem_ctl |
|
+ */ |
|
+static int |
|
+uchcom_set_modem_ctl(ds_hdl_t hdl, uint_t portno, int mask, int val) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_CTLOP, uch->uch_lh, "uchcom_set_modem_ctl"); |
|
+ |
|
+ // XXX: chip is weird and picky, figure out later |
|
+ cmn_err(CE_WARN, "XXX: uchcom_set_modem_ctl"); |
|
+ |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_get_modem_ctl |
|
+ */ |
|
+static int |
|
+uchcom_get_modem_ctl(ds_hdl_t hdl, uint_t portno, int mask, int *valp) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_CTLOP, uch->uch_lh, "uchcom_get_modem_ctl"); |
|
+ |
|
+ // XXX: chip is weird and picky, figure out later |
|
+ cmn_err(CE_WARN, "XXX: uchcom_get_modem_ctl"); |
|
+ |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_break_ctl |
|
+ */ |
|
+static int |
|
+uchcom_break_ctl(ds_hdl_t hdl, uint_t portno, int ctl) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_CTLOP, uch->uch_lh, "uchcom_break_ctl"); |
|
+ |
|
+ // XXX: chip is weird and picky, figure out later |
|
+ cmn_err(CE_WARN, "XXX: uchcom_break_ctl"); |
|
+ |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_tx |
|
+ */ |
|
+/*ARGSUSED*/ |
|
+static int |
|
+uchcom_tx(ds_hdl_t hdl, uint_t portno, mblk_t *mp) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_CTLOP, uch->uch_lh, "uchcom_tx"); |
|
+ |
|
+ ASSERT(mp != NULL && MBLKL(mp) >= 1); |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ uchcom_put_tail(&uch->uch_tx_mp, mp); /* add to the chain */ |
|
+ uchcom_tx_start(uch, NULL); |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+ |
|
+/* |
|
+ * ds_rx |
|
+ */ |
|
+/*ARGSUSED*/ |
|
+static mblk_t * |
|
+uchcom_rx(ds_hdl_t hdl, uint_t portno) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ mblk_t *mp; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_CTLOP, uch->uch_lh, "uchcom_rx"); |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ mp = uch->uch_rx_mp; |
|
+ uch->uch_rx_mp = NULL; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ return (mp); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_stop |
|
+ */ |
|
+/*ARGSUSED*/ |
|
+static void |
|
+uchcom_stop(ds_hdl_t hdl, uint_t portno, int dir) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_CTLOP, uch->uch_lh, "uchcom_stop"); |
|
+ |
|
+ if (dir & DS_TX) { |
|
+ mutex_enter(&uch->uch_lock); |
|
+ uch->uch_port_flags |= UCHCOM_PORT_TX_STOPPED; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ } |
|
+} |
|
+ |
|
+ |
|
+/* |
|
+ * ds_start |
|
+ */ |
|
+/*ARGSUSED*/ |
|
+static void |
|
+uchcom_start(ds_hdl_t hdl, uint_t portno, int dir) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_CTLOP, uch->uch_lh, "uchcom_start"); |
|
+ |
|
+ if (dir & DS_TX) { |
|
+ mutex_enter(&uch->uch_lock); |
|
+ if (uch->uch_port_flags & UCHCOM_PORT_TX_STOPPED) { |
|
+ uch->uch_port_flags &= ~UCHCOM_PORT_TX_STOPPED; |
|
+ uchcom_tx_start(uch, NULL); |
|
+ } |
|
+ mutex_exit(&uch->uch_lock); |
|
+ } |
|
+} |
|
+ |
|
+/* |
|
+ * ds_fifo_flush |
|
+ */ |
|
+/*ARGSUSED*/ |
|
+static int |
|
+uchcom_fifo_flush(ds_hdl_t hdl, uint_t portno, int dir) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_CTLOP, uch->uch_lh, |
|
+ "uchcom_fifo_flush: dir=0x%x", dir); |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ ASSERT(uch->uch_port_state == UCHCOM_PORT_OPEN); |
|
+ |
|
+ if (dir & DS_TX) { |
|
+ freemsg(uch->uch_tx_mp); |
|
+ uch->uch_tx_mp = NULL; |
|
+ } |
|
+ |
|
+ if (dir & DS_RX) { |
|
+ freemsg(uch->uch_rx_mp); |
|
+ uch->uch_rx_mp = NULL; |
|
+ } |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+/* |
|
+ * ds_fifo_drain |
|
+ */ |
|
+/*ARGSUSED*/ |
|
+static int |
|
+uchcom_fifo_drain(ds_hdl_t hdl, uint_t portno, int timeout) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_CTLOP, uch->uch_lh, "uchcom_fifo_drain"); |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ ASSERT(uch->uch_port_state == UCHCOM_PORT_OPEN); |
|
+ |
|
+ if (uchcom_wait_tx_drain(uch, 0) != USB_SUCCESS) { |
|
+ mutex_exit(&uch->uch_lock); |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ /* wait 500 ms until hw fifo drains */ |
|
+ delay(drv_usectohz(500*1000)); |
|
+ |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+/*ARGSUSED*/ |
|
+static usb_pipe_handle_t |
|
+uchcom_out_pipe(ds_hdl_t hdl, uint_t port_num) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ return (uch->uch_bulkout_ph); |
|
+} |
|
+ |
|
+/*ARGSUSED*/ |
|
+static usb_pipe_handle_t |
|
+uchcom_in_pipe(ds_hdl_t hdl, uint_t port_num) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)hdl; |
|
+ |
|
+ return (uch->uch_bulkin_ph); |
|
+} |
|
+ |
|
+/* |
|
+ * configuration clean up |
|
+ */ |
|
+static void |
|
+uchcom_cleanup(uchcom_state_t *uch, int level) |
|
+{ |
|
+ ASSERT(level > 0 && level <= UCHCOM_CLEANUP_LEVEL_MAX); |
|
+ |
|
+ switch (level) { |
|
+ default: |
|
+ case 6: |
|
+ uchcom_close_pipes(uch); |
|
+ /*FALLTHROUGH*/ |
|
+ case 5: |
|
+ usb_unregister_event_cbs(uch->uch_dip, uch->uch_usb_events); |
|
+ /*FALLTHROUGH*/ |
|
+ case 4: |
|
+ uchcom_destroy_pm_components(uch); |
|
+ /*FALLTHROUGH*/ |
|
+ case 3: |
|
+ mutex_destroy(&uch->uch_lock); |
|
+ cv_destroy(&uch->uch_tx_cv); |
|
+ |
|
+ usb_free_log_hdl(uch->uch_lh); |
|
+ uch->uch_lh = NULL; |
|
+ |
|
+ usb_free_descr_tree(uch->uch_dip, uch->uch_dev_data); |
|
+ uch->uch_def_ph = NULL; |
|
+ /*FALLTHROUGH*/ |
|
+ case 2: |
|
+ usb_client_detach(uch->uch_dip, uch->uch_dev_data); |
|
+ /*FALLTHROUGH*/ |
|
+ case 1: |
|
+ kmem_free(uch, sizeof (*uch)); |
|
+ break; |
|
+ } |
|
+} |
|
+ |
|
+/* |
|
+ * device specific attach |
|
+ */ |
|
+static int |
|
+uchcom_dev_attach(uchcom_state_t *uch) |
|
+{ |
|
+ return (uchcom_open_pipes(uch)); |
|
+} |
|
+ |
|
+static int |
|
+uchcom_open_hw_port(uchcom_state_t *uch) |
|
+{ |
|
+ int rval; |
|
+ uint8_t chip_ver[UCHCOM_INPUT_BUF_SIZE]; |
|
+ |
|
+ // initialize device with defaults |
|
+ rval = uchcom_cmd_vendor_write0(uch, UCHCOM_REQ_RESET, |
|
+ UCHCOM_RESET_VALUE, UCHCOM_RESET_INDEX); |
|
+ if (rval != USB_SUCCESS) { |
|
+ USB_DPRINTF_L2(DPRINT_DEF_PIPE, uch->uch_lh, |
|
+ "uchcom_open_hw_port: failed to reset!"); |
|
+ return (rval); |
|
+ } |
|
+ |
|
+ // get chip version |
|
+ rval = uchcom_cmd_vendor_read0(uch, UCHCOM_REQ_GET_VERSION, 0, 0, |
|
+ &chip_ver, sizeof(chip_ver)); |
|
+ if (rval != USB_SUCCESS) { |
|
+ USB_DPRINTF_L2(DPRINT_DEF_PIPE, uch->uch_lh, |
|
+ "uchcom_open_hw_port: failed to read chip version!"); |
|
+ if (rval == USB_FAILURE) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_FAILURE"); |
|
+ if (rval == USB_NO_RESOURCES) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_NO_RESOURCES"); |
|
+ if (rval == USB_NO_BANDWIDTH) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_NO_BANDWIDTH"); |
|
+ if (rval == USB_NOT_SUPPORTED) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_NOT_SUPPORTED"); |
|
+ if (rval == USB_PIPE_ERROR) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_PIPE_ERROR"); |
|
+ if (rval == USB_INVALID_PIPE) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_INVALID_PIPE"); |
|
+ if (rval == USB_NO_FRAME_NUMBER) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_NO_FRAME_NUMBER"); |
|
+ if (rval == USB_INVALID_START_FRAME) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_INVALID_START_FRAME"); |
|
+ if (rval == USB_HC_HARDWARE_ERROR) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_HC_HARDWARE_ERROR"); |
|
+ if (rval == USB_INVALID_REQUEST) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_INVALID_REQUEST"); |
|
+ if (rval == USB_INVALID_CONTEXT) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_INVALID_CONTEXT"); |
|
+ if (rval == USB_INVALID_VERSION) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_INVALID_VERSION"); |
|
+ if (rval == USB_INVALID_ARGS) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_INVALID_ARGS"); |
|
+ if (rval == USB_INVALID_PERM) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_INVALID_PERM"); |
|
+ if (rval == USB_BUSY) cmn_err(CE_WARN, "XXX: uchcom_open_hw_port USB_BUSY"); |
|
+ |
|
+ return (rval); |
|
+ } |
|
+ |
|
+ //mutex_enter(&uch->uch_lock); |
|
+ //uch->uch_chip_version = chip_ver; |
|
+ //mutex_exit(&uch->uch_lock); |
|
+ |
|
+ /* |
|
+ if (chip_ver == 0x20) { |
|
+ cmn_err(CE_WARN, "XXX: chip_ver 0x20"); |
|
+ } else if (chip_ver == 0x30) { |
|
+ cmn_err(CE_WARN, "XXX: chip_ver 0x30"); |
|
+ } else { |
|
+ cmn_err(CE_WARN, "XXX: chip_ver ???"); |
|
+ } |
|
+ */ |
|
+ |
|
+ cmn_err(CE_WARN, "XXX: uchcom_open_hw_port END"); |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+/* |
|
+ * restore device state after CPR resume or reconnect |
|
+ */ |
|
+static int |
|
+uchcom_restore_device_state(uchcom_state_t *uch) |
|
+{ |
|
+ int state; |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ state = uch->uch_dev_state; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ if (state != USB_DEV_DISCONNECTED && state != USB_DEV_SUSPENDED) |
|
+ return (state); |
|
+ |
|
+ if (usb_check_same_device(uch->uch_dip, uch->uch_lh, USB_LOG_L0, |
|
+ DPRINT_MASK_ALL, USB_CHK_ALL, NULL) != USB_SUCCESS) { |
|
+ mutex_enter(&uch->uch_lock); |
|
+ state = uch->uch_dev_state = USB_DEV_DISCONNECTED; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ return (state); |
|
+ } |
|
+ |
|
+ if (state == USB_DEV_DISCONNECTED) { |
|
+ USB_DPRINTF_L0(DPRINT_HOTPLUG, uch->uch_lh, |
|
+ "Device has been reconnected but data may have been lost"); |
|
+ } |
|
+ |
|
+ if (uchcom_reconnect_pipes(uch) != USB_SUCCESS) |
|
+ return (state); |
|
+ |
|
+ /* |
|
+ * init device state |
|
+ */ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ state = uch->uch_dev_state = USB_DEV_ONLINE; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ if ((uchcom_restore_port_state(uch) != USB_SUCCESS)) { |
|
+ USB_DPRINTF_L2(DPRINT_HOTPLUG, uch->uch_lh, |
|
+ "uchcom_restore_device_state: failed"); |
|
+ } |
|
+ |
|
+ return (state); |
|
+} |
|
+ |
|
+/* |
|
+ * restore ports state after CPR resume or reconnect |
|
+ */ |
|
+static int |
|
+uchcom_restore_port_state(uchcom_state_t *uch) |
|
+{ |
|
+ int rval; |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ if (uch->uch_port_state != UCHCOM_PORT_OPEN) { |
|
+ mutex_exit(&uch->uch_lock); |
|
+ return (USB_SUCCESS); |
|
+ } |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ /* open hardware serial port, restoring old settings */ |
|
+ if ((rval = uchcom_open_hw_port(uch)) != USB_SUCCESS) { |
|
+ USB_DPRINTF_L2(DPRINT_HOTPLUG, uch->uch_lh, |
|
+ "uchcom_restore_port_state: failed"); |
|
+ } |
|
+ |
|
+ return (rval); |
|
+} |
|
+ |
|
+/* |
|
+ * create PM components |
|
+ */ |
|
+static int |
|
+uchcom_create_pm_components(uchcom_state_t *uch) |
|
+{ |
|
+ dev_info_t *dip = uch->uch_dip; |
|
+ uchcom_pm_t *pm; |
|
+ uint_t pwr_states; |
|
+ |
|
+ if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) { |
|
+ USB_DPRINTF_L2(DPRINT_PM, uch->uch_lh, |
|
+ "uchcom_create_pm_components: failed"); |
|
+ return (USB_SUCCESS); |
|
+ } |
|
+ |
|
+ pm = uch->uch_pm = kmem_zalloc(sizeof (*pm), KM_SLEEP); |
|
+ |
|
+ pm->pm_pwr_states = (uint8_t)pwr_states; |
|
+ pm->pm_cur_power = USB_DEV_OS_FULL_PWR; |
|
+ pm->pm_wakeup_enabled = usb_handle_remote_wakeup(dip, |
|
+ USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS; |
|
+ |
|
+ (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); |
|
+ |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+/* |
|
+ * destroy PM components |
|
+ */ |
|
+static void |
|
+uchcom_destroy_pm_components(uchcom_state_t *uch) |
|
+{ |
|
+ uchcom_pm_t *pm = uch->uch_pm; |
|
+ dev_info_t *dip = uch->uch_dip; |
|
+ int rval; |
|
+ |
|
+ if (!pm) |
|
+ return; |
|
+ |
|
+ if (uch->uch_dev_state != USB_DEV_DISCONNECTED) { |
|
+ if (pm->pm_wakeup_enabled) { |
|
+ rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); |
|
+ if (rval != DDI_SUCCESS) |
|
+ USB_DPRINTF_L2(DPRINT_PM, uch->uch_lh, |
|
+ "uchcom_destroy_pm_components: " |
|
+ "raising power failed, rval=%d", rval); |
|
+ |
|
+ rval = usb_handle_remote_wakeup(dip, |
|
+ USB_REMOTE_WAKEUP_DISABLE); |
|
+ |
|
+ if (rval != USB_SUCCESS) |
|
+ USB_DPRINTF_L2(DPRINT_PM, uch->uch_lh, |
|
+ "uchcom_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 (*pm)); |
|
+ uch->uch_pm = NULL; |
|
+} |
|
+ |
|
+/* |
|
+ * mark device busy and raise power |
|
+ */ |
|
+static int |
|
+uchcom_pm_set_busy(uchcom_state_t *uch) |
|
+{ |
|
+ uchcom_pm_t *pm = uch->uch_pm; |
|
+ dev_info_t *dip = uch->uch_dip; |
|
+ int rval; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_PM, uch->uch_lh, "uchcom_pm_set_busy"); |
|
+ |
|
+ if (!pm) |
|
+ return (USB_SUCCESS); |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ /* if already marked busy, just increment the counter */ |
|
+ if (pm->pm_busy_cnt++ > 0) { |
|
+ mutex_exit(&uch->uch_lock); |
|
+ return (USB_SUCCESS); |
|
+ } |
|
+ |
|
+ rval = pm_busy_component(dip, 0); |
|
+ ASSERT(rval == DDI_SUCCESS); |
|
+ |
|
+ if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) { |
|
+ mutex_exit(&uch->uch_lock); |
|
+ return (USB_SUCCESS); |
|
+ } |
|
+ |
|
+ /* need to raise power */ |
|
+ pm->pm_raise_power = B_TRUE; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); |
|
+ if (rval != DDI_SUCCESS) |
|
+ USB_DPRINTF_L2(DPRINT_PM, uch->uch_lh, "raising power failed"); |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ pm->pm_raise_power = B_FALSE; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+/* |
|
+ * mark device idle |
|
+ */ |
|
+static void |
|
+uchcom_pm_set_idle(uchcom_state_t *uch) |
|
+{ |
|
+ uchcom_pm_t *pm = uch->uch_pm; |
|
+ dev_info_t *dip = uch->uch_dip; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_PM, uch->uch_lh, "uchcom_pm_set_idle"); |
|
+ |
|
+ if (!pm) |
|
+ return; |
|
+ |
|
+ /* |
|
+ * if more ports use the device, do not mark as yet |
|
+ */ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ if (--pm->pm_busy_cnt > 0) { |
|
+ mutex_exit(&uch->uch_lock); |
|
+ return; |
|
+ } |
|
+ |
|
+ if (pm) |
|
+ (void) pm_idle_component(dip, 0); |
|
+ |
|
+ mutex_exit(&uch->uch_lock); |
|
+} |
|
+ |
|
+/* |
|
+ * Functions to handle power transition for OS levels 0 -> 3 |
|
+ * The same level as OS state, different from USB state |
|
+ */ |
|
+static int |
|
+uchcom_pwrlvl0(uchcom_state_t *uch) |
|
+{ |
|
+ int rval; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_PM, uch->uch_lh, "uchcom_pwrlvl0"); |
|
+ |
|
+ switch (uch->uch_dev_state) { |
|
+ case USB_DEV_ONLINE: |
|
+ /* issue USB D3 command to the device */ |
|
+ rval = usb_set_device_pwrlvl3(uch->uch_dip); |
|
+ ASSERT(rval == USB_SUCCESS); |
|
+ |
|
+ uch->uch_dev_state = USB_DEV_PWRED_DOWN; |
|
+ uch->uch_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, uch->uch_lh, |
|
+ "uchcom_pwrlvl0: illegal device state"); |
|
+ |
|
+ return (USB_FAILURE); |
|
+ } |
|
+} |
|
+ |
|
+static int |
|
+uchcom_pwrlvl1(uchcom_state_t *uch) |
|
+{ |
|
+ USB_DPRINTF_L4(DPRINT_PM, uch->uch_lh, "uchcom_pwrlvl1"); |
|
+ |
|
+ /* issue USB D2 command to the device */ |
|
+ (void) usb_set_device_pwrlvl2(uch->uch_dip); |
|
+ |
|
+ return (USB_FAILURE); |
|
+} |
|
+ |
|
+ |
|
+static int |
|
+uchcom_pwrlvl2(uchcom_state_t *uch) |
|
+{ |
|
+ USB_DPRINTF_L4(DPRINT_PM, uch->uch_lh, "uchcom_pwrlvl2"); |
|
+ |
|
+ /* issue USB D1 command to the device */ |
|
+ (void) usb_set_device_pwrlvl1(uch->uch_dip); |
|
+ |
|
+ return (USB_FAILURE); |
|
+} |
|
+ |
|
+ |
|
+static int |
|
+uchcom_pwrlvl3(uchcom_state_t *uch) |
|
+{ |
|
+ int rval; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_PM, uch->uch_lh, "uchcom_pwrlvl3"); |
|
+ |
|
+ switch (uch->uch_dev_state) { |
|
+ case USB_DEV_PWRED_DOWN: |
|
+ /* Issue USB D0 command to the device here */ |
|
+ rval = usb_set_device_pwrlvl0(uch->uch_dip); |
|
+ ASSERT(rval == USB_SUCCESS); |
|
+ |
|
+ uch->uch_dev_state = USB_DEV_ONLINE; |
|
+ uch->uch_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, uch->uch_lh, |
|
+ "uchcom_pwrlvl3: illegal device state"); |
|
+ |
|
+ return (USB_FAILURE); |
|
+ } |
|
+} |
|
+ |
|
+/* |
|
+ * pipe operations |
|
+ */ |
|
+static int |
|
+uchcom_open_pipes(uchcom_state_t *uch) |
|
+{ |
|
+ int ifc, alt; |
|
+ usb_pipe_policy_t policy; |
|
+ usb_ep_data_t *in_data, *out_data; |
|
+ size_t max_xfer_sz; |
|
+ |
|
+ /* get ep data */ |
|
+ ifc = uch->uch_dev_data->dev_curr_if; |
|
+ alt = 0; |
|
+ |
|
+ in_data = usb_lookup_ep_data(uch->uch_dip, uch->uch_dev_data, ifc, alt, |
|
+ 0, USB_EP_ATTR_BULK, USB_EP_DIR_IN); |
|
+ |
|
+ out_data = usb_lookup_ep_data(uch->uch_dip, uch->uch_dev_data, ifc, alt, |
|
+ 0, USB_EP_ATTR_BULK, USB_EP_DIR_OUT); |
|
+ |
|
+ if ((in_data == NULL) || (out_data == NULL)) { |
|
+ USB_DPRINTF_L2(DPRINT_ATTACH, uch->uch_lh, |
|
+ "uchcom_open_pipes: can't get ep data"); |
|
+ |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ |
|
+ /* |
|
+ * Set buffer sizes. Default to UCHCOM_XFER_SZ_MAX. |
|
+ * Use wMaxPacketSize from endpoint descriptor if it is nonzero. |
|
+ * Cap at a max transfer size of host controller. |
|
+ */ |
|
+ uch->uch_ibuf_sz = uch->uch_obuf_sz = UCHCOM_XFER_SZ_MAX; |
|
+ |
|
+ if (in_data->ep_descr.wMaxPacketSize) |
|
+ uch->uch_ibuf_sz = in_data->ep_descr.wMaxPacketSize; |
|
+ uch->uch_ibuf_sz = min(uch->uch_ibuf_sz, max_xfer_sz); |
|
+ |
|
+ if (out_data->ep_descr.wMaxPacketSize) |
|
+ uch->uch_obuf_sz = out_data->ep_descr.wMaxPacketSize; |
|
+ uch->uch_obuf_sz = min(uch->uch_obuf_sz, max_xfer_sz); |
|
+ |
|
+ /* open pipes */ |
|
+ policy.pp_max_async_reqs = 2; |
|
+ |
|
+ if (usb_pipe_open(uch->uch_dip, &in_data->ep_descr, &policy, |
|
+ USB_FLAGS_SLEEP, &uch->uch_bulkin_ph) != USB_SUCCESS) { |
|
+ |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ |
|
+ if (usb_pipe_open(uch->uch_dip, &out_data->ep_descr, &policy, |
|
+ USB_FLAGS_SLEEP, &uch->uch_bulkout_ph) != USB_SUCCESS) { |
|
+ usb_pipe_close(uch->uch_dip, uch->uch_bulkin_ph, USB_FLAGS_SLEEP, |
|
+ NULL, NULL); |
|
+ |
|
+ return (USB_FAILURE); |
|
+ } |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ uch->uch_bulkin_state = UCHCOM_PIPE_IDLE; |
|
+ uch->uch_bulkout_state = UCHCOM_PIPE_IDLE; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ return (USB_SUCCESS); |
|
+} |
|
+ |
|
+static void |
|
+uchcom_close_pipes(uchcom_state_t *uch) |
|
+{ |
|
+ if (uch->uch_bulkin_ph) |
|
+ usb_pipe_close(uch->uch_dip, uch->uch_bulkin_ph, |
|
+ USB_FLAGS_SLEEP, 0, 0); |
|
+ if (uch->uch_bulkout_ph) |
|
+ usb_pipe_close(uch->uch_dip, uch->uch_bulkout_ph, |
|
+ USB_FLAGS_SLEEP, 0, 0); |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ uch->uch_bulkin_state = UCHCOM_PIPE_CLOSED; |
|
+ uch->uch_bulkout_state = UCHCOM_PIPE_CLOSED; |
|
+ mutex_exit(&uch->uch_lock); |
|
+} |
|
+ |
|
+static void |
|
+uchcom_disconnect_pipes(uchcom_state_t *uch) |
|
+{ |
|
+ uchcom_close_pipes(uch); |
|
+} |
|
+ |
|
+ |
|
+static int |
|
+uchcom_reconnect_pipes(uchcom_state_t *uch) |
|
+{ |
|
+ return (uchcom_open_pipes(uch)); |
|
+} |
|
+ |
|
+/* |
|
+ * pipe callbacks bulk in common and exeception callback |
|
+ */ |
|
+/*ARGSUSED*/ |
|
+void |
|
+uchcom_bulkin_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)req->bulk_client_private; |
|
+ mblk_t *data; |
|
+ int data_len; |
|
+ |
|
+ data = req->bulk_data; |
|
+ data_len = (data) ? MBLKL(data) : 0; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_IN_PIPE, uch->uch_lh, "uchcom_bulkin_cb: " |
|
+ "cr=%d len=%d", |
|
+ req->bulk_completion_reason, |
|
+ data_len); |
|
+ |
|
+ /* save data and notify GSD */ |
|
+ if ((uch->uch_port_state == UCHCOM_PORT_OPEN) && (data_len) && |
|
+ (req->bulk_completion_reason == USB_CR_OK)) { |
|
+ req->bulk_data = NULL; |
|
+ uchcom_put_tail(&uch->uch_rx_mp, data); |
|
+ if (uch->uch_cb.cb_rx) { |
|
+ uch->uch_cb.cb_rx(uch->uch_cb.cb_arg); |
|
+ } |
|
+ } |
|
+ |
|
+ usb_free_bulk_req(req); |
|
+ |
|
+ /* receive more */ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ uch->uch_bulkin_state = UCHCOM_PIPE_IDLE; |
|
+ if ((uch->uch_port_state == UCHCOM_PORT_OPEN) && |
|
+ (uch->uch_dev_state == USB_DEV_ONLINE)) { |
|
+ if (uchcom_rx_start(uch) != USB_SUCCESS) { |
|
+ USB_DPRINTF_L2(DPRINT_IN_PIPE, uch->uch_lh, |
|
+ "uchcom_bulkin_cb: restart rx fail"); |
|
+ } |
|
+ } |
|
+ mutex_exit(&uch->uch_lock); |
|
+} |
|
+ |
|
+/* |
|
+ * pipe callbacks bulk out common and exeception callback |
|
+ */ |
|
+/*ARGSUSED*/ |
|
+void |
|
+uchcom_bulkout_cb(usb_pipe_handle_t pipe, usb_bulk_req_t *req) |
|
+{ |
|
+ uchcom_state_t *uch = (uchcom_state_t *)req->bulk_client_private; |
|
+ int data_len; |
|
+ mblk_t *data = req->bulk_data; |
|
+ |
|
+ data_len = (req->bulk_data) ? MBLKL(req->bulk_data) : 0; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_OUT_PIPE, uch->uch_lh, |
|
+ "uchcom_bulkout_cb: cr=%d len=%d", |
|
+ req->bulk_completion_reason, |
|
+ data_len); |
|
+ |
|
+ /* Re-send data only when port is open */ |
|
+ if ((uch->uch_port_state == UCHCOM_PORT_OPEN) && |
|
+ req->bulk_completion_reason && (data_len > 0)) { |
|
+ uchcom_put_head(&uch->uch_tx_mp, data); |
|
+ req->bulk_data = NULL; |
|
+ } |
|
+ |
|
+ usb_free_bulk_req(req); |
|
+ |
|
+ /* notify GSD */ |
|
+ if (uch->uch_cb.cb_tx) { |
|
+ uch->uch_cb.cb_tx(uch->uch_cb.cb_arg); |
|
+ } |
|
+ |
|
+ /* send more */ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ uch->uch_bulkout_state = UCHCOM_PIPE_IDLE; |
|
+ if (uch->uch_tx_mp == NULL) { |
|
+ cv_broadcast(&uch->uch_tx_cv); |
|
+ } else { |
|
+ uchcom_tx_start(uch, NULL); |
|
+ } |
|
+ mutex_exit(&uch->uch_lock); |
|
+} |
|
+ |
|
+/* |
|
+ * data transfer routines start data receipt |
|
+ */ |
|
+static int |
|
+uchcom_rx_start(uchcom_state_t *uch) |
|
+{ |
|
+ usb_bulk_req_t *br; |
|
+ int rval = USB_FAILURE; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_OUT_PIPE, uch->uch_lh, "uchcom_rx_start"); |
|
+ |
|
+ ASSERT(mutex_owned(&uch->uch_lock)); |
|
+ |
|
+ uch->uch_bulkin_state = UCHCOM_PIPE_BUSY; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ br = usb_alloc_bulk_req(uch->uch_dip, uch->uch_ibuf_sz, USB_FLAGS_SLEEP); |
|
+ br->bulk_len = uch->uch_ibuf_sz; |
|
+ br->bulk_timeout = UCHCOM_BULKIN_TIMEOUT; |
|
+ br->bulk_cb = uchcom_bulkin_cb; |
|
+ br->bulk_exc_cb = uchcom_bulkin_cb; |
|
+ br->bulk_client_private = (usb_opaque_t)uch; |
|
+ br->bulk_attributes = USB_ATTRS_AUTOCLEARING | USB_ATTRS_SHORT_XFER_OK; |
|
+ |
|
+ rval = usb_pipe_bulk_xfer(uch->uch_bulkin_ph, br, 0); |
|
+ |
|
+ if (rval != USB_SUCCESS) { |
|
+ USB_DPRINTF_L2(DPRINT_IN_PIPE, uch->uch_lh, |
|
+ "uchcom_rx_start: xfer failed %d", rval); |
|
+ usb_free_bulk_req(br); |
|
+ } |
|
+ |
|
+ mutex_enter(&uch->uch_lock); |
|
+ if (rval != USB_SUCCESS) { |
|
+ uch->uch_bulkin_state = UCHCOM_PIPE_IDLE; |
|
+ } |
|
+ |
|
+ return (rval); |
|
+} |
|
+ |
|
+/* |
|
+ * data transfer routines start data transmit |
|
+ */ |
|
+static void |
|
+uchcom_tx_start(uchcom_state_t *uch, int *xferd) |
|
+{ |
|
+ int len; /* bytes we can transmit */ |
|
+ mblk_t *data; /* data to be transmitted */ |
|
+ int data_len; /* bytes in 'data' */ |
|
+ mblk_t *mp; /* current msgblk */ |
|
+ int copylen; /* bytes copy from 'mp' to 'data' */ |
|
+ int rval; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_OUT_PIPE, uch->uch_lh, "uchcom_tx_start"); |
|
+ ASSERT(mutex_owned(&uch->uch_lock)); |
|
+ ASSERT(uch->uch_port_state != UCHCOM_PORT_CLOSED); |
|
+ |
|
+ if (xferd) { |
|
+ *xferd = 0; |
|
+ } |
|
+ if ((uch->uch_port_flags & UCHCOM_PORT_TX_STOPPED) || |
|
+ (uch->uch_tx_mp == NULL)) { |
|
+ return; |
|
+ } |
|
+ if (uch->uch_bulkout_state != UCHCOM_PIPE_IDLE) { |
|
+ USB_DPRINTF_L4(DPRINT_OUT_PIPE, uch->uch_lh, |
|
+ "uchcom_tx_start: pipe busy"); |
|
+ return; |
|
+ } |
|
+ ASSERT(MBLKL(uch->uch_tx_mp) > 0); |
|
+ |
|
+ /* send as much data as port can receive */ |
|
+ len = min(msgdsize(uch->uch_tx_mp), uch->uch_obuf_sz); |
|
+ |
|
+ if (len == 0) { |
|
+ return; |
|
+ } |
|
+ |
|
+ if ((data = allocb(len, BPRI_LO)) == NULL) { |
|
+ return; |
|
+ } |
|
+ |
|
+ /* |
|
+ * copy no more than 'len' bytes from mblk chain to transmit mblk 'data' |
|
+ */ |
|
+ data_len = 0; |
|
+ |
|
+ while ((data_len < len) && uch->uch_tx_mp) { |
|
+ mp = uch->uch_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) < 1) { |
|
+ uch->uch_tx_mp = unlinkb(mp); |
|
+ freeb(mp); |
|
+ } else { |
|
+ ASSERT(data_len == len); |
|
+ } |
|
+ } |
|
+ if (data_len <= 0) { |
|
+ USB_DPRINTF_L3(DPRINT_OUT_PIPE, uch->uch_lh, |
|
+ "uchcom_tx_start: copied zero bytes"); |
|
+ freeb(data); |
|
+ return; |
|
+ } |
|
+ |
|
+ uch->uch_bulkout_state = UCHCOM_PIPE_BUSY; |
|
+ mutex_exit(&uch->uch_lock); |
|
+ |
|
+ rval = uchcom_send_data(uch, data); |
|
+ mutex_enter(&uch->uch_lock); |
|
+ |
|
+ if (rval != USB_SUCCESS) { |
|
+ uch->uch_bulkout_state = UCHCOM_PIPE_IDLE; |
|
+ uchcom_put_head(&uch->uch_tx_mp, data); |
|
+ } else { |
|
+ if (xferd) { |
|
+ *xferd = data_len; |
|
+ } |
|
+ } |
|
+} |
|
+ |
|
+static int |
|
+uchcom_send_data(uchcom_state_t *uch, mblk_t *data) |
|
+{ |
|
+ usb_bulk_req_t *br; |
|
+ int len = MBLKL(data); |
|
+ int rval; |
|
+ |
|
+ USB_DPRINTF_L4(DPRINT_OUT_PIPE, uch->uch_lh, "uchcom_send_data: %d " |
|
+ "%x %x %x", len, data->b_rptr[0], |
|
+ (len > 1) ? data->b_rptr[1] : 0, |
|
+ (len > 2) ? data->b_rptr[2] : 0); |
|
+ ASSERT(!mutex_owned(&uch->uch_lock)); |
|
+ |
|
+ br = usb_alloc_bulk_req(uch->uch_dip, 0, USB_FLAGS_SLEEP); |
|
+ br->bulk_data = data; |
|
+ br->bulk_len = len; |
|
+ br->bulk_timeout = UCHCOM_BULKOUT_TIMEOUT; |
|
+ br->bulk_cb = uchcom_bulkout_cb; |
|
+ br->bulk_exc_cb = uchcom_bulkout_cb; |
|
+ br->bulk_client_private = (usb_opaque_t)uch; |
|
+ br->bulk_attributes = USB_ATTRS_AUTOCLEARING; |
|
+ |
|
+ rval = usb_pipe_bulk_xfer(uch->uch_bulkout_ph, br, 0); |
|
+ |
|
+ if (rval != USB_SUCCESS) { |
|
+ USB_DPRINTF_L2(DPRINT_OUT_PIPE, uch->uch_lh, |
|
+ "uchcom_send_data: xfer failed %d", rval); |
|
+ |
|
+ br->bulk_data = NULL; |
|
+ usb_free_bulk_req(br); |
|
+ } |
|
+ |
|
+ return (rval); |
|
+} |
|
+ |
|
+/* |
|
+ * wait until local tx buffer drains. |
|
+ * 'timeout' is in seconds, zero means wait forever |
|
+ */ |
|
+static int |
|
+uchcom_wait_tx_drain(uchcom_state_t *uch, int timeout) |
|
+{ |
|
+ clock_t until; |
|
+ int over = 0; |
|
+ |
|
+ until = ddi_get_lbolt() + drv_usectohz(1000 * 1000 * timeout); |
|
+ |
|
+ while (uch->uch_tx_mp && !over) { |
|
+ if (timeout > 0) { |
|
+ /* whether timedout or signal pending */ |
|
+ over = (cv_timedwait_sig(&uch->uch_tx_cv, |
|
+ &uch->uch_lock, until) <= 0); |
|
+ } else { |
|
+ /* whether a signal is pending */ |
|
+ over = (cv_wait_sig(&uch->uch_tx_cv, |
|
+ &uch->uch_lock) == 0); |
|
+ } |
|
+ } |
|
+ |
|
+ return ((uch->uch_tx_mp == NULL) ? USB_SUCCESS : USB_FAILURE); |
|
+} |
|
+ |
|
+/* |
|
+ * Vendor routines |
|
+ */ |
|
+static int |
|
+uchcom_cmd_vendor_write0(uchcom_state_t *uch, |
|
+ uint16_t reqno, uint16_t val, uint16_t idx) |
|
+{ |
|
+ usb_ctrl_setup_t req; |
|
+ usb_cb_flags_t cb_flags; |
|
+ usb_cr_t cr; |
|
+ int rval; |
|
+ |
|
+ ASSERT(!mutex_owned(&uch->uch_lock)); |
|
+ |
|
+ bzero(&req, sizeof (req)); |
|
+ req.bmRequestType = |
|
+ USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_HOST_TO_DEV; |
|
+ req.bRequest = (uchar_t)reqno; |
|
+ req.wValue = val; |
|
+ req.wIndex = idx; |
|
+ req.wLength = 0; |
|
+ req.attrs = USB_ATTRS_NONE; |
|
+ |
|
+ if ((rval = usb_pipe_ctrl_xfer_wait(uch->uch_def_ph, |
|
+ &req, NULL, &cr, &cb_flags, 0)) != USB_SUCCESS) { |
|
+ USB_DPRINTF_L2(DPRINT_DEF_PIPE, uch->uch_lh, |
|
+ "uchcom_cmd_vendor_write0: 0x%x 0x%x 0x%x failed %d %d 0x%x", |
|
+ reqno, val, idx, rval, cr, cb_flags); |
|
+ } |
|
+ |
|
+ return (rval); |
|
+} |
|
+ |
|
+static int |
|
+uchcom_cmd_vendor_read0(uchcom_state_t *uch, |
|
+ uint16_t reqno, uint16_t val, uint16_t idx, void *buf, uint16_t buflen) |
|
+{ |
|
+ usb_ctrl_setup_t req; |
|
+ usb_cb_flags_t cb_flags; |
|
+ usb_cr_t cr; |
|
+ mblk_t *mp = NULL; |
|
+ int rval; |
|
+ |
|
+ bzero(&req, sizeof (req)); |
|
+ req.bmRequestType = |
|
+ USB_DEV_REQ_TYPE_VENDOR | USB_DEV_REQ_DEV_TO_HOST; |
|
+ req.bRequest = (uchar_t)reqno; |
|
+ req.wValue = val; |
|
+ req.wIndex = idx; |
|
+ req.wLength = buflen; |
|
+ req.attrs = USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_AUTOCLEARING; |
|
+ |
|
+ if ((rval = usb_pipe_ctrl_xfer_wait(uch->uch_def_ph, |
|
+ &req, &mp, &cr, &cb_flags, 0)) != USB_SUCCESS) { |
|
+ USB_DPRINTF_L2(DPRINT_DEF_PIPE, uch->uch_lh, |
|
+ "uchcom_cmd_vendor_read0: get regs req failed :" |
|
+ " cr:%s(%d), cb_flags:(%x)\n", |
|
+ usb_str_cr(cr), cr, cb_flags); |
|
+ cmn_err(CE_WARN, |
|
+ "uchcom_cmd_vendor_read0: get regs req failed :" |
|
+ " cr:%s(%d), cb_flags:(%x)\n", |
|
+ usb_str_cr(cr), cr, cb_flags); |
|
+ } else { |
|
+ bcopy(mp->b_rptr, buf, buflen); |
|
+ } |
|
+ |
|
+ freemsg(mp); |
|
+ return (rval); |
|
+} |
|
+ |
|
+/* |
|
+ * misc routines |
|
+ */ |
|
+static void |
|
+uchcom_put_tail(mblk_t **mpp, mblk_t *bp) |
|
+{ |
|
+ if (*mpp) { |
|
+ linkb(*mpp, bp); |
|
+ } else { |
|
+ *mpp = bp; |
|
+ } |
|
+} |
|
+ |
|
+ |
|
+static void |
|
+uchcom_put_head(mblk_t **mpp, mblk_t *bp) |
|
+{ |
|
+ if (*mpp) { |
|
+ linkb(bp, *mpp); |
|
+ } |
|
+ *mpp = bp; |
|
+} |
|
+ |
|
diff --git a/usr/src/uts/common/io/usb/clients/usbser/uchcom/usbser_uchcom.c b/usr/src/uts/common/io/usb/clients/usbser/uchcom/usbser_uchcom.c |
|
new file mode 100644 |
|
index 0000000000..4e482c9225 |
|
--- /dev/null |
|
+++ b/usr/src/uts/common/io/usb/clients/usbser/uchcom/usbser_uchcom.c |
|
@@ -0,0 +1,189 @@ |
|
+/* |
|
+ * CDDL HEADER START |
|
+ * |
|
+ * The contents of this file are subject to the terms of the |
|
+ * Common Development and Distribution License (the "License"). |
|
+ * You may not use this file except in compliance with the License. |
|
+ * |
|
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
+ * or http://www.opensolaris.org/os/licensing. |
|
+ * See the License for the specific language governing permissions |
|
+ * and limitations under the License. |
|
+ * |
|
+ * When distributing Covered Code, include this CDDL HEADER in each |
|
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
+ * If applicable, add the following below this CDDL HEADER, with the |
|
+ * fields enclosed by brackets "[]" replaced with your own identifying |
|
+ * information: Portions Copyright [yyyy] [name of copyright owner] |
|
+ * |
|
+ * CDDL HEADER END |
|
+ */ |
|
+ |
|
+/* |
|
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
|
+ * Use is subject to license terms. |
|
+ */ |
|
+/* |
|
+ * Copyright 2013 Hans Rosenfeld <[email protected]> |
|
+ */ |
|
+ |
|
+/* |
|
+ * This driver supports WinChipHead CH341/340 adapters. It is a |
|
+ * device-specific driver (DSD) working with the USB generic serial |
|
+ * driver (GSD) usbser. |
|
+ * |
|
+ * It implements the USB-to-serial device-specific driver interface (DSDI) |
|
+ * which is exported by GSD. The DSDI is defined by ds_ops_t structure. |
|
+ */ |
|
+ |
|
+#include <sys/types.h> |
|
+#include <sys/param.h> |
|
+#include <sys/stream.h> |
|
+#include <sys/conf.h> |
|
+#include <sys/ddi.h> |
|
+#include <sys/sunddi.h> |
|
+ |
|
+#include <sys/usb/clients/usbser/usbser.h> |
|
+#include <sys/usb/clients/usbser/uchcom/uchcom_var.h> |
|
+ |
|
+static void *usbser_uchcom_statep; /* soft state handle for usbser */ |
|
+ |
|
+extern ds_ops_t uchcom_ds_ops; /* DSD operations */ |
|
+ |
|
+static int |
|
+usbser_uchcom_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, |
|
+ void **result) |
|
+{ |
|
+ return (usbser_getinfo(dip, infocmd, arg, result, usbser_uchcom_statep)); |
|
+} |
|
+ |
|
+ |
|
+static int |
|
+usbser_uchcom_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) |
|
+{ |
|
+ return (usbser_attach(dip, cmd, usbser_uchcom_statep, &uchcom_ds_ops)); |
|
+} |
|
+ |
|
+ |
|
+static int |
|
+usbser_uchcom_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) |
|
+{ |
|
+ return (usbser_detach(dip, cmd, usbser_uchcom_statep)); |
|
+} |
|
+ |
|
+ |
|
+static int |
|
+usbser_uchcom_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr) |
|
+{ |
|
+ return (usbser_open(rq, dev, flag, sflag, cr, usbser_uchcom_statep)); |
|
+} |
|
+ |
|
+/* |
|
+ * STREAMS structures |
|
+ */ |
|
+struct module_info uchcom_modinfo = { |
|
+ 0, /* module id */ |
|
+ "uchcom", /* 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 uchcom_rinit = { |
|
+ putq, |
|
+ usbser_rsrv, |
|
+ usbser_uchcom_open, |
|
+ usbser_close, |
|
+ NULL, |
|
+ &uchcom_modinfo, |
|
+}; |
|
+ |
|
+static struct qinit uchcom_winit = { |
|
+ usbser_wput, |
|
+ usbser_wsrv, |
|
+ NULL, |
|
+ NULL, |
|
+ NULL, |
|
+ &uchcom_modinfo, |
|
+}; |
|
+ |
|
+static struct streamtab uchcom_str_info = { |
|
+ &uchcom_rinit, |
|
+ &uchcom_winit, |
|
+}; |
|
+ |
|
+static struct cb_ops uchcom_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 */ |
|
+ &uchcom_str_info, /* cb_stream */ |
|
+ (int)(D_64BIT | D_NEW | D_MP | D_HOTPLUG) /* cb_flag */ |
|
+}; |
|
+ |
|
+struct dev_ops uchcom_ops = { |
|
+ DEVO_REV, /* devo_rev */ |
|
+ 0, /* devo_refcnt */ |
|
+ usbser_uchcom_getinfo, /* devo_getinfo */ |
|
+ nulldev, /* devo_identify */ |
|
+ nulldev, /* devo_probe */ |
|
+ usbser_uchcom_attach, /* devo_attach */ |
|
+ usbser_uchcom_detach, /* devo_detach */ |
|
+ nodev, /* devo_reset */ |
|
+ &uchcom_cb_ops, /* devo_cb_ops */ |
|
+ (struct bus_ops *)NULL, /* devo_bus_ops */ |
|
+ usbser_power, /* devo_power */ |
|
+ ddi_quiesce_not_needed, /* devo_quiesce */ |
|
+}; |
|
+ |
|
+static struct modldrv modldrv = { |
|
+ &mod_driverops, |
|
+ "WCH CH341/340 USB UART driver", |
|
+ &uchcom_ops, |
|
+}; |
|
+ |
|
+static struct modlinkage modlinkage = { |
|
+ MODREV_1, |
|
+ &modldrv |
|
+}; |
|
+ |
|
+int |
|
+_init(void) |
|
+{ |
|
+ int error; |
|
+ |
|
+ if ((error = mod_install(&modlinkage)) != 0) |
|
+ return (error); |
|
+ if ((error = ddi_soft_state_init(&usbser_uchcom_statep, |
|
+ usbser_soft_state_size(), 1)) != 0) |
|
+ (void) mod_remove(&modlinkage); |
|
+ return (error); |
|
+} |
|
+ |
|
+ |
|
+int |
|
+_fini(void) |
|
+{ |
|
+ int error; |
|
+ |
|
+ if ((error = mod_remove(&modlinkage)) == 0) |
|
+ ddi_soft_state_fini(&usbser_uchcom_statep); |
|
+ return (error); |
|
+} |
|
+ |
|
+ |
|
+int |
|
+_info(struct modinfo *modinfop) |
|
+{ |
|
+ return (mod_info(&modlinkage, modinfop)); |
|
+} |
|
diff --git a/usr/src/uts/common/sys/usb/clients/usbser/uchcom/uchcom_reg.h b/usr/src/uts/common/sys/usb/clients/usbser/uchcom/uchcom_reg.h |
|
new file mode 100644 |
|
index 0000000000..0e7544d5f0 |
|
--- /dev/null |
|
+++ b/usr/src/uts/common/sys/usb/clients/usbser/uchcom/uchcom_reg.h |
|
@@ -0,0 +1,56 @@ |
|
+/* |
|
+ * CDDL HEADER START |
|
+ * |
|
+ * The contents of this file are subject to the terms of the |
|
+ * Common Development and Distribution License (the "License"). |
|
+ * You may not use this file except in compliance with the License. |
|
+ * |
|
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
+ * or http://www.opensolaris.org/os/licensing. |
|
+ * See the License for the specific language governing permissions |
|
+ * and limitations under the License. |
|
+ * |
|
+ * When distributing Covered Code, include this CDDL HEADER in each |
|
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
+ * If applicable, add the following below this CDDL HEADER, with the |
|
+ * fields enclosed by brackets "[]" replaced with your own identifying |
|
+ * information: Portions Copyright [yyyy] [name of copyright owner] |
|
+ * |
|
+ * CDDL HEADER END |
|
+ */ |
|
+ |
|
+/* |
|
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
|
+ * Use is subject to license terms. |
|
+ */ |
|
+ |
|
+#ifndef _USBSER_UCHCOM_REG_H |
|
+#define _USBSER_UCHCOM_REG_H |
|
+ |
|
+#ifdef __cplusplus |
|
+extern "C" { |
|
+#endif |
|
+ |
|
+#define UCHCOM_VENDOR 0x1a86 |
|
+#define UCHCOM_CHIP_CH340 0x5523 |
|
+#define UCHCOM_CHIP_CH341 0x7523 |
|
+ |
|
+#define UCHCOM_INPUT_BUF_SIZE 8 |
|
+ |
|
+ |
|
+#define UCHCOM_REQ_GET_VERSION 0x5 |
|
+#define UCHCOM_REQ_RESET 0xA1 |
|
+ |
|
+/* |
|
+ * XXX - these magic numbers come from Linux (drivers/usb/serial/ch341.c). |
|
+ * The manufacturer was unresponsive when asked for documentation. |
|
+ */ |
|
+#define UCHCOM_RESET_VALUE 0x501F /* line mode? */ |
|
+#define UCHCOM_RESET_INDEX 0xD90A /* baud rate? */ |
|
+ |
|
+ |
|
+#ifdef __cplusplus |
|
+} |
|
+#endif |
|
+ |
|
+#endif /* _USBSER_UCHCOM_REG_H */ |
|
diff --git a/usr/src/uts/common/sys/usb/clients/usbser/uchcom/uchcom_var.h b/usr/src/uts/common/sys/usb/clients/usbser/uchcom/uchcom_var.h |
|
new file mode 100644 |
|
index 0000000000..6537c84421 |
|
--- /dev/null |
|
+++ b/usr/src/uts/common/sys/usb/clients/usbser/uchcom/uchcom_var.h |
|
@@ -0,0 +1,159 @@ |
|
+/* |
|
+ * CDDL HEADER START |
|
+ * |
|
+ * The contents of this file are subject to the terms of the |
|
+ * Common Development and Distribution License (the "License"). |
|
+ * You may not use this file except in compliance with the License. |
|
+ * |
|
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
+ * or http://www.opensolaris.org/os/licensing. |
|
+ * See the License for the specific language governing permissions |
|
+ * and limitations under the License. |
|
+ * |
|
+ * When distributing Covered Code, include this CDDL HEADER in each |
|
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
+ * If applicable, add the following below this CDDL HEADER, with the |
|
+ * fields enclosed by brackets "[]" replaced with your own identifying |
|
+ * information: Portions Copyright [yyyy] [name of copyright owner] |
|
+ * |
|
+ * CDDL HEADER END |
|
+ */ |
|
+/* |
|
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
|
+ * Use is subject to license terms. |
|
+ */ |
|
+/* |
|
+ * Copyright 2020 Jorge Schrauwen <[email protected]> |
|
+ */ |
|
+ |
|
+#ifndef _SYS_USB_UCHCOM_VAR_H |
|
+#define _SYS_USB_UCHCOM_VAR_H |
|
+ |
|
+ |
|
+#include <sys/types.h> |
|
+#include <sys/dditypes.h> |
|
+#include <sys/note.h> |
|
+ |
|
+#include <sys/usb/clients/usbser/usbser_dsdi.h> |
|
+ |
|
+#ifdef __cplusplus |
|
+extern "C" { |
|
+#endif |
|
+ |
|
+/* |
|
+ * PM support |
|
+ */ |
|
+typedef struct uchcom_pm { |
|
+ 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 */ |
|
+} uchcom_pm_t; |
|
+ |
|
+/* |
|
+ * per device state structure |
|
+ */ |
|
+typedef struct uchcom_state { |
|
+ kmutex_t uch_lock; /* structure lock */ |
|
+ dev_info_t *uch_dip; /* device info */ |
|
+ int uch_dev_flags; /* device flags */ |
|
+ int uch_port_state; /* port state */ |
|
+ int uch_port_flags; /* port flags */ |
|
+ ds_cb_t uch_cb; /* DSD callbacks */ |
|
+ |
|
+ /* |
|
+ * USBA |
|
+ */ |
|
+ usb_client_dev_data_t *uch_dev_data; /* registration data */ |
|
+ usb_event_t *uch_usb_events; /* usb events */ |
|
+ usb_pipe_handle_t uch_def_ph; /* default pipe hdl */ |
|
+ usb_pipe_handle_t uch_bulkin_ph; /* in pipe hdl */ |
|
+ int uch_bulkin_state; /* in pipe state */ |
|
+ usb_pipe_handle_t uch_bulkout_ph; /* in pipe hdl */ |
|
+ int uch_bulkout_state; /* out pipe state */ |
|
+ usb_log_handle_t uch_lh; /* USBA log handle */ |
|
+ int uch_dev_state; /* USB device state */ |
|
+ size_t uch_ibuf_sz; /* input buffer size */ |
|
+ size_t uch_obuf_sz; /* output buffer size */ |
|
+ |
|
+ uchcom_pm_t *uch_pm; /* PM support */ |
|
+ |
|
+ /* |
|
+ * data receive and transmit |
|
+ */ |
|
+ mblk_t *uch_rx_mp; /* rx data */ |
|
+ mblk_t *uch_tx_mp; /* tx data */ |
|
+ kcondvar_t uch_tx_cv; /* tx completion */ |
|
+ |
|
+ /* |
|
+ * other |
|
+ */ |
|
+ uint8_t uch_chip_version; /* chip version */ |
|
+ |
|
+} uchcom_state_t; |
|
+ |
|
+_NOTE(MUTEX_PROTECTS_DATA(uchcom_state::uch_lock, uchcom_state)) |
|
+_NOTE(DATA_READABLE_WITHOUT_LOCK(uchcom_state::{ |
|
+ uch_dip |
|
+ uch_dev_data |
|
+ uch_usb_events |
|
+ uch_def_ph |
|
+ uch_lh |
|
+ uch_ibuch_sz |
|
+ uch_obuch_sz |
|
+ uch_pm |
|
+ uch_port_state |
|
+ uch_cb |
|
+ uch_bulkin_ph |
|
+ uch_bulkout_ph |
|
+})) |
|
+ |
|
+/* port state */ |
|
+enum { |
|
+ UCHCOM_PORT_CLOSED, /* port is closed */ |
|
+ UCHCOM_PORT_OPEN, /* port is open */ |
|
+ UCHCOM_PORT_CLOSING |
|
+}; |
|
+ |
|
+/* port flags */ |
|
+enum { |
|
+ UCHCOM_PORT_TX_STOPPED = 0x0001 /* transmit not allowed */ |
|
+}; |
|
+ |
|
+/* pipe state */ |
|
+enum { |
|
+ UCHCOM_PIPE_CLOSED, /* pipe is closed */ |
|
+ UCHCOM_PIPE_IDLE, /* open but no requests */ |
|
+ UCHCOM_PIPE_BUSY /* servicing request */ |
|
+}; |
|
+ |
|
+/* various numbers */ |
|
+enum { |
|
+ UCHCOM_BULKOUT_TIMEOUT = 15, /* bulkout timeout */ |
|
+ UCHCOM_BULKIN_TIMEOUT = 15, /* bulkin timeout */ |
|
+ UCHCOM_XFER_SZ_MAX = 64, /* max xfer size */ |
|
+ UCHCOM_CLEANUP_LEVEL_MAX = 6 /* cleanup level */ |
|
+}; |
|
+ |
|
+/* |
|
+ * 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_IN_DATA 0x00000400 |
|
+#define DPRINT_OUT_DATA 0x00000800 |
|
+#define DPRINT_CTLOP 0x00001000 |
|
+#define DPRINT_HOTPLUG 0x00002000 |
|
+#define DPRINT_PM 0x00004000 |
|
+#define DPRINT_MASK_ALL 0xFFFFFFFF |
|
+ |
|
+#ifdef __cplusplus |
|
+} |
|
+#endif |
|
+ |
|
+#endif /* _SYS_USB_UCHCOM_VAR_H */ |
|
diff --git a/usr/src/uts/intel/Makefile.intel b/usr/src/uts/intel/Makefile.intel |
|
index 94e2cfb415..c1e30efb92 100644 |
|
--- a/usr/src/uts/intel/Makefile.intel |
|
+++ b/usr/src/uts/intel/Makefile.intel |
|
@@ -526,6 +526,7 @@ DRV_KMODS += usbser |
|
DRV_KMODS += usbsacm |
|
DRV_KMODS += usbsksp |
|
DRV_KMODS += usbsprl |
|
+DRV_KMODS += uchcom |
|
DRV_KMODS += usb_ac |
|
DRV_KMODS += usb_as |
|
DRV_KMODS += usbskel |
|
diff --git a/usr/src/uts/intel/uchcom/Makefile b/usr/src/uts/intel/uchcom/Makefile |
|
new file mode 100644 |
|
index 0000000000..791d0ecfe8 |
|
--- /dev/null |
|
+++ b/usr/src/uts/intel/uchcom/Makefile |
|
@@ -0,0 +1,79 @@ |
|
+# |
|
+# CDDL HEADER START |
|
+# |
|
+# The contents of this file are subject to the terms of the |
|
+# Common Development and Distribution License (the "License"). |
|
+# You may not use this file except in compliance with the License. |
|
+# |
|
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
+# or http://www.opensolaris.org/os/licensing. |
|
+# See the License for the specific language governing permissions |
|
+# and limitations under the License. |
|
+# |
|
+# When distributing Covered Code, include this CDDL HEADER in each |
|
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
+# If applicable, add the following below this CDDL HEADER, with the |
|
+# fields enclosed by brackets "[]" replaced with your own identifying |
|
+# information: Portions Copyright [yyyy] [name of copyright owner] |
|
+# |
|
+# CDDL HEADER END |
|
+# |
|
+# |
|
+# uts/intel/uchcom/Makefile |
|
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
|
+# Use is subject to license terms. |
|
+ |
|
+# |
|
+# Path to the base of the uts directory tree (usually /usr/src/uts). |
|
+# |
|
+UTSBASE = ../.. |
|
+ |
|
+# |
|
+# Define the module and object file sets. |
|
+# |
|
+MODULE = uchcom |
|
+OBJECTS = $(UCHCOM_OBJS:%=$(OBJS_DIR)/%) |
|
+LINTS = $(UCHCOM_OBJS:%.o=$(LINTS_DIR)/%.ln) |
|
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) |
|
+ |
|
+# |
|
+# Include common rules. |
|
+# |
|
+include $(UTSBASE)/intel/Makefile.intel |
|
+ |
|
+LDFLAGS += -dy -Nmisc/usba -Nmisc/usbser |
|
+ |
|
+CERRWARN += $(CNOWARN_UNINIT) |
|
+ |
|
+# needs work |
|
+SMOFF += deref_check |
|
+ |
|
+# |
|
+# 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 |
|
diff --git a/usr/src/uts/sparc/uchcom/Makefile b/usr/src/uts/sparc/uchcom/Makefile |
|
new file mode 100644 |
|
index 0000000000..3fb0735492 |
|
--- /dev/null |
|
+++ b/usr/src/uts/sparc/uchcom/Makefile |
|
@@ -0,0 +1,85 @@ |
|
+# |
|
+# CDDL HEADER START |
|
+# |
|
+# The contents of this file are subject to the terms of the |
|
+# Common Development and Distribution License (the "License"). |
|
+# You may not use this file except in compliance with the License. |
|
+# |
|
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
+# or http://www.opensolaris.org/os/licensing. |
|
+# See the License for the specific language governing permissions |
|
+# and limitations under the License. |
|
+# |
|
+# When distributing Covered Code, include this CDDL HEADER in each |
|
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
+# If applicable, add the following below this CDDL HEADER, with the |
|
+# fields enclosed by brackets "[]" replaced with your own identifying |
|
+# information: Portions Copyright [yyyy] [name of copyright owner] |
|
+# |
|
+# CDDL HEADER END |
|
+# |
|
+# |
|
+# uts/sparc/uchcom/Makefile |
|
+# |
|
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
|
+# Use is subject to license terms. |
|
+# |
|
+# |
|
+# This makefile drives the production of Abstract Control Model of |
|
+# USB Communication Devices Class dirver. |
|
+# |
|
+# Path to the base of the uts directory tree (usually /usr/src/uts). |
|
+# |
|
+UTSBASE = ../.. |
|
+ |
|
+# |
|
+# Define the module and object file sets. |
|
+# |
|
+MODULE = uchcom |
|
+OBJECTS = $(UCHCOM_OBJS:%=$(OBJS_DIR)/%) |
|
+LINTS = $(UCHCOM_OBJS:%.o=$(LINTS_DIR)/%.ln) |
|
+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 |
|
+ |
|
+CERRWARN += $(CNOWARN_UNINIT) |
|
+ |
|
+# |
|
+# 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)/sparc/Makefile.targ |