kyokenn / rogdrv

ASUS ROG userspace mouse driver for Linux
GNU General Public License v3.0
84 stars 13 forks source link

ASUS ROG GX1000 EagleEye #12

Closed dreamtigers closed 3 years ago

dreamtigers commented 4 years ago

Hi, I'm currently working on adding support for my mouse, I've added it to the 50-rogdrv.rules file and my own class EagleEye to device.py.

I've never worked on something like this before, so if I manage to even just change profiles, I'd consider myself well served. That's why I started sniffing USB traffic (in Windows, through Wireshark) when switching profiles with the official program (which from now on I'll call Windows Driver) and getting my feet wet about URB and the likes.

I noticed a pattern: when switching profiles, the Setup Data sent from the host to the device is the following:

40 14 0X 00 00 00 00 00

Where X is the number of the profile I'm switching to.

Acccording to the USB spec^1, the format of Setup Data looks like this:

So, tying the information together, looks like the Windows Driver sends a write request to the mouse to change the profile.

Now going to rogdrv, I added my own Class EagleEye, and overrode the set_profile method, like this:

class EagleEye(Device):
    product_id = 0x17c4
    profiles = 4

    def set_profile(self, profile: int):
        if profile < 1 or profile > self.profiles:
            profile = 1

        logger.debug('setting profile to {}'.format(profile))

        request = [0] * 64
        request[0] = 0x40
        request[1] = 0x14
        request[2] = profile
        print(request)
        print(bytes(request))
        self.query(bytes(request))

So, rogdrv-config recognizes my mouse and it's interfaces correctly (assigns mouse/keyboard/control as it should), but when I run:

./launch_config --debug profile 4

I get a quick flash from the mouse's LED (which I take as something positive), but the program immediately fails with the following error:

Traceback (most recent call last):
  File "./launch_config", line 23, in <module>
    rogdrv_config()
  File "~/rogdrv/rogdrv/__main__.py", line 175, in rogdrv_config
    device.set_profile(int(args[2]))
  File "~/rogdrv/rogdrv/device.py", line 723, in set_profile
    self.query(bytes(request))
  File "~/rogdrv/rogdrv/device.py", line 233, in query
    response = self.read()
  File "~/rogdrv/rogdrv/device.py", line 206, in read
    data = hid.read_device(self._ctl, 64)
  File "~/rogdrv/rogdrv/hid.py", line 50, in read_device
    return device.read(count)
  File "/usr/local/lib/python3.8/dist-packages/hid/__init__.py", line 161, in read
    size = self.__hidcall(hidapi.hid_read, self.__dev, data, size)
  File "/usr/local/lib/python3.8/dist-packages/hid/__init__.py", line 146, in __hidcall
    raise HIDException(err)
hid.HIDException: None

And when I run it with sudo it writes to the device (without visible flash on the mouse)... but does not return from the read() call:

^CTraceback (most recent call last):
  File "./launch_config", line 23, in <module>
    rogdrv_config()
  File "~/rogdrv/rogdrv/__main__.py", line 175, in rogdrv_config
    device.set_profile(int(args[2]))
  File "~/rogdrv/rogdrv/device.py", line 723, in set_profile
    self.query(bytes(request))
  File "~/rogdrv/rogdrv/device.py", line 233, in query
    response = self.read()
  File "~/rogdrv/rogdrv/device.py", line 206, in read
    data = hid.read_device(self._ctl, 64)
  File "~/rogdrv/rogdrv/hid.py", line 50, in read_device
    return device.read(count)
  File "/usr/local/lib/python3.8/dist-packages/hid/__init__.py", line 161, in read
    size = self.__hidcall(hidapi.hid_read, self.__dev, data, size)
  File "/usr/local/lib/python3.8/dist-packages/hid/__init__.py", line 142, in __hidcall
    ret = function(*args, **kwargs)
KeyboardInterrupt

If I could receive some guidance on how to proceed or what I'm doing wrong, I'd greatly appreciate it.

kyokenn commented 4 years ago

I noticed a pattern: when switching profiles, the Setup Data sent from the host to the device is the following:

40 14 0X 00 00 00 00 00

Where X is the number of the profile I'm switching to.

Acccording to the USB spec^1, the format of Setup Data looks like this

