Author Topic: Varmilo VA87 firmware issues (NKRO)  (Read 6928 times)

0 Members and 1 Guest are viewing this topic.

Offline ethereal

  • Thread Starter
  • Posts: 21
Varmilo VA87 firmware issues (NKRO)
« on: Fri, 18 December 2020, 07:42:01 »
Last week I got a Varmilo VA87 RGB (it's exactly the same as the VA87M, except for the RGB lightning). I chose it because of the great build quality (among the best for prebuilt keyboards). And also for the aesthetics (this is more subjective, but at least for me its design is truly gorgeous).

The Varmilo is awesome but I've found some firmware issues with NKRO. I've been researching them during the past days and would like to share my findings.

My unit is a VA87 RGB and is using the latest firmware available (version 1.4 from September 2020). I guess my findings would also apply to the VA87M and VA88M units, since these are pretty much identical to mine.

There are 3 usual ways for implementing NKRO in USB keyboards. This reference describe these 3 methods: https://github.com/qmk/qmk_firmware/blob/master/docs/usb_nkro.txt. The Varmilo implements USB NKRO using a mix of the first and third methods. It appears to the OS as 2 keyboards. The first one implements the boot keyboard, so it's limited to 8 control keys plus 6 normal keys. The second implements a full NKRO keyboard, using a bitmap report. This second keyboard only sends events when you press more than 6 keys at once.

The Varmilo appears to the OS as 4 USB interfaces. One implements the boot keyboard (with 6KRO), one the second keyboard (with full NKRO) and the other 2 are used (I think) for firmware updates.

We can see, on a Linux machine, the Varmilo's USB configurations, interfaces and endpoints:

Code: [Select]
lsusb -d 04d9:8008 -v

Bus 005 Device 110: ID 04d9:8008 Holtek Semiconductor, Inc.
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  idVendor           0x04d9 Holtek Semiconductor, Inc.
  idProduct          0x8008
  bcdDevice            1.04
  iManufacturer           1 HOLTEK
  iProduct                2 USB-HID Keyboard
  iSerial                 3 AP0000000003
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x007b
    bNumInterfaces          4
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower              500mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      1 Boot Interface Subclass
      bInterfaceProtocol      1 Keyboard
      iInterface              0
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      59
         Report Descriptors:
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0
      bInterfaceProtocol      0
      iInterface              0
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      34
         Report Descriptors:
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x04  EP 4 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        2
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0
      bInterfaceProtocol      0
      iInterface              0
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength     181
         Report Descriptors:
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        3
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0
      bInterfaceProtocol      0
      iInterface              0
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      31
         Report Descriptors:
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x85  EP 5 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x06  EP 6 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
can't get debug descriptor: Resource temporarily unavailable
Device Status:     0x0000
  (Bus Powered)

These are the HID USB report descriptors for the Varmilo. There are 4 descriptors, one for each USB interface.

Code: [Select]
usbhid-dump -d 04d9:8008   
005:110:003:DESCRIPTOR         1608293927.780856
 06 01 FF 09 01 A1 01 15 00 26 FF 00 75 08 95 40
 09 20 81 02 09 21 91 02 09 22 95 08 B1 02 C0

005:110:002:DESCRIPTOR         1608293927.782320
 05 01 09 02 A1 01 85 01 09 01 A1 00 05 09 15 00
 25 01 19 01 29 05 75 01 95 05 81 02 95 03 81 01
 05 01 16 01 80 26 FF 7F 09 30 09 31 75 10 95 02
 81 06 15 81 25 7F 09 38 75 08 95 01 81 06 05 0C
 0A 38 02 95 01 81 06 C0 C0 05 01 09 80 A1 01 85
 02 19 81 29 83 15 00 25 01 75 01 95 03 81 02 95
 05 81 01 C0 05 0C 09 01 A1 01 85 03 19 00 2A FF
 07 15 00 26 FF 07 95 01 75 10 81 00 C0 06 02 FF
 09 01 A1 01 85 04 15 00 26 FF 00 09 03 75 08 95
 03 81 00 C0 05 01 09 06 A1 01 85 05 05 07 95 01
 75 08 81 03 95 E8 75 01 15 00 25 01 05 07 19 00
 29 E7 81 00 C0

005:110:001:DESCRIPTOR         1608293927.783889
 06 00 FF 09 01 A1 01 09 02 15 00 26 FF 00 75 08
 95 40 81 02 09 03 15 00 26 FF 00 75 08 95 40 91
 02 C0

005:110:000:DESCRIPTOR         1608293927.784818
 05 01 09 06 A1 01 05 08 15 00 25 01 19 01 29 03
 75 01 95 03 91 02 95 05 91 01 05 07 19 E0 29 E7
 75 01 95 08 81 02 95 08 81 01 15 00 26 FF 00 19
 00 2A FF 00 75 08 95 06 81 00 C0

We are gonna decode the USB descriptors, to get something more meaningful.

This is the interface #0 descriptor (the one that implements the first keyboard). It's similar to the USB HID boot keyboard report specification. We see it sends an 8-bit bitmap for the 8 modifier keys and a 6-field array for 6 non-modifier keys.

Code: [Select]
hid-decode /dev/hidraw1
0x05, 0x01,                    // Usage Page (Generic Desktop)        0
0x09, 0x06,                    // Usage (Keyboard)                    2
0xa1, 0x01,                    // Collection (Application)            4
0x05, 0x08,                    //  Usage Page (LEDs)                  6
0x15, 0x00,                    //  Logical Minimum (0)                8
0x25, 0x01,                    //  Logical Maximum (1)                10
0x19, 0x01,                    //  Usage Minimum (1)                  12
0x29, 0x03,                    //  Usage Maximum (3)                  14
0x75, 0x01,                    //  Report Size (1)                    16
0x95, 0x03,                    //  Report Count (3)                   18
0x91, 0x02,                    //  Output (Data,Var,Abs)              20
0x95, 0x05,                    //  Report Count (5)                   22
0x91, 0x01,                    //  Output (Cnst,Arr,Abs)              24
0x05, 0x07,                    //  Usage Page (Keyboard)              26
0x19, 0xe0,                    //  Usage Minimum (224)                28
0x29, 0xe7,                    //  Usage Maximum (231)                30
0x75, 0x01,                    //  Report Size (1)                    32
0x95, 0x08,                    //  Report Count (8)                   34
0x81, 0x02,                    //  Input (Data,Var,Abs)               36
0x95, 0x08,                    //  Report Count (8)                   38
0x81, 0x01,                    //  Input (Cnst,Arr,Abs)               40
0x15, 0x00,                    //  Logical Minimum (0)                42
0x26, 0xff, 0x00,              //  Logical Maximum (255)              44
0x19, 0x00,                    //  Usage Minimum (0)                  47
0x2a, 0xff, 0x00,              //  Usage Maximum (255)                49
0x75, 0x08,                    //  Report Size (8)                    52
0x95, 0x06,                    //  Report Count (6)                   54
0x81, 0x00,                    //  Input (Data,Arr,Abs)               56
0xc0,                          // End Collection                      58

This is the report for USB interface #2. This one is more complex. It cointains 5 different HID reports. The "System Control" report implements the Wake computer function (i.e. the computer wakes from sleeping mode when pressing any key of the keyboard). The "Consumer Control" report implements the media controls. We can see a "Vendor defined" report, which I don't know what it's used for. And even a HID mouse report (although the Varmilo doesn't implement, AFAIK, any mouse function).

Code: [Select]
hid-decode /dev/hidraw3
0x05, 0x01,                    // Usage Page (Generic Desktop)        0
0x09, 0x02,                    // Usage (Mouse)                       2
0xa1, 0x01,                    // Collection (Application)            4
0x85, 0x01,                    //  Report ID (1)                      6
0x09, 0x01,                    //  Usage (Pointer)                    8
0xa1, 0x00,                    //  Collection (Physical)              10
0x05, 0x09,                    //   Usage Page (Button)               12
0x15, 0x00,                    //   Logical Minimum (0)               14
0x25, 0x01,                    //   Logical Maximum (1)               16
0x19, 0x01,                    //   Usage Minimum (1)                 18
0x29, 0x05,                    //   Usage Maximum (5)                 20
0x75, 0x01,                    //   Report Size (1)                   22
0x95, 0x05,                    //   Report Count (5)                  24
0x81, 0x02,                    //   Input (Data,Var,Abs)              26
0x95, 0x03,                    //   Report Count (3)                  28
0x81, 0x01,                    //   Input (Cnst,Arr,Abs)              30
0x05, 0x01,                    //   Usage Page (Generic Desktop)      32
0x16, 0x01, 0x80,              //   Logical Minimum (-32767)          34
0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           37
0x09, 0x30,                    //   Usage (X)                         40
0x09, 0x31,                    //   Usage (Y)                         42
0x75, 0x10,                    //   Report Size (16)                  44
0x95, 0x02,                    //   Report Count (2)                  46
0x81, 0x06,                    //   Input (Data,Var,Rel)              48
0x15, 0x81,                    //   Logical Minimum (-127)            50
0x25, 0x7f,                    //   Logical Maximum (127)             52
0x09, 0x38,                    //   Usage (Wheel)                     54
0x75, 0x08,                    //   Report Size (8)                   56
0x95, 0x01,                    //   Report Count (1)                  58
0x81, 0x06,                    //   Input (Data,Var,Rel)              60
0x05, 0x0c,                    //   Usage Page (Consumer Devices)     62
0x0a, 0x38, 0x02,              //   Usage (AC Pan)                    64
0x95, 0x01,                    //   Report Count (1)                  67
0x81, 0x06,                    //   Input (Data,Var,Rel)              69
0xc0,                          //  End Collection                     71
0xc0,                          // End Collection                      72
0x05, 0x01,                    // Usage Page (Generic Desktop)        73
0x09, 0x80,                    // Usage (System Control)              75
0xa1, 0x01,                    // Collection (Application)            77
0x85, 0x02,                    //  Report ID (2)                      79
0x19, 0x81,                    //  Usage Minimum (129)                81
0x29, 0x83,                    //  Usage Maximum (131)                83
0x15, 0x00,                    //  Logical Minimum (0)                85
0x25, 0x01,                    //  Logical Maximum (1)                87
0x75, 0x01,                    //  Report Size (1)                    89
0x95, 0x03,                    //  Report Count (3)                   91
0x81, 0x02,                    //  Input (Data,Var,Abs)               93
0x95, 0x05,                    //  Report Count (5)                   95
0x81, 0x01,                    //  Input (Cnst,Arr,Abs)               97
0xc0,                          // End Collection                      99
0x05, 0x0c,                    // Usage Page (Consumer Devices)       100
0x09, 0x01,                    // Usage (Consumer Control)            102
0xa1, 0x01,                    // Collection (Application)            104
0x85, 0x03,                    //  Report ID (3)                      106
0x19, 0x00,                    //  Usage Minimum (0)                  108
0x2a, 0xff, 0x07,              //  Usage Maximum (2047)               110
0x15, 0x00,                    //  Logical Minimum (0)                113
0x26, 0xff, 0x07,              //  Logical Maximum (2047)             115
0x95, 0x01,                    //  Report Count (1)                   118
0x75, 0x10,                    //  Report Size (16)                   120
0x81, 0x00,                    //  Input (Data,Arr,Abs)               122
0xc0,                          // End Collection                      124
0x06, 0x02, 0xff,              // Usage Page (Vendor Usage Page 0xff02) 125
0x09, 0x01,                    // Usage (Vendor Usage 0x01)           128
0xa1, 0x01,                    // Collection (Application)            130
0x85, 0x04,                    //  Report ID (4)                      132
0x15, 0x00,                    //  Logical Minimum (0)                134
0x26, 0xff, 0x00,              //  Logical Maximum (255)              136
0x09, 0x03,                    //  Usage (Vendor Usage 0x03)          139
0x75, 0x08,                    //  Report Size (8)                    141
0x95, 0x03,                    //  Report Count (3)                   143
0x81, 0x00,                    //  Input (Data,Arr,Abs)               145
0xc0,                          // End Collection                      147
0x05, 0x01,                    // Usage Page (Generic Desktop)        148
0x09, 0x06,                    // Usage (Keyboard)                    150
0xa1, 0x01,                    // Collection (Application)            152
0x85, 0x05,                    //  Report ID (5)                      154
0x05, 0x07,                    //  Usage Page (Keyboard)              156
0x95, 0x01,                    //  Report Count (1)                   158
0x75, 0x08,                    //  Report Size (8)                    160
0x81, 0x03,                    //  Input (Cnst,Var,Abs)               162
0x95, 0xe8,                    //  Report Count (232)                 164
0x75, 0x01,                    //  Report Size (1)                    166
0x15, 0x00,                    //  Logical Minimum (0)                168
0x25, 0x01,                    //  Logical Maximum (1)                170
0x05, 0x07,                    //  Usage Page (Keyboard)              172
0x19, 0x00,                    //  Usage Minimum (0)                  174
0x29, 0xe7,                    //  Usage Maximum (231)                176
0x81, 0x00,                    //  Input (Data,Arr,Abs)               178
0xc0,                          // End Collection                      180

The #5 report (Report ID 5) is the one that implements the NKRO keyboard and the truly relevant for the issue in question. We see the Report Size is 1 (1 bit), and the Report Count 232. I.e, the keyboard will be sending a 232-bit bitmap (29 bytes in total). But the HID item is declared as "Input (Data,Arr,Abs)". And here is the problem. In the USB HID specification (https://usb.org/sites/default/files/hid1_11.pdf), we can see, on page 30, the following definitions for "Array" and "Variable":


Quote
Indicates whether the item creates variable or array data fields in reports.
In variable fields, each field represents data from a physical control.
The number of bits reserved for each field is determined by preceding Report Size/ReportCount items.
For example, a bank of eight on/off switches could be reported in 1 byte declared by a variable Input item where each bit represents one switch,on(1)or off (0) (Report Size = 1, Report Count = 8).
Alternatively, a variable Input item could add 1 report byte used to represent the state of four three-position buttons, where the state of each button is represented by two bits (Report Size = 2, Report Count = 4).
Or 1 byte from a variable Input item could represent the x position of a joystick (Report Size = 8, Report Count =1).

An array provides an alternate means for describing the data returned from a group of buttons.
Arrays are more efficient, if less flexible than variable items.
Rather than returning a single bit for each button in the group, an array returns an index in each field that corresponds to the pressed button (like keyboard scan codes).
An out-of range value in and array field is considered no controls asserted.
Buttons or keys in an array that are simultaneously pressed need to be reported in multiple fields.
Therefore, the number of fields in an array input item (Report Count) dictates the maximum number of simultaneous controls that can be reported.
A keyboard could report up to three simultaneous keys using an array with three 8-bit fields (Report Size = 8, Report Count = 3).

This is. In USB HID parlance, a "Variable" item is a bitmap, in which each bit asserts the state of every control or key. Instead an "Array" is a series of fields, each one with the key or control value codes. The Varmilo, on the wire, is sending a 232-bit bitmap (each one asserting the status of 1 key, so we have a 232-key limit. As the keyboard has less than 232 keys, the USB HID reporting format is actually NKRO). But the USB HID report descriptor is wrong.

It contains this part:

Code: [Select]
0x95, 0xe8,                    //  Report Count (232)                 164
0x75, 0x01,                    //  Report Size (1)                    166
0x15, 0x00,                    //  Logical Minimum (0)                168
0x25, 0x01,                    //  Logical Maximum (1)                170
0x05, 0x07,                    //  Usage Page (Keyboard)              172
0x19, 0x00,                    //  Usage Minimum (0)                  174
0x29, 0xe7,                    //  Usage Maximum (231)                176
0x81, 0x00,                    //  Input (Data,Arr,Abs)               178
0xc0,                          // End Collection                      180


But it should be this instead:

Code: [Select]

0x95, 0xe8,                    //  Report Count (232)                 164
0x75, 0x01,                    //  Report Size (1)                    166
0x15, 0x00,                    //  Logical Minimum (0)                168
0x25, 0x01,                    //  Logical Maximum (1)                170
0x05, 0x07,                    //  Usage Page (Keyboard)              172
0x19, 0x00,                    //  Usage Minimum (0)                  174
0x29, 0xe7,                    //  Usage Maximum (231)                176
0x81, 0x02,                    //  Input (Data,Var,Abs)               178
0xc0,                          // End Collection                      180


This is, I think, a firmware error in the keyboard that could have an impact on its NKRO functionality. On the Linux platform, for example, the kernel would take the data sent by the Varmilo as an HID array, when actually it's a bitmap. The end result being that on Linux the Varmilo is limited to 6KRO, as the kernel can only register the events sent by the first keyboard (the one with 6KRO).

I've also tried the Varmilo on a Windows 10 virtual machine. On this OS, the NKRO functionality works fine. Although the Varmilo's firmware is not complying with the USB HID specification, the Windows kernel is able to interpret the data sent by the keyboard. Why does this happen? Well, I guess that the Windows kernel is seeing that the HID descriptor is describing an array with 1-bit elements, and that this doesn't make sense. So it takes the array to actually be a bitmap. (On the Windows platform, which is desktop oriented, the kernel has to deal with a lot of buggy hardware, which is supposed to "just work". So it has to use a lot of hacks in the code in order to get that. I guess this one with USB keyboards is one of them).

What are your thoughts on all of this? I've spent a lot of time the past days researching this issue, learning a lot about themes that were completely new to me. I think my conclusions are right but I would like to see some feedback and comments.
« Last Edit: Sun, 20 December 2020, 17:23:42 by ethereal »

Offline ethereal

  • Thread Starter
  • Posts: 21
Re: Varmilo VA87 firmware issues (NKRO)
« Reply #1 on: Fri, 18 December 2020, 08:30:08 »
BTW, this is the part of the Linux kernel that checks whether the HID items are an array or a variable: https://elixir.bootlin.com/linux/v5.10/source/drivers/hid/hid-core.c#L1530

Code: [Select]
/*
 * Analyse a received field, and fetch the data from it. The field
 * content is stored for next report processing (we do differential
 * reporting to the layer).
 */

static void hid_input_field(struct hid_device *hid, struct hid_field *field,
    __u8 *data, int interrupt)
{
unsigned n;
unsigned count = field->report_count;
unsigned offset = field->report_offset;
unsigned size = field->report_size;
__s32 min = field->logical_minimum;
__s32 max = field->logical_maximum;
__s32 *value;

value = kmalloc_array(count, sizeof(__s32), GFP_ATOMIC);
if (!value)
return;

for (n = 0; n < count; n++) {

value[n] = min < 0 ?
snto32(hid_field_extract(hid, data, offset + n * size,
       size), size) :
hid_field_extract(hid, data, offset + n * size, size);

/* Ignore report if ErrorRollOver */
if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
    value[n] >= min && value[n] <= max &&
    value[n] - min < field->maxusage &&
    field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1)
goto exit;
}

for (n = 0; n < count; n++) {

if (HID_MAIN_ITEM_VARIABLE & field->flags) {
hid_process_event(hid, field, &field->usage[n], value[n], interrupt);
continue;
}

if (field->value[n] >= min && field->value[n] <= max
&& field->value[n] - min < field->maxusage
&& field->usage[field->value[n] - min].hid
&& search(value, field->value[n], count))
hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt);

if (value[n] >= min && value[n] <= max
&& value[n] - min < field->maxusage
&& field->usage[value[n] - min].hid
&& search(field->value, value[n], count))
hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt);
}

