17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
50a05e705Slc * Common Development and Distribution License (the "License").
60a05e705Slc * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate *
21112116d8Sfb * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
227c478bd9Sstevel@tonic-gate * Use is subject to license terms.
2348bbca81SDaniel Hoffman * Copyright (c) 2016 by Delphix. All rights reserved.
247c478bd9Sstevel@tonic-gate */
257c478bd9Sstevel@tonic-gate
26a5eb7107SBryan Cantrill /*
27*0d2006e4SRobert Mustacchi * Copyright 2019 Joyent, Inc.
28a5eb7107SBryan Cantrill */
29a5eb7107SBryan Cantrill
307c478bd9Sstevel@tonic-gate /*
317c478bd9Sstevel@tonic-gate * UGEN: USB Generic Driver support code
327c478bd9Sstevel@tonic-gate *
337c478bd9Sstevel@tonic-gate * This code provides entry points called by the ugen driver or other
347c478bd9Sstevel@tonic-gate * drivers that want to export a ugen interface
357c478bd9Sstevel@tonic-gate *
367c478bd9Sstevel@tonic-gate * The "Universal Generic Driver" (UGEN) for USB devices provides interfaces
377c478bd9Sstevel@tonic-gate * to talk to USB devices. This is very useful for Point of Sale sale
387c478bd9Sstevel@tonic-gate * devices and other simple devices like USB scanner, USB palm pilot.
397c478bd9Sstevel@tonic-gate * The UGEN provides a system call interface to USB devices enabling
4048bbca81SDaniel Hoffman * a USB device vendor to write an application for their
417c478bd9Sstevel@tonic-gate * device instead of writing a driver. This facilitates the vendor to write
427c478bd9Sstevel@tonic-gate * device management s/w quickly in userland.
437c478bd9Sstevel@tonic-gate *
447c478bd9Sstevel@tonic-gate * UGEN supports read/write/poll entry points. An application can be written
457c478bd9Sstevel@tonic-gate * using read/write/aioread/aiowrite/poll system calls to communicate
467c478bd9Sstevel@tonic-gate * with the device.
477c478bd9Sstevel@tonic-gate *
487c478bd9Sstevel@tonic-gate * XXX Theory of Operations
497c478bd9Sstevel@tonic-gate */
507c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h>
517c478bd9Sstevel@tonic-gate #include <sys/usb/usba.h>
527c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
53d29f5a71Szhigang lu - Sun Microsystems - Beijing China #include <sys/strsun.h>
547c478bd9Sstevel@tonic-gate
557c478bd9Sstevel@tonic-gate #include "sys/usb/clients/ugen/usb_ugen.h"
567c478bd9Sstevel@tonic-gate #include "sys/usb/usba/usba_ugen.h"
577c478bd9Sstevel@tonic-gate #include "sys/usb/usba/usba_ugend.h"
587c478bd9Sstevel@tonic-gate
597c478bd9Sstevel@tonic-gate /* Debugging information */
604610e4a0Sfrits uint_t ugen_errmask = (uint_t)UGEN_PRINT_ALL;
614610e4a0Sfrits uint_t ugen_errlevel = USB_LOG_L4;
624610e4a0Sfrits uint_t ugen_instance_debug = (uint_t)-1;
637c478bd9Sstevel@tonic-gate
647c478bd9Sstevel@tonic-gate /* default endpoint descriptor */
657c478bd9Sstevel@tonic-gate static usb_ep_descr_t ugen_default_ep_descr =
667c478bd9Sstevel@tonic-gate {7, 5, 0, USB_EP_ATTR_CONTROL, 8, 0};
677c478bd9Sstevel@tonic-gate
687c478bd9Sstevel@tonic-gate /* tunables */
694610e4a0Sfrits int ugen_busy_loop = 60; /* secs */
704610e4a0Sfrits int ugen_ctrl_timeout = 10;
714610e4a0Sfrits int ugen_bulk_timeout = 10;
724610e4a0Sfrits int ugen_intr_timeout = 10;
734610e4a0Sfrits int ugen_enable_pm = 0;
740a05e705Slc int ugen_isoc_buf_limit = 1000; /* ms */
757c478bd9Sstevel@tonic-gate
767c478bd9Sstevel@tonic-gate
777c478bd9Sstevel@tonic-gate /* local function prototypes */
787c478bd9Sstevel@tonic-gate static int ugen_cleanup(ugen_state_t *);
797c478bd9Sstevel@tonic-gate static int ugen_cpr_suspend(ugen_state_t *);
807c478bd9Sstevel@tonic-gate static void ugen_cpr_resume(ugen_state_t *);
817c478bd9Sstevel@tonic-gate
827c478bd9Sstevel@tonic-gate static void ugen_restore_state(ugen_state_t *);
837c478bd9Sstevel@tonic-gate static int ugen_check_open_flags(ugen_state_t *, dev_t, int);
847c478bd9Sstevel@tonic-gate static int ugen_strategy(struct buf *);
857c478bd9Sstevel@tonic-gate static void ugen_minphys(struct buf *);
867c478bd9Sstevel@tonic-gate
877c478bd9Sstevel@tonic-gate static void ugen_pm_init(ugen_state_t *);
887c478bd9Sstevel@tonic-gate static void ugen_pm_destroy(ugen_state_t *);
897c478bd9Sstevel@tonic-gate static void ugen_pm_busy_component(ugen_state_t *);
907c478bd9Sstevel@tonic-gate static void ugen_pm_idle_component(ugen_state_t *);
917c478bd9Sstevel@tonic-gate
927c478bd9Sstevel@tonic-gate /* endpoint xfer and status management */
937c478bd9Sstevel@tonic-gate static int ugen_epxs_init(ugen_state_t *);
947c478bd9Sstevel@tonic-gate static void ugen_epxs_destroy(ugen_state_t *);
957c478bd9Sstevel@tonic-gate static int ugen_epxs_data_init(ugen_state_t *, usb_ep_data_t *,
967c478bd9Sstevel@tonic-gate uchar_t, uchar_t, uchar_t, uchar_t);
977c478bd9Sstevel@tonic-gate static void ugen_epxs_data_destroy(ugen_state_t *, ugen_ep_t *);
987c478bd9Sstevel@tonic-gate static int ugen_epxs_minor_nodes_create(ugen_state_t *,
997c478bd9Sstevel@tonic-gate usb_ep_descr_t *, uchar_t,
1007c478bd9Sstevel@tonic-gate uchar_t, uchar_t, uchar_t);
1017c478bd9Sstevel@tonic-gate static int ugen_epxs_check_open_nodes(ugen_state_t *);
1027c478bd9Sstevel@tonic-gate
1037c478bd9Sstevel@tonic-gate static int ugen_epx_open(ugen_state_t *, dev_t, int);
1047c478bd9Sstevel@tonic-gate static void ugen_epx_close(ugen_state_t *, dev_t, int);
1057c478bd9Sstevel@tonic-gate static void ugen_epx_shutdown(ugen_state_t *);
1067c478bd9Sstevel@tonic-gate
1077c478bd9Sstevel@tonic-gate static int ugen_epx_open_pipe(ugen_state_t *, ugen_ep_t *, int);
1087c478bd9Sstevel@tonic-gate static void ugen_epx_close_pipe(ugen_state_t *, ugen_ep_t *);
1097c478bd9Sstevel@tonic-gate
1107c478bd9Sstevel@tonic-gate static int ugen_epx_req(ugen_state_t *, struct buf *);
1117c478bd9Sstevel@tonic-gate static int ugen_epx_ctrl_req(ugen_state_t *, ugen_ep_t *,
1127c478bd9Sstevel@tonic-gate struct buf *, boolean_t *);
1137c478bd9Sstevel@tonic-gate static void ugen_epx_ctrl_req_cb(usb_pipe_handle_t, usb_ctrl_req_t *);
1147c478bd9Sstevel@tonic-gate static int ugen_epx_bulk_req(ugen_state_t *, ugen_ep_t *,
1157c478bd9Sstevel@tonic-gate struct buf *, boolean_t *);
1167c478bd9Sstevel@tonic-gate static void ugen_epx_bulk_req_cb(usb_pipe_handle_t, usb_bulk_req_t *);
1177c478bd9Sstevel@tonic-gate static int ugen_epx_intr_IN_req(ugen_state_t *, ugen_ep_t *,
1187c478bd9Sstevel@tonic-gate struct buf *, boolean_t *);
1197c478bd9Sstevel@tonic-gate static int ugen_epx_intr_IN_start_polling(ugen_state_t *, ugen_ep_t *);
1207c478bd9Sstevel@tonic-gate static void ugen_epx_intr_IN_stop_polling(ugen_state_t *, ugen_ep_t *);
1217c478bd9Sstevel@tonic-gate static void ugen_epx_intr_IN_req_cb(usb_pipe_handle_t, usb_intr_req_t *);
1227c478bd9Sstevel@tonic-gate static int ugen_epx_intr_OUT_req(ugen_state_t *, ugen_ep_t *,
1237c478bd9Sstevel@tonic-gate struct buf *, boolean_t *);
1247c478bd9Sstevel@tonic-gate static void ugen_epx_intr_OUT_req_cb(usb_pipe_handle_t, usb_intr_req_t *);
1250a05e705Slc static int ugen_epx_isoc_IN_req(ugen_state_t *, ugen_ep_t *,
1260a05e705Slc struct buf *, boolean_t *);
1270a05e705Slc static int ugen_epx_isoc_IN_start_polling(ugen_state_t *, ugen_ep_t *);
1280a05e705Slc static void ugen_epx_isoc_IN_stop_polling(ugen_state_t *, ugen_ep_t *);
1290a05e705Slc static void ugen_epx_isoc_IN_req_cb(usb_pipe_handle_t, usb_isoc_req_t *);
1300a05e705Slc static int ugen_epx_isoc_OUT_req(ugen_state_t *, ugen_ep_t *,
1310a05e705Slc struct buf *, boolean_t *);
1320a05e705Slc static void ugen_epx_isoc_OUT_req_cb(usb_pipe_handle_t, usb_isoc_req_t *);
1337c478bd9Sstevel@tonic-gate
1347c478bd9Sstevel@tonic-gate static int ugen_eps_open(ugen_state_t *, dev_t, int);
1357c478bd9Sstevel@tonic-gate static void ugen_eps_close(ugen_state_t *, dev_t, int);
1367c478bd9Sstevel@tonic-gate static int ugen_eps_req(ugen_state_t *, struct buf *);
1377c478bd9Sstevel@tonic-gate static void ugen_update_ep_descr(ugen_state_t *, ugen_ep_t *);
1387c478bd9Sstevel@tonic-gate
1397c478bd9Sstevel@tonic-gate /* device status management */
1407c478bd9Sstevel@tonic-gate static int ugen_ds_init(ugen_state_t *);
1417c478bd9Sstevel@tonic-gate static void ugen_ds_destroy(ugen_state_t *);
1427c478bd9Sstevel@tonic-gate static int ugen_ds_open(ugen_state_t *, dev_t, int);
1437c478bd9Sstevel@tonic-gate static void ugen_ds_close(ugen_state_t *, dev_t, int);
1447c478bd9Sstevel@tonic-gate static int ugen_ds_req(ugen_state_t *, struct buf *);
1457c478bd9Sstevel@tonic-gate static void ugen_ds_change(ugen_state_t *);
1467c478bd9Sstevel@tonic-gate static int ugen_ds_minor_nodes_create(ugen_state_t *);
1477c478bd9Sstevel@tonic-gate static void ugen_ds_poll_wakeup(ugen_state_t *);
1487c478bd9Sstevel@tonic-gate
1497c478bd9Sstevel@tonic-gate /* utility functions */
1507c478bd9Sstevel@tonic-gate static int ugen_minor_index_create(ugen_state_t *, ugen_minor_t);
1517c478bd9Sstevel@tonic-gate static ugen_minor_t ugen_devt2minor(ugen_state_t *, dev_t);
1527c478bd9Sstevel@tonic-gate static void ugen_minor_node_table_create(ugen_state_t *);
1537c478bd9Sstevel@tonic-gate static void ugen_minor_node_table_destroy(ugen_state_t *);
1547c478bd9Sstevel@tonic-gate static void ugen_minor_node_table_shrink(ugen_state_t *);
1557c478bd9Sstevel@tonic-gate static int ugen_cr2lcstat(int);
1567c478bd9Sstevel@tonic-gate static void ugen_check_mask(uint_t, uint_t *, uint_t *);
15796603521Sfrits static int ugen_is_valid_minor_node(ugen_state_t *, dev_t);
1587c478bd9Sstevel@tonic-gate
1597c478bd9Sstevel@tonic-gate static kmutex_t ugen_devt_list_mutex;
1607c478bd9Sstevel@tonic-gate static ugen_devt_list_entry_t ugen_devt_list;
1617c478bd9Sstevel@tonic-gate static ugen_devt_cache_entry_t ugen_devt_cache[UGEN_DEVT_CACHE_SIZE];
1627c478bd9Sstevel@tonic-gate static uint_t ugen_devt_cache_index;
1637c478bd9Sstevel@tonic-gate static void ugen_store_devt(ugen_state_t *, minor_t);
1647c478bd9Sstevel@tonic-gate static ugen_state_t *ugen_devt2state(dev_t);
1657c478bd9Sstevel@tonic-gate static void ugen_free_devt(ugen_state_t *);
1667c478bd9Sstevel@tonic-gate
1677c478bd9Sstevel@tonic-gate /*
1687c478bd9Sstevel@tonic-gate * usb_ugen entry points
1697c478bd9Sstevel@tonic-gate *
1707c478bd9Sstevel@tonic-gate * usb_ugen_get_hdl:
1717c478bd9Sstevel@tonic-gate * allocate and initialize handle
1727c478bd9Sstevel@tonic-gate */
1737c478bd9Sstevel@tonic-gate usb_ugen_hdl_t
usb_ugen_get_hdl(dev_info_t * dip,usb_ugen_info_t * usb_ugen_info)1747c478bd9Sstevel@tonic-gate usb_ugen_get_hdl(dev_info_t *dip, usb_ugen_info_t *usb_ugen_info)
1757c478bd9Sstevel@tonic-gate {
1767c478bd9Sstevel@tonic-gate usb_ugen_hdl_impl_t *hdl = kmem_zalloc(sizeof (*hdl), KM_SLEEP);
1777c478bd9Sstevel@tonic-gate ugen_state_t *ugenp = kmem_zalloc(sizeof (ugen_state_t),
1780a05e705Slc KM_SLEEP);
1797c478bd9Sstevel@tonic-gate uint_t len, shift, limit;
1807c478bd9Sstevel@tonic-gate int rval;
1817c478bd9Sstevel@tonic-gate
1827c478bd9Sstevel@tonic-gate hdl->hdl_ugenp = ugenp;
1837c478bd9Sstevel@tonic-gate
1847c478bd9Sstevel@tonic-gate /* masks may not overlap */
1857c478bd9Sstevel@tonic-gate if (usb_ugen_info->usb_ugen_minor_node_ugen_bits_mask &
1867c478bd9Sstevel@tonic-gate usb_ugen_info->usb_ugen_minor_node_instance_mask) {
1877c478bd9Sstevel@tonic-gate usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
1887c478bd9Sstevel@tonic-gate
1897c478bd9Sstevel@tonic-gate return (NULL);
1907c478bd9Sstevel@tonic-gate }
1917c478bd9Sstevel@tonic-gate
1927c478bd9Sstevel@tonic-gate if ((rval = usb_get_dev_data(dip, &ugenp->ug_dev_data,
1937c478bd9Sstevel@tonic-gate usb_owns_device(dip) ? USB_PARSE_LVL_ALL : USB_PARSE_LVL_IF,
1947c478bd9Sstevel@tonic-gate 0)) != USB_SUCCESS) {
1957c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
1967c478bd9Sstevel@tonic-gate "usb_ugen_attach: usb_get_dev_data failed, rval=%d", rval);
1977c478bd9Sstevel@tonic-gate
1987c478bd9Sstevel@tonic-gate return (NULL);
1997c478bd9Sstevel@tonic-gate }
2007c478bd9Sstevel@tonic-gate
2017c478bd9Sstevel@tonic-gate /* Initialize state structure for this instance */
2027c478bd9Sstevel@tonic-gate mutex_init(&ugenp->ug_mutex, NULL, MUTEX_DRIVER,
2030a05e705Slc ugenp->ug_dev_data->dev_iblock_cookie);
2047c478bd9Sstevel@tonic-gate
2057c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
2067c478bd9Sstevel@tonic-gate ugenp->ug_dip = dip;
2077c478bd9Sstevel@tonic-gate ugenp->ug_instance = ddi_get_instance(dip);
2087c478bd9Sstevel@tonic-gate ugenp->ug_hdl = hdl;
2097c478bd9Sstevel@tonic-gate
2107c478bd9Sstevel@tonic-gate /* Allocate a log handle for debug/error messages */
2117c478bd9Sstevel@tonic-gate if (strcmp(ddi_driver_name(dip), "ugen") != 0) {
2127c478bd9Sstevel@tonic-gate char *name;
2137c478bd9Sstevel@tonic-gate
2147c478bd9Sstevel@tonic-gate len = strlen(ddi_driver_name(dip)) + sizeof ("_ugen") + 1;
2157c478bd9Sstevel@tonic-gate name = kmem_alloc(len, KM_SLEEP);
2167c478bd9Sstevel@tonic-gate (void) snprintf(name, len, "%s_ugen", ddi_driver_name(dip));
2177c478bd9Sstevel@tonic-gate
2187c478bd9Sstevel@tonic-gate ugenp->ug_log_hdl = usb_alloc_log_hdl(dip, name, &ugen_errlevel,
2190a05e705Slc &ugen_errmask, &ugen_instance_debug, 0);
2207c478bd9Sstevel@tonic-gate hdl->hdl_log_name = name;
2217c478bd9Sstevel@tonic-gate hdl->hdl_log_name_length = len;
2227c478bd9Sstevel@tonic-gate } else {
2237c478bd9Sstevel@tonic-gate ugenp->ug_log_hdl = usb_alloc_log_hdl(dip, "ugen",
2240a05e705Slc &ugen_errlevel,
2250a05e705Slc &ugen_errmask, &ugen_instance_debug, 0);
2267c478bd9Sstevel@tonic-gate }
2277c478bd9Sstevel@tonic-gate
2287c478bd9Sstevel@tonic-gate hdl->hdl_dip = dip;
2297c478bd9Sstevel@tonic-gate hdl->hdl_flags = usb_ugen_info->usb_ugen_flags;
2307c478bd9Sstevel@tonic-gate
2317c478bd9Sstevel@tonic-gate ugen_check_mask(usb_ugen_info->usb_ugen_minor_node_ugen_bits_mask,
2320a05e705Slc &shift, &limit);
2337c478bd9Sstevel@tonic-gate if (limit == 0) {
2347c478bd9Sstevel@tonic-gate usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
2357c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
2367c478bd9Sstevel@tonic-gate
2377c478bd9Sstevel@tonic-gate return (NULL);
2387c478bd9Sstevel@tonic-gate }
2397c478bd9Sstevel@tonic-gate hdl->hdl_minor_node_ugen_bits_mask = usb_ugen_info->
2400a05e705Slc usb_ugen_minor_node_ugen_bits_mask;
2417c478bd9Sstevel@tonic-gate hdl->hdl_minor_node_ugen_bits_shift = shift;
2427c478bd9Sstevel@tonic-gate hdl->hdl_minor_node_ugen_bits_limit = limit;
2437c478bd9Sstevel@tonic-gate
2447c478bd9Sstevel@tonic-gate ugen_check_mask(usb_ugen_info->usb_ugen_minor_node_instance_mask,
2450a05e705Slc &shift, &limit);
2467c478bd9Sstevel@tonic-gate if (limit == 0) {
2477c478bd9Sstevel@tonic-gate usb_ugen_release_hdl((usb_ugen_hdl_t)hdl);
2487c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
2497c478bd9Sstevel@tonic-gate
2507c478bd9Sstevel@tonic-gate return (NULL);
2517c478bd9Sstevel@tonic-gate }
2527c478bd9Sstevel@tonic-gate
2537c478bd9Sstevel@tonic-gate hdl->hdl_minor_node_instance_mask = usb_ugen_info->
2540a05e705Slc usb_ugen_minor_node_instance_mask;
2557c478bd9Sstevel@tonic-gate hdl->hdl_minor_node_instance_shift = shift;
2567c478bd9Sstevel@tonic-gate hdl->hdl_minor_node_instance_limit = limit;
2577c478bd9Sstevel@tonic-gate
2587c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
2597c478bd9Sstevel@tonic-gate "usb_ugen_get_hdl: instance shift=%d instance limit=%d",
2607c478bd9Sstevel@tonic-gate hdl->hdl_minor_node_instance_shift,
2617c478bd9Sstevel@tonic-gate hdl->hdl_minor_node_instance_limit);
2627c478bd9Sstevel@tonic-gate
2637c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
2647c478bd9Sstevel@tonic-gate "usb_ugen_get_hdl: bits shift=%d bits limit=%d",
2657c478bd9Sstevel@tonic-gate hdl->hdl_minor_node_ugen_bits_shift,
2667c478bd9Sstevel@tonic-gate hdl->hdl_minor_node_ugen_bits_limit);
2677c478bd9Sstevel@tonic-gate
2687c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
2697c478bd9Sstevel@tonic-gate
2707c478bd9Sstevel@tonic-gate return ((usb_ugen_hdl_t)hdl);
2717c478bd9Sstevel@tonic-gate }
2727c478bd9Sstevel@tonic-gate
2737c478bd9Sstevel@tonic-gate
2747c478bd9Sstevel@tonic-gate /*
2757c478bd9Sstevel@tonic-gate * usb_ugen_release_hdl:
2767c478bd9Sstevel@tonic-gate * deallocate a handle
2777c478bd9Sstevel@tonic-gate */
2787c478bd9Sstevel@tonic-gate void
usb_ugen_release_hdl(usb_ugen_hdl_t usb_ugen_hdl)2797c478bd9Sstevel@tonic-gate usb_ugen_release_hdl(usb_ugen_hdl_t usb_ugen_hdl)
2807c478bd9Sstevel@tonic-gate {
2817c478bd9Sstevel@tonic-gate usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
2820a05e705Slc (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
2837c478bd9Sstevel@tonic-gate
2847c478bd9Sstevel@tonic-gate if (usb_ugen_hdl_impl) {
2857c478bd9Sstevel@tonic-gate ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
2867c478bd9Sstevel@tonic-gate
2877c478bd9Sstevel@tonic-gate if (ugenp) {
2887c478bd9Sstevel@tonic-gate mutex_destroy(&ugenp->ug_mutex);
2897c478bd9Sstevel@tonic-gate usb_free_log_hdl(ugenp->ug_log_hdl);
2907c478bd9Sstevel@tonic-gate usb_free_dev_data(usb_ugen_hdl_impl->hdl_dip,
2910a05e705Slc ugenp->ug_dev_data);
2927c478bd9Sstevel@tonic-gate kmem_free(ugenp, sizeof (*ugenp));
2937c478bd9Sstevel@tonic-gate }
2947c478bd9Sstevel@tonic-gate if (usb_ugen_hdl_impl->hdl_log_name) {
2957c478bd9Sstevel@tonic-gate kmem_free(usb_ugen_hdl_impl->hdl_log_name,
2960a05e705Slc usb_ugen_hdl_impl->hdl_log_name_length);
2977c478bd9Sstevel@tonic-gate }
2987c478bd9Sstevel@tonic-gate kmem_free(usb_ugen_hdl_impl, sizeof (*usb_ugen_hdl_impl));
2997c478bd9Sstevel@tonic-gate }
3007c478bd9Sstevel@tonic-gate }
3017c478bd9Sstevel@tonic-gate
3027c478bd9Sstevel@tonic-gate
3037c478bd9Sstevel@tonic-gate /*
3047c478bd9Sstevel@tonic-gate * usb_ugen_attach()
3057c478bd9Sstevel@tonic-gate */
3067c478bd9Sstevel@tonic-gate int
usb_ugen_attach(usb_ugen_hdl_t usb_ugen_hdl,ddi_attach_cmd_t cmd)3077c478bd9Sstevel@tonic-gate usb_ugen_attach(usb_ugen_hdl_t usb_ugen_hdl, ddi_attach_cmd_t cmd)
3087c478bd9Sstevel@tonic-gate {
3097c478bd9Sstevel@tonic-gate usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
3100a05e705Slc (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
3117c478bd9Sstevel@tonic-gate ugen_state_t *ugenp;
3127c478bd9Sstevel@tonic-gate dev_info_t *dip;
3137c478bd9Sstevel@tonic-gate
3147c478bd9Sstevel@tonic-gate if (usb_ugen_hdl == NULL) {
3157c478bd9Sstevel@tonic-gate
3167c478bd9Sstevel@tonic-gate return (USB_FAILURE);
3177c478bd9Sstevel@tonic-gate }
3187c478bd9Sstevel@tonic-gate
3197c478bd9Sstevel@tonic-gate ugenp = usb_ugen_hdl_impl->hdl_ugenp;
3207c478bd9Sstevel@tonic-gate dip = usb_ugen_hdl_impl->hdl_dip;
3217c478bd9Sstevel@tonic-gate
3227c478bd9Sstevel@tonic-gate
3237c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3247c478bd9Sstevel@tonic-gate "usb_ugen_attach: cmd=%d", cmd);
3257c478bd9Sstevel@tonic-gate
3267c478bd9Sstevel@tonic-gate switch (cmd) {
3277c478bd9Sstevel@tonic-gate case DDI_ATTACH:
3287c478bd9Sstevel@tonic-gate
3297c478bd9Sstevel@tonic-gate break;
3307c478bd9Sstevel@tonic-gate case DDI_RESUME:
3317c478bd9Sstevel@tonic-gate ugen_cpr_resume(ugenp);
3327c478bd9Sstevel@tonic-gate
3337c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
3347c478bd9Sstevel@tonic-gate default:
3357c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_ATTA, NULL,
3367c478bd9Sstevel@tonic-gate "usb_ugen_attach: unknown command");
3377c478bd9Sstevel@tonic-gate
3387c478bd9Sstevel@tonic-gate return (USB_FAILURE);
3397c478bd9Sstevel@tonic-gate }
3407c478bd9Sstevel@tonic-gate
3417c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
3427c478bd9Sstevel@tonic-gate ugenp->ug_ser_cookie =
3437c478bd9Sstevel@tonic-gate usb_init_serialization(dip, USB_INIT_SER_CHECK_SAME_THREAD);
3447c478bd9Sstevel@tonic-gate ugenp->ug_cleanup_flags |= UGEN_INIT_LOCKS;
3457c478bd9Sstevel@tonic-gate
3467c478bd9Sstevel@tonic-gate /* Get maximum bulk transfer size supported by the HCD */
3477c478bd9Sstevel@tonic-gate if (usb_pipe_get_max_bulk_transfer_size(dip,
3487c478bd9Sstevel@tonic-gate &ugenp->ug_max_bulk_xfer_sz) != USB_SUCCESS) {
3497c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3507c478bd9Sstevel@tonic-gate "usb_ugen_attach: Getting max bulk xfer sz failed");
3517c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
3527c478bd9Sstevel@tonic-gate
3537c478bd9Sstevel@tonic-gate goto fail;
3547c478bd9Sstevel@tonic-gate }
3557c478bd9Sstevel@tonic-gate
3567c478bd9Sstevel@tonic-gate /* table for mapping 48 bit minor codes to 9 bit index (for ugen) */
3577c478bd9Sstevel@tonic-gate ugen_minor_node_table_create(ugenp);
3587c478bd9Sstevel@tonic-gate
3597c478bd9Sstevel@tonic-gate /* prepare device status node handling */
3607c478bd9Sstevel@tonic-gate if (ugen_ds_init(ugenp) != USB_SUCCESS) {
3617c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3627c478bd9Sstevel@tonic-gate "usb_ugen_attach: preparing dev status failed");
3637c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
3647c478bd9Sstevel@tonic-gate
3657c478bd9Sstevel@tonic-gate goto fail;
3667c478bd9Sstevel@tonic-gate }
3677c478bd9Sstevel@tonic-gate
3687c478bd9Sstevel@tonic-gate /* prepare all available xfer and status endpoints nodes */
3697c478bd9Sstevel@tonic-gate if (ugen_epxs_init(ugenp) != USB_SUCCESS) {
3707c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
3717c478bd9Sstevel@tonic-gate "usb_ugen_attach: preparing endpoints failed");
3727c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
3737c478bd9Sstevel@tonic-gate
3747c478bd9Sstevel@tonic-gate goto fail;
3757c478bd9Sstevel@tonic-gate }
3767c478bd9Sstevel@tonic-gate
3777c478bd9Sstevel@tonic-gate /* reduce table size if not all entries are used */
3787c478bd9Sstevel@tonic-gate ugen_minor_node_table_shrink(ugenp);
3797c478bd9Sstevel@tonic-gate
3807c478bd9Sstevel@tonic-gate /* we are ready to go */
3817c478bd9Sstevel@tonic-gate ugenp->ug_dev_state = USB_DEV_ONLINE;
3827c478bd9Sstevel@tonic-gate
3837c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
3847c478bd9Sstevel@tonic-gate
3857c478bd9Sstevel@tonic-gate /* prepare PM */
3867c478bd9Sstevel@tonic-gate if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
3877c478bd9Sstevel@tonic-gate ugen_pm_init(ugenp);
3887c478bd9Sstevel@tonic-gate }
3897c478bd9Sstevel@tonic-gate
3907c478bd9Sstevel@tonic-gate /*
3917c478bd9Sstevel@tonic-gate * if ugen driver, kill all child nodes otherwise set cfg fails
3927c478bd9Sstevel@tonic-gate * if requested
3937c478bd9Sstevel@tonic-gate */
3947c478bd9Sstevel@tonic-gate if (usb_owns_device(dip) &&
3957c478bd9Sstevel@tonic-gate (usb_ugen_hdl_impl->hdl_flags & USB_UGEN_REMOVE_CHILDREN)) {
3967c478bd9Sstevel@tonic-gate dev_info_t *cdip;
3977c478bd9Sstevel@tonic-gate
3987c478bd9Sstevel@tonic-gate /* save cfgidx so we can restore on detach */
3997c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
4007c478bd9Sstevel@tonic-gate ugenp->ug_initial_cfgidx = usb_get_current_cfgidx(dip);
4017c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
4027c478bd9Sstevel@tonic-gate
4037c478bd9Sstevel@tonic-gate for (cdip = ddi_get_child(dip); cdip; ) {
4047c478bd9Sstevel@tonic-gate dev_info_t *next = ddi_get_next_sibling(cdip);
4057c478bd9Sstevel@tonic-gate (void) ddi_remove_child(cdip, 0);
4067c478bd9Sstevel@tonic-gate cdip = next;
4077c478bd9Sstevel@tonic-gate }
4087c478bd9Sstevel@tonic-gate }
4097c478bd9Sstevel@tonic-gate
4107c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
4117c478bd9Sstevel@tonic-gate fail:
412*0d2006e4SRobert Mustacchi USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
413*0d2006e4SRobert Mustacchi "attach fail");
414*0d2006e4SRobert Mustacchi (void) ugen_cleanup(ugenp);
4157c478bd9Sstevel@tonic-gate
4167c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
4177c478bd9Sstevel@tonic-gate }
4187c478bd9Sstevel@tonic-gate
4197c478bd9Sstevel@tonic-gate
4207c478bd9Sstevel@tonic-gate /*
4217c478bd9Sstevel@tonic-gate * usb_ugen_detach()
4227c478bd9Sstevel@tonic-gate */
4237c478bd9Sstevel@tonic-gate int
usb_ugen_detach(usb_ugen_hdl_t usb_ugen_hdl,ddi_detach_cmd_t cmd)4247c478bd9Sstevel@tonic-gate usb_ugen_detach(usb_ugen_hdl_t usb_ugen_hdl, ddi_detach_cmd_t cmd)
4257c478bd9Sstevel@tonic-gate {
4267c478bd9Sstevel@tonic-gate usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
4270a05e705Slc (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
4287c478bd9Sstevel@tonic-gate int rval = USB_FAILURE;
4297c478bd9Sstevel@tonic-gate
4307c478bd9Sstevel@tonic-gate if (usb_ugen_hdl) {
4317c478bd9Sstevel@tonic-gate ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
4327c478bd9Sstevel@tonic-gate
4337c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
4347c478bd9Sstevel@tonic-gate "usb_ugen_detach cmd %d", cmd);
4357c478bd9Sstevel@tonic-gate
4367c478bd9Sstevel@tonic-gate switch (cmd) {
4377c478bd9Sstevel@tonic-gate case DDI_DETACH:
4387c478bd9Sstevel@tonic-gate rval = ugen_cleanup(ugenp);
4397c478bd9Sstevel@tonic-gate
4407c478bd9Sstevel@tonic-gate break;
4417c478bd9Sstevel@tonic-gate case DDI_SUSPEND:
4427c478bd9Sstevel@tonic-gate rval = ugen_cpr_suspend(ugenp);
4437c478bd9Sstevel@tonic-gate
4447c478bd9Sstevel@tonic-gate break;
4457c478bd9Sstevel@tonic-gate default:
4467c478bd9Sstevel@tonic-gate
4477c478bd9Sstevel@tonic-gate break;
4487c478bd9Sstevel@tonic-gate }
4497c478bd9Sstevel@tonic-gate }
4507c478bd9Sstevel@tonic-gate
4517c478bd9Sstevel@tonic-gate return (rval);
4527c478bd9Sstevel@tonic-gate }
4537c478bd9Sstevel@tonic-gate
4547c478bd9Sstevel@tonic-gate
4557c478bd9Sstevel@tonic-gate /*
4567c478bd9Sstevel@tonic-gate * ugen_cleanup()
4577c478bd9Sstevel@tonic-gate */
4587c478bd9Sstevel@tonic-gate static int
ugen_cleanup(ugen_state_t * ugenp)4597c478bd9Sstevel@tonic-gate ugen_cleanup(ugen_state_t *ugenp)
4607c478bd9Sstevel@tonic-gate {
4617c478bd9Sstevel@tonic-gate dev_info_t *dip = ugenp->ug_dip;
4627c478bd9Sstevel@tonic-gate
4637c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl, "ugen_cleanup");
4647c478bd9Sstevel@tonic-gate
4657c478bd9Sstevel@tonic-gate if (ugenp->ug_cleanup_flags & UGEN_INIT_LOCKS) {
4667c478bd9Sstevel@tonic-gate
4677c478bd9Sstevel@tonic-gate /* shutdown all endpoints */
4687c478bd9Sstevel@tonic-gate ugen_epx_shutdown(ugenp);
4697c478bd9Sstevel@tonic-gate
4707c478bd9Sstevel@tonic-gate /*
4717c478bd9Sstevel@tonic-gate * At this point, no new activity can be initiated.
4727c478bd9Sstevel@tonic-gate * The driver has disabled hotplug callbacks.
4737c478bd9Sstevel@tonic-gate * The Solaris framework has disabled
4747c478bd9Sstevel@tonic-gate * new opens on a device being detached, and does not
4757c478bd9Sstevel@tonic-gate * allow detaching an open device. PM should power
4767c478bd9Sstevel@tonic-gate * down while we are detaching
4777c478bd9Sstevel@tonic-gate *
4787c478bd9Sstevel@tonic-gate * The following ensures that any other driver
4797c478bd9Sstevel@tonic-gate * activity must have drained (paranoia)
4807c478bd9Sstevel@tonic-gate */
4817c478bd9Sstevel@tonic-gate (void) usb_serialize_access(ugenp->ug_ser_cookie,
4820a05e705Slc USB_WAIT, 0);
4837c478bd9Sstevel@tonic-gate usb_release_access(ugenp->ug_ser_cookie);
4847c478bd9Sstevel@tonic-gate
4857c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
4867c478bd9Sstevel@tonic-gate ASSERT(ugenp->ug_open_count == 0);
4877c478bd9Sstevel@tonic-gate ASSERT(ugenp->ug_pending_cmds == 0);
4887c478bd9Sstevel@tonic-gate
4897c478bd9Sstevel@tonic-gate /* dismantle in reverse order */
4907c478bd9Sstevel@tonic-gate ugen_pm_destroy(ugenp);
4917c478bd9Sstevel@tonic-gate ugen_epxs_destroy(ugenp);
4927c478bd9Sstevel@tonic-gate ugen_ds_destroy(ugenp);
4937c478bd9Sstevel@tonic-gate ugen_minor_node_table_destroy(ugenp);
4947c478bd9Sstevel@tonic-gate
4957c478bd9Sstevel@tonic-gate
4967c478bd9Sstevel@tonic-gate /* restore to initial configuration */
4977c478bd9Sstevel@tonic-gate if (usb_owns_device(dip) &&
4987c478bd9Sstevel@tonic-gate (ugenp->ug_dev_state != USB_DEV_DISCONNECTED)) {
4997c478bd9Sstevel@tonic-gate int idx = ugenp->ug_initial_cfgidx;
5007c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
5017c478bd9Sstevel@tonic-gate (void) usb_set_cfg(dip, idx,
5027c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP, NULL, NULL);
5037c478bd9Sstevel@tonic-gate } else {
5047c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
5057c478bd9Sstevel@tonic-gate }
5067c478bd9Sstevel@tonic-gate
5077c478bd9Sstevel@tonic-gate usb_fini_serialization(ugenp->ug_ser_cookie);
5087c478bd9Sstevel@tonic-gate }
5097c478bd9Sstevel@tonic-gate
5107c478bd9Sstevel@tonic-gate ddi_prop_remove_all(dip);
5117c478bd9Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL);
5127c478bd9Sstevel@tonic-gate
5137c478bd9Sstevel@tonic-gate ugen_free_devt(ugenp);
5147c478bd9Sstevel@tonic-gate
5157c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
5167c478bd9Sstevel@tonic-gate }
5177c478bd9Sstevel@tonic-gate
5187c478bd9Sstevel@tonic-gate
5197c478bd9Sstevel@tonic-gate /*
5207c478bd9Sstevel@tonic-gate * ugen_cpr_suspend
5217c478bd9Sstevel@tonic-gate */
5227c478bd9Sstevel@tonic-gate static int
ugen_cpr_suspend(ugen_state_t * ugenp)5237c478bd9Sstevel@tonic-gate ugen_cpr_suspend(ugen_state_t *ugenp)
5247c478bd9Sstevel@tonic-gate {
5257c478bd9Sstevel@tonic-gate int rval = USB_FAILURE;
5267c478bd9Sstevel@tonic-gate int i;
5277c478bd9Sstevel@tonic-gate int prev_state;
5287c478bd9Sstevel@tonic-gate
5297c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
5307c478bd9Sstevel@tonic-gate "ugen_cpr_suspend:");
5317c478bd9Sstevel@tonic-gate
5327c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
5337c478bd9Sstevel@tonic-gate switch (ugenp->ug_dev_state) {
5347c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE:
5357c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED:
5367c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
5377c478bd9Sstevel@tonic-gate "ugen_cpr_suspend:");
5387c478bd9Sstevel@tonic-gate
5397c478bd9Sstevel@tonic-gate prev_state = ugenp->ug_dev_state;
5407c478bd9Sstevel@tonic-gate ugenp->ug_dev_state = USB_DEV_SUSPENDED;
5417c478bd9Sstevel@tonic-gate
5427c478bd9Sstevel@tonic-gate if (ugenp->ug_open_count) {
5437c478bd9Sstevel@tonic-gate /* drain outstanding cmds */
5447c478bd9Sstevel@tonic-gate for (i = 0; i < ugen_busy_loop; i++) {
5457c478bd9Sstevel@tonic-gate if (ugenp->ug_pending_cmds == 0) {
5467c478bd9Sstevel@tonic-gate
5477c478bd9Sstevel@tonic-gate break;
5487c478bd9Sstevel@tonic-gate }
5497c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
5507c478bd9Sstevel@tonic-gate delay(drv_usectohz(100000));
5517c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
5527c478bd9Sstevel@tonic-gate }
5537c478bd9Sstevel@tonic-gate
5547c478bd9Sstevel@tonic-gate /* if still outstanding cmds, fail suspend */
5557c478bd9Sstevel@tonic-gate if (ugenp->ug_pending_cmds) {
5567c478bd9Sstevel@tonic-gate ugenp->ug_dev_state = prev_state;
5577c478bd9Sstevel@tonic-gate
5587c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_CPR,
5597c478bd9Sstevel@tonic-gate ugenp->ug_log_hdl,
5607c478bd9Sstevel@tonic-gate "ugen_cpr_suspend: pending %d",
5617c478bd9Sstevel@tonic-gate ugenp->ug_pending_cmds);
5627c478bd9Sstevel@tonic-gate
5637c478bd9Sstevel@tonic-gate rval = USB_FAILURE;
5647c478bd9Sstevel@tonic-gate break;
5657c478bd9Sstevel@tonic-gate }
5667c478bd9Sstevel@tonic-gate
5677c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
5687c478bd9Sstevel@tonic-gate (void) usb_serialize_access(ugenp->ug_ser_cookie,
5690a05e705Slc USB_WAIT, 0);
5707c478bd9Sstevel@tonic-gate /* close all pipes */
5717c478bd9Sstevel@tonic-gate ugen_epx_shutdown(ugenp);
5727c478bd9Sstevel@tonic-gate
5737c478bd9Sstevel@tonic-gate usb_release_access(ugenp->ug_ser_cookie);
5747c478bd9Sstevel@tonic-gate
5757c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
5767c478bd9Sstevel@tonic-gate }
5777c478bd9Sstevel@tonic-gate
5787c478bd9Sstevel@tonic-gate /* wakeup devstat reads and polls */
5797c478bd9Sstevel@tonic-gate ugen_ds_change(ugenp);
5807c478bd9Sstevel@tonic-gate ugen_ds_poll_wakeup(ugenp);
5817c478bd9Sstevel@tonic-gate
5827c478bd9Sstevel@tonic-gate rval = USB_SUCCESS;
5837c478bd9Sstevel@tonic-gate break;
5847c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED:
5857c478bd9Sstevel@tonic-gate case USB_UGEN_DEV_UNAVAILABLE_RESUME:
5867c478bd9Sstevel@tonic-gate case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
5877c478bd9Sstevel@tonic-gate default:
5887c478bd9Sstevel@tonic-gate
5897c478bd9Sstevel@tonic-gate break;
5907c478bd9Sstevel@tonic-gate }
5917c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
5927c478bd9Sstevel@tonic-gate
5937c478bd9Sstevel@tonic-gate return (rval);
5947c478bd9Sstevel@tonic-gate }
5957c478bd9Sstevel@tonic-gate
5967c478bd9Sstevel@tonic-gate /*
5977c478bd9Sstevel@tonic-gate * ugen_cpr_resume
5987c478bd9Sstevel@tonic-gate */
5997c478bd9Sstevel@tonic-gate static void
ugen_cpr_resume(ugen_state_t * ugenp)6007c478bd9Sstevel@tonic-gate ugen_cpr_resume(ugen_state_t *ugenp)
6017c478bd9Sstevel@tonic-gate {
6027c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_CPR, ugenp->ug_log_hdl,
6037c478bd9Sstevel@tonic-gate "ugen_cpr_resume:");
6047c478bd9Sstevel@tonic-gate
6057c478bd9Sstevel@tonic-gate ugen_restore_state(ugenp);
6067c478bd9Sstevel@tonic-gate }
6077c478bd9Sstevel@tonic-gate
6087c478bd9Sstevel@tonic-gate /*
6097c478bd9Sstevel@tonic-gate * usb_ugen_disconnect_ev_cb:
6107c478bd9Sstevel@tonic-gate */
6117c478bd9Sstevel@tonic-gate int
usb_ugen_disconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)6127c478bd9Sstevel@tonic-gate usb_ugen_disconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)
6137c478bd9Sstevel@tonic-gate {
6147c478bd9Sstevel@tonic-gate usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
6150a05e705Slc (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
6167c478bd9Sstevel@tonic-gate ugen_state_t *ugenp;
6177c478bd9Sstevel@tonic-gate
6187c478bd9Sstevel@tonic-gate if (usb_ugen_hdl_impl == NULL) {
6197c478bd9Sstevel@tonic-gate
6207c478bd9Sstevel@tonic-gate return (USB_FAILURE);
6217c478bd9Sstevel@tonic-gate }
6227c478bd9Sstevel@tonic-gate
6237c478bd9Sstevel@tonic-gate ugenp = usb_ugen_hdl_impl->hdl_ugenp;
6247c478bd9Sstevel@tonic-gate
6257c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
6267c478bd9Sstevel@tonic-gate "usb_ugen_disconnect_ev_cb:");
6277c478bd9Sstevel@tonic-gate
6287c478bd9Sstevel@tonic-gate /* get exclusive access */
6297c478bd9Sstevel@tonic-gate (void) usb_serialize_access(ugenp->ug_ser_cookie, USB_WAIT, 0);
6307c478bd9Sstevel@tonic-gate
6317c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
6327c478bd9Sstevel@tonic-gate ugenp->ug_dev_state = USB_DEV_DISCONNECTED;
6337c478bd9Sstevel@tonic-gate if (ugenp->ug_open_count) {
6347c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
6357c478bd9Sstevel@tonic-gate
6367c478bd9Sstevel@tonic-gate /* close all pipes */
6377c478bd9Sstevel@tonic-gate (void) ugen_epx_shutdown(ugenp);
6387c478bd9Sstevel@tonic-gate
6397c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
6407c478bd9Sstevel@tonic-gate }
6417c478bd9Sstevel@tonic-gate
6427c478bd9Sstevel@tonic-gate
6437c478bd9Sstevel@tonic-gate /* wakeup devstat reads and polls */
6447c478bd9Sstevel@tonic-gate ugen_ds_change(ugenp);
6457c478bd9Sstevel@tonic-gate ugen_ds_poll_wakeup(ugenp);
6467c478bd9Sstevel@tonic-gate
6477c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
6487c478bd9Sstevel@tonic-gate usb_release_access(ugenp->ug_ser_cookie);
6497c478bd9Sstevel@tonic-gate
6507c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
6517c478bd9Sstevel@tonic-gate }
6527c478bd9Sstevel@tonic-gate
6537c478bd9Sstevel@tonic-gate
6547c478bd9Sstevel@tonic-gate /*
6557c478bd9Sstevel@tonic-gate * usb_ugen_reconnect_ev_cb:
6567c478bd9Sstevel@tonic-gate */
6577c478bd9Sstevel@tonic-gate int
usb_ugen_reconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)6587c478bd9Sstevel@tonic-gate usb_ugen_reconnect_ev_cb(usb_ugen_hdl_t usb_ugen_hdl)
6597c478bd9Sstevel@tonic-gate {
6607c478bd9Sstevel@tonic-gate usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
6610a05e705Slc (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
6627c478bd9Sstevel@tonic-gate ugen_state_t *ugenp = usb_ugen_hdl_impl->hdl_ugenp;
6637c478bd9Sstevel@tonic-gate
6647c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
6657c478bd9Sstevel@tonic-gate "usb_ugen_reconnect_ev_cb:");
6667c478bd9Sstevel@tonic-gate
6677c478bd9Sstevel@tonic-gate ugen_restore_state(ugenp);
6687c478bd9Sstevel@tonic-gate
6697c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
6707c478bd9Sstevel@tonic-gate }
6717c478bd9Sstevel@tonic-gate
6727c478bd9Sstevel@tonic-gate
6737c478bd9Sstevel@tonic-gate /*
6747c478bd9Sstevel@tonic-gate * ugen_restore_state:
6757c478bd9Sstevel@tonic-gate * Check for same device; if a different device is attached, set
6767c478bd9Sstevel@tonic-gate * the device status to disconnected.
6777c478bd9Sstevel@tonic-gate * If we were open, then set to UNAVAILABLE until all endpoints have
6787c478bd9Sstevel@tonic-gate * be closed.
6797c478bd9Sstevel@tonic-gate */
6807c478bd9Sstevel@tonic-gate static void
ugen_restore_state(ugen_state_t * ugenp)6817c478bd9Sstevel@tonic-gate ugen_restore_state(ugen_state_t *ugenp)
6827c478bd9Sstevel@tonic-gate {
6837c478bd9Sstevel@tonic-gate dev_info_t *dip = ugenp->ug_dip;
6847c478bd9Sstevel@tonic-gate
6857c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
6867c478bd9Sstevel@tonic-gate "ugen_restore_state");
6877c478bd9Sstevel@tonic-gate
6887c478bd9Sstevel@tonic-gate /* first raise power */
6897c478bd9Sstevel@tonic-gate if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
6907c478bd9Sstevel@tonic-gate ugen_pm_busy_component(ugenp);
6917c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
6927c478bd9Sstevel@tonic-gate }
6937c478bd9Sstevel@tonic-gate
6947c478bd9Sstevel@tonic-gate /* Check if we are talking to the same device */
6957c478bd9Sstevel@tonic-gate if (usb_check_same_device(dip, ugenp->ug_log_hdl,
6967c478bd9Sstevel@tonic-gate USB_LOG_L0, UGEN_PRINT_HOTPLUG, USB_CHK_ALL, NULL) ==
6977c478bd9Sstevel@tonic-gate USB_FAILURE) {
6987c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
6997c478bd9Sstevel@tonic-gate ugenp->ug_dev_state = USB_DEV_DISCONNECTED;
7007c478bd9Sstevel@tonic-gate
7017c478bd9Sstevel@tonic-gate /* wakeup devstat reads and polls */
7027c478bd9Sstevel@tonic-gate ugen_ds_change(ugenp);
7037c478bd9Sstevel@tonic-gate ugen_ds_poll_wakeup(ugenp);
7047c478bd9Sstevel@tonic-gate
7057c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
7067c478bd9Sstevel@tonic-gate
7077c478bd9Sstevel@tonic-gate if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
7087c478bd9Sstevel@tonic-gate ugen_pm_idle_component(ugenp);
7097c478bd9Sstevel@tonic-gate }
7107c478bd9Sstevel@tonic-gate
7117c478bd9Sstevel@tonic-gate return;
7127c478bd9Sstevel@tonic-gate }
7137c478bd9Sstevel@tonic-gate
7147c478bd9Sstevel@tonic-gate /*
7157c478bd9Sstevel@tonic-gate * get exclusive access, we don't want to change state in the
7167c478bd9Sstevel@tonic-gate * middle of some other actions
7177c478bd9Sstevel@tonic-gate */
7187c478bd9Sstevel@tonic-gate (void) usb_serialize_access(ugenp->ug_ser_cookie, USB_WAIT, 0);
7197c478bd9Sstevel@tonic-gate
7207c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
7217c478bd9Sstevel@tonic-gate switch (ugenp->ug_dev_state) {
7227c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED:
7237c478bd9Sstevel@tonic-gate ugenp->ug_dev_state = (ugenp->ug_open_count == 0) ?
7247c478bd9Sstevel@tonic-gate USB_DEV_ONLINE : USB_UGEN_DEV_UNAVAILABLE_RECONNECT;
7257c478bd9Sstevel@tonic-gate
7267c478bd9Sstevel@tonic-gate break;
7277c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED:
7287c478bd9Sstevel@tonic-gate ugenp->ug_dev_state = (ugenp->ug_open_count == 0) ?
7297c478bd9Sstevel@tonic-gate USB_DEV_ONLINE : USB_UGEN_DEV_UNAVAILABLE_RESUME;
7307c478bd9Sstevel@tonic-gate
7317c478bd9Sstevel@tonic-gate break;
7327c478bd9Sstevel@tonic-gate }
7337c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_HOTPLUG, ugenp->ug_log_hdl,
7347c478bd9Sstevel@tonic-gate "ugen_restore_state: state=%d, opencount=%d",
7357c478bd9Sstevel@tonic-gate ugenp->ug_dev_state, ugenp->ug_open_count);
7367c478bd9Sstevel@tonic-gate
7377c478bd9Sstevel@tonic-gate /* wakeup devstat reads and polls */
7387c478bd9Sstevel@tonic-gate ugen_ds_change(ugenp);
7397c478bd9Sstevel@tonic-gate ugen_ds_poll_wakeup(ugenp);
7407c478bd9Sstevel@tonic-gate
7417c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
7427c478bd9Sstevel@tonic-gate usb_release_access(ugenp->ug_ser_cookie);
7437c478bd9Sstevel@tonic-gate
7447c478bd9Sstevel@tonic-gate if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
7457c478bd9Sstevel@tonic-gate ugen_pm_idle_component(ugenp);
7467c478bd9Sstevel@tonic-gate }
7477c478bd9Sstevel@tonic-gate }
7487c478bd9Sstevel@tonic-gate
7497c478bd9Sstevel@tonic-gate
7507c478bd9Sstevel@tonic-gate /*
7517c478bd9Sstevel@tonic-gate * usb_ugen_open:
7527c478bd9Sstevel@tonic-gate */
7537c478bd9Sstevel@tonic-gate /* ARGSUSED */
7547c478bd9Sstevel@tonic-gate int
usb_ugen_open(usb_ugen_hdl_t usb_ugen_hdl,dev_t * devp,int flag,int sflag,cred_t * cr)7557c478bd9Sstevel@tonic-gate usb_ugen_open(usb_ugen_hdl_t usb_ugen_hdl, dev_t *devp, int flag, int sflag,
7567c478bd9Sstevel@tonic-gate cred_t *cr)
7577c478bd9Sstevel@tonic-gate {
7587c478bd9Sstevel@tonic-gate usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
7590a05e705Slc (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
7607c478bd9Sstevel@tonic-gate ugen_state_t *ugenp;
7617c478bd9Sstevel@tonic-gate int rval;
7627c478bd9Sstevel@tonic-gate int minor_node_type;
7637c478bd9Sstevel@tonic-gate
7647c478bd9Sstevel@tonic-gate if (usb_ugen_hdl == NULL) {
7657c478bd9Sstevel@tonic-gate
7667c478bd9Sstevel@tonic-gate return (EINVAL);
7677c478bd9Sstevel@tonic-gate }
7687c478bd9Sstevel@tonic-gate
7697c478bd9Sstevel@tonic-gate ugenp = usb_ugen_hdl_impl->hdl_ugenp;
77096603521Sfrits
77196603521Sfrits if (ugen_is_valid_minor_node(ugenp, *devp) != USB_SUCCESS) {
77296603521Sfrits
77396603521Sfrits return (EINVAL);
77496603521Sfrits }
77596603521Sfrits
7767c478bd9Sstevel@tonic-gate minor_node_type = UGEN_MINOR_TYPE(ugenp, *devp);
7777c478bd9Sstevel@tonic-gate
7787c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7797c478bd9Sstevel@tonic-gate "usb_ugen_open: minor=%u", getminor(*devp));
7807c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7817c478bd9Sstevel@tonic-gate "cfgval=%" PRIu64 " cfgidx=%" PRIu64 " if=%" PRIu64
7827c478bd9Sstevel@tonic-gate " alt=%" PRIu64 " epidx=%" PRIu64 " type=0x%" PRIx64,
7837c478bd9Sstevel@tonic-gate UGEN_MINOR_CFGVAL(ugenp, *devp), UGEN_MINOR_CFGIDX(ugenp, *devp),
7847c478bd9Sstevel@tonic-gate UGEN_MINOR_IF(ugenp, *devp), UGEN_MINOR_ALT(ugenp, *devp),
7857c478bd9Sstevel@tonic-gate UGEN_MINOR_EPIDX(ugenp, *devp), UGEN_MINOR_TYPE(ugenp, *devp));
7867c478bd9Sstevel@tonic-gate
7877c478bd9Sstevel@tonic-gate /* first check for legal open flags */
7887c478bd9Sstevel@tonic-gate if ((rval = ugen_check_open_flags(ugenp, *devp, flag)) != 0) {
7897c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7907c478bd9Sstevel@tonic-gate "usb_ugen_open: check failed, rval=%d", rval);
7917c478bd9Sstevel@tonic-gate
7927c478bd9Sstevel@tonic-gate return (rval);
7937c478bd9Sstevel@tonic-gate }
7947c478bd9Sstevel@tonic-gate
7957c478bd9Sstevel@tonic-gate /* exclude other threads including other opens */
7967c478bd9Sstevel@tonic-gate if (usb_serialize_access(ugenp->ug_ser_cookie,
7977c478bd9Sstevel@tonic-gate USB_WAIT_SIG, 0) <= 0) {
7987c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
7997c478bd9Sstevel@tonic-gate "usb_ugen_open: interrupted");
8007c478bd9Sstevel@tonic-gate
8017c478bd9Sstevel@tonic-gate return (EINTR);
8027c478bd9Sstevel@tonic-gate }
8037c478bd9Sstevel@tonic-gate
8047c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
8057c478bd9Sstevel@tonic-gate
8067c478bd9Sstevel@tonic-gate /* always allow open of dev stat node */
8077c478bd9Sstevel@tonic-gate if (minor_node_type != UGEN_MINOR_DEV_STAT_NODE) {
8087c478bd9Sstevel@tonic-gate
8097c478bd9Sstevel@tonic-gate /* if we are not online or powered down, fail open */
8107c478bd9Sstevel@tonic-gate switch (ugenp->ug_dev_state) {
8117c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE:
8127c478bd9Sstevel@tonic-gate
8137c478bd9Sstevel@tonic-gate break;
8147c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED:
8157c478bd9Sstevel@tonic-gate rval = ENODEV;
8167c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
8177c478bd9Sstevel@tonic-gate
8187c478bd9Sstevel@tonic-gate goto done;
8197c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED:
8207c478bd9Sstevel@tonic-gate case USB_UGEN_DEV_UNAVAILABLE_RESUME:
8217c478bd9Sstevel@tonic-gate case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
8227c478bd9Sstevel@tonic-gate default:
8237c478bd9Sstevel@tonic-gate rval = EBADF;
8247c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
8257c478bd9Sstevel@tonic-gate
8267c478bd9Sstevel@tonic-gate goto done;
8277c478bd9Sstevel@tonic-gate }
8287c478bd9Sstevel@tonic-gate }
8297c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
8307c478bd9Sstevel@tonic-gate
8317c478bd9Sstevel@tonic-gate /* open node depending on type */
8327c478bd9Sstevel@tonic-gate switch (minor_node_type) {
8337c478bd9Sstevel@tonic-gate case UGEN_MINOR_EP_XFER_NODE:
8347c478bd9Sstevel@tonic-gate if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
8357c478bd9Sstevel@tonic-gate ugen_pm_busy_component(ugenp);
8367c478bd9Sstevel@tonic-gate (void) pm_raise_power(ugenp->ug_dip, 0,
8370a05e705Slc USB_DEV_OS_FULL_PWR);
8387c478bd9Sstevel@tonic-gate }
8397c478bd9Sstevel@tonic-gate
8407c478bd9Sstevel@tonic-gate rval = ugen_epx_open(ugenp, *devp, flag);
8417c478bd9Sstevel@tonic-gate if (rval == 0) {
8427c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
8437c478bd9Sstevel@tonic-gate ugenp->ug_open_count++;
8447c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
8457c478bd9Sstevel@tonic-gate } else {
8467c478bd9Sstevel@tonic-gate if (ugenp->ug_hdl->hdl_flags &
8477c478bd9Sstevel@tonic-gate USB_UGEN_ENABLE_PM) {
8487c478bd9Sstevel@tonic-gate ugen_pm_idle_component(ugenp);
8497c478bd9Sstevel@tonic-gate }
8507c478bd9Sstevel@tonic-gate }
8517c478bd9Sstevel@tonic-gate
8527c478bd9Sstevel@tonic-gate break;
8537c478bd9Sstevel@tonic-gate case UGEN_MINOR_EP_STAT_NODE:
8547c478bd9Sstevel@tonic-gate rval = ugen_eps_open(ugenp, *devp, flag);
8557c478bd9Sstevel@tonic-gate if (rval == 0) {
8567c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
8577c478bd9Sstevel@tonic-gate ugenp->ug_open_count++;
8587c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
8597c478bd9Sstevel@tonic-gate }
8607c478bd9Sstevel@tonic-gate
8617c478bd9Sstevel@tonic-gate break;
8627c478bd9Sstevel@tonic-gate case UGEN_MINOR_DEV_STAT_NODE:
8637c478bd9Sstevel@tonic-gate rval = ugen_ds_open(ugenp, *devp, flag);
8647c478bd9Sstevel@tonic-gate
8657c478bd9Sstevel@tonic-gate break;
8667c478bd9Sstevel@tonic-gate default:
8677c478bd9Sstevel@tonic-gate rval = EINVAL;
8687c478bd9Sstevel@tonic-gate
8697c478bd9Sstevel@tonic-gate break;
8707c478bd9Sstevel@tonic-gate }
8717c478bd9Sstevel@tonic-gate done:
8727c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
8737c478bd9Sstevel@tonic-gate
8747c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
8757c478bd9Sstevel@tonic-gate "usb_ugen_open: minor=0x%x rval=%d state=%d cnt=%d",
8767c478bd9Sstevel@tonic-gate getminor(*devp), rval, ugenp->ug_dev_state,
8777c478bd9Sstevel@tonic-gate ugenp->ug_open_count);
8787c478bd9Sstevel@tonic-gate
8797c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
8807c478bd9Sstevel@tonic-gate
8817c478bd9Sstevel@tonic-gate usb_release_access(ugenp->ug_ser_cookie);
8827c478bd9Sstevel@tonic-gate
8837c478bd9Sstevel@tonic-gate return (rval);
8847c478bd9Sstevel@tonic-gate }
8857c478bd9Sstevel@tonic-gate
8867c478bd9Sstevel@tonic-gate
8877c478bd9Sstevel@tonic-gate /*
8887c478bd9Sstevel@tonic-gate * usb_ugen_close()
8897c478bd9Sstevel@tonic-gate */
8907c478bd9Sstevel@tonic-gate /* ARGSUSED */
8917c478bd9Sstevel@tonic-gate int
usb_ugen_close(usb_ugen_hdl_t usb_ugen_hdl,dev_t dev,int flag,int otype,cred_t * cr)8927c478bd9Sstevel@tonic-gate usb_ugen_close(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, int flag, int otype,
8937c478bd9Sstevel@tonic-gate cred_t *cr)
8947c478bd9Sstevel@tonic-gate {
8957c478bd9Sstevel@tonic-gate usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
8960a05e705Slc (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
8977c478bd9Sstevel@tonic-gate ugen_state_t *ugenp;
8987c478bd9Sstevel@tonic-gate int minor_node_type;
8997c478bd9Sstevel@tonic-gate
9007c478bd9Sstevel@tonic-gate if (usb_ugen_hdl == NULL) {
9017c478bd9Sstevel@tonic-gate
9027c478bd9Sstevel@tonic-gate return (EINVAL);
9037c478bd9Sstevel@tonic-gate }
9047c478bd9Sstevel@tonic-gate
9057c478bd9Sstevel@tonic-gate ugenp = usb_ugen_hdl_impl->hdl_ugenp;
90696603521Sfrits if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
90796603521Sfrits
90896603521Sfrits return (EINVAL);
90996603521Sfrits }
91096603521Sfrits
9117c478bd9Sstevel@tonic-gate minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
9127c478bd9Sstevel@tonic-gate
9137c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
9147c478bd9Sstevel@tonic-gate "usb_ugen_close: minor=0x%x", getminor(dev));
9157c478bd9Sstevel@tonic-gate
9167c478bd9Sstevel@tonic-gate /* exclude other threads, including other opens */
9177c478bd9Sstevel@tonic-gate if (usb_serialize_access(ugenp->ug_ser_cookie,
9187c478bd9Sstevel@tonic-gate USB_WAIT_SIG, 0) <= 0) {
9197c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
9207c478bd9Sstevel@tonic-gate "usb_ugen_close: interrupted");
9217c478bd9Sstevel@tonic-gate
9227c478bd9Sstevel@tonic-gate return (EINTR);
9237c478bd9Sstevel@tonic-gate }
9247c478bd9Sstevel@tonic-gate
9257c478bd9Sstevel@tonic-gate /* close node depending on type */
9267c478bd9Sstevel@tonic-gate switch (minor_node_type) {
9277c478bd9Sstevel@tonic-gate case UGEN_MINOR_EP_XFER_NODE:
9287c478bd9Sstevel@tonic-gate ugen_epx_close(ugenp, dev, flag);
9297c478bd9Sstevel@tonic-gate if (ugenp->ug_hdl->hdl_flags & USB_UGEN_ENABLE_PM) {
9307c478bd9Sstevel@tonic-gate ugen_pm_idle_component(ugenp);
9317c478bd9Sstevel@tonic-gate }
9327c478bd9Sstevel@tonic-gate
9337c478bd9Sstevel@tonic-gate break;
9347c478bd9Sstevel@tonic-gate case UGEN_MINOR_EP_STAT_NODE:
9357c478bd9Sstevel@tonic-gate ugen_eps_close(ugenp, dev, flag);
9367c478bd9Sstevel@tonic-gate
9377c478bd9Sstevel@tonic-gate break;
9387c478bd9Sstevel@tonic-gate case UGEN_MINOR_DEV_STAT_NODE:
9397c478bd9Sstevel@tonic-gate ugen_ds_close(ugenp, dev, flag);
9407c478bd9Sstevel@tonic-gate
9417c478bd9Sstevel@tonic-gate break;
9427c478bd9Sstevel@tonic-gate default:
9437c478bd9Sstevel@tonic-gate usb_release_access(ugenp->ug_ser_cookie);
9447c478bd9Sstevel@tonic-gate
9457c478bd9Sstevel@tonic-gate return (EINVAL);
9467c478bd9Sstevel@tonic-gate }
9477c478bd9Sstevel@tonic-gate
9487c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
9497c478bd9Sstevel@tonic-gate if (minor_node_type != UGEN_MINOR_DEV_STAT_NODE) {
9507c478bd9Sstevel@tonic-gate ASSERT(ugenp->ug_open_count > 0);
9517c478bd9Sstevel@tonic-gate if ((--ugenp->ug_open_count == 0) &&
9527c478bd9Sstevel@tonic-gate ((ugenp->ug_dev_state == USB_UGEN_DEV_UNAVAILABLE_RESUME) ||
9537c478bd9Sstevel@tonic-gate (ugenp->ug_dev_state ==
9547c478bd9Sstevel@tonic-gate USB_UGEN_DEV_UNAVAILABLE_RECONNECT))) {
9557c478bd9Sstevel@tonic-gate ugenp->ug_dev_state = USB_DEV_ONLINE;
9567c478bd9Sstevel@tonic-gate
9577c478bd9Sstevel@tonic-gate /* wakeup devstat reads and polls */
9587c478bd9Sstevel@tonic-gate ugen_ds_change(ugenp);
9597c478bd9Sstevel@tonic-gate ugen_ds_poll_wakeup(ugenp);
9607c478bd9Sstevel@tonic-gate }
9617c478bd9Sstevel@tonic-gate }
9627c478bd9Sstevel@tonic-gate
9637c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
9647c478bd9Sstevel@tonic-gate "usb_ugen_close: minor=0x%x state=%d cnt=%d",
9657c478bd9Sstevel@tonic-gate getminor(dev), ugenp->ug_dev_state, ugenp->ug_open_count);
9667c478bd9Sstevel@tonic-gate
9677c478bd9Sstevel@tonic-gate if (ugenp->ug_open_count == 0) {
9687c478bd9Sstevel@tonic-gate ASSERT(ugen_epxs_check_open_nodes(ugenp) == USB_FAILURE);
9697c478bd9Sstevel@tonic-gate }
9707c478bd9Sstevel@tonic-gate
9717c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
9727c478bd9Sstevel@tonic-gate
9737c478bd9Sstevel@tonic-gate usb_release_access(ugenp->ug_ser_cookie);
9747c478bd9Sstevel@tonic-gate
9757c478bd9Sstevel@tonic-gate return (0);
9767c478bd9Sstevel@tonic-gate }
9777c478bd9Sstevel@tonic-gate
9787c478bd9Sstevel@tonic-gate
9797c478bd9Sstevel@tonic-gate /*
9807c478bd9Sstevel@tonic-gate * usb_ugen_read/write()
9817c478bd9Sstevel@tonic-gate */
9827c478bd9Sstevel@tonic-gate /*ARGSUSED*/
9837c478bd9Sstevel@tonic-gate int
usb_ugen_read(usb_ugen_hdl_t usb_ugen_hdl,dev_t dev,struct uio * uiop,cred_t * credp)9847c478bd9Sstevel@tonic-gate usb_ugen_read(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, struct uio *uiop,
9857c478bd9Sstevel@tonic-gate cred_t *credp)
9867c478bd9Sstevel@tonic-gate {
98796603521Sfrits ugen_state_t *ugenp;
98896603521Sfrits usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
9890a05e705Slc (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
99096603521Sfrits
99196603521Sfrits if (usb_ugen_hdl == NULL) {
99296603521Sfrits
99396603521Sfrits return (EINVAL);
99496603521Sfrits }
99596603521Sfrits ugenp = usb_ugen_hdl_impl->hdl_ugenp;
99696603521Sfrits
99796603521Sfrits if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
99896603521Sfrits
99996603521Sfrits return (EINVAL);
100096603521Sfrits }
100196603521Sfrits
10027c478bd9Sstevel@tonic-gate return (physio(ugen_strategy,
10037c478bd9Sstevel@tonic-gate (struct buf *)0, dev, B_READ, ugen_minphys, uiop));
10047c478bd9Sstevel@tonic-gate }
10057c478bd9Sstevel@tonic-gate
10067c478bd9Sstevel@tonic-gate
10077c478bd9Sstevel@tonic-gate /*ARGSUSED*/
10087c478bd9Sstevel@tonic-gate int
usb_ugen_write(usb_ugen_hdl_t usb_ugen_hdl,dev_t dev,struct uio * uiop,cred_t * credp)10097c478bd9Sstevel@tonic-gate usb_ugen_write(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, struct uio *uiop,
10107c478bd9Sstevel@tonic-gate cred_t *credp)
10117c478bd9Sstevel@tonic-gate {
101296603521Sfrits ugen_state_t *ugenp;
101396603521Sfrits usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
10140a05e705Slc (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
101596603521Sfrits
101696603521Sfrits if (usb_ugen_hdl == NULL) {
101796603521Sfrits
101896603521Sfrits return (EINVAL);
101996603521Sfrits }
102096603521Sfrits ugenp = usb_ugen_hdl_impl->hdl_ugenp;
102196603521Sfrits
102296603521Sfrits if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
102396603521Sfrits
102496603521Sfrits return (EINVAL);
102596603521Sfrits }
102696603521Sfrits
10277c478bd9Sstevel@tonic-gate return (physio(ugen_strategy,
10287c478bd9Sstevel@tonic-gate (struct buf *)0, dev, B_WRITE, ugen_minphys, uiop));
10297c478bd9Sstevel@tonic-gate }
10307c478bd9Sstevel@tonic-gate
10317c478bd9Sstevel@tonic-gate
10327c478bd9Sstevel@tonic-gate /*
10337c478bd9Sstevel@tonic-gate * usb_ugen_poll
10347c478bd9Sstevel@tonic-gate */
10357c478bd9Sstevel@tonic-gate int
usb_ugen_poll(usb_ugen_hdl_t usb_ugen_hdl,dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)10367c478bd9Sstevel@tonic-gate usb_ugen_poll(usb_ugen_hdl_t usb_ugen_hdl, dev_t dev, short events,
10377c478bd9Sstevel@tonic-gate int anyyet, short *reventsp, struct pollhead **phpp)
10387c478bd9Sstevel@tonic-gate {
10397c478bd9Sstevel@tonic-gate usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
10400a05e705Slc (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
10417c478bd9Sstevel@tonic-gate ugen_state_t *ugenp;
10427c478bd9Sstevel@tonic-gate int minor_node_type;
10437c478bd9Sstevel@tonic-gate uint_t ep_index;
10447c478bd9Sstevel@tonic-gate ugen_ep_t *epp;
10457c478bd9Sstevel@tonic-gate
10467c478bd9Sstevel@tonic-gate if (usb_ugen_hdl == NULL) {
10477c478bd9Sstevel@tonic-gate
10487c478bd9Sstevel@tonic-gate return (EINVAL);
10497c478bd9Sstevel@tonic-gate }
10507c478bd9Sstevel@tonic-gate
10517c478bd9Sstevel@tonic-gate ugenp = usb_ugen_hdl_impl->hdl_ugenp;
105296603521Sfrits if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
105396603521Sfrits
105496603521Sfrits return (EINVAL);
105596603521Sfrits }
10567c478bd9Sstevel@tonic-gate
10577c478bd9Sstevel@tonic-gate minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
10587c478bd9Sstevel@tonic-gate ep_index = UGEN_MINOR_EPIDX(ugenp, dev);
10597c478bd9Sstevel@tonic-gate epp = &ugenp->ug_ep[ep_index];
10607c478bd9Sstevel@tonic-gate
10617c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
10627c478bd9Sstevel@tonic-gate
10637c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_POLL, ugenp->ug_log_hdl,
10647c478bd9Sstevel@tonic-gate "usb_ugen_poll: "
10657c478bd9Sstevel@tonic-gate "dev=0x%lx events=0x%x anyyet=0x%x rev=0x%p type=%d "
10667c478bd9Sstevel@tonic-gate "devstat=0x%x devstate=0x%x",
10677c478bd9Sstevel@tonic-gate dev, events, anyyet, (void *)reventsp, minor_node_type,
10687c478bd9Sstevel@tonic-gate ugenp->ug_ds.dev_stat, ugenp->ug_ds.dev_state);
10697c478bd9Sstevel@tonic-gate
10707c478bd9Sstevel@tonic-gate *reventsp = 0;
10717c478bd9Sstevel@tonic-gate
10727c478bd9Sstevel@tonic-gate if (ugenp->ug_dev_state == USB_DEV_ONLINE) {
10737c478bd9Sstevel@tonic-gate switch (minor_node_type) {
10747c478bd9Sstevel@tonic-gate case UGEN_MINOR_EP_XFER_NODE:
10757c478bd9Sstevel@tonic-gate /* if interrupt IN ep and there is data, set POLLIN */
10767c478bd9Sstevel@tonic-gate if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_INTR) &&
10777c478bd9Sstevel@tonic-gate (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN)) {
10787c478bd9Sstevel@tonic-gate
10797c478bd9Sstevel@tonic-gate /*
10807c478bd9Sstevel@tonic-gate * if we are not polling, force another
10817c478bd9Sstevel@tonic-gate * read to kick off polling
10827c478bd9Sstevel@tonic-gate */
10837c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
10847c478bd9Sstevel@tonic-gate if ((epp->ep_data) ||
10857c478bd9Sstevel@tonic-gate ((epp->ep_state &
10867c478bd9Sstevel@tonic-gate UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0)) {
10877c478bd9Sstevel@tonic-gate *reventsp |= POLLIN;
1088a5eb7107SBryan Cantrill }
1089a5eb7107SBryan Cantrill
1090a5eb7107SBryan Cantrill if ((!*reventsp && !anyyet) ||
1091a5eb7107SBryan Cantrill (events & POLLET)) {
10927c478bd9Sstevel@tonic-gate *phpp = &epp->ep_pollhead;
10937c478bd9Sstevel@tonic-gate epp->ep_state |=
10947c478bd9Sstevel@tonic-gate UGEN_EP_STATE_INTR_IN_POLL_PENDING;
10957c478bd9Sstevel@tonic-gate }
10967c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
10970a05e705Slc
10980a05e705Slc } else if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_ISOCH) &&
10990a05e705Slc (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN)) {
11000a05e705Slc
11010a05e705Slc /*
11020a05e705Slc * if we are not polling, force another
11030a05e705Slc * read to kick off polling
11040a05e705Slc */
11050a05e705Slc mutex_enter(&epp->ep_mutex);
11060a05e705Slc if ((epp->ep_data) ||
11070a05e705Slc ((epp->ep_state &
11080a05e705Slc UGEN_EP_STATE_ISOC_IN_POLLING_ON) == 0)) {
11090a05e705Slc *reventsp |= POLLIN;
1110a5eb7107SBryan Cantrill }
1111a5eb7107SBryan Cantrill
1112a5eb7107SBryan Cantrill if ((!*reventsp && !anyyet) ||
1113a5eb7107SBryan Cantrill (events & POLLET)) {
11140a05e705Slc *phpp = &epp->ep_pollhead;
11150a05e705Slc epp->ep_state |=
11160a05e705Slc UGEN_EP_STATE_ISOC_IN_POLL_PENDING;
11170a05e705Slc }
11180a05e705Slc mutex_exit(&epp->ep_mutex);
11190a05e705Slc
11207c478bd9Sstevel@tonic-gate } else {
11217c478bd9Sstevel@tonic-gate /* no poll on other ep nodes */
11227c478bd9Sstevel@tonic-gate *reventsp |= POLLERR;
11237c478bd9Sstevel@tonic-gate }
11247c478bd9Sstevel@tonic-gate
11257c478bd9Sstevel@tonic-gate break;
11267c478bd9Sstevel@tonic-gate case UGEN_MINOR_DEV_STAT_NODE:
1127a5eb7107SBryan Cantrill if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED)
11287c478bd9Sstevel@tonic-gate *reventsp |= POLLIN;
1129a5eb7107SBryan Cantrill
1130a5eb7107SBryan Cantrill if ((!*reventsp && !anyyet) || (events & POLLET)) {
11317c478bd9Sstevel@tonic-gate *phpp = &ugenp->ug_ds.dev_pollhead;
11327c478bd9Sstevel@tonic-gate ugenp->ug_ds.dev_stat |=
11337c478bd9Sstevel@tonic-gate UGEN_DEV_STATUS_POLL_PENDING;
11347c478bd9Sstevel@tonic-gate }
11357c478bd9Sstevel@tonic-gate
11367c478bd9Sstevel@tonic-gate break;
11377c478bd9Sstevel@tonic-gate case UGEN_MINOR_EP_STAT_NODE:
11387c478bd9Sstevel@tonic-gate default:
11397c478bd9Sstevel@tonic-gate *reventsp |= POLLERR;
11407c478bd9Sstevel@tonic-gate
11417c478bd9Sstevel@tonic-gate break;
11427c478bd9Sstevel@tonic-gate }
11437c478bd9Sstevel@tonic-gate } else {
1144a5eb7107SBryan Cantrill if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED)
11457c478bd9Sstevel@tonic-gate *reventsp |= POLLHUP|POLLIN;
1146a5eb7107SBryan Cantrill
1147a5eb7107SBryan Cantrill if ((!*reventsp && !anyyet) || (events & POLLET)) {
11487c478bd9Sstevel@tonic-gate *phpp = &ugenp->ug_ds.dev_pollhead;
11497c478bd9Sstevel@tonic-gate ugenp->ug_ds.dev_stat |=
11500a05e705Slc UGEN_DEV_STATUS_POLL_PENDING;
11517c478bd9Sstevel@tonic-gate }
11527c478bd9Sstevel@tonic-gate }
11537c478bd9Sstevel@tonic-gate
11547c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
11557c478bd9Sstevel@tonic-gate
11567c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_POLL, ugenp->ug_log_hdl,
11577c478bd9Sstevel@tonic-gate "usb_ugen_poll end: reventsp=0x%x", *reventsp);
11587c478bd9Sstevel@tonic-gate
11597c478bd9Sstevel@tonic-gate return (0);
11607c478bd9Sstevel@tonic-gate }
11617c478bd9Sstevel@tonic-gate
11627c478bd9Sstevel@tonic-gate
11637c478bd9Sstevel@tonic-gate /*
11647c478bd9Sstevel@tonic-gate * ugen_strategy
11657c478bd9Sstevel@tonic-gate */
11667c478bd9Sstevel@tonic-gate static int
ugen_strategy(struct buf * bp)11677c478bd9Sstevel@tonic-gate ugen_strategy(struct buf *bp)
11687c478bd9Sstevel@tonic-gate {
11697c478bd9Sstevel@tonic-gate dev_t dev = bp->b_edev;
11707c478bd9Sstevel@tonic-gate int rval = 0;
11717c478bd9Sstevel@tonic-gate ugen_state_t *ugenp = ugen_devt2state(dev);
11727c478bd9Sstevel@tonic-gate int minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
11737c478bd9Sstevel@tonic-gate
11747c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
1175112116d8Sfb "ugen_strategy: bp=0x%p minor=0x%x", (void *)bp, getminor(dev));
11767c478bd9Sstevel@tonic-gate
117796603521Sfrits if (ugen_is_valid_minor_node(ugenp, dev) != USB_SUCCESS) {
117896603521Sfrits
117996603521Sfrits return (EINVAL);
118096603521Sfrits }
118196603521Sfrits
11827c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
11837c478bd9Sstevel@tonic-gate ugenp->ug_pending_cmds++;
11847c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
11857c478bd9Sstevel@tonic-gate
11867c478bd9Sstevel@tonic-gate bp_mapin(bp);
11877c478bd9Sstevel@tonic-gate
11887c478bd9Sstevel@tonic-gate switch (minor_node_type) {
11897c478bd9Sstevel@tonic-gate case UGEN_MINOR_EP_XFER_NODE:
11907c478bd9Sstevel@tonic-gate rval = ugen_epx_req(ugenp, bp);
11917c478bd9Sstevel@tonic-gate
11927c478bd9Sstevel@tonic-gate break;
11937c478bd9Sstevel@tonic-gate case UGEN_MINOR_EP_STAT_NODE:
11947c478bd9Sstevel@tonic-gate rval = ugen_eps_req(ugenp, bp);
11957c478bd9Sstevel@tonic-gate
11967c478bd9Sstevel@tonic-gate break;
11977c478bd9Sstevel@tonic-gate case UGEN_MINOR_DEV_STAT_NODE:
11987c478bd9Sstevel@tonic-gate rval = ugen_ds_req(ugenp, bp);
11997c478bd9Sstevel@tonic-gate
12007c478bd9Sstevel@tonic-gate break;
12017c478bd9Sstevel@tonic-gate default:
12027c478bd9Sstevel@tonic-gate rval = EINVAL;
12037c478bd9Sstevel@tonic-gate
12047c478bd9Sstevel@tonic-gate break;
12057c478bd9Sstevel@tonic-gate }
12067c478bd9Sstevel@tonic-gate
12077c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
12087c478bd9Sstevel@tonic-gate ugenp->ug_pending_cmds--;
12097c478bd9Sstevel@tonic-gate
12107c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
12117c478bd9Sstevel@tonic-gate "ugen_strategy: "
12127c478bd9Sstevel@tonic-gate "bp=0x%p cnt=%lu resid=%lu err=%d minor=0x%x rval=%d #cmds=%d",
12137c478bd9Sstevel@tonic-gate (void *)bp, bp->b_bcount, bp->b_resid, geterror(bp),
12147c478bd9Sstevel@tonic-gate getminor(dev), rval, ugenp->ug_pending_cmds);
12157c478bd9Sstevel@tonic-gate
12167c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
12177c478bd9Sstevel@tonic-gate
12187c478bd9Sstevel@tonic-gate if (rval) {
12197c478bd9Sstevel@tonic-gate if (geterror(bp) == 0) {
12207c478bd9Sstevel@tonic-gate bioerror(bp, rval);
12217c478bd9Sstevel@tonic-gate }
12227c478bd9Sstevel@tonic-gate }
12237c478bd9Sstevel@tonic-gate
12247c478bd9Sstevel@tonic-gate biodone(bp);
12257c478bd9Sstevel@tonic-gate
12267c478bd9Sstevel@tonic-gate return (0);
12277c478bd9Sstevel@tonic-gate }
12287c478bd9Sstevel@tonic-gate
12297c478bd9Sstevel@tonic-gate
12307c478bd9Sstevel@tonic-gate /*
12317c478bd9Sstevel@tonic-gate * ugen_minphys:
12327c478bd9Sstevel@tonic-gate */
12337c478bd9Sstevel@tonic-gate static void
ugen_minphys(struct buf * bp)12347c478bd9Sstevel@tonic-gate ugen_minphys(struct buf *bp)
12357c478bd9Sstevel@tonic-gate {
12367c478bd9Sstevel@tonic-gate dev_t dev = bp->b_edev;
12377c478bd9Sstevel@tonic-gate ugen_state_t *ugenp = ugen_devt2state(dev);
12387c478bd9Sstevel@tonic-gate int minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
12397c478bd9Sstevel@tonic-gate uint_t ep_index = UGEN_MINOR_EPIDX(ugenp, dev);
12407c478bd9Sstevel@tonic-gate ugen_ep_t *epp = &ugenp->ug_ep[ep_index];
12417c478bd9Sstevel@tonic-gate
12427c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
12437c478bd9Sstevel@tonic-gate "ugen_phys: bp=0x%p dev=0x%lx index=%d type=0x%x",
12447c478bd9Sstevel@tonic-gate (void *)bp, dev, ep_index, minor_node_type);
12457c478bd9Sstevel@tonic-gate
12467c478bd9Sstevel@tonic-gate switch (minor_node_type) {
12477c478bd9Sstevel@tonic-gate case UGEN_MINOR_EP_XFER_NODE:
12487c478bd9Sstevel@tonic-gate switch (UGEN_XFER_TYPE(epp)) {
12497c478bd9Sstevel@tonic-gate case USB_EP_ATTR_BULK:
12507c478bd9Sstevel@tonic-gate if (bp->b_bcount > ugenp->ug_max_bulk_xfer_sz) {
12517c478bd9Sstevel@tonic-gate bp->b_bcount = ugenp->ug_max_bulk_xfer_sz;
12527c478bd9Sstevel@tonic-gate }
12537c478bd9Sstevel@tonic-gate
12547c478bd9Sstevel@tonic-gate break;
12557c478bd9Sstevel@tonic-gate case USB_EP_ATTR_INTR:
12567c478bd9Sstevel@tonic-gate case USB_EP_ATTR_CONTROL:
12570a05e705Slc case USB_EP_ATTR_ISOCH:
12587c478bd9Sstevel@tonic-gate default:
12597c478bd9Sstevel@tonic-gate
12607c478bd9Sstevel@tonic-gate break;
12617c478bd9Sstevel@tonic-gate }
12627c478bd9Sstevel@tonic-gate break;
12637c478bd9Sstevel@tonic-gate case UGEN_MINOR_EP_STAT_NODE:
12647c478bd9Sstevel@tonic-gate case UGEN_MINOR_DEV_STAT_NODE:
12657c478bd9Sstevel@tonic-gate default:
12667c478bd9Sstevel@tonic-gate
12677c478bd9Sstevel@tonic-gate break;
12687c478bd9Sstevel@tonic-gate }
12697c478bd9Sstevel@tonic-gate }
12707c478bd9Sstevel@tonic-gate
12710a05e705Slc /*
12720a05e705Slc * Get bmAttributes and bAddress of the endpoint which is going to
12730a05e705Slc * be opened
12740a05e705Slc */
12750a05e705Slc static int
ugen_get_ep_descr(ugen_state_t * ugenp,dev_t dev,uint8_t * bmAttr,uint8_t * bAddr)12760a05e705Slc ugen_get_ep_descr(ugen_state_t *ugenp, dev_t dev, uint8_t *bmAttr,
12770a05e705Slc uint8_t *bAddr)
12780a05e705Slc {
12790a05e705Slc uint_t alt = UGEN_MINOR_ALT(ugenp, dev);
12800a05e705Slc uint_t ifc = UGEN_MINOR_IF(ugenp, dev);
12810a05e705Slc uint_t cfgidx = UGEN_MINOR_CFGIDX(ugenp, dev);
12820a05e705Slc usb_cfg_data_t *dev_cfg;
12830a05e705Slc usb_if_data_t *if_data;
12840a05e705Slc usb_alt_if_data_t *alt_if_data;
12850a05e705Slc usb_ep_data_t *ep_data;
12860a05e705Slc int ep;
12870a05e705Slc int epidx = UGEN_MINOR_EPIDX(ugenp, dev);
12880a05e705Slc
12890a05e705Slc USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
12900a05e705Slc "cfg=%d, if=%d, alt=%d, ep=0x%x", cfgidx, ifc,
12910a05e705Slc alt, epidx);
12920a05e705Slc
12930a05e705Slc dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
12940a05e705Slc if_data = &dev_cfg->cfg_if[ifc];
12950a05e705Slc alt_if_data = &if_data->if_alt[alt];
12960a05e705Slc for (ep = 0; ep < alt_if_data->altif_n_ep; ep++) {
12970a05e705Slc ep_data = &alt_if_data->altif_ep[ep];
12980a05e705Slc
12990a05e705Slc if (usb_get_ep_index(ep_data->ep_descr.
13000a05e705Slc bEndpointAddress) == epidx) {
13010a05e705Slc
13020a05e705Slc *bmAttr = ep_data->ep_descr.bmAttributes;
13030a05e705Slc *bAddr = ep_data->ep_descr.bEndpointAddress;
13040a05e705Slc
13050a05e705Slc return (USB_SUCCESS);
13060a05e705Slc }
13070a05e705Slc }
13080a05e705Slc
13090a05e705Slc return (USB_FAILURE);
13100a05e705Slc }
13117c478bd9Sstevel@tonic-gate
13127c478bd9Sstevel@tonic-gate /*
13137c478bd9Sstevel@tonic-gate * check whether flag is appropriate for node type
13147c478bd9Sstevel@tonic-gate */
13157c478bd9Sstevel@tonic-gate static int
ugen_check_open_flags(ugen_state_t * ugenp,dev_t dev,int flag)13167c478bd9Sstevel@tonic-gate ugen_check_open_flags(ugen_state_t *ugenp, dev_t dev, int flag)
13177c478bd9Sstevel@tonic-gate {
13187c478bd9Sstevel@tonic-gate ugen_ep_t *epp;
13197c478bd9Sstevel@tonic-gate int minor_node_type = UGEN_MINOR_TYPE(ugenp, dev);
13207c478bd9Sstevel@tonic-gate int rval = 0;
13210a05e705Slc uint8_t bmAttribute;
13220a05e705Slc uint8_t bAddress;
13237c478bd9Sstevel@tonic-gate
13247c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
13257c478bd9Sstevel@tonic-gate "ugen_check_open_flags: "
13267c478bd9Sstevel@tonic-gate "dev=0x%lx, type=0x%x flag=0x%x idx=%" PRIu64,
13277c478bd9Sstevel@tonic-gate dev, minor_node_type, flag, UGEN_MINOR_EPIDX(ugenp, dev));
13287c478bd9Sstevel@tonic-gate
13297c478bd9Sstevel@tonic-gate switch (minor_node_type) {
13307c478bd9Sstevel@tonic-gate case UGEN_MINOR_EP_XFER_NODE:
13317c478bd9Sstevel@tonic-gate epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
13320a05e705Slc
13330a05e705Slc /*
13340a05e705Slc * Endpoints in two altsetting happen to have the same
13350a05e705Slc * bEndpointAddress, but they are different type, e.g,
13360a05e705Slc * one is BULK and the other is ISOC. They use the same
13370a05e705Slc * slot of ug_ep array. It's OK after switch_alt, because
13380a05e705Slc * after alt switch, ep info is updated to the new endpoint.
13390a05e705Slc * But it's not right here to use the other EP's info for
13400a05e705Slc * checking.
13410a05e705Slc */
13420a05e705Slc if (UGEN_MINOR_EPIDX(ugenp, dev) != 0) {
13430a05e705Slc if ((rval = ugen_get_ep_descr(ugenp, dev, &bmAttribute,
13440a05e705Slc &bAddress)) != USB_SUCCESS) {
13450a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER,
13460a05e705Slc ugenp->ug_log_hdl, "ugen_get_descr: fail");
13470a05e705Slc
13480a05e705Slc return (ENODEV);
13490a05e705Slc }
13500a05e705Slc } else {
13510a05e705Slc bmAttribute = ugen_default_ep_descr.bmAttributes;
13520a05e705Slc bAddress = ugen_default_ep_descr.bEndpointAddress;
13530a05e705Slc }
13540a05e705Slc
13550a05e705Slc USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
13560a05e705Slc "ugen_check_open_flags: epp = %p,"
1357112116d8Sfb "epp type = %d, bmAttr =0x%x, bAddr = 0x%02x", (void *)epp,
13580a05e705Slc UGEN_XFER_TYPE(epp), bmAttribute, bAddress);
13590a05e705Slc
13600a05e705Slc switch (bmAttribute & USB_EP_ATTR_MASK) {
13617c478bd9Sstevel@tonic-gate case USB_EP_ATTR_CONTROL:
13627c478bd9Sstevel@tonic-gate /* read and write must be set, ndelay not allowed */
13637c478bd9Sstevel@tonic-gate if (((flag & (FREAD | FWRITE)) != (FREAD | FWRITE)) ||
13647c478bd9Sstevel@tonic-gate (flag & (FNDELAY | FNONBLOCK))) {
13657c478bd9Sstevel@tonic-gate rval = EACCES;
13667c478bd9Sstevel@tonic-gate }
13677c478bd9Sstevel@tonic-gate
13680a05e705Slc break;
13690a05e705Slc case USB_EP_ATTR_ISOCH:
13700a05e705Slc /* read and write must be set */
13710a05e705Slc if ((flag & (FREAD | FWRITE)) != (FREAD | FWRITE)) {
13720a05e705Slc rval = EACCES;
13730a05e705Slc }
13740a05e705Slc
13757c478bd9Sstevel@tonic-gate break;
13767c478bd9Sstevel@tonic-gate case USB_EP_ATTR_BULK:
13777c478bd9Sstevel@tonic-gate /* ndelay not allowed */
13787c478bd9Sstevel@tonic-gate if (flag & (FNDELAY | FNONBLOCK)) {
13797c478bd9Sstevel@tonic-gate rval = EACCES;
13807c478bd9Sstevel@tonic-gate
13817c478bd9Sstevel@tonic-gate break;
13827c478bd9Sstevel@tonic-gate }
13837c478bd9Sstevel@tonic-gate /*FALLTHRU*/
13847c478bd9Sstevel@tonic-gate case USB_EP_ATTR_INTR:
13857c478bd9Sstevel@tonic-gate /* check flag versus direction */
13860a05e705Slc if ((flag & FWRITE) && (bAddress & USB_EP_DIR_IN)) {
13877c478bd9Sstevel@tonic-gate rval = EACCES;
13887c478bd9Sstevel@tonic-gate }
13897c478bd9Sstevel@tonic-gate if ((flag & FREAD) &&
13900a05e705Slc ((bAddress & USB_EP_DIR_IN) == 0)) {
13917c478bd9Sstevel@tonic-gate rval = EACCES;
13927c478bd9Sstevel@tonic-gate }
13937c478bd9Sstevel@tonic-gate
13947c478bd9Sstevel@tonic-gate break;
13957c478bd9Sstevel@tonic-gate default:
13967c478bd9Sstevel@tonic-gate rval = EINVAL;
13977c478bd9Sstevel@tonic-gate
13987c478bd9Sstevel@tonic-gate break;
13997c478bd9Sstevel@tonic-gate }
14007c478bd9Sstevel@tonic-gate break;
14017c478bd9Sstevel@tonic-gate case UGEN_MINOR_DEV_STAT_NODE:
14027c478bd9Sstevel@tonic-gate /* only reads are supported */
14037c478bd9Sstevel@tonic-gate if (flag & FWRITE) {
14047c478bd9Sstevel@tonic-gate rval = EACCES;
14057c478bd9Sstevel@tonic-gate }
14067c478bd9Sstevel@tonic-gate
14077c478bd9Sstevel@tonic-gate break;
14087c478bd9Sstevel@tonic-gate case UGEN_MINOR_EP_STAT_NODE:
14097c478bd9Sstevel@tonic-gate
14107c478bd9Sstevel@tonic-gate break;
14117c478bd9Sstevel@tonic-gate default:
14127c478bd9Sstevel@tonic-gate rval = EINVAL;
14137c478bd9Sstevel@tonic-gate
14147c478bd9Sstevel@tonic-gate break;
14157c478bd9Sstevel@tonic-gate }
14167c478bd9Sstevel@tonic-gate
14177c478bd9Sstevel@tonic-gate return (rval);
14187c478bd9Sstevel@tonic-gate }
14197c478bd9Sstevel@tonic-gate
14207c478bd9Sstevel@tonic-gate
14217c478bd9Sstevel@tonic-gate /*
14227c478bd9Sstevel@tonic-gate * endpoint management
14237c478bd9Sstevel@tonic-gate *
14247c478bd9Sstevel@tonic-gate * create/initialize all endpoint xfer/stat structures
14257c478bd9Sstevel@tonic-gate */
14267c478bd9Sstevel@tonic-gate static int
ugen_epxs_init(ugen_state_t * ugenp)14277c478bd9Sstevel@tonic-gate ugen_epxs_init(ugen_state_t *ugenp)
14287c478bd9Sstevel@tonic-gate {
14297c478bd9Sstevel@tonic-gate usb_cfg_data_t *dev_cfg = ugenp->ug_dev_data->dev_cfg;
14307c478bd9Sstevel@tonic-gate uchar_t cfgidx, cfgval, iface, alt, ep;
14317c478bd9Sstevel@tonic-gate usb_if_data_t *if_data;
14327c478bd9Sstevel@tonic-gate usb_alt_if_data_t *alt_if_data;
14337c478bd9Sstevel@tonic-gate usb_ep_data_t *ep_data;
14347c478bd9Sstevel@tonic-gate
14357c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
14367c478bd9Sstevel@tonic-gate "ugen_epxs_init:");
14377c478bd9Sstevel@tonic-gate
14387c478bd9Sstevel@tonic-gate /* initialize each ep's mutex first */
14397c478bd9Sstevel@tonic-gate for (ep = 0; ep < UGEN_N_ENDPOINTS; ep++) {
14407c478bd9Sstevel@tonic-gate mutex_init(&ugenp->ug_ep[ep].ep_mutex, NULL, MUTEX_DRIVER,
14417c478bd9Sstevel@tonic-gate ugenp->ug_dev_data->dev_iblock_cookie);
14427c478bd9Sstevel@tonic-gate }
14437c478bd9Sstevel@tonic-gate
14447c478bd9Sstevel@tonic-gate /* init default ep as it does not have a descriptor */
14457c478bd9Sstevel@tonic-gate if (ugen_epxs_data_init(ugenp, NULL, 0, 0,
14467c478bd9Sstevel@tonic-gate ugenp->ug_dev_data->dev_curr_if, 0) != USB_SUCCESS) {
14477c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
14487c478bd9Sstevel@tonic-gate "creating default endpoint failed");
14497c478bd9Sstevel@tonic-gate
14507c478bd9Sstevel@tonic-gate return (USB_FAILURE);
14517c478bd9Sstevel@tonic-gate }
14527c478bd9Sstevel@tonic-gate
14537c478bd9Sstevel@tonic-gate /*
14547c478bd9Sstevel@tonic-gate * walk all endpoints of all alternates of all interfaces of
14557c478bd9Sstevel@tonic-gate * all cfs
14567c478bd9Sstevel@tonic-gate */
14577c478bd9Sstevel@tonic-gate for (cfgidx = 0; cfgidx < ugenp->ug_dev_data->dev_n_cfg; cfgidx++) {
14587c478bd9Sstevel@tonic-gate dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
14597c478bd9Sstevel@tonic-gate cfgval = dev_cfg->cfg_descr.bConfigurationValue;
14607c478bd9Sstevel@tonic-gate for (iface = 0; iface < dev_cfg->cfg_n_if; iface++) {
14617c478bd9Sstevel@tonic-gate if_data = &dev_cfg->cfg_if[iface];
14627c478bd9Sstevel@tonic-gate for (alt = 0; alt < if_data->if_n_alt; alt++) {
14637c478bd9Sstevel@tonic-gate alt_if_data = &if_data->if_alt[alt];
14647c478bd9Sstevel@tonic-gate for (ep = 0; ep < alt_if_data->altif_n_ep;
14657c478bd9Sstevel@tonic-gate ep++) {
14667c478bd9Sstevel@tonic-gate ep_data = &alt_if_data->altif_ep[ep];
14677c478bd9Sstevel@tonic-gate if (ugen_epxs_data_init(ugenp, ep_data,
14687c478bd9Sstevel@tonic-gate cfgval, cfgidx, iface, alt) !=
14697c478bd9Sstevel@tonic-gate USB_SUCCESS) {
14707c478bd9Sstevel@tonic-gate
14717c478bd9Sstevel@tonic-gate return (USB_FAILURE);
14727c478bd9Sstevel@tonic-gate }
14737c478bd9Sstevel@tonic-gate }
14747c478bd9Sstevel@tonic-gate }
14757c478bd9Sstevel@tonic-gate }
14767c478bd9Sstevel@tonic-gate }
14777c478bd9Sstevel@tonic-gate
14787c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
14797c478bd9Sstevel@tonic-gate }
14807c478bd9Sstevel@tonic-gate
14817c478bd9Sstevel@tonic-gate
14827c478bd9Sstevel@tonic-gate /*
14837c478bd9Sstevel@tonic-gate * initialize one endpoint structure
14847c478bd9Sstevel@tonic-gate */
14857c478bd9Sstevel@tonic-gate static int
ugen_epxs_data_init(ugen_state_t * ugenp,usb_ep_data_t * ep_data,uchar_t cfgval,uchar_t cfgidx,uchar_t iface,uchar_t alt)14867c478bd9Sstevel@tonic-gate ugen_epxs_data_init(ugen_state_t *ugenp, usb_ep_data_t *ep_data,
1487993e3fafSRobert Mustacchi uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
14887c478bd9Sstevel@tonic-gate {
14897c478bd9Sstevel@tonic-gate int ep_index;
14907c478bd9Sstevel@tonic-gate ugen_ep_t *epp;
14917c478bd9Sstevel@tonic-gate usb_ep_descr_t *ep_descr;
14927c478bd9Sstevel@tonic-gate
14937c478bd9Sstevel@tonic-gate /* is this the default endpoint */
14947c478bd9Sstevel@tonic-gate ep_index = (ep_data == NULL) ? 0 :
14950a05e705Slc usb_get_ep_index(ep_data->ep_descr.bEndpointAddress);
14967c478bd9Sstevel@tonic-gate epp = &ugenp->ug_ep[ep_index];
14977c478bd9Sstevel@tonic-gate
14987c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
14997c478bd9Sstevel@tonic-gate "ugen_epxs_data_init: "
15007c478bd9Sstevel@tonic-gate "cfgval=%d cfgidx=%d iface=%d alt=%d ep_index=%d",
15017c478bd9Sstevel@tonic-gate cfgval, cfgidx, iface, alt, ep_index);
15027c478bd9Sstevel@tonic-gate
15037c478bd9Sstevel@tonic-gate ep_descr = (ep_data == NULL) ? &ugen_default_ep_descr :
15040a05e705Slc &ep_data->ep_descr;
15057c478bd9Sstevel@tonic-gate
15067c478bd9Sstevel@tonic-gate mutex_init(&epp->ep_mutex, NULL, MUTEX_DRIVER,
15070a05e705Slc ugenp->ug_dev_data->dev_iblock_cookie);
15087c478bd9Sstevel@tonic-gate
15097c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
15107c478bd9Sstevel@tonic-gate
15117c478bd9Sstevel@tonic-gate /* initialize if not yet init'ed */
15127c478bd9Sstevel@tonic-gate if (epp->ep_state == UGEN_EP_STATE_NONE) {
15137c478bd9Sstevel@tonic-gate epp->ep_descr = *ep_descr;
15147c478bd9Sstevel@tonic-gate epp->ep_cfgidx = cfgidx;
15157c478bd9Sstevel@tonic-gate epp->ep_if = iface;
15167c478bd9Sstevel@tonic-gate epp->ep_alt = alt;
15177c478bd9Sstevel@tonic-gate epp->ep_state = UGEN_EP_STATE_ACTIVE;
15187c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
15197c478bd9Sstevel@tonic-gate epp->ep_pipe_policy.pp_max_async_reqs = 1;
15207c478bd9Sstevel@tonic-gate
1521993e3fafSRobert Mustacchi if (ep_data == NULL) {
1522993e3fafSRobert Mustacchi bzero(&epp->ep_xdescr, sizeof (usb_ep_xdescr_t));
1523993e3fafSRobert Mustacchi epp->ep_xdescr.uex_version =
1524993e3fafSRobert Mustacchi USB_EP_XDESCR_CURRENT_VERSION;
1525993e3fafSRobert Mustacchi epp->ep_xdescr.uex_ep = *ep_descr;
1526993e3fafSRobert Mustacchi } else {
1527993e3fafSRobert Mustacchi /*
1528993e3fafSRobert Mustacchi * The only way this could fail is we have a bad
1529993e3fafSRobert Mustacchi * version, which shouldn't be possible inside of the
1530993e3fafSRobert Mustacchi * usba module itself.
1531993e3fafSRobert Mustacchi */
1532993e3fafSRobert Mustacchi (void) usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
1533993e3fafSRobert Mustacchi ugenp->ug_dip, ep_data, &epp->ep_xdescr);
1534993e3fafSRobert Mustacchi }
1535993e3fafSRobert Mustacchi
15367c478bd9Sstevel@tonic-gate cv_init(&epp->ep_wait_cv, NULL, CV_DRIVER, NULL);
15377c478bd9Sstevel@tonic-gate epp->ep_ser_cookie = usb_init_serialization(
15380a05e705Slc ugenp->ug_dip, 0);
15397c478bd9Sstevel@tonic-gate }
15407c478bd9Sstevel@tonic-gate
15417c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
15427c478bd9Sstevel@tonic-gate
15437c478bd9Sstevel@tonic-gate /* create minor nodes for all alts */
15447c478bd9Sstevel@tonic-gate
15457c478bd9Sstevel@tonic-gate return (ugen_epxs_minor_nodes_create(ugenp, ep_descr,
15467c478bd9Sstevel@tonic-gate cfgval, cfgidx, iface, alt));
15477c478bd9Sstevel@tonic-gate }
15487c478bd9Sstevel@tonic-gate
15497c478bd9Sstevel@tonic-gate
15507c478bd9Sstevel@tonic-gate /*
15517c478bd9Sstevel@tonic-gate * undo all endpoint initializations
15527c478bd9Sstevel@tonic-gate */
15537c478bd9Sstevel@tonic-gate static void
ugen_epxs_destroy(ugen_state_t * ugenp)15547c478bd9Sstevel@tonic-gate ugen_epxs_destroy(ugen_state_t *ugenp)
15557c478bd9Sstevel@tonic-gate {
15567c478bd9Sstevel@tonic-gate int i;
15577c478bd9Sstevel@tonic-gate
15587c478bd9Sstevel@tonic-gate for (i = 0; i < UGEN_N_ENDPOINTS; i++) {
15597c478bd9Sstevel@tonic-gate ugen_epxs_data_destroy(ugenp, &ugenp->ug_ep[i]);
15607c478bd9Sstevel@tonic-gate }
15617c478bd9Sstevel@tonic-gate }
15627c478bd9Sstevel@tonic-gate
15637c478bd9Sstevel@tonic-gate
15647c478bd9Sstevel@tonic-gate static void
ugen_epxs_data_destroy(ugen_state_t * ugenp,ugen_ep_t * epp)15657c478bd9Sstevel@tonic-gate ugen_epxs_data_destroy(ugen_state_t *ugenp, ugen_ep_t *epp)
15667c478bd9Sstevel@tonic-gate {
15677c478bd9Sstevel@tonic-gate if (epp) {
15687c478bd9Sstevel@tonic-gate ASSERT(epp->ep_ph == NULL);
15697c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
15707c478bd9Sstevel@tonic-gate if (epp->ep_state != UGEN_EP_STATE_NONE) {
15717c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
15727c478bd9Sstevel@tonic-gate "ugen_epxs_destroy: addr=0x%x",
15737c478bd9Sstevel@tonic-gate UGEN_XFER_ADDR(epp));
15747c478bd9Sstevel@tonic-gate cv_destroy(&epp->ep_wait_cv);
15757c478bd9Sstevel@tonic-gate }
15767c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
15777c478bd9Sstevel@tonic-gate
15787c478bd9Sstevel@tonic-gate mutex_destroy(&epp->ep_mutex);
15797c478bd9Sstevel@tonic-gate usb_fini_serialization(epp->ep_ser_cookie);
15807c478bd9Sstevel@tonic-gate }
15817c478bd9Sstevel@tonic-gate }
15827c478bd9Sstevel@tonic-gate
15837c478bd9Sstevel@tonic-gate
15847c478bd9Sstevel@tonic-gate /*
15857c478bd9Sstevel@tonic-gate * create endpoint status and xfer minor nodes
15867c478bd9Sstevel@tonic-gate *
15877c478bd9Sstevel@tonic-gate * The actual minor node needs more than 18 bits. We create a table
15887c478bd9Sstevel@tonic-gate * and store the full minor node in this table and use the
15897c478bd9Sstevel@tonic-gate * index in the table as minor node. This allows 256 minor nodes
15907c478bd9Sstevel@tonic-gate * and 1024 instances
15917c478bd9Sstevel@tonic-gate */
15927c478bd9Sstevel@tonic-gate static int
ugen_epxs_minor_nodes_create(ugen_state_t * ugenp,usb_ep_descr_t * ep_descr,uchar_t cfgval,uchar_t cfgidx,uchar_t iface,uchar_t alt)15937c478bd9Sstevel@tonic-gate ugen_epxs_minor_nodes_create(ugen_state_t *ugenp, usb_ep_descr_t *ep_descr,
15947c478bd9Sstevel@tonic-gate uchar_t cfgval, uchar_t cfgidx, uchar_t iface, uchar_t alt)
15957c478bd9Sstevel@tonic-gate {
15967c478bd9Sstevel@tonic-gate char node_name[32], *type;
15977c478bd9Sstevel@tonic-gate int vid = ugenp->ug_dev_data->dev_descr->idVendor;
15987c478bd9Sstevel@tonic-gate int pid = ugenp->ug_dev_data->dev_descr->idProduct;
15997c478bd9Sstevel@tonic-gate minor_t minor;
16007c478bd9Sstevel@tonic-gate int minor_index;
16017c478bd9Sstevel@tonic-gate ugen_minor_t minor_code, minor_code_base;
16027c478bd9Sstevel@tonic-gate int owns_device = (usb_owns_device(ugenp->ug_dip) ?
16030a05e705Slc UGEN_OWNS_DEVICE : 0);
16047c478bd9Sstevel@tonic-gate int ep_index =
16050a05e705Slc usb_get_ep_index(ep_descr->bEndpointAddress);
16067c478bd9Sstevel@tonic-gate int ep_addr =
16070a05e705Slc ep_descr->bEndpointAddress & USB_EP_NUM_MASK;
16087c478bd9Sstevel@tonic-gate int ep_type =
16090a05e705Slc ep_descr->bmAttributes & USB_EP_ATTR_MASK;
16107c478bd9Sstevel@tonic-gate int ep_dir =
16110a05e705Slc ep_descr->bEndpointAddress & USB_EP_DIR_IN;
16127c478bd9Sstevel@tonic-gate
16137c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
16147c478bd9Sstevel@tonic-gate "ugen_epxs_minor_nodes_create: "
16157c478bd9Sstevel@tonic-gate "cfgval=%d cfgidx=%d if=%d alt=%d ep=0x%x",
16167c478bd9Sstevel@tonic-gate cfgval, cfgidx, iface, alt, ep_addr);
16177c478bd9Sstevel@tonic-gate
16187c478bd9Sstevel@tonic-gate if (ugenp->ug_instance >= UGEN_MINOR_INSTANCE_LIMIT(ugenp)) {
16197c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
16207c478bd9Sstevel@tonic-gate "instance number too high (%d)", ugenp->ug_instance);
16217c478bd9Sstevel@tonic-gate
16227c478bd9Sstevel@tonic-gate return (USB_FAILURE);
16237c478bd9Sstevel@tonic-gate }
16247c478bd9Sstevel@tonic-gate
16257c478bd9Sstevel@tonic-gate /* create stat and xfer minor node */
16267c478bd9Sstevel@tonic-gate minor_code_base =
16270a05e705Slc ((ugen_minor_t)cfgval) << UGEN_MINOR_CFGVAL_SHIFT |
16280a05e705Slc ((ugen_minor_t)cfgidx) << UGEN_MINOR_CFGIDX_SHIFT |
16290a05e705Slc iface << UGEN_MINOR_IF_SHIFT |
16300a05e705Slc alt << UGEN_MINOR_ALT_SHIFT |
16310a05e705Slc ep_index << UGEN_MINOR_EPIDX_SHIFT | owns_device;
16327c478bd9Sstevel@tonic-gate minor_code = minor_code_base | UGEN_MINOR_EP_XFER_NODE;
16337c478bd9Sstevel@tonic-gate
16347c478bd9Sstevel@tonic-gate minor_index = ugen_minor_index_create(ugenp, minor_code);
16357c478bd9Sstevel@tonic-gate if (minor_index < 0) {
16367c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
16377c478bd9Sstevel@tonic-gate "too many minor nodes, "
16387c478bd9Sstevel@tonic-gate "cannot create %d.%d.%d.%x",
16397c478bd9Sstevel@tonic-gate cfgval, iface, alt, ep_addr);
16407c478bd9Sstevel@tonic-gate /* carry on regardless */
16417c478bd9Sstevel@tonic-gate
16427c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
16437c478bd9Sstevel@tonic-gate }
16447c478bd9Sstevel@tonic-gate minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
16450a05e705Slc ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
16467c478bd9Sstevel@tonic-gate
16477c478bd9Sstevel@tonic-gate if (ep_type == USB_EP_ATTR_CONTROL) {
16487c478bd9Sstevel@tonic-gate type = "cntrl";
16497c478bd9Sstevel@tonic-gate } else {
16507c478bd9Sstevel@tonic-gate type = (ep_dir & USB_EP_DIR_IN) ? "in" : "out";
16517c478bd9Sstevel@tonic-gate }
16527c478bd9Sstevel@tonic-gate
16537c478bd9Sstevel@tonic-gate /*
16547c478bd9Sstevel@tonic-gate * xfer ep node name:
16557c478bd9Sstevel@tonic-gate * vid.pid.[in|out|cntrl].[<cfg>.][if<iface>.][<alt>.]<ep addr>
16567c478bd9Sstevel@tonic-gate */
16577c478bd9Sstevel@tonic-gate if ((ep_addr == 0) && owns_device) {
16587c478bd9Sstevel@tonic-gate (void) sprintf(node_name, "%x.%x.%s%d",
16597c478bd9Sstevel@tonic-gate vid, pid, type, ep_addr);
16607c478bd9Sstevel@tonic-gate } else if (cfgidx == 0 && alt == 0) {
16617c478bd9Sstevel@tonic-gate (void) sprintf(node_name, "%x.%x.if%d%s%d",
16627c478bd9Sstevel@tonic-gate vid, pid, iface, type, ep_addr);
16637c478bd9Sstevel@tonic-gate } else if (cfgidx == 0 && alt != 0) {
16647c478bd9Sstevel@tonic-gate (void) sprintf(node_name, "%x.%x.if%d.%d%s%d",
16657c478bd9Sstevel@tonic-gate vid, pid, iface, alt, type, ep_addr);
16667c478bd9Sstevel@tonic-gate } else if (cfgidx != 0 && alt == 0) {
16677c478bd9Sstevel@tonic-gate (void) sprintf(node_name, "%x.%x.cfg%dif%d%s%d",
16687c478bd9Sstevel@tonic-gate vid, pid, cfgval, iface, type, ep_addr);
16697c478bd9Sstevel@tonic-gate } else if (cfgidx != 0 && alt != 0) {
16707c478bd9Sstevel@tonic-gate (void) sprintf(node_name, "%x.%x.cfg%dif%d.%d%s%d",
16717c478bd9Sstevel@tonic-gate vid, pid, cfgval, iface, alt,
16727c478bd9Sstevel@tonic-gate type, ep_addr);
16737c478bd9Sstevel@tonic-gate }
16747c478bd9Sstevel@tonic-gate
16757c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
16767c478bd9Sstevel@tonic-gate "minor=0x%x index=%d code=0x%" PRIx64 " name=%s",
16777c478bd9Sstevel@tonic-gate minor, minor_index, minor_code, node_name);
16787c478bd9Sstevel@tonic-gate
16797c478bd9Sstevel@tonic-gate ASSERT(minor < L_MAXMIN);
16807c478bd9Sstevel@tonic-gate
16817c478bd9Sstevel@tonic-gate if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
16827c478bd9Sstevel@tonic-gate S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
16837c478bd9Sstevel@tonic-gate
16847c478bd9Sstevel@tonic-gate return (USB_FAILURE);
16857c478bd9Sstevel@tonic-gate }
16867c478bd9Sstevel@tonic-gate
16877c478bd9Sstevel@tonic-gate ugen_store_devt(ugenp, minor);
16887c478bd9Sstevel@tonic-gate
16897c478bd9Sstevel@tonic-gate minor_code = minor_code_base | UGEN_MINOR_EP_STAT_NODE;
16907c478bd9Sstevel@tonic-gate minor_index = ugen_minor_index_create(ugenp, minor_code);
16917c478bd9Sstevel@tonic-gate if (minor_index < 0) {
16927c478bd9Sstevel@tonic-gate USB_DPRINTF_L1(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
16937c478bd9Sstevel@tonic-gate "too many minor nodes, "
16947c478bd9Sstevel@tonic-gate "cannot create %d.%d.%d.%x stat",
16957c478bd9Sstevel@tonic-gate cfgval, iface, alt,
16967c478bd9Sstevel@tonic-gate ep_descr->bEndpointAddress);
16977c478bd9Sstevel@tonic-gate /* carry on regardless */
16987c478bd9Sstevel@tonic-gate
16997c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
17007c478bd9Sstevel@tonic-gate }
17017c478bd9Sstevel@tonic-gate minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
17020a05e705Slc ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
17037c478bd9Sstevel@tonic-gate
17047c478bd9Sstevel@tonic-gate (void) strcat(node_name, "stat");
17057c478bd9Sstevel@tonic-gate
17067c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
17077c478bd9Sstevel@tonic-gate "minor=0x%x index=%d code=0x%" PRIx64 " name=%s",
17087c478bd9Sstevel@tonic-gate minor, minor_index, minor_code, node_name);
17097c478bd9Sstevel@tonic-gate
17107c478bd9Sstevel@tonic-gate ASSERT(minor < L_MAXMIN);
17117c478bd9Sstevel@tonic-gate
17127c478bd9Sstevel@tonic-gate if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
17137c478bd9Sstevel@tonic-gate S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
17147c478bd9Sstevel@tonic-gate
17157c478bd9Sstevel@tonic-gate return (USB_FAILURE);
17167c478bd9Sstevel@tonic-gate }
17177c478bd9Sstevel@tonic-gate
17187c478bd9Sstevel@tonic-gate ugen_store_devt(ugenp, minor);
17197c478bd9Sstevel@tonic-gate
17207c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
17217c478bd9Sstevel@tonic-gate }
17227c478bd9Sstevel@tonic-gate
17237c478bd9Sstevel@tonic-gate
17247c478bd9Sstevel@tonic-gate /*
17257c478bd9Sstevel@tonic-gate * close all non-default pipes and drain default pipe
17267c478bd9Sstevel@tonic-gate */
17277c478bd9Sstevel@tonic-gate static void
ugen_epx_shutdown(ugen_state_t * ugenp)17287c478bd9Sstevel@tonic-gate ugen_epx_shutdown(ugen_state_t *ugenp)
17297c478bd9Sstevel@tonic-gate {
17307c478bd9Sstevel@tonic-gate int i;
17317c478bd9Sstevel@tonic-gate
17327c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
17337c478bd9Sstevel@tonic-gate "ugen_epx_shutdown:");
17347c478bd9Sstevel@tonic-gate
17357c478bd9Sstevel@tonic-gate for (i = 0; i < UGEN_N_ENDPOINTS; i++) {
17367c478bd9Sstevel@tonic-gate ugen_ep_t *epp = &ugenp->ug_ep[i];
17377c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
17387c478bd9Sstevel@tonic-gate if (epp->ep_state != UGEN_EP_STATE_NONE) {
17397c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
17407c478bd9Sstevel@tonic-gate (void) usb_serialize_access(epp->ep_ser_cookie,
17410a05e705Slc USB_WAIT, 0);
17427c478bd9Sstevel@tonic-gate (void) ugen_epx_close_pipe(ugenp, epp);
17437c478bd9Sstevel@tonic-gate usb_release_access(epp->ep_ser_cookie);
17447c478bd9Sstevel@tonic-gate } else {
17457c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
17467c478bd9Sstevel@tonic-gate }
17477c478bd9Sstevel@tonic-gate }
17487c478bd9Sstevel@tonic-gate }
17497c478bd9Sstevel@tonic-gate
17507c478bd9Sstevel@tonic-gate
17517c478bd9Sstevel@tonic-gate /*
17527c478bd9Sstevel@tonic-gate * find cfg index corresponding to cfg value
17537c478bd9Sstevel@tonic-gate */
17547c478bd9Sstevel@tonic-gate static int
ugen_cfgval2idx(ugen_state_t * ugenp,uint_t cfgval)17557c478bd9Sstevel@tonic-gate ugen_cfgval2idx(ugen_state_t *ugenp, uint_t cfgval)
17567c478bd9Sstevel@tonic-gate {
17577c478bd9Sstevel@tonic-gate usb_cfg_data_t *dev_cfg = ugenp->ug_dev_data->dev_cfg;
17587c478bd9Sstevel@tonic-gate int cfgidx;
17597c478bd9Sstevel@tonic-gate
17607c478bd9Sstevel@tonic-gate for (cfgidx = 0; cfgidx < ugenp->ug_dev_data->dev_n_cfg; cfgidx++) {
17617c478bd9Sstevel@tonic-gate dev_cfg = &ugenp->ug_dev_data->dev_cfg[cfgidx];
17627c478bd9Sstevel@tonic-gate if (cfgval == dev_cfg->cfg_descr.bConfigurationValue) {
17637c478bd9Sstevel@tonic-gate
17647c478bd9Sstevel@tonic-gate return (cfgidx);
17657c478bd9Sstevel@tonic-gate }
17667c478bd9Sstevel@tonic-gate }
17677c478bd9Sstevel@tonic-gate
17687c478bd9Sstevel@tonic-gate ASSERT(cfgidx < ugenp->ug_dev_data->dev_n_cfg);
17697c478bd9Sstevel@tonic-gate
17707c478bd9Sstevel@tonic-gate return (0);
17717c478bd9Sstevel@tonic-gate }
17727c478bd9Sstevel@tonic-gate
17737c478bd9Sstevel@tonic-gate
17747c478bd9Sstevel@tonic-gate /*
17757c478bd9Sstevel@tonic-gate * check if any node is open
17767c478bd9Sstevel@tonic-gate */
17777c478bd9Sstevel@tonic-gate static int
ugen_epxs_check_open_nodes(ugen_state_t * ugenp)17787c478bd9Sstevel@tonic-gate ugen_epxs_check_open_nodes(ugen_state_t *ugenp)
17797c478bd9Sstevel@tonic-gate {
17807c478bd9Sstevel@tonic-gate int i;
17817c478bd9Sstevel@tonic-gate
17827c478bd9Sstevel@tonic-gate for (i = 1; i < UGEN_N_ENDPOINTS; i++) {
17837c478bd9Sstevel@tonic-gate ugen_ep_t *epp = &ugenp->ug_ep[i];
17847c478bd9Sstevel@tonic-gate
17857c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
17867c478bd9Sstevel@tonic-gate
17877c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
17887c478bd9Sstevel@tonic-gate "ugen_epxs_check_open_nodes: epp=%d, ep_state=0x%x",
17897c478bd9Sstevel@tonic-gate i, epp->ep_state);
17907c478bd9Sstevel@tonic-gate
17917c478bd9Sstevel@tonic-gate if (epp->ep_state & UGEN_EP_STATE_XS_OPEN) {
17927c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
17937c478bd9Sstevel@tonic-gate
17947c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
17957c478bd9Sstevel@tonic-gate }
17967c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
17977c478bd9Sstevel@tonic-gate }
17987c478bd9Sstevel@tonic-gate
17997c478bd9Sstevel@tonic-gate return (USB_FAILURE);
18007c478bd9Sstevel@tonic-gate }
18017c478bd9Sstevel@tonic-gate
18027c478bd9Sstevel@tonic-gate
18037c478bd9Sstevel@tonic-gate /*
18047c478bd9Sstevel@tonic-gate * check if we can switch alternate
18057c478bd9Sstevel@tonic-gate */
18067c478bd9Sstevel@tonic-gate static int
ugen_epxs_check_alt_switch(ugen_state_t * ugenp,uchar_t iface,uchar_t cfgidx)18077c478bd9Sstevel@tonic-gate ugen_epxs_check_alt_switch(ugen_state_t *ugenp, uchar_t iface, uchar_t cfgidx)
18087c478bd9Sstevel@tonic-gate {
18097c478bd9Sstevel@tonic-gate int i;
18107c478bd9Sstevel@tonic-gate
18117c478bd9Sstevel@tonic-gate for (i = 1; i < UGEN_N_ENDPOINTS; i++) {
18127c478bd9Sstevel@tonic-gate ugen_ep_t *epp = &ugenp->ug_ep[i];
18137c478bd9Sstevel@tonic-gate
18147c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
18157c478bd9Sstevel@tonic-gate
18167c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
18177c478bd9Sstevel@tonic-gate "ugen_epxs_check_alt_switch: epp=%d, ep_state=0x%x",
18187c478bd9Sstevel@tonic-gate i, epp->ep_state);
18197c478bd9Sstevel@tonic-gate
18207c478bd9Sstevel@tonic-gate /*
18217c478bd9Sstevel@tonic-gate * if the endpoint is open and part of this cfg and interface
18227c478bd9Sstevel@tonic-gate * then we cannot switch alternates
18237c478bd9Sstevel@tonic-gate */
18247c478bd9Sstevel@tonic-gate if ((epp->ep_state & UGEN_EP_STATE_XS_OPEN) &&
18257c478bd9Sstevel@tonic-gate (epp->ep_cfgidx == cfgidx) &&
18267c478bd9Sstevel@tonic-gate (epp->ep_if == iface)) {
18277c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
18287c478bd9Sstevel@tonic-gate
18297c478bd9Sstevel@tonic-gate return (USB_FAILURE);
18307c478bd9Sstevel@tonic-gate }
18317c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
18327c478bd9Sstevel@tonic-gate }
18337c478bd9Sstevel@tonic-gate
18347c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
18357c478bd9Sstevel@tonic-gate }
18367c478bd9Sstevel@tonic-gate
18377c478bd9Sstevel@tonic-gate
18387c478bd9Sstevel@tonic-gate /*
18397c478bd9Sstevel@tonic-gate * implicit switch to new cfg and alt
18407c478bd9Sstevel@tonic-gate * If a crummy device fails usb_get_cfg or usb_get_alt_if, we carry on
18417c478bd9Sstevel@tonic-gate * regardless so at least the device can be opened.
18427c478bd9Sstevel@tonic-gate */
18437c478bd9Sstevel@tonic-gate static int
ugen_epxs_switch_cfg_alt(ugen_state_t * ugenp,ugen_ep_t * epp,dev_t dev)18447c478bd9Sstevel@tonic-gate ugen_epxs_switch_cfg_alt(ugen_state_t *ugenp, ugen_ep_t *epp, dev_t dev)
18457c478bd9Sstevel@tonic-gate {
18467c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS;
18477c478bd9Sstevel@tonic-gate uint_t alt;
18487c478bd9Sstevel@tonic-gate uint_t new_alt = UGEN_MINOR_ALT(ugenp, dev);
18497c478bd9Sstevel@tonic-gate uint_t new_if = UGEN_MINOR_IF(ugenp, dev);
18507c478bd9Sstevel@tonic-gate uint_t cur_if = epp->ep_if;
18517c478bd9Sstevel@tonic-gate uint_t new_cfgidx = UGEN_MINOR_CFGIDX(ugenp, dev);
18527c478bd9Sstevel@tonic-gate uint_t cur_cfgidx;
18537c478bd9Sstevel@tonic-gate uint_t cfgval;
18547c478bd9Sstevel@tonic-gate int switched = 0;
18557c478bd9Sstevel@tonic-gate
18567c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
18577c478bd9Sstevel@tonic-gate "ugen_epxs_switch_cfg_alt: old cfgidx=%d, if=%d alt=%d",
18587c478bd9Sstevel@tonic-gate epp->ep_cfgidx, epp->ep_if, epp->ep_alt);
18597c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
18607c478bd9Sstevel@tonic-gate "new cfgidx=%d, if=%d alt=%d ep_state=0x%x",
18617c478bd9Sstevel@tonic-gate new_cfgidx, new_if, new_alt, epp->ep_state);
18627c478bd9Sstevel@tonic-gate
18637c478bd9Sstevel@tonic-gate /* no need to switch if there is only 1 cfg, 1 iface and no alts */
18647c478bd9Sstevel@tonic-gate if ((new_if == 0) && (new_alt == 0) &&
18657c478bd9Sstevel@tonic-gate (ugenp->ug_dev_data->dev_n_cfg == 1) &&
18667c478bd9Sstevel@tonic-gate (ugenp->ug_dev_data->dev_cfg[0].cfg_n_if == 1) &&
18677c478bd9Sstevel@tonic-gate (ugenp->ug_dev_data->
18687c478bd9Sstevel@tonic-gate dev_cfg[0].cfg_if[new_if].if_n_alt == 1)) {
18697c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
18707c478bd9Sstevel@tonic-gate "no need for switching: n_cfg=%d n_alt=%d",
18717c478bd9Sstevel@tonic-gate ugenp->ug_dev_data->dev_n_cfg,
18727c478bd9Sstevel@tonic-gate ugenp->ug_dev_data->
18730a05e705Slc dev_cfg[0].cfg_if[new_if].if_n_alt);
18747c478bd9Sstevel@tonic-gate
18757c478bd9Sstevel@tonic-gate ASSERT(epp->ep_alt == new_alt);
18767c478bd9Sstevel@tonic-gate ASSERT(epp->ep_cfgidx == new_cfgidx);
18777c478bd9Sstevel@tonic-gate ASSERT(epp->ep_if == new_if);
18787c478bd9Sstevel@tonic-gate
18797c478bd9Sstevel@tonic-gate return (rval);
18807c478bd9Sstevel@tonic-gate }
18817c478bd9Sstevel@tonic-gate
18827c478bd9Sstevel@tonic-gate /* no switch for default endpoint */
18837c478bd9Sstevel@tonic-gate if (epp->ep_descr.bEndpointAddress == 0) {
18847c478bd9Sstevel@tonic-gate
18857c478bd9Sstevel@tonic-gate return (rval);
18867c478bd9Sstevel@tonic-gate }
18877c478bd9Sstevel@tonic-gate
18887c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
18897c478bd9Sstevel@tonic-gate if ((ugenp->ug_dev_data->dev_n_cfg > 1) &&
18907c478bd9Sstevel@tonic-gate usb_get_cfg(ugenp->ug_dip, &cfgval,
18917c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP) == USB_SUCCESS) {
18927c478bd9Sstevel@tonic-gate
18937c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
18947c478bd9Sstevel@tonic-gate
18957c478bd9Sstevel@tonic-gate cur_cfgidx = ugen_cfgval2idx(ugenp, cfgval);
18967c478bd9Sstevel@tonic-gate
18977c478bd9Sstevel@tonic-gate if (new_cfgidx != cur_cfgidx) {
18987c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
18997c478bd9Sstevel@tonic-gate
19007c478bd9Sstevel@tonic-gate /*
19017c478bd9Sstevel@tonic-gate * we can't change config if any node
19027c478bd9Sstevel@tonic-gate * is open
19037c478bd9Sstevel@tonic-gate */
19047c478bd9Sstevel@tonic-gate if (ugen_epxs_check_open_nodes(ugenp) ==
19057c478bd9Sstevel@tonic-gate USB_SUCCESS) {
19067c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
19077c478bd9Sstevel@tonic-gate
19087c478bd9Sstevel@tonic-gate return (USB_BUSY);
19097c478bd9Sstevel@tonic-gate }
19107c478bd9Sstevel@tonic-gate
19117c478bd9Sstevel@tonic-gate /*
19127c478bd9Sstevel@tonic-gate * we are going to do this synchronously to
19137c478bd9Sstevel@tonic-gate * keep it simple.
19147c478bd9Sstevel@tonic-gate * This should never hang forever.
19157c478bd9Sstevel@tonic-gate */
19167c478bd9Sstevel@tonic-gate if ((rval = usb_set_cfg(ugenp->ug_dip,
19177c478bd9Sstevel@tonic-gate new_cfgidx, USB_FLAGS_SLEEP, NULL,
19187c478bd9Sstevel@tonic-gate NULL)) != USB_SUCCESS) {
19197c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_XFER,
19207c478bd9Sstevel@tonic-gate ugenp->ug_log_hdl,
19217c478bd9Sstevel@tonic-gate "implicit set cfg (%" PRId64
19227c478bd9Sstevel@tonic-gate ") failed (%d)",
19237c478bd9Sstevel@tonic-gate UGEN_MINOR_CFGIDX(ugenp, dev), rval);
19247c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
19257c478bd9Sstevel@tonic-gate
19267c478bd9Sstevel@tonic-gate return (rval);
19277c478bd9Sstevel@tonic-gate }
19287c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
1929d29f5a71Szhigang lu - Sun Microsystems - Beijing China epp->ep_if = (uchar_t)new_if;
19307c478bd9Sstevel@tonic-gate switched++;
19317c478bd9Sstevel@tonic-gate }
1932d29f5a71Szhigang lu - Sun Microsystems - Beijing China epp->ep_cfgidx = (uchar_t)new_cfgidx;
19337c478bd9Sstevel@tonic-gate
19347c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
19357c478bd9Sstevel@tonic-gate }
19367c478bd9Sstevel@tonic-gate
19377c478bd9Sstevel@tonic-gate /*
19387c478bd9Sstevel@tonic-gate * implicitly switch to new alternate if
19397c478bd9Sstevel@tonic-gate * - we have not switched configuration (if we
19407c478bd9Sstevel@tonic-gate * we switched config, the alternate must be 0)
19417c478bd9Sstevel@tonic-gate * - n_alts is > 1
19427c478bd9Sstevel@tonic-gate * - if the device supports get_alternate iface
19437c478bd9Sstevel@tonic-gate */
19447c478bd9Sstevel@tonic-gate if ((switched && (new_alt > 0)) ||
19457c478bd9Sstevel@tonic-gate ((ugenp->ug_dev_data->dev_cfg[new_cfgidx].
19467c478bd9Sstevel@tonic-gate cfg_if[new_if].if_n_alt > 1) &&
19477c478bd9Sstevel@tonic-gate (usb_get_alt_if(ugenp->ug_dip, new_if, &alt,
19487c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP) == USB_SUCCESS))) {
19497c478bd9Sstevel@tonic-gate if (switched || (alt != new_alt)) {
19507c478bd9Sstevel@tonic-gate if (ugen_epxs_check_alt_switch(ugenp, cur_if,
19517c478bd9Sstevel@tonic-gate new_cfgidx) != USB_SUCCESS) {
19527c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
19537c478bd9Sstevel@tonic-gate
19547c478bd9Sstevel@tonic-gate return (USB_BUSY);
19557c478bd9Sstevel@tonic-gate }
19567c478bd9Sstevel@tonic-gate if ((rval = usb_set_alt_if(ugenp->ug_dip, new_if,
19577c478bd9Sstevel@tonic-gate new_alt, USB_FLAGS_SLEEP, NULL, NULL)) !=
19587c478bd9Sstevel@tonic-gate USB_SUCCESS) {
19597c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_XFER,
19607c478bd9Sstevel@tonic-gate ugenp->ug_log_hdl,
19617c478bd9Sstevel@tonic-gate "implicit set new alternate "
19627c478bd9Sstevel@tonic-gate "(%d) failed (%d)", new_alt, rval);
19637c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
19647c478bd9Sstevel@tonic-gate
19657c478bd9Sstevel@tonic-gate return (rval);
19667c478bd9Sstevel@tonic-gate }
19677c478bd9Sstevel@tonic-gate }
19687c478bd9Sstevel@tonic-gate }
19697c478bd9Sstevel@tonic-gate
19707c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
1971d29f5a71Szhigang lu - Sun Microsystems - Beijing China epp->ep_alt = (uchar_t)new_alt;
19727c478bd9Sstevel@tonic-gate ugen_update_ep_descr(ugenp, epp);
19737c478bd9Sstevel@tonic-gate
19747c478bd9Sstevel@tonic-gate return (rval);
19757c478bd9Sstevel@tonic-gate }
19767c478bd9Sstevel@tonic-gate
19777c478bd9Sstevel@tonic-gate
19787c478bd9Sstevel@tonic-gate /*
19797c478bd9Sstevel@tonic-gate * update endpoint descriptor in ugen_ep structure after
19807c478bd9Sstevel@tonic-gate * switching configuration or alternate
19817c478bd9Sstevel@tonic-gate */
19827c478bd9Sstevel@tonic-gate static void
ugen_update_ep_descr(ugen_state_t * ugenp,ugen_ep_t * epp)19837c478bd9Sstevel@tonic-gate ugen_update_ep_descr(ugen_state_t *ugenp, ugen_ep_t *epp)
19847c478bd9Sstevel@tonic-gate {
19857c478bd9Sstevel@tonic-gate usb_cfg_data_t *dev_cfg = ugenp->ug_dev_data->dev_cfg;
19867c478bd9Sstevel@tonic-gate usb_if_data_t *if_data;
19877c478bd9Sstevel@tonic-gate usb_alt_if_data_t *alt_if_data;
19887c478bd9Sstevel@tonic-gate usb_ep_data_t *ep_data;
19897c478bd9Sstevel@tonic-gate int ep;
19907c478bd9Sstevel@tonic-gate
19917c478bd9Sstevel@tonic-gate dev_cfg = &ugenp->ug_dev_data->dev_cfg[epp->ep_cfgidx];
19927c478bd9Sstevel@tonic-gate if_data = &dev_cfg->cfg_if[epp->ep_if];
19937c478bd9Sstevel@tonic-gate alt_if_data = &if_data->if_alt[epp->ep_alt];
19947c478bd9Sstevel@tonic-gate for (ep = 0; ep < alt_if_data->altif_n_ep; ep++) {
19957c478bd9Sstevel@tonic-gate ep_data = &alt_if_data->altif_ep[ep];
19967c478bd9Sstevel@tonic-gate if (usb_get_ep_index(ep_data->ep_descr.
19977c478bd9Sstevel@tonic-gate bEndpointAddress) ==
19987c478bd9Sstevel@tonic-gate usb_get_ep_index(epp->ep_descr.
19997c478bd9Sstevel@tonic-gate bEndpointAddress)) {
20007c478bd9Sstevel@tonic-gate epp->ep_descr = ep_data->ep_descr;
2001993e3fafSRobert Mustacchi (void) usb_ep_xdescr_fill(USB_EP_XDESCR_CURRENT_VERSION,
2002993e3fafSRobert Mustacchi ugenp->ug_dip, ep_data, &epp->ep_xdescr);
20037c478bd9Sstevel@tonic-gate
20047c478bd9Sstevel@tonic-gate break;
20057c478bd9Sstevel@tonic-gate }
20067c478bd9Sstevel@tonic-gate }
20077c478bd9Sstevel@tonic-gate }
20087c478bd9Sstevel@tonic-gate
20097c478bd9Sstevel@tonic-gate
20107c478bd9Sstevel@tonic-gate /*
20117c478bd9Sstevel@tonic-gate * Xfer endpoint management
20127c478bd9Sstevel@tonic-gate *
20137c478bd9Sstevel@tonic-gate * open an endpoint for xfers
20147c478bd9Sstevel@tonic-gate *
20157c478bd9Sstevel@tonic-gate * Return values: errno
20167c478bd9Sstevel@tonic-gate */
20177c478bd9Sstevel@tonic-gate static int
ugen_epx_open(ugen_state_t * ugenp,dev_t dev,int flag)20187c478bd9Sstevel@tonic-gate ugen_epx_open(ugen_state_t *ugenp, dev_t dev, int flag)
20197c478bd9Sstevel@tonic-gate {
20207c478bd9Sstevel@tonic-gate ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
20217c478bd9Sstevel@tonic-gate int rval;
20227c478bd9Sstevel@tonic-gate
20237c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
20247c478bd9Sstevel@tonic-gate
20257c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20267c478bd9Sstevel@tonic-gate "ugen_epx_open: minor=0x%x flag=0x%x ep_state=0x%x",
20277c478bd9Sstevel@tonic-gate getminor(dev), flag, epp->ep_state);
20287c478bd9Sstevel@tonic-gate
20297c478bd9Sstevel@tonic-gate ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
20307c478bd9Sstevel@tonic-gate
20317c478bd9Sstevel@tonic-gate /* implicit switch to new cfg & alt */
20327c478bd9Sstevel@tonic-gate if ((epp->ep_state & UGEN_EP_STATE_XFER_OPEN) != 0) {
20337c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
20347c478bd9Sstevel@tonic-gate
20357c478bd9Sstevel@tonic-gate return (EBUSY);
20367c478bd9Sstevel@tonic-gate }
20377c478bd9Sstevel@tonic-gate if ((rval = ugen_epxs_switch_cfg_alt(ugenp, epp, dev)) ==
20387c478bd9Sstevel@tonic-gate USB_SUCCESS) {
20397c478bd9Sstevel@tonic-gate rval = ugen_epx_open_pipe(ugenp, epp, flag);
20407c478bd9Sstevel@tonic-gate }
20417c478bd9Sstevel@tonic-gate
20427c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20437c478bd9Sstevel@tonic-gate "ugen_epx_open: state=0x%x", epp->ep_state);
20447c478bd9Sstevel@tonic-gate
20457c478bd9Sstevel@tonic-gate ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
20467c478bd9Sstevel@tonic-gate epp->ep_done = epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
20477c478bd9Sstevel@tonic-gate
20487c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
20497c478bd9Sstevel@tonic-gate
20507c478bd9Sstevel@tonic-gate return (usb_rval2errno(rval));
20517c478bd9Sstevel@tonic-gate }
20527c478bd9Sstevel@tonic-gate
20537c478bd9Sstevel@tonic-gate
20547c478bd9Sstevel@tonic-gate /*
20557c478bd9Sstevel@tonic-gate * close an endpoint for xfers
20567c478bd9Sstevel@tonic-gate */
20577c478bd9Sstevel@tonic-gate static void
ugen_epx_close(ugen_state_t * ugenp,dev_t dev,int flag)20587c478bd9Sstevel@tonic-gate ugen_epx_close(ugen_state_t *ugenp, dev_t dev, int flag)
20597c478bd9Sstevel@tonic-gate {
20607c478bd9Sstevel@tonic-gate ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
20617c478bd9Sstevel@tonic-gate
20627c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
20637c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20647c478bd9Sstevel@tonic-gate "ugen_epx_close: dev=0x%lx flag=0x%x state=0x%x", dev, flag,
20657c478bd9Sstevel@tonic-gate epp->ep_state);
20667c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
20677c478bd9Sstevel@tonic-gate
20687c478bd9Sstevel@tonic-gate ugen_epx_close_pipe(ugenp, epp);
20697c478bd9Sstevel@tonic-gate
20707c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
20717c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20727c478bd9Sstevel@tonic-gate "ugen_epx_close: state=0x%x", epp->ep_state);
20737c478bd9Sstevel@tonic-gate ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
20747c478bd9Sstevel@tonic-gate ASSERT(epp->ep_bp == NULL);
20757c478bd9Sstevel@tonic-gate ASSERT(epp->ep_done == 0);
20767c478bd9Sstevel@tonic-gate ASSERT(epp->ep_data == NULL);
20777c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
20787c478bd9Sstevel@tonic-gate }
20797c478bd9Sstevel@tonic-gate
20807c478bd9Sstevel@tonic-gate
20817c478bd9Sstevel@tonic-gate /*
20827c478bd9Sstevel@tonic-gate * open pipe for this endpoint
20837c478bd9Sstevel@tonic-gate * If the pipe is an interrupt IN pipe, start polling immediately
20847c478bd9Sstevel@tonic-gate */
20857c478bd9Sstevel@tonic-gate static int
ugen_epx_open_pipe(ugen_state_t * ugenp,ugen_ep_t * epp,int flag)20867c478bd9Sstevel@tonic-gate ugen_epx_open_pipe(ugen_state_t *ugenp, ugen_ep_t *epp, int flag)
20877c478bd9Sstevel@tonic-gate {
20887c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS;
20897c478bd9Sstevel@tonic-gate
20907c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
20917c478bd9Sstevel@tonic-gate "ugen_epx_open_pipe: epp=0x%p flag=%d state=0x%x",
2092112116d8Sfb (void *)epp, flag, epp->ep_state);
20937c478bd9Sstevel@tonic-gate
20947c478bd9Sstevel@tonic-gate epp->ep_state |= UGEN_EP_STATE_XFER_OPEN;
20957c478bd9Sstevel@tonic-gate epp->ep_xfer_oflag = flag;
20967c478bd9Sstevel@tonic-gate
20977c478bd9Sstevel@tonic-gate /* if default pipe, just copy the handle */
20987c478bd9Sstevel@tonic-gate if ((epp->ep_descr.bEndpointAddress & USB_EP_NUM_MASK) == 0) {
20997c478bd9Sstevel@tonic-gate epp->ep_ph = ugenp->ug_dev_data->dev_default_ph;
21007c478bd9Sstevel@tonic-gate } else {
21017c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
21027c478bd9Sstevel@tonic-gate
2103993e3fafSRobert Mustacchi rval = usb_pipe_xopen(ugenp->ug_dip,
2104993e3fafSRobert Mustacchi &epp->ep_xdescr, &epp->ep_pipe_policy,
21057c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP, &epp->ep_ph);
21067c478bd9Sstevel@tonic-gate
21077c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
21087c478bd9Sstevel@tonic-gate
21097c478bd9Sstevel@tonic-gate if (rval == USB_SUCCESS) {
21107c478bd9Sstevel@tonic-gate (void) usb_pipe_set_private(epp->ep_ph,
21110a05e705Slc (usb_opaque_t)epp);
21127c478bd9Sstevel@tonic-gate
21137c478bd9Sstevel@tonic-gate /*
21147c478bd9Sstevel@tonic-gate * if interrupt IN pipe, and one xfer mode
21157c478bd9Sstevel@tonic-gate * has not been set, start polling immediately
21167c478bd9Sstevel@tonic-gate */
21177c478bd9Sstevel@tonic-gate if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_INTR) &&
21187c478bd9Sstevel@tonic-gate (!(epp->ep_one_xfer)) &&
21197c478bd9Sstevel@tonic-gate (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN)) {
21207c478bd9Sstevel@tonic-gate if ((rval = ugen_epx_intr_IN_start_polling(
21217c478bd9Sstevel@tonic-gate ugenp, epp)) != USB_SUCCESS) {
21227c478bd9Sstevel@tonic-gate
21237c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
21247c478bd9Sstevel@tonic-gate usb_pipe_close(ugenp->ug_dip,
21257c478bd9Sstevel@tonic-gate epp->ep_ph, USB_FLAGS_SLEEP,
21267c478bd9Sstevel@tonic-gate NULL, NULL);
21277c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
21287c478bd9Sstevel@tonic-gate
21297c478bd9Sstevel@tonic-gate epp->ep_ph = NULL;
21307c478bd9Sstevel@tonic-gate } else {
21317c478bd9Sstevel@tonic-gate epp->ep_state |=
21327c478bd9Sstevel@tonic-gate UGEN_EP_STATE_INTR_IN_POLLING_ON;
21337c478bd9Sstevel@tonic-gate
21347c478bd9Sstevel@tonic-gate /* allow for about 1 sec of data */
21357c478bd9Sstevel@tonic-gate epp->ep_buf_limit =
21367c478bd9Sstevel@tonic-gate (1000/epp->ep_descr.bInterval) *
21377c478bd9Sstevel@tonic-gate epp->ep_descr.wMaxPacketSize;
21387c478bd9Sstevel@tonic-gate }
21397c478bd9Sstevel@tonic-gate }
21400a05e705Slc
21410a05e705Slc /* set ep_buf_limit for isoc IN pipe */
21420a05e705Slc if ((UGEN_XFER_TYPE(epp) == USB_EP_ATTR_ISOCH) &&
21430a05e705Slc (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN)) {
21440a05e705Slc uint16_t max_size;
21450a05e705Slc uint32_t framecnt;
21460a05e705Slc
21470a05e705Slc max_size =
21480a05e705Slc UGEN_PKT_SIZE(epp->ep_descr.wMaxPacketSize);
21490a05e705Slc
21500a05e705Slc /*
21510a05e705Slc * wMaxPacketSize bits 10..0 specifies maximum
21520a05e705Slc * packet size, which can hold 1024 bytes. If
21530a05e705Slc * bits 12..11 is non zero, max_size will be
21540a05e705Slc * greater than 1024 and the endpoint is a
21550a05e705Slc * high-bandwidth endpoint.
21560a05e705Slc */
21570a05e705Slc if (max_size <= 1024) {
21580a05e705Slc /*
21590a05e705Slc * allowing about 1s data of highspeed and 8s
21600a05e705Slc * data of full speed device
21610a05e705Slc */
21620a05e705Slc framecnt = ugen_isoc_buf_limit;
21630a05e705Slc epp->ep_buf_limit = framecnt *
21640a05e705Slc max_size * 8;
21650a05e705Slc } else {
21660a05e705Slc /*
21670a05e705Slc * allow for about 333 ms data for high-speed
21680a05e705Slc * high-bandwidth data
21690a05e705Slc */
21700a05e705Slc framecnt = ugen_isoc_buf_limit/3;
21710a05e705Slc epp->ep_buf_limit =
21720a05e705Slc framecnt * max_size * 8;
21730a05e705Slc }
21740a05e705Slc
21750a05e705Slc epp->ep_isoc_in_inited = 0;
21760a05e705Slc }
21777c478bd9Sstevel@tonic-gate }
21787c478bd9Sstevel@tonic-gate }
21797c478bd9Sstevel@tonic-gate
21807c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) {
21817c478bd9Sstevel@tonic-gate epp->ep_state &= ~(UGEN_EP_STATE_XFER_OPEN |
21827c478bd9Sstevel@tonic-gate UGEN_EP_STATE_INTR_IN_POLLING_ON);
21837c478bd9Sstevel@tonic-gate }
21847c478bd9Sstevel@tonic-gate
21857c478bd9Sstevel@tonic-gate return (rval);
21867c478bd9Sstevel@tonic-gate }
21877c478bd9Sstevel@tonic-gate
21887c478bd9Sstevel@tonic-gate
21897c478bd9Sstevel@tonic-gate /*
21907c478bd9Sstevel@tonic-gate * close an endpoint pipe
21917c478bd9Sstevel@tonic-gate */
21927c478bd9Sstevel@tonic-gate static void
ugen_epx_close_pipe(ugen_state_t * ugenp,ugen_ep_t * epp)21937c478bd9Sstevel@tonic-gate ugen_epx_close_pipe(ugen_state_t *ugenp, ugen_ep_t *epp)
21947c478bd9Sstevel@tonic-gate {
21957c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
2196112116d8Sfb "ugen_epx_close_pipe: epp=0x%p", (void *)epp);
21977c478bd9Sstevel@tonic-gate
21987c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
21997c478bd9Sstevel@tonic-gate if (epp->ep_state & UGEN_EP_STATE_XFER_OPEN) {
22000a05e705Slc
22010a05e705Slc /* free isoc pipe private data ep_isoc_info.isoc_pkt_descr. */
22020a05e705Slc if (UGEN_XFER_TYPE(epp) == USB_EP_ATTR_ISOCH) {
22030a05e705Slc int len;
22040a05e705Slc int n_pkt;
22050a05e705Slc
22060a05e705Slc if (UGEN_XFER_DIR(epp) == USB_EP_DIR_IN &&
22070a05e705Slc (epp->ep_state &
22080a05e705Slc UGEN_EP_STATE_ISOC_IN_POLLING_ON)) {
22090a05e705Slc mutex_exit(&epp->ep_mutex);
22100a05e705Slc usb_pipe_stop_isoc_polling(epp->ep_ph,
22110a05e705Slc USB_FLAGS_SLEEP);
22120a05e705Slc mutex_enter(&epp->ep_mutex);
22130a05e705Slc }
22140a05e705Slc
22150a05e705Slc if (epp->ep_isoc_info.isoc_pkt_descr) {
22160a05e705Slc n_pkt = epp->ep_isoc_info.
22170a05e705Slc isoc_pkts_count;
22180a05e705Slc len = sizeof (ugen_isoc_pkt_descr_t) * n_pkt;
22190a05e705Slc
22200a05e705Slc kmem_free(epp->ep_isoc_info.isoc_pkt_descr,
22210a05e705Slc len);
22220a05e705Slc
22230a05e705Slc epp->ep_isoc_info.isoc_pkt_descr = NULL;
22240a05e705Slc }
22250a05e705Slc epp->ep_isoc_in_inited = 0;
22260a05e705Slc
22270a05e705Slc }
22280a05e705Slc
22290a05e705Slc
22307c478bd9Sstevel@tonic-gate epp->ep_state &= ~(UGEN_EP_STATE_XFER_OPEN |
22310a05e705Slc UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED |
22320a05e705Slc UGEN_EP_STATE_INTR_IN_POLLING_ON |
22330a05e705Slc UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED |
22340a05e705Slc UGEN_EP_STATE_ISOC_IN_POLLING_ON);
22357c478bd9Sstevel@tonic-gate
22367c478bd9Sstevel@tonic-gate if (epp->ep_ph == ugenp->ug_dev_data->dev_default_ph) {
22377c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
22387c478bd9Sstevel@tonic-gate
22397c478bd9Sstevel@tonic-gate (void) usb_pipe_drain_reqs(ugenp->ug_dip,
22407c478bd9Sstevel@tonic-gate epp->ep_ph, 0, USB_FLAGS_SLEEP,
22417c478bd9Sstevel@tonic-gate NULL, NULL);
22427c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
22437c478bd9Sstevel@tonic-gate } else {
22447c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
22457c478bd9Sstevel@tonic-gate usb_pipe_close(ugenp->ug_dip,
22467c478bd9Sstevel@tonic-gate epp->ep_ph, USB_FLAGS_SLEEP, NULL, NULL);
22477c478bd9Sstevel@tonic-gate
22487c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
22497c478bd9Sstevel@tonic-gate epp->ep_ph = NULL;
22507c478bd9Sstevel@tonic-gate }
22517c478bd9Sstevel@tonic-gate
22527c478bd9Sstevel@tonic-gate freemsg(epp->ep_data);
22537c478bd9Sstevel@tonic-gate epp->ep_ph = NULL;
22547c478bd9Sstevel@tonic-gate epp->ep_data = NULL;
22557c478bd9Sstevel@tonic-gate }
22567c478bd9Sstevel@tonic-gate ASSERT(epp->ep_ph == NULL);
22577c478bd9Sstevel@tonic-gate ASSERT(epp->ep_data == NULL);
22587c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
22597c478bd9Sstevel@tonic-gate }
22607c478bd9Sstevel@tonic-gate
22617c478bd9Sstevel@tonic-gate
22627c478bd9Sstevel@tonic-gate /*
22637c478bd9Sstevel@tonic-gate * start endpoint xfer
22647c478bd9Sstevel@tonic-gate *
22657c478bd9Sstevel@tonic-gate * We first serialize at endpoint level for only one request at the time
22667c478bd9Sstevel@tonic-gate *
22677c478bd9Sstevel@tonic-gate * Return values: errno
22687c478bd9Sstevel@tonic-gate */
22697c478bd9Sstevel@tonic-gate static int
ugen_epx_req(ugen_state_t * ugenp,struct buf * bp)22707c478bd9Sstevel@tonic-gate ugen_epx_req(ugen_state_t *ugenp, struct buf *bp)
22717c478bd9Sstevel@tonic-gate {
22727c478bd9Sstevel@tonic-gate dev_t dev = bp->b_edev;
22737c478bd9Sstevel@tonic-gate ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
22747c478bd9Sstevel@tonic-gate boolean_t wait = B_FALSE;
22757c478bd9Sstevel@tonic-gate int rval = 0;
22767c478bd9Sstevel@tonic-gate
22777c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
22787c478bd9Sstevel@tonic-gate "ugen_epx_req: bp=0x%p dev=0x%lx", (void *)bp, dev);
22797c478bd9Sstevel@tonic-gate
22807c478bd9Sstevel@tonic-gate /* single thread per endpoint, one request at the time */
22817c478bd9Sstevel@tonic-gate if (usb_serialize_access(epp->ep_ser_cookie, USB_WAIT_SIG, 0) <=
22827c478bd9Sstevel@tonic-gate 0) {
22837c478bd9Sstevel@tonic-gate
22847c478bd9Sstevel@tonic-gate return (EINTR);
22857c478bd9Sstevel@tonic-gate }
22867c478bd9Sstevel@tonic-gate
22877c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
22887c478bd9Sstevel@tonic-gate switch (ugenp->ug_dev_state) {
22897c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE:
22907c478bd9Sstevel@tonic-gate
22917c478bd9Sstevel@tonic-gate break;
22927c478bd9Sstevel@tonic-gate case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
22937c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED:
22947c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
22957c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_DISCONNECTED;
22967c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
22977c478bd9Sstevel@tonic-gate rval = ENODEV;
22987c478bd9Sstevel@tonic-gate
22997c478bd9Sstevel@tonic-gate break;
23007c478bd9Sstevel@tonic-gate case USB_UGEN_DEV_UNAVAILABLE_RESUME:
23017c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED:
23027c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
23037c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_SUSPENDED;
23047c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
23057c478bd9Sstevel@tonic-gate rval = EBADF;
23067c478bd9Sstevel@tonic-gate
23077c478bd9Sstevel@tonic-gate break;
23087c478bd9Sstevel@tonic-gate default:
23097c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
23107c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_HW_ERR;
23117c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
23127c478bd9Sstevel@tonic-gate rval = EIO;
23137c478bd9Sstevel@tonic-gate
23147c478bd9Sstevel@tonic-gate break;
23157c478bd9Sstevel@tonic-gate }
23167c478bd9Sstevel@tonic-gate
23177c478bd9Sstevel@tonic-gate #ifndef __lock_lint
23187c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
23197c478bd9Sstevel@tonic-gate "ugen_epx_req: lcmd_status=0x%x", epp->ep_lcmd_status);
23207c478bd9Sstevel@tonic-gate #endif
23217c478bd9Sstevel@tonic-gate
23227c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
23237c478bd9Sstevel@tonic-gate
23247c478bd9Sstevel@tonic-gate if (rval) {
23257c478bd9Sstevel@tonic-gate usb_release_access(epp->ep_ser_cookie);
23267c478bd9Sstevel@tonic-gate
23277c478bd9Sstevel@tonic-gate return (rval);
23287c478bd9Sstevel@tonic-gate }
23297c478bd9Sstevel@tonic-gate
23307c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
23317c478bd9Sstevel@tonic-gate ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
23327c478bd9Sstevel@tonic-gate epp->ep_done = 0;
23337c478bd9Sstevel@tonic-gate epp->ep_bp = bp;
23347c478bd9Sstevel@tonic-gate
23357c478bd9Sstevel@tonic-gate switch (epp->ep_descr.bmAttributes & USB_EP_ATTR_MASK) {
23367c478bd9Sstevel@tonic-gate case USB_EP_ATTR_CONTROL:
23377c478bd9Sstevel@tonic-gate rval = ugen_epx_ctrl_req(ugenp, epp, bp, &wait);
23387c478bd9Sstevel@tonic-gate
23397c478bd9Sstevel@tonic-gate break;
23407c478bd9Sstevel@tonic-gate case USB_EP_ATTR_BULK:
23417c478bd9Sstevel@tonic-gate rval = ugen_epx_bulk_req(ugenp, epp, bp, &wait);
23427c478bd9Sstevel@tonic-gate
23437c478bd9Sstevel@tonic-gate break;
23447c478bd9Sstevel@tonic-gate case USB_EP_ATTR_INTR:
23457c478bd9Sstevel@tonic-gate if (bp->b_flags & B_READ) {
23467c478bd9Sstevel@tonic-gate rval = ugen_epx_intr_IN_req(ugenp, epp, bp, &wait);
23477c478bd9Sstevel@tonic-gate } else {
23487c478bd9Sstevel@tonic-gate rval = ugen_epx_intr_OUT_req(ugenp, epp, bp, &wait);
23497c478bd9Sstevel@tonic-gate }
23507c478bd9Sstevel@tonic-gate
23517c478bd9Sstevel@tonic-gate break;
23527c478bd9Sstevel@tonic-gate case USB_EP_ATTR_ISOCH:
23530a05e705Slc if (bp->b_flags & B_READ) {
23540a05e705Slc rval = ugen_epx_isoc_IN_req(ugenp, epp, bp, &wait);
23550a05e705Slc } else {
23560a05e705Slc rval = ugen_epx_isoc_OUT_req(ugenp, epp, bp, &wait);
23570a05e705Slc }
23580a05e705Slc
23590a05e705Slc break;
23607c478bd9Sstevel@tonic-gate default:
23617c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
23627c478bd9Sstevel@tonic-gate rval = USB_INVALID_REQUEST;
23637c478bd9Sstevel@tonic-gate }
23647c478bd9Sstevel@tonic-gate
23657c478bd9Sstevel@tonic-gate /* if the xfer could not immediately be completed, block here */
23667c478bd9Sstevel@tonic-gate if ((rval == USB_SUCCESS) && wait) {
23677c478bd9Sstevel@tonic-gate while (!epp->ep_done) {
23687c478bd9Sstevel@tonic-gate if ((cv_wait_sig(&epp->ep_wait_cv,
23697c478bd9Sstevel@tonic-gate &epp->ep_mutex) <= 0) && !epp->ep_done) {
23707c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_XFER,
23717c478bd9Sstevel@tonic-gate ugenp->ug_log_hdl,
23727c478bd9Sstevel@tonic-gate "ugen_epx_req: interrupted ep=0x%" PRIx64,
23737c478bd9Sstevel@tonic-gate UGEN_MINOR_EPIDX(ugenp, dev));
23747c478bd9Sstevel@tonic-gate
23757c478bd9Sstevel@tonic-gate /*
23767c478bd9Sstevel@tonic-gate * blow away the request except for dflt pipe
23777c478bd9Sstevel@tonic-gate * (this is prevented in USBA)
23787c478bd9Sstevel@tonic-gate */
23797c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
23807c478bd9Sstevel@tonic-gate usb_pipe_reset(ugenp->ug_dip, epp->ep_ph,
23817c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP, NULL, NULL);
23827c478bd9Sstevel@tonic-gate (void) usb_pipe_drain_reqs(ugenp->ug_dip,
23837c478bd9Sstevel@tonic-gate epp->ep_ph, 0,
23847c478bd9Sstevel@tonic-gate USB_FLAGS_SLEEP, NULL, NULL);
23857c478bd9Sstevel@tonic-gate
23867c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
23877c478bd9Sstevel@tonic-gate
23887c478bd9Sstevel@tonic-gate if (geterror(bp) == 0) {
23897c478bd9Sstevel@tonic-gate bioerror(bp, EINTR);
23907c478bd9Sstevel@tonic-gate }
23917c478bd9Sstevel@tonic-gate epp->ep_lcmd_status =
23927c478bd9Sstevel@tonic-gate USB_LC_STAT_INTERRUPTED;
23937c478bd9Sstevel@tonic-gate
23947c478bd9Sstevel@tonic-gate break;
23957c478bd9Sstevel@tonic-gate }
23967c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
23977c478bd9Sstevel@tonic-gate "ugen_epx_req: wakeup");
23987c478bd9Sstevel@tonic-gate }
23997c478bd9Sstevel@tonic-gate }
24007c478bd9Sstevel@tonic-gate
24017c478bd9Sstevel@tonic-gate /* always set lcmd_status if there was a failure */
24027c478bd9Sstevel@tonic-gate if ((rval != USB_SUCCESS) &&
24037c478bd9Sstevel@tonic-gate (epp->ep_lcmd_status == USB_LC_STAT_NOERROR)) {
24047c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_UNSPECIFIED_ERR;
24057c478bd9Sstevel@tonic-gate }
24067c478bd9Sstevel@tonic-gate
24077c478bd9Sstevel@tonic-gate epp->ep_done = 0;
24087c478bd9Sstevel@tonic-gate epp->ep_bp = NULL;
24097c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
24107c478bd9Sstevel@tonic-gate
24117c478bd9Sstevel@tonic-gate usb_release_access(epp->ep_ser_cookie);
24127c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
24137c478bd9Sstevel@tonic-gate "ugen_epx_req: done");
24147c478bd9Sstevel@tonic-gate
24157c478bd9Sstevel@tonic-gate return (usb_rval2errno(rval));
24167c478bd9Sstevel@tonic-gate }
24177c478bd9Sstevel@tonic-gate
24187c478bd9Sstevel@tonic-gate
24197c478bd9Sstevel@tonic-gate /*
24207c478bd9Sstevel@tonic-gate * handle control xfers
24217c478bd9Sstevel@tonic-gate */
24227c478bd9Sstevel@tonic-gate static int
ugen_epx_ctrl_req(ugen_state_t * ugenp,ugen_ep_t * epp,struct buf * bp,boolean_t * wait)24237c478bd9Sstevel@tonic-gate ugen_epx_ctrl_req(ugen_state_t *ugenp, ugen_ep_t *epp,
24247c478bd9Sstevel@tonic-gate struct buf *bp, boolean_t *wait)
24257c478bd9Sstevel@tonic-gate {
24267c478bd9Sstevel@tonic-gate usb_ctrl_req_t *reqp = NULL;
24277c478bd9Sstevel@tonic-gate uchar_t *setup = ((uchar_t *)(bp->b_un.b_addr));
24287c478bd9Sstevel@tonic-gate int rval;
24297c478bd9Sstevel@tonic-gate ushort_t wLength;
24307c478bd9Sstevel@tonic-gate
24317c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
24327c478bd9Sstevel@tonic-gate "ugen_epx_ctrl_req: epp=0x%p state=0x%x bp=0x%p",
2433112116d8Sfb (void *)epp, epp->ep_state, (void *)bp);
24347c478bd9Sstevel@tonic-gate
24357c478bd9Sstevel@tonic-gate /* is this a read following a write with setup data? */
24367c478bd9Sstevel@tonic-gate if (bp->b_flags & B_READ) {
24377c478bd9Sstevel@tonic-gate if (epp->ep_data) {
2438d29f5a71Szhigang lu - Sun Microsystems - Beijing China int ep_len = MBLKL(epp->ep_data);
24397c478bd9Sstevel@tonic-gate int len = min(bp->b_bcount, ep_len);
24407c478bd9Sstevel@tonic-gate
24417c478bd9Sstevel@tonic-gate bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
24427c478bd9Sstevel@tonic-gate epp->ep_data->b_rptr += len;
2443d29f5a71Szhigang lu - Sun Microsystems - Beijing China if (MBLKL(epp->ep_data) == 0) {
24447c478bd9Sstevel@tonic-gate freemsg(epp->ep_data);
24457c478bd9Sstevel@tonic-gate epp->ep_data = NULL;
24467c478bd9Sstevel@tonic-gate }
24477c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount - len;
24487c478bd9Sstevel@tonic-gate } else {
24497c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount;
24507c478bd9Sstevel@tonic-gate }
24517c478bd9Sstevel@tonic-gate
24527c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
24537c478bd9Sstevel@tonic-gate }
24547c478bd9Sstevel@tonic-gate
24557c478bd9Sstevel@tonic-gate /* discard old data if any */
24567c478bd9Sstevel@tonic-gate if (epp->ep_data) {
24577c478bd9Sstevel@tonic-gate freemsg(epp->ep_data);
24587c478bd9Sstevel@tonic-gate epp->ep_data = NULL;
24597c478bd9Sstevel@tonic-gate }
24607c478bd9Sstevel@tonic-gate
24617c478bd9Sstevel@tonic-gate /* allocate and initialize request */
24627c478bd9Sstevel@tonic-gate wLength = (setup[7] << 8) | setup[6];
24637c478bd9Sstevel@tonic-gate reqp = usb_alloc_ctrl_req(ugenp->ug_dip, wLength, USB_FLAGS_NOSLEEP);
24647c478bd9Sstevel@tonic-gate if (reqp == NULL) {
24657c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
24667c478bd9Sstevel@tonic-gate
24677c478bd9Sstevel@tonic-gate return (USB_NO_RESOURCES);
24687c478bd9Sstevel@tonic-gate }
24697c478bd9Sstevel@tonic-gate
24707c478bd9Sstevel@tonic-gate /* assume an LE data stream */
24717c478bd9Sstevel@tonic-gate reqp->ctrl_bmRequestType = setup[0];
24727c478bd9Sstevel@tonic-gate reqp->ctrl_bRequest = setup[1];
24737c478bd9Sstevel@tonic-gate reqp->ctrl_wValue = (setup[3] << 8) | setup[2];
24747c478bd9Sstevel@tonic-gate reqp->ctrl_wIndex = (setup[5] << 8) | setup[4];
24757c478bd9Sstevel@tonic-gate reqp->ctrl_wLength = wLength;
24767c478bd9Sstevel@tonic-gate reqp->ctrl_timeout = ugen_ctrl_timeout;
24777c478bd9Sstevel@tonic-gate reqp->ctrl_attributes = USB_ATTRS_AUTOCLEARING |
24780a05e705Slc USB_ATTRS_SHORT_XFER_OK;
24797c478bd9Sstevel@tonic-gate reqp->ctrl_cb = ugen_epx_ctrl_req_cb;
24807c478bd9Sstevel@tonic-gate reqp->ctrl_exc_cb = ugen_epx_ctrl_req_cb;
24817c478bd9Sstevel@tonic-gate reqp->ctrl_client_private = (usb_opaque_t)ugenp;
24827c478bd9Sstevel@tonic-gate
24837c478bd9Sstevel@tonic-gate /*
24847c478bd9Sstevel@tonic-gate * is this a legal request? No accesses to device are
24857c478bd9Sstevel@tonic-gate * allowed if we don't own the device
24867c478bd9Sstevel@tonic-gate */
24877c478bd9Sstevel@tonic-gate if (((reqp->ctrl_bmRequestType & USB_DEV_REQ_RCPT_MASK) ==
24887c478bd9Sstevel@tonic-gate USB_DEV_REQ_RCPT_DEV) &&
24897c478bd9Sstevel@tonic-gate (((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
24907c478bd9Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV) &&
24917c478bd9Sstevel@tonic-gate (usb_owns_device(ugenp->ug_dip) == B_FALSE))) {
24927c478bd9Sstevel@tonic-gate rval = USB_INVALID_PERM;
24937c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
24947c478bd9Sstevel@tonic-gate
24957c478bd9Sstevel@tonic-gate goto fail;
24967c478bd9Sstevel@tonic-gate }
24977c478bd9Sstevel@tonic-gate
24987c478bd9Sstevel@tonic-gate /* filter out set_cfg and set_if standard requests */
24997c478bd9Sstevel@tonic-gate if ((reqp->ctrl_bmRequestType & USB_DEV_REQ_TYPE_MASK) ==
25007c478bd9Sstevel@tonic-gate USB_DEV_REQ_TYPE_STANDARD) {
25017c478bd9Sstevel@tonic-gate switch (reqp->ctrl_bRequest) {
25027c478bd9Sstevel@tonic-gate case USB_REQ_SET_CFG:
25037c478bd9Sstevel@tonic-gate case USB_REQ_SET_IF:
25047c478bd9Sstevel@tonic-gate rval = USB_INVALID_REQUEST;
25057c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
25067c478bd9Sstevel@tonic-gate
25077c478bd9Sstevel@tonic-gate goto fail;
25087c478bd9Sstevel@tonic-gate default:
25097c478bd9Sstevel@tonic-gate
25107c478bd9Sstevel@tonic-gate break;
25117c478bd9Sstevel@tonic-gate }
25127c478bd9Sstevel@tonic-gate }
25137c478bd9Sstevel@tonic-gate
25147c478bd9Sstevel@tonic-gate /* is this from host to device? */
25157c478bd9Sstevel@tonic-gate if (((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
25167c478bd9Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV) && reqp->ctrl_wLength) {
25177c478bd9Sstevel@tonic-gate if (((bp->b_bcount - UGEN_SETUP_PKT_SIZE) - wLength) != 0) {
25187c478bd9Sstevel@tonic-gate rval = USB_INVALID_REQUEST;
25197c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
25207c478bd9Sstevel@tonic-gate
25217c478bd9Sstevel@tonic-gate goto fail;
25227c478bd9Sstevel@tonic-gate }
25237c478bd9Sstevel@tonic-gate bcopy(bp->b_un.b_addr + UGEN_SETUP_PKT_SIZE,
25247c478bd9Sstevel@tonic-gate reqp->ctrl_data->b_wptr, wLength);
25257c478bd9Sstevel@tonic-gate reqp->ctrl_data->b_wptr += wLength;
25267c478bd9Sstevel@tonic-gate } else if ((reqp->ctrl_bmRequestType & USB_DEV_REQ_DIR_MASK) ==
25277c478bd9Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST) {
25287c478bd9Sstevel@tonic-gate if (bp->b_bcount != UGEN_SETUP_PKT_SIZE) {
25297c478bd9Sstevel@tonic-gate rval = USB_INVALID_REQUEST;
25307c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
25317c478bd9Sstevel@tonic-gate
25327c478bd9Sstevel@tonic-gate goto fail;
25337c478bd9Sstevel@tonic-gate }
25347c478bd9Sstevel@tonic-gate }
25357c478bd9Sstevel@tonic-gate
25367c478bd9Sstevel@tonic-gate /* submit the request */
25377c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
25387c478bd9Sstevel@tonic-gate rval = usb_pipe_ctrl_xfer(epp->ep_ph, reqp, USB_FLAGS_NOSLEEP);
25397c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
25407c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) {
25417c478bd9Sstevel@tonic-gate epp->ep_lcmd_status =
25427c478bd9Sstevel@tonic-gate ugen_cr2lcstat(reqp->ctrl_completion_reason);
25437c478bd9Sstevel@tonic-gate
25447c478bd9Sstevel@tonic-gate goto fail;
25457c478bd9Sstevel@tonic-gate }
2546*0d2006e4SRobert Mustacchi
25477c478bd9Sstevel@tonic-gate *wait = B_TRUE;
25487c478bd9Sstevel@tonic-gate
25497c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
25507c478bd9Sstevel@tonic-gate fail:
25517c478bd9Sstevel@tonic-gate *wait = B_FALSE;
25527c478bd9Sstevel@tonic-gate
25537c478bd9Sstevel@tonic-gate usb_free_ctrl_req(reqp);
25547c478bd9Sstevel@tonic-gate
25557c478bd9Sstevel@tonic-gate return (rval);
25567c478bd9Sstevel@tonic-gate }
25577c478bd9Sstevel@tonic-gate
25587c478bd9Sstevel@tonic-gate
25597c478bd9Sstevel@tonic-gate /*
25607c478bd9Sstevel@tonic-gate * callback for control requests, normal and exception completion
25617c478bd9Sstevel@tonic-gate */
25627c478bd9Sstevel@tonic-gate static void
ugen_epx_ctrl_req_cb(usb_pipe_handle_t ph,usb_ctrl_req_t * reqp)25637c478bd9Sstevel@tonic-gate ugen_epx_ctrl_req_cb(usb_pipe_handle_t ph, usb_ctrl_req_t *reqp)
25647c478bd9Sstevel@tonic-gate {
25657c478bd9Sstevel@tonic-gate ugen_state_t *ugenp = (ugen_state_t *)reqp->ctrl_client_private;
25667c478bd9Sstevel@tonic-gate ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
25677c478bd9Sstevel@tonic-gate
25687c478bd9Sstevel@tonic-gate if (epp == NULL) {
25697c478bd9Sstevel@tonic-gate epp = &ugenp->ug_ep[0];
25707c478bd9Sstevel@tonic-gate }
25717c478bd9Sstevel@tonic-gate
25727c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
25737c478bd9Sstevel@tonic-gate
25747c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
25757c478bd9Sstevel@tonic-gate "ugen_epx_ctrl_req_cb:\n\t"
25767c478bd9Sstevel@tonic-gate "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x",
2577112116d8Sfb (void *)epp, epp->ep_state, (void *)ph, (void *)reqp,
2578112116d8Sfb reqp->ctrl_completion_reason, reqp->ctrl_cb_flags);
25797c478bd9Sstevel@tonic-gate
25807c478bd9Sstevel@tonic-gate ASSERT((reqp->ctrl_cb_flags & USB_CB_INTR_CONTEXT) == 0);
25817c478bd9Sstevel@tonic-gate
25827c478bd9Sstevel@tonic-gate /* save any data for the next read */
25837c478bd9Sstevel@tonic-gate switch (reqp->ctrl_completion_reason) {
25847c478bd9Sstevel@tonic-gate case USB_CR_OK:
25857c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
25867c478bd9Sstevel@tonic-gate
25877c478bd9Sstevel@tonic-gate break;
25887c478bd9Sstevel@tonic-gate case USB_CR_PIPE_RESET:
25897c478bd9Sstevel@tonic-gate
25907c478bd9Sstevel@tonic-gate break;
25917c478bd9Sstevel@tonic-gate default:
25927c478bd9Sstevel@tonic-gate epp->ep_lcmd_status =
25937c478bd9Sstevel@tonic-gate ugen_cr2lcstat(reqp->ctrl_completion_reason);
25947c478bd9Sstevel@tonic-gate if (epp->ep_bp) {
25957c478bd9Sstevel@tonic-gate bioerror(epp->ep_bp, EIO);
25967c478bd9Sstevel@tonic-gate }
25977c478bd9Sstevel@tonic-gate
25987c478bd9Sstevel@tonic-gate break;
25997c478bd9Sstevel@tonic-gate }
26007c478bd9Sstevel@tonic-gate
26017c478bd9Sstevel@tonic-gate if (reqp->ctrl_data) {
26027c478bd9Sstevel@tonic-gate ASSERT(epp->ep_data == NULL);
26037c478bd9Sstevel@tonic-gate epp->ep_data = reqp->ctrl_data;
26047c478bd9Sstevel@tonic-gate reqp->ctrl_data = NULL;
26057c478bd9Sstevel@tonic-gate }
26067c478bd9Sstevel@tonic-gate epp->ep_done++;
26077c478bd9Sstevel@tonic-gate cv_signal(&epp->ep_wait_cv);
26087c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
26097c478bd9Sstevel@tonic-gate
26107c478bd9Sstevel@tonic-gate usb_free_ctrl_req(reqp);
26117c478bd9Sstevel@tonic-gate
26127c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
26137c478bd9Sstevel@tonic-gate "ugen_epx_ctrl_req_cb: done");
26147c478bd9Sstevel@tonic-gate }
26157c478bd9Sstevel@tonic-gate
26167c478bd9Sstevel@tonic-gate
26177c478bd9Sstevel@tonic-gate /*
26187c478bd9Sstevel@tonic-gate * handle bulk xfers
26197c478bd9Sstevel@tonic-gate */
26207c478bd9Sstevel@tonic-gate static int
ugen_epx_bulk_req(ugen_state_t * ugenp,ugen_ep_t * epp,struct buf * bp,boolean_t * wait)26217c478bd9Sstevel@tonic-gate ugen_epx_bulk_req(ugen_state_t *ugenp, ugen_ep_t *epp,
26227c478bd9Sstevel@tonic-gate struct buf *bp, boolean_t *wait)
26237c478bd9Sstevel@tonic-gate {
26247c478bd9Sstevel@tonic-gate int rval;
26257c478bd9Sstevel@tonic-gate usb_bulk_req_t *reqp = usb_alloc_bulk_req(ugenp->ug_dip,
26260a05e705Slc bp->b_bcount, USB_FLAGS_NOSLEEP);
26277c478bd9Sstevel@tonic-gate
26287c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
26297c478bd9Sstevel@tonic-gate "ugen_epx_bulk_req: epp=0x%p state=0x%x bp=0x%p",
2630112116d8Sfb (void *)epp, epp->ep_state, (void *)bp);
26317c478bd9Sstevel@tonic-gate
26327c478bd9Sstevel@tonic-gate if (reqp == NULL) {
26337c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
26347c478bd9Sstevel@tonic-gate
26357c478bd9Sstevel@tonic-gate return (USB_NO_RESOURCES);
26367c478bd9Sstevel@tonic-gate }
26377c478bd9Sstevel@tonic-gate
26387c478bd9Sstevel@tonic-gate ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
26397c478bd9Sstevel@tonic-gate
26407c478bd9Sstevel@tonic-gate /*
26417c478bd9Sstevel@tonic-gate * the transfer count is limited in minphys with what the HCD can
26427c478bd9Sstevel@tonic-gate * do
26437c478bd9Sstevel@tonic-gate */
26447c478bd9Sstevel@tonic-gate reqp->bulk_len = bp->b_bcount;
26457c478bd9Sstevel@tonic-gate reqp->bulk_timeout = ugen_bulk_timeout;
26467c478bd9Sstevel@tonic-gate reqp->bulk_client_private = (usb_opaque_t)ugenp;
26477c478bd9Sstevel@tonic-gate reqp->bulk_attributes = USB_ATTRS_AUTOCLEARING;
26487c478bd9Sstevel@tonic-gate reqp->bulk_cb = ugen_epx_bulk_req_cb;
26497c478bd9Sstevel@tonic-gate reqp->bulk_exc_cb = ugen_epx_bulk_req_cb;
26507c478bd9Sstevel@tonic-gate
26517c478bd9Sstevel@tonic-gate /* copy data into bp for OUT pipes */
26527c478bd9Sstevel@tonic-gate if ((UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) == 0) {
26537c478bd9Sstevel@tonic-gate bcopy(epp->ep_bp->b_un.b_addr, reqp->bulk_data->b_rptr,
26540a05e705Slc bp->b_bcount);
26557c478bd9Sstevel@tonic-gate reqp->bulk_data->b_wptr += bp->b_bcount;
26567c478bd9Sstevel@tonic-gate } else {
26577c478bd9Sstevel@tonic-gate reqp->bulk_attributes |= USB_ATTRS_SHORT_XFER_OK;
26587c478bd9Sstevel@tonic-gate }
26597c478bd9Sstevel@tonic-gate
26607c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
26617c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_bulk_xfer(epp->ep_ph, reqp,
26627c478bd9Sstevel@tonic-gate USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
26637c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
26647c478bd9Sstevel@tonic-gate epp->ep_lcmd_status =
26657c478bd9Sstevel@tonic-gate ugen_cr2lcstat(reqp->bulk_completion_reason);
26667c478bd9Sstevel@tonic-gate usb_free_bulk_req(reqp);
26677c478bd9Sstevel@tonic-gate bioerror(bp, EIO);
26687c478bd9Sstevel@tonic-gate } else {
26697c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
26707c478bd9Sstevel@tonic-gate }
26717c478bd9Sstevel@tonic-gate *wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
26727c478bd9Sstevel@tonic-gate
26737c478bd9Sstevel@tonic-gate return (rval);
26747c478bd9Sstevel@tonic-gate }
26757c478bd9Sstevel@tonic-gate
26767c478bd9Sstevel@tonic-gate
26777c478bd9Sstevel@tonic-gate /*
26787c478bd9Sstevel@tonic-gate * normal and exception bulk request callback
26797c478bd9Sstevel@tonic-gate */
26807c478bd9Sstevel@tonic-gate static void
ugen_epx_bulk_req_cb(usb_pipe_handle_t ph,usb_bulk_req_t * reqp)26817c478bd9Sstevel@tonic-gate ugen_epx_bulk_req_cb(usb_pipe_handle_t ph, usb_bulk_req_t *reqp)
26827c478bd9Sstevel@tonic-gate {
26837c478bd9Sstevel@tonic-gate ugen_state_t *ugenp = (ugen_state_t *)reqp->bulk_client_private;
26847c478bd9Sstevel@tonic-gate ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
26857c478bd9Sstevel@tonic-gate
26867c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
26877c478bd9Sstevel@tonic-gate "ugen_epx_bulk_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
2688112116d8Sfb (void *)ph, (void *)reqp, reqp->bulk_completion_reason,
2689112116d8Sfb reqp->bulk_cb_flags);
26907c478bd9Sstevel@tonic-gate
26917c478bd9Sstevel@tonic-gate ASSERT((reqp->bulk_cb_flags & USB_CB_INTR_CONTEXT) == 0);
26927c478bd9Sstevel@tonic-gate
26937c478bd9Sstevel@tonic-gate /* epp might be NULL if we are closing the pipe */
26947c478bd9Sstevel@tonic-gate if (epp) {
26957c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
26967c478bd9Sstevel@tonic-gate if (epp->ep_bp && reqp->bulk_data) {
2697d29f5a71Szhigang lu - Sun Microsystems - Beijing China int len = min(MBLKL(reqp->bulk_data),
26980a05e705Slc epp->ep_bp->b_bcount);
26997c478bd9Sstevel@tonic-gate if (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) {
27007c478bd9Sstevel@tonic-gate if (len) {
27017c478bd9Sstevel@tonic-gate bcopy(reqp->bulk_data->b_rptr,
27027c478bd9Sstevel@tonic-gate epp->ep_bp->b_un.b_addr, len);
27037c478bd9Sstevel@tonic-gate epp->ep_bp->b_resid =
27047c478bd9Sstevel@tonic-gate epp->ep_bp->b_bcount - len;
27057c478bd9Sstevel@tonic-gate }
27067c478bd9Sstevel@tonic-gate } else {
27077c478bd9Sstevel@tonic-gate epp->ep_bp->b_resid =
27080a05e705Slc epp->ep_bp->b_bcount - len;
27097c478bd9Sstevel@tonic-gate }
27107c478bd9Sstevel@tonic-gate }
27117c478bd9Sstevel@tonic-gate switch (reqp->bulk_completion_reason) {
27127c478bd9Sstevel@tonic-gate case USB_CR_OK:
27137c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
27147c478bd9Sstevel@tonic-gate
27157c478bd9Sstevel@tonic-gate break;
27167c478bd9Sstevel@tonic-gate case USB_CR_PIPE_RESET:
27177c478bd9Sstevel@tonic-gate
27187c478bd9Sstevel@tonic-gate break;
27197c478bd9Sstevel@tonic-gate default:
27207c478bd9Sstevel@tonic-gate epp->ep_lcmd_status =
27217c478bd9Sstevel@tonic-gate ugen_cr2lcstat(reqp->bulk_completion_reason);
27227c478bd9Sstevel@tonic-gate if (epp->ep_bp) {
27237c478bd9Sstevel@tonic-gate bioerror(epp->ep_bp, EIO);
27247c478bd9Sstevel@tonic-gate }
27257c478bd9Sstevel@tonic-gate }
27267c478bd9Sstevel@tonic-gate epp->ep_done++;
27277c478bd9Sstevel@tonic-gate cv_signal(&epp->ep_wait_cv);
27287c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
27297c478bd9Sstevel@tonic-gate }
27307c478bd9Sstevel@tonic-gate
27317c478bd9Sstevel@tonic-gate usb_free_bulk_req(reqp);
27327c478bd9Sstevel@tonic-gate }
27337c478bd9Sstevel@tonic-gate
27347c478bd9Sstevel@tonic-gate
27357c478bd9Sstevel@tonic-gate /*
27367c478bd9Sstevel@tonic-gate * handle intr IN xfers
27377c478bd9Sstevel@tonic-gate */
27387c478bd9Sstevel@tonic-gate static int
ugen_epx_intr_IN_req(ugen_state_t * ugenp,ugen_ep_t * epp,struct buf * bp,boolean_t * wait)27397c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_req(ugen_state_t *ugenp, ugen_ep_t *epp,
27407c478bd9Sstevel@tonic-gate struct buf *bp, boolean_t *wait)
27417c478bd9Sstevel@tonic-gate {
27427c478bd9Sstevel@tonic-gate int len = 0;
27437c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS;
27447c478bd9Sstevel@tonic-gate
27457c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
27467c478bd9Sstevel@tonic-gate "ugen_epx_intr_IN_req: epp=0x%p state=0x%x bp=0x%p",
2747112116d8Sfb (void *)epp, epp->ep_state, (void *)bp);
27487c478bd9Sstevel@tonic-gate
27497c478bd9Sstevel@tonic-gate *wait = B_FALSE;
27507c478bd9Sstevel@tonic-gate
27517c478bd9Sstevel@tonic-gate /* can we satisfy this read? */
27527c478bd9Sstevel@tonic-gate if (epp->ep_data) {
2753d29f5a71Szhigang lu - Sun Microsystems - Beijing China len = min(MBLKL(epp->ep_data),
27540a05e705Slc bp->b_bcount);
27557c478bd9Sstevel@tonic-gate }
27567c478bd9Sstevel@tonic-gate
27577c478bd9Sstevel@tonic-gate /*
27587c478bd9Sstevel@tonic-gate * if polling not active, restart, and return failure
27597c478bd9Sstevel@tonic-gate * immediately unless one xfer mode has been requested
27607c478bd9Sstevel@tonic-gate * if there is some data, return a short read
27617c478bd9Sstevel@tonic-gate */
27627c478bd9Sstevel@tonic-gate if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0) {
27637c478bd9Sstevel@tonic-gate if (len == 0) {
27647c478bd9Sstevel@tonic-gate if (!epp->ep_one_xfer) {
27657c478bd9Sstevel@tonic-gate rval = USB_FAILURE;
27667c478bd9Sstevel@tonic-gate if (epp->ep_lcmd_status ==
27677c478bd9Sstevel@tonic-gate USB_LC_STAT_NOERROR) {
27687c478bd9Sstevel@tonic-gate epp->ep_lcmd_status =
27690a05e705Slc USB_LC_STAT_INTR_BUF_FULL;
27707c478bd9Sstevel@tonic-gate }
27717c478bd9Sstevel@tonic-gate }
27727c478bd9Sstevel@tonic-gate if (ugen_epx_intr_IN_start_polling(ugenp,
27737c478bd9Sstevel@tonic-gate epp) != USB_SUCCESS) {
27747c478bd9Sstevel@tonic-gate epp->ep_lcmd_status =
27757c478bd9Sstevel@tonic-gate USB_LC_STAT_INTR_POLLING_FAILED;
27767c478bd9Sstevel@tonic-gate }
27777c478bd9Sstevel@tonic-gate if (epp->ep_one_xfer) {
27787c478bd9Sstevel@tonic-gate *wait = B_TRUE;
27797c478bd9Sstevel@tonic-gate }
27807c478bd9Sstevel@tonic-gate goto done;
27817c478bd9Sstevel@tonic-gate } else if (epp->ep_data && (len < bp->b_bcount)) {
27827c478bd9Sstevel@tonic-gate bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
27837c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount - len;
27847c478bd9Sstevel@tonic-gate epp->ep_data->b_rptr += len;
27857c478bd9Sstevel@tonic-gate
27867c478bd9Sstevel@tonic-gate goto done;
27877c478bd9Sstevel@tonic-gate }
27887c478bd9Sstevel@tonic-gate }
27897c478bd9Sstevel@tonic-gate
27907c478bd9Sstevel@tonic-gate /*
27917c478bd9Sstevel@tonic-gate * if there is data or FNDELAY, return available data
27927c478bd9Sstevel@tonic-gate */
27937c478bd9Sstevel@tonic-gate if ((len >= bp->b_bcount) ||
27947c478bd9Sstevel@tonic-gate (epp->ep_xfer_oflag & (FNDELAY | FNONBLOCK))) {
27957c478bd9Sstevel@tonic-gate if (epp->ep_data) {
27967c478bd9Sstevel@tonic-gate bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, len);
27977c478bd9Sstevel@tonic-gate epp->ep_data->b_rptr += len;
27987c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount - len;
27997c478bd9Sstevel@tonic-gate } else {
28007c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount;
28017c478bd9Sstevel@tonic-gate }
28027c478bd9Sstevel@tonic-gate } else {
28037c478bd9Sstevel@tonic-gate /* otherwise just wait for data */
28047c478bd9Sstevel@tonic-gate *wait = B_TRUE;
28057c478bd9Sstevel@tonic-gate }
28067c478bd9Sstevel@tonic-gate
28077c478bd9Sstevel@tonic-gate done:
28087c478bd9Sstevel@tonic-gate if (epp->ep_data && (epp->ep_data->b_rptr == epp->ep_data->b_wptr)) {
28097c478bd9Sstevel@tonic-gate freemsg(epp->ep_data);
28107c478bd9Sstevel@tonic-gate epp->ep_data = NULL;
28117c478bd9Sstevel@tonic-gate }
28127c478bd9Sstevel@tonic-gate
28137c478bd9Sstevel@tonic-gate if (*wait) {
28147c478bd9Sstevel@tonic-gate ASSERT(epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON);
28157c478bd9Sstevel@tonic-gate }
28167c478bd9Sstevel@tonic-gate
28177c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28187c478bd9Sstevel@tonic-gate "ugen_epx_intr_IN_req end: rval=%d bcount=%lu len=%d data=0x%p",
2819112116d8Sfb rval, bp->b_bcount, len, (void *)epp->ep_data);
28207c478bd9Sstevel@tonic-gate
28217c478bd9Sstevel@tonic-gate return (rval);
28227c478bd9Sstevel@tonic-gate }
28237c478bd9Sstevel@tonic-gate
28247c478bd9Sstevel@tonic-gate
28257c478bd9Sstevel@tonic-gate /*
28267c478bd9Sstevel@tonic-gate * Start polling on interrupt endpoint, synchronously
28277c478bd9Sstevel@tonic-gate */
28287c478bd9Sstevel@tonic-gate static int
ugen_epx_intr_IN_start_polling(ugen_state_t * ugenp,ugen_ep_t * epp)28297c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_start_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
28307c478bd9Sstevel@tonic-gate {
28317c478bd9Sstevel@tonic-gate int rval = USB_FAILURE;
28327c478bd9Sstevel@tonic-gate usb_intr_req_t *reqp;
28337c478bd9Sstevel@tonic-gate usb_flags_t uflag;
28347c478bd9Sstevel@tonic-gate
28357c478bd9Sstevel@tonic-gate /*
28367c478bd9Sstevel@tonic-gate * if polling is being stopped, we restart polling in the
28377c478bd9Sstevel@tonic-gate * interrrupt callback again
28387c478bd9Sstevel@tonic-gate */
28397c478bd9Sstevel@tonic-gate if (epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED) {
28407c478bd9Sstevel@tonic-gate
28417c478bd9Sstevel@tonic-gate return (rval);
28427c478bd9Sstevel@tonic-gate }
28437c478bd9Sstevel@tonic-gate if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) == 0) {
28447c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28457c478bd9Sstevel@tonic-gate "ugen_epx_intr_IN_start_polling: epp=0x%p state=0x%x",
2846112116d8Sfb (void *)epp, epp->ep_state);
28477c478bd9Sstevel@tonic-gate
28487c478bd9Sstevel@tonic-gate epp->ep_state |= UGEN_EP_STATE_INTR_IN_POLLING_ON;
28497c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
28507c478bd9Sstevel@tonic-gate
28517c478bd9Sstevel@tonic-gate reqp = usb_alloc_intr_req(ugenp->ug_dip, 0,
28520a05e705Slc USB_FLAGS_SLEEP);
28537c478bd9Sstevel@tonic-gate reqp->intr_client_private = (usb_opaque_t)ugenp;
28547c478bd9Sstevel@tonic-gate
28557c478bd9Sstevel@tonic-gate reqp->intr_attributes = USB_ATTRS_AUTOCLEARING |
28560a05e705Slc USB_ATTRS_SHORT_XFER_OK;
28577c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
28587c478bd9Sstevel@tonic-gate if (epp->ep_one_xfer) {
28597c478bd9Sstevel@tonic-gate reqp->intr_attributes |= USB_ATTRS_ONE_XFER;
28607c478bd9Sstevel@tonic-gate uflag = USB_FLAGS_NOSLEEP;
28617c478bd9Sstevel@tonic-gate } else {
28627c478bd9Sstevel@tonic-gate uflag = USB_FLAGS_SLEEP;
28637c478bd9Sstevel@tonic-gate }
28647c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
28657c478bd9Sstevel@tonic-gate
28667c478bd9Sstevel@tonic-gate reqp->intr_len = epp->ep_descr.wMaxPacketSize;
28677c478bd9Sstevel@tonic-gate reqp->intr_cb = ugen_epx_intr_IN_req_cb;
28687c478bd9Sstevel@tonic-gate reqp->intr_exc_cb = ugen_epx_intr_IN_req_cb;
28697c478bd9Sstevel@tonic-gate
28707c478bd9Sstevel@tonic-gate
28717c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_intr_xfer(epp->ep_ph, reqp,
28727c478bd9Sstevel@tonic-gate uflag)) != USB_SUCCESS) {
28737c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28747c478bd9Sstevel@tonic-gate "ugen_epx_intr_IN_start_polling: failed %d", rval);
28757c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp);
28767c478bd9Sstevel@tonic-gate }
28777c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
28787c478bd9Sstevel@tonic-gate if (rval != USB_SUCCESS) {
28797c478bd9Sstevel@tonic-gate epp->ep_state &= ~UGEN_EP_STATE_INTR_IN_POLLING_ON;
28807c478bd9Sstevel@tonic-gate }
28817c478bd9Sstevel@tonic-gate } else {
28827c478bd9Sstevel@tonic-gate rval = USB_SUCCESS;
28837c478bd9Sstevel@tonic-gate }
28847c478bd9Sstevel@tonic-gate
28857c478bd9Sstevel@tonic-gate return (rval);
28867c478bd9Sstevel@tonic-gate }
28877c478bd9Sstevel@tonic-gate
28887c478bd9Sstevel@tonic-gate
28897c478bd9Sstevel@tonic-gate /*
28907c478bd9Sstevel@tonic-gate * stop polling on an interrupt endpoint, asynchronously
28917c478bd9Sstevel@tonic-gate */
28927c478bd9Sstevel@tonic-gate static void
ugen_epx_intr_IN_stop_polling(ugen_state_t * ugenp,ugen_ep_t * epp)28937c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_stop_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
28947c478bd9Sstevel@tonic-gate {
28957c478bd9Sstevel@tonic-gate if ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_ON) &&
28967c478bd9Sstevel@tonic-gate ((epp->ep_state & UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED) == 0)) {
28977c478bd9Sstevel@tonic-gate
28987c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
28997c478bd9Sstevel@tonic-gate "ugen_epx_intr_IN_stop_polling: epp=0x%p state=0x%x",
2900112116d8Sfb (void *)epp, epp->ep_state);
29017c478bd9Sstevel@tonic-gate
29027c478bd9Sstevel@tonic-gate epp->ep_state |= UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED;
29037c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
29047c478bd9Sstevel@tonic-gate usb_pipe_stop_intr_polling(epp->ep_ph, USB_FLAGS_NOSLEEP);
29057c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
29067c478bd9Sstevel@tonic-gate }
29077c478bd9Sstevel@tonic-gate }
29087c478bd9Sstevel@tonic-gate
29097c478bd9Sstevel@tonic-gate
29107c478bd9Sstevel@tonic-gate /*
29117c478bd9Sstevel@tonic-gate * poll management
29127c478bd9Sstevel@tonic-gate */
29137c478bd9Sstevel@tonic-gate static void
ugen_epx_intr_IN_poll_wakeup(ugen_state_t * ugenp,ugen_ep_t * epp)29147c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_poll_wakeup(ugen_state_t *ugenp, ugen_ep_t *epp)
29157c478bd9Sstevel@tonic-gate {
29167c478bd9Sstevel@tonic-gate if (epp->ep_state & UGEN_EP_STATE_INTR_IN_POLL_PENDING) {
29177c478bd9Sstevel@tonic-gate struct pollhead *phpp = &epp->ep_pollhead;
29187c478bd9Sstevel@tonic-gate
29197c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29207c478bd9Sstevel@tonic-gate "ugen_epx_intr_IN_poll_wakeup: state=0x%x", epp->ep_state);
29217c478bd9Sstevel@tonic-gate
29227c478bd9Sstevel@tonic-gate epp->ep_state &= ~UGEN_EP_STATE_INTR_IN_POLL_PENDING;
29237c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
29247c478bd9Sstevel@tonic-gate pollwakeup(phpp, POLLIN);
29257c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
29267c478bd9Sstevel@tonic-gate }
29277c478bd9Sstevel@tonic-gate }
29287c478bd9Sstevel@tonic-gate
29297c478bd9Sstevel@tonic-gate
29307c478bd9Sstevel@tonic-gate /*
29317c478bd9Sstevel@tonic-gate * callback functions for interrupt IN pipe
29327c478bd9Sstevel@tonic-gate */
29337c478bd9Sstevel@tonic-gate static void
ugen_epx_intr_IN_req_cb(usb_pipe_handle_t ph,usb_intr_req_t * reqp)29347c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_req_cb(usb_pipe_handle_t ph, usb_intr_req_t *reqp)
29357c478bd9Sstevel@tonic-gate {
29367c478bd9Sstevel@tonic-gate ugen_state_t *ugenp = (ugen_state_t *)reqp->intr_client_private;
29377c478bd9Sstevel@tonic-gate ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
29387c478bd9Sstevel@tonic-gate
29397c478bd9Sstevel@tonic-gate if (epp == NULL) {
29407c478bd9Sstevel@tonic-gate /* pipe is closing */
29417c478bd9Sstevel@tonic-gate
29427c478bd9Sstevel@tonic-gate goto done;
29437c478bd9Sstevel@tonic-gate }
29447c478bd9Sstevel@tonic-gate
29457c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
29467c478bd9Sstevel@tonic-gate
29477c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29487c478bd9Sstevel@tonic-gate "ugen_epx_intr_IN_req_cb:\n\t"
2949112116d8Sfb "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x len=%ld",
2950112116d8Sfb (void *)epp, epp->ep_state, (void *)ph, (void *)reqp,
2951112116d8Sfb reqp->intr_completion_reason, reqp->intr_cb_flags,
29527c478bd9Sstevel@tonic-gate (reqp->intr_data == NULL) ? 0 :
2953d29f5a71Szhigang lu - Sun Microsystems - Beijing China MBLKL(reqp->intr_data));
29547c478bd9Sstevel@tonic-gate
29557c478bd9Sstevel@tonic-gate ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
29567c478bd9Sstevel@tonic-gate
29577c478bd9Sstevel@tonic-gate if (epp->ep_data && reqp->intr_data) {
29587c478bd9Sstevel@tonic-gate mblk_t *mp;
29597c478bd9Sstevel@tonic-gate
29607c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29610a05e705Slc "intr ep%x coalesce data", epp->ep_descr.bEndpointAddress);
29627c478bd9Sstevel@tonic-gate
29637c478bd9Sstevel@tonic-gate /* coalesce the data into one mblk */
29647c478bd9Sstevel@tonic-gate epp->ep_data->b_cont = reqp->intr_data;
29657c478bd9Sstevel@tonic-gate if ((mp = msgpullup(epp->ep_data, -1)) != NULL) {
29667c478bd9Sstevel@tonic-gate reqp->intr_data = NULL;
29677c478bd9Sstevel@tonic-gate freemsg(epp->ep_data);
29687c478bd9Sstevel@tonic-gate epp->ep_data = mp;
29697c478bd9Sstevel@tonic-gate } else {
29707c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29717c478bd9Sstevel@tonic-gate "msgpullup failed, discard data");
29727c478bd9Sstevel@tonic-gate epp->ep_data->b_cont = NULL;
29737c478bd9Sstevel@tonic-gate }
29747c478bd9Sstevel@tonic-gate } else if (reqp->intr_data) {
29757c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29767c478bd9Sstevel@tonic-gate "setting ep_data");
29777c478bd9Sstevel@tonic-gate
29787c478bd9Sstevel@tonic-gate epp->ep_data = reqp->intr_data;
29797c478bd9Sstevel@tonic-gate reqp->intr_data = NULL;
29807c478bd9Sstevel@tonic-gate }
29817c478bd9Sstevel@tonic-gate
29827c478bd9Sstevel@tonic-gate switch (reqp->intr_completion_reason) {
29837c478bd9Sstevel@tonic-gate case USB_CR_OK:
29847c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
29857c478bd9Sstevel@tonic-gate
29867c478bd9Sstevel@tonic-gate break;
29877c478bd9Sstevel@tonic-gate case USB_CR_PIPE_RESET:
29887c478bd9Sstevel@tonic-gate case USB_CR_STOPPED_POLLING:
29897c478bd9Sstevel@tonic-gate
29907c478bd9Sstevel@tonic-gate break;
29917c478bd9Sstevel@tonic-gate default:
29927c478bd9Sstevel@tonic-gate epp->ep_lcmd_status =
29937c478bd9Sstevel@tonic-gate ugen_cr2lcstat(reqp->intr_completion_reason);
29947c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
29957c478bd9Sstevel@tonic-gate "ugen_exp_intr_cb_req: lcmd_status=0x%x",
29967c478bd9Sstevel@tonic-gate epp->ep_lcmd_status);
29977c478bd9Sstevel@tonic-gate
29987c478bd9Sstevel@tonic-gate break;
29997c478bd9Sstevel@tonic-gate }
30007c478bd9Sstevel@tonic-gate
30017c478bd9Sstevel@tonic-gate /* any non-zero completion reason stops polling */
30027c478bd9Sstevel@tonic-gate if ((reqp->intr_completion_reason) ||
30037c478bd9Sstevel@tonic-gate (epp->ep_one_xfer)) {
30047c478bd9Sstevel@tonic-gate epp->ep_state &= ~(UGEN_EP_STATE_INTR_IN_POLLING_ON |
30050a05e705Slc UGEN_EP_STATE_INTR_IN_POLLING_IS_STOPPED);
30067c478bd9Sstevel@tonic-gate }
30077c478bd9Sstevel@tonic-gate
30087c478bd9Sstevel@tonic-gate /* is there a poll pending? should we stop polling? */
30097c478bd9Sstevel@tonic-gate if (epp->ep_data) {
30107c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3011112116d8Sfb "ugen_epx_intr_IN_req_cb: data len=0x%lx",
3012d29f5a71Szhigang lu - Sun Microsystems - Beijing China MBLKL(epp->ep_data));
30137c478bd9Sstevel@tonic-gate
30147c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_poll_wakeup(ugenp, epp);
30157c478bd9Sstevel@tonic-gate
30167c478bd9Sstevel@tonic-gate /* if there is no space left, stop polling */
30177c478bd9Sstevel@tonic-gate if (epp->ep_data &&
3018d29f5a71Szhigang lu - Sun Microsystems - Beijing China (MBLKL(epp->ep_data) >=
30197c478bd9Sstevel@tonic-gate epp->ep_buf_limit)) {
30207c478bd9Sstevel@tonic-gate ugen_epx_intr_IN_stop_polling(ugenp, epp);
30217c478bd9Sstevel@tonic-gate }
30227c478bd9Sstevel@tonic-gate }
30237c478bd9Sstevel@tonic-gate
30247c478bd9Sstevel@tonic-gate if (reqp->intr_completion_reason && epp->ep_bp) {
30257c478bd9Sstevel@tonic-gate bioerror(epp->ep_bp, EIO);
30267c478bd9Sstevel@tonic-gate epp->ep_done++;
30277c478bd9Sstevel@tonic-gate cv_signal(&epp->ep_wait_cv);
30287c478bd9Sstevel@tonic-gate
30297c478bd9Sstevel@tonic-gate /* can we satisfy the read now */
30307c478bd9Sstevel@tonic-gate } else if (epp->ep_data && epp->ep_bp &&
30317c478bd9Sstevel@tonic-gate (!epp->ep_done || epp->ep_one_xfer)) {
30327c478bd9Sstevel@tonic-gate boolean_t wait;
30337c478bd9Sstevel@tonic-gate
30347c478bd9Sstevel@tonic-gate if ((ugen_epx_intr_IN_req(ugenp, epp, epp->ep_bp, &wait) ==
30357c478bd9Sstevel@tonic-gate USB_SUCCESS) && (wait == B_FALSE)) {
30367c478bd9Sstevel@tonic-gate epp->ep_done++;
30377c478bd9Sstevel@tonic-gate cv_signal(&epp->ep_wait_cv);
30387c478bd9Sstevel@tonic-gate }
30397c478bd9Sstevel@tonic-gate }
30407c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
30417c478bd9Sstevel@tonic-gate
30427c478bd9Sstevel@tonic-gate done:
30437c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp);
30447c478bd9Sstevel@tonic-gate }
30457c478bd9Sstevel@tonic-gate
30467c478bd9Sstevel@tonic-gate
30477c478bd9Sstevel@tonic-gate /*
30487c478bd9Sstevel@tonic-gate * handle intr OUT xfers
30497c478bd9Sstevel@tonic-gate */
30507c478bd9Sstevel@tonic-gate static int
ugen_epx_intr_OUT_req(ugen_state_t * ugenp,ugen_ep_t * epp,struct buf * bp,boolean_t * wait)30517c478bd9Sstevel@tonic-gate ugen_epx_intr_OUT_req(ugen_state_t *ugenp, ugen_ep_t *epp,
30527c478bd9Sstevel@tonic-gate struct buf *bp, boolean_t *wait)
30537c478bd9Sstevel@tonic-gate {
30547c478bd9Sstevel@tonic-gate int rval = USB_SUCCESS;
30557c478bd9Sstevel@tonic-gate usb_intr_req_t *reqp;
30567c478bd9Sstevel@tonic-gate
30577c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
30587c478bd9Sstevel@tonic-gate "ugen_epx_intr_OUT_req: epp=0x%p state=0x%x bp=0x%p",
3059112116d8Sfb (void *)epp, epp->ep_state, (void *)bp);
30607c478bd9Sstevel@tonic-gate
30617c478bd9Sstevel@tonic-gate reqp = usb_alloc_intr_req(ugenp->ug_dip, bp->b_bcount,
30620a05e705Slc USB_FLAGS_NOSLEEP);
30637c478bd9Sstevel@tonic-gate if (reqp == NULL) {
30647c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
30657c478bd9Sstevel@tonic-gate
30667c478bd9Sstevel@tonic-gate return (USB_NO_RESOURCES);
30677c478bd9Sstevel@tonic-gate }
30687c478bd9Sstevel@tonic-gate
30697c478bd9Sstevel@tonic-gate ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
30707c478bd9Sstevel@tonic-gate
30717c478bd9Sstevel@tonic-gate reqp->intr_timeout = ugen_intr_timeout;
30727c478bd9Sstevel@tonic-gate reqp->intr_client_private = (usb_opaque_t)ugenp;
30737c478bd9Sstevel@tonic-gate reqp->intr_len = bp->b_bcount;
30747c478bd9Sstevel@tonic-gate reqp->intr_attributes = USB_ATTRS_AUTOCLEARING;
30757c478bd9Sstevel@tonic-gate reqp->intr_cb = ugen_epx_intr_OUT_req_cb;
30767c478bd9Sstevel@tonic-gate reqp->intr_exc_cb = ugen_epx_intr_OUT_req_cb;
30777c478bd9Sstevel@tonic-gate
30787c478bd9Sstevel@tonic-gate /* copy data from bp */
30797c478bd9Sstevel@tonic-gate bcopy(epp->ep_bp->b_un.b_addr, reqp->intr_data->b_rptr,
30800a05e705Slc bp->b_bcount);
30817c478bd9Sstevel@tonic-gate reqp->intr_data->b_wptr += bp->b_bcount;
30827c478bd9Sstevel@tonic-gate
30837c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
30847c478bd9Sstevel@tonic-gate if ((rval = usb_pipe_intr_xfer(epp->ep_ph, reqp,
30857c478bd9Sstevel@tonic-gate USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
30867c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
30877c478bd9Sstevel@tonic-gate epp->ep_lcmd_status =
30887c478bd9Sstevel@tonic-gate ugen_cr2lcstat(reqp->intr_completion_reason);
30897c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp);
30907c478bd9Sstevel@tonic-gate bioerror(bp, EIO);
30917c478bd9Sstevel@tonic-gate } else {
30927c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
30937c478bd9Sstevel@tonic-gate }
30947c478bd9Sstevel@tonic-gate *wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
30957c478bd9Sstevel@tonic-gate
30967c478bd9Sstevel@tonic-gate return (rval);
30977c478bd9Sstevel@tonic-gate }
30987c478bd9Sstevel@tonic-gate
30997c478bd9Sstevel@tonic-gate
31007c478bd9Sstevel@tonic-gate /*
31017c478bd9Sstevel@tonic-gate * callback functions for interrupt OUT pipe
31027c478bd9Sstevel@tonic-gate */
31037c478bd9Sstevel@tonic-gate static void
ugen_epx_intr_OUT_req_cb(usb_pipe_handle_t ph,usb_intr_req_t * reqp)31047c478bd9Sstevel@tonic-gate ugen_epx_intr_OUT_req_cb(usb_pipe_handle_t ph, usb_intr_req_t *reqp)
31057c478bd9Sstevel@tonic-gate {
31067c478bd9Sstevel@tonic-gate ugen_state_t *ugenp = (ugen_state_t *)reqp->intr_client_private;
31077c478bd9Sstevel@tonic-gate ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
31087c478bd9Sstevel@tonic-gate
31097c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
31107c478bd9Sstevel@tonic-gate "ugen_epx_intr_OUT_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
3111112116d8Sfb (void *)ph, (void *)reqp, reqp->intr_completion_reason,
3112112116d8Sfb reqp->intr_cb_flags);
31137c478bd9Sstevel@tonic-gate
31147c478bd9Sstevel@tonic-gate ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
31157c478bd9Sstevel@tonic-gate
31167c478bd9Sstevel@tonic-gate /* epp might be NULL if we are closing the pipe */
31177c478bd9Sstevel@tonic-gate if (epp) {
31187c478bd9Sstevel@tonic-gate int len;
31197c478bd9Sstevel@tonic-gate
31207c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
31217c478bd9Sstevel@tonic-gate if (epp->ep_bp) {
3122d29f5a71Szhigang lu - Sun Microsystems - Beijing China len = min(MBLKL(reqp->intr_data), epp->ep_bp->b_bcount);
31237c478bd9Sstevel@tonic-gate
31247c478bd9Sstevel@tonic-gate epp->ep_bp->b_resid = epp->ep_bp->b_bcount - len;
31257c478bd9Sstevel@tonic-gate
31267c478bd9Sstevel@tonic-gate switch (reqp->intr_completion_reason) {
31277c478bd9Sstevel@tonic-gate case USB_CR_OK:
31287c478bd9Sstevel@tonic-gate epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
31297c478bd9Sstevel@tonic-gate
31307c478bd9Sstevel@tonic-gate break;
31317c478bd9Sstevel@tonic-gate case USB_CR_PIPE_RESET:
31327c478bd9Sstevel@tonic-gate
31337c478bd9Sstevel@tonic-gate break;
31347c478bd9Sstevel@tonic-gate default:
31357c478bd9Sstevel@tonic-gate epp->ep_lcmd_status =
31367c478bd9Sstevel@tonic-gate ugen_cr2lcstat(
31377c478bd9Sstevel@tonic-gate reqp->intr_completion_reason);
31387c478bd9Sstevel@tonic-gate bioerror(epp->ep_bp, EIO);
31397c478bd9Sstevel@tonic-gate }
31407c478bd9Sstevel@tonic-gate }
31417c478bd9Sstevel@tonic-gate epp->ep_done++;
31427c478bd9Sstevel@tonic-gate cv_signal(&epp->ep_wait_cv);
31437c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
31447c478bd9Sstevel@tonic-gate }
31457c478bd9Sstevel@tonic-gate
31467c478bd9Sstevel@tonic-gate usb_free_intr_req(reqp);
31477c478bd9Sstevel@tonic-gate }
31487c478bd9Sstevel@tonic-gate
31497c478bd9Sstevel@tonic-gate
31500a05e705Slc /*
31510a05e705Slc * handle isoc IN xfers
31520a05e705Slc */
31530a05e705Slc static int
ugen_epx_isoc_IN_req(ugen_state_t * ugenp,ugen_ep_t * epp,struct buf * bp,boolean_t * wait)31540a05e705Slc ugen_epx_isoc_IN_req(ugen_state_t *ugenp, ugen_ep_t *epp,
31550a05e705Slc struct buf *bp, boolean_t *wait)
31560a05e705Slc {
31570a05e705Slc int rval = USB_SUCCESS;
31580a05e705Slc ugen_isoc_pkt_descr_t *pkt_descr;
31590a05e705Slc ushort_t n_pkt;
31600a05e705Slc uint_t pkts_len, len = 0;
31610a05e705Slc
31620a05e705Slc USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
31630a05e705Slc "ugen_epx_isoc_IN_req: epp=0x%p state=0x%x bp=0x%p",
3164112116d8Sfb (void *)epp, epp->ep_state, (void *)bp);
31650a05e705Slc
31660a05e705Slc *wait = B_FALSE;
31670a05e705Slc
31680a05e705Slc /* check if the isoc in pkt info has been initialized */
31690a05e705Slc pkt_descr = epp->ep_isoc_info.isoc_pkt_descr;
31700a05e705Slc n_pkt = epp->ep_isoc_info.isoc_pkts_count;
31710a05e705Slc if ((n_pkt == 0) || (pkt_descr == NULL)) {
31720a05e705Slc rval = USB_FAILURE;
31730a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_ISOC_UNINITIALIZED;
31740a05e705Slc
31750a05e705Slc goto done;
31760a05e705Slc }
31770a05e705Slc
31780a05e705Slc
31790a05e705Slc /* For OUT endpoint, return pkts transfer status of last request */
31800a05e705Slc if (UGEN_XFER_DIR(epp) != USB_EP_DIR_IN) {
31810a05e705Slc if (bp->b_bcount < sizeof (ugen_isoc_pkt_descr_t) * n_pkt) {
31820a05e705Slc rval = USB_INVALID_REQUEST;
31830a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
31840a05e705Slc
31850a05e705Slc return (rval);
31860a05e705Slc }
31870a05e705Slc bcopy(epp->ep_isoc_info.isoc_pkt_descr, bp->b_un.b_addr,
31880a05e705Slc n_pkt * sizeof (ugen_isoc_pkt_descr_t));
31890a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
31900a05e705Slc
31910a05e705Slc return (USB_SUCCESS);
31920a05e705Slc }
31930a05e705Slc
31940a05e705Slc /* read length should be the sum of pkt descrs and data length */
31950a05e705Slc pkts_len = epp->ep_isoc_info.isoc_pkts_length;
31960a05e705Slc if (bp->b_bcount != pkts_len + sizeof (ugen_isoc_pkt_descr_t) * n_pkt) {
31970a05e705Slc rval = USB_INVALID_REQUEST;
31980a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
31990a05e705Slc
32000a05e705Slc goto done;
32010a05e705Slc }
32020a05e705Slc
32030a05e705Slc /* can we satisfy this read? */
32040a05e705Slc if (epp->ep_data) {
3205d29f5a71Szhigang lu - Sun Microsystems - Beijing China len = min(MBLKL(epp->ep_data),
32060a05e705Slc bp->b_bcount);
32070a05e705Slc /*
32080a05e705Slc * every msg block in ep_data must be the size of
32090a05e705Slc * pkts_len(payload length) + pkt descrs len
32100a05e705Slc */
32110a05e705Slc ASSERT((len == 0) || (len == bp->b_bcount));
32120a05e705Slc }
32130a05e705Slc
32140a05e705Slc /*
32150a05e705Slc * if polling not active, restart
32160a05e705Slc * if there is some data, return the data
32170a05e705Slc */
32180a05e705Slc if ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON) == 0) {
32190a05e705Slc if (len == 0) {
32200a05e705Slc rval = USB_FAILURE;
32210a05e705Slc if ((rval = ugen_epx_isoc_IN_start_polling(ugenp,
32220a05e705Slc epp)) != USB_SUCCESS) {
32230a05e705Slc epp->ep_lcmd_status =
32240a05e705Slc USB_LC_STAT_ISOC_POLLING_FAILED;
32250a05e705Slc }
32260a05e705Slc
32270a05e705Slc goto done;
32280a05e705Slc
32290a05e705Slc } else if (epp->ep_data && (len >= bp->b_bcount)) {
32300a05e705Slc bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr,
32310a05e705Slc bp->b_bcount);
32320a05e705Slc bp->b_resid = 0;
32330a05e705Slc epp->ep_data->b_rptr += bp->b_bcount;
32340a05e705Slc
32350a05e705Slc goto done;
32360a05e705Slc }
32370a05e705Slc }
32380a05e705Slc
32390a05e705Slc /*
32400a05e705Slc * if there is data or FNDELAY, return available data
32410a05e705Slc */
32420a05e705Slc if (epp->ep_data && (len >= bp->b_bcount)) {
32430a05e705Slc /* can fulfill this read request */
32440a05e705Slc bcopy(epp->ep_data->b_rptr, bp->b_un.b_addr, bp->b_bcount);
32450a05e705Slc epp->ep_data->b_rptr += bp->b_bcount;
32460a05e705Slc bp->b_resid = 0;
32470a05e705Slc } else if (epp->ep_xfer_oflag & (FNDELAY | FNONBLOCK)) {
32480a05e705Slc bp->b_resid = bp->b_bcount;
32490a05e705Slc } else {
32500a05e705Slc /* otherwise just wait for data */
32510a05e705Slc *wait = B_TRUE;
32520a05e705Slc }
32530a05e705Slc
32540a05e705Slc done:
32550a05e705Slc /* data have been read */
32560a05e705Slc if (epp->ep_data && (epp->ep_data->b_rptr == epp->ep_data->b_wptr)) {
32570a05e705Slc mblk_t *mp = NULL;
32580a05e705Slc
32590a05e705Slc /* remove the just read msg block */
32600a05e705Slc mp = unlinkb(epp->ep_data);
32610a05e705Slc freemsg(epp->ep_data);
32620a05e705Slc
32630a05e705Slc if (mp) {
32640a05e705Slc epp->ep_data = mp;
32650a05e705Slc } else {
32660a05e705Slc epp->ep_data = NULL;
32670a05e705Slc }
32680a05e705Slc }
32690a05e705Slc
32700a05e705Slc if (*wait) {
32710a05e705Slc ASSERT(epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON);
32720a05e705Slc }
32730a05e705Slc
32740a05e705Slc USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
32750a05e705Slc "ugen_epx_isoc_IN_req end: rval=%d bcount=%lu len=%d data=0x%p",
3276112116d8Sfb rval, bp->b_bcount, len, (void *)epp->ep_data);
32770a05e705Slc
32780a05e705Slc return (rval);
32790a05e705Slc }
32800a05e705Slc
32810a05e705Slc
32820a05e705Slc /*
32830a05e705Slc * Start polling on isoc endpoint, asynchronously
32840a05e705Slc */
32850a05e705Slc static int
ugen_epx_isoc_IN_start_polling(ugen_state_t * ugenp,ugen_ep_t * epp)32860a05e705Slc ugen_epx_isoc_IN_start_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
32870a05e705Slc {
32880a05e705Slc int rval = USB_FAILURE;
32890a05e705Slc usb_isoc_req_t *reqp;
32900a05e705Slc ugen_isoc_pkt_descr_t *pkt_descr;
32910a05e705Slc ushort_t n_pkt, pkt;
32920a05e705Slc uint_t pkts_len;
32930a05e705Slc
32940a05e705Slc /*
32950a05e705Slc * if polling is being stopped, we restart polling in the
32960a05e705Slc * isoc callback again
32970a05e705Slc */
32980a05e705Slc if (epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED) {
32990a05e705Slc
33000a05e705Slc return (rval);
33010a05e705Slc }
33020a05e705Slc
33030a05e705Slc if ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON) == 0) {
33040a05e705Slc USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
33050a05e705Slc "ugen_epx_isoc_IN_start_polling: epp=0x%p state=0x%x",
3306112116d8Sfb (void *)epp, epp->ep_state);
33070a05e705Slc
33080a05e705Slc pkts_len = epp->ep_isoc_info.isoc_pkts_length;
33090a05e705Slc n_pkt = epp->ep_isoc_info.isoc_pkts_count;
33100a05e705Slc pkt_descr = epp->ep_isoc_info.isoc_pkt_descr;
33110a05e705Slc
33120a05e705Slc epp->ep_state |= UGEN_EP_STATE_ISOC_IN_POLLING_ON;
33130a05e705Slc mutex_exit(&epp->ep_mutex);
33140a05e705Slc
33150a05e705Slc if ((reqp = usb_alloc_isoc_req(ugenp->ug_dip, n_pkt, pkts_len,
33160a05e705Slc USB_FLAGS_NOSLEEP)) == NULL) {
33170a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
33180a05e705Slc "ugen_epx_isoc_IN_start_polling: alloc isoc "
33190a05e705Slc "req failed");
33200a05e705Slc mutex_enter(&epp->ep_mutex);
33210a05e705Slc epp->ep_state &= ~UGEN_EP_STATE_ISOC_IN_POLLING_ON;
33220a05e705Slc
33230a05e705Slc return (USB_NO_RESOURCES);
33240a05e705Slc }
33250a05e705Slc reqp->isoc_client_private = (usb_opaque_t)ugenp;
33260a05e705Slc
33270a05e705Slc reqp->isoc_attributes = USB_ATTRS_AUTOCLEARING |
33280a05e705Slc USB_ATTRS_SHORT_XFER_OK | USB_ATTRS_ISOC_XFER_ASAP;
33290a05e705Slc
33300a05e705Slc /*
33310a05e705Slc * isoc_pkts_length was defined to be ushort_t. This
33320a05e705Slc * has been obsoleted by usb high speed isoc support.
33330a05e705Slc * It is set here just for compatibility reason
33340a05e705Slc */
33350a05e705Slc reqp->isoc_pkts_length = 0;
33360a05e705Slc
33370a05e705Slc for (pkt = 0; pkt < n_pkt; pkt++) {
33380a05e705Slc reqp->isoc_pkt_descr[pkt].isoc_pkt_length =
33390a05e705Slc pkt_descr[pkt].dsc_isoc_pkt_len;
33400a05e705Slc }
33410a05e705Slc reqp->isoc_pkts_count = n_pkt;
33420a05e705Slc reqp->isoc_cb = ugen_epx_isoc_IN_req_cb;
33430a05e705Slc reqp->isoc_exc_cb = ugen_epx_isoc_IN_req_cb;
33440a05e705Slc
33450a05e705Slc if ((rval = usb_pipe_isoc_xfer(epp->ep_ph, reqp,
33460a05e705Slc USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
33470a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
33480a05e705Slc "ugen_epx_isoc_IN_start_polling: failed %d", rval);
33490a05e705Slc usb_free_isoc_req(reqp);
33500a05e705Slc }
33510a05e705Slc
33520a05e705Slc mutex_enter(&epp->ep_mutex);
33530a05e705Slc if (rval != USB_SUCCESS) {
33540a05e705Slc epp->ep_state &= ~UGEN_EP_STATE_ISOC_IN_POLLING_ON;
33550a05e705Slc }
33560a05e705Slc } else {
33570a05e705Slc rval = USB_SUCCESS;
33580a05e705Slc }
33590a05e705Slc
33600a05e705Slc return (rval);
33610a05e705Slc }
33620a05e705Slc
33630a05e705Slc
33640a05e705Slc /*
33650a05e705Slc * stop polling on an isoc endpoint, asynchronously
33660a05e705Slc */
33670a05e705Slc static void
ugen_epx_isoc_IN_stop_polling(ugen_state_t * ugenp,ugen_ep_t * epp)33680a05e705Slc ugen_epx_isoc_IN_stop_polling(ugen_state_t *ugenp, ugen_ep_t *epp)
33690a05e705Slc {
33700a05e705Slc if ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_ON) &&
33710a05e705Slc ((epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED) == 0)) {
33720a05e705Slc USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
33730a05e705Slc "ugen_epx_isoc_IN_stop_polling: epp=0x%p state=0x%x",
3374112116d8Sfb (void *)epp, epp->ep_state);
33750a05e705Slc
33760a05e705Slc epp->ep_state |= UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED;
33770a05e705Slc mutex_exit(&epp->ep_mutex);
33780a05e705Slc usb_pipe_stop_isoc_polling(epp->ep_ph, USB_FLAGS_NOSLEEP);
33790a05e705Slc mutex_enter(&epp->ep_mutex);
33800a05e705Slc }
33810a05e705Slc }
33820a05e705Slc
33830a05e705Slc
33840a05e705Slc /*
33850a05e705Slc * poll management
33860a05e705Slc */
33870a05e705Slc static void
ugen_epx_isoc_IN_poll_wakeup(ugen_state_t * ugenp,ugen_ep_t * epp)33880a05e705Slc ugen_epx_isoc_IN_poll_wakeup(ugen_state_t *ugenp, ugen_ep_t *epp)
33890a05e705Slc {
33900a05e705Slc if (epp->ep_state & UGEN_EP_STATE_ISOC_IN_POLL_PENDING) {
33910a05e705Slc struct pollhead *phpp = &epp->ep_pollhead;
33920a05e705Slc
33930a05e705Slc USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
33940a05e705Slc "ugen_epx_isoc_IN_poll_wakeup: state=0x%x", epp->ep_state);
33950a05e705Slc
33960a05e705Slc epp->ep_state &= ~UGEN_EP_STATE_ISOC_IN_POLL_PENDING;
33970a05e705Slc mutex_exit(&epp->ep_mutex);
33980a05e705Slc pollwakeup(phpp, POLLIN);
33990a05e705Slc mutex_enter(&epp->ep_mutex);
34000a05e705Slc }
34010a05e705Slc }
34020a05e705Slc
34030a05e705Slc
34040a05e705Slc /*
34050a05e705Slc * callback functions for isoc IN pipe
34060a05e705Slc */
34070a05e705Slc static void
ugen_epx_isoc_IN_req_cb(usb_pipe_handle_t ph,usb_isoc_req_t * reqp)34080a05e705Slc ugen_epx_isoc_IN_req_cb(usb_pipe_handle_t ph, usb_isoc_req_t *reqp)
34090a05e705Slc {
34100a05e705Slc ugen_state_t *ugenp = (ugen_state_t *)reqp->isoc_client_private;
34110a05e705Slc ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
34120a05e705Slc
34130a05e705Slc if (epp == NULL) {
34140a05e705Slc /* pipe is closing */
34150a05e705Slc
34160a05e705Slc goto done;
34170a05e705Slc }
34180a05e705Slc
34190a05e705Slc ASSERT(!mutex_owned(&epp->ep_mutex)); /* not owned */
34200a05e705Slc
34210a05e705Slc mutex_enter(&epp->ep_mutex);
34220a05e705Slc
34230a05e705Slc USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
34240a05e705Slc "ugen_epx_isoc_IN_req_cb: "
3425112116d8Sfb "epp=0x%p state=0x%x ph=0x%p reqp=0x%p cr=%d cb=0x%x len=%ld "
3426112116d8Sfb "isoc error count=%d, pkt cnt=%d", (void *)epp, epp->ep_state,
3427112116d8Sfb (void *)ph, (void *)reqp, reqp->isoc_completion_reason,
3428112116d8Sfb reqp->isoc_cb_flags, (reqp->isoc_data == NULL) ? 0 :
3429d29f5a71Szhigang lu - Sun Microsystems - Beijing China MBLKL(reqp->isoc_data),
34300a05e705Slc reqp->isoc_error_count, reqp->isoc_pkts_count);
34310a05e705Slc
34320a05e705Slc /* Too many packet errors during isoc transfer of this request */
34330a05e705Slc if (reqp->isoc_error_count == reqp->isoc_pkts_count) {
34340a05e705Slc USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
34350a05e705Slc "too many errors(%d) in this req, stop polling",
34360a05e705Slc reqp->isoc_error_count);
34370a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_ISOC_PKT_ERROR;
34380a05e705Slc ugen_epx_isoc_IN_stop_polling(ugenp, epp);
34390a05e705Slc }
34400a05e705Slc
34410a05e705Slc /* Data OK */
34420a05e705Slc if (reqp->isoc_data && !reqp->isoc_completion_reason) {
34430a05e705Slc mblk_t *mp1 = NULL, *mp2 = NULL;
34440a05e705Slc usb_isoc_pkt_descr_t *pkt_descr =
34450a05e705Slc reqp->isoc_pkt_descr;
34460a05e705Slc ushort_t i, n_pkt = reqp->isoc_pkts_count;
34470a05e705Slc
34480a05e705Slc for (i = 0; i < n_pkt; i++) {
34490a05e705Slc USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
34500a05e705Slc "pkt %d: len=%d status=%d actual_len=%d", i,
34510a05e705Slc pkt_descr[i].isoc_pkt_length,
34520a05e705Slc pkt_descr[i].isoc_pkt_status,
34530a05e705Slc pkt_descr[i].isoc_pkt_actual_length);
34540a05e705Slc
34550a05e705Slc /* translate cr to ugen lcstat */
34560a05e705Slc pkt_descr[i].isoc_pkt_status =
34570a05e705Slc ugen_cr2lcstat(pkt_descr[i].isoc_pkt_status);
34580a05e705Slc }
34590a05e705Slc
34600a05e705Slc /* construct data buffer: pkt descriptors + payload */
34610a05e705Slc mp2 = allocb(sizeof (ugen_isoc_pkt_descr_t) * n_pkt, BPRI_HI);
34620a05e705Slc if (mp2 == NULL) {
34630a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
34640a05e705Slc "alloc msgblk failed, discard data");
34650a05e705Slc } else {
34660a05e705Slc /* pkt descrs first */
34670a05e705Slc bcopy(pkt_descr, mp2->b_wptr,
34680a05e705Slc sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
34690a05e705Slc
34700a05e705Slc mp2->b_wptr += sizeof (ugen_isoc_pkt_descr_t) * n_pkt;
34710a05e705Slc
34720a05e705Slc /* payload follows */
34730a05e705Slc linkb(mp2, reqp->isoc_data);
34740a05e705Slc
34750a05e705Slc /* concatenate data bytes in mp2 */
34760a05e705Slc if ((mp1 = msgpullup(mp2, -1)) != NULL) {
34770a05e705Slc /*
34780a05e705Slc * now we get the required data:
34790a05e705Slc * pkt descrs + payload
34800a05e705Slc */
34810a05e705Slc reqp->isoc_data = NULL;
34820a05e705Slc } else {
34830a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER,
34840a05e705Slc ugenp->ug_log_hdl,
34850a05e705Slc "msgpullup status blk failed, "
34860a05e705Slc "discard data");
34870a05e705Slc mp2->b_cont = NULL;
34880a05e705Slc }
34890a05e705Slc
34900a05e705Slc freemsg(mp2);
34910a05e705Slc mp2 = NULL;
34920a05e705Slc }
34930a05e705Slc
34940a05e705Slc if (epp->ep_data && (mp1 != NULL)) {
34950a05e705Slc USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
34960a05e705Slc "ISOC ep%x coalesce ep_data",
34970a05e705Slc epp->ep_descr.bEndpointAddress);
34980a05e705Slc
34990a05e705Slc /* add mp1 to the tail of ep_data */
35000a05e705Slc linkb(epp->ep_data, mp1);
35010a05e705Slc
35020a05e705Slc } else if (mp1 != NULL) {
35030a05e705Slc USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
35040a05e705Slc "setting ep_data");
35050a05e705Slc epp->ep_data = mp1;
35060a05e705Slc }
35070a05e705Slc }
35080a05e705Slc
35090a05e705Slc switch (reqp->isoc_completion_reason) {
35100a05e705Slc case USB_CR_OK:
35110a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
35120a05e705Slc
35130a05e705Slc break;
35140a05e705Slc case USB_CR_PIPE_RESET:
35150a05e705Slc case USB_CR_STOPPED_POLLING:
35160a05e705Slc case USB_CR_PIPE_CLOSING:
35170a05e705Slc
35180a05e705Slc break;
35190a05e705Slc default:
35200a05e705Slc epp->ep_lcmd_status =
35210a05e705Slc ugen_cr2lcstat(reqp->isoc_completion_reason);
35220a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
35230a05e705Slc "ugen_exp_isoc_cb_req: error lcmd_status=0x%x ",
35240a05e705Slc epp->ep_lcmd_status);
35250a05e705Slc
35260a05e705Slc break;
35270a05e705Slc }
35280a05e705Slc
35290a05e705Slc /* any non-zero completion reason signifies polling has stopped */
35300a05e705Slc if (reqp->isoc_completion_reason) {
35310a05e705Slc epp->ep_state &= ~(UGEN_EP_STATE_ISOC_IN_POLLING_ON |
35320a05e705Slc UGEN_EP_STATE_ISOC_IN_POLLING_IS_STOPPED);
35330a05e705Slc }
35340a05e705Slc
35350a05e705Slc
35360a05e705Slc /* is there a poll pending? should we stop polling? */
35370a05e705Slc if (epp->ep_data) {
35380a05e705Slc USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3539112116d8Sfb "ugen_epx_isoc_IN_req_cb: data len=0x%lx, limit=0x%lx",
35400a05e705Slc msgdsize(epp->ep_data),
35410a05e705Slc epp->ep_buf_limit);
35420a05e705Slc
35430a05e705Slc ugen_epx_isoc_IN_poll_wakeup(ugenp, epp);
35440a05e705Slc
35450a05e705Slc
35460a05e705Slc /*
35470a05e705Slc * Since isoc is unreliable xfer, if buffered data size exceeds
35480a05e705Slc * the limit, we just discard and free data in the oldest mblk
35490a05e705Slc */
35500a05e705Slc if (epp->ep_data &&
35510a05e705Slc (msgdsize(epp->ep_data) >= epp->ep_buf_limit)) {
35520a05e705Slc mblk_t *mp = NULL;
35530a05e705Slc
35540a05e705Slc /* exceed buf lenth limit, remove the oldest one */
35550a05e705Slc USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
35560a05e705Slc "ugen_epx_isoc_IN_req_cb: overflow!");
35570a05e705Slc mp = unlinkb(epp->ep_data);
35580a05e705Slc if (epp->ep_data) {
35590a05e705Slc freeb(epp->ep_data);
35600a05e705Slc }
35610a05e705Slc epp->ep_data = mp;
35620a05e705Slc }
35630a05e705Slc
35640a05e705Slc }
35650a05e705Slc
35660a05e705Slc if (reqp->isoc_completion_reason && epp->ep_bp) {
35670a05e705Slc bioerror(epp->ep_bp, EIO);
35680a05e705Slc epp->ep_done++;
35690a05e705Slc cv_signal(&epp->ep_wait_cv);
35700a05e705Slc
35710a05e705Slc } else if (epp->ep_data && epp->ep_bp && !epp->ep_done) {
35720a05e705Slc boolean_t wait;
35730a05e705Slc
35740a05e705Slc /* can we satisfy the read now */
35750a05e705Slc if ((ugen_epx_isoc_IN_req(ugenp, epp, epp->ep_bp, &wait) ==
35760a05e705Slc USB_SUCCESS) && (wait == B_FALSE)) {
35770a05e705Slc epp->ep_done++;
35780a05e705Slc cv_signal(&epp->ep_wait_cv);
35790a05e705Slc }
35800a05e705Slc }
35810a05e705Slc mutex_exit(&epp->ep_mutex);
35820a05e705Slc
35830a05e705Slc done:
35840a05e705Slc
35850a05e705Slc usb_free_isoc_req(reqp);
35860a05e705Slc }
35870a05e705Slc
35880a05e705Slc /*
35890a05e705Slc * handle isoc OUT xfers or init isoc IN polling
35900a05e705Slc */
35910a05e705Slc static int
ugen_epx_isoc_OUT_req(ugen_state_t * ugenp,ugen_ep_t * epp,struct buf * bp,boolean_t * wait)35920a05e705Slc ugen_epx_isoc_OUT_req(ugen_state_t *ugenp, ugen_ep_t *epp,
35930a05e705Slc struct buf *bp, boolean_t *wait)
35940a05e705Slc {
35950a05e705Slc int rval = USB_SUCCESS;
35960a05e705Slc usb_isoc_req_t *reqp;
35970a05e705Slc ugen_isoc_pkt_descr_t *pkt_descr;
35980a05e705Slc ushort_t pkt, n_pkt = 0;
35990a05e705Slc uint_t pkts_len = 0;
36000a05e705Slc uint_t head_len;
36010a05e705Slc char *p;
36020a05e705Slc ugen_isoc_req_head_t *pkth;
36030a05e705Slc
36040a05e705Slc USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
36050a05e705Slc "ugen_epx_isoc_OUT_req: epp=0x%p state=0x%x bp=0x%p",
3606112116d8Sfb (void *)epp, epp->ep_state, (void *)bp);
36070a05e705Slc
36080a05e705Slc *wait = B_FALSE;
36090a05e705Slc
36100a05e705Slc if (bp->b_bcount < sizeof (int)) {
36110a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
36120a05e705Slc rval = USB_INVALID_REQUEST;
36130a05e705Slc
36140a05e705Slc goto done;
36150a05e705Slc }
36160a05e705Slc
3617d29f5a71Szhigang lu - Sun Microsystems - Beijing China /* LINTED E_BAD_PTR_CAST_ALIGN */
36180a05e705Slc pkth = (ugen_isoc_req_head_t *)bp->b_un.b_addr;
36190a05e705Slc n_pkt = pkth->req_isoc_pkts_count;
36200a05e705Slc head_len = sizeof (ugen_isoc_pkt_descr_t) * n_pkt +
36210a05e705Slc sizeof (int);
36220a05e705Slc
36230a05e705Slc if ((n_pkt == 0) ||
36240a05e705Slc (n_pkt > usb_get_max_pkts_per_isoc_request(ugenp->ug_dip)) ||
36250a05e705Slc (bp->b_bcount < head_len)) {
36260a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3627112116d8Sfb "Invalid params: bcount=%lu, head_len=%d, pktcnt=%d",
36280a05e705Slc bp->b_bcount, head_len, n_pkt);
36290a05e705Slc
36300a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
36310a05e705Slc rval = USB_INVALID_REQUEST;
36320a05e705Slc
36330a05e705Slc goto done;
36340a05e705Slc }
36350a05e705Slc
36360a05e705Slc p = bp->b_un.b_addr;
36370a05e705Slc p += sizeof (int); /* points to pkt_descrs */
36380a05e705Slc
36390a05e705Slc pkt_descr = kmem_zalloc(sizeof (ugen_isoc_pkt_descr_t) * n_pkt,
36400a05e705Slc KM_NOSLEEP);
36410a05e705Slc if (pkt_descr == NULL) {
36420a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
36430a05e705Slc rval = USB_NO_RESOURCES;
36440a05e705Slc
36450a05e705Slc goto done;
36460a05e705Slc }
36470a05e705Slc bcopy(p, pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
36480a05e705Slc p += sizeof (ugen_isoc_pkt_descr_t) * n_pkt;
36490a05e705Slc
36500a05e705Slc /* total packet payload length */
36510a05e705Slc for (pkt = 0; pkt < n_pkt; pkt++) {
36520a05e705Slc pkts_len += pkt_descr[pkt].dsc_isoc_pkt_len;
36530a05e705Slc }
36540a05e705Slc
36550a05e705Slc /*
36560a05e705Slc * write length may either be header length for isoc IN endpoint or
36570a05e705Slc * the sum of header and data pkts length for isoc OUT endpoint
36580a05e705Slc */
36590a05e705Slc if (((bp->b_bcount != head_len) &&
36600a05e705Slc (bp->b_bcount != head_len + pkts_len))) {
36610a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
3662112116d8Sfb "invalid length: bcount=%lu, head_len=%d, pkts_len = %d,"
36630a05e705Slc "pktcnt=%d", bp->b_bcount, head_len, pkts_len, n_pkt);
36640a05e705Slc
36650a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
36660a05e705Slc kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
36670a05e705Slc rval = USB_INVALID_REQUEST;
36680a05e705Slc
36690a05e705Slc goto done;
36700a05e705Slc }
36710a05e705Slc
36720a05e705Slc
36730a05e705Slc ASSERT(epp->ep_state & UGEN_EP_STATE_XS_OPEN);
36740a05e705Slc
36750a05e705Slc /* Set parameters for READ */
36760a05e705Slc if (bp->b_bcount == head_len) {
36770a05e705Slc /* must be isoc IN endpoint */
36780a05e705Slc if ((UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) == 0) {
36790a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
36800a05e705Slc kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) *
36810a05e705Slc n_pkt);
36820a05e705Slc rval = USB_INVALID_REQUEST;
36830a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
36840a05e705Slc "write length invalid for OUT ep%x",
36850a05e705Slc epp->ep_descr.bEndpointAddress);
36860a05e705Slc
36870a05e705Slc goto done;
36880a05e705Slc }
36890a05e705Slc
36900a05e705Slc if (epp->ep_isoc_in_inited) {
36910a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
36920a05e705Slc kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) *
36930a05e705Slc n_pkt);
36940a05e705Slc rval = USB_INVALID_REQUEST;
36950a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
36960a05e705Slc "isoc IN polling fail: already inited, need to"
36970a05e705Slc "close the ep before initing again");
36980a05e705Slc
36990a05e705Slc goto done;
37000a05e705Slc }
37010a05e705Slc
37020a05e705Slc /* save pkts info for the READ */
37030a05e705Slc epp->ep_isoc_info.isoc_pkts_count = n_pkt;
37040a05e705Slc epp->ep_isoc_info.isoc_pkts_length = pkts_len;
37050a05e705Slc epp->ep_isoc_info.isoc_pkt_descr = pkt_descr;
37060a05e705Slc
37070a05e705Slc if ((rval = ugen_epx_isoc_IN_start_polling(ugenp,
37080a05e705Slc epp)) != USB_SUCCESS) {
37090a05e705Slc epp->ep_lcmd_status =
37100a05e705Slc USB_LC_STAT_ISOC_POLLING_FAILED;
37110a05e705Slc kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) *
37120a05e705Slc n_pkt);
37130a05e705Slc epp->ep_isoc_info.isoc_pkts_count = 0;
37140a05e705Slc epp->ep_isoc_info.isoc_pkts_length = 0;
37150a05e705Slc epp->ep_isoc_info.isoc_pkt_descr = NULL;
37160a05e705Slc
37170a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
37180a05e705Slc "isoc IN start polling failed");
37190a05e705Slc
37200a05e705Slc goto done;
37210a05e705Slc }
37220a05e705Slc
37230a05e705Slc epp->ep_bp->b_resid = epp->ep_bp->b_bcount - head_len;
37240a05e705Slc
37250a05e705Slc epp->ep_isoc_in_inited++;
37260a05e705Slc USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
37270a05e705Slc "isoc IN ep inited");
37280a05e705Slc
37290a05e705Slc goto done;
37300a05e705Slc }
37310a05e705Slc
37320a05e705Slc /* must be isoc OUT endpoint */
37330a05e705Slc if (UGEN_XFER_DIR(epp) & USB_EP_DIR_IN) {
37340a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_INVALID_REQ;
37350a05e705Slc kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
37360a05e705Slc rval = USB_INVALID_REQUEST;
37370a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
37380a05e705Slc "write length invalid for an IN ep%x",
37390a05e705Slc epp->ep_descr.bEndpointAddress);
37400a05e705Slc
37410a05e705Slc goto done;
37420a05e705Slc }
37430a05e705Slc
37440a05e705Slc /* OUT endpoint, free previous info if there's any */
37450a05e705Slc if (epp->ep_isoc_info.isoc_pkt_descr) {
37460a05e705Slc kmem_free(epp->ep_isoc_info.isoc_pkt_descr,
37470a05e705Slc sizeof (ugen_isoc_pkt_descr_t) *
37480a05e705Slc epp->ep_isoc_info.isoc_pkts_count);
37490a05e705Slc }
37500a05e705Slc
37510a05e705Slc /* save pkts info for the WRITE */
37520a05e705Slc epp->ep_isoc_info.isoc_pkts_count = n_pkt;
37530a05e705Slc epp->ep_isoc_info.isoc_pkts_length = pkts_len;
37540a05e705Slc epp->ep_isoc_info.isoc_pkt_descr = pkt_descr;
37550a05e705Slc
37560a05e705Slc reqp = usb_alloc_isoc_req(ugenp->ug_dip, n_pkt, pkts_len,
37570a05e705Slc USB_FLAGS_NOSLEEP);
37580a05e705Slc if (reqp == NULL) {
37590a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_NO_RESOURCES;
37600a05e705Slc kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
37610a05e705Slc rval = USB_NO_RESOURCES;
37620a05e705Slc epp->ep_isoc_info.isoc_pkts_count = 0;
37630a05e705Slc epp->ep_isoc_info.isoc_pkts_length = 0;
37640a05e705Slc epp->ep_isoc_info.isoc_pkt_descr = NULL;
37650a05e705Slc
37660a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
37670a05e705Slc "alloc isoc out req failed");
37680a05e705Slc goto done;
37690a05e705Slc }
37700a05e705Slc
37710a05e705Slc for (pkt = 0; pkt < n_pkt; pkt++) {
37720a05e705Slc reqp->isoc_pkt_descr[pkt].isoc_pkt_length =
37730a05e705Slc pkt_descr[pkt].dsc_isoc_pkt_len;
37740a05e705Slc }
37750a05e705Slc reqp->isoc_pkts_count = n_pkt;
37760a05e705Slc reqp->isoc_client_private = (usb_opaque_t)ugenp;
37770a05e705Slc reqp->isoc_attributes = USB_ATTRS_AUTOCLEARING |
37780a05e705Slc USB_ATTRS_ISOC_XFER_ASAP;
37790a05e705Slc
37800a05e705Slc reqp->isoc_cb = ugen_epx_isoc_OUT_req_cb;
37810a05e705Slc reqp->isoc_exc_cb = ugen_epx_isoc_OUT_req_cb;
37820a05e705Slc
37830a05e705Slc /* copy data from bp */
37840a05e705Slc bcopy(p, reqp->isoc_data->b_wptr, pkts_len);
37850a05e705Slc reqp->isoc_data->b_wptr += pkts_len;
37860a05e705Slc
37870a05e705Slc mutex_exit(&epp->ep_mutex);
37880a05e705Slc if ((rval = usb_pipe_isoc_xfer(epp->ep_ph, reqp,
37890a05e705Slc USB_FLAGS_NOSLEEP)) != USB_SUCCESS) {
37900a05e705Slc mutex_enter(&epp->ep_mutex);
37910a05e705Slc epp->ep_lcmd_status =
37920a05e705Slc ugen_cr2lcstat(reqp->isoc_completion_reason);
37930a05e705Slc usb_free_isoc_req(reqp);
37940a05e705Slc kmem_free(pkt_descr, sizeof (ugen_isoc_pkt_descr_t) * n_pkt);
37950a05e705Slc
37960a05e705Slc epp->ep_isoc_info.isoc_pkt_descr = NULL;
37970a05e705Slc epp->ep_isoc_info.isoc_pkts_count = 0;
37980a05e705Slc epp->ep_isoc_info.isoc_pkts_length = 0;
37990a05e705Slc
38000a05e705Slc USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
38010a05e705Slc "isoc out xfer failed");
38020a05e705Slc
38030a05e705Slc bioerror(bp, EIO);
38040a05e705Slc } else {
38050a05e705Slc mutex_enter(&epp->ep_mutex);
38060a05e705Slc }
38070a05e705Slc *wait = (rval == USB_SUCCESS) ? B_TRUE : B_FALSE;
38080a05e705Slc
38090a05e705Slc done:
38100a05e705Slc USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
38110a05e705Slc "ugen_epx_isoc_OUT_req end: rval=%d bcount=%lu xfer_len=%d",
38120a05e705Slc rval, bp->b_bcount, pkts_len);
38130a05e705Slc
38140a05e705Slc return (rval);
38150a05e705Slc }
38160a05e705Slc
38170a05e705Slc
38180a05e705Slc /*
38190a05e705Slc * callback functions for isoc OUT pipe
38200a05e705Slc */
38210a05e705Slc static void
ugen_epx_isoc_OUT_req_cb(usb_pipe_handle_t ph,usb_isoc_req_t * reqp)38220a05e705Slc ugen_epx_isoc_OUT_req_cb(usb_pipe_handle_t ph, usb_isoc_req_t *reqp)
38230a05e705Slc {
38240a05e705Slc ugen_state_t *ugenp = (ugen_state_t *)reqp->isoc_client_private;
38250a05e705Slc ugen_ep_t *epp = (ugen_ep_t *)usb_pipe_get_private(ph);
38260a05e705Slc
38270a05e705Slc USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
38280a05e705Slc "ugen_epx_isoc_OUT_req_cb: ph=0x%p reqp=0x%p cr=%d cb=0x%x",
3829112116d8Sfb (void *)ph, (void *)reqp, reqp->isoc_completion_reason,
3830112116d8Sfb reqp->isoc_cb_flags);
38310a05e705Slc
38320a05e705Slc /* epp might be NULL if we are closing the pipe */
38330a05e705Slc if (epp) {
38340a05e705Slc ugen_isoc_pkt_info_t info;
38350a05e705Slc
38360a05e705Slc mutex_enter(&epp->ep_mutex);
38370a05e705Slc
38380a05e705Slc info = epp->ep_isoc_info;
38390a05e705Slc if (epp->ep_bp) {
38400a05e705Slc int len, i;
38410a05e705Slc int headlen;
38420a05e705Slc usb_isoc_pkt_descr_t *pktdesc;
38430a05e705Slc
38440a05e705Slc pktdesc = reqp->isoc_pkt_descr;
38450a05e705Slc headlen = info.isoc_pkts_count *
38460a05e705Slc sizeof (ugen_isoc_pkt_descr_t);
38470a05e705Slc
3848d29f5a71Szhigang lu - Sun Microsystems - Beijing China len = min(headlen + MBLKL(reqp->isoc_data),
3849d29f5a71Szhigang lu - Sun Microsystems - Beijing China epp->ep_bp->b_bcount);
38500a05e705Slc
38510a05e705Slc epp->ep_bp->b_resid = epp->ep_bp->b_bcount - len;
38520a05e705Slc
38530a05e705Slc
38540a05e705Slc switch (reqp->isoc_completion_reason) {
38550a05e705Slc case USB_CR_OK:
38560a05e705Slc
38570a05e705Slc epp->ep_lcmd_status = USB_LC_STAT_NOERROR;
38580a05e705Slc
38590a05e705Slc for (i = 0; i < reqp->isoc_pkts_count; i++) {
38600a05e705Slc pktdesc[i].isoc_pkt_status =
38610a05e705Slc ugen_cr2lcstat(pktdesc[i].
38620a05e705Slc isoc_pkt_status);
38630a05e705Slc }
38640a05e705Slc
38650a05e705Slc /* save the status info */
38660a05e705Slc bcopy(reqp->isoc_pkt_descr,
38670a05e705Slc info.isoc_pkt_descr,
38680a05e705Slc (sizeof (ugen_isoc_pkt_descr_t) *
38690a05e705Slc info.isoc_pkts_count));
38700a05e705Slc
38710a05e705Slc break;
38720a05e705Slc case USB_CR_PIPE_RESET:
38730a05e705Slc
38740a05e705Slc break;
38750a05e705Slc default:
38760a05e705Slc epp->ep_lcmd_status =
38770a05e705Slc ugen_cr2lcstat(
38780a05e705Slc reqp->isoc_completion_reason);
38790a05e705Slc bioerror(epp->ep_bp, EIO);
38800a05e705Slc }
38810a05e705Slc }
38820a05e705Slc epp->ep_done++;
38830a05e705Slc cv_signal(&epp->ep_wait_cv);
38840a05e705Slc mutex_exit(&epp->ep_mutex);
38850a05e705Slc }
38860a05e705Slc
38870a05e705Slc usb_free_isoc_req(reqp);
38880a05e705Slc }
38890a05e705Slc
38900a05e705Slc
38917c478bd9Sstevel@tonic-gate /*
38927c478bd9Sstevel@tonic-gate * Endpoint status node management
38937c478bd9Sstevel@tonic-gate *
38947c478bd9Sstevel@tonic-gate * open/close an endpoint status node.
38957c478bd9Sstevel@tonic-gate *
38967c478bd9Sstevel@tonic-gate * Return values: errno
38977c478bd9Sstevel@tonic-gate */
38987c478bd9Sstevel@tonic-gate static int
ugen_eps_open(ugen_state_t * ugenp,dev_t dev,int flag)38997c478bd9Sstevel@tonic-gate ugen_eps_open(ugen_state_t *ugenp, dev_t dev, int flag)
39007c478bd9Sstevel@tonic-gate {
39017c478bd9Sstevel@tonic-gate ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
39027c478bd9Sstevel@tonic-gate int rval = EBUSY;
39037c478bd9Sstevel@tonic-gate
39047c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
39057c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
39067c478bd9Sstevel@tonic-gate "ugen_eps_open: dev=0x%lx flag=0x%x state=0x%x",
39077c478bd9Sstevel@tonic-gate dev, flag, epp->ep_state);
39087c478bd9Sstevel@tonic-gate
39097c478bd9Sstevel@tonic-gate ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
39107c478bd9Sstevel@tonic-gate
39117c478bd9Sstevel@tonic-gate /* only one open at the time */
39127c478bd9Sstevel@tonic-gate if ((epp->ep_state & UGEN_EP_STATE_STAT_OPEN) == 0) {
39137c478bd9Sstevel@tonic-gate epp->ep_state |= UGEN_EP_STATE_STAT_OPEN;
39147c478bd9Sstevel@tonic-gate epp->ep_stat_oflag = flag;
39157c478bd9Sstevel@tonic-gate rval = 0;
39167c478bd9Sstevel@tonic-gate }
39177c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
39187c478bd9Sstevel@tonic-gate
39197c478bd9Sstevel@tonic-gate return (rval);
39207c478bd9Sstevel@tonic-gate }
39217c478bd9Sstevel@tonic-gate
39227c478bd9Sstevel@tonic-gate
39237c478bd9Sstevel@tonic-gate /*
39247c478bd9Sstevel@tonic-gate * close endpoint status
39257c478bd9Sstevel@tonic-gate */
39267c478bd9Sstevel@tonic-gate static void
ugen_eps_close(ugen_state_t * ugenp,dev_t dev,int flag)39277c478bd9Sstevel@tonic-gate ugen_eps_close(ugen_state_t *ugenp, dev_t dev, int flag)
39287c478bd9Sstevel@tonic-gate {
39297c478bd9Sstevel@tonic-gate ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, dev)];
39307c478bd9Sstevel@tonic-gate
39317c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
39327c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
39337c478bd9Sstevel@tonic-gate "ugen_eps_close: dev=0x%lx flag=0x%x state=0x%x",
39347c478bd9Sstevel@tonic-gate dev, flag, epp->ep_state);
39357c478bd9Sstevel@tonic-gate
39367c478bd9Sstevel@tonic-gate epp->ep_state &= ~(UGEN_EP_STATE_STAT_OPEN |
39370a05e705Slc UGEN_EP_STATE_INTR_IN_POLL_PENDING |
39380a05e705Slc UGEN_EP_STATE_ISOC_IN_POLL_PENDING);
39397c478bd9Sstevel@tonic-gate epp->ep_one_xfer = B_FALSE;
39407c478bd9Sstevel@tonic-gate
39417c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_STAT, ugenp->ug_log_hdl,
39427c478bd9Sstevel@tonic-gate "ugen_eps_close: state=0x%x", epp->ep_state);
39437c478bd9Sstevel@tonic-gate
39447c478bd9Sstevel@tonic-gate ASSERT(epp->ep_state & UGEN_EP_STATE_ACTIVE);
39457c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
39467c478bd9Sstevel@tonic-gate }
39477c478bd9Sstevel@tonic-gate
39487c478bd9Sstevel@tonic-gate
39497c478bd9Sstevel@tonic-gate /*
39507c478bd9Sstevel@tonic-gate * return status info
39517c478bd9Sstevel@tonic-gate *
39527c478bd9Sstevel@tonic-gate * Return values: errno
39537c478bd9Sstevel@tonic-gate */
39547c478bd9Sstevel@tonic-gate static int
ugen_eps_req(ugen_state_t * ugenp,struct buf * bp)39557c478bd9Sstevel@tonic-gate ugen_eps_req(ugen_state_t *ugenp, struct buf *bp)
39567c478bd9Sstevel@tonic-gate {
39577c478bd9Sstevel@tonic-gate ugen_ep_t *epp = &ugenp->ug_ep[UGEN_MINOR_EPIDX(ugenp, bp->b_edev)];
39587c478bd9Sstevel@tonic-gate
39597c478bd9Sstevel@tonic-gate mutex_enter(&epp->ep_mutex);
39607c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
39617c478bd9Sstevel@tonic-gate "ugen_eps_req: bp=0x%p lcmd_status=0x%x bcount=%lu",
3962112116d8Sfb (void *)bp, epp->ep_lcmd_status, bp->b_bcount);
39637c478bd9Sstevel@tonic-gate
39647c478bd9Sstevel@tonic-gate if (bp->b_flags & B_READ) {
39657c478bd9Sstevel@tonic-gate int len = min(sizeof (epp->ep_lcmd_status), bp->b_bcount);
39667c478bd9Sstevel@tonic-gate if (len) {
39677c478bd9Sstevel@tonic-gate bcopy(&epp->ep_lcmd_status, bp->b_un.b_addr, len);
39687c478bd9Sstevel@tonic-gate }
39697c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount - len;
39707c478bd9Sstevel@tonic-gate } else {
39717c478bd9Sstevel@tonic-gate USB_DPRINTF_L3(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
39727c478bd9Sstevel@tonic-gate "ugen_eps_req: control=0x%x",
39737c478bd9Sstevel@tonic-gate *((char *)(bp->b_un.b_addr)));
39747c478bd9Sstevel@tonic-gate
39757c478bd9Sstevel@tonic-gate if (epp->ep_state & UGEN_EP_STATE_XFER_OPEN) {
39767c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
39777c478bd9Sstevel@tonic-gate "ugen_eps_req: cannot change one xfer mode if "
39787c478bd9Sstevel@tonic-gate "endpoint is open");
39797c478bd9Sstevel@tonic-gate
39807c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
39817c478bd9Sstevel@tonic-gate
39827c478bd9Sstevel@tonic-gate return (EINVAL);
39837c478bd9Sstevel@tonic-gate }
39847c478bd9Sstevel@tonic-gate
39857c478bd9Sstevel@tonic-gate if ((epp->ep_descr.bmAttributes & USB_EP_ATTR_INTR) &&
39867c478bd9Sstevel@tonic-gate (epp->ep_descr.bEndpointAddress & USB_EP_DIR_IN)) {
39877c478bd9Sstevel@tonic-gate epp->ep_one_xfer = (*((char *)(bp->b_un.b_addr)) &
39887c478bd9Sstevel@tonic-gate USB_EP_INTR_ONE_XFER) ? B_TRUE : B_FALSE;
39897c478bd9Sstevel@tonic-gate } else {
39907c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
39917c478bd9Sstevel@tonic-gate "ugen_eps_req: not an interrupt endpoint");
39927c478bd9Sstevel@tonic-gate
39937c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
39947c478bd9Sstevel@tonic-gate
39957c478bd9Sstevel@tonic-gate return (EINVAL);
39967c478bd9Sstevel@tonic-gate }
39977c478bd9Sstevel@tonic-gate
39987c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount - 1;
39997c478bd9Sstevel@tonic-gate }
40007c478bd9Sstevel@tonic-gate mutex_exit(&epp->ep_mutex);
40017c478bd9Sstevel@tonic-gate
40027c478bd9Sstevel@tonic-gate return (0);
40037c478bd9Sstevel@tonic-gate }
40047c478bd9Sstevel@tonic-gate
40057c478bd9Sstevel@tonic-gate
40067c478bd9Sstevel@tonic-gate /*
40077c478bd9Sstevel@tonic-gate * device status node management
40087c478bd9Sstevel@tonic-gate */
40097c478bd9Sstevel@tonic-gate static int
ugen_ds_init(ugen_state_t * ugenp)40107c478bd9Sstevel@tonic-gate ugen_ds_init(ugen_state_t *ugenp)
40117c478bd9Sstevel@tonic-gate {
40127c478bd9Sstevel@tonic-gate cv_init(&ugenp->ug_ds.dev_wait_cv, NULL, CV_DRIVER, NULL);
40137c478bd9Sstevel@tonic-gate
40147c478bd9Sstevel@tonic-gate /* Create devstat minor node for this instance */
40157c478bd9Sstevel@tonic-gate if (ugen_ds_minor_nodes_create(ugenp) != USB_SUCCESS) {
40167c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
40177c478bd9Sstevel@tonic-gate "ugen_create_dev_stat_minor_nodes failed");
40187c478bd9Sstevel@tonic-gate
40197c478bd9Sstevel@tonic-gate return (USB_FAILURE);
40207c478bd9Sstevel@tonic-gate }
40217c478bd9Sstevel@tonic-gate
40227c478bd9Sstevel@tonic-gate
40237c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
40247c478bd9Sstevel@tonic-gate }
40257c478bd9Sstevel@tonic-gate
40267c478bd9Sstevel@tonic-gate
40277c478bd9Sstevel@tonic-gate static void
ugen_ds_destroy(ugen_state_t * ugenp)40287c478bd9Sstevel@tonic-gate ugen_ds_destroy(ugen_state_t *ugenp)
40297c478bd9Sstevel@tonic-gate {
40307c478bd9Sstevel@tonic-gate cv_destroy(&ugenp->ug_ds.dev_wait_cv);
40317c478bd9Sstevel@tonic-gate }
40327c478bd9Sstevel@tonic-gate
40337c478bd9Sstevel@tonic-gate
40347c478bd9Sstevel@tonic-gate /*
40357c478bd9Sstevel@tonic-gate * open devstat minor node
40367c478bd9Sstevel@tonic-gate *
40377c478bd9Sstevel@tonic-gate * Return values: errno
40387c478bd9Sstevel@tonic-gate */
40397c478bd9Sstevel@tonic-gate static int
ugen_ds_open(ugen_state_t * ugenp,dev_t dev,int flag)40407c478bd9Sstevel@tonic-gate ugen_ds_open(ugen_state_t *ugenp, dev_t dev, int flag)
40417c478bd9Sstevel@tonic-gate {
40427c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
40437c478bd9Sstevel@tonic-gate "ugen_ds_open: dev=0x%lx flag=0x%x", dev, flag);
40447c478bd9Sstevel@tonic-gate
40457c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
40467c478bd9Sstevel@tonic-gate if ((ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_ACTIVE) == 0) {
40477c478bd9Sstevel@tonic-gate /*
40487c478bd9Sstevel@tonic-gate * first read on device node should return status
40497c478bd9Sstevel@tonic-gate */
40507c478bd9Sstevel@tonic-gate ugenp->ug_ds.dev_stat |= UGEN_DEV_STATUS_CHANGED |
40510a05e705Slc UGEN_DEV_STATUS_ACTIVE;
40527c478bd9Sstevel@tonic-gate ugenp->ug_ds.dev_oflag = flag;
40537c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
40547c478bd9Sstevel@tonic-gate
40557c478bd9Sstevel@tonic-gate return (0);
40567c478bd9Sstevel@tonic-gate } else {
40577c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
40587c478bd9Sstevel@tonic-gate
40597c478bd9Sstevel@tonic-gate return (EBUSY);
40607c478bd9Sstevel@tonic-gate }
40617c478bd9Sstevel@tonic-gate }
40627c478bd9Sstevel@tonic-gate
40637c478bd9Sstevel@tonic-gate
40647c478bd9Sstevel@tonic-gate static void
ugen_ds_close(ugen_state_t * ugenp,dev_t dev,int flag)40657c478bd9Sstevel@tonic-gate ugen_ds_close(ugen_state_t *ugenp, dev_t dev, int flag)
40667c478bd9Sstevel@tonic-gate {
40677c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
40687c478bd9Sstevel@tonic-gate "ugen_ds_close: dev=0x%lx flag=0x%x", dev, flag);
40697c478bd9Sstevel@tonic-gate
40707c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
40717c478bd9Sstevel@tonic-gate ugenp->ug_ds.dev_stat = UGEN_DEV_STATUS_INACTIVE;
40727c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
40737c478bd9Sstevel@tonic-gate }
40747c478bd9Sstevel@tonic-gate
40757c478bd9Sstevel@tonic-gate
40767c478bd9Sstevel@tonic-gate /*
40777c478bd9Sstevel@tonic-gate * request for devstat
40787c478bd9Sstevel@tonic-gate *
40797c478bd9Sstevel@tonic-gate * Return values: errno
40807c478bd9Sstevel@tonic-gate */
40817c478bd9Sstevel@tonic-gate static int
ugen_ds_req(ugen_state_t * ugenp,struct buf * bp)40827c478bd9Sstevel@tonic-gate ugen_ds_req(ugen_state_t *ugenp, struct buf *bp)
40837c478bd9Sstevel@tonic-gate {
40847c478bd9Sstevel@tonic-gate int len = min(sizeof (ugenp->ug_ds.dev_state), bp->b_bcount);
40857c478bd9Sstevel@tonic-gate
40867c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
4087112116d8Sfb "ugen_ds_req: bp=0x%p", (void *)bp);
40887c478bd9Sstevel@tonic-gate
40897c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
40907c478bd9Sstevel@tonic-gate if ((ugenp->ug_ds.dev_oflag & (FNDELAY | FNONBLOCK)) == 0) {
40917c478bd9Sstevel@tonic-gate while ((ugenp->ug_ds.dev_stat &
40927c478bd9Sstevel@tonic-gate UGEN_DEV_STATUS_CHANGED) == 0) {
40937c478bd9Sstevel@tonic-gate if (cv_wait_sig(&ugenp->ug_ds.dev_wait_cv,
40947c478bd9Sstevel@tonic-gate &ugenp->ug_mutex) <= 0) {
40957c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
40967c478bd9Sstevel@tonic-gate
40977c478bd9Sstevel@tonic-gate return (EINTR);
40987c478bd9Sstevel@tonic-gate }
40997c478bd9Sstevel@tonic-gate }
41007c478bd9Sstevel@tonic-gate } else if ((ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_CHANGED) ==
41017c478bd9Sstevel@tonic-gate 0) {
41027c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount;
41037c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
41047c478bd9Sstevel@tonic-gate
41057c478bd9Sstevel@tonic-gate return (0);
41067c478bd9Sstevel@tonic-gate }
41077c478bd9Sstevel@tonic-gate
41087c478bd9Sstevel@tonic-gate ugenp->ug_ds.dev_stat &= ~UGEN_DEV_STATUS_CHANGED;
41097c478bd9Sstevel@tonic-gate switch (ugenp->ug_dev_state) {
41107c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE:
41117c478bd9Sstevel@tonic-gate ugenp->ug_ds.dev_state = USB_DEV_STAT_ONLINE;
41127c478bd9Sstevel@tonic-gate
41137c478bd9Sstevel@tonic-gate break;
41147c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED:
41157c478bd9Sstevel@tonic-gate ugenp->ug_ds.dev_state = USB_DEV_STAT_DISCONNECTED;
41167c478bd9Sstevel@tonic-gate
41177c478bd9Sstevel@tonic-gate break;
41187c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED:
41197c478bd9Sstevel@tonic-gate case USB_UGEN_DEV_UNAVAILABLE_RESUME:
41207c478bd9Sstevel@tonic-gate ugenp->ug_ds.dev_state = USB_DEV_STAT_RESUMED;
41217c478bd9Sstevel@tonic-gate
41227c478bd9Sstevel@tonic-gate break;
41237c478bd9Sstevel@tonic-gate case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
41247c478bd9Sstevel@tonic-gate default:
41257c478bd9Sstevel@tonic-gate ugenp->ug_ds.dev_state = USB_DEV_STAT_UNAVAILABLE;
41267c478bd9Sstevel@tonic-gate
41277c478bd9Sstevel@tonic-gate break;
41287c478bd9Sstevel@tonic-gate }
41297c478bd9Sstevel@tonic-gate
41307c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
41317c478bd9Sstevel@tonic-gate "ugen_ds_req: dev_state=0x%x dev_stat=0x%x",
41327c478bd9Sstevel@tonic-gate ugenp->ug_dev_state, ugenp->ug_ds.dev_stat);
41337c478bd9Sstevel@tonic-gate
41347c478bd9Sstevel@tonic-gate bcopy(&ugenp->ug_ds.dev_state, bp->b_un.b_addr, len);
41357c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount - len;
41367c478bd9Sstevel@tonic-gate
41377c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
41387c478bd9Sstevel@tonic-gate
41397c478bd9Sstevel@tonic-gate return (0);
41407c478bd9Sstevel@tonic-gate }
41417c478bd9Sstevel@tonic-gate
41427c478bd9Sstevel@tonic-gate
41437c478bd9Sstevel@tonic-gate static void
ugen_ds_change(ugen_state_t * ugenp)41447c478bd9Sstevel@tonic-gate ugen_ds_change(ugen_state_t *ugenp)
41457c478bd9Sstevel@tonic-gate {
41467c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
41477c478bd9Sstevel@tonic-gate "ugen_ds_change:");
41487c478bd9Sstevel@tonic-gate
41497c478bd9Sstevel@tonic-gate ugenp->ug_ds.dev_stat |= UGEN_DEV_STATUS_CHANGED;
41507c478bd9Sstevel@tonic-gate cv_signal(&ugenp->ug_ds.dev_wait_cv);
41517c478bd9Sstevel@tonic-gate }
41527c478bd9Sstevel@tonic-gate
41537c478bd9Sstevel@tonic-gate
41547c478bd9Sstevel@tonic-gate /*
41557c478bd9Sstevel@tonic-gate * poll management
41567c478bd9Sstevel@tonic-gate */
41577c478bd9Sstevel@tonic-gate static void
ugen_ds_poll_wakeup(ugen_state_t * ugenp)41587c478bd9Sstevel@tonic-gate ugen_ds_poll_wakeup(ugen_state_t *ugenp)
41597c478bd9Sstevel@tonic-gate {
41607c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_XFER, ugenp->ug_log_hdl,
41617c478bd9Sstevel@tonic-gate "ugen_ds_poll_wakeup:");
41627c478bd9Sstevel@tonic-gate
41637c478bd9Sstevel@tonic-gate if (ugenp->ug_ds.dev_stat & UGEN_DEV_STATUS_POLL_PENDING) {
41647c478bd9Sstevel@tonic-gate struct pollhead *phpp = &ugenp->ug_ds.dev_pollhead;
41657c478bd9Sstevel@tonic-gate ugenp->ug_ds.dev_stat &= ~UGEN_DEV_STATUS_POLL_PENDING;
41667c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
41677c478bd9Sstevel@tonic-gate pollwakeup(phpp, POLLIN);
41687c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
41697c478bd9Sstevel@tonic-gate }
41707c478bd9Sstevel@tonic-gate }
41717c478bd9Sstevel@tonic-gate
41727c478bd9Sstevel@tonic-gate
41737c478bd9Sstevel@tonic-gate /*
41747c478bd9Sstevel@tonic-gate * minor node management:
41757c478bd9Sstevel@tonic-gate */
41767c478bd9Sstevel@tonic-gate static int
ugen_ds_minor_nodes_create(ugen_state_t * ugenp)41777c478bd9Sstevel@tonic-gate ugen_ds_minor_nodes_create(ugen_state_t *ugenp)
41787c478bd9Sstevel@tonic-gate {
41797c478bd9Sstevel@tonic-gate char node_name[32];
41807c478bd9Sstevel@tonic-gate int vid = ugenp->ug_dev_data->dev_descr->idVendor;
41817c478bd9Sstevel@tonic-gate int pid = ugenp->ug_dev_data->dev_descr->idProduct;
41827c478bd9Sstevel@tonic-gate minor_t minor;
41837c478bd9Sstevel@tonic-gate int minor_index;
41847c478bd9Sstevel@tonic-gate int owns_device = (usb_owns_device(ugenp->ug_dip) ?
41850a05e705Slc UGEN_OWNS_DEVICE : 0);
41867c478bd9Sstevel@tonic-gate
41877c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
41887c478bd9Sstevel@tonic-gate "ugen_ds_minor_nodes_create: idx shift=%d inst shift=%d",
41897c478bd9Sstevel@tonic-gate UGEN_MINOR_IDX_SHIFT(ugenp),
41907c478bd9Sstevel@tonic-gate UGEN_MINOR_INSTANCE_SHIFT(ugenp));
41917c478bd9Sstevel@tonic-gate
41927c478bd9Sstevel@tonic-gate if (ugenp->ug_instance >= UGEN_MINOR_INSTANCE_LIMIT(ugenp)) {
41937c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
41947c478bd9Sstevel@tonic-gate "instance number too high (%d)", ugenp->ug_instance);
41957c478bd9Sstevel@tonic-gate
41967c478bd9Sstevel@tonic-gate return (USB_FAILURE);
41977c478bd9Sstevel@tonic-gate }
41987c478bd9Sstevel@tonic-gate
41997c478bd9Sstevel@tonic-gate /* create devstat minor node */
42007c478bd9Sstevel@tonic-gate if (owns_device) {
42017c478bd9Sstevel@tonic-gate (void) sprintf(node_name, "%x.%x.devstat", vid, pid);
42027c478bd9Sstevel@tonic-gate } else {
42037c478bd9Sstevel@tonic-gate (void) sprintf(node_name, "%x.%x.if%ddevstat", vid, pid,
42047c478bd9Sstevel@tonic-gate ugenp->ug_dev_data->dev_curr_if);
42057c478bd9Sstevel@tonic-gate }
42067c478bd9Sstevel@tonic-gate
42077c478bd9Sstevel@tonic-gate minor_index = ugen_minor_index_create(ugenp,
42087c478bd9Sstevel@tonic-gate (UGEN_MINOR_DEV_STAT_NODE | owns_device) <<
42097c478bd9Sstevel@tonic-gate UGEN_MINOR_IDX_SHIFT(ugenp));
42107c478bd9Sstevel@tonic-gate
42117c478bd9Sstevel@tonic-gate if (minor_index < 0) {
42127c478bd9Sstevel@tonic-gate USB_DPRINTF_L0(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
42137c478bd9Sstevel@tonic-gate "too many minor nodes");
42147c478bd9Sstevel@tonic-gate
42157c478bd9Sstevel@tonic-gate return (USB_FAILURE);
42167c478bd9Sstevel@tonic-gate }
42177c478bd9Sstevel@tonic-gate minor = (minor_index << UGEN_MINOR_IDX_SHIFT(ugenp)) |
42187c478bd9Sstevel@tonic-gate ugenp->ug_instance << UGEN_MINOR_INSTANCE_SHIFT(ugenp);
42197c478bd9Sstevel@tonic-gate
42207c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
42217c478bd9Sstevel@tonic-gate "minor=0x%x minor_index=%d name=%s",
42227c478bd9Sstevel@tonic-gate minor, minor_index, node_name);
42237c478bd9Sstevel@tonic-gate
42247c478bd9Sstevel@tonic-gate ASSERT(minor < L_MAXMIN);
42257c478bd9Sstevel@tonic-gate
42267c478bd9Sstevel@tonic-gate if ((ddi_create_minor_node(ugenp->ug_dip, node_name,
42277c478bd9Sstevel@tonic-gate S_IFCHR, minor, DDI_NT_UGEN, 0)) != DDI_SUCCESS) {
42287c478bd9Sstevel@tonic-gate
42297c478bd9Sstevel@tonic-gate return (USB_FAILURE);
42307c478bd9Sstevel@tonic-gate }
42317c478bd9Sstevel@tonic-gate
42327c478bd9Sstevel@tonic-gate ugen_store_devt(ugenp, minor);
42337c478bd9Sstevel@tonic-gate
42347c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
42357c478bd9Sstevel@tonic-gate }
42367c478bd9Sstevel@tonic-gate
42377c478bd9Sstevel@tonic-gate
42387c478bd9Sstevel@tonic-gate /*
42397c478bd9Sstevel@tonic-gate * utility functions:
42407c478bd9Sstevel@tonic-gate *
42417c478bd9Sstevel@tonic-gate * conversion from completion reason to USB_LC_STAT_*
42427c478bd9Sstevel@tonic-gate */
42437c478bd9Sstevel@tonic-gate static struct ugen_cr2lcstat_entry {
42447c478bd9Sstevel@tonic-gate int cr;
42457c478bd9Sstevel@tonic-gate int lcstat;
42467c478bd9Sstevel@tonic-gate } ugen_cr2lcstat_table[] = {
42477c478bd9Sstevel@tonic-gate { USB_CR_OK, USB_LC_STAT_NOERROR },
42487c478bd9Sstevel@tonic-gate { USB_CR_CRC, USB_LC_STAT_CRC },
42497c478bd9Sstevel@tonic-gate { USB_CR_BITSTUFFING, USB_LC_STAT_BITSTUFFING },
42507c478bd9Sstevel@tonic-gate { USB_CR_DATA_TOGGLE_MM, USB_LC_STAT_DATA_TOGGLE_MM },
42517c478bd9Sstevel@tonic-gate { USB_CR_STALL, USB_LC_STAT_STALL },
42527c478bd9Sstevel@tonic-gate { USB_CR_DEV_NOT_RESP, USB_LC_STAT_DEV_NOT_RESP },
42537c478bd9Sstevel@tonic-gate { USB_CR_PID_CHECKFAILURE, USB_LC_STAT_PID_CHECKFAILURE },
42547c478bd9Sstevel@tonic-gate { USB_CR_UNEXP_PID, USB_LC_STAT_UNEXP_PID },
42557c478bd9Sstevel@tonic-gate { USB_CR_DATA_OVERRUN, USB_LC_STAT_DATA_OVERRUN },
42567c478bd9Sstevel@tonic-gate { USB_CR_DATA_UNDERRUN, USB_LC_STAT_DATA_UNDERRUN },
42577c478bd9Sstevel@tonic-gate { USB_CR_BUFFER_OVERRUN, USB_LC_STAT_BUFFER_OVERRUN },
42587c478bd9Sstevel@tonic-gate { USB_CR_BUFFER_UNDERRUN, USB_LC_STAT_BUFFER_UNDERRUN },
42597c478bd9Sstevel@tonic-gate { USB_CR_TIMEOUT, USB_LC_STAT_TIMEOUT },
42607c478bd9Sstevel@tonic-gate { USB_CR_NOT_ACCESSED, USB_LC_STAT_NOT_ACCESSED },
42617c478bd9Sstevel@tonic-gate { USB_CR_NO_RESOURCES, USB_LC_STAT_NO_BANDWIDTH },
42627c478bd9Sstevel@tonic-gate { USB_CR_UNSPECIFIED_ERR, USB_LC_STAT_UNSPECIFIED_ERR },
42637c478bd9Sstevel@tonic-gate { USB_CR_STOPPED_POLLING, USB_LC_STAT_HW_ERR },
42647c478bd9Sstevel@tonic-gate { USB_CR_PIPE_CLOSING, USB_LC_STAT_UNSPECIFIED_ERR },
42657c478bd9Sstevel@tonic-gate { USB_CR_PIPE_RESET, USB_LC_STAT_UNSPECIFIED_ERR },
42667c478bd9Sstevel@tonic-gate { USB_CR_NOT_SUPPORTED, USB_LC_STAT_UNSPECIFIED_ERR },
42677c478bd9Sstevel@tonic-gate { USB_CR_FLUSHED, USB_LC_STAT_UNSPECIFIED_ERR }
42687c478bd9Sstevel@tonic-gate };
42697c478bd9Sstevel@tonic-gate
42707c478bd9Sstevel@tonic-gate #define UGEN_CR2LCSTAT_TABLE_SIZE (sizeof (ugen_cr2lcstat_table) / \
42717c478bd9Sstevel@tonic-gate sizeof (struct ugen_cr2lcstat_entry))
42727c478bd9Sstevel@tonic-gate static int
ugen_cr2lcstat(int cr)42737c478bd9Sstevel@tonic-gate ugen_cr2lcstat(int cr)
42747c478bd9Sstevel@tonic-gate {
42757c478bd9Sstevel@tonic-gate int i;
42767c478bd9Sstevel@tonic-gate
42777c478bd9Sstevel@tonic-gate for (i = 0; i < UGEN_CR2LCSTAT_TABLE_SIZE; i++) {
42787c478bd9Sstevel@tonic-gate if (ugen_cr2lcstat_table[i].cr == cr) {
42797c478bd9Sstevel@tonic-gate
42807c478bd9Sstevel@tonic-gate return (ugen_cr2lcstat_table[i].lcstat);
42817c478bd9Sstevel@tonic-gate }
42827c478bd9Sstevel@tonic-gate }
42837c478bd9Sstevel@tonic-gate
42847c478bd9Sstevel@tonic-gate return (USB_LC_STAT_UNSPECIFIED_ERR);
42857c478bd9Sstevel@tonic-gate }
42867c478bd9Sstevel@tonic-gate
42877c478bd9Sstevel@tonic-gate
42887c478bd9Sstevel@tonic-gate /*
42897c478bd9Sstevel@tonic-gate * create and lookup minor index
42907c478bd9Sstevel@tonic-gate */
42917c478bd9Sstevel@tonic-gate static int
ugen_minor_index_create(ugen_state_t * ugenp,ugen_minor_t minor)42927c478bd9Sstevel@tonic-gate ugen_minor_index_create(ugen_state_t *ugenp, ugen_minor_t minor)
42937c478bd9Sstevel@tonic-gate {
42947c478bd9Sstevel@tonic-gate int i;
42957c478bd9Sstevel@tonic-gate
42967c478bd9Sstevel@tonic-gate /* check if already in the table */
42977c478bd9Sstevel@tonic-gate for (i = 1; i < ugenp->ug_minor_node_table_index; i++) {
42987c478bd9Sstevel@tonic-gate if (ugenp->ug_minor_node_table[i] == minor) {
42997c478bd9Sstevel@tonic-gate
43007c478bd9Sstevel@tonic-gate return (-1);
43017c478bd9Sstevel@tonic-gate }
43027c478bd9Sstevel@tonic-gate }
43037c478bd9Sstevel@tonic-gate if (ugenp->ug_minor_node_table_index <
43047c478bd9Sstevel@tonic-gate (ugenp->ug_minor_node_table_size/sizeof (ugen_minor_t))) {
43057c478bd9Sstevel@tonic-gate ugenp->ug_minor_node_table[ugenp->
43060a05e705Slc ug_minor_node_table_index] = minor;
43077c478bd9Sstevel@tonic-gate
43087c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_ATTA, ugenp->ug_log_hdl,
43097c478bd9Sstevel@tonic-gate "ugen_minor_index_create: %d: 0x%lx",
43107c478bd9Sstevel@tonic-gate ugenp->ug_minor_node_table_index,
43118793b36bSNick Todd (unsigned long)minor);
43127c478bd9Sstevel@tonic-gate
43137c478bd9Sstevel@tonic-gate return (ugenp->ug_minor_node_table_index++);
43147c478bd9Sstevel@tonic-gate } else {
43157c478bd9Sstevel@tonic-gate
43167c478bd9Sstevel@tonic-gate return (-1);
43177c478bd9Sstevel@tonic-gate }
43187c478bd9Sstevel@tonic-gate }
43197c478bd9Sstevel@tonic-gate
43207c478bd9Sstevel@tonic-gate
43217c478bd9Sstevel@tonic-gate static ugen_minor_t
ugen_devt2minor(ugen_state_t * ugenp,dev_t dev)43227c478bd9Sstevel@tonic-gate ugen_devt2minor(ugen_state_t *ugenp, dev_t dev)
43237c478bd9Sstevel@tonic-gate {
43247c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
4325112116d8Sfb "ugen_devt2minor: minorindex=%lu, minor=0x%" PRIx64,
43267c478bd9Sstevel@tonic-gate UGEN_MINOR_GET_IDX(ugenp, dev),
43277c478bd9Sstevel@tonic-gate ugenp->ug_minor_node_table[UGEN_MINOR_GET_IDX(ugenp, dev)]);
43287c478bd9Sstevel@tonic-gate
43297c478bd9Sstevel@tonic-gate ASSERT(UGEN_MINOR_GET_IDX(ugenp, dev) <
43300a05e705Slc ugenp->ug_minor_node_table_index);
43317c478bd9Sstevel@tonic-gate
43327c478bd9Sstevel@tonic-gate return (ugenp->ug_minor_node_table[UGEN_MINOR_GET_IDX(ugenp, dev)]);
43337c478bd9Sstevel@tonic-gate }
43347c478bd9Sstevel@tonic-gate
43357c478bd9Sstevel@tonic-gate
433696603521Sfrits static int
ugen_is_valid_minor_node(ugen_state_t * ugenp,dev_t dev)433796603521Sfrits ugen_is_valid_minor_node(ugen_state_t *ugenp, dev_t dev)
433896603521Sfrits {
433996603521Sfrits int idx = UGEN_MINOR_GET_IDX(ugenp, dev);
434096603521Sfrits
434196603521Sfrits if ((idx < ugenp->ug_minor_node_table_index) &&
434296603521Sfrits (idx > 0)) {
434396603521Sfrits
434496603521Sfrits return (USB_SUCCESS);
434596603521Sfrits }
434696603521Sfrits USB_DPRINTF_L2(UGEN_PRINT_CBOPS, ugenp->ug_log_hdl,
434796603521Sfrits "ugen_is_valid_minor_node: invalid minorindex=%d", idx);
434896603521Sfrits
434996603521Sfrits return (USB_FAILURE);
435096603521Sfrits }
435196603521Sfrits
435296603521Sfrits
43537c478bd9Sstevel@tonic-gate static void
ugen_minor_node_table_create(ugen_state_t * ugenp)43547c478bd9Sstevel@tonic-gate ugen_minor_node_table_create(ugen_state_t *ugenp)
43557c478bd9Sstevel@tonic-gate {
43567c478bd9Sstevel@tonic-gate size_t size = sizeof (ugen_minor_t) * UGEN_MINOR_IDX_LIMIT(ugenp);
43577c478bd9Sstevel@tonic-gate
43587c478bd9Sstevel@tonic-gate /* allocate the max table size needed, we reduce later */
43597c478bd9Sstevel@tonic-gate ugenp->ug_minor_node_table = kmem_zalloc(size, KM_SLEEP);
43607c478bd9Sstevel@tonic-gate ugenp->ug_minor_node_table_size = size;
43617c478bd9Sstevel@tonic-gate ugenp->ug_minor_node_table_index = 1;
43627c478bd9Sstevel@tonic-gate }
43637c478bd9Sstevel@tonic-gate
43647c478bd9Sstevel@tonic-gate
43657c478bd9Sstevel@tonic-gate static void
ugen_minor_node_table_shrink(ugen_state_t * ugenp)43667c478bd9Sstevel@tonic-gate ugen_minor_node_table_shrink(ugen_state_t *ugenp)
43677c478bd9Sstevel@tonic-gate {
43687c478bd9Sstevel@tonic-gate /* reduce the table size to save some memory */
43697c478bd9Sstevel@tonic-gate if (ugenp->ug_minor_node_table_index < UGEN_MINOR_IDX_LIMIT(ugenp)) {
43707c478bd9Sstevel@tonic-gate size_t newsize = sizeof (ugen_minor_t) *
43710a05e705Slc ugenp->ug_minor_node_table_index;
43727c478bd9Sstevel@tonic-gate ugen_minor_t *buf = kmem_zalloc(newsize, KM_SLEEP);
43737c478bd9Sstevel@tonic-gate
43747c478bd9Sstevel@tonic-gate bcopy(ugenp->ug_minor_node_table, buf, newsize);
43757c478bd9Sstevel@tonic-gate kmem_free(ugenp->ug_minor_node_table,
43760a05e705Slc ugenp->ug_minor_node_table_size);
43777c478bd9Sstevel@tonic-gate ugenp->ug_minor_node_table = buf;
43787c478bd9Sstevel@tonic-gate ugenp->ug_minor_node_table_size = newsize;
43797c478bd9Sstevel@tonic-gate }
43807c478bd9Sstevel@tonic-gate }
43817c478bd9Sstevel@tonic-gate
43827c478bd9Sstevel@tonic-gate
43837c478bd9Sstevel@tonic-gate static void
ugen_minor_node_table_destroy(ugen_state_t * ugenp)43847c478bd9Sstevel@tonic-gate ugen_minor_node_table_destroy(ugen_state_t *ugenp)
43857c478bd9Sstevel@tonic-gate {
43867c478bd9Sstevel@tonic-gate if (ugenp->ug_minor_node_table) {
43877c478bd9Sstevel@tonic-gate kmem_free(ugenp->ug_minor_node_table,
43880a05e705Slc ugenp->ug_minor_node_table_size);
43897c478bd9Sstevel@tonic-gate }
43907c478bd9Sstevel@tonic-gate }
43917c478bd9Sstevel@tonic-gate
43927c478bd9Sstevel@tonic-gate
43937c478bd9Sstevel@tonic-gate static void
ugen_check_mask(uint_t mask,uint_t * shift,uint_t * limit)43947c478bd9Sstevel@tonic-gate ugen_check_mask(uint_t mask, uint_t *shift, uint_t *limit)
43957c478bd9Sstevel@tonic-gate {
43967c478bd9Sstevel@tonic-gate uint_t i, j;
43977c478bd9Sstevel@tonic-gate
43987c478bd9Sstevel@tonic-gate for (i = 0; i < UGEN_MINOR_NODE_SIZE; i++) {
43997c478bd9Sstevel@tonic-gate if ((1 << i) & mask) {
44007c478bd9Sstevel@tonic-gate
44017c478bd9Sstevel@tonic-gate break;
44027c478bd9Sstevel@tonic-gate }
44037c478bd9Sstevel@tonic-gate }
44047c478bd9Sstevel@tonic-gate
44057c478bd9Sstevel@tonic-gate for (j = i; j < UGEN_MINOR_NODE_SIZE; j++) {
44067c478bd9Sstevel@tonic-gate if (((1 << j) & mask) == 0) {
44077c478bd9Sstevel@tonic-gate
44087c478bd9Sstevel@tonic-gate break;
44097c478bd9Sstevel@tonic-gate }
44107c478bd9Sstevel@tonic-gate }
44117c478bd9Sstevel@tonic-gate
44127c478bd9Sstevel@tonic-gate *limit = (i == j) ? 0 : 1 << (j - i);
44137c478bd9Sstevel@tonic-gate *shift = i;
44147c478bd9Sstevel@tonic-gate }
44157c478bd9Sstevel@tonic-gate
44167c478bd9Sstevel@tonic-gate
44177c478bd9Sstevel@tonic-gate
44187c478bd9Sstevel@tonic-gate /*
44197c478bd9Sstevel@tonic-gate * power management:
44207c478bd9Sstevel@tonic-gate *
44217c478bd9Sstevel@tonic-gate * ugen_pm_init:
44227c478bd9Sstevel@tonic-gate * Initialize power management and remote wakeup functionality.
44237c478bd9Sstevel@tonic-gate * No mutex is necessary in this function as it's called only by attach.
44247c478bd9Sstevel@tonic-gate */
44257c478bd9Sstevel@tonic-gate static void
ugen_pm_init(ugen_state_t * ugenp)44267c478bd9Sstevel@tonic-gate ugen_pm_init(ugen_state_t *ugenp)
44277c478bd9Sstevel@tonic-gate {
44287c478bd9Sstevel@tonic-gate dev_info_t *dip = ugenp->ug_dip;
44297c478bd9Sstevel@tonic-gate ugen_power_t *ugenpm;
44307c478bd9Sstevel@tonic-gate
44317c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
44327c478bd9Sstevel@tonic-gate "ugen_pm_init:");
44337c478bd9Sstevel@tonic-gate
44347c478bd9Sstevel@tonic-gate /* Allocate the state structure */
44357c478bd9Sstevel@tonic-gate ugenpm = kmem_zalloc(sizeof (ugen_power_t), KM_SLEEP);
44367c478bd9Sstevel@tonic-gate
44377c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
44387c478bd9Sstevel@tonic-gate ugenp->ug_pm = ugenpm;
44397c478bd9Sstevel@tonic-gate ugenpm->pwr_wakeup_enabled = B_FALSE;
44407c478bd9Sstevel@tonic-gate ugenpm->pwr_current = USB_DEV_OS_FULL_PWR;
44417c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
44427c478bd9Sstevel@tonic-gate
44437c478bd9Sstevel@tonic-gate /*
44447c478bd9Sstevel@tonic-gate * If remote wakeup is not available you may not want to do
44457c478bd9Sstevel@tonic-gate * power management.
44467c478bd9Sstevel@tonic-gate */
44477c478bd9Sstevel@tonic-gate if (ugen_enable_pm || usb_handle_remote_wakeup(dip,
44487c478bd9Sstevel@tonic-gate USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
44497c478bd9Sstevel@tonic-gate if (usb_create_pm_components(dip,
44507c478bd9Sstevel@tonic-gate &ugenpm->pwr_states) == USB_SUCCESS) {
44517c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_PM,
44527c478bd9Sstevel@tonic-gate ugenp->ug_log_hdl,
44537c478bd9Sstevel@tonic-gate "ugen_pm_init: "
44547c478bd9Sstevel@tonic-gate "created PM components");
44557c478bd9Sstevel@tonic-gate
44567c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
44577c478bd9Sstevel@tonic-gate ugenpm->pwr_wakeup_enabled = B_TRUE;
44587c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
44597c478bd9Sstevel@tonic-gate
44607c478bd9Sstevel@tonic-gate if (pm_raise_power(dip, 0,
44617c478bd9Sstevel@tonic-gate USB_DEV_OS_FULL_PWR) != DDI_SUCCESS) {
44627c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_PM,
44637c478bd9Sstevel@tonic-gate ugenp->ug_log_hdl,
44647c478bd9Sstevel@tonic-gate "ugen_pm_init: "
44657c478bd9Sstevel@tonic-gate "raising power failed");
44667c478bd9Sstevel@tonic-gate }
44677c478bd9Sstevel@tonic-gate } else {
44687c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_PM,
44697c478bd9Sstevel@tonic-gate ugenp->ug_log_hdl,
44707c478bd9Sstevel@tonic-gate "ugen_pm_init: "
44717c478bd9Sstevel@tonic-gate "create_pm_comps failed");
44727c478bd9Sstevel@tonic-gate }
44737c478bd9Sstevel@tonic-gate } else {
44747c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_PM,
44757c478bd9Sstevel@tonic-gate ugenp->ug_log_hdl, "ugen_pm_init: "
44767c478bd9Sstevel@tonic-gate "failure enabling remote wakeup");
44777c478bd9Sstevel@tonic-gate }
44787c478bd9Sstevel@tonic-gate
44797c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
44807c478bd9Sstevel@tonic-gate "ugen_pm_init: end");
44817c478bd9Sstevel@tonic-gate }
44827c478bd9Sstevel@tonic-gate
44837c478bd9Sstevel@tonic-gate
44847c478bd9Sstevel@tonic-gate /*
44857c478bd9Sstevel@tonic-gate * ugen_pm_destroy:
44867c478bd9Sstevel@tonic-gate * Shut down and destroy power management and remote wakeup functionality.
44877c478bd9Sstevel@tonic-gate */
44887c478bd9Sstevel@tonic-gate static void
ugen_pm_destroy(ugen_state_t * ugenp)44897c478bd9Sstevel@tonic-gate ugen_pm_destroy(ugen_state_t *ugenp)
44907c478bd9Sstevel@tonic-gate {
44917c478bd9Sstevel@tonic-gate dev_info_t *dip = ugenp->ug_dip;
44927c478bd9Sstevel@tonic-gate
44937c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
44947c478bd9Sstevel@tonic-gate "ugen_pm_destroy:");
44957c478bd9Sstevel@tonic-gate
44967c478bd9Sstevel@tonic-gate if (ugenp->ug_pm) {
44977c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
44987c478bd9Sstevel@tonic-gate ugen_pm_busy_component(ugenp);
44997c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
45007c478bd9Sstevel@tonic-gate
45017c478bd9Sstevel@tonic-gate if ((ugenp->ug_pm->pwr_wakeup_enabled) &&
45027c478bd9Sstevel@tonic-gate (ugenp->ug_dev_state != USB_DEV_DISCONNECTED)) {
45037c478bd9Sstevel@tonic-gate int rval;
45047c478bd9Sstevel@tonic-gate
45057c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
45067c478bd9Sstevel@tonic-gate (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
45077c478bd9Sstevel@tonic-gate
45087c478bd9Sstevel@tonic-gate if ((rval = usb_handle_remote_wakeup(dip,
45097c478bd9Sstevel@tonic-gate USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) {
45107c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_PM,
45117c478bd9Sstevel@tonic-gate ugenp->ug_log_hdl, "ugen_pm_destroy: "
45127c478bd9Sstevel@tonic-gate "disabling rmt wakeup: rval=%d", rval);
45137c478bd9Sstevel@tonic-gate }
45147c478bd9Sstevel@tonic-gate /*
45157c478bd9Sstevel@tonic-gate * Since remote wakeup is disabled now,
45167c478bd9Sstevel@tonic-gate * no one can raise power
45177c478bd9Sstevel@tonic-gate * and get to device once power is lowered here.
45187c478bd9Sstevel@tonic-gate */
45197c478bd9Sstevel@tonic-gate } else {
45207c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
45217c478bd9Sstevel@tonic-gate }
45227c478bd9Sstevel@tonic-gate (void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF);
45237c478bd9Sstevel@tonic-gate ugen_pm_idle_component(ugenp);
45247c478bd9Sstevel@tonic-gate
45257c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
45267c478bd9Sstevel@tonic-gate kmem_free(ugenp->ug_pm, sizeof (ugen_power_t));
45277c478bd9Sstevel@tonic-gate ugenp->ug_pm = NULL;
45287c478bd9Sstevel@tonic-gate }
45297c478bd9Sstevel@tonic-gate }
45307c478bd9Sstevel@tonic-gate
45317c478bd9Sstevel@tonic-gate
45327c478bd9Sstevel@tonic-gate /*
45337c478bd9Sstevel@tonic-gate * ugen_power :
45347c478bd9Sstevel@tonic-gate * Power entry point, the workhorse behind pm_raise_power, pm_lower_power,
45357c478bd9Sstevel@tonic-gate * usb_req_raise_power and usb_req_lower_power.
45367c478bd9Sstevel@tonic-gate */
45377c478bd9Sstevel@tonic-gate /*ARGSUSED*/
45387c478bd9Sstevel@tonic-gate int
usb_ugen_power(usb_ugen_hdl_t usb_ugen_hdl,int comp,int level)45397c478bd9Sstevel@tonic-gate usb_ugen_power(usb_ugen_hdl_t usb_ugen_hdl, int comp, int level)
45407c478bd9Sstevel@tonic-gate {
45417c478bd9Sstevel@tonic-gate ugen_power_t *pm;
45427c478bd9Sstevel@tonic-gate int rval = USB_FAILURE;
45437c478bd9Sstevel@tonic-gate usb_ugen_hdl_impl_t *usb_ugen_hdl_impl =
45440a05e705Slc (usb_ugen_hdl_impl_t *)usb_ugen_hdl;
45457c478bd9Sstevel@tonic-gate ugen_state_t *ugenp;
45467c478bd9Sstevel@tonic-gate dev_info_t *dip;
45477c478bd9Sstevel@tonic-gate
45487c478bd9Sstevel@tonic-gate if (usb_ugen_hdl == NULL) {
45497c478bd9Sstevel@tonic-gate
45507c478bd9Sstevel@tonic-gate return (USB_FAILURE);
45517c478bd9Sstevel@tonic-gate }
45527c478bd9Sstevel@tonic-gate
45537c478bd9Sstevel@tonic-gate ugenp = usb_ugen_hdl_impl->hdl_ugenp;
45547c478bd9Sstevel@tonic-gate dip = ugenp->ug_dip;
45557c478bd9Sstevel@tonic-gate
45567c478bd9Sstevel@tonic-gate if (ugenp->ug_pm == NULL) {
45577c478bd9Sstevel@tonic-gate
45587c478bd9Sstevel@tonic-gate return (USB_SUCCESS);
45597c478bd9Sstevel@tonic-gate }
45607c478bd9Sstevel@tonic-gate
45617c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_PM, ugenp->ug_log_hdl,
45627c478bd9Sstevel@tonic-gate "usb_ugen_power: level=%d", level);
45637c478bd9Sstevel@tonic-gate
45647c478bd9Sstevel@tonic-gate (void) usb_serialize_access(ugenp->ug_ser_cookie,
45650a05e705Slc USB_WAIT, 0);
45667c478bd9Sstevel@tonic-gate /*
45677c478bd9Sstevel@tonic-gate * If we are disconnected/suspended, return success. Note that if we
45687c478bd9Sstevel@tonic-gate * return failure, bringing down the system will hang when
45697c478bd9Sstevel@tonic-gate * PM tries to power up all devices
45707c478bd9Sstevel@tonic-gate */
45717c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
45727c478bd9Sstevel@tonic-gate switch (ugenp->ug_dev_state) {
45737c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE:
45747c478bd9Sstevel@tonic-gate
45757c478bd9Sstevel@tonic-gate break;
45767c478bd9Sstevel@tonic-gate case USB_DEV_DISCONNECTED:
45777c478bd9Sstevel@tonic-gate case USB_DEV_SUSPENDED:
45787c478bd9Sstevel@tonic-gate case USB_UGEN_DEV_UNAVAILABLE_RESUME:
45797c478bd9Sstevel@tonic-gate case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
45807c478bd9Sstevel@tonic-gate default:
45817c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
45827c478bd9Sstevel@tonic-gate "ugen_power: disconnected/suspended "
45837c478bd9Sstevel@tonic-gate "dev_state=%d", ugenp->ug_dev_state);
45847c478bd9Sstevel@tonic-gate rval = USB_SUCCESS;
45857c478bd9Sstevel@tonic-gate
45867c478bd9Sstevel@tonic-gate goto done;
45877c478bd9Sstevel@tonic-gate }
45887c478bd9Sstevel@tonic-gate
45897c478bd9Sstevel@tonic-gate pm = ugenp->ug_pm;
45907c478bd9Sstevel@tonic-gate
45917c478bd9Sstevel@tonic-gate /* Check if we are transitioning to a legal power level */
45927c478bd9Sstevel@tonic-gate if (USB_DEV_PWRSTATE_OK(pm->pwr_states, level)) {
45937c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
45947c478bd9Sstevel@tonic-gate "ugen_power: illegal power level=%d "
45957c478bd9Sstevel@tonic-gate "pwr_states: 0x%x", level, pm->pwr_states);
45967c478bd9Sstevel@tonic-gate
45977c478bd9Sstevel@tonic-gate goto done;
45987c478bd9Sstevel@tonic-gate }
45997c478bd9Sstevel@tonic-gate
46007c478bd9Sstevel@tonic-gate switch (level) {
46017c478bd9Sstevel@tonic-gate case USB_DEV_OS_PWR_OFF :
46027c478bd9Sstevel@tonic-gate switch (ugenp->ug_dev_state) {
46037c478bd9Sstevel@tonic-gate case USB_DEV_ONLINE:
46047c478bd9Sstevel@tonic-gate /* Deny the powerdown request if the device is busy */
46057c478bd9Sstevel@tonic-gate if (ugenp->ug_pm->pwr_busy != 0) {
46067c478bd9Sstevel@tonic-gate
46077c478bd9Sstevel@tonic-gate break;
46087c478bd9Sstevel@tonic-gate }
46097c478bd9Sstevel@tonic-gate ASSERT(ugenp->ug_open_count == 0);
46107c478bd9Sstevel@tonic-gate ASSERT(ugenp->ug_pending_cmds == 0);
46117c478bd9Sstevel@tonic-gate ugenp->ug_pm->pwr_current = USB_DEV_OS_PWR_OFF;
46127c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
46137c478bd9Sstevel@tonic-gate
46147c478bd9Sstevel@tonic-gate /* Issue USB D3 command to the device here */
46157c478bd9Sstevel@tonic-gate rval = usb_set_device_pwrlvl3(dip);
46167c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
46177c478bd9Sstevel@tonic-gate
46187c478bd9Sstevel@tonic-gate break;
46197c478bd9Sstevel@tonic-gate default:
46207c478bd9Sstevel@tonic-gate rval = USB_SUCCESS;
46217c478bd9Sstevel@tonic-gate
46227c478bd9Sstevel@tonic-gate break;
46237c478bd9Sstevel@tonic-gate }
46247c478bd9Sstevel@tonic-gate break;
46257c478bd9Sstevel@tonic-gate case USB_DEV_OS_FULL_PWR :
46267c478bd9Sstevel@tonic-gate /*
46277c478bd9Sstevel@tonic-gate * PM framework tries to put us in full power during system
46287c478bd9Sstevel@tonic-gate * shutdown.
46297c478bd9Sstevel@tonic-gate */
46307c478bd9Sstevel@tonic-gate switch (ugenp->ug_dev_state) {
46317c478bd9Sstevel@tonic-gate case USB_UGEN_DEV_UNAVAILABLE_RESUME:
46327c478bd9Sstevel@tonic-gate case USB_UGEN_DEV_UNAVAILABLE_RECONNECT:
46337c478bd9Sstevel@tonic-gate
46347c478bd9Sstevel@tonic-gate break;
46357c478bd9Sstevel@tonic-gate default:
46367c478bd9Sstevel@tonic-gate ugenp->ug_dev_state = USB_DEV_ONLINE;
46377c478bd9Sstevel@tonic-gate
46387c478bd9Sstevel@tonic-gate /* wakeup devstat reads and polls */
46397c478bd9Sstevel@tonic-gate ugen_ds_change(ugenp);
46407c478bd9Sstevel@tonic-gate ugen_ds_poll_wakeup(ugenp);
46417c478bd9Sstevel@tonic-gate
46427c478bd9Sstevel@tonic-gate break;
46437c478bd9Sstevel@tonic-gate }
46447c478bd9Sstevel@tonic-gate ugenp->ug_pm->pwr_current = USB_DEV_OS_FULL_PWR;
46457c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
46467c478bd9Sstevel@tonic-gate rval = usb_set_device_pwrlvl0(dip);
46477c478bd9Sstevel@tonic-gate mutex_enter(&ugenp->ug_mutex);
46487c478bd9Sstevel@tonic-gate
46497c478bd9Sstevel@tonic-gate break;
46507c478bd9Sstevel@tonic-gate default:
46517c478bd9Sstevel@tonic-gate /* Levels 1 and 2 are not supported to keep it simple. */
46527c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_PM, ugenp->ug_log_hdl,
46537c478bd9Sstevel@tonic-gate "ugen_power: power level %d not supported", level);
46547c478bd9Sstevel@tonic-gate
46557c478bd9Sstevel@tonic-gate break;
46567c478bd9Sstevel@tonic-gate }
46577c478bd9Sstevel@tonic-gate done:
46587c478bd9Sstevel@tonic-gate mutex_exit(&ugenp->ug_mutex);
46597c478bd9Sstevel@tonic-gate usb_release_access(ugenp->ug_ser_cookie);
46607c478bd9Sstevel@tonic-gate
46617c478bd9Sstevel@tonic-gate return (rval);
46627c478bd9Sstevel@tonic-gate }
46637c478bd9Sstevel@tonic-gate
46647c478bd9Sstevel@tonic-gate
46657c478bd9Sstevel@tonic-gate static void
ugen_pm_busy_component(ugen_state_t * ugen_statep)46667c478bd9Sstevel@tonic-gate ugen_pm_busy_component(ugen_state_t *ugen_statep)
46677c478bd9Sstevel@tonic-gate {
46687c478bd9Sstevel@tonic-gate ASSERT(!mutex_owned(&ugen_statep->ug_mutex));
46697c478bd9Sstevel@tonic-gate
46707c478bd9Sstevel@tonic-gate if (ugen_statep->ug_pm != NULL) {
46717c478bd9Sstevel@tonic-gate mutex_enter(&ugen_statep->ug_mutex);
46727c478bd9Sstevel@tonic-gate ugen_statep->ug_pm->pwr_busy++;
46737c478bd9Sstevel@tonic-gate
46747c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
46757c478bd9Sstevel@tonic-gate "ugen_pm_busy_component: %d", ugen_statep->ug_pm->pwr_busy);
46767c478bd9Sstevel@tonic-gate
46777c478bd9Sstevel@tonic-gate mutex_exit(&ugen_statep->ug_mutex);
46787c478bd9Sstevel@tonic-gate if (pm_busy_component(ugen_statep->ug_dip, 0) != DDI_SUCCESS) {
46797c478bd9Sstevel@tonic-gate mutex_enter(&ugen_statep->ug_mutex);
46807c478bd9Sstevel@tonic-gate ugen_statep->ug_pm->pwr_busy--;
46817c478bd9Sstevel@tonic-gate
46827c478bd9Sstevel@tonic-gate USB_DPRINTF_L2(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
46837c478bd9Sstevel@tonic-gate "ugen_pm_busy_component failed: %d",
46847c478bd9Sstevel@tonic-gate ugen_statep->ug_pm->pwr_busy);
46857c478bd9Sstevel@tonic-gate
46867c478bd9Sstevel@tonic-gate mutex_exit(&ugen_statep->ug_mutex);
46877c478bd9Sstevel@tonic-gate }
46887c478bd9Sstevel@tonic-gate }
46897c478bd9Sstevel@tonic-gate }
46907c478bd9Sstevel@tonic-gate
46917c478bd9Sstevel@tonic-gate
46927c478bd9Sstevel@tonic-gate static void
ugen_pm_idle_component(ugen_state_t * ugen_statep)46937c478bd9Sstevel@tonic-gate ugen_pm_idle_component(ugen_state_t *ugen_statep)
46947c478bd9Sstevel@tonic-gate {
46957c478bd9Sstevel@tonic-gate ASSERT(!mutex_owned(&ugen_statep->ug_mutex));
46967c478bd9Sstevel@tonic-gate
46977c478bd9Sstevel@tonic-gate if (ugen_statep->ug_pm != NULL) {
46987c478bd9Sstevel@tonic-gate if (pm_idle_component(ugen_statep->ug_dip, 0) == DDI_SUCCESS) {
46997c478bd9Sstevel@tonic-gate mutex_enter(&ugen_statep->ug_mutex);
47007c478bd9Sstevel@tonic-gate ASSERT(ugen_statep->ug_pm->pwr_busy > 0);
47017c478bd9Sstevel@tonic-gate ugen_statep->ug_pm->pwr_busy--;
47027c478bd9Sstevel@tonic-gate
47037c478bd9Sstevel@tonic-gate USB_DPRINTF_L4(UGEN_PRINT_PM, ugen_statep->ug_log_hdl,
47047c478bd9Sstevel@tonic-gate "ugen_pm_idle_component: %d",
47057c478bd9Sstevel@tonic-gate ugen_statep->ug_pm->pwr_busy);
47067c478bd9Sstevel@tonic-gate
47077c478bd9Sstevel@tonic-gate mutex_exit(&ugen_statep->ug_mutex);
47087c478bd9Sstevel@tonic-gate }
47097c478bd9Sstevel@tonic-gate }
47107c478bd9Sstevel@tonic-gate }
47117c478bd9Sstevel@tonic-gate
47127c478bd9Sstevel@tonic-gate
47137c478bd9Sstevel@tonic-gate /*
47147c478bd9Sstevel@tonic-gate * devt lookup support
47157c478bd9Sstevel@tonic-gate * In ugen_strategy and ugen_minphys, we only have the devt and need
47167c478bd9Sstevel@tonic-gate * the ugen_state pointer. Since we don't know instance mask, we can't
47177c478bd9Sstevel@tonic-gate * easily derive a softstate pointer. Therefore, we use a list
47187c478bd9Sstevel@tonic-gate */
47197c478bd9Sstevel@tonic-gate static void
ugen_store_devt(ugen_state_t * ugenp,minor_t minor)47207c478bd9Sstevel@tonic-gate ugen_store_devt(ugen_state_t *ugenp, minor_t minor)
47217c478bd9Sstevel@tonic-gate {
47227c478bd9Sstevel@tonic-gate ugen_devt_list_entry_t *e = kmem_zalloc(
47230a05e705Slc sizeof (ugen_devt_list_entry_t), KM_SLEEP);
47247c478bd9Sstevel@tonic-gate ugen_devt_list_entry_t *t;
47257c478bd9Sstevel@tonic-gate
47267c478bd9Sstevel@tonic-gate mutex_enter(&ugen_devt_list_mutex);
47277c478bd9Sstevel@tonic-gate e->list_dev = makedevice(ddi_driver_major(ugenp->ug_dip), minor);
47287c478bd9Sstevel@tonic-gate e->list_state = ugenp;
47297c478bd9Sstevel@tonic-gate
47307c478bd9Sstevel@tonic-gate t = ugen_devt_list.list_next;
47317c478bd9Sstevel@tonic-gate
47327c478bd9Sstevel@tonic-gate /* check if the entry is already in the list */
47337c478bd9Sstevel@tonic-gate while (t) {
47347c478bd9Sstevel@tonic-gate ASSERT(t->list_dev != e->list_dev);
47357c478bd9Sstevel@tonic-gate t = t->list_next;
47367c478bd9Sstevel@tonic-gate }
47377c478bd9Sstevel@tonic-gate
47387c478bd9Sstevel@tonic-gate /* add to the head of the list */
47397c478bd9Sstevel@tonic-gate e->list_next = ugen_devt_list.list_next;
47407c478bd9Sstevel@tonic-gate if (ugen_devt_list.list_next) {
47417c478bd9Sstevel@tonic-gate ugen_devt_list.list_next->list_prev = e;
47427c478bd9Sstevel@tonic-gate }
47437c478bd9Sstevel@tonic-gate ugen_devt_list.list_next = e;
47447c478bd9Sstevel@tonic-gate mutex_exit(&ugen_devt_list_mutex);
47457c478bd9Sstevel@tonic-gate }
47467c478bd9Sstevel@tonic-gate
47477c478bd9Sstevel@tonic-gate
47487c478bd9Sstevel@tonic-gate static ugen_state_t *
ugen_devt2state(dev_t dev)47497c478bd9Sstevel@tonic-gate ugen_devt2state(dev_t dev)
47507c478bd9Sstevel@tonic-gate {
47517c478bd9Sstevel@tonic-gate ugen_devt_list_entry_t *t;
47527c478bd9Sstevel@tonic-gate ugen_state_t *ugenp = NULL;
47537c478bd9Sstevel@tonic-gate int index, count;
47547c478bd9Sstevel@tonic-gate
47557c478bd9Sstevel@tonic-gate mutex_enter(&ugen_devt_list_mutex);
47567c478bd9Sstevel@tonic-gate
47577c478bd9Sstevel@tonic-gate for (index = ugen_devt_cache_index, count = 0;
47587c478bd9Sstevel@tonic-gate count < UGEN_DEVT_CACHE_SIZE; count++) {
47597c478bd9Sstevel@tonic-gate if (ugen_devt_cache[index].cache_dev == dev) {
47607c478bd9Sstevel@tonic-gate ugen_devt_cache[index].cache_hit++;
47617c478bd9Sstevel@tonic-gate ugenp = ugen_devt_cache[index].cache_state;
47627c478bd9Sstevel@tonic-gate
47637c478bd9Sstevel@tonic-gate mutex_exit(&ugen_devt_list_mutex);
47647c478bd9Sstevel@tonic-gate
47657c478bd9Sstevel@tonic-gate return (ugenp);
47667c478bd9Sstevel@tonic-gate }
47677c478bd9Sstevel@tonic-gate index++;
47687c478bd9Sstevel@tonic-gate index %= UGEN_DEVT_CACHE_SIZE;
47697c478bd9Sstevel@tonic-gate }
47707c478bd9Sstevel@tonic-gate
47717c478bd9Sstevel@tonic-gate t = ugen_devt_list.list_next;
47727c478bd9Sstevel@tonic-gate
47737c478bd9Sstevel@tonic-gate while (t) {
47747c478bd9Sstevel@tonic-gate if (t->list_dev == dev) {
47757c478bd9Sstevel@tonic-gate ugenp = t->list_state;
47767c478bd9Sstevel@tonic-gate ugen_devt_cache_index++;
47777c478bd9Sstevel@tonic-gate ugen_devt_cache_index %= UGEN_DEVT_CACHE_SIZE;
47787c478bd9Sstevel@tonic-gate ugen_devt_cache[ugen_devt_cache_index].cache_dev = dev;
47797c478bd9Sstevel@tonic-gate ugen_devt_cache[ugen_devt_cache_index].cache_state =
47800a05e705Slc ugenp;
47817c478bd9Sstevel@tonic-gate mutex_exit(&ugen_devt_list_mutex);
47827c478bd9Sstevel@tonic-gate
47837c478bd9Sstevel@tonic-gate return (ugenp);
47847c478bd9Sstevel@tonic-gate }
47857c478bd9Sstevel@tonic-gate t = t->list_next;
47867c478bd9Sstevel@tonic-gate }
47877c478bd9Sstevel@tonic-gate mutex_exit(&ugen_devt_list_mutex);
47887c478bd9Sstevel@tonic-gate
47897c478bd9Sstevel@tonic-gate return (ugenp);
47907c478bd9Sstevel@tonic-gate }
47917c478bd9Sstevel@tonic-gate
47927c478bd9Sstevel@tonic-gate
47937c478bd9Sstevel@tonic-gate static void
ugen_free_devt(ugen_state_t * ugenp)47947c478bd9Sstevel@tonic-gate ugen_free_devt(ugen_state_t *ugenp)
47957c478bd9Sstevel@tonic-gate {
47967c478bd9Sstevel@tonic-gate ugen_devt_list_entry_t *e, *next, *prev;
47977c478bd9Sstevel@tonic-gate major_t major = ddi_driver_major(ugenp->ug_dip);
47987c478bd9Sstevel@tonic-gate int instance = ddi_get_instance(ugenp->ug_dip);
47997c478bd9Sstevel@tonic-gate
48007c478bd9Sstevel@tonic-gate mutex_enter(&ugen_devt_list_mutex);
48017c478bd9Sstevel@tonic-gate prev = &ugen_devt_list;
48027c478bd9Sstevel@tonic-gate for (e = prev->list_next; e != 0; e = next) {
48037c478bd9Sstevel@tonic-gate int i = (getminor(e->list_dev) &
48040a05e705Slc ugenp->ug_hdl->hdl_minor_node_instance_mask) >>
48050a05e705Slc ugenp->ug_hdl->hdl_minor_node_instance_shift;
48067c478bd9Sstevel@tonic-gate int m = getmajor(e->list_dev);
48077c478bd9Sstevel@tonic-gate
48087c478bd9Sstevel@tonic-gate next = e->list_next;
48097c478bd9Sstevel@tonic-gate
48107c478bd9Sstevel@tonic-gate if ((i == instance) && (m == major)) {
48117c478bd9Sstevel@tonic-gate prev->list_next = e->list_next;
48127c478bd9Sstevel@tonic-gate if (e->list_next) {
48137c478bd9Sstevel@tonic-gate e->list_next->list_prev = prev;
48147c478bd9Sstevel@tonic-gate }
48157c478bd9Sstevel@tonic-gate kmem_free(e, sizeof (ugen_devt_list_entry_t));
48167c478bd9Sstevel@tonic-gate } else {
48177c478bd9Sstevel@tonic-gate prev = e;
48187c478bd9Sstevel@tonic-gate }
48197c478bd9Sstevel@tonic-gate }
48207c478bd9Sstevel@tonic-gate
48217c478bd9Sstevel@tonic-gate bzero(ugen_devt_cache, sizeof (ugen_devt_cache));
48227c478bd9Sstevel@tonic-gate ugen_devt_cache_index = 0;
48237c478bd9Sstevel@tonic-gate mutex_exit(&ugen_devt_list_mutex);
48247c478bd9Sstevel@tonic-gate }
4825