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