I love my Chromebook, a Lenovo X131e.
I also love my Bluetooth mouse.
I do NOT love when the Bluetooth controller becomes inoperable on my Chromebook, preventing the use of my mouse (and any other Bluetooth devices, as well).
It seems that, sometimes, the Bluetooth controller “hard locks”; typically, the only way to recover from this scenario would be a total reboot. A full reboot is quite a pain, especially while working with a lot of open windows/tabs.
Bluetooth: It’s great…when it works
While the convenience offered by Bluetooth is a wonderful thing, it can cause quite the headache when it inexplicably stops working.
The Chrome OS Bluetooth menu
- The Bluetooth menu in Chrome OS
This is how it looks while my mouse is working.
Unfortunately, things can look quite similar while things are not working.
One might think: “Perhaps if I turn the Bluetooth off, and then back on, it’ll work again.”
WRONG!
Toggle the Bluetooth state, and it will simply refuse to return to the enabled state.
So what’s going on here?
Bluetooth Hardware
Often, despite being internal to the Chromebook, the Bluetooth hardware is connected to the rest of the system via USB — this may come as a surprise to some. Now, for reasons unbeknownst to me, the Bluetooth USB device enters a state in which it is no longer functional. So, with an ordinary USB device, the first action most would take is to unplug the device and plug it back in; not too easy when the device is embedded within the Chromebook.
usbreset.c
— a handy little tool
Enter usbreset.c
— a (relatively) unknown snippet of code, which once compiled, can issue a USB reset ioctl; essentially “unplugging and plugging the device back in” via software.
To compile, open a shell, change to the directory in which usbreset.c
is located, and execute:
gcc -o usbreset usbreset.c
Optionally, the compiled product can be placed into a directory which is part of the $PATH
environment variable, such as /usr/local/sbin
, which will make use much easier.
Following compilation, usbreset
takes one parameter: the USB device path, such as /dev/bus/usb/${bus}/${dev}
The bus and device numbers can be acquired using lsusb — look for the line describing a Bluetooth controller. On my X131e, I receive the following output:
localhost ~ # lsusb Bus 002 Device 004: ID 413c:819b Dell Computer Corp. Bus 002 Device 007: ID 0cf3:3004 Atheros Communications, Inc. AR3012 Bluetooth 4.0 Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 003 Device 002: ID 5986:0299 Acer, Inc Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 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
Great, now I have the bus (002) and device (007) numbers. I can go ahead and issue the following command:
usbreset /dev/bus/usb/002/007
After a few seconds, Bluetooth should be working again. Awesome!
Unfortunately, the device number tends to change.
Easier Procedure
I wrote a simple shell script to grab the bus and device numbers from the output of lsusb, and to issue the reset.
You can find it, along with the source for usbreset.c
, in this bitbucket snippet.
Please note that this was created for use on my X131e; while I have experienced Bluetooth issues on other Chrome OS devices, and have used usbreset
(successfully), I have NOT used this script on those devices. Your mileage may vary; the script will likely need to be adjusted for your setup.
Feedback
If you have any questions, comments, feedback, or suggestions for improvement, please let me know in the comments below.
I hope this is useful to someone else!