Re: [PATCH 1/1] platform/x86/tuxedo: Add virtual LampArray for TUXEDO NB04 devices

From: Pavel Machek
Date: Tue Oct 01 2024 - 16:47:59 EST


Hi!

> > > > LampArray HID device and translates the input from hidraw to the
> > > > corresponding WMI calls. This is a new approach as the leds
> > > > subsystem lacks
> > > > a suitable UAPI for per-key keyboard backlights, and like this
> > > > no new UAPI
> > > > needs to be established.
> > > Please don't.
> > >
> > > a) I don't believe emulating crazy HID interface si right thing to
> > > do. (Ton of magic constants. IIRC it stores key positions with
> > > micrometer accuracy or something that crazy. How is userland going to
> > > use this? Will we update micrometers for every single machine?)
> > >
> > > Even if it is,
> > >
> > > b) The emulation should go to generic layer, it is not specific to
> > > your hardware.
> > >
> > Maybe introducing a misc-device which provides an ioctl-based API similar
> > to the HID LampArray would be a solution?
> >
> > Basically we would need:
> > - ioctl for querying the supported LEDs and their properties
> > - ioctl for enabling/disabling autonomous mode
> > - ioctl for updating a range of LEDs
> > - ioctl for updating multiple LEDs at once
> >
> > If we implement this as a separate subsystem ("illumination subsystem"),
> > then different
> > drivers could use this. This would also allow us to add additional ioctl
> > calls later
> > for more features.
>
> We went over this in the past discussion, the conclusion was iirc that we
> are just wraping hidraw ioctls in other ioctls with no added benefit.

I don't believe that conclusion was widely accepted.

Benefit of doing reasonable interface is ... that kernel would have
reasonable interface. We would get rid of binary tables in the driver,
and long term, we could get something more reasonable than OpenRGB.

