xref: /onnv-gate/usr/src/uts/common/io/usb/hcd/uhci/uhcihub.c (revision 7425:e4dbffd35ebc)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
56898Sfb209375  * Common Development and Distribution License (the "License").
66898Sfb209375  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
226898Sfb209375  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate  * Universal Serial BUS  Host Controller Driver (UHCI)
290Sstevel@tonic-gate  *
300Sstevel@tonic-gate  * The UHCI driver is a driver which interfaces to the Universal
310Sstevel@tonic-gate  * Serial Bus Architecture (USBA) and the Host Controller (HC). The interface to
320Sstevel@tonic-gate  * the Host Controller is defined by the Universal Host Controller Interface.
330Sstevel@tonic-gate  * This file contains the code for root hub related functions.
340Sstevel@tonic-gate  */
350Sstevel@tonic-gate #include <sys/usb/hcd/uhci/uhcid.h>
360Sstevel@tonic-gate #include <sys/usb/hcd/uhci/uhci.h>
370Sstevel@tonic-gate #include <sys/usb/hcd/uhci/uhcihub.h>
380Sstevel@tonic-gate 
390Sstevel@tonic-gate /*
400Sstevel@tonic-gate  *  Function Prototypes
410Sstevel@tonic-gate  */
420Sstevel@tonic-gate static int	uhci_handle_set_clear_port_feature(
430Sstevel@tonic-gate 			uhci_state_t		*uhcip,
440Sstevel@tonic-gate 			uchar_t			bRequest,
450Sstevel@tonic-gate 			uint16_t		wValue,
460Sstevel@tonic-gate 			usb_port_t		port);
470Sstevel@tonic-gate static	void	uhci_handle_port_power(
480Sstevel@tonic-gate 			uhci_state_t		*uhcip,
490Sstevel@tonic-gate 			usb_port_t		port,
500Sstevel@tonic-gate 			uint_t			on);
510Sstevel@tonic-gate static	void	uhci_handle_port_suspend(
520Sstevel@tonic-gate 			uhci_state_t		*uhcip,
530Sstevel@tonic-gate 			usb_port_t		port,
540Sstevel@tonic-gate 			uint_t			on);
550Sstevel@tonic-gate static	void	uhci_handle_port_enable_disable(
560Sstevel@tonic-gate 			uhci_state_t		*uhcip,
570Sstevel@tonic-gate 			usb_port_t		port,
580Sstevel@tonic-gate 			uint_t			on);
590Sstevel@tonic-gate static	void	uhci_handle_port_reset(
600Sstevel@tonic-gate 			uhci_state_t		*uhcip,
610Sstevel@tonic-gate 			usb_port_t		port);
620Sstevel@tonic-gate static	void	uhci_handle_complete_port_reset(
630Sstevel@tonic-gate 			uhci_state_t		*uhcip,
640Sstevel@tonic-gate 			usb_port_t		port);
650Sstevel@tonic-gate static	void	uhci_handle_clear_port_connection(
660Sstevel@tonic-gate 			uhci_state_t		*uhcip,
670Sstevel@tonic-gate 			usb_port_t		port);
680Sstevel@tonic-gate static	void	uhci_handle_get_port_status(
690Sstevel@tonic-gate 			uhci_state_t		*uhcip,
700Sstevel@tonic-gate 			usb_ctrl_req_t		*req,
710Sstevel@tonic-gate 			usb_port_t		port);
720Sstevel@tonic-gate static	void	uhci_handle_get_hub_descriptor(
730Sstevel@tonic-gate 			uhci_state_t		*uhcip,
740Sstevel@tonic-gate 			usb_ctrl_req_t		*req);
750Sstevel@tonic-gate static void	uhci_handle_get_hub_status(
760Sstevel@tonic-gate 			uhci_state_t		*uhcip,
770Sstevel@tonic-gate 			usb_ctrl_req_t		*req);
781001Ssl147100 static void	uhci_handle_get_device_status(
791001Ssl147100 			uhci_state_t		*uhcip,
801001Ssl147100 			usb_ctrl_req_t		*req);
810Sstevel@tonic-gate static uint_t	uhci_get_port_status(
820Sstevel@tonic-gate 			uhci_state_t		*uhcip,
830Sstevel@tonic-gate 			usb_port_t		port);
840Sstevel@tonic-gate static	void	uhci_rh_hcdi_callback(
850Sstevel@tonic-gate 			uhci_state_t		*uhcip,
860Sstevel@tonic-gate 			usba_pipe_handle_data_t	*ph,
870Sstevel@tonic-gate 			usb_opaque_t		req,
880Sstevel@tonic-gate 			usb_cr_t		cr);
890Sstevel@tonic-gate 
900Sstevel@tonic-gate /*
910Sstevel@tonic-gate  * root hub device descriptor
920Sstevel@tonic-gate  */
930Sstevel@tonic-gate static usb_dev_descr_t uhci_rh_dev_descr = {
940Sstevel@tonic-gate 	0x12,	/* Length */
950Sstevel@tonic-gate 	1,	/* Type */
960Sstevel@tonic-gate 	0x110,	/* BCD - v1.1 */
970Sstevel@tonic-gate 	9,	/* Class */
980Sstevel@tonic-gate 	0,	/* Sub class */
990Sstevel@tonic-gate 	0,	/* Protocol */
1000Sstevel@tonic-gate 	8,	/* Max pkt size */
1010Sstevel@tonic-gate 	0,	/* Vendor */
1020Sstevel@tonic-gate 	0,	/* Product id */
1030Sstevel@tonic-gate 	0,	/* Device release */
1040Sstevel@tonic-gate 	0,	/* Manufacturer */
1050Sstevel@tonic-gate 	0,	/* Product */
1060Sstevel@tonic-gate 	0,	/* Sn */
1070Sstevel@tonic-gate 	1	/* No of configs */
1080Sstevel@tonic-gate };
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate /*
1110Sstevel@tonic-gate  * root hub config descriptor
1120Sstevel@tonic-gate  */
1130Sstevel@tonic-gate static uchar_t uhci_rh_config_descr[] = {
1140Sstevel@tonic-gate 	/* config descriptor */
1150Sstevel@tonic-gate 	0x09,		/* bLength */
1160Sstevel@tonic-gate 	0x02,		/* bDescriptorType, Configuration */
1170Sstevel@tonic-gate 	0x19, 0x00,	/* wTotalLength */
1180Sstevel@tonic-gate 	0x01,		/* bNumInterfaces */
1190Sstevel@tonic-gate 	0x01,		/* bConfigurationValue */
1200Sstevel@tonic-gate 	0x00,		/* iConfiguration */
1210Sstevel@tonic-gate 	0x40,		/* bmAttributes */
1220Sstevel@tonic-gate 	0x00,		/* MaxPower */
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate 	/* interface descriptor */
1250Sstevel@tonic-gate 	0x09,		/* bLength */
1260Sstevel@tonic-gate 	0x04,		/* bDescriptorType, Interface */
1270Sstevel@tonic-gate 	0x00,		/* bInterfaceNumber */
1280Sstevel@tonic-gate 	0x00,		/* bAlternateSetting */
1290Sstevel@tonic-gate 	0x01,		/* bNumEndpoints */
1300Sstevel@tonic-gate 	0x09,		/* bInterfaceClass */
1310Sstevel@tonic-gate 	0x01,		/* bInterfaceSubClass */
1320Sstevel@tonic-gate 	0x00,		/* bInterfaceProtocol */
1330Sstevel@tonic-gate 	0x00,		/* iInterface */
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 	/* endpoint descriptor */
1360Sstevel@tonic-gate 	0x07,		/* bLength */
1370Sstevel@tonic-gate 	0x05,		/* bDescriptorType, Endpoint */
1380Sstevel@tonic-gate 	0x81,		/* bEndpointAddress */
1390Sstevel@tonic-gate 	0x03,		/* bmAttributes */
1400Sstevel@tonic-gate 	0x01, 0x00,	/* wMaxPacketSize, 1 +	(OHCI_MAX_RH_PORTS / 8) */
1410Sstevel@tonic-gate 	0x20		/* bInterval */
1420Sstevel@tonic-gate };
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate /*
1460Sstevel@tonic-gate  * uhci_init_root_hub:
1470Sstevel@tonic-gate  *	Initialize the root hub
1480Sstevel@tonic-gate  */
1490Sstevel@tonic-gate int
uhci_init_root_hub(uhci_state_t * uhcip)1500Sstevel@tonic-gate uhci_init_root_hub(uhci_state_t *uhcip)
1510Sstevel@tonic-gate {
1520Sstevel@tonic-gate 	int		i, length;
1530Sstevel@tonic-gate 	usb_hub_descr_t	*root_hub_descr = &uhcip->uhci_root_hub.rh_descr;
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
1560Sstevel@tonic-gate 	    "uhci_init_root_hub:");
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate 	uhcip->uhci_root_hub.rh_num_ports = MAX_RH_PORTS;
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate 	/*
1610Sstevel@tonic-gate 	 * Build the hub descriptor
1620Sstevel@tonic-gate 	 */
1630Sstevel@tonic-gate 	root_hub_descr->bDescriptorType = ROOT_HUB_DESCRIPTOR_TYPE;
1640Sstevel@tonic-gate 	root_hub_descr->bNbrPorts	= MAX_RH_PORTS;
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate 	length = root_hub_descr->bNbrPorts / 8;
1670Sstevel@tonic-gate 	if (length) {
1680Sstevel@tonic-gate 		root_hub_descr->bDescLength = 7 + (2 * (length + 1));
1690Sstevel@tonic-gate 	} else {
1700Sstevel@tonic-gate 		root_hub_descr->bDescLength = ROOT_HUB_DESCRIPTOR_LENGTH;
1710Sstevel@tonic-gate 	}
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	/* Determine the Power Switching Mode */
1740Sstevel@tonic-gate 	root_hub_descr->bPwrOn2PwrGood = 10; /* arbitrary number */
1750Sstevel@tonic-gate 	root_hub_descr->wHubCharacteristics =
1766898Sfb209375 	    HUB_CHARS_NO_POWER_SWITCHING|HUB_CHARS_NO_OVER_CURRENT;
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 	/* Indicate if the device is removable */
1790Sstevel@tonic-gate 	root_hub_descr->DeviceRemovable = 0x0;
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate 	/* Fill in the port power control mask */
1820Sstevel@tonic-gate 	root_hub_descr->PortPwrCtrlMask = 0xff;
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 	for (i = 0; i < uhcip->uhci_root_hub.rh_num_ports; i++) {
1850Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_port_state[i]  = DISCONNECTED;
1860Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_port_status[i] = 0;
1870Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_port_changes[i] = 0;
1880Sstevel@tonic-gate 	}
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate 	/* Finally load the root hub driver */
1910Sstevel@tonic-gate 	return (usba_hubdi_bind_root_hub(uhcip->uhci_dip, uhci_rh_config_descr,
1920Sstevel@tonic-gate 	    sizeof (uhci_rh_config_descr), &uhci_rh_dev_descr));
1930Sstevel@tonic-gate }
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate /*
1970Sstevel@tonic-gate  * uhci_handle_root_hub_request:
1980Sstevel@tonic-gate  *	Intercept a root hub request.
1990Sstevel@tonic-gate  *	Handle the  root hub request through the registers
2000Sstevel@tonic-gate  */
2010Sstevel@tonic-gate int
uhci_handle_root_hub_request(uhci_state_t * uhcip,usba_pipe_handle_data_t * pipe_handle,usb_ctrl_req_t * req)2020Sstevel@tonic-gate uhci_handle_root_hub_request(
2030Sstevel@tonic-gate 	uhci_state_t		*uhcip,
2040Sstevel@tonic-gate 	usba_pipe_handle_data_t  *pipe_handle,
2050Sstevel@tonic-gate 	usb_ctrl_req_t		*req)
2060Sstevel@tonic-gate {
2070Sstevel@tonic-gate 	int		error = USB_SUCCESS;
2080Sstevel@tonic-gate 	uint16_t	port = req->ctrl_wIndex - 1;
2090Sstevel@tonic-gate 	usb_cr_t	completion_reason;
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
2120Sstevel@tonic-gate 	    "uhci_handle_root_hub_request: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%p",
2130Sstevel@tonic-gate 	    req->ctrl_bmRequestType, req->ctrl_bRequest, req->ctrl_wValue,
2140Sstevel@tonic-gate 	    req->ctrl_wIndex, req->ctrl_wLength, (void *)req->ctrl_data);
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	switch (req->ctrl_bmRequestType) {
2191001Ssl147100 	case HUB_GET_DEVICE_STATUS_TYPE:
2201001Ssl147100 		uhci_handle_get_device_status(uhcip, req);
2211001Ssl147100 
2221001Ssl147100 		break;
2231001Ssl147100 	case HUB_HANDLE_PORT_FEATURE_TYPE:
2240Sstevel@tonic-gate 		error = uhci_handle_set_clear_port_feature(uhcip,
2250Sstevel@tonic-gate 		    req->ctrl_bRequest, req->ctrl_wValue, port);
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 		break;
2281001Ssl147100 	case HUB_GET_PORT_STATUS_TYPE:
2290Sstevel@tonic-gate 		uhci_handle_get_port_status(uhcip, req, port);
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate 		break;
2321001Ssl147100 	case HUB_CLASS_REQ_TYPE:
2330Sstevel@tonic-gate 		switch (req->ctrl_bRequest) {
2340Sstevel@tonic-gate 		case USB_REQ_GET_DESCR:
2350Sstevel@tonic-gate 			uhci_handle_get_hub_descriptor(uhcip, req);
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 			break;
2380Sstevel@tonic-gate 		case USB_REQ_GET_STATUS:
2390Sstevel@tonic-gate 			uhci_handle_get_hub_status(uhcip, req);
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate 			break;
2420Sstevel@tonic-gate 		default:
2430Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
2440Sstevel@tonic-gate 			    "uhci_handle_root_hub_request: Unsupported "
2450Sstevel@tonic-gate 			    "request 0x%x", req->ctrl_bmRequestType);
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 			break;
2480Sstevel@tonic-gate 		}
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 		break;
2510Sstevel@tonic-gate 	default:
2520Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
2530Sstevel@tonic-gate 		    "uhci_handle_root_hub_request: Unsupported request 0x%x",
2540Sstevel@tonic-gate 		    req->ctrl_bmRequestType);
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 		break;
2570Sstevel@tonic-gate 	}
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 	completion_reason = (error != USB_SUCCESS) ?
2606898Sfb209375 	    USB_CR_NOT_SUPPORTED : USB_CR_OK;
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
2630Sstevel@tonic-gate 	    "uhci_handle_root_hub_request: error = %d", error);
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	uhci_rh_hcdi_callback(uhcip, pipe_handle, (usb_opaque_t)req,
2666898Sfb209375 	    completion_reason);
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 	return (USB_SUCCESS);
2690Sstevel@tonic-gate }
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate /*
2730Sstevel@tonic-gate  * uhci_handle_set_clear_port_feature:
2740Sstevel@tonic-gate  */
2750Sstevel@tonic-gate static int
uhci_handle_set_clear_port_feature(uhci_state_t * uhcip,uchar_t bRequest,uint16_t wValue,usb_port_t port)2760Sstevel@tonic-gate uhci_handle_set_clear_port_feature(
2770Sstevel@tonic-gate 	uhci_state_t		*uhcip,
2780Sstevel@tonic-gate 	uchar_t			bRequest,
2790Sstevel@tonic-gate 	uint16_t		wValue,
2800Sstevel@tonic-gate 	usb_port_t		port)
2810Sstevel@tonic-gate {
2820Sstevel@tonic-gate 	int    error = USB_SUCCESS;
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
2850Sstevel@tonic-gate 	    "uhci_handle_set_clear_port_feature: 0x%x 0x%x 0x%x",
2860Sstevel@tonic-gate 	    bRequest, wValue, port);
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	switch (bRequest) {
2890Sstevel@tonic-gate 	case USB_REQ_SET_FEATURE:
2900Sstevel@tonic-gate 		switch (wValue) {
2910Sstevel@tonic-gate 		case CFS_PORT_ENABLE:
2920Sstevel@tonic-gate 			uhci_handle_port_enable_disable(uhcip,
2936898Sfb209375 			    port, UHCI_ENABLE_PORT);
2940Sstevel@tonic-gate 			break;
2950Sstevel@tonic-gate 		case CFS_PORT_SUSPEND:
2960Sstevel@tonic-gate 			uhci_handle_port_suspend(uhcip, port, 1);
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 			break;
2990Sstevel@tonic-gate 		case CFS_PORT_RESET:
3000Sstevel@tonic-gate 			uhci_handle_port_reset(uhcip, port);
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 			break;
3030Sstevel@tonic-gate 		case CFS_PORT_POWER:
3040Sstevel@tonic-gate 			uhci_handle_port_power(uhcip, port,
3056898Sfb209375 			    UHCI_ENABLE_PORT_PWR);
3060Sstevel@tonic-gate 			break;
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate 		default:
309978Sfrits 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
3100Sstevel@tonic-gate 			    "uhci_handle_set_clear_port_feature: "
3110Sstevel@tonic-gate 			    "Unsupported request 0x%x 0x%x", bRequest, wValue);
3120Sstevel@tonic-gate 			error = USB_FAILURE;
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 			break;
3150Sstevel@tonic-gate 		}
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 		break;
3180Sstevel@tonic-gate 	case USB_REQ_CLEAR_FEATURE:
3190Sstevel@tonic-gate 		switch (wValue) {
3200Sstevel@tonic-gate 		case CFS_PORT_ENABLE:
3210Sstevel@tonic-gate 			uhci_handle_port_enable_disable(uhcip,
3226898Sfb209375 			    port, UHCI_DISABLE_PORT);
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 			break;
3250Sstevel@tonic-gate 		case CFS_C_PORT_ENABLE:
3260Sstevel@tonic-gate 			uhci_handle_port_enable_disable(uhcip,
3276898Sfb209375 			    port, UHCI_CLEAR_ENDIS_BIT);
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 			break;
3300Sstevel@tonic-gate 		case CFS_PORT_SUSPEND:
3310Sstevel@tonic-gate 			uhci_handle_port_suspend(uhcip, port, 0);
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 			break;
3340Sstevel@tonic-gate 		case CFS_C_PORT_RESET:
3350Sstevel@tonic-gate 			uhci_handle_complete_port_reset(uhcip, port);
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate 			break;
3380Sstevel@tonic-gate 		case CFS_PORT_POWER:
3390Sstevel@tonic-gate 			uhci_handle_port_power(uhcip, port,
3406898Sfb209375 			    UHCI_DISABLE_PORT_PWR);
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 			break;
3430Sstevel@tonic-gate 		case CFS_C_PORT_CONNECTION:
3440Sstevel@tonic-gate 			uhci_handle_clear_port_connection(uhcip, port);
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 			break;
3470Sstevel@tonic-gate 		default:
348978Sfrits 			USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
3490Sstevel@tonic-gate 			    "uhci_handle_set_clear_port_feature: "
3500Sstevel@tonic-gate 			    "Unsupported request 0x%x 0x%x", bRequest, wValue);
3510Sstevel@tonic-gate 			error = USB_FAILURE;
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 			break;
3540Sstevel@tonic-gate 		}
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 		break;
3570Sstevel@tonic-gate 	default:
3580Sstevel@tonic-gate 		USB_DPRINTF_L2(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
3590Sstevel@tonic-gate 		    "uhci_handle_set_clear_port_feature: "
3600Sstevel@tonic-gate 		    "Unsupported request 0x%x 0x%x", bRequest, wValue);
3610Sstevel@tonic-gate 		error = USB_FAILURE;
3620Sstevel@tonic-gate 	}
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	return (error);
3660Sstevel@tonic-gate }
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate /*
3700Sstevel@tonic-gate  * uhci_handle_port_suspend:
3710Sstevel@tonic-gate  */
3720Sstevel@tonic-gate static void
uhci_handle_port_suspend(uhci_state_t * uhcip,usb_port_t port,uint_t on)3730Sstevel@tonic-gate uhci_handle_port_suspend(
3740Sstevel@tonic-gate 	uhci_state_t		*uhcip,
3750Sstevel@tonic-gate 	usb_port_t		port,
3760Sstevel@tonic-gate 	uint_t			on)
3770Sstevel@tonic-gate {
3780Sstevel@tonic-gate 	uint_t	port_status = Get_OpReg16(PORTSC[port]);
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
3810Sstevel@tonic-gate 	    "uhci_handle_port_suspend: port=%d on=%d",
3820Sstevel@tonic-gate 	    port, on);
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	if (on) {
3850Sstevel@tonic-gate 		/* See if the port suspend is already on */
3860Sstevel@tonic-gate 		if (!(port_status & HCR_PORT_SUSPEND)) {
3870Sstevel@tonic-gate 			/* suspend the port */
3880Sstevel@tonic-gate 			Set_OpReg16(PORTSC[port],
3896898Sfb209375 			    (port_status | HCR_PORT_SUSPEND));
3900Sstevel@tonic-gate 		}
3910Sstevel@tonic-gate 	} else {
3920Sstevel@tonic-gate 		/* See if the port suspend is already off */
3930Sstevel@tonic-gate 		if ((port_status & HCR_PORT_SUSPEND)) {
3940Sstevel@tonic-gate 			/* resume the port */
3950Sstevel@tonic-gate 			Set_OpReg16(PORTSC[port],
3966898Sfb209375 			    (port_status & ~HCR_PORT_SUSPEND));
3970Sstevel@tonic-gate 		}
3980Sstevel@tonic-gate 	}
3990Sstevel@tonic-gate }
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate /*
4030Sstevel@tonic-gate  * uhci_handle_port_power:
4040Sstevel@tonic-gate  *	Turn on a root hub port.  NOTE: Driver does not have any control
4050Sstevel@tonic-gate  *	over the power status.
4060Sstevel@tonic-gate  */
4070Sstevel@tonic-gate /* ARGSUSED */
4080Sstevel@tonic-gate static void
uhci_handle_port_power(uhci_state_t * uhcip,usb_port_t port,uint_t on)4090Sstevel@tonic-gate uhci_handle_port_power(
4100Sstevel@tonic-gate 	uhci_state_t		*uhcip,
4110Sstevel@tonic-gate 	usb_port_t		port,
4120Sstevel@tonic-gate 	uint_t			on)
4130Sstevel@tonic-gate {
4140Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
4150Sstevel@tonic-gate 	    "uhci_handle_port_power: nothing to do");
4160Sstevel@tonic-gate }
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate /*
4200Sstevel@tonic-gate  * uhci_handle_port_enable_disable:
4210Sstevel@tonic-gate  *	Handle port enable request.
4220Sstevel@tonic-gate  */
4230Sstevel@tonic-gate static void
uhci_handle_port_enable_disable(uhci_state_t * uhcip,usb_port_t port,uint_t action)4240Sstevel@tonic-gate uhci_handle_port_enable_disable(
4250Sstevel@tonic-gate 	uhci_state_t		*uhcip,
4260Sstevel@tonic-gate 	usb_port_t		port,
4270Sstevel@tonic-gate 	uint_t			action)
4280Sstevel@tonic-gate {
4290Sstevel@tonic-gate 	uint_t	port_status = Get_OpReg16(PORTSC[port]);
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
4320Sstevel@tonic-gate 	    "uhci_handle_port_enable: port = 0x%x, status = 0x%x",
4330Sstevel@tonic-gate 	    port, port_status);
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 	if (action == UHCI_ENABLE_PORT) {
4360Sstevel@tonic-gate 		/* See if the port enable is already on */
4370Sstevel@tonic-gate 		if (!(port_status & HCR_PORT_ENABLE)) {
4380Sstevel@tonic-gate 			/* Enable the port */
4390Sstevel@tonic-gate 			Set_OpReg16(PORTSC[port],
4406898Sfb209375 			    (port_status | HCR_PORT_ENABLE));
4410Sstevel@tonic-gate 		}
4420Sstevel@tonic-gate 	} else if (action == UHCI_DISABLE_PORT) {
4430Sstevel@tonic-gate 		/* See if the port enable is already off */
4440Sstevel@tonic-gate 		if ((port_status & HCR_PORT_ENABLE)) {
4450Sstevel@tonic-gate 			/* Disable the port */
4460Sstevel@tonic-gate 			Set_OpReg16(PORTSC[port],
4476898Sfb209375 			    (port_status & ~HCR_PORT_ENABLE));
4480Sstevel@tonic-gate 		}
4490Sstevel@tonic-gate 	} else {
4500Sstevel@tonic-gate 		/* Clear the Enable/Disable change bit */
4510Sstevel@tonic-gate 		Set_OpReg16(PORTSC[port], (port_status | HCR_PORT_ENDIS_CHG));
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 		/* Update software port_changes register */
4540Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_port_changes[port] &= ~PORT_CHANGE_PESC;
4550Sstevel@tonic-gate 	}
4560Sstevel@tonic-gate }
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate /*
4600Sstevel@tonic-gate  * uhci_root_hub_reset_occurred:
4610Sstevel@tonic-gate  *	Inform the upper layer that reset has occured on the port.
4620Sstevel@tonic-gate  *	This is required because the upper layer is expecting an
4630Sstevel@tonic-gate  *	event immediately after doing a reset. In case of OHCI
4640Sstevel@tonic-gate  *	the HC gets an interrupt for the change in the root hub
4650Sstevel@tonic-gate  *	status, but in case of UHCI we don't. So, we send an
4660Sstevel@tonic-gate  *	event to the upper layer as soon as we complete the reset
4670Sstevel@tonic-gate  *	as long as the root hub pipe is polling.
4680Sstevel@tonic-gate  */
4690Sstevel@tonic-gate void
uhci_root_hub_reset_occurred(uhci_state_t * uhcip,uint16_t port)4700Sstevel@tonic-gate uhci_root_hub_reset_occurred(
4710Sstevel@tonic-gate 	uhci_state_t	*uhcip,
4720Sstevel@tonic-gate 	uint16_t	port)
4730Sstevel@tonic-gate {
4740Sstevel@tonic-gate 	usb_intr_req_t	*intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp;
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
4770Sstevel@tonic-gate 	    "uhci_root_hub_reset_occurred: intr_reqp = 0x%p data = 0x%p",
4786898Sfb209375 	    (void *)intr_reqp, (void *)intr_reqp->intr_data);
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 	*intr_reqp->intr_data->b_wptr++ = (1 << (port+1));
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 	uhci_rh_hcdi_callback(uhcip, uhcip->uhci_root_hub.rh_intr_pipe_handle,
4830Sstevel@tonic-gate 	    (usb_opaque_t)intr_reqp, USB_CR_OK);
4840Sstevel@tonic-gate }
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate /*
4880Sstevel@tonic-gate  * uhci_handle_port_reset:
4890Sstevel@tonic-gate  *	Perform a port reset.
4900Sstevel@tonic-gate  */
4910Sstevel@tonic-gate static void
uhci_handle_port_reset(uhci_state_t * uhcip,usb_port_t port)4920Sstevel@tonic-gate uhci_handle_port_reset(
4930Sstevel@tonic-gate 	uhci_state_t		*uhcip,
4940Sstevel@tonic-gate 	usb_port_t		port)
4950Sstevel@tonic-gate {
4960Sstevel@tonic-gate 	uint_t	port_status = Get_OpReg16(PORTSC[port]);
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
4990Sstevel@tonic-gate 	    "uhci_handle_port_reset: port = 0x%x, status = 0x%x",
5000Sstevel@tonic-gate 	    port, port_status);
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	if (!(port_status & HCR_PORT_CCS)) {
5030Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
5040Sstevel@tonic-gate 		    "port_status & HCR_PORT_CCS == 0: "
5050Sstevel@tonic-gate 		    "port = 0x%x, status = 0x%x", port, port_status);
5060Sstevel@tonic-gate 	}
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	Set_OpReg16(PORTSC[port], (port_status| HCR_PORT_RESET));
5090Sstevel@tonic-gate 
5100Sstevel@tonic-gate 	drv_usecwait(UHCI_RESET_DELAY);
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 	Set_OpReg16(PORTSC[port], (port_status & ~HCR_PORT_RESET));
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate 	drv_usecwait(UHCI_RESET_DELAY/100);
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate 	Set_OpReg16(PORTSC[port], (port_status| HCR_PORT_ENABLE));
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 	/*
5190Sstevel@tonic-gate 	 * The next function is only called if the interrupt pipe
5200Sstevel@tonic-gate 	 * is polling and the USBA is ready to receive the
5210Sstevel@tonic-gate 	 * data. If not, we could panic.
5220Sstevel@tonic-gate 	 */
5230Sstevel@tonic-gate 	if (uhcip->uhci_root_hub.rh_pipe_state != UHCI_PIPE_STATE_ACTIVE) {
5240Sstevel@tonic-gate 		/* make a note that we need to send status back */
5250Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_status = port + 1;
5260Sstevel@tonic-gate 	} else {
5270Sstevel@tonic-gate 		uhci_root_hub_reset_occurred(uhcip, port);
5280Sstevel@tonic-gate 	}
5290Sstevel@tonic-gate }
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate /*
5330Sstevel@tonic-gate  * uhci_handle_complete_port_reset:
5340Sstevel@tonic-gate  *	Perform a port reset change.
5350Sstevel@tonic-gate  */
5360Sstevel@tonic-gate static void
uhci_handle_complete_port_reset(uhci_state_t * uhcip,usb_port_t port)5370Sstevel@tonic-gate uhci_handle_complete_port_reset(
5380Sstevel@tonic-gate 	uhci_state_t		*uhcip,
5390Sstevel@tonic-gate 	usb_port_t		port)
5400Sstevel@tonic-gate {
5410Sstevel@tonic-gate 	uint_t port_status = Get_OpReg16(PORTSC[port]);
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
5440Sstevel@tonic-gate 	    "uhci_handle_complete_port_reset: port = 0x%x status = 0x%x",
5450Sstevel@tonic-gate 	    port, port_status);
5460Sstevel@tonic-gate 
5470Sstevel@tonic-gate 	if (!(port_status & HCR_PORT_CCS)) {
5480Sstevel@tonic-gate 		USB_DPRINTF_L3(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
5490Sstevel@tonic-gate 		    "port_status & HCR_PORT_CCS == 0: "
5500Sstevel@tonic-gate 		    "port = 0x%x, status = 0x%x", port, port_status);
5510Sstevel@tonic-gate 	}
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate 	Set_OpReg16(PORTSC[port], (port_status & (~ HCR_PORT_RESET)));
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 	/* Update software port_changes register */
5560Sstevel@tonic-gate 	uhcip->uhci_root_hub.rh_port_changes[port] &= ~PORT_CHANGE_PRSC;
5570Sstevel@tonic-gate }
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate /*
5610Sstevel@tonic-gate  * uhci_handle_clear_port_connection:
5620Sstevel@tonic-gate  *	Perform a clear port connection.
5630Sstevel@tonic-gate  */
5640Sstevel@tonic-gate static void
uhci_handle_clear_port_connection(uhci_state_t * uhcip,usb_port_t port)5650Sstevel@tonic-gate uhci_handle_clear_port_connection(
5660Sstevel@tonic-gate 	uhci_state_t		*uhcip,
5670Sstevel@tonic-gate 	usb_port_t		port)
5680Sstevel@tonic-gate {
5690Sstevel@tonic-gate 	uint_t port_status = Get_OpReg16(PORTSC[port]);
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
5720Sstevel@tonic-gate 	    "uhci_handle_clear_port_connection: port = 0x%x status = 0x%x",
5730Sstevel@tonic-gate 	    port, port_status);
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 	/* Clear CSC bit */
5760Sstevel@tonic-gate 	Set_OpReg16(PORTSC[port], port_status | HCR_PORT_CSC);
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 	/* Update software port_changes register */
5790Sstevel@tonic-gate 	uhcip->uhci_root_hub.rh_port_changes[port] &= ~PORT_CHANGE_CSC;
5800Sstevel@tonic-gate }
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 
5830Sstevel@tonic-gate /*
5840Sstevel@tonic-gate  * uhci_handle_get_port_status:
5850Sstevel@tonic-gate  *	Handle a get port status request.
5860Sstevel@tonic-gate  */
5870Sstevel@tonic-gate static void
uhci_handle_get_port_status(uhci_state_t * uhcip,usb_ctrl_req_t * req,usb_port_t port)5880Sstevel@tonic-gate uhci_handle_get_port_status(
5890Sstevel@tonic-gate 	uhci_state_t		*uhcip,
5900Sstevel@tonic-gate 	usb_ctrl_req_t		*req,
5910Sstevel@tonic-gate 	usb_port_t		port)
5920Sstevel@tonic-gate {
5930Sstevel@tonic-gate 	uint_t		new_port_status;
5940Sstevel@tonic-gate 	uint_t		old_port_status =
5956898Sfb209375 	    uhcip->uhci_root_hub.rh_port_status[port];
5960Sstevel@tonic-gate 	uint_t		old_port_changes =
5976898Sfb209375 	    uhcip->uhci_root_hub.rh_port_changes[port];
5980Sstevel@tonic-gate 	uint_t		change_status;
5990Sstevel@tonic-gate 	usb_ctrl_req_t	*ctrl_reqp = (usb_ctrl_req_t *)req;
6000Sstevel@tonic-gate 	uint16_t	wLength = req->ctrl_wLength;
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 	ASSERT(wLength == 4);
6030Sstevel@tonic-gate 	ASSERT(ctrl_reqp->ctrl_data != NULL);
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	/* Read the current port status and return it */
6060Sstevel@tonic-gate 	new_port_status = uhci_get_port_status(uhcip, port);
6070Sstevel@tonic-gate 	change_status	= (old_port_status ^ new_port_status) & 0xff;
6080Sstevel@tonic-gate 	change_status	|= old_port_changes;
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
6110Sstevel@tonic-gate 	    "uhci_handle_get_port_status:\n\t"
6120Sstevel@tonic-gate 	    "port%d: old status = 0x%x	new status = 0x%x change = 0x%x",
6130Sstevel@tonic-gate 	    port, old_port_status, new_port_status, change_status);
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 	*ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)new_port_status;
6160Sstevel@tonic-gate 	*ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)(new_port_status >> 8);
6170Sstevel@tonic-gate 	*ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)change_status;
6180Sstevel@tonic-gate 	*ctrl_reqp->ctrl_data->b_wptr++ = (uchar_t)(change_status >> 8);
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 	/* Update the status */
6210Sstevel@tonic-gate 	uhcip->uhci_root_hub.rh_port_status[port] = new_port_status;
6220Sstevel@tonic-gate 	uhcip->uhci_root_hub.rh_port_changes[port] = change_status;
6230Sstevel@tonic-gate }
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate /*
6270Sstevel@tonic-gate  * uhci_handle_get_hub_descriptor:
6280Sstevel@tonic-gate  */
6290Sstevel@tonic-gate static void
uhci_handle_get_hub_descriptor(uhci_state_t * uhcip,usb_ctrl_req_t * req)6300Sstevel@tonic-gate uhci_handle_get_hub_descriptor(
6310Sstevel@tonic-gate 	uhci_state_t		*uhcip,
6320Sstevel@tonic-gate 	usb_ctrl_req_t		*req)
6330Sstevel@tonic-gate {
6340Sstevel@tonic-gate 	uchar_t		raw_descr[ROOT_HUB_DESCRIPTOR_LENGTH];
6350Sstevel@tonic-gate 	usb_hub_descr_t	*root_hub_descr = &uhcip->uhci_root_hub.rh_descr;
6360Sstevel@tonic-gate 
6370Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
6380Sstevel@tonic-gate 	    "uhci_handle_get_hub_descriptor: wLength = 0x%x",
6390Sstevel@tonic-gate 	    req->ctrl_wLength);
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 	ASSERT(req->ctrl_wLength != 0);
6420Sstevel@tonic-gate 	ASSERT(req->ctrl_data != NULL);
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	bzero(&raw_descr, ROOT_HUB_DESCRIPTOR_LENGTH);
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate 	raw_descr[0] = root_hub_descr->bDescLength;
6470Sstevel@tonic-gate 	raw_descr[1] = root_hub_descr->bDescriptorType;
6480Sstevel@tonic-gate 	raw_descr[2] = root_hub_descr->bNbrPorts;
6490Sstevel@tonic-gate 	raw_descr[3] = root_hub_descr->wHubCharacteristics & 0x00ff;
6500Sstevel@tonic-gate 	raw_descr[4] = (root_hub_descr->wHubCharacteristics & 0xff00) >> 8;
6510Sstevel@tonic-gate 	raw_descr[5] = root_hub_descr->bPwrOn2PwrGood;
6520Sstevel@tonic-gate 	raw_descr[6] = root_hub_descr->bHubContrCurrent;
6530Sstevel@tonic-gate 	raw_descr[7] = root_hub_descr->DeviceRemovable;
6540Sstevel@tonic-gate 	raw_descr[8] = root_hub_descr->PortPwrCtrlMask;
6550Sstevel@tonic-gate 
6560Sstevel@tonic-gate 	bcopy(raw_descr, req->ctrl_data->b_wptr, req->ctrl_wLength);
6570Sstevel@tonic-gate 	req->ctrl_data->b_wptr += req->ctrl_wLength;
6580Sstevel@tonic-gate }
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate /*
6620Sstevel@tonic-gate  * uhci_handle_get_hub_status:
6630Sstevel@tonic-gate  */
6640Sstevel@tonic-gate static void
uhci_handle_get_hub_status(uhci_state_t * uhcip,usb_ctrl_req_t * req)6650Sstevel@tonic-gate uhci_handle_get_hub_status(
6660Sstevel@tonic-gate 	uhci_state_t		*uhcip,
6670Sstevel@tonic-gate 	usb_ctrl_req_t		*req)
6680Sstevel@tonic-gate {
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
6710Sstevel@tonic-gate 	    "uhci_handle_get_hub_status: wLength = 0x%x",
6726898Sfb209375 	    req->ctrl_wLength);
6730Sstevel@tonic-gate 	ASSERT(req->ctrl_wLength != 0);
6740Sstevel@tonic-gate 	ASSERT(req->ctrl_data != NULL);
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate 	/*
6770Sstevel@tonic-gate 	 * A good status is always sent because there is no way that
6780Sstevel@tonic-gate 	 * the driver can get to know about the status change of the
6790Sstevel@tonic-gate 	 * over current or power failure of the root hub from the HC.
6800Sstevel@tonic-gate 	 */
6810Sstevel@tonic-gate 	bzero(req->ctrl_data->b_wptr, req->ctrl_wLength);
6820Sstevel@tonic-gate 	req->ctrl_data->b_wptr += req->ctrl_wLength;
6830Sstevel@tonic-gate }
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate /*
6871001Ssl147100  * uhci_handle_get_device_status:
6881001Ssl147100  */
6891001Ssl147100 static void
uhci_handle_get_device_status(uhci_state_t * uhcip,usb_ctrl_req_t * req)6901001Ssl147100 uhci_handle_get_device_status(
6911001Ssl147100 	uhci_state_t		*uhcip,
6921001Ssl147100 	usb_ctrl_req_t		*req)
6931001Ssl147100 {
6941001Ssl147100 	uint16_t	dev_status;
6951001Ssl147100 
6961001Ssl147100 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
6971001Ssl147100 	    "uhci_handle_get_device_status: wLength = 0x%x",
6981001Ssl147100 	    req->ctrl_wLength);
6991001Ssl147100 
7001001Ssl147100 	ASSERT(req->ctrl_wLength != 0);
7011001Ssl147100 	ASSERT(req->ctrl_data != NULL);
7021001Ssl147100 
7031001Ssl147100 	/*
7041001Ssl147100 	 * UHCI doesn't have device status information.
7051001Ssl147100 	 * Simply return what is desired for the request.
7061001Ssl147100 	 */
7071001Ssl147100 	dev_status = USB_DEV_SLF_PWRD_STATUS;
7081001Ssl147100 
7091001Ssl147100 	*req->ctrl_data->b_wptr++ = (uchar_t)dev_status;
7101001Ssl147100 	*req->ctrl_data->b_wptr++ = (uchar_t)(dev_status >> 8);
7111001Ssl147100 }
7121001Ssl147100 
7131001Ssl147100 
7141001Ssl147100 /*
7150Sstevel@tonic-gate  * uhci_handle_root_hub_status_change:
716*7138Szl227052  *	This function is called every 256 ms from the time out handler.
7170Sstevel@tonic-gate  *	It checks for the status change of the root hub and its ports.
7180Sstevel@tonic-gate  */
7190Sstevel@tonic-gate void
uhci_handle_root_hub_status_change(void * arg)7200Sstevel@tonic-gate uhci_handle_root_hub_status_change(void *arg)
7210Sstevel@tonic-gate {
7220Sstevel@tonic-gate 	usb_port_t	port;
7230Sstevel@tonic-gate 	uint_t		old_port_status;
7240Sstevel@tonic-gate 	uint_t		new_port_status;
7250Sstevel@tonic-gate 	ushort_t	port_status;
7260Sstevel@tonic-gate 	uint_t		change_status;
7270Sstevel@tonic-gate 	uchar_t		all_ports_status = 0;
7280Sstevel@tonic-gate 	uhci_state_t	*uhcip = (uhci_state_t *)arg;
7290Sstevel@tonic-gate 	usb_intr_req_t	*curr_intr_reqp;
7300Sstevel@tonic-gate 
7310Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
7320Sstevel@tonic-gate 
7330Sstevel@tonic-gate 	/* reset the timeout id */
7340Sstevel@tonic-gate 	uhcip->uhci_timeout_id = 0;
7350Sstevel@tonic-gate 
7360Sstevel@tonic-gate 	/* Get the current interrupt request pointer */
7370Sstevel@tonic-gate 	curr_intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp;
7380Sstevel@tonic-gate 
7390Sstevel@tonic-gate 	/* Check each port */
7400Sstevel@tonic-gate 	for (port = 0; port < uhcip->uhci_root_hub.rh_num_ports; port++) {
7410Sstevel@tonic-gate 		new_port_status = uhci_get_port_status(uhcip, port);
7420Sstevel@tonic-gate 		old_port_status = uhcip->uhci_root_hub.rh_port_status[port];
7430Sstevel@tonic-gate 
7440Sstevel@tonic-gate 		change_status = (old_port_status ^ new_port_status) & 0xff;
7450Sstevel@tonic-gate 		change_status |= uhcip->uhci_root_hub.rh_port_changes[port];
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate 		/* See if a device was attached/detached */
7480Sstevel@tonic-gate 		if (change_status & PORT_STATUS_CCS) {
7490Sstevel@tonic-gate 			all_ports_status |= 1 << (port + 1);
7500Sstevel@tonic-gate 		}
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate 		port_status = Get_OpReg16(PORTSC[port]);
7530Sstevel@tonic-gate 		Set_OpReg16(PORTSC[port], port_status | HCR_PORT_ENDIS_CHG);
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_port_status[port] = new_port_status;
7560Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_port_changes[port] = change_status;
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate 		USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
7590Sstevel@tonic-gate 		    "port %d old status 0x%x new status 0x%x change 0x%x\n\t"
7600Sstevel@tonic-gate 		    "all_ports_status = 0x%x", port, old_port_status,
7610Sstevel@tonic-gate 		    new_port_status, change_status, all_ports_status);
7620Sstevel@tonic-gate 	}
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate 	if (uhcip->uhci_root_hub.rh_intr_pipe_handle &&
7650Sstevel@tonic-gate 	    all_ports_status && curr_intr_reqp &&
7660Sstevel@tonic-gate 	    (uhcip->uhci_root_hub.rh_pipe_state == UHCI_PIPE_STATE_ACTIVE)) {
7670Sstevel@tonic-gate 
7680Sstevel@tonic-gate 		ASSERT(curr_intr_reqp->intr_data != NULL);
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate 		*curr_intr_reqp->intr_data->b_wptr++ = all_ports_status;
7710Sstevel@tonic-gate 
7720Sstevel@tonic-gate 		uhci_rh_hcdi_callback(uhcip,
7730Sstevel@tonic-gate 		    uhcip->uhci_root_hub.rh_intr_pipe_handle,
7740Sstevel@tonic-gate 		    (usb_opaque_t)curr_intr_reqp, USB_CR_OK);
7750Sstevel@tonic-gate 	}
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate 	if (uhcip->uhci_root_hub.rh_pipe_state == UHCI_PIPE_STATE_ACTIVE) {
7780Sstevel@tonic-gate 		/*
7790Sstevel@tonic-gate 		 * If needed, allocate new interrupt request. Also
7800Sstevel@tonic-gate 		 * start the timer for the root hub interrupt polling.
7810Sstevel@tonic-gate 		 */
7820Sstevel@tonic-gate 		if (uhci_root_hub_allocate_intr_pipe_resource(uhcip, 0) !=
7830Sstevel@tonic-gate 		    USB_SUCCESS) {
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate 			/* Do interrupt pipe cleanup */
7860Sstevel@tonic-gate 			uhci_root_hub_intr_pipe_cleanup(uhcip,
7876898Sfb209375 			    USB_CR_NO_RESOURCES);
7880Sstevel@tonic-gate 		}
7890Sstevel@tonic-gate 	}
7900Sstevel@tonic-gate 
7910Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
7920Sstevel@tonic-gate }
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate static uint_t
uhci_get_port_status(uhci_state_t * uhcip,usb_port_t port)7960Sstevel@tonic-gate uhci_get_port_status(
7970Sstevel@tonic-gate 	uhci_state_t	*uhcip,
7980Sstevel@tonic-gate 	usb_port_t	port)
7990Sstevel@tonic-gate {
8000Sstevel@tonic-gate 	uint_t		new_port_status = PORT_STATUS_PPS;
8010Sstevel@tonic-gate 	ushort_t	port_status = Get_OpReg16(PORTSC[port]);
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 	if (port_status & HCR_PORT_CCS) {
8040Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_CCS;
8050Sstevel@tonic-gate 	}
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 	if (port_status & HCR_PORT_LSDA) {
8080Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_LSDA;
8090Sstevel@tonic-gate 	}
8100Sstevel@tonic-gate 
8110Sstevel@tonic-gate 	if (port_status & HCR_PORT_ENABLE) {
8120Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_PES;
8130Sstevel@tonic-gate 	}
8140Sstevel@tonic-gate 
8150Sstevel@tonic-gate 	if (port_status & HCR_PORT_SUSPEND) {
8160Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_PSS;
8170Sstevel@tonic-gate 	}
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate 	if (port_status & HCR_PORT_RESET) {
8200Sstevel@tonic-gate 		new_port_status |= PORT_STATUS_PRS;
8210Sstevel@tonic-gate 	}
8220Sstevel@tonic-gate 
8230Sstevel@tonic-gate 	return (new_port_status);
8240Sstevel@tonic-gate }
8250Sstevel@tonic-gate 
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate /*
8280Sstevel@tonic-gate  * uhci_root_hub_allocate_intr_pipe_resource:
8290Sstevel@tonic-gate  *	Allocate interrupt requests and initialize them.
8300Sstevel@tonic-gate  */
8310Sstevel@tonic-gate int
uhci_root_hub_allocate_intr_pipe_resource(uhci_state_t * uhcip,usb_flags_t flags)8320Sstevel@tonic-gate uhci_root_hub_allocate_intr_pipe_resource(
8330Sstevel@tonic-gate 	uhci_state_t	*uhcip,
8340Sstevel@tonic-gate 	usb_flags_t	flags)
8350Sstevel@tonic-gate {
8360Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp;
8370Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
8380Sstevel@tonic-gate 
8390Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
8400Sstevel@tonic-gate 	    "uhci_root_hub_allocate_intr_pipe_resource:");
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
8430Sstevel@tonic-gate 
8440Sstevel@tonic-gate 	/* Get the interrupt pipe handle */
8450Sstevel@tonic-gate 	ph = uhcip->uhci_root_hub.rh_intr_pipe_handle;
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate 	/* Get the current interrupt request pointer */
8480Sstevel@tonic-gate 	curr_intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp;
8490Sstevel@tonic-gate 
8500Sstevel@tonic-gate 	/*
8510Sstevel@tonic-gate 	 * If current interrupt request pointer is null,
8520Sstevel@tonic-gate 	 * allocate new interrupt request.
8530Sstevel@tonic-gate 	 */
8540Sstevel@tonic-gate 	if (curr_intr_reqp == NULL) {
8550Sstevel@tonic-gate 		ASSERT(uhcip->uhci_root_hub.rh_client_intr_req);
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate 		if ((curr_intr_reqp = usba_hcdi_dup_intr_req(ph->p_dip,
8580Sstevel@tonic-gate 		    uhcip->uhci_root_hub.rh_client_intr_req,
8590Sstevel@tonic-gate 		    uhcip->uhci_root_hub.rh_client_intr_req->intr_len,
8600Sstevel@tonic-gate 		    flags)) == NULL) {
8610Sstevel@tonic-gate 			USB_DPRINTF_L2(PRINT_MASK_LISTS, uhcip->uhci_log_hdl,
8620Sstevel@tonic-gate 			    "uhci_root_hub_allocate_intr_pipe_resource:"
8630Sstevel@tonic-gate 			    "Interrupt request structure allocation failed");
8640Sstevel@tonic-gate 
8650Sstevel@tonic-gate 			return (USB_NO_RESOURCES);
8660Sstevel@tonic-gate 		}
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_curr_intr_reqp = curr_intr_reqp;
8690Sstevel@tonic-gate 
8700Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
8710Sstevel@tonic-gate 		ph->p_req_count++;
8720Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
8730Sstevel@tonic-gate 	}
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 	if (uhcip->uhci_timeout_id == 0) {
8760Sstevel@tonic-gate 		uhcip->uhci_timeout_id = timeout(
8776898Sfb209375 		    uhci_handle_root_hub_status_change,
878*7138Szl227052 		    (void *)uhcip, UHCI_256_MS);
8790Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_pipe_state =
8806898Sfb209375 		    UHCI_PIPE_STATE_ACTIVE;
8810Sstevel@tonic-gate 	}
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 	return (USB_SUCCESS);
8840Sstevel@tonic-gate }
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate 
8870Sstevel@tonic-gate /*
8880Sstevel@tonic-gate  * uhci_root_hub_intr_pipe_cleanup:
8890Sstevel@tonic-gate  *	Deallocate all interrupt requests and do callback
8900Sstevel@tonic-gate  *	the original client interrupt request.
8910Sstevel@tonic-gate  */
8920Sstevel@tonic-gate void
uhci_root_hub_intr_pipe_cleanup(uhci_state_t * uhcip,usb_cr_t cr)8930Sstevel@tonic-gate uhci_root_hub_intr_pipe_cleanup(uhci_state_t *uhcip, usb_cr_t cr)
8940Sstevel@tonic-gate {
8950Sstevel@tonic-gate 	usb_intr_req_t		*curr_intr_reqp;
8960Sstevel@tonic-gate 	usb_opaque_t		client_intr_reqp;
8970Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph;
8980Sstevel@tonic-gate 	timeout_id_t		timer_id;
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl,
9010Sstevel@tonic-gate 	    "uhci_root_hub_intr_pipe_cleanup:");
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 	/* Get the interrupt pipe handle */
9060Sstevel@tonic-gate 	ph = uhcip->uhci_root_hub.rh_intr_pipe_handle;
9070Sstevel@tonic-gate 
9080Sstevel@tonic-gate 	/* Get the interrupt timerid */
9090Sstevel@tonic-gate 	timer_id = uhcip->uhci_timeout_id;
9100Sstevel@tonic-gate 
9110Sstevel@tonic-gate 	/* Stop the root hub interrupt timer */
9120Sstevel@tonic-gate 	if (timer_id) {
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate 		/* Reset the timer id to zero */
9150Sstevel@tonic-gate 		uhcip->uhci_timeout_id = 0;
9160Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_pipe_state =
9176898Sfb209375 		    UHCI_PIPE_STATE_IDLE;
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 		mutex_exit(&uhcip->uhci_int_mutex);
9200Sstevel@tonic-gate 		(void) untimeout(timer_id);
9210Sstevel@tonic-gate 		mutex_enter(&uhcip->uhci_int_mutex);
9220Sstevel@tonic-gate 	}
9230Sstevel@tonic-gate 
9240Sstevel@tonic-gate 	/* Reset the current interrupt request pointer */
9250Sstevel@tonic-gate 	curr_intr_reqp = uhcip->uhci_root_hub.rh_curr_intr_reqp;
9260Sstevel@tonic-gate 
9270Sstevel@tonic-gate 	/* Deallocate uncompleted interrupt request */
9280Sstevel@tonic-gate 	if (curr_intr_reqp) {
9290Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_curr_intr_reqp = NULL;
9300Sstevel@tonic-gate 		usb_free_intr_req(curr_intr_reqp);
9310Sstevel@tonic-gate 
9320Sstevel@tonic-gate 		mutex_enter(&ph->p_mutex);
9330Sstevel@tonic-gate 		ph->p_req_count--;
9340Sstevel@tonic-gate 		mutex_exit(&ph->p_mutex);
9350Sstevel@tonic-gate 	}
9360Sstevel@tonic-gate 
9370Sstevel@tonic-gate 	client_intr_reqp = (usb_opaque_t)
9380Sstevel@tonic-gate 	    uhcip->uhci_root_hub.rh_client_intr_req;
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate 	/* Callback for original client interrupt request */
9410Sstevel@tonic-gate 	if (client_intr_reqp) {
9420Sstevel@tonic-gate 		uhcip->uhci_root_hub.rh_client_intr_req = NULL;
9430Sstevel@tonic-gate 		uhci_rh_hcdi_callback(uhcip, ph,
9440Sstevel@tonic-gate 		    (usb_opaque_t)client_intr_reqp, cr);
9450Sstevel@tonic-gate 	}
9460Sstevel@tonic-gate }
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 
9490Sstevel@tonic-gate /*
9500Sstevel@tonic-gate  * uhci_rh_hcdi_callback:
9510Sstevel@tonic-gate  *	Convenience wrapper around usba_hcdi_cb() for the root hub.
9520Sstevel@tonic-gate  */
9530Sstevel@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)9540Sstevel@tonic-gate uhci_rh_hcdi_callback(
9550Sstevel@tonic-gate 	uhci_state_t		*uhcip,
9560Sstevel@tonic-gate 	usba_pipe_handle_data_t	*ph,
9570Sstevel@tonic-gate 	usb_opaque_t		req,
9580Sstevel@tonic-gate 	usb_cr_t		cr)
9590Sstevel@tonic-gate {
9600Sstevel@tonic-gate 	USB_DPRINTF_L4(PRINT_MASK_HCDI, uhcip->uhci_log_hdl,
9610Sstevel@tonic-gate 	    "uhci_rh_hcdi_callback: ph=0x%p cr=0x%x req=0x%p",
9626898Sfb209375 	    (void *)ph, cr, (void *)req);
9630Sstevel@tonic-gate 
9640Sstevel@tonic-gate 	ASSERT(mutex_owned(&uhcip->uhci_int_mutex));
9650Sstevel@tonic-gate 
9660Sstevel@tonic-gate 	switch (UHCI_XFER_TYPE(&ph->p_ep)) {
9670Sstevel@tonic-gate 	case USB_EP_ATTR_CONTROL:
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate 		break;
9700Sstevel@tonic-gate 	case USB_EP_ATTR_INTR:
9710Sstevel@tonic-gate 		if ((usb_intr_req_t *)req ==
9720Sstevel@tonic-gate 		    uhcip->uhci_root_hub.rh_curr_intr_reqp) {
9730Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_curr_intr_reqp = NULL;
9740Sstevel@tonic-gate 
9750Sstevel@tonic-gate 			break;
9760Sstevel@tonic-gate 		} else if ((usb_intr_req_t *)req ==
9770Sstevel@tonic-gate 		    uhcip->uhci_root_hub.rh_client_intr_req) {
9780Sstevel@tonic-gate 			uhcip->uhci_root_hub.rh_client_intr_req = NULL;
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate 			break;
9810Sstevel@tonic-gate 		}
9820Sstevel@tonic-gate 		/*FALLTHRU*/
9830Sstevel@tonic-gate 	default:
9840Sstevel@tonic-gate 		ASSERT(req);
9850Sstevel@tonic-gate 		break;
9860Sstevel@tonic-gate 	}
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate 	mutex_exit(&uhcip->uhci_int_mutex);
9890Sstevel@tonic-gate 	usba_hcdi_cb(ph, req, cr);
9900Sstevel@tonic-gate 	mutex_enter(&uhcip->uhci_int_mutex);
9910Sstevel@tonic-gate }
992