diff options
author | Jan200101 <sentrycraft123@gmail.com> | 2023-07-23 19:28:02 +0200 |
---|---|---|
committer | Jan200101 <sentrycraft123@gmail.com> | 2023-07-23 19:28:02 +0200 |
commit | 306751a80f02a58f090c77604abe8dcf31df5eae (patch) | |
tree | 9d8600f8defdaf36e6eb07d7815b506b8302c0fa /SOURCES/0001-Allow-to-set-custom-USB-pollrate-for-specific-device.patch | |
parent | e57ae98adb2244cb95c2570e6fc73b039b98f918 (diff) | |
download | kernel-fsync-306751a80f02a58f090c77604abe8dcf31df5eae.tar.gz kernel-fsync-306751a80f02a58f090c77604abe8dcf31df5eae.zip |
Adopt nobara kernel patches
Diffstat (limited to 'SOURCES/0001-Allow-to-set-custom-USB-pollrate-for-specific-device.patch')
-rw-r--r-- | SOURCES/0001-Allow-to-set-custom-USB-pollrate-for-specific-device.patch | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/SOURCES/0001-Allow-to-set-custom-USB-pollrate-for-specific-device.patch b/SOURCES/0001-Allow-to-set-custom-USB-pollrate-for-specific-device.patch new file mode 100644 index 0000000..ecd0304 --- /dev/null +++ b/SOURCES/0001-Allow-to-set-custom-USB-pollrate-for-specific-device.patch @@ -0,0 +1,258 @@ +From 498e88ae626be4f523063c8a7027b4b02eca31d2 Mon Sep 17 00:00:00 2001 +From: GloriousEggroll <gloriouseggroll@gmail.com> +Date: Tue, 17 Jan 2023 12:08:46 -0700 +Subject: [PATCH] Allow to set custom USB pollrate for specific devices like + so: usbcore.interrupt_interval_override=045e:00db:16,1bcf:0005:1 + +--- + .../admin-guide/kernel-parameters.txt | 8 + + drivers/usb/core/config.c | 170 +++++++++++++++++- + drivers/usb/core/usb.c | 1 + + drivers/usb/core/usb.h | 1 + + 4 files changed, 179 insertions(+), 1 deletion(-) + +diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt +index dbd26fde4..c9b8b80af 100644 +--- a/Documentation/admin-guide/kernel-parameters.txt ++++ b/Documentation/admin-guide/kernel-parameters.txt +@@ -6552,6 +6552,14 @@ + delay after resetting its port); + Example: quirks=0781:5580:bk,0a5c:5834:gij + ++ usbcore.interrupt_interval_override= ++ [USB] A list of USB devices for which a different polling ++ interval than the default shall be used on all interrupt-type ++ endpoints. The format is VendorID:ProductID:interval, with ++ the vendor and product ids specified hexadecimally, and the ++ interval decimally in milliseconds. ++ Example: interrupt_interval_override=045e:00db:16,1bcf:0005:2 ++ + usbhid.mousepoll= + [USBHID] The interval which mice are to be polled at. + +diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c +index 48bc8a481..84bd550ad 100644 +--- a/drivers/usb/core/config.c ++++ b/drivers/usb/core/config.c +@@ -19,6 +19,149 @@ + #define USB_MAXCONFIG 8 /* Arbitrary limit */ + + ++/* A struct associated with the interrupt_interval_override module parameter, representing ++ an user's choice to force a specific interrupt interval upon all interrupt endpoints of ++ a certain device. */ ++struct interrupt_interval_override { ++ /* The vendor ID of the device of which the interrupt interval shall be overridden */ ++ u16 vendor; ++ /* The product ID of the device of which the interrupt interval shall be overridden */ ++ u16 product; ++ /* The new interval measured in milliseconds that shall be given to all endpoints of type interrupt on said device */ ++ unsigned int interval; ++}; ++ ++static DEFINE_MUTEX(interrupt_interval_override_mutex); ++static char interrupt_interval_override_param[128]; ++static struct interrupt_interval_override *interrupt_interval_override_list = NULL; ++static size_t interrupt_interval_override_count = 0; ++ ++static int interrupt_interval_override_param_set(const char *value, const struct kernel_param *kp) ++{ ++ const char *p; ++ unsigned short vendor, product; ++ unsigned int interval; ++ struct interrupt_interval_override* list; ++ struct interrupt_interval_override param; ++ size_t count, max_count, i, len; ++ int err, res; ++ ++ mutex_lock(&interrupt_interval_override_mutex); ++ ++ if (!value || !*value) { ++ /* Unset the current variable. */ ++ kfree(interrupt_interval_override_list); ++ interrupt_interval_override_list = NULL; ++ interrupt_interval_override_count = 0; ++ param_set_copystring(value, kp); /* Does not fail: the empty string is short enough to fit. */ ++ mutex_unlock(&interrupt_interval_override_mutex); ++ return 0; ++ } ++ ++ /* Compute an upper bound on the amount of entries we need. */ ++ for (max_count = 1, i = 0; value[i]; i++) { ++ if (value[i] == ',') ++ max_count++; ++ } ++ ++ /* Ensure we can allocate enough memory before overwriting the global variables. */ ++ list = kcalloc(max_count, ++ sizeof(struct interrupt_interval_override), ++ GFP_KERNEL); ++ ++ if (!list) { ++ mutex_unlock(&interrupt_interval_override_mutex); ++ return -ENOMEM; ++ } ++ ++ err = param_set_copystring(value, kp); ++ if (err) { ++ kfree(list); ++ mutex_unlock(&interrupt_interval_override_mutex); ++ return err; ++ } ++ ++ /* Parse the parameter. Example of a valid parameter: 045e:00db:16,1bcf:0005:2 */ ++ for (count = 0, p = (const char*)value; p && *p;) { ++ res = sscanf(p, "%hx:%hx:%d%zn", &vendor, &product, &interval, &len); ++ ++ /* Check whether all variables (vendor, product, interval, len) were assigned. ++ %zn does not increase the assignment count, so we need to check for value 3 instead of 4. ++ %zn does not consume input either, so setting len shouldn't fail if interval has been properly set. */ ++ if (res != 3) { ++ pr_warn("Error while parsing USB interrupt interval override parameter %s.\n", value); ++ break; ++ } ++ ++ param.vendor = (u16)vendor; ++ param.product = (u16)product; ++ param.interval = interval; ++ list[count++] = param; ++ ++ p += len; ++ if (*p == ',' && *(p+1) != '\0') { ++ p++; ++ continue; ++ } else if(*p == '\0' || (*p == '\n' && *(p+1) == '\0')) { ++ break; ++ } else { ++ pr_warn("Error while parsing USB interrupt interval override parameter %s.\n", value); ++ break; ++ } ++ } ++ ++ /* Overwrite the global variables with the local ones. */ ++ kfree(interrupt_interval_override_list); ++ interrupt_interval_override_list = list; ++ interrupt_interval_override_count = count; ++ mutex_unlock(&interrupt_interval_override_mutex); ++ return 0; ++} ++ ++static const struct kernel_param_ops interrupt_interval_override_param_ops = { ++ .set = interrupt_interval_override_param_set, ++ .get = param_get_string, ++}; ++ ++static struct kparam_string interrupt_interval_override_param_string = { ++ .maxlen = sizeof(interrupt_interval_override_param), ++ .string = interrupt_interval_override_param, ++}; ++ ++device_param_cb(interrupt_interval_override, ++ &interrupt_interval_override_param_ops, ++ &interrupt_interval_override_param_string, ++ 0644); ++MODULE_PARM_DESC(interrupt_interval_override, ++ "Override the polling interval of all interrupt-type endpoints of a specific USB" ++ " device by specifying interrupt_interval_override=vendorID:productID:interval."); ++ ++/* Given an USB device, this checks whether the user has specified they want to override the interrupt ++ polling interval on all interrupt-type endpoints of said device. ++ ++ This function returns the user-desired amount of milliseconds between interrupts on said endpoint. ++ If this function returns zero, the device-requested interrupt interval should be used. */ ++static unsigned int usb_check_interrupt_interval_override(struct usb_device* udev) ++{ ++ size_t i; ++ unsigned int res; ++ u16 vendor = le16_to_cpu(udev->descriptor.idVendor); ++ u16 product = le16_to_cpu(udev->descriptor.idProduct); ++ ++ mutex_lock(&interrupt_interval_override_mutex); ++ for (i = 0; i < interrupt_interval_override_count; i++) { ++ if (interrupt_interval_override_list[i].vendor == vendor ++ && interrupt_interval_override_list[i].product == product) { ++ ++ res = interrupt_interval_override_list[i].interval; ++ mutex_unlock(&interrupt_interval_override_mutex); ++ return res; ++ } ++ } ++ mutex_unlock(&interrupt_interval_override_mutex); ++ return 0; ++} ++ + static inline const char *plural(int n) + { + return (n == 1 ? "" : "s"); +@@ -261,7 +404,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, + struct usb_endpoint_descriptor *d; + struct usb_host_endpoint *endpoint; + int n, i, j, retval; +- unsigned int maxp; ++ unsigned int maxp, ival; + const unsigned short *maxpacket_maxes; + + d = (struct usb_endpoint_descriptor *) buffer; +@@ -386,6 +529,23 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, + endpoint->desc.bInterval = n; + } + ++ /* Override the interrupt polling interval if a module parameter tells us to do so. */ ++ if (usb_endpoint_xfer_int(d)) { ++ ival = usb_check_interrupt_interval_override(udev); ++ if (ival > 0) { ++ switch (udev->speed) { ++ case USB_SPEED_SUPER_PLUS: ++ case USB_SPEED_SUPER: ++ case USB_SPEED_HIGH: ++ endpoint->desc.bInterval = fls(ival) + 3; ++ break; ++ default: /* USB_SPEED_FULL or _LOW */ ++ endpoint->desc.bInterval = ival; ++ break; ++ } ++ } ++ } ++ + /* Some buggy low-speed devices have Bulk endpoints, which is + * explicitly forbidden by the USB spec. In an attempt to make + * them usable, we will try treating them as Interrupt endpoints. +@@ -1092,3 +1252,11 @@ int usb_get_bos_descriptor(struct usb_device *dev) + usb_release_bos_descriptor(dev); + return ret; + } ++ ++void usb_release_interrupt_interval_override_list(void) ++{ ++ mutex_lock(&interrupt_interval_override_mutex); ++ kfree(interrupt_interval_override_list); ++ interrupt_interval_override_list = NULL; ++ mutex_unlock(&interrupt_interval_override_mutex); ++} +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index 11b15d7b3..ec52c6322 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -1066,6 +1066,7 @@ static void __exit usb_exit(void) + return; + + usb_release_quirk_list(); ++ usb_release_interrupt_interval_override_list(); + usb_deregister_device_driver(&usb_generic_driver); + usb_major_cleanup(); + usb_deregister(&usbfs_driver); +diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h +index 82538daac..b6faa897c 100644 +--- a/drivers/usb/core/usb.h ++++ b/drivers/usb/core/usb.h +@@ -37,6 +37,7 @@ extern void usb_authorize_interface(struct usb_interface *); + extern void usb_detect_quirks(struct usb_device *udev); + extern void usb_detect_interface_quirks(struct usb_device *udev); + extern void usb_release_quirk_list(void); ++extern void usb_release_interrupt_interval_override_list(void); + extern bool usb_endpoint_is_ignored(struct usb_device *udev, + struct usb_host_interface *intf, + struct usb_endpoint_descriptor *epd); +-- +2.39.0 + |