Compare commits

..

138 Commits
v0.11 ... 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
Filipe Laíns
1231415470 libratbag 0.14
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-05-17 21:23:57 +01:00
Filipe Laíns
660da27021 data: add Logitech G935
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-05-17 21:19:32 +01:00
Dylan Baker
481b6f029a meson: make finding python more robust
This code assumes that there is a python3 or python3-embed pkg-config
file on the system, which may or may not be true. On Gentoo for example,
only python-X.Y pkg-config files are available and this will fail to
build.

This makes use of the new py_installation.dependency(embed : true) if
possible, and falls back to trying to find a python that matches the
version of python found by the python3 or python module.
2020-05-17 19:19:42 +01:00
Filipe Laíns
768d93a45a github: add device info to bug report template
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-29 14:23:47 +01:00
ksv1986
1c6e039d01 hidpp10: fix handling of hidpp10_device_new() failures
Signed-off-by: ksv1986 <ksv0x07c2@gmail.com>
2020-04-26 15:07:23 +01:00
ksv1986
d16df8d100 hidpp10: fix crash if device failed to initialize properly
Signed-off-by: ksv1986 <ksv0x07c2@gmail.com>
2020-04-26 15:07:23 +01:00
Filipe Laíns
4b1e49d522 hidpp20: fix report type in ColorLedEffects (0x8070) set_zone_effect
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-26 15:07:06 +01:00
Filipe Laíns
786cebcb0b hidpp20: cleanup, use short reports when possible
libratbag has a quite communication-intensive setup routine. This patch
uses short reports when possible. Using short reports will speed up the
setup process and have a lower power consumption, which is relevant for
wireless devices.