memcpy(field->value, value, count * sizeof(__s32));
exit:
kfree(value);
}

/*
 * Output the field into the report.
 */

I've patched this file so that HID_MAIN_ITEM_VARIABLE is asserted for Varmilo's #5 HID report. This effectively makes the relevant data sent by the Varmilo be interpreted as a bitmap, rather than an array (in the USB HID sense). With this change, the Varmilo has NKRO functionality on Linux.

Offline suicidal_orange

  • * Global Moderator
  • Posts: 4771
  • Location: England
Re: Varmilo VA87 firmware issues (NKRO)
« Reply #2 on: Fri, 18 December 2020, 12:54:56 »
That the fix works proves you identified the problem - very impressive!

Now tell Varmillo to fix it so you don't have to do this everytime there's a kernel update...
120/100g linear Zealio R1  
GMK Hyperfuse
'Split everything' perfection  
MX Clear
SA Hack'd by Geeks     
EasyAVR mod

Offline ethereal

  • Thread Starter
  • Posts: 21
Re: Varmilo VA87 firmware issues (NKRO)
« Reply #3 on: Fri, 18 December 2020, 14:23:07 »
Thanks for your feedback!

I intend to do so. The change required in the firmware to fix the issue should be very small. So I hope they will do it.

Offline techwitch

  • Posts: 2
