10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
52326Ssl147100 * Common Development and Distribution License (the "License").
62326Ssl147100 * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
2212194SRaymond.Chen@Sun.COM * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate */
240Sstevel@tonic-gate
250Sstevel@tonic-gate /*
260Sstevel@tonic-gate * USBA: Solaris USB Architecture support for the hub
270Sstevel@tonic-gate * including root hub
280Sstevel@tonic-gate * Most of the code for hubd resides in this file and
290Sstevel@tonic-gate * is shared between the HCD root hub support and hubd
300Sstevel@tonic-gate */
310Sstevel@tonic-gate #define USBA_FRAMEWORK
320Sstevel@tonic-gate #include <sys/usb/usba.h>
330Sstevel@tonic-gate #include <sys/usb/usba/usba_devdb.h>
340Sstevel@tonic-gate #include <sys/sunndi.h>
350Sstevel@tonic-gate #include <sys/usb/usba/usba_impl.h>
360Sstevel@tonic-gate #include <sys/usb/usba/usba_types.h>
370Sstevel@tonic-gate #include <sys/usb/usba/hubdi.h>
380Sstevel@tonic-gate #include <sys/usb/usba/hcdi_impl.h>
390Sstevel@tonic-gate #include <sys/usb/hubd/hub.h>
400Sstevel@tonic-gate #include <sys/usb/hubd/hubdvar.h>
410Sstevel@tonic-gate #include <sys/usb/hubd/hubd_impl.h>
420Sstevel@tonic-gate #include <sys/kobj.h>
430Sstevel@tonic-gate #include <sys/kobj_lex.h>
440Sstevel@tonic-gate #include <sys/fs/dv_node.h>
457492SZhigang.Lu@Sun.COM #include <sys/strsun.h>
460Sstevel@tonic-gate
470Sstevel@tonic-gate /*
4810783SVincent.Wang@Sun.COM * External functions
4910783SVincent.Wang@Sun.COM */
5010783SVincent.Wang@Sun.COM extern boolean_t consconfig_console_is_ready(void);
5110783SVincent.Wang@Sun.COM
5210783SVincent.Wang@Sun.COM /*
530Sstevel@tonic-gate * Prototypes for static functions
540Sstevel@tonic-gate */
550Sstevel@tonic-gate static int usba_hubdi_bus_ctl(
560Sstevel@tonic-gate dev_info_t *dip,
570Sstevel@tonic-gate dev_info_t *rdip,
580Sstevel@tonic-gate ddi_ctl_enum_t op,
590Sstevel@tonic-gate void *arg,
600Sstevel@tonic-gate void *result);
610Sstevel@tonic-gate
620Sstevel@tonic-gate static int usba_hubdi_map_fault(
630Sstevel@tonic-gate dev_info_t *dip,
640Sstevel@tonic-gate dev_info_t *rdip,
650Sstevel@tonic-gate struct hat *hat,
660Sstevel@tonic-gate struct seg *seg,
670Sstevel@tonic-gate caddr_t addr,
680Sstevel@tonic-gate struct devpage *dp,
690Sstevel@tonic-gate pfn_t pfn,
700Sstevel@tonic-gate uint_t prot,
710Sstevel@tonic-gate uint_t lock);
720Sstevel@tonic-gate
730Sstevel@tonic-gate static int hubd_busop_get_eventcookie(dev_info_t *dip,
740Sstevel@tonic-gate dev_info_t *rdip,
750Sstevel@tonic-gate char *eventname,
760Sstevel@tonic-gate ddi_eventcookie_t *cookie);
770Sstevel@tonic-gate static int hubd_busop_add_eventcall(dev_info_t *dip,
780Sstevel@tonic-gate dev_info_t *rdip,
790Sstevel@tonic-gate ddi_eventcookie_t cookie,
800Sstevel@tonic-gate void (*callback)(dev_info_t *dip,
810Sstevel@tonic-gate ddi_eventcookie_t cookie, void *arg,
820Sstevel@tonic-gate void *bus_impldata),
830Sstevel@tonic-gate void *arg, ddi_callback_id_t *cb_id);
840Sstevel@tonic-gate static int hubd_busop_remove_eventcall(dev_info_t *dip,
850Sstevel@tonic-gate ddi_callback_id_t cb_id);
860Sstevel@tonic-gate static int hubd_bus_config(dev_info_t *dip,
870Sstevel@tonic-gate uint_t flag,
880Sstevel@tonic-gate ddi_bus_config_op_t op,
890Sstevel@tonic-gate void *arg,
900Sstevel@tonic-gate dev_info_t **child);
910Sstevel@tonic-gate static int hubd_bus_unconfig(dev_info_t *dip,
920Sstevel@tonic-gate uint_t flag,
930Sstevel@tonic-gate ddi_bus_config_op_t op,
940Sstevel@tonic-gate void *arg);
950Sstevel@tonic-gate static int hubd_bus_power(dev_info_t *dip, void *impl_arg,
960Sstevel@tonic-gate pm_bus_power_op_t op, void *arg, void *result);
970Sstevel@tonic-gate
980Sstevel@tonic-gate static usb_port_t hubd_get_port_num(hubd_t *, struct devctl_iocdata *);
990Sstevel@tonic-gate static dev_info_t *hubd_get_child_dip(hubd_t *, usb_port_t);
1000Sstevel@tonic-gate static uint_t hubd_cfgadm_state(hubd_t *, usb_port_t);
1010Sstevel@tonic-gate static int hubd_toggle_port(hubd_t *, usb_port_t);
1020Sstevel@tonic-gate static void hubd_register_cpr_callback(hubd_t *);
1030Sstevel@tonic-gate static void hubd_unregister_cpr_callback(hubd_t *);
1040Sstevel@tonic-gate
1050Sstevel@tonic-gate /*
1060Sstevel@tonic-gate * Busops vector for USB HUB's
1070Sstevel@tonic-gate */
1080Sstevel@tonic-gate struct bus_ops usba_hubdi_busops = {
1090Sstevel@tonic-gate BUSO_REV,
1100Sstevel@tonic-gate nullbusmap, /* bus_map */
1110Sstevel@tonic-gate NULL, /* bus_get_intrspec */
1120Sstevel@tonic-gate NULL, /* bus_add_intrspec */
1130Sstevel@tonic-gate NULL, /* bus_remove_intrspec */
1140Sstevel@tonic-gate usba_hubdi_map_fault, /* bus_map_fault */
1150Sstevel@tonic-gate ddi_dma_map, /* bus_dma_map */
1160Sstevel@tonic-gate ddi_dma_allochdl,
1170Sstevel@tonic-gate ddi_dma_freehdl,
1180Sstevel@tonic-gate ddi_dma_bindhdl,
1190Sstevel@tonic-gate ddi_dma_unbindhdl,
1200Sstevel@tonic-gate ddi_dma_flush,
1210Sstevel@tonic-gate ddi_dma_win,
1220Sstevel@tonic-gate ddi_dma_mctl, /* bus_dma_ctl */
1230Sstevel@tonic-gate usba_hubdi_bus_ctl, /* bus_ctl */
1240Sstevel@tonic-gate ddi_bus_prop_op, /* bus_prop_op */
1250Sstevel@tonic-gate hubd_busop_get_eventcookie,
1260Sstevel@tonic-gate hubd_busop_add_eventcall,
1270Sstevel@tonic-gate hubd_busop_remove_eventcall,
1280Sstevel@tonic-gate NULL, /* bus_post_event */
1290Sstevel@tonic-gate NULL, /* bus_intr_ctl */
1300Sstevel@tonic-gate hubd_bus_config, /* bus_config */
1310Sstevel@tonic-gate hubd_bus_unconfig, /* bus_unconfig */
1320Sstevel@tonic-gate NULL, /* bus_fm_init */
1330Sstevel@tonic-gate NULL, /* bus_fm_fini */
1340Sstevel@tonic-gate NULL, /* bus_fm_access_enter */
1350Sstevel@tonic-gate NULL, /* bus_fm_access_exit */
1360Sstevel@tonic-gate hubd_bus_power /* bus_power */
1370Sstevel@tonic-gate };
1380Sstevel@tonic-gate
13912194SRaymond.Chen@Sun.COM #define USB_HUB_INTEL_VID 0x8087
14012194SRaymond.Chen@Sun.COM #define USB_HUB_INTEL_PID 0x0020
1410Sstevel@tonic-gate
1420Sstevel@tonic-gate /*
1430Sstevel@tonic-gate * local variables
1440Sstevel@tonic-gate */
1450Sstevel@tonic-gate static kmutex_t usba_hubdi_mutex; /* protects USBA HUB data structures */
1460Sstevel@tonic-gate
1470Sstevel@tonic-gate static usba_list_entry_t usba_hubdi_list;
1480Sstevel@tonic-gate
1490Sstevel@tonic-gate usb_log_handle_t hubdi_log_handle;
1500Sstevel@tonic-gate uint_t hubdi_errlevel = USB_LOG_L4;
1510Sstevel@tonic-gate uint_t hubdi_errmask = (uint_t)-1;
1527492SZhigang.Lu@Sun.COM uint8_t hubdi_min_pm_threshold = 5; /* seconds */
15310468SRaymond.Chen@Sun.COM uint8_t hubdi_reset_delay = 20; /* seconds */
15411987SFei.Feng@Sun.COM extern int modrootloaded;
1550Sstevel@tonic-gate
1560Sstevel@tonic-gate /*
1570Sstevel@tonic-gate * initialize private data
1580Sstevel@tonic-gate */
1590Sstevel@tonic-gate void
usba_hubdi_initialization()1600Sstevel@tonic-gate usba_hubdi_initialization()
1610Sstevel@tonic-gate {
1620Sstevel@tonic-gate hubdi_log_handle = usb_alloc_log_hdl(NULL, "hubdi", &hubdi_errlevel,
1634763Slg150142 &hubdi_errmask, NULL, 0);
1640Sstevel@tonic-gate
1650Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
1660Sstevel@tonic-gate "usba_hubdi_initialization");
1670Sstevel@tonic-gate
1680Sstevel@tonic-gate mutex_init(&usba_hubdi_mutex, NULL, MUTEX_DRIVER, NULL);
1690Sstevel@tonic-gate
1700Sstevel@tonic-gate usba_init_list(&usba_hubdi_list, NULL, NULL);
1710Sstevel@tonic-gate }
1720Sstevel@tonic-gate
1730Sstevel@tonic-gate
1740Sstevel@tonic-gate void
usba_hubdi_destroy()1750Sstevel@tonic-gate usba_hubdi_destroy()
1760Sstevel@tonic-gate {
1770Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
1780Sstevel@tonic-gate "usba_hubdi_destroy");
1790Sstevel@tonic-gate
1800Sstevel@tonic-gate mutex_destroy(&usba_hubdi_mutex);
1810Sstevel@tonic-gate usba_destroy_list(&usba_hubdi_list);
1820Sstevel@tonic-gate
1830Sstevel@tonic-gate usb_free_log_hdl(hubdi_log_handle);
1840Sstevel@tonic-gate }
1850Sstevel@tonic-gate
1860Sstevel@tonic-gate
1870Sstevel@tonic-gate /*
1880Sstevel@tonic-gate * Called by an HUB to attach an instance of the driver
1890Sstevel@tonic-gate * make this instance known to USBA
1900Sstevel@tonic-gate * the HUB should initialize usba_hubdi structure prior
1910Sstevel@tonic-gate * to calling this interface
1920Sstevel@tonic-gate */
1939430SRaymond.Chen@Sun.COM int
usba_hubdi_register(dev_info_t * dip,uint_t flags)1940Sstevel@tonic-gate usba_hubdi_register(dev_info_t *dip,
1950Sstevel@tonic-gate uint_t flags)
1960Sstevel@tonic-gate {
1970Sstevel@tonic-gate usba_hubdi_t *hubdi = kmem_zalloc(sizeof (usba_hubdi_t), KM_SLEEP);
1980Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip);
1990Sstevel@tonic-gate
2000Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
2010Sstevel@tonic-gate "usba_hubdi_register: %s", ddi_node_name(dip));
2020Sstevel@tonic-gate
2030Sstevel@tonic-gate hubdi->hubdi_dip = dip;
2040Sstevel@tonic-gate hubdi->hubdi_flags = flags;
2050Sstevel@tonic-gate
2060Sstevel@tonic-gate usba_device->usb_hubdi = hubdi;
2070Sstevel@tonic-gate
2080Sstevel@tonic-gate /*
2090Sstevel@tonic-gate * add this hubdi instance to the list of known hubdi's
2100Sstevel@tonic-gate */
2110Sstevel@tonic-gate usba_init_list(&hubdi->hubdi_list, (usb_opaque_t)hubdi,
2120Sstevel@tonic-gate usba_hcdi_get_hcdi(usba_device->usb_root_hub_dip)->
2130Sstevel@tonic-gate hcdi_iblock_cookie);
2140Sstevel@tonic-gate mutex_enter(&usba_hubdi_mutex);
2150Sstevel@tonic-gate usba_add_to_list(&usba_hubdi_list, &hubdi->hubdi_list);
2160Sstevel@tonic-gate mutex_exit(&usba_hubdi_mutex);
2170Sstevel@tonic-gate
2180Sstevel@tonic-gate return (DDI_SUCCESS);
2190Sstevel@tonic-gate }
2200Sstevel@tonic-gate
2210Sstevel@tonic-gate
2220Sstevel@tonic-gate /*
2230Sstevel@tonic-gate * Called by an HUB to detach an instance of the driver
2240Sstevel@tonic-gate */
2259430SRaymond.Chen@Sun.COM int
usba_hubdi_unregister(dev_info_t * dip)2260Sstevel@tonic-gate usba_hubdi_unregister(dev_info_t *dip)
2270Sstevel@tonic-gate {
2280Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip);
2290Sstevel@tonic-gate usba_hubdi_t *hubdi = usba_device->usb_hubdi;
2300Sstevel@tonic-gate
2310Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubdi_log_handle,
2320Sstevel@tonic-gate "usba_hubdi_unregister: %s", ddi_node_name(dip));
2330Sstevel@tonic-gate
2340Sstevel@tonic-gate mutex_enter(&usba_hubdi_mutex);
2350Sstevel@tonic-gate (void) usba_rm_from_list(&usba_hubdi_list, &hubdi->hubdi_list);
2360Sstevel@tonic-gate mutex_exit(&usba_hubdi_mutex);
2370Sstevel@tonic-gate
2380Sstevel@tonic-gate usba_destroy_list(&hubdi->hubdi_list);
2390Sstevel@tonic-gate
2400Sstevel@tonic-gate kmem_free(hubdi, sizeof (usba_hubdi_t));
2410Sstevel@tonic-gate
2420Sstevel@tonic-gate return (DDI_SUCCESS);
2430Sstevel@tonic-gate }
2440Sstevel@tonic-gate
2450Sstevel@tonic-gate
2460Sstevel@tonic-gate /*
2470Sstevel@tonic-gate * misc bus routines currently not used
2480Sstevel@tonic-gate */
2490Sstevel@tonic-gate /*ARGSUSED*/
2500Sstevel@tonic-gate static int
usba_hubdi_map_fault(dev_info_t * dip,dev_info_t * rdip,struct hat * hat,struct seg * seg,caddr_t addr,struct devpage * dp,pfn_t pfn,uint_t prot,uint_t lock)2510Sstevel@tonic-gate usba_hubdi_map_fault(dev_info_t *dip,
2520Sstevel@tonic-gate dev_info_t *rdip,
2530Sstevel@tonic-gate struct hat *hat,
2540Sstevel@tonic-gate struct seg *seg,
2550Sstevel@tonic-gate caddr_t addr,
2560Sstevel@tonic-gate struct devpage *dp,
2570Sstevel@tonic-gate pfn_t pfn,
2580Sstevel@tonic-gate uint_t prot,
2590Sstevel@tonic-gate uint_t lock)
2600Sstevel@tonic-gate {
2610Sstevel@tonic-gate return (DDI_FAILURE);
2620Sstevel@tonic-gate }
2630Sstevel@tonic-gate
2640Sstevel@tonic-gate
2650Sstevel@tonic-gate /*
2660Sstevel@tonic-gate * root hub support. the root hub uses the same devi as the HCD
2670Sstevel@tonic-gate */
2680Sstevel@tonic-gate int
usba_hubdi_bind_root_hub(dev_info_t * dip,uchar_t * root_hub_config_descriptor,size_t config_length,usb_dev_descr_t * root_hub_device_descriptor)2690Sstevel@tonic-gate usba_hubdi_bind_root_hub(dev_info_t *dip,
2700Sstevel@tonic-gate uchar_t *root_hub_config_descriptor,
2710Sstevel@tonic-gate size_t config_length,
2720Sstevel@tonic-gate usb_dev_descr_t *root_hub_device_descriptor)
2730Sstevel@tonic-gate {
2740Sstevel@tonic-gate usba_device_t *usba_device;
2750Sstevel@tonic-gate usba_hcdi_t *hcdi = usba_hcdi_get_hcdi(dip);
2760Sstevel@tonic-gate hubd_t *root_hubd;
2770Sstevel@tonic-gate usb_pipe_handle_t ph = NULL;
2780Sstevel@tonic-gate dev_info_t *child = ddi_get_child(dip);
2790Sstevel@tonic-gate
2800Sstevel@tonic-gate if (ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
2810Sstevel@tonic-gate "root-hub") != NDI_SUCCESS) {
2820Sstevel@tonic-gate
2830Sstevel@tonic-gate return (USB_FAILURE);
2840Sstevel@tonic-gate }
2850Sstevel@tonic-gate
2869094SVincent.Wang@Sun.COM usba_add_root_hub(dip);
2879094SVincent.Wang@Sun.COM
2880Sstevel@tonic-gate root_hubd = kmem_zalloc(sizeof (hubd_t), KM_SLEEP);
2890Sstevel@tonic-gate
2900Sstevel@tonic-gate /*
2910Sstevel@tonic-gate * create and initialize a usba_device structure
2920Sstevel@tonic-gate */
2930Sstevel@tonic-gate usba_device = usba_alloc_usba_device(dip);
2940Sstevel@tonic-gate
2950Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex);
2960Sstevel@tonic-gate usba_device->usb_hcdi_ops = hcdi->hcdi_ops;
2970Sstevel@tonic-gate usba_device->usb_cfg = root_hub_config_descriptor;
2980Sstevel@tonic-gate usba_device->usb_cfg_length = config_length;
2990Sstevel@tonic-gate usba_device->usb_dev_descr = root_hub_device_descriptor;
3000Sstevel@tonic-gate usba_device->usb_port = 1;
3010Sstevel@tonic-gate usba_device->usb_addr = ROOT_HUB_ADDR;
3020Sstevel@tonic-gate usba_device->usb_root_hubd = root_hubd;
3030Sstevel@tonic-gate usba_device->usb_cfg_array = kmem_zalloc(sizeof (uchar_t *),
3044763Slg150142 KM_SLEEP);
3050Sstevel@tonic-gate usba_device->usb_cfg_array_length = sizeof (uchar_t *);
3060Sstevel@tonic-gate
3070Sstevel@tonic-gate usba_device->usb_cfg_array_len = kmem_zalloc(sizeof (uint16_t),
3084763Slg150142 KM_SLEEP);
3090Sstevel@tonic-gate usba_device->usb_cfg_array_len_length = sizeof (uint16_t);
3100Sstevel@tonic-gate
3110Sstevel@tonic-gate usba_device->usb_cfg_array[0] = root_hub_config_descriptor;
3120Sstevel@tonic-gate usba_device->usb_cfg_array_len[0] =
3134763Slg150142 sizeof (root_hub_config_descriptor);
3140Sstevel@tonic-gate
3150Sstevel@tonic-gate usba_device->usb_cfg_str_descr = kmem_zalloc(sizeof (uchar_t *),
3164763Slg150142 KM_SLEEP);
3170Sstevel@tonic-gate usba_device->usb_n_cfgs = 1;
3180Sstevel@tonic-gate usba_device->usb_n_ifs = 1;
3190Sstevel@tonic-gate usba_device->usb_dip = dip;
3200Sstevel@tonic-gate
3210Sstevel@tonic-gate usba_device->usb_client_flags = kmem_zalloc(
3220Sstevel@tonic-gate usba_device->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP);
3230Sstevel@tonic-gate
3240Sstevel@tonic-gate usba_device->usb_client_attach_list = kmem_zalloc(
3250Sstevel@tonic-gate usba_device->usb_n_ifs *
3260Sstevel@tonic-gate sizeof (*usba_device->usb_client_attach_list), KM_SLEEP);
3270Sstevel@tonic-gate
3280Sstevel@tonic-gate usba_device->usb_client_ev_cb_list = kmem_zalloc(
3290Sstevel@tonic-gate usba_device->usb_n_ifs *
3300Sstevel@tonic-gate sizeof (*usba_device->usb_client_ev_cb_list), KM_SLEEP);
3310Sstevel@tonic-gate
3320Sstevel@tonic-gate /*
3330Sstevel@tonic-gate * The bDeviceProtocol field of root hub device specifies,
3340Sstevel@tonic-gate * whether root hub is a High or Full speed usb device.
3350Sstevel@tonic-gate */
3360Sstevel@tonic-gate if (root_hub_device_descriptor->bDeviceProtocol) {
3370Sstevel@tonic-gate usba_device->usb_port_status = USBA_HIGH_SPEED_DEV;
3380Sstevel@tonic-gate } else {
3390Sstevel@tonic-gate usba_device->usb_port_status = USBA_FULL_SPEED_DEV;
3400Sstevel@tonic-gate }
3410Sstevel@tonic-gate
3420Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex);
3430Sstevel@tonic-gate
3440Sstevel@tonic-gate usba_set_usba_device(dip, usba_device);
3450Sstevel@tonic-gate
3460Sstevel@tonic-gate /*
3470Sstevel@tonic-gate * For the root hub the default pipe is not yet open
3480Sstevel@tonic-gate */
3490Sstevel@tonic-gate if (usb_pipe_open(dip, NULL, NULL,
3500Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) != USB_SUCCESS) {
3510Sstevel@tonic-gate goto fail;
3520Sstevel@tonic-gate }
3530Sstevel@tonic-gate
3540Sstevel@tonic-gate /*
3550Sstevel@tonic-gate * kill off all OBP children, they may not be fully
3560Sstevel@tonic-gate * enumerated
3570Sstevel@tonic-gate */
3580Sstevel@tonic-gate while (child) {
3590Sstevel@tonic-gate dev_info_t *next = ddi_get_next_sibling(child);
3600Sstevel@tonic-gate (void) ddi_remove_child(child, 0);
3610Sstevel@tonic-gate child = next;
3620Sstevel@tonic-gate }
3630Sstevel@tonic-gate
3640Sstevel@tonic-gate /*
3650Sstevel@tonic-gate * "attach" the root hub driver
3660Sstevel@tonic-gate */
3670Sstevel@tonic-gate if (usba_hubdi_attach(dip, DDI_ATTACH) != DDI_SUCCESS) {
3680Sstevel@tonic-gate goto fail;
3690Sstevel@tonic-gate }
3700Sstevel@tonic-gate
3710Sstevel@tonic-gate return (USB_SUCCESS);
3720Sstevel@tonic-gate
3730Sstevel@tonic-gate fail:
3740Sstevel@tonic-gate (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
3750Sstevel@tonic-gate
3769094SVincent.Wang@Sun.COM usba_rem_root_hub(dip);
3779094SVincent.Wang@Sun.COM
3780Sstevel@tonic-gate if (ph) {
3790Sstevel@tonic-gate usb_pipe_close(dip, ph,
3800Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
3810Sstevel@tonic-gate }
3820Sstevel@tonic-gate
3830Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array,
3844763Slg150142 usba_device->usb_cfg_array_length);
3850Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array_len,
3864763Slg150142 usba_device->usb_cfg_array_len_length);
3870Sstevel@tonic-gate
3880Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *));
3890Sstevel@tonic-gate
3900Sstevel@tonic-gate usba_free_usba_device(usba_device);
3910Sstevel@tonic-gate
3920Sstevel@tonic-gate usba_set_usba_device(dip, NULL);
3930Sstevel@tonic-gate if (root_hubd) {
3940Sstevel@tonic-gate kmem_free(root_hubd, sizeof (hubd_t));
3950Sstevel@tonic-gate }
3960Sstevel@tonic-gate
3970Sstevel@tonic-gate return (USB_FAILURE);
3980Sstevel@tonic-gate }
3990Sstevel@tonic-gate
4000Sstevel@tonic-gate
4010Sstevel@tonic-gate int
usba_hubdi_unbind_root_hub(dev_info_t * dip)4020Sstevel@tonic-gate usba_hubdi_unbind_root_hub(dev_info_t *dip)
4030Sstevel@tonic-gate {
4040Sstevel@tonic-gate usba_device_t *usba_device;
4050Sstevel@tonic-gate
4060Sstevel@tonic-gate /* was root hub attached? */
4070Sstevel@tonic-gate if (!(usba_is_root_hub(dip))) {
4080Sstevel@tonic-gate
4090Sstevel@tonic-gate /* return success anyway */
4100Sstevel@tonic-gate return (USB_SUCCESS);
4110Sstevel@tonic-gate }
4120Sstevel@tonic-gate
4130Sstevel@tonic-gate /*
4140Sstevel@tonic-gate * usba_hubdi_detach also closes the default pipe
4150Sstevel@tonic-gate * and removes properties so there is no need to
4160Sstevel@tonic-gate * do it here
4170Sstevel@tonic-gate */
4180Sstevel@tonic-gate if (usba_hubdi_detach(dip, DDI_DETACH) != DDI_SUCCESS) {
4190Sstevel@tonic-gate
4200Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip)) {
421978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
4220Sstevel@tonic-gate "failure to unbind root hub after attach failure");
4230Sstevel@tonic-gate }
4240Sstevel@tonic-gate
4250Sstevel@tonic-gate return (USB_FAILURE);
4260Sstevel@tonic-gate }
4270Sstevel@tonic-gate
4280Sstevel@tonic-gate usba_device = usba_get_usba_device(dip);
4290Sstevel@tonic-gate
4300Sstevel@tonic-gate kmem_free(usba_device->usb_root_hubd, sizeof (hubd_t));
4310Sstevel@tonic-gate
4320Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array,
4334763Slg150142 usba_device->usb_cfg_array_length);
4340Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_array_len,
4354763Slg150142 usba_device->usb_cfg_array_len_length);
4360Sstevel@tonic-gate
4370Sstevel@tonic-gate kmem_free(usba_device->usb_cfg_str_descr, sizeof (uchar_t *));
4380Sstevel@tonic-gate
4390Sstevel@tonic-gate usba_free_usba_device(usba_device);
4400Sstevel@tonic-gate
4419094SVincent.Wang@Sun.COM usba_rem_root_hub(dip);
4429094SVincent.Wang@Sun.COM
4430Sstevel@tonic-gate (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "root-hub");
4440Sstevel@tonic-gate
4450Sstevel@tonic-gate return (USB_SUCCESS);
4460Sstevel@tonic-gate }
4470Sstevel@tonic-gate
4480Sstevel@tonic-gate
4490Sstevel@tonic-gate /*
4500Sstevel@tonic-gate * Actual Hub Driver support code:
4510Sstevel@tonic-gate * shared by root hub and non-root hubs
4520Sstevel@tonic-gate */
4530Sstevel@tonic-gate #include <sys/usb/usba/usbai_version.h>
4540Sstevel@tonic-gate
4550Sstevel@tonic-gate /* Debugging support */
4569430SRaymond.Chen@Sun.COM uint_t hubd_errlevel = USB_LOG_L4;
4579430SRaymond.Chen@Sun.COM uint_t hubd_errmask = (uint_t)DPRINT_MASK_ALL;
4589430SRaymond.Chen@Sun.COM uint_t hubd_instance_debug = (uint_t)-1;
4590Sstevel@tonic-gate static uint_t hubdi_bus_config_debug = 0;
4600Sstevel@tonic-gate
4610Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errlevel))
4620Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_errmask))
4630Sstevel@tonic-gate _NOTE(DATA_READABLE_WITHOUT_LOCK(hubd_instance_debug))
4640Sstevel@tonic-gate
4650Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", msgb))
4660Sstevel@tonic-gate _NOTE(SCHEME_PROTECTS_DATA("unique", dev_info))
4670Sstevel@tonic-gate
4680Sstevel@tonic-gate
4690Sstevel@tonic-gate /*
4700Sstevel@tonic-gate * local variables:
4710Sstevel@tonic-gate *
4720Sstevel@tonic-gate * Amount of time to wait between resetting the port and accessing
4730Sstevel@tonic-gate * the device. The value is in microseconds.
4740Sstevel@tonic-gate */
4750Sstevel@tonic-gate static uint_t hubd_device_delay = 1000000;
4760Sstevel@tonic-gate
4770Sstevel@tonic-gate /*
4780Sstevel@tonic-gate * enumeration retry
4790Sstevel@tonic-gate */
4800Sstevel@tonic-gate #define HUBD_PORT_RETRY 5
4810Sstevel@tonic-gate static uint_t hubd_retry_enumerate = HUBD_PORT_RETRY;
4820Sstevel@tonic-gate
4830Sstevel@tonic-gate /*
4840Sstevel@tonic-gate * Stale hotremoved device cleanup delay
4850Sstevel@tonic-gate */
4860Sstevel@tonic-gate #define HUBD_STALE_DIP_CLEANUP_DELAY 5000000
4870Sstevel@tonic-gate static uint_t hubd_dip_cleanup_delay = HUBD_STALE_DIP_CLEANUP_DELAY;
4880Sstevel@tonic-gate
4890Sstevel@tonic-gate /*
4900Sstevel@tonic-gate * retries for USB suspend and resume
4910Sstevel@tonic-gate */
4920Sstevel@tonic-gate #define HUBD_SUS_RES_RETRY 2
4930Sstevel@tonic-gate
4940Sstevel@tonic-gate void *hubd_statep;
4950Sstevel@tonic-gate
4960Sstevel@tonic-gate /*
4970Sstevel@tonic-gate * prototypes
4980Sstevel@tonic-gate */
4990Sstevel@tonic-gate static int hubd_cleanup(dev_info_t *dip, hubd_t *hubd);
5000Sstevel@tonic-gate static int hubd_check_ports(hubd_t *hubd);
5010Sstevel@tonic-gate
5020Sstevel@tonic-gate static int hubd_open_intr_pipe(hubd_t *hubd);
5030Sstevel@tonic-gate static void hubd_start_polling(hubd_t *hubd, int always);
5040Sstevel@tonic-gate static void hubd_stop_polling(hubd_t *hubd);
5050Sstevel@tonic-gate static void hubd_close_intr_pipe(hubd_t *hubd);
5060Sstevel@tonic-gate
5070Sstevel@tonic-gate static void hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *req);
5080Sstevel@tonic-gate static void hubd_exception_cb(usb_pipe_handle_t pipe,
5090Sstevel@tonic-gate usb_intr_req_t *req);
5100Sstevel@tonic-gate static void hubd_hotplug_thread(void *arg);
5114844Slg150142 static void hubd_reset_thread(void *arg);
5120Sstevel@tonic-gate static int hubd_create_child(dev_info_t *dip,
5130Sstevel@tonic-gate hubd_t *hubd,
5140Sstevel@tonic-gate usba_device_t *usba_device,
5150Sstevel@tonic-gate usb_port_status_t port_status,
5160Sstevel@tonic-gate usb_port_t port,
5170Sstevel@tonic-gate int iteration);
5180Sstevel@tonic-gate
5190Sstevel@tonic-gate static int hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag,
5200Sstevel@tonic-gate boolean_t retry);
5210Sstevel@tonic-gate
5220Sstevel@tonic-gate static int hubd_get_hub_descriptor(hubd_t *hubd);
5230Sstevel@tonic-gate
5241001Ssl147100 static int hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status);
5251001Ssl147100
5260Sstevel@tonic-gate static int hubd_reset_port(hubd_t *hubd, usb_port_t port);
5270Sstevel@tonic-gate
5280Sstevel@tonic-gate static int hubd_get_hub_status(hubd_t *hubd);
5290Sstevel@tonic-gate
5300Sstevel@tonic-gate static int hubd_handle_port_connect(hubd_t *hubd, usb_port_t port);
5310Sstevel@tonic-gate
5320Sstevel@tonic-gate static int hubd_disable_port(hubd_t *hubd, usb_port_t port);
5330Sstevel@tonic-gate
5340Sstevel@tonic-gate static int hubd_enable_port(hubd_t *hubd, usb_port_t port);
5350Sstevel@tonic-gate static int hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port);
5360Sstevel@tonic-gate
5370Sstevel@tonic-gate static int hubd_determine_port_status(hubd_t *hubd, usb_port_t port,
5380Sstevel@tonic-gate uint16_t *status, uint16_t *change, uint_t ack_flag);
5390Sstevel@tonic-gate
5400Sstevel@tonic-gate static int hubd_enable_all_port_power(hubd_t *hubd);
5410Sstevel@tonic-gate static int hubd_disable_all_port_power(hubd_t *hubd);
5420Sstevel@tonic-gate static int hubd_disable_port_power(hubd_t *hubd, usb_port_t port);
5430Sstevel@tonic-gate static int hubd_enable_port_power(hubd_t *hubd, usb_port_t port);
5440Sstevel@tonic-gate
5450Sstevel@tonic-gate static void hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device);
5460Sstevel@tonic-gate
5470Sstevel@tonic-gate static int hubd_can_suspend(hubd_t *hubd);
5480Sstevel@tonic-gate static void hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd);
5490Sstevel@tonic-gate static int hubd_setdevaddr(hubd_t *hubd, usb_port_t port);
5500Sstevel@tonic-gate static void hubd_setdevconfig(hubd_t *hubd, usb_port_t port);
5510Sstevel@tonic-gate
5520Sstevel@tonic-gate static int hubd_register_events(hubd_t *hubd);
5530Sstevel@tonic-gate static void hubd_do_callback(hubd_t *hubd, dev_info_t *dip,
5540Sstevel@tonic-gate ddi_eventcookie_t cookie);
5550Sstevel@tonic-gate static void hubd_run_callbacks(hubd_t *hubd, usba_event_t type);
5560Sstevel@tonic-gate static void hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type);
5570Sstevel@tonic-gate static void hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd);
5580Sstevel@tonic-gate
5590Sstevel@tonic-gate static int hubd_disconnect_event_cb(dev_info_t *dip);
5600Sstevel@tonic-gate static int hubd_reconnect_event_cb(dev_info_t *dip);
5610Sstevel@tonic-gate static int hubd_pre_suspend_event_cb(dev_info_t *dip);
5620Sstevel@tonic-gate static int hubd_post_resume_event_cb(dev_info_t *dip);
5630Sstevel@tonic-gate static int hubd_cpr_suspend(hubd_t *hubd);
5640Sstevel@tonic-gate static void hubd_cpr_resume(dev_info_t *dip);
5650Sstevel@tonic-gate static int hubd_restore_state_cb(dev_info_t *dip);
5664844Slg150142 static int hubd_check_same_device(hubd_t *hubd, usb_port_t port);
5670Sstevel@tonic-gate
5681001Ssl147100 static int hubd_init_power_budget(hubd_t *hubd);
5691001Ssl147100
5700Sstevel@tonic-gate static ndi_event_definition_t hubd_ndi_event_defs[] = {
5710Sstevel@tonic-gate {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL,
5720Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL},
5730Sstevel@tonic-gate {USBA_EVENT_TAG_HOT_INSERTION, DDI_DEVI_INSERT_EVENT, EPL_KERNEL,
5740Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL},
5750Sstevel@tonic-gate {USBA_EVENT_TAG_POST_RESUME, USBA_POST_RESUME_EVENT, EPL_KERNEL,
5760Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL},
5770Sstevel@tonic-gate {USBA_EVENT_TAG_PRE_SUSPEND, USBA_PRE_SUSPEND_EVENT, EPL_KERNEL,
5780Sstevel@tonic-gate NDI_EVENT_POST_TO_ALL}
5790Sstevel@tonic-gate };
5800Sstevel@tonic-gate
5810Sstevel@tonic-gate #define HUBD_N_NDI_EVENTS \
5820Sstevel@tonic-gate (sizeof (hubd_ndi_event_defs) / sizeof (ndi_event_definition_t))
5830Sstevel@tonic-gate
5840Sstevel@tonic-gate static ndi_event_set_t hubd_ndi_events = {
5850Sstevel@tonic-gate NDI_EVENTS_REV1, HUBD_N_NDI_EVENTS, hubd_ndi_event_defs};
5860Sstevel@tonic-gate
5870Sstevel@tonic-gate /* events received from parent */
5880Sstevel@tonic-gate static usb_event_t hubd_events = {
5890Sstevel@tonic-gate hubd_disconnect_event_cb,
5900Sstevel@tonic-gate hubd_reconnect_event_cb,
5910Sstevel@tonic-gate hubd_pre_suspend_event_cb,
5920Sstevel@tonic-gate hubd_post_resume_event_cb
5930Sstevel@tonic-gate };
5940Sstevel@tonic-gate
5950Sstevel@tonic-gate
5960Sstevel@tonic-gate /*
5970Sstevel@tonic-gate * hubd_get_soft_state() returns the hubd soft state
5989430SRaymond.Chen@Sun.COM *
5999430SRaymond.Chen@Sun.COM * WUSB support extends this function to support wire adapter class
6009430SRaymond.Chen@Sun.COM * devices. The hubd soft state for the wire adapter class device
6019430SRaymond.Chen@Sun.COM * would be stored in usb_root_hubd field of the usba_device structure,
6029430SRaymond.Chen@Sun.COM * just as the USB host controller drivers do.
6030Sstevel@tonic-gate */
6049430SRaymond.Chen@Sun.COM hubd_t *
hubd_get_soft_state(dev_info_t * dip)6050Sstevel@tonic-gate hubd_get_soft_state(dev_info_t *dip)
6060Sstevel@tonic-gate {
6070Sstevel@tonic-gate if (dip == NULL) {
6080Sstevel@tonic-gate
6090Sstevel@tonic-gate return (NULL);
6100Sstevel@tonic-gate }
6110Sstevel@tonic-gate
6129430SRaymond.Chen@Sun.COM if (usba_is_root_hub(dip) || usba_is_wa(dip)) {
6130Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(dip);
6140Sstevel@tonic-gate
6150Sstevel@tonic-gate return (usba_device->usb_root_hubd);
6160Sstevel@tonic-gate } else {
6170Sstevel@tonic-gate int instance = ddi_get_instance(dip);
6180Sstevel@tonic-gate
6190Sstevel@tonic-gate return (ddi_get_soft_state(hubd_statep, instance));
6200Sstevel@tonic-gate }
6210Sstevel@tonic-gate }
6220Sstevel@tonic-gate
6230Sstevel@tonic-gate
6240Sstevel@tonic-gate /*
6250Sstevel@tonic-gate * PM support functions:
6260Sstevel@tonic-gate */
6270Sstevel@tonic-gate /*ARGSUSED*/
6280Sstevel@tonic-gate static void
hubd_pm_busy_component(hubd_t * hubd,dev_info_t * dip,int component)6290Sstevel@tonic-gate hubd_pm_busy_component(hubd_t *hubd, dev_info_t *dip, int component)
6300Sstevel@tonic-gate {
6310Sstevel@tonic-gate if (hubd->h_hubpm != NULL) {
6320Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm++;
6330Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
6340Sstevel@tonic-gate if (pm_busy_component(dip, 0) != DDI_SUCCESS) {
6350Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
6360Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm--;
6370Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
6380Sstevel@tonic-gate }
6390Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
6400Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6410Sstevel@tonic-gate "hubd_pm_busy_component: %d", hubd->h_hubpm->hubp_busy_pm);
6420Sstevel@tonic-gate }
6430Sstevel@tonic-gate }
6440Sstevel@tonic-gate
6450Sstevel@tonic-gate
6460Sstevel@tonic-gate /*ARGSUSED*/
6470Sstevel@tonic-gate static void
hubd_pm_idle_component(hubd_t * hubd,dev_info_t * dip,int component)6480Sstevel@tonic-gate hubd_pm_idle_component(hubd_t *hubd, dev_info_t *dip, int component)
6490Sstevel@tonic-gate {
6500Sstevel@tonic-gate if (hubd->h_hubpm != NULL) {
6510Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
6520Sstevel@tonic-gate if (pm_idle_component(dip, 0) == DDI_SUCCESS) {
6530Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
6540Sstevel@tonic-gate ASSERT(hubd->h_hubpm->hubp_busy_pm > 0);
6550Sstevel@tonic-gate hubd->h_hubpm->hubp_busy_pm--;
6560Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
6570Sstevel@tonic-gate }
6580Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
6590Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6600Sstevel@tonic-gate "hubd_pm_idle_component: %d", hubd->h_hubpm->hubp_busy_pm);
6610Sstevel@tonic-gate }
6620Sstevel@tonic-gate }
6630Sstevel@tonic-gate
6640Sstevel@tonic-gate
6650Sstevel@tonic-gate /*
6660Sstevel@tonic-gate * track power level changes for children of this instance
6670Sstevel@tonic-gate */
6680Sstevel@tonic-gate static void
hubd_set_child_pwrlvl(hubd_t * hubd,usb_port_t port,uint8_t power)6690Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd_t *hubd, usb_port_t port, uint8_t power)
6700Sstevel@tonic-gate {
6710Sstevel@tonic-gate int old_power, new_power, pwr;
6720Sstevel@tonic-gate usb_port_t portno;
6730Sstevel@tonic-gate hub_power_t *hubpm;
6740Sstevel@tonic-gate
6750Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6760Sstevel@tonic-gate "hubd_set_child_pwrlvl: port=%d power=%d",
6770Sstevel@tonic-gate port, power);
6780Sstevel@tonic-gate
6790Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
6800Sstevel@tonic-gate hubpm = hubd->h_hubpm;
6810Sstevel@tonic-gate
6820Sstevel@tonic-gate old_power = 0;
6830Sstevel@tonic-gate for (portno = 1; portno <= hubd->h_hub_descr.bNbrPorts; portno++) {
6840Sstevel@tonic-gate old_power += hubpm->hubp_child_pwrstate[portno];
6850Sstevel@tonic-gate }
6860Sstevel@tonic-gate
6870Sstevel@tonic-gate /* assign the port power */
6880Sstevel@tonic-gate pwr = hubd->h_hubpm->hubp_child_pwrstate[port];
6890Sstevel@tonic-gate hubd->h_hubpm->hubp_child_pwrstate[port] = power;
6900Sstevel@tonic-gate new_power = old_power - pwr + power;
6910Sstevel@tonic-gate
6920Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
6930Sstevel@tonic-gate "hubd_set_child_pwrlvl: new_power=%d old_power=%d",
6940Sstevel@tonic-gate new_power, old_power);
6950Sstevel@tonic-gate
6960Sstevel@tonic-gate if ((new_power > 0) && (old_power == 0)) {
6970Sstevel@tonic-gate /* we have the first child coming out of low power */
6980Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
6990Sstevel@tonic-gate } else if ((new_power == 0) && (old_power > 0)) {
7000Sstevel@tonic-gate /* we have the last child going to low power */
7010Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
7020Sstevel@tonic-gate }
7030Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
7040Sstevel@tonic-gate }
7050Sstevel@tonic-gate
7060Sstevel@tonic-gate
7070Sstevel@tonic-gate /*
7080Sstevel@tonic-gate * given a child dip, locate its port number
7090Sstevel@tonic-gate */
7100Sstevel@tonic-gate static usb_port_t
hubd_child_dip2port(hubd_t * hubd,dev_info_t * dip)7110Sstevel@tonic-gate hubd_child_dip2port(hubd_t *hubd, dev_info_t *dip)
7120Sstevel@tonic-gate {
7130Sstevel@tonic-gate usb_port_t port;
7140Sstevel@tonic-gate
7150Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
7160Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
7170Sstevel@tonic-gate if (hubd->h_children_dips[port] == dip) {
7180Sstevel@tonic-gate
7190Sstevel@tonic-gate break;
7200Sstevel@tonic-gate }
7210Sstevel@tonic-gate }
7220Sstevel@tonic-gate ASSERT(port <= hubd->h_hub_descr.bNbrPorts);
7230Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
7240Sstevel@tonic-gate
7250Sstevel@tonic-gate return (port);
7260Sstevel@tonic-gate }
7270Sstevel@tonic-gate
7280Sstevel@tonic-gate
7290Sstevel@tonic-gate /*
7300Sstevel@tonic-gate * if the hub can be put into low power mode, return success
7310Sstevel@tonic-gate * NOTE: suspend here means going to lower power, not CPR suspend.
7320Sstevel@tonic-gate */
7330Sstevel@tonic-gate static int
hubd_can_suspend(hubd_t * hubd)7340Sstevel@tonic-gate hubd_can_suspend(hubd_t *hubd)
7350Sstevel@tonic-gate {
7360Sstevel@tonic-gate hub_power_t *hubpm;
7370Sstevel@tonic-gate int total_power = 0;
7380Sstevel@tonic-gate usb_port_t port;
7390Sstevel@tonic-gate
7400Sstevel@tonic-gate hubpm = hubd->h_hubpm;
7410Sstevel@tonic-gate
7420Sstevel@tonic-gate if (DEVI_IS_DETACHING(hubd->h_dip)) {
7430Sstevel@tonic-gate
7440Sstevel@tonic-gate return (USB_SUCCESS);
7450Sstevel@tonic-gate }
7460Sstevel@tonic-gate
7470Sstevel@tonic-gate /*
7480Sstevel@tonic-gate * Don't go to lower power if haven't been at full power for enough
7490Sstevel@tonic-gate * time to let hotplug thread kickoff.
7500Sstevel@tonic-gate */
7510Sstevel@tonic-gate if (ddi_get_time() < (hubpm->hubp_time_at_full_power +
7520Sstevel@tonic-gate hubpm->hubp_min_pm_threshold)) {
7530Sstevel@tonic-gate
7540Sstevel@tonic-gate return (USB_FAILURE);
7550Sstevel@tonic-gate }
7560Sstevel@tonic-gate
7570Sstevel@tonic-gate for (port = 1; (total_power == 0) &&
7580Sstevel@tonic-gate (port <= hubd->h_hub_descr.bNbrPorts); port++) {
7590Sstevel@tonic-gate total_power += hubpm->hubp_child_pwrstate[port];
7600Sstevel@tonic-gate }
7610Sstevel@tonic-gate
7620Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7634763Slg150142 "hubd_can_suspend: %d", total_power);
7640Sstevel@tonic-gate
7650Sstevel@tonic-gate return (total_power ? USB_FAILURE : USB_SUCCESS);
7660Sstevel@tonic-gate }
7670Sstevel@tonic-gate
7680Sstevel@tonic-gate
7690Sstevel@tonic-gate /*
7700Sstevel@tonic-gate * resume port depending on current device state
7710Sstevel@tonic-gate */
7720Sstevel@tonic-gate static int
hubd_resume_port(hubd_t * hubd,usb_port_t port)7730Sstevel@tonic-gate hubd_resume_port(hubd_t *hubd, usb_port_t port)
7740Sstevel@tonic-gate {
7750Sstevel@tonic-gate int rval, retry;
7760Sstevel@tonic-gate usb_cr_t completion_reason;
7770Sstevel@tonic-gate usb_cb_flags_t cb_flags;
7780Sstevel@tonic-gate uint16_t status;
7790Sstevel@tonic-gate uint16_t change;
7800Sstevel@tonic-gate int retval = USB_FAILURE;
7810Sstevel@tonic-gate
7820Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
7830Sstevel@tonic-gate
7840Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7850Sstevel@tonic-gate "hubd_resume_port: port=%d state=0x%x (%s)", port,
7860Sstevel@tonic-gate hubd->h_dev_state, usb_str_dev_state(hubd->h_dev_state));
7870Sstevel@tonic-gate
7880Sstevel@tonic-gate switch (hubd->h_dev_state) {
7890Sstevel@tonic-gate case USB_DEV_HUB_CHILD_PWRLVL:
7900Sstevel@tonic-gate /*
7910Sstevel@tonic-gate * This could be a bus ctl for a port other than the one
7920Sstevel@tonic-gate * that has a remote wakeup condition. So check.
7930Sstevel@tonic-gate */
7940Sstevel@tonic-gate if ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0) {
7950Sstevel@tonic-gate /* the port isn't suspended, so don't resume */
7960Sstevel@tonic-gate retval = USB_SUCCESS;
7970Sstevel@tonic-gate
7980Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
7990Sstevel@tonic-gate "hubd_resume_port: port=%d not suspended", port);
8000Sstevel@tonic-gate
8010Sstevel@tonic-gate break;
8020Sstevel@tonic-gate }
8030Sstevel@tonic-gate /*
8040Sstevel@tonic-gate * Device has initiated a wakeup.
8050Sstevel@tonic-gate * Issue a ClearFeature(PortSuspend)
8060Sstevel@tonic-gate */
8070Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
8080Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
8090Sstevel@tonic-gate hubd->h_default_pipe,
8101001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
8110Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE,
8120Sstevel@tonic-gate CFS_PORT_SUSPEND,
8130Sstevel@tonic-gate port,
8140Sstevel@tonic-gate 0, NULL, 0,
8150Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
8160Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
8170Sstevel@tonic-gate "ClearFeature(PortSuspend) fails "
8180Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", rval,
8190Sstevel@tonic-gate completion_reason, cb_flags);
8200Sstevel@tonic-gate }
8210Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
8220Sstevel@tonic-gate
8230Sstevel@tonic-gate /* either way ack changes on the port */
8240Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port,
8254763Slg150142 &status, &change, PORT_CHANGE_PSSC);
8260Sstevel@tonic-gate retval = USB_SUCCESS;
8270Sstevel@tonic-gate
8280Sstevel@tonic-gate break;
8290Sstevel@tonic-gate case USB_DEV_HUB_STATE_RECOVER:
8300Sstevel@tonic-gate /*
8310Sstevel@tonic-gate * When hubd's connect event callback posts a connect
8320Sstevel@tonic-gate * event to its child, it results in this busctl call
8330Sstevel@tonic-gate * which is valid
8340Sstevel@tonic-gate */
8350Sstevel@tonic-gate /* FALLTHRU */
8360Sstevel@tonic-gate case USB_DEV_ONLINE:
8372651Ssl147100 if (((hubd->h_port_state[port] & PORT_STATUS_CCS) == 0) ||
8382651Ssl147100 ((hubd->h_port_state[port] & PORT_STATUS_PSS) == 0)) {
8390Sstevel@tonic-gate /*
8400Sstevel@tonic-gate * the port isn't suspended, or connected
8410Sstevel@tonic-gate * so don't resume
8420Sstevel@tonic-gate */
8430Sstevel@tonic-gate retval = USB_SUCCESS;
8440Sstevel@tonic-gate
8450Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
8460Sstevel@tonic-gate "hubd_resume_port: port=%d not suspended", port);
8470Sstevel@tonic-gate
8480Sstevel@tonic-gate break;
8490Sstevel@tonic-gate }
8500Sstevel@tonic-gate /*
8510Sstevel@tonic-gate * prevent kicking off the hotplug thread
8520Sstevel@tonic-gate */
8530Sstevel@tonic-gate hubd->h_hotplug_thread++;
8540Sstevel@tonic-gate hubd_stop_polling(hubd);
8550Sstevel@tonic-gate
8560Sstevel@tonic-gate /* Now ClearFeature(PortSuspend) */
8570Sstevel@tonic-gate for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) {
8580Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
8590Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
8600Sstevel@tonic-gate hubd->h_default_pipe,
8611001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
8620Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE,
8630Sstevel@tonic-gate CFS_PORT_SUSPEND,
8640Sstevel@tonic-gate port,
8650Sstevel@tonic-gate 0, NULL, 0,
8660Sstevel@tonic-gate &completion_reason, &cb_flags, 0);
8670Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
8680Sstevel@tonic-gate if (rval != USB_SUCCESS) {
8690Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM,
8700Sstevel@tonic-gate hubd->h_log_handle,
8710Sstevel@tonic-gate "ClearFeature(PortSuspend) fails"
8720Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x", rval,
8730Sstevel@tonic-gate completion_reason, cb_flags);
8740Sstevel@tonic-gate } else {
8750Sstevel@tonic-gate /*
8760Sstevel@tonic-gate * As per spec section 11.9 and 7.1.7.7
8770Sstevel@tonic-gate * hub need to provide at least 20ms of
8780Sstevel@tonic-gate * resume signalling, and s/w provide 10ms of
8790Sstevel@tonic-gate * recovery time before accessing the port.
8800Sstevel@tonic-gate */
8810Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
8820Sstevel@tonic-gate delay(drv_usectohz(40000));
8830Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
8840Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port,
8850Sstevel@tonic-gate &status, &change, PORT_CHANGE_PSSC);
8860Sstevel@tonic-gate
8870Sstevel@tonic-gate if ((status & PORT_STATUS_PSS) == 0) {
8880Sstevel@tonic-gate /* the port did finally resume */
8890Sstevel@tonic-gate retval = USB_SUCCESS;
8900Sstevel@tonic-gate
8910Sstevel@tonic-gate break;
8920Sstevel@tonic-gate }
8930Sstevel@tonic-gate }
8940Sstevel@tonic-gate }
8950Sstevel@tonic-gate
8960Sstevel@tonic-gate /* allow hotplug thread again */
8970Sstevel@tonic-gate hubd->h_hotplug_thread--;
8980Sstevel@tonic-gate hubd_start_polling(hubd, 0);
8990Sstevel@tonic-gate
9000Sstevel@tonic-gate break;
9010Sstevel@tonic-gate case USB_DEV_DISCONNECTED:
9020Sstevel@tonic-gate /* Ignore - NO Operation */
9030Sstevel@tonic-gate retval = USB_SUCCESS;
9040Sstevel@tonic-gate
9050Sstevel@tonic-gate break;
9060Sstevel@tonic-gate case USB_DEV_SUSPENDED:
9070Sstevel@tonic-gate case USB_DEV_PWRED_DOWN:
9080Sstevel@tonic-gate default:
9090Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
9100Sstevel@tonic-gate "Improper state for port Resume");
9110Sstevel@tonic-gate
9120Sstevel@tonic-gate break;
9130Sstevel@tonic-gate }
9140Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
9150Sstevel@tonic-gate
9160Sstevel@tonic-gate return (retval);
9170Sstevel@tonic-gate }
9180Sstevel@tonic-gate
9190Sstevel@tonic-gate
9200Sstevel@tonic-gate /*
9210Sstevel@tonic-gate * suspend port depending on device state
9220Sstevel@tonic-gate */
9230Sstevel@tonic-gate static int
hubd_suspend_port(hubd_t * hubd,usb_port_t port)9240Sstevel@tonic-gate hubd_suspend_port(hubd_t *hubd, usb_port_t port)
9250Sstevel@tonic-gate {
9260Sstevel@tonic-gate int rval, retry;
9270Sstevel@tonic-gate int retval = USB_FAILURE;
9280Sstevel@tonic-gate usb_cr_t completion_reason;
9290Sstevel@tonic-gate usb_cb_flags_t cb_flags;
9300Sstevel@tonic-gate uint16_t status;
9310Sstevel@tonic-gate uint16_t change;
9320Sstevel@tonic-gate
9330Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
9340Sstevel@tonic-gate "hubd_suspend_port: port=%d", port);
9350Sstevel@tonic-gate
9360Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
9370Sstevel@tonic-gate
9380Sstevel@tonic-gate switch (hubd->h_dev_state) {
9390Sstevel@tonic-gate case USB_DEV_HUB_STATE_RECOVER:
9400Sstevel@tonic-gate /*
9410Sstevel@tonic-gate * When hubd's connect event callback posts a connect
9420Sstevel@tonic-gate * event to its child, it results in this busctl call
9430Sstevel@tonic-gate * which is valid
9440Sstevel@tonic-gate */
9450Sstevel@tonic-gate /* FALLTHRU */
9460Sstevel@tonic-gate case USB_DEV_HUB_CHILD_PWRLVL:
9470Sstevel@tonic-gate /*
9480Sstevel@tonic-gate * When one child is resuming, the other could timeout
9490Sstevel@tonic-gate * and go to low power mode, which is valid
9500Sstevel@tonic-gate */
9510Sstevel@tonic-gate /* FALLTHRU */
9520Sstevel@tonic-gate case USB_DEV_ONLINE:
9530Sstevel@tonic-gate hubd->h_hotplug_thread++;
9540Sstevel@tonic-gate hubd_stop_polling(hubd);
9550Sstevel@tonic-gate
9560Sstevel@tonic-gate /*
9570Sstevel@tonic-gate * Some devices start an unprovoked resume. According to spec,
9580Sstevel@tonic-gate * normal resume time for port is 10ms. Wait for double that
9590Sstevel@tonic-gate * time, then check to be sure port is really suspended.
9600Sstevel@tonic-gate */
9610Sstevel@tonic-gate for (retry = 0; retry < HUBD_SUS_RES_RETRY; retry++) {
9620Sstevel@tonic-gate /* Now SetFeature(PortSuspend) */
9630Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
9640Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
9650Sstevel@tonic-gate hubd->h_default_pipe,
9661001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
9670Sstevel@tonic-gate USB_REQ_SET_FEATURE,
9680Sstevel@tonic-gate CFS_PORT_SUSPEND,
9690Sstevel@tonic-gate port,
9700Sstevel@tonic-gate 0, NULL, 0,
9710Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) !=
9720Sstevel@tonic-gate USB_SUCCESS) {
9730Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM,
9740Sstevel@tonic-gate hubd->h_log_handle,
9750Sstevel@tonic-gate "SetFeature(PortSuspend) fails"
9760Sstevel@tonic-gate "rval=%d cr=%d cb=0x%x",
9770Sstevel@tonic-gate rval, completion_reason, cb_flags);
9780Sstevel@tonic-gate }
9790Sstevel@tonic-gate
9800Sstevel@tonic-gate /*
9810Sstevel@tonic-gate * some devices start an unprovoked resume
9820Sstevel@tonic-gate * wait and check port status after some time
9830Sstevel@tonic-gate */
9840Sstevel@tonic-gate delay(drv_usectohz(20000));
9850Sstevel@tonic-gate
9860Sstevel@tonic-gate /* either ways ack changes on the port */
9870Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
9880Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port,
9890Sstevel@tonic-gate &status, &change, PORT_CHANGE_PSSC);
9900Sstevel@tonic-gate if (status & PORT_STATUS_PSS) {
9910Sstevel@tonic-gate /* the port is indeed suspended */
9920Sstevel@tonic-gate retval = USB_SUCCESS;
9930Sstevel@tonic-gate
9940Sstevel@tonic-gate break;
995*12819SVincent.Wang@Sun.COM } else {
996*12819SVincent.Wang@Sun.COM USB_DPRINTF_L0(DPRINT_MASK_PM,
997*12819SVincent.Wang@Sun.COM hubd->h_log_handle,
998*12819SVincent.Wang@Sun.COM "hubdi: port%d failed to be suspended!",
999*12819SVincent.Wang@Sun.COM port);
10000Sstevel@tonic-gate }
10010Sstevel@tonic-gate }
10020Sstevel@tonic-gate
10030Sstevel@tonic-gate hubd->h_hotplug_thread--;
10040Sstevel@tonic-gate hubd_start_polling(hubd, 0);
10050Sstevel@tonic-gate
10060Sstevel@tonic-gate break;
10070Sstevel@tonic-gate
10080Sstevel@tonic-gate case USB_DEV_DISCONNECTED:
10090Sstevel@tonic-gate /* Ignore - No Operation */
10100Sstevel@tonic-gate retval = USB_SUCCESS;
10110Sstevel@tonic-gate
10120Sstevel@tonic-gate break;
10130Sstevel@tonic-gate
10140Sstevel@tonic-gate case USB_DEV_SUSPENDED:
10150Sstevel@tonic-gate case USB_DEV_PWRED_DOWN:
10160Sstevel@tonic-gate default:
10170Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
10180Sstevel@tonic-gate "Improper state for port Suspend");
10190Sstevel@tonic-gate
10200Sstevel@tonic-gate break;
10210Sstevel@tonic-gate }
10220Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
10230Sstevel@tonic-gate
10240Sstevel@tonic-gate return (retval);
10250Sstevel@tonic-gate }
10260Sstevel@tonic-gate
10270Sstevel@tonic-gate
10280Sstevel@tonic-gate /*
10290Sstevel@tonic-gate * child post attach/detach notifications
10300Sstevel@tonic-gate */
10310Sstevel@tonic-gate static void
hubd_post_attach(hubd_t * hubd,usb_port_t port,struct attachspec * as)10320Sstevel@tonic-gate hubd_post_attach(hubd_t *hubd, usb_port_t port, struct attachspec *as)
10330Sstevel@tonic-gate {
10340Sstevel@tonic-gate dev_info_t *dip;
10350Sstevel@tonic-gate
10360Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
10370Sstevel@tonic-gate "hubd_post_attach: port=%d result=%d",
10380Sstevel@tonic-gate port, as->result);
10390Sstevel@tonic-gate
10400Sstevel@tonic-gate if (as->result == DDI_SUCCESS) {
10410Sstevel@tonic-gate /*
10420Sstevel@tonic-gate * Check if the child created wants to be power managed.
10430Sstevel@tonic-gate * If yes, the childs power level gets automatically tracked
10440Sstevel@tonic-gate * by DDI_CTLOPS_POWER busctl.
10450Sstevel@tonic-gate * If no, we set power of the new child by default
10460Sstevel@tonic-gate * to USB_DEV_OS_FULL_PWR. Because we should never suspend.
10470Sstevel@tonic-gate */
10480Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
10490Sstevel@tonic-gate dip = hubd->h_children_dips[port];
10500Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
10510Sstevel@tonic-gate if (DEVI(dip)->devi_pm_info == NULL) {
10520Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_FULL_PWR);
10530Sstevel@tonic-gate }
10540Sstevel@tonic-gate }
10550Sstevel@tonic-gate }
10560Sstevel@tonic-gate
10570Sstevel@tonic-gate
10580Sstevel@tonic-gate static void
hubd_post_detach(hubd_t * hubd,usb_port_t port,struct detachspec * ds)10590Sstevel@tonic-gate hubd_post_detach(hubd_t *hubd, usb_port_t port, struct detachspec *ds)
10600Sstevel@tonic-gate {
10610Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
10620Sstevel@tonic-gate "hubd_post_detach: port=%d result=%d", port, ds->result);
10630Sstevel@tonic-gate
10640Sstevel@tonic-gate /*
10650Sstevel@tonic-gate * if the device is successfully detached and is the
10660Sstevel@tonic-gate * last device to detach, mark component as idle
10670Sstevel@tonic-gate */
10680Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
10690Sstevel@tonic-gate if (ds->result == DDI_SUCCESS) {
10700Sstevel@tonic-gate usba_device_t *usba_device = hubd->h_usba_devices[port];
10711001Ssl147100 dev_info_t *pdip = hubd->h_dip;
10720Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
10730Sstevel@tonic-gate
10741001Ssl147100 usba_hubdi_incr_power_budget(pdip, usba_device);
10751001Ssl147100
10760Sstevel@tonic-gate /*
10770Sstevel@tonic-gate * We set power of the detached child
10780Sstevel@tonic-gate * to 0, so that we can suspend if all
10790Sstevel@tonic-gate * our children are gone
10800Sstevel@tonic-gate */
10810Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, USB_DEV_OS_PWR_OFF);
10820Sstevel@tonic-gate
10830Sstevel@tonic-gate /* check for leaks on detaching */
10840Sstevel@tonic-gate if ((usba_device) && (ds->cmd == DDI_DETACH)) {
10850Sstevel@tonic-gate usba_check_for_leaks(usba_device);
10860Sstevel@tonic-gate }
10870Sstevel@tonic-gate } else {
10880Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
10890Sstevel@tonic-gate }
10900Sstevel@tonic-gate }
10910Sstevel@tonic-gate
10920Sstevel@tonic-gate
10930Sstevel@tonic-gate /*
10940Sstevel@tonic-gate * hubd_post_power
10950Sstevel@tonic-gate * After the child's power entry point has been called
10960Sstevel@tonic-gate * we record its power level in our local struct.
10970Sstevel@tonic-gate * If the device has powered off, we suspend port
10980Sstevel@tonic-gate */
10990Sstevel@tonic-gate static int
hubd_post_power(hubd_t * hubd,usb_port_t port,pm_bp_child_pwrchg_t * bpc,int result)11000Sstevel@tonic-gate hubd_post_power(hubd_t *hubd, usb_port_t port, pm_bp_child_pwrchg_t *bpc,
11010Sstevel@tonic-gate int result)
11020Sstevel@tonic-gate {
11030Sstevel@tonic-gate int retval = USB_SUCCESS;
11040Sstevel@tonic-gate
11050Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
11060Sstevel@tonic-gate "hubd_post_power: port=%d", port);
11070Sstevel@tonic-gate
11080Sstevel@tonic-gate if (result == DDI_SUCCESS) {
11090Sstevel@tonic-gate
11100Sstevel@tonic-gate /* record this power in our local struct */
11110Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, bpc->bpc_nlevel);
11120Sstevel@tonic-gate
11130Sstevel@tonic-gate if (bpc->bpc_nlevel == USB_DEV_OS_PWR_OFF) {
11140Sstevel@tonic-gate
11150Sstevel@tonic-gate /* now suspend the port */
11160Sstevel@tonic-gate retval = hubd_suspend_port(hubd, port);
11170Sstevel@tonic-gate } else if (bpc->bpc_nlevel == USB_DEV_OS_FULL_PWR) {
11180Sstevel@tonic-gate
11190Sstevel@tonic-gate /* make sure the port is resumed */
11200Sstevel@tonic-gate retval = hubd_resume_port(hubd, port);
11210Sstevel@tonic-gate }
11220Sstevel@tonic-gate } else {
11230Sstevel@tonic-gate
11240Sstevel@tonic-gate /* record old power in our local struct */
11250Sstevel@tonic-gate hubd_set_child_pwrlvl(hubd, port, bpc->bpc_olevel);
11260Sstevel@tonic-gate
11270Sstevel@tonic-gate if (bpc->bpc_olevel == USB_DEV_OS_PWR_OFF) {
11280Sstevel@tonic-gate
11290Sstevel@tonic-gate /*
11300Sstevel@tonic-gate * As this device failed to transition from
11310Sstevel@tonic-gate * power off state, suspend the port again
11320Sstevel@tonic-gate */
11330Sstevel@tonic-gate retval = hubd_suspend_port(hubd, port);
11340Sstevel@tonic-gate }
11350Sstevel@tonic-gate }
11360Sstevel@tonic-gate
11370Sstevel@tonic-gate return (retval);
11380Sstevel@tonic-gate }
11390Sstevel@tonic-gate
11400Sstevel@tonic-gate
11410Sstevel@tonic-gate /*
11420Sstevel@tonic-gate * bus ctl notifications are handled here, the rest goes up to root hub/hcd
11430Sstevel@tonic-gate */
11440Sstevel@tonic-gate static int
usba_hubdi_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)11450Sstevel@tonic-gate usba_hubdi_bus_ctl(dev_info_t *dip,
11460Sstevel@tonic-gate dev_info_t *rdip,
11470Sstevel@tonic-gate ddi_ctl_enum_t op,
11480Sstevel@tonic-gate void *arg,
11490Sstevel@tonic-gate void *result)
11500Sstevel@tonic-gate {
11510Sstevel@tonic-gate usba_device_t *hub_usba_device = usba_get_usba_device(rdip);
11520Sstevel@tonic-gate dev_info_t *root_hub_dip = hub_usba_device->usb_root_hub_dip;
11530Sstevel@tonic-gate struct attachspec *as;
11540Sstevel@tonic-gate struct detachspec *ds;
11550Sstevel@tonic-gate hubd_t *hubd;
11560Sstevel@tonic-gate usb_port_t port;
11570Sstevel@tonic-gate int circ, rval;
11580Sstevel@tonic-gate int retval = DDI_FAILURE;
11590Sstevel@tonic-gate
11600Sstevel@tonic-gate hubd = hubd_get_soft_state(dip);
11610Sstevel@tonic-gate
11620Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
11630Sstevel@tonic-gate
11640Sstevel@tonic-gate /* flag that we are currently running bus_ctl */
11650Sstevel@tonic-gate hubd->h_bus_ctls++;
11660Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
11670Sstevel@tonic-gate
11680Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
11690Sstevel@tonic-gate "usba_hubdi_bus_ctl:\n\t"
11700Sstevel@tonic-gate "dip=0x%p, rdip=0x%p, op=0x%x, arg=0x%p",
11716898Sfb209375 (void *)dip, (void *)rdip, op, arg);
11720Sstevel@tonic-gate
11730Sstevel@tonic-gate switch (op) {
11740Sstevel@tonic-gate case DDI_CTLOPS_ATTACH:
11750Sstevel@tonic-gate as = (struct attachspec *)arg;
11760Sstevel@tonic-gate port = hubd_child_dip2port(hubd, rdip);
11770Sstevel@tonic-gate
11780Sstevel@tonic-gate /* there is nothing to do at resume time */
11790Sstevel@tonic-gate if (as->cmd == DDI_RESUME) {
11800Sstevel@tonic-gate break;
11810Sstevel@tonic-gate }
11820Sstevel@tonic-gate
11830Sstevel@tonic-gate /* serialize access */
11840Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ);
11850Sstevel@tonic-gate
11860Sstevel@tonic-gate switch (as->when) {
11870Sstevel@tonic-gate case DDI_PRE:
11880Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
11890Sstevel@tonic-gate "DDI_PRE DDI_CTLOPS_ATTACH: dip=%p, port=%d",
11906898Sfb209375 (void *)rdip, port);
11910Sstevel@tonic-gate
11920Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
11930Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_ATTACHING;
11940Sstevel@tonic-gate
11950Sstevel@tonic-gate /* Go busy here. Matching idle is DDI_POST case. */
11960Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0);
11970Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
11980Sstevel@tonic-gate
11990Sstevel@tonic-gate /*
12000Sstevel@tonic-gate * if we suspended the port previously
12010Sstevel@tonic-gate * because child went to low power state, and
12020Sstevel@tonic-gate * someone unloaded the driver, the port would
12030Sstevel@tonic-gate * still be suspended and needs to be resumed
12040Sstevel@tonic-gate */
12050Sstevel@tonic-gate rval = hubd_resume_port(hubd, port);
12060Sstevel@tonic-gate if (rval == USB_SUCCESS) {
12070Sstevel@tonic-gate retval = DDI_SUCCESS;
12080Sstevel@tonic-gate }
12090Sstevel@tonic-gate
12100Sstevel@tonic-gate break;
12110Sstevel@tonic-gate case DDI_POST:
12120Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
12130Sstevel@tonic-gate "DDI_POST DDI_CTLOPS_ATTACH: dip=%p, port=%d",
12146898Sfb209375 (void *)rdip, port);
12150Sstevel@tonic-gate
12160Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
12170Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ATTACHING;
12180Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
12190Sstevel@tonic-gate
12200Sstevel@tonic-gate hubd_post_attach(hubd, port, (struct attachspec *)arg);
12210Sstevel@tonic-gate retval = DDI_SUCCESS;
12220Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
12230Sstevel@tonic-gate
12240Sstevel@tonic-gate /* Matching idle call for DDI_PRE busy call. */
12250Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0);
12260Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
12270Sstevel@tonic-gate }
12280Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ);
12290Sstevel@tonic-gate
12300Sstevel@tonic-gate break;
12310Sstevel@tonic-gate case DDI_CTLOPS_DETACH:
12320Sstevel@tonic-gate ds = (struct detachspec *)arg;
12330Sstevel@tonic-gate port = hubd_child_dip2port(hubd, rdip);
12340Sstevel@tonic-gate
12350Sstevel@tonic-gate /* there is nothing to do at suspend time */
12360Sstevel@tonic-gate if (ds->cmd == DDI_SUSPEND) {
12370Sstevel@tonic-gate break;
12380Sstevel@tonic-gate }
12390Sstevel@tonic-gate
12400Sstevel@tonic-gate /* serialize access */
12410Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ);
12420Sstevel@tonic-gate
12430Sstevel@tonic-gate switch (ds->when) {
12440Sstevel@tonic-gate case DDI_PRE:
12450Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
12460Sstevel@tonic-gate "DDI_PRE DDI_CTLOPS_DETACH: dip=%p port=%d",
12476898Sfb209375 (void *)rdip, port);
12480Sstevel@tonic-gate
12490Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
12500Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_DETACHING;
12510Sstevel@tonic-gate
12520Sstevel@tonic-gate /* Go busy here. Matching idle is DDI_POST case. */
12530Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0);
12540Sstevel@tonic-gate
12550Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
12560Sstevel@tonic-gate retval = DDI_SUCCESS;
12570Sstevel@tonic-gate
12580Sstevel@tonic-gate break;
12590Sstevel@tonic-gate case DDI_POST:
12600Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
12610Sstevel@tonic-gate "DDI_POST DDI_CTLOPS_DETACH: dip=%p port=%d",
12626898Sfb209375 (void *)rdip, port);
12630Sstevel@tonic-gate
12640Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
12650Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_DETACHING;
12660Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
12670Sstevel@tonic-gate
12680Sstevel@tonic-gate /* Matching idle call for DDI_PRE busy call. */
12690Sstevel@tonic-gate hubd_post_detach(hubd, port, (struct detachspec *)arg);
12700Sstevel@tonic-gate retval = DDI_SUCCESS;
12710Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
12720Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0);
12730Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
12740Sstevel@tonic-gate
12750Sstevel@tonic-gate break;
12760Sstevel@tonic-gate }
12770Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ);
12780Sstevel@tonic-gate
12790Sstevel@tonic-gate break;
12800Sstevel@tonic-gate default:
12810Sstevel@tonic-gate retval = usba_bus_ctl(root_hub_dip, rdip, op, arg, result);
12820Sstevel@tonic-gate }
12830Sstevel@tonic-gate
12840Sstevel@tonic-gate /* decrement bus_ctls count */
12850Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
12860Sstevel@tonic-gate hubd->h_bus_ctls--;
12870Sstevel@tonic-gate ASSERT(hubd->h_bus_ctls >= 0);
12880Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
12890Sstevel@tonic-gate
12900Sstevel@tonic-gate return (retval);
12910Sstevel@tonic-gate }
12920Sstevel@tonic-gate
129310783SVincent.Wang@Sun.COM /*
129410783SVincent.Wang@Sun.COM * hubd_config_one:
129510783SVincent.Wang@Sun.COM * enumerate one child according to 'port'
129610783SVincent.Wang@Sun.COM */
129710783SVincent.Wang@Sun.COM
129810783SVincent.Wang@Sun.COM static boolean_t
hubd_config_one(hubd_t * hubd,int port)129910783SVincent.Wang@Sun.COM hubd_config_one(hubd_t *hubd, int port)
130010783SVincent.Wang@Sun.COM {
130110783SVincent.Wang@Sun.COM uint16_t status, change;
130210783SVincent.Wang@Sun.COM dev_info_t *hdip = hubd->h_dip;
130310783SVincent.Wang@Sun.COM dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
130410783SVincent.Wang@Sun.COM boolean_t online_child = B_FALSE, found = B_FALSE;
130510783SVincent.Wang@Sun.COM int prh_circ, rh_circ, circ;
130610783SVincent.Wang@Sun.COM
130710783SVincent.Wang@Sun.COM USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
130810783SVincent.Wang@Sun.COM "hubd_config_one: started, hubd_reset_port = 0x%x", port);
130910783SVincent.Wang@Sun.COM
131010783SVincent.Wang@Sun.COM ndi_hold_devi(hdip); /* so we don't race with detach */
131110783SVincent.Wang@Sun.COM
131210783SVincent.Wang@Sun.COM /*
131310783SVincent.Wang@Sun.COM * this ensures one config activity per system at a time.
131410783SVincent.Wang@Sun.COM * we enter the parent PCI node to have this serialization.
131510783SVincent.Wang@Sun.COM * this also excludes ioctls and deathrow thread
131610783SVincent.Wang@Sun.COM */
131710783SVincent.Wang@Sun.COM ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
131810783SVincent.Wang@Sun.COM ndi_devi_enter(rh_dip, &rh_circ);
131910783SVincent.Wang@Sun.COM
132010783SVincent.Wang@Sun.COM /* exclude other threads */
132110783SVincent.Wang@Sun.COM ndi_devi_enter(hdip, &circ);
132210783SVincent.Wang@Sun.COM mutex_enter(HUBD_MUTEX(hubd));
132310783SVincent.Wang@Sun.COM
132411214SVincent.Wang@Sun.COM hubd_pm_busy_component(hubd, hubd->h_dip, 0);
132511214SVincent.Wang@Sun.COM
132610783SVincent.Wang@Sun.COM if (!hubd->h_children_dips[port]) {
132710783SVincent.Wang@Sun.COM
132810783SVincent.Wang@Sun.COM (void) hubd_determine_port_status(hubd, port,
132910783SVincent.Wang@Sun.COM &status, &change, HUBD_ACK_ALL_CHANGES);
133010783SVincent.Wang@Sun.COM
133110783SVincent.Wang@Sun.COM if (status & PORT_STATUS_CCS) {
133210783SVincent.Wang@Sun.COM online_child |= (hubd_handle_port_connect(hubd,
133310783SVincent.Wang@Sun.COM port) == USB_SUCCESS);
133410783SVincent.Wang@Sun.COM found = online_child;
133510783SVincent.Wang@Sun.COM }
133610783SVincent.Wang@Sun.COM } else {
133710783SVincent.Wang@Sun.COM found = B_TRUE;
133810783SVincent.Wang@Sun.COM }
133910783SVincent.Wang@Sun.COM
134010783SVincent.Wang@Sun.COM mutex_exit(HUBD_MUTEX(hubd));
134110783SVincent.Wang@Sun.COM
134210783SVincent.Wang@Sun.COM ndi_devi_exit(hdip, circ);
134310783SVincent.Wang@Sun.COM ndi_devi_exit(rh_dip, rh_circ);
134410783SVincent.Wang@Sun.COM ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
134510783SVincent.Wang@Sun.COM
134610783SVincent.Wang@Sun.COM if (online_child) {
134710783SVincent.Wang@Sun.COM USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
134810783SVincent.Wang@Sun.COM "hubd_config_one: onlining child");
134910783SVincent.Wang@Sun.COM
135010783SVincent.Wang@Sun.COM (void) ndi_devi_online(hubd->h_dip, 0);
135110783SVincent.Wang@Sun.COM }
135210783SVincent.Wang@Sun.COM
135310783SVincent.Wang@Sun.COM mutex_enter(HUBD_MUTEX(hubd));
135410783SVincent.Wang@Sun.COM
135510783SVincent.Wang@Sun.COM (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
135610783SVincent.Wang@Sun.COM
135710783SVincent.Wang@Sun.COM USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
135810783SVincent.Wang@Sun.COM "hubd_config_one: exit");
135910783SVincent.Wang@Sun.COM
136010783SVincent.Wang@Sun.COM mutex_exit(HUBD_MUTEX(hubd));
136110783SVincent.Wang@Sun.COM
136210783SVincent.Wang@Sun.COM ndi_rele_devi(hdip);
136310783SVincent.Wang@Sun.COM
136410783SVincent.Wang@Sun.COM return (found);
136510783SVincent.Wang@Sun.COM }
13660Sstevel@tonic-gate
13670Sstevel@tonic-gate /*
13680Sstevel@tonic-gate * bus enumeration entry points
13690Sstevel@tonic-gate */
13700Sstevel@tonic-gate static int
hubd_bus_config(dev_info_t * dip,uint_t flag,ddi_bus_config_op_t op,void * arg,dev_info_t ** child)13710Sstevel@tonic-gate hubd_bus_config(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
13720Sstevel@tonic-gate void *arg, dev_info_t **child)
13730Sstevel@tonic-gate {
13740Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip);
13750Sstevel@tonic-gate int rval, circ;
137610783SVincent.Wang@Sun.COM long port;
13770Sstevel@tonic-gate
13780Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
13790Sstevel@tonic-gate "hubd_bus_config: op=%d", op);
13800Sstevel@tonic-gate
13810Sstevel@tonic-gate if (hubdi_bus_config_debug) {
13820Sstevel@tonic-gate flag |= NDI_DEVI_DEBUG;
13830Sstevel@tonic-gate }
13840Sstevel@tonic-gate
138510783SVincent.Wang@Sun.COM if (op == BUS_CONFIG_ONE) {
138610783SVincent.Wang@Sun.COM boolean_t found;
13879595SGuoqing.Zhu@Sun.COM char cname[80];
13889595SGuoqing.Zhu@Sun.COM char *name, *addr;
13899595SGuoqing.Zhu@Sun.COM
139010783SVincent.Wang@Sun.COM USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
139110783SVincent.Wang@Sun.COM "hubd_bus_config: op=%d (BUS_CONFIG_ONE)", op);
139210783SVincent.Wang@Sun.COM
13939595SGuoqing.Zhu@Sun.COM (void) snprintf(cname, 80, "%s", (char *)arg);
13949595SGuoqing.Zhu@Sun.COM /* split name into "name@addr" parts */
13959595SGuoqing.Zhu@Sun.COM i_ddi_parse_name(cname, &name, &addr, NULL);
139610783SVincent.Wang@Sun.COM if (addr && *addr) {
139710783SVincent.Wang@Sun.COM (void) ddi_strtol(addr, NULL, 16, &port);
139810783SVincent.Wang@Sun.COM } else {
139910783SVincent.Wang@Sun.COM return (NDI_FAILURE);
140010783SVincent.Wang@Sun.COM }
140110783SVincent.Wang@Sun.COM
140210783SVincent.Wang@Sun.COM found = hubd_config_one(hubd, port);
140310783SVincent.Wang@Sun.COM
140410783SVincent.Wang@Sun.COM if (found == 0) {
14059595SGuoqing.Zhu@Sun.COM return (NDI_FAILURE);
14069595SGuoqing.Zhu@Sun.COM }
14079595SGuoqing.Zhu@Sun.COM
14080Sstevel@tonic-gate }
14090Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ);
14100Sstevel@tonic-gate rval = ndi_busop_bus_config(dip, flag, op, arg, child, 0);
14110Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ);
14120Sstevel@tonic-gate
14130Sstevel@tonic-gate return (rval);
14140Sstevel@tonic-gate }
14150Sstevel@tonic-gate
14160Sstevel@tonic-gate
14170Sstevel@tonic-gate static int
hubd_bus_unconfig(dev_info_t * dip,uint_t flag,ddi_bus_config_op_t op,void * arg)14180Sstevel@tonic-gate hubd_bus_unconfig(dev_info_t *dip, uint_t flag, ddi_bus_config_op_t op,
14190Sstevel@tonic-gate void *arg)
14200Sstevel@tonic-gate {
14210Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip);
14220Sstevel@tonic-gate dev_info_t *cdip;
14230Sstevel@tonic-gate usb_port_t port;
14240Sstevel@tonic-gate int circ;
14250Sstevel@tonic-gate int rval;
14260Sstevel@tonic-gate
14270Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
14280Sstevel@tonic-gate "hubd_bus_unconfig: op=%d", op);
14290Sstevel@tonic-gate
14300Sstevel@tonic-gate if (hubdi_bus_config_debug) {
14310Sstevel@tonic-gate flag |= NDI_DEVI_DEBUG;
14320Sstevel@tonic-gate }
14330Sstevel@tonic-gate
14340Sstevel@tonic-gate if ((op == BUS_UNCONFIG_ALL) && (flag & NDI_AUTODETACH) == 0) {
14350Sstevel@tonic-gate flag |= NDI_DEVI_REMOVE;
14360Sstevel@tonic-gate }
14370Sstevel@tonic-gate
14380Sstevel@tonic-gate /* serialize access */
14390Sstevel@tonic-gate ndi_devi_enter(dip, &circ);
14400Sstevel@tonic-gate
14410Sstevel@tonic-gate rval = ndi_busop_bus_unconfig(dip, flag, op, arg);
14420Sstevel@tonic-gate
14430Sstevel@tonic-gate /* logically zap children's list */
14440Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
14450Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
14460Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_ZAP;
14470Sstevel@tonic-gate }
14480Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
14490Sstevel@tonic-gate
14500Sstevel@tonic-gate /* fill in what's left */
14510Sstevel@tonic-gate for (cdip = ddi_get_child(dip); cdip;
14520Sstevel@tonic-gate cdip = ddi_get_next_sibling(cdip)) {
14530Sstevel@tonic-gate usba_device_t *usba_device = usba_get_usba_device(cdip);
14540Sstevel@tonic-gate
14550Sstevel@tonic-gate if (usba_device == NULL) {
14560Sstevel@tonic-gate
14570Sstevel@tonic-gate continue;
14580Sstevel@tonic-gate }
14590Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
14600Sstevel@tonic-gate port = usba_device->usb_port;
14610Sstevel@tonic-gate hubd->h_children_dips[port] = cdip;
14620Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP;
14630Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
14640Sstevel@tonic-gate }
14650Sstevel@tonic-gate
14660Sstevel@tonic-gate /* physically zap the children we didn't find */
14670Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
14680Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
14696112Sqz150045 if (hubd->h_port_state[port] & HUBD_CHILD_ZAP) {
14700Sstevel@tonic-gate /* zap the dip and usba_device structure as well */
14710Sstevel@tonic-gate hubd_free_usba_device(hubd, hubd->h_usba_devices[port]);
14720Sstevel@tonic-gate hubd->h_children_dips[port] = NULL;
14730Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_ZAP;
14740Sstevel@tonic-gate }
14750Sstevel@tonic-gate }
14760Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
14770Sstevel@tonic-gate
14780Sstevel@tonic-gate ndi_devi_exit(dip, circ);
14790Sstevel@tonic-gate
14800Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
14810Sstevel@tonic-gate "hubd_bus_unconfig: rval=%d", rval);
14820Sstevel@tonic-gate
14830Sstevel@tonic-gate return (rval);
14840Sstevel@tonic-gate }
14850Sstevel@tonic-gate
14860Sstevel@tonic-gate
14870Sstevel@tonic-gate /* bus_power entry point */
14880Sstevel@tonic-gate static int
hubd_bus_power(dev_info_t * dip,void * impl_arg,pm_bus_power_op_t op,void * arg,void * result)14890Sstevel@tonic-gate hubd_bus_power(dev_info_t *dip, void *impl_arg, pm_bus_power_op_t op,
14900Sstevel@tonic-gate void *arg, void *result)
14910Sstevel@tonic-gate {
14920Sstevel@tonic-gate hubd_t *hubd;
14930Sstevel@tonic-gate int rval, pwrup_res;
14940Sstevel@tonic-gate usb_port_t port;
14950Sstevel@tonic-gate int retval = DDI_FAILURE;
14960Sstevel@tonic-gate pm_bp_child_pwrchg_t *bpc;
14970Sstevel@tonic-gate pm_bp_nexus_pwrup_t bpn;
14980Sstevel@tonic-gate
14990Sstevel@tonic-gate hubd = hubd_get_soft_state(dip);
15000Sstevel@tonic-gate
15010Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUBDI, hubd->h_log_handle,
15020Sstevel@tonic-gate "hubd_bus_power: dip=%p, impl_arg=%p, power_op=%d, arg=%p, "
15036898Sfb209375 "result=%d\n", (void *)dip, impl_arg, op, arg, *(int *)result);
15040Sstevel@tonic-gate
15050Sstevel@tonic-gate bpc = (pm_bp_child_pwrchg_t *)arg;
15060Sstevel@tonic-gate
15070Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
15080Sstevel@tonic-gate hubd->h_bus_pwr++;
15090Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
15100Sstevel@tonic-gate
15110Sstevel@tonic-gate switch (op) {
15120Sstevel@tonic-gate case BUS_POWER_PRE_NOTIFICATION:
15130Sstevel@tonic-gate port = hubd_child_dip2port(hubd, bpc->bpc_dip);
15140Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
15150Sstevel@tonic-gate "hubd_bus_power: BUS_POWER_PRE_NOTIFICATION, port=%d",
15160Sstevel@tonic-gate port);
15170Sstevel@tonic-gate
15180Sstevel@tonic-gate /* go to full power if we are powered down */
15190Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
15200Sstevel@tonic-gate
15210Sstevel@tonic-gate /*
15220Sstevel@tonic-gate * If this case completes normally, idle will be in
15230Sstevel@tonic-gate * hubd_bus_power / BUS_POWER_POST_NOTIFICATION
15240Sstevel@tonic-gate */
15250Sstevel@tonic-gate hubd_pm_busy_component(hubd, dip, 0);
15260Sstevel@tonic-gate
15270Sstevel@tonic-gate /*
15280Sstevel@tonic-gate * raise power only if we have created the components
15290Sstevel@tonic-gate * and are currently in low power
15300Sstevel@tonic-gate */
15310Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_PWRED_DOWN) &&
15320Sstevel@tonic-gate hubd->h_hubpm->hubp_wakeup_enabled) {
15330Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
15340Sstevel@tonic-gate
15350Sstevel@tonic-gate bpn.bpn_comp = 0;
15360Sstevel@tonic-gate bpn.bpn_dip = dip;
15370Sstevel@tonic-gate bpn.bpn_level = USB_DEV_OS_FULL_PWR;
15380Sstevel@tonic-gate bpn.bpn_private = bpc->bpc_private;
15390Sstevel@tonic-gate
15400Sstevel@tonic-gate rval = pm_busop_bus_power(dip, impl_arg,
15410Sstevel@tonic-gate BUS_POWER_NEXUS_PWRUP, (void *)&bpn,
15420Sstevel@tonic-gate (void *)&pwrup_res);
15430Sstevel@tonic-gate
15440Sstevel@tonic-gate if (rval != DDI_SUCCESS || pwrup_res != DDI_SUCCESS) {
15450Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
15460Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0);
15470Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
15480Sstevel@tonic-gate
15490Sstevel@tonic-gate break;
15500Sstevel@tonic-gate }
15510Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
15520Sstevel@tonic-gate }
15530Sstevel@tonic-gate
15540Sstevel@tonic-gate /* indicate that child is changing power level */
15550Sstevel@tonic-gate hubd->h_port_state[port] |= HUBD_CHILD_PWRLVL_CHNG;
15560Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
15570Sstevel@tonic-gate
15580Sstevel@tonic-gate if ((bpc->bpc_olevel == 0) &&
15590Sstevel@tonic-gate (bpc->bpc_nlevel > bpc->bpc_olevel)) {
15600Sstevel@tonic-gate /*
15610Sstevel@tonic-gate * this child is transitioning from power off
15620Sstevel@tonic-gate * to power on state - resume port
15630Sstevel@tonic-gate */
15640Sstevel@tonic-gate rval = hubd_resume_port(hubd, port);
15650Sstevel@tonic-gate if (rval == USB_SUCCESS) {
15660Sstevel@tonic-gate retval = DDI_SUCCESS;
15670Sstevel@tonic-gate } else {
15680Sstevel@tonic-gate /* reset this flag on failure */
15690Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
15700Sstevel@tonic-gate hubd->h_port_state[port] &=
15710Sstevel@tonic-gate ~HUBD_CHILD_PWRLVL_CHNG;
15720Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0);
15730Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
15740Sstevel@tonic-gate }
15750Sstevel@tonic-gate } else {
15760Sstevel@tonic-gate retval = DDI_SUCCESS;
15770Sstevel@tonic-gate }
15780Sstevel@tonic-gate
15790Sstevel@tonic-gate break;
15800Sstevel@tonic-gate case BUS_POWER_POST_NOTIFICATION:
15810Sstevel@tonic-gate port = hubd_child_dip2port(hubd, bpc->bpc_dip);
15820Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
15830Sstevel@tonic-gate "hubd_bus_power: BUS_POWER_POST_NOTIFICATION, port=%d",
15840Sstevel@tonic-gate port);
15850Sstevel@tonic-gate
15860Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
15870Sstevel@tonic-gate hubd->h_port_state[port] &= ~HUBD_CHILD_PWRLVL_CHNG;
15880Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
15890Sstevel@tonic-gate
15900Sstevel@tonic-gate /* record child's pwr and suspend port if required */
15910Sstevel@tonic-gate rval = hubd_post_power(hubd, port, bpc, *(int *)result);
15920Sstevel@tonic-gate if (rval == USB_SUCCESS) {
15930Sstevel@tonic-gate
15940Sstevel@tonic-gate retval = DDI_SUCCESS;
15950Sstevel@tonic-gate }
15960Sstevel@tonic-gate
15970Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
15980Sstevel@tonic-gate
15990Sstevel@tonic-gate /*
16000Sstevel@tonic-gate * Matching idle for the busy in
16010Sstevel@tonic-gate * hubd_bus_power / BUS_POWER_PRE_NOTIFICATION
16020Sstevel@tonic-gate */
16030Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0);
16040Sstevel@tonic-gate
16050Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
16060Sstevel@tonic-gate
16070Sstevel@tonic-gate break;
16080Sstevel@tonic-gate default:
16090Sstevel@tonic-gate retval = pm_busop_bus_power(dip, impl_arg, op, arg, result);
16100Sstevel@tonic-gate
16110Sstevel@tonic-gate break;
16120Sstevel@tonic-gate }
16130Sstevel@tonic-gate
16140Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
16150Sstevel@tonic-gate hubd->h_bus_pwr--;
16160Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
16170Sstevel@tonic-gate
16180Sstevel@tonic-gate return (retval);
16190Sstevel@tonic-gate }
16200Sstevel@tonic-gate
16210Sstevel@tonic-gate
16220Sstevel@tonic-gate /*
16230Sstevel@tonic-gate * functions to handle power transition for OS levels 0 -> 3
16240Sstevel@tonic-gate */
16250Sstevel@tonic-gate static int
hubd_pwrlvl0(hubd_t * hubd)16260Sstevel@tonic-gate hubd_pwrlvl0(hubd_t *hubd)
16270Sstevel@tonic-gate {
16280Sstevel@tonic-gate hub_power_t *hubpm;
16290Sstevel@tonic-gate
16300Sstevel@tonic-gate /* We can't power down if hotplug thread is running */
16310Sstevel@tonic-gate if (hubd->h_hotplug_thread || hubd->h_hubpm->hubp_busy_pm ||
16320Sstevel@tonic-gate (hubd_can_suspend(hubd) == USB_FAILURE)) {
16330Sstevel@tonic-gate
16340Sstevel@tonic-gate return (USB_FAILURE);
16350Sstevel@tonic-gate }
16360Sstevel@tonic-gate
16370Sstevel@tonic-gate switch (hubd->h_dev_state) {
16380Sstevel@tonic-gate case USB_DEV_ONLINE:
16390Sstevel@tonic-gate hubpm = hubd->h_hubpm;
16400Sstevel@tonic-gate
16410Sstevel@tonic-gate /*
16420Sstevel@tonic-gate * To avoid race with bus_power pre_notify on check over
16430Sstevel@tonic-gate * dev_state, we need to correctly set the dev state
16440Sstevel@tonic-gate * before the mutex is dropped in stop polling.
16450Sstevel@tonic-gate */
16460Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_PWRED_DOWN;
16470Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_PWR_OFF;
16480Sstevel@tonic-gate
16490Sstevel@tonic-gate /*
16500Sstevel@tonic-gate * if we are the root hub, do not stop polling
16510Sstevel@tonic-gate * otherwise, we will never see a resume
16520Sstevel@tonic-gate */
16530Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) {
16540Sstevel@tonic-gate /* place holder to implement Global Suspend */
16550Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
16560Sstevel@tonic-gate "Global Suspend: Not Yet Implemented");
16570Sstevel@tonic-gate } else {
16580Sstevel@tonic-gate hubd_stop_polling(hubd);
16590Sstevel@tonic-gate }
16600Sstevel@tonic-gate
16610Sstevel@tonic-gate /* Issue USB D3 command to the device here */
16620Sstevel@tonic-gate (void) usb_set_device_pwrlvl3(hubd->h_dip);
16630Sstevel@tonic-gate
16640Sstevel@tonic-gate break;
16650Sstevel@tonic-gate case USB_DEV_DISCONNECTED:
16660Sstevel@tonic-gate case USB_DEV_SUSPENDED:
16670Sstevel@tonic-gate case USB_DEV_PWRED_DOWN:
16680Sstevel@tonic-gate default:
16690Sstevel@tonic-gate
16700Sstevel@tonic-gate break;
16710Sstevel@tonic-gate }
16720Sstevel@tonic-gate
16730Sstevel@tonic-gate return (USB_SUCCESS);
16740Sstevel@tonic-gate }
16750Sstevel@tonic-gate
16760Sstevel@tonic-gate
16770Sstevel@tonic-gate /* ARGSUSED */
16780Sstevel@tonic-gate static int
hubd_pwrlvl1(hubd_t * hubd)16790Sstevel@tonic-gate hubd_pwrlvl1(hubd_t *hubd)
16800Sstevel@tonic-gate {
16810Sstevel@tonic-gate /* Issue USB D2 command to the device here */
16820Sstevel@tonic-gate (void) usb_set_device_pwrlvl2(hubd->h_dip);
16830Sstevel@tonic-gate
16840Sstevel@tonic-gate return (USB_FAILURE);
16850Sstevel@tonic-gate }
16860Sstevel@tonic-gate
16870Sstevel@tonic-gate
16880Sstevel@tonic-gate /* ARGSUSED */
16890Sstevel@tonic-gate static int
hubd_pwrlvl2(hubd_t * hubd)16900Sstevel@tonic-gate hubd_pwrlvl2(hubd_t *hubd)
16910Sstevel@tonic-gate {
16920Sstevel@tonic-gate /* Issue USB D1 command to the device here */
16930Sstevel@tonic-gate (void) usb_set_device_pwrlvl1(hubd->h_dip);
16940Sstevel@tonic-gate
16950Sstevel@tonic-gate return (USB_FAILURE);
16960Sstevel@tonic-gate }
16970Sstevel@tonic-gate
16980Sstevel@tonic-gate
16990Sstevel@tonic-gate static int
hubd_pwrlvl3(hubd_t * hubd)17000Sstevel@tonic-gate hubd_pwrlvl3(hubd_t *hubd)
17010Sstevel@tonic-gate {
17020Sstevel@tonic-gate hub_power_t *hubpm;
17030Sstevel@tonic-gate int rval;
17040Sstevel@tonic-gate
17050Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, "hubd_pwrlvl3");
17060Sstevel@tonic-gate
17070Sstevel@tonic-gate hubpm = hubd->h_hubpm;
17080Sstevel@tonic-gate switch (hubd->h_dev_state) {
17090Sstevel@tonic-gate case USB_DEV_PWRED_DOWN:
17100Sstevel@tonic-gate ASSERT(hubpm->hubp_current_power == USB_DEV_OS_PWR_OFF);
17110Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) {
17120Sstevel@tonic-gate /* implement global resume here */
17130Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM,
17140Sstevel@tonic-gate hubd->h_log_handle,
17150Sstevel@tonic-gate "Global Resume: Not Yet Implemented");
17160Sstevel@tonic-gate }
17170Sstevel@tonic-gate /* Issue USB D0 command to the device here */
17180Sstevel@tonic-gate rval = usb_set_device_pwrlvl0(hubd->h_dip);
17190Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS);
17200Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE;
17210Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
17220Sstevel@tonic-gate hubpm->hubp_time_at_full_power = ddi_get_time();
17230Sstevel@tonic-gate hubd_start_polling(hubd, 0);
17240Sstevel@tonic-gate
17250Sstevel@tonic-gate /* FALLTHRU */
17260Sstevel@tonic-gate case USB_DEV_ONLINE:
17270Sstevel@tonic-gate /* we are already in full power */
17280Sstevel@tonic-gate
17290Sstevel@tonic-gate /* FALLTHRU */
17300Sstevel@tonic-gate case USB_DEV_DISCONNECTED:
17310Sstevel@tonic-gate case USB_DEV_SUSPENDED:
17320Sstevel@tonic-gate /*
17330Sstevel@tonic-gate * PM framework tries to put you in full power
17340Sstevel@tonic-gate * during system shutdown. If we are disconnected
17350Sstevel@tonic-gate * return success. Also, we should not change state
17360Sstevel@tonic-gate * when we are disconnected or suspended or about to
17370Sstevel@tonic-gate * transition to that state
17380Sstevel@tonic-gate */
17390Sstevel@tonic-gate
17400Sstevel@tonic-gate return (USB_SUCCESS);
17410Sstevel@tonic-gate default:
17420Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
17430Sstevel@tonic-gate "hubd_pwrlvl3: Illegal dev_state=%d", hubd->h_dev_state);
17440Sstevel@tonic-gate
17450Sstevel@tonic-gate return (USB_FAILURE);
17460Sstevel@tonic-gate }
17470Sstevel@tonic-gate }
17480Sstevel@tonic-gate
17490Sstevel@tonic-gate
17500Sstevel@tonic-gate /* power entry point */
17510Sstevel@tonic-gate /* ARGSUSED */
17520Sstevel@tonic-gate int
usba_hubdi_power(dev_info_t * dip,int comp,int level)17530Sstevel@tonic-gate usba_hubdi_power(dev_info_t *dip, int comp, int level)
17540Sstevel@tonic-gate {
17550Sstevel@tonic-gate hubd_t *hubd;
17560Sstevel@tonic-gate hub_power_t *hubpm;
17570Sstevel@tonic-gate int retval;
17580Sstevel@tonic-gate int circ;
17590Sstevel@tonic-gate
17600Sstevel@tonic-gate hubd = hubd_get_soft_state(dip);
17610Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HUBDI, hubd->h_log_handle,
17620Sstevel@tonic-gate "usba_hubdi_power: level=%d", level);
17630Sstevel@tonic-gate
17640Sstevel@tonic-gate ndi_devi_enter(dip, &circ);
17650Sstevel@tonic-gate
17660Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
17670Sstevel@tonic-gate hubpm = hubd->h_hubpm;
17680Sstevel@tonic-gate
17690Sstevel@tonic-gate /* check if we are transitioning to a legal power level */
17700Sstevel@tonic-gate if (USB_DEV_PWRSTATE_OK(hubpm->hubp_pwr_states, level)) {
17710Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HUBDI, hubd->h_log_handle,
17720Sstevel@tonic-gate "usba_hubdi_power: illegal power level=%d "
17730Sstevel@tonic-gate "hubp_pwr_states=0x%x", level, hubpm->hubp_pwr_states);
17740Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
17750Sstevel@tonic-gate
17760Sstevel@tonic-gate ndi_devi_exit(dip, circ);
17770Sstevel@tonic-gate
17780Sstevel@tonic-gate return (DDI_FAILURE);
17790Sstevel@tonic-gate }
17800Sstevel@tonic-gate
17810Sstevel@tonic-gate switch (level) {
17820Sstevel@tonic-gate case USB_DEV_OS_PWR_OFF:
17830Sstevel@tonic-gate retval = hubd_pwrlvl0(hubd);
17840Sstevel@tonic-gate
17850Sstevel@tonic-gate break;
17860Sstevel@tonic-gate case USB_DEV_OS_PWR_1:
17870Sstevel@tonic-gate retval = hubd_pwrlvl1(hubd);
17880Sstevel@tonic-gate
17890Sstevel@tonic-gate break;
17900Sstevel@tonic-gate case USB_DEV_OS_PWR_2:
17910Sstevel@tonic-gate retval = hubd_pwrlvl2(hubd);
17920Sstevel@tonic-gate
17930Sstevel@tonic-gate break;
17940Sstevel@tonic-gate case USB_DEV_OS_FULL_PWR:
17950Sstevel@tonic-gate retval = hubd_pwrlvl3(hubd);
17960Sstevel@tonic-gate
17970Sstevel@tonic-gate break;
17980Sstevel@tonic-gate }
17990Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
18000Sstevel@tonic-gate
18010Sstevel@tonic-gate ndi_devi_exit(dip, circ);
18020Sstevel@tonic-gate
18030Sstevel@tonic-gate return ((retval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
18040Sstevel@tonic-gate }
18050Sstevel@tonic-gate
18060Sstevel@tonic-gate
18070Sstevel@tonic-gate /* power entry point for the root hub */
18080Sstevel@tonic-gate int
usba_hubdi_root_hub_power(dev_info_t * dip,int comp,int level)18090Sstevel@tonic-gate usba_hubdi_root_hub_power(dev_info_t *dip, int comp, int level)
18100Sstevel@tonic-gate {
18110Sstevel@tonic-gate return (usba_hubdi_power(dip, comp, level));
18120Sstevel@tonic-gate }
18130Sstevel@tonic-gate
18140Sstevel@tonic-gate
18150Sstevel@tonic-gate /*
18160Sstevel@tonic-gate * standard driver entry points support code
18170Sstevel@tonic-gate */
18180Sstevel@tonic-gate int
usba_hubdi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)18190Sstevel@tonic-gate usba_hubdi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
18200Sstevel@tonic-gate {
18210Sstevel@tonic-gate int instance = ddi_get_instance(dip);
18220Sstevel@tonic-gate hubd_t *hubd = NULL;
18230Sstevel@tonic-gate int i, rval;
18240Sstevel@tonic-gate int minor;
18256112Sqz150045 uint8_t ports_count;
18260Sstevel@tonic-gate char *log_name = NULL;
18270Sstevel@tonic-gate const char *root_hub_drvname;
18280Sstevel@tonic-gate usb_ep_data_t *ep_data;
18290Sstevel@tonic-gate usba_device_t *child_ud = NULL;
18300Sstevel@tonic-gate usb_dev_descr_t *usb_dev_descr;
18310Sstevel@tonic-gate usb_port_status_t parent_port_status, child_port_status;
18320Sstevel@tonic-gate
18330Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubdi_log_handle,
18344763Slg150142 "hubd_attach instance %d, cmd=0x%x", instance, cmd);
18350Sstevel@tonic-gate
18360Sstevel@tonic-gate switch (cmd) {
18370Sstevel@tonic-gate case DDI_ATTACH:
18380Sstevel@tonic-gate
18390Sstevel@tonic-gate break;
18400Sstevel@tonic-gate case DDI_RESUME:
18410Sstevel@tonic-gate hubd_cpr_resume(dip);
18420Sstevel@tonic-gate
18430Sstevel@tonic-gate return (DDI_SUCCESS);
18440Sstevel@tonic-gate default:
18450Sstevel@tonic-gate return (DDI_FAILURE);
18460Sstevel@tonic-gate }
18470Sstevel@tonic-gate
18480Sstevel@tonic-gate /*
18490Sstevel@tonic-gate * Allocate softc information.
18500Sstevel@tonic-gate */
18510Sstevel@tonic-gate if (usba_is_root_hub(dip)) {
18520Sstevel@tonic-gate /* soft state has already been allocated */
18530Sstevel@tonic-gate hubd = hubd_get_soft_state(dip);
18540Sstevel@tonic-gate minor = HUBD_IS_ROOT_HUB;
18550Sstevel@tonic-gate
18560Sstevel@tonic-gate /* generate readable labels for different root hubs */
18570Sstevel@tonic-gate root_hub_drvname = ddi_driver_name(dip);
18580Sstevel@tonic-gate if (strcmp(root_hub_drvname, "ehci") == 0) {
18590Sstevel@tonic-gate log_name = "eusb";
18600Sstevel@tonic-gate } else if (strcmp(root_hub_drvname, "uhci") == 0) {
18610Sstevel@tonic-gate log_name = "uusb";
18620Sstevel@tonic-gate } else {
18630Sstevel@tonic-gate /* std. for ohci */
18640Sstevel@tonic-gate log_name = "usb";
18650Sstevel@tonic-gate }
18660Sstevel@tonic-gate } else {
18670Sstevel@tonic-gate rval = ddi_soft_state_zalloc(hubd_statep, instance);
18680Sstevel@tonic-gate minor = 0;
18690Sstevel@tonic-gate
18700Sstevel@tonic-gate if (rval != DDI_SUCCESS) {
18710Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
18720Sstevel@tonic-gate "cannot allocate soft state (%d)", instance);
18730Sstevel@tonic-gate goto fail;
18740Sstevel@tonic-gate }
18750Sstevel@tonic-gate
18760Sstevel@tonic-gate hubd = hubd_get_soft_state(dip);
18770Sstevel@tonic-gate if (hubd == NULL) {
18780Sstevel@tonic-gate goto fail;
18790Sstevel@tonic-gate }
18800Sstevel@tonic-gate }
18810Sstevel@tonic-gate
18820Sstevel@tonic-gate hubd->h_log_handle = usb_alloc_log_hdl(dip, log_name, &hubd_errlevel,
18834763Slg150142 &hubd_errmask, &hubd_instance_debug, 0);
18840Sstevel@tonic-gate
18850Sstevel@tonic-gate hubd->h_usba_device = child_ud = usba_get_usba_device(dip);
18860Sstevel@tonic-gate hubd->h_dip = dip;
18870Sstevel@tonic-gate hubd->h_instance = instance;
18880Sstevel@tonic-gate
18890Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex);
18900Sstevel@tonic-gate child_port_status = child_ud->usb_port_status;
18910Sstevel@tonic-gate usb_dev_descr = child_ud->usb_dev_descr;
18920Sstevel@tonic-gate parent_port_status = (child_ud->usb_hs_hub_usba_dev) ?
18930Sstevel@tonic-gate child_ud->usb_hs_hub_usba_dev->usb_port_status : 0;
18940Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex);
18950Sstevel@tonic-gate
18960Sstevel@tonic-gate if ((child_port_status == USBA_FULL_SPEED_DEV) &&
18970Sstevel@tonic-gate (parent_port_status == USBA_HIGH_SPEED_DEV) &&
18980Sstevel@tonic-gate (usb_dev_descr->bcdUSB == 0x100)) {
18990Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle,
19000Sstevel@tonic-gate "Use of a USB1.0 hub behind a high speed port may "
19010Sstevel@tonic-gate "cause unexpected failures");
19020Sstevel@tonic-gate }
19030Sstevel@tonic-gate
19040Sstevel@tonic-gate hubd->h_pipe_policy.pp_max_async_reqs = 1;
19050Sstevel@tonic-gate
19060Sstevel@tonic-gate /* register with USBA as client driver */
19070Sstevel@tonic-gate if (usb_client_attach(dip, USBDRV_VERSION, 0) != USB_SUCCESS) {
19080Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
19090Sstevel@tonic-gate "client attach failed");
19100Sstevel@tonic-gate
19110Sstevel@tonic-gate goto fail;
19120Sstevel@tonic-gate }
19130Sstevel@tonic-gate
19140Sstevel@tonic-gate if (usb_get_dev_data(dip, &hubd->h_dev_data,
19150Sstevel@tonic-gate USB_PARSE_LVL_IF, 0) != USB_SUCCESS) {
19160Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
19170Sstevel@tonic-gate "cannot get dev_data");
19180Sstevel@tonic-gate
19190Sstevel@tonic-gate goto fail;
19200Sstevel@tonic-gate }
19210Sstevel@tonic-gate
19220Sstevel@tonic-gate if ((ep_data = usb_lookup_ep_data(dip, hubd->h_dev_data,
19230Sstevel@tonic-gate hubd->h_dev_data->dev_curr_if, 0, 0,
19240Sstevel@tonic-gate (uint_t)USB_EP_ATTR_INTR, (uint_t)USB_EP_DIR_IN)) == NULL) {
19250Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
19260Sstevel@tonic-gate "no interrupt IN endpoint found");
19270Sstevel@tonic-gate
19280Sstevel@tonic-gate goto fail;
19290Sstevel@tonic-gate }
19300Sstevel@tonic-gate
19310Sstevel@tonic-gate hubd->h_ep1_descr = ep_data->ep_descr;
19320Sstevel@tonic-gate hubd->h_default_pipe = hubd->h_dev_data->dev_default_ph;
19330Sstevel@tonic-gate
19340Sstevel@tonic-gate mutex_init(HUBD_MUTEX(hubd), NULL, MUTEX_DRIVER,
19354763Slg150142 hubd->h_dev_data->dev_iblock_cookie);
19360Sstevel@tonic-gate cv_init(&hubd->h_cv_reset_port, NULL, CV_DRIVER, NULL);
19374844Slg150142 cv_init(&hubd->h_cv_hotplug_dev, NULL, CV_DRIVER, NULL);
19380Sstevel@tonic-gate
19390Sstevel@tonic-gate hubd->h_init_state |= HUBD_LOCKS_DONE;
19400Sstevel@tonic-gate
19410Sstevel@tonic-gate usb_free_descr_tree(dip, hubd->h_dev_data);
19420Sstevel@tonic-gate
19430Sstevel@tonic-gate /*
19440Sstevel@tonic-gate * register this hub instance with usba
19450Sstevel@tonic-gate */
19460Sstevel@tonic-gate rval = usba_hubdi_register(dip, 0);
19470Sstevel@tonic-gate if (rval != USB_SUCCESS) {
1948978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
19490Sstevel@tonic-gate "usba_hubdi_register failed");
19500Sstevel@tonic-gate goto fail;
19510Sstevel@tonic-gate }
19520Sstevel@tonic-gate
19530Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
19540Sstevel@tonic-gate hubd->h_init_state |= HUBD_HUBDI_REGISTERED;
19550Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE;
19560Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
19570Sstevel@tonic-gate
19580Sstevel@tonic-gate /* now create components to power manage this device */
19590Sstevel@tonic-gate hubd_create_pm_components(dip, hubd);
19600Sstevel@tonic-gate
19610Sstevel@tonic-gate /*
19620Sstevel@tonic-gate * Event handling: definition and registration
19630Sstevel@tonic-gate *
19640Sstevel@tonic-gate * first the definition:
19650Sstevel@tonic-gate * get event handle
19660Sstevel@tonic-gate */
19670Sstevel@tonic-gate (void) ndi_event_alloc_hdl(dip, 0, &hubd->h_ndi_event_hdl, NDI_SLEEP);
19680Sstevel@tonic-gate
19690Sstevel@tonic-gate /* bind event set to the handle */
19700Sstevel@tonic-gate if (ndi_event_bind_set(hubd->h_ndi_event_hdl, &hubd_ndi_events,
19710Sstevel@tonic-gate NDI_SLEEP)) {
19720Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
19730Sstevel@tonic-gate "binding event set failed");
19740Sstevel@tonic-gate
19750Sstevel@tonic-gate goto fail;
19760Sstevel@tonic-gate }
19770Sstevel@tonic-gate
19780Sstevel@tonic-gate /* event registration */
19790Sstevel@tonic-gate if (hubd_register_events(hubd) != USB_SUCCESS) {
1980978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
19810Sstevel@tonic-gate "hubd_register_events failed");
19820Sstevel@tonic-gate
19830Sstevel@tonic-gate goto fail;
19840Sstevel@tonic-gate }
19850Sstevel@tonic-gate
19860Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
19870Sstevel@tonic-gate hubd->h_init_state |= HUBD_EVENTS_REGISTERED;
19880Sstevel@tonic-gate
19891001Ssl147100 if ((hubd_get_hub_descriptor(hubd)) != USB_SUCCESS) {
19901001Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
19911001Ssl147100
19921001Ssl147100 goto fail;
19931001Ssl147100 }
19941001Ssl147100
19951001Ssl147100 if (ddi_prop_exists(DDI_DEV_T_ANY, dip,
19961001Ssl147100 (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
19971001Ssl147100 "hub-ignore-power-budget") == 1) {
19981001Ssl147100 hubd->h_ignore_pwr_budget = B_TRUE;
19991001Ssl147100 } else {
20001001Ssl147100 hubd->h_ignore_pwr_budget = B_FALSE;
20011001Ssl147100
20021001Ssl147100 /* initialize hub power budget variables */
20031001Ssl147100 if (hubd_init_power_budget(hubd) != USB_SUCCESS) {
20041001Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
20051001Ssl147100 "hubd_init_power_budget failed");
20061001Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
20071001Ssl147100
20081001Ssl147100 goto fail;
20091001Ssl147100 }
20101001Ssl147100 }
20111001Ssl147100
20120Sstevel@tonic-gate /* initialize and create children */
20130Sstevel@tonic-gate if (hubd_check_ports(hubd) != USB_SUCCESS) {
2014978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
20150Sstevel@tonic-gate "hubd_check_ports failed");
20160Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
20170Sstevel@tonic-gate
20180Sstevel@tonic-gate goto fail;
20190Sstevel@tonic-gate }
20200Sstevel@tonic-gate
20210Sstevel@tonic-gate /*
20220Sstevel@tonic-gate * create cfgadm nodes
20230Sstevel@tonic-gate */
20240Sstevel@tonic-gate hubd->h_ancestry_str = (char *)kmem_zalloc(HUBD_APID_NAMELEN, KM_SLEEP);
20250Sstevel@tonic-gate hubd_get_ancestry_str(hubd);
20260Sstevel@tonic-gate
20270Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
20280Sstevel@tonic-gate "#ports=0x%x", hubd->h_hub_descr.bNbrPorts);
20290Sstevel@tonic-gate
20300Sstevel@tonic-gate for (i = 1; i <= hubd->h_hub_descr.bNbrPorts; i++) {
20310Sstevel@tonic-gate char ap_name[HUBD_APID_NAMELEN];
20320Sstevel@tonic-gate
20330Sstevel@tonic-gate (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
20340Sstevel@tonic-gate hubd->h_ancestry_str, i);
20350Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
20360Sstevel@tonic-gate "ap_name=%s", ap_name);
20370Sstevel@tonic-gate
20380Sstevel@tonic-gate if (ddi_create_minor_node(dip, ap_name, S_IFCHR, instance,
20390Sstevel@tonic-gate DDI_NT_USB_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
2040978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
20410Sstevel@tonic-gate "cannot create attachment point node (%d)",
20420Sstevel@tonic-gate instance);
20430Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
20440Sstevel@tonic-gate
20450Sstevel@tonic-gate goto fail;
20460Sstevel@tonic-gate }
20470Sstevel@tonic-gate }
20486112Sqz150045
20496112Sqz150045 ports_count = hubd->h_hub_descr.bNbrPorts;
20500Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
20510Sstevel@tonic-gate
20520Sstevel@tonic-gate /* create minor nodes */
20530Sstevel@tonic-gate if (ddi_create_minor_node(dip, "hubd", S_IFCHR,
20540Sstevel@tonic-gate instance | minor, DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
20550Sstevel@tonic-gate
2056978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
20570Sstevel@tonic-gate "cannot create devctl minor node (%d)", instance);
20580Sstevel@tonic-gate
20590Sstevel@tonic-gate goto fail;
20600Sstevel@tonic-gate }
20610Sstevel@tonic-gate
20620Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
20630Sstevel@tonic-gate hubd->h_init_state |= HUBD_MINOR_NODE_CREATED;
20640Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
20650Sstevel@tonic-gate
20666112Sqz150045 if (ndi_prop_update_int(DDI_DEV_T_NONE, dip,
20676112Sqz150045 "usb-port-count", ports_count) != DDI_PROP_SUCCESS) {
20686112Sqz150045 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
20699430SRaymond.Chen@Sun.COM "usb-port-count update failed");
20706112Sqz150045 }
20716112Sqz150045
20720Sstevel@tonic-gate /*
20730Sstevel@tonic-gate * host controller driver has already reported this dev
20740Sstevel@tonic-gate * if we are the root hub
20750Sstevel@tonic-gate */
20760Sstevel@tonic-gate if (!usba_is_root_hub(dip)) {
20770Sstevel@tonic-gate ddi_report_dev(dip);
20780Sstevel@tonic-gate }
20790Sstevel@tonic-gate
20800Sstevel@tonic-gate /* enable deathrow thread */
20810Sstevel@tonic-gate hubd->h_cleanup_enabled = B_TRUE;
20820Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
20830Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0);
20840Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
20850Sstevel@tonic-gate
20860Sstevel@tonic-gate return (DDI_SUCCESS);
20870Sstevel@tonic-gate
20880Sstevel@tonic-gate fail:
20890Sstevel@tonic-gate {
20900Sstevel@tonic-gate char *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
20910Sstevel@tonic-gate
20923435Slg150142 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
20930Sstevel@tonic-gate "cannot attach %s", ddi_pathname(dip, pathname));
20940Sstevel@tonic-gate
20950Sstevel@tonic-gate kmem_free(pathname, MAXPATHLEN);
20960Sstevel@tonic-gate }
20970Sstevel@tonic-gate
20980Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
20990Sstevel@tonic-gate hubd_pm_idle_component(hubd, dip, 0);
21000Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
21010Sstevel@tonic-gate
21020Sstevel@tonic-gate if (hubd) {
21030Sstevel@tonic-gate rval = hubd_cleanup(dip, hubd);
21040Sstevel@tonic-gate if (rval != USB_SUCCESS) {
2105978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
21060Sstevel@tonic-gate "failure to complete cleanup after attach failure");
21070Sstevel@tonic-gate }
21080Sstevel@tonic-gate }
21090Sstevel@tonic-gate
21100Sstevel@tonic-gate return (DDI_FAILURE);
21110Sstevel@tonic-gate }
21120Sstevel@tonic-gate
21130Sstevel@tonic-gate
21140Sstevel@tonic-gate int
usba_hubdi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)21150Sstevel@tonic-gate usba_hubdi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
21160Sstevel@tonic-gate {
21170Sstevel@tonic-gate hubd_t *hubd = hubd_get_soft_state(dip);
21180Sstevel@tonic-gate int rval;
21190Sstevel@tonic-gate
21200Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
21210Sstevel@tonic-gate "hubd_detach: cmd=0x%x", cmd);
21220Sstevel@tonic-gate
21230Sstevel@tonic-gate switch (cmd) {
21240Sstevel@tonic-gate case DDI_DETACH:
21250Sstevel@tonic-gate rval = hubd_cleanup(dip, hubd);
21260Sstevel@tonic-gate
21270Sstevel@tonic-gate return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
21280Sstevel@tonic-gate case DDI_SUSPEND:
21290Sstevel@tonic-gate rval = hubd_cpr_suspend(hubd);
21300Sstevel@tonic-gate
21310Sstevel@tonic-gate return ((rval == USB_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
21320Sstevel@tonic-gate default:
21330Sstevel@tonic-gate return (DDI_FAILURE);
21340Sstevel@tonic-gate }
21350Sstevel@tonic-gate }
21360Sstevel@tonic-gate
21370Sstevel@tonic-gate
21380Sstevel@tonic-gate /*
21390Sstevel@tonic-gate * hubd_setdevaddr
21400Sstevel@tonic-gate * set the device addrs on this port
21410Sstevel@tonic-gate */
21420Sstevel@tonic-gate static int
hubd_setdevaddr(hubd_t * hubd,usb_port_t port)21430Sstevel@tonic-gate hubd_setdevaddr(hubd_t *hubd, usb_port_t port)
21440Sstevel@tonic-gate {
21450Sstevel@tonic-gate int rval;
21460Sstevel@tonic-gate usb_cr_t completion_reason;
21470Sstevel@tonic-gate usb_cb_flags_t cb_flags;
21480Sstevel@tonic-gate usb_pipe_handle_t ph;
21490Sstevel@tonic-gate dev_info_t *child_dip = NULL;
21500Sstevel@tonic-gate uchar_t address = 0;
21510Sstevel@tonic-gate usba_device_t *usba_device;
21520Sstevel@tonic-gate int retry = 0;
21530Sstevel@tonic-gate long time_delay;
21540Sstevel@tonic-gate
21550Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
21560Sstevel@tonic-gate "hubd_setdevaddr: port=%d", port);
21570Sstevel@tonic-gate
21580Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
21590Sstevel@tonic-gate
21600Sstevel@tonic-gate child_dip = hubd->h_children_dips[port];
21610Sstevel@tonic-gate address = hubd->h_usba_devices[port]->usb_addr;
21620Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port];
21630Sstevel@tonic-gate
21640Sstevel@tonic-gate /* close the default pipe with addr x */
21650Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
21660Sstevel@tonic-gate ph = usba_get_dflt_pipe_handle(child_dip);
21670Sstevel@tonic-gate usb_pipe_close(child_dip, ph,
21680Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
21690Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
21700Sstevel@tonic-gate
21710Sstevel@tonic-gate /*
21720Sstevel@tonic-gate * As this device has been reset, temporarily
21730Sstevel@tonic-gate * assign the default address
21740Sstevel@tonic-gate */
21750Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex);
21760Sstevel@tonic-gate address = usba_device->usb_addr;
21770Sstevel@tonic-gate usba_device->usb_addr = USBA_DEFAULT_ADDR;
21780Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex);
21790Sstevel@tonic-gate
21800Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
21810Sstevel@tonic-gate
21820Sstevel@tonic-gate time_delay = drv_usectohz(hubd_device_delay / 20);
21830Sstevel@tonic-gate for (retry = 0; retry < hubd_retry_enumerate; retry++) {
21840Sstevel@tonic-gate
21850Sstevel@tonic-gate /* open child's default pipe with USBA_DEFAULT_ADDR */
21860Sstevel@tonic-gate if (usb_pipe_open(child_dip, NULL, NULL,
21870Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph) !=
21880Sstevel@tonic-gate USB_SUCCESS) {
21890Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
21900Sstevel@tonic-gate "hubd_setdevaddr: Unable to open default pipe");
21910Sstevel@tonic-gate
21920Sstevel@tonic-gate break;
21930Sstevel@tonic-gate }
21940Sstevel@tonic-gate
21950Sstevel@tonic-gate /* Set the address of the device */
21960Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
21970Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV,
21980Sstevel@tonic-gate USB_REQ_SET_ADDRESS, /* bRequest */
21990Sstevel@tonic-gate address, /* wValue */
22000Sstevel@tonic-gate 0, /* wIndex */
22010Sstevel@tonic-gate 0, /* wLength */
22020Sstevel@tonic-gate NULL, 0,
22030Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
22040Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
22050Sstevel@tonic-gate "hubd_setdevaddr(%d): rval=%d cr=%d cb_fl=0x%x",
22060Sstevel@tonic-gate retry, rval, completion_reason, cb_flags);
22070Sstevel@tonic-gate }
22080Sstevel@tonic-gate
22090Sstevel@tonic-gate usb_pipe_close(child_dip, ph,
22100Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
22110Sstevel@tonic-gate
22120Sstevel@tonic-gate if (rval == USB_SUCCESS) {
22130Sstevel@tonic-gate
22140Sstevel@tonic-gate break;
22150Sstevel@tonic-gate }
22160Sstevel@tonic-gate
22170Sstevel@tonic-gate delay(time_delay);
22180Sstevel@tonic-gate }
22190Sstevel@tonic-gate
22200Sstevel@tonic-gate /* Reset to the old address */
22210Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex);
22220Sstevel@tonic-gate usba_device->usb_addr = address;
22230Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex);
22240Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
22250Sstevel@tonic-gate
22260Sstevel@tonic-gate usba_clear_data_toggle(usba_device);
22270Sstevel@tonic-gate
22280Sstevel@tonic-gate return (rval);
22290Sstevel@tonic-gate }
22300Sstevel@tonic-gate
22310Sstevel@tonic-gate
22320Sstevel@tonic-gate /*
22330Sstevel@tonic-gate * hubd_setdevconfig
22340Sstevel@tonic-gate * set the device addrs on this port
22350Sstevel@tonic-gate */
22360Sstevel@tonic-gate static void
hubd_setdevconfig(hubd_t * hubd,usb_port_t port)22370Sstevel@tonic-gate hubd_setdevconfig(hubd_t *hubd, usb_port_t port)
22380Sstevel@tonic-gate {
22390Sstevel@tonic-gate int rval;
22400Sstevel@tonic-gate usb_cr_t completion_reason;
22410Sstevel@tonic-gate usb_cb_flags_t cb_flags;
22420Sstevel@tonic-gate usb_pipe_handle_t ph;
22430Sstevel@tonic-gate dev_info_t *child_dip = NULL;
22440Sstevel@tonic-gate usba_device_t *usba_device = NULL;
22450Sstevel@tonic-gate uint16_t config_value;
22460Sstevel@tonic-gate
22470Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
22480Sstevel@tonic-gate "hubd_setdevconfig: port=%d", port);
22490Sstevel@tonic-gate
22500Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
22510Sstevel@tonic-gate
22520Sstevel@tonic-gate child_dip = hubd->h_children_dips[port];
22530Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port];
22540Sstevel@tonic-gate config_value = hubd->h_usba_devices[port]->usb_cfg_value;
22550Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
22560Sstevel@tonic-gate
22570Sstevel@tonic-gate /* open the default control pipe */
22580Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL,
22590Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) ==
22600Sstevel@tonic-gate USB_SUCCESS) {
22610Sstevel@tonic-gate
22620Sstevel@tonic-gate /* Set the default configuration of the device */
22630Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
22640Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV,
22650Sstevel@tonic-gate USB_REQ_SET_CFG, /* bRequest */
22660Sstevel@tonic-gate config_value, /* wValue */
22670Sstevel@tonic-gate 0, /* wIndex */
22680Sstevel@tonic-gate 0, /* wLength */
22690Sstevel@tonic-gate NULL, 0,
22700Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
22710Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
22720Sstevel@tonic-gate "hubd_setdevconfig: set device config failed: "
22730Sstevel@tonic-gate "cr=%d cb_fl=0x%x rval=%d",
22740Sstevel@tonic-gate completion_reason, cb_flags, rval);
22750Sstevel@tonic-gate }
22760Sstevel@tonic-gate /*
22770Sstevel@tonic-gate * After setting the configuration, we make this default
22780Sstevel@tonic-gate * control pipe persistent, so that it gets re-opened
22790Sstevel@tonic-gate * on posting a connect event
22800Sstevel@tonic-gate */
22810Sstevel@tonic-gate usba_persistent_pipe_close(usba_device);
22820Sstevel@tonic-gate } else {
22830Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
22840Sstevel@tonic-gate "pipe open fails: rval=%d", rval);
22850Sstevel@tonic-gate }
22860Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
22870Sstevel@tonic-gate }
22880Sstevel@tonic-gate
22890Sstevel@tonic-gate
22900Sstevel@tonic-gate /*ARGSUSED*/
22910Sstevel@tonic-gate static int
hubd_check_disconnected_ports(dev_info_t * dip,void * arg)22920Sstevel@tonic-gate hubd_check_disconnected_ports(dev_info_t *dip, void *arg)
22930Sstevel@tonic-gate {
22940Sstevel@tonic-gate int circ;
22950Sstevel@tonic-gate usb_port_t port;
22960Sstevel@tonic-gate hubd_t *hubd;
22970Sstevel@tonic-gate major_t hub_major = ddi_name_to_major("hubd");
22989430SRaymond.Chen@Sun.COM major_t hwahc_major = ddi_name_to_major("hwahc");
22999430SRaymond.Chen@Sun.COM major_t usbmid_major = ddi_name_to_major("usb_mid");
23000Sstevel@tonic-gate
23010Sstevel@tonic-gate /*
23020Sstevel@tonic-gate * make sure dip is a usb hub, major of root hub is HCD
23030Sstevel@tonic-gate * major
23040Sstevel@tonic-gate */
23050Sstevel@tonic-gate if (!usba_is_root_hub(dip)) {
23069430SRaymond.Chen@Sun.COM if (ddi_driver_major(dip) == usbmid_major) {
23079430SRaymond.Chen@Sun.COM /*
23089430SRaymond.Chen@Sun.COM * need to walk the children since it might be a
23099430SRaymond.Chen@Sun.COM * HWA device
23109430SRaymond.Chen@Sun.COM */
23119430SRaymond.Chen@Sun.COM
23129430SRaymond.Chen@Sun.COM return (DDI_WALK_CONTINUE);
23139430SRaymond.Chen@Sun.COM }
23149430SRaymond.Chen@Sun.COM
23159430SRaymond.Chen@Sun.COM /* TODO: DWA device may also need special handling */
23169430SRaymond.Chen@Sun.COM
23179430SRaymond.Chen@Sun.COM if (((ddi_driver_major(dip) != hub_major) &&
23189430SRaymond.Chen@Sun.COM (ddi_driver_major(dip) != hwahc_major)) ||
23191333Scth !i_ddi_devi_attached(dip)) {
23200Sstevel@tonic-gate
23210Sstevel@tonic-gate return (DDI_WALK_PRUNECHILD);
23220Sstevel@tonic-gate }
23230Sstevel@tonic-gate }
23240Sstevel@tonic-gate
23250Sstevel@tonic-gate hubd = hubd_get_soft_state(dip);
23260Sstevel@tonic-gate if (hubd == NULL) {
23270Sstevel@tonic-gate
23280Sstevel@tonic-gate return (DDI_WALK_PRUNECHILD);
23290Sstevel@tonic-gate }
23300Sstevel@tonic-gate
23310Sstevel@tonic-gate /* walk child list and remove nodes with flag DEVI_DEVICE_REMOVED */
23320Sstevel@tonic-gate ndi_devi_enter(dip, &circ);
23330Sstevel@tonic-gate
23349430SRaymond.Chen@Sun.COM if (ddi_driver_major(dip) != hwahc_major) {
23359430SRaymond.Chen@Sun.COM /* for normal usb hub or root hub */
23369430SRaymond.Chen@Sun.COM mutex_enter(HUBD_MUTEX(hubd));
23379430SRaymond.Chen@Sun.COM for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
23389430SRaymond.Chen@Sun.COM dev_info_t *cdip = hubd->h_children_dips[port];
23399430SRaymond.Chen@Sun.COM
23409430SRaymond.Chen@Sun.COM if (cdip == NULL || DEVI_IS_DEVICE_REMOVED(cdip) == 0) {
23419430SRaymond.Chen@Sun.COM
23429430SRaymond.Chen@Sun.COM continue;
23439430SRaymond.Chen@Sun.COM }
23449430SRaymond.Chen@Sun.COM
23459430SRaymond.Chen@Sun.COM (void) hubd_delete_child(hubd, port, NDI_DEVI_REMOVE,
23469430SRaymond.Chen@Sun.COM B_TRUE);
23479430SRaymond.Chen@Sun.COM }
23489430SRaymond.Chen@Sun.COM mutex_exit(HUBD_MUTEX(hubd));
23499430SRaymond.Chen@Sun.COM } else {
23509430SRaymond.Chen@Sun.COM /* for HWA */
23519430SRaymond.Chen@Sun.COM if (hubd->h_cleanup_child != NULL) {
23529430SRaymond.Chen@Sun.COM if (hubd->h_cleanup_child(dip) != USB_SUCCESS) {
23539430SRaymond.Chen@Sun.COM ndi_devi_exit(dip, circ);
23549430SRaymond.Chen@Sun.COM
23559430SRaymond.Chen@Sun.COM return (DDI_WALK_PRUNECHILD);
23569430SRaymond.Chen@Sun.COM }
23579430SRaymond.Chen@Sun.COM } else {
23589430SRaymond.Chen@Sun.COM ndi_devi_exit(dip, circ);
23599430SRaymond.Chen@Sun.COM
23609430SRaymond.Chen@Sun.COM return (DDI_WALK_PRUNECHILD);
23619430SRaymond.Chen@Sun.COM }
23629430SRaymond.Chen@Sun.COM }
23639430SRaymond.Chen@Sun.COM
23640Sstevel@tonic-gate ndi_devi_exit(dip, circ);
23650Sstevel@tonic-gate
23660Sstevel@tonic-gate /* skip siblings of root hub */
23670Sstevel@tonic-gate if (usba_is_root_hub(dip)) {
23680Sstevel@tonic-gate
23690Sstevel@tonic-gate return (DDI_WALK_PRUNESIB);
23700Sstevel@tonic-gate }
23710Sstevel@tonic-gate
23720Sstevel@tonic-gate return (DDI_WALK_CONTINUE);
23730Sstevel@tonic-gate }
23740Sstevel@tonic-gate
23750Sstevel@tonic-gate
23760Sstevel@tonic-gate /*
23770Sstevel@tonic-gate * this thread will walk all children under the root hub for this
23780Sstevel@tonic-gate * USB bus instance and attempt to remove them
23790Sstevel@tonic-gate */
23800Sstevel@tonic-gate static void
hubd_root_hub_cleanup_thread(void * arg)23810Sstevel@tonic-gate hubd_root_hub_cleanup_thread(void *arg)
23820Sstevel@tonic-gate {
23830Sstevel@tonic-gate int circ;
23840Sstevel@tonic-gate hubd_t *root_hubd = (hubd_t *)arg;
23850Sstevel@tonic-gate dev_info_t *rh_dip = root_hubd->h_dip;
23860Sstevel@tonic-gate #ifndef __lock_lint
23870Sstevel@tonic-gate callb_cpr_t cprinfo;
23880Sstevel@tonic-gate
23890Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, HUBD_MUTEX(root_hubd), callb_generic_cpr,
23900Sstevel@tonic-gate "USB root hub");
23910Sstevel@tonic-gate #endif
23920Sstevel@tonic-gate
23930Sstevel@tonic-gate for (;;) {
23940Sstevel@tonic-gate /* don't race with detach */
23950Sstevel@tonic-gate ndi_hold_devi(rh_dip);
23960Sstevel@tonic-gate
23970Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd));
23980Sstevel@tonic-gate root_hubd->h_cleanup_needed = 0;
23990Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd));
24000Sstevel@tonic-gate
24010Sstevel@tonic-gate (void) devfs_clean(rh_dip, NULL, 0);
24020Sstevel@tonic-gate
24030Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &circ);
24040Sstevel@tonic-gate ddi_walk_devs(rh_dip, hubd_check_disconnected_ports,
24054763Slg150142 NULL);
24060Sstevel@tonic-gate #ifdef __lock_lint
24070Sstevel@tonic-gate (void) hubd_check_disconnected_ports(rh_dip, NULL);
24080Sstevel@tonic-gate #endif
24090Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), circ);
24100Sstevel@tonic-gate
24110Sstevel@tonic-gate /* quit if we are not enabled anymore */
24120Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd));
24130Sstevel@tonic-gate if ((root_hubd->h_cleanup_enabled == B_FALSE) ||
24140Sstevel@tonic-gate (root_hubd->h_cleanup_needed == B_FALSE)) {
24150Sstevel@tonic-gate root_hubd->h_cleanup_active = B_FALSE;
24160Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd));
24170Sstevel@tonic-gate ndi_rele_devi(rh_dip);
24180Sstevel@tonic-gate
24190Sstevel@tonic-gate break;
24200Sstevel@tonic-gate }
24210Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd));
24220Sstevel@tonic-gate ndi_rele_devi(rh_dip);
24230Sstevel@tonic-gate
24240Sstevel@tonic-gate #ifndef __lock_lint
24250Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd));
24260Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo);
24270Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd));
24280Sstevel@tonic-gate
24290Sstevel@tonic-gate delay(drv_usectohz(hubd_dip_cleanup_delay));
24300Sstevel@tonic-gate
24310Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd));
24320Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, HUBD_MUTEX(root_hubd));
24330Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd));
24340Sstevel@tonic-gate #endif
24350Sstevel@tonic-gate }
24360Sstevel@tonic-gate
24370Sstevel@tonic-gate #ifndef __lock_lint
24380Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd));
24390Sstevel@tonic-gate CALLB_CPR_EXIT(&cprinfo);
24400Sstevel@tonic-gate #endif
24410Sstevel@tonic-gate }
24420Sstevel@tonic-gate
24430Sstevel@tonic-gate
24449430SRaymond.Chen@Sun.COM void
hubd_schedule_cleanup(dev_info_t * rh_dip)24450Sstevel@tonic-gate hubd_schedule_cleanup(dev_info_t *rh_dip)
24460Sstevel@tonic-gate {
24479430SRaymond.Chen@Sun.COM hubd_t *root_hubd;
24489430SRaymond.Chen@Sun.COM
24499430SRaymond.Chen@Sun.COM /*
24509430SRaymond.Chen@Sun.COM * The usb_root_hub_dip pointer for the child hub of the WUSB
24519430SRaymond.Chen@Sun.COM * wire adapter class device points to the wire adapter, not
24529430SRaymond.Chen@Sun.COM * the root hub. Need to find the real root hub dip so that
24539430SRaymond.Chen@Sun.COM * the cleanup thread only starts from the root hub.
24549430SRaymond.Chen@Sun.COM */
24559430SRaymond.Chen@Sun.COM while (!usba_is_root_hub(rh_dip)) {
24569430SRaymond.Chen@Sun.COM root_hubd = hubd_get_soft_state(rh_dip);
24579430SRaymond.Chen@Sun.COM if (root_hubd != NULL) {
24589430SRaymond.Chen@Sun.COM rh_dip = root_hubd->h_usba_device->usb_root_hub_dip;
24599430SRaymond.Chen@Sun.COM if (rh_dip == NULL) {
24609430SRaymond.Chen@Sun.COM USB_DPRINTF_L2(DPRINT_MASK_ATTA,
24619430SRaymond.Chen@Sun.COM root_hubd->h_log_handle,
24629430SRaymond.Chen@Sun.COM "hubd_schedule_cleanup: null rh dip");
24639430SRaymond.Chen@Sun.COM
24649430SRaymond.Chen@Sun.COM return;
24659430SRaymond.Chen@Sun.COM }
24669430SRaymond.Chen@Sun.COM } else {
24679430SRaymond.Chen@Sun.COM USB_DPRINTF_L2(DPRINT_MASK_ATTA,
24689430SRaymond.Chen@Sun.COM root_hubd->h_log_handle,
24699430SRaymond.Chen@Sun.COM "hubd_schedule_cleanup: cannot find root hub");
24709430SRaymond.Chen@Sun.COM
24719430SRaymond.Chen@Sun.COM return;
24729430SRaymond.Chen@Sun.COM }
24739430SRaymond.Chen@Sun.COM }
24749430SRaymond.Chen@Sun.COM root_hubd = hubd_get_soft_state(rh_dip);
24750Sstevel@tonic-gate
24760Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(root_hubd));
24770Sstevel@tonic-gate root_hubd->h_cleanup_needed = B_TRUE;
24780Sstevel@tonic-gate if (root_hubd->h_cleanup_enabled && !(root_hubd->h_cleanup_active)) {
24790Sstevel@tonic-gate root_hubd->h_cleanup_active = B_TRUE;
24800Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd));
24810Sstevel@tonic-gate (void) thread_create(NULL, 0,
24820Sstevel@tonic-gate hubd_root_hub_cleanup_thread,
24830Sstevel@tonic-gate (void *)root_hubd, 0, &p0, TS_RUN,
24840Sstevel@tonic-gate minclsyspri);
24850Sstevel@tonic-gate } else {
24860Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(root_hubd));
24870Sstevel@tonic-gate }
24880Sstevel@tonic-gate }
24890Sstevel@tonic-gate
24900Sstevel@tonic-gate
24910Sstevel@tonic-gate /*
24920Sstevel@tonic-gate * hubd_restore_device_state:
24930Sstevel@tonic-gate * - set config for the hub
24940Sstevel@tonic-gate * - power cycle all the ports
24950Sstevel@tonic-gate * - for each port that was connected
24960Sstevel@tonic-gate * - reset port
24970Sstevel@tonic-gate * - assign addrs to the device on this port
24980Sstevel@tonic-gate * - restart polling
24990Sstevel@tonic-gate * - reset suspend flag
25000Sstevel@tonic-gate */
25010Sstevel@tonic-gate static void
hubd_restore_device_state(dev_info_t * dip,hubd_t * hubd)25020Sstevel@tonic-gate hubd_restore_device_state(dev_info_t *dip, hubd_t *hubd)
25030Sstevel@tonic-gate {
25040Sstevel@tonic-gate int rval;
25050Sstevel@tonic-gate int retry;
25060Sstevel@tonic-gate uint_t hub_prev_state;
25070Sstevel@tonic-gate usb_port_t port;
25080Sstevel@tonic-gate uint16_t status;
25090Sstevel@tonic-gate uint16_t change;
25100Sstevel@tonic-gate dev_info_t *ch_dip;
25110Sstevel@tonic-gate boolean_t ehci_root_hub;
25120Sstevel@tonic-gate
25130Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
25140Sstevel@tonic-gate "hubd_restore_device_state:");
25150Sstevel@tonic-gate
25160Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
25170Sstevel@tonic-gate hub_prev_state = hubd->h_dev_state;
25180Sstevel@tonic-gate ASSERT(hub_prev_state != USB_DEV_PWRED_DOWN);
25190Sstevel@tonic-gate
25200Sstevel@tonic-gate /* First bring the device to full power */
25210Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0);
25220Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
25230Sstevel@tonic-gate
25240Sstevel@tonic-gate (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
25250Sstevel@tonic-gate
25260Sstevel@tonic-gate if (!usba_is_root_hub(dip) &&
25270Sstevel@tonic-gate (usb_check_same_device(dip, hubd->h_log_handle, USB_LOG_L0,
25280Sstevel@tonic-gate DPRINT_MASK_HOTPLUG,
25290Sstevel@tonic-gate USB_CHK_BASIC|USB_CHK_CFG, NULL) != USB_SUCCESS)) {
25300Sstevel@tonic-gate
25310Sstevel@tonic-gate /* change the device state to disconnected */
25320Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
25330Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED;
25340Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0);
25350Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
25360Sstevel@tonic-gate
25370Sstevel@tonic-gate return;
25380Sstevel@tonic-gate }
25390Sstevel@tonic-gate
25400Sstevel@tonic-gate ehci_root_hub = (strcmp(ddi_driver_name(dip), "ehci") == 0);
25410Sstevel@tonic-gate
25420Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
25430Sstevel@tonic-gate /* First turn off all port power */
25440Sstevel@tonic-gate rval = hubd_disable_all_port_power(hubd);
25450Sstevel@tonic-gate if (rval != USB_SUCCESS) {
25460Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
25470Sstevel@tonic-gate "hubd_restore_device_state:"
25480Sstevel@tonic-gate "turning off port power failed");
25490Sstevel@tonic-gate }
25500Sstevel@tonic-gate
25510Sstevel@tonic-gate /* Settling time before turning on again */
25520Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
25530Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay / 100));
25540Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
25550Sstevel@tonic-gate
25560Sstevel@tonic-gate /* enable power on all ports so we can see connects */
25570Sstevel@tonic-gate if (hubd_enable_all_port_power(hubd) != USB_SUCCESS) {
25580Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
25590Sstevel@tonic-gate "hubd_restore_device_state: turn on port power failed");
25600Sstevel@tonic-gate
25610Sstevel@tonic-gate /* disable whatever was enabled */
25620Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd);
25630Sstevel@tonic-gate
25640Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0);
25650Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
25660Sstevel@tonic-gate
25670Sstevel@tonic-gate return;
25680Sstevel@tonic-gate }
25690Sstevel@tonic-gate
25700Sstevel@tonic-gate /*
25710Sstevel@tonic-gate * wait at least 3 frames before accessing devices
25720Sstevel@tonic-gate * (note that delay's minimal time is one clock tick which
25730Sstevel@tonic-gate * is 10ms unless hires_tick has been changed)
25740Sstevel@tonic-gate */
25750Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
25760Sstevel@tonic-gate delay(drv_usectohz(10000));
25770Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
25780Sstevel@tonic-gate
25790Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_HUB_STATE_RECOVER;
25800Sstevel@tonic-gate
25810Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
25820Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
25830Sstevel@tonic-gate "hubd_restore_device_state: port=%d", port);
25840Sstevel@tonic-gate
25850Sstevel@tonic-gate /*
25860Sstevel@tonic-gate * the childen_dips list may have dips that have been
25870Sstevel@tonic-gate * already deallocated. we only get a post_detach notification
25880Sstevel@tonic-gate * but not a destroy notification
25890Sstevel@tonic-gate */
25900Sstevel@tonic-gate ch_dip = hubd->h_children_dips[port];
25910Sstevel@tonic-gate if (ch_dip) {
25920Sstevel@tonic-gate /* get port status */
25930Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port,
25940Sstevel@tonic-gate &status, &change, PORT_CHANGE_CSC);
25950Sstevel@tonic-gate
25960Sstevel@tonic-gate /* check if it is truly connected */
25970Sstevel@tonic-gate if (status & PORT_STATUS_CCS) {
25980Sstevel@tonic-gate /*
25990Sstevel@tonic-gate * Now reset port and assign the device
26000Sstevel@tonic-gate * its original address
26010Sstevel@tonic-gate */
26020Sstevel@tonic-gate retry = 0;
26030Sstevel@tonic-gate do {
26040Sstevel@tonic-gate (void) hubd_reset_port(hubd, port);
26050Sstevel@tonic-gate
26060Sstevel@tonic-gate /* required for ppx */
26070Sstevel@tonic-gate (void) hubd_enable_port(hubd, port);
26080Sstevel@tonic-gate
26090Sstevel@tonic-gate if (retry) {
26100Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
26110Sstevel@tonic-gate delay(drv_usectohz(
26124763Slg150142 hubd_device_delay/2));
26130Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
26140Sstevel@tonic-gate }
26150Sstevel@tonic-gate
26160Sstevel@tonic-gate rval = hubd_setdevaddr(hubd, port);
26170Sstevel@tonic-gate retry++;
26180Sstevel@tonic-gate } while ((rval != USB_SUCCESS) &&
26190Sstevel@tonic-gate (retry < hubd_retry_enumerate));
26200Sstevel@tonic-gate
26210Sstevel@tonic-gate hubd_setdevconfig(hubd, port);
26220Sstevel@tonic-gate
26230Sstevel@tonic-gate if (hub_prev_state == USB_DEV_DISCONNECTED) {
26240Sstevel@tonic-gate /* post a connect event */
26250Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
26260Sstevel@tonic-gate hubd_post_event(hubd, port,
26270Sstevel@tonic-gate USBA_EVENT_TAG_HOT_INSERTION);
26280Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
26290Sstevel@tonic-gate } else {
26300Sstevel@tonic-gate /*
26310Sstevel@tonic-gate * Since we have this device connected
26320Sstevel@tonic-gate * mark it reinserted to prevent
26330Sstevel@tonic-gate * cleanup thread from stepping in.
26340Sstevel@tonic-gate */
2635495Scth mutex_exit(HUBD_MUTEX(hubd));
2636495Scth mutex_enter(&(DEVI(ch_dip)->devi_lock));
26370Sstevel@tonic-gate DEVI_SET_DEVICE_REINSERTED(ch_dip);
2638495Scth mutex_exit(&(DEVI(ch_dip)->devi_lock));
26390Sstevel@tonic-gate
26400Sstevel@tonic-gate /*
26410Sstevel@tonic-gate * reopen pipes for children for
26420Sstevel@tonic-gate * their DDI_RESUME
26430Sstevel@tonic-gate */
26440Sstevel@tonic-gate rval = usba_persistent_pipe_open(
26450Sstevel@tonic-gate usba_get_usba_device(ch_dip));
26460Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
26470Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS);
26480Sstevel@tonic-gate }
26490Sstevel@tonic-gate } else {
26500Sstevel@tonic-gate /*
26510Sstevel@tonic-gate * Mark this dip for deletion as the device
26520Sstevel@tonic-gate * is not physically present, and schedule
26530Sstevel@tonic-gate * cleanup thread upon post resume
26540Sstevel@tonic-gate */
26550Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
26560Sstevel@tonic-gate
26570Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA,
26580Sstevel@tonic-gate hubd->h_log_handle,
26590Sstevel@tonic-gate "hubd_restore_device_state: "
26600Sstevel@tonic-gate "dip=%p on port=%d marked for cleanup",
26616898Sfb209375 (void *)ch_dip, port);
2662495Scth mutex_enter(&(DEVI(ch_dip)->devi_lock));
26630Sstevel@tonic-gate DEVI_SET_DEVICE_REMOVED(ch_dip);
2664495Scth mutex_exit(&(DEVI(ch_dip)->devi_lock));
26650Sstevel@tonic-gate
26660Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
26670Sstevel@tonic-gate }
26680Sstevel@tonic-gate } else if (ehci_root_hub) {
26690Sstevel@tonic-gate /* get port status */
26700Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port,
26710Sstevel@tonic-gate &status, &change, PORT_CHANGE_CSC);
26720Sstevel@tonic-gate
26730Sstevel@tonic-gate /* check if it is truly connected */
26740Sstevel@tonic-gate if (status & PORT_STATUS_CCS) {
26750Sstevel@tonic-gate /*
26760Sstevel@tonic-gate * reset the port to find out if we have
26770Sstevel@tonic-gate * 2.0 device connected or 1.X. A 2.0
26780Sstevel@tonic-gate * device will still be seen as connected,
26790Sstevel@tonic-gate * while a 1.X device will switch over to
26800Sstevel@tonic-gate * the companion controller.
26810Sstevel@tonic-gate */
26820Sstevel@tonic-gate (void) hubd_reset_port(hubd, port);
26830Sstevel@tonic-gate
26840Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port,
26850Sstevel@tonic-gate &status, &change, PORT_CHANGE_CSC);
26860Sstevel@tonic-gate
26870Sstevel@tonic-gate if (status &
26880Sstevel@tonic-gate (PORT_STATUS_CCS | PORT_STATUS_HSDA)) {
26890Sstevel@tonic-gate /*
26900Sstevel@tonic-gate * We have a USB 2.0 device
26910Sstevel@tonic-gate * connected. Power cycle this port
26920Sstevel@tonic-gate * so that hotplug thread can
26930Sstevel@tonic-gate * enumerate this device.
26940Sstevel@tonic-gate */
26950Sstevel@tonic-gate (void) hubd_toggle_port(hubd, port);
26960Sstevel@tonic-gate } else {
26970Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA,
26980Sstevel@tonic-gate hubd->h_log_handle,
26990Sstevel@tonic-gate "hubd_restore_device_state: "
27000Sstevel@tonic-gate "device on port %d switched over",
27010Sstevel@tonic-gate port);
27020Sstevel@tonic-gate }
27030Sstevel@tonic-gate }
27040Sstevel@tonic-gate
27050Sstevel@tonic-gate }
27060Sstevel@tonic-gate }
27070Sstevel@tonic-gate
27080Sstevel@tonic-gate
27090Sstevel@tonic-gate /* if the device had remote wakeup earlier, enable it again */
27100Sstevel@tonic-gate if (hubd->h_hubpm->hubp_wakeup_enabled) {
27110Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
27120Sstevel@tonic-gate (void) usb_handle_remote_wakeup(hubd->h_dip,
27130Sstevel@tonic-gate USB_REMOTE_WAKEUP_ENABLE);
27140Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
27150Sstevel@tonic-gate }
27160Sstevel@tonic-gate
27170Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE;
27180Sstevel@tonic-gate hubd_start_polling(hubd, 0);
27190Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0);
27200Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
27210Sstevel@tonic-gate }
27220Sstevel@tonic-gate
27230Sstevel@tonic-gate
27240Sstevel@tonic-gate /*
27250Sstevel@tonic-gate * hubd_cleanup:
27260Sstevel@tonic-gate * cleanup hubd and deallocate. this function is called for
27270Sstevel@tonic-gate * handling attach failures and detaching including dynamic
27280Sstevel@tonic-gate * reconfiguration. If called from attaching, it must clean
27290Sstevel@tonic-gate * up the whole thing and return success.
27300Sstevel@tonic-gate */
27310Sstevel@tonic-gate /*ARGSUSED*/
27320Sstevel@tonic-gate static int
hubd_cleanup(dev_info_t * dip,hubd_t * hubd)27330Sstevel@tonic-gate hubd_cleanup(dev_info_t *dip, hubd_t *hubd)
27340Sstevel@tonic-gate {
27350Sstevel@tonic-gate int circ, rval, old_dev_state;
27360Sstevel@tonic-gate hub_power_t *hubpm;
27370Sstevel@tonic-gate #ifdef DEBUG
27380Sstevel@tonic-gate usb_port_t port;
27390Sstevel@tonic-gate #endif
27400Sstevel@tonic-gate
27410Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
27420Sstevel@tonic-gate "hubd_cleanup:");
27430Sstevel@tonic-gate
27440Sstevel@tonic-gate if ((hubd->h_init_state & HUBD_LOCKS_DONE) == 0) {
27450Sstevel@tonic-gate goto done;
27460Sstevel@tonic-gate }
27470Sstevel@tonic-gate
27480Sstevel@tonic-gate /* ensure we are the only one active */
27490Sstevel@tonic-gate ndi_devi_enter(dip, &circ);
27500Sstevel@tonic-gate
27510Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
27520Sstevel@tonic-gate
27530Sstevel@tonic-gate /* Cleanup failure is only allowed if called from detach */
27540Sstevel@tonic-gate if (DEVI_IS_DETACHING(dip)) {
27550Sstevel@tonic-gate dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
27560Sstevel@tonic-gate
27570Sstevel@tonic-gate /*
27580Sstevel@tonic-gate * We are being called from detach.
27590Sstevel@tonic-gate * Fail immediately if the hotplug thread is running
27600Sstevel@tonic-gate * else set the dev_state to disconnected so that
27610Sstevel@tonic-gate * hotplug thread just exits without doing anything.
27620Sstevel@tonic-gate */
27630Sstevel@tonic-gate if (hubd->h_bus_ctls || hubd->h_bus_pwr ||
27640Sstevel@tonic-gate hubd->h_hotplug_thread) {
27650Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
27660Sstevel@tonic-gate ndi_devi_exit(dip, circ);
27670Sstevel@tonic-gate
27680Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
27690Sstevel@tonic-gate "hubd_cleanup: hotplug thread/bus ctl active "
27700Sstevel@tonic-gate "- failing detach");
27710Sstevel@tonic-gate
27720Sstevel@tonic-gate return (USB_FAILURE);
27730Sstevel@tonic-gate }
27740Sstevel@tonic-gate
27750Sstevel@tonic-gate /*
27760Sstevel@tonic-gate * if the deathrow thread is still active or about
27770Sstevel@tonic-gate * to become active, fail detach
27780Sstevel@tonic-gate * the roothup can only be detached if nexus drivers
27790Sstevel@tonic-gate * are unloaded or explicitly offlined
27800Sstevel@tonic-gate */
27810Sstevel@tonic-gate if (rh_dip == dip) {
27820Sstevel@tonic-gate if (hubd->h_cleanup_needed ||
27830Sstevel@tonic-gate hubd->h_cleanup_active) {
27840Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
27850Sstevel@tonic-gate ndi_devi_exit(dip, circ);
27860Sstevel@tonic-gate
27870Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA,
27880Sstevel@tonic-gate hubd->h_log_handle,
27890Sstevel@tonic-gate "hubd_cleanup: deathrow still active?"
27900Sstevel@tonic-gate "- failing detach");
27910Sstevel@tonic-gate
27920Sstevel@tonic-gate return (USB_FAILURE);
27930Sstevel@tonic-gate }
27940Sstevel@tonic-gate }
27950Sstevel@tonic-gate }
27960Sstevel@tonic-gate
27970Sstevel@tonic-gate old_dev_state = hubd->h_dev_state;
27980Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED;
27990Sstevel@tonic-gate
28000Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
28010Sstevel@tonic-gate "hubd_cleanup: stop polling");
28020Sstevel@tonic-gate hubd_close_intr_pipe(hubd);
28030Sstevel@tonic-gate
28040Sstevel@tonic-gate ASSERT((hubd->h_bus_ctls || hubd->h_bus_pwr ||
28050Sstevel@tonic-gate hubd->h_hotplug_thread) == 0);
28060Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
28070Sstevel@tonic-gate
28080Sstevel@tonic-gate /*
28090Sstevel@tonic-gate * deallocate events, if events are still registered
28100Sstevel@tonic-gate * (ie. children still attached) then we have to fail the detach
28110Sstevel@tonic-gate */
28120Sstevel@tonic-gate if (hubd->h_ndi_event_hdl) {
28130Sstevel@tonic-gate
28140Sstevel@tonic-gate rval = ndi_event_free_hdl(hubd->h_ndi_event_hdl);
28150Sstevel@tonic-gate if (DEVI_IS_ATTACHING(dip)) {
28160Sstevel@tonic-gate
28170Sstevel@tonic-gate /* It must return success if attaching. */
28180Sstevel@tonic-gate ASSERT(rval == NDI_SUCCESS);
28190Sstevel@tonic-gate
28200Sstevel@tonic-gate } else if (rval != NDI_SUCCESS) {
28210Sstevel@tonic-gate
2822978Sfrits USB_DPRINTF_L2(DPRINT_MASK_ALL, hubd->h_log_handle,
28230Sstevel@tonic-gate "hubd_cleanup: ndi_event_free_hdl failed");
28240Sstevel@tonic-gate ndi_devi_exit(dip, circ);
28250Sstevel@tonic-gate
28260Sstevel@tonic-gate return (USB_FAILURE);
28270Sstevel@tonic-gate
28280Sstevel@tonic-gate }
28290Sstevel@tonic-gate }
28300Sstevel@tonic-gate
28310Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
28320Sstevel@tonic-gate
28331001Ssl147100 if (hubd->h_init_state & HUBD_CHILDREN_CREATED) {
28340Sstevel@tonic-gate #ifdef DEBUG
28351001Ssl147100 for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
28361001Ssl147100 ASSERT(hubd->h_usba_devices[port] == NULL);
28371001Ssl147100 ASSERT(hubd->h_children_dips[port] == NULL);
28381001Ssl147100 }
28390Sstevel@tonic-gate #endif
28401001Ssl147100 kmem_free(hubd->h_children_dips, hubd->h_cd_list_length);
28411001Ssl147100 kmem_free(hubd->h_usba_devices, hubd->h_cd_list_length);
28421001Ssl147100 }
28430Sstevel@tonic-gate
28440Sstevel@tonic-gate /*
28450Sstevel@tonic-gate * Disable the event callbacks first, after this point, event
28460Sstevel@tonic-gate * callbacks will never get called. Note we shouldn't hold
28470Sstevel@tonic-gate * mutex while unregistering events because there may be a
28480Sstevel@tonic-gate * competing event callback thread. Event callbacks are done
28490Sstevel@tonic-gate * with ndi mutex held and this can cause a potential deadlock.
28500Sstevel@tonic-gate * Note that cleanup can't fail after deregistration of events.
28510Sstevel@tonic-gate */
28520Sstevel@tonic-gate if (hubd->h_init_state & HUBD_EVENTS_REGISTERED) {
28530Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
28540Sstevel@tonic-gate usb_unregister_event_cbs(dip, &hubd_events);
28550Sstevel@tonic-gate hubd_unregister_cpr_callback(hubd);
28560Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
28570Sstevel@tonic-gate }
28580Sstevel@tonic-gate
28590Sstevel@tonic-gate /* restore the old dev state so that device can be put into low power */
28600Sstevel@tonic-gate hubd->h_dev_state = old_dev_state;
28610Sstevel@tonic-gate hubpm = hubd->h_hubpm;
28620Sstevel@tonic-gate
28630Sstevel@tonic-gate if ((hubpm) && (hubd->h_dev_state != USB_DEV_DISCONNECTED)) {
28640Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, dip, 0);
28650Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
28660Sstevel@tonic-gate if (hubd->h_hubpm->hubp_wakeup_enabled) {
28670Sstevel@tonic-gate /*
28680Sstevel@tonic-gate * Bring the hub to full power before
28690Sstevel@tonic-gate * issuing the disable remote wakeup command
28700Sstevel@tonic-gate */
28710Sstevel@tonic-gate (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR);
28720Sstevel@tonic-gate
28730Sstevel@tonic-gate if ((rval = usb_handle_remote_wakeup(hubd->h_dip,
28740Sstevel@tonic-gate USB_REMOTE_WAKEUP_DISABLE)) != USB_SUCCESS) {
28750Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM,
28760Sstevel@tonic-gate hubd->h_log_handle,
28770Sstevel@tonic-gate "hubd_cleanup: disable remote wakeup "
28780Sstevel@tonic-gate "fails=%d", rval);
28790Sstevel@tonic-gate }
28800Sstevel@tonic-gate }
28810Sstevel@tonic-gate
28820Sstevel@tonic-gate (void) pm_lower_power(hubd->h_dip, 0, USB_DEV_OS_PWR_OFF);
28830Sstevel@tonic-gate
28840Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
28850Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, dip, 0);
28860Sstevel@tonic-gate }
28870Sstevel@tonic-gate
28880Sstevel@tonic-gate if (hubpm) {
28890Sstevel@tonic-gate if (hubpm->hubp_child_pwrstate) {
28900Sstevel@tonic-gate kmem_free(hubpm->hubp_child_pwrstate,
28910Sstevel@tonic-gate MAX_PORTS + 1);
28920Sstevel@tonic-gate }
28930Sstevel@tonic-gate kmem_free(hubpm, sizeof (hub_power_t));
28940Sstevel@tonic-gate }
28950Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
28960Sstevel@tonic-gate
28970Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
28980Sstevel@tonic-gate "hubd_cleanup: freeing space");
28990Sstevel@tonic-gate
29000Sstevel@tonic-gate if (hubd->h_init_state & HUBD_HUBDI_REGISTERED) {
29010Sstevel@tonic-gate rval = usba_hubdi_unregister(dip);
29020Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS);
29030Sstevel@tonic-gate }
29040Sstevel@tonic-gate
29050Sstevel@tonic-gate if (hubd->h_init_state & HUBD_LOCKS_DONE) {
29060Sstevel@tonic-gate mutex_destroy(HUBD_MUTEX(hubd));
29070Sstevel@tonic-gate cv_destroy(&hubd->h_cv_reset_port);
29084844Slg150142 cv_destroy(&hubd->h_cv_hotplug_dev);
29090Sstevel@tonic-gate }
29100Sstevel@tonic-gate
29110Sstevel@tonic-gate ndi_devi_exit(dip, circ);
29120Sstevel@tonic-gate
29130Sstevel@tonic-gate if (hubd->h_init_state & HUBD_MINOR_NODE_CREATED) {
29140Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL);
29150Sstevel@tonic-gate }
29160Sstevel@tonic-gate
29170Sstevel@tonic-gate if (usba_is_root_hub(dip)) {
29180Sstevel@tonic-gate usb_pipe_close(dip, hubd->h_default_pipe,
29190Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
29200Sstevel@tonic-gate }
29210Sstevel@tonic-gate
29220Sstevel@tonic-gate done:
29230Sstevel@tonic-gate if (hubd->h_ancestry_str) {
29240Sstevel@tonic-gate kmem_free(hubd->h_ancestry_str, HUBD_APID_NAMELEN);
29250Sstevel@tonic-gate }
29260Sstevel@tonic-gate
29270Sstevel@tonic-gate usb_client_detach(dip, hubd->h_dev_data);
29280Sstevel@tonic-gate
29290Sstevel@tonic-gate usb_free_log_hdl(hubd->h_log_handle);
29300Sstevel@tonic-gate
29310Sstevel@tonic-gate if (!usba_is_root_hub(dip)) {
29320Sstevel@tonic-gate ddi_soft_state_free(hubd_statep, ddi_get_instance(dip));
29330Sstevel@tonic-gate }
29340Sstevel@tonic-gate
29350Sstevel@tonic-gate ddi_prop_remove_all(dip);
29360Sstevel@tonic-gate
29370Sstevel@tonic-gate return (USB_SUCCESS);
29380Sstevel@tonic-gate }
29390Sstevel@tonic-gate
29400Sstevel@tonic-gate
29410Sstevel@tonic-gate /*
29422651Ssl147100 * hubd_determine_port_connection:
29432651Ssl147100 * Determine which port is in connect status but does not
29442651Ssl147100 * have connect status change bit set, and mark port change
29452651Ssl147100 * bit accordingly.
29462651Ssl147100 * This function is applied during hub attach time.
29472651Ssl147100 */
29482651Ssl147100 static usb_port_mask_t
hubd_determine_port_connection(hubd_t * hubd)29492651Ssl147100 hubd_determine_port_connection(hubd_t *hubd)
29502651Ssl147100 {
29512651Ssl147100 usb_port_t port;
29522651Ssl147100 usb_hub_descr_t *hub_descr;
29532651Ssl147100 uint16_t status;
29542651Ssl147100 uint16_t change;
29552651Ssl147100 usb_port_mask_t port_change = 0;
29562651Ssl147100
29572651Ssl147100 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
29582651Ssl147100
29592651Ssl147100 hub_descr = &hubd->h_hub_descr;
29602651Ssl147100
29612651Ssl147100 for (port = 1; port <= hub_descr->bNbrPorts; port++) {
29622651Ssl147100
29632651Ssl147100 (void) hubd_determine_port_status(hubd, port, &status,
29642651Ssl147100 &change, 0);
29652651Ssl147100
29662651Ssl147100 /* Check if port is in connect status */
29672651Ssl147100 if (!(status & PORT_STATUS_CCS)) {
29682651Ssl147100
29692651Ssl147100 continue;
29702651Ssl147100 }
29712651Ssl147100
29722651Ssl147100 /*
29732651Ssl147100 * Check if port Connect Status Change bit has been set.
29742651Ssl147100 * If already set, the connection will be handled by
29752651Ssl147100 * intr polling callback, not during attach.
29762651Ssl147100 */
29772651Ssl147100 if (change & PORT_CHANGE_CSC) {
29782651Ssl147100
29792651Ssl147100 continue;
29802651Ssl147100 }
29812651Ssl147100
29822651Ssl147100 port_change |= 1 << port;
29832651Ssl147100 }
29842651Ssl147100
29852651Ssl147100 return (port_change);
29862651Ssl147100 }
29872651Ssl147100
29882651Ssl147100
29892651Ssl147100 /*
29900Sstevel@tonic-gate * hubd_check_ports:
29910Sstevel@tonic-gate * - get hub descriptor
29920Sstevel@tonic-gate * - check initial port status
29930Sstevel@tonic-gate * - enable power on all ports
29940Sstevel@tonic-gate * - enable polling on ep1
29950Sstevel@tonic-gate */
29960Sstevel@tonic-gate static int
hubd_check_ports(hubd_t * hubd)29970Sstevel@tonic-gate hubd_check_ports(hubd_t *hubd)
29980Sstevel@tonic-gate {
29992651Ssl147100 int rval;
30002651Ssl147100 usb_port_mask_t port_change = 0;
30012651Ssl147100 hubd_hotplug_arg_t *arg;
30020Sstevel@tonic-gate
30030Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
30040Sstevel@tonic-gate
30050Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
30060Sstevel@tonic-gate "hubd_check_ports: addr=0x%x", usb_get_addr(hubd->h_dip));
30070Sstevel@tonic-gate
30080Sstevel@tonic-gate /*
30090Sstevel@tonic-gate * First turn off all port power
30100Sstevel@tonic-gate */
30110Sstevel@tonic-gate if ((rval = hubd_disable_all_port_power(hubd)) != USB_SUCCESS) {
30120Sstevel@tonic-gate
30130Sstevel@tonic-gate /* disable whatever was enabled */
30140Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd);
30150Sstevel@tonic-gate
30160Sstevel@tonic-gate return (rval);
30170Sstevel@tonic-gate }
30180Sstevel@tonic-gate
30190Sstevel@tonic-gate /*
30200Sstevel@tonic-gate * do not switch on immediately (instantly on root hub)
30210Sstevel@tonic-gate * and allow time to settle
30220Sstevel@tonic-gate */
30230Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
30240Sstevel@tonic-gate delay(drv_usectohz(10000));
30250Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
30260Sstevel@tonic-gate
30270Sstevel@tonic-gate /*
30280Sstevel@tonic-gate * enable power on all ports so we can see connects
30290Sstevel@tonic-gate */
30300Sstevel@tonic-gate if ((rval = hubd_enable_all_port_power(hubd)) != USB_SUCCESS) {
30310Sstevel@tonic-gate /* disable whatever was enabled */
30320Sstevel@tonic-gate (void) hubd_disable_all_port_power(hubd);
30330Sstevel@tonic-gate
30340Sstevel@tonic-gate return (rval);
30350Sstevel@tonic-gate }
30360Sstevel@tonic-gate
30370Sstevel@tonic-gate /* wait at least 3 frames before accessing devices */
30380Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
30390Sstevel@tonic-gate delay(drv_usectohz(10000));
30400Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
30410Sstevel@tonic-gate
30420Sstevel@tonic-gate /*
30430Sstevel@tonic-gate * allocate arrays for saving the dips of each child per port
30440Sstevel@tonic-gate *
30450Sstevel@tonic-gate * ports go from 1 - n, allocate 1 more entry
30460Sstevel@tonic-gate */
30470Sstevel@tonic-gate hubd->h_cd_list_length =
30484763Slg150142 (sizeof (dev_info_t **)) * (hubd->h_hub_descr.bNbrPorts + 1);
30490Sstevel@tonic-gate
30500Sstevel@tonic-gate hubd->h_children_dips = (dev_info_t **)kmem_zalloc(
30514763Slg150142 hubd->h_cd_list_length, KM_SLEEP);
30520Sstevel@tonic-gate hubd->h_usba_devices = (usba_device_t **)kmem_zalloc(
30534763Slg150142 hubd->h_cd_list_length, KM_SLEEP);
30540Sstevel@tonic-gate
30551001Ssl147100 hubd->h_init_state |= HUBD_CHILDREN_CREATED;
30561001Ssl147100
30572651Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
30582651Ssl147100 arg = (hubd_hotplug_arg_t *)kmem_zalloc(
30592651Ssl147100 sizeof (hubd_hotplug_arg_t), KM_SLEEP);
30602651Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
30612651Ssl147100
30622651Ssl147100 if ((rval = hubd_open_intr_pipe(hubd)) != USB_SUCCESS) {
30632651Ssl147100 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
30642651Ssl147100
30652651Ssl147100 return (rval);
30662651Ssl147100 }
30672651Ssl147100
30682651Ssl147100 hubd_start_polling(hubd, 0);
30692651Ssl147100
30702651Ssl147100 /*
30712651Ssl147100 * Some hub devices, like the embedded hub in the CKS ErgoMagic
30722651Ssl147100 * keyboard, may only have connection status bit set, but not
30732651Ssl147100 * have connect status change bit set when a device has been
30742651Ssl147100 * connected to its downstream port before the hub is enumerated.
30752651Ssl147100 * Then when the hub is in enumeration, the devices connected to
30762651Ssl147100 * it cannot be detected by the intr pipe and won't be enumerated.
30772651Ssl147100 * We need to check such situation here and enumerate the downstream
30782651Ssl147100 * devices for such hubs.
30792651Ssl147100 */
30802651Ssl147100 port_change = hubd_determine_port_connection(hubd);
30812651Ssl147100
30822651Ssl147100 if (port_change) {
30832651Ssl147100 hubd_pm_busy_component(hubd, hubd->h_dip, 0);
30842651Ssl147100
30852651Ssl147100 arg->hubd = hubd;
30862651Ssl147100 arg->hotplug_during_attach = B_TRUE;
30872651Ssl147100 hubd->h_port_change |= port_change;
30882651Ssl147100
30892651Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
30902651Ssl147100 "hubd_check_ports: port change=0x%x, need to connect",
30912651Ssl147100 hubd->h_port_change);
30922651Ssl147100
30932651Ssl147100 if (usb_async_req(hubd->h_dip, hubd_hotplug_thread,
30942651Ssl147100 (void *)arg, 0) == USB_SUCCESS) {
30952651Ssl147100 hubd->h_hotplug_thread++;
30962651Ssl147100 } else {
30972651Ssl147100 /* mark this device as idle */
30982651Ssl147100 hubd_pm_idle_component(hubd, hubd->h_dip, 0);
30992651Ssl147100 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
31002651Ssl147100 }
31012651Ssl147100 } else {
31022651Ssl147100 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
31030Sstevel@tonic-gate }
31040Sstevel@tonic-gate
31050Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
31060Sstevel@tonic-gate "hubd_check_ports done");
31070Sstevel@tonic-gate
31082651Ssl147100 return (USB_SUCCESS);
31090Sstevel@tonic-gate }
31100Sstevel@tonic-gate
31110Sstevel@tonic-gate
31120Sstevel@tonic-gate /*
31130Sstevel@tonic-gate * hubd_get_hub_descriptor:
31140Sstevel@tonic-gate */
31150Sstevel@tonic-gate static int
hubd_get_hub_descriptor(hubd_t * hubd)31160Sstevel@tonic-gate hubd_get_hub_descriptor(hubd_t *hubd)
31170Sstevel@tonic-gate {
31180Sstevel@tonic-gate usb_hub_descr_t *hub_descr = &hubd->h_hub_descr;
31190Sstevel@tonic-gate mblk_t *data = NULL;
31200Sstevel@tonic-gate usb_cr_t completion_reason;
31210Sstevel@tonic-gate usb_cb_flags_t cb_flags;
31220Sstevel@tonic-gate uint16_t length;
31230Sstevel@tonic-gate int rval;
312412194SRaymond.Chen@Sun.COM usb_req_attrs_t attr = 0;
31250Sstevel@tonic-gate
31260Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
31270Sstevel@tonic-gate "hubd_get_hub_descriptor:");
31280Sstevel@tonic-gate
312912194SRaymond.Chen@Sun.COM if ((hubd->h_dev_data->dev_descr->idVendor == USB_HUB_INTEL_VID) &&
313012194SRaymond.Chen@Sun.COM (hubd->h_dev_data->dev_descr->idProduct == USB_HUB_INTEL_PID)) {
313112194SRaymond.Chen@Sun.COM attr = USB_ATTRS_SHORT_XFER_OK;
313212194SRaymond.Chen@Sun.COM }
313312194SRaymond.Chen@Sun.COM
31340Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
31350Sstevel@tonic-gate ASSERT(hubd->h_default_pipe != 0);
31360Sstevel@tonic-gate
31370Sstevel@tonic-gate /* get hub descriptor length first by requesting 8 bytes only */
31380Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
31390Sstevel@tonic-gate
31400Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
31410Sstevel@tonic-gate hubd->h_default_pipe,
31421001Ssl147100 HUB_CLASS_REQ_TYPE,
31430Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */
31440Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_HUB, /* wValue */
31450Sstevel@tonic-gate 0, /* wIndex */
31460Sstevel@tonic-gate 8, /* wLength */
31470Sstevel@tonic-gate &data, 0,
31480Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
31490Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
31500Sstevel@tonic-gate "get hub descriptor failed: cr=%d cb_fl=0x%x rval=%d",
31510Sstevel@tonic-gate completion_reason, cb_flags, rval);
31520Sstevel@tonic-gate freemsg(data);
31530Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
31540Sstevel@tonic-gate
31550Sstevel@tonic-gate return (rval);
31560Sstevel@tonic-gate }
31570Sstevel@tonic-gate
31580Sstevel@tonic-gate length = *(data->b_rptr);
31590Sstevel@tonic-gate
31600Sstevel@tonic-gate if (length > 8) {
31610Sstevel@tonic-gate freemsg(data);
31620Sstevel@tonic-gate data = NULL;
31630Sstevel@tonic-gate
31640Sstevel@tonic-gate /* get complete hub descriptor */
316512194SRaymond.Chen@Sun.COM rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
31660Sstevel@tonic-gate hubd->h_default_pipe,
31671001Ssl147100 HUB_CLASS_REQ_TYPE,
31680Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */
31690Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_HUB, /* wValue */
31700Sstevel@tonic-gate 0, /* wIndex */
31710Sstevel@tonic-gate length, /* wLength */
317212194SRaymond.Chen@Sun.COM &data, attr,
317312194SRaymond.Chen@Sun.COM &completion_reason, &cb_flags, 0);
317412194SRaymond.Chen@Sun.COM
317512194SRaymond.Chen@Sun.COM /*
317612194SRaymond.Chen@Sun.COM * Hub descriptor data less than 9 bytes is not valid and
317712194SRaymond.Chen@Sun.COM * may cause trouble if we use it. See USB2.0 Tab11-13.
317812194SRaymond.Chen@Sun.COM */
317912194SRaymond.Chen@Sun.COM if ((rval != USB_SUCCESS) || (MBLKL(data) <= 8)) {
31800Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
31810Sstevel@tonic-gate "get hub descriptor failed: "
318212194SRaymond.Chen@Sun.COM "cr=%d cb_fl=0x%x rval=%d, len=%ld",
318312194SRaymond.Chen@Sun.COM completion_reason, cb_flags, rval,
318412194SRaymond.Chen@Sun.COM (data)?MBLKL(data):0);
31850Sstevel@tonic-gate freemsg(data);
31860Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
31870Sstevel@tonic-gate
31880Sstevel@tonic-gate return (rval);
31890Sstevel@tonic-gate }
31900Sstevel@tonic-gate }
31910Sstevel@tonic-gate
31920Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
31930Sstevel@tonic-gate
31940Sstevel@tonic-gate /* parse the hub descriptor */
31950Sstevel@tonic-gate /* only 32 ports are supported at present */
31960Sstevel@tonic-gate ASSERT(*(data->b_rptr + 2) <= 32);
31970Sstevel@tonic-gate if (usb_parse_CV_descr("cccscccccc",
31987492SZhigang.Lu@Sun.COM data->b_rptr, MBLKL(data),
31990Sstevel@tonic-gate (void *)hub_descr, sizeof (usb_hub_descr_t)) == 0) {
32000Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
32010Sstevel@tonic-gate "parsing hub descriptor failed");
32020Sstevel@tonic-gate
32030Sstevel@tonic-gate freemsg(data);
32040Sstevel@tonic-gate
32050Sstevel@tonic-gate return (USB_FAILURE);
32060Sstevel@tonic-gate }
32070Sstevel@tonic-gate
32080Sstevel@tonic-gate freemsg(data);
32090Sstevel@tonic-gate
32100Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
32111001Ssl147100 "rval=0x%x bNbrPorts=0x%x wHubChars=0x%x "
32121001Ssl147100 "PwrOn2PwrGood=0x%x HubContrCurrent=%dmA", rval,
32130Sstevel@tonic-gate hub_descr->bNbrPorts, hub_descr->wHubCharacteristics,
32141001Ssl147100 hub_descr->bPwrOn2PwrGood, hub_descr->bHubContrCurrent);
32150Sstevel@tonic-gate
32160Sstevel@tonic-gate if (hub_descr->bNbrPorts > MAX_PORTS) {
32170Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle,
32180Sstevel@tonic-gate "Hub driver supports max of %d ports on hub. "
32190Sstevel@tonic-gate "Hence using the first %d port of %d ports available",
32200Sstevel@tonic-gate MAX_PORTS, MAX_PORTS, hub_descr->bNbrPorts);
32210Sstevel@tonic-gate
32220Sstevel@tonic-gate hub_descr->bNbrPorts = MAX_PORTS;
32230Sstevel@tonic-gate }
32240Sstevel@tonic-gate
32250Sstevel@tonic-gate return (USB_SUCCESS);
32260Sstevel@tonic-gate }
32270Sstevel@tonic-gate
32280Sstevel@tonic-gate
32290Sstevel@tonic-gate /*
32301001Ssl147100 * hubd_get_hub_status_words:
32311001Ssl147100 */
32321001Ssl147100 static int
hubd_get_hub_status_words(hubd_t * hubd,uint16_t * status)32331001Ssl147100 hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status)
32341001Ssl147100 {
32351001Ssl147100 usb_cr_t completion_reason;
32361001Ssl147100 usb_cb_flags_t cb_flags;
32371001Ssl147100 mblk_t *data = NULL;
32381001Ssl147100
32391001Ssl147100 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
32401001Ssl147100
32411001Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
32421001Ssl147100
32431001Ssl147100 if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe,
32441001Ssl147100 HUB_CLASS_REQ_TYPE,
32451001Ssl147100 USB_REQ_GET_STATUS,
32461001Ssl147100 0,
32471001Ssl147100 0,
32481001Ssl147100 GET_STATUS_LENGTH,
32491001Ssl147100 &data, 0,
32501001Ssl147100 &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
32511001Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
32521001Ssl147100 "get hub status failed: cr=%d cb=0x%x",
32531001Ssl147100 completion_reason, cb_flags);
32541001Ssl147100
32551001Ssl147100 if (data) {
32561001Ssl147100 freemsg(data);
32571001Ssl147100 }
32581001Ssl147100
32591001Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
32601001Ssl147100
32611001Ssl147100 return (USB_FAILURE);
32621001Ssl147100 }
32631001Ssl147100
32641001Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
32651001Ssl147100
32661001Ssl147100 status[0] = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
32671001Ssl147100 status[1] = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
32681001Ssl147100
32691001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
32701001Ssl147100 "hub status=0x%x change=0x%x", status[0], status[1]);
32711001Ssl147100
32721001Ssl147100 freemsg(data);
32731001Ssl147100
32741001Ssl147100 return (USB_SUCCESS);
32751001Ssl147100 }
32761001Ssl147100
32771001Ssl147100
32781001Ssl147100 /*
32790Sstevel@tonic-gate * hubd_open_intr_pipe:
32800Sstevel@tonic-gate * we read all descriptors first for curiosity and then simply
32810Sstevel@tonic-gate * open the pipe
32820Sstevel@tonic-gate */
32830Sstevel@tonic-gate static int
hubd_open_intr_pipe(hubd_t * hubd)32840Sstevel@tonic-gate hubd_open_intr_pipe(hubd_t *hubd)
32850Sstevel@tonic-gate {
32860Sstevel@tonic-gate int rval;
32870Sstevel@tonic-gate
32880Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
32890Sstevel@tonic-gate "hubd_open_intr_pipe:");
32900Sstevel@tonic-gate
32910Sstevel@tonic-gate ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_IDLE);
32920Sstevel@tonic-gate
32930Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_OPENING;
32940Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
32950Sstevel@tonic-gate
32960Sstevel@tonic-gate if ((rval = usb_pipe_open(hubd->h_dip,
32970Sstevel@tonic-gate &hubd->h_ep1_descr, &hubd->h_pipe_policy,
32980Sstevel@tonic-gate 0, &hubd->h_ep1_ph)) != USB_SUCCESS) {
3299978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
33000Sstevel@tonic-gate "open intr pipe failed (%d)", rval);
33010Sstevel@tonic-gate
33020Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
33030Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE;
33040Sstevel@tonic-gate
33050Sstevel@tonic-gate return (rval);
33060Sstevel@tonic-gate }
33070Sstevel@tonic-gate
33080Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
33090Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE;
33100Sstevel@tonic-gate
33110Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
33126898Sfb209375 "open intr pipe succeeded, ph=0x%p", (void *)hubd->h_ep1_ph);
33130Sstevel@tonic-gate
33140Sstevel@tonic-gate return (USB_SUCCESS);
33150Sstevel@tonic-gate }
33160Sstevel@tonic-gate
33170Sstevel@tonic-gate
33180Sstevel@tonic-gate /*
33190Sstevel@tonic-gate * hubd_start_polling:
33200Sstevel@tonic-gate * start or restart the polling
33210Sstevel@tonic-gate */
33220Sstevel@tonic-gate static void
hubd_start_polling(hubd_t * hubd,int always)33230Sstevel@tonic-gate hubd_start_polling(hubd_t *hubd, int always)
33240Sstevel@tonic-gate {
33250Sstevel@tonic-gate usb_intr_req_t *reqp;
33260Sstevel@tonic-gate int rval;
33270Sstevel@tonic-gate usb_pipe_state_t pipe_state;
33280Sstevel@tonic-gate
33290Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
33300Sstevel@tonic-gate "start polling: always=%d dev_state=%d pipe_state=%d\n\t"
33310Sstevel@tonic-gate "thread=%d ep1_ph=0x%p",
33320Sstevel@tonic-gate always, hubd->h_dev_state, hubd->h_intr_pipe_state,
33336898Sfb209375 hubd->h_hotplug_thread, (void *)hubd->h_ep1_ph);
33340Sstevel@tonic-gate
33350Sstevel@tonic-gate /*
33360Sstevel@tonic-gate * start or restart polling on the intr pipe
33370Sstevel@tonic-gate * only if hotplug thread is not running
33380Sstevel@tonic-gate */
33390Sstevel@tonic-gate if ((always == HUBD_ALWAYS_START_POLLING) ||
33400Sstevel@tonic-gate ((hubd->h_dev_state == USB_DEV_ONLINE) &&
33410Sstevel@tonic-gate (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) &&
33420Sstevel@tonic-gate (hubd->h_hotplug_thread == 0) && hubd->h_ep1_ph)) {
33430Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
33440Sstevel@tonic-gate "start polling requested");
33450Sstevel@tonic-gate
33460Sstevel@tonic-gate reqp = usb_alloc_intr_req(hubd->h_dip, 0, USB_FLAGS_SLEEP);
33470Sstevel@tonic-gate
33480Sstevel@tonic-gate reqp->intr_client_private = (usb_opaque_t)hubd;
33490Sstevel@tonic-gate reqp->intr_attributes = USB_ATTRS_SHORT_XFER_OK |
33504763Slg150142 USB_ATTRS_AUTOCLEARING;
33510Sstevel@tonic-gate reqp->intr_len = hubd->h_ep1_descr.wMaxPacketSize;
33520Sstevel@tonic-gate reqp->intr_cb = hubd_read_cb;
33530Sstevel@tonic-gate reqp->intr_exc_cb = hubd_exception_cb;
33540Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
33550Sstevel@tonic-gate if ((rval = usb_pipe_intr_xfer(hubd->h_ep1_ph, reqp,
33560Sstevel@tonic-gate USB_FLAGS_SLEEP)) != USB_SUCCESS) {
33570Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
33580Sstevel@tonic-gate "start polling failed, rval=%d", rval);
33590Sstevel@tonic-gate usb_free_intr_req(reqp);
33600Sstevel@tonic-gate }
33610Sstevel@tonic-gate
33620Sstevel@tonic-gate rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state,
33634763Slg150142 USB_FLAGS_SLEEP);
33640Sstevel@tonic-gate if (pipe_state != USB_PIPE_STATE_ACTIVE) {
33650Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
33660Sstevel@tonic-gate "intr pipe state=%d, rval=%d", pipe_state, rval);
33670Sstevel@tonic-gate }
33680Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
33696898Sfb209375 "start polling request 0x%p", (void *)reqp);
33700Sstevel@tonic-gate
33710Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
33720Sstevel@tonic-gate }
33730Sstevel@tonic-gate }
33740Sstevel@tonic-gate
33750Sstevel@tonic-gate
33760Sstevel@tonic-gate /*
33770Sstevel@tonic-gate * hubd_stop_polling
33780Sstevel@tonic-gate * stop polling but do not close the pipe
33790Sstevel@tonic-gate */
33800Sstevel@tonic-gate static void
hubd_stop_polling(hubd_t * hubd)33810Sstevel@tonic-gate hubd_stop_polling(hubd_t *hubd)
33820Sstevel@tonic-gate {
33830Sstevel@tonic-gate int rval;
33840Sstevel@tonic-gate usb_pipe_state_t pipe_state;
33850Sstevel@tonic-gate
33860Sstevel@tonic-gate if (hubd->h_ep1_ph) {
33870Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
33880Sstevel@tonic-gate "hubd_stop_polling:");
33890Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_STOPPED;
33900Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
33910Sstevel@tonic-gate
33920Sstevel@tonic-gate usb_pipe_stop_intr_polling(hubd->h_ep1_ph, USB_FLAGS_SLEEP);
33930Sstevel@tonic-gate rval = usb_pipe_get_state(hubd->h_ep1_ph, &pipe_state,
33944763Slg150142 USB_FLAGS_SLEEP);
33950Sstevel@tonic-gate
33960Sstevel@tonic-gate if (pipe_state != USB_PIPE_STATE_IDLE) {
33970Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
33980Sstevel@tonic-gate "intr pipe state=%d, rval=%d", pipe_state, rval);
33990Sstevel@tonic-gate }
34000Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
34010Sstevel@tonic-gate if (hubd->h_intr_pipe_state == HUBD_INTR_PIPE_STOPPED) {
34020Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_ACTIVE;
34030Sstevel@tonic-gate }
34040Sstevel@tonic-gate }
34050Sstevel@tonic-gate }
34060Sstevel@tonic-gate
34070Sstevel@tonic-gate
34080Sstevel@tonic-gate /*
34090Sstevel@tonic-gate * hubd_close_intr_pipe:
34100Sstevel@tonic-gate * close the pipe (which also stops the polling
34110Sstevel@tonic-gate * and wait for the hotplug thread to exit
34120Sstevel@tonic-gate */
34130Sstevel@tonic-gate static void
hubd_close_intr_pipe(hubd_t * hubd)34140Sstevel@tonic-gate hubd_close_intr_pipe(hubd_t *hubd)
34150Sstevel@tonic-gate {
34160Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
34170Sstevel@tonic-gate "hubd_close_intr_pipe:");
34180Sstevel@tonic-gate
34190Sstevel@tonic-gate /*
34200Sstevel@tonic-gate * Now that no async operation is outstanding on pipe,
34210Sstevel@tonic-gate * we can change the state to HUBD_INTR_PIPE_CLOSING
34220Sstevel@tonic-gate */
34230Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_CLOSING;
34240Sstevel@tonic-gate
34250Sstevel@tonic-gate ASSERT(hubd->h_hotplug_thread == 0);
34260Sstevel@tonic-gate
34270Sstevel@tonic-gate if (hubd->h_ep1_ph) {
34280Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
34290Sstevel@tonic-gate usb_pipe_close(hubd->h_dip, hubd->h_ep1_ph, USB_FLAGS_SLEEP,
34304763Slg150142 NULL, NULL);
34310Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
34320Sstevel@tonic-gate hubd->h_ep1_ph = NULL;
34330Sstevel@tonic-gate }
34340Sstevel@tonic-gate
34350Sstevel@tonic-gate hubd->h_intr_pipe_state = HUBD_INTR_PIPE_IDLE;
34360Sstevel@tonic-gate }
34370Sstevel@tonic-gate
34380Sstevel@tonic-gate
34390Sstevel@tonic-gate /*
34400Sstevel@tonic-gate * hubd_exception_cb
34410Sstevel@tonic-gate * interrupt ep1 exception callback function.
34420Sstevel@tonic-gate * this callback executes in taskq thread context and assumes
34430Sstevel@tonic-gate * autoclearing
34440Sstevel@tonic-gate */
34450Sstevel@tonic-gate /*ARGSUSED*/
34460Sstevel@tonic-gate static void
hubd_exception_cb(usb_pipe_handle_t pipe,usb_intr_req_t * reqp)34470Sstevel@tonic-gate hubd_exception_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp)
34480Sstevel@tonic-gate {
34490Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)(reqp->intr_client_private);
34500Sstevel@tonic-gate
34510Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
34520Sstevel@tonic-gate "hubd_exception_cb: "
34536898Sfb209375 "req=0x%p cr=%d data=0x%p cb_flags=0x%x", (void *)reqp,
34546898Sfb209375 reqp->intr_completion_reason, (void *)reqp->intr_data,
34550Sstevel@tonic-gate reqp->intr_cb_flags);
34560Sstevel@tonic-gate
34570Sstevel@tonic-gate ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
34580Sstevel@tonic-gate
34590Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
34600Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
34610Sstevel@tonic-gate
34620Sstevel@tonic-gate switch (reqp->intr_completion_reason) {
34630Sstevel@tonic-gate case USB_CR_PIPE_RESET:
34640Sstevel@tonic-gate /* only restart polling after autoclearing */
34650Sstevel@tonic-gate if ((hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE) &&
34660Sstevel@tonic-gate (hubd->h_port_reset_wait == 0)) {
34670Sstevel@tonic-gate hubd_start_polling(hubd, 0);
34680Sstevel@tonic-gate }
34690Sstevel@tonic-gate
34700Sstevel@tonic-gate break;
34710Sstevel@tonic-gate case USB_CR_DEV_NOT_RESP:
34720Sstevel@tonic-gate case USB_CR_STOPPED_POLLING:
34730Sstevel@tonic-gate case USB_CR_PIPE_CLOSING:
34740Sstevel@tonic-gate case USB_CR_UNSPECIFIED_ERR:
34750Sstevel@tonic-gate /* never restart polling on these conditions */
34760Sstevel@tonic-gate default:
34770Sstevel@tonic-gate /* for all others, wait for the autoclearing PIPE_RESET cb */
34780Sstevel@tonic-gate
34790Sstevel@tonic-gate break;
34800Sstevel@tonic-gate }
34810Sstevel@tonic-gate
34820Sstevel@tonic-gate usb_free_intr_req(reqp);
34830Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
34840Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
34850Sstevel@tonic-gate }
34860Sstevel@tonic-gate
34870Sstevel@tonic-gate
34880Sstevel@tonic-gate /*
34890Sstevel@tonic-gate * helper function to convert LE bytes to a portmask
34900Sstevel@tonic-gate */
34910Sstevel@tonic-gate static usb_port_mask_t
hubd_mblk2portmask(mblk_t * data)34920Sstevel@tonic-gate hubd_mblk2portmask(mblk_t *data)
34930Sstevel@tonic-gate {
34947492SZhigang.Lu@Sun.COM int len = min(MBLKL(data), sizeof (usb_port_mask_t));
34950Sstevel@tonic-gate usb_port_mask_t rval = 0;
34960Sstevel@tonic-gate int i;
34970Sstevel@tonic-gate
34980Sstevel@tonic-gate for (i = 0; i < len; i++) {
34990Sstevel@tonic-gate rval |= data->b_rptr[i] << (i * 8);
35000Sstevel@tonic-gate }
35010Sstevel@tonic-gate
35020Sstevel@tonic-gate return (rval);
35030Sstevel@tonic-gate }
35040Sstevel@tonic-gate
35050Sstevel@tonic-gate
35060Sstevel@tonic-gate /*
35070Sstevel@tonic-gate * hubd_read_cb:
35080Sstevel@tonic-gate * interrupt ep1 callback function
35090Sstevel@tonic-gate *
35100Sstevel@tonic-gate * the status indicates just a change on the pipe with no indication
35110Sstevel@tonic-gate * of what the change was
35120Sstevel@tonic-gate *
35130Sstevel@tonic-gate * known conditions:
35140Sstevel@tonic-gate * - reset port completion
35150Sstevel@tonic-gate * - connect
35160Sstevel@tonic-gate * - disconnect
35170Sstevel@tonic-gate *
35180Sstevel@tonic-gate * for handling the hotplugging, create a new thread that can do
35190Sstevel@tonic-gate * synchronous usba calls
35200Sstevel@tonic-gate */
35210Sstevel@tonic-gate static void
hubd_read_cb(usb_pipe_handle_t pipe,usb_intr_req_t * reqp)35220Sstevel@tonic-gate hubd_read_cb(usb_pipe_handle_t pipe, usb_intr_req_t *reqp)
35230Sstevel@tonic-gate {
35240Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)(reqp->intr_client_private);
35250Sstevel@tonic-gate size_t length;
35260Sstevel@tonic-gate mblk_t *data = reqp->intr_data;
35272651Ssl147100 int mem_flag = 0;
35282651Ssl147100 hubd_hotplug_arg_t *arg;
35290Sstevel@tonic-gate
35300Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
35316898Sfb209375 "hubd_read_cb: ph=0x%p req=0x%p", (void *)pipe, (void *)reqp);
35320Sstevel@tonic-gate
35330Sstevel@tonic-gate ASSERT((reqp->intr_cb_flags & USB_CB_INTR_CONTEXT) == 0);
35340Sstevel@tonic-gate
35350Sstevel@tonic-gate /*
35360Sstevel@tonic-gate * At present, we are not handling notification for completion of
35370Sstevel@tonic-gate * asynchronous pipe reset, for which this data ptr could be NULL
35380Sstevel@tonic-gate */
35390Sstevel@tonic-gate
35400Sstevel@tonic-gate if (data == NULL) {
35410Sstevel@tonic-gate usb_free_intr_req(reqp);
35420Sstevel@tonic-gate
35430Sstevel@tonic-gate return;
35440Sstevel@tonic-gate }
35450Sstevel@tonic-gate
35462651Ssl147100 arg = (hubd_hotplug_arg_t *)kmem_zalloc(
35472651Ssl147100 sizeof (hubd_hotplug_arg_t), KM_SLEEP);
35482651Ssl147100 mem_flag = 1;
35492651Ssl147100
35500Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
35510Sstevel@tonic-gate
35520Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_SUSPENDED) ||
35530Sstevel@tonic-gate (hubd->h_intr_pipe_state != HUBD_INTR_PIPE_ACTIVE)) {
35540Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
35550Sstevel@tonic-gate usb_free_intr_req(reqp);
35562651Ssl147100 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
35570Sstevel@tonic-gate
35580Sstevel@tonic-gate return;
35590Sstevel@tonic-gate }
35600Sstevel@tonic-gate
35610Sstevel@tonic-gate ASSERT(hubd->h_ep1_ph == pipe);
35620Sstevel@tonic-gate
35637492SZhigang.Lu@Sun.COM length = MBLKL(data);
35640Sstevel@tonic-gate
35650Sstevel@tonic-gate /*
35660Sstevel@tonic-gate * Only look at the data and startup the hotplug thread if
35670Sstevel@tonic-gate * there actually is data.
35680Sstevel@tonic-gate */
35690Sstevel@tonic-gate if (length != 0) {
35700Sstevel@tonic-gate usb_port_mask_t port_change = hubd_mblk2portmask(data);
35710Sstevel@tonic-gate
35720Sstevel@tonic-gate /*
35730Sstevel@tonic-gate * if a port change was already reported and we are waiting for
35740Sstevel@tonic-gate * reset port completion then wake up the hotplug thread which
35750Sstevel@tonic-gate * should be waiting on reset port completion
35760Sstevel@tonic-gate *
35770Sstevel@tonic-gate * if there is disconnect event instead of reset completion, let
35780Sstevel@tonic-gate * the hotplug thread figure this out
35790Sstevel@tonic-gate */
35800Sstevel@tonic-gate
35810Sstevel@tonic-gate /* remove the reset wait bits from the status */
35820Sstevel@tonic-gate hubd->h_port_change |= port_change &
35834763Slg150142 ~hubd->h_port_reset_wait;
35840Sstevel@tonic-gate
35850Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
35860Sstevel@tonic-gate "port change=0x%x port_reset_wait=0x%x",
35870Sstevel@tonic-gate hubd->h_port_change, hubd->h_port_reset_wait);
35880Sstevel@tonic-gate
35890Sstevel@tonic-gate /* there should be only one reset bit active at the time */
35900Sstevel@tonic-gate if (hubd->h_port_reset_wait & port_change) {
35910Sstevel@tonic-gate hubd->h_port_reset_wait = 0;
35920Sstevel@tonic-gate cv_signal(&hubd->h_cv_reset_port);
35930Sstevel@tonic-gate }
35940Sstevel@tonic-gate
35950Sstevel@tonic-gate /*
35960Sstevel@tonic-gate * kick off the thread only if device is ONLINE and it is not
35970Sstevel@tonic-gate * during attaching or detaching
35980Sstevel@tonic-gate */
35990Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_ONLINE) &&
36000Sstevel@tonic-gate (!DEVI_IS_ATTACHING(hubd->h_dip)) &&
36010Sstevel@tonic-gate (!DEVI_IS_DETACHING(hubd->h_dip)) &&
36020Sstevel@tonic-gate (hubd->h_port_change) &&
36030Sstevel@tonic-gate (hubd->h_hotplug_thread == 0)) {
36040Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CALLBACK, hubd->h_log_handle,
36050Sstevel@tonic-gate "creating hotplug thread: "
36060Sstevel@tonic-gate "dev_state=%d", hubd->h_dev_state);
36070Sstevel@tonic-gate
36080Sstevel@tonic-gate /*
36090Sstevel@tonic-gate * Mark this device as busy. The will be marked idle
36100Sstevel@tonic-gate * if the async req fails or at the exit of hotplug
36110Sstevel@tonic-gate * thread
36120Sstevel@tonic-gate */
36130Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
36140Sstevel@tonic-gate
36152651Ssl147100 arg->hubd = hubd;
36162651Ssl147100 arg->hotplug_during_attach = B_FALSE;
36172651Ssl147100
36180Sstevel@tonic-gate if (usb_async_req(hubd->h_dip,
36190Sstevel@tonic-gate hubd_hotplug_thread,
36202651Ssl147100 (void *)arg, 0) == USB_SUCCESS) {
36210Sstevel@tonic-gate hubd->h_hotplug_thread++;
36222651Ssl147100 mem_flag = 0;
36230Sstevel@tonic-gate } else {
36240Sstevel@tonic-gate /* mark this device as idle */
36250Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd,
36260Sstevel@tonic-gate hubd->h_dip, 0);
36270Sstevel@tonic-gate }
36280Sstevel@tonic-gate }
36290Sstevel@tonic-gate }
36300Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
36310Sstevel@tonic-gate
36322651Ssl147100 if (mem_flag == 1) {
36332651Ssl147100 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
36342651Ssl147100 }
36352651Ssl147100
36360Sstevel@tonic-gate usb_free_intr_req(reqp);
36370Sstevel@tonic-gate }
36380Sstevel@tonic-gate
36390Sstevel@tonic-gate
36400Sstevel@tonic-gate /*
36410Sstevel@tonic-gate * hubd_hotplug_thread:
36420Sstevel@tonic-gate * handles resetting of port, and creating children
36430Sstevel@tonic-gate *
36440Sstevel@tonic-gate * the ports to check are indicated in h_port_change bit mask
36450Sstevel@tonic-gate * XXX note that one time poll doesn't work on the root hub
36460Sstevel@tonic-gate */
36470Sstevel@tonic-gate static void
hubd_hotplug_thread(void * arg)36480Sstevel@tonic-gate hubd_hotplug_thread(void *arg)
36490Sstevel@tonic-gate {
36502651Ssl147100 hubd_hotplug_arg_t *hd_arg = (hubd_hotplug_arg_t *)arg;
36512651Ssl147100 hubd_t *hubd = hd_arg->hubd;
36522651Ssl147100 boolean_t attach_flg = hd_arg->hotplug_during_attach;
36530Sstevel@tonic-gate usb_port_t port;
36540Sstevel@tonic-gate uint16_t nports;
36550Sstevel@tonic-gate uint16_t status, change;
36560Sstevel@tonic-gate hub_power_t *hubpm;
36570Sstevel@tonic-gate dev_info_t *hdip = hubd->h_dip;
36580Sstevel@tonic-gate dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
36594763Slg150142 dev_info_t *child_dip;
36600Sstevel@tonic-gate boolean_t online_child = B_FALSE;
36610Sstevel@tonic-gate boolean_t offline_child = B_FALSE;
36620Sstevel@tonic-gate boolean_t pwrup_child = B_FALSE;
36634763Slg150142 int prh_circ, rh_circ, chld_circ, circ, old_state;
36640Sstevel@tonic-gate
36650Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
36660Sstevel@tonic-gate "hubd_hotplug_thread: started");
36670Sstevel@tonic-gate
366810783SVincent.Wang@Sun.COM /*
366910783SVincent.Wang@Sun.COM * Before console is init'd, we temporarily block the hotplug
367010783SVincent.Wang@Sun.COM * threads so that BUS_CONFIG_ONE through hubd_bus_config() can be
367110783SVincent.Wang@Sun.COM * processed quickly. This reduces the time needed for vfs_mountroot()
367211987SFei.Feng@Sun.COM * to mount the root FS from a USB disk. And on SPARC platform,
367311987SFei.Feng@Sun.COM * in order to load 'consconfig' successfully after OBP is gone,
367411987SFei.Feng@Sun.COM * we need to check 'modrootloaded' to make sure root filesystem is
367511987SFei.Feng@Sun.COM * available.
367610783SVincent.Wang@Sun.COM */
367711987SFei.Feng@Sun.COM while (!modrootloaded || !consconfig_console_is_ready()) {
367810783SVincent.Wang@Sun.COM delay(drv_usectohz(10000));
367910783SVincent.Wang@Sun.COM }
368010783SVincent.Wang@Sun.COM
36812651Ssl147100 kmem_free(arg, sizeof (hubd_hotplug_arg_t));
36822651Ssl147100
36830Sstevel@tonic-gate /*
36840Sstevel@tonic-gate * if our bus power entry point is active, process the change
36850Sstevel@tonic-gate * on the next notification of interrupt pipe
36860Sstevel@tonic-gate */
36870Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
36880Sstevel@tonic-gate if (hubd->h_bus_pwr || (hubd->h_hotplug_thread > 1)) {
36890Sstevel@tonic-gate hubd->h_hotplug_thread--;
36900Sstevel@tonic-gate
36910Sstevel@tonic-gate /* mark this device as idle */
36920Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0);
36930Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
36940Sstevel@tonic-gate
36950Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
36960Sstevel@tonic-gate "hubd_hotplug_thread: "
36970Sstevel@tonic-gate "bus_power in progress/hotplugging undesirable - quit");
36980Sstevel@tonic-gate
36990Sstevel@tonic-gate return;
37000Sstevel@tonic-gate }
37010Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
37020Sstevel@tonic-gate
37030Sstevel@tonic-gate ndi_hold_devi(hdip); /* so we don't race with detach */
37040Sstevel@tonic-gate
37050Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
37060Sstevel@tonic-gate
37070Sstevel@tonic-gate /* is this the root hub? */
37080Sstevel@tonic-gate if (hdip == rh_dip) {
37090Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_PWRED_DOWN) {
37100Sstevel@tonic-gate hubpm = hubd->h_hubpm;
37110Sstevel@tonic-gate
37120Sstevel@tonic-gate /* mark the root hub as full power */
37130Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
37140Sstevel@tonic-gate hubpm->hubp_time_at_full_power = ddi_get_time();
37150Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
37160Sstevel@tonic-gate
37170Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
37180Sstevel@tonic-gate "hubd_hotplug_thread: call pm_power_has_changed");
37190Sstevel@tonic-gate
37200Sstevel@tonic-gate (void) pm_power_has_changed(hdip, 0,
37214763Slg150142 USB_DEV_OS_FULL_PWR);
37220Sstevel@tonic-gate
37230Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
37240Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_ONLINE;
37250Sstevel@tonic-gate }
37260Sstevel@tonic-gate
37270Sstevel@tonic-gate } else {
37280Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
37290Sstevel@tonic-gate "hubd_hotplug_thread: not root hub");
37300Sstevel@tonic-gate }
37310Sstevel@tonic-gate
37320Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
37330Sstevel@tonic-gate
37340Sstevel@tonic-gate /*
37350Sstevel@tonic-gate * this ensures one hotplug activity per system at a time.
37360Sstevel@tonic-gate * we enter the parent PCI node to have this serialization.
37370Sstevel@tonic-gate * this also excludes ioctls and deathrow thread
37380Sstevel@tonic-gate * (a bit crude but easier to debug)
37390Sstevel@tonic-gate */
37400Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
37410Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ);
37420Sstevel@tonic-gate
37430Sstevel@tonic-gate /* exclude other threads */
37440Sstevel@tonic-gate ndi_devi_enter(hdip, &circ);
37450Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
37460Sstevel@tonic-gate
374711214SVincent.Wang@Sun.COM ASSERT(hubd->h_intr_pipe_state == HUBD_INTR_PIPE_ACTIVE);
374811214SVincent.Wang@Sun.COM
374911214SVincent.Wang@Sun.COM nports = hubd->h_hub_descr.bNbrPorts;
375011214SVincent.Wang@Sun.COM
375111214SVincent.Wang@Sun.COM hubd_stop_polling(hubd);
375211214SVincent.Wang@Sun.COM
37530Sstevel@tonic-gate while ((hubd->h_dev_state == USB_DEV_ONLINE) &&
37540Sstevel@tonic-gate (hubd->h_port_change)) {
37550Sstevel@tonic-gate /*
37560Sstevel@tonic-gate * The 0th bit is the hub status change bit.
37570Sstevel@tonic-gate * handle loss of local power here
37580Sstevel@tonic-gate */
37590Sstevel@tonic-gate if (hubd->h_port_change & HUB_CHANGE_STATUS) {
37600Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
37610Sstevel@tonic-gate "hubd_hotplug_thread: hub status change!");
37620Sstevel@tonic-gate
37630Sstevel@tonic-gate /*
37640Sstevel@tonic-gate * This should be handled properly. For now,
37650Sstevel@tonic-gate * mask off the bit.
37660Sstevel@tonic-gate */
37670Sstevel@tonic-gate hubd->h_port_change &= ~HUB_CHANGE_STATUS;
37680Sstevel@tonic-gate
37690Sstevel@tonic-gate /*
37700Sstevel@tonic-gate * check and ack hub status
37710Sstevel@tonic-gate * this causes stall conditions
37720Sstevel@tonic-gate * when local power is removed
37730Sstevel@tonic-gate */
37740Sstevel@tonic-gate (void) hubd_get_hub_status(hubd);
37750Sstevel@tonic-gate }
37760Sstevel@tonic-gate
37770Sstevel@tonic-gate for (port = 1; port <= nports; port++) {
37780Sstevel@tonic-gate usb_port_mask_t port_mask;
37790Sstevel@tonic-gate boolean_t was_connected;
37800Sstevel@tonic-gate
37810Sstevel@tonic-gate port_mask = 1 << port;
37820Sstevel@tonic-gate was_connected =
37834763Slg150142 (hubd->h_port_state[port] & PORT_STATUS_CCS) &&
37844763Slg150142 (hubd->h_children_dips[port]);
37850Sstevel@tonic-gate
37860Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
37870Sstevel@tonic-gate "hubd_hotplug_thread: "
37880Sstevel@tonic-gate "port %d mask=0x%x change=0x%x connected=0x%x",
37890Sstevel@tonic-gate port, port_mask, hubd->h_port_change,
37900Sstevel@tonic-gate was_connected);
37910Sstevel@tonic-gate
37920Sstevel@tonic-gate /*
37930Sstevel@tonic-gate * is this a port connection that changed?
37940Sstevel@tonic-gate */
37950Sstevel@tonic-gate if ((hubd->h_port_change & port_mask) == 0) {
37960Sstevel@tonic-gate
37970Sstevel@tonic-gate continue;
37980Sstevel@tonic-gate }
37990Sstevel@tonic-gate hubd->h_port_change &= ~port_mask;
38000Sstevel@tonic-gate
38010Sstevel@tonic-gate /* ack all changes */
38020Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port,
38030Sstevel@tonic-gate &status, &change, HUBD_ACK_ALL_CHANGES);
38040Sstevel@tonic-gate
38050Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
38060Sstevel@tonic-gate "handle port %d:\n\t"
38070Sstevel@tonic-gate "new status=0x%x change=0x%x was_conn=0x%x ",
38080Sstevel@tonic-gate port, status, change, was_connected);
38090Sstevel@tonic-gate
38100Sstevel@tonic-gate /* Recover a disabled port */
38110Sstevel@tonic-gate if (change & PORT_CHANGE_PESC) {
38120Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
38134763Slg150142 hubd->h_log_handle,
38144763Slg150142 "port%d Disabled - "
38154763Slg150142 "status=0x%x, change=0x%x",
38164763Slg150142 port, status, change);
38170Sstevel@tonic-gate
38180Sstevel@tonic-gate /*
38190Sstevel@tonic-gate * if the port was connected and is still
38200Sstevel@tonic-gate * connected, recover the port
38210Sstevel@tonic-gate */
38220Sstevel@tonic-gate if (was_connected && (status &
38230Sstevel@tonic-gate PORT_STATUS_CCS)) {
38240Sstevel@tonic-gate online_child |=
38250Sstevel@tonic-gate (hubd_recover_disabled_port(hubd,
38260Sstevel@tonic-gate port) == USB_SUCCESS);
38270Sstevel@tonic-gate }
38280Sstevel@tonic-gate }
38290Sstevel@tonic-gate
38300Sstevel@tonic-gate /*
38310Sstevel@tonic-gate * Now check what changed on the port
38320Sstevel@tonic-gate */
38332651Ssl147100 if ((change & PORT_CHANGE_CSC) || attach_flg) {
38340Sstevel@tonic-gate if ((status & PORT_STATUS_CCS) &&
38350Sstevel@tonic-gate (!was_connected)) {
38360Sstevel@tonic-gate /* new device plugged in */
38370Sstevel@tonic-gate online_child |=
38380Sstevel@tonic-gate (hubd_handle_port_connect(hubd,
38390Sstevel@tonic-gate port) == USB_SUCCESS);
38400Sstevel@tonic-gate
38410Sstevel@tonic-gate } else if ((status & PORT_STATUS_CCS) &&
38420Sstevel@tonic-gate was_connected) {
38430Sstevel@tonic-gate /*
38440Sstevel@tonic-gate * In this case we can never be sure
38450Sstevel@tonic-gate * if the device indeed got hotplugged
38460Sstevel@tonic-gate * or the hub is falsely reporting the
38470Sstevel@tonic-gate * change.
38484763Slg150142 */
38494763Slg150142 child_dip = hubd->h_children_dips[port];
38504763Slg150142
38514763Slg150142 mutex_exit(HUBD_MUTEX(hubd));
38524763Slg150142 /*
38534763Slg150142 * this ensures we do not race with
38544763Slg150142 * other threads which are detaching
38554763Slg150142 * the child driver at the same time.
38564763Slg150142 */
38574763Slg150142 ndi_devi_enter(child_dip, &chld_circ);
38584763Slg150142 /*
38594763Slg150142 * Now check if the driver remains
38604763Slg150142 * attached.
38610Sstevel@tonic-gate */
38624763Slg150142 if (i_ddi_devi_attached(child_dip)) {
38634763Slg150142 /*
38644763Slg150142 * first post a disconnect event
38654763Slg150142 * to the child.
38664763Slg150142 */
38674763Slg150142 hubd_post_event(hubd, port,
38684763Slg150142 USBA_EVENT_TAG_HOT_REMOVAL);
38694763Slg150142 mutex_enter(HUBD_MUTEX(hubd));
38704763Slg150142
38714763Slg150142 /*
38724763Slg150142 * then reset the port and
38734763Slg150142 * recover the device
38744763Slg150142 */
38754763Slg150142 online_child |=
38764763Slg150142 (hubd_handle_port_connect(
38774763Slg150142 hubd, port) == USB_SUCCESS);
38784763Slg150142
38794763Slg150142 mutex_exit(HUBD_MUTEX(hubd));
38804763Slg150142 }
38814763Slg150142
38824763Slg150142 ndi_devi_exit(child_dip, chld_circ);
38830Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
38840Sstevel@tonic-gate } else if (was_connected) {
38850Sstevel@tonic-gate /* this is a disconnect */
38860Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
38870Sstevel@tonic-gate hubd_post_event(hubd, port,
38880Sstevel@tonic-gate USBA_EVENT_TAG_HOT_REMOVAL);
38890Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
38900Sstevel@tonic-gate
38910Sstevel@tonic-gate offline_child = B_TRUE;
38920Sstevel@tonic-gate }
38930Sstevel@tonic-gate }
38940Sstevel@tonic-gate
38950Sstevel@tonic-gate /*
38960Sstevel@tonic-gate * Check if any port is coming out of suspend
38970Sstevel@tonic-gate */
38980Sstevel@tonic-gate if (change & PORT_CHANGE_PSSC) {
38990Sstevel@tonic-gate /* a resuming device could have disconnected */
39000Sstevel@tonic-gate if (was_connected &&
39010Sstevel@tonic-gate hubd->h_children_dips[port]) {
39020Sstevel@tonic-gate
39030Sstevel@tonic-gate /* device on this port resuming */
39040Sstevel@tonic-gate dev_info_t *dip;
39050Sstevel@tonic-gate
39060Sstevel@tonic-gate dip = hubd->h_children_dips[port];
39070Sstevel@tonic-gate
39080Sstevel@tonic-gate /*
39090Sstevel@tonic-gate * Don't raise power on detaching child
39100Sstevel@tonic-gate */
39110Sstevel@tonic-gate if (!DEVI_IS_DETACHING(dip)) {
39120Sstevel@tonic-gate /*
39130Sstevel@tonic-gate * As this child is not
39140Sstevel@tonic-gate * detaching, we set this
39150Sstevel@tonic-gate * flag, causing bus_ctls
39160Sstevel@tonic-gate * to stall detach till
39170Sstevel@tonic-gate * pm_raise_power returns
39180Sstevel@tonic-gate * and flag it for a deferred
39190Sstevel@tonic-gate * raise_power.
39200Sstevel@tonic-gate *
39210Sstevel@tonic-gate * pm_raise_power is deferred
39220Sstevel@tonic-gate * because we need to release
39230Sstevel@tonic-gate * the locks first.
39240Sstevel@tonic-gate */
39250Sstevel@tonic-gate hubd->h_port_state[port] |=
39264763Slg150142 HUBD_CHILD_RAISE_POWER;
39270Sstevel@tonic-gate pwrup_child = B_TRUE;
39280Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
39290Sstevel@tonic-gate
39300Sstevel@tonic-gate /*
39310Sstevel@tonic-gate * make sure that child
39320Sstevel@tonic-gate * doesn't disappear
39330Sstevel@tonic-gate */
39340Sstevel@tonic-gate ndi_hold_devi(dip);
39350Sstevel@tonic-gate
39360Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
39370Sstevel@tonic-gate }
39380Sstevel@tonic-gate }
39390Sstevel@tonic-gate }
39402326Ssl147100
39412326Ssl147100 /*
39422326Ssl147100 * Check if the port is over-current
39432326Ssl147100 */
39442326Ssl147100 if (change & PORT_CHANGE_OCIC) {
39452326Ssl147100 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
39462326Ssl147100 hubd->h_log_handle,
39472326Ssl147100 "Port%d in over current condition, "
39482326Ssl147100 "please check the attached device to "
39492326Ssl147100 "clear the condition. The system will "
39502326Ssl147100 "try to recover the port, but if not "
39512326Ssl147100 "successful, you need to re-connect "
39522326Ssl147100 "the hub or reboot the system to bring "
39532326Ssl147100 "the port back to work", port);
39542326Ssl147100
39552326Ssl147100 if (!(status & PORT_STATUS_PPS)) {
39562326Ssl147100 /*
39572326Ssl147100 * Try to enable port power, but
39582326Ssl147100 * possibly fail. Ignore failure
39592326Ssl147100 */
39602326Ssl147100 (void) hubd_enable_port_power(hubd,
39612326Ssl147100 port);
39622326Ssl147100
39632326Ssl147100 /*
39642326Ssl147100 * Delay some time to avoid
39652326Ssl147100 * over-current event to happen
39662326Ssl147100 * too frequently in some cases
39672326Ssl147100 */
39682326Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
39692326Ssl147100 delay(drv_usectohz(500000));
39702326Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
39712326Ssl147100 }
39722326Ssl147100 }
39730Sstevel@tonic-gate }
39740Sstevel@tonic-gate }
39750Sstevel@tonic-gate
39760Sstevel@tonic-gate /* release locks so we can do a devfs_clean */
39770Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
39780Sstevel@tonic-gate
39790Sstevel@tonic-gate /* delete cached dv_node's but drop locks first */
39800Sstevel@tonic-gate ndi_devi_exit(hdip, circ);
39810Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ);
39820Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
39830Sstevel@tonic-gate
39840Sstevel@tonic-gate (void) devfs_clean(rh_dip, NULL, 0);
39850Sstevel@tonic-gate
39860Sstevel@tonic-gate /* now check if any children need onlining */
39870Sstevel@tonic-gate if (online_child) {
39880Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
39890Sstevel@tonic-gate "hubd_hotplug_thread: onlining children");
39900Sstevel@tonic-gate
399110783SVincent.Wang@Sun.COM (void) ndi_devi_online(hubd->h_dip, 0);
39920Sstevel@tonic-gate }
39930Sstevel@tonic-gate
39940Sstevel@tonic-gate /* now check if any disconnected devices need to be cleaned up */
39950Sstevel@tonic-gate if (offline_child) {
39960Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
39970Sstevel@tonic-gate "hubd_hotplug_thread: scheduling cleanup");
39980Sstevel@tonic-gate
39990Sstevel@tonic-gate hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip);
40000Sstevel@tonic-gate }
40010Sstevel@tonic-gate
40020Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
40030Sstevel@tonic-gate
40040Sstevel@tonic-gate /* now raise power on the children that have woken up */
40050Sstevel@tonic-gate if (pwrup_child) {
40060Sstevel@tonic-gate old_state = hubd->h_dev_state;
40070Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_HUB_CHILD_PWRLVL;
40080Sstevel@tonic-gate for (port = 1; port <= nports; port++) {
40090Sstevel@tonic-gate if (hubd->h_port_state[port] & HUBD_CHILD_RAISE_POWER) {
40100Sstevel@tonic-gate dev_info_t *dip = hubd->h_children_dips[port];
40110Sstevel@tonic-gate
40120Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
40130Sstevel@tonic-gate
40140Sstevel@tonic-gate /* Get the device to full power */
40150Sstevel@tonic-gate (void) pm_busy_component(dip, 0);
40160Sstevel@tonic-gate (void) pm_raise_power(dip, 0,
40170Sstevel@tonic-gate USB_DEV_OS_FULL_PWR);
40180Sstevel@tonic-gate (void) pm_idle_component(dip, 0);
40190Sstevel@tonic-gate
40200Sstevel@tonic-gate /* release the hold on the child */
40210Sstevel@tonic-gate ndi_rele_devi(dip);
40220Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
40230Sstevel@tonic-gate hubd->h_port_state[port] &=
40240Sstevel@tonic-gate ~HUBD_CHILD_RAISE_POWER;
40250Sstevel@tonic-gate }
40260Sstevel@tonic-gate }
40270Sstevel@tonic-gate /*
40280Sstevel@tonic-gate * make sure that we don't accidentally
40290Sstevel@tonic-gate * over write the disconnect state
40300Sstevel@tonic-gate */
40310Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_HUB_CHILD_PWRLVL) {
40320Sstevel@tonic-gate hubd->h_dev_state = old_state;
40330Sstevel@tonic-gate }
40340Sstevel@tonic-gate }
40350Sstevel@tonic-gate
40360Sstevel@tonic-gate /*
40370Sstevel@tonic-gate * start polling can immediately kick off read callback
40380Sstevel@tonic-gate * we need to set the h_hotplug_thread to 0 so that
40390Sstevel@tonic-gate * the callback is not dropped
40404844Slg150142 *
40414844Slg150142 * if there is device during reset, still stop polling to avoid the
40424844Slg150142 * read callback interrupting the reset, the polling will be started
40434844Slg150142 * in hubd_reset_thread.
40440Sstevel@tonic-gate */
40454844Slg150142 for (port = 1; port <= MAX_PORTS; port++) {
40464844Slg150142 if (hubd->h_reset_port[port]) {
40474844Slg150142
40484844Slg150142 break;
40494844Slg150142 }
40504844Slg150142 }
40514844Slg150142 if (port > MAX_PORTS) {
40524844Slg150142 hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
40534844Slg150142 }
40540Sstevel@tonic-gate
40550Sstevel@tonic-gate /*
40560Sstevel@tonic-gate * Earlier we would set the h_hotplug_thread = 0 before
40570Sstevel@tonic-gate * polling was restarted so that
40580Sstevel@tonic-gate * if there is any root hub status change interrupt, we can still kick
40590Sstevel@tonic-gate * off the hotplug thread. This was valid when this interrupt was
40600Sstevel@tonic-gate * delivered in hardware, and only ONE interrupt would be delivered.
40610Sstevel@tonic-gate * Now that we poll on the root hub looking for status change in
40620Sstevel@tonic-gate * software, this assignment is no longer required.
40630Sstevel@tonic-gate */
40640Sstevel@tonic-gate hubd->h_hotplug_thread--;
40650Sstevel@tonic-gate
40660Sstevel@tonic-gate /* mark this device as idle */
40670Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
40680Sstevel@tonic-gate
40694844Slg150142 cv_broadcast(&hubd->h_cv_hotplug_dev);
40704844Slg150142
40710Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
40720Sstevel@tonic-gate "hubd_hotplug_thread: exit");
40730Sstevel@tonic-gate
40740Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
40750Sstevel@tonic-gate
40760Sstevel@tonic-gate ndi_rele_devi(hdip);
40770Sstevel@tonic-gate }
40780Sstevel@tonic-gate
40790Sstevel@tonic-gate
40800Sstevel@tonic-gate /*
40810Sstevel@tonic-gate * hubd_handle_port_connect:
40820Sstevel@tonic-gate * Transition a port from Disabled to Enabled. Ensure that the
40830Sstevel@tonic-gate * port is in the correct state before attempting to
40840Sstevel@tonic-gate * access the device.
40850Sstevel@tonic-gate */
40860Sstevel@tonic-gate static int
hubd_handle_port_connect(hubd_t * hubd,usb_port_t port)40870Sstevel@tonic-gate hubd_handle_port_connect(hubd_t *hubd, usb_port_t port)
40880Sstevel@tonic-gate {
40890Sstevel@tonic-gate int rval;
40900Sstevel@tonic-gate int retry;
40910Sstevel@tonic-gate long time_delay;
40920Sstevel@tonic-gate long settling_time;
40930Sstevel@tonic-gate uint16_t status;
40940Sstevel@tonic-gate uint16_t change;
40950Sstevel@tonic-gate usb_addr_t hubd_usb_addr;
40960Sstevel@tonic-gate usba_device_t *usba_device;
40970Sstevel@tonic-gate usb_port_status_t port_status = 0;
40980Sstevel@tonic-gate usb_port_status_t hub_port_status = 0;
40990Sstevel@tonic-gate
41000Sstevel@tonic-gate /* Get the hub address and port status */
41010Sstevel@tonic-gate usba_device = hubd->h_usba_device;
41020Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex);
41030Sstevel@tonic-gate hubd_usb_addr = usba_device->usb_addr;
41040Sstevel@tonic-gate hub_port_status = usba_device->usb_port_status;
41050Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex);
41060Sstevel@tonic-gate
41070Sstevel@tonic-gate /*
41080Sstevel@tonic-gate * If a device is connected, transition the
41090Sstevel@tonic-gate * port from Disabled to the Enabled state.
41100Sstevel@tonic-gate * The device will receive downstream packets
41110Sstevel@tonic-gate * in the Enabled state.
41120Sstevel@tonic-gate *
41130Sstevel@tonic-gate * reset port and wait for the hub to report
41140Sstevel@tonic-gate * completion
41150Sstevel@tonic-gate */
41160Sstevel@tonic-gate change = status = 0;
41170Sstevel@tonic-gate
41180Sstevel@tonic-gate /*
41190Sstevel@tonic-gate * According to section 9.1.2 of USB 2.0 spec, the host should
41200Sstevel@tonic-gate * wait for atleast 100ms to allow completion of an insertion
41210Sstevel@tonic-gate * process and for power at the device to become stable.
41220Sstevel@tonic-gate * We wait for 200 ms
41230Sstevel@tonic-gate */
41240Sstevel@tonic-gate settling_time = drv_usectohz(hubd_device_delay / 5);
41250Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
41260Sstevel@tonic-gate delay(settling_time);
41270Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
41280Sstevel@tonic-gate
41290Sstevel@tonic-gate /* calculate 600 ms delay time */
41300Sstevel@tonic-gate time_delay = (6 * drv_usectohz(hubd_device_delay)) / 10;
41310Sstevel@tonic-gate
41320Sstevel@tonic-gate for (retry = 0; (hubd->h_dev_state == USB_DEV_ONLINE) &&
41330Sstevel@tonic-gate (retry < hubd_retry_enumerate); retry++) {
41340Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
41350Sstevel@tonic-gate "resetting port%d, retry=%d", port, retry);
41360Sstevel@tonic-gate
41370Sstevel@tonic-gate if ((rval = hubd_reset_port(hubd, port)) != USB_SUCCESS) {
41380Sstevel@tonic-gate (void) hubd_determine_port_status(hubd,
41390Sstevel@tonic-gate port, &status, &change, 0);
41400Sstevel@tonic-gate
41410Sstevel@tonic-gate /* continue only if port is still connected */
41420Sstevel@tonic-gate if (status & PORT_STATUS_CCS) {
41430Sstevel@tonic-gate continue;
41440Sstevel@tonic-gate }
41450Sstevel@tonic-gate
41460Sstevel@tonic-gate /* carry on regardless */
41470Sstevel@tonic-gate }
41480Sstevel@tonic-gate
41490Sstevel@tonic-gate /*
41500Sstevel@tonic-gate * according to USB 2.0 spec section 11.24.2.7.1.2
41510Sstevel@tonic-gate * at the end of port reset, the hub enables the port.
41520Sstevel@tonic-gate * But for some strange reasons, uhci port remains disabled.
41530Sstevel@tonic-gate * And because the port remains disabled for the settling
41540Sstevel@tonic-gate * time below, the device connected to the port gets wedged
41550Sstevel@tonic-gate * - fails to enumerate (device not responding)
41560Sstevel@tonic-gate * Hence, we enable it here immediately and later again after
41570Sstevel@tonic-gate * the delay
41580Sstevel@tonic-gate */
41590Sstevel@tonic-gate (void) hubd_enable_port(hubd, port);
41600Sstevel@tonic-gate
41610Sstevel@tonic-gate /* we skip this delay in the first iteration */
41620Sstevel@tonic-gate if (retry) {
41630Sstevel@tonic-gate /*
41640Sstevel@tonic-gate * delay for device to signal disconnect/connect so
41650Sstevel@tonic-gate * that hub properly recognizes the speed of the device
41660Sstevel@tonic-gate */
41670Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
41680Sstevel@tonic-gate delay(settling_time);
41690Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
41700Sstevel@tonic-gate
41710Sstevel@tonic-gate /*
41720Sstevel@tonic-gate * When a low speed device is connected to any port of
41730Sstevel@tonic-gate * PPX it has to be explicitly enabled
41740Sstevel@tonic-gate * Also, if device intentionally signals
41750Sstevel@tonic-gate * disconnect/connect, it will disable the port.
41760Sstevel@tonic-gate * So enable it again.
41770Sstevel@tonic-gate */
41780Sstevel@tonic-gate (void) hubd_enable_port(hubd, port);
41790Sstevel@tonic-gate }
41800Sstevel@tonic-gate
41810Sstevel@tonic-gate if ((rval = hubd_determine_port_status(hubd, port, &status,
41820Sstevel@tonic-gate &change, 0)) != USB_SUCCESS) {
41830Sstevel@tonic-gate
4184978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
41850Sstevel@tonic-gate "getting status failed (%d)", rval);
41860Sstevel@tonic-gate
41870Sstevel@tonic-gate (void) hubd_disable_port(hubd, port);
41880Sstevel@tonic-gate
41890Sstevel@tonic-gate continue;
41900Sstevel@tonic-gate }
41910Sstevel@tonic-gate
41920Sstevel@tonic-gate if (status & PORT_STATUS_POCI) {
4193978Sfrits USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
41940Sstevel@tonic-gate "port %d overcurrent", port);
41950Sstevel@tonic-gate
41960Sstevel@tonic-gate (void) hubd_disable_port(hubd, port);
41970Sstevel@tonic-gate
41980Sstevel@tonic-gate /* ack changes */
41990Sstevel@tonic-gate (void) hubd_determine_port_status(hubd,
42000Sstevel@tonic-gate port, &status, &change, PORT_CHANGE_OCIC);
42010Sstevel@tonic-gate
42020Sstevel@tonic-gate continue;
42030Sstevel@tonic-gate }
42040Sstevel@tonic-gate
42050Sstevel@tonic-gate /* is status really OK? */
42060Sstevel@tonic-gate if ((status & PORT_STATUS_OK) != PORT_STATUS_OK) {
42070Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
42080Sstevel@tonic-gate "port %d status (0x%x) not OK on retry %d",
42090Sstevel@tonic-gate port, status, retry);
42100Sstevel@tonic-gate
42110Sstevel@tonic-gate /* check if we still have the connection */
42120Sstevel@tonic-gate if (!(status & PORT_STATUS_CCS)) {
42130Sstevel@tonic-gate /* lost connection, set exit condition */
42140Sstevel@tonic-gate retry = hubd_retry_enumerate;
42150Sstevel@tonic-gate
42160Sstevel@tonic-gate break;
42170Sstevel@tonic-gate }
42180Sstevel@tonic-gate } else {
42190Sstevel@tonic-gate /*
42200Sstevel@tonic-gate * Determine if the device is high or full
42210Sstevel@tonic-gate * or low speed.
42220Sstevel@tonic-gate */
42230Sstevel@tonic-gate if (status & PORT_STATUS_LSDA) {
42240Sstevel@tonic-gate port_status = USBA_LOW_SPEED_DEV;
42250Sstevel@tonic-gate } else if (status & PORT_STATUS_HSDA) {
42260Sstevel@tonic-gate port_status = USBA_HIGH_SPEED_DEV;
42270Sstevel@tonic-gate } else {
42280Sstevel@tonic-gate port_status = USBA_FULL_SPEED_DEV;
42290Sstevel@tonic-gate }
42300Sstevel@tonic-gate
42310Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
42320Sstevel@tonic-gate "creating child port%d, status=0x%x "
42330Sstevel@tonic-gate "port status=0x%x",
42340Sstevel@tonic-gate port, status, port_status);
42350Sstevel@tonic-gate
42360Sstevel@tonic-gate /*
42370Sstevel@tonic-gate * if the child already exists, set addrs and config
42380Sstevel@tonic-gate * to the device post connect event to the child
42390Sstevel@tonic-gate */
42400Sstevel@tonic-gate if (hubd->h_children_dips[port]) {
42410Sstevel@tonic-gate /* set addrs to this device */
42420Sstevel@tonic-gate rval = hubd_setdevaddr(hubd, port);
42430Sstevel@tonic-gate
42440Sstevel@tonic-gate /*
42450Sstevel@tonic-gate * This delay is important for the CATC hub
42460Sstevel@tonic-gate * to enumerate. But, avoid delay in the first
42470Sstevel@tonic-gate * iteration
42480Sstevel@tonic-gate */
42490Sstevel@tonic-gate if (retry) {
42500Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
42510Sstevel@tonic-gate delay(drv_usectohz(
42520Sstevel@tonic-gate hubd_device_delay/100));
42530Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
42540Sstevel@tonic-gate }
42550Sstevel@tonic-gate
42560Sstevel@tonic-gate if (rval == USB_SUCCESS) {
42570Sstevel@tonic-gate /*
42584844Slg150142 * if the port is resetting, check if
42594844Slg150142 * device's descriptors have changed.
42604844Slg150142 */
42614844Slg150142 if ((hubd->h_reset_port[port]) &&
42624844Slg150142 (hubd_check_same_device(hubd,
42634844Slg150142 port) != USB_SUCCESS)) {
42644844Slg150142 retry = hubd_retry_enumerate;
42654844Slg150142
42664844Slg150142 break;
42674844Slg150142 }
42684844Slg150142
42694844Slg150142 /*
42700Sstevel@tonic-gate * set the default config for
42710Sstevel@tonic-gate * this device
42720Sstevel@tonic-gate */
42730Sstevel@tonic-gate hubd_setdevconfig(hubd, port);
42740Sstevel@tonic-gate
42750Sstevel@tonic-gate /*
42764844Slg150142 * if we are doing Default reset, do
42774844Slg150142 * not post reconnect event since we
42784844Slg150142 * don't know where reset function is
42794844Slg150142 * called.
42804844Slg150142 */
42814844Slg150142 if (hubd->h_reset_port[port]) {
42824844Slg150142
42834844Slg150142 return (USB_SUCCESS);
42844844Slg150142 }
42854844Slg150142
42864844Slg150142 /*
42870Sstevel@tonic-gate * indicate to the child that
42880Sstevel@tonic-gate * it is online again
42890Sstevel@tonic-gate */
42900Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
42910Sstevel@tonic-gate hubd_post_event(hubd, port,
42920Sstevel@tonic-gate USBA_EVENT_TAG_HOT_INSERTION);
42930Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
42940Sstevel@tonic-gate
42950Sstevel@tonic-gate return (USB_SUCCESS);
42960Sstevel@tonic-gate }
42970Sstevel@tonic-gate } else {
42980Sstevel@tonic-gate /*
42990Sstevel@tonic-gate * We need to release access here
43000Sstevel@tonic-gate * so that busctls on other ports can
43010Sstevel@tonic-gate * continue and don't cause a deadlock
43020Sstevel@tonic-gate * when busctl and removal of prom node
43030Sstevel@tonic-gate * takes concurrently. This also ensures
43040Sstevel@tonic-gate * busctls for attach of successfully
43050Sstevel@tonic-gate * enumerated devices on other ports can
43060Sstevel@tonic-gate * continue concurrently with the process
43070Sstevel@tonic-gate * of enumerating the new devices. This
43080Sstevel@tonic-gate * reduces the overall boot time of the system.
43090Sstevel@tonic-gate */
43100Sstevel@tonic-gate rval = hubd_create_child(hubd->h_dip,
43114763Slg150142 hubd,
43124763Slg150142 hubd->h_usba_device,
43134763Slg150142 port_status, port,
43144763Slg150142 retry);
43150Sstevel@tonic-gate if (rval == USB_SUCCESS) {
43160Sstevel@tonic-gate usba_update_hotplug_stats(hubd->h_dip,
43170Sstevel@tonic-gate USBA_TOTAL_HOTPLUG_SUCCESS|
43180Sstevel@tonic-gate USBA_HOTPLUG_SUCCESS);
43190Sstevel@tonic-gate hubd->h_total_hotplug_success++;
43200Sstevel@tonic-gate
43210Sstevel@tonic-gate if (retry > 0) {
4322978Sfrits USB_DPRINTF_L2(
43230Sstevel@tonic-gate DPRINT_MASK_HOTPLUG,
43240Sstevel@tonic-gate hubd->h_log_handle,
43250Sstevel@tonic-gate "device on port %d "
43260Sstevel@tonic-gate "enumerated after %d %s",
43270Sstevel@tonic-gate port, retry,
43280Sstevel@tonic-gate (retry > 1) ? "retries" :
43290Sstevel@tonic-gate "retry");
43300Sstevel@tonic-gate
43310Sstevel@tonic-gate }
43320Sstevel@tonic-gate
43330Sstevel@tonic-gate return (USB_SUCCESS);
43340Sstevel@tonic-gate }
43350Sstevel@tonic-gate }
43360Sstevel@tonic-gate }
43370Sstevel@tonic-gate
43380Sstevel@tonic-gate /* wait a while until it settles? */
43390Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
43400Sstevel@tonic-gate "disabling port %d again", port);
43410Sstevel@tonic-gate
43420Sstevel@tonic-gate (void) hubd_disable_port(hubd, port);
43430Sstevel@tonic-gate if (retry) {
43440Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
43450Sstevel@tonic-gate delay(time_delay);
43460Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
43470Sstevel@tonic-gate }
43480Sstevel@tonic-gate
43490Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
43500Sstevel@tonic-gate "retrying on port %d", port);
43510Sstevel@tonic-gate }
43520Sstevel@tonic-gate
43530Sstevel@tonic-gate if (retry >= hubd_retry_enumerate) {
43540Sstevel@tonic-gate /*
43550Sstevel@tonic-gate * If it is a High Speed Root Hub and connected device
43560Sstevel@tonic-gate * Is a Low/Full Speed, it will be handled by USB 1.1
43570Sstevel@tonic-gate * Host Controller. In this case, USB 2.0 Host Controller
43580Sstevel@tonic-gate * will transfer the ownership of this port to USB 1.1
43590Sstevel@tonic-gate * Host Controller. So don't display any error message on
43600Sstevel@tonic-gate * the console.
43610Sstevel@tonic-gate */
43620Sstevel@tonic-gate if ((hubd_usb_addr == ROOT_HUB_ADDR) &&
43630Sstevel@tonic-gate (hub_port_status == USBA_HIGH_SPEED_DEV) &&
43640Sstevel@tonic-gate (port_status != USBA_HIGH_SPEED_DEV)) {
43650Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
43660Sstevel@tonic-gate hubd->h_log_handle,
43670Sstevel@tonic-gate "hubd_handle_port_connect: Low/Full speed "
43680Sstevel@tonic-gate "device is connected to High Speed root hub");
43690Sstevel@tonic-gate } else {
43700Sstevel@tonic-gate USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
43710Sstevel@tonic-gate hubd->h_log_handle,
43720Sstevel@tonic-gate "Connecting device on port %d failed", port);
43730Sstevel@tonic-gate }
43740Sstevel@tonic-gate
43750Sstevel@tonic-gate (void) hubd_disable_port(hubd, port);
43760Sstevel@tonic-gate usba_update_hotplug_stats(hubd->h_dip,
43770Sstevel@tonic-gate USBA_TOTAL_HOTPLUG_FAILURE|USBA_HOTPLUG_FAILURE);
43780Sstevel@tonic-gate hubd->h_total_hotplug_failure++;
43790Sstevel@tonic-gate
43800Sstevel@tonic-gate /*
43810Sstevel@tonic-gate * the port should be automagically
43820Sstevel@tonic-gate * disabled but just in case, we do
43830Sstevel@tonic-gate * it here
43840Sstevel@tonic-gate */
43850Sstevel@tonic-gate (void) hubd_disable_port(hubd, port);
43860Sstevel@tonic-gate
43870Sstevel@tonic-gate /* ack all changes because we disabled this port */
43880Sstevel@tonic-gate (void) hubd_determine_port_status(hubd,
43890Sstevel@tonic-gate port, &status, &change, HUBD_ACK_ALL_CHANGES);
43900Sstevel@tonic-gate
43910Sstevel@tonic-gate }
43920Sstevel@tonic-gate
43930Sstevel@tonic-gate return (USB_FAILURE);
43940Sstevel@tonic-gate }
43950Sstevel@tonic-gate
43960Sstevel@tonic-gate
43970Sstevel@tonic-gate /*
43980Sstevel@tonic-gate * hubd_get_hub_status:
43990Sstevel@tonic-gate */
44000Sstevel@tonic-gate static int
hubd_get_hub_status(hubd_t * hubd)44010Sstevel@tonic-gate hubd_get_hub_status(hubd_t *hubd)
44020Sstevel@tonic-gate {
44030Sstevel@tonic-gate int rval;
44040Sstevel@tonic-gate usb_cr_t completion_reason;
44050Sstevel@tonic-gate usb_cb_flags_t cb_flags;
44061001Ssl147100 uint16_t stword[2];
44070Sstevel@tonic-gate uint16_t status;
44080Sstevel@tonic-gate uint16_t change;
44090Sstevel@tonic-gate usb_cfg_descr_t cfg_descr;
44100Sstevel@tonic-gate size_t cfg_length;
44110Sstevel@tonic-gate uchar_t *usb_cfg;
44120Sstevel@tonic-gate uint8_t MaxPower;
44132326Ssl147100 usb_hub_descr_t *hub_descr;
44142326Ssl147100 usb_port_t port;
44152326Ssl147100
44162326Ssl147100 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
44171001Ssl147100 "hubd_get_hub_status:");
44181001Ssl147100
44191001Ssl147100 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
44201001Ssl147100
44211001Ssl147100 if ((hubd_get_hub_status_words(hubd, stword)) != USB_SUCCESS) {
44220Sstevel@tonic-gate
44230Sstevel@tonic-gate return (USB_FAILURE);
44240Sstevel@tonic-gate }
44251001Ssl147100 status = stword[0];
44261001Ssl147100 change = stword[1];
44270Sstevel@tonic-gate
44280Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
44290Sstevel@tonic-gate
44300Sstevel@tonic-gate /* Obtain the raw configuration descriptor */
44310Sstevel@tonic-gate usb_cfg = usb_get_raw_cfg_data(hubd->h_dip, &cfg_length);
44320Sstevel@tonic-gate
44330Sstevel@tonic-gate /* get configuration descriptor */
44340Sstevel@tonic-gate rval = usb_parse_cfg_descr(usb_cfg, cfg_length,
44354763Slg150142 &cfg_descr, USB_CFG_DESCR_SIZE);
44360Sstevel@tonic-gate
44370Sstevel@tonic-gate if (rval != USB_CFG_DESCR_SIZE) {
44380Sstevel@tonic-gate
44392326Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
44400Sstevel@tonic-gate "get hub configuration descriptor failed.");
44410Sstevel@tonic-gate
44420Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
44430Sstevel@tonic-gate
44440Sstevel@tonic-gate return (USB_FAILURE);
44450Sstevel@tonic-gate } else {
44460Sstevel@tonic-gate MaxPower = cfg_descr.bMaxPower;
44470Sstevel@tonic-gate }
44480Sstevel@tonic-gate
44490Sstevel@tonic-gate /* check if local power status changed. */
44500Sstevel@tonic-gate if (change & C_HUB_LOCAL_POWER_STATUS) {
44510Sstevel@tonic-gate
44520Sstevel@tonic-gate /*
44530Sstevel@tonic-gate * local power has been lost, check the maximum
44540Sstevel@tonic-gate * power consumption of current configuration.
44550Sstevel@tonic-gate * see USB2.0 spec Table 11-12.
44560Sstevel@tonic-gate */
44570Sstevel@tonic-gate if (status & HUB_LOCAL_POWER_STATUS) {
44580Sstevel@tonic-gate
44590Sstevel@tonic-gate if (MaxPower == 0) {
44600Sstevel@tonic-gate
44610Sstevel@tonic-gate /*
44620Sstevel@tonic-gate * Self-powered only hub. Because it could
44630Sstevel@tonic-gate * not draw any power from USB bus.
44640Sstevel@tonic-gate * It can't work well on this condition.
44650Sstevel@tonic-gate */
44662326Ssl147100 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
44670Sstevel@tonic-gate hubd->h_log_handle,
44680Sstevel@tonic-gate "local power has been lost, "
44690Sstevel@tonic-gate "please disconnect hub");
44700Sstevel@tonic-gate } else {
44710Sstevel@tonic-gate
44720Sstevel@tonic-gate /*
44730Sstevel@tonic-gate * Bus-powered only or self/bus-powered hub.
44740Sstevel@tonic-gate */
44752326Ssl147100 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG,
44760Sstevel@tonic-gate hubd->h_log_handle,
44770Sstevel@tonic-gate "local power has been lost,"
44780Sstevel@tonic-gate "the hub could draw %d"
44790Sstevel@tonic-gate " mA power from the USB bus.",
44800Sstevel@tonic-gate 2*MaxPower);
44810Sstevel@tonic-gate }
44820Sstevel@tonic-gate
44830Sstevel@tonic-gate }
44840Sstevel@tonic-gate
44852326Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
44860Sstevel@tonic-gate "clearing feature C_HUB_LOCAL_POWER ");
44870Sstevel@tonic-gate
44880Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
44890Sstevel@tonic-gate hubd->h_default_pipe,
44902326Ssl147100 HUB_HANDLE_HUB_FEATURE_TYPE,
44910Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE,
44920Sstevel@tonic-gate CFS_C_HUB_LOCAL_POWER,
44930Sstevel@tonic-gate 0,
44940Sstevel@tonic-gate 0,
44950Sstevel@tonic-gate NULL, 0,
44960Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
44972326Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
44980Sstevel@tonic-gate hubd->h_log_handle,
44990Sstevel@tonic-gate "clear feature C_HUB_LOCAL_POWER "
45000Sstevel@tonic-gate "failed (%d 0x%x %d)",
45010Sstevel@tonic-gate rval, completion_reason, cb_flags);
45020Sstevel@tonic-gate }
45030Sstevel@tonic-gate
45040Sstevel@tonic-gate }
45050Sstevel@tonic-gate
45060Sstevel@tonic-gate if (change & C_HUB_OVER_CURRENT) {
45070Sstevel@tonic-gate
45080Sstevel@tonic-gate if (status & HUB_OVER_CURRENT) {
45092326Ssl147100
45102326Ssl147100 if (usba_is_root_hub(hubd->h_dip)) {
45112326Ssl147100 /*
45122326Ssl147100 * The root hub should be automatically
45132326Ssl147100 * recovered when over-current condition is
45142326Ssl147100 * cleared. But there might be exception and
45152326Ssl147100 * need user interaction to recover.
45162326Ssl147100 */
45172326Ssl147100 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
45182326Ssl147100 hubd->h_log_handle,
45192326Ssl147100 "Root hub over current condition, "
45202326Ssl147100 "please check your system to clear the "
45212326Ssl147100 "condition as soon as possible. And you "
45222326Ssl147100 "may need to reboot the system to bring "
45232326Ssl147100 "the root hub back to work if it cannot "
45242326Ssl147100 "recover automatically");
45252326Ssl147100 } else {
45262326Ssl147100 /*
45272326Ssl147100 * The driver would try to recover port power
45282326Ssl147100 * on over current condition. When the recovery
45292326Ssl147100 * fails, the user may still need to offline
45302326Ssl147100 * this hub in order to recover.
45312326Ssl147100 * The port power is automatically disabled,
45322326Ssl147100 * so we won't see disconnects.
45332326Ssl147100 */
45342326Ssl147100 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
45352326Ssl147100 hubd->h_log_handle,
45362326Ssl147100 "Hub global over current condition, "
45372326Ssl147100 "please disconnect the devices connected "
45382326Ssl147100 "to the hub to clear the condition. And "
45392326Ssl147100 "you may need to re-connect the hub if "
45402326Ssl147100 "the ports do not work");
45412326Ssl147100 }
45422326Ssl147100 }
45432326Ssl147100
45442326Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
45450Sstevel@tonic-gate "clearing feature C_HUB_OVER_CURRENT");
45460Sstevel@tonic-gate
45470Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
45480Sstevel@tonic-gate hubd->h_default_pipe,
45492326Ssl147100 HUB_HANDLE_HUB_FEATURE_TYPE,
45500Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE,
45510Sstevel@tonic-gate CFS_C_HUB_OVER_CURRENT,
45520Sstevel@tonic-gate 0,
45530Sstevel@tonic-gate 0,
45540Sstevel@tonic-gate NULL, 0,
45550Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
45562326Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
45570Sstevel@tonic-gate hubd->h_log_handle,
45580Sstevel@tonic-gate "clear feature C_HUB_OVER_CURRENT "
45590Sstevel@tonic-gate "failed (%d 0x%x %d)",
45600Sstevel@tonic-gate rval, completion_reason, cb_flags);
45610Sstevel@tonic-gate }
45622326Ssl147100
45632326Ssl147100 /*
45642326Ssl147100 * Try to recover all port power if they are turned off.
45652326Ssl147100 * Don't do this for root hub, but rely on the root hub
45662326Ssl147100 * to recover itself.
45672326Ssl147100 */
45682326Ssl147100 if (!usba_is_root_hub(hubd->h_dip)) {
45692326Ssl147100
45702326Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
45712326Ssl147100
45722326Ssl147100 /*
45732326Ssl147100 * Only check the power status of the 1st port
45742326Ssl147100 * since all port power status should be the same.
45752326Ssl147100 */
45762326Ssl147100 (void) hubd_determine_port_status(hubd, 1, &status,
45772326Ssl147100 &change, 0);
45782326Ssl147100
45792326Ssl147100 if (status & PORT_STATUS_PPS) {
45802326Ssl147100
45812326Ssl147100 return (USB_SUCCESS);
45822326Ssl147100 }
45832326Ssl147100
45842326Ssl147100 hub_descr = &hubd->h_hub_descr;
45852326Ssl147100
45862326Ssl147100 for (port = 1; port <= hub_descr->bNbrPorts;
45872326Ssl147100 port++) {
45882326Ssl147100
45892326Ssl147100 (void) hubd_enable_port_power(hubd, port);
45902326Ssl147100 }
45912326Ssl147100
45922326Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
45932326Ssl147100
45942326Ssl147100 /*
45952326Ssl147100 * Delay some time to avoid over-current event
45962326Ssl147100 * to happen too frequently in some cases
45972326Ssl147100 */
45982326Ssl147100 delay(drv_usectohz(500000));
45992326Ssl147100 }
46000Sstevel@tonic-gate }
46010Sstevel@tonic-gate
46020Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
46030Sstevel@tonic-gate
46040Sstevel@tonic-gate return (USB_SUCCESS);
46050Sstevel@tonic-gate }
46060Sstevel@tonic-gate
46070Sstevel@tonic-gate
46080Sstevel@tonic-gate /*
46090Sstevel@tonic-gate * hubd_reset_port:
46100Sstevel@tonic-gate */
46110Sstevel@tonic-gate static int
hubd_reset_port(hubd_t * hubd,usb_port_t port)46120Sstevel@tonic-gate hubd_reset_port(hubd_t *hubd, usb_port_t port)
46130Sstevel@tonic-gate {
46140Sstevel@tonic-gate int rval;
46150Sstevel@tonic-gate usb_cr_t completion_reason;
46160Sstevel@tonic-gate usb_cb_flags_t cb_flags;
46170Sstevel@tonic-gate usb_port_mask_t port_mask = 1 << port;
46180Sstevel@tonic-gate mblk_t *data;
46190Sstevel@tonic-gate uint16_t status;
46200Sstevel@tonic-gate uint16_t change;
46210Sstevel@tonic-gate int i;
462211066Srafael.vanoni@sun.com clock_t delta;
46230Sstevel@tonic-gate
46240Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
46250Sstevel@tonic-gate "hubd_reset_port: port=%d", port);
46260Sstevel@tonic-gate
46270Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
46280Sstevel@tonic-gate
46290Sstevel@tonic-gate hubd->h_port_reset_wait |= port_mask;
46300Sstevel@tonic-gate
46310Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
46320Sstevel@tonic-gate
46330Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
46340Sstevel@tonic-gate hubd->h_default_pipe,
46351001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
46360Sstevel@tonic-gate USB_REQ_SET_FEATURE,
46370Sstevel@tonic-gate CFS_PORT_RESET,
46380Sstevel@tonic-gate port,
46390Sstevel@tonic-gate 0,
46400Sstevel@tonic-gate NULL, 0,
46410Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4642978Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
46430Sstevel@tonic-gate "reset port%d failed (%d 0x%x %d)",
46440Sstevel@tonic-gate port, completion_reason, cb_flags, rval);
46450Sstevel@tonic-gate
46460Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
46470Sstevel@tonic-gate
46480Sstevel@tonic-gate return (USB_FAILURE);
46490Sstevel@tonic-gate }
46500Sstevel@tonic-gate
46510Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
46520Sstevel@tonic-gate
46530Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
46540Sstevel@tonic-gate "waiting on cv for reset completion");
46550Sstevel@tonic-gate
46560Sstevel@tonic-gate /*
46570Sstevel@tonic-gate * wait for port status change event
46580Sstevel@tonic-gate */
465911066Srafael.vanoni@sun.com delta = drv_usectohz(hubd_device_delay / 10);
46600Sstevel@tonic-gate for (i = 0; i < hubd_retry_enumerate; i++) {
46610Sstevel@tonic-gate /*
46620Sstevel@tonic-gate * start polling ep1 for receiving notification on
46630Sstevel@tonic-gate * reset completion
46640Sstevel@tonic-gate */
46650Sstevel@tonic-gate hubd_start_polling(hubd, HUBD_ALWAYS_START_POLLING);
46660Sstevel@tonic-gate
46670Sstevel@tonic-gate /*
46680Sstevel@tonic-gate * sleep a max of 100ms for reset completion
46690Sstevel@tonic-gate * notification to be received
46700Sstevel@tonic-gate */
46710Sstevel@tonic-gate if (hubd->h_port_reset_wait & port_mask) {
467211066Srafael.vanoni@sun.com rval = cv_reltimedwait(&hubd->h_cv_reset_port,
467311066Srafael.vanoni@sun.com &hubd->h_mutex, delta, TR_CLOCK_TICK);
46740Sstevel@tonic-gate if ((rval <= 0) &&
46750Sstevel@tonic-gate (hubd->h_port_reset_wait & port_mask)) {
46760Sstevel@tonic-gate /* we got woken up because of a timeout */
46770Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT,
46780Sstevel@tonic-gate hubd->h_log_handle,
46790Sstevel@tonic-gate "timeout: reset port=%d failed", port);
46800Sstevel@tonic-gate
46810Sstevel@tonic-gate hubd->h_port_reset_wait &= ~port_mask;
46820Sstevel@tonic-gate
46830Sstevel@tonic-gate hubd_stop_polling(hubd);
46840Sstevel@tonic-gate
46850Sstevel@tonic-gate return (USB_FAILURE);
46860Sstevel@tonic-gate }
46870Sstevel@tonic-gate }
46880Sstevel@tonic-gate
46890Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
46900Sstevel@tonic-gate "reset completion received");
46910Sstevel@tonic-gate
46920Sstevel@tonic-gate hubd_stop_polling(hubd);
46930Sstevel@tonic-gate
46940Sstevel@tonic-gate data = NULL;
46950Sstevel@tonic-gate
46960Sstevel@tonic-gate /* check status to determine whether reset completed */
46970Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
46980Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
46990Sstevel@tonic-gate hubd->h_default_pipe,
47001001Ssl147100 HUB_GET_PORT_STATUS_TYPE,
47010Sstevel@tonic-gate USB_REQ_GET_STATUS,
47020Sstevel@tonic-gate 0,
47030Sstevel@tonic-gate port,
47040Sstevel@tonic-gate GET_STATUS_LENGTH,
47050Sstevel@tonic-gate &data, 0,
47060Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
4707978Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT,
47080Sstevel@tonic-gate hubd->h_log_handle,
47090Sstevel@tonic-gate "get status port%d failed (%d 0x%x %d)",
47100Sstevel@tonic-gate port, completion_reason, cb_flags, rval);
47110Sstevel@tonic-gate
47120Sstevel@tonic-gate if (data) {
47130Sstevel@tonic-gate freemsg(data);
47140Sstevel@tonic-gate data = NULL;
47150Sstevel@tonic-gate }
47160Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
47170Sstevel@tonic-gate
47180Sstevel@tonic-gate continue;
47190Sstevel@tonic-gate }
47200Sstevel@tonic-gate
47210Sstevel@tonic-gate status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
47220Sstevel@tonic-gate change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
47230Sstevel@tonic-gate
47240Sstevel@tonic-gate freemsg(data);
47250Sstevel@tonic-gate
47260Sstevel@tonic-gate /* continue only if port is still connected */
47270Sstevel@tonic-gate if (!(status & PORT_STATUS_CCS)) {
47280Sstevel@tonic-gate
47290Sstevel@tonic-gate /* lost connection, set exit condition */
47300Sstevel@tonic-gate i = hubd_retry_enumerate;
47310Sstevel@tonic-gate
47320Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
47330Sstevel@tonic-gate
47340Sstevel@tonic-gate break;
47350Sstevel@tonic-gate }
47360Sstevel@tonic-gate
47370Sstevel@tonic-gate if (status & PORT_STATUS_PRS) {
47380Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
47390Sstevel@tonic-gate "port%d reset active", port);
47400Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
47410Sstevel@tonic-gate
47420Sstevel@tonic-gate continue;
47430Sstevel@tonic-gate } else {
47440Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
47450Sstevel@tonic-gate "port%d reset inactive", port);
47460Sstevel@tonic-gate }
47470Sstevel@tonic-gate
47480Sstevel@tonic-gate if (change & PORT_CHANGE_PRSC) {
47490Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
47500Sstevel@tonic-gate "clearing feature CFS_C_PORT_RESET");
47510Sstevel@tonic-gate
47520Sstevel@tonic-gate if (usb_pipe_sync_ctrl_xfer(hubd->h_dip,
47530Sstevel@tonic-gate hubd->h_default_pipe,
47541001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
47550Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE,
47560Sstevel@tonic-gate CFS_C_PORT_RESET,
47570Sstevel@tonic-gate port,
47580Sstevel@tonic-gate 0,
47590Sstevel@tonic-gate NULL, 0,
47600Sstevel@tonic-gate &completion_reason, &cb_flags, 0) != USB_SUCCESS) {
47610Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT,
47620Sstevel@tonic-gate hubd->h_log_handle,
47630Sstevel@tonic-gate "clear feature CFS_C_PORT_RESET"
47640Sstevel@tonic-gate " port%d failed (%d 0x%x %d)",
47650Sstevel@tonic-gate port, completion_reason, cb_flags, rval);
47660Sstevel@tonic-gate }
47670Sstevel@tonic-gate }
47680Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
47690Sstevel@tonic-gate
47700Sstevel@tonic-gate break;
47710Sstevel@tonic-gate }
47720Sstevel@tonic-gate
47730Sstevel@tonic-gate if (i >= hubd_retry_enumerate) {
47740Sstevel@tonic-gate /* port reset has failed */
47750Sstevel@tonic-gate rval = USB_FAILURE;
47760Sstevel@tonic-gate }
47770Sstevel@tonic-gate
47780Sstevel@tonic-gate return (rval);
47790Sstevel@tonic-gate }
47800Sstevel@tonic-gate
47810Sstevel@tonic-gate
47820Sstevel@tonic-gate /*
47830Sstevel@tonic-gate * hubd_enable_port:
47840Sstevel@tonic-gate * this may fail if the hub as been disconnected
47850Sstevel@tonic-gate */
47860Sstevel@tonic-gate static int
hubd_enable_port(hubd_t * hubd,usb_port_t port)47870Sstevel@tonic-gate hubd_enable_port(hubd_t *hubd, usb_port_t port)
47880Sstevel@tonic-gate {
47890Sstevel@tonic-gate int rval;
47900Sstevel@tonic-gate usb_cr_t completion_reason;
47910Sstevel@tonic-gate usb_cb_flags_t cb_flags;
47920Sstevel@tonic-gate
47930Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
47940Sstevel@tonic-gate "hubd_enable_port: port=%d", port);
47950Sstevel@tonic-gate
47960Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
47970Sstevel@tonic-gate
47980Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
47990Sstevel@tonic-gate
48000Sstevel@tonic-gate /* Do not issue a SetFeature(PORT_ENABLE) on external hubs */
48010Sstevel@tonic-gate if (!usba_is_root_hub(hubd->h_dip)) {
48020Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
48030Sstevel@tonic-gate
48040Sstevel@tonic-gate return (USB_SUCCESS);
48050Sstevel@tonic-gate }
48060Sstevel@tonic-gate
48070Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
48080Sstevel@tonic-gate hubd->h_default_pipe,
48091001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
48100Sstevel@tonic-gate USB_REQ_SET_FEATURE,
48110Sstevel@tonic-gate CFS_PORT_ENABLE,
48120Sstevel@tonic-gate port,
48130Sstevel@tonic-gate 0,
48140Sstevel@tonic-gate NULL, 0,
48150Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
48160Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
48170Sstevel@tonic-gate "enable port%d failed (%d 0x%x %d)",
48180Sstevel@tonic-gate port, completion_reason, cb_flags, rval);
48190Sstevel@tonic-gate }
48200Sstevel@tonic-gate
48210Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
48220Sstevel@tonic-gate
48230Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
48240Sstevel@tonic-gate "enabling port done");
48250Sstevel@tonic-gate
48260Sstevel@tonic-gate return (rval);
48270Sstevel@tonic-gate }
48280Sstevel@tonic-gate
48290Sstevel@tonic-gate
48300Sstevel@tonic-gate /*
48310Sstevel@tonic-gate * hubd_disable_port
48320Sstevel@tonic-gate */
48330Sstevel@tonic-gate static int
hubd_disable_port(hubd_t * hubd,usb_port_t port)48340Sstevel@tonic-gate hubd_disable_port(hubd_t *hubd, usb_port_t port)
48350Sstevel@tonic-gate {
48360Sstevel@tonic-gate int rval;
48370Sstevel@tonic-gate usb_cr_t completion_reason;
48380Sstevel@tonic-gate usb_cb_flags_t cb_flags;
48390Sstevel@tonic-gate
48400Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
48410Sstevel@tonic-gate "hubd_disable_port: port=%d", port);
48420Sstevel@tonic-gate
48430Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
48440Sstevel@tonic-gate
48450Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
48460Sstevel@tonic-gate
48470Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
48480Sstevel@tonic-gate hubd->h_default_pipe,
48491001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
48500Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE,
48510Sstevel@tonic-gate CFS_PORT_ENABLE,
48520Sstevel@tonic-gate port,
48530Sstevel@tonic-gate 0,
48540Sstevel@tonic-gate NULL, 0,
48550Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
48560Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
48570Sstevel@tonic-gate "disable port%d failed (%d 0x%x %d)", port,
48580Sstevel@tonic-gate completion_reason, cb_flags, rval);
48590Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
48600Sstevel@tonic-gate
48610Sstevel@tonic-gate return (USB_FAILURE);
48620Sstevel@tonic-gate }
48630Sstevel@tonic-gate
48640Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
48650Sstevel@tonic-gate "clearing feature CFS_C_PORT_ENABLE");
48660Sstevel@tonic-gate
48670Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
48680Sstevel@tonic-gate hubd->h_default_pipe,
48691001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
48700Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE,
48710Sstevel@tonic-gate CFS_C_PORT_ENABLE,
48720Sstevel@tonic-gate port,
48730Sstevel@tonic-gate 0,
48740Sstevel@tonic-gate NULL, 0,
48750Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
48760Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT,
48770Sstevel@tonic-gate hubd->h_log_handle,
48780Sstevel@tonic-gate "clear feature CFS_C_PORT_ENABLE port%d failed "
48790Sstevel@tonic-gate "(%d 0x%x %d)",
48800Sstevel@tonic-gate port, completion_reason, cb_flags, rval);
48810Sstevel@tonic-gate
48820Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
48830Sstevel@tonic-gate
48840Sstevel@tonic-gate return (USB_FAILURE);
48850Sstevel@tonic-gate }
48860Sstevel@tonic-gate
48870Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
48880Sstevel@tonic-gate
48890Sstevel@tonic-gate return (USB_SUCCESS);
48900Sstevel@tonic-gate }
48910Sstevel@tonic-gate
48920Sstevel@tonic-gate
48930Sstevel@tonic-gate /*
48940Sstevel@tonic-gate * hubd_determine_port_status:
48950Sstevel@tonic-gate */
48960Sstevel@tonic-gate static int
hubd_determine_port_status(hubd_t * hubd,usb_port_t port,uint16_t * status,uint16_t * change,uint_t ack_flag)48970Sstevel@tonic-gate hubd_determine_port_status(hubd_t *hubd, usb_port_t port,
48980Sstevel@tonic-gate uint16_t *status, uint16_t *change, uint_t ack_flag)
48990Sstevel@tonic-gate {
49000Sstevel@tonic-gate int rval;
49010Sstevel@tonic-gate mblk_t *data = NULL;
49020Sstevel@tonic-gate usb_cr_t completion_reason;
49030Sstevel@tonic-gate usb_cb_flags_t cb_flags;
49040Sstevel@tonic-gate
49050Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
49060Sstevel@tonic-gate "hubd_determine_port_status: port=%d, state=0x%x ack=0x%x", port,
49070Sstevel@tonic-gate hubd->h_port_state[port], ack_flag);
49080Sstevel@tonic-gate
49090Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
49100Sstevel@tonic-gate
49110Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
49120Sstevel@tonic-gate
49130Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
49140Sstevel@tonic-gate hubd->h_default_pipe,
49151001Ssl147100 HUB_GET_PORT_STATUS_TYPE,
49160Sstevel@tonic-gate USB_REQ_GET_STATUS,
49170Sstevel@tonic-gate 0,
49180Sstevel@tonic-gate port,
49190Sstevel@tonic-gate GET_STATUS_LENGTH,
49200Sstevel@tonic-gate &data, 0,
49210Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
49220Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
49230Sstevel@tonic-gate "port=%d get status failed (%d 0x%x %d)",
49240Sstevel@tonic-gate port, completion_reason, cb_flags, rval);
49250Sstevel@tonic-gate
49260Sstevel@tonic-gate if (data) {
49270Sstevel@tonic-gate freemsg(data);
49280Sstevel@tonic-gate }
49290Sstevel@tonic-gate
49300Sstevel@tonic-gate *status = *change = 0;
49310Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
49320Sstevel@tonic-gate
49330Sstevel@tonic-gate return (rval);
49340Sstevel@tonic-gate }
49350Sstevel@tonic-gate
49360Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
49377492SZhigang.Lu@Sun.COM if (MBLKL(data) != GET_STATUS_LENGTH) {
49380Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
49396898Sfb209375 "port %d: length incorrect %ld",
49407492SZhigang.Lu@Sun.COM port, MBLKL(data));
49410Sstevel@tonic-gate freemsg(data);
49420Sstevel@tonic-gate *status = *change = 0;
49430Sstevel@tonic-gate
49440Sstevel@tonic-gate return (rval);
49450Sstevel@tonic-gate }
49460Sstevel@tonic-gate
49470Sstevel@tonic-gate
49480Sstevel@tonic-gate *status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr);
49490Sstevel@tonic-gate *change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2);
49500Sstevel@tonic-gate
49510Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49520Sstevel@tonic-gate "port%d status=0x%x, change=0x%x", port, *status, *change);
49530Sstevel@tonic-gate
49540Sstevel@tonic-gate freemsg(data);
49550Sstevel@tonic-gate
49560Sstevel@tonic-gate if (*status & PORT_STATUS_CCS) {
49570Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49580Sstevel@tonic-gate "port%d connected", port);
49590Sstevel@tonic-gate
49600Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_CCS & ack_flag);
49610Sstevel@tonic-gate } else {
49620Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49630Sstevel@tonic-gate "port%d disconnected", port);
49640Sstevel@tonic-gate
49650Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_CCS & ack_flag);
49660Sstevel@tonic-gate }
49670Sstevel@tonic-gate
49680Sstevel@tonic-gate if (*status & PORT_STATUS_PES) {
49690Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49700Sstevel@tonic-gate "port%d enabled", port);
49710Sstevel@tonic-gate
49720Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PES & ack_flag);
49730Sstevel@tonic-gate } else {
49740Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49750Sstevel@tonic-gate "port%d disabled", port);
49760Sstevel@tonic-gate
49770Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PES & ack_flag);
49780Sstevel@tonic-gate }
49790Sstevel@tonic-gate
49800Sstevel@tonic-gate if (*status & PORT_STATUS_PSS) {
49810Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49820Sstevel@tonic-gate "port%d suspended", port);
49830Sstevel@tonic-gate
49840Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PSS & ack_flag);
49850Sstevel@tonic-gate } else {
49860Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49870Sstevel@tonic-gate "port%d not suspended", port);
49880Sstevel@tonic-gate
49890Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PSS & ack_flag);
49900Sstevel@tonic-gate }
49910Sstevel@tonic-gate
49920Sstevel@tonic-gate if (*change & PORT_CHANGE_PRSC) {
49930Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
49940Sstevel@tonic-gate "port%d reset completed", port);
49950Sstevel@tonic-gate
49960Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_CHANGE_PRSC & ack_flag);
49970Sstevel@tonic-gate } else {
49980Sstevel@tonic-gate
49990Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_CHANGE_PRSC & ack_flag);
50000Sstevel@tonic-gate }
50010Sstevel@tonic-gate
50020Sstevel@tonic-gate if (*status & PORT_STATUS_POCI) {
5003978Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
5004978Sfrits "port%d overcurrent!", port);
50050Sstevel@tonic-gate
50060Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_POCI & ack_flag);
50070Sstevel@tonic-gate } else {
50080Sstevel@tonic-gate
50090Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_POCI & ack_flag);
50100Sstevel@tonic-gate }
50110Sstevel@tonic-gate
50120Sstevel@tonic-gate if (*status & PORT_STATUS_PRS) {
50130Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50140Sstevel@tonic-gate "port%d reset active", port);
50150Sstevel@tonic-gate
50160Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PRS & ack_flag);
50170Sstevel@tonic-gate } else {
50180Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50190Sstevel@tonic-gate "port%d reset inactive", port);
50200Sstevel@tonic-gate
50210Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PRS & ack_flag);
50220Sstevel@tonic-gate }
50230Sstevel@tonic-gate if (*status & PORT_STATUS_PPS) {
50240Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50250Sstevel@tonic-gate "port%d power on", port);
50260Sstevel@tonic-gate
50270Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_PPS & ack_flag);
50280Sstevel@tonic-gate } else {
50290Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50300Sstevel@tonic-gate "port%d power off", port);
50310Sstevel@tonic-gate
50320Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_PPS & ack_flag);
50330Sstevel@tonic-gate }
50340Sstevel@tonic-gate if (*status & PORT_STATUS_LSDA) {
50350Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50360Sstevel@tonic-gate "port%d low speed", port);
50370Sstevel@tonic-gate
50380Sstevel@tonic-gate hubd->h_port_state[port] |= (PORT_STATUS_LSDA & ack_flag);
50390Sstevel@tonic-gate } else {
50400Sstevel@tonic-gate hubd->h_port_state[port] &= ~(PORT_STATUS_LSDA & ack_flag);
50410Sstevel@tonic-gate if (*status & PORT_STATUS_HSDA) {
50420Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT,
50430Sstevel@tonic-gate hubd->h_log_handle, "port%d "
50440Sstevel@tonic-gate "high speed", port);
50450Sstevel@tonic-gate
50460Sstevel@tonic-gate hubd->h_port_state[port] |=
50474763Slg150142 (PORT_STATUS_HSDA & ack_flag);
50480Sstevel@tonic-gate } else {
50490Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT,
50500Sstevel@tonic-gate hubd->h_log_handle, "port%d "
50510Sstevel@tonic-gate "full speed", port);
50520Sstevel@tonic-gate
50530Sstevel@tonic-gate hubd->h_port_state[port] &=
50544763Slg150142 ~(PORT_STATUS_HSDA & ack_flag);
50550Sstevel@tonic-gate }
50560Sstevel@tonic-gate }
50570Sstevel@tonic-gate
50580Sstevel@tonic-gate /*
50590Sstevel@tonic-gate * Acknowledge connection, enable, reset status
50600Sstevel@tonic-gate */
50610Sstevel@tonic-gate if (ack_flag) {
50620Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
50630Sstevel@tonic-gate if (*change & PORT_CHANGE_CSC & ack_flag) {
50640Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50650Sstevel@tonic-gate "clearing feature CFS_C_PORT_CONNECTION");
50660Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
50670Sstevel@tonic-gate hubd->h_default_pipe,
50681001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
50690Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE,
50700Sstevel@tonic-gate CFS_C_PORT_CONNECTION,
50710Sstevel@tonic-gate port,
50720Sstevel@tonic-gate 0, NULL, 0,
50730Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) !=
50740Sstevel@tonic-gate USB_SUCCESS) {
50750Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT,
50760Sstevel@tonic-gate hubd->h_log_handle,
50770Sstevel@tonic-gate "clear feature CFS_C_PORT_CONNECTION"
50780Sstevel@tonic-gate " port%d failed (%d 0x%x %d)",
50790Sstevel@tonic-gate port, completion_reason, cb_flags, rval);
50800Sstevel@tonic-gate }
50810Sstevel@tonic-gate }
50820Sstevel@tonic-gate if (*change & PORT_CHANGE_PESC & ack_flag) {
50830Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
50840Sstevel@tonic-gate "clearing feature CFS_C_PORT_ENABLE");
50850Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
50860Sstevel@tonic-gate hubd->h_default_pipe,
50871001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
50880Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE,
50890Sstevel@tonic-gate CFS_C_PORT_ENABLE,
50900Sstevel@tonic-gate port,
50910Sstevel@tonic-gate 0, NULL, 0,
50920Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) !=
50930Sstevel@tonic-gate USB_SUCCESS) {
50940Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT,
50950Sstevel@tonic-gate hubd->h_log_handle,
50960Sstevel@tonic-gate "clear feature CFS_C_PORT_ENABLE"
50970Sstevel@tonic-gate " port%d failed (%d 0x%x %d)",
50980Sstevel@tonic-gate port, completion_reason, cb_flags, rval);
50990Sstevel@tonic-gate }
51000Sstevel@tonic-gate }
51010Sstevel@tonic-gate if (*change & PORT_CHANGE_PSSC & ack_flag) {
51020Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
51030Sstevel@tonic-gate "clearing feature CFS_C_PORT_SUSPEND");
51040Sstevel@tonic-gate
51050Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
51060Sstevel@tonic-gate hubd->h_default_pipe,
51071001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
51080Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE,
51090Sstevel@tonic-gate CFS_C_PORT_SUSPEND,
51100Sstevel@tonic-gate port,
51110Sstevel@tonic-gate 0, NULL, 0,
51120Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) !=
51130Sstevel@tonic-gate USB_SUCCESS) {
51140Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT,
51150Sstevel@tonic-gate hubd->h_log_handle,
51160Sstevel@tonic-gate "clear feature CFS_C_PORT_SUSPEND"
51170Sstevel@tonic-gate " port%d failed (%d 0x%x %d)",
51180Sstevel@tonic-gate port, completion_reason, cb_flags, rval);
51190Sstevel@tonic-gate }
51200Sstevel@tonic-gate }
51210Sstevel@tonic-gate if (*change & PORT_CHANGE_OCIC & ack_flag) {
51220Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
51230Sstevel@tonic-gate "clearing feature CFS_C_PORT_OVER_CURRENT");
51240Sstevel@tonic-gate
51250Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
51260Sstevel@tonic-gate hubd->h_default_pipe,
51271001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
51280Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE,
51290Sstevel@tonic-gate CFS_C_PORT_OVER_CURRENT,
51300Sstevel@tonic-gate port,
51310Sstevel@tonic-gate 0, NULL, 0,
51320Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) !=
51330Sstevel@tonic-gate USB_SUCCESS) {
51340Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT,
51350Sstevel@tonic-gate hubd->h_log_handle,
51360Sstevel@tonic-gate "clear feature CFS_C_PORT_OVER_CURRENT"
51370Sstevel@tonic-gate " port%d failed (%d 0x%x %d)",
51380Sstevel@tonic-gate port, completion_reason, cb_flags, rval);
51390Sstevel@tonic-gate }
51400Sstevel@tonic-gate }
51410Sstevel@tonic-gate if (*change & PORT_CHANGE_PRSC & ack_flag) {
51420Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
51430Sstevel@tonic-gate "clearing feature CFS_C_PORT_RESET");
51440Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
51450Sstevel@tonic-gate hubd->h_default_pipe,
51461001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
51470Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE,
51480Sstevel@tonic-gate CFS_C_PORT_RESET,
51490Sstevel@tonic-gate port,
51500Sstevel@tonic-gate 0, NULL, 0,
51510Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) !=
51520Sstevel@tonic-gate USB_SUCCESS) {
51530Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT,
51540Sstevel@tonic-gate hubd->h_log_handle,
51550Sstevel@tonic-gate "clear feature CFS_C_PORT_RESET"
51560Sstevel@tonic-gate " port%d failed (%d 0x%x %d)",
51570Sstevel@tonic-gate port, completion_reason, cb_flags, rval);
51580Sstevel@tonic-gate }
51590Sstevel@tonic-gate }
51600Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
51610Sstevel@tonic-gate }
51620Sstevel@tonic-gate
51630Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
51640Sstevel@tonic-gate "new port%d state 0x%x", port, hubd->h_port_state[port]);
51650Sstevel@tonic-gate
51660Sstevel@tonic-gate
51670Sstevel@tonic-gate return (USB_SUCCESS);
51680Sstevel@tonic-gate }
51690Sstevel@tonic-gate
51700Sstevel@tonic-gate
51710Sstevel@tonic-gate /*
51720Sstevel@tonic-gate * hubd_recover_disabled_port
51730Sstevel@tonic-gate * if the port got disabled because of an error
51740Sstevel@tonic-gate * enable it. If hub doesn't suport enable port,
51750Sstevel@tonic-gate * reset the port to bring the device to life again
51760Sstevel@tonic-gate */
51770Sstevel@tonic-gate static int
hubd_recover_disabled_port(hubd_t * hubd,usb_port_t port)51780Sstevel@tonic-gate hubd_recover_disabled_port(hubd_t *hubd, usb_port_t port)
51790Sstevel@tonic-gate {
51800Sstevel@tonic-gate uint16_t status;
51810Sstevel@tonic-gate uint16_t change;
51820Sstevel@tonic-gate int rval = USB_FAILURE;
51830Sstevel@tonic-gate
51840Sstevel@tonic-gate /* first try enabling the port */
51850Sstevel@tonic-gate (void) hubd_enable_port(hubd, port);
51860Sstevel@tonic-gate
51870Sstevel@tonic-gate /* read the port status */
51880Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port, &status, &change,
51890Sstevel@tonic-gate PORT_CHANGE_PESC);
51900Sstevel@tonic-gate
51910Sstevel@tonic-gate if (status & PORT_STATUS_PES) {
51920Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
51934763Slg150142 "Port%d now Enabled", port);
51940Sstevel@tonic-gate } else if (status & PORT_STATUS_CCS) {
51950Sstevel@tonic-gate /* first post a disconnect event to the child */
51960Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
51970Sstevel@tonic-gate hubd_post_event(hubd, port, USBA_EVENT_TAG_HOT_REMOVAL);
51980Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
51990Sstevel@tonic-gate
52000Sstevel@tonic-gate /* then reset the port and recover the device */
52010Sstevel@tonic-gate rval = hubd_handle_port_connect(hubd, port);
52020Sstevel@tonic-gate
52030Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
52044763Slg150142 "Port%d now Enabled by force", port);
52050Sstevel@tonic-gate }
52060Sstevel@tonic-gate
52070Sstevel@tonic-gate return (rval);
52080Sstevel@tonic-gate }
52090Sstevel@tonic-gate
52100Sstevel@tonic-gate
52110Sstevel@tonic-gate /*
52120Sstevel@tonic-gate * hubd_enable_all_port_power:
52130Sstevel@tonic-gate */
52140Sstevel@tonic-gate static int
hubd_enable_all_port_power(hubd_t * hubd)52150Sstevel@tonic-gate hubd_enable_all_port_power(hubd_t *hubd)
52160Sstevel@tonic-gate {
52170Sstevel@tonic-gate usb_hub_descr_t *hub_descr;
52180Sstevel@tonic-gate int wait;
52190Sstevel@tonic-gate usb_port_t port;
52200Sstevel@tonic-gate uint_t retry;
52210Sstevel@tonic-gate uint16_t status;
52220Sstevel@tonic-gate uint16_t change;
52230Sstevel@tonic-gate
52240Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
52250Sstevel@tonic-gate "hubd_enable_all_port_power");
52260Sstevel@tonic-gate
52270Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
52280Sstevel@tonic-gate
52290Sstevel@tonic-gate hub_descr = &hubd->h_hub_descr;
52300Sstevel@tonic-gate
52310Sstevel@tonic-gate /*
52320Sstevel@tonic-gate * According to section 11.11 of USB, for hubs with no power
52330Sstevel@tonic-gate * switches, bPwrOn2PwrGood is zero. But we wait for some
52340Sstevel@tonic-gate * arbitrary time to enable power to become stable.
52350Sstevel@tonic-gate *
52360Sstevel@tonic-gate * If an hub supports port power switching, we need to wait
52370Sstevel@tonic-gate * at least 20ms before accessing corresponding usb port.
52380Sstevel@tonic-gate */
52390Sstevel@tonic-gate if ((hub_descr->wHubCharacteristics &
52400Sstevel@tonic-gate HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) {
52410Sstevel@tonic-gate wait = hubd_device_delay / 10;
52420Sstevel@tonic-gate } else {
52430Sstevel@tonic-gate wait = max(HUB_DEFAULT_POPG,
52440Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood) * 2 * 1000;
52450Sstevel@tonic-gate }
52460Sstevel@tonic-gate
52470Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
52480Sstevel@tonic-gate "hubd_enable_all_port_power: popg=%d wait=%d",
52490Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood, wait);
52500Sstevel@tonic-gate
52510Sstevel@tonic-gate /*
52520Sstevel@tonic-gate * Enable power per port. we ignore gang power and power mask
52530Sstevel@tonic-gate * and always enable all ports one by one.
52540Sstevel@tonic-gate */
52550Sstevel@tonic-gate for (port = 1; port <= hub_descr->bNbrPorts; port++) {
52560Sstevel@tonic-gate /*
52570Sstevel@tonic-gate * Transition the port from the Powered Off to the
52580Sstevel@tonic-gate * Disconnected state by supplying power to the port.
52590Sstevel@tonic-gate */
52600Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT,
52610Sstevel@tonic-gate hubd->h_log_handle,
52620Sstevel@tonic-gate "hubd_enable_all_port_power: power port=%d", port);
52630Sstevel@tonic-gate
52640Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port);
52650Sstevel@tonic-gate }
52660Sstevel@tonic-gate
52670Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
52680Sstevel@tonic-gate delay(drv_usectohz(wait));
52690Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
52700Sstevel@tonic-gate
52710Sstevel@tonic-gate /* For retry if any, use some extra delay */
52720Sstevel@tonic-gate wait = max(wait, hubd_device_delay / 10);
52730Sstevel@tonic-gate
52740Sstevel@tonic-gate /* Check each port power status for a given usb hub */
52750Sstevel@tonic-gate for (port = 1; port <= hub_descr->bNbrPorts; port++) {
52760Sstevel@tonic-gate
52770Sstevel@tonic-gate /* Get port status */
52780Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port,
52790Sstevel@tonic-gate &status, &change, 0);
52800Sstevel@tonic-gate
52810Sstevel@tonic-gate for (retry = 0; ((!(status & PORT_STATUS_PPS)) &&
52820Sstevel@tonic-gate (retry < HUBD_PORT_RETRY)); retry++) {
52830Sstevel@tonic-gate
52840Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
52850Sstevel@tonic-gate "Retry is in progress %d: port %d status %d",
52860Sstevel@tonic-gate retry, port, status);
52870Sstevel@tonic-gate
52880Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port);
52890Sstevel@tonic-gate
52900Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
52910Sstevel@tonic-gate delay(drv_usectohz(wait));
52920Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
52930Sstevel@tonic-gate
52940Sstevel@tonic-gate /* Get port status */
52950Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port,
52960Sstevel@tonic-gate &status, &change, 0);
52970Sstevel@tonic-gate }
52980Sstevel@tonic-gate
52990Sstevel@tonic-gate /* Print warning message if port has no power */
53000Sstevel@tonic-gate if (!(status & PORT_STATUS_PPS)) {
53010Sstevel@tonic-gate
5302978Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
53030Sstevel@tonic-gate "hubd_enable_all_port_power: port %d power-on "
53040Sstevel@tonic-gate "failed, port status 0x%x", port, status);
53050Sstevel@tonic-gate }
53060Sstevel@tonic-gate }
53070Sstevel@tonic-gate
53080Sstevel@tonic-gate return (USB_SUCCESS);
53090Sstevel@tonic-gate }
53100Sstevel@tonic-gate
53110Sstevel@tonic-gate
53120Sstevel@tonic-gate /*
53130Sstevel@tonic-gate * hubd_enable_port_power:
53140Sstevel@tonic-gate * enable individual port power
53150Sstevel@tonic-gate */
53160Sstevel@tonic-gate static int
hubd_enable_port_power(hubd_t * hubd,usb_port_t port)53170Sstevel@tonic-gate hubd_enable_port_power(hubd_t *hubd, usb_port_t port)
53180Sstevel@tonic-gate {
53190Sstevel@tonic-gate int rval;
53200Sstevel@tonic-gate usb_cr_t completion_reason;
53210Sstevel@tonic-gate usb_cb_flags_t cb_flags;
53220Sstevel@tonic-gate
53230Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
53240Sstevel@tonic-gate "hubd_enable_port_power: port=%d", port);
53250Sstevel@tonic-gate
53260Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
53270Sstevel@tonic-gate ASSERT(hubd->h_default_pipe != 0);
53280Sstevel@tonic-gate
53290Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
53300Sstevel@tonic-gate
53310Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
53320Sstevel@tonic-gate hubd->h_default_pipe,
53331001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
53340Sstevel@tonic-gate USB_REQ_SET_FEATURE,
53350Sstevel@tonic-gate CFS_PORT_POWER,
53360Sstevel@tonic-gate port,
53370Sstevel@tonic-gate 0, NULL, 0,
53380Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
53390Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
53400Sstevel@tonic-gate "set port power failed (%d 0x%x %d)",
53410Sstevel@tonic-gate completion_reason, cb_flags, rval);
53420Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
53430Sstevel@tonic-gate
53440Sstevel@tonic-gate return (USB_FAILURE);
53450Sstevel@tonic-gate } else {
53460Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
53470Sstevel@tonic-gate hubd->h_port_state[port] |= PORT_STATUS_PPS;
53480Sstevel@tonic-gate
53490Sstevel@tonic-gate return (USB_SUCCESS);
53500Sstevel@tonic-gate }
53510Sstevel@tonic-gate }
53520Sstevel@tonic-gate
53530Sstevel@tonic-gate
53540Sstevel@tonic-gate /*
53550Sstevel@tonic-gate * hubd_disable_all_port_power:
53560Sstevel@tonic-gate */
53570Sstevel@tonic-gate static int
hubd_disable_all_port_power(hubd_t * hubd)53580Sstevel@tonic-gate hubd_disable_all_port_power(hubd_t *hubd)
53590Sstevel@tonic-gate {
53600Sstevel@tonic-gate usb_port_t port;
53610Sstevel@tonic-gate
53620Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
53630Sstevel@tonic-gate "hubd_disable_all_port_power");
53640Sstevel@tonic-gate
53650Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
53660Sstevel@tonic-gate
53670Sstevel@tonic-gate /*
53680Sstevel@tonic-gate * disable power per port, ignore gang power and power mask
53690Sstevel@tonic-gate */
53700Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
53710Sstevel@tonic-gate (void) hubd_disable_port_power(hubd, port);
53720Sstevel@tonic-gate }
53730Sstevel@tonic-gate
53740Sstevel@tonic-gate return (USB_SUCCESS);
53750Sstevel@tonic-gate }
53760Sstevel@tonic-gate
53770Sstevel@tonic-gate
53780Sstevel@tonic-gate /*
53790Sstevel@tonic-gate * hubd_disable_port_power:
53800Sstevel@tonic-gate * disable individual port power
53810Sstevel@tonic-gate */
53820Sstevel@tonic-gate static int
hubd_disable_port_power(hubd_t * hubd,usb_port_t port)53830Sstevel@tonic-gate hubd_disable_port_power(hubd_t *hubd, usb_port_t port)
53840Sstevel@tonic-gate {
53850Sstevel@tonic-gate int rval;
53860Sstevel@tonic-gate usb_cr_t completion_reason;
53870Sstevel@tonic-gate usb_cb_flags_t cb_flags;
53880Sstevel@tonic-gate
53890Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle,
53900Sstevel@tonic-gate "hubd_disable_port_power: port=%d", port);
53910Sstevel@tonic-gate
53920Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
53930Sstevel@tonic-gate
53940Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
53950Sstevel@tonic-gate
53960Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip,
53970Sstevel@tonic-gate hubd->h_default_pipe,
53981001Ssl147100 HUB_HANDLE_PORT_FEATURE_TYPE,
53990Sstevel@tonic-gate USB_REQ_CLEAR_FEATURE,
54000Sstevel@tonic-gate CFS_PORT_POWER,
54010Sstevel@tonic-gate port,
54020Sstevel@tonic-gate 0, NULL, 0,
54030Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
54040Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
54050Sstevel@tonic-gate "clearing port%d power failed (%d 0x%x %d)",
54060Sstevel@tonic-gate port, completion_reason, cb_flags, rval);
54070Sstevel@tonic-gate
54080Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
54090Sstevel@tonic-gate
54100Sstevel@tonic-gate return (USB_FAILURE);
54110Sstevel@tonic-gate } else {
54120Sstevel@tonic-gate
54130Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
54140Sstevel@tonic-gate ASSERT(completion_reason == 0);
54150Sstevel@tonic-gate hubd->h_port_state[port] &= ~PORT_STATUS_PPS;
54160Sstevel@tonic-gate
54170Sstevel@tonic-gate return (USB_SUCCESS);
54180Sstevel@tonic-gate }
54190Sstevel@tonic-gate }
54200Sstevel@tonic-gate
54210Sstevel@tonic-gate
54220Sstevel@tonic-gate /*
54230Sstevel@tonic-gate * Search the database of user preferences and find out the preferred
54240Sstevel@tonic-gate * configuration for this new device
54250Sstevel@tonic-gate */
54269430SRaymond.Chen@Sun.COM int
hubd_select_device_configuration(hubd_t * hubd,usb_port_t port,dev_info_t * child_dip,usba_device_t * child_ud)54270Sstevel@tonic-gate hubd_select_device_configuration(hubd_t *hubd, usb_port_t port,
54280Sstevel@tonic-gate dev_info_t *child_dip, usba_device_t *child_ud)
54290Sstevel@tonic-gate {
54300Sstevel@tonic-gate char *pathname = NULL;
54310Sstevel@tonic-gate char *tmp_path = NULL;
54320Sstevel@tonic-gate int user_conf;
54330Sstevel@tonic-gate int pathlen;
54340Sstevel@tonic-gate usb_dev_descr_t *usbdev_ptr;
54350Sstevel@tonic-gate usba_configrec_t *user_pref;
54360Sstevel@tonic-gate
54370Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex);
54380Sstevel@tonic-gate usbdev_ptr = child_ud->usb_dev_descr;
54390Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex);
54400Sstevel@tonic-gate
54410Sstevel@tonic-gate /* try to get pathname for this device */
54420Sstevel@tonic-gate tmp_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
54430Sstevel@tonic-gate (void) ddi_pathname(child_dip, tmp_path);
54440Sstevel@tonic-gate
54450Sstevel@tonic-gate pathlen = strlen(tmp_path) + 32;
54460Sstevel@tonic-gate pathname = kmem_zalloc(pathlen, KM_SLEEP);
54470Sstevel@tonic-gate
54480Sstevel@tonic-gate /*
54490Sstevel@tonic-gate * We haven't initialized the node and it doesn't have an address
54500Sstevel@tonic-gate * yet. Append port number to the physical pathname
54510Sstevel@tonic-gate */
54520Sstevel@tonic-gate (void) sprintf(pathname, "%s@%d", tmp_path, port);
54530Sstevel@tonic-gate
54540Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
54550Sstevel@tonic-gate "hubd_select_device_configuration: Device=%s\n\t"
54560Sstevel@tonic-gate "Child path=%s",
54570Sstevel@tonic-gate usba_get_mfg_prod_sn_str(child_dip, tmp_path, MAXPATHLEN),
54580Sstevel@tonic-gate pathname);
54590Sstevel@tonic-gate kmem_free(tmp_path, MAXPATHLEN);
54600Sstevel@tonic-gate
54610Sstevel@tonic-gate
54620Sstevel@tonic-gate /* database search for user preferences */
54630Sstevel@tonic-gate user_pref = usba_devdb_get_user_preferences(usbdev_ptr->idVendor,
54640Sstevel@tonic-gate usbdev_ptr->idProduct, child_ud->usb_serialno_str, pathname);
54650Sstevel@tonic-gate
54660Sstevel@tonic-gate if (user_pref) {
54670Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
54680Sstevel@tonic-gate "hubd_select_device_configuration: "
54690Sstevel@tonic-gate "usba_devdb_get_user_preferences "
54700Sstevel@tonic-gate "return user_conf=%d\npreferred driver=%s path=%s",
54710Sstevel@tonic-gate user_pref->cfg_index, user_pref->driver,
54720Sstevel@tonic-gate user_pref->pathname);
54730Sstevel@tonic-gate
54740Sstevel@tonic-gate user_conf = user_pref->cfg_index;
54750Sstevel@tonic-gate
54760Sstevel@tonic-gate if (user_pref->driver) {
54770Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex);
54780Sstevel@tonic-gate child_ud->usb_preferred_driver = user_pref->driver;
54790Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex);
54800Sstevel@tonic-gate }
54810Sstevel@tonic-gate } else {
54820Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
54830Sstevel@tonic-gate "hubd_select_device_configuration: No match found");
54840Sstevel@tonic-gate
54850Sstevel@tonic-gate /* select default configuration for this device */
54860Sstevel@tonic-gate user_conf = USBA_DEV_CONFIG_INDEX_UNDEFINED;
54870Sstevel@tonic-gate }
54880Sstevel@tonic-gate kmem_free(pathname, pathlen);
54890Sstevel@tonic-gate
54900Sstevel@tonic-gate /* if the device has just one configuration, set default value */
54910Sstevel@tonic-gate if (usbdev_ptr->bNumConfigurations == 1) {
54920Sstevel@tonic-gate user_conf = USB_DEV_DEFAULT_CONFIG_INDEX;
54930Sstevel@tonic-gate }
54940Sstevel@tonic-gate
54950Sstevel@tonic-gate return (user_conf);
54960Sstevel@tonic-gate }
54970Sstevel@tonic-gate
54980Sstevel@tonic-gate
54990Sstevel@tonic-gate /*
55000Sstevel@tonic-gate * Retrieves config cloud for this configuration
55010Sstevel@tonic-gate */
55020Sstevel@tonic-gate int
hubd_get_this_config_cloud(hubd_t * hubd,dev_info_t * dip,usba_device_t * child_ud,uint16_t conf_index)55030Sstevel@tonic-gate hubd_get_this_config_cloud(hubd_t *hubd, dev_info_t *dip,
55040Sstevel@tonic-gate usba_device_t *child_ud, uint16_t conf_index)
55050Sstevel@tonic-gate {
55060Sstevel@tonic-gate usb_cfg_descr_t *confdescr;
55070Sstevel@tonic-gate mblk_t *pdata = NULL;
55080Sstevel@tonic-gate int rval;
55090Sstevel@tonic-gate size_t size;
55100Sstevel@tonic-gate char *tmpbuf;
55110Sstevel@tonic-gate usb_cr_t completion_reason;
55120Sstevel@tonic-gate usb_cb_flags_t cb_flags;
55130Sstevel@tonic-gate usb_pipe_handle_t def_ph;
55140Sstevel@tonic-gate
55150Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
55160Sstevel@tonic-gate "hubd_get_this_config_cloud: conf_index=%d", conf_index);
55170Sstevel@tonic-gate
55180Sstevel@tonic-gate
55190Sstevel@tonic-gate /* alloc temporary space for config descriptor */
55200Sstevel@tonic-gate confdescr = (usb_cfg_descr_t *)kmem_zalloc(USB_CFG_DESCR_SIZE,
55210Sstevel@tonic-gate KM_SLEEP);
55220Sstevel@tonic-gate
55230Sstevel@tonic-gate /* alloc temporary space for string descriptor */
55240Sstevel@tonic-gate tmpbuf = kmem_zalloc(USB_MAXSTRINGLEN, KM_SLEEP);
55250Sstevel@tonic-gate
55260Sstevel@tonic-gate def_ph = usba_get_dflt_pipe_handle(dip);
55270Sstevel@tonic-gate
55280Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
55290Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
55300Sstevel@tonic-gate USB_REQ_GET_DESCR,
55310Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_CFG | conf_index,
55320Sstevel@tonic-gate 0,
55330Sstevel@tonic-gate USB_CFG_DESCR_SIZE,
55340Sstevel@tonic-gate &pdata,
55350Sstevel@tonic-gate 0,
55360Sstevel@tonic-gate &completion_reason,
55370Sstevel@tonic-gate &cb_flags,
55380Sstevel@tonic-gate 0)) == USB_SUCCESS) {
55390Sstevel@tonic-gate
55400Sstevel@tonic-gate /* this must be true since we didn't allow data underruns */
55417492SZhigang.Lu@Sun.COM if (MBLKL(pdata) != USB_CFG_DESCR_SIZE) {
55421001Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
55431001Ssl147100 "device returned incorrect configuration "
55441001Ssl147100 "descriptor size.");
55451001Ssl147100
55461001Ssl147100 rval = USB_FAILURE;
55471001Ssl147100 goto done;
55481001Ssl147100 }
55490Sstevel@tonic-gate
55500Sstevel@tonic-gate /*
55510Sstevel@tonic-gate * Parse the configuration descriptor
55520Sstevel@tonic-gate */
55530Sstevel@tonic-gate size = usb_parse_cfg_descr(pdata->b_rptr,
55547492SZhigang.Lu@Sun.COM MBLKL(pdata), confdescr,
55550Sstevel@tonic-gate USB_CFG_DESCR_SIZE);
55560Sstevel@tonic-gate
55570Sstevel@tonic-gate /* if parse cfg descr error, it should return failure */
55580Sstevel@tonic-gate if (size == USB_PARSE_ERROR) {
55590Sstevel@tonic-gate
55600Sstevel@tonic-gate if (pdata->b_rptr[1] != USB_DESCR_TYPE_CFG) {
5561978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
55620Sstevel@tonic-gate hubd->h_log_handle,
55630Sstevel@tonic-gate "device returned incorrect "
55640Sstevel@tonic-gate "configuration descriptor type.");
55650Sstevel@tonic-gate }
55660Sstevel@tonic-gate rval = USB_FAILURE;
55670Sstevel@tonic-gate goto done;
55680Sstevel@tonic-gate }
55690Sstevel@tonic-gate
55700Sstevel@tonic-gate if (confdescr->wTotalLength < USB_CFG_DESCR_SIZE) {
5571978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
55720Sstevel@tonic-gate hubd->h_log_handle,
55730Sstevel@tonic-gate "device returned incorrect "
55740Sstevel@tonic-gate "configuration descriptor size.");
55750Sstevel@tonic-gate
55760Sstevel@tonic-gate rval = USB_FAILURE;
55770Sstevel@tonic-gate goto done;
55780Sstevel@tonic-gate }
55790Sstevel@tonic-gate
55800Sstevel@tonic-gate freemsg(pdata);
55810Sstevel@tonic-gate pdata = NULL;
55820Sstevel@tonic-gate
55830Sstevel@tonic-gate /* Now fetch the complete config cloud */
55840Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(dip, def_ph,
55850Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
55860Sstevel@tonic-gate USB_REQ_GET_DESCR,
55870Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_CFG | conf_index,
55880Sstevel@tonic-gate 0,
55890Sstevel@tonic-gate confdescr->wTotalLength,
55900Sstevel@tonic-gate &pdata,
55910Sstevel@tonic-gate 0,
55920Sstevel@tonic-gate &completion_reason,
55930Sstevel@tonic-gate &cb_flags,
55940Sstevel@tonic-gate 0)) == USB_SUCCESS) {
55950Sstevel@tonic-gate
55967492SZhigang.Lu@Sun.COM if (MBLKL(pdata) !=
55970Sstevel@tonic-gate confdescr->wTotalLength) {
55980Sstevel@tonic-gate
5599978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
56000Sstevel@tonic-gate hubd->h_log_handle,
56010Sstevel@tonic-gate "device returned incorrect "
56020Sstevel@tonic-gate "configuration descriptor.");
56030Sstevel@tonic-gate
56040Sstevel@tonic-gate rval = USB_FAILURE;
56050Sstevel@tonic-gate goto done;
56060Sstevel@tonic-gate }
56070Sstevel@tonic-gate
56080Sstevel@tonic-gate /*
56090Sstevel@tonic-gate * copy config descriptor into usba_device
56100Sstevel@tonic-gate */
56110Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex);
56120Sstevel@tonic-gate child_ud->usb_cfg_array[conf_index] =
56130Sstevel@tonic-gate kmem_alloc(confdescr->wTotalLength, KM_SLEEP);
56140Sstevel@tonic-gate child_ud->usb_cfg_array_len[conf_index] =
56154763Slg150142 confdescr->wTotalLength;
56160Sstevel@tonic-gate bcopy((caddr_t)pdata->b_rptr,
56170Sstevel@tonic-gate (caddr_t)child_ud->usb_cfg_array[conf_index],
56180Sstevel@tonic-gate confdescr->wTotalLength);
56190Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex);
56200Sstevel@tonic-gate
56210Sstevel@tonic-gate /*
56220Sstevel@tonic-gate * retrieve string descriptor describing this
56230Sstevel@tonic-gate * configuration
56240Sstevel@tonic-gate */
56250Sstevel@tonic-gate if (confdescr->iConfiguration) {
56260Sstevel@tonic-gate
56270Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
56280Sstevel@tonic-gate hubd->h_log_handle,
56290Sstevel@tonic-gate "Get conf str descr for config_index=%d",
56300Sstevel@tonic-gate conf_index);
56310Sstevel@tonic-gate
56320Sstevel@tonic-gate /*
56330Sstevel@tonic-gate * Now fetch the string descriptor describing
56340Sstevel@tonic-gate * this configuration
56350Sstevel@tonic-gate */
56360Sstevel@tonic-gate if ((rval = usb_get_string_descr(dip,
56370Sstevel@tonic-gate USB_LANG_ID, confdescr->iConfiguration,
56380Sstevel@tonic-gate tmpbuf, USB_MAXSTRINGLEN)) ==
56390Sstevel@tonic-gate USB_SUCCESS) {
56400Sstevel@tonic-gate size = strlen(tmpbuf);
56410Sstevel@tonic-gate if (size > 0) {
56420Sstevel@tonic-gate child_ud->usb_cfg_str_descr
56430Sstevel@tonic-gate [conf_index] = (char *)
56440Sstevel@tonic-gate kmem_zalloc(size + 1,
56450Sstevel@tonic-gate KM_SLEEP);
56460Sstevel@tonic-gate (void) strcpy(
56470Sstevel@tonic-gate child_ud->usb_cfg_str_descr
56480Sstevel@tonic-gate [conf_index], tmpbuf);
56490Sstevel@tonic-gate }
56500Sstevel@tonic-gate } else {
56510Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
56520Sstevel@tonic-gate hubd->h_log_handle,
56530Sstevel@tonic-gate "hubd_get_this_config_cloud: "
56540Sstevel@tonic-gate "getting config string (%d) "
56550Sstevel@tonic-gate "failed",
56560Sstevel@tonic-gate confdescr->iConfiguration);
56570Sstevel@tonic-gate
56580Sstevel@tonic-gate /* ignore this error */
56590Sstevel@tonic-gate rval = USB_SUCCESS;
56600Sstevel@tonic-gate }
56610Sstevel@tonic-gate }
56620Sstevel@tonic-gate }
56630Sstevel@tonic-gate }
56640Sstevel@tonic-gate
56650Sstevel@tonic-gate done:
56660Sstevel@tonic-gate if (rval != USB_SUCCESS) {
56670Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
56680Sstevel@tonic-gate "hubd_get_this_config_cloud: "
56690Sstevel@tonic-gate "error in retrieving config descriptor for "
56700Sstevel@tonic-gate "config index=%d rval=%d cr=%d",
56710Sstevel@tonic-gate conf_index, rval, completion_reason);
56720Sstevel@tonic-gate }
56730Sstevel@tonic-gate
56740Sstevel@tonic-gate if (pdata) {
56750Sstevel@tonic-gate freemsg(pdata);
56760Sstevel@tonic-gate pdata = NULL;
56770Sstevel@tonic-gate }
56780Sstevel@tonic-gate
56790Sstevel@tonic-gate kmem_free(confdescr, USB_CFG_DESCR_SIZE);
56800Sstevel@tonic-gate kmem_free(tmpbuf, USB_MAXSTRINGLEN);
56810Sstevel@tonic-gate
56820Sstevel@tonic-gate return (rval);
56830Sstevel@tonic-gate }
56840Sstevel@tonic-gate
56850Sstevel@tonic-gate
56860Sstevel@tonic-gate /*
56870Sstevel@tonic-gate * Retrieves the entire config cloud for all configurations of the device
56880Sstevel@tonic-gate */
56890Sstevel@tonic-gate int
hubd_get_all_device_config_cloud(hubd_t * hubd,dev_info_t * dip,usba_device_t * child_ud)56900Sstevel@tonic-gate hubd_get_all_device_config_cloud(hubd_t *hubd, dev_info_t *dip,
56910Sstevel@tonic-gate usba_device_t *child_ud)
56920Sstevel@tonic-gate {
56930Sstevel@tonic-gate int rval = USB_SUCCESS;
56940Sstevel@tonic-gate int ncfgs;
56950Sstevel@tonic-gate uint16_t size;
56960Sstevel@tonic-gate uint16_t conf_index;
56970Sstevel@tonic-gate uchar_t **cfg_array;
56980Sstevel@tonic-gate uint16_t *cfg_array_len;
56990Sstevel@tonic-gate char **str_descr;
57000Sstevel@tonic-gate
57010Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
57020Sstevel@tonic-gate "hubd_get_all_device_config_cloud: Start");
57030Sstevel@tonic-gate
57040Sstevel@tonic-gate /* alloc pointer array for conf. descriptors */
57050Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex);
57060Sstevel@tonic-gate ncfgs = child_ud->usb_n_cfgs;
57070Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex);
57080Sstevel@tonic-gate
57090Sstevel@tonic-gate size = sizeof (uchar_t *) * ncfgs;
57100Sstevel@tonic-gate cfg_array = kmem_zalloc(size, KM_SLEEP);
57110Sstevel@tonic-gate cfg_array_len = kmem_zalloc(ncfgs * sizeof (uint16_t), KM_SLEEP);
57120Sstevel@tonic-gate str_descr = kmem_zalloc(size, KM_SLEEP);
57130Sstevel@tonic-gate
57140Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex);
57150Sstevel@tonic-gate child_ud->usb_cfg_array = cfg_array;
57160Sstevel@tonic-gate child_ud->usb_cfg_array_len = cfg_array_len;
57170Sstevel@tonic-gate child_ud->usb_cfg_array_length = size;
57180Sstevel@tonic-gate child_ud->usb_cfg_array_len_length = ncfgs * sizeof (uint16_t);
57190Sstevel@tonic-gate child_ud->usb_cfg_str_descr = str_descr;
57200Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex);
57210Sstevel@tonic-gate
57220Sstevel@tonic-gate /* Get configuration descriptor for each configuration */
57230Sstevel@tonic-gate for (conf_index = 0; (conf_index < ncfgs) &&
57240Sstevel@tonic-gate (rval == USB_SUCCESS); conf_index++) {
57250Sstevel@tonic-gate
57260Sstevel@tonic-gate rval = hubd_get_this_config_cloud(hubd, dip, child_ud,
57270Sstevel@tonic-gate conf_index);
57280Sstevel@tonic-gate }
57290Sstevel@tonic-gate
57300Sstevel@tonic-gate return (rval);
57310Sstevel@tonic-gate }
57320Sstevel@tonic-gate
57330Sstevel@tonic-gate
57340Sstevel@tonic-gate /*
57350Sstevel@tonic-gate * hubd_ready_device:
57360Sstevel@tonic-gate * Update the usba_device structure
57370Sstevel@tonic-gate * Set the given configuration
57380Sstevel@tonic-gate * Prepares the device node for driver to online. If an existing
57390Sstevel@tonic-gate * OBP node is found, it will switch to the OBP node.
57400Sstevel@tonic-gate */
57419430SRaymond.Chen@Sun.COM dev_info_t *
hubd_ready_device(hubd_t * hubd,dev_info_t * child_dip,usba_device_t * child_ud,uint_t config_index)57420Sstevel@tonic-gate hubd_ready_device(hubd_t *hubd, dev_info_t *child_dip, usba_device_t *child_ud,
57431001Ssl147100 uint_t config_index)
57440Sstevel@tonic-gate {
57450Sstevel@tonic-gate usb_cr_t completion_reason;
57460Sstevel@tonic-gate usb_cb_flags_t cb_flags;
57470Sstevel@tonic-gate size_t size;
57480Sstevel@tonic-gate usb_cfg_descr_t config_descriptor;
57490Sstevel@tonic-gate usb_pipe_handle_t def_ph;
57500Sstevel@tonic-gate usba_pipe_handle_data_t *ph;
57510Sstevel@tonic-gate
57520Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
57536898Sfb209375 "hubd_ready_device: dip=0x%p, user_conf_index=%d",
57546898Sfb209375 (void *)child_dip, config_index);
57550Sstevel@tonic-gate
57560Sstevel@tonic-gate size = usb_parse_cfg_descr(
57570Sstevel@tonic-gate child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE,
57580Sstevel@tonic-gate &config_descriptor, USB_CFG_DESCR_SIZE);
57590Sstevel@tonic-gate ASSERT(size == USB_CFG_DESCR_SIZE);
57600Sstevel@tonic-gate
57610Sstevel@tonic-gate def_ph = usba_get_dflt_pipe_handle(child_dip);
57620Sstevel@tonic-gate
57630Sstevel@tonic-gate /* Set the configuration */
57640Sstevel@tonic-gate (void) usb_pipe_sync_ctrl_xfer(child_dip, def_ph,
57650Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV,
57660Sstevel@tonic-gate USB_REQ_SET_CFG, /* bRequest */
57670Sstevel@tonic-gate config_descriptor.bConfigurationValue, /* wValue */
57680Sstevel@tonic-gate 0, /* wIndex */
57690Sstevel@tonic-gate 0, /* wLength */
57700Sstevel@tonic-gate NULL,
57710Sstevel@tonic-gate 0,
57720Sstevel@tonic-gate &completion_reason,
57730Sstevel@tonic-gate &cb_flags,
57740Sstevel@tonic-gate 0);
57750Sstevel@tonic-gate
57760Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex);
57770Sstevel@tonic-gate child_ud->usb_active_cfg_ndx = config_index;
57780Sstevel@tonic-gate child_ud->usb_cfg = child_ud->usb_cfg_array[config_index];
57790Sstevel@tonic-gate child_ud->usb_cfg_length = config_descriptor.wTotalLength;
57800Sstevel@tonic-gate child_ud->usb_cfg_value = config_descriptor.bConfigurationValue;
57810Sstevel@tonic-gate child_ud->usb_n_ifs = config_descriptor.bNumInterfaces;
57820Sstevel@tonic-gate child_ud->usb_dip = child_dip;
57830Sstevel@tonic-gate
57840Sstevel@tonic-gate child_ud->usb_client_flags = kmem_zalloc(
57854763Slg150142 child_ud->usb_n_ifs * USBA_CLIENT_FLAG_SIZE, KM_SLEEP);
57860Sstevel@tonic-gate
57870Sstevel@tonic-gate child_ud->usb_client_attach_list = kmem_zalloc(
57884763Slg150142 child_ud->usb_n_ifs *
57894763Slg150142 sizeof (*child_ud->usb_client_attach_list), KM_SLEEP);
57900Sstevel@tonic-gate
57910Sstevel@tonic-gate child_ud->usb_client_ev_cb_list = kmem_zalloc(
57924763Slg150142 child_ud->usb_n_ifs *
57934763Slg150142 sizeof (*child_ud->usb_client_ev_cb_list), KM_SLEEP);
57940Sstevel@tonic-gate
57950Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex);
57960Sstevel@tonic-gate
57970Sstevel@tonic-gate /* ready the device node */
57980Sstevel@tonic-gate child_dip = usba_ready_device_node(child_dip);
57990Sstevel@tonic-gate
58000Sstevel@tonic-gate /* set owner of default pipe to child dip */
58010Sstevel@tonic-gate ph = usba_get_ph_data(def_ph);
58020Sstevel@tonic-gate mutex_enter(&ph->p_mutex);
58030Sstevel@tonic-gate mutex_enter(&ph->p_ph_impl->usba_ph_mutex);
58040Sstevel@tonic-gate ph->p_ph_impl->usba_ph_dip = ph->p_dip = child_dip;
58050Sstevel@tonic-gate mutex_exit(&ph->p_ph_impl->usba_ph_mutex);
58060Sstevel@tonic-gate mutex_exit(&ph->p_mutex);
58070Sstevel@tonic-gate
58080Sstevel@tonic-gate return (child_dip);
58090Sstevel@tonic-gate }
58100Sstevel@tonic-gate
58110Sstevel@tonic-gate
58120Sstevel@tonic-gate /*
58130Sstevel@tonic-gate * hubd_create_child
58140Sstevel@tonic-gate * - create child dip
58150Sstevel@tonic-gate * - open default pipe
58160Sstevel@tonic-gate * - get device descriptor
58170Sstevel@tonic-gate * - set the address
58180Sstevel@tonic-gate * - get device string descriptors
58190Sstevel@tonic-gate * - get the entire config cloud (all configurations) of the device
58200Sstevel@tonic-gate * - set user preferred configuration
58210Sstevel@tonic-gate * - close default pipe
58220Sstevel@tonic-gate * - load appropriate driver(s)
58230Sstevel@tonic-gate */
58240Sstevel@tonic-gate static int
hubd_create_child(dev_info_t * dip,hubd_t * hubd,usba_device_t * hubd_ud,usb_port_status_t port_status,usb_port_t port,int iteration)58250Sstevel@tonic-gate hubd_create_child(dev_info_t *dip,
58260Sstevel@tonic-gate hubd_t *hubd,
58270Sstevel@tonic-gate usba_device_t *hubd_ud,
58280Sstevel@tonic-gate usb_port_status_t port_status,
58290Sstevel@tonic-gate usb_port_t port,
58300Sstevel@tonic-gate int iteration)
58310Sstevel@tonic-gate {
58320Sstevel@tonic-gate dev_info_t *child_dip = NULL;
58330Sstevel@tonic-gate usb_dev_descr_t usb_dev_descr;
58340Sstevel@tonic-gate int rval;
58350Sstevel@tonic-gate usba_device_t *child_ud = NULL;
58360Sstevel@tonic-gate usba_device_t *parent_ud = NULL;
58370Sstevel@tonic-gate usb_pipe_handle_t ph = NULL; /* default pipe handle */
58380Sstevel@tonic-gate mblk_t *pdata = NULL;
58390Sstevel@tonic-gate usb_cr_t completion_reason;
58401001Ssl147100 int user_conf_index;
58411001Ssl147100 uint_t config_index;
58420Sstevel@tonic-gate usb_cb_flags_t cb_flags;
58430Sstevel@tonic-gate uchar_t address = 0;
58440Sstevel@tonic-gate uint16_t length;
58450Sstevel@tonic-gate size_t size;
58460Sstevel@tonic-gate usb_addr_t parent_usb_addr;
58470Sstevel@tonic-gate usb_port_t parent_usb_port;
58480Sstevel@tonic-gate usba_device_t *parent_usba_dev;
58490Sstevel@tonic-gate usb_port_status_t parent_port_status;
58500Sstevel@tonic-gate
58510Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
58520Sstevel@tonic-gate "hubd_create_child: port=%d", port);
58530Sstevel@tonic-gate
58540Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
58550Sstevel@tonic-gate ASSERT(hubd->h_usba_devices[port] == NULL);
58560Sstevel@tonic-gate
58570Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
58580Sstevel@tonic-gate
58590Sstevel@tonic-gate /*
58600Sstevel@tonic-gate * create a dip which can be used to open the pipe. we set
58610Sstevel@tonic-gate * the name after getting the descriptors from the device
58620Sstevel@tonic-gate */
58630Sstevel@tonic-gate rval = usba_create_child_devi(dip,
58644763Slg150142 "device", /* driver name */
58654763Slg150142 hubd_ud->usb_hcdi_ops, /* usba_hcdi ops */
58664763Slg150142 hubd_ud->usb_root_hub_dip,
58674763Slg150142 port_status, /* low speed device */
58684763Slg150142 child_ud,
58694763Slg150142 &child_dip);
58700Sstevel@tonic-gate
58710Sstevel@tonic-gate if (rval != USB_SUCCESS) {
58720Sstevel@tonic-gate
5873978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
58740Sstevel@tonic-gate "usb_create_child_devi failed (%d)", rval);
58750Sstevel@tonic-gate
58760Sstevel@tonic-gate goto fail_cleanup;
58770Sstevel@tonic-gate }
58780Sstevel@tonic-gate
58790Sstevel@tonic-gate child_ud = usba_get_usba_device(child_dip);
58800Sstevel@tonic-gate ASSERT(child_ud != NULL);
58810Sstevel@tonic-gate
58820Sstevel@tonic-gate parent_ud = hubd->h_usba_device;
58830Sstevel@tonic-gate mutex_enter(&parent_ud->usb_mutex);
58840Sstevel@tonic-gate parent_port_status = parent_ud->usb_port_status;
58850Sstevel@tonic-gate
58860Sstevel@tonic-gate /*
58870Sstevel@tonic-gate * To support split transactions, update address and port
58880Sstevel@tonic-gate * of high speed hub to which given device is connected.
58890Sstevel@tonic-gate */
58900Sstevel@tonic-gate if (parent_port_status == USBA_HIGH_SPEED_DEV) {
58910Sstevel@tonic-gate parent_usba_dev = parent_ud;
58920Sstevel@tonic-gate parent_usb_addr = parent_ud->usb_addr;
58930Sstevel@tonic-gate parent_usb_port = port;
58940Sstevel@tonic-gate } else {
58950Sstevel@tonic-gate parent_usba_dev = parent_ud->usb_hs_hub_usba_dev;
58960Sstevel@tonic-gate parent_usb_addr = parent_ud->usb_hs_hub_addr;
58970Sstevel@tonic-gate parent_usb_port = parent_ud->usb_hs_hub_port;
58980Sstevel@tonic-gate }
58990Sstevel@tonic-gate mutex_exit(&parent_ud->usb_mutex);
59000Sstevel@tonic-gate
59010Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex);
59020Sstevel@tonic-gate address = child_ud->usb_addr;
59030Sstevel@tonic-gate child_ud->usb_addr = 0;
59040Sstevel@tonic-gate child_ud->usb_dev_descr = kmem_alloc(sizeof (usb_dev_descr_t),
59050Sstevel@tonic-gate KM_SLEEP);
59060Sstevel@tonic-gate bzero(&usb_dev_descr, sizeof (usb_dev_descr_t));
59070Sstevel@tonic-gate usb_dev_descr.bMaxPacketSize0 =
59080Sstevel@tonic-gate (port_status == USBA_LOW_SPEED_DEV) ? 8 : 64;
59090Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
59100Sstevel@tonic-gate sizeof (usb_dev_descr_t));
59110Sstevel@tonic-gate child_ud->usb_port = port;
59120Sstevel@tonic-gate child_ud->usb_hs_hub_usba_dev = parent_usba_dev;
59130Sstevel@tonic-gate child_ud->usb_hs_hub_addr = parent_usb_addr;
59140Sstevel@tonic-gate child_ud->usb_hs_hub_port = parent_usb_port;
59150Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex);
59160Sstevel@tonic-gate
59170Sstevel@tonic-gate /* Open the default pipe */
59180Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL,
59190Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) {
5920978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
5921978Sfrits "usb_pipe_open failed (%d)", rval);
59220Sstevel@tonic-gate
59230Sstevel@tonic-gate goto fail_cleanup;
59240Sstevel@tonic-gate }
59250Sstevel@tonic-gate
59260Sstevel@tonic-gate /*
59270Sstevel@tonic-gate * get device descriptor
59280Sstevel@tonic-gate */
59290Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59300Sstevel@tonic-gate "hubd_create_child: get device descriptor: 64 bytes");
59310Sstevel@tonic-gate
59320Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
59330Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
59340Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */
59350Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */
59360Sstevel@tonic-gate 0, /* wIndex */
59370Sstevel@tonic-gate 64, /* wLength */
59380Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK,
59390Sstevel@tonic-gate &completion_reason, &cb_flags, 0);
59400Sstevel@tonic-gate
59410Sstevel@tonic-gate if ((rval != USB_SUCCESS) &&
59420Sstevel@tonic-gate (!((completion_reason == USB_CR_DATA_OVERRUN) && pdata))) {
59430Sstevel@tonic-gate
59440Sstevel@tonic-gate /*
59450Sstevel@tonic-gate * rval != USB_SUCCESS AND
59460Sstevel@tonic-gate * completion_reason != USB_CR_DATA_OVERRUN
59470Sstevel@tonic-gate * pdata could be != NULL.
59480Sstevel@tonic-gate * Free pdata now to prevent memory leak.
59490Sstevel@tonic-gate */
59500Sstevel@tonic-gate freemsg(pdata);
59510Sstevel@tonic-gate pdata = NULL;
59520Sstevel@tonic-gate
59530Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59540Sstevel@tonic-gate "hubd_create_child: get device descriptor: 8 bytes");
59550Sstevel@tonic-gate
59560Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
59570Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
59580Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */
59590Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */
59600Sstevel@tonic-gate 0, /* wIndex */
59610Sstevel@tonic-gate 8, /* wLength */
59620Sstevel@tonic-gate &pdata, USB_ATTRS_NONE,
59630Sstevel@tonic-gate &completion_reason, &cb_flags, 0);
59640Sstevel@tonic-gate
59650Sstevel@tonic-gate if (rval != USB_SUCCESS) {
5966978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59670Sstevel@tonic-gate "getting device descriptor failed (%s 0x%x %d)",
59680Sstevel@tonic-gate usb_str_cr(completion_reason), cb_flags, rval);
59690Sstevel@tonic-gate goto fail_cleanup;
59700Sstevel@tonic-gate }
59710Sstevel@tonic-gate } else {
59720Sstevel@tonic-gate ASSERT(completion_reason == USB_CR_OK);
59730Sstevel@tonic-gate }
59740Sstevel@tonic-gate
59750Sstevel@tonic-gate ASSERT(pdata != NULL);
59760Sstevel@tonic-gate
59770Sstevel@tonic-gate size = usb_parse_dev_descr(
59784763Slg150142 pdata->b_rptr,
59797492SZhigang.Lu@Sun.COM MBLKL(pdata),
59804763Slg150142 &usb_dev_descr,
59814763Slg150142 sizeof (usb_dev_descr_t));
59820Sstevel@tonic-gate
59830Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59840Sstevel@tonic-gate "parsing device descriptor returned %lu", size);
59850Sstevel@tonic-gate
59860Sstevel@tonic-gate length = *(pdata->b_rptr);
59870Sstevel@tonic-gate freemsg(pdata);
59880Sstevel@tonic-gate pdata = NULL;
59890Sstevel@tonic-gate if (size < 8) {
5990978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59910Sstevel@tonic-gate "get device descriptor returned %lu bytes", size);
59920Sstevel@tonic-gate
59930Sstevel@tonic-gate goto fail_cleanup;
59940Sstevel@tonic-gate }
59950Sstevel@tonic-gate
59960Sstevel@tonic-gate if (length < 8) {
5997978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
59980Sstevel@tonic-gate "fail enumeration: bLength=%d", length);
59990Sstevel@tonic-gate
60000Sstevel@tonic-gate goto fail_cleanup;
60010Sstevel@tonic-gate }
60020Sstevel@tonic-gate
60030Sstevel@tonic-gate /* Set the address of the device */
60040Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
60050Sstevel@tonic-gate USB_DEV_REQ_HOST_TO_DEV,
60060Sstevel@tonic-gate USB_REQ_SET_ADDRESS, /* bRequest */
60070Sstevel@tonic-gate address, /* wValue */
60080Sstevel@tonic-gate 0, /* wIndex */
60090Sstevel@tonic-gate 0, /* wLength */
60100Sstevel@tonic-gate NULL, 0,
60110Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
60120Sstevel@tonic-gate char buffer[64];
6013978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
60140Sstevel@tonic-gate "setting address failed (cr=%s cb_flags=%s rval=%d)",
60150Sstevel@tonic-gate usb_str_cr(completion_reason),
60160Sstevel@tonic-gate usb_str_cb_flags(cb_flags, buffer, sizeof (buffer)),
60170Sstevel@tonic-gate rval);
60180Sstevel@tonic-gate
60190Sstevel@tonic-gate goto fail_cleanup;
60200Sstevel@tonic-gate }
60210Sstevel@tonic-gate
60220Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
60230Sstevel@tonic-gate "set address 0x%x done", address);
60240Sstevel@tonic-gate
60250Sstevel@tonic-gate /* now close the pipe for addr 0 */
60260Sstevel@tonic-gate usb_pipe_close(child_dip, ph,
60270Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
60280Sstevel@tonic-gate
60290Sstevel@tonic-gate /*
60300Sstevel@tonic-gate * This delay is important for the CATC hub to enumerate
60310Sstevel@tonic-gate * But, avoid delay in the first iteration
60320Sstevel@tonic-gate */
60330Sstevel@tonic-gate if (iteration) {
60340Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay/100));
60350Sstevel@tonic-gate }
60360Sstevel@tonic-gate
60370Sstevel@tonic-gate /* assign the address in the usba_device structure */
60380Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex);
60390Sstevel@tonic-gate child_ud->usb_addr = address;
60400Sstevel@tonic-gate child_ud->usb_no_cpr = 0;
60410Sstevel@tonic-gate child_ud->usb_port_status = port_status;
60420Sstevel@tonic-gate /* save this device descriptor */
60430Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
60444763Slg150142 sizeof (usb_dev_descr_t));
60450Sstevel@tonic-gate child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
60460Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex);
60470Sstevel@tonic-gate
60480Sstevel@tonic-gate /* re-open the pipe for the device with the new address */
60490Sstevel@tonic-gate if ((rval = usb_pipe_open(child_dip, NULL, NULL,
60500Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &ph)) != USB_SUCCESS) {
6051978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
60520Sstevel@tonic-gate "usb_pipe_open failed (%d)", rval);
60530Sstevel@tonic-gate
60540Sstevel@tonic-gate goto fail_cleanup;
60550Sstevel@tonic-gate }
60560Sstevel@tonic-gate
60570Sstevel@tonic-gate /*
60580Sstevel@tonic-gate * Get full device descriptor only if we have not received full
60590Sstevel@tonic-gate * device descriptor earlier.
60600Sstevel@tonic-gate */
60610Sstevel@tonic-gate if (size < length) {
60620Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
60630Sstevel@tonic-gate "hubd_create_child: get full device descriptor: "
60640Sstevel@tonic-gate "%d bytes", length);
60650Sstevel@tonic-gate
60660Sstevel@tonic-gate if ((rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
60670Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
60680Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */
60690Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */
60700Sstevel@tonic-gate 0, /* wIndex */
60710Sstevel@tonic-gate length, /* wLength */
60720Sstevel@tonic-gate &pdata, 0,
60730Sstevel@tonic-gate &completion_reason, &cb_flags, 0)) != USB_SUCCESS) {
60740Sstevel@tonic-gate freemsg(pdata);
60750Sstevel@tonic-gate pdata = NULL;
60760Sstevel@tonic-gate
60770Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG,
60780Sstevel@tonic-gate hubd->h_log_handle,
60790Sstevel@tonic-gate "hubd_create_child: get full device descriptor: "
60800Sstevel@tonic-gate "64 bytes");
60810Sstevel@tonic-gate
60820Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
60830Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST |
60840Sstevel@tonic-gate USB_DEV_REQ_TYPE_STANDARD,
60850Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */
60860Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV, /* wValue */
60870Sstevel@tonic-gate 0, /* wIndex */
60880Sstevel@tonic-gate 64, /* wLength */
60890Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK,
60900Sstevel@tonic-gate &completion_reason, &cb_flags, 0);
60910Sstevel@tonic-gate
60920Sstevel@tonic-gate /* we have to trust the data now */
60930Sstevel@tonic-gate if (pdata) {
60940Sstevel@tonic-gate int len = *(pdata->b_rptr);
60950Sstevel@tonic-gate
60967492SZhigang.Lu@Sun.COM length = MBLKL(pdata);
60970Sstevel@tonic-gate if (length < len) {
60980Sstevel@tonic-gate
60990Sstevel@tonic-gate goto fail_cleanup;
61000Sstevel@tonic-gate }
61010Sstevel@tonic-gate } else if (rval != USB_SUCCESS) {
6102978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
61030Sstevel@tonic-gate hubd->h_log_handle,
61040Sstevel@tonic-gate "getting device descriptor failed "
61050Sstevel@tonic-gate "(%d 0x%x %d)",
61064763Slg150142 completion_reason, cb_flags, rval);
61070Sstevel@tonic-gate
61080Sstevel@tonic-gate goto fail_cleanup;
61090Sstevel@tonic-gate }
61100Sstevel@tonic-gate }
61110Sstevel@tonic-gate
61120Sstevel@tonic-gate size = usb_parse_dev_descr(
61134763Slg150142 pdata->b_rptr,
61147492SZhigang.Lu@Sun.COM MBLKL(pdata),
61154763Slg150142 &usb_dev_descr,
61164763Slg150142 sizeof (usb_dev_descr_t));
61170Sstevel@tonic-gate
61180Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
61190Sstevel@tonic-gate "parsing device descriptor returned %lu", size);
61200Sstevel@tonic-gate
61210Sstevel@tonic-gate /*
61220Sstevel@tonic-gate * For now, free the data
61230Sstevel@tonic-gate * eventually, each configuration may need to be looked at
61240Sstevel@tonic-gate */
61250Sstevel@tonic-gate freemsg(pdata);
61260Sstevel@tonic-gate pdata = NULL;
61270Sstevel@tonic-gate
61280Sstevel@tonic-gate if (size != USB_DEV_DESCR_SIZE) {
6129978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
61300Sstevel@tonic-gate "fail enumeration: descriptor size=%lu "
61310Sstevel@tonic-gate "expected size=%u", size, USB_DEV_DESCR_SIZE);
61320Sstevel@tonic-gate
61330Sstevel@tonic-gate goto fail_cleanup;
61340Sstevel@tonic-gate }
61350Sstevel@tonic-gate
61360Sstevel@tonic-gate /*
61370Sstevel@tonic-gate * save the device descriptor in usba_device since it is needed
61380Sstevel@tonic-gate * later on again
61390Sstevel@tonic-gate */
61400Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex);
61410Sstevel@tonic-gate bcopy(&usb_dev_descr, child_ud->usb_dev_descr,
61424763Slg150142 sizeof (usb_dev_descr_t));
61430Sstevel@tonic-gate child_ud->usb_n_cfgs = usb_dev_descr.bNumConfigurations;
61440Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex);
61450Sstevel@tonic-gate }
61460Sstevel@tonic-gate
61470Sstevel@tonic-gate if (usb_dev_descr.bNumConfigurations == 0) {
6148978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
61490Sstevel@tonic-gate "device descriptor:\n\t"
61500Sstevel@tonic-gate "l=0x%x type=0x%x USB=0x%x class=0x%x subclass=0x%x\n\t"
61510Sstevel@tonic-gate "protocol=0x%x maxpktsize=0x%x "
61520Sstevel@tonic-gate "Vid=0x%x Pid=0x%x rel=0x%x\n\t"
61530Sstevel@tonic-gate "Mfg=0x%x P=0x%x sn=0x%x #config=0x%x",
61540Sstevel@tonic-gate usb_dev_descr.bLength, usb_dev_descr.bDescriptorType,
61550Sstevel@tonic-gate usb_dev_descr.bcdUSB, usb_dev_descr.bDeviceClass,
61560Sstevel@tonic-gate usb_dev_descr.bDeviceSubClass,
61570Sstevel@tonic-gate usb_dev_descr.bDeviceProtocol,
61580Sstevel@tonic-gate usb_dev_descr.bMaxPacketSize0,
61590Sstevel@tonic-gate usb_dev_descr.idVendor,
61600Sstevel@tonic-gate usb_dev_descr.idProduct, usb_dev_descr.bcdDevice,
61610Sstevel@tonic-gate usb_dev_descr.iManufacturer, usb_dev_descr.iProduct,
61620Sstevel@tonic-gate usb_dev_descr.iSerialNumber,
61630Sstevel@tonic-gate usb_dev_descr.bNumConfigurations);
61640Sstevel@tonic-gate goto fail_cleanup;
61650Sstevel@tonic-gate }
61660Sstevel@tonic-gate
61670Sstevel@tonic-gate
61680Sstevel@tonic-gate /* get the device string descriptor(s) */
61690Sstevel@tonic-gate usba_get_dev_string_descrs(child_dip, child_ud);
61700Sstevel@tonic-gate
61710Sstevel@tonic-gate /* retrieve config cloud for all configurations */
61720Sstevel@tonic-gate rval = hubd_get_all_device_config_cloud(hubd, child_dip, child_ud);
61730Sstevel@tonic-gate if (rval != USB_SUCCESS) {
6174978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
61750Sstevel@tonic-gate "failed to get configuration descriptor(s)");
61760Sstevel@tonic-gate
61770Sstevel@tonic-gate goto fail_cleanup;
61780Sstevel@tonic-gate }
61790Sstevel@tonic-gate
61800Sstevel@tonic-gate /* get the preferred configuration for this device */
61810Sstevel@tonic-gate user_conf_index = hubd_select_device_configuration(hubd, port,
61820Sstevel@tonic-gate child_dip, child_ud);
61830Sstevel@tonic-gate
61840Sstevel@tonic-gate /* Check if the user selected configuration index is in range */
61851001Ssl147100 if ((user_conf_index >= usb_dev_descr.bNumConfigurations) ||
61861001Ssl147100 (user_conf_index < 0)) {
6187978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
61880Sstevel@tonic-gate "Configuration index for device idVendor=%d "
61890Sstevel@tonic-gate "idProduct=%d is=%d, and is out of range[0..%d]",
61900Sstevel@tonic-gate usb_dev_descr.idVendor, usb_dev_descr.idProduct,
61910Sstevel@tonic-gate user_conf_index, usb_dev_descr.bNumConfigurations - 1);
61920Sstevel@tonic-gate
61930Sstevel@tonic-gate /* treat this as user didn't specify configuration */
61940Sstevel@tonic-gate user_conf_index = USBA_DEV_CONFIG_INDEX_UNDEFINED;
61950Sstevel@tonic-gate }
61960Sstevel@tonic-gate
61970Sstevel@tonic-gate
61980Sstevel@tonic-gate /*
61990Sstevel@tonic-gate * Warn users of a performance hit if connecting a
62000Sstevel@tonic-gate * High Speed behind a 1.1 hub, which is behind a
62010Sstevel@tonic-gate * 2.0 port.
62020Sstevel@tonic-gate */
62030Sstevel@tonic-gate if ((parent_port_status != USBA_HIGH_SPEED_DEV) &&
62040Sstevel@tonic-gate !(usba_is_root_hub(parent_ud->usb_dip)) &&
62050Sstevel@tonic-gate (parent_usb_addr)) {
62060Sstevel@tonic-gate
62070Sstevel@tonic-gate /*
62080Sstevel@tonic-gate * Now that we know the root port is a high speed port
62090Sstevel@tonic-gate * and that the parent port is not a high speed port,
62100Sstevel@tonic-gate * let's find out if the device itself is a high speed
62110Sstevel@tonic-gate * device. If it is a high speed device,
62120Sstevel@tonic-gate * USB_DESCR_TYPE_SETUP_DEV_QLF should return a value,
62130Sstevel@tonic-gate * otherwise the command will fail.
62140Sstevel@tonic-gate */
62150Sstevel@tonic-gate rval = usb_pipe_sync_ctrl_xfer(child_dip, ph,
62160Sstevel@tonic-gate USB_DEV_REQ_DEV_TO_HOST | USB_DEV_REQ_TYPE_STANDARD,
62170Sstevel@tonic-gate USB_REQ_GET_DESCR, /* bRequest */
62180Sstevel@tonic-gate USB_DESCR_TYPE_SETUP_DEV_QLF, /* wValue */
62190Sstevel@tonic-gate 0, /* wIndex */
62200Sstevel@tonic-gate 10, /* wLength */
62210Sstevel@tonic-gate &pdata, USB_ATTRS_SHORT_XFER_OK,
62220Sstevel@tonic-gate &completion_reason, &cb_flags, 0);
62230Sstevel@tonic-gate
62240Sstevel@tonic-gate if (pdata) {
62250Sstevel@tonic-gate freemsg(pdata);
62260Sstevel@tonic-gate pdata = NULL;
62270Sstevel@tonic-gate }
62280Sstevel@tonic-gate
62290Sstevel@tonic-gate /*
62300Sstevel@tonic-gate * USB_DESCR_TYPE_SETUP_DEV_QLF query was successful
62310Sstevel@tonic-gate * that means this is a high speed device behind a
62320Sstevel@tonic-gate * high speed root hub, but running at full speed
62330Sstevel@tonic-gate * because there is a full speed hub in the middle.
62340Sstevel@tonic-gate */
62350Sstevel@tonic-gate if (rval == USB_SUCCESS) {
62364763Slg150142 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
62374763Slg150142 hubd->h_log_handle,
62384763Slg150142 "Connecting a high speed device to a "
62394763Slg150142 "non high speed hub (port %d) will result "
62406112Sqz150045 "in a loss of performance. Please connect "
62414763Slg150142 "the device to a high speed hub to get "
62424763Slg150142 "the maximum performance.",
62434763Slg150142 port);
62440Sstevel@tonic-gate }
62450Sstevel@tonic-gate }
62460Sstevel@tonic-gate
62470Sstevel@tonic-gate /*
62480Sstevel@tonic-gate * Now we try to online the device by attaching a driver
62490Sstevel@tonic-gate * The following truth table illustrates the logic:-
62500Sstevel@tonic-gate * Cfgndx Driver Action
62510Sstevel@tonic-gate * 0 0 loop all configs for driver with full
62520Sstevel@tonic-gate * compatible properties.
62530Sstevel@tonic-gate * 0 1 set first configuration,
62540Sstevel@tonic-gate * compatible prop = drivername.
62550Sstevel@tonic-gate * 1 0 Set config, full compatible prop
62560Sstevel@tonic-gate * 1 1 Set config, compatible prop = drivername.
62570Sstevel@tonic-gate *
62580Sstevel@tonic-gate * Note:
62590Sstevel@tonic-gate * cfgndx = user_conf_index
62600Sstevel@tonic-gate * Driver = usb_preferred_driver
62610Sstevel@tonic-gate */
62620Sstevel@tonic-gate if (user_conf_index == USBA_DEV_CONFIG_INDEX_UNDEFINED) {
62630Sstevel@tonic-gate if (child_ud->usb_preferred_driver) {
62640Sstevel@tonic-gate /*
62650Sstevel@tonic-gate * It is the job of the "preferred driver" to put the
62660Sstevel@tonic-gate * device in the desired configuration. Till then
62670Sstevel@tonic-gate * put the device in config index 0.
62680Sstevel@tonic-gate */
62691001Ssl147100 if ((rval = usba_hubdi_check_power_budget(dip, child_ud,
62701001Ssl147100 USB_DEV_DEFAULT_CONFIG_INDEX)) != USB_SUCCESS) {
62711001Ssl147100
62721001Ssl147100 goto fail_cleanup;
62731001Ssl147100 }
62741001Ssl147100
62750Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip,
62760Sstevel@tonic-gate child_ud, USB_DEV_DEFAULT_CONFIG_INDEX);
62770Sstevel@tonic-gate
62780Sstevel@tonic-gate /*
62790Sstevel@tonic-gate * Assign the dip before onlining to avoid race
62800Sstevel@tonic-gate * with busctl
62810Sstevel@tonic-gate */
62820Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
62830Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip;
62840Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
62850Sstevel@tonic-gate
62860Sstevel@tonic-gate (void) usba_bind_driver(child_dip);
62870Sstevel@tonic-gate } else {
62880Sstevel@tonic-gate /*
62890Sstevel@tonic-gate * loop through all the configurations to see if we
62900Sstevel@tonic-gate * can find a driver for any one config. If not, set
62910Sstevel@tonic-gate * the device in config_index 0
62920Sstevel@tonic-gate */
62930Sstevel@tonic-gate rval = USB_FAILURE;
62940Sstevel@tonic-gate for (config_index = 0;
62950Sstevel@tonic-gate (config_index < usb_dev_descr.bNumConfigurations) &&
62960Sstevel@tonic-gate (rval != USB_SUCCESS); config_index++) {
62970Sstevel@tonic-gate
62980Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip,
62990Sstevel@tonic-gate child_ud, config_index);
63000Sstevel@tonic-gate
63010Sstevel@tonic-gate /*
63020Sstevel@tonic-gate * Assign the dip before onlining to avoid race
63030Sstevel@tonic-gate * with busctl
63040Sstevel@tonic-gate */
63050Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
63060Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip;
63070Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
63080Sstevel@tonic-gate
63090Sstevel@tonic-gate rval = usba_bind_driver(child_dip);
63101001Ssl147100
63111001Ssl147100 /*
63121001Ssl147100 * Normally power budget should be checked
63131001Ssl147100 * before device is configured. A failure in
63141001Ssl147100 * power budget checking will stop the device
63151001Ssl147100 * from being configured with current
63161001Ssl147100 * config_index and may enable the device to
63171001Ssl147100 * be configured in another configuration.
63181001Ssl147100 * This may break the user experience that a
63191001Ssl147100 * device which previously worked in config
63201001Ssl147100 * A now works in config B after power budget
63211001Ssl147100 * control is enabled. To avoid such situation,
63221001Ssl147100 * power budget checking is moved here and will
63231001Ssl147100 * fail the child creation directly if config
63241001Ssl147100 * A exceeds the power available.
63251001Ssl147100 */
63261001Ssl147100 if (rval == USB_SUCCESS) {
63271001Ssl147100 if ((usba_hubdi_check_power_budget(dip,
63281001Ssl147100 child_ud, config_index)) !=
63291001Ssl147100 USB_SUCCESS) {
63301001Ssl147100
63311001Ssl147100 goto fail_cleanup;
63321001Ssl147100 }
63331001Ssl147100 }
63340Sstevel@tonic-gate }
63350Sstevel@tonic-gate if (rval != USB_SUCCESS) {
63361001Ssl147100
63371001Ssl147100 if ((usba_hubdi_check_power_budget(dip,
63381001Ssl147100 child_ud, 0)) != USB_SUCCESS) {
63391001Ssl147100
63401001Ssl147100 goto fail_cleanup;
63411001Ssl147100 }
63421001Ssl147100
63430Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip,
63440Sstevel@tonic-gate child_ud, 0);
63450Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
63460Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip;
63470Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
63480Sstevel@tonic-gate }
63490Sstevel@tonic-gate } /* end else loop all configs */
63500Sstevel@tonic-gate } else {
63511001Ssl147100
63521001Ssl147100 if ((usba_hubdi_check_power_budget(dip, child_ud,
63531001Ssl147100 (uint_t)user_conf_index)) != USB_SUCCESS) {
63541001Ssl147100
63551001Ssl147100 goto fail_cleanup;
63561001Ssl147100 }
63571001Ssl147100
63580Sstevel@tonic-gate child_dip = hubd_ready_device(hubd, child_dip,
63591001Ssl147100 child_ud, (uint_t)user_conf_index);
63600Sstevel@tonic-gate
63610Sstevel@tonic-gate /*
63620Sstevel@tonic-gate * Assign the dip before onlining to avoid race
63630Sstevel@tonic-gate * with busctl
63640Sstevel@tonic-gate */
63650Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
63660Sstevel@tonic-gate hubd->h_children_dips[port] = child_dip;
63670Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
63680Sstevel@tonic-gate
63690Sstevel@tonic-gate (void) usba_bind_driver(child_dip);
63700Sstevel@tonic-gate }
63710Sstevel@tonic-gate
63721001Ssl147100 usba_hubdi_decr_power_budget(dip, child_ud);
63731001Ssl147100
63740Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
63750Sstevel@tonic-gate if (hubd->h_usba_devices[port] == NULL) {
63760Sstevel@tonic-gate hubd->h_usba_devices[port] = usba_get_usba_device(child_dip);
63770Sstevel@tonic-gate } else {
63780Sstevel@tonic-gate ASSERT(hubd->h_usba_devices[port] ==
63794763Slg150142 usba_get_usba_device(child_dip));
63800Sstevel@tonic-gate }
63810Sstevel@tonic-gate
63820Sstevel@tonic-gate return (USB_SUCCESS);
63830Sstevel@tonic-gate
63840Sstevel@tonic-gate
63850Sstevel@tonic-gate fail_cleanup:
63860Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
63870Sstevel@tonic-gate "hubd_create_child: fail_cleanup");
63880Sstevel@tonic-gate
63890Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
63900Sstevel@tonic-gate hubd->h_children_dips[port] = NULL;
63910Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
63920Sstevel@tonic-gate
63930Sstevel@tonic-gate if (pdata) {
63940Sstevel@tonic-gate freemsg(pdata);
63950Sstevel@tonic-gate }
63960Sstevel@tonic-gate
63970Sstevel@tonic-gate if (ph) {
63980Sstevel@tonic-gate usb_pipe_close(child_dip, ph,
63990Sstevel@tonic-gate USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
64000Sstevel@tonic-gate }
64010Sstevel@tonic-gate
64020Sstevel@tonic-gate if (child_dip) {
64030Sstevel@tonic-gate int rval = usba_destroy_child_devi(child_dip,
64040Sstevel@tonic-gate NDI_DEVI_REMOVE);
64050Sstevel@tonic-gate if (rval != USB_SUCCESS) {
6406978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
64070Sstevel@tonic-gate "failure to remove child node");
64080Sstevel@tonic-gate }
64090Sstevel@tonic-gate }
64100Sstevel@tonic-gate
64110Sstevel@tonic-gate if (child_ud) {
64120Sstevel@tonic-gate /* to make sure we free the address */
64130Sstevel@tonic-gate mutex_enter(&child_ud->usb_mutex);
64140Sstevel@tonic-gate child_ud->usb_addr = address;
64150Sstevel@tonic-gate ASSERT(child_ud->usb_ref_count == 0);
64160Sstevel@tonic-gate mutex_exit(&child_ud->usb_mutex);
64170Sstevel@tonic-gate
64180Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
64190Sstevel@tonic-gate if (hubd->h_usba_devices[port] == NULL) {
64200Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
64210Sstevel@tonic-gate usba_free_usba_device(child_ud);
64220Sstevel@tonic-gate } else {
64230Sstevel@tonic-gate hubd_free_usba_device(hubd, hubd->h_usba_devices[port]);
64240Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
64250Sstevel@tonic-gate }
64260Sstevel@tonic-gate }
64270Sstevel@tonic-gate
64280Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
64290Sstevel@tonic-gate
64300Sstevel@tonic-gate return (USB_FAILURE);
64310Sstevel@tonic-gate }
64320Sstevel@tonic-gate
64330Sstevel@tonic-gate
64340Sstevel@tonic-gate /*
64350Sstevel@tonic-gate * hubd_delete_child:
64360Sstevel@tonic-gate * - free usb address
64370Sstevel@tonic-gate * - lookup child dips, there may be multiple on this port
64380Sstevel@tonic-gate * - offline each child devi
64390Sstevel@tonic-gate */
64400Sstevel@tonic-gate static int
hubd_delete_child(hubd_t * hubd,usb_port_t port,uint_t flag,boolean_t retry)64410Sstevel@tonic-gate hubd_delete_child(hubd_t *hubd, usb_port_t port, uint_t flag, boolean_t retry)
64420Sstevel@tonic-gate {
64430Sstevel@tonic-gate dev_info_t *child_dip;
64440Sstevel@tonic-gate usba_device_t *usba_device;
64450Sstevel@tonic-gate int rval = USB_SUCCESS;
64460Sstevel@tonic-gate
64470Sstevel@tonic-gate child_dip = hubd->h_children_dips[port];
64481001Ssl147100 usba_device = hubd->h_usba_devices[port];
64490Sstevel@tonic-gate
64500Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
64510Sstevel@tonic-gate "hubd_delete_child: port=%d, dip=0x%p usba_device=0x%p",
64526898Sfb209375 port, (void *)child_dip, (void *)usba_device);
64530Sstevel@tonic-gate
64540Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
64550Sstevel@tonic-gate if (child_dip) {
64560Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
64570Sstevel@tonic-gate "hubd_delete_child:\n\t"
64580Sstevel@tonic-gate "dip = 0x%p (%s) at port %d",
64596898Sfb209375 (void *)child_dip, ddi_node_name(child_dip), port);
64600Sstevel@tonic-gate
64611001Ssl147100 if (usba_device) {
64621001Ssl147100 usba_hubdi_incr_power_budget(hubd->h_dip, usba_device);
64631001Ssl147100 }
64641001Ssl147100
64650Sstevel@tonic-gate rval = usba_destroy_child_devi(child_dip, flag);
64660Sstevel@tonic-gate
64679430SRaymond.Chen@Sun.COM if ((rval != USB_SUCCESS) && usba_is_hwa(child_dip)) {
64689430SRaymond.Chen@Sun.COM /*
64699430SRaymond.Chen@Sun.COM * This is only useful for HWA device node.
64709430SRaymond.Chen@Sun.COM * Since hwahc interface must hold hwarc interface
64719430SRaymond.Chen@Sun.COM * open until hwahc is detached, the first call to
64729430SRaymond.Chen@Sun.COM * ndi_devi_unconfig_one() can only offline hwahc
64739430SRaymond.Chen@Sun.COM * driver but not hwarc driver. Need to make a second
64749430SRaymond.Chen@Sun.COM * call to ndi_devi_unconfig_one() to make the hwarc
64759430SRaymond.Chen@Sun.COM * driver detach.
64769430SRaymond.Chen@Sun.COM */
64779430SRaymond.Chen@Sun.COM rval = usba_destroy_child_devi(child_dip, flag);
64789430SRaymond.Chen@Sun.COM }
64799430SRaymond.Chen@Sun.COM
64800Sstevel@tonic-gate if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) {
64810Sstevel@tonic-gate /*
64820Sstevel@tonic-gate * if the child was still < DS_INITIALIZED
64830Sstevel@tonic-gate * then our bus_unconfig was not called and
64840Sstevel@tonic-gate * we have to zap the child here
64850Sstevel@tonic-gate */
64860Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
64870Sstevel@tonic-gate if (hubd->h_children_dips[port] == child_dip) {
64880Sstevel@tonic-gate usba_device_t *ud =
64894763Slg150142 hubd->h_usba_devices[port];
64904844Slg150142 hubd->h_children_dips[port] = NULL;
64910Sstevel@tonic-gate if (ud) {
64920Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
64930Sstevel@tonic-gate
64940Sstevel@tonic-gate mutex_enter(&ud->usb_mutex);
64950Sstevel@tonic-gate ud->usb_ref_count = 0;
64960Sstevel@tonic-gate mutex_exit(&ud->usb_mutex);
64970Sstevel@tonic-gate
64980Sstevel@tonic-gate usba_free_usba_device(ud);
64990Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
65000Sstevel@tonic-gate hubd->h_usba_devices[port] = NULL;
65010Sstevel@tonic-gate }
65020Sstevel@tonic-gate }
65030Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
65040Sstevel@tonic-gate }
65050Sstevel@tonic-gate }
65060Sstevel@tonic-gate
65070Sstevel@tonic-gate if ((rval != USB_SUCCESS) && retry) {
65080Sstevel@tonic-gate
65090Sstevel@tonic-gate hubd_schedule_cleanup(usba_device->usb_root_hub_dip);
65100Sstevel@tonic-gate }
65110Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
65120Sstevel@tonic-gate
65130Sstevel@tonic-gate return (rval);
65140Sstevel@tonic-gate }
65150Sstevel@tonic-gate
65160Sstevel@tonic-gate
65170Sstevel@tonic-gate /*
65180Sstevel@tonic-gate * hubd_free_usba_device:
65190Sstevel@tonic-gate * free usb device structure unless it is associated with
65200Sstevel@tonic-gate * the root hub which is handled differently
65210Sstevel@tonic-gate */
65220Sstevel@tonic-gate static void
hubd_free_usba_device(hubd_t * hubd,usba_device_t * usba_device)65230Sstevel@tonic-gate hubd_free_usba_device(hubd_t *hubd, usba_device_t *usba_device)
65240Sstevel@tonic-gate {
65250Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
65260Sstevel@tonic-gate "hubd_free_usba_device: hubd=0x%p, usba_device=0x%p",
65276898Sfb209375 (void *)hubd, (void *)usba_device);
65280Sstevel@tonic-gate
65290Sstevel@tonic-gate if (usba_device && (usba_device->usb_addr != ROOT_HUB_ADDR)) {
65300Sstevel@tonic-gate usb_port_t port = usba_device->usb_port;
65310Sstevel@tonic-gate dev_info_t *dip = hubd->h_children_dips[port];
65320Sstevel@tonic-gate
65330Sstevel@tonic-gate #ifdef DEBUG
65340Sstevel@tonic-gate if (dip) {
65350Sstevel@tonic-gate ASSERT(i_ddi_node_state(dip) < DS_INITIALIZED);
65360Sstevel@tonic-gate }
65370Sstevel@tonic-gate #endif
65380Sstevel@tonic-gate
65390Sstevel@tonic-gate port = usba_device->usb_port;
65400Sstevel@tonic-gate hubd->h_usba_devices[port] = NULL;
65410Sstevel@tonic-gate
65420Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
65430Sstevel@tonic-gate usba_free_usba_device(usba_device);
65440Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
65450Sstevel@tonic-gate }
65460Sstevel@tonic-gate }
65470Sstevel@tonic-gate
65480Sstevel@tonic-gate
65490Sstevel@tonic-gate /*
65500Sstevel@tonic-gate * event support
65510Sstevel@tonic-gate *
65520Sstevel@tonic-gate * busctl event support
65530Sstevel@tonic-gate */
65540Sstevel@tonic-gate static int
hubd_busop_get_eventcookie(dev_info_t * dip,dev_info_t * rdip,char * eventname,ddi_eventcookie_t * cookie)65550Sstevel@tonic-gate hubd_busop_get_eventcookie(dev_info_t *dip,
65560Sstevel@tonic-gate dev_info_t *rdip,
65570Sstevel@tonic-gate char *eventname,
65580Sstevel@tonic-gate ddi_eventcookie_t *cookie)
65590Sstevel@tonic-gate {
65600Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
65610Sstevel@tonic-gate
65620Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
65630Sstevel@tonic-gate "hubd_busop_get_eventcookie: dip=0x%p, rdip=0x%p, "
65640Sstevel@tonic-gate "event=%s", (void *)dip, (void *)rdip, eventname);
65650Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
65660Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d)",
65670Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip),
65680Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip));
65690Sstevel@tonic-gate
65700Sstevel@tonic-gate /* return event cookie, iblock cookie, and level */
65710Sstevel@tonic-gate return (ndi_event_retrieve_cookie(hubd->h_ndi_event_hdl,
65724763Slg150142 rdip, eventname, cookie, NDI_EVENT_NOPASS));
65730Sstevel@tonic-gate }
65740Sstevel@tonic-gate
65750Sstevel@tonic-gate
65760Sstevel@tonic-gate static int
hubd_busop_add_eventcall(dev_info_t * dip,dev_info_t * rdip,ddi_eventcookie_t cookie,void (* callback)(dev_info_t * dip,ddi_eventcookie_t cookie,void * arg,void * bus_impldata),void * arg,ddi_callback_id_t * cb_id)65770Sstevel@tonic-gate hubd_busop_add_eventcall(dev_info_t *dip,
65780Sstevel@tonic-gate dev_info_t *rdip,
65790Sstevel@tonic-gate ddi_eventcookie_t cookie,
65800Sstevel@tonic-gate void (*callback)(dev_info_t *dip,
65810Sstevel@tonic-gate ddi_eventcookie_t cookie, void *arg,
65820Sstevel@tonic-gate void *bus_impldata),
65830Sstevel@tonic-gate void *arg, ddi_callback_id_t *cb_id)
65840Sstevel@tonic-gate {
65850Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
65860Sstevel@tonic-gate usb_port_t port = hubd_child_dip2port(hubd, rdip);
65870Sstevel@tonic-gate
65880Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
65890Sstevel@tonic-gate "hubd_busop_add_eventcall: dip=0x%p, rdip=0x%p "
65900Sstevel@tonic-gate "cookie=0x%p, cb=0x%p, arg=0x%p",
65910Sstevel@tonic-gate (void *)dip, (void *)rdip, (void *)cookie, (void *)callback, arg);
65920Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
65930Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d, event=%s)",
65940Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip),
65950Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip),
65960Sstevel@tonic-gate ndi_event_cookie_to_name(hubd->h_ndi_event_hdl, cookie));
65970Sstevel@tonic-gate
65980Sstevel@tonic-gate /* Set flag on children registering events */
65990Sstevel@tonic-gate switch (ndi_event_cookie_to_tag(hubd->h_ndi_event_hdl, cookie)) {
66000Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_REMOVAL:
66010Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
66020Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT;
66030Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
66040Sstevel@tonic-gate
66050Sstevel@tonic-gate break;
66060Sstevel@tonic-gate case USBA_EVENT_TAG_PRE_SUSPEND:
66070Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
66080Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND;
66090Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
66100Sstevel@tonic-gate
66110Sstevel@tonic-gate break;
66120Sstevel@tonic-gate default:
66130Sstevel@tonic-gate
66140Sstevel@tonic-gate break;
66150Sstevel@tonic-gate }
66160Sstevel@tonic-gate
66170Sstevel@tonic-gate /* add callback to our event set */
66180Sstevel@tonic-gate return (ndi_event_add_callback(hubd->h_ndi_event_hdl,
66194763Slg150142 rdip, cookie, callback, arg, NDI_SLEEP, cb_id));
66200Sstevel@tonic-gate }
66210Sstevel@tonic-gate
66220Sstevel@tonic-gate
66230Sstevel@tonic-gate static int
hubd_busop_remove_eventcall(dev_info_t * dip,ddi_callback_id_t cb_id)66240Sstevel@tonic-gate hubd_busop_remove_eventcall(dev_info_t *dip, ddi_callback_id_t cb_id)
66250Sstevel@tonic-gate {
66260Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
66270Sstevel@tonic-gate ndi_event_callbacks_t *id = (ndi_event_callbacks_t *)cb_id;
66280Sstevel@tonic-gate
66290Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
66300Sstevel@tonic-gate "hubd_busop_remove_eventcall: dip=0x%p, rdip=0x%p "
66316898Sfb209375 "cookie=0x%p", (void *)dip, (void *)id->ndi_evtcb_dip,
66326898Sfb209375 (void *)id->ndi_evtcb_cookie);
66330Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
66340Sstevel@tonic-gate "(dip=%s%d, rdip=%s%d, event=%s)",
66350Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip),
66360Sstevel@tonic-gate ddi_driver_name(id->ndi_evtcb_dip),
66370Sstevel@tonic-gate ddi_get_instance(id->ndi_evtcb_dip),
66380Sstevel@tonic-gate ndi_event_cookie_to_name(hubd->h_ndi_event_hdl,
66390Sstevel@tonic-gate id->ndi_evtcb_cookie));
66400Sstevel@tonic-gate
66410Sstevel@tonic-gate /* remove event registration from our event set */
66420Sstevel@tonic-gate return (ndi_event_remove_callback(hubd->h_ndi_event_hdl, cb_id));
66430Sstevel@tonic-gate }
66440Sstevel@tonic-gate
66450Sstevel@tonic-gate
66460Sstevel@tonic-gate /*
66470Sstevel@tonic-gate * event distribution
66480Sstevel@tonic-gate *
66490Sstevel@tonic-gate * hubd_do_callback:
66500Sstevel@tonic-gate * Post this event to the specified child
66510Sstevel@tonic-gate */
66520Sstevel@tonic-gate static void
hubd_do_callback(hubd_t * hubd,dev_info_t * cdip,ddi_eventcookie_t cookie)66530Sstevel@tonic-gate hubd_do_callback(hubd_t *hubd, dev_info_t *cdip, ddi_eventcookie_t cookie)
66540Sstevel@tonic-gate {
66550Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
66560Sstevel@tonic-gate "hubd_do_callback");
66570Sstevel@tonic-gate
66580Sstevel@tonic-gate (void) ndi_event_do_callback(hubd->h_ndi_event_hdl, cdip, cookie, NULL);
66590Sstevel@tonic-gate }
66600Sstevel@tonic-gate
66610Sstevel@tonic-gate
66620Sstevel@tonic-gate /*
66630Sstevel@tonic-gate * hubd_run_callbacks:
66640Sstevel@tonic-gate * Send this event to all children
66650Sstevel@tonic-gate */
66660Sstevel@tonic-gate static void
hubd_run_callbacks(hubd_t * hubd,usba_event_t type)66670Sstevel@tonic-gate hubd_run_callbacks(hubd_t *hubd, usba_event_t type)
66680Sstevel@tonic-gate {
66690Sstevel@tonic-gate usb_port_t port;
66700Sstevel@tonic-gate
66710Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
66720Sstevel@tonic-gate "hubd_run_callbacks");
66730Sstevel@tonic-gate
66740Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
66750Sstevel@tonic-gate for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) {
66760Sstevel@tonic-gate /*
66770Sstevel@tonic-gate * the childen_dips list may have dips that have been
66780Sstevel@tonic-gate * already deallocated. we only get a post_detach notification
66790Sstevel@tonic-gate * but not a destroy notification
66800Sstevel@tonic-gate */
66810Sstevel@tonic-gate if (hubd->h_children_dips[port]) {
66820Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
66830Sstevel@tonic-gate hubd_post_event(hubd, port, type);
66840Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
66850Sstevel@tonic-gate }
66860Sstevel@tonic-gate }
66870Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
66880Sstevel@tonic-gate }
66890Sstevel@tonic-gate
66900Sstevel@tonic-gate
66910Sstevel@tonic-gate /*
66920Sstevel@tonic-gate * hubd_post_event
66930Sstevel@tonic-gate * post event to a child on the port depending on the type
66940Sstevel@tonic-gate */
66950Sstevel@tonic-gate static void
hubd_post_event(hubd_t * hubd,usb_port_t port,usba_event_t type)66960Sstevel@tonic-gate hubd_post_event(hubd_t *hubd, usb_port_t port, usba_event_t type)
66970Sstevel@tonic-gate {
66980Sstevel@tonic-gate int rval;
66990Sstevel@tonic-gate dev_info_t *dip;
67000Sstevel@tonic-gate usba_device_t *usba_device;
67010Sstevel@tonic-gate ddi_eventcookie_t cookie, rm_cookie, suspend_cookie;
67020Sstevel@tonic-gate
67030Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
67040Sstevel@tonic-gate "hubd_post_event: port=%d event=%s", port,
67050Sstevel@tonic-gate ndi_event_tag_to_name(hubd->h_ndi_event_hdl, type));
67060Sstevel@tonic-gate
67070Sstevel@tonic-gate cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl, type);
67080Sstevel@tonic-gate rm_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl,
67090Sstevel@tonic-gate USBA_EVENT_TAG_HOT_REMOVAL);
67100Sstevel@tonic-gate suspend_cookie = ndi_event_tag_to_cookie(hubd->h_ndi_event_hdl,
67110Sstevel@tonic-gate USBA_EVENT_TAG_PRE_SUSPEND);
67120Sstevel@tonic-gate
67130Sstevel@tonic-gate /*
67140Sstevel@tonic-gate * Hotplug daemon may be attaching a driver that may be registering
67150Sstevel@tonic-gate * event callbacks. So it already has got the device tree lock and
67160Sstevel@tonic-gate * event handle mutex. So to prevent a deadlock while posting events,
67170Sstevel@tonic-gate * we grab and release the locks in the same order.
67180Sstevel@tonic-gate */
67190Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
67200Sstevel@tonic-gate dip = hubd->h_children_dips[port];
67210Sstevel@tonic-gate usba_device = hubd->h_usba_devices[port];
67220Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
67230Sstevel@tonic-gate
67240Sstevel@tonic-gate switch (type) {
67250Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_REMOVAL:
67260Sstevel@tonic-gate /* Clear the registered event flag */
67270Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
67280Sstevel@tonic-gate hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_DISCONNECT;
67290Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
67300Sstevel@tonic-gate
67310Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie);
67320Sstevel@tonic-gate usba_persistent_pipe_close(usba_device);
67330Sstevel@tonic-gate
67340Sstevel@tonic-gate /*
67350Sstevel@tonic-gate * Mark the dip for deletion only after the driver has
67360Sstevel@tonic-gate * seen the disconnect event to prevent cleanup thread
67370Sstevel@tonic-gate * from stepping in between.
67380Sstevel@tonic-gate */
6739495Scth mutex_enter(&(DEVI(dip)->devi_lock));
67400Sstevel@tonic-gate DEVI_SET_DEVICE_REMOVED(dip);
6741495Scth mutex_exit(&(DEVI(dip)->devi_lock));
67420Sstevel@tonic-gate
67430Sstevel@tonic-gate break;
67440Sstevel@tonic-gate case USBA_EVENT_TAG_PRE_SUSPEND:
67450Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
67460Sstevel@tonic-gate hubd->h_child_events[port] &= ~HUBD_CHILD_EVENT_PRESUSPEND;
67470Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
67480Sstevel@tonic-gate
67490Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie);
67500Sstevel@tonic-gate /*
67510Sstevel@tonic-gate * persistent pipe close for this event is taken care by the
67520Sstevel@tonic-gate * caller after verfying that all children can suspend
67530Sstevel@tonic-gate */
67540Sstevel@tonic-gate
67550Sstevel@tonic-gate break;
67560Sstevel@tonic-gate case USBA_EVENT_TAG_HOT_INSERTION:
67570Sstevel@tonic-gate /*
67580Sstevel@tonic-gate * Check if this child has missed the disconnect event before
67590Sstevel@tonic-gate * it registered for event callbacks
67600Sstevel@tonic-gate */
67610Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
67620Sstevel@tonic-gate if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_DISCONNECT) {
67630Sstevel@tonic-gate /* clear the flag and post disconnect event */
67640Sstevel@tonic-gate hubd->h_child_events[port] &=
67650Sstevel@tonic-gate ~HUBD_CHILD_EVENT_DISCONNECT;
67660Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
67670Sstevel@tonic-gate hubd_do_callback(hubd, dip, rm_cookie);
67680Sstevel@tonic-gate usba_persistent_pipe_close(usba_device);
67690Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
67700Sstevel@tonic-gate }
67710Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
67720Sstevel@tonic-gate
67730Sstevel@tonic-gate /*
67740Sstevel@tonic-gate * Mark the dip as reinserted to prevent cleanup thread
67750Sstevel@tonic-gate * from stepping in.
67760Sstevel@tonic-gate */
6777495Scth mutex_enter(&(DEVI(dip)->devi_lock));
67780Sstevel@tonic-gate DEVI_SET_DEVICE_REINSERTED(dip);
6779495Scth mutex_exit(&(DEVI(dip)->devi_lock));
67800Sstevel@tonic-gate
67810Sstevel@tonic-gate rval = usba_persistent_pipe_open(usba_device);
67820Sstevel@tonic-gate if (rval != USB_SUCCESS) {
6783978Sfrits USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
67840Sstevel@tonic-gate hubd->h_log_handle,
67850Sstevel@tonic-gate "failed to reopen all pipes on reconnect");
67860Sstevel@tonic-gate }
67870Sstevel@tonic-gate
67880Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie);
67890Sstevel@tonic-gate
67900Sstevel@tonic-gate /*
67910Sstevel@tonic-gate * We might see a connect event only if hotplug thread for
67920Sstevel@tonic-gate * disconnect event don't run in time.
67930Sstevel@tonic-gate * Set the flag again, so we don't miss posting a
67940Sstevel@tonic-gate * disconnect event.
67950Sstevel@tonic-gate */
67960Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
67970Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_DISCONNECT;
67980Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
67990Sstevel@tonic-gate
68000Sstevel@tonic-gate break;
68010Sstevel@tonic-gate case USBA_EVENT_TAG_POST_RESUME:
68020Sstevel@tonic-gate /*
68030Sstevel@tonic-gate * Check if this child has missed the pre-suspend event before
68040Sstevel@tonic-gate * it registered for event callbacks
68050Sstevel@tonic-gate */
68060Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
68070Sstevel@tonic-gate if (hubd->h_child_events[port] & HUBD_CHILD_EVENT_PRESUSPEND) {
68080Sstevel@tonic-gate /* clear the flag and post pre_suspend event */
68090Sstevel@tonic-gate hubd->h_port_state[port] &=
68100Sstevel@tonic-gate ~HUBD_CHILD_EVENT_PRESUSPEND;
68110Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
68120Sstevel@tonic-gate hubd_do_callback(hubd, dip, suspend_cookie);
68130Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
68140Sstevel@tonic-gate }
68150Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
68160Sstevel@tonic-gate
68170Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex);
68180Sstevel@tonic-gate usba_device->usb_no_cpr = 0;
68190Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex);
68200Sstevel@tonic-gate
68210Sstevel@tonic-gate /*
68220Sstevel@tonic-gate * Since the pipe has already been opened by hub
68230Sstevel@tonic-gate * at DDI_RESUME time, there is no need for a
68240Sstevel@tonic-gate * persistent pipe open
68250Sstevel@tonic-gate */
68260Sstevel@tonic-gate hubd_do_callback(hubd, dip, cookie);
68270Sstevel@tonic-gate
68280Sstevel@tonic-gate /*
68290Sstevel@tonic-gate * Set the flag again, so we don't miss posting a
68300Sstevel@tonic-gate * pre-suspend event. This enforces a tighter
68310Sstevel@tonic-gate * dev_state model.
68320Sstevel@tonic-gate */
68330Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
68340Sstevel@tonic-gate hubd->h_child_events[port] |= HUBD_CHILD_EVENT_PRESUSPEND;
68350Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
68360Sstevel@tonic-gate break;
68370Sstevel@tonic-gate }
68380Sstevel@tonic-gate }
68390Sstevel@tonic-gate
68400Sstevel@tonic-gate
68410Sstevel@tonic-gate /*
68420Sstevel@tonic-gate * handling of events coming from above
68430Sstevel@tonic-gate */
68440Sstevel@tonic-gate static int
hubd_disconnect_event_cb(dev_info_t * dip)68450Sstevel@tonic-gate hubd_disconnect_event_cb(dev_info_t *dip)
68460Sstevel@tonic-gate {
68470Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
68480Sstevel@tonic-gate usb_port_t port, nports;
68490Sstevel@tonic-gate usba_device_t *usba_dev;
68500Sstevel@tonic-gate usba_event_t tag = USBA_EVENT_TAG_HOT_REMOVAL;
68510Sstevel@tonic-gate int circ;
68520Sstevel@tonic-gate
68530Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
68540Sstevel@tonic-gate "hubd_disconnect_event_cb: tag=%d", tag);
68550Sstevel@tonic-gate
68560Sstevel@tonic-gate ndi_devi_enter(dip, &circ);
68570Sstevel@tonic-gate
68580Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
68590Sstevel@tonic-gate switch (hubd->h_dev_state) {
68600Sstevel@tonic-gate case USB_DEV_ONLINE:
68610Sstevel@tonic-gate case USB_DEV_PWRED_DOWN:
68620Sstevel@tonic-gate hubd->h_dev_state = USB_DEV_DISCONNECTED;
68630Sstevel@tonic-gate /* stop polling on the interrupt pipe */
68640Sstevel@tonic-gate hubd_stop_polling(hubd);
68650Sstevel@tonic-gate
68660Sstevel@tonic-gate /* FALLTHROUGH */
68670Sstevel@tonic-gate case USB_DEV_SUSPENDED:
68680Sstevel@tonic-gate /* we remain in this state */
68690Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
68700Sstevel@tonic-gate hubd_run_callbacks(hubd, tag);
68710Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
68720Sstevel@tonic-gate
68730Sstevel@tonic-gate /* close all the open pipes of our children */
68740Sstevel@tonic-gate nports = hubd->h_hub_descr.bNbrPorts;
68750Sstevel@tonic-gate for (port = 1; port <= nports; port++) {
68760Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port];
68770Sstevel@tonic-gate if (usba_dev != NULL) {
68780Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
68790Sstevel@tonic-gate usba_persistent_pipe_close(usba_dev);
68800Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
68810Sstevel@tonic-gate }
68820Sstevel@tonic-gate }
68830Sstevel@tonic-gate
68840Sstevel@tonic-gate break;
68850Sstevel@tonic-gate case USB_DEV_DISCONNECTED:
68860Sstevel@tonic-gate /* avoid passing multiple disconnects to children */
68870Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
68880Sstevel@tonic-gate "hubd_disconnect_event_cb: Already disconnected");
68890Sstevel@tonic-gate
68900Sstevel@tonic-gate break;
68910Sstevel@tonic-gate default:
68920Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
68930Sstevel@tonic-gate "hubd_disconnect_event_cb: Illegal devstate=%d",
68940Sstevel@tonic-gate hubd->h_dev_state);
68950Sstevel@tonic-gate
68960Sstevel@tonic-gate break;
68970Sstevel@tonic-gate }
68980Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
68990Sstevel@tonic-gate
69000Sstevel@tonic-gate ndi_devi_exit(dip, circ);
69010Sstevel@tonic-gate
69020Sstevel@tonic-gate return (USB_SUCCESS);
69030Sstevel@tonic-gate }
69040Sstevel@tonic-gate
69050Sstevel@tonic-gate
69060Sstevel@tonic-gate static int
hubd_reconnect_event_cb(dev_info_t * dip)69070Sstevel@tonic-gate hubd_reconnect_event_cb(dev_info_t *dip)
69080Sstevel@tonic-gate {
69090Sstevel@tonic-gate int rval, circ;
69100Sstevel@tonic-gate
69110Sstevel@tonic-gate ndi_devi_enter(dip, &circ);
69120Sstevel@tonic-gate rval = hubd_restore_state_cb(dip);
69130Sstevel@tonic-gate ndi_devi_exit(dip, circ);
69140Sstevel@tonic-gate
69150Sstevel@tonic-gate return (rval);
69160Sstevel@tonic-gate }
69170Sstevel@tonic-gate
69180Sstevel@tonic-gate
69190Sstevel@tonic-gate /*
69200Sstevel@tonic-gate * hubd_pre_suspend_event_cb
69210Sstevel@tonic-gate * propogate event for binary compatibility of old drivers
69220Sstevel@tonic-gate */
69230Sstevel@tonic-gate static int
hubd_pre_suspend_event_cb(dev_info_t * dip)69240Sstevel@tonic-gate hubd_pre_suspend_event_cb(dev_info_t *dip)
69250Sstevel@tonic-gate {
69260Sstevel@tonic-gate int circ;
69270Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
69280Sstevel@tonic-gate
69290Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
69300Sstevel@tonic-gate "hubd_pre_suspend_event_cb");
69310Sstevel@tonic-gate
69320Sstevel@tonic-gate /* disable hotplug thread */
69330Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
69340Sstevel@tonic-gate hubd->h_hotplug_thread++;
69350Sstevel@tonic-gate hubd_stop_polling(hubd);
69360Sstevel@tonic-gate
69370Sstevel@tonic-gate /* keep PM out till we see a cpr resume */
69380Sstevel@tonic-gate (void) hubd_pm_busy_component(hubd, hubd->h_dip, 0);
69390Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
69400Sstevel@tonic-gate
69410Sstevel@tonic-gate ndi_devi_enter(dip, &circ);
69420Sstevel@tonic-gate hubd_run_callbacks(hubd, USBA_EVENT_TAG_PRE_SUSPEND);
69430Sstevel@tonic-gate ndi_devi_exit(dip, circ);
69440Sstevel@tonic-gate
69450Sstevel@tonic-gate return (USB_SUCCESS);
69460Sstevel@tonic-gate }
69470Sstevel@tonic-gate
69480Sstevel@tonic-gate
69490Sstevel@tonic-gate /*
69500Sstevel@tonic-gate * hubd_post_resume_event_cb
69510Sstevel@tonic-gate * propogate event for binary compatibility of old drivers
69520Sstevel@tonic-gate */
69530Sstevel@tonic-gate static int
hubd_post_resume_event_cb(dev_info_t * dip)69540Sstevel@tonic-gate hubd_post_resume_event_cb(dev_info_t *dip)
69550Sstevel@tonic-gate {
69560Sstevel@tonic-gate int circ;
69570Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
69580Sstevel@tonic-gate
69590Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
69600Sstevel@tonic-gate "hubd_post_resume_event_cb");
69610Sstevel@tonic-gate
69620Sstevel@tonic-gate ndi_devi_enter(dip, &circ);
69630Sstevel@tonic-gate hubd_run_callbacks(hubd, USBA_EVENT_TAG_POST_RESUME);
69640Sstevel@tonic-gate ndi_devi_exit(dip, circ);
69650Sstevel@tonic-gate
69660Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
69670Sstevel@tonic-gate
69680Sstevel@tonic-gate /* enable PM */
69690Sstevel@tonic-gate (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
69700Sstevel@tonic-gate
69710Sstevel@tonic-gate /* allow hotplug thread */
69720Sstevel@tonic-gate hubd->h_hotplug_thread--;
69730Sstevel@tonic-gate
69740Sstevel@tonic-gate /* start polling */
69750Sstevel@tonic-gate hubd_start_polling(hubd, 0);
69760Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
69770Sstevel@tonic-gate
69780Sstevel@tonic-gate return (USB_SUCCESS);
69790Sstevel@tonic-gate }
69800Sstevel@tonic-gate
69810Sstevel@tonic-gate
69820Sstevel@tonic-gate /*
69830Sstevel@tonic-gate * hubd_cpr_suspend
69840Sstevel@tonic-gate * save the current state of the driver/device
69850Sstevel@tonic-gate */
69860Sstevel@tonic-gate static int
hubd_cpr_suspend(hubd_t * hubd)69870Sstevel@tonic-gate hubd_cpr_suspend(hubd_t *hubd)
69880Sstevel@tonic-gate {
69890Sstevel@tonic-gate usb_port_t port, nports;
69900Sstevel@tonic-gate usba_device_t *usba_dev;
69910Sstevel@tonic-gate uchar_t no_cpr = 0;
69920Sstevel@tonic-gate int rval = USB_FAILURE;
69930Sstevel@tonic-gate
69940Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
69950Sstevel@tonic-gate "hubd_cpr_suspend: Begin");
69960Sstevel@tonic-gate
69970Sstevel@tonic-gate /* Make sure device is powered up to save state. */
69980Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
69990Sstevel@tonic-gate hubd_pm_busy_component(hubd, hubd->h_dip, 0);
70000Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
70010Sstevel@tonic-gate
70020Sstevel@tonic-gate /* bring the device to full power */
70030Sstevel@tonic-gate (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR);
70040Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
70050Sstevel@tonic-gate
70060Sstevel@tonic-gate switch (hubd->h_dev_state) {
70070Sstevel@tonic-gate case USB_DEV_ONLINE:
70080Sstevel@tonic-gate case USB_DEV_PWRED_DOWN:
70090Sstevel@tonic-gate case USB_DEV_DISCONNECTED:
70100Sstevel@tonic-gate /* find out if all our children have been quiesced */
70110Sstevel@tonic-gate nports = hubd->h_hub_descr.bNbrPorts;
70120Sstevel@tonic-gate for (port = 1; (no_cpr == 0) && (port <= nports); port++) {
70130Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port];
70140Sstevel@tonic-gate if (usba_dev != NULL) {
70150Sstevel@tonic-gate mutex_enter(&usba_dev->usb_mutex);
70160Sstevel@tonic-gate no_cpr += usba_dev->usb_no_cpr;
70170Sstevel@tonic-gate mutex_exit(&usba_dev->usb_mutex);
70180Sstevel@tonic-gate }
70190Sstevel@tonic-gate }
70200Sstevel@tonic-gate if (no_cpr > 0) {
70210Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
70220Sstevel@tonic-gate "Children busy - can't checkpoint");
70230Sstevel@tonic-gate /* remain in same state to fail checkpoint */
70240Sstevel@tonic-gate
70250Sstevel@tonic-gate break;
70260Sstevel@tonic-gate } else {
70270Sstevel@tonic-gate /*
70280Sstevel@tonic-gate * do not suspend if our hotplug thread
70290Sstevel@tonic-gate * or the deathrow thread is active
70300Sstevel@tonic-gate */
70310Sstevel@tonic-gate if ((hubd->h_hotplug_thread > 1) ||
70320Sstevel@tonic-gate (hubd->h_cleanup_active == B_TRUE)) {
70330Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG,
70340Sstevel@tonic-gate hubd->h_log_handle,
70350Sstevel@tonic-gate "hotplug thread active - can't cpr");
70360Sstevel@tonic-gate /* remain in same state to fail checkpoint */
70370Sstevel@tonic-gate
70380Sstevel@tonic-gate break;
70390Sstevel@tonic-gate }
70400Sstevel@tonic-gate
70410Sstevel@tonic-gate /* quiesce ourselves now */
70420Sstevel@tonic-gate hubd_stop_polling(hubd);
70430Sstevel@tonic-gate
70440Sstevel@tonic-gate /* close all the open pipes of our children */
70450Sstevel@tonic-gate for (port = 1; port <= nports; port++) {
70460Sstevel@tonic-gate usba_dev = hubd->h_usba_devices[port];
70470Sstevel@tonic-gate if (usba_dev != NULL) {
70480Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
70490Sstevel@tonic-gate usba_persistent_pipe_close(usba_dev);
7050*12819SVincent.Wang@Sun.COM if (hubd_suspend_port(hubd, port)) {
7051*12819SVincent.Wang@Sun.COM USB_DPRINTF_L0(
7052*12819SVincent.Wang@Sun.COM DPRINT_MASK_HOTPLUG,
7053*12819SVincent.Wang@Sun.COM hubd->h_log_handle,
7054*12819SVincent.Wang@Sun.COM "suspending port %d failed",
7055*12819SVincent.Wang@Sun.COM port);
7056*12819SVincent.Wang@Sun.COM }
70570Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
70580Sstevel@tonic-gate }
7059*12819SVincent.Wang@Sun.COM
70600Sstevel@tonic-gate }
7061*12819SVincent.Wang@Sun.COM hubd->h_dev_state = USB_DEV_SUSPENDED;
70620Sstevel@tonic-gate
70630Sstevel@tonic-gate /*
70640Sstevel@tonic-gate * if we are the root hub, we close our pipes
70650Sstevel@tonic-gate * ourselves.
70660Sstevel@tonic-gate */
70670Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) {
70680Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
70690Sstevel@tonic-gate usba_persistent_pipe_close(
70700Sstevel@tonic-gate usba_get_usba_device(hubd->h_dip));
70710Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
70720Sstevel@tonic-gate }
70730Sstevel@tonic-gate rval = USB_SUCCESS;
70740Sstevel@tonic-gate
70750Sstevel@tonic-gate break;
70760Sstevel@tonic-gate }
70770Sstevel@tonic-gate case USB_DEV_SUSPENDED:
70780Sstevel@tonic-gate default:
70790Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
70800Sstevel@tonic-gate "hubd_cpr_suspend: Illegal dev state=%d",
70810Sstevel@tonic-gate hubd->h_dev_state);
70820Sstevel@tonic-gate
70830Sstevel@tonic-gate break;
70840Sstevel@tonic-gate }
70850Sstevel@tonic-gate
70860Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0);
70870Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
70880Sstevel@tonic-gate
70890Sstevel@tonic-gate return (rval);
70900Sstevel@tonic-gate }
70910Sstevel@tonic-gate
70920Sstevel@tonic-gate static void
hubd_cpr_resume(dev_info_t * dip)70930Sstevel@tonic-gate hubd_cpr_resume(dev_info_t *dip)
70940Sstevel@tonic-gate {
70950Sstevel@tonic-gate int rval, circ;
70960Sstevel@tonic-gate
70970Sstevel@tonic-gate ndi_devi_enter(dip, &circ);
70980Sstevel@tonic-gate /*
70990Sstevel@tonic-gate * if we are the root hub, we open our pipes
71000Sstevel@tonic-gate * ourselves.
71010Sstevel@tonic-gate */
71020Sstevel@tonic-gate if (usba_is_root_hub(dip)) {
71030Sstevel@tonic-gate rval = usba_persistent_pipe_open(
71040Sstevel@tonic-gate usba_get_usba_device(dip));
71050Sstevel@tonic-gate ASSERT(rval == USB_SUCCESS);
71060Sstevel@tonic-gate }
71070Sstevel@tonic-gate (void) hubd_restore_state_cb(dip);
71080Sstevel@tonic-gate ndi_devi_exit(dip, circ);
71090Sstevel@tonic-gate }
71100Sstevel@tonic-gate
71110Sstevel@tonic-gate
71120Sstevel@tonic-gate /*
71130Sstevel@tonic-gate * hubd_restore_state_cb
71140Sstevel@tonic-gate * Event callback to restore device state
71150Sstevel@tonic-gate */
71160Sstevel@tonic-gate static int
hubd_restore_state_cb(dev_info_t * dip)71170Sstevel@tonic-gate hubd_restore_state_cb(dev_info_t *dip)
71180Sstevel@tonic-gate {
71190Sstevel@tonic-gate hubd_t *hubd = (hubd_t *)hubd_get_soft_state(dip);
71200Sstevel@tonic-gate
71210Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
71220Sstevel@tonic-gate "hubd_restore_state_cb: Begin");
71230Sstevel@tonic-gate
71240Sstevel@tonic-gate /* restore the state of this device */
71250Sstevel@tonic-gate hubd_restore_device_state(dip, hubd);
71260Sstevel@tonic-gate
71270Sstevel@tonic-gate return (USB_SUCCESS);
71280Sstevel@tonic-gate }
71290Sstevel@tonic-gate
71300Sstevel@tonic-gate
71310Sstevel@tonic-gate /*
71320Sstevel@tonic-gate * registering for events
71330Sstevel@tonic-gate */
71340Sstevel@tonic-gate static int
hubd_register_events(hubd_t * hubd)71350Sstevel@tonic-gate hubd_register_events(hubd_t *hubd)
71360Sstevel@tonic-gate {
71370Sstevel@tonic-gate int rval = USB_SUCCESS;
71380Sstevel@tonic-gate
71390Sstevel@tonic-gate if (usba_is_root_hub(hubd->h_dip)) {
71400Sstevel@tonic-gate hubd_register_cpr_callback(hubd);
71410Sstevel@tonic-gate } else {
71420Sstevel@tonic-gate rval = usb_register_event_cbs(hubd->h_dip, &hubd_events, 0);
71430Sstevel@tonic-gate }
71440Sstevel@tonic-gate
71450Sstevel@tonic-gate return (rval);
71460Sstevel@tonic-gate }
71470Sstevel@tonic-gate
71480Sstevel@tonic-gate
71490Sstevel@tonic-gate /*
71500Sstevel@tonic-gate * hubd cpr callback related functions
71510Sstevel@tonic-gate *
71520Sstevel@tonic-gate * hubd_cpr_post_user_callb:
71530Sstevel@tonic-gate * This function is called during checkpoint & resume -
71540Sstevel@tonic-gate * 1. after user threads are stopped during checkpoint
71550Sstevel@tonic-gate * 2. after kernel threads are resumed during resume
71560Sstevel@tonic-gate */
71570Sstevel@tonic-gate /* ARGSUSED */
71580Sstevel@tonic-gate static boolean_t
hubd_cpr_post_user_callb(void * arg,int code)71590Sstevel@tonic-gate hubd_cpr_post_user_callb(void *arg, int code)
71600Sstevel@tonic-gate {
71610Sstevel@tonic-gate hubd_cpr_t *cpr_cb = (hubd_cpr_t *)arg;
71620Sstevel@tonic-gate hubd_t *hubd = cpr_cb->statep;
71630Sstevel@tonic-gate int retry = 0;
71640Sstevel@tonic-gate
71650Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
71660Sstevel@tonic-gate "hubd_cpr_post_user_callb");
71670Sstevel@tonic-gate
71680Sstevel@tonic-gate switch (code) {
71690Sstevel@tonic-gate case CB_CODE_CPR_CHKPT:
71700Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle,
71710Sstevel@tonic-gate "hubd_cpr_post_user_callb: CB_CODE_CPR_CHKPT");
71720Sstevel@tonic-gate
71730Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
71740Sstevel@tonic-gate
71750Sstevel@tonic-gate /* turn off deathrow thread */
71760Sstevel@tonic-gate hubd->h_cleanup_enabled = B_FALSE;
71770Sstevel@tonic-gate
71780Sstevel@tonic-gate /* give up if deathrow thread doesn't exit */
71790Sstevel@tonic-gate while ((hubd->h_cleanup_active == B_TRUE) && (retry++ < 3)) {
71800Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
71810Sstevel@tonic-gate delay(drv_usectohz(hubd_dip_cleanup_delay));
71820Sstevel@tonic-gate
71830Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_EVENTS, hubd->h_log_handle,
71840Sstevel@tonic-gate "hubd_cpr_post_user_callb, waiting for "
71850Sstevel@tonic-gate "deathrow thread to exit");
71860Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
71870Sstevel@tonic-gate }
71880Sstevel@tonic-gate
71890Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
71900Sstevel@tonic-gate
71910Sstevel@tonic-gate /* save the state of the device */
71920Sstevel@tonic-gate (void) hubd_pre_suspend_event_cb(hubd->h_dip);
71930Sstevel@tonic-gate
71940Sstevel@tonic-gate return (B_TRUE);
71950Sstevel@tonic-gate case CB_CODE_CPR_RESUME:
71960Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_EVENTS, hubd->h_log_handle,
71970Sstevel@tonic-gate "hubd_cpr_post_user_callb: CB_CODE_CPR_RESUME");
71980Sstevel@tonic-gate
71990Sstevel@tonic-gate /* restore the state of the device */
72000Sstevel@tonic-gate (void) hubd_post_resume_event_cb(hubd->h_dip);
72010Sstevel@tonic-gate
72020Sstevel@tonic-gate /* turn on deathrow thread */
72030Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
72040Sstevel@tonic-gate hubd->h_cleanup_enabled = B_TRUE;
72050Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
72060Sstevel@tonic-gate
72070Sstevel@tonic-gate hubd_schedule_cleanup(hubd->h_usba_device->usb_root_hub_dip);
72080Sstevel@tonic-gate
72090Sstevel@tonic-gate return (B_TRUE);
72100Sstevel@tonic-gate default:
72110Sstevel@tonic-gate
72120Sstevel@tonic-gate return (B_FALSE);
72130Sstevel@tonic-gate }
72140Sstevel@tonic-gate
72150Sstevel@tonic-gate }
72160Sstevel@tonic-gate
72170Sstevel@tonic-gate
72180Sstevel@tonic-gate /* register callback with cpr framework */
72190Sstevel@tonic-gate void
hubd_register_cpr_callback(hubd_t * hubd)72200Sstevel@tonic-gate hubd_register_cpr_callback(hubd_t *hubd)
72210Sstevel@tonic-gate {
72220Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
72230Sstevel@tonic-gate "hubd_register_cpr_callback");
72240Sstevel@tonic-gate
72250Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
72260Sstevel@tonic-gate hubd->h_cpr_cb =
72270Sstevel@tonic-gate (hubd_cpr_t *)kmem_zalloc(sizeof (hubd_cpr_t), KM_SLEEP);
72280Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
72290Sstevel@tonic-gate mutex_init(&hubd->h_cpr_cb->lockp, NULL, MUTEX_DRIVER,
72300Sstevel@tonic-gate hubd->h_dev_data->dev_iblock_cookie);
72310Sstevel@tonic-gate hubd->h_cpr_cb->statep = hubd;
72320Sstevel@tonic-gate hubd->h_cpr_cb->cpr.cc_lockp = &hubd->h_cpr_cb->lockp;
72330Sstevel@tonic-gate hubd->h_cpr_cb->cpr.cc_id = callb_add(hubd_cpr_post_user_callb,
72340Sstevel@tonic-gate (void *)hubd->h_cpr_cb, CB_CL_CPR_POST_USER, "hubd");
72350Sstevel@tonic-gate }
72360Sstevel@tonic-gate
72370Sstevel@tonic-gate
72380Sstevel@tonic-gate /* unregister callback with cpr framework */
72390Sstevel@tonic-gate void
hubd_unregister_cpr_callback(hubd_t * hubd)72400Sstevel@tonic-gate hubd_unregister_cpr_callback(hubd_t *hubd)
72410Sstevel@tonic-gate {
72420Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_EVENTS, hubd->h_log_handle,
72430Sstevel@tonic-gate "hubd_unregister_cpr_callback");
72440Sstevel@tonic-gate
72450Sstevel@tonic-gate if (hubd->h_cpr_cb) {
72460Sstevel@tonic-gate (void) callb_delete(hubd->h_cpr_cb->cpr.cc_id);
72470Sstevel@tonic-gate mutex_destroy(&hubd->h_cpr_cb->lockp);
72480Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
72490Sstevel@tonic-gate kmem_free(hubd->h_cpr_cb, sizeof (hubd_cpr_t));
72500Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
72510Sstevel@tonic-gate }
72520Sstevel@tonic-gate }
72530Sstevel@tonic-gate
72540Sstevel@tonic-gate
72550Sstevel@tonic-gate /*
72560Sstevel@tonic-gate * Power management
72570Sstevel@tonic-gate *
72580Sstevel@tonic-gate * create the pm components required for power management
72590Sstevel@tonic-gate */
72600Sstevel@tonic-gate static void
hubd_create_pm_components(dev_info_t * dip,hubd_t * hubd)72610Sstevel@tonic-gate hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd)
72620Sstevel@tonic-gate {
72630Sstevel@tonic-gate hub_power_t *hubpm;
72640Sstevel@tonic-gate
72650Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
72660Sstevel@tonic-gate "hubd_create_pm_components: Begin");
72670Sstevel@tonic-gate
72680Sstevel@tonic-gate /* Allocate the state structure */
72690Sstevel@tonic-gate hubpm = kmem_zalloc(sizeof (hub_power_t), KM_SLEEP);
72700Sstevel@tonic-gate
72710Sstevel@tonic-gate hubd->h_hubpm = hubpm;
72720Sstevel@tonic-gate hubpm->hubp_hubd = hubd;
72730Sstevel@tonic-gate hubpm->hubp_pm_capabilities = 0;
72740Sstevel@tonic-gate hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
72750Sstevel@tonic-gate hubpm->hubp_time_at_full_power = ddi_get_time();
72760Sstevel@tonic-gate hubpm->hubp_min_pm_threshold = hubdi_min_pm_threshold;
72770Sstevel@tonic-gate
72780Sstevel@tonic-gate /* alloc memory to save power states of children */
72790Sstevel@tonic-gate hubpm->hubp_child_pwrstate = (uint8_t *)
72804763Slg150142 kmem_zalloc(MAX_PORTS + 1, KM_SLEEP);
72810Sstevel@tonic-gate
72820Sstevel@tonic-gate /*
72830Sstevel@tonic-gate * if the enable remote wakeup fails
72840Sstevel@tonic-gate * we still want to enable
72850Sstevel@tonic-gate * parent notification so we can PM the children
72860Sstevel@tonic-gate */
72870Sstevel@tonic-gate usb_enable_parent_notification(dip);
72880Sstevel@tonic-gate
72890Sstevel@tonic-gate if (usb_handle_remote_wakeup(dip,
72900Sstevel@tonic-gate USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
72910Sstevel@tonic-gate uint_t pwr_states;
72920Sstevel@tonic-gate
72930Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
72940Sstevel@tonic-gate "hubd_create_pm_components: "
72950Sstevel@tonic-gate "Remote Wakeup Enabled");
72960Sstevel@tonic-gate
72970Sstevel@tonic-gate if (usb_create_pm_components(dip, &pwr_states) ==
72980Sstevel@tonic-gate USB_SUCCESS) {
72990Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
73000Sstevel@tonic-gate hubpm->hubp_wakeup_enabled = 1;
73010Sstevel@tonic-gate hubpm->hubp_pwr_states = (uint8_t)pwr_states;
73020Sstevel@tonic-gate
73030Sstevel@tonic-gate /* we are busy now till end of the attach */
73040Sstevel@tonic-gate hubd_pm_busy_component(hubd, dip, 0);
73050Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
73060Sstevel@tonic-gate
73070Sstevel@tonic-gate /* bring the device to full power */
73080Sstevel@tonic-gate (void) pm_raise_power(dip, 0,
73090Sstevel@tonic-gate USB_DEV_OS_FULL_PWR);
73100Sstevel@tonic-gate }
73110Sstevel@tonic-gate }
73120Sstevel@tonic-gate
73130Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
73140Sstevel@tonic-gate "hubd_create_pm_components: END");
73150Sstevel@tonic-gate }
73160Sstevel@tonic-gate
73170Sstevel@tonic-gate
73180Sstevel@tonic-gate /*
73190Sstevel@tonic-gate * Attachment point management
73200Sstevel@tonic-gate */
73210Sstevel@tonic-gate /* ARGSUSED */
73220Sstevel@tonic-gate int
usba_hubdi_open(dev_info_t * dip,dev_t * devp,int flags,int otyp,cred_t * credp)73230Sstevel@tonic-gate usba_hubdi_open(dev_info_t *dip, dev_t *devp, int flags, int otyp,
73240Sstevel@tonic-gate cred_t *credp)
73250Sstevel@tonic-gate {
73260Sstevel@tonic-gate hubd_t *hubd;
73270Sstevel@tonic-gate
73280Sstevel@tonic-gate if (otyp != OTYP_CHR)
73290Sstevel@tonic-gate return (EINVAL);
73300Sstevel@tonic-gate
73310Sstevel@tonic-gate hubd = hubd_get_soft_state(dip);
73320Sstevel@tonic-gate if (hubd == NULL) {
73330Sstevel@tonic-gate return (ENXIO);
73340Sstevel@tonic-gate }
73350Sstevel@tonic-gate
73360Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
73370Sstevel@tonic-gate "hubd_open:");
73380Sstevel@tonic-gate
73390Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
73400Sstevel@tonic-gate if ((flags & FEXCL) && (hubd->h_softstate & HUBD_SS_ISOPEN)) {
73410Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
73420Sstevel@tonic-gate
73430Sstevel@tonic-gate return (EBUSY);
73440Sstevel@tonic-gate }
73450Sstevel@tonic-gate
73460Sstevel@tonic-gate hubd->h_softstate |= HUBD_SS_ISOPEN;
73470Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
73480Sstevel@tonic-gate
73490Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "opened");
73500Sstevel@tonic-gate
73510Sstevel@tonic-gate return (0);
73520Sstevel@tonic-gate }
73530Sstevel@tonic-gate
73540Sstevel@tonic-gate
73550Sstevel@tonic-gate /* ARGSUSED */
73560Sstevel@tonic-gate int
usba_hubdi_close(dev_info_t * dip,dev_t dev,int flag,int otyp,cred_t * credp)73570Sstevel@tonic-gate usba_hubdi_close(dev_info_t *dip, dev_t dev, int flag, int otyp,
73580Sstevel@tonic-gate cred_t *credp)
73590Sstevel@tonic-gate {
73600Sstevel@tonic-gate hubd_t *hubd;
73610Sstevel@tonic-gate
73620Sstevel@tonic-gate if (otyp != OTYP_CHR) {
73630Sstevel@tonic-gate return (EINVAL);
73640Sstevel@tonic-gate }
73650Sstevel@tonic-gate
73660Sstevel@tonic-gate hubd = hubd_get_soft_state(dip);
73670Sstevel@tonic-gate
73680Sstevel@tonic-gate if (hubd == NULL) {
73690Sstevel@tonic-gate return (ENXIO);
73700Sstevel@tonic-gate }
73710Sstevel@tonic-gate
73720Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "hubd_close:");
73730Sstevel@tonic-gate
73740Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
73750Sstevel@tonic-gate hubd->h_softstate &= ~HUBD_SS_ISOPEN;
73760Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
73770Sstevel@tonic-gate
73780Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle, "closed");
73790Sstevel@tonic-gate
73800Sstevel@tonic-gate return (0);
73810Sstevel@tonic-gate }
73820Sstevel@tonic-gate
73830Sstevel@tonic-gate
73840Sstevel@tonic-gate /*
73850Sstevel@tonic-gate * hubd_ioctl: cfgadm controls
73860Sstevel@tonic-gate */
73870Sstevel@tonic-gate /* ARGSUSED */
73880Sstevel@tonic-gate int
usba_hubdi_ioctl(dev_info_t * self,dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)73890Sstevel@tonic-gate usba_hubdi_ioctl(dev_info_t *self, dev_t dev, int cmd, intptr_t arg,
73900Sstevel@tonic-gate int mode, cred_t *credp, int *rvalp)
73910Sstevel@tonic-gate {
73920Sstevel@tonic-gate int rv = 0;
73930Sstevel@tonic-gate char *msg; /* for messages */
73940Sstevel@tonic-gate hubd_t *hubd;
73950Sstevel@tonic-gate usb_port_t port = 0;
73960Sstevel@tonic-gate dev_info_t *child_dip = NULL;
73970Sstevel@tonic-gate dev_info_t *rh_dip;
73980Sstevel@tonic-gate devctl_ap_state_t ap_state;
73990Sstevel@tonic-gate struct devctl_iocdata *dcp = NULL;
74000Sstevel@tonic-gate usb_pipe_state_t prev_pipe_state = 0;
74010Sstevel@tonic-gate int circ, rh_circ, prh_circ;
74020Sstevel@tonic-gate
74030Sstevel@tonic-gate if ((hubd = hubd_get_soft_state(self)) == NULL) {
74040Sstevel@tonic-gate
74050Sstevel@tonic-gate return (ENXIO);
74060Sstevel@tonic-gate }
74070Sstevel@tonic-gate
74080Sstevel@tonic-gate rh_dip = hubd->h_usba_device->usb_root_hub_dip;
74090Sstevel@tonic-gate
74100Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
74110Sstevel@tonic-gate "usba_hubdi_ioctl: "
74120Sstevel@tonic-gate "cmd=%x, arg=%lx, mode=%x, cred=%p, rval=%p dev=0x%lx",
74136898Sfb209375 cmd, arg, mode, (void *)credp, (void *)rvalp, dev);
74140Sstevel@tonic-gate
74150Sstevel@tonic-gate /* read devctl ioctl data */
74160Sstevel@tonic-gate if ((cmd != DEVCTL_AP_CONTROL) &&
74170Sstevel@tonic-gate (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)) {
74180Sstevel@tonic-gate
74190Sstevel@tonic-gate return (EFAULT);
74200Sstevel@tonic-gate }
74210Sstevel@tonic-gate
74220Sstevel@tonic-gate /*
74230Sstevel@tonic-gate * make sure the hub is connected before trying any
74240Sstevel@tonic-gate * of the following operations:
74250Sstevel@tonic-gate * configure, connect, disconnect
74260Sstevel@tonic-gate */
74270Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
74280Sstevel@tonic-gate
74290Sstevel@tonic-gate switch (cmd) {
74300Sstevel@tonic-gate case DEVCTL_AP_DISCONNECT:
74310Sstevel@tonic-gate case DEVCTL_AP_UNCONFIGURE:
74320Sstevel@tonic-gate case DEVCTL_AP_CONFIGURE:
74330Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_DISCONNECTED) {
74340Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
74350Sstevel@tonic-gate "hubd: already gone");
74360Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
74370Sstevel@tonic-gate if (dcp) {
74380Sstevel@tonic-gate ndi_dc_freehdl(dcp);
74390Sstevel@tonic-gate }
74400Sstevel@tonic-gate
74410Sstevel@tonic-gate return (EIO);
74420Sstevel@tonic-gate }
74430Sstevel@tonic-gate
74440Sstevel@tonic-gate /* FALLTHROUGH */
74450Sstevel@tonic-gate case DEVCTL_AP_GETSTATE:
74460Sstevel@tonic-gate if ((port = hubd_get_port_num(hubd, dcp)) == 0) {
74470Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
74480Sstevel@tonic-gate "hubd: bad port");
74490Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
74500Sstevel@tonic-gate if (dcp) {
74510Sstevel@tonic-gate ndi_dc_freehdl(dcp);
74520Sstevel@tonic-gate }
74530Sstevel@tonic-gate
74540Sstevel@tonic-gate return (EINVAL);
74550Sstevel@tonic-gate }
74560Sstevel@tonic-gate break;
74570Sstevel@tonic-gate
74580Sstevel@tonic-gate case DEVCTL_AP_CONTROL:
74590Sstevel@tonic-gate
74600Sstevel@tonic-gate break;
74610Sstevel@tonic-gate default:
74620Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
74630Sstevel@tonic-gate if (dcp) {
74640Sstevel@tonic-gate ndi_dc_freehdl(dcp);
74650Sstevel@tonic-gate }
74660Sstevel@tonic-gate
74670Sstevel@tonic-gate return (ENOTTY);
74680Sstevel@tonic-gate }
74690Sstevel@tonic-gate
74700Sstevel@tonic-gate /* should not happen, just in case */
74710Sstevel@tonic-gate if (hubd->h_dev_state == USB_DEV_SUSPENDED) {
74720Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
74730Sstevel@tonic-gate if (dcp) {
74740Sstevel@tonic-gate ndi_dc_freehdl(dcp);
74750Sstevel@tonic-gate }
74760Sstevel@tonic-gate
74770Sstevel@tonic-gate return (EIO);
74780Sstevel@tonic-gate }
74790Sstevel@tonic-gate
74804844Slg150142 if (hubd->h_reset_port[port]) {
74814844Slg150142 USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle,
74824844Slg150142 "This port is resetting, just return");
74834844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
74844844Slg150142 if (dcp) {
74854844Slg150142 ndi_dc_freehdl(dcp);
74864844Slg150142 }
74874844Slg150142
74884844Slg150142 return (EIO);
74894844Slg150142 }
74904844Slg150142
74910Sstevel@tonic-gate hubd_pm_busy_component(hubd, hubd->h_dip, 0);
74920Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
74930Sstevel@tonic-gate
74940Sstevel@tonic-gate /* go full power */
74950Sstevel@tonic-gate (void) pm_raise_power(hubd->h_dip, 0, USB_DEV_OS_FULL_PWR);
74960Sstevel@tonic-gate
74970Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
74980Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ);
74990Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ);
75000Sstevel@tonic-gate
75010Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
75020Sstevel@tonic-gate
75034763Slg150142 hubd->h_hotplug_thread++;
75044763Slg150142
75054763Slg150142 /* stop polling if it was active */
75060Sstevel@tonic-gate if (hubd->h_ep1_ph) {
75070Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
75080Sstevel@tonic-gate (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state,
75094763Slg150142 USB_FLAGS_SLEEP);
75100Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
75110Sstevel@tonic-gate
75120Sstevel@tonic-gate if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) {
75130Sstevel@tonic-gate hubd_stop_polling(hubd);
75140Sstevel@tonic-gate }
75150Sstevel@tonic-gate }
75160Sstevel@tonic-gate
75170Sstevel@tonic-gate switch (cmd) {
75180Sstevel@tonic-gate case DEVCTL_AP_DISCONNECT:
75190Sstevel@tonic-gate if (hubd_delete_child(hubd, port,
75200Sstevel@tonic-gate NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS) {
75210Sstevel@tonic-gate rv = EIO;
75220Sstevel@tonic-gate }
75230Sstevel@tonic-gate
75240Sstevel@tonic-gate break;
75250Sstevel@tonic-gate case DEVCTL_AP_UNCONFIGURE:
75260Sstevel@tonic-gate if (hubd_delete_child(hubd, port,
75270Sstevel@tonic-gate NDI_UNCONFIG, B_FALSE) != USB_SUCCESS) {
75280Sstevel@tonic-gate rv = EIO;
75290Sstevel@tonic-gate }
75300Sstevel@tonic-gate
75310Sstevel@tonic-gate break;
75320Sstevel@tonic-gate case DEVCTL_AP_CONFIGURE:
75330Sstevel@tonic-gate /* toggle port */
75340Sstevel@tonic-gate if (hubd_toggle_port(hubd, port) != USB_SUCCESS) {
75350Sstevel@tonic-gate rv = EIO;
75360Sstevel@tonic-gate
75370Sstevel@tonic-gate break;
75380Sstevel@tonic-gate }
75390Sstevel@tonic-gate
75400Sstevel@tonic-gate (void) hubd_handle_port_connect(hubd, port);
75410Sstevel@tonic-gate child_dip = hubd_get_child_dip(hubd, port);
75420Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
75430Sstevel@tonic-gate
75440Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ);
75450Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ);
75460Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
754711214SVincent.Wang@Sun.COM if (child_dip == NULL) {
75480Sstevel@tonic-gate rv = EIO;
754911214SVincent.Wang@Sun.COM } else {
755011214SVincent.Wang@Sun.COM ndi_hold_devi(child_dip);
755111214SVincent.Wang@Sun.COM if (ndi_devi_online(child_dip, 0) != NDI_SUCCESS)
755211214SVincent.Wang@Sun.COM rv = EIO;
755311214SVincent.Wang@Sun.COM ndi_rele_devi(child_dip);
75540Sstevel@tonic-gate }
75550Sstevel@tonic-gate ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
75560Sstevel@tonic-gate ndi_devi_enter(rh_dip, &rh_circ);
75570Sstevel@tonic-gate ndi_devi_enter(hubd->h_dip, &circ);
75580Sstevel@tonic-gate
75590Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
75600Sstevel@tonic-gate
75610Sstevel@tonic-gate break;
75620Sstevel@tonic-gate case DEVCTL_AP_GETSTATE:
75630Sstevel@tonic-gate switch (hubd_cfgadm_state(hubd, port)) {
75640Sstevel@tonic-gate case HUBD_CFGADM_DISCONNECTED:
75650Sstevel@tonic-gate /* port previously 'disconnected' by cfgadm */
75660Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_DISCONNECTED;
75670Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
75680Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK;
75690Sstevel@tonic-gate
75700Sstevel@tonic-gate break;
75710Sstevel@tonic-gate case HUBD_CFGADM_UNCONFIGURED:
75720Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_CONNECTED;
75730Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
75740Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK;
75750Sstevel@tonic-gate
75760Sstevel@tonic-gate break;
75770Sstevel@tonic-gate case HUBD_CFGADM_CONFIGURED:
75780Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_CONNECTED;
75790Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
75800Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK;
75810Sstevel@tonic-gate
75820Sstevel@tonic-gate break;
75830Sstevel@tonic-gate case HUBD_CFGADM_STILL_REFERENCED:
75840Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_EMPTY;
75850Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_CONFIGURED;
75860Sstevel@tonic-gate ap_state.ap_condition = AP_COND_UNUSABLE;
75870Sstevel@tonic-gate
75880Sstevel@tonic-gate break;
75890Sstevel@tonic-gate case HUBD_CFGADM_EMPTY:
75900Sstevel@tonic-gate default:
75910Sstevel@tonic-gate ap_state.ap_rstate = AP_RSTATE_EMPTY;
75920Sstevel@tonic-gate ap_state.ap_ostate = AP_OSTATE_UNCONFIGURED;
75930Sstevel@tonic-gate ap_state.ap_condition = AP_COND_OK;
75940Sstevel@tonic-gate
75950Sstevel@tonic-gate break;
75960Sstevel@tonic-gate }
75970Sstevel@tonic-gate
75980Sstevel@tonic-gate ap_state.ap_last_change = (time_t)-1;
75990Sstevel@tonic-gate ap_state.ap_error_code = 0;
76000Sstevel@tonic-gate ap_state.ap_in_transition = 0;
76010Sstevel@tonic-gate
76020Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
76030Sstevel@tonic-gate "DEVCTL_AP_GETSTATE: "
76040Sstevel@tonic-gate "ostate=0x%x, rstate=0x%x, condition=0x%x",
76050Sstevel@tonic-gate ap_state.ap_ostate,
76060Sstevel@tonic-gate ap_state.ap_rstate, ap_state.ap_condition);
76070Sstevel@tonic-gate
76080Sstevel@tonic-gate /* copy the return-AP-state information to the user space */
76090Sstevel@tonic-gate if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) {
76100Sstevel@tonic-gate rv = EFAULT;
76110Sstevel@tonic-gate }
76120Sstevel@tonic-gate
76130Sstevel@tonic-gate break;
76140Sstevel@tonic-gate case DEVCTL_AP_CONTROL:
76150Sstevel@tonic-gate {
76160Sstevel@tonic-gate /*
76170Sstevel@tonic-gate * Generic devctl for hardware-specific functionality.
76180Sstevel@tonic-gate * For list of sub-commands see hubd_impl.h
76190Sstevel@tonic-gate */
76200Sstevel@tonic-gate hubd_ioctl_data_t ioc; /* for 64 byte copies */
76210Sstevel@tonic-gate
76220Sstevel@tonic-gate /* copy user ioctl data in first */
76230Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL
76240Sstevel@tonic-gate if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
76250Sstevel@tonic-gate hubd_ioctl_data_32_t ioc32;
76260Sstevel@tonic-gate
76270Sstevel@tonic-gate if (ddi_copyin((void *)arg, (void *)&ioc32,
76284763Slg150142 sizeof (ioc32), mode) != 0) {
76290Sstevel@tonic-gate rv = EFAULT;
76300Sstevel@tonic-gate
76310Sstevel@tonic-gate break;
76320Sstevel@tonic-gate }
76330Sstevel@tonic-gate ioc.cmd = (uint_t)ioc32.cmd;
76340Sstevel@tonic-gate ioc.port = (uint_t)ioc32.port;
76350Sstevel@tonic-gate ioc.get_size = (uint_t)ioc32.get_size;
76360Sstevel@tonic-gate ioc.buf = (caddr_t)(uintptr_t)ioc32.buf;
76370Sstevel@tonic-gate ioc.bufsiz = (uint_t)ioc32.bufsiz;
76380Sstevel@tonic-gate ioc.misc_arg = (uint_t)ioc32.misc_arg;
76390Sstevel@tonic-gate } else
76400Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */
76410Sstevel@tonic-gate if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc),
76420Sstevel@tonic-gate mode) != 0) {
76430Sstevel@tonic-gate rv = EFAULT;
76440Sstevel@tonic-gate
76450Sstevel@tonic-gate break;
76460Sstevel@tonic-gate }
76470Sstevel@tonic-gate
76480Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle,
76490Sstevel@tonic-gate "DEVCTL_AP_CONTROL: ioc: cmd=0x%x port=%d get_size=%d"
76500Sstevel@tonic-gate "\n\tbuf=0x%p, bufsiz=%d, misc_arg=%d", ioc.cmd,
76516898Sfb209375 ioc.port, ioc.get_size, (void *)ioc.buf, ioc.bufsiz,
76526898Sfb209375 ioc.misc_arg);
76530Sstevel@tonic-gate
76540Sstevel@tonic-gate /*
76550Sstevel@tonic-gate * To avoid BE/LE and 32/64 issues, a get_size always
76560Sstevel@tonic-gate * returns a 32-bit number.
76570Sstevel@tonic-gate */
76580Sstevel@tonic-gate if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) {
76590Sstevel@tonic-gate rv = EINVAL;
76600Sstevel@tonic-gate
76610Sstevel@tonic-gate break;
76620Sstevel@tonic-gate }
76630Sstevel@tonic-gate
76640Sstevel@tonic-gate switch (ioc.cmd) {
76650Sstevel@tonic-gate case USB_DESCR_TYPE_DEV:
76660Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_DEVICE_DESC";
76670Sstevel@tonic-gate if (ioc.get_size) {
76680Sstevel@tonic-gate /* uint32 so this works 32/64 */
76690Sstevel@tonic-gate uint32_t size = sizeof (usb_dev_descr_t);
76700Sstevel@tonic-gate
76710Sstevel@tonic-gate if (ddi_copyout((void *)&size, ioc.buf,
76720Sstevel@tonic-gate ioc.bufsiz, mode) != 0) {
76730Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
76740Sstevel@tonic-gate hubd->h_log_handle,
76750Sstevel@tonic-gate "%s: get_size copyout failed", msg);
76760Sstevel@tonic-gate rv = EIO;
76770Sstevel@tonic-gate
76780Sstevel@tonic-gate break;
76790Sstevel@tonic-gate }
76800Sstevel@tonic-gate } else { /* send out the actual descr */
76810Sstevel@tonic-gate usb_dev_descr_t *dev_descrp;
76820Sstevel@tonic-gate
76830Sstevel@tonic-gate /* check child_dip */
76840Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd,
76850Sstevel@tonic-gate ioc.port)) == NULL) {
76860Sstevel@tonic-gate rv = EINVAL;
76870Sstevel@tonic-gate
76880Sstevel@tonic-gate break;
76890Sstevel@tonic-gate }
76900Sstevel@tonic-gate
76910Sstevel@tonic-gate dev_descrp = usb_get_dev_descr(child_dip);
76920Sstevel@tonic-gate if (ioc.bufsiz != sizeof (*dev_descrp)) {
76930Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
76940Sstevel@tonic-gate hubd->h_log_handle,
76950Sstevel@tonic-gate "%s: bufsize passed (%d) != sizeof "
76960Sstevel@tonic-gate "usba_device_descr_t (%d)", msg,
76970Sstevel@tonic-gate ioc.bufsiz, dev_descrp->bLength);
76980Sstevel@tonic-gate rv = EINVAL;
76990Sstevel@tonic-gate
77000Sstevel@tonic-gate break;
77010Sstevel@tonic-gate }
77020Sstevel@tonic-gate
77030Sstevel@tonic-gate if (ddi_copyout((void *)dev_descrp,
77040Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) {
77050Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
77060Sstevel@tonic-gate hubd->h_log_handle,
77070Sstevel@tonic-gate "%s: copyout failed.", msg);
77080Sstevel@tonic-gate rv = EIO;
77090Sstevel@tonic-gate
77100Sstevel@tonic-gate break;
77110Sstevel@tonic-gate }
77120Sstevel@tonic-gate }
77130Sstevel@tonic-gate break;
77140Sstevel@tonic-gate case USB_DESCR_TYPE_STRING:
77150Sstevel@tonic-gate {
77160Sstevel@tonic-gate char *str;
77170Sstevel@tonic-gate uint32_t size;
77180Sstevel@tonic-gate usba_device_t *usba_device;
77190Sstevel@tonic-gate
77200Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_STRING_DESCR";
77210Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
77220Sstevel@tonic-gate "%s: string request: %d", msg, ioc.misc_arg);
77230Sstevel@tonic-gate
77240Sstevel@tonic-gate /* recheck */
77250Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
77260Sstevel@tonic-gate NULL) {
77270Sstevel@tonic-gate rv = EINVAL;
77280Sstevel@tonic-gate
77290Sstevel@tonic-gate break;
77300Sstevel@tonic-gate }
77310Sstevel@tonic-gate usba_device = usba_get_usba_device(child_dip);
77320Sstevel@tonic-gate
77330Sstevel@tonic-gate switch (ioc.misc_arg) {
77340Sstevel@tonic-gate case HUBD_MFG_STR:
77350Sstevel@tonic-gate str = usba_device->usb_mfg_str;
77360Sstevel@tonic-gate
77370Sstevel@tonic-gate break;
77380Sstevel@tonic-gate case HUBD_PRODUCT_STR:
77390Sstevel@tonic-gate str = usba_device->usb_product_str;
77400Sstevel@tonic-gate
77410Sstevel@tonic-gate break;
77420Sstevel@tonic-gate case HUBD_SERIALNO_STR:
77430Sstevel@tonic-gate str = usba_device->usb_serialno_str;
77440Sstevel@tonic-gate
77450Sstevel@tonic-gate break;
77460Sstevel@tonic-gate case HUBD_CFG_DESCR_STR:
77470Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex);
77480Sstevel@tonic-gate str = usba_device->usb_cfg_str_descr[
77494763Slg150142 usba_device->usb_active_cfg_ndx];
77500Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex);
77510Sstevel@tonic-gate
77520Sstevel@tonic-gate break;
77530Sstevel@tonic-gate default:
77540Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
77550Sstevel@tonic-gate hubd->h_log_handle,
77560Sstevel@tonic-gate "%s: Invalid string request", msg);
77570Sstevel@tonic-gate rv = EINVAL;
77580Sstevel@tonic-gate
77590Sstevel@tonic-gate break;
77600Sstevel@tonic-gate } /* end of switch */
77610Sstevel@tonic-gate
77620Sstevel@tonic-gate if (rv != 0) {
77630Sstevel@tonic-gate
77640Sstevel@tonic-gate break;
77650Sstevel@tonic-gate }
77660Sstevel@tonic-gate
77670Sstevel@tonic-gate size = (str != NULL) ? strlen(str) + 1 : 0;
77680Sstevel@tonic-gate if (ioc.get_size) {
77690Sstevel@tonic-gate if (ddi_copyout((void *)&size, ioc.buf,
77700Sstevel@tonic-gate ioc.bufsiz, mode) != 0) {
77710Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
77720Sstevel@tonic-gate hubd->h_log_handle,
77730Sstevel@tonic-gate "%s: copyout of size failed.", msg);
77740Sstevel@tonic-gate rv = EIO;
77750Sstevel@tonic-gate
77760Sstevel@tonic-gate break;
77770Sstevel@tonic-gate }
77780Sstevel@tonic-gate } else {
77790Sstevel@tonic-gate if (size == 0) {
77800Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS,
77810Sstevel@tonic-gate hubd->h_log_handle,
77820Sstevel@tonic-gate "%s: String is NULL", msg);
77830Sstevel@tonic-gate rv = EINVAL;
77840Sstevel@tonic-gate
77850Sstevel@tonic-gate break;
77860Sstevel@tonic-gate }
77870Sstevel@tonic-gate
77880Sstevel@tonic-gate if (ioc.bufsiz != size) {
77890Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
77900Sstevel@tonic-gate hubd->h_log_handle,
77910Sstevel@tonic-gate "%s: string buf size wrong", msg);
77920Sstevel@tonic-gate rv = EINVAL;
77930Sstevel@tonic-gate
77940Sstevel@tonic-gate break;
77950Sstevel@tonic-gate }
77960Sstevel@tonic-gate
77970Sstevel@tonic-gate if (ddi_copyout((void *)str, ioc.buf,
77980Sstevel@tonic-gate ioc.bufsiz, mode) != 0) {
77990Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
78000Sstevel@tonic-gate hubd->h_log_handle,
78010Sstevel@tonic-gate "%s: copyout failed.", msg);
78020Sstevel@tonic-gate rv = EIO;
78030Sstevel@tonic-gate
78040Sstevel@tonic-gate break;
78050Sstevel@tonic-gate }
78060Sstevel@tonic-gate }
78070Sstevel@tonic-gate break;
78080Sstevel@tonic-gate }
78090Sstevel@tonic-gate case HUBD_GET_CFGADM_NAME:
78100Sstevel@tonic-gate {
78110Sstevel@tonic-gate uint32_t name_len;
78120Sstevel@tonic-gate const char *name;
78130Sstevel@tonic-gate
78140Sstevel@tonic-gate /* recheck */
78150Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
78160Sstevel@tonic-gate NULL) {
78170Sstevel@tonic-gate rv = EINVAL;
78180Sstevel@tonic-gate
78190Sstevel@tonic-gate break;
78200Sstevel@tonic-gate }
78210Sstevel@tonic-gate name = ddi_node_name(child_dip);
78220Sstevel@tonic-gate if (name == NULL) {
78230Sstevel@tonic-gate name = "unsupported";
78240Sstevel@tonic-gate }
78250Sstevel@tonic-gate name_len = strlen(name) + 1;
78260Sstevel@tonic-gate
78270Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: HUBD_GET_CFGADM_NAME";
78280Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
78290Sstevel@tonic-gate "%s: name=%s name_len=%d", msg, name, name_len);
78300Sstevel@tonic-gate
78310Sstevel@tonic-gate if (ioc.get_size) {
78320Sstevel@tonic-gate if (ddi_copyout((void *)&name_len,
78330Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) {
78340Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
78350Sstevel@tonic-gate hubd->h_log_handle,
78360Sstevel@tonic-gate "%s: copyout of size failed", msg);
78370Sstevel@tonic-gate rv = EIO;
78380Sstevel@tonic-gate
78390Sstevel@tonic-gate break;
78400Sstevel@tonic-gate }
78410Sstevel@tonic-gate } else {
78420Sstevel@tonic-gate if (ioc.bufsiz != name_len) {
78430Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
78440Sstevel@tonic-gate hubd->h_log_handle,
78450Sstevel@tonic-gate "%s: string buf length wrong", msg);
78460Sstevel@tonic-gate rv = EINVAL;
78470Sstevel@tonic-gate
78480Sstevel@tonic-gate break;
78490Sstevel@tonic-gate }
78500Sstevel@tonic-gate
78510Sstevel@tonic-gate if (ddi_copyout((void *)name, ioc.buf,
78520Sstevel@tonic-gate ioc.bufsiz, mode) != 0) {
78530Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
78540Sstevel@tonic-gate hubd->h_log_handle,
78550Sstevel@tonic-gate "%s: copyout failed.", msg);
78560Sstevel@tonic-gate rv = EIO;
78570Sstevel@tonic-gate
78580Sstevel@tonic-gate break;
78590Sstevel@tonic-gate }
78600Sstevel@tonic-gate }
78610Sstevel@tonic-gate
78620Sstevel@tonic-gate break;
78630Sstevel@tonic-gate }
78640Sstevel@tonic-gate
78650Sstevel@tonic-gate /*
78660Sstevel@tonic-gate * Return the config index for the currently-configured
78670Sstevel@tonic-gate * configuration.
78680Sstevel@tonic-gate */
78690Sstevel@tonic-gate case HUBD_GET_CURRENT_CONFIG:
78700Sstevel@tonic-gate {
78710Sstevel@tonic-gate uint_t config_index;
78720Sstevel@tonic-gate uint32_t size = sizeof (config_index);
78730Sstevel@tonic-gate usba_device_t *usba_device;
78740Sstevel@tonic-gate
78750Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_CURRENT_CONFIG";
78760Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
78770Sstevel@tonic-gate "%s", msg);
78780Sstevel@tonic-gate
78790Sstevel@tonic-gate /*
78800Sstevel@tonic-gate * Return the config index for the configuration
78810Sstevel@tonic-gate * currently in use.
78820Sstevel@tonic-gate * Recheck if child_dip exists
78830Sstevel@tonic-gate */
78840Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
78850Sstevel@tonic-gate NULL) {
78860Sstevel@tonic-gate rv = EINVAL;
78870Sstevel@tonic-gate
78880Sstevel@tonic-gate break;
78890Sstevel@tonic-gate }
78900Sstevel@tonic-gate
78910Sstevel@tonic-gate usba_device = usba_get_usba_device(child_dip);
78920Sstevel@tonic-gate mutex_enter(&usba_device->usb_mutex);
78930Sstevel@tonic-gate config_index = usba_device->usb_active_cfg_ndx;
78940Sstevel@tonic-gate mutex_exit(&usba_device->usb_mutex);
78950Sstevel@tonic-gate
78960Sstevel@tonic-gate if (ioc.get_size) {
78970Sstevel@tonic-gate if (ddi_copyout((void *)&size,
78980Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) {
78990Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79000Sstevel@tonic-gate hubd->h_log_handle,
79010Sstevel@tonic-gate "%s: copyout of size failed.", msg);
79020Sstevel@tonic-gate rv = EIO;
79030Sstevel@tonic-gate
79040Sstevel@tonic-gate break;
79050Sstevel@tonic-gate }
79060Sstevel@tonic-gate } else {
79070Sstevel@tonic-gate if (ioc.bufsiz != size) {
79080Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79090Sstevel@tonic-gate hubd->h_log_handle,
79100Sstevel@tonic-gate "%s: buffer size wrong", msg);
79110Sstevel@tonic-gate rv = EINVAL;
79120Sstevel@tonic-gate
79130Sstevel@tonic-gate break;
79140Sstevel@tonic-gate }
79150Sstevel@tonic-gate if (ddi_copyout((void *)&config_index,
79160Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) {
79170Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79180Sstevel@tonic-gate hubd->h_log_handle,
79190Sstevel@tonic-gate "%s: copyout failed", msg);
79200Sstevel@tonic-gate rv = EIO;
79210Sstevel@tonic-gate }
79220Sstevel@tonic-gate }
79230Sstevel@tonic-gate
79240Sstevel@tonic-gate break;
79250Sstevel@tonic-gate }
79260Sstevel@tonic-gate case HUBD_GET_DEVICE_PATH:
79270Sstevel@tonic-gate {
79280Sstevel@tonic-gate char *path;
79290Sstevel@tonic-gate uint32_t size;
79300Sstevel@tonic-gate
79310Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: GET_DEVICE_PATH";
79320Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
79330Sstevel@tonic-gate "%s", msg);
79340Sstevel@tonic-gate
79350Sstevel@tonic-gate /* Recheck if child_dip exists */
79360Sstevel@tonic-gate if ((child_dip = hubd_get_child_dip(hubd, ioc.port)) ==
79370Sstevel@tonic-gate NULL) {
79380Sstevel@tonic-gate rv = EINVAL;
79390Sstevel@tonic-gate
79400Sstevel@tonic-gate break;
79410Sstevel@tonic-gate }
79420Sstevel@tonic-gate
79430Sstevel@tonic-gate /* ddi_pathname doesn't supply /devices, so we do. */
79440Sstevel@tonic-gate path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
79450Sstevel@tonic-gate (void) strcpy(path, "/devices");
79460Sstevel@tonic-gate (void) ddi_pathname(child_dip, path + strlen(path));
79470Sstevel@tonic-gate size = strlen(path) + 1;
79480Sstevel@tonic-gate
79490Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
79500Sstevel@tonic-gate "%s: device path=%s size=%d", msg, path, size);
79510Sstevel@tonic-gate
79520Sstevel@tonic-gate if (ioc.get_size) {
79530Sstevel@tonic-gate if (ddi_copyout((void *)&size,
79540Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) {
79550Sstevel@tonic-gate
79560Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79570Sstevel@tonic-gate hubd->h_log_handle,
79580Sstevel@tonic-gate "%s: copyout of size failed.", msg);
79590Sstevel@tonic-gate rv = EIO;
79600Sstevel@tonic-gate }
79610Sstevel@tonic-gate } else {
79620Sstevel@tonic-gate if (ioc.bufsiz != size) {
79630Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79640Sstevel@tonic-gate hubd->h_log_handle,
79650Sstevel@tonic-gate "%s: buffer wrong size.", msg);
79660Sstevel@tonic-gate rv = EINVAL;
79670Sstevel@tonic-gate } else if (ddi_copyout((void *)path,
79680Sstevel@tonic-gate ioc.buf, ioc.bufsiz, mode) != 0) {
79690Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79700Sstevel@tonic-gate hubd->h_log_handle,
79710Sstevel@tonic-gate "%s: copyout failed.", msg);
79720Sstevel@tonic-gate rv = EIO;
79730Sstevel@tonic-gate }
79740Sstevel@tonic-gate }
79750Sstevel@tonic-gate kmem_free(path, MAXPATHLEN);
79760Sstevel@tonic-gate
79770Sstevel@tonic-gate break;
79780Sstevel@tonic-gate }
79790Sstevel@tonic-gate case HUBD_REFRESH_DEVDB:
79800Sstevel@tonic-gate msg = "DEVCTL_AP_CONTROL: HUBD_REFRESH_DEVDB";
79810Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_CBOPS, hubd->h_log_handle,
79820Sstevel@tonic-gate "%s", msg);
79830Sstevel@tonic-gate
79840Sstevel@tonic-gate if ((rv = usba_devdb_refresh()) != USB_SUCCESS) {
79850Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS,
79860Sstevel@tonic-gate hubd->h_log_handle,
79870Sstevel@tonic-gate "%s: Failed: %d", msg, rv);
79880Sstevel@tonic-gate rv = EIO;
79890Sstevel@tonic-gate }
79900Sstevel@tonic-gate
79910Sstevel@tonic-gate break;
79920Sstevel@tonic-gate default:
79930Sstevel@tonic-gate rv = ENOTSUP;
79940Sstevel@tonic-gate } /* end switch */
79950Sstevel@tonic-gate
79960Sstevel@tonic-gate break;
79970Sstevel@tonic-gate }
79980Sstevel@tonic-gate
79990Sstevel@tonic-gate default:
80000Sstevel@tonic-gate rv = ENOTTY;
80010Sstevel@tonic-gate }
80020Sstevel@tonic-gate
80030Sstevel@tonic-gate if (dcp) {
80040Sstevel@tonic-gate ndi_dc_freehdl(dcp);
80050Sstevel@tonic-gate }
80060Sstevel@tonic-gate
80070Sstevel@tonic-gate /* allow hotplug thread now */
80080Sstevel@tonic-gate hubd->h_hotplug_thread--;
80090Sstevel@tonic-gate
80100Sstevel@tonic-gate if ((hubd->h_dev_state == USB_DEV_ONLINE) &&
80110Sstevel@tonic-gate hubd->h_ep1_ph && (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) {
80120Sstevel@tonic-gate hubd_start_polling(hubd, 0);
80130Sstevel@tonic-gate }
80140Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
80150Sstevel@tonic-gate
80160Sstevel@tonic-gate ndi_devi_exit(hubd->h_dip, circ);
80170Sstevel@tonic-gate ndi_devi_exit(rh_dip, rh_circ);
80180Sstevel@tonic-gate ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
80190Sstevel@tonic-gate
80200Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
80210Sstevel@tonic-gate hubd_pm_idle_component(hubd, hubd->h_dip, 0);
80220Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
80230Sstevel@tonic-gate
80240Sstevel@tonic-gate return (rv);
80250Sstevel@tonic-gate }
80260Sstevel@tonic-gate
80270Sstevel@tonic-gate
80280Sstevel@tonic-gate /*
80290Sstevel@tonic-gate * Helper func used only to help construct the names for the attachment point
80300Sstevel@tonic-gate * minor nodes. Used only in usba_hubdi_attach.
80310Sstevel@tonic-gate * Returns whether it found ancestry or not (USB_SUCCESS if yes).
80320Sstevel@tonic-gate * ports between the root hub and the device represented by dip.
80330Sstevel@tonic-gate * E.g., "2.4.3.1" means this device is
80340Sstevel@tonic-gate * plugged into port 1 of a hub that is
80350Sstevel@tonic-gate * plugged into port 3 of a hub that is
80360Sstevel@tonic-gate * plugged into port 4 of a hub that is
80370Sstevel@tonic-gate * plugged into port 2 of the root hub.
80380Sstevel@tonic-gate * NOTE: Max ap_id path len is HUBD_APID_NAMELEN (32 chars), which is
80390Sstevel@tonic-gate * more than sufficient (as hubs are a max 6 levels deep, port needs 3
80400Sstevel@tonic-gate * chars plus NULL each)
80410Sstevel@tonic-gate */
80429430SRaymond.Chen@Sun.COM void
hubd_get_ancestry_str(hubd_t * hubd)80430Sstevel@tonic-gate hubd_get_ancestry_str(hubd_t *hubd)
80440Sstevel@tonic-gate {
80459430SRaymond.Chen@Sun.COM char ap_name[HUBD_APID_NAMELEN];
80469430SRaymond.Chen@Sun.COM dev_info_t *pdip;
80479430SRaymond.Chen@Sun.COM hubd_t *phubd;
80489430SRaymond.Chen@Sun.COM usb_port_t port;
80490Sstevel@tonic-gate
80500Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
80516898Sfb209375 "hubd_get_ancestry_str: hubd=0x%p", (void *)hubd);
80520Sstevel@tonic-gate
80539430SRaymond.Chen@Sun.COM ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
80549430SRaymond.Chen@Sun.COM
80559430SRaymond.Chen@Sun.COM /*
80569430SRaymond.Chen@Sun.COM * The function is extended to support wire adapter class
80579430SRaymond.Chen@Sun.COM * devices introduced by WUSB spec. The node name is no
80589430SRaymond.Chen@Sun.COM * longer "hub" only.
80599430SRaymond.Chen@Sun.COM * Generate the ap_id str based on the parent and child
80609430SRaymond.Chen@Sun.COM * relationship instead of retrieving it from the hub
80619430SRaymond.Chen@Sun.COM * device path, which simplifies the algorithm.
80629430SRaymond.Chen@Sun.COM */
80639430SRaymond.Chen@Sun.COM if (usba_is_root_hub(hubd->h_dip)) {
80649430SRaymond.Chen@Sun.COM hubd->h_ancestry_str[0] = '\0';
80659430SRaymond.Chen@Sun.COM } else {
80669430SRaymond.Chen@Sun.COM port = hubd->h_usba_device->usb_port;
80679430SRaymond.Chen@Sun.COM mutex_exit(HUBD_MUTEX(hubd));
80689430SRaymond.Chen@Sun.COM
80699430SRaymond.Chen@Sun.COM pdip = ddi_get_parent(hubd->h_dip);
80700Sstevel@tonic-gate /*
80719430SRaymond.Chen@Sun.COM * The parent of wire adapter device might be usb_mid.
80729430SRaymond.Chen@Sun.COM * Need to look further up for hub device
80730Sstevel@tonic-gate */
80749430SRaymond.Chen@Sun.COM if (strcmp(ddi_driver_name(pdip), "usb_mid") == 0) {
80759430SRaymond.Chen@Sun.COM pdip = ddi_get_parent(pdip);
80769430SRaymond.Chen@Sun.COM ASSERT(pdip != NULL);
80779430SRaymond.Chen@Sun.COM }
80789430SRaymond.Chen@Sun.COM
80799430SRaymond.Chen@Sun.COM phubd = hubd_get_soft_state(pdip);
80809430SRaymond.Chen@Sun.COM
80819430SRaymond.Chen@Sun.COM mutex_enter(HUBD_MUTEX(phubd));
80829430SRaymond.Chen@Sun.COM (void) snprintf(ap_name, HUBD_APID_NAMELEN, "%s%d",
80839430SRaymond.Chen@Sun.COM phubd->h_ancestry_str, port);
80849430SRaymond.Chen@Sun.COM mutex_exit(HUBD_MUTEX(phubd));
80859430SRaymond.Chen@Sun.COM
80869430SRaymond.Chen@Sun.COM mutex_enter(HUBD_MUTEX(hubd));
80879430SRaymond.Chen@Sun.COM (void) strcpy(hubd->h_ancestry_str, ap_name);
80880Sstevel@tonic-gate (void) strcat(hubd->h_ancestry_str, ".");
80890Sstevel@tonic-gate }
80900Sstevel@tonic-gate }
80910Sstevel@tonic-gate
80920Sstevel@tonic-gate
80930Sstevel@tonic-gate /* Get which port to operate on. */
80940Sstevel@tonic-gate static usb_port_t
hubd_get_port_num(hubd_t * hubd,struct devctl_iocdata * dcp)80950Sstevel@tonic-gate hubd_get_port_num(hubd_t *hubd, struct devctl_iocdata *dcp)
80960Sstevel@tonic-gate {
80970Sstevel@tonic-gate int32_t port;
80980Sstevel@tonic-gate
80990Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
81000Sstevel@tonic-gate
81010Sstevel@tonic-gate /* Get which port to operate on. */
81020Sstevel@tonic-gate if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) {
81030Sstevel@tonic-gate USB_DPRINTF_L2(DPRINT_MASK_CBOPS, hubd->h_log_handle,
81040Sstevel@tonic-gate "hubd_get_port_num: port lookup failed");
81050Sstevel@tonic-gate port = 0;
81060Sstevel@tonic-gate }
81070Sstevel@tonic-gate
81080Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
81096898Sfb209375 "hubd_get_port_num: hubd=0x%p, port=%d", (void *)hubd, port);
81100Sstevel@tonic-gate
81110Sstevel@tonic-gate return ((usb_port_t)port);
81120Sstevel@tonic-gate }
81130Sstevel@tonic-gate
81140Sstevel@tonic-gate
81150Sstevel@tonic-gate /* check if child still exists */
81160Sstevel@tonic-gate static dev_info_t *
hubd_get_child_dip(hubd_t * hubd,usb_port_t port)81170Sstevel@tonic-gate hubd_get_child_dip(hubd_t *hubd, usb_port_t port)
81180Sstevel@tonic-gate {
81190Sstevel@tonic-gate dev_info_t *child_dip = hubd->h_children_dips[port];
81200Sstevel@tonic-gate
81210Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
81226898Sfb209375 "hubd_get_child_dip: hubd=0x%p, port=%d", (void *)hubd, port);
81230Sstevel@tonic-gate
81240Sstevel@tonic-gate ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
81250Sstevel@tonic-gate
81260Sstevel@tonic-gate return (child_dip);
81270Sstevel@tonic-gate }
81280Sstevel@tonic-gate
81290Sstevel@tonic-gate
81300Sstevel@tonic-gate /*
81310Sstevel@tonic-gate * hubd_cfgadm_state:
81320Sstevel@tonic-gate *
81330Sstevel@tonic-gate * child_dip list port_state cfgadm_state
81340Sstevel@tonic-gate * -------------- ---------- ------------
81350Sstevel@tonic-gate * != NULL connected configured or
81360Sstevel@tonic-gate * unconfigured
81370Sstevel@tonic-gate * != NULL not connected disconnect but
81380Sstevel@tonic-gate * busy/still referenced
81390Sstevel@tonic-gate * NULL connected logically disconnected
81400Sstevel@tonic-gate * NULL not connected empty
81410Sstevel@tonic-gate */
81420Sstevel@tonic-gate static uint_t
hubd_cfgadm_state(hubd_t * hubd,usb_port_t port)81430Sstevel@tonic-gate hubd_cfgadm_state(hubd_t *hubd, usb_port_t port)
81440Sstevel@tonic-gate {
81450Sstevel@tonic-gate uint_t state;
81460Sstevel@tonic-gate dev_info_t *child_dip = hubd_get_child_dip(hubd, port);
81470Sstevel@tonic-gate
81480Sstevel@tonic-gate if (child_dip) {
81490Sstevel@tonic-gate if (hubd->h_port_state[port] & PORT_STATUS_CCS) {
81500Sstevel@tonic-gate /*
81510Sstevel@tonic-gate * connected, now check if driver exists
81520Sstevel@tonic-gate */
81530Sstevel@tonic-gate if (DEVI_IS_DEVICE_OFFLINE(child_dip) ||
81541333Scth !i_ddi_devi_attached(child_dip)) {
81550Sstevel@tonic-gate state = HUBD_CFGADM_UNCONFIGURED;
81560Sstevel@tonic-gate } else {
81570Sstevel@tonic-gate state = HUBD_CFGADM_CONFIGURED;
81580Sstevel@tonic-gate }
81590Sstevel@tonic-gate } else {
81600Sstevel@tonic-gate /*
81610Sstevel@tonic-gate * this means that the dip is around for
81620Sstevel@tonic-gate * a device that is still referenced but
81630Sstevel@tonic-gate * has been yanked out. So the cfgadm info
81640Sstevel@tonic-gate * for this state should be EMPTY (port empty)
81650Sstevel@tonic-gate * and CONFIGURED (dip still valid).
81660Sstevel@tonic-gate */
81670Sstevel@tonic-gate state = HUBD_CFGADM_STILL_REFERENCED;
81680Sstevel@tonic-gate }
81690Sstevel@tonic-gate } else {
81700Sstevel@tonic-gate /* connected but no child dip */
81710Sstevel@tonic-gate if (hubd->h_port_state[port] & PORT_STATUS_CCS) {
81720Sstevel@tonic-gate /* logically disconnected */
81730Sstevel@tonic-gate state = HUBD_CFGADM_DISCONNECTED;
81740Sstevel@tonic-gate } else {
81750Sstevel@tonic-gate /* physically disconnected */
81760Sstevel@tonic-gate state = HUBD_CFGADM_EMPTY;
81770Sstevel@tonic-gate }
81780Sstevel@tonic-gate }
81790Sstevel@tonic-gate
81800Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
81810Sstevel@tonic-gate "hubd_cfgadm_state: hubd=0x%p, port=%d state=0x%x",
81826898Sfb209375 (void *)hubd, port, state);
81830Sstevel@tonic-gate
81840Sstevel@tonic-gate return (state);
81850Sstevel@tonic-gate }
81860Sstevel@tonic-gate
81870Sstevel@tonic-gate
81880Sstevel@tonic-gate /*
81890Sstevel@tonic-gate * hubd_toggle_port:
81900Sstevel@tonic-gate */
81910Sstevel@tonic-gate static int
hubd_toggle_port(hubd_t * hubd,usb_port_t port)81920Sstevel@tonic-gate hubd_toggle_port(hubd_t *hubd, usb_port_t port)
81930Sstevel@tonic-gate {
81940Sstevel@tonic-gate usb_hub_descr_t *hub_descr;
81950Sstevel@tonic-gate int wait;
81960Sstevel@tonic-gate uint_t retry;
81970Sstevel@tonic-gate uint16_t status;
81980Sstevel@tonic-gate uint16_t change;
81990Sstevel@tonic-gate
82000Sstevel@tonic-gate USB_DPRINTF_L4(DPRINT_MASK_CBOPS, hubd->h_log_handle,
82016898Sfb209375 "hubd_toggle_port: hubd=0x%p, port=%d", (void *)hubd, port);
82020Sstevel@tonic-gate
82030Sstevel@tonic-gate if ((hubd_disable_port_power(hubd, port)) != USB_SUCCESS) {
82040Sstevel@tonic-gate
82050Sstevel@tonic-gate return (USB_FAILURE);
82060Sstevel@tonic-gate }
82070Sstevel@tonic-gate
82080Sstevel@tonic-gate /*
82090Sstevel@tonic-gate * see hubd_enable_all_port_power() which
82100Sstevel@tonic-gate * requires longer delay for hubs.
82110Sstevel@tonic-gate */
82120Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
82130Sstevel@tonic-gate delay(drv_usectohz(hubd_device_delay / 10));
82140Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
82150Sstevel@tonic-gate
82160Sstevel@tonic-gate hub_descr = &hubd->h_hub_descr;
82170Sstevel@tonic-gate
82180Sstevel@tonic-gate /*
82190Sstevel@tonic-gate * According to section 11.11 of USB, for hubs with no power
82200Sstevel@tonic-gate * switches, bPwrOn2PwrGood is zero. But we wait for some
82210Sstevel@tonic-gate * arbitrary time to enable power to become stable.
82220Sstevel@tonic-gate *
82230Sstevel@tonic-gate * If an hub supports port power swicthing, we need to wait
82240Sstevel@tonic-gate * at least 20ms before accesing corresonding usb port.
82250Sstevel@tonic-gate */
82260Sstevel@tonic-gate if ((hub_descr->wHubCharacteristics &
82270Sstevel@tonic-gate HUB_CHARS_NO_POWER_SWITCHING) || (!hub_descr->bPwrOn2PwrGood)) {
82280Sstevel@tonic-gate wait = hubd_device_delay / 10;
82290Sstevel@tonic-gate } else {
82300Sstevel@tonic-gate wait = max(HUB_DEFAULT_POPG,
82310Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood) * 2 * 1000;
82320Sstevel@tonic-gate }
82330Sstevel@tonic-gate
82340Sstevel@tonic-gate USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle,
82350Sstevel@tonic-gate "hubd_toggle_port: popg=%d wait=%d",
82360Sstevel@tonic-gate hub_descr->bPwrOn2PwrGood, wait);
82370Sstevel@tonic-gate
82380Sstevel@tonic-gate retry = 0;
82390Sstevel@tonic-gate
82400Sstevel@tonic-gate do {
82410Sstevel@tonic-gate (void) hubd_enable_port_power(hubd, port);
82420Sstevel@tonic-gate
82430Sstevel@tonic-gate mutex_exit(HUBD_MUTEX(hubd));
82440Sstevel@tonic-gate delay(drv_usectohz(wait));
82450Sstevel@tonic-gate mutex_enter(HUBD_MUTEX(hubd));
82460Sstevel@tonic-gate
82470Sstevel@tonic-gate /* Get port status */
82480Sstevel@tonic-gate (void) hubd_determine_port_status(hubd, port,
82490Sstevel@tonic-gate &status, &change, 0);
82500Sstevel@tonic-gate
82510Sstevel@tonic-gate /* For retry if any, use some extra delay */
82520Sstevel@tonic-gate wait = max(wait, hubd_device_delay / 10);
82530Sstevel@tonic-gate
82540Sstevel@tonic-gate retry++;
82550Sstevel@tonic-gate
82560Sstevel@tonic-gate } while ((!(status & PORT_STATUS_PPS)) && (retry < HUBD_PORT_RETRY));
82570Sstevel@tonic-gate
82580Sstevel@tonic-gate /* Print warning message if port has no power */
82590Sstevel@tonic-gate if (!(status & PORT_STATUS_PPS)) {
82600Sstevel@tonic-gate
8261978Sfrits USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle,
82620Sstevel@tonic-gate "hubd_toggle_port: port %d power-on failed, "
82630Sstevel@tonic-gate "port status 0x%x", port, status);
82640Sstevel@tonic-gate
82650Sstevel@tonic-gate return (USB_FAILURE);
82660Sstevel@tonic-gate }
82670Sstevel@tonic-gate
82680Sstevel@tonic-gate return (USB_SUCCESS);
82690Sstevel@tonic-gate }
82701001Ssl147100
82711001Ssl147100
82721001Ssl147100 /*
82731001Ssl147100 * hubd_init_power_budget:
82741001Ssl147100 * Init power budget variables in hubd structure. According
82751001Ssl147100 * to USB spec, the power budget rules are:
82761001Ssl147100 * 1. local-powered hubs including root-hubs can supply
82771001Ssl147100 * 500mA to each port at maximum
82781001Ssl147100 * 2. two bus-powered hubs are not allowed to concatenate
82791001Ssl147100 * 3. bus-powered hubs can supply 100mA to each port at
82801001Ssl147100 * maximum, and the power consumed by all downstream
82811001Ssl147100 * ports and the hub itself cannot exceed the max power
82821001Ssl147100 * supplied by the upstream port, i.e., 500mA
82831001Ssl147100 * The routine is only called during hub attach time
82841001Ssl147100 */
82851001Ssl147100 static int
hubd_init_power_budget(hubd_t * hubd)82861001Ssl147100 hubd_init_power_budget(hubd_t *hubd)
82871001Ssl147100 {
82881001Ssl147100 uint16_t status = 0;
82891001Ssl147100 usba_device_t *hubd_ud = NULL;
82901001Ssl147100 size_t size;
82911001Ssl147100 usb_cfg_descr_t cfg_descr;
82921001Ssl147100 dev_info_t *pdip = NULL;
82931001Ssl147100 hubd_t *phubd = NULL;
82941001Ssl147100
82951001Ssl147100 if (hubd->h_ignore_pwr_budget) {
82961001Ssl147100
82971001Ssl147100 return (USB_SUCCESS);
82981001Ssl147100 }
82991001Ssl147100
83001001Ssl147100 USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle,
83011001Ssl147100 "hubd_init_power_budget:");
83021001Ssl147100
83031001Ssl147100 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
83041001Ssl147100 ASSERT(hubd->h_default_pipe != 0);
83051001Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
83061001Ssl147100
83071001Ssl147100 /* get device status */
83081001Ssl147100 if ((usb_get_status(hubd->h_dip, hubd->h_default_pipe,
83091001Ssl147100 HUB_GET_DEVICE_STATUS_TYPE,
83101001Ssl147100 0, &status, 0)) != USB_SUCCESS) {
83111001Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
83121001Ssl147100
83131001Ssl147100 return (USB_FAILURE);
83141001Ssl147100 }
83151001Ssl147100
83161001Ssl147100 hubd_ud = usba_get_usba_device(hubd->h_dip);
83171001Ssl147100
83181001Ssl147100 size = usb_parse_cfg_descr(hubd_ud->usb_cfg, hubd_ud->usb_cfg_length,
83191001Ssl147100 &cfg_descr, USB_CFG_DESCR_SIZE);
83201001Ssl147100
83211001Ssl147100 if (size != USB_CFG_DESCR_SIZE) {
83221001Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
83231001Ssl147100 "get hub configuration descriptor failed");
83241001Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
83251001Ssl147100
83261001Ssl147100 return (USB_FAILURE);
83271001Ssl147100 }
83281001Ssl147100
83291001Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
83301001Ssl147100
83311001Ssl147100 hubd->h_local_pwr_capable = (cfg_descr.bmAttributes &
83321001Ssl147100 USB_CFG_ATTR_SELFPWR);
83331001Ssl147100
83341001Ssl147100 if (hubd->h_local_pwr_capable) {
83351001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
83361001Ssl147100 "hub is capable of local power");
83371001Ssl147100 }
83381001Ssl147100
83391001Ssl147100 hubd->h_local_pwr_on = (status &
83401001Ssl147100 USB_DEV_SLF_PWRD_STATUS) && hubd->h_local_pwr_capable;
83411001Ssl147100
83421001Ssl147100 if (hubd->h_local_pwr_on) {
83431001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
83441001Ssl147100 "hub is local-powered");
83451001Ssl147100
83461001Ssl147100 hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD *
83471001Ssl147100 USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
83481001Ssl147100 } else {
83491001Ssl147100 hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD *
83501001Ssl147100 USB_LOW_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
83511001Ssl147100
83521001Ssl147100 hubd->h_pwr_left = (USB_PWR_UNIT_LOAD *
83531001Ssl147100 USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT;
83541001Ssl147100
83551001Ssl147100 ASSERT(!usba_is_root_hub(hubd->h_dip));
83561001Ssl147100
83571001Ssl147100 if (!usba_is_root_hub(hubd->h_dip)) {
83581001Ssl147100 /*
83591001Ssl147100 * two bus-powered hubs are not
83601001Ssl147100 * allowed to be concatenated
83611001Ssl147100 */
83621001Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
83631001Ssl147100
83641001Ssl147100 pdip = ddi_get_parent(hubd->h_dip);
83651001Ssl147100 phubd = hubd_get_soft_state(pdip);
83661001Ssl147100 ASSERT(phubd != NULL);
83671001Ssl147100
83681001Ssl147100 if (!phubd->h_ignore_pwr_budget) {
83691001Ssl147100 mutex_enter(HUBD_MUTEX(phubd));
83701001Ssl147100 if (phubd->h_local_pwr_on == B_FALSE) {
83713435Slg150142 USB_DPRINTF_L1(DPRINT_MASK_HUB,
83721001Ssl147100 hubd->h_log_handle,
83731001Ssl147100 "two bus-powered hubs cannot "
83741001Ssl147100 "be concatenated");
83751001Ssl147100
83761001Ssl147100 mutex_exit(HUBD_MUTEX(phubd));
83771001Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
83781001Ssl147100
83791001Ssl147100 return (USB_FAILURE);
83801001Ssl147100 }
83811001Ssl147100 mutex_exit(HUBD_MUTEX(phubd));
83821001Ssl147100 }
83831001Ssl147100
83841001Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
83851001Ssl147100
83861001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
83871001Ssl147100 "hub is bus-powered");
83881001Ssl147100 } else {
83891001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle,
83901001Ssl147100 "root-hub must be local-powered");
83911001Ssl147100 }
83921001Ssl147100
83931001Ssl147100 /*
83941001Ssl147100 * Subtract the power consumed by the hub itself
83951001Ssl147100 * and get the power that can be supplied to
83961001Ssl147100 * downstream ports
83971001Ssl147100 */
83981001Ssl147100 hubd->h_pwr_left -=
83991001Ssl147100 hubd->h_hub_descr.bHubContrCurrent /
84001001Ssl147100 USB_CFG_DESCR_PWR_UNIT;
84011001Ssl147100 if (hubd->h_pwr_left < 0) {
84021001Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle,
84031001Ssl147100 "hubd->h_pwr_left is less than bHubContrCurrent, "
84041001Ssl147100 "should fail");
84051001Ssl147100
84061001Ssl147100 return (USB_FAILURE);
84071001Ssl147100 }
84081001Ssl147100 }
84091001Ssl147100
84101001Ssl147100 return (USB_SUCCESS);
84111001Ssl147100 }
84121001Ssl147100
84131001Ssl147100
84141001Ssl147100 /*
84151001Ssl147100 * usba_hubdi_check_power_budget:
84161001Ssl147100 * Check if the hub has enough power budget to allow a
84171001Ssl147100 * child device to select a configuration of config_index.
84181001Ssl147100 */
84191001Ssl147100 int
usba_hubdi_check_power_budget(dev_info_t * dip,usba_device_t * child_ud,uint_t config_index)84201001Ssl147100 usba_hubdi_check_power_budget(dev_info_t *dip, usba_device_t *child_ud,
84211001Ssl147100 uint_t config_index)
84221001Ssl147100 {
84231001Ssl147100 int16_t pwr_left, pwr_limit, pwr_required;
84241001Ssl147100 size_t size;
84251001Ssl147100 usb_cfg_descr_t cfg_descr;
84261001Ssl147100 hubd_t *hubd;
84271001Ssl147100
84281001Ssl147100 if ((hubd = hubd_get_soft_state(dip)) == NULL) {
84291001Ssl147100
84301001Ssl147100 return (USB_FAILURE);
84311001Ssl147100 }
84321001Ssl147100
84331001Ssl147100 if (hubd->h_ignore_pwr_budget) {
84341001Ssl147100
84351001Ssl147100 return (USB_SUCCESS);
84361001Ssl147100 }
84371001Ssl147100
84381001Ssl147100 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
84391001Ssl147100 "usba_hubdi_check_power_budget: "
84406898Sfb209375 "dip=0x%p child_ud=0x%p conf_index=%d", (void *)dip,
84416898Sfb209375 (void *)child_ud, config_index);
84421001Ssl147100
84431001Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
84441001Ssl147100 pwr_limit = hubd->h_pwr_limit;
84451001Ssl147100 if (hubd->h_local_pwr_on == B_FALSE) {
84461001Ssl147100 pwr_left = hubd->h_pwr_left;
84471001Ssl147100 pwr_limit = (pwr_limit <= pwr_left) ? pwr_limit : pwr_left;
84481001Ssl147100 }
84491001Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
84501001Ssl147100
84511001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
84521001Ssl147100 "usba_hubdi_check_power_budget: "
84531001Ssl147100 "available power is %dmA", pwr_limit * USB_CFG_DESCR_PWR_UNIT);
84541001Ssl147100
84551001Ssl147100 size = usb_parse_cfg_descr(
84561001Ssl147100 child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE,
84571001Ssl147100 &cfg_descr, USB_CFG_DESCR_SIZE);
84581001Ssl147100
84591001Ssl147100 if (size != USB_CFG_DESCR_SIZE) {
84601001Ssl147100 USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
84611001Ssl147100 "get hub configuration descriptor failed");
84621001Ssl147100
84631001Ssl147100 return (USB_FAILURE);
84641001Ssl147100 }
84651001Ssl147100
84661001Ssl147100 pwr_required = cfg_descr.bMaxPower;
84671001Ssl147100
84681001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
84691001Ssl147100 "usba_hubdi_check_power_budget: "
84701001Ssl147100 "child bmAttributes=0x%x bMaxPower=%d "
84711001Ssl147100 "with config_index=%d", cfg_descr.bmAttributes,
84721001Ssl147100 pwr_required, config_index);
84731001Ssl147100
84741001Ssl147100 if (pwr_required > pwr_limit) {
84753435Slg150142 USB_DPRINTF_L1(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
84761001Ssl147100 "configuration %d for device %s %s at port %d "
84771001Ssl147100 "exceeds power available for this port, please "
84781001Ssl147100 "re-insert your device into another hub port which "
84791001Ssl147100 "has enough power",
84801001Ssl147100 config_index,
84811001Ssl147100 child_ud->usb_mfg_str,
84821001Ssl147100 child_ud->usb_product_str,
84831001Ssl147100 child_ud->usb_port);
84841001Ssl147100
84851001Ssl147100 return (USB_FAILURE);
84861001Ssl147100 }
84871001Ssl147100
84881001Ssl147100 return (USB_SUCCESS);
84891001Ssl147100 }
84901001Ssl147100
84911001Ssl147100
84921001Ssl147100 /*
84931001Ssl147100 * usba_hubdi_incr_power_budget:
84941001Ssl147100 * Increase the hub power budget value when a child device
84951001Ssl147100 * is removed from a bus-powered hub port.
84961001Ssl147100 */
84971001Ssl147100 void
usba_hubdi_incr_power_budget(dev_info_t * dip,usba_device_t * child_ud)84981001Ssl147100 usba_hubdi_incr_power_budget(dev_info_t *dip, usba_device_t *child_ud)
84991001Ssl147100 {
85001001Ssl147100 uint16_t pwr_value;
85011001Ssl147100 hubd_t *hubd = hubd_get_soft_state(dip);
85021001Ssl147100
85031001Ssl147100 ASSERT(hubd != NULL);
85041001Ssl147100
85051001Ssl147100 if (hubd->h_ignore_pwr_budget) {
85061001Ssl147100
85071001Ssl147100 return;
85081001Ssl147100 }
85091001Ssl147100
85101001Ssl147100 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
85111001Ssl147100 "usba_hubdi_incr_power_budget: "
85126898Sfb209375 "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud);
85131001Ssl147100
85141001Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
85151001Ssl147100 if (hubd->h_local_pwr_on == B_TRUE) {
85161001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
85171001Ssl147100 "usba_hubdi_incr_power_budget: "
85181001Ssl147100 "hub is local powered");
85191001Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
85201001Ssl147100
85211001Ssl147100 return;
85221001Ssl147100 }
85231001Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
85241001Ssl147100
85251001Ssl147100 mutex_enter(&child_ud->usb_mutex);
85261001Ssl147100 if (child_ud->usb_pwr_from_hub == 0) {
85271001Ssl147100 mutex_exit(&child_ud->usb_mutex);
85281001Ssl147100
85291001Ssl147100 return;
85301001Ssl147100 }
85311001Ssl147100 pwr_value = child_ud->usb_pwr_from_hub;
85321001Ssl147100 mutex_exit(&child_ud->usb_mutex);
85331001Ssl147100
85341001Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
85351001Ssl147100 hubd->h_pwr_left += pwr_value;
85361001Ssl147100
85371001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
85381001Ssl147100 "usba_hubdi_incr_power_budget: "
85391001Ssl147100 "available power is %dmA, increased by %dmA",
85401001Ssl147100 hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT,
85411001Ssl147100 pwr_value * USB_CFG_DESCR_PWR_UNIT);
85421001Ssl147100
85431001Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
85441001Ssl147100
85451001Ssl147100 mutex_enter(&child_ud->usb_mutex);
85461001Ssl147100 child_ud->usb_pwr_from_hub = 0;
85471001Ssl147100 mutex_exit(&child_ud->usb_mutex);
85481001Ssl147100 }
85491001Ssl147100
85501001Ssl147100
85511001Ssl147100 /*
85521001Ssl147100 * usba_hubdi_decr_power_budget:
85531001Ssl147100 * Decrease the hub power budget value when a child device
85541001Ssl147100 * is inserted to a bus-powered hub port.
85551001Ssl147100 */
85561001Ssl147100 void
usba_hubdi_decr_power_budget(dev_info_t * dip,usba_device_t * child_ud)85571001Ssl147100 usba_hubdi_decr_power_budget(dev_info_t *dip, usba_device_t *child_ud)
85581001Ssl147100 {
85591001Ssl147100 uint16_t pwr_value;
85601001Ssl147100 size_t size;
85611001Ssl147100 usb_cfg_descr_t cfg_descr;
85621001Ssl147100 hubd_t *hubd = hubd_get_soft_state(dip);
85631001Ssl147100
85641001Ssl147100 ASSERT(hubd != NULL);
85651001Ssl147100
85661001Ssl147100 if (hubd->h_ignore_pwr_budget) {
85671001Ssl147100
85681001Ssl147100 return;
85691001Ssl147100 }
85701001Ssl147100
85711001Ssl147100 USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle,
85721001Ssl147100 "usba_hubdi_decr_power_budget: "
85736898Sfb209375 "dip=0x%p child_ud=0x%p", (void *)dip, (void *)child_ud);
85741001Ssl147100
85751001Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
85761001Ssl147100 if (hubd->h_local_pwr_on == B_TRUE) {
85771001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
85781001Ssl147100 "usba_hubdi_decr_power_budget: "
85791001Ssl147100 "hub is local powered");
85801001Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
85811001Ssl147100
85821001Ssl147100 return;
85831001Ssl147100 }
85841001Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
85851001Ssl147100
85861001Ssl147100 mutex_enter(&child_ud->usb_mutex);
85871001Ssl147100 if (child_ud->usb_pwr_from_hub > 0) {
85881001Ssl147100 mutex_exit(&child_ud->usb_mutex);
85891001Ssl147100
85901001Ssl147100 return;
85911001Ssl147100 }
85921001Ssl147100 mutex_exit(&child_ud->usb_mutex);
85931001Ssl147100
85941001Ssl147100 size = usb_parse_cfg_descr(
85951001Ssl147100 child_ud->usb_cfg, child_ud->usb_cfg_length,
85961001Ssl147100 &cfg_descr, USB_CFG_DESCR_SIZE);
85971001Ssl147100 ASSERT(size == USB_CFG_DESCR_SIZE);
85981001Ssl147100
85991001Ssl147100 mutex_enter(HUBD_MUTEX(hubd));
86001001Ssl147100 pwr_value = cfg_descr.bMaxPower;
86011001Ssl147100 hubd->h_pwr_left -= pwr_value;
86021001Ssl147100 ASSERT(hubd->h_pwr_left >= 0);
86031001Ssl147100
86041001Ssl147100 USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle,
86051001Ssl147100 "usba_hubdi_decr_power_budget: "
86061001Ssl147100 "available power is %dmA, decreased by %dmA",
86071001Ssl147100 hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT,
86081001Ssl147100 pwr_value * USB_CFG_DESCR_PWR_UNIT);
86091001Ssl147100
86101001Ssl147100 mutex_exit(HUBD_MUTEX(hubd));
86111001Ssl147100
86121001Ssl147100 mutex_enter(&child_ud->usb_mutex);
86131001Ssl147100 child_ud->usb_pwr_from_hub = pwr_value;
86141001Ssl147100 mutex_exit(&child_ud->usb_mutex);
86151001Ssl147100 }
86164844Slg150142
86174844Slg150142 /*
86184844Slg150142 * hubd_wait_for_hotplug_exit:
86196112Sqz150045 * Waiting for the exit of the running hotplug thread or ioctl thread.
86204844Slg150142 */
86214844Slg150142 static int
hubd_wait_for_hotplug_exit(hubd_t * hubd)86224844Slg150142 hubd_wait_for_hotplug_exit(hubd_t *hubd)
86234844Slg150142 {
862411066Srafael.vanoni@sun.com clock_t until = drv_usectohz(1000000);
86254844Slg150142 int rval;
86264844Slg150142
86274844Slg150142 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
86284844Slg150142
86294844Slg150142 if (hubd->h_hotplug_thread) {
86304844Slg150142 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
86314844Slg150142 "waiting for hubd hotplug thread exit");
863211066Srafael.vanoni@sun.com rval = cv_reltimedwait(&hubd->h_cv_hotplug_dev,
863311066Srafael.vanoni@sun.com &hubd->h_mutex, until, TR_CLOCK_TICK);
86344844Slg150142
86354844Slg150142 if ((rval <= 0) && (hubd->h_hotplug_thread)) {
86364844Slg150142
86374844Slg150142 return (USB_FAILURE);
86384844Slg150142 }
86394844Slg150142 }
86404844Slg150142
86414844Slg150142 return (USB_SUCCESS);
86424844Slg150142 }
86434844Slg150142
86444844Slg150142 /*
86454844Slg150142 * hubd_reset_thread:
86464844Slg150142 * handles the "USB_RESET_LVL_REATTACH" reset of usb device.
86474844Slg150142 *
86484844Slg150142 * - delete the child (force detaching the device and its children)
86494844Slg150142 * - reset the corresponding parent hub port
86504844Slg150142 * - create the child (force re-attaching the device and its children)
86514844Slg150142 */
86524844Slg150142 static void
hubd_reset_thread(void * arg)86534844Slg150142 hubd_reset_thread(void *arg)
86544844Slg150142 {
86554844Slg150142 hubd_reset_arg_t *hd_arg = (hubd_reset_arg_t *)arg;
86564844Slg150142 hubd_t *hubd = hd_arg->hubd;
86574844Slg150142 uint16_t reset_port = hd_arg->reset_port;
86584844Slg150142 uint16_t status, change;
86594844Slg150142 hub_power_t *hubpm;
86604844Slg150142 dev_info_t *hdip = hubd->h_dip;
86614844Slg150142 dev_info_t *rh_dip = hubd->h_usba_device->usb_root_hub_dip;
86624844Slg150142 dev_info_t *child_dip;
86634844Slg150142 boolean_t online_child = B_FALSE;
86644844Slg150142 int prh_circ, rh_circ, circ, devinst;
86654844Slg150142 char *devname;
866610468SRaymond.Chen@Sun.COM int i = 0;
866710468SRaymond.Chen@Sun.COM int rval = USB_FAILURE;
86684844Slg150142
86694844Slg150142 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
86704844Slg150142 "hubd_reset_thread: started, hubd_reset_port = 0x%x", reset_port);
86714844Slg150142
86724844Slg150142 kmem_free(arg, sizeof (hubd_reset_arg_t));
86734844Slg150142
86744844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
86754844Slg150142
86764844Slg150142 child_dip = hubd->h_children_dips[reset_port];
86774844Slg150142 ASSERT(child_dip != NULL);
86784844Slg150142
86794844Slg150142 devname = (char *)ddi_driver_name(child_dip);
86804844Slg150142 devinst = ddi_get_instance(child_dip);
86814844Slg150142
86824844Slg150142 /* if our bus power entry point is active, quit the reset */
86834844Slg150142 if (hubd->h_bus_pwr) {
86844844Slg150142 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
86854844Slg150142 "%s%d is under bus power management, cannot be reset. "
86864844Slg150142 "Please disconnect and reconnect this device.",
86874844Slg150142 devname, devinst);
86884844Slg150142
86894844Slg150142 goto Fail;
86904844Slg150142 }
86914844Slg150142
86924844Slg150142 if (hubd_wait_for_hotplug_exit(hubd) == USB_FAILURE) {
86934844Slg150142 /* we got woken up because of a timeout */
86944844Slg150142 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
86954844Slg150142 hubd->h_log_handle, "Time out when resetting the device"
86964844Slg150142 " %s%d. Please disconnect and reconnect this device.",
86974844Slg150142 devname, devinst);
86984844Slg150142
86994844Slg150142 goto Fail;
87004844Slg150142 }
87014844Slg150142
87024844Slg150142 hubd->h_hotplug_thread++;
87034844Slg150142
87044844Slg150142 /* is this the root hub? */
87054844Slg150142 if ((hdip == rh_dip) &&
87064844Slg150142 (hubd->h_dev_state == USB_DEV_PWRED_DOWN)) {
87074844Slg150142 hubpm = hubd->h_hubpm;
87084844Slg150142
87094844Slg150142 /* mark the root hub as full power */
87104844Slg150142 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
87114844Slg150142 hubpm->hubp_time_at_full_power = ddi_get_time();
87124844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
87134844Slg150142
87144844Slg150142 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
87154844Slg150142 "hubd_reset_thread: call pm_power_has_changed");
87164844Slg150142
87174844Slg150142 (void) pm_power_has_changed(hdip, 0,
87184844Slg150142 USB_DEV_OS_FULL_PWR);
87194844Slg150142
87204844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
87214844Slg150142 hubd->h_dev_state = USB_DEV_ONLINE;
87224844Slg150142 }
87234844Slg150142
87244844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
87254844Slg150142
87264844Slg150142 /*
87274844Slg150142 * this ensures one reset activity per system at a time.
87284844Slg150142 * we enter the parent PCI node to have this serialization.
87294844Slg150142 * this also excludes ioctls and deathrow thread
87304844Slg150142 */
87314844Slg150142 ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
87324844Slg150142 ndi_devi_enter(rh_dip, &rh_circ);
87334844Slg150142
87344844Slg150142 /* exclude other threads */
87354844Slg150142 ndi_devi_enter(hdip, &circ);
87364844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
87374844Slg150142
87384844Slg150142 /*
87394844Slg150142 * We need to make sure that the child is still online for a hotplug
87404844Slg150142 * thread could have inserted which detached the child.
87414844Slg150142 */
87424844Slg150142 if (hubd->h_children_dips[reset_port]) {
87434844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
87444844Slg150142 /* First disconnect the device */
87454844Slg150142 hubd_post_event(hubd, reset_port, USBA_EVENT_TAG_HOT_REMOVAL);
874610468SRaymond.Chen@Sun.COM
874710468SRaymond.Chen@Sun.COM /* delete cached dv_node's but drop locks first */
874810468SRaymond.Chen@Sun.COM ndi_devi_exit(hdip, circ);
874910468SRaymond.Chen@Sun.COM ndi_devi_exit(rh_dip, rh_circ);
875010468SRaymond.Chen@Sun.COM ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
875110468SRaymond.Chen@Sun.COM
875210468SRaymond.Chen@Sun.COM (void) devfs_clean(rh_dip, NULL, DV_CLEAN_FORCE);
875310468SRaymond.Chen@Sun.COM
875410468SRaymond.Chen@Sun.COM /*
875510468SRaymond.Chen@Sun.COM * workaround only for storage device. When it's able to force
875610468SRaymond.Chen@Sun.COM * detach a driver, this code can be removed safely.
875710468SRaymond.Chen@Sun.COM *
875810468SRaymond.Chen@Sun.COM * If we're to reset storage device and the device is used, we
875910468SRaymond.Chen@Sun.COM * will wait at most extra 20s for applications to exit and
876010468SRaymond.Chen@Sun.COM * close the device. This is especially useful for HAL-based
876110468SRaymond.Chen@Sun.COM * applications.
876210468SRaymond.Chen@Sun.COM */
876310468SRaymond.Chen@Sun.COM if ((strcmp(devname, "scsa2usb") == 0) &&
876410468SRaymond.Chen@Sun.COM DEVI(child_dip)->devi_ref != 0) {
876510468SRaymond.Chen@Sun.COM while (i++ < hubdi_reset_delay) {
876610468SRaymond.Chen@Sun.COM mutex_enter(HUBD_MUTEX(hubd));
876710468SRaymond.Chen@Sun.COM rval = hubd_delete_child(hubd, reset_port,
876810468SRaymond.Chen@Sun.COM NDI_DEVI_REMOVE, B_FALSE);
876910468SRaymond.Chen@Sun.COM mutex_exit(HUBD_MUTEX(hubd));
877010468SRaymond.Chen@Sun.COM if (rval == USB_SUCCESS)
877110468SRaymond.Chen@Sun.COM break;
877210468SRaymond.Chen@Sun.COM
877310468SRaymond.Chen@Sun.COM delay(drv_usectohz(1000000)); /* 1s */
877410468SRaymond.Chen@Sun.COM }
877510468SRaymond.Chen@Sun.COM }
877610468SRaymond.Chen@Sun.COM
877710468SRaymond.Chen@Sun.COM ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
877810468SRaymond.Chen@Sun.COM ndi_devi_enter(rh_dip, &rh_circ);
877910468SRaymond.Chen@Sun.COM ndi_devi_enter(hdip, &circ);
878010468SRaymond.Chen@Sun.COM
87814844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
87824844Slg150142
87834844Slg150142 /* Then force detaching the device */
878410468SRaymond.Chen@Sun.COM if ((rval != USB_SUCCESS) && (hubd_delete_child(hubd,
878510468SRaymond.Chen@Sun.COM reset_port, NDI_DEVI_REMOVE, B_FALSE) != USB_SUCCESS)) {
87864844Slg150142 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
87874844Slg150142 "%s%d cannot be reset due to other applications "
87884844Slg150142 "are using it, please first close these "
87894844Slg150142 "applications, then disconnect and reconnect"
87904844Slg150142 "the device.", devname, devinst);
87914844Slg150142
87924844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
87934844Slg150142 /* post a re-connect event */
87944844Slg150142 hubd_post_event(hubd, reset_port,
87954844Slg150142 USBA_EVENT_TAG_HOT_INSERTION);
87964844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
87974844Slg150142 } else {
87984844Slg150142 (void) hubd_determine_port_status(hubd, reset_port,
87994844Slg150142 &status, &change, HUBD_ACK_ALL_CHANGES);
88004844Slg150142
88014844Slg150142 /* Reset the parent hubd port and create new child */
88024844Slg150142 if (status & PORT_STATUS_CCS) {
88034844Slg150142 online_child |= (hubd_handle_port_connect(hubd,
88044844Slg150142 reset_port) == USB_SUCCESS);
88054844Slg150142 }
88064844Slg150142 }
88074844Slg150142 }
88084844Slg150142
88094844Slg150142 /* release locks so we can do a devfs_clean */
88104844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
88114844Slg150142
88124844Slg150142 /* delete cached dv_node's but drop locks first */
88134844Slg150142 ndi_devi_exit(hdip, circ);
88144844Slg150142 ndi_devi_exit(rh_dip, rh_circ);
88154844Slg150142 ndi_devi_exit(ddi_get_parent(rh_dip), prh_circ);
88164844Slg150142
88174844Slg150142 (void) devfs_clean(rh_dip, NULL, 0);
88184844Slg150142
88194844Slg150142 /* now check if any children need onlining */
88204844Slg150142 if (online_child) {
88214844Slg150142 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
88224844Slg150142 "hubd_reset_thread: onlining children");
88234844Slg150142
88244844Slg150142 (void) ndi_devi_online(hubd->h_dip, 0);
88254844Slg150142 }
88264844Slg150142
88274844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
88284844Slg150142
88294844Slg150142 /* allow hotplug thread now */
88304844Slg150142 hubd->h_hotplug_thread--;
88314844Slg150142 Fail:
88324844Slg150142 hubd_start_polling(hubd, 0);
88334844Slg150142
88344844Slg150142 /* mark this device as idle */
88354844Slg150142 (void) hubd_pm_idle_component(hubd, hubd->h_dip, 0);
88364844Slg150142
88374844Slg150142 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
88384844Slg150142 "hubd_reset_thread: exit, %d", hubd->h_hotplug_thread);
88394844Slg150142
88404844Slg150142 hubd->h_reset_port[reset_port] = B_FALSE;
88414844Slg150142
88424844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
88434844Slg150142
88444844Slg150142 ndi_rele_devi(hdip);
88454844Slg150142 }
88464844Slg150142
88474844Slg150142 /*
88484844Slg150142 * hubd_check_same_device:
88496112Sqz150045 * - open the default pipe of the device.
88506112Sqz150045 * - compare the old and new descriptors of the device.
88516112Sqz150045 * - close the default pipe.
88524844Slg150142 */
88534844Slg150142 static int
hubd_check_same_device(hubd_t * hubd,usb_port_t port)88544844Slg150142 hubd_check_same_device(hubd_t *hubd, usb_port_t port)
88554844Slg150142 {
88566112Sqz150045 dev_info_t *dip = hubd->h_children_dips[port];
88574844Slg150142 usb_pipe_handle_t ph;
88586112Sqz150045 int rval = USB_FAILURE;
88594844Slg150142
88604844Slg150142 ASSERT(mutex_owned(HUBD_MUTEX(hubd)));
88614844Slg150142
88624844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
88634844Slg150142 /* Open the default pipe to operate the device */
88644844Slg150142 if (usb_pipe_open(dip, NULL, NULL,
88654844Slg150142 USB_FLAGS_SLEEP| USBA_FLAGS_PRIVILEGED,
88664844Slg150142 &ph) == USB_SUCCESS) {
88674844Slg150142 /*
88684844Slg150142 * Check that if the device's descriptors are different
88694844Slg150142 * from the values saved before the port reset.
88704844Slg150142 */
88714844Slg150142 rval = usb_check_same_device(dip,
88724844Slg150142 hubd->h_log_handle, USB_LOG_L0,
88734844Slg150142 DPRINT_MASK_ALL, USB_CHK_ALL, NULL);
88744844Slg150142
88754844Slg150142 usb_pipe_close(dip, ph, USB_FLAGS_SLEEP |
88764844Slg150142 USBA_FLAGS_PRIVILEGED, NULL, NULL);
88774844Slg150142 }
88784844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
88794844Slg150142
88804844Slg150142 return (rval);
88814844Slg150142 }
88824844Slg150142
88834844Slg150142 /*
88844844Slg150142 * usba_hubdi_reset_device
88856112Sqz150045 * Called by usb_reset_device to handle usb device reset.
88864844Slg150142 */
88874844Slg150142 int
usba_hubdi_reset_device(dev_info_t * dip,usb_dev_reset_lvl_t reset_level)88884844Slg150142 usba_hubdi_reset_device(dev_info_t *dip, usb_dev_reset_lvl_t reset_level)
88894844Slg150142 {
88904844Slg150142 hubd_t *hubd;
88914844Slg150142 usb_port_t port = 0;
88924844Slg150142 dev_info_t *hdip;
88934844Slg150142 usb_pipe_state_t prev_pipe_state = 0;
88944844Slg150142 usba_device_t *usba_device;
88954844Slg150142 hubd_reset_arg_t *arg;
88964844Slg150142 int i, ph_open_cnt;
88974844Slg150142 int rval = USB_FAILURE;
88984844Slg150142
88994844Slg150142 if ((!dip) || usba_is_root_hub(dip)) {
890010468SRaymond.Chen@Sun.COM USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
890110468SRaymond.Chen@Sun.COM "usba_hubdi_reset_device: NULL dip or root hub");
89024844Slg150142
89034844Slg150142 return (USB_INVALID_ARGS);
89044844Slg150142 }
89054844Slg150142
89064844Slg150142 if (!usb_owns_device(dip)) {
890710468SRaymond.Chen@Sun.COM USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
890810468SRaymond.Chen@Sun.COM "usba_hubdi_reset_device: Not owns the device");
89094844Slg150142
89104844Slg150142 return (USB_INVALID_PERM);
89114844Slg150142 }
89124844Slg150142
89134844Slg150142 if ((reset_level != USB_RESET_LVL_REATTACH) &&
89144844Slg150142 (reset_level != USB_RESET_LVL_DEFAULT)) {
891510468SRaymond.Chen@Sun.COM USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
891610468SRaymond.Chen@Sun.COM "usba_hubdi_reset_device: Unknown flags");
89174844Slg150142
89184844Slg150142 return (USB_INVALID_ARGS);
89194844Slg150142 }
89204844Slg150142
89214844Slg150142 if ((hdip = ddi_get_parent(dip)) == NULL) {
892210468SRaymond.Chen@Sun.COM USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
892310468SRaymond.Chen@Sun.COM "usba_hubdi_reset_device: fail to get parent hub");
89244844Slg150142
89254844Slg150142 return (USB_INVALID_ARGS);
89264844Slg150142 }
89274844Slg150142
89284844Slg150142 if ((hubd = hubd_get_soft_state(hdip)) == NULL) {
892910468SRaymond.Chen@Sun.COM USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubdi_log_handle,
893010468SRaymond.Chen@Sun.COM "usba_hubdi_reset_device: fail to get hub softstate");
89314844Slg150142
89324844Slg150142 return (USB_INVALID_ARGS);
89334844Slg150142 }
89344844Slg150142
89354844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
89364844Slg150142
89374844Slg150142 /* make sure the hub is connected before trying any kinds of reset. */
89384844Slg150142 if ((hubd->h_dev_state == USB_DEV_DISCONNECTED) ||
89394844Slg150142 (hubd->h_dev_state == USB_DEV_SUSPENDED)) {
89404844Slg150142 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
89414844Slg150142 "usb_reset_device: the state %d of the hub/roothub "
89426898Sfb209375 "associated to the device 0x%p is incorrect",
89436898Sfb209375 hubd->h_dev_state, (void *)dip);
89444844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
89454844Slg150142
89464844Slg150142 return (USB_INVALID_ARGS);
89474844Slg150142 }
89484844Slg150142
89494844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
89504844Slg150142
89514844Slg150142 port = hubd_child_dip2port(hubd, dip);
89524844Slg150142
89534844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
89544844Slg150142
89554844Slg150142 if (hubd->h_reset_port[port]) {
89564844Slg150142 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
89574844Slg150142 "usb_reset_device: the corresponding port is resetting");
89584844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
89594844Slg150142
89604844Slg150142 return (USB_SUCCESS);
89614844Slg150142 }
89624844Slg150142
89634844Slg150142 /*
89644844Slg150142 * For Default reset, client drivers should first close all the pipes
89654844Slg150142 * except default pipe before calling the function, also should not
89664844Slg150142 * call the function during interrupt context.
89674844Slg150142 */
89684844Slg150142 if (reset_level == USB_RESET_LVL_DEFAULT) {
89694844Slg150142 usba_device = hubd->h_usba_devices[port];
89704844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
89714844Slg150142
89724844Slg150142 if (servicing_interrupt()) {
89734844Slg150142 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
89744844Slg150142 "usb_reset_device: during interrput context, quit");
89754844Slg150142
89764844Slg150142 return (USB_INVALID_CONTEXT);
89774844Slg150142 }
89784844Slg150142 /* Check if all the pipes have been closed */
89794844Slg150142 for (ph_open_cnt = 0, i = 1; i < USBA_N_ENDPOINTS; i++) {
89804844Slg150142 if (usba_device->usb_ph_list[i].usba_ph_data) {
89814844Slg150142 ph_open_cnt++;
89824844Slg150142 break;
89834844Slg150142 }
89844844Slg150142 }
89854844Slg150142 if (ph_open_cnt) {
89864844Slg150142 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
89874844Slg150142 "usb_reset_device: %d pipes are still open",
89884844Slg150142 ph_open_cnt);
89894844Slg150142
89904844Slg150142 return (USB_BUSY);
89914844Slg150142 }
89924844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
89934844Slg150142 }
89944844Slg150142
89954844Slg150142 /* Don't perform reset while the device is detaching */
89964844Slg150142 if (hubd->h_port_state[port] & HUBD_CHILD_DETACHING) {
89974844Slg150142 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
89984844Slg150142 "usb_reset_device: the device is detaching, "
89994844Slg150142 "cannot be reset");
90004844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
90014844Slg150142
90024844Slg150142 return (USB_FAILURE);
90034844Slg150142 }
90044844Slg150142
90054844Slg150142 hubd->h_reset_port[port] = B_TRUE;
90064844Slg150142 hdip = hubd->h_dip;
90074844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
90084844Slg150142
90094844Slg150142 /* Don't allow hub detached during the reset */
90104844Slg150142 ndi_hold_devi(hdip);
90114844Slg150142
90124844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
90134844Slg150142 hubd_pm_busy_component(hubd, hdip, 0);
90144844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
90154844Slg150142 /* go full power */
90164844Slg150142 (void) pm_raise_power(hdip, 0, USB_DEV_OS_FULL_PWR);
90174844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
90184844Slg150142
90194844Slg150142 hubd->h_hotplug_thread++;
90204844Slg150142
90214844Slg150142 /* stop polling if it was active */
90224844Slg150142 if (hubd->h_ep1_ph) {
90234844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
90244844Slg150142 (void) usb_pipe_get_state(hubd->h_ep1_ph, &prev_pipe_state,
90254844Slg150142 USB_FLAGS_SLEEP);
90264844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
90274844Slg150142
90284844Slg150142 if (prev_pipe_state == USB_PIPE_STATE_ACTIVE) {
90294844Slg150142 hubd_stop_polling(hubd);
90304844Slg150142 }
90314844Slg150142 }
90324844Slg150142
90334844Slg150142 switch (reset_level) {
90344844Slg150142 case USB_RESET_LVL_REATTACH:
90354844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
90364844Slg150142 arg = (hubd_reset_arg_t *)kmem_zalloc(
90374844Slg150142 sizeof (hubd_reset_arg_t), KM_SLEEP);
90384844Slg150142 arg->hubd = hubd;
90394844Slg150142 arg->reset_port = port;
90404844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
90414844Slg150142
90424844Slg150142 if ((rval = usb_async_req(hdip, hubd_reset_thread,
90434844Slg150142 (void *)arg, 0)) == USB_SUCCESS) {
90444844Slg150142 hubd->h_hotplug_thread--;
90454844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
90464844Slg150142
90474844Slg150142 return (USB_SUCCESS);
90484844Slg150142 } else {
90494844Slg150142 USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle,
90504844Slg150142 "Cannot create reset thread, the device %s%d failed"
90514844Slg150142 " to reset", ddi_driver_name(dip),
90524844Slg150142 ddi_get_instance(dip));
90534844Slg150142
90544844Slg150142 kmem_free(arg, sizeof (hubd_reset_arg_t));
90554844Slg150142 }
90564844Slg150142
90574844Slg150142 break;
90584844Slg150142 case USB_RESET_LVL_DEFAULT:
90594844Slg150142 /*
90604844Slg150142 * Reset hub port and then recover device's address, set back
90614844Slg150142 * device's configuration, hubd_handle_port_connect() will
90624844Slg150142 * handle errors happened during this process.
90634844Slg150142 */
90644844Slg150142 if ((rval = hubd_handle_port_connect(hubd, port))
90654844Slg150142 == USB_SUCCESS) {
90664844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
90674844Slg150142 /* re-open the default pipe */
90684844Slg150142 rval = usba_persistent_pipe_open(usba_device);
90694844Slg150142 mutex_enter(HUBD_MUTEX(hubd));
90704844Slg150142 if (rval != USB_SUCCESS) {
90714844Slg150142 USB_DPRINTF_L2(DPRINT_MASK_ATTA,
90724844Slg150142 hubd->h_log_handle, "failed to reopen "
90734844Slg150142 "default pipe after reset, disable hub"
90744844Slg150142 "port for %s%d", ddi_driver_name(dip),
90754844Slg150142 ddi_get_instance(dip));
90764844Slg150142 /*
90774844Slg150142 * Disable port to set out a hotplug thread
90784844Slg150142 * which will handle errors.
90794844Slg150142 */
90804844Slg150142 (void) hubd_disable_port(hubd, port);
90814844Slg150142 }
90824844Slg150142 }
90834844Slg150142
90844844Slg150142 break;
90854844Slg150142 default:
90864844Slg150142
90874844Slg150142 break;
90884844Slg150142 }
90894844Slg150142
90904844Slg150142 /* allow hotplug thread now */
90914844Slg150142 hubd->h_hotplug_thread--;
90924844Slg150142
90934844Slg150142 if ((hubd->h_dev_state == USB_DEV_ONLINE) && hubd->h_ep1_ph &&
90944844Slg150142 (prev_pipe_state == USB_PIPE_STATE_ACTIVE)) {
90954844Slg150142 hubd_start_polling(hubd, 0);
90964844Slg150142 }
90974844Slg150142
90984844Slg150142 hubd_pm_idle_component(hubd, hdip, 0);
90994844Slg150142
91004844Slg150142 /* Clear reset mark for the port. */
91014844Slg150142 hubd->h_reset_port[port] = B_FALSE;
91024844Slg150142
91034844Slg150142 mutex_exit(HUBD_MUTEX(hubd));
91044844Slg150142
91054844Slg150142 ndi_rele_devi(hdip);
91064844Slg150142
91074844Slg150142 return (rval);
91084844Slg150142 }
9109