28 Commits
dm5 ... v0.16

Author SHA1 Message Date
Filipe Laíns
952bd9dc07 libratbag 0.16
Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-06-24 15:11:34 +01:00
staticssleever668
6b97d94a2f sinowealth: print out firmware version when probing 2021-06-23 15:40:38 +00:00
Thomas Weißschuh
ccaa8d1b6c ratbagd: register task to run as soon as possible
Previously the scheduled task would only have been executed after some
other event woke up the mainloop.
If no such event happens, the task will be delayed significantly.

Fixes #1121
2021-06-21 22:47:42 +01:00
Elián Hanisch
abe451df33 sinowealth-nubwo: fix implicit declaration of function ‘vasprintf’
driver-sinowealth-nubwo.c was missing to include "config.h" so
_GNU_SOURCE wasn't defined soon enough.

Co-authored-by: Elián Hanisch <elian.hanisch@ing.unlp.edu.ar>
2021-04-29 01:38:03 +01:00
Sethox
a3fa876c3d data: add SEL variant to Roccat Kone Pure
The last part added is for a variant called "Roccat Kone Pure SEL".

PR #1152
2021-04-21 03:45:25 +01:00
Filipe Laíns
230647c7f3 openinput: fail probe if we can't enumerate
These functions are mandatory[1], fail to probe if they fail.

[1] https://github.com/openinput-fw/openinput-protocol/tree/master/specification/device-protocol#mandatory-functions

Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-04-04 15:08:21 +01:00
Filipe Laíns
3f99446a1b ci: temporarily disable arch
See my description of the problem in [1].
Essentially, docker is mishandling the failuire when a syscall is not
available, returning EPERM instead of ENOSYS, and making code misbehave.
The current issue with arch is glibc, which explodes on EPERM, causing
pacman to misbehave.

Once the fix hits CircleCI, we will re-enable.

[1] https://github.com/fwupd/fwupd/issues/2894

Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-03-01 10:34:10 +10:00
Filip Laurentiu
a766f8e909 data: add new identifier for DeviceMatch to match MX Anywhere2 mouse
PR #1137
2021-02-28 19:02:52 +00:00
Pavel Kovar
9ae975f9a9 data: add support for Logitech M500s mouse 2021-02-17 17:53:27 +00:00
Filipe Laíns
780934b1a1 data: add openinput device
Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-02-16 00:15:59 +00:00
Filipe Laíns
337fb935c4 openinput: refactor function discovery
Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-02-16 00:15:59 +00:00
Filipe Laíns
c9cf5fe90c openinput: add function discovery
Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-02-16 00:15:59 +00:00
Filipe Laíns
18af0f97d2 openinput: print device protocol name
Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-02-16 00:15:59 +00:00
Filipe Laíns
286f13c654 openinput: read firmware information
Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-02-16 00:15:59 +00:00
Filipe Laíns
c6b9cc0c2a openinput: add error support
Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-02-16 00:15:59 +00:00
Filipe Laíns
7d82bb51a6 openinput: save protocol version
Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-02-16 00:15:59 +00:00
Filipe Laíns
8976033da7 openinput: add openinput_send_report
Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-02-16 00:15:59 +00:00
Filipe Laíns
c9c6a1675f openinput: add barebones driver
This is the minimal code required to have a working driver, now we just
need a .device file.

Note the hardcoding of `profile->is_active = true` in
openinput_read_profile, this works because we just have one profile.

Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-02-16 00:15:59 +00:00
the Weld
f1e2a4ccff data: add extra USB device ID for MX Vertical
Fixes #1132, for my own model of MX Vertical at the least.
2021-02-15 20:00:34 +00:00
Filipe Laíns
104543fdf1 data: add G Pro X Wireless Superlight
Support for this receiver, and for the device using the powerplay
receiver, still needs to be added to the kernel driver. For now, you can
used the wired connection to configure the device.

You should also not try to use with the powerplay receiver as the kernel
driver does not support the connection and won't forward any input
events to userspace.

Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-01-30 10:59:37 +00:00
Matt
b3275e10d7 data: add Logitech M545 2020-12-30 17:43:52 +00:00
Filipe Laíns
120908bda8 data: add G915 TKL
Closes #1008

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-10-22 20:20:04 +01:00
Filipe Laíns
32d6443553 data: add an ID to the MX Anywhere 2
Fixes #1078

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-10-17 18:22:54 +01:00
Calvin Walton
ae943eaee7 data: Add configuration for SteelSeries Kinzu V2 Pro Edition
Appears to be basically the same as the V2 - the differences were
mostly in hardware (upgraded switches & image sensor). I've verified
that the list of DPI settings is correct and matches the Windows
software. Adjusting DPI config in both profiles works correctly.
2020-10-15 01:50:44 +01:00
Calvin Walton
4a9503f2da data: add device IDs for Logitech MX Anywhere 3 2020-10-14 21:57:19 +01:00
Filipe Laíns
e268100d31 libratbag-data: log message if the device is a Logitech receiver
We have been receiving countless issues and PRs reporting or "fixing"
the device IDs because they think it's wrong. Hopefully this will give
them an hint that is intended behavior. If they look closely at the log,
on a recent enough kernel, they should see their device show up with the
internal ID.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-10-09 17:37:44 +01:00
Filipe Laíns
8ec0e8bbc5 Revert "meson: bump required meson version"
This reverts commit e753db72f9.

The new version is too recent for lgtm.com.
See https://lgtm.com/projects/g/libratbag/libratbag/logs/rev/pr-c8dd47f8b4d0c40fc0a27b13e031bbb8e6c76987/lang:cpp/stage:Build%20merge_7b2f50275451a899db1bf70b6eaae9d88ecc56d0

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-10-08 01:46:03 +01:00
bluemner
e6a9fba498 Update add support for G-Shift keys on g600 2020-10-05 00:44:27 +01:00
24 changed files with 673 additions and 477 deletions

View File

@@ -258,7 +258,7 @@ workflows:
compile_and_test:
jobs:
- ubuntu_20_04
- arch
# - arch
- fedora_32
- doc_build
- doc_deploy:

View File

@@ -1,10 +0,0 @@
[Device]
Name=Dream Machines DM5 Blink
DeviceMatch=usb:258a:0027
Driver=sinowealth
[Driver/sinowealth]
Buttons=8
Leds=1
LedType=RGB
DpiRange=100:16000@100

View File

@@ -3,7 +3,3 @@ Name=Glorious Model D
DeviceMatch=usb:258a:0033
Driver=sinowealth
[Driver/sinowealth]
Buttons=6
Leds=2
DpiRange=100:12000@100

View File

@@ -3,8 +3,3 @@ Name=Glorious Model O
DeviceMatch=usb:258a:0036
Driver=sinowealth
[Driver/sinowealth]
Buttons=6
Leds=2
LedType=RBG
DpiRange=100:12000@100

View File

@@ -0,0 +1,5 @@
# Logitech M500s
[Device]
Name=Logitech M500s
DeviceMatch=usb:046d:c093
Driver=hidpp20

View File

@@ -0,0 +1,5 @@
# Logitech M545
[Device]
Name=Logitech M545
DeviceMatch=usb:046d:4052
Driver=hidpp20

View File

@@ -0,0 +1,5 @@
# Logitech MX Anywhere 3
[Device]
Name=Logitech MX Anywhere 3
DeviceMatch=bluetooth:046d:b025;usb:046d:4090
Driver=hidpp20

View File

