Skip to content

george-hawkins/wake-on-mouse

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 

Repository files navigation

Disabling wake on mouse

TLDR; assuming that the file /etc/udev/rules.d/10-wakeup.rules doesn't exist. Just run the included script like so:

$ ./wakeup-rule | sudo tee /etc/udev/rules.d/10-wakeup.rules > /dev/null

And then plug your USB mouse in and out to activate the rule.

The above script works for Ubuntu 20.04 LTS. If you run it on its own you'll see it produces output like this:

# Udev rule to disable wakeup for Logitech_USB_Receiver.
ACTION=="bind" DRIVER=="usbhid" ATTR{bInterfaceProtocol}=="02" ATTRS{idVendor}=="046d" ATTRS{idProduct}=="c52b" RUN+="/bin/sh -c 'echo disabled > /sys$env{DEVPATH}/../power/wakeup'"

The idVendor and idProduct values are the USB vendor and product IDs specific to your particular make of USB mouse. In the case above, they're actually the values for the USB bluetooth receiver for a wireless Logitech mouse. If you've got multiple devices connected to such a receiver, e.g. mouse and keyboard, then wakeup will be disabled for all these devices. In this situation, you can still wake your computer by e.g. quickly pressing the power button.

For other setups, it may be necessary to change the ACTION above from bind to e.g. add. Finding the appropriate action is discussed below.

Note: I've tried the above script with both a wired USB mouse and a mouse connected via a USB bluetooth receiver.

Update

The above rule activates both when the system starts up and when you switch a mouse from one USB port to another while the system is running. Initially, I thought this could be handled with a far simpler rule with no need to escape out to any external logic with RUN.

The following rule works if you switch a USB mouse from one USB port to another on a running system:

ACTION=="bind" ATTRS{idVendor}=="046d" ATTRS{idProduct}=="c52b" ATTR{power/wakeup}=="*" ATTR{power/wakeup}="disabled"

I.e. it looks for an event where the power/wakeup attribute is available and sets it to disabled.

However, surprisingly to me, no such event occurs when a system boots up. If a system reboots then presumably everything under /sys is rebuilt as part of the process - yet, for whatever reason, no events occur during this process that involve the power/wakeup attribute for the USB device associated with the mouse.

So in the end, I worked things out like this:

  • I copied the wake-on-mouse script here into /root.
  • I added the following lines into /etc/udev/rules.d/10-wakeup.rules.
ACTION=="*" RUN+="/root/wake-on-mouse"

ACTION=="*" ATTR{power/wakeup}=="*" RUN+="/bin/sh -c 'echo $env{DEVPATH} $env{ACTION} >> /tmp/wakeup.log'"

Then I shutdown the system and restarted - the wake-on-mouse script results in a lot of files of the form /tmp/wakeup-full-1622712145572487697.0Mij2Ounxv.log (the first number is nanos-since-epoch and the second sequence is random and generated by mktemp).

The script logs a lot of information that ultimately turned out to be useless but it made clear what was possible.

First though, wakeup.log made clear was that there was no suitable power/wakeup event - which I still find surprising.

Similarly surprising, there weren't any suitable events where DEVPATH pointed directly to the relevant device directory containing the wakeup file in its power subdirectory.

The best I could find was one of the child devices of this device - a whole tree of such devices are created and if I couldn't get the device itself I wanted one of its direct children so I could make the RUN component of my rule as simple as possible:

RUN+="/bin/sh -c 'echo disabled > /sys$env{DEVPATH}/../power/wakeup'"

Looking at the wakeup-full-xyz.log files, it was clear there were three such candidates - with names (depending on the USB port involved) like this:

  • /devices/pci0000:00/0000:00:14.0/usb1/1-6/1-6:1.0
  • /devices/pci0000:00/0000:00:14.0/usb1/1-6/1-6:1.1
  • /devices/pci0000:00/0000:00:14.0/usb1/1-6/1-6:1.2

You can look at these devices like so:

$ udevadm info --path=/devices/pci0000:00/0000:00:14.0/usb1/1-6/1-6:1.0 --attribute-walk | less
  looking at device '/devices/pci0000:00/0000:00:14.0/usb1/1-6/1-6:1.1':
    ...
    DRIVER=="usbhid"
    ...
    ATTR{bInterfaceProtocol}=="02"
    ...

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-6':
    ...
    DRIVERS=="usb"
    ...
    ATTRS{idVendor}=="046d"
    ...
    ATTRS{idProduct}=="c52b"
    ...

Note DRIVER vs DRIVERS and ATTR vs ATTRS - without the S it means the attribute belongs to the device itself, with the S means it's an attribute of the parent - i.e. udevadm writes things out such that you can directly use them in a udev rule.

So selecting for DRIVER=="usbhid" means the rule will only fire for this device and not one of its many parents. It turns out that would actually be enough - the following rule would work perfectly well:

DRIVER=="usbhid" ATTRS{idVendor}=="046d" ATTRS{idProduct}=="c52b" RUN+="/bin/sh -c 'echo disabled > /sys$env{DEVPATH}/../power/wakeup'"

But it will fire several times during startup. For overkill, one can find what additional constraints are needed to get it to fire only once.

With a little additional work, with udevadm, it's possible to see what attributes distinguish the three devices shown above, i.e. 1-6:1.0, 1-6:1.1 and 1-6:1.2. There are a few but after some research the most meaningful one turns out to be bInterfaceProtocol - this is a numerical value that indicates the protocol and therefore the kind of thing involved.