Is it still HID? Because I'm using HID API, not just raw USB. HID protocol consists of 64-bytes request and response messages. Looks like reading from device is failing. You can try to skip the reading operation by replacing self.query(bytes(request)) with self.write(bytes(request))

But it would be better it you check the Wireshark again. Does the driver reads the response of each command it sends to the mouse? This is how the most of ROG mices work.

dreamtigers commented 4 years ago

Is it still HID? Because I'm using HID API, not just raw USB.

I'm not sure, I think it's not, since the request is 8-bytes and not 64-bytes as specified by the HID protocol. We're talking about Windows, right? How can I make sure if the Windows Driver is using HID or not? Could you explain to me/guide me on to how to install HID on Windows?

Looks like reading from device is failing. You can try to skip the reading operation by replacing self.query(bytes(request)) with self.write(bytes(request))

I tried this and rogdrv-config now fails when fetching the current profile from the mouse (that is, at get_profile()). I imagine it's due to the raw USB vs HID issue.

Does the driver reads the response of each command it sends to the mouse? This is how the most of ROG mices work.

Yes, as far as I know. It goes:

* host to 1.1.0: change profile request (0x40)
* 1.1.0 to host: response
* 1.1.2 to host: response
* host to 1.1.2: I'm not sure what this is
* host to 1.1.0: read request (0xc0)
* 1.1.0 to host: response

I'm not sure what interfaces 1.1.0 and 1.1.2 represent.

I can append a log if you want it.

dreamtigers commented 4 years ago

Here's the log, it happens when I switch from any profile (say, 2) to 1:

No.     Time           Source                Destination           Protocol Length Info
      13 28.299548      host                  1.1.0                 USB      36     URB_CONTROL out

Frame 13: 36 bytes on wire (288 bits), 36 bytes captured (288 bits) on interface wireshark_extcap1732, id 0
     Interface id: 0 (wireshark_extcap1732)
     Encapsulation type: USB packets with USBPcap header (152)
     Arrival Time: Sep 20, 2020 21:18:57.940519000 Hora estándar de Venezuela
     [Time shift for this packet: 0.000000000 seconds]
     Epoch Time: 1600651137.940519000 seconds
     [Time delta from previous captured frame: 28.299548000 seconds]
     [Time delta from previous displayed frame: 28.299548000 seconds]
     [Time since reference or first frame: 28.299548000 seconds]
     Frame Number: 13
     Frame Length: 36 bytes (288 bits)
     Capture Length: 36 bytes (288 bits)
     [Frame is marked: False]
     [Frame is ignored: False]
     [Protocols in frame: usb]
USB URB
     [Source: host]
     [Destination: 1.1.0]
     USBPcap pseudoheader length: 28
     IRP ID: 0xffffb3884aa4f010
     IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
     URB Function: URB_FUNCTION_VENDOR_DEVICE (0x0017)
     IRP information: 0x00, Direction: FDO -> PDO
     URB bus id: 1
     Device address: 1
     Endpoint: 0x00, Direction: OUT
     URB transfer type: URB_CONTROL (0x02)
     Packet Data Length: 8
     [Response in: 14]
     Control transfer stage: Setup (0)
     [bInterfaceClass: Unknown (0xffff)]
Setup Data
     bmRequestType: 0x40
     bRequest: 14
     wValue: 0x0001
     wIndex: 0 (0x0000)
     wLength: 0

No.     Time           Source                Destination           Protocol Length Info
      14 28.299812      1.1.0                 host                  USB      28     URB_CONTROL out

Frame 14: 28 bytes on wire (224 bits), 28 bytes captured (224 bits) on interface wireshark_extcap1732, id 0
     Interface id: 0 (wireshark_extcap1732)
     Encapsulation type: USB packets with USBPcap header (152)
     Arrival Time: Sep 20, 2020 21:18:57.940783000 Hora estándar de Venezuela
     [Time shift for this packet: 0.000000000 seconds]
     Epoch Time: 1600651137.940783000 seconds
     [Time delta from previous captured frame: 0.000264000 seconds]
     [Time delta from previous displayed frame: 0.000264000 seconds]
     [Time since reference or first frame: 28.299812000 seconds]
     Frame Number: 14
     Frame Length: 28 bytes (224 bits)
     Capture Length: 28 bytes (224 bits)
     [Frame is marked: False]
     [Frame is ignored: False]
     [Protocols in frame: usb]
