• Trying Rust

    stephengeorgewest09/20/2021 at 04:02 0 comments

    extern crate hidapi;
    
    use hidapi::HidApi;
    
    fn main() {
      let mut found = false;
      match HidApi::new() {
        Ok(api) => {
          for device in api.device_list() {
            if device.vendor_id() == 0x1a86 && device.product_id() == 0xe041 {
              found = true;
              println!(
                "{:04x}:{:04x}, {:?}",
                device.vendor_id(), device.product_id(), device.serial_number()
              );
              
              match device.open_device(&api){
                Ok(hid_device) => {
                  let data = [0x00, 0x55, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
                  match hid_device.write(&data) {
                    Ok(result) => {
                      eprintln!("write: {}", result);
                    },
                    Err(e) => {
                      eprintln!("write Error: {}", e);
                    }
                  }
                },
                Err(e) => {
                  eprintln!("Open Error: {}", e);
                }
              }
            }
          }
        },
        Err(e) => {
          eprintln!("HIDApi Error: {}", e);
        },
      }
      if !found {
        println!("No device found");
      }
    }
    PS C:\Users\Stephen\Documents\GitHub\USBSwitch\Rust> cargo build
        Finished dev [unoptimized + debuginfo] target(s) in 0.02s
    PS C:\Users\Stephen\Documents\GitHub\USBSwitch\Rust> .\target\debug\usbshare.exe
    No device found
    PS C:\Users\Stephen\Documents\GitHub\USBSwitch\Rust> .\target\debug\usbshare.exe
    1a86:e041, None
    write: 9

    That was surprisingly not hard. Sometimes the write fails, but it still switches, I think it switches before/while sending the ACK.

    Trying linux.

    stephen@AO751h:~/Documents/usbshare/usbshare$ cargo build
       Compiling usbshare v0.1.0 (/home/stephen/Documents/usbshare/usbshare)
        Finished dev [unoptimized + debuginfo] target(s) in 3.07s
    stephen@AO751h:~/Documents/usbshare/usbshare$ ./target/debug/usbshare
    1a86:e041, None
    Open Error: Failed opening hid device
    stephen@AO751h:~/Documents/usbshare/usbshare$ ls -alh /dev/hidraw0
    crw-rw-r-- 1 root plugdev 243, 0 Sep 19 21:17 /dev/hidraw0
    stephen@AO751h:~/Documents/usbshare/usbshare$ sudo ./target/debug/usbshare                                                                                                                                                                                                                              
    [sudo] password for stephen:             
    1a86:e041, None
    write Error: hidapi error: hid_error is not implemented yet

     Sudo works.

    Oh, udev again, Rust HID is using "0002:0003:00" instead of "/dev/hidraw#". New udev rules:

    #works for pyusb control access, and rust hid
    SUBSYSTEM=="usb",  ATTR{idVendor}=="1a86", ATTR{idProduct}=="e041", MODE="0664", GROUP="plugdev"
    
    #works or chrome WebHID
    KERNEL=="hidraw*", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="e041", MODE="0664", GROUP="plugdev"
    stephen@AO751h:~/Documents/usbshare/usbshare$ sudo nano /etc/udev/rules.d/99-hid.rules
    stephen@AO751h:~/Documents/usbshare/usbshare$ ./target/debug/usbshare
    1a86:e041, None, "0002:0007:00"
    Open Error: Failed opening hid device
    stephen@AO751h:~/Documents/usbshare/usbshare$ sudo udevadm control --reload-rules
    stephen@AO751h:~/Documents/usbshare/usbshare$ sudo udevadm trigger
    stephen@AO751h:~/Documents/usbshare/usbshare$ ./target/debug/usbshare
    1a86:e041, None, "0002:0007:00"
    write Error: hidapi error: hid_error is not implemented yet

    Success.

  • UDEV for linux

    stephengeorgewest09/18/2021 at 17:48 2 comments

    Doesn't work on linux. Udev strikes again. udev helper things:

    sudo udevadm control --reload-rules
    sudo udevadm trigger
    journalctl -f
    udevadm monitor --environment
    udevadm info -q all -a /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-2
    udevadm info -q all -a /dev/hidraw1
    sudo udevadm control --log-priority=dubug
    

    run udevadm monitor --environment and re-plug:

    stephen@AO751h:~$ udevadm info -q all -a /dev/hidraw1
    
    Udevadm info starts with the device specified by the devpath and then
    walks up the chain of parent devices. It prints for every device
    found, all possible attributes in the udev rules key format.
    A rule to match, can be composed by the attributes of the device
    and the attributes from one single parent device.
    
      looking at device '/devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2:1.0/0003:1A86:E041.000E/hidraw/hidraw1':
        KERNEL=="hidraw1"
        SUBSYSTEM=="hidraw"
        DRIVER==""
    
      looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2:1.0/0003:1A86:E041.000E':
        KERNELS=="0003:1A86:E041.000E"
        SUBSYSTEMS=="hid"
        DRIVERS=="hid-generic"
        ATTRS{country}=="00"
    
      looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2:1.0':
        KERNELS=="2-2:1.0"
        SUBSYSTEMS=="usb"
        DRIVERS=="usbhid"
        ATTRS{authorized}=="1"
        ATTRS{bAlternateSetting}==" 0"
        ATTRS{bInterfaceClass}=="03"
        ATTRS{bInterfaceNumber}=="00"
        ATTRS{bInterfaceProtocol}=="00"
        ATTRS{bInterfaceSubClass}=="00"
        ATTRS{bNumEndpoints}=="01"
        ATTRS{supports_autosuspend}=="1"
    
      looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2/2-2':
        KERNELS=="2-2"
        SUBSYSTEMS=="usb"
        DRIVERS=="usb"
        ATTRS{authorized}=="1"
        ATTRS{avoid_reset_quirk}=="0"
        ATTRS{bConfigurationValue}=="1"
        ATTRS{bDeviceClass}=="00"
        ATTRS{bDeviceProtocol}=="00"
        ATTRS{bDeviceSubClass}=="00"
        ATTRS{bMaxPacketSize0}=="8"
        ATTRS{bMaxPower}=="100mA"
        ATTRS{bNumConfigurations}=="1"
        ATTRS{bNumInterfaces}==" 1"
        ATTRS{bcdDevice}=="1000"
        ATTRS{bmAttributes}=="80"
        ATTRS{busnum}=="2"
        ATTRS{configuration}==""
        ATTRS{devnum}=="12"
        ATTRS{devpath}=="2"
        ATTRS{idProduct}=="e041"
        ATTRS{idVendor}=="1a86"
        ATTRS{ltm_capable}=="no"
        ATTRS{maxchild}=="0"
        ATTRS{quirks}=="0x0"
        ATTRS{removable}=="unknown"
        ATTRS{rx_lanes}=="1"
        ATTRS{speed}=="12"
        ATTRS{tx_lanes}=="1"
        ATTRS{urbnum}=="14"
        ATTRS{version}==" 1.10"
    
      looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb2':
        KERNELS=="usb2"
        SUBSYSTEMS=="usb"
        DRIVERS=="usb"
        ATTRS{authorized}=="1"
        ATTRS{authorized_default}=="1"
        ATTRS{avoid_reset_quirk}=="0"
        ATTRS{bConfigurationValue}=="1"
        ATTRS{bDeviceClass}=="09"
        ATTRS{bDeviceProtocol}=="00"
        ATTRS{bDeviceSubClass}=="00"
        ATTRS{bMaxPacketSize0}=="64"
        ATTRS{bMaxPower}=="0mA"
        ATTRS{bNumConfigurations}=="1"
        ATTRS{bNumInterfaces}==" 1"
        ATTRS{bcdDevice}=="0504"
        ATTRS{bmAttributes}=="e0"
        ATTRS{busnum}=="2"
        ATTRS{configuration}==""
        ATTRS{devnum}=="1"
        ATTRS{devpath}=="0"
        ATTRS{idProduct}=="0001"
        ATTRS{idVendor}=="1d6b"
        ATTRS{interface_authorized_default}=="1"
        ATTRS{ltm_capable}=="no"
        ATTRS{manufacturer}=="Linux 5.4.0-77-generic uhci_hcd"
        ATTRS{maxchild}=="2"
        ATTRS{product}=="UHCI Host Controller"
        ATTRS{quirks}=="0x0"
        ATTRS{removable}=="unknown"
        ATTRS{rx_lanes}=="1"
        ATTRS{serial}=="0000:00:1d.0"
        ATTRS{speed}=="12"
        ATTRS{tx_lanes}=="1"
        ATTRS{urbnum}=="207"
        ATTRS{version}==" 1.10"
    
      looking at parent device '/devices/pci0000:00/0000:00:1d.0':
        KERNELS=="0000:00:1d.0"
        SUBSYSTEMS=="pci"
        DRIVERS=="uhci_hcd"
        ATTRS{ari_enabled}=="0"
        ATTRS{broken_parity_status}=="0"
        ATTRS{class}=="0x0c0300"
        ATTRS{consistent_dma_mask_bits}=="32"
        ATTRS{d3cold_allowed}=="0"
        ATTRS{device}=="0x8114"
        ATTRS{dma_mask_bits}=="32"
        ATTRS{driver_override}=="(null)"
        ATTRS{enable}=="1"
        ATTRS{irq}=="23"
        ATTRS{local_cpulist}=="0-1"
        ATTRS{local_cpus}=="3"
        ATTRS{msi_bus}=="1"
        ATTRS{revision}=="0x07"
        ATTRS{subsystem_device}=="0x0244"
        ATTRS{subsystem_vendor}=="0x1025"
        ATTRS{vendor}=="0x8086"
    
      looking at parent device '/devices/pci0000:00':
        KERNELS=="pci0000:00"
        SUBSYSTEMS==""
     DRIVERS==""
    ...
    Read more »

  • Back to JS with webHID

    stephengeorgewest09/18/2021 at 06:44 0 comments

    document.addEventListener('DOMContentLoaded', async () => {
        let permissionsRequestButton = document.getElementById("switch");
        permissionsRequestButton.addEventListener("click", () => {
            navigator.hid.requestDevice({ 
                filters: [{ vendorId: 0x1a86, productId: 0xe041 }] 
            }).then((devices) =>{
                devices[0].open().then(() => {
                    const wValue = new Uint8Array([
                        0x55, 0x02, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00
                    ]);
                    devices[0].sendReport(0, wValue).then(() => {
                        console.log("Sent output report: "+ wValue);
                    }).catch(err => {
                        console.log(err + " " + wValue)
                    })
                })
            });
        });
    });

    I guess I only really need the one USBHID SET_REPORT

  • Trying python

    stephengeorgewest09/17/2021 at 23:46 0 comments

    import usb.core
    import usb.util
    import usb.control
    
    # find our device
    dev = usb.core.find(idVendor=0x1a86, idProduct=0xe041)
    
    # was it found?
    if dev is None:
        raise ValueError('Device not found')
    
    # usb.control.set_configuration(dev, 0)
    
    bmRequestType = usb.util.build_request_type(usb.util.CTRL_OUT, usb.util.CTRL_TYPE_STANDARD, usb.util.CTRL_RECIPIENT_DEVICE) #// should be 0x00
    bRequest = 0x09
    bConfigurationValue = 1
    wIndex = 0
    wLength = 0
    
    dev.ctrl_transfer(bmRequestType, bRequest, bConfigurationValue)
    dev.ctrl_transfer(bmRequestType, bRequest, bConfigurationValue)

    Doesn't work though. PyUSB sent out a message that was 64 long instead of the 36.

    Unless that is libusb doing the padding, IDK.
    I don't know if the device keys off the length, or if it is missing the other hid messages first.

    The mystery exe runs in wine and puts the icon in the panel, but doesn't send off any usb communication though.

    I noticed that the last SET_REPORT differed from the previous pile of SET_REPORT

    55 01 00 00   00 00 00 00

    Then55 02 00 00     00 00 00 00

    Switching to hid I guess.

    libhidapi-hidraw0 won't open the device, and libhidapi-libusb.so.0 won't accept the buffer "ctypes.ArgumentError: argument 2: <type 'exceptions.TypeError'>: wrong type"

    import hid
    
    hid.lib
    # libhidapi-hidraw0
    # hid.HIDException: unable to open device
    h = hid.Device(vid, pid)
    h.open(vid, pid)
    
    buf = [0]*8
    buf[0] = 0x55
    buf[1] = 0x01
    #libhidapi-libusb.so.0
    #ctypes.ArgumentError: argument 2: <type 'exceptions.TypeError'>: wrong type
    h.send_feature_report([0x55, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
    h.send_feature_report([0x55, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])

  • Trying WebUSB

    stephengeorgewest09/14/2021 at 16:53 0 comments

    Trying to find a quick way to test writing out a single usb message, I didn't find any test programs.
    Nirsoft USBDeview, and microsoft usbview.exe helped see the devices, but that was it.
    HIDPyToy didn't help, because I needed to send a non-hid message.

    Now I'm looking at webusb, https://wicg.github.io/webusb/#enumdef-usbrequesttype

    It won't be useful, but it might be an easy test, and easy to port...? Should look something like:

    device = await navigator.usb.requestDevice({ 
        filters: [{
            vendorId: device.vendorId,
            productId: device.productId
        }]
    });
    await device.open();
    await device.controlTransferOut({
        requestType: 'standard',
        recipient: 'device',
        request: 0x09,
        value: 0x01,
        index: 0x00
    })

    Device.open ->  DOMException: Access denied. Looks like a windows problem.

    Ok, trying linux. ... no chrome installed. installing chromium. navigator.usb doesn't exist on chromium 92. Should be in since chrome 61. Maybe it is just stripped out of chromium. Downloading to be a local file worked, oh HTTPS only. DOMException: Access denied. Added udev rule. not fixed. maybe udev didn't work

    Maybe I should have checked usb-internals first. It looks like the "Testing Tool Panel" is what I wanted. I'm guessing the error is the same permissions error.

    yep, Udev didn't work. Fixed, I wrote "=" instead of "==", and now the testing panel doesn't report any errors, but also doesn't trigger the device switch, and the webcode errors with: "An attempt to claim a USB device interface has been blocked because it implements a protected class." Oh, it looks like it gets setup as an hid device, so webUSB blocks it... I guess I'll try another way.

  • Hardware/Software investigation

    stephengeorgewest09/14/2021 at 16:02 0 comments

     The usb hub has 2 un-connected ports. the 4 CH440G look to be the actual switches. CH9884 doesn't seem to have any data sheets online.

    When the port is selected, it connects a generic usb hub. same Vendor/Product as a Monoprice "aquamini".

    When the main hub is disconnected, it connects a device to the bus instead:
    idVendor:                        0x1A86 = Nanjing Qinherg Electronics Co., Ltd.
    idProduct:                       0xE041

    Which I'm guessing is CH9884

    The "Drivers" cd was a burned cd rom with USBShare.exe, and an installer, that I assume puts it somewhere and adds it to startup. I didn't run the installer. Running strings on the exe showed some usb.

    VID_1A86&PID_E041
    VID_1A86&PID_E040
    VID_5131&PID_2007

    I ran the exe through ghidra, to see if I could find anything, but I didn't find much. the USB identifiers, and some other strings. \(〇_o)/   I don't have the patience for this. I just decided to run the exe, and wireshark it.

    version 1.0.0
    Build: May 14 2011
    Maybe it still works

    Well, it puts a it turns green when connected to the usb hub, and ... well it turns green when not connected too. Turns red, then yellow when switching away.
    In yellow state, the program continuously asks (I think it is just connected checks?):


    Frame 31356: 44 bytes on wire (352 bits), 44 bytes captured (352 bits) on interface wireshark_extcap2440, id 0
    USB URB
        [Source: host]
        [Destination: 2.4.0]
        USBPcap pseudoheader length: 28
        IRP ID: 0xffff898c72c27010
        IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
        URB Function: URB_FUNCTION_CLASS_INTERFACE (0x001b)
        IRP information: 0x00, Direction: FDO -> PDO
            0000 000. = Reserved: 0x00
            .... ...0 = Direction: FDO -> PDO (0x0)
        URB bus id: 2
        Device address: 4
        Endpoint: 0x00, Direction: OUT
            0... .... = Direction: OUT (0)
            .... 0000 = Endpoint number: 0
        URB transfer type: URB_CONTROL (0x02)
        Packet Data Length: 16
        [Response in: 31357]
        Control transfer stage: Setup (0)
        [bInterfaceClass: HID (0x03)]
    Setup Data
        bmRequestType: 0x21
            0... .... = Direction: Host-to-device
            .01. .... = Type: Class (0x1)
            ...0 0001 = Recipient: Interface (0x01)
        bRequest: SET_REPORT (0x09)
        wValue: 0x0200
            ReportID: 0
            ReportType: Output (2)
        wIndex: 0
        wLength: 8
        bRequest: 9
        wValue: 0x0200
        wIndex: 0 (0x0000)
        wLength: 8
        Data Fragment: 5501000000000000
    

     In yellow state,  "Switch" gets enabled,

    data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIEAAABWCAYAAADom/XHAAAEv0lEQVR4nO2dT2vjRhjGBZuYXupEtiVbf51T99DC7qFqmoUeurCXpBvosT2UOfTQnspSWOhlWdosOvfSTzH0kwjUW75G0Bd4epAsj+SxI6dQT6Tn8AMjZCn28xvNyHlf23J++xsf/fVPTSz+7Mzo+gYXL15g4fnw/AB+ECIII4RRjDCKEcXLmni5RLw8IwZi7RM6JegnVpZlIMNiQ4KiKECGAyUgegnu7u7wf3LoN2HoUAJCCUgpgXrnRgkGSJZl8P0AQRjWMlCCgZFlGRx3jvnCQxCGlGCIZFmGyXQKx53D9wOEUWyIBFLAsqwKAdn1ReUpEitBmu/zRkiIvZ/TH7Isgz2ZYOa48IyRIE+RqMHnKVL5kBfYNVxKYNsTzBzHIAmkgJWkyP/zC6QEj1eCQkJYFpI0b2yXwoKQ6j5KcHmKREhle3mMekoRsnpejjRZby+PVz1Hpkiq7e1z9xlDJWiGVQcvxTpMKZAkSR1Wnq4eq3K0R3h5zM2AK2FWVx8p9luHPHIMlqAiL0enkNXjKigpqpGbpMiLHGmiC15ztdCG25ZlWNOD+RIU5SgvrwCrsCVEHb6AVOSgBH2RQKZbL+F5mmxMA0II5RL/0OmAEpglQbFtUVdsfg6w8blAM0Ap2sdoHruxMKQEJklAKAGhBIQSEEpAKAGhBIQSEEpAKAGhBIQSEEpAKIGCFJp/96pVRQ/hQVXIw8BICXSBN+sLSf8l2KgAkhADqvmjBEWBVQWQtsA0X1cEr4tByyIQIZJq246K4kYxSnsfZb903fzS98pjQyVQq4fVqaAVpBRKhbESlnb90C47U4JvrBeqyiOlqrnvlcfGSrCuLFYqiVujd102ph/lzRGs7KMpON0q2gBKzcyVYBW+VCqJO1cLl+Rpop8OKMFjkWAdYrOSuEu1cPMY+qaU9nTQXF9QAkMk0N7bt6cE3XSg7WjetTDcFTolOKwEhBIQSkAoAaEEhBIQSkAoAaEEhBKQAUpwe3tLDoBREhx6JAwdSkAoAaEEhBIQSkAoAaEEhBKQRyJBs5PI0n6JNemvBOpX3CvbUm1vAOmhBK1exA0oQf8l2NpptEWCrc2lusbU9v797jN83BLs/CGsjs2l9zamFkpT6+HDoARtCbpeCXb1FeoaU7c2tR4+DErQYI81wc7m0pJGY+q9gg0PQyUo6n7C++8OdjWXNkXQfpcBMViCOtAOnxNsay7d9lO72qbWw4dBCQglIJSAUILDvxFDhhIQSkAoAdkmwaH/KEIJiAkSZFkGMiw2JPj15x8xuv6A0eU7HL98g6PzH3D07Fs8+ewKTz69vJezszN8PB5jPD7B+OQUJ6enOD21S+w1tm3DtifEACbTKRx3Dn8lwej6QynB1Xscv3qL469+KkVIvsfR59/dyydPn1YhT2BPpphMp5hOZyUzp2bmEFNw3DnmCw9BGCKKl4oE3/xeXg1evcXxyzc4/vqXTjx7/hwzx4XjunDdOdz5AvOFh/nCw8Lzazzfh+cHxAB8P0AQhgijuCXB65tShKv3GF2+68wX5+d14J7vw/cD+EEIPwgRhFFNGEUIo5gYQhQvEcVLxMszRYLrm5LXK/7oxJcXF5UAQR18+0TlycoTEvP4Fxm/q6h5bDNLAAAAAElFTkSuQmCCand Upon clicking "switch", it fires off:
    Frame 31702: 36 bytes on wire (288 bits), 36 bytes captured (288 bits) on interface wireshark_extcap2440, id 0
    USB URB
        [Source: host]
        [Destination: 2.4.0]
        USBPcap pseudoheader length: 28
        IRP ID: 0xffff898c81fc6970
        IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
        URB Function: URB_FUNCTION_SELECT_CONFIGURATION (0x0000)
        IRP information: 0x00, Direction: FDO -> PDO
            0000 000. = Reserved: 0x00
            .... ...0 = Direction: FDO -> PDO (0x0)
        URB bus id: 2
        Device address: 4
        Endpoint: 0x00, Direction: OUT
            0... .... = Direction: OUT (0)
            .... 0000 = Endpoint number: 0
        URB transfer type: URB_CONTROL (0x02)
        Packet Data Length: 8
        [Response in: 31705]
        Control transfer stage: Setup (0)
    Setup Data
        bmRequestType: 0x00
            0... .... = Direction: Host-to-device
            .00. .... = Type: Standard (0x0)
            ...0 0000 = Recipient: Device (0x00)
        bRequest: SET CONFIGURATION (9)
        bConfigurationValue: 1
        wIndex: 0 (0x0000)
        wLength: 0

    So, I guess that is it.