gary-rowe / hid4java

A cross-platform Java Native Access (JNA) wrapper for the libusb/hidapi library. Works out of the box on Windows/Mac/Linux.
MIT License
229 stars 71 forks source link

Call to open() fails on Mint Linux #106

Closed wholder closed 3 years ago

wholder commented 3 years ago

I have a Java program using Hid4Java that runs successfully on macOs and Windows 10, but fails on Mint Linux 19 when it tries to open() the device. Here is printout from test code when run on these three operating systems:

macOs 10.14.6
 VID  PID - Description                    - Manf                 - Serial Num      - Can open
03EB:2141 - Atmel-ICE CMSIS-DAP            - Atmel Corp.          - J41800089996    - yes

Microsoft Windows [Version 10.0.19041.508]
03EB:2141 - Atmel-ICE CMSIS-DAP            - Atmel Corp.          - J41800089996    - yes

Mint Linux, version="19 (Tara)", 4.15.0-118-generic
03EB:2141 - Atmel-ICE CMSIS-DAP            - Atmel Corp.          - J41800089996    - no

The troubleshooting section does not seem to cover Mint Linux, but I tried the advice given for Ubuntu and created a rules file in /etc/udev/rules.d/that contains:

KERNEL=="hidraw*", ATTRS{idVendor}=="03EB", ATTRS{idProduct}=="2141", MODE="0666", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl"

Then, used the following commands to verify the "plugdev" group:

$ groups
parallels adm cdrom sudo dip plugdev lpadmin sambashare

$ whoami
parallels

$ groups parallels
parallels : parallels adm cdrom sudo dip plugdev lpadmin sambashare

What am I missing?

Wayne

p.s. these test code I used is, as follows:

import org.hid4java.*;

public class ListHIDDevices {

  public static void main(String[] args) throws HidException {
    HidServicesSpecification hidServicesSpecification = new HidServicesSpecification();
    hidServicesSpecification.setAutoShutdown(true);
    hidServicesSpecification.setScanInterval(500);
    hidServicesSpecification.setPauseInterval(5000);
    hidServicesSpecification.setScanMode(ScanMode.SCAN_AT_FIXED_INTERVAL_WITH_PAUSE_AFTER_WRITE);
    HidServices hidServices = HidManager.getHidServices(hidServicesSpecification);
    hidServices.start();
    System.out.printf(" %-3s  %-3s - %-30s - %-20s - %-15s - %-10s\n", "VID", "PID", "Description", "Manf", "Serial Num", "Can open");
    for (HidDevice hidDevice : hidServices.getAttachedHidDevices()) {
      String manf = hidDevice.getManufacturer().trim();
      String prod = hidDevice.getProduct();
      int vendor = hidDevice.getVendorId();
      int product = hidDevice.getProductId();
      String serialNum = hidDevice.getSerialNumber();
      serialNum = serialNum != null ? serialNum : "";
      System.out.printf("%04X:%04X - %-30s - %-20s - %-15s", vendor, product, prod, manf, serialNum);
      if (hidDevice.isOpen()) {
        System.out.print(" - open");
      } else if (hidDevice.open() && hidDevice.isOpen()) {
        hidDevice.setNonBlocking(true);
        System.out.print(" - yes");
        hidDevice.close();
      } else {
        System.out.print(" - no");
      }
      System.out.println();
    }
    hidServices.shutdown();
  }
}
wholder commented 3 years ago

BTW, running as root works:

parallels@parallels-Parallels-Virtual-Platform:~/Documents$ sudo java -jar Hid4JavaTest.jar
 VID  PID - Description                    - Manf                 - Serial Num      - Can open  
03EB:2141 - Atmel-ICE CMSIS-DAP            - Atmel Corp.          - J41800089996    - yes

Wayne

gary-rowe commented 3 years ago

Hi @wholder,

I did some brief digging around and it could be a permissions issue with /dev/hidraw*. You could experiment with chmod 666 /dev/hidrawX (assuming lsusb indicates /dev/hidrawX as where your device is showing up on your system). If it bursts into life then a bit more narrowing of permissions would be needed.

Let me know how you get on.

wholder commented 3 years ago

You could experiment with chmod 666 /dev/hidrawX (assuming lsusb indicates /dev/hidrawX as where your device is showing up on your system).

First, thinks for trying to help. I'm not sure what you mean by "assuming lsusb indicates /dev/hidrawX as where your device is showing up". When I run lsusb -v, a stream of text but, nothing in the text includes the string "hidraw", or anything similar. However, I'm not that skilled with linux and know even less about these "rules" files and how they work. I also tried running lsusb -t and got the printout included below. The Atmel-ICE show up as being Device 006 on Bus 001, so I'm guessing the relevant item in the tree is:

|__ Port 6: Dev 6, If 0, Class=Human Interface Device, Driver=usbhid, 480M

Does this help narrow things down?

Wayne

$ lsusb -t
/:  Bus 04.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 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
/:  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
    |__ Port 3: Dev 5, If 0, Class=Printer, Driver=usblp, 480M
    |__ Port 5: Dev 4, If 0, Class=Printer, Driver=usblp, 480M
    |__ Port 6: Dev 6, If 0, Class=Human Interface Device, Driver=usbhid, 480M
    |__ Port 6: Dev 6, If 1, Class=Vendor Specific Class, Driver=, 480M
gary-rowe commented 3 years ago

Yes, I have accidentally misled you with the mapping between the lsusb output and /dev/hidraw. It is more complex than I thought (Linux is not my primary dev platform) so I've written up a more detailed explanation in the wiki. You may find the information there (and the links) helpful.

I still think it's a permissions problem, possibly in /dev/hidrawX or /etc/udev/rules.d/<your rules file>.

wholder commented 3 years ago

Ok, after a few hours of trial and error and reading through more web posts than I can remember, I stumbled upon the fix. It's as simple as adding a single ':' (colon) character so that MODE="0666" becomes MODE:="0666". Apparently, versions of Linux based on Ubuntu, as I think is the case for Mint, require the colon to indicate an assignment. To simplify making the change, I used this command line:

echo 'KERNEL=="hidraw*", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="*", MODE:="0666", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl"' | sudo tee /etc/udev/rules.d/100-atmel.rules

where this is entered as a single line followed by pressing enter. Then, to be sure, I recommend a restart for the new rule to take effect.

Wayne

gary-rowe commented 3 years ago

Thanks for coming back with an answer. It's interesting that the solution involves := which is essentially the final keyword in the udev rules. It could be that there are other files in your system that are overriding the values in your .rules file.

I would counsel against using a wildcard in the idProduct field since a given vendor is unlikely to make a vast number of variants of a device - you may find explicitly declaring the product IDs to be a more secure approach.

Anyway, I'll close this issue now and I've updated the wiki to reflect your findings.