Compare commits

..

48 Commits
v0.14 ... dm5

Author SHA1 Message Date
Thomas H.P. Andersen
e28a6a92d6 sinowealth: add definition for repeated clicks 2020-10-05 01:49:34 +02:00
Thomas H.P. Andersen
381de107be sinowealth: add .device option for led RGB vs RBG 2020-10-05 01:49:34 +02:00
Marian Beermann
f474776a4d sinowealth: working button remapping 2020-10-05 01:40:36 +02:00
Marian Beermann
fd99aa29a7 sinowealth: SINOWEALTH_BUTTON_TPYE_SWITCH_DPI 2020-10-05 01:40:36 +02:00
Marian Beermann
53bc81f6fe sinowealth: use macro action with keycode_from_macro() 2020-10-05 01:40:36 +02:00
Marian Beermann
ff215126b2 sinowealth: fix button_data being discarded 2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
78793296a2 WIP: sinowealth: button write support
Seems like it should work - but it does not...
2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
a6cb7f8301 sinowealth: revamp button reading 2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
9cb444bb07 sinowealth: use enum for sensor 2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
121a843071 WIP: sinowealth: read button mapping 2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
62a620c056 sinowealth: use commonly available breathing mode
The DM5 does not have the RGB_BREATHING1 mode, but
does have the RGB_BREATHING7 mode. This changes the
driver to map to the commonly available mode.
2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
9d30ec5437 sinowealth: read device-specific from .device file
This moves device-specific specifications to the
.device files. The number of LEDs and buttons varies
as well as sensor (dpi specs)
2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
e570e29a1e sinowealth: parse led color as RGB
This was parsed as RBG but this is not the layout used on the
DM5. The RBG-parsing seems intentional, so this might need to
flip on either device or firmware version.
2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
1f79d8e6e2 add device file for Dream Machines DM5 Blink 2020-10-05 01:40:36 +02:00
Thomas H.P. Andersen
e753db72f9 meson: bump required meson version
Bump the required meson version to match the features that we make
use of.

We now also make use of the 'copy:' keyword in configure_file that
is available in version 0.47.
2020-10-05 00:39:43 +01:00
Thomas H.P. Andersen
e1f4fac9a2 ratbagd-json: fix compilation with clang
Compilation on clang failed as we would try to jump to
the 'out' label and then jump over the initialization
of a variable with __attribute__((cleanup)).

We move the initaliztion up to make sure it happens before
the goto.
2020-09-29 02:11:09 +01:00
Thomas H.P. Andersen
8f981c8b87 circleci: bump to fedora 32
Bump to fedora 32 to updated meson version
2020-09-29 02:08:17 +01:00
Thomas H.P. Andersen
01d2cf8477 sinowealth: silence warning 2020-09-29 01:07:12 +02:00
Filipe Laíns
f7c22ed88c libratbag 0.15
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-09-25 21:38:01 +01:00
Thomas H.P. Andersen
aeda65cbfa sinowealth: remove per-resolution led
The glorious mice uses a single profile. To differentiate between
resolutions each one has customizable color. A led on the bottom
of the mouse shows the active resolution color.

Libratbag does not support a per-resolution color.

To allow adjusting these colors the sinowealth driver defines a led
for each resolution. This is a misuse of the model and will cause
problems when the mouse is added to Piper.

This patch removes the per-resolution leds and hardcodes the led
count to one.
2020-09-25 20:51:45 +01:00
Pipat Saengow
ed2439e5e2 sinowealthnubwo: more reasonable default and refactor 2020-09-21 17:39:28 +02:00
Pipat Saengow
f407521e8f Add driver for Nubwo X7 Spectrum
Nubwo X7 Spectrum is a sinowealth-based gaming mouse.
The sinowealth-nubwo driver included in this commit provides