USB URB
     [Source: 1.1.0]
     [Destination: host]
     USBPcap pseudoheader length: 28
     IRP ID: 0xffffb3884aa4f010
     IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
     URB Function: URB_FUNCTION_CONTROL_TRANSFER (0x0008)
     IRP information: 0x01, Direction: PDO -> FDO
     URB bus id: 1
     Device address: 1
     Endpoint: 0x00, Direction: OUT
     URB transfer type: URB_CONTROL (0x02)
     Packet Data Length: 0
     [Request in: 13]
     [Time from request: 0.000264000 seconds]
     Control transfer stage: Complete (3)
     [bInterfaceClass: Unknown (0xffff)]

No.     Time           Source                Destination           Protocol Length Info
      15 28.400637      1.1.2                 host                  USB      35     URB_INTERRUPT in

Frame 15: 35 bytes on wire (280 bits), 35 bytes captured (280 bits) on interface wireshark_extcap1732, id 0
     Interface id: 0 (wireshark_extcap1732)
     Encapsulation type: USB packets with USBPcap header (152)
     Arrival Time: Sep 20, 2020 21:18:58.041608000 Hora estándar de Venezuela
     [Time shift for this packet: 0.000000000 seconds]
     Epoch Time: 1600651138.041608000 seconds
     [Time delta from previous captured frame: 0.100825000 seconds]
     [Time delta from previous displayed frame: 0.100825000 seconds]
     [Time since reference or first frame: 28.400637000 seconds]
     Frame Number: 15
     Frame Length: 35 bytes (280 bits)
     Capture Length: 35 bytes (280 bits)
     [Frame is marked: False]
     [Frame is ignored: False]
     [Protocols in frame: usb]
USB URB
     [Source: 1.1.2]
     [Destination: host]
     USBPcap pseudoheader length: 27
     IRP ID: 0xffffb3884b2cd050
     IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
     URB Function: URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER (0x0009)
     IRP information: 0x01, Direction: PDO -> FDO
     URB bus id: 1
     Device address: 1
     Endpoint: 0x82, Direction: IN
     URB transfer type: URB_INTERRUPT (0x01)
     Packet Data Length: 8
     [bInterfaceClass: HID (0x03)]
Leftover Capture Data: 0000000000000000

No.     Time           Source                Destination           Protocol Length Info
      16 28.400792      host                  1.1.2                 USB      27     URB_INTERRUPT in

Frame 16: 27 bytes on wire (216 bits), 27 bytes captured (216 bits) on interface wireshark_extcap1732, id 0
     Interface id: 0 (wireshark_extcap1732)
     Encapsulation type: USB packets with USBPcap header (152)
     Arrival Time: Sep 20, 2020 21:18:58.041763000 Hora estándar de Venezuela
     [Time shift for this packet: 0.000000000 seconds]
     Epoch Time: 1600651138.041763000 seconds
     [Time delta from previous captured frame: 0.000155000 seconds]
     [Time delta from previous displayed frame: 0.000155000 seconds]
     [Time since reference or first frame: 28.400792000 seconds]
     Frame Number: 16
     Frame Length: 27 bytes (216 bits)
     Capture Length: 27 bytes (216 bits)
     [Frame is marked: False]
     [Frame is ignored: False]
     [Protocols in frame: usb]
USB URB
     [Source: host]
     [Destination: 1.1.2]
     USBPcap pseudoheader length: 27
     IRP ID: 0xffffb3884b2cd050
     IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
     URB Function: URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER (0x0009)
     IRP information: 0x00, Direction: FDO -> PDO
     URB bus id: 1
     Device address: 1
     Endpoint: 0x82, Direction: IN
     URB transfer type: URB_INTERRUPT (0x01)
     Packet Data Length: 0
     [Response in: 27]
     [bInterfaceClass: HID (0x03)]

No.     Time           Source                Destination           Protocol Length Info
      17 28.407917      host                  1.1.0                 USB      36     URB_CONTROL in

