17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
237c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * EHCI Host Controller Driver (EHCI)
317c478bd9Sstevel@tonic-gate  *
327c478bd9Sstevel@tonic-gate  * The EHCI driver is a software driver which interfaces to the Universal
337c478bd9Sstevel@tonic-gate  * Serial Bus layer (USBA) and the Host Controller (HC). The interface to
347c478bd9Sstevel@tonic-gate  * the Host Controller is defined by the EHCI Host Controller Interface.
357c478bd9Sstevel@tonic-gate  *
367c478bd9Sstevel@tonic-gate  * This module contains the code for root hub related functions.
377c478bd9Sstevel@tonic-gate  *
387c478bd9Sstevel@tonic-gate  * NOTE:
397c478bd9Sstevel@tonic-gate  *
407c478bd9Sstevel@tonic-gate  * ONE_XFER is not supported on root hub interrupt polling
417c478bd9Sstevel@tonic-gate  */
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehcid.h>
447c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/ehci/ehci_util.h>
457c478bd9Sstevel@tonic-gate #include <sys/usb/usba/usba_types.h>
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate /* Static function prototypes */
487c478bd9Sstevel@tonic-gate static int	ehci_handle_set_clear_port_feature(
497c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
507c478bd9Sstevel@tonic-gate 				uchar_t 		bRequest,
517c478bd9Sstevel@tonic-gate 				uint16_t		wValue,
527c478bd9Sstevel@tonic-gate 				uint16_t		port);
537c478bd9Sstevel@tonic-gate static void	ehci_handle_port_power(
547c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
557c478bd9Sstevel@tonic-gate 				uint16_t		port,
567c478bd9Sstevel@tonic-gate 				uint_t			on);
577c478bd9Sstevel@tonic-gate static void	ehci_handle_port_enable(
587c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
597c478bd9Sstevel@tonic-gate 				uint16_t		port,
607c478bd9Sstevel@tonic-gate 				uint_t			on);
617c478bd9Sstevel@tonic-gate static void	ehci_handle_clrchng_port_enable(
627c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
637c478bd9Sstevel@tonic-gate 				uint16_t		port);
647c478bd9Sstevel@tonic-gate static void	ehci_handle_port_suspend(
657c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
667c478bd9Sstevel@tonic-gate 				uint16_t		port,
677c478bd9Sstevel@tonic-gate 				uint_t			on);
687c478bd9Sstevel@tonic-gate static void	ehci_handle_clrchng_port_suspend(
697c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
707c478bd9Sstevel@tonic-gate 				uint16_t		port);
717c478bd9Sstevel@tonic-gate static void	ehci_handle_port_reset(
727c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
737c478bd9Sstevel@tonic-gate 				uint16_t		port);
747c478bd9Sstevel@tonic-gate static void	ehci_root_hub_reset_occured(
757c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip);
767c478bd9Sstevel@tonic-gate static void	ehci_handle_complete_port_reset(
777c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
787c478bd9Sstevel@tonic-gate 				uint16_t		port);
797c478bd9Sstevel@tonic-gate static void	ehci_handle_clear_port_connection(
807c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
817c478bd9Sstevel@tonic-gate 				uint16_t		port);
827c478bd9Sstevel@tonic-gate static void	ehci_handle_clrchng_port_over_current(
837c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
847c478bd9Sstevel@tonic-gate 				uint16_t		port);
857c478bd9Sstevel@tonic-gate static void	ehci_handle_get_port_status(
867c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
877c478bd9Sstevel@tonic-gate 				uint16_t		port);
887c478bd9Sstevel@tonic-gate static void	ehci_handle_get_hub_descriptor(
897c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip);
907c478bd9Sstevel@tonic-gate static void	ehci_handle_get_hub_status(
917c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip);
927c478bd9Sstevel@tonic-gate static uint_t	ehci_get_root_hub_port_status(
937c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
947c478bd9Sstevel@tonic-gate 				uint16_t		port);
957c478bd9Sstevel@tonic-gate static int	ehci_is_port_owner(
967c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
977c478bd9Sstevel@tonic-gate 				uint16_t		port);
987c478bd9Sstevel@tonic-gate static int	ehci_root_hub_allocate_intr_pipe_resource(
997c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1007c478bd9Sstevel@tonic-gate 				usb_flags_t		flags);
1017c478bd9Sstevel@tonic-gate static void	ehci_root_hub_intr_pipe_cleanup(
1027c478bd9Sstevel@tonic-gate 				ehci_state_t		*ehcip,
1037c478bd9Sstevel@tonic-gate 				usb_cr_t		completion_reason);
1047c478bd9Sstevel@tonic-gate static void	ehci_handle_root_hub_status_change(void *arg);
1057c478bd9Sstevel@tonic-gate static void	ehci_root_hub_hcdi_callback(
1067c478bd9Sstevel@tonic-gate 				usba_pipe_handle_data_t	*ph,
1077c478bd9Sstevel@tonic-gate 				usb_cr_t		completion_reason);
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate /*
1117c478bd9Sstevel@tonic-gate  * ehci_init_root_hub:
1127c478bd9Sstevel@tonic-gate  *
1137c478bd9Sstevel@tonic-gate  * Initialize the root hub
1147c478bd9Sstevel@tonic-gate  */
1157c478bd9Sstevel@tonic-gate int
1167c478bd9Sstevel@tonic-gate ehci_init_root_hub(ehci_state_t	*ehcip)
1177c478bd9Sstevel@tonic-gate {
1187c478bd9Sstevel@tonic-gate 	usb_hub_descr_t		*root_hub_descr =
1197c478bd9Sstevel@tonic-gate 				    &ehcip->ehci_root_hub.rh_descr;
1207c478bd9Sstevel@tonic-gate 	uint_t			i, length, port_state;
1217c478bd9Sstevel@tonic-gate 	uint32_t		capability;
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
1247c478bd9Sstevel@tonic-gate 	    "ehci_init_root_hub:");
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 	/* Read the EHCI capability register */
1277c478bd9Sstevel@tonic-gate 	capability = Get_Cap(ehci_hcs_params);
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	/*
1307c478bd9Sstevel@tonic-gate 	 * Build the Root hub descriptor by looking EHCI capability
1317c478bd9Sstevel@tonic-gate 	 * and operational registers.
1327c478bd9Sstevel@tonic-gate 	 */
1337c478bd9Sstevel@tonic-gate 	root_hub_descr->bDescriptorType = ROOT_HUB_DESCRIPTOR_TYPE;
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	if ((capability & EHCI_HCS_NUM_PORTS) > EHCI_MAX_RH_PORTS) {
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
1387c478bd9Sstevel@tonic-gate 		    "ehci_init_root_hub: Invalid no of root hub ports 0x%x",
1397c478bd9Sstevel@tonic-gate 		    capability & EHCI_HCS_NUM_PORTS);
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
1427c478bd9Sstevel@tonic-gate 	}
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate 	/* Obtain the number of downstream ports */
1457c478bd9Sstevel@tonic-gate 	root_hub_descr->bNbrPorts = capability & EHCI_HCS_NUM_PORTS;
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate 	length = root_hub_descr->bNbrPorts / 8;
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate 	if (length) {
1507c478bd9Sstevel@tonic-gate 		root_hub_descr->bDescLength = 7 + (2 * (length + 1));
1517c478bd9Sstevel@tonic-gate 	} else {
1527c478bd9Sstevel@tonic-gate 		root_hub_descr->bDescLength = ROOT_HUB_DESCRIPTOR_LENGTH;
1537c478bd9Sstevel@tonic-gate 	}
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 	/*
1567c478bd9Sstevel@tonic-gate 	 * Obtain the number of Classic or Companion USB 1.1 (OHCI/UHCI)
1577c478bd9Sstevel@tonic-gate 	 * Host Controllers information.
1587c478bd9Sstevel@tonic-gate 	 */
1597c478bd9Sstevel@tonic-gate 	ehcip->ehci_root_hub.rh_companion_controllers = (capability &
1607c478bd9Sstevel@tonic-gate 	    EHCI_HCS_NUM_COMP_CTRLS) >> EHCI_HCS_NUM_COMP_CTRL_SHIFT;
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate 	/*
1637c478bd9Sstevel@tonic-gate 	 * Determine the Power Switching Mode
1647c478bd9Sstevel@tonic-gate 	 *
1657c478bd9Sstevel@tonic-gate 	 * EHCI Specification, root hub supports either no power switching
1667c478bd9Sstevel@tonic-gate 	 * individual port power switching. Also determine the Over-current
1677c478bd9Sstevel@tonic-gate 	 * Protection Mode.
1687c478bd9Sstevel@tonic-gate 	 */
1697c478bd9Sstevel@tonic-gate 	if (capability & EHCI_HCS_PORT_POWER_CONTROL) {
1707c478bd9Sstevel@tonic-gate 		/* Each port is powered individually */
1717c478bd9Sstevel@tonic-gate 		root_hub_descr-> wHubCharacteristics =
1727c478bd9Sstevel@tonic-gate 		    HUB_CHARS_INDIVIDUAL_PORT_POWER;
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 		/* Assume individual overcurrent reporting */
1757c478bd9Sstevel@tonic-gate 		root_hub_descr->wHubCharacteristics |=
1767c478bd9Sstevel@tonic-gate 		    HUB_CHARS_INDIV_OVER_CURRENT;
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 		/* Each port will start off in the POWERED_OFF mode */
1797c478bd9Sstevel@tonic-gate 		port_state = POWERED_OFF;
1807c478bd9Sstevel@tonic-gate 	} else {
1817c478bd9Sstevel@tonic-gate 		/* The ports are powered when the ctlr is powered */
1827c478bd9Sstevel@tonic-gate 		root_hub_descr->
1837c478bd9Sstevel@tonic-gate 		    wHubCharacteristics = HUB_CHARS_NO_POWER_SWITCHING;
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 		/* Assume no overcurrent reporting */
1867c478bd9Sstevel@tonic-gate 		root_hub_descr->wHubCharacteristics |=
1877c478bd9Sstevel@tonic-gate 		    HUB_CHARS_NO_OVER_CURRENT;
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 		port_state = DISCONNECTED;
1907c478bd9Sstevel@tonic-gate 	}
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	/* Look at the port indicator information */
1937c478bd9Sstevel@tonic-gate 	if (capability & EHCI_HCS_PORT_INDICATOR) {
1947c478bd9Sstevel@tonic-gate 		root_hub_descr->wHubCharacteristics |= HUB_CHARS_PORT_INDICATOR;
1957c478bd9Sstevel@tonic-gate 	}
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	/*
1987c478bd9Sstevel@tonic-gate 	 * Obtain the power on to power good time of the ports.
1997c478bd9Sstevel@tonic-gate 	 *
2007c478bd9Sstevel@tonic-gate 	 * Assume: Zero for this field.
2017c478bd9Sstevel@tonic-gate 	 */
2027c478bd9Sstevel@tonic-gate 	root_hub_descr->bPwrOn2PwrGood = 2;
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
2057c478bd9Sstevel@tonic-gate 	    "Power on to power good %d", root_hub_descr->bPwrOn2PwrGood);
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	/* Indicate if the device is removable */
2087c478bd9Sstevel@tonic-gate 	root_hub_descr->DeviceRemovable = 0;
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	/* Set PortPowerControlMask to zero */
2117c478bd9Sstevel@tonic-gate 	root_hub_descr->PortPwrCtrlMask = 0;
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	/* Set the state of each port and initialize the status */
2147c478bd9Sstevel@tonic-gate 	for (i = 0; i < root_hub_descr->bNbrPorts; i++) {
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 		/* Initilize state/status of each root hub port */
2177c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_port_state[i] = port_state;
2187c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_port_status[i] = 0;
2197c478bd9Sstevel@tonic-gate 	}
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
2227c478bd9Sstevel@tonic-gate }
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate /*
2267c478bd9Sstevel@tonic-gate  * ehci_load_root_hub_driver:
2277c478bd9Sstevel@tonic-gate  *
2287c478bd9Sstevel@tonic-gate  * Attach the root hub
2297c478bd9Sstevel@tonic-gate  */
2307c478bd9Sstevel@tonic-gate static usb_dev_descr_t ehci_root_hub_device_descriptor = {
2317c478bd9Sstevel@tonic-gate 	0x12,		/* bLength */
2327c478bd9Sstevel@tonic-gate 	0x01,		/* bDescriptorType, Device */
2337c478bd9Sstevel@tonic-gate 	0x200,		/* bcdUSB, v2.0 */
2347c478bd9Sstevel@tonic-gate 	0x09,		/* bDeviceClass */
2357c478bd9Sstevel@tonic-gate 	0x00,		/* bDeviceSubClass */
2367c478bd9Sstevel@tonic-gate 	0x01,		/* bDeviceProtocol */
2377c478bd9Sstevel@tonic-gate 	0x40,		/* bMaxPacketSize0 */
2387c478bd9Sstevel@tonic-gate 	0x00,		/* idVendor */
2397c478bd9Sstevel@tonic-gate 	0x00,		/* idProduct */
2407c478bd9Sstevel@tonic-gate 	0x00,		/* bcdDevice */
2417c478bd9Sstevel@tonic-gate 	0x00,		/* iManufacturer */
2427c478bd9Sstevel@tonic-gate 	0x00,		/* iProduct */
2437c478bd9Sstevel@tonic-gate 	0x00,		/* iSerialNumber */
2447c478bd9Sstevel@tonic-gate 	0x01		/* bNumConfigurations */
2457c478bd9Sstevel@tonic-gate };
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate static uchar_t ehci_root_hub_config_descriptor[] = {
2487c478bd9Sstevel@tonic-gate 	/* One configuartion */
2497c478bd9Sstevel@tonic-gate 	0x09,		/* bLength */
2507c478bd9Sstevel@tonic-gate 	0x02,		/* bDescriptorType, Configuartion */
2517c478bd9Sstevel@tonic-gate 	0x19, 0x00,	/* wTotalLength */
2527c478bd9Sstevel@tonic-gate 	0x01,		/* bNumInterfaces */
2537c478bd9Sstevel@tonic-gate 	0x01,		/* bConfigurationValue */
2547c478bd9Sstevel@tonic-gate 	0x00,		/* iConfiguration */
2557c478bd9Sstevel@tonic-gate 	0x40,		/* bmAttributes */
2567c478bd9Sstevel@tonic-gate 	0x00,		/* MaxPower */
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	/* One Interface */
2597c478bd9Sstevel@tonic-gate 	0x09,		/* bLength */
2607c478bd9Sstevel@tonic-gate 	0x04,		/* bDescriptorType, Interface */
2617c478bd9Sstevel@tonic-gate 	0x00,		/* bInterfaceNumber */
2627c478bd9Sstevel@tonic-gate 	0x00,		/* bAlternateSetting */
2637c478bd9Sstevel@tonic-gate 	0x01,		/* bNumEndpoints */
2647c478bd9Sstevel@tonic-gate 	0x09,		/* bInterfaceClass */
2657c478bd9Sstevel@tonic-gate 	0x01,		/* bInterfaceSubClass */
2667c478bd9Sstevel@tonic-gate 	0x00,		/* bInterfaceProtocol */
2677c478bd9Sstevel@tonic-gate 	0x00,		/* iInterface */
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	/* One Endpoint (status change endpoint) */
2707c478bd9Sstevel@tonic-gate 	0x07,		/* bLength */
2717c478bd9Sstevel@tonic-gate 	0x05,		/* bDescriptorType, Endpoint */
2727c478bd9Sstevel@tonic-gate 	0x81,		/* bEndpointAddress */
2737c478bd9Sstevel@tonic-gate 	0x03,		/* bmAttributes */
2747c478bd9Sstevel@tonic-gate 	0x01, 0x00,	/* wMaxPacketSize, 1 +  (EHCI_MAX_RH_PORTS / 8) */
2757c478bd9Sstevel@tonic-gate 	0xff		/* bInterval */
2767c478bd9Sstevel@tonic-gate };
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate int
2797c478bd9Sstevel@tonic-gate ehci_load_root_hub_driver(ehci_state_t	*ehcip)
2807c478bd9Sstevel@tonic-gate {
2817c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
2827c478bd9Sstevel@tonic-gate 	    "ehci_load_root_hub_driver:");
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	return (usba_hubdi_bind_root_hub(ehcip->ehci_dip,
2857c478bd9Sstevel@tonic-gate 	    ehci_root_hub_config_descriptor,
2867c478bd9Sstevel@tonic-gate 	    sizeof (ehci_root_hub_config_descriptor),
2877c478bd9Sstevel@tonic-gate 	    &ehci_root_hub_device_descriptor));
2887c478bd9Sstevel@tonic-gate }
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate /*
2927c478bd9Sstevel@tonic-gate  * ehci_unload_root_hub_driver:
2937c478bd9Sstevel@tonic-gate  */
2947c478bd9Sstevel@tonic-gate int
2957c478bd9Sstevel@tonic-gate ehci_unload_root_hub_driver(ehci_state_t	*ehcip)
2967c478bd9Sstevel@tonic-gate {
2977c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
2987c478bd9Sstevel@tonic-gate 	    "ehci_unload_root_hub_driver:");
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	return (usba_hubdi_unbind_root_hub(ehcip->ehci_dip));
3017c478bd9Sstevel@tonic-gate }
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate /*
3057c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_pipe_open:
3067c478bd9Sstevel@tonic-gate  *
3077c478bd9Sstevel@tonic-gate  * Handle opening of control and interrupt pipes on root hub.
3087c478bd9Sstevel@tonic-gate  */
3097c478bd9Sstevel@tonic-gate /* ARGSUSED */
3107c478bd9Sstevel@tonic-gate int
3117c478bd9Sstevel@tonic-gate ehci_handle_root_hub_pipe_open(
3127c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
3137c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
3147c478bd9Sstevel@tonic-gate {
3157c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
3167c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
3177c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
3207c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_pipe_open: Root hub pipe open");
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 	switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
3257c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
3267c478bd9Sstevel@tonic-gate 		/* Save control pipe handle */
3277c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_ctrl_pipe_handle = ph;
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 		/* Set state of the root hub control pipe as idle */
3307c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_ctrl_pipe_state = EHCI_PIPE_STATE_IDLE;
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_curr_ctrl_reqp = NULL;
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
3357c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_open: Root hub control "
3367c478bd9Sstevel@tonic-gate 		    "pipe open succeeded");
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 		break;
3397c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
3407c478bd9Sstevel@tonic-gate 		/* Save interrupt pipe handle */
3417c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_handle = ph;
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate 		/* Set state of the root hub interrupt pipe as idle */
3447c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_state = EHCI_PIPE_STATE_IDLE;
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_client_intr_reqp = NULL;
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_curr_intr_reqp = NULL;
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
3517c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_open: Root hub interrupt "
3527c478bd9Sstevel@tonic-gate 		    "pipe open succeeded");
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 		break;
3557c478bd9Sstevel@tonic-gate 	default:
3567c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
3577c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_open: Root hub pipe open"
3587c478bd9Sstevel@tonic-gate 		    "failed");
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
3617c478bd9Sstevel@tonic-gate 	}
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate 	ehcip->ehci_open_pipe_count++;
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
3667c478bd9Sstevel@tonic-gate }
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate /*
3707c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_pipe_close:
3717c478bd9Sstevel@tonic-gate  *
3727c478bd9Sstevel@tonic-gate  * Handle closing of control and interrupt pipes on root hub.
3737c478bd9Sstevel@tonic-gate  */
3747c478bd9Sstevel@tonic-gate /* ARGSUSED */
3757c478bd9Sstevel@tonic-gate int
3767c478bd9Sstevel@tonic-gate ehci_handle_root_hub_pipe_close(usba_pipe_handle_data_t	*ph)
3777c478bd9Sstevel@tonic-gate {
3787c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
3797c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
3807c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
3837c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_pipe_close: Root hub pipe close");
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
3887c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
3897c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.
3907c478bd9Sstevel@tonic-gate 		    rh_ctrl_pipe_state != EHCI_PIPE_STATE_CLOSE);
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate 		/* Set state of the root hub control pipe as close */
3937c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_ctrl_pipe_state = EHCI_PIPE_STATE_CLOSE;
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 		/* Set root hub control pipe handle to null */
3967c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_ctrl_pipe_handle = NULL;
3977c478bd9Sstevel@tonic-gate 
3987c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
3997c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_close: "
4007c478bd9Sstevel@tonic-gate 		    "Root hub control pipe close succeeded");
4017c478bd9Sstevel@tonic-gate 		break;
4027c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
4037c478bd9Sstevel@tonic-gate 		ASSERT((eptd->bEndpointAddress & USB_EP_NUM_MASK) == 1);
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.
4067c478bd9Sstevel@tonic-gate 		    rh_intr_pipe_state != EHCI_PIPE_STATE_CLOSE);
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 		/* Set state of the root hub interrupt pipe as close */
4097c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_state = EHCI_PIPE_STATE_CLOSE;
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 		/* Do interrupt pipe cleanup */
4127c478bd9Sstevel@tonic-gate 		ehci_root_hub_intr_pipe_cleanup(ehcip, USB_CR_PIPE_CLOSING);
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 		/* Set root hub interrupt pipe handle to null */
4157c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_handle = NULL;
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
4187c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_close: "
4197c478bd9Sstevel@tonic-gate 		    "Root hub interrupt pipe close succeeded");
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 		break;
4227c478bd9Sstevel@tonic-gate 	default:
4237c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
4247c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_close: "
4257c478bd9Sstevel@tonic-gate 		    "Root hub pipe close failed");
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
4287c478bd9Sstevel@tonic-gate 	}
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	ehcip->ehci_open_pipe_count--;
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
4337c478bd9Sstevel@tonic-gate }
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate /*
4377c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_pipe_reset:
4387c478bd9Sstevel@tonic-gate  *
4397c478bd9Sstevel@tonic-gate  * Handle resetting of control and interrupt pipes on root hub.
4407c478bd9Sstevel@tonic-gate  */
4417c478bd9Sstevel@tonic-gate /* ARGSUSED */
4427c478bd9Sstevel@tonic-gate int
4437c478bd9Sstevel@tonic-gate ehci_handle_root_hub_pipe_reset(
4447c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
4457c478bd9Sstevel@tonic-gate 	usb_flags_t		usb_flags)
4467c478bd9Sstevel@tonic-gate {
4477c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
4487c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
4497c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
4507c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
4537c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_pipe_reset: Root hub pipe reset");
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 	switch (eptd->bmAttributes & USB_EP_ATTR_MASK) {
4587c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
4597c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_ctrl_pipe_state = EHCI_PIPE_STATE_IDLE;
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
4627c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_reset: Pipe reset"
4637c478bd9Sstevel@tonic-gate 		    "for the root hub control pipe successful");
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 		break;
4667c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
4677c478bd9Sstevel@tonic-gate 		ASSERT((eptd->bEndpointAddress & USB_EP_NUM_MASK) == 1);
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 		if ((ehcip->ehci_root_hub.rh_client_intr_reqp) &&
4707c478bd9Sstevel@tonic-gate 		    (ehcip->ehci_root_hub.rh_intr_pipe_state !=
4717c478bd9Sstevel@tonic-gate 		    EHCI_PIPE_STATE_IDLE)) {
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.
4747c478bd9Sstevel@tonic-gate 			    rh_intr_pipe_state = EHCI_PIPE_STATE_RESET;
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 			/* Do interrupt pipe cleanup */
4777c478bd9Sstevel@tonic-gate 			ehci_root_hub_intr_pipe_cleanup(
4787c478bd9Sstevel@tonic-gate 			    ehcip, USB_CR_PIPE_RESET);
4797c478bd9Sstevel@tonic-gate 		}
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.
4827c478bd9Sstevel@tonic-gate 		    rh_intr_pipe_state == EHCI_PIPE_STATE_IDLE);
4837c478bd9Sstevel@tonic-gate 
4847c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
4857c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_reset: "
4867c478bd9Sstevel@tonic-gate 		    "Pipe reset for root hub interrupt pipe successful");
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate 		break;
4897c478bd9Sstevel@tonic-gate 	default:
4907c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
4917c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_reset: "
4927c478bd9Sstevel@tonic-gate 		    "Root hub pipe reset failed");
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
4957c478bd9Sstevel@tonic-gate 		break;
4967c478bd9Sstevel@tonic-gate 	}
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate 	return (error);
5017c478bd9Sstevel@tonic-gate }
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate /*
5057c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_request:
5067c478bd9Sstevel@tonic-gate  *
5077c478bd9Sstevel@tonic-gate  * Intercept a root hub request. Handle the  root hub request through the
5087c478bd9Sstevel@tonic-gate  * registers
5097c478bd9Sstevel@tonic-gate  */
5107c478bd9Sstevel@tonic-gate /* ARGSUSED */
5117c478bd9Sstevel@tonic-gate int
5127c478bd9Sstevel@tonic-gate ehci_handle_root_hub_request(
5137c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
5147c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
5157c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp)
5167c478bd9Sstevel@tonic-gate {
5177c478bd9Sstevel@tonic-gate 	uchar_t			bmRequestType = ctrl_reqp->ctrl_bmRequestType;
5187c478bd9Sstevel@tonic-gate 	uchar_t			bRequest = ctrl_reqp->ctrl_bRequest;
5197c478bd9Sstevel@tonic-gate 	uint16_t		wValue = ctrl_reqp->ctrl_wValue;
5207c478bd9Sstevel@tonic-gate 	uint16_t		wIndex = ctrl_reqp->ctrl_wIndex;
5217c478bd9Sstevel@tonic-gate 	uint16_t		wLength = ctrl_reqp->ctrl_wLength;
5227c478bd9Sstevel@tonic-gate 	mblk_t			*data = ctrl_reqp->ctrl_data;
5237c478bd9Sstevel@tonic-gate 	uint16_t		port = wIndex - 1;
5247c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason;
5257c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
5287c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_request: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%p",
5297c478bd9Sstevel@tonic-gate 	    bmRequestType, bRequest, wValue, wIndex, wLength, (void *)data);
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 	if (ehcip->ehci_root_hub.
5347c478bd9Sstevel@tonic-gate 	    rh_ctrl_pipe_state != EHCI_PIPE_STATE_IDLE) {
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
5377c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_request: Pipe is not idle");
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
5427c478bd9Sstevel@tonic-gate 	}
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	/* Save the current control request pointer */
5457c478bd9Sstevel@tonic-gate 	ehcip->ehci_root_hub.rh_curr_ctrl_reqp = ctrl_reqp;
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 	/* Set pipe state to active */
5487c478bd9Sstevel@tonic-gate 	ehcip->ehci_root_hub.rh_ctrl_pipe_state = EHCI_PIPE_STATE_ACTIVE;
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate 	switch (bmRequestType) {
5537c478bd9Sstevel@tonic-gate 	case HANDLE_PORT_FEATURE:
5547c478bd9Sstevel@tonic-gate 		error = ehci_handle_set_clear_port_feature(ehcip,
5557c478bd9Sstevel@tonic-gate 		    bRequest, wValue, port);
5567c478bd9Sstevel@tonic-gate 		break;
5577c478bd9Sstevel@tonic-gate 	case GET_PORT_STATUS:
5587c478bd9Sstevel@tonic-gate 		ehci_handle_get_port_status(ehcip, port);
5597c478bd9Sstevel@tonic-gate 		break;
5607c478bd9Sstevel@tonic-gate 	case HUB_CLASS_REQ:
5617c478bd9Sstevel@tonic-gate 		switch (bRequest) {
5627c478bd9Sstevel@tonic-gate 		case USB_REQ_GET_STATUS:
5637c478bd9Sstevel@tonic-gate 			ehci_handle_get_hub_status(ehcip);
5647c478bd9Sstevel@tonic-gate 			break;
5657c478bd9Sstevel@tonic-gate 		case USB_REQ_GET_DESCR:
5667c478bd9Sstevel@tonic-gate 			ehci_handle_get_hub_descriptor(ehcip);
5677c478bd9Sstevel@tonic-gate 			break;
5687c478bd9Sstevel@tonic-gate 		default:
5697c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
5707c478bd9Sstevel@tonic-gate 			    "ehci_handle_root_hub_request:"
5717c478bd9Sstevel@tonic-gate 			    "Unsupported request 0x%x", bRequest);
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 			error = USB_FAILURE;
5747c478bd9Sstevel@tonic-gate 			break;
5757c478bd9Sstevel@tonic-gate 		}
5767c478bd9Sstevel@tonic-gate 		break;
5777c478bd9Sstevel@tonic-gate 	default:
5787c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
5797c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_request: "
5807c478bd9Sstevel@tonic-gate 		    "Unsupported request 0x%x", bmRequestType);
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
5837c478bd9Sstevel@tonic-gate 		break;
5847c478bd9Sstevel@tonic-gate 	}
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 	completion_reason = (error) ? USB_CR_NOT_SUPPORTED : USB_CR_OK;
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
5897c478bd9Sstevel@tonic-gate 	ehci_root_hub_hcdi_callback(ph, completion_reason);
5907c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
5937c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_request: error = %d", error);
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
5967c478bd9Sstevel@tonic-gate }
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate /*
6007c478bd9Sstevel@tonic-gate  * ehci_handle_set_clear_port_feature:
6017c478bd9Sstevel@tonic-gate  */
6027c478bd9Sstevel@tonic-gate static int
6037c478bd9Sstevel@tonic-gate ehci_handle_set_clear_port_feature(
6047c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
6057c478bd9Sstevel@tonic-gate 	uchar_t 		bRequest,
6067c478bd9Sstevel@tonic-gate 	uint16_t		wValue,
6077c478bd9Sstevel@tonic-gate 	uint16_t		port)
6087c478bd9Sstevel@tonic-gate {
6097c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
6127c478bd9Sstevel@tonic-gate 	    "ehci_handle_set_clear_port_feature: 0x%x 0x%x 0x%x",
6137c478bd9Sstevel@tonic-gate 	    bRequest, wValue, port);
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	switch (bRequest) {
6167c478bd9Sstevel@tonic-gate 	case USB_REQ_SET_FEATURE:
6177c478bd9Sstevel@tonic-gate 		switch (wValue) {
6187c478bd9Sstevel@tonic-gate 		case CFS_PORT_ENABLE:
6197c478bd9Sstevel@tonic-gate 			ehci_handle_port_enable(ehcip, port, 1);
6207c478bd9Sstevel@tonic-gate 			break;
6217c478bd9Sstevel@tonic-gate 		case CFS_PORT_SUSPEND:
6227c478bd9Sstevel@tonic-gate 			ehci_handle_port_suspend(ehcip, port, 1);
6237c478bd9Sstevel@tonic-gate 			break;
6247c478bd9Sstevel@tonic-gate 		case CFS_PORT_RESET:
6257c478bd9Sstevel@tonic-gate 			ehci_handle_port_reset(ehcip, port);
6267c478bd9Sstevel@tonic-gate 			break;
6277c478bd9Sstevel@tonic-gate 		case CFS_PORT_POWER:
6287c478bd9Sstevel@tonic-gate 			ehci_handle_port_power(ehcip, port, 1);
6297c478bd9Sstevel@tonic-gate 			break;
6307c478bd9Sstevel@tonic-gate 		default:
6317c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
6327c478bd9Sstevel@tonic-gate 			    "ehci_handle_set_clear_port_feature: "
6337c478bd9Sstevel@tonic-gate 			    "Unsupported request 0x%x 0x%x", bRequest, wValue);
6347c478bd9Sstevel@tonic-gate 
6357c478bd9Sstevel@tonic-gate 			error = USB_FAILURE;
6367c478bd9Sstevel@tonic-gate 			break;
6377c478bd9Sstevel@tonic-gate 		}
6387c478bd9Sstevel@tonic-gate 		break;
6397c478bd9Sstevel@tonic-gate 	case USB_REQ_CLEAR_FEATURE:
6407c478bd9Sstevel@tonic-gate 		switch (wValue) {
6417c478bd9Sstevel@tonic-gate 		case CFS_PORT_ENABLE:
6427c478bd9Sstevel@tonic-gate 			ehci_handle_port_enable(ehcip, port, 0);
6437c478bd9Sstevel@tonic-gate 			break;
6447c478bd9Sstevel@tonic-gate 		case CFS_C_PORT_ENABLE:
6457c478bd9Sstevel@tonic-gate 			ehci_handle_clrchng_port_enable(ehcip, port);
6467c478bd9Sstevel@tonic-gate 			break;
6477c478bd9Sstevel@tonic-gate 		case CFS_PORT_SUSPEND:
6487c478bd9Sstevel@tonic-gate 			ehci_handle_port_suspend(ehcip, port, 0);
6497c478bd9Sstevel@tonic-gate 			break;
6507c478bd9Sstevel@tonic-gate 		case CFS_C_PORT_SUSPEND:
6517c478bd9Sstevel@tonic-gate 			ehci_handle_clrchng_port_suspend(ehcip, port);
6527c478bd9Sstevel@tonic-gate 			break;
6537c478bd9Sstevel@tonic-gate 		case CFS_C_PORT_RESET:
6547c478bd9Sstevel@tonic-gate 			ehci_handle_complete_port_reset(ehcip, port);
6557c478bd9Sstevel@tonic-gate 			break;
6567c478bd9Sstevel@tonic-gate 		case CFS_PORT_POWER:
6577c478bd9Sstevel@tonic-gate 			ehci_handle_port_power(ehcip, port, 0);
6587c478bd9Sstevel@tonic-gate 			break;
6597c478bd9Sstevel@tonic-gate 		case CFS_C_PORT_CONNECTION:
6607c478bd9Sstevel@tonic-gate 			ehci_handle_clear_port_connection(ehcip, port);
6617c478bd9Sstevel@tonic-gate 			break;
6627c478bd9Sstevel@tonic-gate 		case CFS_C_PORT_OVER_CURRENT:
6637c478bd9Sstevel@tonic-gate 			ehci_handle_clrchng_port_over_current(ehcip, port);
6647c478bd9Sstevel@tonic-gate 			break;
6657c478bd9Sstevel@tonic-gate 		default:
6667c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
6677c478bd9Sstevel@tonic-gate 			    "ehci_handle_set_clear_port_feature: "
6687c478bd9Sstevel@tonic-gate 			    "Unsupported request 0x%x 0x%x", bRequest, wValue);
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 			error = USB_FAILURE;
6717c478bd9Sstevel@tonic-gate 			break;
6727c478bd9Sstevel@tonic-gate 		}
6737c478bd9Sstevel@tonic-gate 		    break;
6747c478bd9Sstevel@tonic-gate 	default:
6757c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
6767c478bd9Sstevel@tonic-gate 		    "ehci_handle_set_clear_port_feature: "
6777c478bd9Sstevel@tonic-gate 		    "Unsupported request 0x%x 0x%x", bRequest, wValue);
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
6807c478bd9Sstevel@tonic-gate 		break;
6817c478bd9Sstevel@tonic-gate 	}
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 	return (error);
6847c478bd9Sstevel@tonic-gate }
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate /*
6887c478bd9Sstevel@tonic-gate  * ehci_handle_port_power:
6897c478bd9Sstevel@tonic-gate  *
6907c478bd9Sstevel@tonic-gate  * Turn on a root hub port.
6917c478bd9Sstevel@tonic-gate  */
6927c478bd9Sstevel@tonic-gate static void
6937c478bd9Sstevel@tonic-gate ehci_handle_port_power(
6947c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
6957c478bd9Sstevel@tonic-gate 	uint16_t		port,
6967c478bd9Sstevel@tonic-gate 	uint_t			on)
6977c478bd9Sstevel@tonic-gate {
6987c478bd9Sstevel@tonic-gate 	uint_t			port_status;
6997c478bd9Sstevel@tonic-gate 	ehci_root_hub_t		*rh;
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
7047c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 	rh = &ehcip->ehci_root_hub;
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
7097c478bd9Sstevel@tonic-gate 	    "ehci_handle_port_power: port = 0x%x status = 0x%x on = %d",
7107c478bd9Sstevel@tonic-gate 	    port, port_status, on);
7117c478bd9Sstevel@tonic-gate 
7127c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
7137c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
7147c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 		return;
7177c478bd9Sstevel@tonic-gate 	}
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate 	if (on) {
7207c478bd9Sstevel@tonic-gate 		/* See if the port power is already on */
7217c478bd9Sstevel@tonic-gate 		if (!(port_status & EHCI_RH_PORT_POWER)) {
7227c478bd9Sstevel@tonic-gate 			/* Turn the port on */
7237c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
7247c478bd9Sstevel@tonic-gate 			    port_status | EHCI_RH_PORT_POWER);
7257c478bd9Sstevel@tonic-gate 		}
7267c478bd9Sstevel@tonic-gate 
7277c478bd9Sstevel@tonic-gate 		rh->rh_port_status[port] = 0;
7287c478bd9Sstevel@tonic-gate 		rh->rh_port_state[port] = DISCONNECTED;
7297c478bd9Sstevel@tonic-gate 	} else {
7307c478bd9Sstevel@tonic-gate 		/* See if the port power is already OFF */
7317c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_POWER) {
7327c478bd9Sstevel@tonic-gate 			/* Turn-off the port */
7337c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
7347c478bd9Sstevel@tonic-gate 			    port_status & ~EHCI_RH_PORT_POWER);
7357c478bd9Sstevel@tonic-gate 		}
7367c478bd9Sstevel@tonic-gate 
7377c478bd9Sstevel@tonic-gate 		rh->rh_port_status[port] = 0;
7387c478bd9Sstevel@tonic-gate 		rh->rh_port_state[port] = POWERED_OFF;
7397c478bd9Sstevel@tonic-gate 	}
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
7427c478bd9Sstevel@tonic-gate 	    "ehci_handle_port_power done: port = 0x%x status = 0x%x on = %d",
7437c478bd9Sstevel@tonic-gate 	    port, Get_OpReg(ehci_rh_port_status[port]), on);
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
7467c478bd9Sstevel@tonic-gate }
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate /*
7507c478bd9Sstevel@tonic-gate  * ehci_handle_port_enable:
7517c478bd9Sstevel@tonic-gate  *
7527c478bd9Sstevel@tonic-gate  * Handle port enable request.
7537c478bd9Sstevel@tonic-gate  */
7547c478bd9Sstevel@tonic-gate static void
7557c478bd9Sstevel@tonic-gate ehci_handle_port_enable(
7567c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
7577c478bd9Sstevel@tonic-gate 	uint16_t		port,
7587c478bd9Sstevel@tonic-gate 	uint_t			on)
7597c478bd9Sstevel@tonic-gate {
7607c478bd9Sstevel@tonic-gate 	uint_t			port_status;
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
7657c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
7687c478bd9Sstevel@tonic-gate 	    "ehci_handle_port_enable: port = 0x%x, status = 0x%x",
7697c478bd9Sstevel@tonic-gate 	    port, port_status);
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
7727c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
7737c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 		return;
7767c478bd9Sstevel@tonic-gate 	}
7777c478bd9Sstevel@tonic-gate 
7787c478bd9Sstevel@tonic-gate 	if (on) {
7797c478bd9Sstevel@tonic-gate 		/* See if the port enable is already on */
7807c478bd9Sstevel@tonic-gate 		if (!(port_status & EHCI_RH_PORT_ENABLE)) {
7817c478bd9Sstevel@tonic-gate 			/* Enable the port */
7827c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
7837c478bd9Sstevel@tonic-gate 			    port_status | EHCI_RH_PORT_ENABLE);
7847c478bd9Sstevel@tonic-gate 		}
7857c478bd9Sstevel@tonic-gate 	} else {
7867c478bd9Sstevel@tonic-gate 		/* See if the port enable is already off */
7877c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_ENABLE) {
7887c478bd9Sstevel@tonic-gate 			/* Disable the port */
7897c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
7907c478bd9Sstevel@tonic-gate 			    port_status & ~EHCI_RH_PORT_ENABLE);
7917c478bd9Sstevel@tonic-gate 		}
7927c478bd9Sstevel@tonic-gate 	}
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
7957c478bd9Sstevel@tonic-gate }
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate /*
7997c478bd9Sstevel@tonic-gate  * ehci_handle_clrchng_port_enable:
8007c478bd9Sstevel@tonic-gate  *
8017c478bd9Sstevel@tonic-gate  * Handle clear port enable change bit.
8027c478bd9Sstevel@tonic-gate  */
8037c478bd9Sstevel@tonic-gate static void
8047c478bd9Sstevel@tonic-gate ehci_handle_clrchng_port_enable(
8057c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
8067c478bd9Sstevel@tonic-gate 	uint16_t		port)
8077c478bd9Sstevel@tonic-gate {
8087c478bd9Sstevel@tonic-gate 	uint_t			port_status;
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
8137c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
8167c478bd9Sstevel@tonic-gate 	    "ehci_handle_port_enable: port = 0x%x, status = 0x%x",
8177c478bd9Sstevel@tonic-gate 	    port, port_status);
8187c478bd9Sstevel@tonic-gate 
8197c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
8207c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
8217c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate 		return;
8247c478bd9Sstevel@tonic-gate 	}
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 	/* Clear the PortEnableStatusChange Bit */
8277c478bd9Sstevel@tonic-gate 	Set_OpReg(ehci_rh_port_status[port],
8287c478bd9Sstevel@tonic-gate 	    port_status | EHCI_RH_PORT_ENABLE_CHANGE);
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
8317c478bd9Sstevel@tonic-gate }
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate /*
8357c478bd9Sstevel@tonic-gate  * ehci_handle_port_suspend:
8367c478bd9Sstevel@tonic-gate  *
8377c478bd9Sstevel@tonic-gate  * Handle port suspend/resume request.
8387c478bd9Sstevel@tonic-gate  */
8397c478bd9Sstevel@tonic-gate static void
8407c478bd9Sstevel@tonic-gate ehci_handle_port_suspend(
8417c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
8427c478bd9Sstevel@tonic-gate 	uint16_t		port,
8437c478bd9Sstevel@tonic-gate 	uint_t			on)
8447c478bd9Sstevel@tonic-gate {
8457c478bd9Sstevel@tonic-gate 	uint_t			port_status;
8467c478bd9Sstevel@tonic-gate 
8477c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
8507c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
8517c478bd9Sstevel@tonic-gate 
8527c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
8537c478bd9Sstevel@tonic-gate 	    "ehci_handle_port_suspend: port = 0x%x, status = 0x%x",
8547c478bd9Sstevel@tonic-gate 	    port, port_status);
8557c478bd9Sstevel@tonic-gate 
8567c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
8577c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
8587c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
8597c478bd9Sstevel@tonic-gate 
8607c478bd9Sstevel@tonic-gate 		return;
8617c478bd9Sstevel@tonic-gate 	}
8627c478bd9Sstevel@tonic-gate 
8637c478bd9Sstevel@tonic-gate 	if (on) {
8647c478bd9Sstevel@tonic-gate 		/*
8657c478bd9Sstevel@tonic-gate 		 * Suspend port only if port is enabled and
8667c478bd9Sstevel@tonic-gate 		 * it is not already in suspend state.
8677c478bd9Sstevel@tonic-gate 		 */
8687c478bd9Sstevel@tonic-gate 		if ((port_status & EHCI_RH_PORT_ENABLE) &&
8697c478bd9Sstevel@tonic-gate 		    (!(port_status & EHCI_RH_PORT_SUSPEND))) {
8707c478bd9Sstevel@tonic-gate 			/* Suspend the port */
8717c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
8727c478bd9Sstevel@tonic-gate 			    port_status | EHCI_RH_PORT_SUSPEND);
8737c478bd9Sstevel@tonic-gate 
8747c478bd9Sstevel@tonic-gate 			mutex_exit(&ehcip->ehci_int_mutex);
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 			/* Wait 10ms for port move to suspend state */
8777c478bd9Sstevel@tonic-gate 			delay(drv_usectohz(EHCI_PORT_SUSPEND_TIMEWAIT));
8787c478bd9Sstevel@tonic-gate 
8797c478bd9Sstevel@tonic-gate 			return;
8807c478bd9Sstevel@tonic-gate 		}
8817c478bd9Sstevel@tonic-gate 	} else {
8827c478bd9Sstevel@tonic-gate 		/* Perform resume only if port is in suspend state */
8837c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_SUSPEND) {
8847c478bd9Sstevel@tonic-gate 			/* Resume the port */
8857c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
8867c478bd9Sstevel@tonic-gate 			    port_status | EHCI_RH_PORT_RESUME);
8877c478bd9Sstevel@tonic-gate 		}
8887c478bd9Sstevel@tonic-gate 	}
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
8917c478bd9Sstevel@tonic-gate }
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate 
8947c478bd9Sstevel@tonic-gate /*
8957c478bd9Sstevel@tonic-gate  * ehci_handle_clrchng_port_suspend:
8967c478bd9Sstevel@tonic-gate  *
8977c478bd9Sstevel@tonic-gate  * Handle port clear port suspend change bit.
8987c478bd9Sstevel@tonic-gate  */
8997c478bd9Sstevel@tonic-gate static void
9007c478bd9Sstevel@tonic-gate ehci_handle_clrchng_port_suspend(
9017c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
9027c478bd9Sstevel@tonic-gate 	uint16_t		port)
9037c478bd9Sstevel@tonic-gate {
9047c478bd9Sstevel@tonic-gate 	uint_t			port_status;
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
9077c478bd9Sstevel@tonic-gate 
9087c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
9097c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
9127c478bd9Sstevel@tonic-gate 	    "ehci_handle_clrchng_port_suspend: port = 0x%x, "
9137c478bd9Sstevel@tonic-gate 	    "status = 0x%x", port, port_status);
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
9167c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
9177c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 		return;
9207c478bd9Sstevel@tonic-gate 	}
9217c478bd9Sstevel@tonic-gate 
9227c478bd9Sstevel@tonic-gate 	/* Return if port is not in resume state */
9237c478bd9Sstevel@tonic-gate 	if (!(port_status & EHCI_RH_PORT_RESUME)) {
9247c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate 		return;
9277c478bd9Sstevel@tonic-gate 	}
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate 	/* Wait for 20ms to terminate resume */
9327c478bd9Sstevel@tonic-gate 	delay(drv_usectohz(EHCI_PORT_RESUME_TIMEWAIT));
9337c478bd9Sstevel@tonic-gate 
9347c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
9357c478bd9Sstevel@tonic-gate 
9367c478bd9Sstevel@tonic-gate 	Set_OpReg(ehci_rh_port_status[port],
9377c478bd9Sstevel@tonic-gate 	    port_status & ~EHCI_RH_PORT_RESUME);
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate 	/* Wait 2ms for port to return to high speed mode */
9427c478bd9Sstevel@tonic-gate 	delay(drv_usectohz(EHCI_PORT_RESUME_COMP_TIMEWAIT));
9437c478bd9Sstevel@tonic-gate }
9447c478bd9Sstevel@tonic-gate 
9457c478bd9Sstevel@tonic-gate 
9467c478bd9Sstevel@tonic-gate /*
9477c478bd9Sstevel@tonic-gate  * ehci_handle_port_reset:
9487c478bd9Sstevel@tonic-gate  *
9497c478bd9Sstevel@tonic-gate  * Perform a port reset.
9507c478bd9Sstevel@tonic-gate  */
9517c478bd9Sstevel@tonic-gate static void
9527c478bd9Sstevel@tonic-gate ehci_handle_port_reset(
9537c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
9547c478bd9Sstevel@tonic-gate 	uint16_t		port)
9557c478bd9Sstevel@tonic-gate {
9567c478bd9Sstevel@tonic-gate 	ehci_root_hub_t		*rh;
9577c478bd9Sstevel@tonic-gate 	uint_t			port_status;
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 	/* Get the root hub structure */
9627c478bd9Sstevel@tonic-gate 	rh = &ehcip->ehci_root_hub;
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 	/* Get the port status information */
9657c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
9667c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
9697c478bd9Sstevel@tonic-gate 	    "ehci_handle_port_reset: port = 0x%x status = 0x%x",
9707c478bd9Sstevel@tonic-gate 	    port, port_status);
9717c478bd9Sstevel@tonic-gate 
9727c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
9737c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
9747c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
9757c478bd9Sstevel@tonic-gate 
9767c478bd9Sstevel@tonic-gate 		return;
9777c478bd9Sstevel@tonic-gate 	}
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 	if (port_status & EHCI_RH_PORT_LOW_SPEED) {
9807c478bd9Sstevel@tonic-gate 		/* Check for classic or companion host controllers */
9817c478bd9Sstevel@tonic-gate 		if (rh->rh_companion_controllers) {
9827c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
9837c478bd9Sstevel@tonic-gate 			    "ehci_handle_port_reset: Low speed device "
9847c478bd9Sstevel@tonic-gate 			    "and handover this port to Companion controller");
9857c478bd9Sstevel@tonic-gate 
9867c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port],
9877c478bd9Sstevel@tonic-gate 			    port_status | EHCI_RH_PORT_OWNER_CLASSIC);
9887c478bd9Sstevel@tonic-gate 		} else {
9897c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L1(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
9907c478bd9Sstevel@tonic-gate 			    "Low speed device is not supported");
9917c478bd9Sstevel@tonic-gate 		}
9927c478bd9Sstevel@tonic-gate 	} else {
9937c478bd9Sstevel@tonic-gate 		Set_OpReg(ehci_rh_port_status[port],
9947c478bd9Sstevel@tonic-gate 		    ((port_status | EHCI_RH_PORT_RESET) &
9957c478bd9Sstevel@tonic-gate 		    ~EHCI_RH_PORT_ENABLE));
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 		/* Wait 10ms for reset to complete */
10007c478bd9Sstevel@tonic-gate 		delay(drv_usectohz(EHCI_PORT_RESET_TIMEWAIT));
10017c478bd9Sstevel@tonic-gate 
10027c478bd9Sstevel@tonic-gate 		mutex_enter(&ehcip->ehci_int_mutex);
10037c478bd9Sstevel@tonic-gate 
10047c478bd9Sstevel@tonic-gate 		port_status = Get_OpReg(ehci_rh_port_status[port]) &
10057c478bd9Sstevel@tonic-gate 		    ~EHCI_RH_PORT_CLEAR_MASK;
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate 		Set_OpReg(ehci_rh_port_status[port],
10087c478bd9Sstevel@tonic-gate 		    (port_status & ~EHCI_RH_PORT_RESET));
10097c478bd9Sstevel@tonic-gate 
10107c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
10117c478bd9Sstevel@tonic-gate 
10127c478bd9Sstevel@tonic-gate 		/*
10137c478bd9Sstevel@tonic-gate 		 * Wait 2ms for hardware to enable this port
10147c478bd9Sstevel@tonic-gate 		 * if connected usb device is high speed.
10157c478bd9Sstevel@tonic-gate 		 */
10167c478bd9Sstevel@tonic-gate 		delay(drv_usectohz(EHCI_PORT_RESET_COMP_TIMEWAIT));
10177c478bd9Sstevel@tonic-gate 
10187c478bd9Sstevel@tonic-gate 		mutex_enter(&ehcip->ehci_int_mutex);
10197c478bd9Sstevel@tonic-gate 
10207c478bd9Sstevel@tonic-gate 		port_status = Get_OpReg(ehci_rh_port_status[port]) &
10217c478bd9Sstevel@tonic-gate 		    ~EHCI_RH_PORT_CLEAR_MASK;
10227c478bd9Sstevel@tonic-gate 
10237c478bd9Sstevel@tonic-gate 		/*
10247c478bd9Sstevel@tonic-gate 		 * If port is not enabled, connected device is a
10257c478bd9Sstevel@tonic-gate 		 * Full-speed usb device.
10267c478bd9Sstevel@tonic-gate 		 */
10277c478bd9Sstevel@tonic-gate 		if (!(port_status & EHCI_RH_PORT_ENABLE)) {
10287c478bd9Sstevel@tonic-gate 			/* Check for classic or companion host controllers */
10297c478bd9Sstevel@tonic-gate 			if (rh->rh_companion_controllers) {
10307c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
10317c478bd9Sstevel@tonic-gate 				    ehcip->ehci_log_hdl,
10327c478bd9Sstevel@tonic-gate 				    "ehci_handle_port_reset: Full speed device "
10337c478bd9Sstevel@tonic-gate 				    "and handover this port to Companion host "
10347c478bd9Sstevel@tonic-gate 				    "controller");
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate 				Set_OpReg(ehci_rh_port_status[port],
10377c478bd9Sstevel@tonic-gate 				    port_status | EHCI_RH_PORT_OWNER_CLASSIC);
10387c478bd9Sstevel@tonic-gate 			} else {
10397c478bd9Sstevel@tonic-gate 				USB_DPRINTF_L1(PRINT_MASK_ROOT_HUB,
10407c478bd9Sstevel@tonic-gate 				    ehcip->ehci_log_hdl,
10417c478bd9Sstevel@tonic-gate 				    "Full speed device is not supported");
10427c478bd9Sstevel@tonic-gate 			}
10437c478bd9Sstevel@tonic-gate 		} else {
10447c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
10457c478bd9Sstevel@tonic-gate 			    "ehci_handle_port_reset: High speed device ");
10467c478bd9Sstevel@tonic-gate 
10477c478bd9Sstevel@tonic-gate 			port_status = Get_OpReg(ehci_rh_port_status[port]) &
10487c478bd9Sstevel@tonic-gate 			    ~EHCI_RH_PORT_CLEAR_MASK;
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate 			/*
10517c478bd9Sstevel@tonic-gate 			 * Enable over-current, connect, and disconnect
10527c478bd9Sstevel@tonic-gate 			 * wakeup bits.
10537c478bd9Sstevel@tonic-gate 			 */
10547c478bd9Sstevel@tonic-gate 			Set_OpReg(ehci_rh_port_status[port], (port_status |
10557c478bd9Sstevel@tonic-gate 			    EHCI_RH_PORT_OVER_CURENT_ENABLE |
10567c478bd9Sstevel@tonic-gate 			    EHCI_RH_PORT_DISCONNECT_ENABLE |
10577c478bd9Sstevel@tonic-gate 			    EHCI_RH_PORT_CONNECT_ENABLE));
10587c478bd9Sstevel@tonic-gate 
10597c478bd9Sstevel@tonic-gate 			/*
10607c478bd9Sstevel@tonic-gate 			 * The next function is only called if the interrupt
10617c478bd9Sstevel@tonic-gate 			 * pipe is polling and the USBA is ready to receive
10627c478bd9Sstevel@tonic-gate 			 * the data.
10637c478bd9Sstevel@tonic-gate 			 */
10647c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.
10657c478bd9Sstevel@tonic-gate 			    rh_intr_pending_status |= (1 << port);
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate 			if (ehcip->ehci_root_hub.
10687c478bd9Sstevel@tonic-gate 			    rh_intr_pipe_state == EHCI_PIPE_STATE_ACTIVE) {
10697c478bd9Sstevel@tonic-gate 
10707c478bd9Sstevel@tonic-gate 				ehci_root_hub_reset_occured(ehcip);
10717c478bd9Sstevel@tonic-gate 			}
10727c478bd9Sstevel@tonic-gate 		}
10737c478bd9Sstevel@tonic-gate 	}
10747c478bd9Sstevel@tonic-gate 
10757c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
10767c478bd9Sstevel@tonic-gate }
10777c478bd9Sstevel@tonic-gate 
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate /*
10807c478bd9Sstevel@tonic-gate  * ehci_root_hub_reset_occured:
10817c478bd9Sstevel@tonic-gate  *
10827c478bd9Sstevel@tonic-gate  * Inform the upper layer that reset has occured on the port. This is
10837c478bd9Sstevel@tonic-gate  * required because the upper layer is expecting a an evernt immidiately
10847c478bd9Sstevel@tonic-gate  * after doing reset. In case of OHCI, the controller gets an interrupt
10857c478bd9Sstevel@tonic-gate  * for the change in the root hub status but in case of EHCI, we dont.
10867c478bd9Sstevel@tonic-gate  * So, send a event to the upper layer as soon as we complete the reset.
10877c478bd9Sstevel@tonic-gate  */
10887c478bd9Sstevel@tonic-gate void
10897c478bd9Sstevel@tonic-gate ehci_root_hub_reset_occured(
10907c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip)
10917c478bd9Sstevel@tonic-gate {
10927c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp =
10937c478bd9Sstevel@tonic-gate 				    ehcip->ehci_root_hub.rh_curr_intr_reqp;
10947c478bd9Sstevel@tonic-gate 	usb_port_mask_t		port_mask;
10957c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
10987c478bd9Sstevel@tonic-gate 	    "ehci_root_hub_reset_occured: curr_intr_reqp = 0x%p data = 0x%p",
10997c478bd9Sstevel@tonic-gate 	    curr_intr_reqp, curr_intr_reqp->intr_data);
11007c478bd9Sstevel@tonic-gate 
11017c478bd9Sstevel@tonic-gate 	/* Get the interrupt pipe handle */
11027c478bd9Sstevel@tonic-gate 	ph = ehcip->ehci_root_hub.rh_intr_pipe_handle;
11037c478bd9Sstevel@tonic-gate 
11047c478bd9Sstevel@tonic-gate 	/* Get the pending status */
11057c478bd9Sstevel@tonic-gate 	port_mask = ehcip->ehci_root_hub.rh_intr_pending_status << 1;
11067c478bd9Sstevel@tonic-gate 
11077c478bd9Sstevel@tonic-gate 	do {
11087c478bd9Sstevel@tonic-gate 		*curr_intr_reqp->intr_data->b_wptr++ = (uchar_t)port_mask;
11097c478bd9Sstevel@tonic-gate 		port_mask >>= 8;
11107c478bd9Sstevel@tonic-gate 	} while (port_mask != 0);
11117c478bd9Sstevel@tonic-gate 
11127c478bd9Sstevel@tonic-gate 	ehci_root_hub_hcdi_callback(ph, USB_CR_OK);
11137c478bd9Sstevel@tonic-gate 
11147c478bd9Sstevel@tonic-gate 	/* Reset pending status */
11157c478bd9Sstevel@tonic-gate 	ehcip->ehci_root_hub.rh_intr_pending_status = 0;
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 	/* If needed, allocate new interrupt request */
11187c478bd9Sstevel@tonic-gate 	if ((ehci_root_hub_allocate_intr_pipe_resource(
11197c478bd9Sstevel@tonic-gate 	    ehcip, 0)) != USB_SUCCESS) {
11207c478bd9Sstevel@tonic-gate 
11217c478bd9Sstevel@tonic-gate 		/* Do interrupt pipe cleanup */
11227c478bd9Sstevel@tonic-gate 		ehci_root_hub_intr_pipe_cleanup(ehcip, USB_CR_NO_RESOURCES);
11237c478bd9Sstevel@tonic-gate 	}
11247c478bd9Sstevel@tonic-gate }
11257c478bd9Sstevel@tonic-gate 
11267c478bd9Sstevel@tonic-gate 
11277c478bd9Sstevel@tonic-gate /*
11287c478bd9Sstevel@tonic-gate  * ehci_handle_complete_port_reset:
11297c478bd9Sstevel@tonic-gate  *
11307c478bd9Sstevel@tonic-gate  * Perform a port reset change.
11317c478bd9Sstevel@tonic-gate  */
11327c478bd9Sstevel@tonic-gate static void
11337c478bd9Sstevel@tonic-gate ehci_handle_complete_port_reset(
11347c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
11357c478bd9Sstevel@tonic-gate 	uint16_t		port)
11367c478bd9Sstevel@tonic-gate {
11377c478bd9Sstevel@tonic-gate 	uint_t			port_status;
11387c478bd9Sstevel@tonic-gate 
11397c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
11407c478bd9Sstevel@tonic-gate 
11417c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
11427c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
11437c478bd9Sstevel@tonic-gate 
11447c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
11457c478bd9Sstevel@tonic-gate 	    "ehci_handle_complete_port_reset: port = 0x%x status = 0x%x",
11467c478bd9Sstevel@tonic-gate 	    port, port_status);
11477c478bd9Sstevel@tonic-gate 
11487c478bd9Sstevel@tonic-gate 	/* Check port is owned by ehci */
11497c478bd9Sstevel@tonic-gate 	if (ehci_is_port_owner(ehcip, port) != USB_SUCCESS) {
11507c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate 		return;
11537c478bd9Sstevel@tonic-gate 	}
11547c478bd9Sstevel@tonic-gate 
11557c478bd9Sstevel@tonic-gate 	if (port_status & EHCI_RH_PORT_RESET) {
11567c478bd9Sstevel@tonic-gate 		Set_OpReg(ehci_rh_port_status[port],
11577c478bd9Sstevel@tonic-gate 		    port_status & ~EHCI_RH_PORT_RESET);
11587c478bd9Sstevel@tonic-gate 
11597c478bd9Sstevel@tonic-gate 	}
11607c478bd9Sstevel@tonic-gate 
11617c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
11627c478bd9Sstevel@tonic-gate }
11637c478bd9Sstevel@tonic-gate 
11647c478bd9Sstevel@tonic-gate 
11657c478bd9Sstevel@tonic-gate /*
11667c478bd9Sstevel@tonic-gate  * ehci_handle_clear_port_connection:
11677c478bd9Sstevel@tonic-gate  *
11687c478bd9Sstevel@tonic-gate  * Perform a clear port connection.
11697c478bd9Sstevel@tonic-gate  */
11707c478bd9Sstevel@tonic-gate static void
11717c478bd9Sstevel@tonic-gate ehci_handle_clear_port_connection(
11727c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
11737c478bd9Sstevel@tonic-gate 	uint16_t		port)
11747c478bd9Sstevel@tonic-gate {
11757c478bd9Sstevel@tonic-gate 	uint_t			port_status;
11767c478bd9Sstevel@tonic-gate 
11777c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
11787c478bd9Sstevel@tonic-gate 
11797c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
11807c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
11817c478bd9Sstevel@tonic-gate 
11827c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
11837c478bd9Sstevel@tonic-gate 	    "ehci_handle_clear_port_connection: port = 0x%x"
11847c478bd9Sstevel@tonic-gate 	    "status = 0x%x", port, port_status);
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 	Set_OpReg(ehci_rh_port_status[port],
11877c478bd9Sstevel@tonic-gate 	    port_status | EHCI_RH_PORT_CONNECT_STS_CHANGE);
11887c478bd9Sstevel@tonic-gate 
11897c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
11907c478bd9Sstevel@tonic-gate }
11917c478bd9Sstevel@tonic-gate 
11927c478bd9Sstevel@tonic-gate 
11937c478bd9Sstevel@tonic-gate /*
11947c478bd9Sstevel@tonic-gate  * ehci_handle_clrchng_port_over_current:
11957c478bd9Sstevel@tonic-gate  *
11967c478bd9Sstevel@tonic-gate  * Perform a clear port connection.
11977c478bd9Sstevel@tonic-gate  */
11987c478bd9Sstevel@tonic-gate static void
11997c478bd9Sstevel@tonic-gate ehci_handle_clrchng_port_over_current(
12007c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
12017c478bd9Sstevel@tonic-gate 	uint16_t		port)
12027c478bd9Sstevel@tonic-gate {
12037c478bd9Sstevel@tonic-gate 	uint_t			port_status;
12047c478bd9Sstevel@tonic-gate 
12057c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
12067c478bd9Sstevel@tonic-gate 
12077c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
12087c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
12097c478bd9Sstevel@tonic-gate 
12107c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
12117c478bd9Sstevel@tonic-gate 	    "ehci_handle_clrchng_port_over_current: port = 0x%x"
12127c478bd9Sstevel@tonic-gate 	    "status = 0x%x", port, port_status);
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate 	Set_OpReg(ehci_rh_port_status[port],
12157c478bd9Sstevel@tonic-gate 	    port_status | EHCI_RH_PORT_OVER_CURR_CHANGE);
12167c478bd9Sstevel@tonic-gate 
12177c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
12187c478bd9Sstevel@tonic-gate }
12197c478bd9Sstevel@tonic-gate 
12207c478bd9Sstevel@tonic-gate 
12217c478bd9Sstevel@tonic-gate /*
12227c478bd9Sstevel@tonic-gate  * ehci_handle_get_port_status:
12237c478bd9Sstevel@tonic-gate  *
12247c478bd9Sstevel@tonic-gate  * Handle a get port status request.
12257c478bd9Sstevel@tonic-gate  */
12267c478bd9Sstevel@tonic-gate static void
12277c478bd9Sstevel@tonic-gate ehci_handle_get_port_status(
12287c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
12297c478bd9Sstevel@tonic-gate 	uint16_t		port)
12307c478bd9Sstevel@tonic-gate {
12317c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp;
12327c478bd9Sstevel@tonic-gate 	mblk_t			*message;
12337c478bd9Sstevel@tonic-gate 	uint_t			new_port_status = 0;
12347c478bd9Sstevel@tonic-gate 	uint_t			change_status = 0;
12357c478bd9Sstevel@tonic-gate 	uint_t			port_status;
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
12387c478bd9Sstevel@tonic-gate 
12397c478bd9Sstevel@tonic-gate 	ctrl_reqp = ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
12407c478bd9Sstevel@tonic-gate 
12417c478bd9Sstevel@tonic-gate 	/* Get the root hub port status information */
12427c478bd9Sstevel@tonic-gate 	port_status = ehci_get_root_hub_port_status(ehcip, port);
12437c478bd9Sstevel@tonic-gate 
12447c478bd9Sstevel@tonic-gate 	new_port_status = port_status & PORT_STATUS_MASK;
12457c478bd9Sstevel@tonic-gate 	change_status = (port_status >> 16) & PORT_CHANGE_MASK;
12467c478bd9Sstevel@tonic-gate 
12477c478bd9Sstevel@tonic-gate 	ehcip->ehci_root_hub.rh_port_status[port] = new_port_status;
12487c478bd9Sstevel@tonic-gate 
12497c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
12507c478bd9Sstevel@tonic-gate 	    "ehci_handle_get_port_status: port = %d new status = 0x%x"
12517c478bd9Sstevel@tonic-gate 	    "change = 0x%x", port, new_port_status, change_status);
12527c478bd9Sstevel@tonic-gate 
12537c478bd9Sstevel@tonic-gate 	message = ctrl_reqp->ctrl_data;
12547c478bd9Sstevel@tonic-gate 
12557c478bd9Sstevel@tonic-gate 	ASSERT(message != NULL);
12567c478bd9Sstevel@tonic-gate 
12577c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)new_port_status;
12587c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)(new_port_status >> 8);
12597c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)change_status;
12607c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)(change_status >> 8);
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate 	/* Save the data in control request */
12637c478bd9Sstevel@tonic-gate 	ctrl_reqp->ctrl_data = message;
12647c478bd9Sstevel@tonic-gate 
12657c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
12667c478bd9Sstevel@tonic-gate }
12677c478bd9Sstevel@tonic-gate 
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate /*
12707c478bd9Sstevel@tonic-gate  * ehci_handle_get_hub_descriptor:
12717c478bd9Sstevel@tonic-gate  */
12727c478bd9Sstevel@tonic-gate static void
12737c478bd9Sstevel@tonic-gate ehci_handle_get_hub_descriptor(
12747c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip)
12757c478bd9Sstevel@tonic-gate {
12767c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp;
12777c478bd9Sstevel@tonic-gate 	mblk_t			*message;
12787c478bd9Sstevel@tonic-gate 	usb_hub_descr_t		*root_hub_descr;
12797c478bd9Sstevel@tonic-gate 	size_t			length;
12807c478bd9Sstevel@tonic-gate 	uchar_t			raw_descr[ROOT_HUB_DESCRIPTOR_LENGTH];
12817c478bd9Sstevel@tonic-gate 
12827c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
12837c478bd9Sstevel@tonic-gate 
12847c478bd9Sstevel@tonic-gate 	ctrl_reqp = ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
12857c478bd9Sstevel@tonic-gate 	root_hub_descr = &ehcip->ehci_root_hub.rh_descr;
12867c478bd9Sstevel@tonic-gate 	length = ctrl_reqp->ctrl_wLength;
12877c478bd9Sstevel@tonic-gate 
12887c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
12897c478bd9Sstevel@tonic-gate 	    "ehci_handle_get_hub_descriptor: Ctrl Req  = 0x%p", ctrl_reqp);
12907c478bd9Sstevel@tonic-gate 
12917c478bd9Sstevel@tonic-gate 	message = ctrl_reqp->ctrl_data;
12927c478bd9Sstevel@tonic-gate 
12937c478bd9Sstevel@tonic-gate 	ASSERT(message != NULL);
12947c478bd9Sstevel@tonic-gate 
12957c478bd9Sstevel@tonic-gate 	bzero(&raw_descr, ROOT_HUB_DESCRIPTOR_LENGTH);
12967c478bd9Sstevel@tonic-gate 
12977c478bd9Sstevel@tonic-gate 	raw_descr[0] = root_hub_descr->bDescLength;
12987c478bd9Sstevel@tonic-gate 	raw_descr[1] = root_hub_descr->bDescriptorType;
12997c478bd9Sstevel@tonic-gate 	raw_descr[2] = root_hub_descr->bNbrPorts;
13007c478bd9Sstevel@tonic-gate 	raw_descr[3] = root_hub_descr->wHubCharacteristics & 0x00FF;
13017c478bd9Sstevel@tonic-gate 	raw_descr[4] = (root_hub_descr->wHubCharacteristics & 0xFF00) >> 8;
13027c478bd9Sstevel@tonic-gate 	raw_descr[5] = root_hub_descr->bPwrOn2PwrGood;
13037c478bd9Sstevel@tonic-gate 	raw_descr[6] = root_hub_descr->bHubContrCurrent;
13047c478bd9Sstevel@tonic-gate 	raw_descr[7] = root_hub_descr->DeviceRemovable;
13057c478bd9Sstevel@tonic-gate 	raw_descr[8] = root_hub_descr->PortPwrCtrlMask;
13067c478bd9Sstevel@tonic-gate 
13077c478bd9Sstevel@tonic-gate 	bcopy(raw_descr, message->b_wptr, length);
13087c478bd9Sstevel@tonic-gate 	message->b_wptr += length;
13097c478bd9Sstevel@tonic-gate 
13107c478bd9Sstevel@tonic-gate 	/* Save the data in control request */
13117c478bd9Sstevel@tonic-gate 	ctrl_reqp->ctrl_data = message;
13127c478bd9Sstevel@tonic-gate 
13137c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
13147c478bd9Sstevel@tonic-gate }
13157c478bd9Sstevel@tonic-gate 
13167c478bd9Sstevel@tonic-gate 
13177c478bd9Sstevel@tonic-gate /*
13187c478bd9Sstevel@tonic-gate  * ehci_handle_get_hub_status:
13197c478bd9Sstevel@tonic-gate  *
13207c478bd9Sstevel@tonic-gate  * Handle a get hub status request.
13217c478bd9Sstevel@tonic-gate  */
13227c478bd9Sstevel@tonic-gate static void
13237c478bd9Sstevel@tonic-gate ehci_handle_get_hub_status(
13247c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip)
13257c478bd9Sstevel@tonic-gate {
13267c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*ctrl_reqp;
13277c478bd9Sstevel@tonic-gate 	mblk_t			*message;
13287c478bd9Sstevel@tonic-gate 	uint_t			new_root_hub_status;
13297c478bd9Sstevel@tonic-gate 
13307c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
13317c478bd9Sstevel@tonic-gate 
13327c478bd9Sstevel@tonic-gate 	ctrl_reqp = ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
13337c478bd9Sstevel@tonic-gate 
13347c478bd9Sstevel@tonic-gate 	/*
13357c478bd9Sstevel@tonic-gate 	 * For EHCI, there is no overall hub status information.
13367c478bd9Sstevel@tonic-gate 	 * Only individual root hub port status information is
13377c478bd9Sstevel@tonic-gate 	 * available. So return zero for the root hub status
13387c478bd9Sstevel@tonic-gate 	 * request.
13397c478bd9Sstevel@tonic-gate 	 */
13407c478bd9Sstevel@tonic-gate 	new_root_hub_status = 0;
13417c478bd9Sstevel@tonic-gate 
13427c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
13437c478bd9Sstevel@tonic-gate 	    "ehci_handle_get_hub_status: new root hub status = 0x%x",
13447c478bd9Sstevel@tonic-gate 	    new_root_hub_status);
13457c478bd9Sstevel@tonic-gate 
13467c478bd9Sstevel@tonic-gate 	message = ctrl_reqp->ctrl_data;
13477c478bd9Sstevel@tonic-gate 
13487c478bd9Sstevel@tonic-gate 	ASSERT(message != NULL);
13497c478bd9Sstevel@tonic-gate 
13507c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)new_root_hub_status;
13517c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)(new_root_hub_status >> 8);
13527c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)(new_root_hub_status >> 16);
13537c478bd9Sstevel@tonic-gate 	*message->b_wptr++ = (uchar_t)(new_root_hub_status >> 24);
13547c478bd9Sstevel@tonic-gate 
13557c478bd9Sstevel@tonic-gate 	/* Save the data in control request */
13567c478bd9Sstevel@tonic-gate 	ctrl_reqp->ctrl_data = message;
13577c478bd9Sstevel@tonic-gate 
13587c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
13597c478bd9Sstevel@tonic-gate }
13607c478bd9Sstevel@tonic-gate 
13617c478bd9Sstevel@tonic-gate 
13627c478bd9Sstevel@tonic-gate /*
13637c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_pipe_start_intr_polling:
13647c478bd9Sstevel@tonic-gate  *
13657c478bd9Sstevel@tonic-gate  * Handle start polling on root hub interrupt pipe.
13667c478bd9Sstevel@tonic-gate  */
13677c478bd9Sstevel@tonic-gate /* ARGSUSED */
13687c478bd9Sstevel@tonic-gate int
13697c478bd9Sstevel@tonic-gate ehci_handle_root_hub_pipe_start_intr_polling(
13707c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
13717c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*client_intr_reqp,
13727c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
13737c478bd9Sstevel@tonic-gate {
13747c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
13757c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
13767c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
13777c478bd9Sstevel@tonic-gate 	int			error = USB_SUCCESS;
13787c478bd9Sstevel@tonic-gate 	uint_t			pipe_state;
13797c478bd9Sstevel@tonic-gate 
13807c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
13817c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_pipe_start_intr_polling: "
13827c478bd9Sstevel@tonic-gate 	    "Root hub pipe start polling");
13837c478bd9Sstevel@tonic-gate 
13847c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
13857c478bd9Sstevel@tonic-gate 
13867c478bd9Sstevel@tonic-gate 	ASSERT((eptd->bEndpointAddress & USB_EP_NUM_MASK) == 1);
13877c478bd9Sstevel@tonic-gate 
13887c478bd9Sstevel@tonic-gate 	ASSERT((client_intr_reqp->intr_attributes & USB_ATTRS_ONE_XFER) == 0);
13897c478bd9Sstevel@tonic-gate 
13907c478bd9Sstevel@tonic-gate 	pipe_state = ehcip->ehci_root_hub.rh_intr_pipe_state;
13917c478bd9Sstevel@tonic-gate 
13927c478bd9Sstevel@tonic-gate 	switch (pipe_state) {
13937c478bd9Sstevel@tonic-gate 	case EHCI_PIPE_STATE_IDLE:
13947c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.rh_intr_pipe_timer_id == 0);
13957c478bd9Sstevel@tonic-gate 
13967c478bd9Sstevel@tonic-gate 		/*
13977c478bd9Sstevel@tonic-gate 		 * Save the Original Client's Interrupt IN request
13987c478bd9Sstevel@tonic-gate 		 * information. We use this for final callback
13997c478bd9Sstevel@tonic-gate 		 */
14007c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.rh_client_intr_reqp == NULL);
14017c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_client_intr_reqp = client_intr_reqp;
14027c478bd9Sstevel@tonic-gate 
14037c478bd9Sstevel@tonic-gate 		error = ehci_root_hub_allocate_intr_pipe_resource(ehcip, flags);
14047c478bd9Sstevel@tonic-gate 
14057c478bd9Sstevel@tonic-gate 		if (error != USB_SUCCESS) {
14067c478bd9Sstevel@tonic-gate 			/* Reset client interrupt request pointer */
14077c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.rh_client_intr_reqp = NULL;
14087c478bd9Sstevel@tonic-gate 
14097c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
14107c478bd9Sstevel@tonic-gate 			    "ehci_handle_root_hub_pipe_start_intr_polling: "
14117c478bd9Sstevel@tonic-gate 			    "No Resources");
14127c478bd9Sstevel@tonic-gate 
14137c478bd9Sstevel@tonic-gate 			return (error);
14147c478bd9Sstevel@tonic-gate 		}
14157c478bd9Sstevel@tonic-gate 
14167c478bd9Sstevel@tonic-gate 		/* Check whether we need to send the reset data up */
14177c478bd9Sstevel@tonic-gate 		if (ehcip->ehci_root_hub.rh_intr_pending_status) {
14187c478bd9Sstevel@tonic-gate 			ehci_root_hub_reset_occured(ehcip);
14197c478bd9Sstevel@tonic-gate 		}
14207c478bd9Sstevel@tonic-gate 
14217c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
14227c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_start_intr_polling: "
14237c478bd9Sstevel@tonic-gate 		    "Start polling for root hub successful");
14247c478bd9Sstevel@tonic-gate 
14257c478bd9Sstevel@tonic-gate 		break;
14267c478bd9Sstevel@tonic-gate 	case EHCI_PIPE_STATE_ACTIVE:
14277c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
14287c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_start_intr_polling: "
14297c478bd9Sstevel@tonic-gate 		    "Polling for root hub is already in progress");
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate 		break;
14327c478bd9Sstevel@tonic-gate 	default:
14337c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
14347c478bd9Sstevel@tonic-gate 		    "ehci_handle_root_hub_pipe_start_intr_polling: "
14357c478bd9Sstevel@tonic-gate 		    "Pipe is in error state 0x%x", pipe_state);
14367c478bd9Sstevel@tonic-gate 
14377c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
14387c478bd9Sstevel@tonic-gate 
14397c478bd9Sstevel@tonic-gate 		break;
14407c478bd9Sstevel@tonic-gate 	}
14417c478bd9Sstevel@tonic-gate 
14427c478bd9Sstevel@tonic-gate 	return (error);
14437c478bd9Sstevel@tonic-gate }
14447c478bd9Sstevel@tonic-gate 
14457c478bd9Sstevel@tonic-gate 
14467c478bd9Sstevel@tonic-gate /*
14477c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_pipe_stop_intr_polling:
14487c478bd9Sstevel@tonic-gate  *
14497c478bd9Sstevel@tonic-gate  * Handle stop polling on root hub intr pipe.
14507c478bd9Sstevel@tonic-gate  */
14517c478bd9Sstevel@tonic-gate /* ARGSUSED */
14527c478bd9Sstevel@tonic-gate void
14537c478bd9Sstevel@tonic-gate ehci_handle_root_hub_pipe_stop_intr_polling(
14547c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
14557c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
14567c478bd9Sstevel@tonic-gate {
14577c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
14587c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
14597c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd = &ph->p_ep;
14607c478bd9Sstevel@tonic-gate 
14617c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
14627c478bd9Sstevel@tonic-gate 
14637c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
14647c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_pipe_stop_intr_polling: "
14657c478bd9Sstevel@tonic-gate 	    "Root hub pipe stop polling");
14667c478bd9Sstevel@tonic-gate 
14677c478bd9Sstevel@tonic-gate 	ASSERT((eptd->bEndpointAddress & USB_EP_NUM_MASK) == 1);
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate 	if (ehcip->ehci_root_hub.rh_intr_pipe_state ==
14707c478bd9Sstevel@tonic-gate 	    EHCI_PIPE_STATE_ACTIVE) {
14717c478bd9Sstevel@tonic-gate 
14727c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_state =
14737c478bd9Sstevel@tonic-gate 		    EHCI_PIPE_STATE_STOP_POLLING;
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate 		/* Do interrupt pipe cleanup */
14767c478bd9Sstevel@tonic-gate 		ehci_root_hub_intr_pipe_cleanup(ehcip, USB_CR_STOPPED_POLLING);
14777c478bd9Sstevel@tonic-gate 
14787c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.
14797c478bd9Sstevel@tonic-gate 		    rh_intr_pipe_state == EHCI_PIPE_STATE_IDLE);
14807c478bd9Sstevel@tonic-gate 
14817c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
14827c478bd9Sstevel@tonic-gate 		    "ehci_hcdi_pipe_stop_intr_polling: Stop polling for root"
14837c478bd9Sstevel@tonic-gate 		    "hub successful");
14847c478bd9Sstevel@tonic-gate 	} else {
14857c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB,
14867c478bd9Sstevel@tonic-gate 		    ehcip->ehci_log_hdl, "ehci_hcdi_pipe_stop_intr_polling: "
14877c478bd9Sstevel@tonic-gate 		    "Polling for root hub is already stopped");
14887c478bd9Sstevel@tonic-gate 	}
14897c478bd9Sstevel@tonic-gate }
14907c478bd9Sstevel@tonic-gate 
14917c478bd9Sstevel@tonic-gate 
14927c478bd9Sstevel@tonic-gate /*
14937c478bd9Sstevel@tonic-gate  * ehci_get_root_hub_port_status:
14947c478bd9Sstevel@tonic-gate  *
14957c478bd9Sstevel@tonic-gate  * Construct root hub port status and change information
14967c478bd9Sstevel@tonic-gate  */
14977c478bd9Sstevel@tonic-gate static uint_t
14987c478bd9Sstevel@tonic-gate ehci_get_root_hub_port_status(
14997c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
15007c478bd9Sstevel@tonic-gate 	uint16_t		port)
15017c478bd9Sstevel@tonic-gate {
15027c478bd9Sstevel@tonic-gate 	uint_t			new_port_status = 0;
15037c478bd9Sstevel@tonic-gate 	uint_t			change_status = 0;
15047c478bd9Sstevel@tonic-gate 	uint_t			port_status;
15057c478bd9Sstevel@tonic-gate 
15067c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
15077c478bd9Sstevel@tonic-gate 
15087c478bd9Sstevel@tonic-gate 	/* Read the current port status */
15097c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]);
15107c478bd9Sstevel@tonic-gate 
15117c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
15127c478bd9Sstevel@tonic-gate 	    "ehci_get_root_hub_port_status: port %d "
15137c478bd9Sstevel@tonic-gate 	    "port status = 0x%x", port, port_status);
15147c478bd9Sstevel@tonic-gate 
15157c478bd9Sstevel@tonic-gate 	/*
15167c478bd9Sstevel@tonic-gate 	 * EHCI root hub port status and control register information
15177c478bd9Sstevel@tonic-gate 	 * format is different what Hub driver wants. So EHCI driver
15187c478bd9Sstevel@tonic-gate 	 * needs to contruct the proper root hub port status information.
15197c478bd9Sstevel@tonic-gate 	 *
15207c478bd9Sstevel@tonic-gate 	 * Send all port status information only if port is owned by EHCI
15217c478bd9Sstevel@tonic-gate 	 * host controller.
15227c478bd9Sstevel@tonic-gate 	 */
15237c478bd9Sstevel@tonic-gate 	if ((port_status & EHCI_RH_PORT_OWNER) == EHCI_RH_PORT_OWNER_EHCI) {
15247c478bd9Sstevel@tonic-gate 
15257c478bd9Sstevel@tonic-gate 		/* First construct port change information */
15267c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_ENABLE_CHANGE) {
15277c478bd9Sstevel@tonic-gate 			change_status |= PORT_CHANGE_PESC;
15287c478bd9Sstevel@tonic-gate 		}
15297c478bd9Sstevel@tonic-gate 
15307c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_RESUME) {
15317c478bd9Sstevel@tonic-gate 			change_status |= PORT_CHANGE_PSSC;
15327c478bd9Sstevel@tonic-gate 		}
15337c478bd9Sstevel@tonic-gate 
15347c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_OVER_CURR_CHANGE) {
15357c478bd9Sstevel@tonic-gate 			change_status |= PORT_CHANGE_OCIC;
15367c478bd9Sstevel@tonic-gate 		}
15377c478bd9Sstevel@tonic-gate 
15387c478bd9Sstevel@tonic-gate 		/* Now construct port status information */
15397c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_CONNECT_STATUS) {
15407c478bd9Sstevel@tonic-gate 			new_port_status |= PORT_STATUS_CCS;
15417c478bd9Sstevel@tonic-gate 		}
15427c478bd9Sstevel@tonic-gate 
15437c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_ENABLE) {
15447c478bd9Sstevel@tonic-gate 			new_port_status |=
15457c478bd9Sstevel@tonic-gate 			    (PORT_STATUS_PES | PORT_STATUS_HSDA);
15467c478bd9Sstevel@tonic-gate 		}
15477c478bd9Sstevel@tonic-gate 
15487c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_SUSPEND) {
15497c478bd9Sstevel@tonic-gate 			new_port_status |= PORT_STATUS_PSS;
15507c478bd9Sstevel@tonic-gate 		}
15517c478bd9Sstevel@tonic-gate 
15527c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_OVER_CURR_ACTIVE) {
15537c478bd9Sstevel@tonic-gate 			new_port_status |= PORT_STATUS_POCI;
15547c478bd9Sstevel@tonic-gate 		}
15557c478bd9Sstevel@tonic-gate 
15567c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_RESET) {
15577c478bd9Sstevel@tonic-gate 			new_port_status |= PORT_STATUS_PRS;
15587c478bd9Sstevel@tonic-gate 		}
15597c478bd9Sstevel@tonic-gate 
15607c478bd9Sstevel@tonic-gate 		if (port_status & EHCI_RH_PORT_INDICATOR) {
15617c478bd9Sstevel@tonic-gate 			new_port_status |= PORT_STATUS_PIC;
15627c478bd9Sstevel@tonic-gate 		}
15637c478bd9Sstevel@tonic-gate 	}
15647c478bd9Sstevel@tonic-gate 
15657c478bd9Sstevel@tonic-gate 	/*
15667c478bd9Sstevel@tonic-gate 	 * Send the following port status and change information
15677c478bd9Sstevel@tonic-gate 	 * even if port is not owned by EHCI.
15687c478bd9Sstevel@tonic-gate 	 *
15697c478bd9Sstevel@tonic-gate 	 * Additional port change information.
15707c478bd9Sstevel@tonic-gate 	 */
15717c478bd9Sstevel@tonic-gate 	if (port_status & EHCI_RH_PORT_CONNECT_STS_CHANGE) {
15727c478bd9Sstevel@tonic-gate 		change_status |= PORT_CHANGE_CSC;
15737c478bd9Sstevel@tonic-gate 	}
15747c478bd9Sstevel@tonic-gate 
15757c478bd9Sstevel@tonic-gate 	/* Additional port status information */
15767c478bd9Sstevel@tonic-gate 	if (port_status & EHCI_RH_PORT_POWER) {
15777c478bd9Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_PPS;
15787c478bd9Sstevel@tonic-gate 	}
15797c478bd9Sstevel@tonic-gate 
15807c478bd9Sstevel@tonic-gate 	if ((!(port_status & EHCI_RH_PORT_ENABLE)) &&
15817c478bd9Sstevel@tonic-gate 	    (port_status & EHCI_RH_PORT_LOW_SPEED)) {
15827c478bd9Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_LSDA;
15837c478bd9Sstevel@tonic-gate 	}
15847c478bd9Sstevel@tonic-gate 
15857c478bd9Sstevel@tonic-gate 	/*
15867c478bd9Sstevel@tonic-gate 	 * Construct complete root hub port status and change information.
15877c478bd9Sstevel@tonic-gate 	 */
15887c478bd9Sstevel@tonic-gate 	port_status = ((change_status << 16) | new_port_status);
15897c478bd9Sstevel@tonic-gate 
15907c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
15917c478bd9Sstevel@tonic-gate 	    "ehci_get_root_hub_port_status: port = %d new status = 0x%x "
15927c478bd9Sstevel@tonic-gate 	    "change status = 0x%x complete port status 0x%x", port,
15937c478bd9Sstevel@tonic-gate 	    new_port_status, change_status, port_status);
15947c478bd9Sstevel@tonic-gate 
15957c478bd9Sstevel@tonic-gate 	return (port_status);
15967c478bd9Sstevel@tonic-gate }
15977c478bd9Sstevel@tonic-gate 
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate /*
16007c478bd9Sstevel@tonic-gate  * ehci_is_port_owner:
16017c478bd9Sstevel@tonic-gate  *
16027c478bd9Sstevel@tonic-gate  * Check whether given port is owned by ehci.
16037c478bd9Sstevel@tonic-gate  */
16047c478bd9Sstevel@tonic-gate static int
16057c478bd9Sstevel@tonic-gate ehci_is_port_owner(
16067c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
16077c478bd9Sstevel@tonic-gate 	uint16_t		port)
16087c478bd9Sstevel@tonic-gate {
16097c478bd9Sstevel@tonic-gate 	uint_t			port_status;
16107c478bd9Sstevel@tonic-gate 
16117c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
16127c478bd9Sstevel@tonic-gate 
16137c478bd9Sstevel@tonic-gate 	port_status = Get_OpReg(ehci_rh_port_status[port]) &
16147c478bd9Sstevel@tonic-gate 	    ~EHCI_RH_PORT_CLEAR_MASK;
16157c478bd9Sstevel@tonic-gate 
16167c478bd9Sstevel@tonic-gate 	/*
16177c478bd9Sstevel@tonic-gate 	 * Don't perform anything if port is owned by classis host
16187c478bd9Sstevel@tonic-gate 	 * controller and return success.
16197c478bd9Sstevel@tonic-gate 	 */
16207c478bd9Sstevel@tonic-gate 	if ((port_status & EHCI_RH_PORT_OWNER) == EHCI_RH_PORT_OWNER_CLASSIC) {
16217c478bd9Sstevel@tonic-gate 
16227c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
16237c478bd9Sstevel@tonic-gate 		    "ehci_handle_set_clear_port_feature: "
16247c478bd9Sstevel@tonic-gate 		    "Port %d is owned by classic host controller", port);
16257c478bd9Sstevel@tonic-gate 
16267c478bd9Sstevel@tonic-gate 		return (USB_FAILURE);
16277c478bd9Sstevel@tonic-gate 	}
16287c478bd9Sstevel@tonic-gate 
16297c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
16307c478bd9Sstevel@tonic-gate }
16317c478bd9Sstevel@tonic-gate 
16327c478bd9Sstevel@tonic-gate 
16337c478bd9Sstevel@tonic-gate /*
16347c478bd9Sstevel@tonic-gate  * ehci_root_hub_allocate_intr_pipe_resource:
16357c478bd9Sstevel@tonic-gate  *
16367c478bd9Sstevel@tonic-gate  * Allocate interrupt requests and initialize them.
16377c478bd9Sstevel@tonic-gate  */
16387c478bd9Sstevel@tonic-gate static int
16397c478bd9Sstevel@tonic-gate ehci_root_hub_allocate_intr_pipe_resource(
16407c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
16417c478bd9Sstevel@tonic-gate 	usb_flags_t		flags)
16427c478bd9Sstevel@tonic-gate {
16437c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
16447c478bd9Sstevel@tonic-gate 	size_t			length;
16457c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp;
16467c478bd9Sstevel@tonic-gate 
16477c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
16487c478bd9Sstevel@tonic-gate 	    "ehci_root_hub_allocate_intr_pipe_resource");
16497c478bd9Sstevel@tonic-gate 
16507c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
16517c478bd9Sstevel@tonic-gate 
16527c478bd9Sstevel@tonic-gate 	/* Get the interrupt pipe handle */
16537c478bd9Sstevel@tonic-gate 	ph = ehcip->ehci_root_hub.rh_intr_pipe_handle;
16547c478bd9Sstevel@tonic-gate 
16557c478bd9Sstevel@tonic-gate 	/* Get the current interrupt request pointer */
16567c478bd9Sstevel@tonic-gate 	curr_intr_reqp = ehcip->ehci_root_hub.rh_curr_intr_reqp;
16577c478bd9Sstevel@tonic-gate 
16587c478bd9Sstevel@tonic-gate 	/*
16597c478bd9Sstevel@tonic-gate 	 * If current interrupt request pointer is null,
16607c478bd9Sstevel@tonic-gate 	 * allocate new interrupt request.
16617c478bd9Sstevel@tonic-gate 	 */
16627c478bd9Sstevel@tonic-gate 	if (curr_intr_reqp == NULL) {
16637c478bd9Sstevel@tonic-gate 		ASSERT(ehcip->ehci_root_hub.rh_client_intr_reqp);
16647c478bd9Sstevel@tonic-gate 
16657c478bd9Sstevel@tonic-gate 		/* Get the length of interrupt transfer */
16667c478bd9Sstevel@tonic-gate 		length = ehcip->ehci_root_hub.
16677c478bd9Sstevel@tonic-gate 		    rh_client_intr_reqp->intr_len;
16687c478bd9Sstevel@tonic-gate 
16697c478bd9Sstevel@tonic-gate 		curr_intr_reqp = usba_hcdi_dup_intr_req(ph->p_dip,
16707c478bd9Sstevel@tonic-gate 		    ehcip->ehci_root_hub.rh_client_intr_reqp,
16717c478bd9Sstevel@tonic-gate 		    length, flags);
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate 		if (curr_intr_reqp == NULL) {
16747c478bd9Sstevel@tonic-gate 
16757c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
16767c478bd9Sstevel@tonic-gate 			    "ehci_root_hub_allocate_intr_pipe_resource:"
16777c478bd9Sstevel@tonic-gate 			    "Interrupt request structure allocation failed");
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate 			return (USB_NO_RESOURCES);
16807c478bd9Sstevel@tonic-gate 		}
16817c478bd9Sstevel@tonic-gate 
16827c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_curr_intr_reqp = curr_intr_reqp;
16837c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
16847c478bd9Sstevel@tonic-gate 		ph->p_req_count++;
16857c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
16867c478bd9Sstevel@tonic-gate 	}
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate 	/* Start the timer for the root hub interrupt pipe polling */
16897c478bd9Sstevel@tonic-gate 	if (ehcip->ehci_root_hub.rh_intr_pipe_timer_id == 0) {
16907c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_timer_id =
16917c478bd9Sstevel@tonic-gate 		    timeout(ehci_handle_root_hub_status_change,
16927c478bd9Sstevel@tonic-gate 		    (void *)ehcip, drv_usectohz(EHCI_RH_POLL_TIME));
16937c478bd9Sstevel@tonic-gate 
16947c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.
16957c478bd9Sstevel@tonic-gate 		    rh_intr_pipe_state = EHCI_PIPE_STATE_ACTIVE;
16967c478bd9Sstevel@tonic-gate 	}
16977c478bd9Sstevel@tonic-gate 
16987c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
16997c478bd9Sstevel@tonic-gate }
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate 
17027c478bd9Sstevel@tonic-gate /*
17037c478bd9Sstevel@tonic-gate  * ehci_root_hub_intr_pipe_cleanup:
17047c478bd9Sstevel@tonic-gate  *
17057c478bd9Sstevel@tonic-gate  * Deallocate all interrupt requests and do callback
17067c478bd9Sstevel@tonic-gate  * the original client interrupt request.
17077c478bd9Sstevel@tonic-gate  */
17087c478bd9Sstevel@tonic-gate static void
17097c478bd9Sstevel@tonic-gate ehci_root_hub_intr_pipe_cleanup(
17107c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip,
17117c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason)
17127c478bd9Sstevel@tonic-gate {
17137c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp;
17147c478bd9Sstevel@tonic-gate 	usb_opaque_t		client_intr_reqp;
17157c478bd9Sstevel@tonic-gate 	timeout_id_t		timer_id;
17167c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
17177c478bd9Sstevel@tonic-gate 
17187c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
17197c478bd9Sstevel@tonic-gate 	    "ehci_root_hub_intr_pipe_cleanup");
17207c478bd9Sstevel@tonic-gate 
17217c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
17227c478bd9Sstevel@tonic-gate 
17237c478bd9Sstevel@tonic-gate 	/* Get the interrupt pipe handle */
17247c478bd9Sstevel@tonic-gate 	ph = ehcip->ehci_root_hub.rh_intr_pipe_handle;
17257c478bd9Sstevel@tonic-gate 
17267c478bd9Sstevel@tonic-gate 	/* Get the interrupt timerid */
17277c478bd9Sstevel@tonic-gate 	timer_id = ehcip->ehci_root_hub.rh_intr_pipe_timer_id;
17287c478bd9Sstevel@tonic-gate 
17297c478bd9Sstevel@tonic-gate 	/* Stop the root hub interrupt timer */
17307c478bd9Sstevel@tonic-gate 	if (timer_id) {
17317c478bd9Sstevel@tonic-gate 		/* Reset the timer id to zero */
17327c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_timer_id = 0;
17337c478bd9Sstevel@tonic-gate 
17347c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
17357c478bd9Sstevel@tonic-gate 		(void) untimeout(timer_id);
17367c478bd9Sstevel@tonic-gate 		mutex_enter(&ehcip->ehci_int_mutex);
17377c478bd9Sstevel@tonic-gate 	}
17387c478bd9Sstevel@tonic-gate 
17397c478bd9Sstevel@tonic-gate 	/* Reset the current interrupt request pointer */
17407c478bd9Sstevel@tonic-gate 	curr_intr_reqp = ehcip->ehci_root_hub.rh_curr_intr_reqp;
17417c478bd9Sstevel@tonic-gate 
17427c478bd9Sstevel@tonic-gate 	/* Deallocate uncompleted interrupt request */
17437c478bd9Sstevel@tonic-gate 	if (curr_intr_reqp) {
17447c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_curr_intr_reqp = NULL;
17457c478bd9Sstevel@tonic-gate 		usb_free_intr_req(curr_intr_reqp);
17467c478bd9Sstevel@tonic-gate 
17477c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
17487c478bd9Sstevel@tonic-gate 		ph->p_req_count--;
17497c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
17507c478bd9Sstevel@tonic-gate 	}
17517c478bd9Sstevel@tonic-gate 
17527c478bd9Sstevel@tonic-gate 	client_intr_reqp = (usb_opaque_t)
17537c478bd9Sstevel@tonic-gate 	    ehcip->ehci_root_hub.rh_client_intr_reqp;
17547c478bd9Sstevel@tonic-gate 
17557c478bd9Sstevel@tonic-gate 	/* Callback for original client interrupt request */
17567c478bd9Sstevel@tonic-gate 	if (client_intr_reqp) {
17577c478bd9Sstevel@tonic-gate 		ehci_root_hub_hcdi_callback(ph, completion_reason);
17587c478bd9Sstevel@tonic-gate 	}
17597c478bd9Sstevel@tonic-gate }
17607c478bd9Sstevel@tonic-gate 
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate /*
17637c478bd9Sstevel@tonic-gate  * ehci_handle_root_hub_status_change:
17647c478bd9Sstevel@tonic-gate  *
17657c478bd9Sstevel@tonic-gate  * A root hub status change interrupt will occur any time there is a change
17667c478bd9Sstevel@tonic-gate  * in the root hub status register or one of the port status registers.
17677c478bd9Sstevel@tonic-gate  */
17687c478bd9Sstevel@tonic-gate static void
17697c478bd9Sstevel@tonic-gate ehci_handle_root_hub_status_change(void *arg)
17707c478bd9Sstevel@tonic-gate {
17717c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = (ehci_state_t *)arg;
17727c478bd9Sstevel@tonic-gate 	usb_hub_descr_t		*root_hub_descr =
17737c478bd9Sstevel@tonic-gate 				&ehcip->ehci_root_hub.rh_descr;
17747c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp;
17757c478bd9Sstevel@tonic-gate 	usb_port_mask_t		port_mask = 0;
17767c478bd9Sstevel@tonic-gate 	uint_t			new_port_status;
17777c478bd9Sstevel@tonic-gate 	uint_t			change_status;
17787c478bd9Sstevel@tonic-gate 	uint_t			port_status;
17797c478bd9Sstevel@tonic-gate 	mblk_t			*message;
17807c478bd9Sstevel@tonic-gate 	size_t			length;
17817c478bd9Sstevel@tonic-gate 	usb_ep_descr_t		*eptd;
17827c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
17837c478bd9Sstevel@tonic-gate 	int			i;
17847c478bd9Sstevel@tonic-gate 
17857c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
17867c478bd9Sstevel@tonic-gate 
17877c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
17887c478bd9Sstevel@tonic-gate 	    "ehci_handle_root_hub_status_change: state = %d",
17897c478bd9Sstevel@tonic-gate 	    ehcip->ehci_root_hub.rh_intr_pipe_state);
17907c478bd9Sstevel@tonic-gate 
17917c478bd9Sstevel@tonic-gate 	/* Get the current interrupt request pointer */
17927c478bd9Sstevel@tonic-gate 	curr_intr_reqp = ehcip->ehci_root_hub.rh_curr_intr_reqp;
17937c478bd9Sstevel@tonic-gate 
17947c478bd9Sstevel@tonic-gate 	ph = ehcip->ehci_root_hub.rh_intr_pipe_handle;
17957c478bd9Sstevel@tonic-gate 
17967c478bd9Sstevel@tonic-gate 	/* Check whether timeout handler is valid */
17977c478bd9Sstevel@tonic-gate 	if (ehcip->ehci_root_hub.rh_intr_pipe_timer_id) {
17987c478bd9Sstevel@tonic-gate 		/* Check host controller is in operational state */
17997c478bd9Sstevel@tonic-gate 		if ((ehci_state_is_operational(ehcip)) != USB_SUCCESS) {
18007c478bd9Sstevel@tonic-gate 			/* Reset the timer id */
18017c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.rh_intr_pipe_timer_id = 0;
18027c478bd9Sstevel@tonic-gate 
18037c478bd9Sstevel@tonic-gate 			/* Do interrupt pipe cleanup */
18047c478bd9Sstevel@tonic-gate 			ehci_root_hub_intr_pipe_cleanup(
18057c478bd9Sstevel@tonic-gate 			    ehcip, USB_CR_HC_HARDWARE_ERR);
18067c478bd9Sstevel@tonic-gate 
18077c478bd9Sstevel@tonic-gate 			mutex_exit(&ehcip->ehci_int_mutex);
18087c478bd9Sstevel@tonic-gate 
18097c478bd9Sstevel@tonic-gate 			return;
18107c478bd9Sstevel@tonic-gate 		}
18117c478bd9Sstevel@tonic-gate 	} else {
18127c478bd9Sstevel@tonic-gate 		mutex_exit(&ehcip->ehci_int_mutex);
18137c478bd9Sstevel@tonic-gate 
18147c478bd9Sstevel@tonic-gate 		return;
18157c478bd9Sstevel@tonic-gate 	}
18167c478bd9Sstevel@tonic-gate 
18177c478bd9Sstevel@tonic-gate 	eptd = &ehcip->ehci_root_hub.rh_intr_pipe_handle->p_ep;
18187c478bd9Sstevel@tonic-gate 
18197c478bd9Sstevel@tonic-gate 	/* Check each port */
18207c478bd9Sstevel@tonic-gate 	for (i = 0; i < root_hub_descr->bNbrPorts; i++) {
18217c478bd9Sstevel@tonic-gate 
18227c478bd9Sstevel@tonic-gate 		port_status = ehci_get_root_hub_port_status(ehcip, i);
18237c478bd9Sstevel@tonic-gate 
18247c478bd9Sstevel@tonic-gate 		new_port_status = port_status & PORT_STATUS_MASK;
18257c478bd9Sstevel@tonic-gate 		change_status = (port_status >> 16) & PORT_CHANGE_MASK;
18267c478bd9Sstevel@tonic-gate 
18277c478bd9Sstevel@tonic-gate 		/*
18287c478bd9Sstevel@tonic-gate 		 * If there is change in the port status then set the bit in the
18297c478bd9Sstevel@tonic-gate 		 * bitmap of changes and inform hub driver about these changes.
18307c478bd9Sstevel@tonic-gate 		 * Hub driver will take care of these changes.
18317c478bd9Sstevel@tonic-gate 		 */
18327c478bd9Sstevel@tonic-gate 		if (change_status) {
18337c478bd9Sstevel@tonic-gate 
18347c478bd9Sstevel@tonic-gate 			/* See if a device was attached/detached */
18357c478bd9Sstevel@tonic-gate 			if (change_status & PORT_CHANGE_CSC) {
18367c478bd9Sstevel@tonic-gate 				/*
18377c478bd9Sstevel@tonic-gate 				 * Update the state depending on whether
18387c478bd9Sstevel@tonic-gate 				 * the port was attached or detached.
18397c478bd9Sstevel@tonic-gate 				 */
18407c478bd9Sstevel@tonic-gate 				if (new_port_status & PORT_STATUS_CCS) {
18417c478bd9Sstevel@tonic-gate 					ehcip->ehci_root_hub.
18427c478bd9Sstevel@tonic-gate 					    rh_port_state[i] = DISABLED;
18437c478bd9Sstevel@tonic-gate 
18447c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
18457c478bd9Sstevel@tonic-gate 					    ehcip->ehci_log_hdl,
1846*8f1c3597Shx 					    "Port %d connected", i+1);
18477c478bd9Sstevel@tonic-gate 				} else {
18487c478bd9Sstevel@tonic-gate 					ehcip->ehci_root_hub.
18497c478bd9Sstevel@tonic-gate 					    rh_port_state[i] = DISCONNECTED;
18507c478bd9Sstevel@tonic-gate 
18517c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
18527c478bd9Sstevel@tonic-gate 					    ehcip->ehci_log_hdl,
1853*8f1c3597Shx 					    "Port %d disconnected", i+1);
18547c478bd9Sstevel@tonic-gate 				}
18557c478bd9Sstevel@tonic-gate 			}
18567c478bd9Sstevel@tonic-gate 
18577c478bd9Sstevel@tonic-gate 			/* See if port enable status changed */
18587c478bd9Sstevel@tonic-gate 			if (change_status & PORT_CHANGE_PESC) {
18597c478bd9Sstevel@tonic-gate 				/*
18607c478bd9Sstevel@tonic-gate 				 * Update the state depending on whether
18617c478bd9Sstevel@tonic-gate 				 * the port was enabled or disabled.
18627c478bd9Sstevel@tonic-gate 				 */
18637c478bd9Sstevel@tonic-gate 				if (new_port_status & PORT_STATUS_PES) {
18647c478bd9Sstevel@tonic-gate 					ehcip->ehci_root_hub.
18657c478bd9Sstevel@tonic-gate 					    rh_port_state[i] = ENABLED;
18667c478bd9Sstevel@tonic-gate 
18677c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
18687c478bd9Sstevel@tonic-gate 					    ehcip->ehci_log_hdl,
1869*8f1c3597Shx 					    "Port %d enabled", i+1);
18707c478bd9Sstevel@tonic-gate 				} else {
18717c478bd9Sstevel@tonic-gate 					ehcip->ehci_root_hub.
18727c478bd9Sstevel@tonic-gate 					    rh_port_state[i] = DISABLED;
18737c478bd9Sstevel@tonic-gate 
18747c478bd9Sstevel@tonic-gate 					USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB,
18757c478bd9Sstevel@tonic-gate 					    ehcip->ehci_log_hdl,
1876*8f1c3597Shx 					    "Port %d disabled", i+1);
18777c478bd9Sstevel@tonic-gate 				}
18787c478bd9Sstevel@tonic-gate 			}
18797c478bd9Sstevel@tonic-gate 
18807c478bd9Sstevel@tonic-gate 			port_mask |= 1 << (i + 1);
18817c478bd9Sstevel@tonic-gate 
18827c478bd9Sstevel@tonic-gate 			/* Update the status */
18837c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.
18847c478bd9Sstevel@tonic-gate 			    rh_port_status[i] = new_port_status;
18857c478bd9Sstevel@tonic-gate 		}
18867c478bd9Sstevel@tonic-gate 	}
18877c478bd9Sstevel@tonic-gate 
18887c478bd9Sstevel@tonic-gate 	if (ph && port_mask && curr_intr_reqp) {
18897c478bd9Sstevel@tonic-gate 		length = eptd->wMaxPacketSize;
18907c478bd9Sstevel@tonic-gate 
18917c478bd9Sstevel@tonic-gate 		ASSERT(length != 0);
18927c478bd9Sstevel@tonic-gate 
18937c478bd9Sstevel@tonic-gate 		/* Get the  message block */
18947c478bd9Sstevel@tonic-gate 		message = curr_intr_reqp->intr_data;
18957c478bd9Sstevel@tonic-gate 
18967c478bd9Sstevel@tonic-gate 		ASSERT(message != NULL);
18977c478bd9Sstevel@tonic-gate 
18987c478bd9Sstevel@tonic-gate 		do {
1899*8f1c3597Shx 			/*
1900*8f1c3597Shx 			 * check that the mblk is big enough when we
1901*8f1c3597Shx 			 * are writing bytes into it
1902*8f1c3597Shx 			 */
1903*8f1c3597Shx 			if (message->b_wptr >= message->b_datap->db_lim) {
1904*8f1c3597Shx 
1905*8f1c3597Shx 				USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB,
1906*8f1c3597Shx 				    ehcip->ehci_log_hdl,
1907*8f1c3597Shx 				    "ehci_handle_root_hub_status_change"
1908*8f1c3597Shx 				    "mblk data overflow.");
1909*8f1c3597Shx 
1910*8f1c3597Shx 				break;
1911*8f1c3597Shx 			}
19127c478bd9Sstevel@tonic-gate 			*message->b_wptr++ = (uchar_t)port_mask;
19137c478bd9Sstevel@tonic-gate 			port_mask >>= 8;
19147c478bd9Sstevel@tonic-gate 		} while (port_mask != 0);
19157c478bd9Sstevel@tonic-gate 
19167c478bd9Sstevel@tonic-gate 		ehci_root_hub_hcdi_callback(ph, USB_CR_OK);
19177c478bd9Sstevel@tonic-gate 	}
19187c478bd9Sstevel@tonic-gate 
19197c478bd9Sstevel@tonic-gate 	/* Reset the timer id */
19207c478bd9Sstevel@tonic-gate 	ehcip->ehci_root_hub.rh_intr_pipe_timer_id = 0;
19217c478bd9Sstevel@tonic-gate 
19227c478bd9Sstevel@tonic-gate 	if (ehcip->ehci_root_hub.rh_intr_pipe_state ==
19237c478bd9Sstevel@tonic-gate 	    EHCI_PIPE_STATE_ACTIVE) {
19247c478bd9Sstevel@tonic-gate 		/*
19257c478bd9Sstevel@tonic-gate 		 * If needed, allocate new interrupt request. Also
19267c478bd9Sstevel@tonic-gate 		 * start the timer for the root hub interrupt polling.
19277c478bd9Sstevel@tonic-gate 		 */
19287c478bd9Sstevel@tonic-gate 		if ((ehci_root_hub_allocate_intr_pipe_resource(
19297c478bd9Sstevel@tonic-gate 		    ehcip, 0)) != USB_SUCCESS) {
19307c478bd9Sstevel@tonic-gate 
19317c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
19327c478bd9Sstevel@tonic-gate 			    "ehci_handle_root_hub_status_change: No Resources");
19337c478bd9Sstevel@tonic-gate 
19347c478bd9Sstevel@tonic-gate 			/* Do interrupt pipe cleanup */
19357c478bd9Sstevel@tonic-gate 			ehci_root_hub_intr_pipe_cleanup(
19367c478bd9Sstevel@tonic-gate 			    ehcip, USB_CR_NO_RESOURCES);
19377c478bd9Sstevel@tonic-gate 		}
19387c478bd9Sstevel@tonic-gate 	}
19397c478bd9Sstevel@tonic-gate 
19407c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
19417c478bd9Sstevel@tonic-gate }
19427c478bd9Sstevel@tonic-gate 
19437c478bd9Sstevel@tonic-gate 
19447c478bd9Sstevel@tonic-gate /*
19457c478bd9Sstevel@tonic-gate  * ehci_root_hub_hcdi_callback()
19467c478bd9Sstevel@tonic-gate  *
19477c478bd9Sstevel@tonic-gate  * Convenience wrapper around usba_hcdi_cb() for the root hub.
19487c478bd9Sstevel@tonic-gate  */
19497c478bd9Sstevel@tonic-gate static void
19507c478bd9Sstevel@tonic-gate ehci_root_hub_hcdi_callback(
19517c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
19527c478bd9Sstevel@tonic-gate 	usb_cr_t		completion_reason)
19537c478bd9Sstevel@tonic-gate {
19547c478bd9Sstevel@tonic-gate 	ehci_state_t		*ehcip = ehci_obtain_state(
19557c478bd9Sstevel@tonic-gate 				    ph->p_usba_device->usb_root_hub_dip);
19567c478bd9Sstevel@tonic-gate 	uchar_t			attributes = ph->p_ep.bmAttributes &
19577c478bd9Sstevel@tonic-gate 							USB_EP_ATTR_MASK;
19587c478bd9Sstevel@tonic-gate 	usb_opaque_t		curr_xfer_reqp;
19597c478bd9Sstevel@tonic-gate 	uint_t			pipe_state = 0;
19607c478bd9Sstevel@tonic-gate 
19617c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl,
19627c478bd9Sstevel@tonic-gate 	    "ehci_root_hub_hcdi_callback: ph = 0x%p, cr = 0x%x",
19637c478bd9Sstevel@tonic-gate 	    ph, completion_reason);
19647c478bd9Sstevel@tonic-gate 
19657c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
19667c478bd9Sstevel@tonic-gate 
19677c478bd9Sstevel@tonic-gate 	/* Set the pipe state as per completion reason */
19687c478bd9Sstevel@tonic-gate 	switch (completion_reason) {
19697c478bd9Sstevel@tonic-gate 	case USB_CR_OK:
19707c478bd9Sstevel@tonic-gate 		switch (attributes) {
19717c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_CONTROL:
19727c478bd9Sstevel@tonic-gate 			pipe_state = EHCI_PIPE_STATE_IDLE;
19737c478bd9Sstevel@tonic-gate 			break;
19747c478bd9Sstevel@tonic-gate 		case USB_EP_ATTR_INTR:
19757c478bd9Sstevel@tonic-gate 			pipe_state = ehcip->ehci_root_hub.
19767c478bd9Sstevel@tonic-gate 			    rh_intr_pipe_state;
19777c478bd9Sstevel@tonic-gate 			break;
19787c478bd9Sstevel@tonic-gate 		}
19797c478bd9Sstevel@tonic-gate 		break;
19807c478bd9Sstevel@tonic-gate 	case USB_CR_NO_RESOURCES:
19817c478bd9Sstevel@tonic-gate 	case USB_CR_NOT_SUPPORTED:
19827c478bd9Sstevel@tonic-gate 	case USB_CR_STOPPED_POLLING:
19837c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_RESET:
19847c478bd9Sstevel@tonic-gate 	case USB_CR_HC_HARDWARE_ERR:
19857c478bd9Sstevel@tonic-gate 		/* Set pipe state to idle */
19867c478bd9Sstevel@tonic-gate 		pipe_state = EHCI_PIPE_STATE_IDLE;
19877c478bd9Sstevel@tonic-gate 		break;
19887c478bd9Sstevel@tonic-gate 	case USB_CR_PIPE_CLOSING:
19897c478bd9Sstevel@tonic-gate 		break;
19907c478bd9Sstevel@tonic-gate 	default:
19917c478bd9Sstevel@tonic-gate 		/* Set pipe state to error */
19927c478bd9Sstevel@tonic-gate 		pipe_state = EHCI_PIPE_STATE_ERROR;
19937c478bd9Sstevel@tonic-gate 		break;
19947c478bd9Sstevel@tonic-gate 	}
19957c478bd9Sstevel@tonic-gate 
19967c478bd9Sstevel@tonic-gate 	switch (attributes) {
19977c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
19987c478bd9Sstevel@tonic-gate 		curr_xfer_reqp = (usb_opaque_t)
19997c478bd9Sstevel@tonic-gate 		    ehcip->ehci_root_hub.rh_curr_ctrl_reqp;
20007c478bd9Sstevel@tonic-gate 
20017c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_curr_ctrl_reqp = NULL;
20027c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_ctrl_pipe_state = pipe_state;
20037c478bd9Sstevel@tonic-gate 		break;
20047c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
20057c478bd9Sstevel@tonic-gate 		/* if curr_intr_reqp available then use this request */
20067c478bd9Sstevel@tonic-gate 		if (ehcip->ehci_root_hub.rh_curr_intr_reqp) {
20077c478bd9Sstevel@tonic-gate 			curr_xfer_reqp = (usb_opaque_t)ehcip->
20087c478bd9Sstevel@tonic-gate 			    ehci_root_hub.rh_curr_intr_reqp;
20097c478bd9Sstevel@tonic-gate 
20107c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.rh_curr_intr_reqp = NULL;
20117c478bd9Sstevel@tonic-gate 		} else {
20127c478bd9Sstevel@tonic-gate 			/* no current request, use client's request */
20137c478bd9Sstevel@tonic-gate 			curr_xfer_reqp = (usb_opaque_t)
20147c478bd9Sstevel@tonic-gate 			    ehcip->ehci_root_hub.rh_client_intr_reqp;
20157c478bd9Sstevel@tonic-gate 
20167c478bd9Sstevel@tonic-gate 			ehcip->ehci_root_hub.rh_client_intr_reqp = NULL;
20177c478bd9Sstevel@tonic-gate 		}
20187c478bd9Sstevel@tonic-gate 		ehcip->ehci_root_hub.rh_intr_pipe_state = pipe_state;
20197c478bd9Sstevel@tonic-gate 		break;
20207c478bd9Sstevel@tonic-gate 	}
20217c478bd9Sstevel@tonic-gate 
20227c478bd9Sstevel@tonic-gate 	ASSERT(curr_xfer_reqp != NULL);
20237c478bd9Sstevel@tonic-gate 
20247c478bd9Sstevel@tonic-gate 	mutex_exit(&ehcip->ehci_int_mutex);
20257c478bd9Sstevel@tonic-gate 	usba_hcdi_cb(ph, curr_xfer_reqp, completion_reason);
20267c478bd9Sstevel@tonic-gate 	mutex_enter(&ehcip->ehci_int_mutex);
20277c478bd9Sstevel@tonic-gate }
2028