Re: [PATCH v5 0/1] platform/x86/tuxedo: Add virtual LampArray for TUXEDO NB04 devices
From: Werner Sembach
Date: Sat Feb 01 2025 - 14:57:51 EST
Am 01.02.25 um 05:39 schrieb Armin Wolf:
Am 21.01.25 um 23:31 schrieb Werner Sembach:
Hi,
after some other work, picked this up again.
Only coding style changes vs v4.
I now got my feet a little wet with hid-bpf regarding something else, and
with that knowledge I would leave the long arrays in the beginning in the
kernel code for the time being:
sirius_16_ansii_kbl_mapping and sirius_16_iso_kbl_mapping are required
during initialization so they have to exist in the kernel code anyway.
report_descriptor will most likly not change even for future models and
afaik having report_descriptors in kernel drivers is not unheard of.
So the only things that could be meaningfully moved to a hid-bpf program
are the sirius_16_*_kbl_mapping_pos_* arrays. But for these is have to give
out some fallback value anyway for the case where a hid-bpf file is missing
or fails to load. So why not use real world values from my test device for
these values?
As soon as there is a future device that can use the same driver with just
these pos arrays different, then I would implement that change via a bpf
program instead of a change to the kernel driver.
Let me know if you too think this is a sensefull approach?
Another question: Would this patch need to wait for a userspace
implementation of lamp array before it can get accepted?
It would be nice if you could test the LampArray implementation. But other
than that
userspace can catch up later.
I tested with the attached programs (I directly edited the source files to test
different things, this is just a snapshot).
Still, i am interested in the opinion of the LED maintainers regarding the
fake HID interface.
Thanks,
Armin Wolf
The folder structure and naming scheme with nb04 is im preparation for
other parts of tuxedo-drivers to be upstreamed. NB04 is one of the
board_vendor dmi strings on TUXEDO devices that aligns with which part of
tuxedo-drivers implements the features of that device. They are independent
of each other so I plan to put them in different subfolders to reflect
that.
Best regards,
Werner Sembach
Werner Sembach (1):
platform/x86/tuxedo: Add virtual LampArray for TUXEDO NB04 devices
MAINTAINERS | 6 +
drivers/platform/x86/Kconfig | 2 +
drivers/platform/x86/Makefile | 3 +
drivers/platform/x86/tuxedo/Kbuild | 6 +
drivers/platform/x86/tuxedo/Kconfig | 6 +
drivers/platform/x86/tuxedo/nb04/Kbuild | 9 +
drivers/platform/x86/tuxedo/nb04/Kconfig | 14 +
.../platform/x86/tuxedo/nb04/wmi_ab_init.c | 103 +++
.../platform/x86/tuxedo/nb04/wmi_ab_init.h | 18 +
.../x86/tuxedo/nb04/wmi_ab_virt_lamparray.c | 772 ++++++++++++++++++
.../x86/tuxedo/nb04/wmi_ab_virt_lamparray.h | 18 +
.../platform/x86/tuxedo/nb04/wmi_xx_util.c | 97 +++
.../platform/x86/tuxedo/nb04/wmi_xx_util.h | 112 +++
13 files changed, 1166 insertions(+)
create mode 100644 drivers/platform/x86/tuxedo/Kbuild
create mode 100644 drivers/platform/x86/tuxedo/Kconfig
create mode 100644 drivers/platform/x86/tuxedo/nb04/Kbuild
create mode 100644 drivers/platform/x86/tuxedo/nb04/Kconfig
create mode 100644 drivers/platform/x86/tuxedo/nb04/wmi_ab_init.c
create mode 100644 drivers/platform/x86/tuxedo/nb04/wmi_ab_init.h
create mode 100644 drivers/platform/x86/tuxedo/nb04/wmi_ab_virt_lamparray.c
create mode 100644 drivers/platform/x86/tuxedo/nb04/wmi_ab_virt_lamparray.h
create mode 100644 drivers/platform/x86/tuxedo/nb04/wmi_xx_util.c
create mode 100644 drivers/platform/x86/tuxedo/nb04/wmi_xx_util.h
cmake_minimum_required(VERSION 3.5)
project(LampArrayPlayground)
add_executable(lap lap.cpp)
target_compile_features(lap PRIVATE cxx_std_17)
add_executable(laploop laploop.cpp)
target_compile_features(laploop PRIVATE cxx_std_17)
add_executable(laploopsingle laploopsingle.cpp)
target_compile_features(laploopsingle PRIVATE cxx_std_17)
#include <iostream>
#include <iomanip>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/hidraw.h>
#include <cstdlib>
#include <cstdint>
using std::cout, std::cerr, std::endl, std::hex, std::setw, std::setfill, std::ios;
struct __attribute__ ((packed)) lamp_array_attributes_report {
const uint8_t report_id = 1;
uint16_t lamp_count;
uint32_t bounding_box_width_in_micrometers;
uint32_t bounding_box_height_in_micrometers;
uint32_t bounding_box_depth_in_micrometers;
uint32_t lamp_array_kind;
uint32_t min_update_interval_in_microseconds;
};
struct __attribute__ ((packed)) lamp_attributes_request_report {
const uint8_t report_id = 2;
uint16_t lamp_id;
};
struct __attribute__ ((packed)) lamp_attributes_response_report {
const uint8_t report_id = 3;
uint16_t lamp_id;
uint32_t position_x_in_micrometers;
uint32_t position_y_in_micrometers;
uint32_t position_z_in_micrometers;
uint32_t update_latency_in_microseconds;
uint32_t lamp_purpose;
uint8_t red_level_count;
uint8_t green_level_count;
uint8_t blue_level_count;
uint8_t intensity_level_count;
uint8_t is_programmable;
uint8_t input_binding;
};
struct __attribute__ ((packed)) lamp_multi_update_report {
const uint8_t report_id = 4;
uint8_t lamp_count;
uint8_t lamp_update_flags;
uint16_t lamp_id[8];
struct {
uint8_t red_update_channel;
uint8_t green_update_channel;
uint8_t blue_update_channel;
uint8_t intensity_update_channel;
} update_channels[8];
};
struct __attribute__ ((packed)) lamp_range_update_report {
const uint8_t report_id = 5;
uint8_t lamp_update_flags;
uint16_t lamp_id_start;
uint16_t lamp_id_end;
uint8_t red_update_channel;
uint8_t green_update_channel;
uint8_t blue_update_channel;
uint8_t intensity_update_channel;
};
struct __attribute__ ((packed)) lamp_array_control_report {
const uint8_t report_id = 6;
uint8_t autonomous_mode;
};
int main(int argc, char *argv[])
{
if (argc < 2) {
cout << "Usage: " << argv[0] << "<hidraw_path>" << endl;
return EXIT_SUCCESS;
}
int hidraw = open(argv[1], O_WRONLY|O_NONBLOCK);
if (hidraw < 0) {
cerr << "main: open(\"" << argv[1] << "\", O_WRONLY|O_NONBLOCK) failed." << endl;
return hidraw;
}
struct hidraw_report_descriptor report_descriptor;
int result = ioctl(hidraw, HIDIOCGRDESCSIZE, &report_descriptor.size);
if (result < 0) {
cerr << "main: ioctl(hidraw, HIDIOCGRDESCSIZE, &report_descriptor.size) failed." << endl;
close(hidraw);
return result;
}
result = ioctl(hidraw, HIDIOCGRDESC, &report_descriptor);
if (result < 0) {
cerr << "main: ioctl(hidraw, HIDIOCGRDESC, &report_descriptor) failed." << endl;
close(hidraw);
return result;
}
ios default_cout_state(nullptr);
default_cout_state.copyfmt(cout);
cout << "Report descriptor:" << endl;
cout << hex << setfill('0');
for (int i = 0; i < report_descriptor.size; ++i) {
if (i % 8 != 7 && i != report_descriptor.size - 1) {
cout << setw(2) << (int)report_descriptor.value[i] << " ";
}
else {
cout << setw(2) << (int)report_descriptor.value[i] << endl;
}
}
cout << endl;
cout.copyfmt(default_cout_state);
struct lamp_array_attributes_report lamp_array_attributes_report;
result = ioctl(hidraw, HIDIOCGFEATURE(sizeof(struct lamp_array_attributes_report)), &lamp_array_attributes_report);
if (result < 0) {
cerr << "ioctl(hidraw, HIDIOCGFEATURE(sizeof(struct lamp_array_attributes_report)) failed." << endl;
close(hidraw);
return result;
}
cout << "lamp count: " << lamp_array_attributes_report.lamp_count << endl;
cout << "bounding box depth: " << lamp_array_attributes_report.bounding_box_depth_in_micrometers << endl;
cout << "bounding box height: " << lamp_array_attributes_report.bounding_box_height_in_micrometers << endl;
cout << "bounding box width: " << lamp_array_attributes_report.bounding_box_width_in_micrometers << endl;
cout << endl;
cout << "Attributes Report:" << endl;
cout << hex << setfill('0');
for (int i = 0; i < sizeof(struct lamp_array_attributes_report); ++i) {
if (i % 8 != 7 && i != sizeof(struct lamp_array_attributes_report) - 1) {
cout << setw(2) << (int)((uint8_t *)&lamp_array_attributes_report)[i] << " ";
}
else {
cout << setw(2) << (int)((uint8_t *)&lamp_array_attributes_report)[i] << endl;
}
}
cout.copyfmt(default_cout_state);
struct lamp_array_control_report lamp_array_control_report;
lamp_array_control_report.autonomous_mode = 0;
result = ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_array_control_report)), &lamp_array_control_report);
if (result < 0) {
cerr << "main: ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_array_control_report)), lamp_array_control_report) failed." << endl;
close(hidraw);
return result;
}
struct lamp_range_update_report lamp_range_update_report;
lamp_range_update_report.red_update_channel = 0xff;
lamp_range_update_report.green_update_channel = 0xff;
lamp_range_update_report.blue_update_channel = 0xff;
lamp_range_update_report.intensity_update_channel = 0xff;
lamp_range_update_report.lamp_update_flags = 0x01;
lamp_range_update_report.lamp_id_start = 0;
lamp_range_update_report.lamp_id_end = lamp_array_attributes_report.lamp_count - 1;
result = ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_range_update_report)), &lamp_range_update_report);
if (result < 0) {
cerr << "main: ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_range_update_report)), lamp_range_update_report) failed." << endl;
close(hidraw);
return result;
}
struct lamp_multi_update_report lamp_multi_update_report;
lamp_multi_update_report.lamp_count = 3;
lamp_multi_update_report.lamp_id[0] = 0;
lamp_multi_update_report.update_channels[0].red_update_channel = 0xff;
lamp_multi_update_report.update_channels[0].green_update_channel = 0;
lamp_multi_update_report.update_channels[0].blue_update_channel = 0;
lamp_multi_update_report.update_channels[0].intensity_update_channel = 0xff;
lamp_multi_update_report.lamp_id[1] = 1;
lamp_multi_update_report.update_channels[1].red_update_channel = 0;
lamp_multi_update_report.update_channels[1].green_update_channel = 0xff;
lamp_multi_update_report.update_channels[1].blue_update_channel = 0;
lamp_multi_update_report.update_channels[1].intensity_update_channel = 0xff;
lamp_multi_update_report.lamp_id[2] = 2;
lamp_multi_update_report.update_channels[2].red_update_channel = 0;
lamp_multi_update_report.update_channels[2].green_update_channel = 0;
lamp_multi_update_report.update_channels[2].blue_update_channel = 0xff;
lamp_multi_update_report.update_channels[2].intensity_update_channel = 0xff;
lamp_multi_update_report.lamp_update_flags = 0x01;
result = ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_multi_update_report)), &lamp_multi_update_report);
if (result < 0) {
cerr << "main: ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_multi_update_report)), lamp_multi_update_report) failed." << endl;
close(hidraw);
return result;
}
close(hidraw);
return EXIT_SUCCESS;
}
#include <iostream>
#include <iomanip>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/hidraw.h>
#include <cstdlib>
#include <cstdint>
using std::cout, std::cerr, std::endl, std::hex, std::setw, std::setfill, std::ios;
struct __attribute__ ((packed)) lamp_array_attributes_report {
const uint8_t report_id = 1;
uint16_t lamp_count;
uint32_t bounding_box_width_in_micrometers;
uint32_t bounding_box_height_in_micrometers;
uint32_t bounding_box_depth_in_micrometers;
uint32_t lamp_array_kind;
uint32_t min_update_interval_in_microseconds;
};
struct __attribute__ ((packed)) lamp_attributes_request_report {
const uint8_t report_id = 2;
uint16_t lamp_id;
};
struct __attribute__ ((packed)) lamp_attributes_response_report {
const uint8_t report_id = 3;
uint16_t lamp_id;
uint32_t position_x_in_micrometers;
uint32_t position_y_in_micrometers;
uint32_t position_z_in_micrometers;
uint32_t update_latency_in_microseconds;
uint32_t lamp_purpose;
uint8_t red_level_count;
uint8_t green_level_count;
uint8_t blue_level_count;
uint8_t intensity_level_count;
uint8_t is_programmable;
uint8_t input_binding;
};
struct __attribute__ ((packed)) lamp_multi_update_report {
const uint8_t report_id = 4;
uint8_t lamp_count;
uint8_t lamp_update_flags;
uint16_t lamp_id[8];
struct {
uint8_t red_update_channel;
uint8_t green_update_channel;
uint8_t blue_update_channel;
uint8_t intensity_update_channel;
} update_channels[8];
};
struct __attribute__ ((packed)) lamp_range_update_report {
const uint8_t report_id = 5;
uint8_t lamp_update_flags;
uint16_t lamp_id_start;
uint16_t lamp_id_end;
uint8_t red_update_channel;
uint8_t green_update_channel;
uint8_t blue_update_channel;
uint8_t intensity_update_channel;
};
struct __attribute__ ((packed)) lamp_array_control_report {
const uint8_t report_id = 6;
uint8_t autonomous_mode;
};
int main(int argc, char *argv[])
{
if (argc < 2) {
cout << "Usage: " << argv[0] << "<hidraw_path>" << endl;
return EXIT_SUCCESS;
}
int hidraw = open(argv[1], O_WRONLY|O_NONBLOCK);
if (hidraw < 0) {
cerr << "main: open(\"" << argv[1] << "\", O_WRONLY|O_NONBLOCK) failed." << endl;
return hidraw;
}
int result = 0;
struct lamp_range_update_report lamp_range_update_report;
lamp_range_update_report.intensity_update_channel = 0xff;
lamp_range_update_report.lamp_update_flags = 0x01;
lamp_range_update_report.lamp_id_start = 0;
lamp_range_update_report.lamp_id_end = 101;
while (true) {
lamp_range_update_report.red_update_channel = 0xff;
lamp_range_update_report.green_update_channel = 0;
lamp_range_update_report.blue_update_channel = 0;
result = ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_range_update_report)), &lamp_range_update_report);
if (result < 0) {
cerr << "main: ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_range_update_report)), lamp_range_update_report) failed." << endl;
close(hidraw);
return result;
}
lamp_range_update_report.red_update_channel = 0;
lamp_range_update_report.green_update_channel = 0xff;
lamp_range_update_report.blue_update_channel = 0;
result = ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_range_update_report)), &lamp_range_update_report);
if (result < 0) {
cerr << "main: ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_range_update_report)), lamp_range_update_report) failed." << endl;
close(hidraw);
return result;
}
lamp_range_update_report.red_update_channel = 0;
lamp_range_update_report.green_update_channel = 0;
lamp_range_update_report.blue_update_channel = 0xff;
result = ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_range_update_report)), &lamp_range_update_report);
if (result < 0) {
cerr << "main: ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_range_update_report)), lamp_range_update_report) failed." << endl;
close(hidraw);
return result;
}
}
close(hidraw);
return EXIT_SUCCESS;
}
#include <iostream>
#include <iomanip>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/hidraw.h>
#include <cstdlib>
#include <cstdint>
using std::cout, std::cerr, std::endl, std::hex, std::setw, std::setfill, std::ios;
struct __attribute__ ((packed)) lamp_array_attributes_report {
const uint8_t report_id = 1;
uint16_t lamp_count;
uint32_t bounding_box_width_in_micrometers;
uint32_t bounding_box_height_in_micrometers;
uint32_t bounding_box_depth_in_micrometers;
uint32_t lamp_array_kind;
uint32_t min_update_interval_in_microseconds;
};
struct __attribute__ ((packed)) lamp_attributes_request_report {
const uint8_t report_id = 2;
uint16_t lamp_id;
};
struct __attribute__ ((packed)) lamp_attributes_response_report {
const uint8_t report_id = 3;
uint16_t lamp_id;
uint32_t position_x_in_micrometers;
uint32_t position_y_in_micrometers;
uint32_t position_z_in_micrometers;
uint32_t update_latency_in_microseconds;
uint32_t lamp_purpose;
uint8_t red_level_count;
uint8_t green_level_count;
uint8_t blue_level_count;
uint8_t intensity_level_count;
uint8_t is_programmable;
uint8_t input_binding;
};
struct __attribute__ ((packed)) lamp_multi_update_report {
const uint8_t report_id = 4;
uint8_t lamp_count;
uint8_t lamp_update_flags;
uint16_t lamp_id[8];
struct {
uint8_t red_update_channel;
uint8_t green_update_channel;
uint8_t blue_update_channel;
uint8_t intensity_update_channel;
} update_channels[8];
};
struct __attribute__ ((packed)) lamp_range_update_report {
const uint8_t report_id = 5;
uint8_t lamp_update_flags;
uint16_t lamp_id_start;
uint16_t lamp_id_end;
uint8_t red_update_channel;
uint8_t green_update_channel;
uint8_t blue_update_channel;
uint8_t intensity_update_channel;
};
struct __attribute__ ((packed)) lamp_array_control_report {
const uint8_t report_id = 6;
uint8_t autonomous_mode;
};
int main(int argc, char *argv[])
{
if (argc < 2) {
cout << "Usage: " << argv[0] << "<hidraw_path>" << endl;
return EXIT_SUCCESS;
}
int hidraw = open(argv[1], O_WRONLY|O_NONBLOCK);
if (hidraw < 0) {
cerr << "main: open(\"" << argv[1] << "\", O_WRONLY|O_NONBLOCK) failed." << endl;
return hidraw;
}
int result = 0;
struct lamp_multi_update_report lamp_multi_update_report;
lamp_multi_update_report.lamp_count = 1;
lamp_multi_update_report.lamp_id[0] = 0;
lamp_multi_update_report.update_channels[0].intensity_update_channel = 0xff;
lamp_multi_update_report.lamp_update_flags = 0x01;
while (true) {
lamp_multi_update_report.update_channels[0].red_update_channel = 0xff;
lamp_multi_update_report.update_channels[0].green_update_channel = 0;
lamp_multi_update_report.update_channels[0].blue_update_channel = 0;
result = ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_multi_update_report)), &lamp_multi_update_report);
if (result < 0) {
cerr << "main: ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_multi_update_report)), lamp_multi_update_report) failed." << endl;
close(hidraw);
return result;
}
lamp_multi_update_report.update_channels[0].red_update_channel = 0;
lamp_multi_update_report.update_channels[0].green_update_channel = 0xff;
lamp_multi_update_report.update_channels[0].blue_update_channel = 0;
result = ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_multi_update_report)), &lamp_multi_update_report);
if (result < 0) {
cerr << "main: ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_multi_update_report)), lamp_multi_update_report) failed." << endl;
close(hidraw);
return result;
}
lamp_multi_update_report.update_channels[0].red_update_channel = 0;
lamp_multi_update_report.update_channels[0].green_update_channel = 0;
lamp_multi_update_report.update_channels[0].blue_update_channel = 0xff;
result = ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_multi_update_report)), &lamp_multi_update_report);
if (result < 0) {
cerr << "main: ioctl(hidraw, HIDIOCSFEATURE(sizeof(struct lamp_multi_update_report)), lamp_multi_update_report) failed." << endl;
close(hidraw);
return result;
}
}
close(hidraw);
return EXIT_SUCCESS;
}