Frame 17: 36 bytes on wire (288 bits), 36 bytes captured (288 bits) on interface wireshark_extcap1732, id 0
     Interface id: 0 (wireshark_extcap1732)
     Encapsulation type: USB packets with USBPcap header (152)
     Arrival Time: Sep 20, 2020 21:18:58.048888000 Hora estándar de Venezuela
     [Time shift for this packet: 0.000000000 seconds]
     Epoch Time: 1600651138.048888000 seconds
     [Time delta from previous captured frame: 0.007125000 seconds]
     [Time delta from previous displayed frame: 0.007125000 seconds]
     [Time since reference or first frame: 28.407917000 seconds]
     Frame Number: 17
     Frame Length: 36 bytes (288 bits)
     Capture Length: 36 bytes (288 bits)
     [Frame is marked: False]
     [Frame is ignored: False]
     [Protocols in frame: usb]
USB URB
     [Source: host]
     [Destination: 1.1.0]
     USBPcap pseudoheader length: 28
     IRP ID: 0xffffb3884ad49260
     IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
     URB Function: URB_FUNCTION_VENDOR_DEVICE (0x0017)
     IRP information: 0x00, Direction: FDO -> PDO
     URB bus id: 1
     Device address: 1
     Endpoint: 0x80, Direction: IN
     URB transfer type: URB_CONTROL (0x02)
     Packet Data Length: 8
     [Response in: 18]
     Control transfer stage: Setup (0)
     [bInterfaceClass: Unknown (0xffff)]
Setup Data
     bmRequestType: 0xc0
     bRequest: 5
     wValue: 0x0000
     wIndex: 0 (0x0000)
     wLength: 2

No.     Time           Source                Destination           Protocol Length Info
      18 *REF*          1.1.0                 host                  USB      30     URB_CONTROL in

Frame 18: 30 bytes on wire (240 bits), 30 bytes captured (240 bits) on interface wireshark_extcap1732, id 0
     Interface id: 0 (wireshark_extcap1732)
     Encapsulation type: USB packets with USBPcap header (152)
     Arrival Time: Sep 20, 2020 21:18:58.049158000 Hora estándar de Venezuela
     [Time shift for this packet: 0.000000000 seconds]
     Epoch Time: 1600651138.049158000 seconds
     [Time delta from previous captured frame: 0.000270000 seconds]
     [Time delta from previous displayed frame: 0.000270000 seconds]
     [Time since reference or first frame: 0.000000000 seconds]
     [This is a Time Reference frame]
     Frame Number: 18
     Frame Length: 30 bytes (240 bits)
     Capture Length: 30 bytes (240 bits)
     [Frame is marked: False]
     [Frame is ignored: False]
     [Protocols in frame: usb]
USB URB
     [Source: 1.1.0]
     [Destination: host]
     USBPcap pseudoheader length: 28
     IRP ID: 0xffffb3884ad49260
     IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
     URB Function: URB_FUNCTION_CONTROL_TRANSFER (0x0008)
     IRP information: 0x01, Direction: PDO -> FDO
     URB bus id: 1
     Device address: 1
     Endpoint: 0x80, Direction: IN
     URB transfer type: URB_CONTROL (0x02)
     Packet Data Length: 2
     [Request in: 17]
     [Time from request: 0.000270000 seconds]
     Control transfer stage: Complete (3)
     [bInterfaceClass: Unknown (0xffff)]
CONTROL response data: 0101
kyokenn commented 4 years ago

This is how it looks like for my mouse: Request (switch profile): 1 Response (same data, because mouse confirmed selected profile): 2 You can ignore the USB packet headers, so the actual 64-bytes data starts at 0x50 0x02 ...

Looks like your packets are 8-byte length instead of 64 (they are also parsed with Wireshark as Setup Data), so you need to change this line: request = [0] * 64 This one allocates a 64-bytes buffer and fills with zeros.

Also you need to override the read method - https://github.com/kyokenn/rogdrv/blob/master/rogdrv/device.py#L199 because it tries read 64 bytes by default, so thats why it waits forever. I think your response packets are 8-bytes length too.

dreamtigers commented 4 years ago

This is how it looks like for my mouse:

I see, thank you! Our mice definitely behave differently (mine doesn't respond the request with the same data, for example).

Also you need to override the read method - https://github.com/kyokenn/rogdrv/blob/master/rogdrv/device.py#L199 because it tries read 64 bytes by default, so thats why it waits forever. I think your response packets are 8-bytes length too.

I'll apply these changes and what I can get.