Goodbye Webcam

I have suspected my webcam to have an electrical default for a while. As a result I used to have plenty of USB connect/disconnect/error events in the kernel journal like the following lines:

kernel: usb 1-1.2: new full-speed USB device number 123 using ehci-pci
kernel: usb 1-1.2: new high-speed USB device number 126 using ehci-pci
kernel: usb 1-1.2: new high-speed USB device number 127 using ehci-pci
kernel: usb 1-1.2: new high-speed USB device number 5 using ehci-pci
kernel: usb 1-1.2: new high-speed USB device number 6 using ehci-pci
kernel: usb 1-1.2: new high-speed USB device number 7 using ehci-pci
kernel: usb 1-1.2: new high-speed USB device number 8 using ehci-pci
[...]
mtp-probe[8351]: checking bus 1, device 7: "/sys/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2"
mtp-probe[8351]: bus: 1, device: 7 was not an MTP device
[...]
kernel: usb 1-1.2: USB disconnect, device number 88
kernel: usb 1-1.2: new high-speed USB device number 89 using ehci-pci
kernel: usb 1-1.2: device not accepting address 89, error -71
kernel: usb 1-1.2: new high-speed USB device number 90 using ehci-pci
kernel: usb 1-1.2: device descriptor read/64, error -71
kernel: usb 1-1.2: device descriptor read/64, error -71

It is quite annoying, right?

Well, how can I virtually cut off this usb device without physically altering my laptop?
There is a way to unbind a whole hub by echoing its pci address to /sys/bus/pci/drivers/ehci-pci/unbind (ehci-pci may vary depending on the driver used by your system to manage the usb hubs). The pci address can be found in the logs of mtp-probe which says in my case that the hub has the address 0000:00:1a.0.

With this method, you will lose all devices connected to the same hub (Bus 1). So, what will I lose?

sakura ~ # lsusb -s 1:
Bus 001 Device 004: ID 058f:a014 Alcor Micro Corp. Asus Integrated Webcam
Bus 001 Device 003: ID 13d3:3304 IMC Networks Asus Integrated Bluetooth module [AR3011]
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Oh god, I will lose my bluetooth module... Oh wait, I don't use it. NUKE ALL THE HUB \o/

echo "0000:00:1a.0" > /sys/bus/pci/drivers/ehci-pci/unbind

The kernel confirms that the hub has been deregistered:

kernel: ehci-pci 0000:00:1a.0: remove, state 1
kernel: usb usb1: USB disconnect, device number 1
kernel: usb 1-1: USB disconnect, device number 2
kernel: usb 1-1.1: USB disconnect, device number 3
kernel: usb 1-1.2: USB disconnect, device number 4
kernel: ehci-pci 0000:00:1a.0: USB bus 1 deregistered

And a lsusb -s 1: will return an empty response, that's good. The webcam is now disabled.

Unfortunately this setting does not persist after a reboot. What can I do?

Let make a little udev rule to disable the hub on the first add/remove event. Create a file, say, /etc/udev/rules.d/01-disable-webcam.rules with the following content:

ACTION=="add|remove",DEVPATH=="/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2",RUN="/usr/local/bin/disable-webcam.sh"

The ACTION part tells to match all add and remove events, and the DEVPATH specifies the device path. Finally we override the RUN variable with the path of a tiny script:

#!/bin/bash

echo "0000:00:1a.0" > /sys/bus/pci/drivers/ehci-pci/unbind

With this rule, you should now see something like the following stack at boot:

mtp-probe[2348]: checking bus 1, device 4: "/sys/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2"
mtp-probe[2348]: bus: 1, device: 4 was not an MTP device
kernel: ehci-pci 0000:00:1a.0: remove, state 1
kernel: usb usb1: USB disconnect, device number 1
kernel: usb 1-1: USB disconnect, device number 2
kernel: usb 1-1.1: USB disconnect, device number 3
kernel: usb 1-1.2: USB disconnect, device number 4
kernel: ehci-pci 0000:00:1a.0: USB bus 1 deregistered

Enjoy!