I'm developing a custom HID-compliant UPS based on Arduino Micro. When I connect it to Apple OSX (I'm using Macbook as a host machine) or Windows 10 VM machine, my "UPS" is correctly determined by the operating system and I can also report remaining battery capacity and other parameters back to the host.
However, when I plug it to Ubuntu 18.04 LTS, it is not working (not showing up in the Power settings). At the same time, the commercial APC UPS works just fine under Ubuntu. Posting of lsusb is below:
abratchik@ubuntu-parallels-vm:~$ lsusb
Bus 001 Device 003: ID 203a:fffa
Bus 001 Device 002: ID 203a:fffa
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 002: ID 203a:fff9
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
Bus 002 Device 004: ID 2341:8036 Arduino SA Leonardo (CDC ACM, HID)
Bus 002 Device 006: ID 051d:0003 American Power Conversion UPS
Bus 002 Device 003: ID 203a:fffe
Bus 002 Device 002: ID 203a:fffc
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
abratchik@ubuntu-parallels-vm:~$ lsusb -t
/: Bus 04.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 5000M
|__ Port 1: Dev 2, If 0, Class=Video, Driver=uvcvideo, 5000M
|__ Port 1: Dev 2, If 1, Class=Video, Driver=uvcvideo, 5000M
/: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 480M
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M
|__ Port 1: Dev 2, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 1: Dev 2, If 1, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 2: Dev 3, If 0, Class=Hub, Driver=hub/15p, 12M
|__ Port 5: Dev 4, If 2, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 5: Dev 4, If 0, Class=Communications, Driver=cdc_acm, 12M
|__ Port 5: Dev 4, If 1, Class=CDC Data, Driver=cdc_acm, 12M
|__ Port 4: Dev 6, If 0, Class=Human Interface Device, Driver=usbhid, 12M
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/15p, 480M
|__ Port 1: Dev 2, If 0, Class=Printer, Driver=usblp, 480M
|__ Port 2: Dev 3, If 0, Class=Printer, Driver=usblp, 480M
In the first printout, my custom UPS is coming with ID 2341:8036 and APC UPS is 051d:0003. Second printout also shows both of them identified correctly as HID device (Bus 02 port 5 interface 2 for my UPS and port 4 interface 0 for APC) and both are using usbhid driver, which is also correct and as expected.
However, APC is correctly detected by Ubuntu as UPS while my UPS is not showing up at all:
abratchik@ubuntu-parallels-vm:~$ upower -e
/org/freedesktop/UPower/devices/line_power_ADP0
/org/freedesktop/UPower/devices/battery_BAT0
/org/freedesktop/UPower/devices/ups_hiddev3
/org/freedesktop/UPower/devices/DisplayDevice
abratchik@ubuntu-parallels-vm:~$ upower -i /org/freedesktop/UPower/devices/ups_hiddev3
native-path: /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2.4/2-2.4:1.0/usbmisc/hiddev3
vendor: American Power Conversion
model: Smart-UPS C 1500 FW:UPS 10.0 / ID=1005
serial: 3S1838X02676
power supply: yes
updated: Thu 03 Dec 2020 10:00:04 AM +04 (-11 seconds ago)
has history: yes
has statistics: yes
ups
present: yes
state: fully-charged
warning-level: none
time to empty: 2.0 hours
percentage: 100%
icon-name: 'battery-full-charged-symbolic'
Also RX/TX LEDs are not blinking on Arduino board so looks like there is no exchange between the board and the VM. The same board is working just fine under OSX and Windows 10 as I mentioned above.
I googled a bit and found that many articles on this subject are referring to usbhid-ups driver, which is not installed by default and requires some configuration as I understood. I really would like to avoid this and make my UPS fully "plug-and-play", similar to APC UPS. Already lost of ideas what could be different for my UPS, except may be Vendor ID/Product ID but that would be my last option to try and change these. Any hint what I'm missing is much appreciated.
Finally I understood how it works. The usbhid driver in Linux was not guilty, it is just low-level parser of HID protocol and don't really do any intelligent job other than de-ciphering the device responses and passing them over to the managing layer, which is udev (Linux Kernel Device Manager) and UPower manager, which is sitting on top of udev.
The UPower actually defines a list of all supported Vendor ID/Product ID and feeds it to udev in form of udev rules. So I was right - Vendor ID/Product ID does matter in this case - if you are APC then your UPS will be detected automatically, otherwise the device is simply ignored by the UPower even if it is HID-compliant.
Fortunately, there is a workaround. The udev rules are stored in the /etc/udev/rules.d/ folder in text format so additional rules can be defined easily. One have to create the file 98-upower-hid.rules (I assume exact file name is not that important) in this folder and add the following lines:
Reboot. After that the Arduino Board with the UPS sketch is recognized by Ubuntu just fine.