Mercurial > illumos > illumos-gate
changeset 1001:991c73813ffc
4108775 The hub driver needs to do power budgeting.
6340699 NULL pointer reference in usba module causes panic
author | sl147100 |
---|---|
date | Mon, 28 Nov 2005 00:08:21 -0800 |
parents | dd54117d55b1 |
children | 07d048eb5eb1 |
files | usr/src/uts/common/io/usb/hcd/ehci/ehci_hub.c usr/src/uts/common/io/usb/hcd/openhci/ohci_hub.c usr/src/uts/common/io/usb/hcd/uhci/uhcihub.c usr/src/uts/common/io/usb/usba/hubdi.c usr/src/uts/common/io/usb/usba/usbai_util.c usr/src/uts/common/sys/usb/hubd/hub.h usr/src/uts/common/sys/usb/hubd/hubdvar.h usr/src/uts/common/sys/usb/usba/hubdi.h usr/src/uts/common/sys/usb/usba/usba_types.h |
diffstat | 9 files changed, 773 insertions(+), 95 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/io/usb/hcd/ehci/ehci_hub.c Sun Nov 27 19:11:23 2005 -0800 +++ b/usr/src/uts/common/io/usb/hcd/ehci/ehci_hub.c Mon Nov 28 00:08:21 2005 -0800 @@ -89,6 +89,8 @@ ehci_state_t *ehcip); static void ehci_handle_get_hub_status( ehci_state_t *ehcip); +static void ehci_handle_get_device_status( + ehci_state_t *ehcip); static uint_t ehci_get_root_hub_port_status( ehci_state_t *ehcip, uint16_t port); @@ -550,14 +552,17 @@ mutex_exit(&ehcip->ehci_int_mutex); switch (bmRequestType) { - case HANDLE_PORT_FEATURE: + case HUB_GET_DEVICE_STATUS_TYPE: + ehci_handle_get_device_status(ehcip); + break; + case HUB_HANDLE_PORT_FEATURE_TYPE: error = ehci_handle_set_clear_port_feature(ehcip, bRequest, wValue, port); break; - case GET_PORT_STATUS: + case HUB_GET_PORT_STATUS_TYPE: ehci_handle_get_port_status(ehcip, port); break; - case HUB_CLASS_REQ: + case HUB_CLASS_REQ_TYPE: switch (bRequest) { case USB_REQ_GET_STATUS: ehci_handle_get_hub_status(ehcip); @@ -1360,6 +1365,47 @@ /* + * ehci_handle_get_device_status: + * + * Handle a get device status request. + */ +static void +ehci_handle_get_device_status( + ehci_state_t *ehcip) +{ + usb_ctrl_req_t *ctrl_reqp; + mblk_t *message; + uint16_t dev_status; + + mutex_enter(&ehcip->ehci_int_mutex); + + ctrl_reqp = ehcip->ehci_root_hub.rh_curr_ctrl_reqp; + + /* + * For EHCI, there is no device status information. + * Simply return what is desired for the request. + */ + dev_status = USB_DEV_SLF_PWRD_STATUS; + + USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ehcip->ehci_log_hdl, + "ehci_handle_get_device_status: device status = 0x%x", + dev_status); + + message = ctrl_reqp->ctrl_data; + + ASSERT(message != NULL); + + *message->b_wptr++ = (uchar_t)dev_status; + *message->b_wptr++ = (uchar_t)(dev_status >> 8); + + /* Save the data in control request */ + ctrl_reqp->ctrl_data = message; + + mutex_exit(&ehcip->ehci_int_mutex); +} + + +/* * ehci_handle_root_hub_pipe_start_intr_polling: * * Handle start polling on root hub interrupt pipe.
--- a/usr/src/uts/common/io/usb/hcd/openhci/ohci_hub.c Sun Nov 27 19:11:23 2005 -0800 +++ b/usr/src/uts/common/io/usb/hcd/openhci/ohci_hub.c Mon Nov 28 00:08:21 2005 -0800 @@ -79,6 +79,8 @@ ohci_state_t *ohcip); static void ohci_handle_get_hub_status( ohci_state_t *ohcip); +static void ohci_handle_get_device_status( + ohci_state_t *ohcip); static int ohci_root_hub_allocate_intr_pipe_resource( ohci_state_t *ohcip, usb_flags_t flags); @@ -557,14 +559,17 @@ mutex_exit(&ohcip->ohci_int_mutex); switch (bmRequestType) { - case HANDLE_PORT_FEATURE: + case HUB_GET_DEVICE_STATUS_TYPE: + ohci_handle_get_device_status(ohcip); + break; + case HUB_HANDLE_PORT_FEATURE_TYPE: error = ohci_handle_set_clear_port_feature(ohcip, bRequest, wValue, port); break; - case GET_PORT_STATUS: + case HUB_GET_PORT_STATUS_TYPE: ohci_handle_get_port_status(ohcip, port); break; - case HUB_CLASS_REQ: + case HUB_CLASS_REQ_TYPE: switch (bRequest) { case USB_REQ_GET_STATUS: ohci_handle_get_hub_status(ohcip); @@ -1155,6 +1160,47 @@ /* + * ohci_handle_get_device_status: + * + * Handle a get device status request. + */ +static void +ohci_handle_get_device_status( + ohci_state_t *ohcip) +{ + usb_ctrl_req_t *ctrl_reqp; + mblk_t *message; + uint16_t dev_status; + + mutex_enter(&ohcip->ohci_int_mutex); + + ctrl_reqp = ohcip->ohci_root_hub.rh_curr_ctrl_reqp; + + /* + * OHCI doesn't have device status information. + * Simply return what is desired for the request. + */ + dev_status = USB_DEV_SLF_PWRD_STATUS; + + USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, ohcip->ohci_log_hdl, + "ohci_handle_get_device_status: device status = 0x%x", + dev_status); + + message = ctrl_reqp->ctrl_data; + + ASSERT(message != NULL); + + *message->b_wptr++ = (uchar_t)dev_status; + *message->b_wptr++ = (uchar_t)(dev_status >> 8); + + /* Save the data in control request */ + ctrl_reqp->ctrl_data = message; + + mutex_exit(&ohcip->ohci_int_mutex); +} + + +/* * ohci_handle_root_hub_pipe_start_intr_polling: * * Handle start polling on root hub interrupt pipe.
--- a/usr/src/uts/common/io/usb/hcd/uhci/uhcihub.c Sun Nov 27 19:11:23 2005 -0800 +++ b/usr/src/uts/common/io/usb/hcd/uhci/uhcihub.c Mon Nov 28 00:08:21 2005 -0800 @@ -77,6 +77,9 @@ static void uhci_handle_get_hub_status( uhci_state_t *uhcip, usb_ctrl_req_t *req); +static void uhci_handle_get_device_status( + uhci_state_t *uhcip, + usb_ctrl_req_t *req); static uint_t uhci_get_port_status( uhci_state_t *uhcip, usb_port_t port); @@ -215,16 +218,20 @@ ASSERT(mutex_owned(&uhcip->uhci_int_mutex)); switch (req->ctrl_bmRequestType) { - case HANDLE_PORT_FEATURE: + case HUB_GET_DEVICE_STATUS_TYPE: + uhci_handle_get_device_status(uhcip, req); + + break; + case HUB_HANDLE_PORT_FEATURE_TYPE: error = uhci_handle_set_clear_port_feature(uhcip, req->ctrl_bRequest, req->ctrl_wValue, port); break; - case GET_PORT_STATUS: + case HUB_GET_PORT_STATUS_TYPE: uhci_handle_get_port_status(uhcip, req, port); break; - case HUB_CLASS_REQ: + case HUB_CLASS_REQ_TYPE: switch (req->ctrl_bRequest) { case USB_REQ_GET_DESCR: uhci_handle_get_hub_descriptor(uhcip, req); @@ -679,6 +686,34 @@ /* + * uhci_handle_get_device_status: + */ +static void +uhci_handle_get_device_status( + uhci_state_t *uhcip, + usb_ctrl_req_t *req) +{ + uint16_t dev_status; + + USB_DPRINTF_L4(PRINT_MASK_ROOT_HUB, uhcip->uhci_log_hdl, + "uhci_handle_get_device_status: wLength = 0x%x", + req->ctrl_wLength); + + ASSERT(req->ctrl_wLength != 0); + ASSERT(req->ctrl_data != NULL); + + /* + * UHCI doesn't have device status information. + * Simply return what is desired for the request. + */ + dev_status = USB_DEV_SLF_PWRD_STATUS; + + *req->ctrl_data->b_wptr++ = (uchar_t)dev_status; + *req->ctrl_data->b_wptr++ = (uchar_t)(dev_status >> 8); +} + + +/* * uhci_handle_root_hub_status_change: * This function is called every 32 seconds from the time out handler. * It checks for the status change of the root hub and its ports.
--- a/usr/src/uts/common/io/usb/usba/hubdi.c Sun Nov 27 19:11:23 2005 -0800 +++ b/usr/src/uts/common/io/usb/usba/hubdi.c Mon Nov 28 00:08:21 2005 -0800 @@ -513,6 +513,8 @@ static int hubd_get_hub_descriptor(hubd_t *hubd); +static int hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status); + static int hubd_reset_port(hubd_t *hubd, usb_port_t port); static int hubd_get_hub_status(hubd_t *hubd); @@ -554,6 +556,8 @@ static void hubd_cpr_resume(dev_info_t *dip); static int hubd_restore_state_cb(dev_info_t *dip); +static int hubd_init_power_budget(hubd_t *hubd); + static ndi_event_definition_t hubd_ndi_event_defs[] = { {USBA_EVENT_TAG_HOT_REMOVAL, DDI_DEVI_REMOVE_EVENT, EPL_KERNEL, NDI_EVENT_POST_TO_ALL}, @@ -789,7 +793,7 @@ mutex_exit(HUBD_MUTEX(hubd)); if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_CLEAR_FEATURE, CFS_PORT_SUSPEND, port, @@ -840,7 +844,7 @@ mutex_exit(HUBD_MUTEX(hubd)); rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_CLEAR_FEATURE, CFS_PORT_SUSPEND, port, @@ -945,7 +949,7 @@ mutex_exit(HUBD_MUTEX(hubd)); if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_SET_FEATURE, CFS_PORT_SUSPEND, port, @@ -1045,8 +1049,11 @@ mutex_enter(HUBD_MUTEX(hubd)); if (ds->result == DDI_SUCCESS) { usba_device_t *usba_device = hubd->h_usba_devices[port]; + dev_info_t *pdip = hubd->h_dip; mutex_exit(HUBD_MUTEX(hubd)); + usba_hubdi_incr_power_budget(pdip, usba_device); + /* * We set power of the detached child * to 0, so that we can suspend if all @@ -1869,6 +1876,29 @@ mutex_enter(HUBD_MUTEX(hubd)); hubd->h_init_state |= HUBD_EVENTS_REGISTERED; + if ((hubd_get_hub_descriptor(hubd)) != USB_SUCCESS) { + mutex_exit(HUBD_MUTEX(hubd)); + + goto fail; + } + + if (ddi_prop_exists(DDI_DEV_T_ANY, dip, + (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), + "hub-ignore-power-budget") == 1) { + hubd->h_ignore_pwr_budget = B_TRUE; + } else { + hubd->h_ignore_pwr_budget = B_FALSE; + + /* initialize hub power budget variables */ + if (hubd_init_power_budget(hubd) != USB_SUCCESS) { + USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, + "hubd_init_power_budget failed"); + mutex_exit(HUBD_MUTEX(hubd)); + + goto fail; + } + } + /* initialize and create children */ if (hubd_check_ports(hubd) != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_MASK_ATTA, hubd->h_log_handle, @@ -2623,14 +2653,16 @@ mutex_enter(HUBD_MUTEX(hubd)); + if (hubd->h_init_state & HUBD_CHILDREN_CREATED) { #ifdef DEBUG - for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { - ASSERT(hubd->h_usba_devices[port] == NULL); - ASSERT(hubd->h_children_dips[port] == NULL); - } + for (port = 1; port <= hubd->h_hub_descr.bNbrPorts; port++) { + ASSERT(hubd->h_usba_devices[port] == NULL); + ASSERT(hubd->h_children_dips[port] == NULL); + } #endif - kmem_free(hubd->h_children_dips, hubd->h_cd_list_length); - kmem_free(hubd->h_usba_devices, hubd->h_cd_list_length); + kmem_free(hubd->h_children_dips, hubd->h_cd_list_length); + kmem_free(hubd->h_usba_devices, hubd->h_cd_list_length); + } /* * Disable the event callbacks first, after this point, event @@ -2738,18 +2770,13 @@ static int hubd_check_ports(hubd_t *hubd) { - int rval; + int rval; ASSERT(mutex_owned(HUBD_MUTEX(hubd))); USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, "hubd_check_ports: addr=0x%x", usb_get_addr(hubd->h_dip)); - if ((rval = hubd_get_hub_descriptor(hubd)) != USB_SUCCESS) { - - return (rval); - } - /* * First turn off all port power */ @@ -2797,6 +2824,8 @@ hubd->h_usba_devices = (usba_device_t **)kmem_zalloc( hubd->h_cd_list_length, KM_SLEEP); + hubd->h_init_state |= HUBD_CHILDREN_CREATED; + if ((rval = hubd_open_intr_pipe(hubd)) == USB_SUCCESS) { hubd_start_polling(hubd, 0); } @@ -2832,7 +2861,7 @@ if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HUB_CLASS_REQ, + HUB_CLASS_REQ_TYPE, USB_REQ_GET_DESCR, /* bRequest */ USB_DESCR_TYPE_SETUP_HUB, /* wValue */ 0, /* wIndex */ @@ -2857,7 +2886,7 @@ /* get complete hub descriptor */ if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HUB_CLASS_REQ, + HUB_CLASS_REQ_TYPE, USB_REQ_GET_DESCR, /* bRequest */ USB_DESCR_TYPE_SETUP_HUB, /* wValue */ 0, /* wIndex */ @@ -2894,10 +2923,10 @@ freemsg(data); USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, - "rval = 0x%x bNbrPorts = 0x%x wHubChars = 0x%x " - "PwrOn2PwrGood = 0x%x", rval, + "rval=0x%x bNbrPorts=0x%x wHubChars=0x%x " + "PwrOn2PwrGood=0x%x HubContrCurrent=%dmA", rval, hub_descr->bNbrPorts, hub_descr->wHubCharacteristics, - hub_descr->bPwrOn2PwrGood); + hub_descr->bPwrOn2PwrGood, hub_descr->bHubContrCurrent); if (hub_descr->bNbrPorts > MAX_PORTS) { USB_DPRINTF_L0(DPRINT_MASK_ATTA, hubd->h_log_handle, @@ -2913,6 +2942,55 @@ /* + * hubd_get_hub_status_words: + */ +static int +hubd_get_hub_status_words(hubd_t *hubd, uint16_t *status) +{ + usb_cr_t completion_reason; + usb_cb_flags_t cb_flags; + mblk_t *data = NULL; + + ASSERT(mutex_owned(HUBD_MUTEX(hubd))); + + mutex_exit(HUBD_MUTEX(hubd)); + + if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, + HUB_CLASS_REQ_TYPE, + USB_REQ_GET_STATUS, + 0, + 0, + GET_STATUS_LENGTH, + &data, 0, + &completion_reason, &cb_flags, 0) != USB_SUCCESS) { + USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, + "get hub status failed: cr=%d cb=0x%x", + completion_reason, cb_flags); + + if (data) { + freemsg(data); + } + + mutex_enter(HUBD_MUTEX(hubd)); + + return (USB_FAILURE); + } + + mutex_enter(HUBD_MUTEX(hubd)); + + status[0] = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); + status[1] = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); + + USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, + "hub status=0x%x change=0x%x", status[0], status[1]); + + freemsg(data); + + return (USB_SUCCESS); +} + + +/* * hubd_open_intr_pipe: * we read all descriptors first for curiosity and then simply * open the pipe @@ -3915,7 +3993,7 @@ int rval; usb_cr_t completion_reason; usb_cb_flags_t cb_flags; - mblk_t *data = NULL; + uint16_t stword[2]; uint16_t status; uint16_t change; usb_cfg_descr_t cfg_descr; @@ -3923,40 +4001,19 @@ uchar_t *usb_cfg; uint8_t MaxPower; - mutex_exit(HUBD_MUTEX(hubd)); - if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HUB_CLASS_REQ, - USB_REQ_GET_STATUS, - 0, - 0, - GET_STATUS_LENGTH, - &data, 0, - &completion_reason, &cb_flags, 0) != USB_SUCCESS) { - USB_DPRINTF_L2(DPRINT_MASK_PORT, hubd->h_log_handle, - "get hub status failed: cr=%d cb=0x%x", - completion_reason, cb_flags); - - if (data) { - freemsg(data); - } - - mutex_enter(HUBD_MUTEX(hubd)); + USB_DPRINTF_L4(DPRINT_MASK_PORT, hubd->h_log_handle, + "hubd_get_hub_status:"); + + ASSERT(mutex_owned(HUBD_MUTEX(hubd))); + + if ((hubd_get_hub_status_words(hubd, stword)) != USB_SUCCESS) { return (USB_FAILURE); } - - mutex_enter(HUBD_MUTEX(hubd)); - - status = (*(data->b_rptr + 1) << 8) | *(data->b_rptr); - change = (*(data->b_rptr + 3) << 8) | *(data->b_rptr + 2); - - if (status || change) { - USB_DPRINTF_L3(DPRINT_MASK_PORT, hubd->h_log_handle, - "hub status = 0x%x change = 0x%x", status, change); - } + status = stword[0]; + change = stword[1]; mutex_exit(HUBD_MUTEX(hubd)); - freemsg(data); /* Obtain the raw configuration descriptor */ usb_cfg = usb_get_raw_cfg_data(hubd->h_dip, &cfg_length); @@ -4100,7 +4157,7 @@ if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_SET_FEATURE, CFS_PORT_RESET, port, @@ -4167,7 +4224,7 @@ mutex_exit(HUBD_MUTEX(hubd)); if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - GET_PORT_STATUS, + HUB_GET_PORT_STATUS_TYPE, USB_REQ_GET_STATUS, 0, port, @@ -4221,7 +4278,7 @@ if (usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_CLEAR_FEATURE, CFS_C_PORT_RESET, port, @@ -4276,7 +4333,7 @@ if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_SET_FEATURE, CFS_PORT_ENABLE, port, @@ -4316,7 +4373,7 @@ if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_CLEAR_FEATURE, CFS_PORT_ENABLE, port, @@ -4336,7 +4393,7 @@ if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_CLEAR_FEATURE, CFS_C_PORT_ENABLE, port, @@ -4382,7 +4439,7 @@ if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - GET_PORT_STATUS, + HUB_GET_PORT_STATUS_TYPE, USB_REQ_GET_STATUS, 0, port, @@ -4535,7 +4592,7 @@ "clearing feature CFS_C_PORT_CONNECTION"); if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_CLEAR_FEATURE, CFS_C_PORT_CONNECTION, port, @@ -4554,7 +4611,7 @@ "clearing feature CFS_C_PORT_ENABLE"); if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_CLEAR_FEATURE, CFS_C_PORT_ENABLE, port, @@ -4574,7 +4631,7 @@ if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_CLEAR_FEATURE, CFS_C_PORT_SUSPEND, port, @@ -4594,7 +4651,7 @@ if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_CLEAR_FEATURE, CFS_C_PORT_OVER_CURRENT, port, @@ -4613,7 +4670,7 @@ "clearing feature CFS_C_PORT_RESET"); if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_CLEAR_FEATURE, CFS_C_PORT_RESET, port, @@ -4800,7 +4857,7 @@ if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_SET_FEATURE, CFS_PORT_POWER, port, @@ -4865,7 +4922,7 @@ if ((rval = usb_pipe_sync_ctrl_xfer(hubd->h_dip, hubd->h_default_pipe, - HANDLE_PORT_FEATURE, + HUB_HANDLE_PORT_FEATURE_TYPE, USB_REQ_CLEAR_FEATURE, CFS_PORT_POWER, port, @@ -5008,7 +5065,14 @@ 0)) == USB_SUCCESS) { /* this must be true since we didn't allow data underruns */ - ASSERT((pdata->b_wptr - pdata->b_rptr) == USB_CFG_DESCR_SIZE); + if ((pdata->b_wptr - pdata->b_rptr) != USB_CFG_DESCR_SIZE) { + USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, + "device returned incorrect configuration " + "descriptor size."); + + rval = USB_FAILURE; + goto done; + } /* * Parse the configuration descriptor @@ -5203,7 +5267,7 @@ */ static dev_info_t * hubd_ready_device(hubd_t *hubd, dev_info_t *child_dip, usba_device_t *child_ud, - int config_index) + uint_t config_index) { usb_cr_t completion_reason; usb_cb_flags_t cb_flags; @@ -5216,8 +5280,6 @@ "hubd_ready_device: dip=0x%p, user_conf_index=%d", child_dip, config_index); - ASSERT(config_index >= 0); - size = usb_parse_cfg_descr( child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE, &config_descriptor, USB_CFG_DESCR_SIZE); @@ -5302,7 +5364,8 @@ usb_pipe_handle_t ph = NULL; /* default pipe handle */ mblk_t *pdata = NULL; usb_cr_t completion_reason; - int user_conf_index, config_index; + int user_conf_index; + uint_t config_index; usb_cb_flags_t cb_flags; uchar_t address = 0; uint16_t length; @@ -5646,7 +5709,8 @@ child_dip, child_ud); /* Check if the user selected configuration index is in range */ - if (user_conf_index >= usb_dev_descr.bNumConfigurations) { + if ((user_conf_index >= usb_dev_descr.bNumConfigurations) || + (user_conf_index < 0)) { USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, "Configuration index for device idVendor=%d " "idProduct=%d is=%d, and is out of range[0..%d]", @@ -5729,6 +5793,12 @@ * device in the desired configuration. Till then * put the device in config index 0. */ + if ((rval = usba_hubdi_check_power_budget(dip, child_ud, + USB_DEV_DEFAULT_CONFIG_INDEX)) != USB_SUCCESS) { + + goto fail_cleanup; + } + child_dip = hubd_ready_device(hubd, child_dip, child_ud, USB_DEV_DEFAULT_CONFIG_INDEX); @@ -5764,8 +5834,39 @@ mutex_exit(HUBD_MUTEX(hubd)); rval = usba_bind_driver(child_dip); + + /* + * Normally power budget should be checked + * before device is configured. A failure in + * power budget checking will stop the device + * from being configured with current + * config_index and may enable the device to + * be configured in another configuration. + * This may break the user experience that a + * device which previously worked in config + * A now works in config B after power budget + * control is enabled. To avoid such situation, + * power budget checking is moved here and will + * fail the child creation directly if config + * A exceeds the power available. + */ + if (rval == USB_SUCCESS) { + if ((usba_hubdi_check_power_budget(dip, + child_ud, config_index)) != + USB_SUCCESS) { + + goto fail_cleanup; + } + } } if (rval != USB_SUCCESS) { + + if ((usba_hubdi_check_power_budget(dip, + child_ud, 0)) != USB_SUCCESS) { + + goto fail_cleanup; + } + child_dip = hubd_ready_device(hubd, child_dip, child_ud, 0); mutex_enter(HUBD_MUTEX(hubd)); @@ -5774,8 +5875,15 @@ } } /* end else loop all configs */ } else { + + if ((usba_hubdi_check_power_budget(dip, child_ud, + (uint_t)user_conf_index)) != USB_SUCCESS) { + + goto fail_cleanup; + } + child_dip = hubd_ready_device(hubd, child_dip, - child_ud, user_conf_index); + child_ud, (uint_t)user_conf_index); /* * Assign the dip before onlining to avoid race @@ -5788,6 +5896,8 @@ (void) usba_bind_driver(child_dip); } + usba_hubdi_decr_power_budget(dip, child_ud); + mutex_enter(HUBD_MUTEX(hubd)); if (hubd->h_usba_devices[port] == NULL) { hubd->h_usba_devices[port] = usba_get_usba_device(child_dip); @@ -5862,10 +5972,11 @@ int rval = USB_SUCCESS; child_dip = hubd->h_children_dips[port]; + usba_device = hubd->h_usba_devices[port]; USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, "hubd_delete_child: port=%d, dip=0x%p usba_device=0x%p", - port, child_dip); + port, child_dip, usba_device); mutex_exit(HUBD_MUTEX(hubd)); if (child_dip) { @@ -5874,6 +5985,10 @@ "dip = 0x%p (%s) at port %d", child_dip, ddi_node_name(child_dip), port); + if (usba_device) { + usba_hubdi_incr_power_budget(hubd->h_dip, usba_device); + } + rval = usba_destroy_child_devi(child_dip, flag); if ((rval == USB_SUCCESS) && (flag & NDI_DEVI_REMOVE)) { @@ -5904,9 +6019,6 @@ } if ((rval != USB_SUCCESS) && retry) { - mutex_enter(HUBD_MUTEX(hubd)); - usba_device = hubd->h_usba_devices[port]; - mutex_exit(HUBD_MUTEX(hubd)); hubd_schedule_cleanup(usba_device->usb_root_hub_dip); } @@ -7650,3 +7762,349 @@ return (USB_SUCCESS); } + + +/* + * hubd_init_power_budget: + * Init power budget variables in hubd structure. According + * to USB spec, the power budget rules are: + * 1. local-powered hubs including root-hubs can supply + * 500mA to each port at maximum + * 2. two bus-powered hubs are not allowed to concatenate + * 3. bus-powered hubs can supply 100mA to each port at + * maximum, and the power consumed by all downstream + * ports and the hub itself cannot exceed the max power + * supplied by the upstream port, i.e., 500mA + * The routine is only called during hub attach time + */ +static int +hubd_init_power_budget(hubd_t *hubd) +{ + uint16_t status = 0; + usba_device_t *hubd_ud = NULL; + size_t size; + usb_cfg_descr_t cfg_descr; + dev_info_t *pdip = NULL; + hubd_t *phubd = NULL; + + if (hubd->h_ignore_pwr_budget) { + + return (USB_SUCCESS); + } + + USB_DPRINTF_L4(DPRINT_MASK_HUB, hubd->h_log_handle, + "hubd_init_power_budget:"); + + ASSERT(mutex_owned(HUBD_MUTEX(hubd))); + ASSERT(hubd->h_default_pipe != 0); + mutex_exit(HUBD_MUTEX(hubd)); + + /* get device status */ + if ((usb_get_status(hubd->h_dip, hubd->h_default_pipe, + HUB_GET_DEVICE_STATUS_TYPE, + 0, &status, 0)) != USB_SUCCESS) { + mutex_enter(HUBD_MUTEX(hubd)); + + return (USB_FAILURE); + } + + hubd_ud = usba_get_usba_device(hubd->h_dip); + + size = usb_parse_cfg_descr(hubd_ud->usb_cfg, hubd_ud->usb_cfg_length, + &cfg_descr, USB_CFG_DESCR_SIZE); + + if (size != USB_CFG_DESCR_SIZE) { + USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, + "get hub configuration descriptor failed"); + mutex_enter(HUBD_MUTEX(hubd)); + + return (USB_FAILURE); + } + + mutex_enter(HUBD_MUTEX(hubd)); + + hubd->h_local_pwr_capable = (cfg_descr.bmAttributes & + USB_CFG_ATTR_SELFPWR); + + if (hubd->h_local_pwr_capable) { + USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, + "hub is capable of local power"); + } + + hubd->h_local_pwr_on = (status & + USB_DEV_SLF_PWRD_STATUS) && hubd->h_local_pwr_capable; + + if (hubd->h_local_pwr_on) { + USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, + "hub is local-powered"); + + hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD * + USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT; + } else { + hubd->h_pwr_limit = (USB_PWR_UNIT_LOAD * + USB_LOW_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT; + + hubd->h_pwr_left = (USB_PWR_UNIT_LOAD * + USB_HIGH_PWR_VALUE) / USB_CFG_DESCR_PWR_UNIT; + + ASSERT(!usba_is_root_hub(hubd->h_dip)); + + if (!usba_is_root_hub(hubd->h_dip)) { + /* + * two bus-powered hubs are not + * allowed to be concatenated + */ + mutex_exit(HUBD_MUTEX(hubd)); + + pdip = ddi_get_parent(hubd->h_dip); + phubd = hubd_get_soft_state(pdip); + ASSERT(phubd != NULL); + + if (!phubd->h_ignore_pwr_budget) { + mutex_enter(HUBD_MUTEX(phubd)); + if (phubd->h_local_pwr_on == B_FALSE) { + USB_DPRINTF_L0(DPRINT_MASK_HUB, + hubd->h_log_handle, + "two bus-powered hubs cannot " + "be concatenated"); + + mutex_exit(HUBD_MUTEX(phubd)); + mutex_enter(HUBD_MUTEX(hubd)); + + return (USB_FAILURE); + } + mutex_exit(HUBD_MUTEX(phubd)); + } + + mutex_enter(HUBD_MUTEX(hubd)); + + USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, + "hub is bus-powered"); + } else { + USB_DPRINTF_L3(DPRINT_MASK_HUB, hubd->h_log_handle, + "root-hub must be local-powered"); + } + + /* + * Subtract the power consumed by the hub itself + * and get the power that can be supplied to + * downstream ports + */ + hubd->h_pwr_left -= + hubd->h_hub_descr.bHubContrCurrent / + USB_CFG_DESCR_PWR_UNIT; + if (hubd->h_pwr_left < 0) { + USB_DPRINTF_L2(DPRINT_MASK_HUB, hubd->h_log_handle, + "hubd->h_pwr_left is less than bHubContrCurrent, " + "should fail"); + + return (USB_FAILURE); + } + } + + return (USB_SUCCESS); +} + + +/* + * usba_hubdi_check_power_budget: + * Check if the hub has enough power budget to allow a + * child device to select a configuration of config_index. + */ +int +usba_hubdi_check_power_budget(dev_info_t *dip, usba_device_t *child_ud, + uint_t config_index) +{ + int16_t pwr_left, pwr_limit, pwr_required; + size_t size; + usb_cfg_descr_t cfg_descr; + hubd_t *hubd; + + if ((hubd = hubd_get_soft_state(dip)) == NULL) { + + return (USB_FAILURE); + } + + if (hubd->h_ignore_pwr_budget) { + + return (USB_SUCCESS); + } + + USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, + "usba_hubdi_check_power_budget: " + "dip=0x%p child_ud=0x%p conf_index=%d", dip, + child_ud, config_index); + + mutex_enter(HUBD_MUTEX(hubd)); + pwr_limit = hubd->h_pwr_limit; + if (hubd->h_local_pwr_on == B_FALSE) { + pwr_left = hubd->h_pwr_left; + pwr_limit = (pwr_limit <= pwr_left) ? pwr_limit : pwr_left; + } + mutex_exit(HUBD_MUTEX(hubd)); + + USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, + "usba_hubdi_check_power_budget: " + "available power is %dmA", pwr_limit * USB_CFG_DESCR_PWR_UNIT); + + size = usb_parse_cfg_descr( + child_ud->usb_cfg_array[config_index], USB_CFG_DESCR_SIZE, + &cfg_descr, USB_CFG_DESCR_SIZE); + + if (size != USB_CFG_DESCR_SIZE) { + USB_DPRINTF_L2(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, + "get hub configuration descriptor failed"); + + return (USB_FAILURE); + } + + pwr_required = cfg_descr.bMaxPower; + + USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, + "usba_hubdi_check_power_budget: " + "child bmAttributes=0x%x bMaxPower=%d " + "with config_index=%d", cfg_descr.bmAttributes, + pwr_required, config_index); + + if (pwr_required > pwr_limit) { + USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG, hubd->h_log_handle, + "configuration %d for device %s %s at port %d " + "exceeds power available for this port, please " + "re-insert your device into another hub port which " + "has enough power", + config_index, + child_ud->usb_mfg_str, + child_ud->usb_product_str, + child_ud->usb_port); + + return (USB_FAILURE); + } + + return (USB_SUCCESS); +} + + +/* + * usba_hubdi_incr_power_budget: + * Increase the hub power budget value when a child device + * is removed from a bus-powered hub port. + */ +void +usba_hubdi_incr_power_budget(dev_info_t *dip, usba_device_t *child_ud) +{ + uint16_t pwr_value; + hubd_t *hubd = hubd_get_soft_state(dip); + + ASSERT(hubd != NULL); + + if (hubd->h_ignore_pwr_budget) { + + return; + } + + USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, + "usba_hubdi_incr_power_budget: " + "dip=0x%p child_ud=0x%p", dip, child_ud); + + mutex_enter(HUBD_MUTEX(hubd)); + if (hubd->h_local_pwr_on == B_TRUE) { + USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, + "usba_hubdi_incr_power_budget: " + "hub is local powered"); + mutex_exit(HUBD_MUTEX(hubd)); + + return; + } + mutex_exit(HUBD_MUTEX(hubd)); + + mutex_enter(&child_ud->usb_mutex); + if (child_ud->usb_pwr_from_hub == 0) { + mutex_exit(&child_ud->usb_mutex); + + return; + } + pwr_value = child_ud->usb_pwr_from_hub; + mutex_exit(&child_ud->usb_mutex); + + mutex_enter(HUBD_MUTEX(hubd)); + hubd->h_pwr_left += pwr_value; + + USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, + "usba_hubdi_incr_power_budget: " + "available power is %dmA, increased by %dmA", + hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT, + pwr_value * USB_CFG_DESCR_PWR_UNIT); + + mutex_exit(HUBD_MUTEX(hubd)); + + mutex_enter(&child_ud->usb_mutex); + child_ud->usb_pwr_from_hub = 0; + mutex_exit(&child_ud->usb_mutex); +} + + +/* + * usba_hubdi_decr_power_budget: + * Decrease the hub power budget value when a child device + * is inserted to a bus-powered hub port. + */ +void +usba_hubdi_decr_power_budget(dev_info_t *dip, usba_device_t *child_ud) +{ + uint16_t pwr_value; + size_t size; + usb_cfg_descr_t cfg_descr; + hubd_t *hubd = hubd_get_soft_state(dip); + + ASSERT(hubd != NULL); + + if (hubd->h_ignore_pwr_budget) { + + return; + } + + USB_DPRINTF_L4(DPRINT_MASK_ATTA, hubd->h_log_handle, + "usba_hubdi_decr_power_budget: " + "dip=0x%p child_ud=0x%p", dip, child_ud); + + mutex_enter(HUBD_MUTEX(hubd)); + if (hubd->h_local_pwr_on == B_TRUE) { + USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, + "usba_hubdi_decr_power_budget: " + "hub is local powered"); + mutex_exit(HUBD_MUTEX(hubd)); + + return; + } + mutex_exit(HUBD_MUTEX(hubd)); + + mutex_enter(&child_ud->usb_mutex); + if (child_ud->usb_pwr_from_hub > 0) { + mutex_exit(&child_ud->usb_mutex); + + return; + } + mutex_exit(&child_ud->usb_mutex); + + size = usb_parse_cfg_descr( + child_ud->usb_cfg, child_ud->usb_cfg_length, + &cfg_descr, USB_CFG_DESCR_SIZE); + ASSERT(size == USB_CFG_DESCR_SIZE); + + mutex_enter(HUBD_MUTEX(hubd)); + pwr_value = cfg_descr.bMaxPower; + hubd->h_pwr_left -= pwr_value; + ASSERT(hubd->h_pwr_left >= 0); + + USB_DPRINTF_L3(DPRINT_MASK_ATTA, hubd->h_log_handle, + "usba_hubdi_decr_power_budget: " + "available power is %dmA, decreased by %dmA", + hubd->h_pwr_left * USB_CFG_DESCR_PWR_UNIT, + pwr_value * USB_CFG_DESCR_PWR_UNIT); + + mutex_exit(HUBD_MUTEX(hubd)); + + mutex_enter(&child_ud->usb_mutex); + child_ud->usb_pwr_from_hub = pwr_value; + mutex_exit(&child_ud->usb_mutex); +}
--- a/usr/src/uts/common/io/usb/usba/usbai_util.c Sun Nov 27 19:11:23 2005 -0800 +++ b/usr/src/uts/common/io/usb/usba/usbai_util.c Mon Nov 28 00:08:21 2005 -0800 @@ -562,6 +562,7 @@ uint_t cfg_index = (uint_t)((uintptr_t)(request->arg)); size_t size; usb_cfg_descr_t confdescr; + dev_info_t *pdip; usba_device = usba_get_usba_device(dip); @@ -582,6 +583,40 @@ return (USB_BUSY); } + /* + * check if the configuration meets the + * power budget requirement + */ + if (usba_is_root_hub(dip)) { + /* + * root hub should never be multi-configured. + * the code is here just to ensure + */ + usba_release_ph_data(ph_impl); + + return (USB_FAILURE); + } + pdip = ddi_get_parent(dip); + + /* + * increase the power budget value back to the unconfigured + * state to eliminate the influence of the old configuration + * before checking the new configuration; but remember to + * make a decrement before leaving this routine to restore + * the power consumption state of the device no matter it + * is in the new or old configuration + */ + usba_hubdi_incr_power_budget(pdip, usba_device); + + if ((usba_hubdi_check_power_budget(pdip, usba_device, + cfg_index)) != USB_SUCCESS) { + usba_hubdi_decr_power_budget(pdip, usba_device); + + usba_release_ph_data(ph_impl); + + return (USB_FAILURE); + } + size = usb_parse_cfg_descr(usba_device->usb_cfg_array[cfg_index], USB_CFG_DESCR_SIZE, &confdescr, USB_CFG_DESCR_SIZE); @@ -612,6 +647,14 @@ "configuration#", usba_device->usb_cfg_value); } + /* + * usba_device->usb_cfg always stores current configuration + * descriptor no matter SET_CFG request succeeded or not, + * so usba_hubdi_decr_power_budget can be done regardless + * of rval above + */ + usba_hubdi_decr_power_budget(pdip, usba_device); + USB_DPRINTF_L4(DPRINT_MASK_USBA, usbai_log_handle, "rval=%d, cb_flags=%d, cr=%d", rval, cb_flags, completion_reason);
--- a/usr/src/uts/common/sys/usb/hubd/hub.h Sun Nov 27 19:11:23 2005 -0800 +++ b/usr/src/uts/common/sys/usb/hubd/hub.h Mon Nov 28 00:08:21 2005 -0800 @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -82,16 +82,21 @@ #define HUB_CHANGE_STATUS 0x01 /* Class Specific bmRequestType values Table 11-10 */ -#define HANDLE_PORT_FEATURE (USB_DEV_REQ_HOST_TO_DEV \ - |USB_DEV_REQ_TYPE_CLASS \ - |USB_DEV_REQ_RCPT_OTHER) +#define HUB_HANDLE_PORT_FEATURE_TYPE (USB_DEV_REQ_HOST_TO_DEV \ + |USB_DEV_REQ_TYPE_CLASS \ + |USB_DEV_REQ_RCPT_OTHER) + +#define HUB_GET_PORT_STATUS_TYPE (USB_DEV_REQ_DEV_TO_HOST \ + |USB_DEV_REQ_TYPE_CLASS \ + |USB_DEV_REQ_RCPT_OTHER) -#define GET_PORT_STATUS (USB_DEV_REQ_DEV_TO_HOST \ - |USB_DEV_REQ_TYPE_CLASS \ - |USB_DEV_REQ_RCPT_OTHER) +#define HUB_CLASS_REQ_TYPE (USB_DEV_REQ_DEV_TO_HOST \ + |USB_DEV_REQ_TYPE_CLASS) -#define HUB_CLASS_REQ (USB_DEV_REQ_DEV_TO_HOST \ - |USB_DEV_REQ_TYPE_CLASS) +/* bmRequestType for getting device status */ +#define HUB_GET_DEVICE_STATUS_TYPE (USB_DEV_REQ_DEV_TO_HOST \ + |USB_DEV_REQ_TYPE_STANDARD \ + |USB_DEV_REQ_RCPT_DEV) /* Port Status Field Bits - Table 11-15 */ #define PORT_STATUS_CCS 0x0001 /* port connection status */
--- a/usr/src/uts/common/sys/usb/hubd/hubdvar.h Sun Nov 27 19:11:23 2005 -0800 +++ b/usr/src/uts/common/sys/usb/hubd/hubdvar.h Mon Nov 28 00:08:21 2005 -0800 @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -224,6 +224,22 @@ boolean_t h_cleanup_enabled; boolean_t h_cleanup_needed; boolean_t h_cleanup_active; + + /* + * for power budget support + * h_pwr_limit and h_pwr_left are expressed + * in 2mA units + */ + boolean_t h_local_pwr_capable; + boolean_t h_local_pwr_on; + uint16_t h_pwr_limit; /* per port pwr limit */ + int16_t h_pwr_left; /* limit on the whole hub */ + + /* + * conf file override to power budget property + * if 1, power budget is disabled + */ + boolean_t h_ignore_pwr_budget; } hubd_t; _NOTE(MUTEX_PROTECTS_DATA(hubd::h_mutex, hubd)) @@ -238,6 +254,7 @@ hubd::h_instance hubd::h_hubpm hubd::h_dip + hubd::h_ignore_pwr_budget )) _NOTE(SCHEME_PROTECTS_DATA("stable data", usb_ep_descr)) @@ -251,6 +268,7 @@ #define HUBD_LOCKS_DONE 0x0001 #define HUBD_HUBDI_REGISTERED 0x0002 #define HUBD_MINOR_NODE_CREATED 0x0004 +#define HUBD_CHILDREN_CREATED 0x0008 #define HUBD_EVENTS_REGISTERED 0x0020 /* @@ -344,6 +362,20 @@ /* enumeration timeout */ #define HUBDI_ENUM_TIMEOUT 1 /* 1 second */ +/* power budget unit in mA */ +#define USB_PWR_UNIT_LOAD 100 + +/* power values in 100mA units */ +#define USB_HIGH_PWR_VALUE 5 +#define USB_LOW_PWR_VALUE 1 + +/* + * According to section 9.6.3 of USB 2.0 spec, + * bMaxPower in the device configuration descriptor + * is expressed in 2mA units + */ +#define USB_CFG_DESCR_PWR_UNIT 2 + #ifdef __cplusplus } #endif
--- a/usr/src/uts/common/sys/usb/usba/hubdi.h Sun Nov 27 19:11:23 2005 -0800 +++ b/usr/src/uts/common/sys/usb/usba/hubdi.h Mon Nov 28 00:08:21 2005 -0800 @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -33,6 +33,8 @@ extern "C" { #endif +#include <sys/usb/usba/usba_types.h> + /* USBA calls these: */ void usba_hubdi_initialization(); void usba_hubdi_destruction(); @@ -60,6 +62,11 @@ usb_dev_descr_t *); int usba_hubdi_unbind_root_hub(dev_info_t *); +/* power budget control routines */ +void usba_hubdi_incr_power_budget(dev_info_t *, usba_device_t *); +void usba_hubdi_decr_power_budget(dev_info_t *, usba_device_t *); +int usba_hubdi_check_power_budget(dev_info_t *, usba_device_t *, uint_t); + #ifdef __cplusplus } #endif
--- a/usr/src/uts/common/sys/usb/usba/usba_types.h Sun Nov 27 19:11:23 2005 -0800 +++ b/usr/src/uts/common/sys/usb/usba/usba_types.h Mon Nov 28 00:08:21 2005 -0800 @@ -295,6 +295,12 @@ uchar_t usb_n_cfgs; uchar_t usb_n_ifs; + /* + * power drawn from hub, if > 0, the power has been + * subtracted from the parent hub's power budget + */ + uint16_t usb_pwr_from_hub; + /* ref count, if > 0, this structure is in use */ int usb_ref_count;