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;