- LED support (except color mode that are not available in libratbag)
- DPI and reporting support
- NO button mapping/macros support
2020-09-21 17:39:28 +02:00
heimer16
3c4fe23f70 data: update Logitech g9x (#913)
* add LED for Logitech g9x
* update min dpi range and step for Logitech g9x

Co-authored-by: Erin Heimer <heimer16@gmail.com>
2020-09-11 22:28:57 +02:00
Mike Crowe
9e2ac55dbc libratbag-private.h: Ensure that _GNU_SOURCE is defined early enough
When compiling on Ubuntu 20.04 with GCC 9.3.0 and glibc 2.31, I get:

In file included from ../src/libratbag-private.h:32,
                 from ../src/driver-sinowealth.c:24:
../src/libratbag-util.h: In function ‘asprintf_safe’:
../src/libratbag-util.h:154:7: warning: implicit declaration of function ‘vasprintf’; did you mean ‘vsprintf’? [-Wimplicit-function-declaration]
  154 |  rc = vasprintf(&result, fmt, args);
      |       ^~~~~~~~~
      |       vsprintf

This appears to be because <features.h> is being included by another
header before config.h has been included. If _GNU_SOURCE has not been
defined at that point, then the other macros that control whether
vasprintf is declared are not set correctly.

Include config.h right at the top of libratbag-private.h to ensure
that _GNU_SOURCE is defined before any system headers are included to
avoid this problem.
2020-08-30 19:16:51 +02:00
Nguyễn Chính Hữu
1e72ebaa7b data: add the G203 name to the .device file 2020-08-29 22:23:35 +01:00
peelz
920bc2b993 driver-hidpp10: fix segfault when committing unnamed profiles 2020-08-23 16:21:05 +01:00
TurkeyTheGobbler
20b573668e g300: use the correct step size 2020-07-25 17:26:37 +01:00
Daniel Lehmann
49acc11db3 Fix enumeration of Logitech G5 (2007)
Before this patch, ratbagd would abort initialization of the device because
no profiles are returned.

Once that was handled more gracefully, ratbagd would fail because it doesn't
read the DpiList from its own config file, as no ProfileType is specified.
Specifying G500 as the closest match.
2020-07-23 01:04:14 +01:00
Stephan Lachnit
364e3fd965 data: add additional USB-ID for the M720 Thriatlon
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-07-23 01:00:33 +01:00
Marian Beermann
bb230d390d sinowealth: fix reading RGB brightness from device 2020-07-23 00:45:29 +01:00
Marian Beermann
93a076bed9 sinowealth: code style changes 2020-07-23 00:45:29 +01:00
Marian Beermann
e7b50c9061 sinowealth: rewrite RGB LED mode conversions 2020-07-23 00:45:29 +01:00
Marian Beermann
9d533bdc3e sinowealth: fix dpi_count not being updated
The firmware of these mice only considers the first dpi_count zero bits
in dpi_enabled.
2020-07-23 00:45:29 +01:00
Marian Beermann
d7906b54b4 sinowealth: code cleanup and style changes 2020-07-23 00:45:29 +01:00
Marian Beermann
f1eba370ed sinowealth: do not typedef structs 2020-07-23 00:45:29 +01:00
Marian Beermann
7b5ab2ed99 sinowealth: separate capability setting logic from read_profile 2020-07-23 00:45:29 +01:00
Marian Beermann
d2d5471836 sinowealth: implement brightness and speed controls 2020-07-23 00:45:29 +01:00
Marian Beermann
9cfc1e98e3 sinowealth: fixed rgb
Turns out it was caused by two mistakes:
1.) While writing the struct I overlooked a changed offset in my notes,
    so a bunch of padding was missing in the middle of the struct (unk4[33]);
    that padding is probably the set of colors (11) used by tail mode