Re: Varmilo VA87 firmware issues (NKRO)
« Reply #4 on: Wed, 30 December 2020, 11:22:14 »
i've got a va108m that i think is hitting this (or a similar) issue. mind posting the patch file so i can test it out?

Offline ethereal

  • Thread Starter
  • Posts: 21
Re: Varmilo VA87 firmware issues (NKRO)
« Reply #5 on: Fri, 01 January 2021, 08:42:07 »
Sure thing. ;)

The patch isn't very elaborate. You just test if the device in question is the Varmilo (by checking the vendor and device IDs). Then, if the number of elements in the HID report is 232, you force it to be of type "Variable" (by asserting the HID_MAIN_ITEM_VARIABLE bit). With this patch applied, my VA87 RGB works fine, with full NKRO functionality.

Patch is for a 4.19.160 kernel (latest Debian 10 kernel). It should be easy to adapt to your particular kernel version.

You may have to change the USB vendor and device IDs (maybe they're different in your VA108M). The "232" hardcoded value may also have to be changed (maybe the VA108M uses a different number of elements in the HID report descriptor).

Code: [Select]
--- linux-source-4.19/drivers/hid/hid-core.c    2020-11-24 13:27:27.000000000 +0100
+++ linux-source-4.19-varmilo/drivers/hid/hid-core.c    2021-01-01 14:01:39.703343522 +0100
@@ -1362,7 +1362,11 @@
        value = kmalloc_array(count, sizeof(__s32), GFP_ATOMIC);
        if (!value)
                return;
-
+
+       if ( (hid->vendor == 0x04d9) && (hid->product == 0x8008) && (count == 232) ) {
+               field->flags = field->flags | HID_MAIN_ITEM_VARIABLE;
+       }
+
        for (n = 0; n < count; n++) {
 
                value[n] = min < 0 ?
« Last Edit: Fri, 01 January 2021, 14:33:38 by ethereal »

Offline techwitch

  • Posts: 2
Re: Varmilo VA87 firmware issues (NKRO)
« Reply #6 on: Mon, 04 January 2021, 20:39:55 »
thanks a bunch! i wasn't quite following which numbers were important in your initial post, but with that patch i've got it figured out. i'll post back here if it works for me

Offline ethereal

  • Thread Starter
  • Posts: 21
Re: Varmilo VA87 firmware issues (NKRO)
« Reply #7 on: Tue, 05 January 2021, 05:33:43 »
BTW, 2 weeks ago I contacted Varmilo's support, telling them about this issue.

If your VA108M is also affected, maybe you should also contact them.

Offline maxammann

  • Posts: 2
Re: Varmilo VA87 firmware issues (NKRO)
« Reply #8 on: Fri, 05 February 2021, 07:27:34 »
Hello,

just registered to answer here. I don't have this problem on my VA88M. NKRO works for me: http://gadzikowski.com/nkeyrollover.html
I did not yet validate the usbhid dump but at least it does not seem to be a problem for me. I'm running: 5.10.13-arch1-1

Offline ethereal

  • Thread Starter
  • Posts: 21
Re: Varmilo VA87 firmware issues (NKRO)
« Reply #9 on: Sat, 06 February 2021, 11:57:23 »
Thanks for reporting!

The VA88M is, AFAIK, almost the same as the VA87M (or my VA87 RGB). The only difference should be the layout. I expected both models to behave in exactly the same way regarding NKRO functionality.

I'm curious how the VA88M's HID descriptors look like. Could you please post the output of hid-decode on the relevant descriptor? (On Archlinux it's really easy to run hid-decode, you only have to install "community/hid-tools". Then you run it on the appropriate hidraw device for your VA88M: "hid-decode /dev/hidraw0", "hid-decode /dev/hidraw1", ...).

Offline maxammann

  • Posts: 2
Re: Varmilo VA87 firmware issues (NKRO)
« Reply #10 on: Sat, 13 February 2021, 04:22:41 »
Sure!

sudo lsusb -d 05ac:024f -v
Code: [Select]
Bus 001 Device 045: ID 05ac:024f Apple, Inc. Aluminium Keyboard (ANSI)
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0         8
  idVendor           0x05ac Apple, Inc.
  idProduct          0x024f Aluminium Keyboard (ANSI)
  bcdDevice            1.00
  iManufacturer           1 AONE
  iProduct                2 Varmilo Keyboard
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x005b
    bNumInterfaces          3
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower              350mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      1 Boot Interface Subclass
      bInterfaceProtocol      1 Keyboard
      iInterface              0
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.10
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      75
         Report Descriptors:
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0
      bInterfaceProtocol      0
      iInterface              0
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.10
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      85
         Report Descriptors:
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0010  1x 16 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        2
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0
      bInterfaceProtocol      0
      iInterface              0
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.10
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      33
         Report Descriptors:
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0020  1x 32 bytes
        bInterval               4
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x04  EP 4 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0020  1x 32 bytes
        bInterval               4
can't get device qualifier: Resource temporarily unavailable
can't get debug descriptor: Resource temporarily unavailable
Device Status:     0x0000
  (Bus Powered)

Now I god the devices for my varmillo using this script: https://arvchristos.github.io/post/matching-dev-hidraw-devices-with-physical-devices/

Code: [Select]
0x05, 0x01,                    // Usage Page (Generic Desktop)        0
0x09, 0x06,                    // Usage (Keyboard)                    2
0xa1, 0x01,                    // Collection (Application)            4
0x05, 0x07,                    //  Usage Page (Keyboard)              6
0x19, 0xe0,                    //  Usage Minimum (224)                8
0x29, 0xe7,                    //  Usage Maximum (231)                10
0x15, 0x00,                    //  Logical Minimum (0)                12
0x25, 0x01,                    //  Logical Maximum (1)                14
0x75, 0x01,                    //  Report Size (1)                    16
0x95, 0x08,                    //  Report Count (8)                   18
0x81, 0x02,                    //  Input (Data,Var,Abs)               20
0x95, 0x01,                    //  Report Count (1)                   22
0x75, 0x08,                    //  Report Size (8)                    24
0x81, 0x01,                    //  Input (Cnst,Arr,Abs)               26
0x05, 0x08,                    //  Usage Page (LEDs)                  28
0x19, 0x01,                    //  Usage Minimum (1)                  30
0x29, 0x05,                    //  Usage Maximum (5)                  32
0x95, 0x05,                    //  Report Count (5)                   34
0x75, 0x01,                    //  Report Size (1)                    36
0x91, 0x02,                    //  Output (Data,Var,Abs)              38
0x95, 0x01,                    //  Report Count (1)                   40
0x75, 0x03,                    //  Report Size (3)                    42
0x91, 0x01,                    //  Output (Cnst,Arr,Abs)              44
0x05, 0x07,                    //  Usage Page (Keyboard)              46
0x19, 0x00,                    //  Usage Minimum (0)                  48
0x2a, 0xff, 0x00,              //  Usage Maximum (255)                50
0x95, 0x05,                    //  Report Count (5)                   53
0x75, 0x08,                    //  Report Size (8)                    55
0x15, 0x00,                    //  Logical Minimum (0)                57
0x26, 0xff, 0x00,              //  Logical Maximum (255)              59
0x81, 0x00,                    //  Input (Data,Arr,Abs)               62
0x05, 0xff,                    //  Usage Page (Vendor Usage Page 0xff) 64
0x09, 0x03,                    //  Usage (Vendor Usage 0x03)          66
0x75, 0x08,                    //  Report Size (8)                    68
0x95, 0x01,                    //  Report Count (1)                   70
0x81, 0x02,                    //  Input (Data,Var,Abs)               72
0xc0,                          // End Collection                      74

Code: [Select]
0x05, 0x01,                    // Usage Page (Generic Desktop)        0
0x09, 0x06,                    // Usage (Keyboard)                    2
0xa1, 0x01,                    // Collection (Application)            4
0x85, 0x05,                    //  Report ID (5)                      6
0x15, 0x00,                    //  Logical Minimum (0)                8
0x25, 0x01,                    //  Logical Maximum (1)                10
0x75, 0x01,                    //  Report Size (1)                    12
0x95, 0x70,                    //  Report Count (112)                 14
0x05, 0x07,                    //  Usage Page (Keyboard)              16
0x19, 0xe0,                    //  Usage Minimum (224)                18
0x29, 0xe7,                    //  Usage Maximum (231)                20
0x19, 0x00,                    //  Usage Minimum (0)                  22
0x29, 0x67,                    //  Usage Maximum (103)                24
0x81, 0x02,                    //  Input (Data,Var,Abs)               26
0x95, 0x08,                    //  Report Count (8)                   28
0x81, 0x01,                    //  Input (Cnst,Arr,Abs)               30
0xc0,                          // End Collection                      32
0x05, 0x01,                    // Usage Page (Generic Desktop)        33
0x09, 0x80,                    // Usage (System Control)              35
0xa1, 0x01,                    // Collection (Application)            37
0x85, 0x02,                    //  Report ID (2)                      39
0x19, 0x81,                    //  Usage Minimum (129)                41
0x29, 0x83,                    //  Usage Maximum (131)                43
0x15, 0x00,                    //  Logical Minimum (0)                45
0x25, 0x01,                    //  Logical Maximum (1)                47
0x75, 0x01,                    //  Report Size (1)                    49
0x95, 0x03,                    //  Report Count (3)                   51
0x81, 0x02,                    //  Input (Data,Var,Abs)               53
0x95, 0x05,                    //  Report Count (5)                   55
0x81, 0x01,                    //  Input (Cnst,Arr,Abs)               57
0xc0,                          // End Collection                      59
0x05, 0x0c,                    // Usage Page (Consumer Devices)       60
0x09, 0x01,                    // Usage (Consumer Control)            62
0xa1, 0x01,                    // Collection (Application)            64
0x85, 0x03,                    //  Report ID (3)                      66
0x19, 0x00,                    //  Usage Minimum (0)                  68
0x2a, 0xff, 0x02,              //  Usage Maximum (767)                70
0x15, 0x00,                    //  Logical Minimum (0)                73
0x26, 0xff, 0x7f,              //  Logical Maximum (32767)            75
0x95, 0x01,                    //  Report Count (1)                   78
0x75, 0x10,                    //  Report Size (16)                   80
0x81, 0x00,                    //  Input (Data,Arr,Abs)               82
0xc0,                          // End Collection                      84

Code: [Select]
0x06, 0x01, 0xff,              // Usage Page (Vendor Usage Page 0xff01) 0
0x09, 0x01,                    // Usage (Vendor Usage 0x01)           3
0xa1, 0x01,                    // Collection (Application)            5
0x15, 0x00,                    //  Logical Minimum (0)                7
0x26, 0xff, 0x00,              //  Logical Maximum (255)              9
0x09, 0x20,                    //  Usage (Vendor Usage 0x20)          12
0x75, 0x08,                    //  Report Size (8)                    14
0x95, 0x20,                    //  Report Count (32)                  16
0x81, 0x02,                    //  Input (Data,Var,Abs)               18
0x09, 0x21,                    //  Usage (Vendor Usage 0x21)          20
0x95, 0x20,                    //  Report Count (32)                  22
0x91, 0x02,                    //  Output (Data,Var,Abs)              24
0x09, 0x22,                    //  Usage (Vendor Usage 0x22)          26
0x95, 0x08,                    //  Report Count (8)                   28
0xb1, 0x02,                    //  Feature (Data,Var,Abs)             30
0xc0,                          // End Collection                      32


Are you sure you got the correct hid device? Because it says
Code: [Select]
0x09, 0x02,                    // Usage (Mouse)                       2

in your snippet.
« Last Edit: Sat, 13 February 2021, 04:25:57 by maxammann »

Offline ethereal

  • Thread Starter
  • Posts: 21
Re: Varmilo VA87 firmware issues (NKRO)
« Reply #11 on: Sun, 14 February 2021, 07:57:43 »
Thanks for the info, maxammann!!

I've look into the output of the commands you've kindly provided. USB-wise, your VA88M is completely different than my unit. That's why you have NKRO functionality.

On your VA88M, this is the HID descriptor that implements the keyboard with full NKRO. It has the right "Input (Data,Var,Abs)" declaration to describe a bitmap:

Code: [Select]
0x05, 0x07,                    //  Usage Page (Keyboard)              16
0x19, 0xe0,                    //  Usage Minimum (224)                18
0x29, 0xe7,                    //  Usage Maximum (231)                20
0x19, 0x00,                    //  Usage Minimum (0)                  22
0x29, 0x67,                    //  Usage Maximum (103)                24
0x81, 0x02,                    //  Input (Data,Var,Abs)


I've found many more differences at an USB level. Your VA88M has 3 USB interfaces (my VA87 RGB has 4). Yours doesn't describe a mouse (mine does, although my keyboard doesn't implement pointer functionality). Your unit also implements 2 keyboards, one with 5KRO (this is weird) and another with full NKRO; mine implements one with 6KRO (to be similar to the boot keyboard) and one with full NKRO. Lastly, your VA88M identifies itselt as an "Apple" product (mine shows as a "Holtek").

Varmilo has different TKL models: the VA87/88, the VA87/88 RGB, the VA92 for the Japanese language, the VD87/88 with Bluetooth, the VA87/88 Mac, etc. Externally all are very similar but I guess the firmware inside is different on each one.
« Last Edit: Sun, 14 February 2021, 08:01:02 by ethereal »

Offline ethereal

  • Thread Starter
  • Posts: 21
Re: Varmilo VA87 firmware issues (NKRO)
« Reply #12 on: Sun, 14 February 2021, 07:58:34 »

[/code]

Are you sure you got the correct hid device? Because it says
Code: [Select]
0x09, 0x02,                    // Usage (Mouse)                       2

in your snippet.

Yes. For each USB interface, you can define more than one report descriptor (each one implements a different functionality). As you've noted, there is one descriptor implementing a mouse (although my keyboard doesn't have pointer functionality). But there is also another one implementing a keyboard:

Code: [Select]
0x05, 0x07,                    //  Usage Page (Keyboard)              172
0x19, 0x00,                    //  Usage Minimum (0)                  174
0x29, 0xe7,                    //  Usage Maximum (231)                176
0x81, 0x00,                    //  Input (Data,Arr,Abs)
« Last Edit: Sun, 14 February 2021, 08:00:15 by ethereal »

Offline ethereal

  • Thread Starter
  • Posts: 21
Re: Varmilo VA87 firmware issues (NKRO)
« Reply #13 on: Mon, 30 May 2022, 06:50:24 »
As I said, I reported the bug to Varmilo's support. I referenced this thread with has all the technical details.

They didn't even bother to reply to my email. After many days, I contacted someone from Varmilo's sales department, asking if their support email was working fine. He told me they had received my email and that they would try to solve the bug if they were able to.

So far, they haven't released any new firmware version for my VA87 RGB, nor they have contacted me. I'm disappointed with Varmilo's support. They make keyboards with great build quality, but their support is not top notch. I provided them all the technical details of the bug. With all that info, the fix should be a breeze (most probably, changing only a line a code). But they didn't even bother to fix it.

My next keyboard will be one with open source firmware such as QMK. This way, I won't have to rely again on the non-existent support from a keyboard company.