> For reference
> https://lore.kernel.org/all/20231011190017.1230898-1-wse@xxxxxxxxxxxxxxxxxxx/
>
> I don't know the exact message anymore, but if relevant I can dig for it
> (it's a over 5 month long e-mail thread).
>
> And we would need to write code to apply this wrapper to devices
> implementing LampArray in firmware.

Yes, that would be long term plan.

I bought gaming keyboard to play with rgb leds. I don't really want to
do crazy arrays in the driver as you did below. And I'd really like to
have 100-line application in userland, talking to kernel, not full
OpenRGB which is huge and depends on QT IIRC.

(Work-in-progress version is attached. Note it is smaller than tables
for the fake-HID implementation).

Best regards,
Pavel


> > > > +
> > > > +static const uint8_t sirius_16_ansii_kbl_mapping[] = {
> > > > +    0x29, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42,
> > > > +    0x43, 0x44, 0x45, 0xf1, 0x46, 0x4c,   0x4a, 0x4d, 0x4b, 0x4e,
> > > > +    0x35, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
> > > > +    0x27, 0x2d, 0x2e, 0x2a,               0x53, 0x55, 0x54, 0x56,
> > > > +    0x2b, 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c, 0x12,
> > > > +    0x13, 0x2f, 0x30, 0x31,               0x5f, 0x60, 0x61,
> > > > +    0x39, 0x04, 0x16, 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f,
> > > > +    0x33, 0x34, 0x28,                     0x5c, 0x5d, 0x5e, 0x57,
> > > > +    0xe1, 0x1d, 0x1b, 0x06, 0x19, 0x05, 0x11, 0x10, 0x36, 0x37,
> > > > +    0x38, 0xe5, 0x52,                     0x59, 0x5a, 0x5b,
> > > > +    0xe0, 0xfe, 0xe3, 0xe2, 0x2c, 0xe6, 0x65, 0xe4, 0x50, 0x51,
> > > > +    0x4f,                                 0x62, 0x63, 0x58
> > > > +};
> > > > +
> > > > +static const uint32_t sirius_16_ansii_kbl_mapping_pos_x[] = {
> > > > +     25000,  41700,  58400,  75100,  91800, 108500, 125200,
> > > > 141900, 158600, 175300,
> > > > +    192000, 208700, 225400, 242100, 258800, 275500,   294500,
> > > > 311200, 327900, 344600,
> > > > +     24500,  42500,  61000,  79500,  98000, 116500, 135000,
> > > > 153500, 172000, 190500,
> > > > +    209000, 227500, 246000, 269500,                   294500,
> > > > 311200, 327900, 344600,
> > > > +     31000,  51500,  70000,  88500, 107000, 125500, 144000,
> > > > 162500, 181000, 199500,
> > > > +    218000, 236500, 255000, 273500,                   294500,
> > > > 311200, 327900,
> > > > +     33000,  57000,  75500,  94000, 112500, 131000, 149500,
> > > > 168000, 186500, 205000,
> > > > +    223500, 242000, 267500,                           294500,
> > > > 311200, 327900, 344600,
> > > > +     37000,  66000,  84500, 103000, 121500, 140000, 158500,
> > > > 177000, 195500, 214000,
> > > > +    232500, 251500, 273500,                           294500,
> > > > 311200, 327900,
> > > > +     28000,  47500,  66000,  84500, 140000, 195500, 214000,
> > > > 234000, 255000, 273500,
> > > > +    292000,                                           311200,
> > > > 327900, 344600
> > > > +};
> > > > +
> > > > +static const uint32_t sirius_16_ansii_kbl_mapping_pos_y[] = {
> > > > +     53000,  53000,  53000,  53000,  53000,  53000,  53000,
> > > > 53000,  53000,  53000,
> > > > +     53000,  53000,  53000,  53000,  53000,  53000, 53000, 
> > > > 53000,  53000,  53000,
> > > > +     67500,  67500,  67500,  67500,  67500,  67500,  67500,
> > > > 67500,  67500,  67500,
> > > > +     67500,  67500,  67500,  67500, 67500,  67500,  67500,  67500,
> > > > +     85500,  85500,  85500,  85500,  85500,  85500,  85500,
> > > > 85500,  85500,  85500,
> > > > +     85500,  85500,  85500,  85500, 85500,  85500,  85500,
> > > > +    103500, 103500, 103500, 103500, 103500, 103500, 103500,
> > > > 103500, 103500, 103500,
> > > > +    103500, 103500, 103500,                           103500,
> > > > 103500, 103500,  94500,
> > > > +    121500, 121500, 121500, 121500, 121500, 121500, 121500,
> > > > 121500, 121500, 121500,
> > > > +    121500, 121500, 129000,                           121500,
> > > > 121500, 121500,
> > > > +    139500, 139500, 139500, 139500, 139500, 139500, 139500,
> > > > 139500, 147000, 147000,
> > > > +    147000,                                           139500,
> > > > 139500, 130500
> > > > +};
> > > > +
> > > > +static const uint32_t sirius_16_ansii_kbl_mapping_pos_z[] = {
> > > > +      5000,   5000,   5000,   5000,   5000,   5000,   5000,
> > > > 5000,   5000,   5000,
> > > > +      5000,   5000,   5000,   5000,   5000,   5000, 5000,  
> > > > 5000,   5000,   5000,
> > > > +      5250,   5250,   5250,   5250,   5250,   5250,   5250,
> > > > 5250,   5250,   5250,
> > > > +      5250,   5250,   5250,   5250, 5250,   5250,   5250,   5250,
> > > > +      5500,   5500,   5500,   5500,   5500,   5500,   5500,
> > > > 5500,   5500,   5500,
> > > > +      5500,   5500,   5500,   5500, 5500,   5500,   5500,
> > > > +      5750,   5750,   5750,   5750,   5750,   5750,   5750,
> > > > 5750,   5750,   5750,
> > > > +      5750,   5750,   5750, 5750,   5750,   5750,   5625,
> > > > +      6000,   6000,   6000,   6000,   6000,   6000,   6000,
> > > > 6000,   6000,   6000,
> > > > +      6000,   6000,   6125, 6000,   6000,   6000,
> > > > +      6250,   6250,   6250,   6250,   6250,   6250,   6250,
> > > > 6250,   6375,   6375,
> > > > +      6375, 6250,   6250,   6125
> > > > +};
> > > > +
> > > > +static const uint8_t sirius_16_iso_kbl_mapping[] = {
> > > > +    0x29, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42,
> > > > +    0x43, 0x44, 0x45, 0xf1, 0x46, 0x4c,   0x4a, 0x4d, 0x4b, 0x4e,
> > > > +    0x35, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
> > > > +    0x27, 0x2d, 0x2e, 0x2a,               0x53, 0x55, 0x54, 0x56,
> > > > +    0x2b, 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c, 0x12,
> > > > +    0x13, 0x2f, 0x30,                     0x5f, 0x60, 0x61,
> > > > +    0x39, 0x04, 0x16, 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f,
> > > > +    0x33, 0x34, 0x32, 0x28,               0x5c, 0x5d, 0x5e, 0x57,
> > > > +    0xe1, 0x64, 0x1d, 0x1b, 0x06, 0x19, 0x05, 0x11, 0x10, 0x36,
> > > > +    0x37, 0x38, 0xe5, 0x52,               0x59, 0x5a, 0x5b,
> > > > +    0xe0, 0xfe, 0xe3, 0xe2, 0x2c, 0xe6, 0x65, 0xe4, 0x50, 0x51,
> > > > +    0x4f,                                 0x62, 0x63, 0x58
> > > > +};
> > > > +
> > > > +static const uint32_t sirius_16_iso_kbl_mapping_pos_x[] = {
> > > > +     25000,  41700,  58400,  75100,  91800, 108500, 125200,
> > > > 141900, 158600, 175300,
> > > > +    192000, 208700, 225400, 242100, 258800, 275500,   294500,
> > > > 311200, 327900, 344600,
> > > > +     24500,  42500,  61000,  79500,  98000, 116500, 135000,
> > > > 153500, 172000, 190500,
> > > > +    209000, 227500, 246000, 269500,                   294500,
> > > > 311200, 327900, 344600,
> > > > +     31000,  51500,  70000,  88500, 107000, 125500, 144000,
> > > > 162500, 181000, 199500,
> > > > +    218000, 234500, 251000,                           294500,
> > > > 311200, 327900,
> > > > +     33000,  57000,  75500,  94000, 112500, 131000, 149500,
> > > > 168000, 186500, 205000,
> > > > +    223500, 240000, 256500, 271500,                   294500,
> > > > 311200, 327900, 344600,
> > > > +     28000,  47500,  66000,  84500, 103000, 121500, 140000,
> > > > 158500, 177000, 195500,
> > > > +    214000, 232500, 251500, 273500,                   294500,
> > > > 311200, 327900,
> > > > +     28000,  47500,  66000,  84500, 140000, 195500, 214000,
> > > > 234000, 255000, 273500,
> > > > +    292000,                                           311200,
> > > > 327900, 344600
> > > > +};
> > > > +
> > > > +static const uint32_t sirius_16_iso_kbl_mapping_pos_y[] = {
> > > > +     53000,  53000,  53000,  53000,  53000,  53000,  53000,
> > > > 53000,  53000,  53000,
> > > > +     53000,  53000,  53000,  53000,  53000,  53000, 53000, 
> > > > 53000,  53000,  53000,
> > > > +     67500,  67500,  67500,  67500,  67500,  67500,  67500,
> > > > 67500,  67500,  67500,
> > > > +     67500,  67500,  67500,  67500, 67500,  67500,  67500,  67500,
> > > > +     85500,  85500,  85500,  85500,  85500,  85500,  85500,
> > > > 85500,  85500,  85500,
> > > > +     85500,  85500,  85500, 85500,  85500,  85500,
> > > > +    103500, 103500, 103500, 103500, 103500, 103500, 103500,
> > > > 103500, 103500, 103500,
> > > > +    103500, 103500, 103500,  94500,                   103500,
> > > > 103500, 103500,  94500,
> > > > +    121500, 121500, 121500, 121500, 121500, 121500, 121500,
> > > > 121500, 121500, 121500,
> > > > +    121500, 121500, 121500, 129000,                   121500,
> > > > 121500, 121500,
> > > > +    139500, 139500, 139500, 139500, 139500, 139500, 139500,
> > > > 139500, 147000, 147000,
> > > > +    147000,                                           139500,
> > > > 139500, 130500
> > > > +};
> > > > +
> > > > +static const uint32_t sirius_16_iso_kbl_mapping_pos_z[] = {
> > > > +      5000,   5000,   5000,   5000,   5000,   5000,   5000,
> > > > 5000,   5000,   5000,
> > > > +      5000,   5000,   5000,   5000, 5000, 5000, 5000,   5000,  
> > > > 5000,   5000,
> > > > +      5250,   5250,   5250,   5250,   5250,   5250,   5250,
> > > > 5250,   5250,   5250,
> > > > +      5250,   5250,   5250,   5250, 5250,   5250,   5250,   5250,
> > > > +      5500,   5500,   5500,   5500,   5500,   5500,   5500,
> > > > 5500,   5500,   5500,
> > > > +      5500,   5500,   5500, 5500,   5500,   5500,
> > > > +      5750,   5750,   5750,   5750,   5750,   5750,   5750,
> > > > 5750,   5750,   5750,
> > > > +      5750,   5750,   5750,   5750, 5750,   5750,   5750,   5625,
> > > > +      6000,   6000,   6000,   6000,   6000,   6000,   6000,
> > > > 6000,   6000,   6000,
> > > > +      6000,   6000,   6000,   6125, 6000,   6000,   6000,
> > > > +      6250,   6250,   6250,   6250,   6250,   6250,   6250,
> > > > 6250,   6375,   6375,
> > > > +      6375, 6250,   6250,   6125
> > > > +};
> > > ...
> > > > +
> > > > +static uint8_t report_descriptor[327] = {
> > > > +    0x05, 0x59,            // Usage Page (Lighting and Illumination)
> > > > +    0x09, 0x01,            // Usage (Lamp Array)
> > > > +    0xa1, 0x01,            // Collection (Application)
> > > > +    0x85, LAMP_ARRAY_ATTRIBUTES_REPORT_ID, //  Report ID (1)
> > > > +    0x09, 0x02,            //  Usage (Lamp Array Attributes Report)
> > > > +    0xa1, 0x02,            //  Collection (Logical)
> > > > +    0x09, 0x03,            //   Usage (Lamp Count)
> > > > +    0x15, 0x00,            //   Logical Minimum (0)
> > > > +    0x27, 0xff, 0xff, 0x00, 0x00,    //   Logical Maximum (65535)
> > > > +    0x75, 0x10,            //   Report Size (16)
> > > > +    0x95, 0x01,            //   Report Count (1)
> > > > +    0xb1, 0x03,            //   Feature (Cnst,Var,Abs)
> > > > +    0x09, 0x04,            //   Usage (Bounding Box Width In
> > > > Micrometers)
> > > > +    0x09, 0x05,            //   Usage (Bounding Box Height In
> > > > Micrometers)
> > > > +    0x09, 0x06,            //   Usage (Bounding Box Depth In
> > > > Micrometers)
> > > > +    0x09, 0x07,            //   Usage (Lamp Array Kind)
> > > > +    0x09, 0x08,            //   Usage (Min Update Interval In
> > > > Microseconds)
> > > > +    0x15, 0x00,            //   Logical Minimum (0)
> > > > +    0x27, 0xff, 0xff, 0xff, 0x7f,    //   Logical Maximum (2147483647)
> > > > +    0x75, 0x20,            //   Report Size (32)
> > > > +    0x95, 0x05,            //   Report Count (5)
> > > > +    0xb1, 0x03,            //   Feature (Cnst,Var,Abs)
> > > > +    0xc0,                //  End Collection
> > > > +    0x85, LAMP_ATTRIBUTES_REQUEST_REPORT_ID, //  Report ID (2)
> > > > +    0x09, 0x20,            //  Usage (Lamp Attributes Request Report)
> > > > +    0xa1, 0x02,            //  Collection (Logical)
> > > > +    0x09, 0x21,            //   Usage (Lamp Id)
> > > > +    0x15, 0x00,            //   Logical Minimum (0)
> > > > +    0x27, 0xff, 0xff, 0x00, 0x00,    //   Logical Maximum (65535)
> > > > +    0x75, 0x10,            //   Report Size (16)
> > > > +    0x95, 0x01,            //   Report Count (1)
> > > > +    0xb1, 0x02,            //   Feature (Data,Var,Abs)
> > > > +    0xc0,                //  End Collection
> > > > +    0x85, LAMP_ATTRIBUTES_RESPONSE_REPORT_ID, //  Report ID (3)
> > > > +    0x09, 0x22,            //  Usage (Lamp Attributes Response Report)
> > > > +    0xa1, 0x02,            //  Collection (Logical)
> > > > +    0x09, 0x21,            //   Usage (Lamp Id)
> > > > +    0x15, 0x00,            //   Logical Minimum (0)
> > > > +    0x27, 0xff, 0xff, 0x00, 0x00,    //   Logical Maximum (65535)
> > > > +    0x75, 0x10,            //   Report Size (16)
> > > > +    0x95, 0x01,            //   Report Count (1)
> > > > +    0xb1, 0x02,            //   Feature (Data,Var,Abs)
> > > > +    0x09, 0x23,            //   Usage (Position X In Micrometers)
> > > > +    0x09, 0x24,            //   Usage (Position Y In Micrometers)
> > > > +    0x09, 0x25,            //   Usage (Position Z In Micrometers)
> > > > +    0x09, 0x27,            //   Usage (Update Latency In Microseconds)
> > > > +    0x09, 0x26,            //   Usage (Lamp Purposes)
> > > > +    0x15, 0x00,            //   Logical Minimum (0)
> > > > +    0x27, 0xff, 0xff, 0xff, 0x7f,    //   Logical Maximum (2147483647)
> > > > +    0x75, 0x20,            //   Report Size (32)
> > > > +    0x95, 0x05,            //   Report Count (5)
> > > > +    0xb1, 0x02,            //   Feature (Data,Var,Abs)
> > > > +    0x09, 0x28,            //   Usage (Red Level Count)
> > > > +    0x09, 0x29,            //   Usage (Green Level Count)
> > > > +    0x09, 0x2a,            //   Usage (Blue Level Count)
> > > > +    0x09, 0x2b,            //   Usage (Intensity Level Count)
> > > > +    0x09, 0x2c,            //   Usage (Is Programmable)
> > > > +    0x09, 0x2d,            //   Usage (Input Binding)
> > > > +    0x15, 0x00,            //   Logical Minimum (0)
> > > > +    0x26, 0xff, 0x00,        //   Logical Maximum (255)
> > > > +    0x75, 0x08,            //   Report Size (8)
> > > > +    0x95, 0x06,            //   Report Count (6)
> > > > +    0xb1, 0x02,            //   Feature (Data,Var,Abs)
> > > > +    0xc0,                //  End Collection
> > > > +    0x85, LAMP_MULTI_UPDATE_REPORT_ID, //  Report ID (4)
> > > > +    0x09, 0x50,            //  Usage (Lamp Multi Update Report)
> > > > +    0xa1, 0x02,            //  Collection (Logical)
> > > > +    0x09, 0x03,            //   Usage (Lamp Count)
> > > > +    0x09, 0x55,            //   Usage (Lamp Update Flags)
> > > > +    0x15, 0x00,            //   Logical Minimum (0)
> > > > +    0x25, 0x08,            //   Logical Maximum (8)
> > > > +    0x75, 0x08,            //   Report Size (8)
> > > > +    0x95, 0x02,            //   Report Count (2)
> > > > +    0xb1, 0x02,            //   Feature (Data,Var,Abs)
> > > > +    0x09, 0x21,            //   Usage (Lamp Id)
> > > > +    0x15, 0x00,            //   Logical Minimum (0)
> > > > +    0x27, 0xff, 0xff, 0x00, 0x00,    //   Logical Maximum (65535)
> > > > +    0x75, 0x10,            //   Report Size (16)
> > > > +    0x95, 0x08,            //   Report Count (8)
> > > > +    0xb1, 0x02,            //   Feature (Data,Var,Abs)
> > > > +    0x09, 0x51,            //   Usage (Red Update Channel)
> > > > +    0x09, 0x52,            //   Usage (Green Update Channel)
> > > > +    0x09, 0x53,            //   Usage (Blue Update Channel)
> > > > +    0x09, 0x54,            //   Usage (Intensity Update Channel)
> > > > +    0x09, 0x51,            //   Usage (Red Update Channel)
> > > > +    0x09, 0x52,            //   Usage (Green Update Channel)
> > > > +    0x09, 0x53,            //   Usage (Blue Update Channel)
> > > > +    0x09, 0x54,            //   Usage (Intensity Update Channel)
> > > > +    0x09, 0x51,            //   Usage (Red Update Channel)
> > > > +    0x09, 0x52,            //   Usage (Green Update Channel)
> > > > +    0x09, 0x53,            //   Usage (Blue Update Channel)
> > > > +    0x09, 0x54,            //   Usage (Intensity Update Channel)
> > > > +    0x09, 0x51,            //   Usage (Red Update Channel)
> > > > +    0x09, 0x52,            //   Usage (Green Update Channel)
> > > > +    0x09, 0x53,            //   Usage (Blue Update Channel)
> > > > +    0x09, 0x54,            //   Usage (Intensity Update Channel)
> > > > +    0x09, 0x51,            //   Usage (Red Update Channel)
> > > > +    0x09, 0x52,            //   Usage (Green Update Channel)
> > > > +    0x09, 0x53,            //   Usage (Blue Update Channel)
> > > > +    0x09, 0x54,            //   Usage (Intensity Update Channel)
> > > > +    0x09, 0x51,            //   Usage (Red Update Channel)
> > > > +    0x09, 0x52,            //   Usage (Green Update Channel)
> > > > +    0x09, 0x53,            //   Usage (Blue Update Channel)
> > > > +    0x09, 0x54,            //   Usage (Intensity Update Channel)
> > > > +    0x09, 0x51,            //   Usage (Red Update Channel)
> > > > +    0x09, 0x52,            //   Usage (Green Update Channel)
> > > > +    0x09, 0x53,            //   Usage (Blue Update Channel)
> > > > +    0x09, 0x54,            //   Usage (Intensity Update Channel)
> > > > +    0x09, 0x51,            //   Usage (Red Update Channel)
> > > > +    0x09, 0x52,            //   Usage (Green Update Channel)
> > > > +    0x09, 0x53,            //   Usage (Blue Update Channel)
> > > > +    0x09, 0x54,            //   Usage (Intensity Update Channel)
> > > > +    0x15, 0x00,            //   Logical Minimum (0)
> > > > +    0x26, 0xff, 0x00,        //   Logical Maximum (255)
> > > > +    0x75, 0x08,            //   Report Size (8)
> > > > +    0x95, 0x20,            //   Report Count (32)
> > > > +    0xb1, 0x02,            //   Feature (Data,Var,Abs)
> > > > +    0xc0,                //  End Collection
> > > > +    0x85, LAMP_RANGE_UPDATE_REPORT_ID, //  Report ID (5)
> > > > +    0x09, 0x60,            //  Usage (Lamp Range Update Report)
> > > > +    0xa1, 0x02,            //  Collection (Logical)
> > > > +    0x09, 0x55,            //   Usage (Lamp Update Flags)
> > > > +    0x15, 0x00,            //   Logical Minimum (0)
> > > > +    0x25, 0x08,            //   Logical Maximum (8)
> > > > +    0x75, 0x08,            //   Report Size (8)
> > > > +    0x95, 0x01,            //   Report Count (1)
> > > > +    0xb1, 0x02,            //   Feature (Data,Var,Abs)
> > > > +    0x09, 0x61,            //   Usage (Lamp Id Start)
> > > > +    0x09, 0x62,            //   Usage (Lamp Id End)
> > > > +    0x15, 0x00,            //   Logical Minimum (0)
> > > > +    0x27, 0xff, 0xff, 0x00, 0x00,    //   Logical Maximum (65535)
> > > > +    0x75, 0x10,            //   Report Size (16)
> > > > +    0x95, 0x02,            //   Report Count (2)
> > > > +    0xb1, 0x02,            //   Feature (Data,Var,Abs)
> > > > +    0x09, 0x51,            //   Usage (Red Update Channel)
> > > > +    0x09, 0x52,            //   Usage (Green Update Channel)
> > > > +    0x09, 0x53,            //   Usage (Blue Update Channel)
> > > > +    0x09, 0x54,            //   Usage (Intensity Update Channel)
> > > > +    0x15, 0x00,            //   Logical Minimum (0)
> > > > +    0x26, 0xff, 0x00,        //   Logical Maximum (255)
> > > > +    0x75, 0x08,            //   Report Size (8)
> > > > +    0x95, 0x04,            //   Report Count (4)
> > > > +    0xb1, 0x02,            //   Feature (Data,Var,Abs)
> > > > +    0xc0,                //  End Collection
> > > > +    0x85, LAMP_ARRAY_CONTROL_REPORT_ID, //  Report ID (6)
> > > > +    0x09, 0x70,            //  Usage (Lamp Array Control Report)
> > > > +    0xa1, 0x02,            //  Collection (Logical)
> > > > +    0x09, 0x71,            //   Usage (Autonomous Mode)
> > > > +    0x15, 0x00,            //   Logical Minimum (0)
> > > > +    0x25, 0x01,            //   Logical Maximum (1)
> > > > +    0x75, 0x08,            //   Report Size (8)
> > > > +    0x95, 0x01,            //   Report Count (1)
> > > > +    0xb1, 0x02,            //   Feature (Data,Var,Abs)
> > > > +    0xc0,                //  End Collection
> > > > +    0xc0                // End Collection
> > > > +};
> > > > +

