1 // SPDX-License-Identifier: GPL-2.0 2 3 /* 4 * offload.c - USB offload related functions 5 * 6 * Copyright (c) 2025, Google LLC. 7 * 8 * Author: Guan-Yu Lin 9 */ 10 11 #include <linux/usb.h> 12 13 #include "usb.h" 14 15 /** 16 * usb_offload_get - increment the offload_usage of a USB device 17 * @udev: the USB device to increment its offload_usage 18 * 19 * Incrementing the offload_usage of a usb_device indicates that offload is 20 * enabled on this usb_device; that is, another entity is actively handling USB 21 * transfers. This information allows the USB driver to adjust its power 22 * management policy based on offload activity. 23 * 24 * Return: 0 on success. A negative error code otherwise. 25 */ 26 int usb_offload_get(struct usb_device *udev) 27 { 28 int ret = 0; 29 30 if (!usb_get_dev(udev)) 31 return -ENODEV; 32 33 if (pm_runtime_get_if_active(&udev->dev) != 1) { 34 ret = -EBUSY; 35 goto err_rpm; 36 } 37 38 spin_lock(&udev->offload_lock); 39 40 if (udev->offload_pm_locked) { 41 ret = -EAGAIN; 42 goto err; 43 } 44 45 udev->offload_usage++; 46 47 err: 48 spin_unlock(&udev->offload_lock); 49 pm_runtime_put_autosuspend(&udev->dev); 50 err_rpm: 51 usb_put_dev(udev); 52 53 return ret; 54 } 55 EXPORT_SYMBOL_GPL(usb_offload_get); 56 57 /** 58 * usb_offload_put - drop the offload_usage of a USB device 59 * @udev: the USB device to drop its offload_usage 60 * 61 * The inverse operation of usb_offload_get, which drops the offload_usage of 62 * a USB device. This information allows the USB driver to adjust its power 63 * management policy based on offload activity. 64 * 65 * Return: 0 on success. A negative error code otherwise. 66 */ 67 int usb_offload_put(struct usb_device *udev) 68 { 69 int ret = 0; 70 71 if (!usb_get_dev(udev)) 72 return -ENODEV; 73 74 if (pm_runtime_get_if_active(&udev->dev) != 1) { 75 ret = -EBUSY; 76 goto err_rpm; 77 } 78 79 spin_lock(&udev->offload_lock); 80 81 if (udev->offload_pm_locked) { 82 ret = -EAGAIN; 83 goto err; 84 } 85 86 /* Drop the count when it wasn't 0, ignore the operation otherwise. */ 87 if (udev->offload_usage) 88 udev->offload_usage--; 89 90 err: 91 spin_unlock(&udev->offload_lock); 92 pm_runtime_put_autosuspend(&udev->dev); 93 err_rpm: 94 usb_put_dev(udev); 95 96 return ret; 97 } 98 EXPORT_SYMBOL_GPL(usb_offload_put); 99 100 /** 101 * usb_offload_check - check offload activities on a USB device 102 * @udev: the USB device to check its offload activity. 103 * 104 * Check if there are any offload activity on the USB device right now. This 105 * information could be used for power management or other forms of resource 106 * management. 107 * 108 * The caller must hold @udev's device lock. In addition, the caller should 109 * ensure the device itself and the downstream usb devices are all marked as 110 * "offload_pm_locked" to ensure the correctness of the return value. 111 * 112 * Returns true on any offload activity, false otherwise. 113 */ 114 bool usb_offload_check(struct usb_device *udev) __must_hold(&udev->dev->mutex) 115 { 116 struct usb_device *child; 117 bool active = false; 118 int port1; 119 120 if (udev->offload_usage) 121 return true; 122 123 usb_hub_for_each_child(udev, port1, child) { 124 usb_lock_device(child); 125 active = usb_offload_check(child); 126 usb_unlock_device(child); 127 128 if (active) 129 break; 130 } 131 132 return active; 133 } 134 EXPORT_SYMBOL_GPL(usb_offload_check); 135 136 /** 137 * usb_offload_set_pm_locked - set the PM lock state of a USB device 138 * @udev: the USB device to modify 139 * @locked: the new lock state 140 * 141 * Setting @locked to true prevents offload_usage from being modified. This 142 * ensures that offload activities cannot be started or stopped during critical 143 * power management transitions, maintaining a stable state for the duration 144 * of the transition. 145 */ 146 void usb_offload_set_pm_locked(struct usb_device *udev, bool locked) 147 { 148 spin_lock(&udev->offload_lock); 149 udev->offload_pm_locked = locked; 150 spin_unlock(&udev->offload_lock); 151 } 152 EXPORT_SYMBOL_GPL(usb_offload_set_pm_locked); 153