2.) The LED bodies actually use RBG ordering instead of RGB
2020-07-23 00:45:29 +01:00
Marian Beermann
691661276f pad struct sinowealth_config_report to full size of report 2020-07-23 00:45:29 +01:00
Marian Beermann
25ed99db84 Add sinowealth driver 2020-07-23 00:45:29 +01:00
Dejan
02226d0428 data: add another device ID for G203 mouse 2020-07-09 21:54:56 +01:00
Stephan Lachnit
f822fb0fa7 remove unused string format to fix flake8 (#1024)
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-07-06 11:48:47 +02:00
Stephan Lachnit
b88be0add0 ci: bump Ubuntu to 20.04
This also removes the manual addition of the universe repository, since it's already enabled in the circleci image.
Because of this, software-properties-common doesn't need to be installed anymore, and libnss-systemd isn't going to be installed (see c425780220).
However, DEBIAN_FRONTEND=noninteractive needs to be set, else the image will loop while trying to configure tzdata (aka the timezome).

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-06-09 10:55:31 +10:00
Stephan Lachnit
9fc6d12486 data: add additional USB-ID for Roccat Kone Pure
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-06-07 20:58:41 +02:00
Stephan Lachnit
0c64dad9e1 fix wrong driver id for Roccat Kone Pure
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-06-07 20:58:41 +02:00
Farid Gh
f61485c603 data: add bluetooth ID to the MX Vertical 2020-05-29 13:57:21 +01:00
Filipe Laíns
8c295479d8 hidpp20: keep unsupported button mappings
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-05-25 18:08:40 +01:00
Dylan Baker
e55b04ec7d meson: Fix python3 version detection with python3 module
I misread the documentation about the python3 module. The newer python
module puts the language_version in the object returned by
`pymod.find_installation()`, while the python3 module has the same
method in the *module*.

Fixes #985
2020-05-19 19:47:24 +01:00
25 changed files with 1259 additions and 37 deletions

View File

@@ -156,10 +156,7 @@ ubuntu_settings: &ubuntu_settings
- run:
name: Install prerequisites
command: |
apt-get update
apt-get install -y software-properties-common
apt-get remove -y libnss-systemd
add-apt-repository universe
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y git ${UBUNTU_DEP_BUILD} ${UBUNTU_DEP_TEST}
- checkout
@@ -234,35 +231,35 @@ docs_deploy: &docs_deploy
version: 2
jobs:
fedora_31:
fedora_32:
<<: *fedora_settings
docker:
- image: fedora:31
- image: fedora:32
arch:
<<: *arch_settings
docker:
- image: archlinux
ubuntu_19_10:
ubuntu_20_04:
<<: *ubuntu_settings
docker:
- image: ubuntu:19.10
- image: ubuntu:20.04
doc_build:
<<: *doc_build
docker:
- image: fedora:31
- image: fedora:32
doc_deploy:
<<: *docs_deploy
docker:
- image: fedora:31
- image: fedora:32
workflows:
version: 2
compile_and_test:
jobs:
- ubuntu_19_10
- ubuntu_20_04
- arch
- fedora_31
- fedora_32
- doc_build
- doc_deploy:
requires:

View File

@@ -0,0 +1,10 @@
[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

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

View File

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

View File

@@ -1,4 +1,4 @@
[Device]
Name=Logitech M720
DeviceMatch=usb:046d:405e
DeviceMatch=usb:046d:405e;bluetooth:046d:b015
Driver=hidpp20

View File

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

View File

@@ -1,6 +1,6 @@
# G102 and G203 (USB)
# G102, G103 and G203 (USB)
[Device]
Name=Logitech Gaming Mouse G102/G103
DeviceMatch=usb:046d:c084
Name=Logitech Gaming Mouse G102/G103/G203
DeviceMatch=usb:046d:c084;usb:046d:c092
Driver=hidpp20
LedTypes=logo;side;

View File

@@ -7,3 +7,4 @@ Driver=hidpp10
[Driver/hidpp10]
DpiList=400;800;1600;2000
Profiles=1
ProfileType=G500

View File

@@ -4,8 +4,10 @@
Name=G9x [Original]
DeviceMatch=usb:046d:c066
Driver=hidpp10
LedTypes=dpi
[Driver/hidpp10]
DpiRange=0:5700@23.53
DpiRange=200:5700@50
ProfileType=G500
Profiles=5
Leds=1

View File

@@ -0,0 +1,5 @@
[Device]
Name=Nubwo x7 spectrum
DeviceMatch=usb:258a:0012
Driver=sinowealth_nubwo

View File

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

View File

@@ -1,8 +1,8 @@
project('libratbag', 'c',
version : '0.14',
version : '0.15',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
meson_version : '>= 0.40.0')
meson_version : '>= 0.53.0')
# The DBus API version. Increase this every time the DBus API changes.
# No backwards/forwards guarantee, clients are expected to understand
@@ -175,6 +175,8 @@ src_libratbag = [
'src/driver-roccat-kone-pure.c',
'src/driver-gskill.c',
'src/driver-steelseries.c',
'src/driver-sinowealth.c',
'src/driver-sinowealth-nubwo.c',
'src/driver-test.c',
'src/libratbag.c',
'src/libratbag.h',
@@ -346,6 +348,10 @@ data_files = files(
'data/devices/steelseries-rival.device',
'data/devices/steelseries-sensei-310.device',
'data/devices/steelseries-sensei-raw.device',
'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,
@@ -451,10 +457,9 @@ 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',
configuration : config_ratbagd_devel)
copy : true)
# This is a hack. We always install the devel policy file into
# /etc/dbus-1/system.d, independent of any prefixes we use otherwise.
@@ -545,16 +550,17 @@ dep_python3 = dependency('', required : false)
if meson.version().version_compare('<0.48.0')
python = import('python3')
py3 = python.find_python()
py_version = python.language_version()
else
pymod = import('python')
py3 = pymod.find_installation()
if meson.version().version_compare('>= 0.53.0')
dep_python3 = py3.dependency(embed : true)
endif
py_version = py3.language_version()
endif
# From python 3.8 we neeed python3-embed
py_version = py3.language_version()
embed = py_version.version_compare('>= 3.8') ? '-embed' : ''
if not dep_python3.found()
dep_python3 = dependency('python-@0@@1@'.format(py_version, embed), required : false)

View File

@@ -515,6 +515,7 @@ int ratbagd_parse_json(const char *data, struct ratbag_test_device *device)
JsonArray *arr;
int r = -EINVAL;
g_autoptr(GError) error = NULL;
g_autoptr(GList) list = NULL;
error = 0;
parser = json_parser_new();
@@ -540,7 +541,7 @@ int ratbagd_parse_json(const char *data, struct ratbag_test_device *device)
num_buttons = device->num_buttons;
num_leds = device->num_leds;
g_autoptr(GList) list = json_array_get_elements(arr);
list = json_array_get_elements(arr);
GList *l = list;
int idx = 0;
while (l != NULL) {

View File

@@ -573,7 +573,8 @@ hidpp10drv_commit(struct ratbag_device *device)
return rc;
p.enabled = profile->is_enabled;
strncpy_safe((char*)p.name, profile->name, 24);
if (profile->name != NULL)
strncpy_safe((char*)p.name, profile->name, 24);
ratbag_profile_for_each_resolution(profile, resolution) {
p.dpi_modes[resolution->index].xres = resolution->dpi_x;

View File

@@ -37,7 +37,7 @@
#define LOGITECH_G300_BUTTON_MAX 8
#define LOGITECH_G300_NUM_DPI 4
#define LOGITECH_G300_NUM_LED 1
#define LOGITECH_G300_DPI_MIN 250
#define LOGITECH_G300_DPI_MIN 100
#define LOGITECH_G300_DPI_MAX 2500
#define LOGITECH_G300_REPORT_ID_GET_ACTIVE 0xF0
@@ -51,7 +51,7 @@
#define LOGITECH_G300_REPORT_SIZE_PROFILE 35
struct logitech_g300_resolution {
uint8_t dpi :7; /* Range 1-10. dpi = 250*value */
uint8_t dpi :7; /* Range 1-10. dpi = 100*value */
uint8_t is_default :1;
} __attribute__((packed));
@@ -449,11 +449,10 @@ logitech_g300_read_profile(struct ratbag_profile *profile)
ratbag_profile_for_each_resolution(profile, resolution) {
struct logitech_g300_resolution *res =
&report->dpi_levels[resolution->index];
unsigned int dpis[10] = { 250, 500, 750, 1000, 1250, 1500, 1750,
2000, 2250, 2500 };
unsigned int dpis[49] = { 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 1600, 1650, 1700, 1750, 1800, 1850, 1900, 1950, 2000, 2050, 2100, 2150, 2200, 2250, 2300, 2350, 2400, 2450, 2500, };
resolution->dpi_x = res->dpi * 250;
resolution->dpi_y = res->dpi * 250;
resolution->dpi_x = res->dpi * 50;
resolution->dpi_y = res->dpi * 50;
resolution->is_default = res->is_default;
resolution->is_active = res->is_default;
@@ -546,7 +545,7 @@ logitech_g300_write_profile(struct ratbag_profile *profile)
struct logitech_g300_resolution *res =
&report->dpi_levels[resolution->index];
res->dpi = resolution->dpi_x / 250;
res->dpi = resolution->dpi_x / 50;
res->is_default = resolution->is_default;
if (profile->is_active && resolution->is_active)

View File

@@ -835,7 +835,7 @@ roccat_remove(struct ratbag_device *device)
struct ratbag_driver roccat_driver = {
.name = "Roccat Kone Pure",
.id = "roccat",
.id = "roccat-kone-pure",
.probe = roccat_probe,
.remove = roccat_remove,
.write_profile = roccat_write_profile,

View File

@@ -0,0 +1,319 @@
/*
* Copyright © 2020 Pipat Saengow
*
* 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 "libratbag-enums.h"
#include "libratbag-hidraw.h"
#include "libratbag-private.h"
#include "libratbag-util.h"
#include "shared-macro.h"
#include <stdint.h>
#include <string.h>
#define SINOWEALTHNUBWO_PERF_CMD_REPORTID 0x02
#define SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID 0x03
#define SINOWEALTHNUBWO_GET_FIRMWARE_CMD_REPORTID 0x04
#define SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE 256
#define SINOWEALTHNUBWO_GET_FIRMWARE_MSGOFFSET 48
#define SINOWEALTHNUBWO_PERF_CMD_MSGSIZE 16
// Actually more but I only implemented one
#define SINOWEALTHNUBWO_NUM_PROFILES 1
#define SINOWEALTHNUBWO_NUM_RESOLUTIONS 1
// Actually 8 but I am not going to implement macros.
#define SINOWEALTHNUBWO_NUM_BUTTONS 0
#define SINOWEALTHNUBWO_NUM_LEDS 1
//Magic set_feature that must be called before requesting firmware string
static uint8_t PREFIRMWARE_QUERY_MSG[] = {0x02, 0x01, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static unsigned int REPORT_RATES[] = { 125, 250, 333, 500, 1000 };
static uint8_t REPORT_RATES_ENCODED[] = {0x08, 0x04, 0x03, 0x02, 0x01};
static uint8_t REPORT_RATES_CMD[] = {0x02, 0x06, 0xbb, 0xaa, 0x28, 0x00, 0x01, 0x00};
static unsigned int DPILIST[] = { 1000, 2000, 3000, 5000, 15000};
static uint8_t DPI_ENCODED[] = {0x04, 0x03, 0x02, 0x01, 0x00};
static uint8_t DPI_CMD[] = { 0x02, 0x06, 0xbb, 0xaa, 0x32, 0x00, 0x01, 0x00 };
enum
sinowealthnubwo_color_mode {
SINOWEALTHNUBWO_COLOR_OFF = 0x00,
SINOWEALTHNUBWO_COLOR_ON = 0x01,
SINOWEALTHNUBWO_COLOR_BREATHING = 0x02,
SINOWEALTHNUBWO_COLOR_COLOR_SHIFT = 0x03,
SINOWEALTHNUBWO_COLOR_SPECTRUM = 0x04,
SINOWEALTHNUBWO_COLOR_MARQUEE = 0x05,
};
struct
sinowealthnubwo_aesthetic_report {
uint8_t report_id; //0x03
uint8_t cmd[7];
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t color_mode;
uint8_t padzero; // 0x00
uint8_t brightness; // 0x01 to 0x03
uint8_t tempo; // 0x05 0x03 0x01
uint8_t padzero2[16*3+1];
} __packed__;
static uint8_t AESTHETIC_CMD[] = {0x06, 0xbb, 0xaa, 0x2a, 0x00, 0x0a, 0x00};
static int
sinowealthnubwo_test_hidraw(struct ratbag_device *device)
{
return
ratbag_hidraw_has_report(device, SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID)
&& ratbag_hidraw_has_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID)
&& ratbag_hidraw_has_report(device, SINOWEALTHNUBWO_GET_FIRMWARE_CMD_REPORTID);
}
static int
sinowealth_get_firmware_string(struct ratbag_device *device, char **output)
{
uint8_t buff[SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE + 1] = {0}; //Purposefully overalloacate to prevent buffer overrun
int size;
size = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID, PREFIRMWARE_QUERY_MSG, ARRAY_LENGTH(PREFIRMWARE_QUERY_MSG));
if (size < 0) {
log_error(device->ratbag, "Error while sending pre-firmware request message: %d\n", size);
return size;
}
size = ratbag_hidraw_get_feature_report(device, SINOWEALTHNUBWO_GET_FIRMWARE_CMD_REPORTID, buff, SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE);
if (size != SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE) {
log_error(device->ratbag ,"Firmware report reply size mismatch expected %d got %d\n", SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE, size);
return size;
}
*output = strdup_ascii_only((char *) buff + SINOWEALTHNUBWO_GET_FIRMWARE_MSGOFFSET);
return 0;
}
static int
sinowealthnubwo_probe(struct ratbag_device *device)
{
int error;
struct ratbag_profile *profile;
struct ratbag_resolution *resolution;
struct ratbag_led *led;
error = ratbag_find_hidraw(device, sinowealthnubwo_test_hidraw);
if (error)
return error;
char *fwstr;
error = sinowealth_get_firmware_string(device, &fwstr);
if (error)
return error;
log_info(device->ratbag, "Firmware: %s\n", fwstr);
free(fwstr);
ratbag_device_init_profiles(device,
SINOWEALTHNUBWO_NUM_PROFILES,
SINOWEALTHNUBWO_NUM_RESOLUTIONS,
SINOWEALTHNUBWO_NUM_BUTTONS,
SINOWEALTHNUBWO_NUM_LEDS);
ratbag_device_for_each_profile(device, profile) {
profile->is_active = true;
ratbag_profile_set_cap(profile, RATBAG_PROFILE_CAP_WRITE_ONLY);
ratbag_profile_set_report_rate_list(profile, REPORT_RATES, ARRAY_LENGTH(REPORT_RATES));
ratbag_profile_for_each_resolution(profile, resolution) {
ratbag_resolution_set_dpi_list(resolution, DPILIST, ARRAY_LENGTH(DPILIST));
resolution->dpi_x = resolution->dpi_y = DPILIST[ARRAY_LENGTH(DPILIST)-1];
resolution->is_active = true;
resolution->is_default = true;
}
ratbag_profile_for_each_led(profile, led) {
led->mode = RATBAG_LED_OFF;
led->color.red = led->color.green = led->color.blue = 0xFF;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
led->type = RATBAG_LED_TYPE_SIDE;
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_BREATHING);
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
//Actually more
}
}
return 0;
}
static uint8_t
encode_dpi(unsigned int dpi)
{
for (size_t i = 0; i < ARRAY_LENGTH(DPILIST); i++)
if (DPILIST[i] == dpi)
return DPI_ENCODED[i];
return DPI_ENCODED[0];
}
static uint8_t
encode_report_rate(unsigned int reportrate)
{
for (size_t i = 0; i < ARRAY_LENGTH(REPORT_RATES); i++)
if (REPORT_RATES[i] == reportrate)
return REPORT_RATES_ENCODED[i];
return REPORT_RATES_ENCODED[0];
}
static int
sinowealthnubwo_set_dpi(struct ratbag_device *device, int dpi)
{
uint8_t buf[SINOWEALTHNUBWO_PERF_CMD_MSGSIZE] = {0};
memcpy(buf, DPI_CMD, ARRAY_LENGTH(DPI_CMD));
buf[ARRAY_LENGTH(DPI_CMD)] = encode_dpi(dpi);
int error = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID, buf, SINOWEALTHNUBWO_PERF_CMD_MSGSIZE);
if (error < 0)
return error;
return 0;
}
static int
sinowealthnubwo_set_report_rate(struct ratbag_device *device, int reportrate)
{
uint8_t buf[SINOWEALTHNUBWO_PERF_CMD_MSGSIZE] = {0};
memcpy(buf, REPORT_RATES_CMD, ARRAY_LENGTH(REPORT_RATES_CMD));
buf[ARRAY_LENGTH(REPORT_RATES_CMD)] = encode_report_rate(reportrate);
int error = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID, buf, SINOWEALTHNUBWO_PERF_CMD_MSGSIZE);
if (error < 0)
return error;
return 0;
}
static enum sinowealthnubwo_color_mode encode_color(enum ratbag_led_mode mode)
{
switch (mode) {
case RATBAG_LED_OFF:
return SINOWEALTHNUBWO_COLOR_OFF;
case RATBAG_LED_ON:
return SINOWEALTHNUBWO_COLOR_ON;
case RATBAG_LED_CYCLE:
return SINOWEALTHNUBWO_COLOR_MARQUEE;
case RATBAG_LED_BREATHING:
return SINOWEALTHNUBWO_COLOR_BREATHING;
default:
return SINOWEALTHNUBWO_COLOR_OFF;
}
}
static uint8_t normalize_duration(int duration)
{
const int MAX_DURATION = 10000;
const uint8_t avail_dura[] = {0x01, 0x03, 0x05};
const int selected = (duration * ((int) ARRAY_LENGTH(avail_dura)) - 1) / MAX_DURATION;
return avail_dura[selected];
}
static uint8_t normalize_brightness(int brightness)
{
const int MAX_BRIGHTNESS = 255;
return 1 + (brightness * 3 - 1) / MAX_BRIGHTNESS;
}
static int
sinowealthnubwo_set_aesthetic(struct ratbag_device *device, struct ratbag_led *led) {
struct sinowealthnubwo_aesthetic_report report;
memset(&report, 0, sizeof(struct sinowealthnubwo_aesthetic_report));
report.report_id = SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID;
memcpy(report.cmd, AESTHETIC_CMD, ARRAY_LENGTH(AESTHETIC_CMD));
report.r = led->color.red;
report.g = led->color.green;
report.b = led->color.blue;
report.color_mode = encode_color(led->mode);
report.tempo = normalize_duration(led->ms);
report.brightness = normalize_brightness(led->brightness);
int size = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID, (uint8_t *) &report, sizeof(report));
if (size < 0)
return size;
return 0;
}
static int
sinowealthnubwo_write_profile(struct ratbag_device *device, struct ratbag_profile *profile)
{
struct ratbag_resolution *resolution;
struct ratbag_led *led;
int error;
log_debug(device->ratbag, "Writing updates\n");
log_debug(device->ratbag, "Setting report rate\n");
error = sinowealthnubwo_set_report_rate(device, profile->hz);
if (error)
return error;
ratbag_profile_for_each_resolution(profile, resolution) {
if (!resolution->dirty)
continue;
log_debug(device->ratbag, "Setting DPI\n");
error = sinowealthnubwo_set_dpi(device, resolution->dpi_x);
if (error)
return error;
}
ratbag_profile_for_each_led(profile, led) {
if (!led->dirty)
continue;
log_debug(device->ratbag, "Setting aesthetic\n");
error = sinowealthnubwo_set_aesthetic(device, led);
if (error)
return error;
}
return 0;
}
static int
sinowealthnubwo_commit(struct ratbag_device *device)
{
struct ratbag_profile *profile;
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty) continue;
int error = sinowealthnubwo_write_profile(device, profile);
if (error)
return error;
}
return 0;
}
static void
sinowealthnubwo_remove(struct ratbag_device *device)
{
ratbag_close_hidraw(device);
}
struct ratbag_driver sinowealth_nubwo_driver = {
.name = "Sinowealth Nubwo",
.id = "sinowealth_nubwo",
.probe = sinowealthnubwo_probe,
.remove = sinowealthnubwo_remove,
.commit = sinowealthnubwo_commit,
};

744
src/driver-sinowealth.c Normal file
View File

@@ -0,0 +1,744 @@
/*
* Copyright © 2020 Marian Beermann
*
* 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 "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
/* 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];
} __attribute__((packed));
_Static_assert(sizeof(struct sinowealth_color) == 3, "Invalid size");
enum rgb_effect {
RGB_OFF = 0,
RGB_GLORIOUS = 0x1, /* unicorn mode */
RGB_SINGLE = 0x2, /* single constant color */
RGB_BREATHING = 0x5, /* Full RGB breathing */
RGB_BREATHING7 = 0x3, /* breathing with seven user-defined colors */
RGB_BREATHING1 = 0xa, /* single color breathing */
RGB_TAIL = 0x4,
RGB_RAVE = 0x7,
RGB_WAVE = 0x9
};
struct sinowealth_config_report {
uint8_t report_id; /* SINOWEALTH_REPORT_ID_CONFIG */
uint8_t command_id;
uint8_t unknown1;
/* always 0 when config is read from device,
* has to be 0x7b when writing config to device
*/
uint8_t config_write;
uint8_t unknown2[6];
/* 0x80 - SINOWEALTH_XY_INDEPENDENT */
uint8_t config;
uint8_t dpi_count:4;
uint8_t active_dpi:4;
/* bit set: disabled, unset: enabled
* this structure has support for eight DPI slots,
* but the glorious software only exposes six
*/
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];
uint8_t rgb_effect; /* see enum rgb_effect */
/* 0x40 - brightness (constant)
* 0x1/2/3 - speed
*/
uint8_t glorious_mode;
uint8_t glorious_direction;
uint8_t single_mode;
struct sinowealth_color 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];
/* 0x10/20/30/40 - brightness
* 0x1/2/3 - speed
*/
uint8_t tail_mode;
uint8_t unknown3[33];
/* 0x10/20/30/40 - brightness
* 0x1/2/3 - speed
*/
uint8_t rave_mode;
struct sinowealth_color 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;
uint8_t unknown4;
/* 0x1 - 2 mm
* 0x2 - 3 mm
*/
uint8_t lift_off_distance;
uint8_t padding[SINOWEALTH_CONFIG_SIZE - SINOWEALTH_CONFIG_SIZE_USED];
} __attribute__((packed));
_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)
{
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;
}
static int
sinowealth_dpi_to_raw(struct ratbag_device *device, unsigned 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;
}
static struct ratbag_color
sinowealth_raw_to_color(struct sinowealth_color raw)
{
return (struct ratbag_color) {.red = raw.data[0], .green = raw.data[1], .blue = raw.data[2]};
}
static struct sinowealth_color
sinowealth_color_to_raw(struct ratbag_color color)
{
return (struct sinowealth_color) {.data[0] = color.red, .data[1] = color.green, .data[2] = color.blue};
}
static struct ratbag_color
sinowealth_rbg_to_color(struct sinowealth_color raw)
{
return (struct ratbag_color) {.red = raw.data[0], .green = raw.data[2], .blue = raw.data[1]};
}
static struct sinowealth_color
sinowealth_color_to_rbg(struct ratbag_color color)
{
return (struct sinowealth_color) {.data[0] = color.red, .data[2] = color.green, .data[1] = color.blue};
}
static int
sinowealth_rgb_mode_to_brightness(uint8_t mode)
{
/* brightness is 0-4 in the upper nibble of mode,
* while libratbag uses 0-255.
*/
return min((mode >> 4) * 64, 255);
}
static uint8_t
sinowealth_brightness_to_rgb_mode(int brightness)
{
/* convert 0-255 to 0-4 in the upper nibble of mode */
return ((brightness + 1) / 64) << 4;
}
static int
sinowealth_rgb_mode_to_duration(uint8_t mode)
{
switch (mode & SINOWEALTH_RGB_SPEED_BITS) {
case 0: return 10000; /* static: does not translate to duration */
case 1: return 1500;
case 2: return 1000;
case 3: return 500;
default: return 0;
}
}
static uint8_t
sinowealth_duration_to_rgb_mode(int duration_ms)
{
uint8_t mode = 0;
if (duration_ms <= 500) {
mode |= 3;
} else if (duration_ms <= 1000) {
mode |= 2;
} else {
mode |= 1;
}
return mode;
}
static void
sinowealth_set_led_from_rgb_mode(struct ratbag_led *led, uint8_t mode)
{
led->brightness = sinowealth_rgb_mode_to_brightness(mode);
led->ms = sinowealth_rgb_mode_to_duration(mode);
}
static uint8_t
sinowealth_led_to_rgb_mode(const struct ratbag_led *led)
{
uint8_t mode;
mode = sinowealth_brightness_to_rgb_mode(led->brightness);
mode |= sinowealth_duration_to_rgb_mode(led->ms);
return mode;
}
static int
sinowealth_read_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct sinowealth_data *drv_data = device->drv_data;
struct sinowealth_config_report *config = &drv_data->config;
struct ratbag_resolution *resolution;
struct ratbag_led *led;
unsigned int hz = 1000; /* TODO */
int rc;
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)) {
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*) config, SINOWEALTH_CONFIG_SIZE);
/* The GET_FEATURE report length has to be 520, but the actual data returned is less */
if (rc != SINOWEALTH_CONFIG_SIZE_USED) {
log_error(device->ratbag, "Could not read device configuration: %d\n", rc);
return -1;
}
/* TODO */
ratbag_profile_set_report_rate_list(profile, &hz, 1);
ratbag_profile_set_report_rate(profile, hz);
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]);
} else {
resolution->dpi_x = sinowealth_raw_to_dpi(device, config->dpi[resolution->index]);
resolution->dpi_y = resolution->dpi_x;
}
if (config->dpi_enabled & (1<<resolution->index)) {
/* DPI step is disabled, fake it by setting DPI to 0 */
resolution->dpi_x = 0;
resolution->dpi_y = 0;
}
resolution->is_active = resolution->index == config->active_dpi - 1u;
resolution->is_default = resolution->is_active;
}
/* Body lighting */
led = ratbag_profile_get_led(profile, 0);
switch (config->rgb_effect) {
case RGB_OFF:
led->mode = RATBAG_LED_OFF;
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->brightness = sinowealth_rgb_mode_to_brightness(config->single_mode);
break;
case RGB_GLORIOUS:
case RGB_BREATHING:
case RGB_BREATHING7:
case RGB_TAIL:
case RGB_RAVE:
case RGB_WAVE:
led->mode = RATBAG_LED_CYCLE;
sinowealth_set_led_from_rgb_mode(led, config->glorious_mode);
break;
case RGB_BREATHING1:
led->mode = RATBAG_LED_BREATHING;
led->color = sinowealth_rbg_to_color(config->breathing1_color);
sinowealth_set_led_from_rgb_mode(led, config->breathing1_mode);
break;
}
ratbag_led_unref(led);
profile->is_active = true;
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;
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);
profile = ratbag_device_get_profile(device, 0);
ratbag_profile_for_each_resolution(profile, resolution) {
ratbag_resolution_set_dpi_list_from_range(resolution,
dpirange->min,
dpirange->max);
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;
}
}
ratbag_profile_unref(profile);
}
static int
sinowealth_test_hidraw(struct ratbag_device *device)
{
/* Only the keyboard interface has this report */
return ratbag_hidraw_has_report(device, SINOWEALTH_REPORT_ID_CONFIG);
}
static int
sinowealth_probe(struct ratbag_device *device)
{
int rc;
struct sinowealth_data *drv_data = 0;
struct ratbag_profile *profile = 0;
rc = ratbag_find_hidraw(device, sinowealth_test_hidraw);
if (rc)
goto err;
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
sinowealth_init_profile(device);
profile = ratbag_device_get_profile(device, 0);
rc = sinowealth_read_profile(profile);
if (rc) {
rc = -ENODEV;
goto err;
}
rc = sinowealth_read_buttons(profile);
if (rc) {
rc = -ENODEV;
goto err;
}
return 0;
err:
free(drv_data);
ratbag_set_drv_data(device, 0);
return rc;
}
static int
sinowealth_commit(struct ratbag_device *device)
{
struct ratbag_profile *profile = ratbag_device_get_profile(device, 0);
struct sinowealth_data *drv_data = device->drv_data;
struct sinowealth_config_report *config = &drv_data->config;
struct ratbag_resolution *resolution;
struct ratbag_led *led;
int rc;
uint8_t dpi_enabled = 0;
/* Check if any resolution requires independent XY DPIs */
config->config &= ~SINOWEALTH_XY_INDEPENDENT;
ratbag_profile_for_each_resolution(profile, resolution) {
if (resolution->dpi_x != resolution->dpi_y && resolution->dpi_x && resolution->dpi_y) {
config->config |= SINOWEALTH_XY_INDEPENDENT;
break;
}
}
config->dpi_count = 0;
ratbag_profile_for_each_resolution(profile, resolution) {
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);
} else {
config->dpi[resolution->index] = sinowealth_dpi_to_raw(device, resolution->dpi_x);
}
dpi_enabled |= 1<<resolution->index;
config->dpi_count++;
}
config->dpi_enabled = ~dpi_enabled; /* config->dpi_enabled is inverted */
/* Body lighting */
led = ratbag_profile_get_led(profile, 0);
switch(led->mode) {
case RATBAG_LED_OFF:
config->rgb_effect = RGB_OFF;
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);
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->breathing1_color = sinowealth_color_to_rbg(led->color);
config->breathing1_mode = sinowealth_led_to_rgb_mode(led);
break;
}
ratbag_led_unref(led);
config->config_write = 0x7b; /* magic */
rc = ratbag_hidraw_set_feature_report(device, SINOWEALTH_REPORT_ID_CONFIG,
(uint8_t*) config, SINOWEALTH_CONFIG_SIZE);
if (rc != SINOWEALTH_CONFIG_SIZE) {
log_error(device->ratbag, "Error while writing config: %d\n", rc);
ratbag_profile_unref(profile);
return -1;
}
rc = sinowealth_write_buttons(profile);
ratbag_profile_unref(profile);
return rc;
}
static void
sinowealth_remove(struct ratbag_device *device)
{
ratbag_close_hidraw(device);
free(ratbag_get_drv_data(device));
}
struct ratbag_driver sinowealth_driver = {
.name = "Sinowealth",
.id = "sinowealth",
.probe = sinowealth_probe,
.remove = sinowealth_remove,
.commit = sinowealth_commit
};