--
People of Russia, stop Putin before his war on Ukraine escalates.
// SPDX-License-Identifier: GPL-2.0-or-later
/*
*/

#include <linux/device.h>
#include <linux/input.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/hid-roccat.h>
#include <linux/usb.h>

struct hx_device {};

static unsigned char keys[] = {
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x20, 0x21, 0x22,
0x23, 0x24, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
0x31, 0x32, 0x33, 0x34, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x41,
0x44, 0x45, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x51, 0x54, 0x55,
0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5E, 0x5F, 0x61, 0x64, 0x65, 0x68, 0x69, 0x6A,
0x6B, 0x6C, 0x6E, 0x6F, 0x74, 0x75, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E,
0x7F, 0x81, 0x84, 0x85, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x91,
0x94, 0x95 };

struct hid_device *one_hdev;

static int set_direct_color(struct hid_device *hdev, int color, int val)
{
const int s = 264;
unsigned char *buf = kmalloc(s, GFP_KERNEL);
int i, ret;

/* Zero out buffer */
memset(buf, 0x00, s);

/* Set up Direct packet */
for (i = 0; i < sizeof(keys)/sizeof(keys[0]); i++) {
buf[keys[i]] = val;
}

buf[0x00] = 0x07;
buf[0x01] = 0x16; // HYPERX_ALLOY_ELITE_PACKET_ID_DIRECT
buf[0x02] = color; // HYPERX_ALLOY_ELITE_COLOR_CHANNEL_GREEN
buf[0x03] = 0xA0;

ret = hid_hw_power(hdev, PM_HINT_FULLON);
if (ret) {
hid_err(hdev, "Failed to power on HID device\n");
return ret;
}

// ioctl(3, HIDIOCSFEATURE(264), 0xbfce5974) = 264
// -> hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT);
//
printk(KERN_INFO "Set feature report -- direct\n");
i = hid_hw_raw_request(hdev, buf[0], buf, s, HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
printk("raw: %d, einval %d, eagain %d\n", i, -EINVAL, -EAGAIN);
msleep(100);
return 0;
}

#define SIZE 128
const int real_size = SIZE;

static ssize_t hx_sysfs_read(struct file *fp, struct kobject *kobj,
struct bin_attribute * b,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
struct hx_device *hx = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;

if (off >= real_size)
return 0;

if (off != 0 || count != real_size)
return -EINVAL;

printk("read\n");
set_direct_color(one_hdev, 2, 0xff);

return retval ? retval : real_size;
}

static ssize_t hx_sysfs_write(struct file *fp, struct kobject *kobj,
struct bin_attribute * b,
void const *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
struct hx_device *hx = hid_get_drvdata(dev_get_drvdata(dev));
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
int retval;

if (off != 0 || count != real_size)
return -EINVAL;

printk("Write\n");

return retval ? retval : real_size;
}

static struct bin_attribute hx_control_attr = { \
.attr = { .name = "thingy", .mode = 0660 }, \
.size = SIZE, \
.read = hx_sysfs_read, \
};

static int hx_create_sysfs_attributes(struct usb_interface *intf)
{
return sysfs_create_bin_file(&intf->dev.kobj, &hx_control_attr);
}

static void hx_remove_sysfs_attributes(struct usb_interface *intf)
{
sysfs_remove_bin_file(&intf->dev.kobj, &hx_control_attr);
}

static int hx_init_hx_device_struct(struct usb_device *usb_dev,
struct hx_device *hx)
{
//mutex_init(&hx->hx_lock);
return 0;
}

static int hx_init_specials(struct hid_device *hdev)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct usb_device *usb_dev = interface_to_usbdev(intf);
struct hx_device *hx;
int retval;

hx = kzalloc(sizeof(*hx), GFP_KERNEL);
if (!hx) {
hid_err(hdev, "can't alloc device descriptor\n");
return -ENOMEM;
}
hid_set_drvdata(hdev, hx);

retval = hx_create_sysfs_attributes(intf);
if (retval) {
hid_err(hdev, "cannot create sysfs files\n");
goto exit;
}

return 0;
exit:
kfree(hx);
return retval;
}