We now check the report descriptor for the supported report IDs and
adjust the report type used based on the device capabilities. Because of
that, this patch should work without any issues as the report type will
get overwritten if it not supported.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-26 15:07:06 +01:00
Filipe Laíns
d53454bcb3 hidpp10: fix circular loop on hidpp10_onboard_profiles_read_macro
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-26 15:06:25 +01:00
Filipe Laíns
a0def65b64 hidpp10: add debug log on hidpp10_onboard_profiles_parse_macro fail
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-26 15:06:25 +01:00
Stephan Lachnit
2a40279903 doc: replace some tabs with spaces
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-04-26 15:06:08 +01:00
Filipe Laíns
02c95422f0 hidpp20: fix mapping button to consumer usages
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-18 02:31:17 +01:00
Filipe Laíns
4ce1cf2807 hidpp10: convert key actions to macro
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-18 02:31:17 +01:00
Filipe Laíns
89168f2187 hidpp10: add support from writting simple macro actions
Fixes #900

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-18 02:31:17 +01:00
Filipe Laíns
bb92337268 ci: remove dbus policy dir hack in arch
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-18 02:29:05 +01:00
Filipe Laíns
a8316c07f3 ratbagd: create path if not present before installing dbus policy dir
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-18 02:29:05 +01:00
Filipe Laíns
39769c57dd ci: save cache between jobs for fedora and arch
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-17 05:57:38 +01:00
Filipe Laíns
200a8af7a7 ci: cache packages in arch
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-17 05:57:38 +01:00
FFY00
fcf9f7745d ci: add arch
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-17 05:57:38 +01:00
Filipe Laíns
633c9fc55d libratbag-data: add missing header entry (#958)
Dicovered in #957

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-16 15:59:50 +02:00
Peter Hutterer
ee73434ead circleci: bump to use ubuntu 19.10
The 19.04 repos give us 404s, so let's update to the next version.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2020-04-16 08:01:53 +10:00
Filipe Laíns
d60b1c9d71 data: overwrite led count in G305
Fixes #949

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-14 19:00:12 +01:00
Filipe Laíns
88a60f55b4 hidpp20: allow overwritting the led count
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-14 19:00:12 +01:00
D33M0N
dba202ae8b data: added bluetooth ID to the MX Anywhere 2 2020-04-03 19:27:02 +01:00
Filipe Laíns
6cdcce9de4 hidpp20: log error number when commiting
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-03 17:02:44 +02:00
Filipe Laíns
e2c3373145 hidpp10: log errors when comitting
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-04-03 17:02:44 +02:00
Filipe Laíns
02575a9008 hidpp20: add button/macro opcodes
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-03-22 19:17:47 +00:00
Filipe Laíns
1097b1ba68 meson: silence swig warnings
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-03-22 19:13:58 +00:00
Filipe Laíns
77b67775da ratbagctl: don't print empty properties
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-03-22 19:08:58 +00:00
Filipe Laíns
5b23c9ebae data: add Logitech M720
Fixes #939

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-03-22 18:58:07 +00:00
Stephan Lachnit
6d0974be25 circleci: add diffutils dependency for fedora
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-02-21 15:21:42 +01:00
simrat39
ea9111a2a6 devices: Set g402 LedTypes
Signed-off-by: simrat39 <simrats169169@gmail.com>
2020-02-10 15:49:45 +00:00
Filipe Laíns
42787ee422 meson: libratbag 0.13
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:32:55 +00:00
Filipe Laíns
611dd9be0d hidpp20: keep unsupported effects
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:31:42 +00:00
Filipe Laíns
fd20f9e642 hidpp20: simplify hidpp20_onboard_profiles_write_led
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:31:42 +00:00
Filipe Laíns
9be5576967 hidpp20: add some missing led effects
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:31:42 +00:00
Filipe Laíns
9fe511fcc5 hidpp20: add profile field to special bindings in onboard profiles
New devices define a profile field in the button configuration. This
allows us to map a button to set the target profile as active. Even
though we don't support configuring this right now, some devices, such
as the G915/G815, come pre-configured with this action on th M-Keys.
This patch makes us able to read and write those settings, this way they
don't get lost when we write a profile to the device.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:31:42 +00:00
Filipe Laíns
f0b69aab19 hidpp20: only set active resolution if there are resolutions on 0x8100
This prevents us from trying to set a resolution on keyboards.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:31:42 +00:00
Filipe Laíns
7610b80ced hidpp20: log errors when failed to write sector
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-09 23:31:42 +00:00
Filipe Laíns
f0a05bb522 data: add the Logitech G Powerplay
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-02-10 08:24:10 +10:00
Torbjørn Fylling
2669cc28b2 data: add support for Logitech G815 keyboard 2020-01-31 10:13:37 +00:00
Peter Hutterer
302702d94a Declare a bunch of structs as extern
gcc 10 complains about multiple definitions of these when linking.

/usr/bin/ld: libhidpp.a(src_hidpp-generic.c.o):/builddir/build/BUILD/libratbag-0.12/x86_64-redhat-linux-gnu/../src/libratbag-private.h:588:
multiple definition of `etekcity_driver';
libutil.a(src_libratbag-util.c.o):/builddir/build/BUILD/libratbag-0.12/x86_64-redhat-linux-gnu/../src/libratbag-private.h:588:
first defined here

Special case here is the test_driver struct, without the extern gcc seems to
think it's a new struct with all zeros, causing a segfault when we then try to
strcmp the test driver name.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2020-01-29 02:12:28 +00:00
Filipe Laíns
e1136c4d30 hidpp20: add debug log when failed to read led (8070, getZoneEffects)
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-28 21:41:45 +01:00
Filipe Laíns
0012853a1d hidpp20: make sure getZoneEffect (8070) is supported
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-28 21:41:45 +01:00
Filipe Laíns
afb85f868c hidpp20: change unsupported effect log msg to debug verbosity
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-28 21:41:45 +01:00
Filipe Laíns
b76fcdadde hidpp20: enable 0x8070 (ColorLedEffects) standalone use
0x8071 is still missing but as of now we don't have any device that
needs it.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-28 21:41:45 +01:00
Stephan Lachnit
ba7c092ee2 driver-hidpp20: replace goto with return
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-01-08 07:36:52 +10:00
Stephan Lachnit
3483bd8c14 driver-hidpp20: minor cleanup
Removes useless setting of the dpi index when updating resolutions.
Removes weird empty line.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2020-01-08 07:36:52 +10:00
Filipe Laíns
0fb0eb3d42 hidpp20: log errors in hidpp20drv_commit
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-06 19:22:57 +00:00
Filipe Laíns
5fb1f4429d ratbagd: log error if failed to commit
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-06 19:22:57 +00:00
Filipe Laíns
5f5e294598 hidpp20: add support for reading/writing the report rate via 0x8060
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-06 19:18:06 +00:00
Filipe Laíns
b4218737cd hidpp20: fix bug that was unsetting some capabilities
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-06 19:18:06 +00:00
Filipe Laíns
3875bb70fd hidpp20: implement dirty flag for report rate
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-06 19:13:01 +00:00
Filipe Laíns
e9d67b7235 ratbag: add dirty flag for report rate
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-06 19:13:01 +00:00
Filipe Laíns
8a476a8724 hidpp: fix supported_report_types data type
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2020-01-03 22:40:03 +00:00
Sadika Sumanapala
d67cbec5f8 Add M590 bluetooth ID 2020-01-03 08:17:43 +10:00
Stephan Lachnit
f82c6f72b6 Use EditorConfig instead of vim comments
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-24 08:04:11 +10:00
Stephan Lachnit
af30fc28cd CircleCI: use variables for dependencies
Use separate variable lists for dependencies.
Uninstall check merged in upstream meson, so it can be simplified.
`libsolv` doesn't need to be updated any more with Fedora 31.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-24 07:34:18 +10:00
Filipe Laíns
3fb8a51456 docs: adjust description to say we support other devices than mice
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-23 06:26:55 +10:00
Matthias Bach
ceaa3c3ad5 Reorder the initialisation of the ratbagd client
When the API version mismatches we want to fail early before performing
any more complex initialisation. We do however have to check for
devices first so that we can distinguish cases where we cannot access
the daemon from those where it is too old to provide an API version.
2019-12-22 16:58:20 +00:00
Matthias Bach
551b918ba8 Give a more specific error message if user cannot reach ratbagd via D-BUS
If access failed, e.g. due to insufficient permissions, don't confuse
the user by reporting an API mismatch.

This is a minimal improvement to target issue #802. As, if access is
restricted to a group, that group is known in Meson, it would even be
possible to report that group in the error message. But, that would
require converting ratbagd.py to a template or finding some other way to
inject that information at build time.
2019-12-22 16:58:20 +00:00
Filipe Laíns
3105428ba1 tests: fix data parse for hidpp index
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-20 10:30:10 +01:00
Benjamin Tissoires
b3f2406180 data: add G910
Same situation than the G513, 0x8100 is not present.
But heh.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2019-12-20 09:15:24 +00:00
Benjamin Tissoires
902160f430 ratbag-command fixes
- File "./build/ratbag-command", line 2289, in open_ratbagd
    r = Ratbagd(1)
  TypeError: __init__() takes 1 positional argument but 2 were given
- AttributeError: module 'libratbag' has no attribute
  'ratbag_device_get_bus'
- File "./build/ratbag-command", line 267, in model
    return "{}:{04x}:{04x}:{d}".format(bus, vid, pid, version)
  KeyError: '04x'
- swig/python detected a memory leak of type 'uint32_t *', no destructor
  found.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
2019-12-19 15:56:52 +01:00
Filipe Laíns
fd6d0b6198 data: add G513
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-19 13:58:02 +01:00
Filipe Laíns
5ed98fdb22 hidpp20: only enable 0x8070 or 0x8071 if 0x8100 is present
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-19 13:58:02 +01:00
Filipe Laíns
e0e73c2971 data: add G915
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-19 12:06:15 +00:00
Filipe Laíns
0d3b4f1b64 libratbag: add 'switches' led type
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-19 12:06:15 +00:00
Filipe Laíns
20b6e3deeb hidpp20: only populate resolutions if the device supports
This patch moves the 0x8100 initialization routine out of
hidpp20drv_init_feature because it needs the capabilities to be
processed first.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-19 12:05:54 +00:00
Filipe Laíns
ffcdee912d libratbag: allow no resolutions
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-19 12:05:54 +00:00
Filipe Laíns
5357d4cbc2 meson: libratbag 0.12
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-18 19:25:30 +00:00
Filipe Laíns
26c534cc74 hidpp20: check supported report types
Fixes #854

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-18 19:23:31 +00:00
Stephan Lachnit
904c7988bb hidpp20: set dictionary output to raw
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-17 15:54:42 +00:00
Stephan Lachnit
7d255a2f93 Fix missing capability in ratbagctl.test
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-17 15:54:42 +00:00
Stephan Lachnit
d0a5cb1f0e Minor ratbagd/ratbagc adjustments
Bumped copyright to 2019.
Fixed typo in profile.capabilities description.
only ratbagd.py: Removed vim helper line since the file has a proper file extension.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-17 15:54:42 +00:00
Qiu Wenbo
71b1d66287 hidpp20: fix a double free error of hidpp20
These are two fields of a union and should have the same value. What the
code doing here is trying to free a pointer twice.

Signed-off-by: Qiu Wenbo <qiuwenbo@phytium.com.cn>
2019-12-14 14:25:44 +00:00
Qiu Wenbo
32ebbca42f hidpp20: fix G305 quirk detection
Signed-off-by: Qiu Wenbo <qiuwenbo@phytium.com.cn>
2019-12-14 14:25:44 +00:00
Stephan Lachnit
a0afbfeb3f Update CircleCI config
Rename fedora_latest to fedora_31 which is more representative.
Drop the Fedora Rawhide workflow which was always disabled anyway.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-13 09:34:26 +10:00
Stephan Lachnit
9dfd4fd03a remove .gitignore
Use a personal .gitignore for your build directory instead.

Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-12 20:06:16 +00:00
Peter Hutterer
b0ea75979a data: add a test for unifying receiver IDs
We don't want any device files that use the receiver itself as a match so
let's test for that. In virtually all cases this means the user must update
the kernel and hope support for that receiver is already present in that
version.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-12-11 00:27:35 +00:00
Stephan Lachnit
a5c1ca202e drivers: add Roccat Kone Pure driver
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2019-12-11 00:20:02 +00:00
Peter Hutterer
8d40ad6c0c libratbag: only log to stderr for error messages
This was copied from libinput and made sense while libratbag was a library -
we want "unhandled" messages to end up somewhere and stderr is the most likely
candidate here.

Now that we always run within ratbagd and it's all effectively inseperable,
just log to stdout for anything below error.

The switch statement was reformatted for consistency, only the ERROR case has
changed code.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-12-11 09:07:10 +10:00
Peter Hutterer
72d11de597 ratbagd: print two verbose messages at startup
When no device is present, starting ratbagd even with --verbose prints exactly
nothing. Let's be more verbose here to give the user an indication that
something's happening. The user wants everything, is probably willing to
accept anything but in the end all we can say is that we have nothing.

Related to #862

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-12-10 22:48:53 +00:00
Filipe Laíns
47cc5fce92 github: add issue templates
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-05 10:01:59 +00:00
Filipe Laíns
5da791b268 ratbagd: support the old --verbose=raw syntax
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-05 09:44:35 +00:00
Filipe Laíns
e63802b01a ratbagd: make raw the default verbosity level (#857)
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-05 08:07:46 +10:00
Jeff Huang
cd89b123ce Add Logitech M585/M590 support. (#845) 2019-11-27 15:52:18 +10:00
Peter Hutterer
f6546b3222 circle CI: add diff as fedora package
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-11-18 08:50:36 +10:00
Peter Hutterer
cd8ddc8444 circle CI: switch to Fedora 31
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-11-18 07:16:22 +10:00
Luke
cdb3226ef1 meson: don't use brackets, parenthesis and braces in filenames
Renamed filenames to avoid brackets, and added a check to data-parse-test.py to
check for brackets, parentheses, and braces so they aren't shipped in filenames
in the future.
2019-11-17 00:11:42 +00:00
71 changed files with 3223 additions and 324 deletions

View File

@@ -1,5 +1,12 @@
# vim: set expandtab shiftwidth=2 tabstop=8:
libratbag_references:
build_dependencies: &build_dependencies
FEDORA_DEP_BUILD: gcc gcc-c++ meson dbus-daemon glib2-devel json-glib-devel libevdev-devel libudev-devel libunistring-devel python3-devel python3-evdev swig
FEDORA_DEP_TEST: check-devel python3-gobject python3-lxml valgrind diffutils
FEDORA_DEP_DOC: python3-sphinx python3-sphinx_rtd_theme
UBUNTU_DEP_BUILD: gcc g++ meson pkg-config systemd libevdev-dev libglib2.0-dev libjson-glib-dev libsystemd-dev libudev-dev libunistring-dev python3-dev python3-evdev swig
UBUNTU_DEP_TEST: check python3-gi python3-lxml valgrind
ARCH_DEP_BUILD: meson swig glib2 libevdev libudev.so libunistring json-glib python python-evdev python-gobject
ARCH_DEP_TEST: check valgrind python-lxml
default_settings: &default_settings
working_directory: ~/libratbag
environment:
@@ -52,10 +59,7 @@ libratbag_references:
name: Checking if any files are left after uninstall
command: |
PREFIX=/root/test_install
# workaround until https://github.com/mesonbuild/meson/pull/2033 is merged
# and a new release appears
git clone https://github.com/whot/meson -b wip/remove-directories-on-uninstall
./meson/meson.py build_install --prefix=$PREFIX
meson build_install --prefix=$PREFIX
ninja -C build_install install
ninja -C build_install uninstall
if [ -d $PREFIX ]
@@ -73,33 +77,34 @@ libratbag_references:
mkdir /run/dbus
/usr/bin/dbus-daemon --system --fork
fedora_prep_cache: &fedora_prep_cache
<<: *default_settings
steps:
- run:
name: Initializing Fedora dnf cache
command: dnf install -y --downloadonly libsolv tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml libunistring-devel dbus-daemon json-glib-devel
- persist_to_workspace:
root: /var/cache/
paths:
- dnf/*
save_date: &save_date
run:
name: Save date
command: date +%F > /tmp/date
fedora_save_cache: &fedora_save_cache
save_cache:
key: fedora-cache-v1-{{ checksum "/tmp/date" }}
paths:
- /var/cache/dnf
fedora_fetch_cache: &fedora_fetch_cache
attach_workspace:
at: /var/cache/
restore_cache:
keys:
- fedora-cache-v1
fedora_install: &fedora_install
run:
name: Install prerequisites
command: |
echo keepcache=1 >> /etc/dnf/dnf.conf
dnf upgrade -y libsolv
dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml python3-devel swig libunistring-devel dbus-daemon json-glib-devel
dnf install -y git ${FEDORA_DEP_BUILD} ${FEDORA_DEP_TEST}
sed -i 's/systemd//' /etc/nsswitch.conf
fedora_settings: &fedora_settings
<<: *default_settings
steps:
- *save_date
- *fedora_fetch_cache
- *fedora_install
- checkout
@@ -111,7 +116,39 @@ fedora_settings: &fedora_settings
- *build_buildtype_plain
- *build_buildtype_release
- *export_logs
- *fedora_save_cache
environment:
*build_dependencies
arch_save_cache: &arch_save_cache
save_cache:
key: arch-cache-v1-{{ checksum "/tmp/date" }}
paths:
- /var/cache/pacman/pkg
arch_fetch_cache: &arch_fetch_cache
restore_cache:
keys:
- arch-cache-v1
arch_settings: &arch_settings
<<: *default_settings
steps:
- *save_date
- *arch_fetch_cache
- run:
name: Install prerequisites
command: pacman -Syu --noconfirm --noprogressbar --needed base-devel git tree ${ARCH_DEP_BUILD} ${ARCH_DEP_TEST}
- *start_dbus
- checkout
- *build_and_test
- *install
- *check_install
- *check_uninstall
- *export_logs
- *arch_save_cache
environment:
*build_dependencies
ubuntu_settings: &ubuntu_settings
<<: *default_settings
@@ -119,12 +156,9 @@ ubuntu_settings: &ubuntu_settings
- run:
name: Install prerequisites
command: |
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y software-properties-common
apt-get remove -y libnss-systemd
add-apt-repository universe
apt-get update
apt-get install -y tree git gcc g++ pkg-config meson check libudev-dev libevdev-dev libsystemd-dev valgrind python3-gi python3-evdev libglib2.0-dev python3-lxml python3-dev swig systemd libunistring-dev libjson-glib-dev
apt-get install -y git ${UBUNTU_DEP_BUILD} ${UBUNTU_DEP_TEST}
- checkout
- *start_dbus
- *build_and_test
@@ -134,6 +168,8 @@ ubuntu_settings: &ubuntu_settings
- *build_buildtype_plain
- *build_buildtype_release
- *export_logs
environment:
*build_dependencies
doc_build: &doc_build
<<: *default_settings
@@ -142,7 +178,7 @@ doc_build: &doc_build
- *fedora_install
- run:
name: Install documentation build-deps
command: dnf install -y python3-sphinx python3-sphinx_rtd_theme
command: dnf install -y ${FEDORA_DEP_DOC}
- checkout
- *build_with_docs
- *export_logs
@@ -152,6 +188,8 @@ doc_build: &doc_build
root: build
paths:
- doc/html/*
environment:
*build_dependencies
docs_deploy: &docs_deploy
<<: *default_settings
@@ -193,48 +231,38 @@ docs_deploy: &docs_deploy
version: 2
jobs:
fedora_rawhide:
fedora_32:
<<: *fedora_settings
docker:
- image: fedora:rawhide
fedora_cache:
<<: *fedora_prep_cache
- image: fedora:32
arch:
<<: *arch_settings
docker:
- image: fedora:30
fedora_latest:
<<: *fedora_settings
docker:
- image: fedora:30
ubuntu_19_04:
- image: archlinux
ubuntu_20_04:
<<: *ubuntu_settings
docker:
- image: ubuntu:19.04
- image: ubuntu:20.04
doc_build:
<<: *doc_build
docker:
- image: fedora:30
- image: fedora:32
doc_deploy:
<<: *docs_deploy
docker:
- image: fedora:30
- image: fedora:32
workflows:
version: 2
compile_and_test:
jobs:
# - fedora_rawhide
- fedora_cache
- ubuntu_19_04
- fedora_latest:
requires:
- fedora_cache
- doc_build:
requires:
- fedora_cache
- ubuntu_20_04
- arch
- fedora_32
- doc_build
- doc_deploy:
requires:
- fedora_cache
- doc_build
filters:
branches:

20
.editorconfig Normal file
View File

@@ -0,0 +1,20 @@
root = true
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.{c,h}]
indent_style = tab
tab_width = 8
trim_trailing_whitespace = true
[{*.{py,py.in},tools/ratbagctl.*.in}]
indent_style = space
indent_size = 4
[*.{yml,xsl}]
indent_style = space
indent_size = 2

42
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,42 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Information**
<!-- If your issue is related to a Logitech device please make sure you are running at least Linux 5.2 -->
- `ratbagd` version (`ratbagd --version`):
- Distribution:
- Kernel version (ex. `uname -srmo`): `KERNEL VERSION HERE`
Device info (if applicable):
```
$ ratbagctl <device> info
OUTPUT HERE
```
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Logs**
Please start the daemon with verbosity and reproduce the issue.
First make sure it isn't running already.
```
$ ratbagd --verbose=raw
OUTPUT HERE
```
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

11
.gitignore vendored
View File

@@ -1,11 +0,0 @@
*.o
*.pc
*.swp
*~
*.sig
*.tar.*
*.announce
*.patch
*.rej
*.trs
tags

View File

@@ -3,9 +3,10 @@ libratbag
<img src="https://libratbag.github.io/_images/logo.svg" alt="" width="30%" align="right">
libratbag provides **ratbagd**, a DBus daemon to configure gaming mice.
The daemon provides a generic way to access the various features exposed by
these mice and abstracts away hardware-specific and kernel-specific quirks.
libratbag provides **ratbagd**, a DBus daemon to configure input devices,
mainly gaming mice. The daemon provides a generic way to access the various
features exposed by these mice and abstracts away hardware-specific and
kernel-specific quirks.
libratbag currently supports devices from Logitech, Etekcity, GSkill,
Roccat, Steelseries. See [the device

View File

@@ -1,5 +1,4 @@
#!/usr/bin/env python3
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# Copyright © 2017 Red Hat, Inc.
#
@@ -27,6 +26,7 @@
import argparse
import configparser
import re
def assertIn(element, l):
@@ -57,7 +57,7 @@ def check_match_str(string):
def check_ledtypes_str(string):
permitted_types = ['logo', 'side', 'battery', 'dpi']
permitted_types = ['logo', 'side', 'battery', 'dpi', 'switches']
types = string.split(';')
for t in types:
@@ -137,9 +137,8 @@ def check_section_hidpp10(section):
pass
try:
index = int(section['DeviceIndex'])
# 10 is arbitrarily chosen
assert(index > 0 and index < 10)
index = int(section['DeviceIndex'], 16)
assert(index > 0 and index <= 0xff)
except KeyError:
pass
@@ -169,14 +168,13 @@ def check_section_hidpp10(section):
def check_section_hidpp20(section):
permitted = ['DeviceIndex', 'Quirk']
permitted = ['DeviceIndex', 'Leds', 'Quirk']
for key in section.keys():
assertIn(key, permitted)
try:
index = int(section['DeviceIndex'])
# 10 is arbitrarily chosen
assert(index > 0 and index < 10)
index = int(section['DeviceIndex'], 16)
assert(index > 0 and index <= 0xff)
except KeyError:
pass
@@ -190,6 +188,15 @@ def check_section_driver(driver, section):
assert('Unsupported driver section {}'.format(driver))
def validate_data_file_name(path):
# Matching any of the characters in the regular expression will throw an
# error. Currently only tests the square brackets [], parentheses, and curly
# braces.
illegal_characters_regex = '([\[\]\{\}\(\)])'
found_characters = re.findall(illegal_characters_regex, path)
if found_characters:
raise AssertionError("data file name '{}' contains illegal characters: '{}'".format(path, ''.join(found_characters)))
def parse_data_file(path):
print('Parsing file {}'.format(path))
data = configparser.ConfigParser(strict=True)
@@ -216,4 +223,5 @@ if __name__ == "__main__":
parser.add_argument('file', nargs='+')
args = parser.parse_args()
for path in args.file:
validate_data_file_name(path)
parse_data_file(path)

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

@@ -1,5 +1,4 @@
#!/usr/bin/env python3
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# Copyright © 2018 Red Hat, Inc.
#

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

@@ -0,0 +1,5 @@
# Logitech M585/M590
[Device]
Name=Logitech M585/M590
DeviceMatch=usb:046d:406b;bluetooth:046d:b01b
Driver=hidpp20

View File

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

View File

@@ -1,4 +1,4 @@
[Device]
Name=Logitech MX Anywhere 2
DeviceMatch=usb:046d:404a;bluetooth:046d:b013;bluetooth:046d:b018
DeviceMatch=usb:046d:404a;bluetooth:046d:b013;bluetooth:046d:b018;bluetooth:046d:b01f
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

@@ -0,0 +1,8 @@
[Device]
Name=Logitech G Powerplay
DeviceMatch=usb:046d:405f
LedTypes=logo
Driver=hidpp20
[Driver/hidpp20]
DeviceIndex=7

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

@@ -4,4 +4,5 @@ DeviceMatch=usb:046d:4074
Driver=hidpp20
[Driver/hidpp20]
Leds=0
Quirk=G305

View File

@@ -2,4 +2,5 @@
[Device]
Name=Logitech G402 Gaming Mouse
DeviceMatch=usb:046d:c07e
LedTypes=logo
Driver=hidpp20

View File

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

View File

@@ -0,0 +1,5 @@
[Device]
Name=Logitech G513
DeviceMatch=usb:046d:c33c
LedTypes=switches
Driver=hidpp20

View File

@@ -0,0 +1,5 @@
[Device]
Name=Logitech G815
DeviceMatch=usb:046d:c33f
LedTypes=logo;switches
Driver=hidpp20

View File

@@ -0,0 +1,8 @@
[Device]
Name=Logitech G910
DeviceMatch=usb:046d:c335
LedTypes=logo;switches
Driver=hidpp20
[Driver/hidpp20]
DeviceIndex=ff

View File

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

View File

@@ -0,0 +1,5 @@
[Device]
Name=Logitech G935
DeviceMatch=usb:046d:0a87
LedTypes=logo;side
Driver=hidpp20

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

74
data/devices/receiver-check.py Executable file
View File

@@ -0,0 +1,74 @@
#!/usr/bin/env python3
#
# Copyright © 2018 Red Hat, Inc.
#
# 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.
#
import argparse
import os
import sys
import configparser
# see the IDs from
# https://github.com/torvalds/linux/blob/master/drivers/hid/hid-ids.h#L772
# https://github.com/torvalds/linux/blob/master/drivers/hid/hid-logitech-dj.c#L1826
logitech_receivers = [
0xc50c, # USB_DEVICE_ID_S510_RECEIVER
0xc517, # USB_DEVICE_ID_S510_RECEIVER_2
0xc512, # USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500
0xc513, # USB_DEVICE_ID_MX3000_RECEIVER
0xc51b, # USB_DEVICE_ID_LOGITECH_27MHZ_MOUSE_RECEIVER
0xc52b, # USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER
0xc52f, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER
0xc532, # USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2
0xc534, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2
0xc539, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1
0xc53f, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1
0xc53a, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY
]
RECEIVERS = ['usb:046d:{}'.format(r) for r in logitech_receivers]
def parse_data_file(path):
data = configparser.ConfigParser(strict=True)
# Don't convert to lowercase
data.optionxform = lambda option: option
data.read(path)
matches = data['Device']['DeviceMatch']
return matches.split(';')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Checks that no unifying receiver ID is in the device files')
parser.add_argument('file', nargs='+')
args = parser.parse_args()
receiver_found = False
for path in args.file:
matches = parse_data_file(path)
fname = os.path.basename(path)
for m in matches:
if m in RECEIVERS:
print('Receiver ID {} found in file {}'.format(m, fname))
receiver_found = True
if receiver_found:
sys.exit(1)

View File

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

View File

@@ -85,13 +85,13 @@ interact with ratbagd.
:type: i
:flags: read-only,constant
The DBus API version. This is a stopgap feature while the DBus API
is still in flux. The version has no backwards or
forward-compatibilty guarantees - your client must understand
**exactly** the same version, it is not enough to support an older
or a newer version.
The DBus API version. This is a stopgap feature while the DBus API
is still in flux. The version has no backwards or
forward-compatibilty guarantees - your client must understand
**exactly** the same version, it is not enough to support an older
or a newer version.
This API will be removed once the DBus API is declared stable.
This API will be removed once the DBus API is declared stable.
.. attribute:: Devices

View File

@@ -1,8 +1,8 @@
project('libratbag', 'c',
version : '0.11',
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
@@ -172,8 +172,11 @@ src_libratbag = [
'src/driver-logitech-g300.c',
'src/driver-logitech-g600.c',
'src/driver-roccat.c',
'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',
@@ -281,7 +284,9 @@ data_files = files(
'data/devices/gskill-MX-780.device',
'data/devices/logitech-M325.device',
'data/devices/logitech-M570.device',
'data/devices/logitech-M585-M590.device',
'data/devices/logitech-M705.device',
'data/devices/logitech-M720.device',
'data/devices/logitech-MX-Ergo.device',
'data/devices/logitech-MX-Master.device',
'data/devices/logitech-MX-Master-2S.device',
@@ -289,6 +294,7 @@ data_files = files(
'data/devices/logitech-MX518.device',
'data/devices/logitech-T650.device',
'data/devices/logitech-Wireless-Touchpad.device',
'data/devices/logitech-g-powerplay.device',
'data/devices/logitech-g-pro-wireless.device',
'data/devices/logitech-g-pro.device',
'data/devices/logitech-g102-g203.device',
@@ -308,6 +314,7 @@ data_files = files(
'data/devices/logitech-g502-hero.device',
'data/devices/logitech-g502-proteus-core.device',
'data/devices/logitech-g502-proteus-spectrum.device',
'data/devices/logitech-g513.device',
'data/devices/logitech-g600.device',
'data/devices/logitech-g602.device',
'data/devices/logitech-g603.device',
@@ -322,12 +329,17 @@ data_files = files(
'data/devices/logitech-g900.device',
'data/devices/logitech-g903-hero.device',
'data/devices/logitech-g903.device',
'data/devices/logitech-g9x-[Call-of-Duty-MW3-Edition].device',
'data/devices/logitech-g9x-[Original].device',
'data/devices/logitech-g815.device',
'data/devices/logitech-g910.device',
'data/devices/logitech-g915.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-Vertical.device',
'data/devices/roccat-kone-pure.device',
'data/devices/roccat-kone-xtd.device',
'data/devices/steelseries-kinzu-v2.device',
'data/devices/steelseries-kinzu-v3.device',
@@ -336,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,
@@ -347,6 +363,9 @@ test('data-parse-test', data_parse_test, args: data_files)
duplicate_test = find_program(join_paths(meson.source_root(), 'data/devices/duplicate-check.py'))
test('duplicate-test', duplicate_test, args : data_files)
reciever_id_test = find_program(join_paths(meson.source_root(), 'data/devices/receiver-check.py'))
test('receiver-id-test', reciever_id_test, args : data_files)
#### tests ####
enable_tests = get_option('tests')
if enable_tests
@@ -438,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.
@@ -526,12 +544,29 @@ configure_file(input : 'dbus/org.freedesktop.ratbag1.conf.in',
#### tools ####
# This is a special idiomatic construct in meson to get a fast dependency that
# doesn't exist and isn't logged as "not found".
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
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)
if not dep_python3.found()
dep_python3 = dependency('python3@0@'.format(embed))
endif
endif
config_ratbagctl = configuration_data()
@@ -595,12 +630,6 @@ swig_gen = generator(
'@INPUT@'],
)
# From python 3.8 we neeed python3-embed
dep_python3 = dependency('python3-embed', required: false)
if not dep_python3.found()
dep_python3 = dependency('python3')
endif
wrapper_deps = [
dep_python3,
dep_libratbag,
@@ -613,7 +642,9 @@ shared_library(
'libratbag',
c_source,
name_prefix: '_',
c_args : ['-Wno-missing-prototypes', '-Wno-format-nonliteral'],
c_args : ['-Wno-missing-prototypes',
'-Wno-format-nonliteral',
'-Wno-missing-field-initializers'],
extra_files: [ i_source ] + src_libratbag ,
dependencies: deps_libratbag + wrapper_deps,
include_directories : include_directories('src', 'tools'),
@@ -626,7 +657,10 @@ shared_library(
'hidpp',
c_source,
name_prefix: '_',
c_args : ['-Wno-missing-prototypes', '-Wno-format-nonliteral'],
c_args : ['-Wno-missing-prototypes',
'-Wno-format-nonliteral',
'-Wno-missing-field-initializers',
'-Wno-strict-prototypes'],
extra_files: [ i_source ] + src_libratbag ,
dependencies: deps_libratbag + wrapper_deps,
include_directories : include_directories('src', 'tools'),

12
ratbagd/.gitignore vendored
View File

@@ -1,12 +0,0 @@
*.a
*.log
*.o
*.stamp
*.swp
*.trs
*~
.dirstamp
.libs/
/*.tar.xz
tags
__pycache__

View File

@@ -172,6 +172,8 @@ static void ratbagd_device_commit_pending(void *data)
int r;
r = ratbag_device_commit(device->lib_device);
if (r)
log_error("error committing device (%d)\n", r);
if (r < 0)
ratbagd_device_resync(device, device->ctx->bus);
ratbagd_device_unref(device);

View File

@@ -170,7 +170,7 @@ static void parse_led_member(JsonObject *obj, const gchar *name,
if (streq(name, "type")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > RATBAG_LED_TYPE_WHEEL)
if (v < 0 || v > RATBAG_LED_TYPE_SWITCHES)
parser_error("type");
led->type = v;
@@ -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

@@ -3,7 +3,7 @@
ratbagd \- system daemon to introspect and modify configurable mice
.SH SYNOPSIS
.B ratbagd
.RB [ \-\-verbose[=raw]|\-\-quiet|\-\-version|\-\-help]
.RB [ \-\-verbose[=debug]|\-\-quiet|\-\-version|\-\-help]
.SH DESCRIPTION
.B ratbagd
starts the daemon. It shouldn't be invoked directly;
@@ -14,7 +14,7 @@ is normally started through DBus activation.
.B \-\-help
Print help and exit
.TP 8
.B \-\-verbose, \-\-verbose=raw
.B \-\-verbose, \-\-verbose=debug
Enable verbose output, optionally including raw messages as sent and
received from the device. This should be use to debug any issues with the
device.

View File

@@ -29,6 +29,7 @@
#include <assert.h>
#include <errno.h>
#include <libgen.h>
#include <libratbag.h>
#include <libudev.h>
#include <stdio.h>
@@ -356,6 +357,7 @@ static int ratbagd_new(struct ratbagd **out)
if (r < 0)
return r;
log_verbose("Initializing libratbag\n");
ctx->lib_ctx = ratbag_create_context(&ratbagd_lib_interface, ctx);
if (!ctx->lib_ctx)
return -ENOMEM;
@@ -520,6 +522,8 @@ static int ratbagd_run(struct ratbagd *ctx)
ctx);
sd_event_add_post(ctx->event, NULL, before_idle_cb, ctx);
log_verbose("DBus server ready\n");
return sd_event_loop(ctx->event);
}
@@ -544,6 +548,13 @@ static bool install_ratbagd_devel_dbus_policy(void)
log_error("Failed to source policy file: %m\n");
goto out;
}
if (mkdir_p(dirname(strdupa(DBUS_POLICY_DST)), 0755)) {
log_error("Failed to create destination path: %m\n");
goto out;
}
out = open(DBUS_POLICY_DST, O_CREAT|O_WRONLY, 0644);
if (out == -1) {
log_error("Failed to open destination: %m\n");
@@ -617,12 +628,12 @@ int main(int argc, char *argv[])
return 0;
} else if (streq(argv[1], "--quiet")) {
log_level = LL_QUIET;
} else if (streq(argv[1], "--verbose=raw")) {
} else if (streq(argv[1], "--verbose") || streq(argv[1], "--verbose=raw")) {
log_level = LL_RAW;
} else if (streq(argv[1], "--verbose")) {
} else if (streq(argv[1], "--verbose=debug")) {
log_level = LL_VERBOSE;
} else {
fprintf(stderr, "Usage: %s [--version | --quiet | --verbose[=raw]]\n",
fprintf(stderr, "Usage: %s [--version | --quiet | --verbose[=debug]]\n",
program_invocation_short_name);
r = -EINVAL;
goto exit;

View File

@@ -164,6 +164,7 @@ hidpp10drv_map_button(struct ratbag_device *device,
{
struct hidpp10_profile profile;
int ret;
unsigned int modifiers = 0;
ret = hidpp10_get_profile(hidpp10, button->profile->index, &profile);
if (ret)
@@ -178,6 +179,7 @@ hidpp10drv_map_button(struct ratbag_device *device,
button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
button->action.action.key.key = ratbag_hidraw_get_keycode_from_keyboard_usage(device,
profile.buttons[button->index].keys.key);
modifiers = profile.buttons[button->index].keys.modifier_flags;
break;
case PROFILE_BUTTON_TYPE_CONSUMER_CONTROL:
button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
@@ -199,6 +201,14 @@ hidpp10drv_map_button(struct ratbag_device *device,
}
}
if (button->action.type == RATBAG_BUTTON_ACTION_TYPE_KEY) {
ret = ratbag_button_macro_new_from_keycode(button, button->action.action.key.key, modifiers);
if (ret < 0) {
log_error(device->ratbag, "hidpp10: error while reading button %d\n", button->index);
button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
}
}
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
}
@@ -299,6 +309,8 @@ hidpp10drv_write_button(struct hidpp10_profile *profile,
struct hidpp10drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp10_device *hidpp10 = drv_data->dev;
uint8_t code;
unsigned int modifiers, key;
int rc;
if (hidpp10->profile_type == HIDPP10_PROFILE_UNKNOWN)
return -ENOTSUP;
@@ -330,6 +342,26 @@ hidpp10drv_write_button(struct hidpp10_profile *profile,
profile->buttons[button->index].special.special = code;
break;
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
rc = ratbag_action_keycode_from_macro(action, &key, &modifiers);
if (rc < 0) {
log_error(device->ratbag, "hidpp10: can't convert macro action to keycode in button %d\n", button->index);
return -EINVAL;
}
code = ratbag_hidraw_get_keyboard_usage_from_keycode(device, key);
if (code == 0) {
code = ratbag_hidraw_get_consumer_usage_from_keycode(device, key);
if (code == 0)
return -EINVAL;
profile->buttons[button->index].consumer_control.type = PROFILE_BUTTON_TYPE_CONSUMER_CONTROL;
profile->buttons[button->index].consumer_control.consumer_control = code;
} else {
profile->buttons[button->index].keys.type = PROFILE_BUTTON_TYPE_KEYS;
profile->buttons[button->index].keys.key = code;
profile->buttons[button->index].keys.modifier_flags = modifiers;
}
break;
default:
return -ENOTSUP;
}
@@ -541,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;
@@ -558,8 +591,10 @@ hidpp10drv_commit(struct ratbag_device *device)
continue;
rc = hidpp10drv_write_button(&p, button, &action);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp10: failed to update buttons (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
}
ratbag_profile_for_each_led(profile, led)
@@ -567,8 +602,10 @@ hidpp10drv_commit(struct ratbag_device *device)
if (dev->profile_type != HIDPP10_PROFILE_UNKNOWN) {
rc = hidpp10_set_profile(dev, profile->index, &p);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp10: failed to set profile (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
}
/* Update the current resolution in case it changed */
@@ -576,8 +613,10 @@ hidpp10drv_commit(struct ratbag_device *device)
rc = hidpp10_set_current_resolution(dev,
active_resolution->dpi_x,
active_resolution->dpi_y);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp10: failed to set active resolution (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
active_resolution = NULL;
}
@@ -631,9 +670,9 @@ hidpp10drv_probe(struct ratbag_device *device)
* If there is a special need like for G700(s), then add a DeviceIndex
* entry to the .device file.
*/
dev = hidpp10_device_new(&base, device_idx, type, profile_count);
rc = hidpp10_device_new(&base, device_idx, type, profile_count, &dev);
if (!dev) {
if (rc) {
log_error(device->ratbag,
"Failed to get HID++1.0 device for %s\n",
device->name);
@@ -714,14 +753,14 @@ static void
hidpp10drv_remove(struct ratbag_device *device)
{
struct hidpp10drv_data *drv_data;
struct hidpp10_device *dev;
ratbag_close_hidraw(device);
drv_data = ratbag_get_drv_data(device);
dev = drv_data->dev;
if (!drv_data)
return;
hidpp10_device_destroy(dev);
hidpp10_device_destroy(drv_data->dev);
free(drv_data);
}

View File

@@ -69,6 +69,7 @@ struct hidpp20drv_data {
unsigned num_controls;
struct hidpp20_control_id *controls;
struct hidpp20_profiles *profiles;
struct hidpp20_led *leds;
union hidpp20_generic_led_zone_info led_infos;
unsigned int report_rates[4];
@@ -334,14 +335,24 @@ static void
hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_data)
{
struct hidpp20_profile *profile;
struct hidpp20_led *h_led;
struct hidpp20_led h_led_val;
struct hidpp20_led *h_led = &h_led_val;
struct hidpp20_color_led_zone_info* led_info;
struct hidpp20_color_led_info info;
int rc;
led_info = &drv_data->led_infos.color_leds_8070[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
} else {
rc = hidpp20_color_led_effects_get_zone_effect(drv_data->dev, led->index, h_led);
if (rc) {
log_debug(led->profile->device->ratbag,
"Failed to read led settings\n");
}
}
switch (h_led->mode) {
case HIDPP20_LED_ON:
@@ -397,10 +408,10 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
break;
default:
log_bug_libratbag(led->profile->device->ratbag,
"%s: Unknown effect id %d\n",
led->profile->device->name,
ei.effect_id);
log_debug(led->profile->device->ratbag,
"%s: Unsupported effect (%d)\n",
led->profile->device->name,
ei.effect_id);
break;
}
}
@@ -472,10 +483,10 @@ hidpp20drv_read_led_8071(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
break;
default:
log_bug_libratbag(led->profile->device->ratbag,
"%s: Unknown effect id %d\n",
led->profile->device->name,
ei.effect_id);
log_debug(led->profile->device->ratbag,
"%s: Unsupported effect (%d)\n",
led->profile->device->name,
ei.effect_id);
break;
}
}
@@ -564,7 +575,7 @@ hidpp20drv_update_button_8100(struct ratbag_button *button)
code = ratbag_hidraw_get_keyboard_usage_from_keycode(device, key);
if (code == 0) {
subtype = HIDPP20_BUTTON_HID_TYPE_CONSUMER_CONTROL;
code = ratbag_hidraw_get_consumer_usage_from_keycode(device, action->action.key.key);
code = ratbag_hidraw_get_consumer_usage_from_keycode(device, key);
if (code == 0)
return -EINVAL;
}
@@ -691,11 +702,13 @@ hidpp20drv_update_led_8070_8071(struct ratbag_led *led, struct ratbag_profile* p
struct hidpp20drv_data *drv_data)
{
struct hidpp20_profile *h_profile;
struct hidpp20_led *h_led;
struct hidpp20_led h_led_val;
struct hidpp20_led *h_led = &h_led_val;
h_profile = &drv_data->profiles->profiles[profile->index];
h_led = &(h_profile->leds[led->index]);
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
h_profile = &drv_data->profiles->profiles[profile->index];
h_led = &(h_profile->leds[led->index]);
}
if (!h_led)
return -EINVAL;
@@ -720,6 +733,13 @@ hidpp20drv_update_led_8070_8071(struct ratbag_led *led, struct ratbag_profile* p
h_led->period = led->ms;
h_led->brightness = led->brightness * 100 / 255;
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)) {
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
hidpp20_color_led_effects_set_zone_effect(drv_data->dev,
led->index,
h_led_val);
}
return RATBAG_SUCCESS;
}
@@ -822,9 +842,12 @@ hidpp20drv_read_report_rate_8060(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag *ratbag = device->ratbag;
struct ratbag_profile *profile;
uint8_t bitflags_ms;
int nrates = 0;
int rc;
uint8_t rate_ms;
unsigned rate_hz;
rc = hidpp20_adjustable_report_rate_get_report_rate_list(drv_data->dev,
&bitflags_ms);
@@ -843,6 +866,37 @@ hidpp20drv_read_report_rate_8060(struct ratbag_device *device)
drv_data->num_report_rates = nrates;
if (!hidpp20_adjustable_report_rate_get_report_rate(drv_data->dev, &rate_ms)) {
switch (rate_ms)
{
case 1:
rate_hz = 1000;
break;
case 2:
case 3: /* 3ms = 333.(3)Hz, we round to 500Hz */
rate_hz = 500;
break;
case 4:
case 5: /* 5ms = 200Hz, we round to 250Hz */
rate_hz = 250;
break;
case 6: /* 6ms = 166.(6)Hz, we round to 125Hz */
case 7: /* 7ms = 142Hz, we round to 125Hz */
case 8:
rate_hz = 125;
break;
default:
rate_hz = 0;
break;
}
if (rate_hz) {
log_debug(ratbag, "report rate is %u\n", rate_hz);
ratbag_device_for_each_profile(device, profile)
profile->hz = rate_hz;
}
}
log_debug(ratbag, "device has %d report rates\n", nrates);
return 0;
@@ -943,7 +997,7 @@ hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct hidpp20_sensor *sensor;
int rc, i;
int i;
int dpi = dpi_x; /* dpi_x == dpi_y if we don't have the individual resolution cap */
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
@@ -959,14 +1013,13 @@ hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
sensor = &drv_data->sensors[0];
/* validate that the sensor accepts the given DPI */
rc = -EINVAL;
if (dpi < sensor->dpi_min || dpi > sensor->dpi_max)
goto out;
return -EINVAL;
if (sensor->dpi_steps) {
for (i = sensor->dpi_min; i < dpi; i += sensor->dpi_steps) {
}
if (i != dpi)
goto out;
return -EINVAL;
} else {
i = 0;
while (sensor->dpi_list[i]) {
@@ -974,13 +1027,24 @@ hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
break;
}
if (sensor->dpi_list[i] != dpi)
goto out;
return -EINVAL;
}
rc = hidpp20_adjustable_dpi_set_sensor_dpi(drv_data->dev, sensor, dpi);
return hidpp20_adjustable_dpi_set_sensor_dpi(drv_data->dev, sensor, dpi);
}
out:
return rc;
static int
hidpp20drv_update_report_rate_8060(struct ratbag_profile *profile, int hz)
{
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
int rc;
rc = hidpp20_adjustable_report_rate_set_report_rate(drv_data->dev, 1000/hz);
if (rc)
return rc;
return RATBAG_SUCCESS;
}
static int
@@ -1001,12 +1065,20 @@ hidpp20drv_update_report_rate(struct ratbag_profile *profile, int hz)
{
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
int rc;
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
return hidpp20drv_update_report_rate_8100(profile, hz);
if (drv_data->capabilities & HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060)
return -ENOTSUP;
if (drv_data->capabilities & HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060) {
rc = hidpp20drv_update_report_rate_8060(profile, hz);
/* re-populate the profile with the correct value if we fail */
if (rc)
hidpp20drv_read_report_rate_8060(profile->device);
return rc;
}
return -ENOTSUP;
}
@@ -1179,6 +1251,60 @@ hidpp20drv_read_profile_8100(struct ratbag_profile *profile)
ratbag_profile_set_report_rate(profile, p->report_rate);
}
static int
hidpp20drv_init_leds_8070_8071(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag *ratbag = device->ratbag;
/* we only support 0x8071 via 0x8100 */
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) &&
drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071) {
log_debug(ratbag, "disabling 0x8071 (RGB Effects) feature because the device doesn't have 0x8100 (Onboard Memory Profiles)\n");
drv_data->capabilities &= ~HIDPP_CAP_RGB_EFFECTS_8071;
}
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070 ||
drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071) {
/* we read the profile once to get the correct number of
* supported leds. */
if (hidpp20drv_read_color_leds(device))
return 0;
device->num_leds = drv_data->num_leds;
}
return 0;
}
static int
hidpp20drv_init_profile_8100(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
struct ratbag *ratbag = device->ratbag;
int rc;
log_debug(ratbag, "initializing onboard profiles\n");
rc = hidpp20_onboard_profiles_allocate(drv_data->dev, &drv_data->profiles);
if (rc < 0)
return rc;
rc = hidpp20_onboard_profiles_initialize(drv_data->dev, drv_data->profiles);
if (rc < 0)
return rc;
drv_data->num_profiles = drv_data->profiles->num_profiles;
drv_data->num_buttons = drv_data->profiles->num_buttons;
if (drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201)
drv_data->num_resolutions = drv_data->profiles->num_modes;
/* We ignore the profile's num_leds and require
* HIDPP_PAGE_COLOR_LED_EFFECTS to succeed instead
*/
return 0;
}
static void
hidpp20drv_read_profile(struct ratbag_profile *profile)
{
@@ -1313,25 +1439,11 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
log_debug(ratbag, "device has color effects\n");
drv_data->capabilities |= HIDPP_CAP_COLOR_LED_EFFECTS_8070;
/* we read the profile once to get the correct number of
* supported leds. */
if (hidpp20drv_read_color_leds(device))
return 0;
device->num_leds = drv_data->num_leds;
break;
}
case HIDPP_PAGE_RGB_EFFECTS: {
log_debug(ratbag, "device has color effects\n");
drv_data->capabilities |= HIDPP_CAP_RGB_EFFECTS_8071;
/* we read the profile once to get the correct number of
* supported leds. */
if (hidpp20drv_read_color_leds(device))
return 0;
device->num_leds = drv_data->num_leds;
break;
}
case HIDPP_PAGE_LED_SW_CONTROL: {
@@ -1349,22 +1461,6 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
case HIDPP_PAGE_ONBOARD_PROFILES: {
log_debug(ratbag, "device has onboard profiles\n");
drv_data->capabilities |= HIDPP_CAP_ONBOARD_PROFILES_8100;
rc = hidpp20_onboard_profiles_allocate(drv_data->dev, &drv_data->profiles);
if (rc < 0)
return rc;
rc = hidpp20_onboard_profiles_initialize(drv_data->dev, drv_data->profiles);
if (rc < 0)
return rc;
drv_data->num_profiles = drv_data->profiles->num_profiles;
drv_data->num_resolutions = drv_data->profiles->num_modes;
drv_data->num_buttons = drv_data->profiles->num_buttons;
/* We ignore the profile's num_leds and require
* HIDPP_PAGE_COLOR_LED_EFFECTS to succeed instead
*/
break;
}
case HIDPP_PAGE_MOUSE_BUTTON_SPY: {
@@ -1386,27 +1482,27 @@ hidpp20drv_commit(struct ratbag_device *device)
struct ratbag_led *led;
struct ratbag_resolution *resolution;
int rc;
unsigned int dpi_index = 0;
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty)
continue;
rc = hidpp20drv_update_report_rate(profile, profile->hz);
if (rc)
return RATBAG_ERROR_DEVICE;
if (profile->rate_dirty) {
rc = hidpp20drv_update_report_rate(profile, profile->hz);
if (rc) {
log_error(device->ratbag, "hidpp20: failed to update report rate (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
}
ratbag_profile_for_each_resolution(profile, resolution) {
if (resolution->is_active) {
log_raw(device->ratbag, "dpi index: %d, profile %d\n", resolution->index, profile->index);
dpi_index = resolution->index;
}
rc = hidpp20drv_update_resolution_dpi(resolution,
resolution->dpi_x,
resolution->dpi_y);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp20: failed to update resolution (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
}
list_for_each(button, &profile->buttons, link) {
@@ -1414,8 +1510,10 @@ hidpp20drv_commit(struct ratbag_device *device)
continue;
rc = hidpp20drv_update_button(button);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp20: failed to update button (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
}
list_for_each(led, &profile->leds, link) {
@@ -1423,10 +1521,11 @@ hidpp20drv_commit(struct ratbag_device *device)
continue;
rc = hidpp20drv_update_led(led);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp20: failed to update led (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
}
}
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
@@ -1435,17 +1534,18 @@ hidpp20drv_commit(struct ratbag_device *device)
rc = hidpp20_onboard_profiles_commit(drv_data->dev,
drv_data->profiles);
if (rc)
if (rc) {
log_error(device->ratbag, "hidpp20: failed to commit profile (%d)\n", rc);
return RATBAG_ERROR_DEVICE;
}
list_for_each(profile, &device->profiles, link) {
if (profile->is_active) {
ratbag_profile_for_each_resolution(profile, resolution) {
if (resolution->is_active)
dpi_index = resolution->index;
hidpp20_onboard_profiles_set_current_dpi_index(drv_data->dev,
resolution->index);
}
hidpp20_onboard_profiles_set_current_dpi_index(drv_data->dev,
dpi_index);
}
}
}
@@ -1476,8 +1576,21 @@ hidpp20drv_20_probe(struct ratbag_device *device)
return rc;
}
return 0;
/* initializations that depend on other features */
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070 ||
drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071) {
rc = hidpp20drv_init_leds_8070_8071(device);
if (rc < 0)
return rc;
}
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
rc = hidpp20drv_init_profile_8100(device);
if (rc < 0)
return rc;
}
return 0;
}
static int
@@ -1509,9 +1622,9 @@ hidpp20drv_remove(struct ratbag_device *device)
if (drv_data->profiles)
hidpp20_onboard_profiles_destroy(drv_data->profiles);
free(drv_data->led_infos.color_leds_8070);
free(drv_data->led_infos.color_leds_8071);
free(drv_data->controls);
free(drv_data->sensors);
free(drv_data->leds);
if (drv_data->dev)
hidpp20_device_destroy(drv_data->dev);
free(drv_data);
@@ -1523,6 +1636,11 @@ hidpp20drv_init_device(struct ratbag_device *device,
{
struct ratbag_profile *profile;
bool active_profile = false;
int num;
num = ratbag_device_data_hidpp20_get_led_count(device->data);
if (num >= 0)
drv_data->num_leds = num;
ratbag_device_init_profiles(device,
drv_data->num_profiles,
@@ -1576,7 +1694,7 @@ hidpp20drv_probe(struct ratbag_device *device)
* If there is a special need like for G900, we can add this in the
* device data file.
*/
dev = hidpp20_device_new(&base, device_idx);
dev = hidpp20_device_new(&base, device_idx, (struct hidpp_hid_report*) device->hidraw[0].reports, device->hidraw[0].num_reports);
if (!dev) {
rc = -ENODEV;
goto err;
@@ -1593,7 +1711,7 @@ hidpp20drv_probe(struct ratbag_device *device)
/* add some defaults that will be overwritten by the device */
drv_data->num_profiles = 1;
drv_data->num_resolutions = 1;
drv_data->num_resolutions = 0;
drv_data->num_buttons = 8;
drv_data->num_leds = 0;

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

@@ -0,0 +1,845 @@
/*
* Copyright © 2015 Red Hat, Inc.
*
* 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 <assert.h>
#include <errno.h>
#include <libevdev/libevdev.h>
#include <linux/input.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
#define ROCCAT_PROFILE_MAX 4
#define ROCCAT_BUTTON_MAX 17
#define ROCCAT_NUM_DPI 5
#define ROCCAT_LED_MAX 0
#define ROCCAT_MAX_RETRY_READY 10
#define ROCCAT_REPORT_ID_CONFIGURE_PROFILE 4
#define ROCCAT_REPORT_ID_PROFILE 5
#define ROCCAT_REPORT_ID_SETTINGS 6
#define ROCCAT_REPORT_ID_KEY_MAPPING 7
#define ROCCAT_REPORT_ID_MACRO 8
#define ROCCAT_REPORT_SIZE_PROFILE 59
#define ROCCAT_REPORT_SIZE_SETTINGS 31
#define ROCCAT_REPORT_SIZE_MACRO 2082
#define ROCCAT_CONFIG_SETTINGS 0x80
#define ROCCAT_CONFIG_KEY_MAPPING 0x90
#define ROCCAT_MAX_MACRO_LENGTH 500
struct roccat_settings_report {
uint8_t reportID;
uint8_t twoB;
uint8_t profileID;
uint8_t x_y_linked;
uint8_t x_sensitivity; /* 0x06 means 0 */
uint8_t y_sensitivity; /* 0x06 means 0 */
uint8_t dpi_mask;
uint8_t xres[5];
uint8_t current_dpi;
uint8_t yres[5];
uint8_t padding1;
uint8_t report_rate;
uint8_t padding2[11];
// uint16_t checksum;
// uint8_t padding2[4];
// uint8_t light;
// uint8_t light_heartbit;
// uint8_t padding3[5];
} __attribute__((packed));
struct roccat_macro {
uint8_t reportID;
uint8_t twentytwo;
uint8_t height;
uint8_t profile;
uint8_t button_index;
uint8_t active;
uint8_t padding[24];
char group[24];
char name[24];
uint16_t length;
struct {
uint8_t keycode;
uint8_t flag;
uint16_t time;
} keys[ROCCAT_MAX_MACRO_LENGTH];
uint16_t checksum;
} __attribute__((packed));
struct roccat_data {
uint8_t profiles[(ROCCAT_PROFILE_MAX + 1)][ROCCAT_REPORT_SIZE_PROFILE];
struct roccat_settings_report settings[(ROCCAT_PROFILE_MAX + 1)];
struct roccat_macro macros[(ROCCAT_PROFILE_MAX + 1)][(ROCCAT_BUTTON_MAX + 1)];
};
struct roccat_button_type_mapping {
uint8_t raw;
enum ratbag_button_type type;
};
static const struct roccat_button_type_mapping roccat_button_type_mapping[] = {
{ 0, RATBAG_BUTTON_TYPE_LEFT },
{ 1, RATBAG_BUTTON_TYPE_RIGHT },
{ 2, RATBAG_BUTTON_TYPE_MIDDLE },
{ 3, RATBAG_BUTTON_TYPE_EXTRA },
{ 4, RATBAG_BUTTON_TYPE_SIDE },
{ 5, RATBAG_BUTTON_TYPE_WHEEL_UP },
{ 6, RATBAG_BUTTON_TYPE_WHEEL_DOWN },
{ 7, RATBAG_BUTTON_TYPE_RESOLUTION_UP },
{ 8, RATBAG_BUTTON_TYPE_RESOLUTION_DOWN },
{ 9, RATBAG_BUTTON_TYPE_LEFT },
{ 10, RATBAG_BUTTON_TYPE_RIGHT },
{ 11, RATBAG_BUTTON_TYPE_MIDDLE },
{ 12, RATBAG_BUTTON_TYPE_EXTRA },
{ 13, RATBAG_BUTTON_TYPE_SIDE },
{ 14, RATBAG_BUTTON_TYPE_WHEEL_UP },
{ 15, RATBAG_BUTTON_TYPE_WHEEL_DOWN },
{ 16, RATBAG_BUTTON_TYPE_RESOLUTION_UP },
{ 17, RATBAG_BUTTON_TYPE_RESOLUTION_DOWN },
};
static enum ratbag_button_type
roccat_raw_to_button_type(uint8_t data)
{
const struct roccat_button_type_mapping *mapping;
ARRAY_FOR_EACH(roccat_button_type_mapping, mapping) {
if (mapping->raw == data)
return mapping->type;
}
return RATBAG_BUTTON_TYPE_UNKNOWN;
}
struct roccat_button_mapping {
uint8_t raw;
struct ratbag_button_action action;
};
static struct roccat_button_mapping roccat_button_mapping[] = {
/* FIXME: { 0, Disabled }, */
{ 1, BUTTON_ACTION_BUTTON(1) },
{ 2, BUTTON_ACTION_BUTTON(2) },
{ 3, BUTTON_ACTION_BUTTON(3) },
{ 4, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK) },
/* FIXME: { 5, Shortcut (modifier + key) }, */
{ 6, BUTTON_ACTION_NONE },
{ 7, BUTTON_ACTION_BUTTON(4) },
{ 8, BUTTON_ACTION_BUTTON(5) },
//{ 9, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT) },
//{ 10, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT) },
{ 13, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP) },
{ 14, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN) },
/* FIXME: { 15, quicklaunch }, -> hidraw report 03 00 60 07 01 00 00 00 */
{ 16, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP) },
{ 17, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP) },
{ 18, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN) },
{ 20, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP) },
{ 21, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP) },
{ 22, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN) },
{ 26, BUTTON_ACTION_KEY(KEY_LEFTMETA) },
/* FIXME: { 27, open driver }, -> hidraw report 02 83 01 00 00 00 00 00 */
//{ 32, BUTTON_ACTION_KEY(KEY_CONFIG) },
{ 33, BUTTON_ACTION_KEY(KEY_PREVIOUSSONG) },
{ 34, BUTTON_ACTION_KEY(KEY_NEXTSONG) },
{ 35, BUTTON_ACTION_KEY(KEY_PLAYPAUSE) },
{ 36, BUTTON_ACTION_KEY(KEY_STOPCD) },
{ 37, BUTTON_ACTION_KEY(KEY_MUTE) },
{ 38, BUTTON_ACTION_KEY(KEY_VOLUMEUP) },
{ 39, BUTTON_ACTION_KEY(KEY_VOLUMEDOWN) },
{ 48, BUTTON_ACTION_MACRO },
{ 65, BUTTON_ACTION_SPECIAL(RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE) },
/* FIXME: { 66, Easywheel sensitivity }, */
/* FIXME: { 67, Easywheel profile }, */
/* FIXME: { 68, Easywheel CPI }, */
/* FIXME: { 81, Other Easyshift }, -> hidraw report 03 00 ff 05 01 00 00 00 */
/* FIXME: { 82, Other Easyshift Lock }, -> hidraw report 03 00 ff 05 01 00 00 00 */
/* FIXME: { 83, Both Easyshift }, -> hidraw report 03 00 ff 04 01 00 00 00 */
};
static const struct ratbag_button_action*
roccat_raw_to_button_action(uint8_t data)
{
struct roccat_button_mapping *mapping;
ARRAY_FOR_EACH(roccat_button_mapping, mapping) {
if (mapping->raw == data)
return &mapping->action;
}
return NULL;
}
static uint8_t
roccat_button_action_to_raw(const struct ratbag_button_action *action)
{
struct roccat_button_mapping *mapping;
ARRAY_FOR_EACH(roccat_button_mapping, mapping) {
if (ratbag_button_action_match(&mapping->action, action))
return mapping->raw;
}
return 0;
}
static inline uint16_t
roccat_get_unaligned_u16(uint8_t *buf)
{
return (buf[1] << 8) | buf[0];
}
static inline uint16_t
roccat_compute_crc(uint8_t *buf, unsigned int len)
{
unsigned i;
uint16_t crc = 0;
if (len < 3)
return 0;
for (i = 0; i < len - 2; i++) {
crc += buf[i];
}
return crc;
}
static inline int
roccat_crc_is_valid(struct ratbag_device *device, uint8_t *buf, unsigned int len)
{
uint16_t crc;
uint16_t given_crc;
if (len < 3)
return 0;
crc = roccat_compute_crc(buf, len);
given_crc = roccat_get_unaligned_u16(&buf[len - 2]);
log_raw(device->ratbag,
"checksum computed: 0x%04x, checksum given: 0x%04x\n",
crc,
given_crc);
return crc == given_crc;
}
static int
roccat_is_ready(struct ratbag_device *device)
{
uint8_t buf[3] = { 0 };
int rc;
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_CONFIGURE_PROFILE,
buf, sizeof(buf));
if (rc < 0)
return rc;
if (rc != sizeof(buf))
return -EIO;
if (buf[1] == 0x03)
msleep(100);
if (buf[1] == 0x02)
return 2;
return buf[1] == 0x01;
}
static int
roccat_wait_ready(struct ratbag_device *device)
{
unsigned count = 0;
int rc;
msleep(10);
while (count < ROCCAT_MAX_RETRY_READY) {
rc = roccat_is_ready(device);
if (rc < 0)
return rc;
if (rc == 1)
return 0;
if (rc == 2)
return 2;
msleep(10);
count++;
}
return -ETIMEDOUT;
}
static int
roccat_current_profile(struct ratbag_device *device)
{
uint8_t buf[3];
int ret;
ret = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_PROFILE, buf,
sizeof(buf));
if (ret < 0)
return ret;
if (ret != 3)
return -EIO;
return buf[2];
}
static int
roccat_set_current_profile(struct ratbag_device *device, unsigned int index)
{
uint8_t buf[] = {ROCCAT_REPORT_ID_PROFILE, 0x03, index};
int ret;
if (index > ROCCAT_PROFILE_MAX)
return -EINVAL;
ret = ratbag_hidraw_set_feature_report(device, buf[0], buf,
sizeof(buf));
if (ret < 0)
return ret;
if (ret != sizeof(buf))
return -EIO;
ret = roccat_wait_ready(device);
if (ret)
log_error(device->ratbag,
"Error while waiting for the device to be ready: %s (%d)\n",
strerror(-ret), ret);
return ret;
}
static int
roccat_set_config_profile(struct ratbag_device *device, uint8_t profile, uint8_t type)
{
uint8_t buf[] = {ROCCAT_REPORT_ID_CONFIGURE_PROFILE, profile, type};
int ret;
if (profile > ROCCAT_PROFILE_MAX)
return -EINVAL;
ret = ratbag_hidraw_set_feature_report(device, buf[0], buf,
sizeof(buf));
if (ret < 0)
return ret;
if (ret != sizeof(buf))
return -EIO;
ret = roccat_wait_ready(device);
if (ret < 0)
log_error(device->ratbag,
"Error while waiting for the device to be ready: %s (%d)\n",
strerror(-ret), ret);
return ret;
}
static const struct ratbag_button_action *
roccat_button_to_action(struct ratbag_profile *profile,
unsigned int button_index)
{
struct ratbag_device *device = profile->device;
struct roccat_data *drv_data = ratbag_get_drv_data(device);
uint8_t data;
data = drv_data->profiles[profile->index][3 + button_index * 3];
return roccat_raw_to_button_action(data);
}
static int
roccat_write_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
unsigned int index = profile->index;
struct roccat_data *drv_data;
int rc;
uint8_t *buf;
uint16_t *crc;
assert(index <= ROCCAT_PROFILE_MAX);
drv_data = ratbag_get_drv_data(device);
buf = drv_data->profiles[index];
crc = (uint16_t *)&buf[ROCCAT_REPORT_SIZE_PROFILE - 2];
*crc = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_PROFILE);
roccat_set_config_profile(device, index, ROCCAT_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE);
if (rc < ROCCAT_REPORT_SIZE_PROFILE)
return -EIO;
log_raw(device->ratbag, "profile: %d written %s:%d\n",
buf[2],
__FILE__, __LINE__);
rc = roccat_wait_ready(device);
if (rc)
log_error(device->ratbag,
"Error while waiting for the device to be ready: %s (%d)\n",
strerror(-rc), rc);
return rc;
}
static void
roccat_read_button(struct ratbag_button *button)
{
const struct ratbag_button_action *action;
struct ratbag_device *device;
struct roccat_macro *macro;
struct roccat_data *drv_data;
uint8_t *buf;
unsigned j, time;
int rc;
device = button->profile->device;
drv_data = ratbag_get_drv_data(device);
action = roccat_button_to_action(button->profile, button->index);
if (action)
ratbag_button_set_action(button, action);
button->type = roccat_raw_to_button_type(button->index);
// if (action == NULL)
// log_error(device->ratbag, "button: %d -> %d %s:%d\n",
// button->index, drv_data->profiles[button->profile->index][3 + button->index * 3],
// __FILE__, __LINE__);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
if (action && action->type == RATBAG_BUTTON_ACTION_TYPE_MACRO) {
struct ratbag_button_macro *m = NULL;
roccat_set_config_profile(device,
button->profile->index,
0);
roccat_set_config_profile(device,
button->profile->index,
button->index);
macro = &drv_data->macros[button->profile->index][button->index];
buf = (uint8_t*)macro;
buf[0] = ROCCAT_REPORT_ID_MACRO;
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO);
if (rc != ROCCAT_REPORT_SIZE_MACRO) {
log_error(device->ratbag,
"Unable to retrieve the macro for button %d of profile %d: %s (%d)\n",
button->index, button->profile->index,
rc < 0 ? strerror(-rc) : "not read enough", rc);
} else {
if (buf[0] != ROCCAT_REPORT_ID_MACRO) {
log_error(device->ratbag,
"Error while reading the macro of button %d of profile %d.\n",
button->index,
button->profile->index);
goto out_macro;
}
if (!roccat_crc_is_valid(device, buf, ROCCAT_REPORT_SIZE_MACRO)) {
log_error(device->ratbag,
"wrong checksum while reading the macro of button %d of profile %d.\n",
button->index,
button->profile->index);
goto out_macro;
}
m = ratbag_button_macro_new(macro->name);
log_raw(device->ratbag,
"macro on button %d of profile %d is named '%s', and contains %d events:\n",
button->index, button->profile->index,
macro->name, macro->length);
for (j = 0; j < macro->length; j++) {
unsigned int keycode = ratbag_hidraw_get_keycode_from_keyboard_usage(device,
macro->keys[j].keycode);
ratbag_button_macro_set_event(m,
j * 2,
macro->keys[j].flag & 0x01 ? RATBAG_MACRO_EVENT_KEY_PRESSED : RATBAG_MACRO_EVENT_KEY_RELEASED,
keycode);
if (macro->keys[j].time)
time = macro->keys[j].time;
else
time = macro->keys[j].flag & 0x01 ? 10 : 50;
ratbag_button_macro_set_event(m,
j * 2 + 1,
RATBAG_MACRO_EVENT_WAIT,
time);
log_raw(device->ratbag,
" - %s %s\n",
libevdev_event_code_get_name(EV_KEY, keycode),
macro->keys[j].flag & 0x80 ? "released" : "pressed");
}
ratbag_button_copy_macro(button, m);
}
out_macro:
msleep(10);
ratbag_button_macro_unref(m);
}
}
static int
roccat_write_macro(struct ratbag_button *button,
const struct ratbag_button_action *action)
{
struct ratbag_device *device;
struct roccat_macro *macro;
struct roccat_data *drv_data;
uint8_t *buf;
unsigned i, count = 0;
int rc;
if (action->type != RATBAG_BUTTON_ACTION_TYPE_MACRO)
return 0;
device = button->profile->device;
drv_data = ratbag_get_drv_data(device);
macro = &drv_data->macros[button->profile->index][button->index];
buf = (uint8_t*)macro;
memset(buf, 0, ROCCAT_REPORT_SIZE_MACRO);
for (i = 0; i < MAX_MACRO_EVENTS && count < ROCCAT_MAX_MACRO_LENGTH; i++) {
if (action->macro->events[i].type == RATBAG_MACRO_EVENT_INVALID)
return -EINVAL; /* should not happen, ever */
if (action->macro->events[i].type == RATBAG_MACRO_EVENT_NONE)
break;
/* ignore the first wait */
if (action->macro->events[i].type == RATBAG_MACRO_EVENT_WAIT &&
!count)
continue;
if (action->macro->events[i].type == RATBAG_MACRO_EVENT_KEY_PRESSED ||
action->macro->events[i].type == RATBAG_MACRO_EVENT_KEY_RELEASED) {
macro->keys[count].keycode = ratbag_hidraw_get_keyboard_usage_from_keycode(device, action->macro->events[i].event.key);
}
switch (action->macro->events[i].type) {
case RATBAG_MACRO_EVENT_KEY_PRESSED:
macro->keys[count].flag = 0x01;
break;
case RATBAG_MACRO_EVENT_KEY_RELEASED:
macro->keys[count].flag = 0x02;
break;
case RATBAG_MACRO_EVENT_WAIT:
macro->keys[--count].time = action->macro->events[i].event.timeout;
break;
case RATBAG_MACRO_EVENT_INVALID:
case RATBAG_MACRO_EVENT_NONE:
/* should not happen */
log_error(device->ratbag,
"something went wrong while writing a macro.\n");
}
count++;
}
macro->reportID = ROCCAT_REPORT_ID_MACRO;
macro->twentytwo = 0x22;
macro->height = 0x08;
macro->profile = button->profile->index;
macro->button_index = button->index;
macro->active = 0x01;
strcpy(macro->group, "g0");
strncpy(macro->name, action->macro->name, 23);
macro->length = count;
macro->checksum = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_MACRO);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO);
if (rc < 0)
return rc;
if (rc != ROCCAT_REPORT_SIZE_MACRO)
return -EIO;
rc = roccat_wait_ready(device);
if (rc)
log_error(device->ratbag,
"Error while waiting for the device to be ready: %s (%d)\n",
strerror(-rc), rc);
return rc;
}
static int
roccat_write_button(struct ratbag_button *button,
const struct ratbag_button_action *action)
{
struct ratbag_profile *profile = button->profile;
struct ratbag_device *device = profile->device;
struct roccat_data *drv_data = ratbag_get_drv_data(device);
uint8_t rc, *data;
data = &drv_data->profiles[profile->index][3 + button->index * 3];
rc = roccat_button_action_to_raw(action);
if (!rc)
return -EINVAL;
*data = rc;
rc = roccat_write_profile(button->profile);
if (rc) {
log_error(device->ratbag,
"unable to write the profile to the device: '%s' (%d)\n",
strerror(-rc), rc);
return rc;
}
rc = roccat_write_macro(button, action);
if (rc) {
log_error(device->ratbag,
"unable to write the macro to the device: '%s' (%d)\n",
strerror(-rc), rc);
return rc;
}
return rc;
}
static int
roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
{
struct ratbag_profile *profile = resolution->profile;
struct ratbag_device *device = profile->device;
struct roccat_data *drv_data = ratbag_get_drv_data(device);
struct roccat_settings_report *settings_report;
uint8_t *buf;
int rc;
if (dpi_x < 100 || dpi_x > 5000 || dpi_x % 50)
return -EINVAL;
if (dpi_y < 100 || dpi_y > 5000 || dpi_y % 50)
return -EINVAL;
settings_report = &drv_data->settings[profile->index];
settings_report->xres[resolution->index] = dpi_x / 50;
settings_report->yres[resolution->index] = dpi_y / 50;
if (resolution->is_active)
settings_report->current_dpi = resolution->index;
buf = (uint8_t*)settings_report;
// No checksum for settings report on Kone Pure.
//settings_report->checksum = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_SETTINGS);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS);
if (rc < 0)
return rc;
if (rc != ROCCAT_REPORT_SIZE_SETTINGS)
return -EIO;
rc = roccat_wait_ready(device);
if (rc)
log_error(device->ratbag,
"Error while waiting for the device to be ready: %s (%d)\n",
strerror(-rc), rc);
return rc;
}
static void
roccat_read_profile(struct ratbag_profile *profile)
{
struct ratbag_device *device = profile->device;
struct roccat_data *drv_data;
struct ratbag_resolution *resolution;
struct ratbag_button *button;
struct roccat_settings_report *setting_report;
uint8_t *buf;
unsigned int report_rate;
int dpi_x, dpi_y;
int rc;
unsigned int report_rates[] = { 125, 250, 500, 1000 };
assert(profile->index <= ROCCAT_PROFILE_MAX);
drv_data = ratbag_get_drv_data(device);
setting_report = &drv_data->settings[profile->index];
buf = (uint8_t*)setting_report;
roccat_set_config_profile(device, profile->index, ROCCAT_CONFIG_SETTINGS);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS);
if (rc < ROCCAT_REPORT_SIZE_SETTINGS)
return;
/* first retrieve the report rate, it is set per profile */
if (setting_report->report_rate < ARRAY_LENGTH(report_rates)) {
report_rate = report_rates[setting_report->report_rate];
} else {
log_error(device->ratbag,
"error while reading the report rate of the mouse (0x%02x)\n",
buf[26]);
report_rate = 0;
}
ratbag_profile_set_report_rate_list(profile, report_rates,
ARRAY_LENGTH(report_rates));
ratbag_profile_set_report_rate(profile, report_rate);
ratbag_profile_for_each_resolution(profile, resolution) {
dpi_x = setting_report->xres[resolution->index] * 50;
dpi_y = setting_report->yres[resolution->index] * 50;
if (!(setting_report->dpi_mask & (1 << resolution->index))) {
/* the profile is disabled, overwrite it */
dpi_x = 0;
dpi_y = 0;
}
ratbag_resolution_set_resolution(resolution, dpi_x, dpi_y);
ratbag_resolution_set_cap(resolution,
RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
resolution->is_active = (resolution->index == setting_report->current_dpi);
ratbag_resolution_set_dpi_list_from_range(resolution, 100, 5000);
}
buf = drv_data->profiles[profile->index];
roccat_set_config_profile(device, profile->index, ROCCAT_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE);
msleep(10);
if (rc < ROCCAT_REPORT_SIZE_PROFILE)
return;
// Buttons were read from the buffer that was yet un-initialized with the device data.
ratbag_profile_for_each_button(profile, button)
roccat_read_button(button);
if (!roccat_crc_is_valid(device, buf, ROCCAT_REPORT_SIZE_PROFILE))
log_error(device->ratbag,
"Error while reading profile %d, continuing...\n",
profile->index);
log_raw(device->ratbag, "profile: %d %s:%d\n",
buf[2],
__FILE__, __LINE__);
}
static int
roccat_probe(struct ratbag_device *device)
{
int rc;
struct ratbag_profile *profile;
struct roccat_data *drv_data;
int active_idx;
rc = ratbag_open_hidraw(device);
if (rc)
return rc;
if (!ratbag_hidraw_has_report(device, ROCCAT_REPORT_ID_KEY_MAPPING)) {
ratbag_close_hidraw(device);
return -ENODEV;
}
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
/* profiles are 0-indexed */
ratbag_device_init_profiles(device,
ROCCAT_PROFILE_MAX + 1,
ROCCAT_NUM_DPI,
ROCCAT_BUTTON_MAX + 1,
ROCCAT_LED_MAX);
ratbag_device_for_each_profile(device, profile)
roccat_read_profile(profile);
active_idx = roccat_current_profile(device);
if (active_idx < 0) {
log_error(device->ratbag,
"Can't talk to the mouse: '%s' (%d)\n",
strerror(-active_idx),
active_idx);
rc = -ENODEV;
goto err;
}
list_for_each(profile, &device->profiles, link) {
if (profile->index == (unsigned int)active_idx) {
profile->is_active = true;
break;
}
}
log_raw(device->ratbag,
"'%s' is in profile %d\n",
ratbag_device_get_name(device),
profile->index);
return 0;
err:
free(drv_data);
ratbag_set_drv_data(device, NULL);
return rc;
}
static void
roccat_remove(struct ratbag_device *device)
{
ratbag_close_hidraw(device);
free(ratbag_get_drv_data(device));
}
struct ratbag_driver roccat_driver = {
.name = "Roccat Kone Pure",
.id = "roccat-kone-pure",
.probe = roccat_probe,
.remove = roccat_remove,
.write_profile = roccat_write_profile,
.set_active_profile = roccat_set_current_profile,
.write_button = roccat_write_button,
.write_resolution_dpi = roccat_write_resolution_dpi,
};

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

@@ -306,6 +306,33 @@ hidpp_read_response(struct hidpp_device *dev, uint8_t *buf, size_t size)
return rc >= 0 ? rc : -errno;
}
void
hidpp_get_supported_report_types(struct hidpp_device *dev, struct hidpp_hid_report *reports, unsigned int num_reports)
{
if (!reports) {
hidpp_log_debug(dev, "hidpp: we don't have information about the hid reports, ignoring checks\n");
dev->supported_report_types = 0xffff;
return;
}
/* reset the bits we are gonna check */
dev->supported_report_types &= (0xff & ~HIDPP_REPORT_SHORT);
dev->supported_report_types &= (0xff & ~HIDPP_REPORT_LONG);
for (unsigned i = 0; i < num_reports; i++)
if ((reports[i].usage_page & 0xff00) == 0xff00) /* vendor defined usage page (0xff00-0xffff) */
switch (reports[i].report_id) {
case REPORT_ID_SHORT:
hidpp_log_debug(dev, "hidpp: device supports short reports\n");
dev->supported_report_types |= HIDPP_REPORT_SHORT;
break;
case REPORT_ID_LONG:
hidpp_log_debug(dev, "hidpp: device supports long reports\n");
dev->supported_report_types |= HIDPP_REPORT_LONG;
break;
}
}
void
hidpp_log(struct hidpp_device *dev,
enum hidpp_log_priority priority,
@@ -363,6 +390,7 @@ hidpp_device_init(struct hidpp_device *dev, int fd)
{
dev->hidraw_fd = fd;
hidpp_device_set_log_handler(dev, simple_log, HIDPP_LOG_PRIORITY_INFO, NULL);
dev->supported_report_types = 0;
}
void

View File

@@ -95,13 +95,23 @@ typedef void (*hidpp_log_handler)(void *userdata,
const char *format, va_list args)
__attribute__ ((format (printf, 3, 0)));
struct hidpp_hid_report {
unsigned int report_id;
unsigned int usage_page;
unsigned int usage;
};
struct hidpp_device {
int hidraw_fd;
void *userdata;
hidpp_log_handler log_handler;
enum hidpp_log_priority log_priority;
unsigned supported_report_types;
};
#define HIDPP_REPORT_SHORT (1 << 0)
#define HIDPP_REPORT_LONG (1 << 1)
void
hidpp_device_init(struct hidpp_device *dev, int fd);
void
@@ -140,6 +150,11 @@ hidpp_write_command(struct hidpp_device *dev, uint8_t *cmd, int size);
int
hidpp_read_response(struct hidpp_device *dev, uint8_t *buf, size_t size);
void
hidpp_get_supported_report_types(struct hidpp_device *dev,
struct hidpp_hid_report *reports,
unsigned int num_reports);
void
hidpp_log(struct hidpp_device *dev,
enum hidpp_log_priority priority,

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];
@@ -1144,6 +1148,9 @@ hidpp10_onboard_profiles_read_macro(struct hidpp10_device *device,
}
if (rc == -ENOMEM) {
if ((unsigned) offset + mem_index > 0xff)
goto out_err;
offset += mem_index;
if (offset & 0x01)
offset--;
@@ -1198,8 +1205,10 @@ hidpp10_onboard_profiles_parse_macro(struct hidpp10_device *device,
hidpp_log_raw(&device->base, "*** macro starts at (0x%02x, 0x%04x) ***\n", page, offset);
rc = hidpp10_onboard_profiles_read_macro(device, page, offset, &macro);
if (rc)
if (rc) {
hidpp_log_raw(&device->base, "hidpp10: failed to read macro\n");
return rc;
}
count = rc;
@@ -2754,12 +2763,14 @@ hidpp10_get_device_info(struct hidpp10_device *dev)
return hidpp10_get_current_profile(dev, &current_profile);
}
struct hidpp10_device*
int
hidpp10_device_new(const struct hidpp_device *base,
int idx,
enum hidpp10_profile_type type,
unsigned int profile_count)
unsigned int profile_count,
struct hidpp10_device **out)
{
int rc;
struct hidpp10_device *dev;
dev = zalloc(sizeof(*dev));
@@ -2770,12 +2781,14 @@ hidpp10_device_new(const struct hidpp_device *base,
dev->profile_count = profile_count;
dev->profiles = zalloc(dev->profile_count * sizeof(struct hidpp10_profile));
if (hidpp10_get_device_info(dev) != 0) {
if ((rc = hidpp10_get_device_info(dev)) != 0) {
hidpp10_device_destroy(dev);
dev = NULL;
}
return dev;
*out = dev;
return rc;
}
int

View File

@@ -69,11 +69,12 @@ struct hidpp10_device {
unsigned int profile_count;
};
struct hidpp10_device*
int
hidpp10_device_new(const struct hidpp_device *base,
int idx,
enum hidpp10_profile_type type,
unsigned int profile_count);
unsigned int profile_count,
struct hidpp10_device **out);
int
hidpp10_device_read_profiles(struct hidpp10_device *dev);

View File

@@ -133,6 +133,16 @@ hidpp20_request_command_allow_error(struct hidpp20_device *device, union hidpp20
}
msg->msg.address |= DEVICE_SW_ID;
/* some mice don't support short reports */
if (msg->msg.report_id == REPORT_ID_SHORT && !(device->base.supported_report_types & HIDPP_REPORT_SHORT))
msg->msg.report_id = REPORT_ID_LONG;
/* sanity check */
if (msg->msg.report_id == REPORT_ID_LONG && !(device->base.supported_report_types & HIDPP_REPORT_LONG)) {
hidpp_log_error(&device->base, "hidpp20: trying to use unsupported report type\n");
return -EINVAL;
}
msg_len = msg->msg.report_id == REPORT_ID_SHORT ? SHORT_MESSAGE_LENGTH : LONG_MESSAGE_LENGTH;
/* Send the message to the Device */
@@ -250,7 +260,7 @@ hidpp_root_get_feature(struct hidpp20_device *device,
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.sub_id = HIDPP_PAGE_ROOT_IDX,
.msg.address = CMD_ROOT_GET_FEATURE,
@@ -277,7 +287,7 @@ hidpp20_root_get_protocol_version(struct hidpp20_device *device,
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.sub_id = HIDPP_PAGE_ROOT_IDX,
.msg.address = CMD_ROOT_GET_PROTOCOL_VERSION,
@@ -311,7 +321,7 @@ hidpp20_feature_set_get_count(struct hidpp20_device *device, uint8_t reg)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.sub_id = reg,
.msg.address = CMD_FEATURE_SET_GET_COUNT,
@@ -333,7 +343,7 @@ hidpp20_feature_set_get_feature_id(struct hidpp20_device *device,
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.sub_id = reg,
.msg.address = CMD_FEATURE_SET_GET_FEATURE_ID,
@@ -419,7 +429,7 @@ hidpp20_batterylevel_get_battery_level(struct hidpp20_device *device,
{
uint8_t feature_index;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS,
};
@@ -556,7 +566,7 @@ int hidpp20_led_sw_control_get_led_count(struct hidpp20_device* device)
{
union hidpp20_message msg = {
.msg = {
.report_id = REPORT_ID_LONG,
.report_id = REPORT_ID_SHORT,
.address = CMD_LED_SW_CONTROL_GET_LED_COUNT,
.device_idx = device->index,
},
@@ -584,7 +594,7 @@ int hidpp20_led_sw_control_get_led_info(struct hidpp20_device* device,
{
union hidpp20_message msg = {
.msg = {
.report_id = REPORT_ID_LONG,
.report_id = REPORT_ID_SHORT,
.address = CMD_LED_SW_CONTROL_GET_LED_INFO,
.device_idx = device->index,
},
@@ -672,7 +682,7 @@ int hidpp20_led_sw_control_get_led_state(struct hidpp20_device* device,
uint8_t feature_idx;
union hidpp20_message msg = {
.msg = {
.report_id = REPORT_ID_LONG,
.report_id = REPORT_ID_SHORT,
.address = CMD_LED_SW_CONTROL_GET_LED_STATE,
.device_idx = device->index,
}
@@ -757,7 +767,7 @@ static int
hidpp20_kbd_reprogrammable_keys_get_count(struct hidpp20_device *device, uint8_t reg)
{
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.sub_id = reg,
.msg.address = CMD_KBD_REPROGRAMMABLE_KEYS_GET_COUNT,
@@ -778,7 +788,7 @@ hidpp20_kbd_reprogrammable_keys_get_info(struct hidpp20_device *device,
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.sub_id = reg,
.msg.address = CMD_KBD_REPROGRAMMABLE_KEYS_GET_CTRL_ID_INFO,
@@ -858,6 +868,8 @@ err:
#define CMD_COLOR_LED_EFFECTS_GET_INFO 0x00
#define CMD_COLOR_LED_EFFECTS_GET_ZONE_INFO 0x10
#define CMD_COLOR_LED_EFFECTS_GET_ZONE_EFFECT_INFO 0x20
#define CMD_COLOR_LED_EFFECTS_SET_ZONE_EFFECT 0x30
#define CMD_COLOR_LED_EFFECTS_GET_ZONE_EFFECT 0xe0
int
hidpp20_color_led_effects_get_info(struct hidpp20_device *device,
@@ -883,6 +895,7 @@ hidpp20_color_led_effects_get_info(struct hidpp20_device *device,
return rc;
*info = *(struct hidpp20_color_led_info *)msg.msg.parameters;
device->led_ext_caps = info->ext_caps;
return 0;
}
@@ -895,7 +908,7 @@ hidpp20_color_led_effects_get_zone_info(struct hidpp20_device *device,
int rc;
struct hidpp20_color_led_zone_info *msg_info;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.sub_id = reg,
.msg.address = CMD_COLOR_LED_EFFECTS_GET_ZONE_INFO,
@@ -1002,6 +1015,80 @@ hidpp20_color_led_effect_get_zone_effect_info(struct hidpp20_device *device,
return 0;
}
int
hidpp20_color_led_effects_set_zone_effect(struct hidpp20_device *device,
uint8_t zone_index,
struct hidpp20_led led)
{
uint8_t feature_index;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.address = CMD_COLOR_LED_EFFECTS_SET_ZONE_EFFECT,
.msg.device_idx = device->index,
.msg.parameters[0] = zone_index,
.msg.parameters[12] = 1, /* write to RAM and flash */
};
int rc;
struct hidpp20_internal_led *internal_led = (struct hidpp20_internal_led*) &msg.msg.parameters[1];
hidpp20_onboard_profiles_write_led(internal_led, &led);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_COLOR_LED_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
return 0;
}
int
hidpp20_color_led_effects_get_zone_effect(struct hidpp20_device *device,
uint8_t zone_index,
struct hidpp20_led *led)
{
uint8_t feature_index;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_SHORT,
.msg.address = CMD_COLOR_LED_EFFECTS_GET_ZONE_EFFECT,
.msg.device_idx = device->index,
.msg.parameters[0] = zone_index,
};
struct hidpp20_internal_led *internal_led;
int rc;
/* hidpp20_color_led_effects_get_info() must be called first to set the capabilities */
if (!(device->led_ext_caps & HIDPP20_COLOR_LED_INFO_EXT_CAP_HAS_ZONE_EFFECT))
return -ENOTSUP;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_COLOR_LED_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
if (msg.msg.parameters[0] != zone_index)
return -EPROTO;
internal_led = (struct hidpp20_internal_led*) &msg.msg.parameters[1];
hidpp20_onboard_profiles_read_led(led, *internal_led);
hidpp_log_debug(&device->base, "zone %u has effect %u\n", zone_index, led->mode);
return 0;
}
/* -------------------------------------------------------------------------- */
/* 0x8071: RGB Effects */
/* -------------------------------------------------------------------------- */
@@ -1022,7 +1109,7 @@ hidpp20_rgb_effects_get_device_info(struct hidpp20_device *device,
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
@@ -1059,7 +1146,7 @@ hidpp20_rgb_effects_get_cluster_info(struct hidpp20_device *device,
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = cluster_index,
@@ -1142,7 +1229,7 @@ hidpp20_rgb_effects_get_effect_info(struct hidpp20_device *device,
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = cluster_index,
@@ -1186,7 +1273,7 @@ hidpp20_special_keys_buttons_get_count(struct hidpp20_device *device, uint8_t re
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.sub_id = reg,
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_COUNT,
@@ -1206,7 +1293,7 @@ hidpp20_special_keys_buttons_get_info(struct hidpp20_device *device,
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.sub_id = reg,
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_INFO,
@@ -1236,7 +1323,7 @@ hidpp20_special_keys_buttons_get_reporting(struct hidpp20_device *device,
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.sub_id = reg,
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_REPORTING,
@@ -1377,7 +1464,7 @@ hidpp20_mousepointer_get_mousepointer_info(struct hidpp20_device *device,
{
uint8_t feature_index;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_MOUSE_POINTER_BASIC_GET_INFO,
};
@@ -1414,7 +1501,7 @@ hidpp20_adjustable_dpi_get_count(struct hidpp20_device *device, uint8_t reg)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.sub_id = reg,
.msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_COUNT,
@@ -1435,7 +1522,7 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
int rc;
unsigned i = 1, dpi_index = 0;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.sub_id = reg,
.msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI_LIST,
@@ -1483,7 +1570,7 @@ hidpp20_adjustable_dpi_get_dpi(struct hidpp20_device *device,
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.sub_id = reg,
.msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI,
@@ -1566,7 +1653,7 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
uint16_t returned_parameters;
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_ADJUSTABLE_DPI_SET_SENSOR_DPI,
.msg.parameters[0] = sensor->index,
@@ -1602,6 +1689,8 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
/* -------------------------------------------------------------------------- */
#define CMD_ADJUSTABLE_REPORT_RATE_GET_REPORT_RATE_LIST 0x00
#define CMD_ADJUSTABLE_REPORT_RATE_GET_REPORT_RATE 0x10
#define CMD_ADJUSTABLE_REPORT_RATE_SET_REPORT_RATE 0x20
int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *device,
uint8_t *bitflags_ms)
@@ -1609,10 +1698,9 @@ int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *d
uint8_t feature_index;
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_ADJUSTABLE_REPORT_RATE_GET_REPORT_RATE_LIST,
.msg.parameters[0] = 0,
};
feature_index = hidpp_root_get_feature_idx(device,
@@ -1631,6 +1719,60 @@ int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *d
return 0;
}
int hidpp20_adjustable_report_rate_get_report_rate(struct hidpp20_device *device,
uint8_t *rate_ms)
{
uint8_t feature_index;
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_ADJUSTABLE_REPORT_RATE_GET_REPORT_RATE,
.msg.parameters[0] = 0,
};
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ADJUSTABLE_REPORT_RATE);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
*rate_ms = msg.msg.parameters[0];
return 0;
}
int hidpp20_adjustable_report_rate_set_report_rate(struct hidpp20_device *device,
uint8_t rate_ms)
{
uint8_t feature_index;
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_ADJUSTABLE_REPORT_RATE_SET_REPORT_RATE,
.msg.parameters[0] = rate_ms,
};
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ADJUSTABLE_REPORT_RATE);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
return 0;
}
/* -------------------------------------------------------------------------- */
/* 0x8100 - Onboard Profiles */
/* -------------------------------------------------------------------------- */
@@ -2378,7 +2520,7 @@ hidpp20_onboard_profiles_write_dict(struct hidpp20_device *device,
memset(data + buffer_index, 0xff, sector_size - buffer_index);
hidpp_log_buf_debug(&device->base,
hidpp_log_buf_raw(&device->base,
"dictionary: ",
data,
hidpp20_onboard_profiles_compute_dict_size(device,
@@ -2389,6 +2531,9 @@ hidpp20_onboard_profiles_write_dict(struct hidpp20_device *device,
sector_size,
data,
true);
if (rc)
hidpp_log_error(&device->base, "failed to write profile dictionary\n");
return rc;
}
@@ -2426,6 +2571,7 @@ hidpp20_buttons_to_cpu(struct hidpp20_device *device,
break;
case HIDPP20_BUTTON_SPECIAL:
button->special.special = b->special.special;
button->special.profile = b->special.profile;
break;
case HIDPP20_BUTTON_MACRO:
if (profile->macros[i]) {
@@ -2446,6 +2592,7 @@ hidpp20_buttons_to_cpu(struct hidpp20_device *device,
case HIDPP20_BUTTON_DISABLED:
break;
default:
memcpy(b, button, sizeof(*b));
break;
}
}
@@ -2485,6 +2632,7 @@ hidpp20_buttons_from_cpu(struct hidpp20_profile *profile,
break;
case HIDPP20_BUTTON_SPECIAL:
button->special.special = b->special.special;
button->special.profile = b->special.profile;
break;
case HIDPP20_BUTTON_DISABLED:
break;
@@ -2495,12 +2643,13 @@ hidpp20_buttons_from_cpu(struct hidpp20_profile *profile,
button->macro.zero = 0;
break;
default:
memcpy(b, button, sizeof(*b));
break;
}
}
}
static void
void
hidpp20_onboard_profiles_read_led(struct hidpp20_led *led,
struct hidpp20_internal_led internal_led)
{
@@ -2516,6 +2665,10 @@ hidpp20_onboard_profiles_read_led(struct hidpp20_led *led,
if (brightness == 0)
brightness = 100;
break;
case HIDPP20_LED_STARLIGHT:
led->color = internal_led.effect.starlight.color_sky;
led->extra_color = internal_led.effect.starlight.color_star;
break;
case HIDPP20_LED_BREATHING:
period = hidpp_be_u16_to_cpu(internal_led.effect.breath.period_or_speed);
brightness = internal_led.effect.breath.intensity;
@@ -2523,11 +2676,18 @@ hidpp20_onboard_profiles_read_led(struct hidpp20_led *led,
brightness = 100;
led->color = internal_led.effect.breath.color;
break;
case HIDPP20_LED_RIPPLE:
period = hidpp_be_u16_to_cpu(internal_led.effect.breath.period_or_speed);
led->color = internal_led.effect.ripple.color;
break;
case HIDPP20_LED_ON:
led->color = internal_led.effect.fixed.color;
break;
case HIDPP20_LED_OFF:
break;
default:
memcpy(led->original, &internal_led, sizeof(internal_led));
break;
}
led->period = period;
@@ -2627,7 +2787,7 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
profiles->sector_size,
data);
if (rc && device->quirk != HIDPP20_QUIRK_G305) {
if (rc && device->quirk == HIDPP20_QUIRK_G305) {
/* The G305 has a bug where it throws an ERR_INVALID_ARGUMENT
if the sector has not been written to yet. If this happens
we will read the ROM profiles.*/
@@ -2703,7 +2863,7 @@ read_profiles:
return profiles->num_profiles;
}
static void
void
hidpp20_onboard_profiles_write_led(struct hidpp20_internal_led *internal_led,
struct hidpp20_led *led)
{
@@ -2722,24 +2882,31 @@ hidpp20_onboard_profiles_write_led(struct hidpp20_internal_led *internal_led,
else
internal_led->effect.cycle.intensity = 0;
break;
case HIDPP20_LED_STARLIGHT:
internal_led->effect.starlight.color_sky = led->color;
internal_led->effect.starlight.color_star = led->extra_color;
break;
case HIDPP20_LED_BREATHING:
internal_led->effect.breath.color.red = led->color.red;
internal_led->effect.breath.color.blue = led->color.blue;
internal_led->effect.breath.color.green = led->color.green;
internal_led->effect.breath.color = led->color;
internal_led->effect.breath.period_or_speed = hidpp_cpu_to_be_u16(period);
if (brightness < 100)
internal_led->effect.breath.intensity = brightness;
else
internal_led->effect.breath.intensity = 0;
break;
case HIDPP20_LED_RIPPLE:
internal_led->effect.ripple.color = led->color;
internal_led->effect.ripple.period = hidpp_cpu_to_be_u16(period);
break;
case HIDPP20_LED_ON:
internal_led->effect.fixed.color.red = led->color.red;
internal_led->effect.fixed.color.blue = led->color.blue;
internal_led->effect.fixed.color.green = led->color.green;
internal_led->effect.fixed.color = led->color;
internal_led->effect.fixed.effect = 0;
break;
case HIDPP20_LED_OFF:
break;
default:
memcpy(internal_led, led->original, sizeof(*internal_led));
break;
}
}
@@ -2787,8 +2954,10 @@ hidpp20_onboard_profiles_write_profile(struct hidpp20_device *device,
memcpy(pdata->profile.name.txt, profile->name, sizeof(profile->name));
rc = hidpp20_onboard_profiles_write_sector(device, sector, sector_size, data, true);
if (rc < 0)
if (rc < 0) {
hidpp_log_error(&device->base, "failed to write profile\n");
return rc;
}
return 0;
}
@@ -2871,7 +3040,7 @@ hidpp20_onboard_profiles_get_code_from_special(enum ratbag_button_action_special
/* -------------------------------------------------------------------------- */
struct hidpp20_device *
hidpp20_device_new(const struct hidpp_device *base, unsigned int idx)
hidpp20_device_new(const struct hidpp_device *base, unsigned int idx, struct hidpp_hid_report *reports, unsigned int num_reports)
{
struct hidpp20_device *dev;
int rc;
@@ -2884,6 +3053,14 @@ hidpp20_device_new(const struct hidpp_device *base, unsigned int idx)
dev->proto_major = 1;
dev->proto_minor = 0;
dev->led_ext_caps = 0;
hidpp_get_supported_report_types(&(dev->base), reports, num_reports);
if (!(dev->base.supported_report_types & HIDPP_REPORT_SHORT) &&
!(dev->base.supported_report_types & HIDPP_REPORT_LONG))
goto err;
rc = hidpp20_root_get_protocol_version(dev, &dev->proto_major, &dev->proto_minor);
if (rc) {
/* communication error, best to ignore the device */

View File

@@ -65,6 +65,7 @@ struct hidpp20_device {
unsigned feature_count;
struct hidpp20_feature *feature_list;
enum hidpp20_quirk quirk;
unsigned int led_ext_caps;
};
int hidpp20_request_command(struct hidpp20_device *dev, union hidpp20_message *msg);
@@ -80,7 +81,8 @@ const char *hidpp20_get_quirk_string(enum hidpp20_quirk quirk);
/* -------------------------------------------------------------------------- */
struct hidpp20_device *
hidpp20_device_new(const struct hidpp_device *base, unsigned int idx);
hidpp20_device_new(const struct hidpp_device *base, unsigned int idx,
struct hidpp_hid_report *reports, unsigned int num_reports);
void
hidpp20_device_destroy(struct hidpp20_device *device);
@@ -431,6 +433,12 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *device,
uint8_t *bitflags_ms);
int hidpp20_adjustable_report_rate_get_report_rate(struct hidpp20_device *device,
uint8_t *rate_ms);
int hidpp20_adjustable_report_rate_set_report_rate(struct hidpp20_device *device,
uint8_t rate_ms);
/* -------------------------------------------------------------------------- */
/* 0x8070v4 - Color LED effects */
/* -------------------------------------------------------------------------- */
@@ -494,6 +502,18 @@ hidpp20_rgb_effects_get_effect_info(struct hidpp20_device *device,
uint8_t effect_index,
struct hidpp20_rgb_effect_info *info);
struct hidpp20_led;
int
hidpp20_color_led_effects_set_zone_effect(struct hidpp20_device *device,
uint8_t zone_index,
struct hidpp20_led led);
int
hidpp20_color_led_effects_get_zone_effect(struct hidpp20_device *device,
uint8_t zone_index,
struct hidpp20_led *led);
enum hidpp20_color_led_location {
HIDPP20_COLOR_LED_LOCATION_UNDEFINED = 0,
HIDPP20_COLOR_LED_LOCATION_PRIMARY,
@@ -557,14 +577,42 @@ enum hidpp20_color_led_zone_effect {
#define HIDPP_PAGE_ONBOARD_PROFILES 0x8100
/* type */
#define HIDPP20_BUTTON_HID_TYPE 0x80
#define HIDPP20_BUTTON_SPECIAL 0x90
/* standard opcodes (0x80) */
#define HIDPP20_BUTTON_HID_TYPE_NOOP 0x00
#define HIDPP20_BUTTON_HID_TYPE_MOUSE 0x01
#define HIDPP20_BUTTON_HID_TYPE_KEYBOARD 0x02
#define HIDPP20_BUTTON_HID_TYPE_CONSUMER_CONTROL 0x03
#define HIDPP20_BUTTON_SPECIAL 0x90
/* special opcodes (0x90) */
#define HIDPP20_BUTTON_SPECIAL_NOOP 0x00
#define HIDPP20_BUTTON_SPECIAL_TILT_LEFT 0x01
#define HIDPP20_BUTTON_SPECIAL_TILT_RIGHT 0x02
#define HIDPP20_BUTTON_SPECIAL_NEXT_DPI 0x03
#define HIDPP20_BUTTON_SPECIAL_PREV_DPI 0x04
#define HIDPP20_BUTTON_SPECIAL_CYCLE_DPI 0x05
#define HIDPP20_BUTTON_SPECIAL_DEFAULT_DPI 0x06
#define HIDPP20_BUTTON_SPECIAL_SHIFT_DPI 0x07
#define HIDPP20_BUTTON_SPECIAL_NEXT_PROFILE 0x08
#define HIDPP20_BUTTON_SPECIAL_PREV_PROFILE 0x09
#define HIDPP20_BUTTON_SPECIAL_CYCLE_PROFILE 0x0a
#define HIDPP20_BUTTON_SPECIAL_GSHIFT 0x0b
/* memory model >= 3 */
#define HIDPP20_BUTTON_SPECIAL_BATTERY_INDICATOR 0x0c
#define HIDPP20_BUTTON_SPECIAL_ENABLE_PROFILE 0x0d
#define HIDPP20_BUTTON_SPECIAL_PERFORMANCE_SWITCH 0x0e
#define HIDPP20_BUTTON_SPECIAL_HOST 0x0f
#define HIDPP20_BUTTON_SPECIAL_SCROLL_DOWN 0x10
#define HIDPP20_BUTTON_SPECIAL_SCROLL_UP 0x11
/* buttons */
#define HIDPP20_BUTTON_MACRO 0x00
#define HIDPP20_BUTTON_DISABLED 0xFF
/* modifiers */
#define HIDPP20_MODIFIER_KEY_CTRL 0x01
#define HIDPP20_MODIFIER_KEY_SHIFT 0x02
@@ -598,6 +646,8 @@ union hidpp20_button_binding {
struct {
uint8_t type; /* HIDPP20_BUTTON_SPECIAL */
uint8_t special;
uint8_t reserved;
uint8_t profile;
} __attribute__((packed)) special;
struct {
uint8_t type; /* HIDPP20_BUTTON_MACRO */
@@ -697,7 +747,11 @@ enum hidpp20_led_mode {
HIDPP20_LED_OFF = 0x00,
HIDPP20_LED_ON = 0x01,
HIDPP20_LED_CYCLE = 0x03,
HIDPP20_LED_COLOR_WAVE = 0x04,
HIDPP20_LED_STARLIGHT = 0x05,
HIDPP20_LED_BREATHING = 0x0a,
HIDPP20_LED_RIPPLE = 0x0b,
HIDPP20_LED_CUSTOM = 0x0c,
};
enum hidpp20_led_waveform {
@@ -722,35 +776,76 @@ struct hidpp20_internal_led {
uint16_t period_or_speed; /* period in ms, speed is device dependent */
uint8_t intensity; /* 1 - 100 percent, 0 means 100 */
} __attribute__((packed)) cycle;
struct hidpp20_led_starlight {
struct hidpp20_color color_sky;
struct hidpp20_color color_star;
} __attribute__((packed)) starlight;
struct hidpp20_led_breath {
struct hidpp20_color color;
uint16_t period_or_speed; /* period in ms, speed is device dependent */
uint8_t waveform; /* enum hidpp20_led_waveform */
uint8_t intensity; /* 1 - 100 percent, 0 means 100 */
} __attribute__((packed)) breath;
struct hidpp20_led_ripple {
struct hidpp20_color color;
uint8_t reserved;
uint16_t period;
} __attribute__((packed)) ripple;
struct hidpp20_led_custom {
uint8_t slot;
uint16_t init_frame;
uint16_t lenght;
uint16_t frame_period;
uint8_t intensity;
} __attribute__((packed)) custom;
uint8_t padding[10];
} __attribute__((packed)) effect;
};
_Static_assert(sizeof(struct hidpp20_led_fixed) == 4, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_cycle) == 8, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_starlight) == 6, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_breath) == 7, "Invalid size");
_Static_assert(sizeof(struct hidpp20_internal_led) == 11, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_ripple) == 6, "Invalid size");
_Static_assert(sizeof(struct hidpp20_led_custom) == 8, "Invalid size");
typedef uint8_t percent_t;
struct hidpp20_led {
enum hidpp20_led_mode mode;
struct hidpp20_color color;
struct hidpp20_color extra_color;
uint16_t period;
percent_t brightness;
uint8_t original[sizeof(struct hidpp20_internal_led)];
};
#define HIDPP20_MACRO_NOOP 0x01
/* macro opcodes */
/* control commands */
#define HIDPP20_MACRO_WAIT_FOR_RELEASE 0x01
#define HIDPP20_MACRO_REPEAT_WHILE_PRESSED 0x02
#define HIDPP20_MACRO_REPEAT_UNTIL_CANCELED 0x03
#define HIDPP20_MACRO_END 0xff
/* delay & jump commands */
#define HIDPP20_MACRO_NOOP 0x00 /* wait the duration of a macro step */
#define HIDPP20_MACRO_DELAY 0x40
#define HIDPP20_MACRO_JUMP 0x60
/* button & key event generation */
#define HIDPP20_MACRO_BUTTON_DOWN 0x41
#define HIDPP20_MACRO_BUTTON_UP 0x42
#define HIDPP20_MACRO_KEY_PRESS 0x43
#define HIDPP20_MACRO_KEY_RELEASE 0x44
#define HIDPP20_MACRO_JUMP 0x60
#define HIDPP20_MACRO_END 0xff
#define HIDPP20_MACRO_CONS_DOWN 0x45
#define HIDPP20_MACRO_CONS_UP 0x46
/* analog commands */
#define HIDPP20_MACRO_ROLLER 0x20
#define HIDPP20_MACRO_ACPAN 0x21
#define HIDPP20_MACRO_XY 0x61
union hidpp20_macro_data {
struct {
@@ -918,6 +1013,14 @@ hidpp20_onboard_profiles_allocate_sector(struct hidpp20_profiles *profiles)
return zalloc(profiles->sector_size);
}
void
hidpp20_onboard_profiles_read_led(struct hidpp20_led *led,
struct hidpp20_internal_led internal_led);
void
hidpp20_onboard_profiles_write_led(struct hidpp20_internal_led *internal_led,
struct hidpp20_led *led);
/* -------------------------------------------------------------------------- */
/* 0x8110 - Mouse Button Spy */
/* -------------------------------------------------------------------------- */

View File

@@ -140,20 +140,21 @@ hidraw_is_receiver(int fd)
return true;
}
static struct hidpp10_device *
hidpp10_init(int fd)
static int
hidpp10_init(int fd, struct hidpp10_device **out)
{
struct hidpp_device base;
hidpp_device_init(&base, fd);
return hidpp10_device_new(&base, HIDPP_RECEIVER_IDX,
HIDPP10_PROFILE_UNKNOWN, 1);
HIDPP10_PROFILE_UNKNOWN, 1, out);
}
_EXPORT_ int
lur_receiver_new_from_hidraw(int fd, void *userdata, struct lur_receiver **out)
{
int rc;
struct lur_receiver *receiver;
if (!hidraw_is_receiver(fd))
@@ -165,8 +166,8 @@ lur_receiver_new_from_hidraw(int fd, void *userdata, struct lur_receiver **out)
receiver->userdata = userdata;
list_init(&receiver->devices);
receiver->hidppdev = hidpp10_init(fd);
if (!receiver->hidppdev)
rc = hidpp10_init(fd, &receiver->hidppdev);
if (rc)
goto error;
*out = receiver;
@@ -174,8 +175,7 @@ lur_receiver_new_from_hidraw(int fd, void *userdata, struct lur_receiver **out)
error:
free(receiver);
return -errno;
return rc;
}
_EXPORT_ int
@@ -203,7 +203,7 @@ lur_receiver_enumerate(struct lur_receiver *lur,
uint32_t serial;
bool is_new_device = true;
d = hidpp10_device_new(&base, i, HIDPP10_PROFILE_UNKNOWN, 1);
rc = hidpp10_device_new(&base, i, HIDPP10_PROFILE_UNKNOWN, 1, &d);
if (!d)
continue;

View File

@@ -51,11 +51,14 @@ enum driver {
LOGITECH_G300,
LOGITECH_G600,
STEELSERIES,
SINOWEALTH,
SINOWEALTH_NUBWO,
};
struct data_hidpp20 {
int index;
enum hidpp20_quirk quirk;
int led_count;
};
struct data_hidpp10 {
@@ -79,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;
@@ -90,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];
@@ -157,6 +168,7 @@ init_data_hidpp20(struct ratbag *ratbag,
char *str;
data->hidpp20.index = -1;
data->hidpp20.led_count = -1;
num = g_key_file_get_integer(keyfile, group, "DeviceIndex", &error);
if (num != 0 || !error)
@@ -164,6 +176,13 @@ init_data_hidpp20(struct ratbag *ratbag,
if (error)
g_error_free(error);
error = NULL;
num = g_key_file_get_integer(keyfile, group, "Leds", &error);
if (!error)
data->hidpp20.led_count = num;
if (error)
g_error_free(error);
str = g_key_file_get_string(keyfile, group, "Quirk", NULL);
data->hidpp20.quirk = HIDPP20_QUIRK_NONE;
if (str) {
@@ -243,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;
@@ -258,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 *
@@ -346,6 +411,8 @@ parse_ledtypes(char **strv, enum ratbag_led_type *types, size_t ntypes)
types[i] = RATBAG_LED_TYPE_SIDE;
else if (streq(s, "wheel"))
types[i] = RATBAG_LED_TYPE_WHEEL;
else if (streq(s, "switches"))
types[i] = RATBAG_LED_TYPE_SWITCHES;
else
return -1;
@@ -574,6 +641,14 @@ ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data)
return data->hidpp20.index;
}
int
ratbag_device_data_hidpp20_get_led_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP20);
return data->hidpp20.led_count;
}
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data)
{
@@ -647,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

@@ -80,6 +80,9 @@ ratbag_device_data_hidpp10_get_led_count(const struct ratbag_device_data *data);
int
ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data);
int
ratbag_device_data_hidpp20_get_led_count(const struct ratbag_device_data *data);
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data);
@@ -117,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

@@ -293,6 +293,7 @@ enum ratbag_led_type {
RATBAG_LED_TYPE_BATTERY,
RATBAG_LED_TYPE_DPI,
RATBAG_LED_TYPE_WHEEL,
RATBAG_LED_TYPE_SWITCHES,
};
/**

View File

@@ -23,6 +23,7 @@
#pragma once
#include "config.h"
#include <linux/input.h>
#include <stdint.h>
#include <stdbool.h>
@@ -295,6 +296,7 @@ struct ratbag_profile {
unsigned int hz; /**< report rate in Hz */
unsigned int rates[8]; /**< report rates available */
size_t nrates; /**< number of entries in rates */
bool rate_dirty;
unsigned int num_resolutions;
@@ -585,14 +587,16 @@ ratbag_led_set_mode_capability(struct ratbag_led *led,
}
/* list of all supported drivers */
struct ratbag_driver etekcity_driver;
struct ratbag_driver hidpp20_driver;
struct ratbag_driver hidpp10_driver;
struct ratbag_driver logitech_g300_driver;
struct ratbag_driver logitech_g600_driver;
struct ratbag_driver roccat_driver;
struct ratbag_driver gskill_driver;
struct ratbag_driver steelseries_driver;
extern struct ratbag_driver etekcity_driver;
extern struct ratbag_driver hidpp20_driver;
extern struct ratbag_driver hidpp10_driver;
extern struct ratbag_driver logitech_g300_driver;
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

@@ -31,7 +31,7 @@
#include "libratbag-util.h"
#include "libratbag-test.h"
struct ratbag_driver test_driver;
extern struct ratbag_driver test_driver;
static inline void
ratbag_register_test_drivers(struct ratbag *ratbag)

View File

@@ -37,6 +37,8 @@
#include <stdlib.h>
#include <iconv.h>
#include <errno.h>
#include <libgen.h>
#include <sys/stat.h>
#include "libratbag-util.h"
#include "libratbag-private.h"
@@ -188,3 +190,18 @@ err:
iconv_close(converter);
return ret;
}
int mkdir_p(char *dir, mode_t mode)
{
struct stat sb;
if (!dir)
return EINVAL;
if (!stat(dir, &sb))
return 0;
mkdir_p(dirname(strdupa(dir)), mode);
return mkdir(dir, mode);
}

View File

@@ -62,6 +62,8 @@ int list_empty(const struct list *list);
pos = tmp, \
tmp = container_of(pos->member.next, tmp, member))
int mkdir_p(char *dir, mode_t mode);
static inline char*
strncpy_safe(char *dest, const char *src, size_t n)
{

View File

@@ -71,17 +71,29 @@ ratbag_default_log_func(struct ratbag *ratbag,
const char *format, va_list args)
{
const char *prefix;
FILE *out = stdout;
switch(priority) {
case RATBAG_LOG_PRIORITY_RAW: prefix = "raw"; break;
case RATBAG_LOG_PRIORITY_DEBUG: prefix = "debug"; break;
case RATBAG_LOG_PRIORITY_INFO: prefix = "info"; break;
case RATBAG_LOG_PRIORITY_ERROR: prefix = "error"; break;
default: prefix="<invalid priority>"; break;
case RATBAG_LOG_PRIORITY_RAW:
prefix = "raw";
break;
case RATBAG_LOG_PRIORITY_DEBUG:
prefix = "debug";
break;
case RATBAG_LOG_PRIORITY_INFO:
prefix = "info";
break;
case RATBAG_LOG_PRIORITY_ERROR:
prefix = "error";
out = stderr;
break;
default:
prefix="<invalid priority>";
break;
}
fprintf(stderr, "ratbag %s: ", prefix);
vfprintf(stderr, format, args);
fprintf(out, "ratbag %s: ", prefix);
vfprintf(out, format, args);
}
void
@@ -256,7 +268,7 @@ ratbag_sanity_check_device(struct ratbag_device *device)
}
nres = ratbag_profile_get_num_resolutions(profile);
if (nres == 0 || nres > 16) {
if (nres > 16) {
log_bug_libratbag(ratbag,
"%s: invalid number of resolutions (%d)\n",
device->name,
@@ -590,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;
}
@@ -937,6 +951,8 @@ ratbag_device_commit(struct ratbag_device *device)
list_for_each(profile, &device->profiles, link) {
profile->dirty = false;
profile->rate_dirty = false;
list_for_each(button, &profile->buttons, link)
button->dirty = false;
@@ -1105,6 +1121,7 @@ ratbag_profile_set_report_rate(struct ratbag_profile *profile,
if (profile->hz != hz) {
profile->hz = hz;
profile->dirty = true;
profile->rate_dirty = true;
}
return RATBAG_SUCCESS;

View File

@@ -52,6 +52,28 @@
%typemap(freearg) (unsigned int *rates, size_t nrates) = (unsigned int *resolutions, size_t nres);
/* END OF custom typemap for handling ratbag_resolution_get_report_rate_list */
/* uintXX_t mapping: Python -> C */
%typemap(in) uint8_t {
$1 = (uint8_t) PyInt_AsLong($input);
}
%typemap(in) uint16_t {
$1 = (uint16_t) PyInt_AsLong($input);
}
%typemap(in) uint32_t {
$1 = (uint32_t) PyInt_AsLong($input);
}
/* uintXX_t mapping: C -> Python */
%typemap(out) uint8_t {
$result = PyInt_FromLong((long) $1);
}
%typemap(out) uint16_t {
$result = PyInt_FromLong((long) $1);
}
%typemap(out) uint32_t {
$result = PyInt_FromLong((long) $1);
}
/* Parse the header file to generate wrappers */
%include "libratbag.h"
%include "libratbag-enums.h"

View File

@@ -98,7 +98,9 @@ main(int argc, char **argv)
error(1, errno, "Failed to open path %s", path);
hidpp_device_init(&base, fd);
dev = hidpp10_device_new(&base, HIDPP_WIRED_DEVICE_IDX, HIDPP10_PROFILE_UNKNOWN, 5);
rc = hidpp10_device_new(&base, HIDPP_WIRED_DEVICE_IDX, HIDPP10_PROFILE_UNKNOWN, 5, &dev);
if (rc)
return rc;
if (argc == 2)
rc = dump_all_pages(dev);

View File

@@ -115,7 +115,7 @@ main(int argc, char **argv)
error(1, errno, "Failed to open path %s", path);
hidpp_device_init(&base, fd);
dev = hidpp20_device_new(&base, 0xff);
dev = hidpp20_device_new(&base, 0xff, NULL, 0);
if (!dev)
error(1, 0, "Failed to open %s as a HID++ 2.0 device", path);

View File

@@ -91,7 +91,7 @@ main(int argc, char **argv)
error(1, errno, "Failed to open path %s", path);
hidpp_device_init(&base, fd);
dev = hidpp20_device_new(&base, 0xff);
dev = hidpp20_device_new(&base, 0xff, NULL, 0);
if (!dev)
error(1, 0, "Failed to open %s as a HID++ 2.0 device", path);

View File

@@ -1,7 +1,5 @@
#!/usr/bin/env python3
#
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# Copyright 2017 Red Hat, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a

View File

@@ -1,6 +1,4 @@
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# Copyright 2017 Red Hat, Inc.
# Copyright 2017-2019 Red Hat, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@@ -137,7 +135,7 @@ class Ratbagd(object):
"""
def __init__(self):
def __init__(self, apiversion):
os.environ['LIBRATBAG_DATA_DIR'] = LIBRATBAG_DATA_DIR
self._ratbag = libratbag.ratbag_create_context(libratbag.interface, None)
self._devices = {}
@@ -251,13 +249,13 @@ class RatbagdDevice(metaclass=MetaRatbag):
@property
def model(self):
"""The unique identifier for this device model."""
bus = libratbag.ratbag_device_get_bus(self._device)
bus = libratbag.ratbag_device_get_bustype(self._device)
if not bus:
return "unknown"
vid = libratbag.ratbag_device_get_vendor_id(self._device)
pid = libratbag.ratbag_device_get_product_id(self._device)
version = libratbag.ratbag_device_get_product_version(self._device)
return "{}:{04x}:{04x}:{d}".format(bus, vid, pid, version)
return "{}:{:04x}:{:04x}:{:d}".format(bus, vid, pid, version)
@property
def name(self):
@@ -324,7 +322,7 @@ class RatbagdProfile(metaclass=MetaRatbag):
"""The capabilities of this profile as an array. Capabilities not
present on the profile are not in the list. Thus use e.g.
if RatbagdProfile.CAP_WRITABLE_NAME is in profile.capabilities:
if RatbagdProfile.CAP_WRITABLE_NAME in profile.capabilities:
do something
"""
return self._capabilities

View File

@@ -1,7 +1,5 @@
#!/usr/bin/env python3
#
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# This file is part of libratbag.
#
# Copyright 2017 Red Hat, Inc.

View File

@@ -1,7 +1,5 @@
#!/usr/bin/env python3
#
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# Copyright 2016 Red Hat, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
@@ -195,14 +193,18 @@ def print_profile(d, p, level):
" (active)" if p.is_active else ""))
if p.enabled:
print(" " * level + "Name: {}".format(p.name or 'n/a'))
print(" " * level + "Report Rate: {}Hz".format(p.report_rate))
print(" " * level + "Resolutions:")
for r in p.resolutions:
print_resolution(d, p, r, level + 2)
for b in p.buttons:
print_button(d, p, b, level)
for l in p.leds:
print_led(d, p, l, level)
if p.report_rate:
print(" " * level + "Report Rate: {}Hz".format(p.report_rate))
if p.resolutions:
print(" " * level + "Resolutions:")
for r in p.resolutions:
print_resolution(d, p, r, level + 2)
if p.buttons:
for b in p.buttons:
print_button(d, p, b, level)
if p.leds:
for l in p.leds:
print_led(d, p, l, level)
def print_device(d, level):

View File

@@ -1,7 +1,5 @@
#!/usr/bin/env python3
#
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# This file is part of libratbag.
#
# Copyright 2017 Red Hat, Inc.
@@ -374,6 +372,7 @@ class TestRatbagCtlDPI(TestRatbagCtl):
{ "is_active": false,
"resolutions": [
{ "xres": 1000, "yres": 1100,
"capabilities": [1],
"is_active": false },
{ "xres": 1300, "yres": 1400,
"capabilities": [1],

View File

@@ -1,6 +1,4 @@
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# Copyright 2016 Red Hat, Inc.
# Copyright 2016-2019 Red Hat, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@@ -195,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)
@@ -252,11 +250,13 @@ class Ratbagd(_RatbagdDBus):
def __init__(self, api_version):
super().__init__("Manager", None)
result = self._get_dbus_property("Devices") or []
self._devices = [RatbagdDevice(objpath) for objpath in result]
self._proxy.connect("notify::g-name-owner", self._on_name_owner_changed)
result = self._get_dbus_property("Devices")
if result is None and not self._proxy.get_cached_property_names():
raise RatbagdUnavailable("Make sure it is running and your user is in the required groups.")
if self.api_version != api_version:
raise RatbagdIncompatible(self.api_version or -1, api_version)
self._devices = [RatbagdDevice(objpath) for objpath in result or []]
self._proxy.connect("notify::g-name-owner", self._on_name_owner_changed)
def _on_name_owner_changed(self, *kwargs):
self.emit("daemon-disappeared")
@@ -428,7 +428,7 @@ class RatbagdProfile(_RatbagdDBus):
"""The capabilities of this profile as an array. Capabilities not
present on the profile are not in the list. Thus use e.g.
if RatbagdProfile.CAP_WRITABLE_NAME is in profile.capabilities:
if RatbagdProfile.CAP_WRITABLE_NAME in profile.capabilities:
do something
"""
return self._get_dbus_property("Capabilities") or []

View File

@@ -74,6 +74,9 @@ led_type_to_str(enum ratbag_led_type type)
case RATBAG_LED_TYPE_WHEEL:
str = "wheel";
break;
case RATBAG_LED_TYPE_SWITCHES:
str = "switches";
break;
default:
assert(!"Invalid LED type");
break;

View File

@@ -88,4 +88,4 @@ str_to_special_action(const char *str);
struct ratbag_device *
ratbag_cmd_open_device(struct ratbag *ratbag, const char *path);
const struct ratbag_interface interface;
extern const struct ratbag_interface interface;

View File

@@ -1,5 +1,3 @@
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# This file is part of libratbag.
#
# Copyright 2017 Red Hat, Inc.
@@ -62,9 +60,9 @@ def start_ratbagd(verbosity=0):
args = [os.path.join('@MESON_BUILD_ROOT@', "ratbagd.devel")]
if verbosity >= 3:
args.append('--verbose=raw')
elif verbosity >= 2:
args.append('--verbose')
elif verbosity >= 2:
args.append('--verbose=debug')
elif verbosity == 0:
args.append('--quiet')