@@ -1,4 +1,4 @@
[Device]
Name=Logitech MX Anywhere 2
DeviceMatch=usb:046d:404a;bluetooth:046d:b013;bluetooth:046d:b018;bluetooth:046d:b01f
DeviceMatch=usb:046d:404a;usb:046d:4072;usb:046d:4063;bluetooth:046d:b013;bluetooth:046d:b018;bluetooth:046d:b01f
Driver=hidpp20

View File

@@ -1,4 +1,4 @@
[Device]
Name=Logitech MX Vertical
DeviceMatch=bluetooth:046d:b020;usb:046d:407b
DeviceMatch=bluetooth:046d:b020;usb:046d:407b;usb:046d:c08a
Driver=hidpp20

View File

@@ -0,0 +1,5 @@
[Device]
Name=Logitech G Pro X Wireless Superlight
DeviceMatch=usb:046d:4093;usb:046d:c094
LedTypes=logo;battery;dpi;
Driver=hidpp20

View File

@@ -0,0 +1,8 @@
[Device]
Name=Logitech G915 TKL
DeviceMatch=usb:046d:c343;bluetooth:046d:b35f
LedTypes=logo;switches
Driver=hidpp20
[Driver/hidpp20]
DeviceIndex=1

View File

@@ -0,0 +1,4 @@
[Device]
Name=openinput
DeviceMatch=usb:1d50:616a
Driver=openinput

View File

@@ -1,4 +1,4 @@
[Device]
Name=Roccat Kone Pure
DeviceMatch=usb:1e7d:2dc2;usb:1e7d:2dbe
DeviceMatch=usb:1e7d:2dc2;usb:1e7d:2dbe;usb:1e7d:2dcb
Driver=roccat-kone-pure

View File

@@ -0,0 +1,11 @@
[Device]
Name=SteelSeries Kinzu V2 Pro Edition
DeviceMatch=usb:1038:1366
Driver=steelseries
[Driver/steelseries]
DeviceVersion=1
Buttons=3
Leds=0
DpiList=400;800;1600;3200
MacroLength=0

View File