static void hx_remove_specials(struct hid_device *hdev)
{
struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
struct hx_device *hx;

hx_remove_sysfs_attributes(intf);

hx = hid_get_drvdata(hdev);
kfree(hx);
}

static int num;

static int hx_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int retval;

if (!hid_is_usb(hdev))
return -EINVAL;

if (++num != 2)
return -EINVAL;

retval = hid_parse(hdev);
if (retval) {
hid_err(hdev, "parse failed\n");
goto exit;
}

retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (retval) {
hid_err(hdev, "hw start failed\n");
goto exit;
}

printk("Have device.\n");

if (!hid_is_usb(hdev)) {
printk("Not an usb?\n");
}

{
struct usb_interface *interface = to_usb_interface(hdev->dev.parent);
struct usb_device *dev = interface_to_usbdev(interface);
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
char manufacturer[128];
char product[128];

// Retrieve manufacturer string
retval = usb_string(dev, dev->descriptor.iManufacturer, manufacturer, sizeof(manufacturer));
if (retval > 0)
printk(KERN_INFO "Manufacturer: %s\n", manufacturer);
else
printk(KERN_ERR "Failed to get manufacturer string\n");

// Retrieve product string
retval = usb_string(dev, dev->descriptor.iProduct, product, sizeof(product));
if (retval > 0)
printk(KERN_INFO "Product: %s\n", product);
else
printk(KERN_ERR "Failed to get product string\n");

}