View File

@@ -807,8 +807,12 @@ hidpp10_get_current_profile(struct hidpp10_device *dev, uint8_t *current_profile
__CMD_PROFILE);
res = hidpp10_request_command(dev, &profile);
if (res)
return res;
if (res) {
/* Profiles not supported */
hidpp_log_debug(&dev->base, "Profiles not supported\n");
*current_profile = 0;
return 0;
}
type = profile.msg.parameters[0];
page = profile.msg.parameters[1];

View File

@@ -2592,6 +2592,7 @@ hidpp20_buttons_to_cpu(struct hidpp20_device *device,
case HIDPP20_BUTTON_DISABLED:
break;
default:
memcpy(b, button, sizeof(*b));
break;
}
}
@@ -2642,6 +2643,7 @@ hidpp20_buttons_from_cpu(struct hidpp20_profile *profile,
button->macro.zero = 0;
break;
default:
memcpy(b, button, sizeof(*b));
break;
}
}

View File

@@ -51,6 +51,8 @@ enum driver {
LOGITECH_G300,
LOGITECH_G600,
STEELSERIES,
SINOWEALTH,
SINOWEALTH_NUBWO,
};
struct data_hidpp20 {
@@ -80,6 +82,13 @@ 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;
@@ -91,6 +100,7 @@ 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];
@@ -252,6 +262,50 @@ 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;
@@ -267,6 +321,8 @@ 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_NUBWO, "sinowealth_nubwo", NULL},
};
const char *
@@ -666,3 +722,35 @@ 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,3 +120,22 @@ 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