@@ -1,8 +1,8 @@
project('libratbag', 'c',
version : '0.15',
version : '0.16',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
meson_version : '>= 0.53.0')
meson_version : '>= 0.40.0')
# The DBus API version. Increase this every time the DBus API changes.
# No backwards/forwards guarantee, clients are expected to understand
@@ -174,6 +174,7 @@ src_libratbag = [
'src/driver-roccat.c',
'src/driver-roccat-kone-pure.c',
'src/driver-gskill.c',
'src/driver-openinput.c',
'src/driver-steelseries.c',
'src/driver-sinowealth.c',
'src/driver-sinowealth-nubwo.c',
@@ -283,6 +284,8 @@ data_files = files(
'data/devices/etekcity-scroll-alpha.device',
'data/devices/gskill-MX-780.device',
'data/devices/logitech-M325.device',
'data/devices/logitech-M500s.device',
'data/devices/logitech-M545.device',
'data/devices/logitech-M570.device',
'data/devices/logitech-M585-M590.device',
'data/devices/logitech-M705.device',
@@ -296,6 +299,7 @@ data_files = files(
'data/devices/logitech-Wireless-Touchpad.device',
'data/devices/logitech-g-powerplay.device',
'data/devices/logitech-g-pro-wireless.device',
'data/devices/logitech-g-pro-x-wireless-superlight.device',
'data/devices/logitech-g-pro.device',
'data/devices/logitech-g102-g203.device',
'data/devices/logitech-g300.device',
@@ -332,16 +336,20 @@ data_files = files(
'data/devices/logitech-g815.device',
'data/devices/logitech-g910.device',
'data/devices/logitech-g915.device',
'data/devices/logitech-g915-tkl.device',
'data/devices/logitech-g935.device',
'data/devices/logitech-g9x-Call-of-Duty-MW3-Edition.device',
'data/devices/logitech-g9x-Original.device',
'data/devices/logitech-Marathon-M705.device',
'data/devices/logitech-MX-Anywhere2.device',
'data/devices/logitech-MX-Anywhere2S.device',
'data/devices/logitech-MX-Anywhere-3.device',
'data/devices/logitech-MX-Vertical.device',
'data/devices/openinput.device',
'data/devices/roccat-kone-pure.device',
'data/devices/roccat-kone-xtd.device',
'data/devices/steelseries-kinzu-v2.device',
'data/devices/steelseries-kinzu-v2-pro.device',
'data/devices/steelseries-kinzu-v3.device',
'data/devices/steelseries-rival-310.device',
'data/devices/steelseries-rival-600.device',
@@ -351,7 +359,6 @@ data_files = files(
'data/devices/glorious-model-d.device',
'data/devices/glorious-model-o.device',
'data/devices/nubwo-x7-spectrum.device',
'data/devices/dream-machines-dm5-blink.device',
)
install_data(data_files,
@@ -457,9 +464,10 @@ install_man('ratbagd/ratbagd.8')
# (org.freedesktop.ratbag_devel1). This server is used by ratbagdctl.devel.
#
config_ratbagd_devel = configuration_data()
dbus_devel_policy = configure_file(input : 'dbus/org.freedesktop.ratbag_devel1.conf.in',
output : 'org.freedesktop.ratbag_devel1.conf',
copy : true)
configuration : config_ratbagd_devel)
# This is a hack. We always install the devel policy file into
# /etc/dbus-1/system.d, independent of any prefixes we use otherwise.

View File

@@ -697,5 +697,5 @@ void ratbagd_schedule_task(struct ratbagd *ctx,
cb->callback = callback;
cb->userdata = userdata;
sd_event_add_post(ctx->event, &source, ratbagd_callback_handler, cb);
sd_event_add_defer(ctx->event, &source, ratbagd_callback_handler, cb);
}

View File

@@ -34,7 +34,7 @@
#include "libratbag-hidraw.h"
#define LOGITECH_G600_NUM_PROFILES 3
#define LOGITECH_G600_NUM_BUTTONS 20
#define LOGITECH_G600_NUM_BUTTONS 41 // 20 buttons + 1 color buffer + 20 G-shift
#define LOGITECH_G600_NUM_DPI 4
#define LOGITECH_G600_NUM_LED 1
#define LOGITECH_G600_DPI_MIN 200
@@ -109,20 +109,41 @@ static const struct logitech_g600_button_type_mapping logitech_g600_button_type_
{ 3, RATBAG_BUTTON_TYPE_WHEEL_LEFT },
{ 4, RATBAG_BUTTON_TYPE_WHEEL_RIGHT },
{ 5, RATBAG_BUTTON_TYPE_PINKIE },
{ 6, RATBAG_BUTTON_TYPE_PROFILE_CYCLE_UP },
{ 7, RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP },
{ 8, RATBAG_BUTTON_TYPE_SIDE }, // 8 = G9 ... 19 = G20
{ 9, RATBAG_BUTTON_TYPE_SIDE },
{ 10, RATBAG_BUTTON_TYPE_SIDE },
{ 11, RATBAG_BUTTON_TYPE_SIDE },
{ 12, RATBAG_BUTTON_TYPE_SIDE },
{ 13, RATBAG_BUTTON_TYPE_SIDE },
{ 14, RATBAG_BUTTON_TYPE_SIDE },
{ 15, RATBAG_BUTTON_TYPE_SIDE },
{ 16, RATBAG_BUTTON_TYPE_SIDE },
{ 17, RATBAG_BUTTON_TYPE_SIDE },
{ 18, RATBAG_BUTTON_TYPE_SIDE },
{ 19, RATBAG_BUTTON_TYPE_SIDE },
{ 6, RATBAG_BUTTON_TYPE_PROFILE_CYCLE_UP }, // G07
{ 7, RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP }, // G08
{ 8, RATBAG_BUTTON_TYPE_SIDE }, // G09
{ 9, RATBAG_BUTTON_TYPE_SIDE }, // G10
{ 10, RATBAG_BUTTON_TYPE_SIDE }, // G11
{ 11, RATBAG_BUTTON_TYPE_SIDE }, // G12
{ 12, RATBAG_BUTTON_TYPE_SIDE }, // G13
{ 13, RATBAG_BUTTON_TYPE_SIDE }, // G14
{ 14, RATBAG_BUTTON_TYPE_SIDE }, // G15
{ 15, RATBAG_BUTTON_TYPE_SIDE }, // G16
{ 16, RATBAG_BUTTON_TYPE_SIDE }, // G17
{ 17, RATBAG_BUTTON_TYPE_SIDE }, // G18
{ 18, RATBAG_BUTTON_TYPE_SIDE }, // G19
{ 19, RATBAG_BUTTON_TYPE_SIDE }, // G20
{ 20, RATBAG_BUTTON_TYPE_UNKNOWN }, //HACK is where the G-shift color information is located
{ 21, RATBAG_BUTTON_TYPE_LEFT }, // G-shift + left
{ 22, RATBAG_BUTTON_TYPE_RIGHT }, // G-shift + right click
{ 23, RATBAG_BUTTON_TYPE_MIDDLE }, // G-shift + middle
{ 24, RATBAG_BUTTON_TYPE_WHEEL_LEFT}, // G-shift + scroll left
{ 25, RATBAG_BUTTON_TYPE_WHEEL_RIGHT}, // G-shift + scroll right
{ 26, RATBAG_BUTTON_TYPE_PINKIE }, // G-shift + Pinky(shift modifer)
{ 27, RATBAG_BUTTON_TYPE_PROFILE_CYCLE_UP }, // G-shift + 07
{ 28, RATBAG_BUTTON_TYPE_RESOLUTION_CYCLE_UP }, // G-shift + G08
{ 29, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G09
{ 30, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G10
{ 31, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G11
{ 32, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G12
{ 33, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G13
{ 34, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G14
{ 35, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G15
{ 36, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G16
{ 37, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G17
{ 38, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G18
{ 39, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G19
{ 40, RATBAG_BUTTON_TYPE_SIDE }, // G-shift + G20
};
static enum ratbag_button_type
@@ -144,7 +165,7 @@ struct logitech_g600_button_mapping {
};
static struct logitech_g600_button_mapping logitech_g600_button_mapping[] = {
/* 0x00 is either key or unassigned. Must be handled separatly */
/* 0x00 is either key or unassigned. Must be handled separately */
{ 0x01, BUTTON_ACTION_BUTTON(1) },
{ 0x02, BUTTON_ACTION_BUTTON(2) },
{ 0x03, BUTTON_ACTION_BUTTON(3) },
@@ -600,6 +621,12 @@ logitech_g600_write_profile(struct ratbag_profile *profile)
report->led_duration = 0x0f;
}
// For now the default will copy over the main color into the g-shift color
// future update may add support to set this via cli command
report->g_shift_color[0] = report->led_red;
report->g_shift_color[1] = report->led_green;
report->g_shift_color[2] = report->led_blue;
buf = (uint8_t*)report;
rc = ratbag_hidraw_raw_request(device, report->id,

482
src/driver-openinput.c Normal file
View File

@@ -0,0 +1,482 @@
/*
* Copyright © 2021 Filipe Laíns
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include "libratbag-private.h"
/* reports */
#define OI_REPORT_SHORT 0x20
#define OI_REPORT_LONG 0x21
#define OI_REPORT_SHORT_SIZE 8
#define OI_REPORT_LONG_SIZE 32
#define OI_REPORT_MAX_SIZE OI_REPORT_LONG_SIZE
#define OI_REPORT_DATA_INDEX 3
#define OI_REPORT_DATA_MAX_SIZE OI_REPORT_LONG_SIZE - OI_REPORT_DATA_INDEX
/* protocol function pages */
#define OI_PAGE_INFO 0x00
#define OI_PAGE_GIMMICKS 0xFD
#define OI_PAGE_DEBUG 0xFE
#define OI_PAGE_ERROR 0xFF
/* info page (0x00) functions */
#define OI_FUNCTION_VERSION 0x00
#define OI_FUNCTION_FW_INFO 0x01
#define OI_FUNCTION_SUPPORTED_FUNCTION_PAGES 0x02
#define OI_FUNCTION_SUPPORTED_FUNCTIONS 0x03
/* error page (0xFF) */
#define OI_ERROR_INVALID_VALUE 0x01
#define OI_ERROR_UNSUPPORTED_FUNCTION 0x02
#define OI_ERROR_CUSTOM 0xFE
static unsigned int report_rates[] = { 125, 250, 500, 750, 1000 };
struct openinput_drv_data {
unsigned int num_profiles;
unsigned int num_resolutions;
unsigned int num_buttons;
unsigned int num_leds;
unsigned int fw_major;
unsigned int fw_minor;
unsigned int fw_patch;
uint64_t supported;
};
struct oi_report_t {
uint8_t id;
uint8_t function_page;
uint8_t function;
uint8_t data[29];
} __attribute__((__packed__));
#define CASE_RETURN_STRING(a) case a: return #a; break
static const char*
openinput_function_page_get_name(uint8_t page)
{
static char numeric[16];
char *str;
switch(page) {
CASE_RETURN_STRING(OI_PAGE_INFO);
CASE_RETURN_STRING(OI_PAGE_GIMMICKS);
CASE_RETURN_STRING(OI_PAGE_DEBUG);
CASE_RETURN_STRING(OI_PAGE_ERROR);
default:
sprintf_safe(numeric, "0x%02x", page);
str = numeric;
break;
}
return str;
}
static const char*
openinput_function_get_name(uint8_t page, uint8_t function)
{
static char numeric[32];
char *str = NULL;
switch(page) {
case OI_PAGE_INFO:
switch (function) {
CASE_RETURN_STRING(OI_FUNCTION_VERSION);
CASE_RETURN_STRING(OI_FUNCTION_FW_INFO);
CASE_RETURN_STRING(OI_FUNCTION_SUPPORTED_FUNCTION_PAGES);
CASE_RETURN_STRING(OI_FUNCTION_SUPPORTED_FUNCTIONS);
}
}
if (!str) {
sprintf_safe(numeric, "0x%02x 0x%02x", page, function);
str = numeric;
}
return str;
}
#undef CASE_RETURN_STRING
static const char*
openinput_get_error_string(struct oi_report_t *report)
{
char help_str[OI_REPORT_LONG_SIZE - OI_REPORT_DATA_INDEX + 1] = {0};
char *str;
switch (report->function) {
case OI_ERROR_INVALID_VALUE:
xasprintf(&str, "Invalid value (in position %u)", report->data[2]);
break;
case OI_ERROR_CUSTOM:
memcpy(help_str, report->data, sizeof(help_str)-1); /* copy error string */
xasprintf(&str, "Custom error (%s)", help_str);
break;
case OI_ERROR_UNSUPPORTED_FUNCTION:
xasprintf(&str, "Unsupported function (0x%02x, 0x%02x)", report->data[0], report->data[1]);
break;
default:
xasprintf(&str, "Unkown error (%u)", report->function);
break;
}
return str;
}
static size_t
openinput_get_report_size(unsigned int report)
{
switch (report) {
case OI_REPORT_SHORT:
return OI_REPORT_SHORT_SIZE;
case OI_REPORT_LONG:
return OI_REPORT_LONG_SIZE;
default:
return 0;
}
}
static int
openinput_send_report(struct ratbag_device *device, struct oi_report_t *report)
{
int ret;
uint8_t buffer[OI_REPORT_MAX_SIZE];
size_t size = openinput_get_report_size(report->id);
memcpy(buffer, report, size);
ret = ratbag_hidraw_output_report(device, buffer, size);
if (ret < 0) {
log_error(device->ratbag, "openinput: failed to send data to device (%s)\n",
strerror(-ret));
return ret;
}
ret = ratbag_hidraw_read_input_report(device, buffer, OI_REPORT_MAX_SIZE);
if (ret < 0) {
log_error(device->ratbag, "openinput: failed to read data from device (%s)\n",
strerror(-ret));
return ret;
}
memcpy(report, buffer, openinput_get_report_size(buffer[0]));
/* check for error */
if (report->function_page == OI_PAGE_ERROR) {
log_error(device->ratbag, "openinput: %s\n", openinput_get_error_string(report));
return report->function;
}
return 0;
}
static int
openinput_info_version(struct ratbag_device *device)
{
int ret;
struct openinput_drv_data *drv_data = ratbag_get_drv_data(device);
struct oi_report_t report = {
.id = OI_REPORT_SHORT,
.function_page = OI_PAGE_INFO,
.function = OI_FUNCTION_VERSION
};
ret = openinput_send_report(device, &report);
if (ret)
return ret;
drv_data->fw_major = report.data[0];
drv_data->fw_minor = report.data[1];
drv_data->fw_patch = report.data[2];
log_info(device->ratbag, "openinput: protocol version %u.%u.%u\n",
drv_data->fw_major, drv_data->fw_minor, drv_data->fw_patch);
return 0;
}
#define OI_FUNCTION_FW_INFO_VENDOR 0x00
#define OI_FUNCTION_FW_INFO_VERSION 0x01
#define OI_FUNCTION_FW_INFO_DEVICE_NAME 0x02
static int
openinput_info_fw_info(struct ratbag_device *device,
uint8_t field_id,
unsigned char *description,
size_t description_size)
{
int ret;
struct oi_report_t report = {
.id = OI_REPORT_SHORT,
.function_page = OI_PAGE_INFO,
.function = OI_FUNCTION_FW_INFO,
.data = {field_id}
};
ret = openinput_send_report(device, &report);
if (ret)
return ret;
memcpy(description, report.data, min(sizeof(report.data), description_size));
return 0;
}
static int
openinput_info_supported_function_pages(struct ratbag_device *device,
uint8_t start_index,
uint8_t *pages_count,
uint8_t *pages_left,
uint8_t *pages,
size_t pages_size)
{
int ret;
struct oi_report_t report = {
.id = OI_REPORT_SHORT,
.function_page = OI_PAGE_INFO,
.function = OI_FUNCTION_SUPPORTED_FUNCTION_PAGES,
.data = {start_index}
};
ret = openinput_send_report(device, &report);
if (ret)
return ret;
*pages_count = report.data[0];
*pages_left = report.data[1];
memcpy(pages, report.data + 2, min(sizeof(report.data), pages_size));
return 0;
}
static int
openinput_info_supported_functions(struct ratbag_device *device,
uint8_t function_page,
uint8_t start_index,
uint8_t *functions_count,
uint8_t *functions_left,
uint8_t *functions,
size_t functions_size)
{
int ret;
struct oi_report_t report = {
.id = OI_REPORT_SHORT,
.function_page = OI_PAGE_INFO,
.function = OI_FUNCTION_SUPPORTED_FUNCTIONS,
.data = {function_page, start_index}
};
ret = openinput_send_report(device, &report);
if (ret)
return ret;
*functions_count = report.data[0];
*functions_left = report.data[1];
memcpy(functions, report.data + 2, min(sizeof(report.data), functions_size));
return 0;
}
static int
openinput_read_supported_functions(struct ratbag_device *device, uint8_t page)
{
struct ratbag *ratbag = device->ratbag;
int ret;
uint8_t i, total, read = 0, count = 0, left = 0;
uint8_t buffer[OI_REPORT_DATA_MAX_SIZE];
ret = openinput_info_supported_functions(device,
page,
read,
&count, &left,
buffer, sizeof(buffer));
if (ret)
return ret;
total = count + left;
uint8_t functions[total];
memcpy(functions, buffer, count);
/* there are still functions left to read! */
while (left) {
ret = openinput_info_supported_functions(device,
page,
read,
&count, &left,
buffer, sizeof(buffer));
if (ret)
return ret;
/* make sure the new size values make sense, to avoid deadlocks */
if (total != (read + count + left)) {
log_error(ratbag, "openinput: invalid number of functions left to read (%u)\n", left);
return -EINVAL;
}
log_debug(ratbag, "openinput: read %u functions, %u left\n", count, left);
memcpy(functions + read, buffer, count);
read += count;
}
/* iterate over read functions */
for (i = 0; i < total; i++) {
log_debug(ratbag, "openinput: found function %s\n", openinput_function_get_name(page, functions[i]));
/* TODO: set bits in drv_data->supported when we implement support for certain capabilities */
}
return 0;
}
static int
openinput_read_supported_function_pages(struct ratbag_device *device)
{
struct ratbag *ratbag = device->ratbag;
int ret;
uint8_t i, total, read = 0, count = 0, left = 0;
uint8_t buffer[OI_REPORT_DATA_MAX_SIZE];
log_debug(ratbag, "openinput: starting reading device functions...\n");
ret = openinput_info_supported_function_pages(device,
read,
&count, &left,
buffer, sizeof(buffer));
if (ret)
return ret;
total = count + left;
uint8_t pages[total];
memcpy(pages, buffer, count);
/* there are still function pages left to read! */
while (left) {
ret = openinput_info_supported_function_pages(device,
read,
&count, &left,
buffer, sizeof(buffer));
if (ret)
return ret;
/* make sure the new size values make sense, to avoid deadlocks */
if (total != (read + count + left)) {
log_error(ratbag, "openinput: invalid number of function pages left to read (%u)\n", left);
return -EINVAL;
}
log_debug(ratbag, "openinput: read %u pages, %u left\n", count, left);
memcpy(pages + read, buffer, count);
read += count;
}
/* iterate over read function pages, and read their functions */
for (i = 0; i < total; i++) {
log_debug(ratbag, "openinput: found function page %s\n", openinput_function_page_get_name(pages[i]));
openinput_read_supported_functions(device, pages[i]);
}
return 0;
}
static void
openinput_read_profile(struct ratbag_profile *profile)
{
ratbag_profile_set_report_rate_list(profile, report_rates, sizeof(*report_rates));
profile->is_active = true;
}
static int
openinput_test_hidraw(struct ratbag_device *device)
{
return ratbag_hidraw_has_report(device, OI_REPORT_SHORT);
}
static int
openinput_probe(struct ratbag_device *device)
{
int ret;
struct openinput_drv_data *drv_data;
struct ratbag_profile *profile;
unsigned char str[OI_REPORT_DATA_MAX_SIZE];
ret = ratbag_find_hidraw(device, openinput_test_hidraw);
if (ret)
return ret;
drv_data = zalloc(sizeof(*drv_data));
drv_data->num_profiles = 1;
ratbag_set_drv_data(device, drv_data);
openinput_info_version(device);
ret = openinput_info_fw_info(device, OI_FUNCTION_FW_INFO_VENDOR, str, sizeof(str));
if (ret)
return ret;
log_info(device->ratbag, "openinput: firmware vendor: %s\n", str);
ret = openinput_info_fw_info(device, OI_FUNCTION_FW_INFO_VERSION, str, sizeof(str));
if (ret)
return ret;
log_info(device->ratbag, "openinput: firmware version: %s\n", str);
ret = openinput_info_fw_info(device, OI_FUNCTION_FW_INFO_DEVICE_NAME, str, sizeof(str));
if (ret)
return ret;
log_info(device->ratbag, "openinput: device: %s\n", str);
ret = openinput_read_supported_function_pages(device);
if (ret)
return ret;
ratbag_device_init_profiles(device,
drv_data->num_profiles,
drv_data->num_resolutions,
drv_data->num_buttons,
drv_data->num_leds);
ratbag_device_for_each_profile(device, profile)
openinput_read_profile(profile);
return 0;
}
static void
openinput_remove(struct ratbag_device *device)
{
ratbag_close_hidraw(device);
free(ratbag_get_drv_data(device));
}
struct ratbag_driver openinput_driver = {
.name = "openinput",
.id = "openinput",
.probe = openinput_probe,
.remove = openinput_remove,
};

View File

@@ -21,6 +21,7 @@
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include "libratbag-enums.h"
#include "libratbag-hidraw.h"
#include "libratbag-private.h"

View File

@@ -23,36 +23,38 @@
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
#include "libratbag-data.h"
#define SINOWEALTH_REPORT_ID_CONFIG 0x4
#define SINOWEALTH_REPORT_ID_CMD 0x5
#define SINOWEALTH_CMD_FIRMWARE_VERSION 0x1
#define SINOWEALTH_CMD_GET_CONFIG 0x11
#define SINOWEALTH_CMD_GET_BUTTONS 0x12
#define SINOWEALTH_CONFIG_SIZE 520
#define SINOWEALTH_CONFIG_SIZE_USED 131
#define SINOWEALTH_BUTTON_SIZE 88
#define SINOWEALTH_XY_INDEPENDENT 0x80
/* The PC software only goes down to 400, but the PMW3360 doesn't care */
#define SINOWEALTH_DPI_MIN 100
#define SINOWEALTH_DPI_MAX 12000
#define SINOWEALTH_DPI_STEP 100
/* other models might have up to eight */
#define SINOWEALTH_NUM_DPIS 6
#define SINOWEALTH_RGB_BRIGHTNESS_BITS 0xF0
#define SINOWEALTH_RGB_SPEED_BITS 0x0F
#define SINOWEALTH_BUTTON_TYPE_BUTTON 0x11
#define SINOWEALTH_BUTTON_TYPE_KEY 0x21
#define SINOWEALTH_BUTTON_TYPE_REPEATED 0x31
#define SINOWEALTH_BUTTON_TYPE_SWITCH_DPI 0x41
#define SINOWEALTH_BUTTON_TYPE_DISABLED 0x50
struct sinowealth_color {
uint8_t data[3];
struct sinowealth_rgb8 {
uint8_t r, g, b;
} __attribute__((packed));
_Static_assert(sizeof(struct sinowealth_color) == 3, "Invalid size");
_Static_assert(sizeof(struct sinowealth_rgb8) == 3, "Invalid size");
struct sinowealth_rbg8 {
uint8_t r, b, g;
} __attribute__((packed));
_Static_assert(sizeof(struct sinowealth_rbg8) == 3, "Invalid size");
enum rgb_effect {
RGB_OFF = 0,
@@ -86,13 +88,11 @@ struct sinowealth_config_report {
uint8_t dpi_enabled;
/* DPI/CPI is encoded in the way the PMW3360 sensor accepts it
* value = (DPI - 100) / 100
* or the way the PMW3389 sensor accepts it
* value = DPI / 100
* If XY are identical, dpi[0-6] contain the sensitivities,
* while in XY independent mode each entry takes two chars for X and Y.
*/
uint8_t dpi[16];
struct sinowealth_color dpi_color[8];
struct sinowealth_rgb8 dpi_color[8];
uint8_t rgb_effect; /* see enum rgb_effect */
/* 0x40 - brightness (constant)
* 0x1/2/3 - speed
@@ -100,13 +100,13 @@ struct sinowealth_config_report {
uint8_t glorious_mode;
uint8_t glorious_direction;
uint8_t single_mode;
struct sinowealth_color single_color;
struct sinowealth_rbg8 single_color;
/* 0x40 - brightness (constant)
* 0x1/2/3 - speed
*/
uint8_t breathing7_mode;
uint8_t breathing7_colorcount; /* 7, constant */
struct sinowealth_color breathing7_colors[7];
struct sinowealth_rbg8 breathing7_colors[7];
/* 0x10/20/30/40 - brightness
* 0x1/2/3 - speed
*/
@@ -116,14 +116,14 @@ struct sinowealth_config_report {
* 0x1/2/3 - speed
*/
uint8_t rave_mode;
struct sinowealth_color rave_colors[2];
struct sinowealth_rbg8 rave_colors[2];
/* 0x10/20/30/40 - brightness
* 0x1/2/3 - speed
*/
uint8_t wave_mode;
/* 0x1/2/3 - speed */
uint8_t breathing1_mode;
struct sinowealth_color breathing1_color;
struct sinowealth_rbg8 breathing1_color;
uint8_t unknown4;
/* 0x1 - 2 mm
* 0x2 - 3 mm
@@ -134,96 +134,46 @@ struct sinowealth_config_report {
_Static_assert(sizeof(struct sinowealth_config_report) == SINOWEALTH_CONFIG_SIZE, "Invalid size");
struct sinowealth_button_data {
uint8_t type;
uint8_t data[3];
} __attribute__((packed));
struct sinowealth_button_report {
uint8_t report_id;
uint8_t command_id;
uint8_t unknown1[6];
struct sinowealth_button_data buttons[20];
uint8_t padding[SINOWEALTH_CONFIG_SIZE - SINOWEALTH_BUTTON_SIZE];
} __attribute__((packed));
_Static_assert(sizeof(struct sinowealth_button_report) == SINOWEALTH_CONFIG_SIZE, "Invalid size");
enum sinowealth_sensor {
PWM3360,
PWM3389,
};
enum sinowealth_led_format {
LED_RGB,
LED_RBG,
};
struct sinowealth_data {
/* this is kinda unnecessary at this time, but all the other drivers do it too ;) */
struct sinowealth_config_report config;
/* holds button data so we can send back unchanged values for the buttons we do not parse yet */
struct sinowealth_button_report buttons;
/* Specifies sensor used in the device */
enum sinowealth_sensor sensor;
/* whether the devices stores the main LED in RGB or RBG format */
enum sinowealth_led_format led_type;
};
static int
sinowealth_raw_to_dpi(struct ratbag_device *device, int raw)
sinowealth_raw_to_dpi(int raw)
{
struct sinowealth_data *drv_data = device->drv_data;
int dpi;
if (drv_data->sensor == PWM3360)
dpi = (raw + 1) * 100;
else
dpi = raw * 100;
return dpi;
return (raw + 1) * 100;
}
static int
sinowealth_dpi_to_raw(struct ratbag_device *device, unsigned int dpi)
sinowealth_dpi_to_raw(int dpi)
{
struct sinowealth_data *drv_data = device->drv_data;
struct dpi_range *dpirange = NULL;
int raw;
dpirange = ratbag_device_data_sinowealth_get_dpi_range(device->data);
assert(dpi >= dpirange->min && dpi <= dpirange->max);
if (drv_data->sensor == PWM3360)
raw = dpi / 100 - 1;
else
raw = dpi / 100;
return raw;
assert(dpi >= SINOWEALTH_DPI_MIN && dpi <= SINOWEALTH_DPI_MAX);
return dpi / 100 - 1;
}
static struct ratbag_color
sinowealth_raw_to_color(struct sinowealth_color raw)
sinowealth_raw_to_color(struct sinowealth_rgb8 raw)
{
return (struct ratbag_color) {.red = raw.data[0], .green = raw.data[1], .blue = raw.data[2]};
return (struct ratbag_color) {.red = raw.r, .green = raw.g, .blue = raw.b};
}
static struct sinowealth_color
static struct sinowealth_rgb8
sinowealth_color_to_raw(struct ratbag_color color)
{
return (struct sinowealth_color) {.data[0] = color.red, .data[1] = color.green, .data[2] = color.blue};
return (struct sinowealth_rgb8) {.r = color.red, .g = color.green, .b = color.blue};
}
static struct ratbag_color
sinowealth_rbg_to_color(struct sinowealth_color raw)
sinowealth_rbg_to_color(struct sinowealth_rbg8 raw)
{
return (struct ratbag_color) {.red = raw.data[0], .green = raw.data[2], .blue = raw.data[1]};
return (struct ratbag_color) {.red = raw.r, .green = raw.g, .blue = raw.b};
}
static struct sinowealth_color
static struct sinowealth_rbg8
sinowealth_color_to_rbg(struct ratbag_color color)
{
return (struct sinowealth_color) {.data[0] = color.red, .data[2] = color.green, .data[1] = color.blue};
return (struct sinowealth_rbg8) {.r = color.red, .g = color.green, .b = color.blue};
}
static int
@@ -295,6 +245,20 @@ sinowealth_read_profile(struct ratbag_profile *profile)
unsigned int hz = 1000; /* TODO */
int rc;
/* Print firmware version */
uint8_t version[6] = {SINOWEALTH_REPORT_ID_CMD, SINOWEALTH_CMD_FIRMWARE_VERSION};
rc = ratbag_hidraw_set_feature_report(device, SINOWEALTH_REPORT_ID_CMD, version, sizeof(version));
if (rc != sizeof(version)) {
log_error(device->ratbag, "Error while sending read firmware version command: %d\n", rc);
return -1;
}
rc = ratbag_hidraw_get_feature_report(device, SINOWEALTH_REPORT_ID_CMD, version, sizeof(version));
if (rc != sizeof(version)) {
log_error(device->ratbag, "Could not read firmware version: %d\n", rc);
return -1;
}
log_debug(device->ratbag, "firmware version: %.4s\n", version + 2);
uint8_t cmd[6] = {SINOWEALTH_REPORT_ID_CMD, SINOWEALTH_CMD_GET_CONFIG};
rc = ratbag_hidraw_set_feature_report(device, SINOWEALTH_REPORT_ID_CMD, cmd, sizeof(cmd));
if (rc != sizeof(cmd)) {
@@ -316,10 +280,10 @@ sinowealth_read_profile(struct ratbag_profile *profile)
ratbag_profile_for_each_resolution(profile, resolution) {
if (config->config & SINOWEALTH_XY_INDEPENDENT) {
resolution->dpi_x = sinowealth_raw_to_dpi(device, config->dpi[resolution->index * 2]);
resolution->dpi_y = sinowealth_raw_to_dpi(device, config->dpi[resolution->index * 2 + 1]);
resolution->dpi_x = sinowealth_raw_to_dpi(config->dpi[resolution->index * 2]);
resolution->dpi_y = sinowealth_raw_to_dpi(config->dpi[resolution->index * 2 + 1]);
} else {
resolution->dpi_x = sinowealth_raw_to_dpi(device, config->dpi[resolution->index]);
resolution->dpi_x = sinowealth_raw_to_dpi(config->dpi[resolution->index]);
resolution->dpi_y = resolution->dpi_x;
}
if (config->dpi_enabled & (1<<resolution->index)) {
@@ -339,10 +303,7 @@ sinowealth_read_profile(struct ratbag_profile *profile)
break;
case RGB_SINGLE:
led->mode = RATBAG_LED_ON;
if (drv_data->led_type == LED_RGB)
led->color = sinowealth_raw_to_color(config->single_color);
else
led->color = sinowealth_rbg_to_color(config->single_color);
led->color = sinowealth_rbg_to_color(config->single_color);
led->brightness = sinowealth_rgb_mode_to_brightness(config->single_mode);
break;
case RGB_GLORIOUS:
@@ -367,243 +328,43 @@ sinowealth_read_profile(struct ratbag_profile *profile)
return 0;
}
struct sinowealth_button_mapping {
uint8_t raw;
struct ratbag_button_action action;
};
static struct sinowealth_button_mapping sinowealth_button_mapping[] = {
{ 0x01, BUTTON_ACTION_BUTTON(1) },
{ 0x02, BUTTON_ACTION_BUTTON(2) },
{ 0x04, BUTTON_ACTION_BUTTON(3) },
{ 0x08, BUTTON_ACTION_BUTTON(5) },
{ 0x10, BUTTON_ACTION_BUTTON(4) },
};
static const struct ratbag_button_action*
sinowealth_raw_to_button_action(uint8_t data)
{
struct sinowealth_button_mapping *mapping;
ARRAY_FOR_EACH(sinowealth_button_mapping, mapping) {
if (mapping->raw == data)
return &mapping->action;
}
return NULL;
}
static uint8_t
sinowealth_button_action_to_raw(const struct ratbag_button_action *action)
{
struct sinowealth_button_mapping *mapping;
ARRAY_FOR_EACH(sinowealth_button_mapping, mapping) {
if (ratbag_button_action_match(&mapping->action, action))
return mapping->raw;
}
return 0x00;
}
static int
sinowealth_read_buttons(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct sinowealth_data *drv_data = device->drv_data;
struct sinowealth_button_report *buf = &drv_data->buttons;
struct ratbag_button *button;
struct sinowealth_button_data button_data;
enum ratbag_button_type button_types[8] = {RATBAG_BUTTON_TYPE_UNKNOWN};
uint8_t cmd[6] = {SINOWEALTH_REPORT_ID_CMD, SINOWEALTH_CMD_GET_BUTTONS};
int rc;
rc = ratbag_hidraw_set_feature_report(device, SINOWEALTH_REPORT_ID_CMD, cmd, sizeof(cmd));
if (rc != sizeof(cmd)) {
log_error(device->ratbag, "Error while sending read config command: %d\n", rc);
return -1;
}
rc = ratbag_hidraw_get_feature_report(device, SINOWEALTH_REPORT_ID_CONFIG, (uint8_t*) buf, SINOWEALTH_CONFIG_SIZE);
if (rc != SINOWEALTH_BUTTON_SIZE) {
log_error(device->ratbag, "Could not read device button configuration: %d\n", rc);
return -1;
}
button_types[0] = RATBAG_BUTTON_TYPE_LEFT;
button_types[1] = RATBAG_BUTTON_TYPE_RIGHT;
button_types[2] = RATBAG_BUTTON_TYPE_MIDDLE;
button_types[3] = RATBAG_BUTTON_TYPE_THUMB;
button_types[4] = RATBAG_BUTTON_TYPE_THUMB2;
button_types[5] = RATBAG_BUTTON_TYPE_RESOLUTION_UP;
button_types[6] = RATBAG_BUTTON_TYPE_RESOLUTION_DOWN;
button_types[7] = RATBAG_BUTTON_TYPE_THUMB3;
ratbag_profile_for_each_button(profile, button) {
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
button->type = button_types[button->index];
button_data = buf->buttons[button->index];
if (button_data.type == SINOWEALTH_BUTTON_TYPE_BUTTON) {
const struct ratbag_button_action *action;
action = sinowealth_raw_to_button_action(button_data.data[0]);
if (action)
ratbag_button_set_action(button, action);
} else if (button_data.type == SINOWEALTH_BUTTON_TYPE_KEY) {
unsigned int key, modifiers;
int rc;
key = ratbag_hidraw_get_keycode_from_keyboard_usage(device, button_data.data[1]);
modifiers = 0;
if (button_data.data[0] & 0x01)
modifiers |= MODIFIER_LEFTCTRL;
if (button_data.data[0] & 0x02)
modifiers |= MODIFIER_LEFTSHIFT;
if (button_data.data[0] & 0x04)
modifiers |= MODIFIER_LEFTALT;
if (button_data.data[0] & 0x08)
modifiers |= MODIFIER_LEFTMETA;
rc = ratbag_button_macro_new_from_keycode(button, key, modifiers);
if (rc < 0) {
log_error(device->ratbag,
"Error while reading button %d\n",
button->index);
button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
}
}
}
return 0;
}
static int
sinowealth_write_buttons(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct sinowealth_data *drv_data = device->drv_data;
struct sinowealth_button_report *buf = &drv_data->buttons;
struct ratbag_button *button;
int rc;
buf->report_id = 0x04;
buf->command_id = 0x12;
buf->unknown1[1] = 0x50;
ratbag_profile_for_each_button(profile, button) {
struct sinowealth_button_data *button_data;
struct ratbag_button_action *action = &button->action;
button_data = &buf->buttons[button->index];
if (action->type == RATBAG_BUTTON_ACTION_TYPE_BUTTON) {
button_data->type = SINOWEALTH_BUTTON_TYPE_BUTTON;
button_data->data[0] = sinowealth_button_action_to_raw(action);
} else if (action->type == RATBAG_BUTTON_ACTION_TYPE_MACRO) {
unsigned int key, modifiers;
rc = ratbag_action_keycode_from_macro(action,
&key,
&modifiers);
if (rc < 0) {
log_error(device->ratbag,
"Error while writing macro for button %d\n",
button->index);
}
button_data->type = SINOWEALTH_BUTTON_TYPE_KEY;
button_data->data[0] = modifiers;
button_data->data[1] = ratbag_hidraw_get_keyboard_usage_from_keycode(device, key);
}
}
rc = ratbag_hidraw_set_feature_report(device, SINOWEALTH_REPORT_ID_CONFIG,
(uint8_t*) buf, SINOWEALTH_CONFIG_SIZE);
if (rc != SINOWEALTH_CONFIG_SIZE) {
log_error(device->ratbag, "Error while writing buttons: %d\n", rc);
return -1;
}
return 0;
}
static void
sinowealth_init_profile(struct ratbag_device *device)
{
struct ratbag_profile *profile;
struct ratbag_resolution *resolution;
struct ratbag_led *led;
struct sinowealth_data *drv_data = device->drv_data;
struct dpi_range *dpirange = NULL;
int button_count, led_count;
const char *led_type;
/* number of DPIs = all DPIs from min to max (inclusive) and "0 DPI" as a special value
* to signal a disabled DPI step.
*/
int num_dpis = (SINOWEALTH_DPI_MAX - SINOWEALTH_DPI_MIN) / SINOWEALTH_DPI_STEP + 2;
unsigned int dpis[num_dpis];
dpirange = ratbag_device_data_sinowealth_get_dpi_range(device->data);
button_count = ratbag_device_data_sinowealth_get_button_count(device->data);
led_count = ratbag_device_data_sinowealth_get_led_count(device->data);
led_type = ratbag_device_data_sinowealth_get_led_type(device->data);
if (!dpirange)
{
log_error(device->ratbag, "DpiRange must be defined in .device file\n");
}
if (button_count < 0)
{
log_error(device->ratbag, "Button count must be defined in .device file\n");
button_count = 0;
}
if (led_count < 0)
{
log_error(device->ratbag, "Led count must be defined in .device file\n");
led_count = 0;
}
if (!led_type)
log_error(device->ratbag, "Led type must be defined in .device file. Defaulting to RGB.\n");
if (dpirange->max == 12000)
drv_data->sensor = PWM3360;
else
drv_data->sensor = PWM3389;
if (led_type && streq(led_type, "RBG"))
drv_data->led_type = LED_RBG;
else
drv_data->led_type = LED_RGB;
ratbag_device_init_profiles(device, 1, SINOWEALTH_NUM_DPIS, button_count, led_count);
/* TODO: Button remapping */
ratbag_device_init_profiles(device, 1, SINOWEALTH_NUM_DPIS, 0, 1);
profile = ratbag_device_get_profile(device, 0);
/* Generate DPI list */
dpis[0] = 0; /* 0 DPI = disabled */
for (int i = 1; i < num_dpis; i++) {
dpis[i] = SINOWEALTH_DPI_MIN + (i - 1) * SINOWEALTH_DPI_STEP;
}
ratbag_profile_for_each_resolution(profile, resolution) {
ratbag_resolution_set_dpi_list_from_range(resolution,
dpirange->min,
dpirange->max);
ratbag_resolution_set_dpi_list(resolution, dpis, num_dpis);
ratbag_resolution_set_cap(resolution, RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
}
/* Set up LED capabilities */
ratbag_profile_for_each_led(profile, led) {
if (led->index == 0) {
led->type = RATBAG_LED_TYPE_SIDE;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
} else {
led->type = RATBAG_LED_TYPE_DPI;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
}
}
led = ratbag_profile_get_led(profile, 0);
led->type = RATBAG_LED_TYPE_SIDE;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
ratbag_led_unref(led);
ratbag_profile_unref(profile);
}
@@ -638,12 +399,6 @@ sinowealth_probe(struct ratbag_device *device)
goto err;
}
rc = sinowealth_read_buttons(profile);
if (rc) {
rc = -ENODEV;
goto err;
}
return 0;
err:
@@ -677,10 +432,10 @@ sinowealth_commit(struct ratbag_device *device)
if (!resolution->dpi_x || !resolution->dpi_y)
continue;
if (config->config & SINOWEALTH_XY_INDEPENDENT) {
config->dpi[resolution->index * 2] = sinowealth_dpi_to_raw(device, resolution->dpi_x);
config->dpi[resolution->index * 2 + 1] = sinowealth_dpi_to_raw(device, resolution->dpi_y);
config->dpi[resolution->index * 2] = sinowealth_dpi_to_raw(resolution->dpi_x);
config->dpi[resolution->index * 2 + 1] = sinowealth_dpi_to_raw(resolution->dpi_y);
} else {
config->dpi[resolution->index] = sinowealth_dpi_to_raw(device, resolution->dpi_x);
config->dpi[resolution->index] = sinowealth_dpi_to_raw(resolution->dpi_x);
}
dpi_enabled |= 1<<resolution->index;
config->dpi_count++;
@@ -695,17 +450,14 @@ sinowealth_commit(struct ratbag_device *device)
break;
case RATBAG_LED_ON:
config->rgb_effect = RGB_SINGLE;
if (drv_data->led_type == LED_RGB)
config->single_color = sinowealth_color_to_raw(led->color);
else
config->single_color = sinowealth_color_to_rbg(led->color);
config->single_color = sinowealth_color_to_rbg(led->color);
break;
case RATBAG_LED_CYCLE:
config->rgb_effect = RGB_GLORIOUS;
config->glorious_mode = sinowealth_led_to_rgb_mode(led);
break;
case RATBAG_LED_BREATHING:
config->rgb_effect = RGB_BREATHING7;
config->rgb_effect = RGB_BREATHING1;
config->breathing1_color = sinowealth_color_to_rbg(led->color);
config->breathing1_mode = sinowealth_led_to_rgb_mode(led);
break;
@@ -722,10 +474,8 @@ sinowealth_commit(struct ratbag_device *device)
return -1;
}
rc = sinowealth_write_buttons(profile);
ratbag_profile_unref(profile);
return rc;
return 0;
}
static void

View File

@@ -34,6 +34,7 @@
#include "libratbag-private.h"
#include "libratbag-data.h"
#include "hidpp20.h"
#include "usb-ids.h"
#define GROUP_DEVICE "Device"
@@ -53,6 +54,7 @@ enum driver {
STEELSERIES,
SINOWEALTH,
SINOWEALTH_NUBWO,
OPENINPUT,
};
struct data_hidpp20 {
@@ -82,13 +84,6 @@ struct data_steelseries {
int short_button;
};
struct data_sinowealth {
int button_count;
int led_count;
char *led_type;
struct dpi_range *dpi_range;
};
struct ratbag_device_data {
int refcount;
char *name;
@@ -100,7 +95,6 @@ struct ratbag_device_data {
struct data_hidpp20 hidpp20;
struct data_hidpp10 hidpp10;
struct data_steelseries steelseries;
struct data_sinowealth sinowealth;
};
enum ratbag_led_type led_types[20];
@@ -262,50 +256,6 @@ init_data_steelseries(struct ratbag *ratbag,
g_error_free(error);
}
static void
init_data_sinowealth(struct ratbag *ratbag,
GKeyFile *keyfile,
struct ratbag_device_data *data)
{
const char *group = "Driver/sinowealth";
GError *error = NULL;
_cleanup_(freep) char *str = NULL;
int num;
char *led_type;
data->sinowealth.button_count = -1;
data->sinowealth.led_count = -1;
num = g_key_file_get_integer(keyfile, group, "Buttons", &error);
if (num != 0 || !error)
data->sinowealth.button_count = num;
if (error)
g_error_free(error);
error = NULL;
num = g_key_file_get_integer(keyfile, group, "Leds", &error);
if (num > 0 || !error)
data->sinowealth.led_count = num;
if (error)
g_error_free(error);
error = NULL;
led_type = g_key_file_get_string(keyfile, group, "LedType", NULL);
if (led_type)
data->sinowealth.led_type = led_type;
if (error)
g_error_free(error);
error = NULL;
str = g_key_file_get_string(keyfile, group, "DpiRange", NULL);
if (str)
data->sinowealth.dpi_range = dpi_range_from_string(str);
error = NULL;
}
static const struct driver_map {
enum driver map;
const char *driver;
@@ -321,8 +271,9 @@ static const struct driver_map {
{ LOGITECH_G300, "logitech_g300", NULL},
{ LOGITECH_G600, "logitech_g600", NULL},
{ STEELSERIES, "steelseries", init_data_steelseries },
{ SINOWEALTH, "sinowealth", init_data_sinowealth },
{ SINOWEALTH, "sinowealth", NULL },
{ SINOWEALTH_NUBWO, "sinowealth_nubwo", NULL},
{ OPENINPUT, "openinput", NULL },
};
const char *
@@ -569,7 +520,9 @@ ratbag_device_data_new_for_id(struct ratbag *ratbag, const struct input_id *id)
goto out;
}
if (!data)
if (id->vendor == USB_VENDOR_ID_LOGITECH && (id->product & 0xff00) == 0xc500)
log_debug(ratbag, "%04x:%04x is a Logitech receiver, not a device. Ignoring...\n", id->vendor, id->product);
else if (!data)
log_debug(ratbag, "No data file found for %04x:%04x\n", id->vendor, id->product);
out:
@@ -722,35 +675,3 @@ ratbag_device_data_steelseries_get_short_button(const struct ratbag_device_data
return data->steelseries.short_button;
}
int
ratbag_device_data_sinowealth_get_button_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == SINOWEALTH);
return data->sinowealth.button_count;
}
int
ratbag_device_data_sinowealth_get_led_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == SINOWEALTH);
return data->sinowealth.led_count;
}
struct dpi_range *
ratbag_device_data_sinowealth_get_dpi_range(const struct ratbag_device_data *data)
{
assert(data->drivertype == SINOWEALTH);
return data->sinowealth.dpi_range;
}
const char *
ratbag_device_data_sinowealth_get_led_type(const struct ratbag_device_data *data)
{
assert(data->drivertype == SINOWEALTH);
return data->sinowealth.led_type;
}

View File

@@ -120,22 +120,3 @@ ratbag_device_data_steelseries_get_mono_led(const struct ratbag_device_data *dat
int
ratbag_device_data_steelseries_get_short_button(const struct ratbag_device_data *data);
/**
* @return The button count or -1 if not set
*/
int
ratbag_device_data_sinowealth_get_button_count(const struct ratbag_device_data *data);
/**
* @return The led count or -1 if not set
*/
int
ratbag_device_data_sinowealth_get_led_count(const struct ratbag_device_data *data);
struct dpi_range *
ratbag_device_data_sinowealth_get_dpi_range(const struct ratbag_device_data *data);
const char *
ratbag_device_data_sinowealth_get_led_type(const struct ratbag_device_data *data);

View File

@@ -597,6 +597,7 @@ extern struct ratbag_driver gskill_driver;
extern struct ratbag_driver steelseries_driver;
extern struct ratbag_driver sinowealth_driver;
extern struct ratbag_driver sinowealth_nubwo_driver;
extern struct ratbag_driver openinput_driver;
struct ratbag_device*
ratbag_device_new(struct ratbag *ratbag, struct udev_device *udev_device,

View File

@@ -604,6 +604,7 @@ ratbag_create_context(const struct ratbag_interface *interface,
ratbag_register_driver(ratbag, &steelseries_driver);
ratbag_register_driver(ratbag, &sinowealth_driver);
ratbag_register_driver(ratbag, &sinowealth_nubwo_driver);
ratbag_register_driver(ratbag, &openinput_driver);
return ratbag;
}