It turns out 2 indicates a mouse. You can confirm this with lsusb:

$ lsusb -v | fgrep -i mouse
      bInterfaceProtocol      2 Mouse

Similarly, you'll find it in hid.h:

$ fgrep -i mouse /usr/include/linux/hid.h
#define USB_INTERFACE_PROTOCOL_MOUSE	2

So you can add ATTR{bInterfaceProtocol}=="02" as an additional constraint. With a little more experimentation, it turns out the rule still runs twice - for the ACTION values add and bind.

So one could choose either. However, a little more experimentation shows that while add and bind both show up during system startup, only bind shows up if you switch the mouse from one USB port to another while the system is running. So the following rule fires once both during system startup and when switching USB port:

ACTION=="bind" DRIVER=="usbhid" ATTR{bInterfaceProtocol}=="02" ATTRS{idVendor}=="046d" ATTRS{idProduct}=="c52b" RUN+="/bin/sh -c 'echo disabled > /sys$env{DEVPATH}/../power/wakeup'"

Note: even though we're using bInterfaceProtocol to select for the mouse device, we're disabling wakeup for its parent device. As noted above, in my case this is a USB bluetooth receiver - so disabling wake for this device will disable it for all devices connected to it, not just the mouse.

Notes

Find the mouse USB vendor and product ID:

$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 006: ID 413c:2107 Dell Computer Corp. 
Bus 001 Device 029: ID 046d:c52b Logitech, Inc. Unifying Receiver
Bus 001 Device 004: ID 05e3:0610 Genesys Logic, Inc. 4-port hub
Bus 001 Device 005: ID 8087:0026 Intel Corp. 
Bus 001 Device 002: ID 103c:84fd HP TracerLED
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

In my case above, it's the Logitech device and its ID is shown as 046d:c52b. The vendor ID is 046d and the product ID is c52b.

An alternative way to find this values is to look for idVendor and idProduct in the output of:

$ udevadm info --name=/dev/input/mouse0 --attribute-walk

Create a udev rule file:

$ sudo vim /etc/udev/rules.d/10-wakeup.rules

And add a rule with ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52b" (change 046d and c52b to match your device) and add whatever other elements (described above) are necessary.

That's it - plug the device in and out and the rule will activate (you don't need to use udevadm control --reload-rules as suggested on some pages).

Update: some of the following information is stale - trying to work with ATTR{power/wakeup}, as noted above, turned out to be a dead end - but the rest of the information is still relevant.

There are lots of pages on the web with differing suggestions as to how to do this. For nearly everything I tried, I ended up with a line like this in /var/log/syslog:

systemd-udevd[88948]: 1-7.1: /etc/udev/rules.d/10-wakeup.rules:1 Failed to write ATTR{/sys/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7.1/power/wakeup}, ignoring: No such file or directory

It turned out there were two issues:

  • Most pages list the relevant ACTION as add whereas on my system this attribute is only available when bind occurs.
  • The matching rules match device children, so even though the ATTRS{idProduct}=="c52b" matched the correct device, it also matched children that didn't have the power/wakeup attribute. Adding the check ATTR{power/wakeup}=="*" resolved this.

To find the correct action, I added the following temporary rule to /etc/udev/rules.d/10-wakeup.rules:

ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52b", ATTR{power/wakeup}=="*" RUN+="/bin/sh -c 'echo $(date --rfc-3339=seconds) $env{DEVPATH} $env{ACTION} >> /tmp/udev-wakeup.log 2>&1'"

I.e. a very unconstrained rule, with just checks for the particular device and the power/wakeup attribute. Then after plugging the device in and out, I could find the relevant action in /tmp/udev-wakeup.log:

$ cat /tmp/wkup 
2021-06-02 09:41:25+02:00 /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7.1 bind
2021-06-02 09:51:05+02:00 /devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7.1/1-7.1:1.2/0003:046D:C52B.0051/0003:046D:101B.0052/power_supply/hidpp_battery_18 change

Note that this also picked up a battery-related child device - but I could see that if I checked for just the bind action I would get the device I wanted.

You can check the current power/wakeup value by taking the device path shown above and doing:

$ dev=/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7.1
$ cat /sys$dev/power/wakeup
enabled

You might wonder how you know what environment values, e.g. DEVPATH and ACTION above, you can check for. You can discover this with:

$ udevadm monitor --environment --udev

Just plug the device in and out and you'll see it display the relevant events and the related environment values.

Note: --environment is not mentioned at all in the man page for udevadm on my system.

And if you want to see what else you can match on you can do:

$ udevadm info --name=/dev/bus/usb/001/029 --attribute-walk

Where 001/029 are the values you saw for Bus and Device when using lsusb up above.

Other interesting alternatives to --attribute-walk are --query=all and --query=property.

An alternative to using lsusb at all is to use udevadm info about your mouse device:

$ udevadm info --name=/dev/input/mouse0 --attribute-walk

You'll see the relevant idVendor and idProduct values listed for one of its parents.

You can also use udevadm info with a /devices path (like above, i.e. something under /sys) using --path instead of --name:

$ udevadm info --path=/devices/pci0000:00/0000:00:14.0/usb1/1-7/1-7.1 --attribute-walk

Another way to find the relevant USB device, if you have the product ID, is like so:

$ grep c52b /sys/bus/usb/devices/*/idProduct

You can then see if wakeup is enabled for this device like so:

$ dev=$(fgrep -l c52b /sys/bus/usb/devices/*/idProduct)
$ cat ${dev%/*}/power/wakeup
enabled

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages