Skip to content

Commit 5866275

Browse files
committed
minor changes + add README
1 parent ada06b8 commit 5866275

File tree

2 files changed

+260
-4
lines changed

2 files changed

+260
-4
lines changed

README.md

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
# usb_oc-dkms
2+
3+
Kernel module for overclocking USB devices (controllers, mice, keyboards, etc.). Equivalent of the `hidusbf` driver on Windows.
4+
5+
This allows you to change their poll rates by loading this out-of-tree module, instead of having to patch and re-compile the whole kernel.
6+
7+
## Installation and setup
8+
9+
Firstly find the `VID:PID` of your USB device with `lsusb`, such as `054c:0ce6`.
10+
11+
### 0. Pick your bInterval value
12+
13+
Check the speed of your USB device with (replace with your device's `VID:PID`):
14+
15+
```console
16+
$ lsusb -d 054c:0ce6 -v | grep '^Negotiated speed:'
17+
18+
Negotiated speed: Full Speed (12Mbps)
19+
```
20+
21+
#### Low Speed (USB 1.1) (1.5 Mbps)
22+
23+
Extremely rare, rather forget about overclocking those too much. Note that "0" itself might not be a valid value.
24+
25+
| Value of bInterval | Polling Period | Frequency |
26+
| ------------------ | -------------- | --------- |
27+
| 0 to 15 | 8 ms | 125 Hz |
28+
| 16 to 35 | 16 ms | 62.5 Hz |
29+
| 36 to 255 | 32 ms | 31.25 Hz |
30+
31+
#### Full Speed (USB 1.1) (12 Mbps)
32+
33+
This is most of the devices.
34+
35+
| Value of bInterval | Polling Period | Frequency |
36+
| ------------------ | -------------- | --------- |
37+
| 1 | 1 ms | 1000 Hz |
38+
| 2 (*to 3*) | 2 ms | 500 Hz |
39+
| 4 (*to 7*) | 4 ms | 250 Hz |
40+
| 8 (*to 15*) | 8 ms | 125 Hz |
41+
| 16 (*to 31*) | 16 ms | 62.5 Hz |
42+
| 32 (*to 255*) | 32 ms | 31.25 Hz |
43+
44+
#### High Speed (USB 2.0) (480 Mbps)
45+
46+
Still has a maximum of 1000 Hz, but the values of `bInterval` differ (`Period = 2^(bInterval - 1)`).
47+
48+
| Value of bInterval | Polling Period | Frequency |
49+
| ------------------ | -------------- | --------- |
50+
| 1 | 1 ms | 1000 Hz |
51+
| 2 | 2 ms | 500 Hz |
52+
| 3 | 4 ms | 250 Hz |
53+
| 4 | 8 ms | 125 Hz |
54+
| 5 | 16 ms | 62.5 Hz |
55+
| 6 | 32 ms | 31.25 Hz |
56+
| 7 to 255 | 32 ms | 31.25 Hz |
57+
58+
#### Higher than that
59+
60+
Don't quote me on this, I pulled the table below out of my ass and didn't test it yet :3 The formula seems to be `Period = 2^(bInterval - 1) * 0.125`.
61+
62+
| Value of bInterval | Polling Period | Frequency |
63+
| ------------------ | -------------- | --------- |
64+
| 1 | 0.125 ms | 8000 Hz |
65+
| 2 | 0.250 ms | 4000 Hz |
66+
| 3 | 0.500 ms | 2000 Hz |
67+
| 4 | 1 ms | 1000 Hz |
68+
| 5 | 2 ms | 500 Hz |
69+
| 6 | 4 ms | 250 Hz |
70+
71+
### 1. Install
72+
73+
You can go to [Releases](https://github.com/p0358/usb_oc-dkms/releases) section and download the latest `.deb`, `.rpm` or `.pkg.tar.zst`.
74+
75+
#### Debian/Ubuntu/Mint/Pop_OS (.deb)
76+
77+
```console
78+
# curl -Lo /tmp/usb-oc-dkms.deb https://github.com/p0358/usb_oc-dkms/releases/download/v1.0/usb-oc-dkms_1.0_amd64.deb
79+
# apt install /tmp/usb-oc-dkms.deb
80+
```
81+
82+
#### Arch Linux/CachyOS/EndevourOS/Manjaro (.pkg.tar.zst)
83+
84+
AUR: [usb_oc-dkms](https://aur.archlinux.org/packages/usb_oc-dkms) or:
85+
86+
```console
87+
# curl -Lo /tmp/usb_oc-dkms.pkg.tar.zst https://github.com/p0358/usb_oc-dkms/releases/download/v1.0/usb_oc-dkms-1.0-1-any.pkg.tar.zst
88+
# pacman -U /tmp/usb_oc-dkms.pkg.tar.zst
89+
```
90+
91+
#### Fedora (.rpm)
92+
93+
```console
94+
# curl -Lo /tmp/usb_oc-dkms.rpm https://github.com/p0358/usb_oc-dkms/releases/download/v1.0/usb_oc-dkms-1.0-1.fc45.noarch.rpm
95+
# dnf install /tmp/usb_oc-dkms.rpm
96+
```
97+
98+
#### openSUSE (.rpm)
99+
100+
```console
101+
# curl -Lo /tmp/usb_oc-dkms.rpm https://github.com/p0358/usb_oc-dkms/releases/download/v1.0/usb_oc-dkms-1.0-1.fc45.noarch.rpm
102+
# zypper install --allow-unsigned-rpm /tmp/usb_oc-dkms.rpm
103+
```
104+
105+
#### Nobara
106+
107+
Use this instead: https://github.com/GloriousEggroll/Linux-Pollrate-Patch
108+
109+
#### Yet another distro
110+
111+
I guess you're on your own.
112+
113+
### 2. Test it out
114+
115+
Load the kernel module and confirm that it's been loaded:
116+
117+
```console
118+
# modprobe usb_oc
119+
# lsmod | grep usb_oc
120+
usb_oc 16384 0
121+
```
122+
123+
You can change the module configuration on-the-fly without unloading the module. The changes will be applied immediately. Assuming you want, for the device with `VID:PID` of `054c:0ce6`, to set bInterval to `1`, run:
124+
125+
```console
126+
# echo -n '054c:0ce6:1' > /sys/module/usb_oc/parameters/interrupt_interval_override
127+
```
128+
129+
If you want to overclock multiple devices, you'd do:
130+
131+
```console
132+
# echo -n '054c:0ce6:1,1234:5678:1' > /sys/module/usb_oc/parameters/interrupt_interval_override
133+
```
134+
135+
Do monitor the output of `dmesg` command to diagnose the module and see any warnings.
136+
137+
See the FAQ section for how to benchmark your device to confirm the actual overclocked rate is working. Some devices might just be impossible to overclock and will not send more data than they were designed for, when polled more frequently. Some will, for which this module is made. And some will already be set to their maximum polling rate of given USB version.
138+
139+
Once you're done with testing, you may unload the module with `rmmod usb_oc`. Reconnect any USB devices if you want to restore their bIntervals to default.
140+
141+
### 3. Configure the module to auto-load on system startup
142+
143+
If you found a working configuration, you will probably want to persist it, so that it always overclocks your device when you boot up your computer.
144+
145+
Firstly, set the module to load on system startup:
146+
147+
```console
148+
# echo 'usb_oc' > /etc/modules-load.d/usb_oc.conf
149+
```
150+
151+
Secondly, set the module configuration (again, assuming you want, for the device with `VID:PID` of `054c:0ce6`, to set bInterval to `1`):
152+
153+
```console
154+
# echo 'options usb_oc interrupt_interval_override=054c:0ce6:1' > /etc/modules-load.d/usb_oc.conf
155+
```
156+
157+
After rebooting the machine, verify the module was loaded and working with:
158+
159+
```console
160+
# dmesg | grep usb_oc
161+
```
162+
163+
### 4. Success
164+
165+
Share you overclock success stories at [#1](https://github.com/p0358/usb_oc-dkms/discussions/1) and failure stories at [#2](https://github.com/p0358/usb_oc-dkms/discussions/2)!
166+
167+
## Notes
168+
169+
### Secure Boot
170+
171+
If you have Secure Boot enabled (check with `mokutil --sb-state`) and you get `modprobe: ERROR: could not insert 'usb_oc': Key was rejected by service` when trying to modprobe, you need to enroll DKMS's MOK key to your machine in order for the module to be possible to load.
172+
173+
Just run:
174+
175+
```bash
176+
mokutil --timeout -1
177+
mokutil --import /var/lib/dkms/mok.pub
178+
```
179+
180+
You'll be prompted to create a password. Enter it twice.
181+
182+
Reboot the computer. At boot you'll see the MOK Manager EFI interface. Press any key to enter it.
183+
184+
- "Enroll MOK"
185+
- "Continue"
186+
- "Yes"
187+
- Enter the password you set up just now.
188+
- Select "OK" and the computer will reboot again.
189+
190+
Now the module should be possible to load.
191+
192+
### USB 1.1 and bandwidth limitations
193+
194+
If your device is USB 1.1 ("Full Speed" 12 Mbps, or, heavens forbid, "Low Speed" 1.5 Mbps) and you overclock it too high (1000 Hz), you may run into bandwidth issues and sometimes experience some lagged input in games. At least I think that's why it happens and goes away with higher bInterval.
195+
196+
I did the maths and with 12 Mbps and 1000 Hz, it should be roughly ~1572 bytes per poll though, which doesn't seem that little. I don't know how chatty XInput protocol is. Perhaps it's just the particular device that can't keep up. In any case, at some point you might have diminishing results with overclocking the device *too much*.
197+
198+
### Little quirk: the device is reset after the bInterval descriptor change is made
199+
200+
This is so that the change is picked up. But be careful with this, and not only for this reason, but in general:
201+
202+
> [!WARNING]
203+
> Unload this module if you're trying to do something "sensitive"/"risky" with the overclocked device, such as trying to flash its firmware. I am not responsible for any problems if you do dumb things, please use common sense here.
204+
205+
## FAQ
206+
207+
### Why does Fedora RPM package use DKMS instead of AKMODS?
208+
209+
Because I couldn't figure out the kmod counterpart and didn't care enough to spend more time on it, if the DKMS one works (and then it works on more distros like openSUSE too). But if you care, then contributions are welcome to add it, including the CI workflow to build the appropriate RPM files (as far as I understand, we'd want to publish SRPM in Releases, but still would want to at least check the binary modules are indeed building from those for current kernel...).
210+
211+
### How to benchmark the device to confirm it was overclocked?
212+
213+
For controllers you can use **[Gamepadla Polling](https://github.com/cakama3a/Polling)** ([gamepadla-polling<sup>AUR</sup>](https://aur.archlinux.org/packages/gamepadla-polling)) and for mouse you can use **[evhz](https://git.sr.ht/~iank/evhz)** ([evhz-git<sup>AUR</sup>](https://aur.archlinux.org/packages/evhz-git)).
214+
215+
### Why is USB overclocking not part of upstream kernel?
216+
217+
What do I know? A you can see in "Alternatives" section below, some means of overclocking some devices exist, and yet the patchset to overclock any device didn't seem to get accepted. And some distros outside of Nobara seem to be [allergic](https://github.com/CachyOS/linux-cachyos/issues/240#issuecomment-2430116447) to it for some reason too. But who cares, this module allows you to overclock your devices anyway, without care about what your distro thinks about it or recompiling the whole kernel!
218+
219+
## Alternatives
220+
221+
The `usbhid` driver has options for [`mousepoll`](https://wiki.archlinux.org/title/Mouse_polling_rate#Set_polling_interval), but it [reportedly doesn't work with USB 3 devices](https://wiki.archlinux.org/title/Mouse_polling_rate#Polling_rate_not_changing). The option `kbpoll` also exists for keyboards, but likely suffers from the same problem. It also has an option `jspoll` for joysticks, but it doesn't work with gamepads using other drivers like `xpad`.
222+
223+
[Linux-Kernel_MiSTer] has a downstream patch for [`xpad` driver adding `cpoll` option](https://github.com/MiSTer-devel/Linux-Kernel_MiSTer/blob/master/drivers/input/joystick/xpad.c). Unfortunately it is not upstreamed.
224+
225+
Nobara comes with [this patch](https://github.com/GloriousEggroll/Linux-Pollrate-Patch) applied to its kernel.
226+
227+
Some devices have settings apps that allow changing on-device configuration used by the device's firmware and its processor. Sometimes this configuration include the poll rate that will be advertised by the device, without needing a kernel module for it (for example Solaar for select Logitech mice).
228+
229+
## Thanks to
230+
231+
Some inspiration (parts of code/scripts) taken from:
232+
233+
- https://github.com/hannesmann/gcadapter-oc-kmod (skeleton for a simple DKMS USB overclock module)
234+
- https://github.com/GloriousEggroll/Linux-Pollrate-Patch (kernel patchset with a configurable USB overclocking parameter)
235+
- https://github.com/strongtz/i915-sriov-dkms (CI and Makefiles)
236+
- https://github.com/KyleGospo/openrgb-dkms (Fedora dkms rpm spec file)
237+
238+
## Dev notes
239+
240+
For updating, remember to update the version in:
241+
242+
- `dkms.conf`
243+
- `src/usb_oc.c`
244+
- `packaging/arch/PKGBUILD`
245+
- `packaging/rpm/usb_oc-dkms.spec`
246+
- (in this README in the Install section in package URLs)

src/usb_oc.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,11 @@ static unsigned short usb_check_interrupt_interval_override(struct usb_device* u
142142
u16 product = le16_to_cpu(udev->descriptor.idProduct);
143143

144144
mutex_lock(&interrupt_interval_override_mutex);
145-
for (i = 0; i < interrupt_interval_override_count; i++) {
145+
for (i = 0; i < interrupt_interval_override_count; i++)
146+
{
146147
if (interrupt_interval_override_list[i].vendor == vendor
147-
&& interrupt_interval_override_list[i].product == product) {
148+
&& interrupt_interval_override_list[i].product == product)
149+
{
148150

149151
res = interrupt_interval_override_list[i].interval;
150152
mutex_unlock(&interrupt_interval_override_mutex);
@@ -284,8 +286,16 @@ static int usb_device_cb(struct usb_device* udev, void* data)
284286

285287
static int __init on_module_init(void)
286288
{
287-
printk(KERN_INFO "usb_oc: [DEBUG] [on_module_init]\n");
288-
usb_for_each_dev(NULL, &usb_device_cb);
289+
if (interrupt_interval_override_count > 0)
290+
{
291+
usb_for_each_dev(NULL, &usb_device_cb);
292+
}
293+
else
294+
{
295+
printk(KERN_WARNING "usb_oc: No configuration specified in \"interrupt_interval_override\" module parameter.\n");
296+
printk(KERN_WARNING "usb_oc: You can configure it on runtime by writing into: /sys/module/usb_oc/parameters/interrupt_interval_override\n");
297+
}
298+
289299
usb_register_notify(&usb_nb);
290300

291301
configured = 1;

0 commit comments

Comments
 (0)