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
5112116d8Sfb  * Common Development and Distribution License (the "License").
6112116d8Sfb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22112116d8Sfb  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * Universal Serial BUS  Host Controller Driver (UHCI)
297c478bd9Sstevel@tonic-gate  *
307c478bd9Sstevel@tonic-gate  * The UHCI driver is a driver which interfaces to the Universal
317c478bd9Sstevel@tonic-gate  * Serial Bus Architecture (USBA) and the Host Controller (HC). The interface to
327c478bd9Sstevel@tonic-gate  * the Host Controller is defined by the Universal Host Controller Interface.
337c478bd9Sstevel@tonic-gate  * This file contains the code for root hub related functions.
347c478bd9Sstevel@tonic-gate  */
357c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/uhci/uhcid.h>
367c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/uhci/uhci.h>
377c478bd9Sstevel@tonic-gate #include <sys/usb/hcd/uhci/uhcihub.h>
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate /*
407c478bd9Sstevel@tonic-gate  *  Function Prototypes
417c478bd9Sstevel@tonic-gate  */
427c478bd9Sstevel@tonic-gate static int	uhci_handle_set_clear_port_feature(
437c478bd9Sstevel@tonic-gate 			uhci_state_t		*uhcip,
447c478bd9Sstevel@tonic-gate 			uchar_t			bRequest,
457c478bd9Sstevel@tonic-gate 			uint16_t		wValue,
467c478bd9Sstevel@tonic-gate 			usb_port_t		port);
477c478bd9Sstevel@tonic-gate static	void	uhci_handle_port_power(
487c478bd9Sstevel@tonic-gate 			uhci_state_t		*uhcip,
497c478bd9Sstevel@tonic-gate 			usb_port_t		port,
507c478bd9Sstevel@tonic-gate 			uint_t			on);
517c478bd9Sstevel@tonic-gate static	void	uhci_handle_port_suspend(
527c478bd9Sstevel@tonic-gate 			uhci_state_t		*uhcip,
537c478bd9Sstevel@tonic-gate 			usb_port_t		port,
547c478bd9Sstevel@tonic-gate 			uint_t			on);
557c478bd9Sstevel@tonic-gate static	void	uhci_handle_port_enable_disable(
567c478bd9Sstevel@tonic-gate 			uhci_state_t		*uhcip,
577c478bd9Sstevel@tonic-gate 			usb_port_t		port,
587c478bd9Sstevel@tonic-gate 			uint_t			on);
597c478bd9Sstevel@tonic-gate static	void	uhci_handle_port_reset(
607c478bd9Sstevel@tonic-gate 			uhci_state_t		*uhcip,
617c478bd9Sstevel@tonic-gate 			usb_port_t		port);
627c478bd9Sstevel@tonic-gate static	void	uhci_handle_complete_port_reset(
637c478bd9Sstevel@tonic-gate 			uhci_state_t		*uhcip,
647c478bd9Sstevel@tonic-gate 			usb_port_t		port);
657c478bd9Sstevel@tonic-gate static	void	uhci_handle_clear_port_connection(
667c478bd9Sstevel@tonic-gate 			uhci_state_t		*uhcip,
677c478bd9Sstevel@tonic-gate 			usb_port_t		port);
687c478bd9Sstevel@tonic-gate static	void	uhci_handle_get_port_status(
697c478bd9Sstevel@tonic-gate 			uhci_state_t		*uhcip,
707c478bd9Sstevel@tonic-gate 			usb_ctrl_req_t		*req,
717c478bd9Sstevel@tonic-gate 			usb_port_t		port);
727c478bd9Sstevel@tonic-gate static	void	uhci_handle_get_hub_descriptor(
737c478bd9Sstevel@tonic-gate 			uhci_state_t		*uhcip,
747c478bd9Sstevel@tonic-gate 			usb_ctrl_req_t		*req);
757c478bd9Sstevel@tonic-gate static void	uhci_handle_get_hub_status(
767c478bd9Sstevel@tonic-gate 			uhci_state_t		*uhcip,
777c478bd9Sstevel@tonic-gate 			usb_ctrl_req_t		*req);
7835f36846Ssl static void	uhci_handle_get_device_status(
7935f36846Ssl 			uhci_state_t		*uhcip,
8035f36846Ssl 			usb_ctrl_req_t		*req);
817c478bd9Sstevel@tonic-gate static uint_t	uhci_get_port_status(
827c478bd9Sstevel@tonic-gate 			uhci_state_t		*uhcip,
837c478bd9Sstevel@tonic-gate 			usb_port_t		port);
847c478bd9Sstevel@tonic-gate static	void	uhci_rh_hcdi_callback(
857c478bd9Sstevel@tonic-gate 			uhci_state_t		*uhcip,
867c478bd9Sstevel@tonic-gate 			usba_pipe_handle_data_t	*ph,
877c478bd9Sstevel@tonic-gate 			usb_opaque_t		req,
887c478bd9Sstevel@tonic-gate 			usb_cr_t		cr);
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate /*
917c478bd9Sstevel@tonic-gate  * root hub device descriptor
927c478bd9Sstevel@tonic-gate  */
937c478bd9Sstevel@tonic-gate static usb_dev_descr_t uhci_rh_dev_descr = {
947c478bd9Sstevel@tonic-gate 	0x12,	/* Length */
957c478bd9Sstevel@tonic-gate 	1,	/* Type */
967c478bd9Sstevel@tonic-gate 	0x110,	/* BCD - v1.1 */
977c478bd9Sstevel@tonic-gate 	9,	/* Class */
987c478bd9Sstevel@tonic-gate 	0,	/* Sub class */
997c478bd9Sstevel@tonic-gate 	0,	/* Protocol */
1007c478bd9Sstevel@tonic-gate 	8,	/* Max pkt size */
1017c478bd9Sstevel@tonic-gate 	0,	/* Vendor */
1027c478bd9Sstevel@tonic-gate 	0,	/* Product id */
1037c478bd9Sstevel@tonic-gate 	0,	/* Device release */
1047c478bd9Sstevel@tonic-gate 	0,	/* Manufacturer */
1057c478bd9Sstevel@tonic-gate 	0,	/* Product */
1067c478bd9Sstevel@tonic-gate 	0,	/* Sn */
1077c478bd9Sstevel@tonic-gate 	1	/* No of configs */
1087c478bd9Sstevel@tonic-gate };
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate /*
1117c478bd9Sstevel@tonic-gate  * root hub config descriptor
1127c478bd9Sstevel@tonic-gate  */
1137c478bd9Sstevel@tonic-gate static uchar_t uhci_rh_config_descr[] = {
1147c478bd9Sstevel@tonic-gate 	/* config descriptor */
1157c478bd9Sstevel@tonic-gate 	0x09,		/* bLength */
1167c478bd9Sstevel@tonic-gate 	0x02,		/* bDescriptorType, Configuration */
1177c478bd9Sstevel@tonic-gate 	0x19, 0x00,	/* wTotalLength */
1187c478bd9Sstevel@tonic-gate 	0x01,		/* bNumInterfaces */
1197c478bd9Sstevel@tonic-gate 	0x01,		/* bConfigurationValue */
1207c478bd9Sstevel@tonic-gate 	0x00,		/* iConfiguration */
1217c478bd9Sstevel@tonic-gate 	0x40,		/* bmAttributes */
1227c478bd9Sstevel@tonic-gate 	0x00,		/* MaxPower */
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate 	/* interface descriptor */
1257c478bd9Sstevel@tonic-gate 	0x09,		/* bLength */
1267c478bd9Sstevel@tonic-gate 	0x04,		/* bDescriptorType, Interface */
1277c478bd9Sstevel@tonic-gate 	0x00,		/* bInterfaceNumber */
1287c478bd9Sstevel@tonic-gate 	0x00,		/* bAlternateSetting */
1297c478bd9Sstevel@tonic-gate 	0x01,		/* bNumEndpoints */
1307c478bd9Sstevel@tonic-gate 	0x09,		/* bInterfaceClass */
1317c478bd9Sstevel@tonic-gate 	0x01,		/* bInterfaceSubClass */
1327c478bd9Sstevel@tonic-gate 	0x00,		/* bInterfaceProtocol */
1337c478bd9Sstevel@tonic-gate 	0x00,		/* iInterface */
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	/* endpoint descriptor */
1367c478bd9Sstevel@tonic-gate 	0x07,		/* bLength */
1377c478bd9Sstevel@tonic-gate 	0x05,		/* bDescriptorType, Endpoint */
1387c478bd9Sstevel@tonic-gate 	0x81,		/* bEndpointAddress */
1397c478bd9Sstevel@tonic-gate 	0x03,		/* bmAttributes */
1407c478bd9Sstevel@tonic-gate 	0x01, 0x00,	/* wMaxPacketSize, 1 +	(OHCI_MAX_RH_PORTS / 8) */
1417c478bd9Sstevel@tonic-gate 	0x20		/* bInterval */
1427c478bd9Sstevel@tonic-gate };
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate /*
1467c478bd9Sstevel@tonic-gate  * uhci_init_root_hub:
1477c478bd9Sstevel@tonic-gate  *	Initialize the root hub
1487c478bd9Sstevel@tonic-gate  */
1497c478bd9Sstevel@tonic-gate int
uhci_init_root_hub(uhci_state_t * uhcip)1507c478bd9Sstevel@tonic-gate uhci_init_root_hub(uhci_state_t *uhcip)
1517c478bd9Sstevel@tonic-gate {
1527c478bd9Sstevel@tonic-gate 	int		i, length;
1537c478bd9Sstevel@tonic-gate 	usb_hub_descr_t	*root_hub_descr = &uhcip->uhci_root_hub.rh_descr;
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
1567c478bd9Sstevel@tonic-gate 	    "uhci_init_root_hub:");
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 	uhcip->uhci_root_hub.rh_num_ports = MAX_RH_PORTS;
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate 	/*
1617c478bd9Sstevel@tonic-gate 	 * Build the hub descriptor
1627c478bd9Sstevel@tonic-gate 	 */
1637c478bd9Sstevel@tonic-gate 	root_hub_descr->bDescriptorType = ROOT_HUB_DESCRIPTOR_TYPE;
1647c478bd9Sstevel@tonic-gate 	root_hub_descr->bNbrPorts	= MAX_RH_PORTS;
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate 	length = root_hub_descr->bNbrPorts / 8;
1677c478bd9Sstevel@tonic-gate 	if (length) {
1687c478bd9Sstevel@tonic-gate 		root_hub_descr->bDescLength = 7 + (2 * (length + 1));
1697c478bd9Sstevel@tonic-gate 	} else {
1707c478bd9Sstevel@tonic-gate 		root_hub_descr->bDescLength = ROOT_HUB_DESCRIPTOR_LENGTH;
1717c478bd9Sstevel@tonic-gate 	}
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate 	/* Determine the Power Switching Mode */
1747c478bd9Sstevel@tonic-gate 	root_hub_descr->bPwrOn2PwrGood = 10; /* arbitrary number */
1757c478bd9Sstevel@tonic-gate 	root_hub_descr->wHubCharacteristics =
176112116d8Sfb 	    HUB_CHARS_NO_POWER_SWITCHING|HUB_CHARS_NO_OVER_CURRENT;
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 	/* Indicate if the device is removable */
1797c478bd9Sstevel@tonic-gate 	root_hub_descr->DeviceRemovable = 0x0;
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 	/* Fill in the port power control mask */
1827c478bd9Sstevel@tonic-gate 	root_hub_descr->PortPwrCtrlMask = 0xff;
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 	for (i = 0; i < uhcip->uhci_root_hub.rh_num_ports; i++) {
1857c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_port_state[i]  = DISCONNECTED;
1867c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_port_status[i] = 0;
1877c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_port_changes[i] = 0;
1887c478bd9Sstevel@tonic-gate 	}
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 	/* Finally load the root hub driver */
1917c478bd9Sstevel@tonic-gate 	return (usba_hubdi_bind_root_hub(uhcip->uhci_dip, uhci_rh_config_descr,
1927c478bd9Sstevel@tonic-gate 	    sizeof (uhci_rh_config_descr), &uhci_rh_dev_descr));
1937c478bd9Sstevel@tonic-gate }
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate /*
1977c478bd9Sstevel@tonic-gate  * uhci_handle_root_hub_request:
1987c478bd9Sstevel@tonic-gate  *	Intercept a root hub request.
1997c478bd9Sstevel@tonic-gate  *	Handle the  root hub request through the registers
2007c478bd9Sstevel@tonic-gate  */
2017c478bd9Sstevel@tonic-gate int
uhci_handle_root_hub_request(uhci_state_t * uhcip,usba_pipe_handle_data_t * pipe_handle,usb_ctrl_req_t * req)2027c478bd9Sstevel@tonic-gate uhci_handle_root_hub_request(
2037c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
2047c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t  *pipe_handle,
2057c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*req)
2067c478bd9Sstevel@tonic-gate {
2077c478bd9Sstevel@tonic-gate 	int		error = USB_SUCCESS;
2087c478bd9Sstevel@tonic-gate 	uint16_t	port = req->ctrl_wIndex - 1;
2097c478bd9Sstevel@tonic-gate 	usb_cr_t	completion_reason;
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
2127c478bd9Sstevel@tonic-gate 	    "uhci_handle_root_hub_request: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%p",
2137c478bd9Sstevel@tonic-gate 	    req->ctrl_bmRequestType, req->ctrl_bRequest, req->ctrl_wValue,
2147c478bd9Sstevel@tonic-gate 	    req->ctrl_wIndex, req->ctrl_wLength, (void *)req->ctrl_data);
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 	switch (req->ctrl_bmRequestType) {
21935f36846Ssl 	case HUB_GET_DEVICE_STATUS_TYPE:
22035f36846Ssl 		uhci_handle_get_device_status(uhcip, req);
22135f36846Ssl 
22235f36846Ssl 		break;
22335f36846Ssl 	case HUB_HANDLE_PORT_FEATURE_TYPE:
2247c478bd9Sstevel@tonic-gate 		error = uhci_handle_set_clear_port_feature(uhcip,
2257c478bd9Sstevel@tonic-gate 		    req->ctrl_bRequest, req->ctrl_wValue, port);
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 		break;
22835f36846Ssl 	case HUB_GET_PORT_STATUS_TYPE:
2297c478bd9Sstevel@tonic-gate 		uhci_handle_get_port_status(uhcip, req, port);
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 		break;
23235f36846Ssl 	case HUB_CLASS_REQ_TYPE:
2337c478bd9Sstevel@tonic-gate 		switch (req->ctrl_bRequest) {
2347c478bd9Sstevel@tonic-gate 		case USB_REQ_GET_DESCR:
2357c478bd9Sstevel@tonic-gate 			uhci_handle_get_hub_descriptor(uhcip, req);
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 			break;
2387c478bd9Sstevel@tonic-gate 		case USB_REQ_GET_STATUS:
2397c478bd9Sstevel@tonic-gate 			uhci_handle_get_hub_status(uhcip, req);
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 			break;
2427c478bd9Sstevel@tonic-gate 		default:
2437c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
2447c478bd9Sstevel@tonic-gate 			    "uhci_handle_root_hub_request: Unsupported "
2457c478bd9Sstevel@tonic-gate 			    "request 0x%x", req->ctrl_bmRequestType);
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 			break;
2487c478bd9Sstevel@tonic-gate 		}
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 		break;
2517c478bd9Sstevel@tonic-gate 	default:
2527c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
2537c478bd9Sstevel@tonic-gate 		    "uhci_handle_root_hub_request: Unsupported request 0x%x",
2547c478bd9Sstevel@tonic-gate 		    req->ctrl_bmRequestType);
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 		break;
2577c478bd9Sstevel@tonic-gate 	}
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 	completion_reason = (error != USB_SUCCESS) ?
260112116d8Sfb 	    USB_CR_NOT_SUPPORTED : USB_CR_OK;
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
2637c478bd9Sstevel@tonic-gate 	    "uhci_handle_root_hub_request: error = %d", error);
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	uhci_rh_hcdi_callback(uhcip, pipe_handle, (usb_opaque_t)req,
266112116d8Sfb 	    completion_reason);
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
2697c478bd9Sstevel@tonic-gate }
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate /*
2737c478bd9Sstevel@tonic-gate  * uhci_handle_set_clear_port_feature:
2747c478bd9Sstevel@tonic-gate  */
2757c478bd9Sstevel@tonic-gate static int
uhci_handle_set_clear_port_feature(uhci_state_t * uhcip,uchar_t bRequest,uint16_t wValue,usb_port_t port)2767c478bd9Sstevel@tonic-gate uhci_handle_set_clear_port_feature(
2777c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
2787c478bd9Sstevel@tonic-gate 	uchar_t			bRequest,
2797c478bd9Sstevel@tonic-gate 	uint16_t		wValue,
2807c478bd9Sstevel@tonic-gate 	usb_port_t		port)
2817c478bd9Sstevel@tonic-gate {
2827c478bd9Sstevel@tonic-gate 	int    error = USB_SUCCESS;
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
2857c478bd9Sstevel@tonic-gate 	    "uhci_handle_set_clear_port_feature: 0x%x 0x%x 0x%x",
2867c478bd9Sstevel@tonic-gate 	    bRequest, wValue, port);
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 	switch (bRequest) {
2897c478bd9Sstevel@tonic-gate 	case USB_REQ_SET_FEATURE:
2907c478bd9Sstevel@tonic-gate 		switch (wValue) {
2917c478bd9Sstevel@tonic-gate 		case CFS_PORT_ENABLE:
2927c478bd9Sstevel@tonic-gate 			uhci_handle_port_enable_disable(uhcip,
293112116d8Sfb 			    port, UHCI_ENABLE_PORT);
2947c478bd9Sstevel@tonic-gate 			break;
2957c478bd9Sstevel@tonic-gate 		case CFS_PORT_SUSPEND:
2967c478bd9Sstevel@tonic-gate 			uhci_handle_port_suspend(uhcip, port, 1);
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 			break;
2997c478bd9Sstevel@tonic-gate 		case CFS_PORT_RESET:
3007c478bd9Sstevel@tonic-gate 			uhci_handle_port_reset(uhcip, port);
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 			break;
3037c478bd9Sstevel@tonic-gate 		case CFS_PORT_POWER:
3047c478bd9Sstevel@tonic-gate 			uhci_handle_port_power(uhcip, port,
305112116d8Sfb 			    UHCI_ENABLE_PORT_PWR);
3067c478bd9Sstevel@tonic-gate 			break;
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 		default:
309d291d9f2Sfrits 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
3107c478bd9Sstevel@tonic-gate 			    "uhci_handle_set_clear_port_feature: "
3117c478bd9Sstevel@tonic-gate 			    "Unsupported request 0x%x 0x%x", bRequest, wValue);
3127c478bd9Sstevel@tonic-gate 			error = USB_FAILURE;
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 			break;
3157c478bd9Sstevel@tonic-gate 		}
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 		break;
3187c478bd9Sstevel@tonic-gate 	case USB_REQ_CLEAR_FEATURE:
3197c478bd9Sstevel@tonic-gate 		switch (wValue) {
3207c478bd9Sstevel@tonic-gate 		case CFS_PORT_ENABLE:
3217c478bd9Sstevel@tonic-gate 			uhci_handle_port_enable_disable(uhcip,
322112116d8Sfb 			    port, UHCI_DISABLE_PORT);
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 			break;
3257c478bd9Sstevel@tonic-gate 		case CFS_C_PORT_ENABLE:
3267c478bd9Sstevel@tonic-gate 			uhci_handle_port_enable_disable(uhcip,
327112116d8Sfb 			    port, UHCI_CLEAR_ENDIS_BIT);
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 			break;
3307c478bd9Sstevel@tonic-gate 		case CFS_PORT_SUSPEND:
3317c478bd9Sstevel@tonic-gate 			uhci_handle_port_suspend(uhcip, port, 0);
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 			break;
3347c478bd9Sstevel@tonic-gate 		case CFS_C_PORT_RESET:
3357c478bd9Sstevel@tonic-gate 			uhci_handle_complete_port_reset(uhcip, port);
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 			break;
3387c478bd9Sstevel@tonic-gate 		case CFS_PORT_POWER:
3397c478bd9Sstevel@tonic-gate 			uhci_handle_port_power(uhcip, port,
340112116d8Sfb 			    UHCI_DISABLE_PORT_PWR);
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 			break;
3437c478bd9Sstevel@tonic-gate 		case CFS_C_PORT_CONNECTION:
3447c478bd9Sstevel@tonic-gate 			uhci_handle_clear_port_connection(uhcip, port);
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 			break;
3477c478bd9Sstevel@tonic-gate 		default:
348d291d9f2Sfrits 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
3497c478bd9Sstevel@tonic-gate 			    "uhci_handle_set_clear_port_feature: "
3507c478bd9Sstevel@tonic-gate 			    "Unsupported request 0x%x 0x%x", bRequest, wValue);
3517c478bd9Sstevel@tonic-gate 			error = USB_FAILURE;
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 			break;
3547c478bd9Sstevel@tonic-gate 		}
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 		break;
3577c478bd9Sstevel@tonic-gate 	default:
3587c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
3597c478bd9Sstevel@tonic-gate 		    "uhci_handle_set_clear_port_feature: "
3607c478bd9Sstevel@tonic-gate 		    "Unsupported request 0x%x 0x%x", bRequest, wValue);
3617c478bd9Sstevel@tonic-gate 		error = USB_FAILURE;
3627c478bd9Sstevel@tonic-gate 	}
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	return (error);
3667c478bd9Sstevel@tonic-gate }
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate /*
3707c478bd9Sstevel@tonic-gate  * uhci_handle_port_suspend:
3717c478bd9Sstevel@tonic-gate  */
3727c478bd9Sstevel@tonic-gate static void
uhci_handle_port_suspend(uhci_state_t * uhcip,usb_port_t port,uint_t on)3737c478bd9Sstevel@tonic-gate uhci_handle_port_suspend(
3747c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
3757c478bd9Sstevel@tonic-gate 	usb_port_t		port,
3767c478bd9Sstevel@tonic-gate 	uint_t			on)
3777c478bd9Sstevel@tonic-gate {
3787c478bd9Sstevel@tonic-gate 	uint_t	port_status = Get_OpReg16(PORTSC[port]);
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
3817c478bd9Sstevel@tonic-gate 	    "uhci_handle_port_suspend: port=%d on=%d",
3827c478bd9Sstevel@tonic-gate 	    port, on);
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	if (on) {
3857c478bd9Sstevel@tonic-gate 		/* See if the port suspend is already on */
3867c478bd9Sstevel@tonic-gate 		if (!(port_status & HCR_PORT_SUSPEND)) {
3877c478bd9Sstevel@tonic-gate 			/* suspend the port */
3887c478bd9Sstevel@tonic-gate 			Set_OpReg16(PORTSC[port],
389112116d8Sfb 			    (port_status | HCR_PORT_SUSPEND));
3907c478bd9Sstevel@tonic-gate 		}
3917c478bd9Sstevel@tonic-gate 	} else {
3927c478bd9Sstevel@tonic-gate 		/* See if the port suspend is already off */
3937c478bd9Sstevel@tonic-gate 		if ((port_status & HCR_PORT_SUSPEND)) {
3947c478bd9Sstevel@tonic-gate 			/* resume the port */
3957c478bd9Sstevel@tonic-gate 			Set_OpReg16(PORTSC[port],
396112116d8Sfb 			    (port_status & ~HCR_PORT_SUSPEND));
3977c478bd9Sstevel@tonic-gate 		}
3987c478bd9Sstevel@tonic-gate 	}
3997c478bd9Sstevel@tonic-gate }
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate /*
4037c478bd9Sstevel@tonic-gate  * uhci_handle_port_power:
4047c478bd9Sstevel@tonic-gate  *	Turn on a root hub port.  NOTE: Driver does not have any control
4057c478bd9Sstevel@tonic-gate  *	over the power status.
4067c478bd9Sstevel@tonic-gate  */
4077c478bd9Sstevel@tonic-gate /* ARGSUSED */
4087c478bd9Sstevel@tonic-gate static void
uhci_handle_port_power(uhci_state_t * uhcip,usb_port_t port,uint_t on)4097c478bd9Sstevel@tonic-gate uhci_handle_port_power(
4107c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
4117c478bd9Sstevel@tonic-gate 	usb_port_t		port,
4127c478bd9Sstevel@tonic-gate 	uint_t			on)
4137c478bd9Sstevel@tonic-gate {
4147c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
4157c478bd9Sstevel@tonic-gate 	    "uhci_handle_port_power: nothing to do");
4167c478bd9Sstevel@tonic-gate }
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate /*
4207c478bd9Sstevel@tonic-gate  * uhci_handle_port_enable_disable:
4217c478bd9Sstevel@tonic-gate  *	Handle port enable request.
4227c478bd9Sstevel@tonic-gate  */
4237c478bd9Sstevel@tonic-gate static void
uhci_handle_port_enable_disable(uhci_state_t * uhcip,usb_port_t port,uint_t action)4247c478bd9Sstevel@tonic-gate uhci_handle_port_enable_disable(
4257c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
4267c478bd9Sstevel@tonic-gate 	usb_port_t		port,
4277c478bd9Sstevel@tonic-gate 	uint_t			action)
4287c478bd9Sstevel@tonic-gate {
4297c478bd9Sstevel@tonic-gate 	uint_t	port_status = Get_OpReg16(PORTSC[port]);
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
4327c478bd9Sstevel@tonic-gate 	    "uhci_handle_port_enable: port = 0x%x, status = 0x%x",
4337c478bd9Sstevel@tonic-gate 	    port, port_status);
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 	if (action == UHCI_ENABLE_PORT) {
4367c478bd9Sstevel@tonic-gate 		/* See if the port enable is already on */
4377c478bd9Sstevel@tonic-gate 		if (!(port_status & HCR_PORT_ENABLE)) {
4387c478bd9Sstevel@tonic-gate 			/* Enable the port */
4397c478bd9Sstevel@tonic-gate 			Set_OpReg16(PORTSC[port],
440112116d8Sfb 			    (port_status | HCR_PORT_ENABLE));
4417c478bd9Sstevel@tonic-gate 		}
4427c478bd9Sstevel@tonic-gate 	} else if (action == UHCI_DISABLE_PORT) {
4437c478bd9Sstevel@tonic-gate 		/* See if the port enable is already off */
4447c478bd9Sstevel@tonic-gate 		if ((port_status & HCR_PORT_ENABLE)) {
4457c478bd9Sstevel@tonic-gate 			/* Disable the port */
4467c478bd9Sstevel@tonic-gate 			Set_OpReg16(PORTSC[port],
447112116d8Sfb 			    (port_status & ~HCR_PORT_ENABLE));
4487c478bd9Sstevel@tonic-gate 		}
4497c478bd9Sstevel@tonic-gate 	} else {
4507c478bd9Sstevel@tonic-gate 		/* Clear the Enable/Disable change bit */
4517c478bd9Sstevel@tonic-gate 		Set_OpReg16(PORTSC[port], (port_status | HCR_PORT_ENDIS_CHG));
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 		/* Update software port_changes register */
4547c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_port_changes[port] &= ~PORT_CHANGE_PESC;
4557c478bd9Sstevel@tonic-gate 	}
4567c478bd9Sstevel@tonic-gate }
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate /*
4607c478bd9Sstevel@tonic-gate  * uhci_root_hub_reset_occurred:
4617c478bd9Sstevel@tonic-gate  *	Inform the upper layer that reset has occured on the port.
4627c478bd9Sstevel@tonic-gate  *	This is required because the upper layer is expecting an
4637c478bd9Sstevel@tonic-gate  *	event immediately after doing a reset. In case of OHCI
4647c478bd9Sstevel@tonic-gate  *	the HC gets an interrupt for the change in the root hub
4657c478bd9Sstevel@tonic-gate  *	status, but in case of UHCI we don't. So, we send an
4667c478bd9Sstevel@tonic-gate  *	event to the upper layer as soon as we complete the reset
4677c478bd9Sstevel@tonic-gate  *	as long as the root hub pipe is polling.
4687c478bd9Sstevel@tonic-gate  */
4697c478bd9Sstevel@tonic-gate void
uhci_root_hub_reset_occurred(uhci_state_t * uhcip,uint16_t port)4707c478bd9Sstevel@tonic-gate uhci_root_hub_reset_occurred(
4717c478bd9Sstevel@tonic-gate 	uhci_state_t	*uhcip,
4727c478bd9Sstevel@tonic-gate 	uint16_t	port)
4737c478bd9Sstevel@tonic-gate {
4747c478bd9Sstevel@tonic-gate 	usb_intr_req_t	*intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp;
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
4777c478bd9Sstevel@tonic-gate 	    "uhci_root_hub_reset_occurred: intr_reqp = 0x%p data = 0x%p",
478112116d8Sfb 	    (void *)intr_reqp, (void *)intr_reqp->intr_data);
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 	*intr_reqp->intr_data->b_wptr++ = (1 << (port+1));
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 	uhci_rh_hcdi_callback(uhcip, uhcip->uhci_root_hub.rh_intr_pipe_handle,
4837c478bd9Sstevel@tonic-gate 	    (usb_opaque_t)intr_reqp, USB_CR_OK);
4847c478bd9Sstevel@tonic-gate }
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate /*
4887c478bd9Sstevel@tonic-gate  * uhci_handle_port_reset:
4897c478bd9Sstevel@tonic-gate  *	Perform a port reset.
4907c478bd9Sstevel@tonic-gate  */
4917c478bd9Sstevel@tonic-gate static void
uhci_handle_port_reset(uhci_state_t * uhcip,usb_port_t port)4927c478bd9Sstevel@tonic-gate uhci_handle_port_reset(
4937c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
4947c478bd9Sstevel@tonic-gate 	usb_port_t		port)
4957c478bd9Sstevel@tonic-gate {
4967c478bd9Sstevel@tonic-gate 	uint_t	port_status = Get_OpReg16(PORTSC[port]);
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
4997c478bd9Sstevel@tonic-gate 	    "uhci_handle_port_reset: port = 0x%x, status = 0x%x",
5007c478bd9Sstevel@tonic-gate 	    port, port_status);
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate 	if (!(port_status & HCR_PORT_CCS)) {
5037c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
5047c478bd9Sstevel@tonic-gate 		    "port_status & HCR_PORT_CCS == 0: "
5057c478bd9Sstevel@tonic-gate 		    "port = 0x%x, status = 0x%x", port, port_status);
5067c478bd9Sstevel@tonic-gate 	}
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 	Set_OpReg16(PORTSC[port], (port_status| HCR_PORT_RESET));
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate 	drv_usecwait(UHCI_RESET_DELAY);
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	Set_OpReg16(PORTSC[port], (port_status & ~HCR_PORT_RESET));
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 	drv_usecwait(UHCI_RESET_DELAY/100);
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	Set_OpReg16(PORTSC[port], (port_status| HCR_PORT_ENABLE));
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate 	/*
5197c478bd9Sstevel@tonic-gate 	 * The next function is only called if the interrupt pipe
5207c478bd9Sstevel@tonic-gate 	 * is polling and the USBA is ready to receive the
5217c478bd9Sstevel@tonic-gate 	 * data. If not, we could panic.
5227c478bd9Sstevel@tonic-gate 	 */
5237c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_root_hub.rh_pipe_state != UHCI_PIPE_STATE_ACTIVE) {
5247c478bd9Sstevel@tonic-gate 		/* make a note that we need to send status back */
5257c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_status = port + 1;
5267c478bd9Sstevel@tonic-gate 	} else {
5277c478bd9Sstevel@tonic-gate 		uhci_root_hub_reset_occurred(uhcip, port);
5287c478bd9Sstevel@tonic-gate 	}
5297c478bd9Sstevel@tonic-gate }
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate /*
5337c478bd9Sstevel@tonic-gate  * uhci_handle_complete_port_reset:
5347c478bd9Sstevel@tonic-gate  *	Perform a port reset change.
5357c478bd9Sstevel@tonic-gate  */
5367c478bd9Sstevel@tonic-gate static void
uhci_handle_complete_port_reset(uhci_state_t * uhcip,usb_port_t port)5377c478bd9Sstevel@tonic-gate uhci_handle_complete_port_reset(
5387c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
5397c478bd9Sstevel@tonic-gate 	usb_port_t		port)
5407c478bd9Sstevel@tonic-gate {
5417c478bd9Sstevel@tonic-gate 	uint_t port_status = Get_OpReg16(PORTSC[port]);
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
5447c478bd9Sstevel@tonic-gate 	    "uhci_handle_complete_port_reset: port = 0x%x status = 0x%x",
5457c478bd9Sstevel@tonic-gate 	    port, port_status);
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 	if (!(port_status & HCR_PORT_CCS)) {
5487c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
5497c478bd9Sstevel@tonic-gate 		    "port_status & HCR_PORT_CCS == 0: "
5507c478bd9Sstevel@tonic-gate 		    "port = 0x%x, status = 0x%x", port, port_status);
5517c478bd9Sstevel@tonic-gate 	}
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 	Set_OpReg16(PORTSC[port], (port_status & (~ HCR_PORT_RESET)));
5547c478bd9Sstevel@tonic-gate 
5557c478bd9Sstevel@tonic-gate 	/* Update software port_changes register */
5567c478bd9Sstevel@tonic-gate 	uhcip->uhci_root_hub.rh_port_changes[port] &= ~PORT_CHANGE_PRSC;
5577c478bd9Sstevel@tonic-gate }
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate /*
5617c478bd9Sstevel@tonic-gate  * uhci_handle_clear_port_connection:
5627c478bd9Sstevel@tonic-gate  *	Perform a clear port connection.
5637c478bd9Sstevel@tonic-gate  */
5647c478bd9Sstevel@tonic-gate static void
uhci_handle_clear_port_connection(uhci_state_t * uhcip,usb_port_t port)5657c478bd9Sstevel@tonic-gate uhci_handle_clear_port_connection(
5667c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
5677c478bd9Sstevel@tonic-gate 	usb_port_t		port)
5687c478bd9Sstevel@tonic-gate {
5697c478bd9Sstevel@tonic-gate 	uint_t port_status = Get_OpReg16(PORTSC[port]);
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
5727c478bd9Sstevel@tonic-gate 	    "uhci_handle_clear_port_connection: port = 0x%x status = 0x%x",
5737c478bd9Sstevel@tonic-gate 	    port, port_status);
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 	/* Clear CSC bit */
5767c478bd9Sstevel@tonic-gate 	Set_OpReg16(PORTSC[port], port_status | HCR_PORT_CSC);
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	/* Update software port_changes register */
5797c478bd9Sstevel@tonic-gate 	uhcip->uhci_root_hub.rh_port_changes[port] &= ~PORT_CHANGE_CSC;
5807c478bd9Sstevel@tonic-gate }
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate /*
5847c478bd9Sstevel@tonic-gate  * uhci_handle_get_port_status:
5857c478bd9Sstevel@tonic-gate  *	Handle a get port status request.
5867c478bd9Sstevel@tonic-gate  */
5877c478bd9Sstevel@tonic-gate static void
uhci_handle_get_port_status(uhci_state_t * uhcip,usb_ctrl_req_t * req,usb_port_t port)5887c478bd9Sstevel@tonic-gate uhci_handle_get_port_status(
5897c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
5907c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*req,
5917c478bd9Sstevel@tonic-gate 	usb_port_t		port)
5927c478bd9Sstevel@tonic-gate {
5937c478bd9Sstevel@tonic-gate 	uint_t		new_port_status;
5947c478bd9Sstevel@tonic-gate 	uint_t		old_port_status =
595112116d8Sfb 	    uhcip->uhci_root_hub.rh_port_status[port];
5967c478bd9Sstevel@tonic-gate 	uint_t		old_port_changes =
597112116d8Sfb 	    uhcip->uhci_root_hub.rh_port_changes[port];
5987c478bd9Sstevel@tonic-gate 	uint_t		change_status;
5997c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t	*ctrl_reqp = (usb_ctrl_req_t *)req;
6007c478bd9Sstevel@tonic-gate 	uint16_t	wLength = req->ctrl_wLength;
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 	ASSERT(wLength == 4);
6037c478bd9Sstevel@tonic-gate 	ASSERT(ctrl_reqp->ctrl_data != NULL);
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 	/* Read the current port status and return it */
6067c478bd9Sstevel@tonic-gate 	new_port_status = uhci_get_port_status(uhcip, port);
6077c478bd9Sstevel@tonic-gate 	change_status	= (old_port_status ^ new_port_status) & 0xff;
6087c478bd9Sstevel@tonic-gate 	change_status	|= old_port_changes;
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
6117c478bd9Sstevel@tonic-gate 	    "uhci_handle_get_port_status:\n\t"
6127c478bd9Sstevel@tonic-gate 	    "port%d: old status = 0x%x	new status = 0x%x change = 0x%x",
6137c478bd9Sstevel@tonic-gate 	    port, old_port_status, new_port_status, change_status);
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	*ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)new_port_status;
6167c478bd9Sstevel@tonic-gate 	*ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)(new_port_status >> 8);
6177c478bd9Sstevel@tonic-gate 	*ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)change_status;
6187c478bd9Sstevel@tonic-gate 	*ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)(change_status >> 8);
6197c478bd9Sstevel@tonic-gate 
6207c478bd9Sstevel@tonic-gate 	/* Update the status */
6217c478bd9Sstevel@tonic-gate 	uhcip->uhci_root_hub.rh_port_status[port] = new_port_status;
6227c478bd9Sstevel@tonic-gate 	uhcip->uhci_root_hub.rh_port_changes[port] = change_status;
6237c478bd9Sstevel@tonic-gate }
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate /*
6277c478bd9Sstevel@tonic-gate  * uhci_handle_get_hub_descriptor:
6287c478bd9Sstevel@tonic-gate  */
6297c478bd9Sstevel@tonic-gate static void
uhci_handle_get_hub_descriptor(uhci_state_t * uhcip,usb_ctrl_req_t * req)6307c478bd9Sstevel@tonic-gate uhci_handle_get_hub_descriptor(
6317c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
6327c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*req)
6337c478bd9Sstevel@tonic-gate {
6347c478bd9Sstevel@tonic-gate 	uchar_t		raw_descr[ROOT_HUB_DESCRIPTOR_LENGTH];
6357c478bd9Sstevel@tonic-gate 	usb_hub_descr_t	*root_hub_descr = &uhcip->uhci_root_hub.rh_descr;
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
6387c478bd9Sstevel@tonic-gate 	    "uhci_handle_get_hub_descriptor: wLength = 0x%x",
6397c478bd9Sstevel@tonic-gate 	    req->ctrl_wLength);
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 	ASSERT(req->ctrl_wLength != 0);
6427c478bd9Sstevel@tonic-gate 	ASSERT(req->ctrl_data != NULL);
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate 	bzero(&raw_descr, ROOT_HUB_DESCRIPTOR_LENGTH);
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 	raw_descr[0] = root_hub_descr->bDescLength;
6477c478bd9Sstevel@tonic-gate 	raw_descr[1] = root_hub_descr->bDescriptorType;
6487c478bd9Sstevel@tonic-gate 	raw_descr[2] = root_hub_descr->bNbrPorts;
6497c478bd9Sstevel@tonic-gate 	raw_descr[3] = root_hub_descr->wHubCharacteristics & 0x00ff;
6507c478bd9Sstevel@tonic-gate 	raw_descr[4] = (root_hub_descr->wHubCharacteristics & 0xff00) >> 8;
6517c478bd9Sstevel@tonic-gate 	raw_descr[5] = root_hub_descr->bPwrOn2PwrGood;
6527c478bd9Sstevel@tonic-gate 	raw_descr[6] = root_hub_descr->bHubContrCurrent;
6537c478bd9Sstevel@tonic-gate 	raw_descr[7] = root_hub_descr->DeviceRemovable;
6547c478bd9Sstevel@tonic-gate 	raw_descr[8] = root_hub_descr->PortPwrCtrlMask;
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 	bcopy(raw_descr, req->ctrl_data->b_wptr, req->ctrl_wLength);
6577c478bd9Sstevel@tonic-gate 	req->ctrl_data->b_wptr += req->ctrl_wLength;
6587c478bd9Sstevel@tonic-gate }
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate /*
6627c478bd9Sstevel@tonic-gate  * uhci_handle_get_hub_status:
6637c478bd9Sstevel@tonic-gate  */
6647c478bd9Sstevel@tonic-gate static void
uhci_handle_get_hub_status(uhci_state_t * uhcip,usb_ctrl_req_t * req)6657c478bd9Sstevel@tonic-gate uhci_handle_get_hub_status(
6667c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
6677c478bd9Sstevel@tonic-gate 	usb_ctrl_req_t		*req)
6687c478bd9Sstevel@tonic-gate {
6697c478bd9Sstevel@tonic-gate 
6707c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
6717c478bd9Sstevel@tonic-gate 	    "uhci_handle_get_hub_status: wLength = 0x%x",
672112116d8Sfb 	    req->ctrl_wLength);
6737c478bd9Sstevel@tonic-gate 	ASSERT(req->ctrl_wLength != 0);
6747c478bd9Sstevel@tonic-gate 	ASSERT(req->ctrl_data != NULL);
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 	/*
6777c478bd9Sstevel@tonic-gate 	 * A good status is always sent because there is no way that
6787c478bd9Sstevel@tonic-gate 	 * the driver can get to know about the status change of the
6797c478bd9Sstevel@tonic-gate 	 * over current or power failure of the root hub from the HC.
6807c478bd9Sstevel@tonic-gate 	 */
6817c478bd9Sstevel@tonic-gate 	bzero(req->ctrl_data->b_wptr, req->ctrl_wLength);
6827c478bd9Sstevel@tonic-gate 	req->ctrl_data->b_wptr += req->ctrl_wLength;
6837c478bd9Sstevel@tonic-gate }
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate 
68635f36846Ssl /*
68735f36846Ssl  * uhci_handle_get_device_status:
68835f36846Ssl  */
68935f36846Ssl static void
uhci_handle_get_device_status(uhci_state_t * uhcip,usb_ctrl_req_t * req)69035f36846Ssl uhci_handle_get_device_status(
69135f36846Ssl 	uhci_state_t		*uhcip,
69235f36846Ssl 	usb_ctrl_req_t		*req)
69335f36846Ssl {
69435f36846Ssl 	uint16_t	dev_status;
69535f36846Ssl 
69635f36846Ssl 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
69735f36846Ssl 	    "uhci_handle_get_device_status: wLength = 0x%x",
69835f36846Ssl 	    req->ctrl_wLength);
69935f36846Ssl 
70035f36846Ssl 	ASSERT(req->ctrl_wLength != 0);
70135f36846Ssl 	ASSERT(req->ctrl_data != NULL);
70235f36846Ssl 
70335f36846Ssl 	/*
70435f36846Ssl 	 * UHCI doesn't have device status information.
70535f36846Ssl 	 * Simply return what is desired for the request.
70635f36846Ssl 	 */
70735f36846Ssl 	dev_status = USB_DEV_SLF_PWRD_STATUS;
70835f36846Ssl 
70935f36846Ssl 	*req->ctrl_data->b_wptr++ = (uchar_t)dev_status;
71035f36846Ssl 	*req->ctrl_data->b_wptr++ = (uchar_t)(dev_status >> 8);
71135f36846Ssl }
71235f36846Ssl 
71335f36846Ssl 
7147c478bd9Sstevel@tonic-gate /*
7157c478bd9Sstevel@tonic-gate  * uhci_handle_root_hub_status_change:
716*4f71203dSzl  *	This function is called every 256 ms from the time out handler.
7177c478bd9Sstevel@tonic-gate  *	It checks for the status change of the root hub and its ports.
7187c478bd9Sstevel@tonic-gate  */
7197c478bd9Sstevel@tonic-gate void
uhci_handle_root_hub_status_change(void * arg)7207c478bd9Sstevel@tonic-gate uhci_handle_root_hub_status_change(void *arg)
7217c478bd9Sstevel@tonic-gate {
7227c478bd9Sstevel@tonic-gate 	usb_port_t	port;
7237c478bd9Sstevel@tonic-gate 	uint_t		old_port_status;
7247c478bd9Sstevel@tonic-gate 	uint_t		new_port_status;
7257c478bd9Sstevel@tonic-gate 	ushort_t	port_status;
7267c478bd9Sstevel@tonic-gate 	uint_t		change_status;
7277c478bd9Sstevel@tonic-gate 	uchar_t		all_ports_status = 0;
7287c478bd9Sstevel@tonic-gate 	uhci_state_t	*uhcip = (uhci_state_t *)arg;
7297c478bd9Sstevel@tonic-gate 	usb_intr_req_t	*curr_intr_reqp;
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
7327c478bd9Sstevel@tonic-gate 
7337c478bd9Sstevel@tonic-gate 	/* reset the timeout id */
7347c478bd9Sstevel@tonic-gate 	uhcip->uhci_timeout_id = 0;
7357c478bd9Sstevel@tonic-gate 
7367c478bd9Sstevel@tonic-gate 	/* Get the current interrupt request pointer */
7377c478bd9Sstevel@tonic-gate 	curr_intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp;
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate 	/* Check each port */
7407c478bd9Sstevel@tonic-gate 	for (port = 0; port < uhcip->uhci_root_hub.rh_num_ports; port++) {
7417c478bd9Sstevel@tonic-gate 		new_port_status = uhci_get_port_status(uhcip, port);
7427c478bd9Sstevel@tonic-gate 		old_port_status = uhcip->uhci_root_hub.rh_port_status[port];
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 		change_status = (old_port_status ^ new_port_status) & 0xff;
7457c478bd9Sstevel@tonic-gate 		change_status |= uhcip->uhci_root_hub.rh_port_changes[port];
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate 		/* See if a device was attached/detached */
7487c478bd9Sstevel@tonic-gate 		if (change_status & PORT_STATUS_CCS) {
7497c478bd9Sstevel@tonic-gate 			all_ports_status |= 1 << (port + 1);
7507c478bd9Sstevel@tonic-gate 		}
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 		port_status = Get_OpReg16(PORTSC[port]);
7537c478bd9Sstevel@tonic-gate 		Set_OpReg16(PORTSC[port], port_status | HCR_PORT_ENDIS_CHG);
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_port_status[port] = new_port_status;
7567c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_port_changes[port] = change_status;
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
7597c478bd9Sstevel@tonic-gate 		    "port %d old status 0x%x new status 0x%x change 0x%x\n\t"
7607c478bd9Sstevel@tonic-gate 		    "all_ports_status = 0x%x", port, old_port_status,
7617c478bd9Sstevel@tonic-gate 		    new_port_status, change_status, all_ports_status);
7627c478bd9Sstevel@tonic-gate 	}
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_root_hub.rh_intr_pipe_handle &&
7657c478bd9Sstevel@tonic-gate 	    all_ports_status && curr_intr_reqp &&
7667c478bd9Sstevel@tonic-gate 	    (uhcip->uhci_root_hub.rh_pipe_state == UHCI_PIPE_STATE_ACTIVE)) {
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 		ASSERT(curr_intr_reqp->intr_data != NULL);
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 		*curr_intr_reqp->intr_data->b_wptr++ = all_ports_status;
7717c478bd9Sstevel@tonic-gate 
7727c478bd9Sstevel@tonic-gate 		uhci_rh_hcdi_callback(uhcip,
7737c478bd9Sstevel@tonic-gate 		    uhcip->uhci_root_hub.rh_intr_pipe_handle,
7747c478bd9Sstevel@tonic-gate 		    (usb_opaque_t)curr_intr_reqp, USB_CR_OK);
7757c478bd9Sstevel@tonic-gate 	}
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_root_hub.rh_pipe_state == UHCI_PIPE_STATE_ACTIVE) {
7787c478bd9Sstevel@tonic-gate 		/*
7797c478bd9Sstevel@tonic-gate 		 * If needed, allocate new interrupt request. Also
7807c478bd9Sstevel@tonic-gate 		 * start the timer for the root hub interrupt polling.
7817c478bd9Sstevel@tonic-gate 		 */
7827c478bd9Sstevel@tonic-gate 		if (uhci_root_hub_allocate_intr_pipe_resource(uhcip, 0) !=
7837c478bd9Sstevel@tonic-gate 		    USB_SUCCESS) {
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate 			/* Do interrupt pipe cleanup */
7867c478bd9Sstevel@tonic-gate 			uhci_root_hub_intr_pipe_cleanup(uhcip,
787112116d8Sfb 			    USB_CR_NO_RESOURCES);
7887c478bd9Sstevel@tonic-gate 		}
7897c478bd9Sstevel@tonic-gate 	}
7907c478bd9Sstevel@tonic-gate 
7917c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
7927c478bd9Sstevel@tonic-gate }
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 
7957c478bd9Sstevel@tonic-gate static uint_t
uhci_get_port_status(uhci_state_t * uhcip,usb_port_t port)7967c478bd9Sstevel@tonic-gate uhci_get_port_status(
7977c478bd9Sstevel@tonic-gate 	uhci_state_t	*uhcip,
7987c478bd9Sstevel@tonic-gate 	usb_port_t	port)
7997c478bd9Sstevel@tonic-gate {
8007c478bd9Sstevel@tonic-gate 	uint_t		new_port_status = PORT_STATUS_PPS;
8017c478bd9Sstevel@tonic-gate 	ushort_t	port_status = Get_OpReg16(PORTSC[port]);
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 	if (port_status & HCR_PORT_CCS) {
8047c478bd9Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_CCS;
8057c478bd9Sstevel@tonic-gate 	}
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 	if (port_status & HCR_PORT_LSDA) {
8087c478bd9Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_LSDA;
8097c478bd9Sstevel@tonic-gate 	}
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate 	if (port_status & HCR_PORT_ENABLE) {
8127c478bd9Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_PES;
8137c478bd9Sstevel@tonic-gate 	}
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate 	if (port_status & HCR_PORT_SUSPEND) {
8167c478bd9Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_PSS;
8177c478bd9Sstevel@tonic-gate 	}
8187c478bd9Sstevel@tonic-gate 
8197c478bd9Sstevel@tonic-gate 	if (port_status & HCR_PORT_RESET) {
8207c478bd9Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_PRS;
8217c478bd9Sstevel@tonic-gate 	}
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate 	return (new_port_status);
8247c478bd9Sstevel@tonic-gate }
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate /*
8287c478bd9Sstevel@tonic-gate  * uhci_root_hub_allocate_intr_pipe_resource:
8297c478bd9Sstevel@tonic-gate  *	Allocate interrupt requests and initialize them.
8307c478bd9Sstevel@tonic-gate  */
8317c478bd9Sstevel@tonic-gate int
uhci_root_hub_allocate_intr_pipe_resource(uhci_state_t * uhcip,usb_flags_t flags)8327c478bd9Sstevel@tonic-gate uhci_root_hub_allocate_intr_pipe_resource(
8337c478bd9Sstevel@tonic-gate 	uhci_state_t	*uhcip,
8347c478bd9Sstevel@tonic-gate 	usb_flags_t	flags)
8357c478bd9Sstevel@tonic-gate {
8367c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp;
8377c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
8407c478bd9Sstevel@tonic-gate 	    "uhci_root_hub_allocate_intr_pipe_resource:");
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 	/* Get the interrupt pipe handle */
8457c478bd9Sstevel@tonic-gate 	ph = uhcip->uhci_root_hub.rh_intr_pipe_handle;
8467c478bd9Sstevel@tonic-gate 
8477c478bd9Sstevel@tonic-gate 	/* Get the current interrupt request pointer */
8487c478bd9Sstevel@tonic-gate 	curr_intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp;
8497c478bd9Sstevel@tonic-gate 
8507c478bd9Sstevel@tonic-gate 	/*
8517c478bd9Sstevel@tonic-gate 	 * If current interrupt request pointer is null,
8527c478bd9Sstevel@tonic-gate 	 * allocate new interrupt request.
8537c478bd9Sstevel@tonic-gate 	 */
8547c478bd9Sstevel@tonic-gate 	if (curr_intr_reqp == NULL) {
8557c478bd9Sstevel@tonic-gate 		ASSERT(uhcip->uhci_root_hub.rh_client_intr_req);
8567c478bd9Sstevel@tonic-gate 
8577c478bd9Sstevel@tonic-gate 		if ((curr_intr_reqp = usba_hcdi_dup_intr_req(ph->p_dip,
8587c478bd9Sstevel@tonic-gate 		    uhcip->uhci_root_hub.rh_client_intr_req,
8597c478bd9Sstevel@tonic-gate 		    uhcip->uhci_root_hub.rh_client_intr_req->intr_len,
8607c478bd9Sstevel@tonic-gate 		    flags)) == NULL) {
8617c478bd9Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
8627c478bd9Sstevel@tonic-gate 			    "uhci_root_hub_allocate_intr_pipe_resource:"
8637c478bd9Sstevel@tonic-gate 			    "Interrupt request structure allocation failed");
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate 			return (USB_NO_RESOURCES);
8667c478bd9Sstevel@tonic-gate 		}
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_curr_intr_reqp = curr_intr_reqp;
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
8717c478bd9Sstevel@tonic-gate 		ph->p_req_count++;
8727c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
8737c478bd9Sstevel@tonic-gate 	}
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	if (uhcip->uhci_timeout_id == 0) {
8767c478bd9Sstevel@tonic-gate 		uhcip->uhci_timeout_id = timeout(
877112116d8Sfb 		    uhci_handle_root_hub_status_change,
878*4f71203dSzl 		    (void *)uhcip, UHCI_256_MS);
8797c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_pipe_state =
880112116d8Sfb 		    UHCI_PIPE_STATE_ACTIVE;
8817c478bd9Sstevel@tonic-gate 	}
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate 	return (USB_SUCCESS);
8847c478bd9Sstevel@tonic-gate }
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate /*
8887c478bd9Sstevel@tonic-gate  * uhci_root_hub_intr_pipe_cleanup:
8897c478bd9Sstevel@tonic-gate  *	Deallocate all interrupt requests and do callback
8907c478bd9Sstevel@tonic-gate  *	the original client interrupt request.
8917c478bd9Sstevel@tonic-gate  */
8927c478bd9Sstevel@tonic-gate void
uhci_root_hub_intr_pipe_cleanup(uhci_state_t * uhcip,usb_cr_t cr)8937c478bd9Sstevel@tonic-gate uhci_root_hub_intr_pipe_cleanup(uhci_state_t *uhcip, usb_cr_t cr)
8947c478bd9Sstevel@tonic-gate {
8957c478bd9Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp;
8967c478bd9Sstevel@tonic-gate 	usb_opaque_t		client_intr_reqp;
8977c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
8987c478bd9Sstevel@tonic-gate 	timeout_id_t		timer_id;
8997c478bd9Sstevel@tonic-gate 
9007c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
9017c478bd9Sstevel@tonic-gate 	    "uhci_root_hub_intr_pipe_cleanup:");
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 	/* Get the interrupt pipe handle */
9067c478bd9Sstevel@tonic-gate 	ph = uhcip->uhci_root_hub.rh_intr_pipe_handle;
9077c478bd9Sstevel@tonic-gate 
9087c478bd9Sstevel@tonic-gate 	/* Get the interrupt timerid */
9097c478bd9Sstevel@tonic-gate 	timer_id = uhcip->uhci_timeout_id;
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate 	/* Stop the root hub interrupt timer */
9127c478bd9Sstevel@tonic-gate 	if (timer_id) {
9137c478bd9Sstevel@tonic-gate 
9147c478bd9Sstevel@tonic-gate 		/* Reset the timer id to zero */
9157c478bd9Sstevel@tonic-gate 		uhcip->uhci_timeout_id = 0;
9167c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_pipe_state =
917112116d8Sfb 		    UHCI_PIPE_STATE_IDLE;
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 		mutex_exit(&uhcip->uhci_int_mutex);
9207c478bd9Sstevel@tonic-gate 		(void) untimeout(timer_id);
9217c478bd9Sstevel@tonic-gate 		mutex_enter(&uhcip->uhci_int_mutex);
9227c478bd9Sstevel@tonic-gate 	}
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate 	/* Reset the current interrupt request pointer */
9257c478bd9Sstevel@tonic-gate 	curr_intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp;
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate 	/* Deallocate uncompleted interrupt request */
9287c478bd9Sstevel@tonic-gate 	if (curr_intr_reqp) {
9297c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_curr_intr_reqp = NULL;
9307c478bd9Sstevel@tonic-gate 		usb_free_intr_req(curr_intr_reqp);
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
9337c478bd9Sstevel@tonic-gate 		ph->p_req_count--;
9347c478bd9Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
9357c478bd9Sstevel@tonic-gate 	}
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 	client_intr_reqp = (usb_opaque_t)
9387c478bd9Sstevel@tonic-gate 	    uhcip->uhci_root_hub.rh_client_intr_req;
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate 	/* Callback for original client interrupt request */
9417c478bd9Sstevel@tonic-gate 	if (client_intr_reqp) {
9427c478bd9Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_client_intr_req = NULL;
9437c478bd9Sstevel@tonic-gate 		uhci_rh_hcdi_callback(uhcip, ph,
9447c478bd9Sstevel@tonic-gate 		    (usb_opaque_t)client_intr_reqp, cr);
9457c478bd9Sstevel@tonic-gate 	}
9467c478bd9Sstevel@tonic-gate }
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 
9497c478bd9Sstevel@tonic-gate /*
9507c478bd9Sstevel@tonic-gate  * uhci_rh_hcdi_callback:
9517c478bd9Sstevel@tonic-gate  *	Convenience wrapper around usba_hcdi_cb() for the root hub.
9527c478bd9Sstevel@tonic-gate  */
9537c478bd9Sstevel@tonic-gate static void
uhci_rh_hcdi_callback(uhci_state_t * uhcip,usba_pipe_handle_data_t * ph,usb_opaque_t req,usb_cr_t cr)9547c478bd9Sstevel@tonic-gate uhci_rh_hcdi_callback(
9557c478bd9Sstevel@tonic-gate 	uhci_state_t		*uhcip,
9567c478bd9Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
9577c478bd9Sstevel@tonic-gate 	usb_opaque_t		req,
9587c478bd9Sstevel@tonic-gate 	usb_cr_t		cr)
9597c478bd9Sstevel@tonic-gate {
9607c478bd9Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
9617c478bd9Sstevel@tonic-gate 	    "uhci_rh_hcdi_callback: ph=0x%p cr=0x%x req=0x%p",
962112116d8Sfb 	    (void *)ph, cr, (void *)req);
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
9657c478bd9Sstevel@tonic-gate 
9667c478bd9Sstevel@tonic-gate 	switch (UHCI_XFER_TYPE(&ph->p_ep)) {
9677c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate 		break;
9707c478bd9Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
9717c478bd9Sstevel@tonic-gate 		if ((usb_intr_req_t *)req ==
9727c478bd9Sstevel@tonic-gate 		    uhcip->uhci_root_hub.rh_curr_intr_reqp) {
9737c478bd9Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_curr_intr_reqp = NULL;
9747c478bd9Sstevel@tonic-gate 
9757c478bd9Sstevel@tonic-gate 			break;
9767c478bd9Sstevel@tonic-gate 		} else if ((usb_intr_req_t *)req ==
9777c478bd9Sstevel@tonic-gate 		    uhcip->uhci_root_hub.rh_client_intr_req) {
9787c478bd9Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_client_intr_req = NULL;
9797c478bd9Sstevel@tonic-gate 
9807c478bd9Sstevel@tonic-gate 			break;
9817c478bd9Sstevel@tonic-gate 		}
9827c478bd9Sstevel@tonic-gate 		/*FALLTHRU*/
9837c478bd9Sstevel@tonic-gate 	default:
9847c478bd9Sstevel@tonic-gate 		ASSERT(req);
9857c478bd9Sstevel@tonic-gate 		break;
9867c478bd9Sstevel@tonic-gate 	}
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
9897c478bd9Sstevel@tonic-gate 	usba_hcdi_cb(ph, req, cr);
9907c478bd9Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
9917c478bd9Sstevel@tonic-gate }
992