@@ -23,6 +23,7 @@
#pragma once
#include "config.h"
#include <linux/input.h>
#include <stdint.h>
#include <stdbool.h>
@@ -594,6 +595,8 @@ extern struct ratbag_driver logitech_g600_driver;
extern struct ratbag_driver roccat_driver;
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;
struct ratbag_device*
ratbag_device_new(struct ratbag *ratbag, struct udev_device *udev_device,

View File

@@ -602,6 +602,8 @@ ratbag_create_context(const struct ratbag_interface *interface,
ratbag_register_driver(ratbag, &roccat_driver);
ratbag_register_driver(ratbag, &gskill_driver);
ratbag_register_driver(ratbag, &steelseries_driver);
ratbag_register_driver(ratbag, &sinowealth_driver);
ratbag_register_driver(ratbag, &sinowealth_nubwo_driver);
return ratbag;
}

View File

@@ -193,7 +193,7 @@ class _RatbagdDBus(GObject.GObject):
# args to .Set are "interface name", "function name", value-variant
val = GLib.Variant("{}".format(type), value)
if readwrite:
pval = GLib.Variant("(ssv)".format(type), (self._interface, property, val))
pval = GLib.Variant("(ssv)", (self._interface, property, val))
self._proxy.call_sync("org.freedesktop.DBus.Properties.Set",
pval, Gio.DBusCallFlags.NO_AUTO_START,
2000, None)