retval = hx_init_specials(hdev);
if (retval) {
hid_err(hdev, "couldn't install mouse\n");
goto exit_stop;
}

// Example call to set_direct_color function
for (int i=0; i<20; i++) {
set_direct_color(hdev, 0x01, 0); // Example values
set_direct_color(hdev, 0x02, 0); // Example values
set_direct_color(hdev, 0x03, 0); // Example values
set_direct_color(hdev, 0x01, 0xFF); // Example values
set_direct_color(hdev, 0x02, 0xFF); // Example values
set_direct_color(hdev, 0x03, 0xFF); // Example values
}
one_hdev = hdev;
return 0;

exit_stop:
hid_hw_stop(hdev);
exit:
return retval;
}

static void hx_remove(struct hid_device *hdev)
{
hx_remove_specials(hdev);
hid_hw_stop(hdev);
}

static const struct hid_device_id hx_devices[] = {
{ HID_USB_DEVICE(0x0951, 0x16be) },
{ }
};

MODULE_DEVICE_TABLE(hid, hx_devices);

static struct hid_driver hx_driver = {
.name = "hx",
.id_table = hx_devices,
.probe = hx_probe,
.remove = hx_remove
};
module_hid_driver(hx_driver);

MODULE_AUTHOR("Pavel Machek");
MODULE_DESCRIPTION("USB HyperX elite backlight driver");
MODULE_LICENSE("GPL v2");

Attachment: signature.asc
Description: PGP signature