4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
24 */
25
26 /*
27 * USBA: Solaris USB Architecture support for the hub
28 * including root hub
29 * Most of the code for hubd resides in this file and
30 * is shared between the HCD root hub support and hubd
31 */
32 #define USBA_FRAMEWORK
33 #include <sys/usb/usba.h>
34 #include <sys/usb/usba/usba_devdb.h>
35 #include <sys/sunndi.h>
36 #include <sys/usb/usba/usba_impl.h>
37 #include <sys/usb/usba/usba_types.h>
38 #include <sys/usb/usba/hubdi.h>
39 #include <sys/usb/usba/hcdi_impl.h>
40 #include <sys/usb/hubd/hub.h>
41 #include <sys/usb/hubd/hubdvar.h>
42 #include <sys/usb/hubd/hubd_impl.h>
43 #include <sys/kobj.h>
732 * NOTE: suspend here means going to lower power, not CPR suspend.
733 */
734 static int
735 hubd_can_suspend(hubd_t *hubd)
736 {
737 hub_power_t *hubpm;
738 int total_power = 0;
739 usb_port_t port;
740
741 hubpm = hubd->h_hubpm;
742
743 if (DEVI_IS_DETACHING(hubd->h_dip)) {
744
745 return (USB_SUCCESS);
746 }
747
748 /*
749 * Don't go to lower power if haven't been at full power for enough
750 * time to let hotplug thread kickoff.
751 */
752 if (ddi_get_time() < (hubpm->hubp_time_at_full_power +
753 hubpm->hubp_min_pm_threshold)) {
754
755 return (USB_FAILURE);
756 }
757
758 for (port = 1; (total_power == 0) &&
759 (port <= hubd->h_hub_descr.bNbrPorts); port++) {
760 total_power += hubpm->hubp_child_pwrstate[port];
761 }
762
763 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
764 "hubd_can_suspend: %d", total_power);
765
766 return (total_power ? USB_FAILURE : USB_SUCCESS);
767 }
768
769
770 /*
771 * resume port depending on current device state
772 */
1703 hub_power_t *hubpm;
1704 int rval;
1705
1706 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, "hubd_pwrlvl3");
1707
1708 hubpm = hubd->h_hubpm;
1709 switch (hubd->h_dev_state) {
1710 case USB_DEV_PWRED_DOWN:
1711 ASSERT(hubpm->hubp_current_power == USB_DEV_OS_PWR_OFF);
1712 if (usba_is_root_hub(hubd->h_dip)) {
1713 /* implement global resume here */
1714 USB_DPRINTF_L2(DPRINT_MASK_PM,
1715 hubd->h_log_handle,
1716 "Global Resume: Not Yet Implemented");
1717 }
1718 /* Issue USB D0 command to the device here */
1719 rval = usb_set_device_pwrlvl0(hubd->h_dip);
1720 ASSERT(rval == USB_SUCCESS);
1721 hubd->h_dev_state = USB_DEV_ONLINE;
1722 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
1723 hubpm->hubp_time_at_full_power = ddi_get_time();
1724 hubd_start_polling(hubd, 0);
1725
1726 /* FALLTHRU */
1727 case USB_DEV_ONLINE:
1728 /* we are already in full power */
1729
1730 /* FALLTHRU */
1731 case USB_DEV_DISCONNECTED:
1732 case USB_DEV_SUSPENDED:
1733 /*
1734 * PM framework tries to put you in full power
1735 * during system shutdown. If we are disconnected
1736 * return success. Also, we should not change state
1737 * when we are disconnected or suspended or about to
1738 * transition to that state
1739 */
1740
1741 return (USB_SUCCESS);
1742 default:
1743 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
3695
3696 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3697 "hubd_hotplug_thread: "
3698 "bus_power in progress/hotplugging undesirable - quit");
3699
3700 return;
3701 }
3702 mutex_exit(HUBD_MUTEX(hubd));
3703
3704 ndi_hold_devi(hdip); /* so we don't race with detach */
3705
3706 mutex_enter(HUBD_MUTEX(hubd));
3707
3708 /* is this the root hub? */
3709 if (hdip == rh_dip) {
3710 if (hubd->h_dev_state == USB_DEV_PWRED_DOWN) {
3711 hubpm = hubd->h_hubpm;
3712
3713 /* mark the root hub as full power */
3714 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
3715 hubpm->hubp_time_at_full_power = ddi_get_time();
3716 mutex_exit(HUBD_MUTEX(hubd));
3717
3718 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3719 "hubd_hotplug_thread: call pm_power_has_changed");
3720
3721 (void) pm_power_has_changed(hdip, 0,
3722 USB_DEV_OS_FULL_PWR);
3723
3724 mutex_enter(HUBD_MUTEX(hubd));
3725 hubd->h_dev_state = USB_DEV_ONLINE;
3726 }
3727
3728 } else {
3729 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3730 "hubd_hotplug_thread: not root hub");
3731 }
3732
3733 mutex_exit(HUBD_MUTEX(hubd));
3734
3735 /*
7256 /*
7257 * Power management
7258 *
7259 * create the pm components required for power management
7260 */
7261 static void
7262 hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd)
7263 {
7264 hub_power_t *hubpm;
7265
7266 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7267 "hubd_create_pm_components: Begin");
7268
7269 /* Allocate the state structure */
7270 hubpm = kmem_zalloc(sizeof (hub_power_t), KM_SLEEP);
7271
7272 hubd->h_hubpm = hubpm;
7273 hubpm->hubp_hubd = hubd;
7274 hubpm->hubp_pm_capabilities = 0;
7275 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
7276 hubpm->hubp_time_at_full_power = ddi_get_time();
7277 hubpm->hubp_min_pm_threshold = hubdi_min_pm_threshold;
7278
7279 /* alloc memory to save power states of children */
7280 hubpm->hubp_child_pwrstate = (uint8_t *)
7281 kmem_zalloc(MAX_PORTS + 1, KM_SLEEP);
7282
7283 /*
7284 * if the enable remote wakeup fails
7285 * we still want to enable
7286 * parent notification so we can PM the children
7287 */
7288 usb_enable_parent_notification(dip);
7289
7290 if (usb_handle_remote_wakeup(dip,
7291 USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
7292 uint_t pwr_states;
7293
7294 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
7295 "hubd_create_pm_components: "
7296 "Remote Wakeup Enabled");
7297
8692
8693 if (hubd_wait_for_hotplug_exit(hubd) == USB_FAILURE) {
8694 /* we got woken up because of a timeout */
8695 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
8696 hubd->h_log_handle, "Time out when resetting the device"
8697 " %s%d. Please disconnect and reconnect this device.",
8698 devname, devinst);
8699
8700 goto Fail;
8701 }
8702
8703 hubd->h_hotplug_thread++;
8704
8705 /* is this the root hub? */
8706 if ((hdip == rh_dip) &&
8707 (hubd->h_dev_state == USB_DEV_PWRED_DOWN)) {
8708 hubpm = hubd->h_hubpm;
8709
8710 /* mark the root hub as full power */
8711 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
8712 hubpm->hubp_time_at_full_power = ddi_get_time();
8713 mutex_exit(HUBD_MUTEX(hubd));
8714
8715 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8716 "hubd_reset_thread: call pm_power_has_changed");
8717
8718 (void) pm_power_has_changed(hdip, 0,
8719 USB_DEV_OS_FULL_PWR);
8720
8721 mutex_enter(HUBD_MUTEX(hubd));
8722 hubd->h_dev_state = USB_DEV_ONLINE;
8723 }
8724
8725 mutex_exit(HUBD_MUTEX(hubd));
8726
8727 /*
8728 * this ensures one reset activity per system at a time.
8729 * we enter the parent PCI node to have this serialization.
8730 * this also excludes ioctls and deathrow thread
8731 */
8732 ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
|
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
25 */
26
27 /*
28 * USBA: Solaris USB Architecture support for the hub
29 * including root hub
30 * Most of the code for hubd resides in this file and
31 * is shared between the HCD root hub support and hubd
32 */
33 #define USBA_FRAMEWORK
34 #include <sys/usb/usba.h>
35 #include <sys/usb/usba/usba_devdb.h>
36 #include <sys/sunndi.h>
37 #include <sys/usb/usba/usba_impl.h>
38 #include <sys/usb/usba/usba_types.h>
39 #include <sys/usb/usba/hubdi.h>
40 #include <sys/usb/usba/hcdi_impl.h>
41 #include <sys/usb/hubd/hub.h>
42 #include <sys/usb/hubd/hubdvar.h>
43 #include <sys/usb/hubd/hubd_impl.h>
44 #include <sys/kobj.h>
733 * NOTE: suspend here means going to lower power, not CPR suspend.
734 */
735 static int
736 hubd_can_suspend(hubd_t *hubd)
737 {
738 hub_power_t *hubpm;
739 int total_power = 0;
740 usb_port_t port;
741
742 hubpm = hubd->h_hubpm;
743
744 if (DEVI_IS_DETACHING(hubd->h_dip)) {
745
746 return (USB_SUCCESS);
747 }
748
749 /*
750 * Don't go to lower power if haven't been at full power for enough
751 * time to let hotplug thread kickoff.
752 */
753 if (gethrtime() < (hubpm->hubp_time_at_full_power +
754 hubpm->hubp_min_pm_threshold)) {
755
756 return (USB_FAILURE);
757 }
758
759 for (port = 1; (total_power == 0) &&
760 (port <= hubd->h_hub_descr.bNbrPorts); port++) {
761 total_power += hubpm->hubp_child_pwrstate[port];
762 }
763
764 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
765 "hubd_can_suspend: %d", total_power);
766
767 return (total_power ? USB_FAILURE : USB_SUCCESS);
768 }
769
770
771 /*
772 * resume port depending on current device state
773 */
1704 hub_power_t *hubpm;
1705 int rval;
1706
1707 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle, "hubd_pwrlvl3");
1708
1709 hubpm = hubd->h_hubpm;
1710 switch (hubd->h_dev_state) {
1711 case USB_DEV_PWRED_DOWN:
1712 ASSERT(hubpm->hubp_current_power == USB_DEV_OS_PWR_OFF);
1713 if (usba_is_root_hub(hubd->h_dip)) {
1714 /* implement global resume here */
1715 USB_DPRINTF_L2(DPRINT_MASK_PM,
1716 hubd->h_log_handle,
1717 "Global Resume: Not Yet Implemented");
1718 }
1719 /* Issue USB D0 command to the device here */
1720 rval = usb_set_device_pwrlvl0(hubd->h_dip);
1721 ASSERT(rval == USB_SUCCESS);
1722 hubd->h_dev_state = USB_DEV_ONLINE;
1723 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
1724 hubpm->hubp_time_at_full_power = gethrtime();
1725 hubd_start_polling(hubd, 0);
1726
1727 /* FALLTHRU */
1728 case USB_DEV_ONLINE:
1729 /* we are already in full power */
1730
1731 /* FALLTHRU */
1732 case USB_DEV_DISCONNECTED:
1733 case USB_DEV_SUSPENDED:
1734 /*
1735 * PM framework tries to put you in full power
1736 * during system shutdown. If we are disconnected
1737 * return success. Also, we should not change state
1738 * when we are disconnected or suspended or about to
1739 * transition to that state
1740 */
1741
1742 return (USB_SUCCESS);
1743 default:
1744 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
3696
3697 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3698 "hubd_hotplug_thread: "
3699 "bus_power in progress/hotplugging undesirable - quit");
3700
3701 return;
3702 }
3703 mutex_exit(HUBD_MUTEX(hubd));
3704
3705 ndi_hold_devi(hdip); /* so we don't race with detach */
3706
3707 mutex_enter(HUBD_MUTEX(hubd));
3708
3709 /* is this the root hub? */
3710 if (hdip == rh_dip) {
3711 if (hubd->h_dev_state == USB_DEV_PWRED_DOWN) {
3712 hubpm = hubd->h_hubpm;
3713
3714 /* mark the root hub as full power */
3715 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
3716 hubpm->hubp_time_at_full_power = gethrtime();
3717 mutex_exit(HUBD_MUTEX(hubd));
3718
3719 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3720 "hubd_hotplug_thread: call pm_power_has_changed");
3721
3722 (void) pm_power_has_changed(hdip, 0,
3723 USB_DEV_OS_FULL_PWR);
3724
3725 mutex_enter(HUBD_MUTEX(hubd));
3726 hubd->h_dev_state = USB_DEV_ONLINE;
3727 }
3728
3729 } else {
3730 USB_DPRINTF_L4(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
3731 "hubd_hotplug_thread: not root hub");
3732 }
3733
3734 mutex_exit(HUBD_MUTEX(hubd));
3735
3736 /*
7257 /*
7258 * Power management
7259 *
7260 * create the pm components required for power management
7261 */
7262 static void
7263 hubd_create_pm_components(dev_info_t *dip, hubd_t *hubd)
7264 {
7265 hub_power_t *hubpm;
7266
7267 USB_DPRINTF_L4(DPRINT_MASK_PM, hubd->h_log_handle,
7268 "hubd_create_pm_components: Begin");
7269
7270 /* Allocate the state structure */
7271 hubpm = kmem_zalloc(sizeof (hub_power_t), KM_SLEEP);
7272
7273 hubd->h_hubpm = hubpm;
7274 hubpm->hubp_hubd = hubd;
7275 hubpm->hubp_pm_capabilities = 0;
7276 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
7277 hubpm->hubp_time_at_full_power = gethrtime();
7278 hubpm->hubp_min_pm_threshold = hubdi_min_pm_threshold * NANOSEC;
7279
7280 /* alloc memory to save power states of children */
7281 hubpm->hubp_child_pwrstate = (uint8_t *)
7282 kmem_zalloc(MAX_PORTS + 1, KM_SLEEP);
7283
7284 /*
7285 * if the enable remote wakeup fails
7286 * we still want to enable
7287 * parent notification so we can PM the children
7288 */
7289 usb_enable_parent_notification(dip);
7290
7291 if (usb_handle_remote_wakeup(dip,
7292 USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS) {
7293 uint_t pwr_states;
7294
7295 USB_DPRINTF_L2(DPRINT_MASK_PM, hubd->h_log_handle,
7296 "hubd_create_pm_components: "
7297 "Remote Wakeup Enabled");
7298
8693
8694 if (hubd_wait_for_hotplug_exit(hubd) == USB_FAILURE) {
8695 /* we got woken up because of a timeout */
8696 USB_DPRINTF_L0(DPRINT_MASK_HOTPLUG,
8697 hubd->h_log_handle, "Time out when resetting the device"
8698 " %s%d. Please disconnect and reconnect this device.",
8699 devname, devinst);
8700
8701 goto Fail;
8702 }
8703
8704 hubd->h_hotplug_thread++;
8705
8706 /* is this the root hub? */
8707 if ((hdip == rh_dip) &&
8708 (hubd->h_dev_state == USB_DEV_PWRED_DOWN)) {
8709 hubpm = hubd->h_hubpm;
8710
8711 /* mark the root hub as full power */
8712 hubpm->hubp_current_power = USB_DEV_OS_FULL_PWR;
8713 hubpm->hubp_time_at_full_power = gethrtime();
8714 mutex_exit(HUBD_MUTEX(hubd));
8715
8716 USB_DPRINTF_L3(DPRINT_MASK_HOTPLUG, hubd->h_log_handle,
8717 "hubd_reset_thread: call pm_power_has_changed");
8718
8719 (void) pm_power_has_changed(hdip, 0,
8720 USB_DEV_OS_FULL_PWR);
8721
8722 mutex_enter(HUBD_MUTEX(hubd));
8723 hubd->h_dev_state = USB_DEV_ONLINE;
8724 }
8725
8726 mutex_exit(HUBD_MUTEX(hubd));
8727
8728 /*
8729 * this ensures one reset activity per system at a time.
8730 * we enter the parent PCI node to have this serialization.
8731 * this also excludes ioctls and deathrow thread
8732 */
8733 ndi_devi_enter(ddi_get_parent(rh_dip), &prh_circ);
|