| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * offload.c - USB offload related functions
- *
- * Copyright (c) 2025, Google LLC.
- *
- * Author: Guan-Yu Lin
- */
- #include <linux/usb.h>
- #include "usb.h"
- /**
- * usb_offload_get - increment the offload_usage of a USB device
- * @udev: the USB device to increment its offload_usage
- *
- * Incrementing the offload_usage of a usb_device indicates that offload is
- * enabled on this usb_device; that is, another entity is actively handling USB
- * transfers. This information allows the USB driver to adjust its power
- * management policy based on offload activity.
- *
- * Return: 0 on success. A negative error code otherwise.
- */
- int usb_offload_get(struct usb_device *udev)
- {
- int ret = 0;
- if (!usb_get_dev(udev))
- return -ENODEV;
- if (pm_runtime_get_if_active(&udev->dev) != 1) {
- ret = -EBUSY;
- goto err_rpm;
- }
- spin_lock(&udev->offload_lock);
- if (udev->offload_pm_locked) {
- ret = -EAGAIN;
- goto err;
- }
- udev->offload_usage++;
- err:
- spin_unlock(&udev->offload_lock);
- pm_runtime_put_autosuspend(&udev->dev);
- err_rpm:
- usb_put_dev(udev);
- return ret;
- }
- EXPORT_SYMBOL_GPL(usb_offload_get);
- /**
- * usb_offload_put - drop the offload_usage of a USB device
- * @udev: the USB device to drop its offload_usage
- *
- * The inverse operation of usb_offload_get, which drops the offload_usage of
- * a USB device. This information allows the USB driver to adjust its power
- * management policy based on offload activity.
- *
- * Return: 0 on success. A negative error code otherwise.
- */
- int usb_offload_put(struct usb_device *udev)
- {
- int ret = 0;
- if (!usb_get_dev(udev))
- return -ENODEV;
- if (pm_runtime_get_if_active(&udev->dev) != 1) {
- ret = -EBUSY;
- goto err_rpm;
- }
- spin_lock(&udev->offload_lock);
- if (udev->offload_pm_locked) {
- ret = -EAGAIN;
- goto err;
- }
- /* Drop the count when it wasn't 0, ignore the operation otherwise. */
- if (udev->offload_usage)
- udev->offload_usage--;
- err:
- spin_unlock(&udev->offload_lock);
- pm_runtime_put_autosuspend(&udev->dev);
- err_rpm:
- usb_put_dev(udev);
- return ret;
- }
- EXPORT_SYMBOL_GPL(usb_offload_put);
- /**
- * usb_offload_check - check offload activities on a USB device
- * @udev: the USB device to check its offload activity.
- *
- * Check if there are any offload activity on the USB device right now. This
- * information could be used for power management or other forms of resource
- * management.
- *
- * The caller must hold @udev's device lock. In addition, the caller should
- * ensure the device itself and the downstream usb devices are all marked as
- * "offload_pm_locked" to ensure the correctness of the return value.
- *
- * Returns true on any offload activity, false otherwise.
- */
- bool usb_offload_check(struct usb_device *udev) __must_hold(&udev->dev->mutex)
- {
- struct usb_device *child;
- bool active = false;
- int port1;
- if (udev->offload_usage)
- return true;
- usb_hub_for_each_child(udev, port1, child) {
- usb_lock_device(child);
- active = usb_offload_check(child);
- usb_unlock_device(child);
- if (active)
- break;
- }
- return active;
- }
- EXPORT_SYMBOL_GPL(usb_offload_check);
- /**
- * usb_offload_set_pm_locked - set the PM lock state of a USB device
- * @udev: the USB device to modify
- * @locked: the new lock state
- *
- * Setting @locked to true prevents offload_usage from being modified. This
- * ensures that offload activities cannot be started or stopped during critical
- * power management transitions, maintaining a stable state for the duration
- * of the transition.
- */
- void usb_offload_set_pm_locked(struct usb_device *udev, bool locked)
- {
- spin_lock(&udev->offload_lock);
- udev->offload_pm_locked = locked;
- spin_unlock(&udev->offload_lock);
- }
- EXPORT_SYMBOL_GPL(usb_offload_set_pm_locked);
|