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; 29 30 usb_lock_device(udev); 31 if (udev->state == USB_STATE_NOTATTACHED) { 32 usb_unlock_device(udev); 33 return -ENODEV; 34 } 35 36 if (udev->state == USB_STATE_SUSPENDED || 37 udev->offload_at_suspend) { 38 usb_unlock_device(udev); 39 return -EBUSY; 40 } 41 42 /* 43 * offload_usage could only be modified when the device is active, since 44 * it will alter the suspend flow of the device. 45 */ 46 ret = usb_autoresume_device(udev); 47 if (ret < 0) { 48 usb_unlock_device(udev); 49 return ret; 50 } 51 52 udev->offload_usage++; 53 usb_autosuspend_device(udev); 54 usb_unlock_device(udev); 55 56 return ret; 57 } 58 EXPORT_SYMBOL_GPL(usb_offload_get); 59 60 /** 61 * usb_offload_put - drop the offload_usage of a USB device 62 * @udev: the USB device to drop its offload_usage 63 * 64 * The inverse operation of usb_offload_get, which drops the offload_usage of 65 * a USB device. This information allows the USB driver to adjust its power 66 * management policy based on offload activity. 67 * 68 * Return: 0 on success. A negative error code otherwise. 69 */ 70 int usb_offload_put(struct usb_device *udev) 71 { 72 int ret; 73 74 usb_lock_device(udev); 75 if (udev->state == USB_STATE_NOTATTACHED) { 76 usb_unlock_device(udev); 77 return -ENODEV; 78 } 79 80 if (udev->state == USB_STATE_SUSPENDED || 81 udev->offload_at_suspend) { 82 usb_unlock_device(udev); 83 return -EBUSY; 84 } 85 86 /* 87 * offload_usage could only be modified when the device is active, since 88 * it will alter the suspend flow of the device. 89 */ 90 ret = usb_autoresume_device(udev); 91 if (ret < 0) { 92 usb_unlock_device(udev); 93 return ret; 94 } 95 96 /* Drop the count when it wasn't 0, ignore the operation otherwise. */ 97 if (udev->offload_usage) 98 udev->offload_usage--; 99 usb_autosuspend_device(udev); 100 usb_unlock_device(udev); 101 102 return ret; 103 } 104 EXPORT_SYMBOL_GPL(usb_offload_put); 105 106 /** 107 * usb_offload_check - check offload activities on a USB device 108 * @udev: the USB device to check its offload activity. 109 * 110 * Check if there are any offload activity on the USB device right now. This 111 * information could be used for power management or other forms of resource 112 * management. 113 * 114 * The caller must hold @udev's device lock. In addition, the caller should 115 * ensure downstream usb devices are all either suspended or marked as 116 * "offload_at_suspend" to ensure the correctness of the return value. 117 * 118 * Returns true on any offload activity, false otherwise. 119 */ 120 bool usb_offload_check(struct usb_device *udev) __must_hold(&udev->dev->mutex) 121 { 122 struct usb_device *child; 123 bool active; 124 int port1; 125 126 usb_hub_for_each_child(udev, port1, child) { 127 usb_lock_device(child); 128 active = usb_offload_check(child); 129 usb_unlock_device(child); 130 if (active) 131 return true; 132 } 133 134 return !!udev->offload_usage; 135 } 136 EXPORT_SYMBOL_GPL(usb_offload_check); 137