145 Commits

Author SHA1 Message Date
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
Peter Hutterer
ce377fc36c meson.build: libratbag 0.11
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-11-05 11:06:27 +10:00
Mateusz Mojsiejuk
fe1308fe38 data: add Steelseries Kinzu V3 2019-11-02 21:21:41 +00:00
FFY00
983d3e2a88 data: fix G502 Hero Wireless .device file
Signed-off-by: FFY00 <lains@archlinux.org>
2019-10-23 18:47:17 +01:00
Filipe Laíns
3a3d9e1148 devices: add Logitech G604
Fixes #827

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-10-23 18:46:44 +01:00
Pieter
afbba26feb data: add wireded ID to G502 Hero Wireless 2019-10-23 18:45:14 +01:00
Peter Hutterer
8cc37047fb tools: fix typo in ratbagd.py
Reported in https://github.com/libratbag/piper/issues/406

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-10-16 10:50:58 +10:00
Benjamin Tissoires
c8f56cc535 swig: generate python helpers to use hidpp C library
```
$> sudo python3
Python 3.7.1 (default, Nov 23 2018, 10:01:49)
[GCC 8.2.1 20181105 (Red Hat 8.2.1-5)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import hidpp
>>> f = open('/dev/hidraw1', 'r+b')
>>> base = hidpp.hidpp_device()
>>> hidpp.hidpp_device_init(base, f.fileno())
>>> dev = hidpp.hidpp20_device_new(base, 0xff)
>>> hidpp.hidpp20_feature_get_name(0x8100)
'HIDPP_PAGE_ONBOARD_PROFILES'
>>> hidpp.new_uint8_tp()
<Swig Object of type 'uint8_t *' at 0x7f865198fb70>
>>> feature_index = hidpp.new_uint8_tp()
>>> feature_type = hidpp.new_uint8_tp()
>>> feature_version = hidpp.new_uint8_tp()
>>> hidpp.hidpp_root_get_feature(dev, 0x8100, feature_index, feature_type, feature_version)
0
>>> hidpp.uint8_tp_value(feature_index)
14
```

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-09-26 09:12:42 +01:00
Dustin Falgout
b6936ddd3b Add Support For Logitech MX Master 3 (#819) 2019-09-20 15:32:43 +10:00
Filipe Laíns
d4d9712316 hidpp20: add suport for the G602
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-09-11 09:43:41 +01:00
Filipe Laíns
2353af214b hidpp20: fallback to 1st profile when no 8100 profile is active
When the windows driver puts a device in host mode it will mark all 8100
profiles as disabled. If no 8100 profile is enabled no libratbag profile
will get enabled. This patch prevents this by marking the first profile
as enabled if no profile is already enabled.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-09-11 09:33:45 +01:00
Filipe Laíns
a4d19efb93 hidpp20: fix reading cluster info in the 8071 feature
TBH I don't know why I wrote the original code in the first place, it
clearly doesn't follow the spec.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-09-11 15:09:43 +10:00
Filipe Laíns
cb0b458824 hidpp20: make cluster_info a single variable in hidpp20drv_read_led_8071
This was probably leftover code.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-09-11 15:09:43 +10:00
Adrian Pascu
48c476b28c Add missing MX master PID
As seen here: https://github.com/torvalds/linux/blob/master/drivers/hid/hid-logitech-hidpp.c#L3717
This mouse is a warranty replacement, shows up as MX Master in Logitech Options but has MX Master 2S Hardware.
2019-09-02 13:24:18 +10:00
Filipe Laíns
d6c3bf881d hidp20: rename profile mapping 0x04
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-30 11:11:23 +01:00
rHermes
1b000e627a Add G502 Hero Wireless to meson.build 2019-08-20 11:03:54 +02:00
rHermes
75bcf39114 Add Logitech G502 Hero Wireless device 2019-08-20 11:03:54 +02:00
Peter Hutterer
33ced3dfb7 hidpp10: if we can't fetch the current profile, error out
Right now we initialize any hidpp10 device with one profile and then go and
fetch the data. But we never check for return codes, so it always succeeds
even when we can't get anything out of the device at all. Eventually we fail
the sanity checks with "invalid dpi list" which is confusing at best.

Let's avoid this by requiring at least the current profile to work. This will
break devices that have a single emulated profile, i.e. those that don't have
profiles but rely on libratbag to emulate one based on the
current-resolution/current-... calls. Not sure we actually support any of
those, so let's see.

Closes #738 though it doesn't fix it, it just bails out better.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-19 15:49:24 +02:00
Peter Hutterer
906f17add2 Rename ratbag_test_driver to ratbag_try_driver
This is confusing because we do have a test driver and we have an optional
test_device argument here too.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-19 15:49:24 +02:00
Filipe Laíns
016de548fc hidpp20: fix setting default resolution
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-17 10:02:28 +02:00
banditopazzo
665c022382 added bluetooth device ID in profiles for Logitech MX Master AMZ (amazon) 2019-08-15 08:37:58 +10:00
Filipe Laíns
81c7b42bec hidpp20/data: add the G305
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-13 08:52:06 +01:00
Filipe Laíns
edb054c6f8 hidpp20: add quirk system
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-13 08:52:06 +01:00
Peter Hutterer
8ae995b72d hidpp10: try switching to profile 0 where the device is in factory profiles
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-04 17:17:56 +02:00
Peter Hutterer
a941b75ef2 hidpp10: if the current profile is the factory profile, pretend it's 0
Rather than bail out completely, let's pretend the factory profile is profile
0 and keep going. This gives us a pretend-function mouse, though whether this
actually works is questionable.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-04 17:17:56 +02:00
Peter Hutterer
8cb85da6be release.sh: make this script usable for piper
Remove some hardcoded names and fix the version number extraction.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-04 17:17:45 +02:00
Peter Hutterer
7ff59a636f release.sh: drop the module file and section handling
leftover from when this was the xorg release script, we need neither here

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-04 17:17:45 +02:00
Peter Hutterer
6cd050f541 release.sh: fix version number replacement
sed treats . as "any character", so this breaks if we have a 0.10 version
number.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-04 17:17:45 +02:00
Peter Hutterer
ab3f1df06e meson.build: libratbag 0.10
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-02 11:19:16 +10:00
Peter Hutterer
caa31e17cf circle CI: add meson plain and release build types
Second time lucky.

Fixes #770

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-01 20:02:28 +10:00
Peter Hutterer
b7e6103496 circleci: move existing config to the new location
CircleCI 2.0 requires a new config file location, otherwise it won't run. And
since 1.0 is sunset now, this means our CI wasn't getting executed.

The actual format of the config is the same, we were already using 2.0
features.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-01 10:24:09 +10:00
Peter Hutterer
b1b6a2761e Revert "circle CI: add meson plain and release build types"
This broke CircleCI but since it didn't run for that build, no-one noticed.

This reverts commit 3bc31edd72.
2019-08-01 10:24:09 +10:00
Peter Hutterer
11f4f32947 Revert "hidpp20: add quirk system"
This reverts commit 196d53e65f.
2019-08-01 09:47:00 +10:00
Peter Hutterer
4f5d0dffec Revert "hidpp20/data: add the G305"
This reverts commit cad05ffced.
2019-08-01 09:47:00 +10:00
Peter Hutterer
d213bdb86e data: add a test for duplicate DeviceMatch lines
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-01 09:22:03 +10:00
Peter Hutterer
e69e1de17b devices: drop the G700s wireless entry
The G700 and G700s both use the receiver's product id, so they both end up
having the same DeviceMatch. One of those two never gets loaded but which one
is implementation-defined and may change accidentally.

Let's drop the G700s entry because the G700 is closer to a lowest comon
denominator. Once we have kernel support for the actual product id, we can
re-instante them.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-01 09:22:03 +10:00
Filipe Laíns
cad05ffced hidpp20/data: add the G305
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-01 09:20:47 +10:00
Filipe Laíns
196d53e65f hidpp20: add quirk system
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-08-01 09:20:47 +10:00
Peter Hutterer
c606b1abe3 tools: flake8 fixes
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 09:08:06 +10:00
Peter Hutterer
b4009669b9 ratbagd: Add an APIVersion property to make version checks easier
While the DBus API is still in flux, things can break at any time.

Keeping piper and libratbag in version-step works terribly, doubly so where
piper is flatpaked but ratbagd comes from some repo somewhere. We can't check
at build-time, we can't run ratbagd --version at runtime.

so let's add a version field for now and drop it if we ever get to stable.
Meanwhile, ratbagd.py will throw an exception if the expected version doesn't
match exactly.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 09:08:06 +10:00
Peter Hutterer
d458305f93 tools: ratbagdctl.test: if an exception occurs during startup, terminate
Don't leave a ratbagd.devel process hanging if something goes wrong.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 09:08:06 +10:00
Peter Hutterer
9c11960665 ratbagd: document the various commandline options in the man page
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 08:46:19 +10:00
Peter Hutterer
e8fc8b45d1 ratbagd: drop the hardcoded test device
Force the tests to load a test device with JSON that matches their
expectations instead of having a magic device pre-setup where the tests have
to know what the values are.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 08:36:16 +10:00
Peter Hutterer
e64a121572 tools: switch the button tests over to json 2019-07-31 08:36:16 +10:00
Peter Hutterer
607e47953c tools: switch led tests to use the json parsing 2019-07-31 08:36:16 +10:00
Peter Hutterer
a5816055dc tools: switch the report rate to json 2019-07-31 08:36:16 +10:00
Peter Hutterer
62ac0772f5 tools: switch the dpi test to json 2019-07-31 08:36:16 +10:00
Peter Hutterer
fc9e63a5aa tools: switch the resolution tests to use the json loading 2019-07-31 08:36:16 +10:00
Peter Hutterer
936a8f7a92 tools: switch the ratbagctl.test to use the new json loading 2019-07-31 08:36:16 +10:00
Peter Hutterer
818e665853 ratbagd: add ability to load json for test devices
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-31 08:36:16 +10:00
Peter Hutterer
3a3b11ade4 ratbagctl: autogenerate the choices list for the specials and led modes
Let's not maintain that list manually if we can fetch it from the enums.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 12:39:11 +02:00
Peter Hutterer
3bc31edd72 circle CI: add meson plain and release build types
Fixes #770

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 12:31:01 +02:00
Peter Hutterer
485df20a83 tools: fix typo in ratbagd.py
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 19:30:11 +10:00
Peter Hutterer
49291c4b45 ratbagd: encode the test device path without underscores
sd-bus encodes underscores as _5f which looks like memory corruption. Let's
skip them, they don't add value anyway.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 19:28:07 +10:00
Filipe Laíns
02ec2442d3 hidpp20: add support for RGB Effects feature (0x8071)
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-30 15:12:10 +10:00
Filipe Laíns
a8b4d9c6f3 hidpp20: add new profile layout (0x04)
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-30 15:11:54 +10:00
Peter Hutterer
22a6ac1c7e hidpp10: make raw debugging messages a bit easier to read
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-30 12:01:42 +10:00
Peter Hutterer
42c302c860 tools: fix a few comments referring to old names
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-29 09:53:36 +10:00
Peter Hutterer
e2b1581aef tools: ratbagctl.test: set the data dir correctly
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-29 09:35:56 +10:00
Peter Hutterer
10a3e87980 meson.build: remove unused fallback SVG name
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-29 06:55:30 +10:00
Peter Hutterer
2d465a63e1 meson: drop the unused libratbag version define
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-29 06:52:14 +10:00
Eric Culp
c2315323f4 meson.build: fix builds with --buildtype=plain
When building with --buildtype=plain, the build fails with the following
error:

    cc1: error: ‘-Wformat-security’ ignored without ‘-Wformat’ [-Werror=format-security]

-Werror=format-security was recently added in 43def1b3a5 by #745
2019-07-27 09:43:49 +10:00
Peter Hutterer
05d7e9b324 ratbagd: poke DBus to reload its config after installing the devel policy
Fixes #753
2019-07-26 16:42:01 +10:00
Peter Hutterer
71a399f20f ratbagd: encode the test device iteration in the test device name
The sequence triggered by the ratbagctl.test suite is:
- DBus call Commit()
- ratbagd schedules the callback as idle func
- DBus call ResetTestDevice()
- ratbagd cleans up the test device
- callback is called on next idle
  - boom! the device is already freed

To avoid this, we need to take a ref of the device while it's being scheduled
for the commit handling. But that blocks the object paths for new test devices
because the test device name is encoded in those paths (like "hidraw" for real
devices) and it doesn't change. So let's add a count to the test device name
to make sure the new device doesn't re-use the old device's paths.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 16:41:04 +10:00
Peter Hutterer
8259c2e706 ratbagd: introduce ref/unref for ratbagd_device pointers
We'll need to keep references around to devices even when caller is done with
them.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 16:41:04 +10:00
Peter Hutterer
89980a4a72 meson: add the prefix to the dbus dir
Otherwise where the directory is used within meson.build, the prefix would be
missing. No real effect since we don't use it anywhere but let's be safe.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
79e94b4a7e ratbagd: install the devel dbus policy file on startup
Don't rely on some other tool to install the policy file, let's just do this
in ratbagd.devel directly so it's immediately executable.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
122f05c272 tools: drop the RATBAGCTL_DEVEL environ, just use RATBAG_TEST instead
Which is the same env that ratbagd uses as well. And we don't have variable
values in RATBAGCTL_DEVEL anymore, so no point in having two different
variables here.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
b06ac1035e tools: add --use-existing-ratbagd to the ratbagctl.test script as well
This makes it possible to run the script against a manually started
ratbagd.devel - with a lot less effort.

And since we're don't need to be root to do this, let's move that check too.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
647fe432b2 tools: add argument to ratbagctl.devel to connect to running ratbagd
This makes it a lot easier to start ratbagd.devel in gdb, then run
ratbagctl.devel against it.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
4e2e9f037b Drop the ratbagctl.devel sha bus name
We already have a separate name for the devel bus anyway, the git sha doesn't
give us a huge amount of benefits but makes manual debugging a major PITA.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
f92fee0b15 meson: document the various built bits a bit better
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-26 09:34:31 +10:00
Peter Hutterer
d7c394f379 tools: ratbactl.test: if RATBAGCTL_DEVEL is set, use the existing server
When there's a bug that's triggered by the test suite it's hard to figure out
what exactly crashes. So allow starting ratbagd.devel manually and run the
test suite against that process so we can gdb it.

The steps required for this are a bit insane, so the instructions are listed.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 21:38:12 +10:00
Peter Hutterer
7fec19f8f2 test: switch the resolution capabilities to an array
Just like the profile ones

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 09:08:55 +02:00
Peter Hutterer
11c1e2d71a Fix setting the capability flags on the resolutions
2 hours of debugging... ffs

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 09:08:55 +02:00
Peter Hutterer
e2cbaf9dea tools: print the Model in ratbagctl info
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 07:26:27 +02:00
Peter Hutterer
bf9d86e228 hidpp10: if the profile name is the empty string, make it NULL
Because our profile struct can never have an NULL name, let's assume the
empty string equals NULL.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-25 13:02:18 +10:00
Filipe Laíns
f24a3bd441 hidpp20: fix 0x1001 comments, add missing ASE and use get_unaligned_be_u16
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-24 19:17:31 +10:00
Filipe Laíns
df5742ebfe libratbag-util: move data manipulation functions out of hidpp-generic
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-24 19:17:21 +10:00
Peter Hutterer
0c569e0bf7 tools: use super() in the ratbagctl test
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-24 09:16:24 +02:00
Peter Hutterer
5ea1731f38 tools: use super() without arguments in ratbagctl
I *think* this is a Python2 leftover or something. Either way, let's remove
it.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-24 09:16:24 +02:00
Peter Hutterer
572064aba8 tools: use super() instead of the named parent class in ratbagd.py
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-24 09:16:24 +02:00
Peter Hutterer
da2764e78d Append the created profiles/leds/buttons/resolutions, don't insert
We're initializing them with incrementing indices, let's make sure the list is
initialized that way too. Makes things more obvious in the log when the first
profile to be queried from the device isn't profile 4.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-23 13:47:11 +02:00
Peter Hutterer
4e56afae22 roccat: use the new get/set feature helpers
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-23 13:46:23 +02:00
Peter Hutterer
d46251900c hidraw: add a feature report get/set helper functions
No real functional changes since they just forward to the existing function.
But it reduces the cognitive load a bit when reading the code.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-23 13:46:23 +02:00
Peter Hutterer
43def1b3a5 meson.build: enable -Werror=format-security
We don't want to accidentally ignore those.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-07-19 07:17:45 +02:00
Filipe Laíns
247ff7695d data: add MX Vertical
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-19 07:57:53 +10:00
Filipe Laíns
7a14026f2d hidpp20: fix warnings
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-07-17 15:13:51 +01:00
75 changed files with 3827 additions and 827 deletions

View File

@@ -1,5 +1,10 @@
# 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
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
default_settings: &default_settings
working_directory: ~/libratbag
environment:
@@ -13,6 +18,18 @@ libratbag_references:
ninja -v -C build ${NINJA_ARGS}
environment:
MESON_PARAMS: --prefix=/usr
build_buildtype_plain: &build_buildtype_plain
run:
<<: *build_default
name: Build with buildtype plain
environment:
MESON_PARAMS: --prefix=/usr -Dbuildtype=plain
build_buildtype_release: &build_buildtype_release
run:
<<: *build_default
name: Build with buildtype release
environment:
MESON_PARAMS: --prefix=/usr -Dbuildtype=release
build_and_test: &build_and_test
run:
<<: *build_default
@@ -40,10 +57,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 ]
@@ -66,11 +80,13 @@ fedora_prep_cache: &fedora_prep_cache
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
command: dnf install -y --downloadonly git ${FEDORA_DEP_BUILD} ${FEDORA_DEP_TEST} ${FEDORA_DEP_DOC}
- persist_to_workspace:
root: /var/cache/
paths:
- dnf/*
environment:
*build_dependencies
fedora_fetch_cache: &fedora_fetch_cache
attach_workspace:
@@ -81,8 +97,7 @@ fedora_install: &fedora_install
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
dnf install -y git ${FEDORA_DEP_BUILD} ${FEDORA_DEP_TEST}
sed -i 's/systemd//' /etc/nsswitch.conf
fedora_settings: &fedora_settings
@@ -96,8 +111,11 @@ fedora_settings: &fedora_settings
- *install
- *check_install
- *check_uninstall
- *build_buildtype_plain
- *build_buildtype_release
- *export_logs
environment:
*build_dependencies
ubuntu_settings: &ubuntu_settings
<<: *default_settings
@@ -110,14 +128,18 @@ ubuntu_settings: &ubuntu_settings
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
apt-get install -y git ${UBUNTU_DEP_BUILD} ${UBUNTU_DEP_TEST}
- checkout
- *start_dbus
- *build_and_test
- *install
- *check_install
- *check_uninstall
- *build_buildtype_plain
- *build_buildtype_release
- *export_logs
environment:
*build_dependencies
doc_build: &doc_build
<<: *default_settings
@@ -126,7 +148,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
@@ -136,6 +158,8 @@ doc_build: &doc_build
root: build
paths:
- doc/html/*
environment:
*build_dependencies
docs_deploy: &docs_deploy
<<: *default_settings
@@ -177,18 +201,14 @@ docs_deploy: &docs_deploy
version: 2
jobs:
fedora_rawhide:
<<: *fedora_settings
docker:
- image: fedora:rawhide
fedora_cache:
<<: *fedora_prep_cache
docker:
- image: fedora:30
fedora_latest:
- image: fedora:31
fedora_31:
<<: *fedora_settings
docker:
- image: fedora:30
- image: fedora:31
ubuntu_19_04:
<<: *ubuntu_settings
docker:
@@ -196,21 +216,20 @@ jobs:
doc_build:
<<: *doc_build
docker:
- image: fedora:30
- image: fedora:31
doc_deploy:
<<: *docs_deploy
docker:
- image: fedora:30
- image: fedora:31
workflows:
version: 2
compile_and_test:
jobs:
# - fedora_rawhide
- fedora_cache
- ubuntu_19_04
- fedora_latest:
- fedora_31:
requires:
- fedora_cache
- doc_build:

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

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

@@ -0,0 +1,36 @@
---
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`
**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']
permitted = ['DeviceIndex', '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)

57
data/devices/duplicate-check.py Executable file
View File

@@ -0,0 +1,57 @@
#!/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
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='Device duplicate match checker')
parser.add_argument('file', nargs='+')
args = parser.parse_args()
device_matches = {}
duplicates = False
for path in args.file:
matches = parse_data_file(path)
fname = os.path.basename(path)
for m in matches:
if m in device_matches:
print('Duplicate DeviceMatch={} in {} and {}'.format(m, fname, device_matches[m]))
duplicates = True
device_matches[m] = fname
if duplicates:
sys.exit(1)

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,5 @@
# Logitech MX Master 3
[Device]
Name=Logitech MX Master 3
DeviceMatch=bluetooth:046d:b023;usb:046d:4082
Driver=hidpp20

View File

@@ -1,5 +1,5 @@
# Logitech MX Master
[Device]
Name=Logitech MX Master
DeviceMatch=usb:046d:4041;bluetooth:046d:b012;usb:046d:4060;bluetooth:046d:b017
DeviceMatch=usb:046d:4041;bluetooth:046d:b012;usb:046d:4060;bluetooth:046d:b017;bluetooth:046d:b01e;usb:046d:4071
Driver=hidpp20

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech MX Vertical
DeviceMatch=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

@@ -0,0 +1,7 @@
[Device]
Name=Logitech Gaming Mouse G305
DeviceMatch=usb:046d:4074
Driver=hidpp20
[Driver/hidpp20]
Quirk=G305

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech G502 Hero Wireless
DeviceMatch=usb:046d:407f;usb:046d:c08d
Driver=hidpp20

View File

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

View File

@@ -6,3 +6,4 @@ Driver=hidpp20
[Driver/hidpp20]
DeviceIndex=1
Quirk=G602

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech G604
DeviceMatch=usb:046d:4085;bluetooth:046d:b024
Driver=hidpp20

View File

@@ -1,12 +0,0 @@
# Logitech G700s over wireless USB
# http://support.logitech.com/en_us/product/g700s-rechargable-wireless-gaming-mouse/specs
[Device]
Name=Logitech G700s
DeviceMatch=usb:046d:c531
Driver=hidpp10
[Driver/hidpp10]
DpiRange=0:8200@50
ProfileType=G700
DeviceIndex=1
Profiles=5

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

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
Driver=roccat-kone-pure

View File

@@ -0,0 +1,11 @@
[Device]
Name=SteelSeries Kinzu V3
DeviceMatch=usb:1038:1388
Driver=steelseries
[Driver/steelseries]
DeviceVersion=1
Buttons=3
Leds=0
DpiList=400;800;1600;2000
MacroLength=0

View File

@@ -12,15 +12,15 @@
<busconfig>
<policy user="root">
<allow own="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow send_destination="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow receive_sender="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow own="org.freedesktop.ratbag_devel1"/>
<allow send_destination="org.freedesktop.ratbag_devel1"/>
<allow receive_sender="org.freedesktop.ratbag_devel1"/>
</policy>
<!-- allow everyone to talk to the daemon -->
<policy context="default">
<allow send_destination="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow receive_sender="org.freedesktop.ratbag_devel1_@ratbagd_sha@"/>
<allow send_destination="org.freedesktop.ratbag_devel1"/>
<allow receive_sender="org.freedesktop.ratbag_devel1"/>
</policy>
</busconfig>

View File

@@ -80,6 +80,19 @@ org.freedesktop.ratbag1.Manager
The **org.freedesktop.ratbag1.Manager** interface is the entry point to
interact with ratbagd.
.. attribute:: APIVersion
: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.
This API will be removed once the DBus API is declared stable.
.. attribute:: Devices
:type: ao

View File

@@ -1,14 +1,14 @@
project('libratbag', 'c',
version : '0.9.906',
version : '0.13',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
meson_version : '>= 0.40.0')
libratbag_version = meson.project_version().split('.')
git = find_program('git')
r = run_command('tools/ratbag-git-sha.sh')
ratbagd_sha = r.stdout().strip()
# The DBus API version. Increase this every time the DBus API changes.
# No backwards/forwards guarantee, clients are expected to understand
# whatever ratbagd speaks or bail out. This should be removed if we ever
# finish the API and declare it stable.
ratbagd_api_version = 1
# We use libtool-version numbers because it's easier to understand.
# Before making a release, the libratbag_so_* and liblur_so_*
@@ -31,6 +31,8 @@ cc = meson.get_compiler('c')
cflags = ['-Wno-unused-parameter',
'-fvisibility=hidden',
'-Wmissing-prototypes',
'-Wformat', # required by format-security
'-Werror=format-security',
'-Wstrict-prototypes']
add_project_arguments(cflags, language: 'c')
@@ -39,7 +41,7 @@ add_project_arguments(cflags, language: 'c')
config_h = configuration_data()
config_h.set('_GNU_SOURCE', '1')
config_h.set_quoted('RATBAG_VERSION', meson.project_version())
config_h.set_quoted('FALLBACK_SVG_NAME', 'fallback.svg')
config_h.set('RATBAGD_API_VERSION', ratbagd_api_version)
libratbag_data_dir = join_paths(get_option('prefix'),
get_option('datadir'),
'libratbag')
@@ -70,6 +72,7 @@ pkgconfig = import('pkgconfig')
dep_udev = dependency('libudev')
dep_libevdev = dependency('libevdev')
dep_glib = dependency('glib-2.0')
dep_json_glib = dependency('json-glib-1.0')
dep_lm = cc.find_library('m')
dep_unistring = cc.find_library('unistring')
@@ -118,7 +121,10 @@ lib_libhidpp = static_library('hidpp',
dependencies : deps_libhidpp)
dep_libhidpp = declare_dependency(link_with: lib_libhidpp)
### liblur.a ####
### liblur ####
#
# liblur is the library to handle logitech unifying receivers.
#
install_headers('src/liblur.h')
src_liblur = [
@@ -154,6 +160,9 @@ pkgconfig.generate (
)
#### libratbag.so ####
#
# libratbag is an internal-only library and the bit that does the actual
# work talking to the mouse. it's used by ratbagd.
src_libratbag = [
'src/libratbag-enums.h',
'src/libratbag.h',
@@ -163,6 +172,7 @@ 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-test.c',
@@ -182,6 +192,7 @@ deps_libratbag = [
dep_udev,
dep_libevdev,
dep_glib,
dep_json_glib,
dep_libutil,
dep_libhidpp,
]
@@ -242,6 +253,8 @@ executable('hidpp20-reset',
)
#### lur-command ####
#
# A tool to access and manipulate logitech unifying receivers.
src_lur_command = [ 'tools/lur-command.c' ]
executable('lur-command',
src_lur_command,
@@ -269,19 +282,23 @@ 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-MX-Ergo.device',
'data/devices/logitech-MX-Master.device',
'data/devices/logitech-MX-Master-2S.device',
'data/devices/logitech-MX-Master-3.device',
'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',
'data/devices/logitech-g300.device',
'data/devices/logitech-g302.device',
'data/devices/logitech-g303.device',
'data/devices/logitech-g305.device',
'data/devices/logitech-g402.device',
'data/devices/logitech-g403-hero.device',
'data/devices/logitech-g403-wireless.device',
@@ -290,16 +307,18 @@ data_files = files(
'data/devices/logitech-g5.device',
'data/devices/logitech-g500.device',
'data/devices/logitech-g500s.device',
'data/devices/logitech-g502-hero-wireless.device',
'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',
'data/devices/logitech-g604.device',
'data/devices/logitech-g7.device',
'data/devices/logitech-g700-wireless.device',
'data/devices/logitech-g700.device',
'data/devices/logitech-g700s-wireless.device',
'data/devices/logitech-g700s.device',
'data/devices/logitech-g703-hero.device',
'data/devices/logitech-g703.device',
@@ -307,13 +326,19 @@ 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-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',
'data/devices/steelseries-rival-310.device',
'data/devices/steelseries-rival-600.device',
'data/devices/steelseries-rival.device',
@@ -327,6 +352,12 @@ install_data(data_files,
data_parse_test = find_program(join_paths(meson.source_root(), 'data/devices/data-parse-test.py'))
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
@@ -374,6 +405,10 @@ if enable_tests
endif
#### ratbagd ####
#
# The main item of this repo, a DBus server that uses libratbag to talk to
# the mice. The DBus API is the public-facing API.
#
src_ratbagd = [
'src/shared-macro.h',
'src/shared-rbtree.h',
@@ -386,6 +421,8 @@ src_ratbagd = [
'ratbagd/ratbagd-profile.c',
'ratbagd/ratbagd-resolution.c',
'ratbagd/ratbagd-test.c',
'ratbagd/ratbagd-json.c',
'ratbagd/ratbagd-json.h',
'src/libratbag-util.h',
'src/libratbag-util.c',
]
@@ -407,21 +444,34 @@ executable('ratbagd',
install_man('ratbagd/ratbagd.8')
#### ratbagd_devel ####
#
# A development ratbagd server that owns a different name on the bus
# (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)
# This is a hack. We always install the devel policy file into
# /etc/dbus-1/system.d, independent of any prefixes we use otherwise.
# This should never be used outside of developer machines anyway, but
# installations on those may use different prefixes for building.
# This is not set in stone, suggest something better if you can.
dbussystemdir = join_paths('/', get_option('sysconfdir'), 'dbus-1', 'system.d')
executable('ratbagd.devel',
src_ratbagd,
dependencies : deps_ratbagd,
include_directories : include_directories('src'),
install : false,
c_args : ['-DRATBAG_DBUS_INTERFACE="ratbag_devel1_@0@"'.format(ratbagd_sha),
c_args : ['-DRATBAG_DBUS_INTERFACE="ratbag_devel1"',
'-DDBUS_POLICY_SRC="@0@/@1@"'.format(meson.build_root(), dbus_devel_policy),
'-DDBUS_POLICY_DST="@0@/@1@"'.format(dbussystemdir, dbus_devel_policy),
'-DDISABLE_COREDUMP=1'],
)
config_ratbagd_devel = configuration_data()
config_ratbagd_devel.set('ratbagd_sha', ratbagd_sha)
configure_file(input : 'dbus/org.freedesktop.ratbag_devel1.conf.in',
output : 'org.freedesktop.ratbag_devel1.conf',
configuration : config_ratbagd_devel)
#### unit file ####
if enable_systemd
@@ -460,7 +510,7 @@ endif
dbusdir = get_option('dbus-root-dir')
if dbusdir == ''
dbusdir = join_paths(get_option('datadir'), 'dbus-1')
dbusdir = join_paths(get_option('prefix'), get_option('datadir'), 'dbus-1')
endif
configure_file(input : 'dbus/org.freedesktop.ratbag1.service.in',
@@ -495,15 +545,22 @@ else
py3 = pymod.find_installation()
endif
# ratbagctl calls ratbagd over DBus
config_ratbagctl = configuration_data()
config_ratbagctl.set('RATBAGD_API_VERSION', ratbagd_api_version)
config_ratbagctl.set('version', meson.project_version())
ratbagctl_in = configure_file(input : 'tools/ratbagctl.in.in',
output : 'ratbagctl.in',
configuration: config_ratbagctl)
# ratbagctl is the commandline tool to interact with ratbagd over DBus.
custom_target('ratbagctl',
output : 'ratbagctl',
input : ['tools/ratbagctl.in', 'tools/ratbagd.py'],
input : [ratbagctl_in, 'tools/ratbagd.py'],
build_by_default : true,
command : [py3, join_paths(meson.source_root(), 'tools', 'merge_ratbagd.py'),
'@INPUT@',
'--output', '@OUTPUT@',
'--version', meson.project_version()],
'--output', '@OUTPUT@'],
install: true,
install_dir: get_option('bindir'))
@@ -516,9 +573,9 @@ man_ratbagctl = configure_file (
)
config_ratbagctl_devel = configuration_data()
config_ratbagctl_devel.set('ratbagd_sha', ratbagd_sha)
config_ratbagctl_devel.set('MESON_BUILD_ROOT', meson.build_root())
config_ratbagctl_devel.set('LIBRATBAG_DATA_DIR', libratbag_data_dir_devel)
config_ratbagctl_devel.set('RATBAGD_API_VERSION', ratbagd_api_version)
configure_file(input : 'tools/toolbox.py',
output : 'toolbox.py',
@@ -574,20 +631,36 @@ shared_library(
install: false,
)
i_source = join_paths(meson.source_root(), 'src', 'hidpp.i')
c_source = swig_gen.process(i_source)
shared_library(
'hidpp',
c_source,
name_prefix: '_',
c_args : ['-Wno-missing-prototypes', '-Wno-format-nonliteral'],
extra_files: [ i_source ] + src_libratbag ,
dependencies: deps_libratbag + wrapper_deps,
include_directories : include_directories('src', 'tools'),
install: false,
)
# ratbagc is the layer that maps ratbagctl to the swig bindings
ratbagc_py_conf = configuration_data()
ratbagc_py_conf.set('LIBRATBAG_DATA_DIR', libratbag_data_dir_devel)
ratbagc_py = configure_file(input: 'tools/ratbagc.py.in',
output: 'ratbagc.py',
configuration: ratbagc_py_conf)
# ratbag-command is a commandline tool with the UI as ratbagctl but instead
# of connecting to ratbagd over DBus, it uses libratbag directly. This is a
# development tool for protocol debugging etc.
custom_target('ratbag-command',
output : 'ratbag-command',
input : ['tools/ratbagctl.in', ratbagc_py],
input : [ratbagctl_in, ratbagc_py],
build_by_default : true,
command : [py3, join_paths(meson.source_root(), 'tools', 'merge_ratbagd.py'),
'@INPUT@',
'--output', '@OUTPUT@',
'--version', meson.project_version()],
'--output', '@OUTPUT@'],
install: false)
#### output files ####

12
ratbagd/.gitignore vendored
View File

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

View File

@@ -41,6 +41,8 @@
#include "libratbag-util.h"
struct ratbagd_device {
unsigned int refcount;
struct ratbagd *ctx;
RBNode node;
char *sysname;
@@ -170,8 +172,11 @@ 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);
}
static int ratbagd_device_commit(sd_bus_message *m,
@@ -182,7 +187,7 @@ static int ratbagd_device_commit(sd_bus_message *m,
ratbagd_schedule_task(device->ctx,
ratbagd_device_commit_pending,
device);
ratbagd_device_ref(device));
CHECK_CALL(sd_bus_reply_method_return(m, "u", 0));
@@ -230,7 +235,7 @@ int ratbagd_device_new(struct ratbagd_device **out,
const char *sysname,
struct ratbag_device *lib_device)
{
_cleanup_(ratbagd_device_freep) struct ratbagd_device *device = NULL;
_cleanup_(ratbagd_device_unrefp) struct ratbagd_device *device = NULL;
struct ratbag_profile *profile;
unsigned int i;
int r;
@@ -240,6 +245,7 @@ int ratbagd_device_new(struct ratbagd_device **out,
assert(sysname);
device = zalloc(sizeof(*device));
device->refcount = 1;
device->ctx = ctx;
rbnode_init(&device->node);
device->lib_device = ratbag_device_ref(lib_device);
@@ -282,12 +288,12 @@ int ratbagd_device_new(struct ratbagd_device **out,
return 0;
}
struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device)
static void ratbagd_device_free(struct ratbagd_device *device)
{
unsigned int i;
if (!device)
return NULL;
return;
assert(!ratbagd_device_linked(device));
@@ -301,7 +307,26 @@ struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device)
assert(!device->lib_device); /* ratbag yields !NULL if still pinned */
return mfree(device);
mfree(device);
}
struct ratbagd_device *ratbagd_device_ref(struct ratbagd_device *device)
{
assert(device->refcount > 0);
++device->refcount;
return device;
}
struct ratbagd_device *ratbagd_device_unref(struct ratbagd_device *device)
{
assert(device->refcount > 0);
--device->refcount;
if (device->refcount == 0)
ratbagd_device_free(device);
return NULL;
}
const char *ratbagd_device_get_sysname(struct ratbagd_device *device)

562
ratbagd/ratbagd-json.c Normal file
View File

@@ -0,0 +1,562 @@
/*
* Copyright © 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"),
* 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.
*/
/* JSON parser for ratbag test devices. The JSON format is not fixed and may
change at any time, but basically looks like this:
{
"profiles": [
{
"is_active": bool,
"is_default": bool,
"is_disabled": bool,
"rate": int,
"report_rates": [ int, ...],
"capabilities": [int, ...] # profile capabilities
"resolutions": [
{
"xres": int,
"yres": int,
"dpi_min", int,
"dpi_max", int,
"is_active": bool,
"is_default": bool,
"capabilities": [int, ...] # resolution capabilities
}
]
"buttons": [
"action_type": "<enum>" # none, button, key, special, macro, unknown
"button": int, # if button
"key": int, # if key
"special": "<enum>", # if special, for values see see special_lookup()
"macro": ["+B", "-B", "t400"] # if macro. uppercase only
]
"leds": [ ]
},
{
... next profile ...
}
]
}
ratbagd uses a minimum sane device (1 profile, 1 resolution, etc.) and any
JSON instructions get merged into that device. Which means you usually
only need to set those bits you care about being checked.
*/
#include "libratbag-util.h"
#include "ratbagd-json.h"
#include "ratbagd-test.h"
#include <json-glib/json-glib.h>
#include <libevdev/libevdev.h>
/* Hack because we don't do proper data passing */
static int num_resolutions;
static int num_buttons;
static int num_leds;
static int parse_error = 0;
#define parser_error(element_) { \
log_error("json: parser error: %s:%d: element '%s'\n", __func__, __LINE__, (element_)); \
parse_error = -EINVAL; \
goto out; \
}
static void parse_resolution_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_resolution *resolution = data;
if (streq(name, "xres")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("xres");
resolution->xres = v;
log_verbose("json: xres: %d\n", v);
} else if (streq(name, "yres")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("yres");
resolution->yres = v;
log_verbose("json: yres: %d\n", v);
} else if (streq(name, "dpi_min")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("dpi_min");
resolution->dpi_min = v;
log_verbose("json: dpi_min: %d\n", v);
} else if (streq(name, "dpi_max")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("dpi_min");
resolution->dpi_max = v;
log_verbose("json: dpi_max: %d\n", v);
} else if (streq(name, "is_active")) {
gboolean v = json_object_get_boolean_member(obj, name);
resolution->active = v;
log_verbose("json: is_active: %d\n", v);
} else if (streq(name, "is_default")) {
gboolean v = json_object_get_boolean_member(obj, name);
resolution->dflt = v;
log_verbose("json: is_default: %d\n", v);
} else if (streq(name, "capabilities")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(resolution->caps));
for (size_t s = 0; s < json_array_get_length(a); s++) {
int v = json_array_get_int_element(a, s);
if (v > RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION)
parser_error("capabilities");
resolution->caps[s] = v;
}
log_verbose("json: caps: %d %d %d %d %d...\n",
resolution->caps[0],
resolution->caps[1],
resolution->caps[2],
resolution->caps[3],
resolution->caps[4]);
} else {
log_error("json: unknown resolution key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_resolution(JsonNode *node,
struct ratbag_test_resolution *resolution)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_resolution_member, resolution);
}
static void parse_led_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_led *led = data;
if (streq(name, "type")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > RATBAG_LED_TYPE_SWITCHES)
parser_error("type");
led->type = v;
log_verbose("json: type: %d\n", v);
} else if (streq(name, "mode")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > RATBAG_LED_BREATHING)
parser_error("mode");
led->mode = v;
log_verbose("json: mode: %d\n", v);
} else if (streq(name, "duration")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 10000)
parser_error("duration");
led->ms = v;
log_verbose("json: duration: %d\n", v);
} else if (streq(name, "brightness")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 100)
parser_error("brightness");
led->brightness = v;
log_verbose("json: brightness: %d\n", v);
} else if (streq(name, "color")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) == 3);
led->color.red = json_array_get_int_element(a, 0);
led->color.green = json_array_get_int_element(a, 1);
led->color.blue = json_array_get_int_element(a, 2);
log_verbose("json: color: %02x%02x%02x\n",
led->color.red,
led->color.green,
led->color.blue);
} else {
log_error("json: unknown led key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_led(JsonNode *node, struct ratbag_test_led *led)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_led_member, led);
}
static enum ratbag_button_action_special
special_lookup(const char *string)
{
const struct {
const char *key;
enum ratbag_button_action_special special;
} lut[] = {
{ "invalid", RATBAG_BUTTON_ACTION_SPECIAL_INVALID},
{ "unknown", RATBAG_BUTTON_ACTION_SPECIAL_UNKNOWN},
{ "doubleclick", RATBAG_BUTTON_ACTION_SPECIAL_DOUBLECLICK},
{ "wheel-left", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_LEFT},
{ "wheel-right", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_RIGHT},
{ "wheel-up", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP},
{ "wheel-down", RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN},
{ "ratchet-mode-switch", RATBAG_BUTTON_ACTION_SPECIAL_RATCHET_MODE_SWITCH},
{ "resolution-cycle-up", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP},
{ "resolution-cycledown", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_DOWN},
{ "resolution-up", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP},
{ "resolution-down", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DOWN},
{ "resolution-alternate", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_ALTERNATE},
{ "resolution-default", RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_DEFAULT},
{ "profile-cycle-up", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP},
{ "profile-cycle-down", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_DOWN},
{ "profile-up", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_UP},
{ "profile-down", RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_DOWN},
{ "second-mode", RATBAG_BUTTON_ACTION_SPECIAL_SECOND_MODE},
{ "battery-level", RATBAG_BUTTON_ACTION_SPECIAL_BATTERY_LEVEL},
};
for (size_t i = 0; i < ARRAY_LENGTH(lut); i++) {
if (streq(string, lut[i].key))
return lut[i].special;
}
parser_error("special");
out:
return RATBAG_BUTTON_ACTION_SPECIAL_INVALID;
}
static enum ratbag_button_action_type
action_type_lookup(const char *string)
{
const struct {
const char *key;
enum ratbag_button_action_type type;
} lut[] = {
{ "none", RATBAG_BUTTON_ACTION_TYPE_NONE},
{ "button", RATBAG_BUTTON_ACTION_TYPE_BUTTON},
{ "special", RATBAG_BUTTON_ACTION_TYPE_SPECIAL},
{ "key", RATBAG_BUTTON_ACTION_TYPE_KEY},
{ "macro", RATBAG_BUTTON_ACTION_TYPE_MACRO},
{ "unknown", RATBAG_BUTTON_ACTION_TYPE_UNKNOWN},
};
for (size_t i = 0; i < ARRAY_LENGTH(lut); i++) {
if (streq(string, lut[i].key))
return lut[i].type;
}
parser_error("action_type");
out:
return RATBAG_BUTTON_ACTION_TYPE_UNKNOWN;
}
static inline struct ratbag_test_macro_event
parse_macro(const char *m)
{
struct ratbag_test_macro_event event = {
.type = RATBAG_MACRO_EVENT_INVALID,
.value = 0,
};
char keyname[32];
if (strlen(m) < 2)
goto out;
switch(m[0]) {
case '+':
event.type = RATBAG_MACRO_EVENT_KEY_PRESSED;
snprintf(keyname, sizeof(keyname), "KEY_%s", m + 1);
event.value = libevdev_event_code_from_name(EV_KEY,
keyname);
log_verbose("json: macro: +%s\n", keyname);
break;
case '-':
event.type = RATBAG_MACRO_EVENT_KEY_RELEASED;
snprintf(keyname, sizeof(keyname), "KEY_%s", m + 1);
event.value = libevdev_event_code_from_name(EV_KEY,
keyname);
log_verbose("json: macro: -%s\n", keyname);
break;
case 't':
event.type = RATBAG_MACRO_EVENT_WAIT;
event.value = atoi(m+1);
log_verbose("json: macro: t%d\n", event.value);
break;
default:
parser_error("macro");
break;
}
out:
return event;
}
static void parse_button_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_button *button = data;
if (streq(name, "action_type")) {
const gchar* v = json_object_get_string_member(obj, name);
button->action_type = action_type_lookup(v);
log_verbose("json: action_type: %s\n", v);
} else if (streq(name, "button")) {
gint v = json_object_get_int_member(obj, name);
if (v < 0 || v > 32)
parser_error("button");
button->button = v;
log_verbose("json: button: %d\n", v);
} else if (streq(name, "key")) {
gint v = json_object_get_int_member(obj, name);
if (v < 0 || v > KEY_MAX)
parser_error("key");
button->key = v;
log_verbose("json: key: %d\n", v);
} else if (streq(name, "special")) {
const gchar *v = json_object_get_string_member(obj, name);
button->special = special_lookup(v);
log_verbose("json: special: %s\n", v);
} else if (streq(name, "macro")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(button->macro));
for (size_t s = 0; s < json_array_get_length(a); s++) {
const gchar *v = json_array_get_string_element(a, s);
button->macro[s] = parse_macro(v);
}
} else {
log_error("json: unknown button key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_button(JsonNode *node, struct ratbag_test_button *button)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_button_member, button);
}
static void parse_profile_member(JsonObject *obj, const gchar *name,
JsonNode *node, void *data)
{
struct ratbag_test_profile *profile = data;
if (streq(name, "name")) {
const char *v = json_object_get_string_member(obj, name);
if (v == NULL)
parser_error("name");
free(profile->name);
profile->name = strdup_safe(v);
log_verbose("name: %s\n", v);
} else if (streq(name, "is_default")) {
gboolean v = json_object_get_boolean_member(obj, name);
profile->dflt = v;
log_verbose("json: is_default: %d\n", v);
} else if (streq(name, "is_active")) {
gboolean v = json_object_get_boolean_member(obj, name);
profile->active = v;
log_verbose("json: is_active: %d\n", v);
} else if (streq(name, "is_disabled")) {
gboolean v = json_object_get_boolean_member(obj, name);
profile->disabled = v;
log_verbose("json: is_disabled: %d\n", v);
} else if (streq(name, "rate")) {
gboolean v = json_object_get_int_member(obj, name);
if (v < 0 || v > 20000)
parser_error("rate");
profile->hz = v;
log_verbose("json: rate: %d\n", v);
} else if (streq(name, "report_rates")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(profile->report_rates));
for (size_t s = 0; s < json_array_get_length(a); s++) {
int v = json_array_get_int_element(a, s);
if (v < 0 || v > 20000)
parser_error("report_rate");
profile->report_rates[s] = v;
}
log_verbose("json: report rates: %d %d %d %d %d\n",
profile->report_rates[0],
profile->report_rates[1],
profile->report_rates[2],
profile->report_rates[3],
profile->report_rates[4]);
} else if (streq(name, "capabilities")) {
JsonArray *a = json_object_get_array_member(obj, name);
assert(json_array_get_length(a) < ARRAY_LENGTH(profile->caps));
for (size_t s = 0; s < json_array_get_length(a); s++) {
int v = json_array_get_int_element(a, s);
profile->caps[s] = v;
}
log_verbose("json: caps: %d %d %d %d %d...\n",
profile->caps[0],
profile->caps[1],
profile->caps[2],
profile->caps[3],
profile->caps[4]);
} else if (streq(name, "resolutions")) {
JsonArray *a = json_object_get_array_member(obj, name);
GList *list = json_array_get_elements(a);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing resolution %d\n", idx);
parse_resolution(l->data, &profile->resolutions[idx]);
l = g_list_next(l);
idx++;
}
g_list_free(list);
num_resolutions = max(idx, num_resolutions);
} else if (streq(name, "leds")) {
JsonArray *a = json_object_get_array_member(obj, name);
GList *list = json_array_get_elements(a);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing LED %d\n", idx);
parse_led(l->data, &profile->leds[idx]);
l = g_list_next(l);
idx++;
}
g_list_free(list);
num_leds = max(idx, num_leds);
} else if (streq(name, "buttons")) {
JsonArray *a = json_object_get_array_member(obj, name);
GList *list = json_array_get_elements(a);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing button %d\n", idx);
parse_button(l->data, &profile->buttons[idx]);
l = g_list_next(l);
idx++;
}
g_list_free(list);
num_buttons = max(idx, num_buttons);
} else {
log_error("json: unknown profile key '%s'\n", name);
parse_error = -EINVAL;
}
out:
return;
}
static void parse_profile(JsonNode *node,
struct ratbag_test_profile *profile)
{
JsonObject *obj = json_node_get_object(node);
json_object_foreach_member(obj, parse_profile_member, profile);
}
/* declared here because this isn't really public API, we just need to
* access it from the tests */
int ratbagd_parse_json(const char *data, struct ratbag_test_device *device)
{
g_autoptr(JsonParser) parser = NULL;
JsonNode *root;
JsonObject *obj;
JsonArray *arr;
int r = -EINVAL;
g_autoptr(GError) error = NULL;
error = 0;
parser = json_parser_new();
if (!json_parser_load_from_data(parser, data, strlen(data), &error)) {
log_error("Failed to load JSON: %s\n", error->message);
r = -EINVAL;
goto out;
}
log_verbose("json: data: %s\n", data);
root = json_parser_get_root(parser);
if (JSON_NODE_TYPE(root) != JSON_NODE_OBJECT)
parser_error("root");
obj = json_node_get_object(root);
arr = json_object_get_array_member(obj, "profiles");
if (!arr)
parser_error("profiles");
/* Our test device is preloaded with sane defaults, let's keep those */
num_resolutions = device->num_resolutions;
num_buttons = device->num_buttons;
num_leds = device->num_leds;
g_autoptr(GList) list = json_array_get_elements(arr);
GList *l = list;
int idx = 0;
while (l != NULL) {
log_verbose("json: processing profile %d\n", idx);
parse_profile(l->data, &device->profiles[idx]);
if (parse_error != 0)
goto out;;
l = g_list_next(l);
idx++;
}
device->num_profiles = idx;
device->num_resolutions = num_resolutions;
device->num_buttons = num_buttons;
device->num_leds = num_leds;
r = 0;
out:
return r;
}

28
ratbagd/ratbagd-json.h Normal file
View File

@@ -0,0 +1,28 @@
/*
* Copyright © 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"),
* 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.
*/
#pragma once
#include "libratbag-test.h"
int ratbagd_parse_json(const char *data, struct ratbag_test_device *device);

View File

@@ -30,159 +30,21 @@
#include <systemd/sd-event.h>
#include "libratbag-test.h"
#include "ratbagd-json.h"
/* A pre-setup sane device. Use for sanity testing by toggling the various
* error conditions.
*/
static const struct ratbag_test_device ratbagd_test_device_descr = {
.num_profiles = 4,
.num_resolutions = 3,
.num_buttons = 4,
.num_leds = 3,
.profiles = {
{
.name = NULL,
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 0 },
{ .button_type = RATBAG_BUTTON_TYPE_MIDDLE,
.action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = KEY_3 },
{ .button_type = RATBAG_BUTTON_TYPE_RIGHT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL,
.special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_MACRO,
.macro = {
{ .type = RATBAG_MACRO_EVENT_KEY_PRESSED,
.value = KEY_B },
{ .type = RATBAG_MACRO_EVENT_KEY_RELEASED,
.value = KEY_B },
{ .type = RATBAG_MACRO_EVENT_WAIT,
.value = 300 },
},
}
},
.resolutions = {
{ .xres = 100, .yres = 200,
.dpi_min = 50, .dpi_max = 5000},
{ .xres = 200, .yres = 300, .active = true, .dflt = true },
{ .xres = 300, .yres = 400 },
},
.active = true,
.dflt = false,
.hz = 1000,
.report_rates = {500, 1000},
.leds = {
{
.mode = RATBAG_LED_OFF,
.color = { .red = 255, .green = 0, .blue = 0 },
.ms = 1000,
.brightness = 20,
.type = RATBAG_LED_TYPE_LOGO,
},
{
.mode = RATBAG_LED_ON,
.color = { .red = 255, .green = 0, .blue = 0 },
.ms = 1000,
.brightness = 20,
.type = RATBAG_LED_TYPE_SIDE,
},
{
.mode = RATBAG_LED_CYCLE,
.color = { .red = 255, .green = 255, .blue = 0 },
.ms = 333,
.brightness = 40,
.type = RATBAG_LED_TYPE_SIDE,
}
},
},
{
.buttons = {
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 4 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 5 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 6 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_KEY,
.key = 7 },
},
.resolutions = {
{ .xres = 1100, .yres = 1200, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 1200, .yres = 1300, .dflt = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 1300, .yres = 1400, .active = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
},
.hz = 2000,
.active = false,
.dflt = true,
.name = "test profile 2",
},
{
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_SPECIAL,
.special = RATBAG_BUTTON_ACTION_SPECIAL_PROFILE_CYCLE_UP },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_MACRO,
.macro = {
{ .type = RATBAG_MACRO_EVENT_KEY_PRESSED,
.value = KEY_A },
{ .type = RATBAG_MACRO_EVENT_KEY_RELEASED,
.value = KEY_A },
{ .type = RATBAG_MACRO_EVENT_WAIT,
.value = 150 },
}
},
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 2 },
{ .action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 3 },
},
.resolutions = {
{ .xres = 2100, .yres = 2200, .active = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 2200, .yres = 2300, .dflt = true, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
{ .xres = 2300, .yres = 2400, .caps = RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION },
},
.hz = 3000,
.leds = {
{
.mode = RATBAG_LED_ON,
.color = { .red = 255, .green = 0, .blue = 0 },
.ms = 1000,
.brightness = 20
},
{
.mode = RATBAG_LED_CYCLE,
.color = { .red = 255, .green = 255, .blue = 0 },
.ms = 333,
.brightness = 40
}
},
.active = false,
.dflt = false,
},
{
.disabled = true,
},
},
.destroyed = NULL,
.destroyed_data = NULL,
};
int ratbagd_reset_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
static int load_test_device(sd_bus_message *m,
struct ratbagd *ctx,
const struct ratbag_test_device *source)
{
static int count;
static struct ratbagd_device *ratbagd_test_device = NULL;
struct ratbagd *ctx = userdata;
struct ratbag_device *device;
int r;
char devicename[64];
if (ratbagd_test_device) {
ratbagd_device_unlink(ratbagd_test_device);
ratbagd_device_free(ratbagd_test_device);
ratbagd_device_unref(ratbagd_test_device);
(void) sd_bus_emit_properties_changed(ctx->bus,
RATBAGD_OBJ_ROOT,
@@ -191,9 +53,10 @@ int ratbagd_reset_test_device(sd_bus_message *m,
NULL);
}
device = ratbag_device_new_test_device(ctx->lib_ctx, &ratbagd_test_device_descr);
device = ratbag_device_new_test_device(ctx->lib_ctx, source);
r = ratbagd_device_new(&ratbagd_test_device, ctx, "test_device", device);
snprintf(devicename, sizeof(devicename), "testdevice%d", count++);
r = ratbagd_device_new(&ratbagd_test_device, ctx, devicename, device);
/* the ratbagd_device takes its own reference, drop ours */
ratbag_device_unref(device);
@@ -216,6 +79,55 @@ int ratbagd_reset_test_device(sd_bus_message *m,
return 0;
}
static const struct ratbag_test_device default_device_descr = {
.num_profiles = 1,
.num_resolutions = 1,
.num_buttons = 1,
.num_leds = 0,
.profiles = {
{
.name = NULL,
.buttons = {
{ .button_type = RATBAG_BUTTON_TYPE_LEFT,
.action_type = RATBAG_BUTTON_ACTION_TYPE_BUTTON,
.button = 0 },
},
.resolutions = {
{ .xres = 1000, .yres = 1000,
.dpi_min = 1000, .dpi_max = 1000},
},
.disabled = false,
.active = true,
.dflt = true,
.hz = 1000,
.report_rates = {1000},
},
},
.destroyed = NULL,
.destroyed_data = NULL,
};
int ratbagd_load_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd *ctx = userdata;
struct ratbag_test_device td = default_device_descr;
char *data;
int r = 0;
CHECK_CALL(sd_bus_message_read(m, "s", &data));
r = ratbagd_parse_json(data, &td);
if (r != 0) {
log_error("Failed to parse JSON data\n");
} else {
r = load_test_device(m, ctx, &td);
}
return sd_bus_reply_method_return(m, "i", r);
}
#endif
void ratbagd_init_test_device(struct ratbagd *ctx)
@@ -223,7 +135,7 @@ void ratbagd_init_test_device(struct ratbagd *ctx)
#ifdef RATBAG_DEVELOPER_EDITION
setenv("RATBAG_TEST", "1", 0);
ratbagd_reset_test_device(NULL, ctx, NULL);
load_test_device(NULL, ctx, &default_device_descr);
#endif
}

View File

@@ -32,4 +32,7 @@ void ratbagd_init_test_device(struct ratbagd *ctx);
int ratbagd_reset_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error);
int ratbagd_load_test_device(sd_bus_message *m,
void *userdata,
sd_bus_error *error);
#endif /* RATBAG_DEVELOPER_EDITION */

View File

@@ -3,12 +3,27 @@
ratbagd \- system daemon to introspect and modify configurable mice
.SH SYNOPSIS
.B ratbagd
.RB [ \-\-verbose ]
.RB [ \-\-verbose[=debug]|\-\-quiet|\-\-version|\-\-help]
.SH DESCRIPTION
.B ratbagd
starts the daemon. It shouldn't be invoked directly;
.B ratbagd
is normally started through DBus activation.
.SH OPTIONS
.TP 8
.B \-\-help
Print help and exit
.TP 8
.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.
.TP 8
.B \-\-quiet
Disable any output but error messages.
.TP 8
.B \-\-version
Show the version number.
.SH SEE ALSO
.BR ratbagctl (1)
.SH AUTHORS

View File

@@ -166,9 +166,10 @@ static int ratbagd_get_devices(sd_bus *bus,
static const sd_bus_vtable ratbagd_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("APIVersion", "i", 0, offsetof(struct ratbagd, api_version), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Devices", "ao", ratbagd_get_devices, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
#ifdef RATBAG_DEVELOPER_EDITION
SD_BUS_METHOD("ResetTestDevice", "", "u", ratbagd_reset_test_device, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("LoadTestDevice", "s", "i", ratbagd_load_test_device, SD_BUS_VTABLE_UNPRIVILEGED),
#endif /* RATBAG_DEVELOPER_EDITION */
SD_BUS_VTABLE_END,
};
@@ -200,7 +201,7 @@ static void ratbagd_process_device(struct ratbagd *ctx,
/* device was removed, unlink it and destroy our context */
if (device) {
ratbagd_device_unlink(device);
ratbagd_device_free(device);
ratbagd_device_unref(device);
(void) sd_bus_emit_properties_changed(ctx->bus,
RATBAGD_OBJ_ROOT,
@@ -283,7 +284,7 @@ static struct ratbagd *ratbagd_free(struct ratbagd *ctx)
RATBAGD_DEVICE_FOREACH_SAFE(device, tmp, ctx) {
ratbagd_device_unlink(device);
ratbagd_device_free(device);
ratbagd_device_unref(device);
}
ctx->bus = sd_bus_flush_close_unref(ctx->bus);
@@ -345,6 +346,7 @@ static int ratbagd_new(struct ratbagd **out)
int r;
ctx = zalloc(sizeof(*ctx));
ctx->api_version = RATBAGD_API_VERSION;
r = sd_event_default(&ctx->event);
if (r < 0)
@@ -354,6 +356,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;
@@ -518,9 +521,88 @@ 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);
}
static bool install_ratbagd_devel_dbus_policy(void)
{
bool rc = true;
#ifdef RATBAG_DEVELOPER_EDITION
_cleanup_close_ int in = -1, out = -1;
int nread, nwrite;
char buf[40960];
int r;
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL;
sd_bus *bus = NULL;
rc = false;
log_verbose("Installing DBus policy file to %s\n", DBUS_POLICY_DST);
in = open(DBUS_POLICY_SRC, O_RDONLY);
if (in == -1) {
log_error("Failed to source policy file: %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");
goto out;
}
nread = read(in, buf, sizeof(buf));
if (nread < 0) {
log_error("Failed to read policy file: %m\n");
goto out;
}
nwrite = write(out, buf, nread);
if (nread != nwrite) {
log_error("Failed to write policy file: %m\n");
goto out;
}
/* Now poke DBus to reload itself */
r = sd_bus_open_system(&bus);
if (r < 0) {
log_error("Unable to open system bus: %s\n", strerror(-r));
goto out;
}
r = sd_bus_call_method(bus, "org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"ReloadConfig",
&error,
&m,
"");
if (r < 0) {
log_error("Failed to call DBus ReloadConfig: %s\n",
error.message);
goto out;
}
rc = true;
out:
sd_bus_error_free(&error);
sd_bus_message_unref(m);
sd_bus_unref(bus);
#endif
return rc;
}
static void remove_ratbagd_devel_dbus_policy(void)
{
#ifdef RATBAG_DEVELOPER_EDITION
unlink(DBUS_POLICY_DST);
#endif
}
int main(int argc, char *argv[])
{
struct ratbagd *ctx = NULL;
@@ -538,18 +620,21 @@ 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;
}
}
if (!install_ratbagd_devel_dbus_policy())
goto exit;
r = ratbagd_new(&ctx);
if (r < 0)
goto exit;
@@ -558,6 +643,7 @@ int main(int argc, char *argv[])
r = ratbagd_run(ctx);
remove_ratbagd_devel_dbus_policy();
exit:
ratbagd_free(ctx);

View File

@@ -170,7 +170,8 @@ int ratbagd_device_new(struct ratbagd_device **out,
struct ratbagd *ctx,
const char *sysname,
struct ratbag_device *lib_device);
struct ratbagd_device *ratbagd_device_free(struct ratbagd_device *device);
struct ratbagd_device *ratbagd_device_ref(struct ratbagd_device *device);
struct ratbagd_device *ratbagd_device_unref(struct ratbagd_device *device);
const char *ratbagd_device_get_sysname(struct ratbagd_device *device);
const char *ratbagd_device_get_path(struct ratbagd_device *device);
unsigned int ratbagd_device_get_num_buttons(struct ratbagd_device *device);
@@ -181,7 +182,7 @@ bool ratbagd_device_linked(struct ratbagd_device *device);
void ratbagd_device_link(struct ratbagd_device *device);
void ratbagd_device_unlink(struct ratbagd_device *device);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_device *, ratbagd_device_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbagd_device *, ratbagd_device_unref);
struct ratbagd_device *ratbagd_device_lookup(struct ratbagd *ctx,
const char *name);
@@ -217,6 +218,8 @@ struct ratbagd_device *ratbagd_device_next(struct ratbagd_device *device);
*/
struct ratbagd {
int api_version;
sd_event *event;
struct ratbag *lib_ctx;
struct udev_monitor *monitor;

View File

@@ -90,13 +90,11 @@ check_option_args() {
#
check_modules_specification() {
if [ x"$MODFILE" = x ]; then
if [ x"${INPUT_MODULES}" = x ]; then
echo ""
echo "Error: no modules specified (blank command line)."
usage
exit 1
fi
if [ x"${INPUT_MODULES}" = x ]; then
echo ""
echo "Error: no modules specified (blank command line)."
usage
exit 1
fi
}
@@ -112,7 +110,7 @@ Subject: [ANNOUNCE] $pkg_name $pkg_version
To: $list_to
Cc: $list_cc
libratbag $tag_name is out.
$pkg_name $tag_name is out.
The list of interesting changes are:
@@ -129,43 +127,12 @@ RELEASE
The libratbag project does not generate tarballs for releases, you can
grab one directly from github:
https://github.com/libratbag/libratbag/archive/$tag_name/$tarball
https://github.com/libratbag/$pkg_name/archive/$tag_name/$tarball
RELEASE
done
}
#------------------------------------------------------------------------------
# Function: read_modfile
#------------------------------------------------------------------------------
#
# Read the module names from the file and set a variable to hold them
# This will be the same interface as cmd line supplied modules
#
read_modfile() {
if [ x"$MODFILE" != x ]; then
# Make sure the file is sane
if [ ! -r "$MODFILE" ]; then
echo "Error: module file '$MODFILE' is not readable or does not exist."
exit 1
fi
# read from input file, skipping blank and comment lines
while read line; do
# skip blank lines
if [ x"$line" = x ]; then
continue
fi
# skip comment lines
if echo "$line" | $GREP -q "^#" ; then
continue;
fi
INPUT_MODULES="$INPUT_MODULES $line"
done <"$MODFILE"
fi
return 0
}
#------------------------------------------------------------------------------
# Function: print_epilog
#------------------------------------------------------------------------------
@@ -215,46 +182,6 @@ process_modules() {
done
}
#------------------------------------------------------------------------------
# Function: get_section
#------------------------------------------------------------------------------
# Code 'return 0' on success
# Code 'return 1' on error
# Sets global variable $section
get_section() {
local module_url
local full_module_url
# Obtain the git url in order to find the section to which this module belongs
full_module_url=`git config --get remote.$remote_name.url | sed 's:\.git$::'`
if [ $? -ne 0 ]; then
echo "Error: unable to obtain git url for remote \"$remote_name\"."
return 1
fi
# The last part of the git url will tell us the section. Look for xorg first
echo "$full_module_url"
module_url=`echo "$full_module_url" | $GREP -o "libratbag/.*"`
if [ $? -eq 0 ]; then
module_url=`echo $module_url | cut -d'/' -f2,3`
else
echo "Error: unable to locate a valid project url from \"$full_module_url\"."
echo "Cannot establish url as one of libratbag"
cd $top_src
return 1
fi
# Find the section (subdirs) where the tarballs are to be uploaded
# The module relative path can be app/xfs, xserver, or mesa/drm for example
section=`echo $module_url | cut -d'/' -f1`
if [ $? -ne 0 ]; then
echo "Error: unable to extract section from $module_url first field."
return 1
fi
return 0
}
#------------------------------------------------------------------------------
# Function: process_module
#------------------------------------------------------------------------------
@@ -299,26 +226,27 @@ process_module() {
current_branch=`git branch | $GREP "\*" | sed -e "s/\* //"`
remote_name=`git config --get branch.$current_branch.remote`
remote_branch=`git config --get branch.$current_branch.merge | cut -d'/' -f3,4`
remote_url_root="https://github.com/libratbag/libratbag/archive"
echo "Info: working off the \"$current_branch\" branch tracking the remote \"$remote_name/$remote_branch\"."
# Obtain the section
get_section
if [ $? -ne 0 ]; then
cd $top_src
return 1
fi
# Find out the tarname from meson.build
pkg_name=$($GREP '^project(' meson.build | sed 's|project(\([^\s,]*\).*|\1|' | sed "s/'//g")
pkg_version=$($GREP -m 1 '^ version : ' meson.build | sed 's| version : \([^\s,]*\).*|\1|' | sed "s/'//g")
pkg_version=$($GREP -m 1 '^[ ]*version \?: ' meson.build | sed 's|[ ]*version \?: \([^\s,]*\).*|\1|' | sed "s/'//g")
tar_name="$pkg_name-$pkg_version"
targz=$tar_name.tar.gz
tag_name=$(echo "v$pkg_version" | sed 's|.0$||')
# libratbag tags with v0.3 but piper user just 0.3
case `git describe` in
v*)
tag_name=$(echo "v$pkg_version" | sed 's|\.0$||')
;;
*)
tag_name=$(echo "$pkg_version" | sed 's|\.0$||')
;;
esac
remote_url_root="https://github.com/libratbag/$pkg_name/archive"
# Now get the tarballs from github directly to compute their checksum
remote_targz_url=$remote_url_root/$tag_name/$targz
echo "downloading the tarbal from $remote_targz_url"
echo "downloading the tarball from $remote_targz_url"
curl $remote_targz_url > $targz
# Obtain the top commit SHA which should be the version bump
@@ -513,13 +441,6 @@ do
exit 1
;;
*)
if [ x"${MODFILE}" != x ]; then
echo ""
echo "Error: specifying both modules and --modfile is not permitted"
echo ""
usage
exit 1
fi
INPUT_MODULES="${INPUT_MODULES} $1"
;;
esac
@@ -530,9 +451,6 @@ done
# If no modules specified (blank cmd line) display help
check_modules_specification
# Read the module file and normalize input in INPUT_MODULES
read_modfile
# Loop through each module to release
# Exit on error if --no-quit no specified
process_modules

View File

@@ -412,9 +412,12 @@ hidpp10drv_read_profile(struct ratbag_profile *profile)
profile->is_enabled = p.enabled;
if (profile->name)
if (profile->name) {
free(profile->name);
profile->name = strdup_safe((char*)p.name);
profile->name = NULL;
}
if (p.name && p.name[0] != '\0')
profile->name = strdup_safe((char*)p.name);
rc = hidpp10_get_current_profile(hidpp10, &idx);
if (rc == 0 && (unsigned int)idx == profile->index)

View File

@@ -57,6 +57,7 @@
#define HIDPP_CAP_LED_SW_CONTROL_1300 (1 << 7)
#define HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060 (1 << 8)
#define HIDPP_CAP_BATTERY_VOLTAGE_1001 (1 << 9)
#define HIDPP_CAP_RGB_EFFECTS_8071 (1 << 10)
#define HIDPP_HIDDEN_FEATURE (1 << 6)
@@ -68,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];
@@ -333,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[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
led_info = &drv_data->led_infos.color_leds_8070[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:
@@ -359,7 +371,7 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
/* pre-filled, only override if unknown */
if (led->type == RATBAG_LED_TYPE_UNKNOWN)
led->type = hidpp20_8070_get_location_mapping(led_info->location);
led->type = hidpp20_led_get_location_mapping(led_info->location);
led->color.red = h_led->color.red;
led->color.green = h_led->color.green;
led->color.blue = h_led->color.blue;
@@ -396,10 +408,85 @@ 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;
}
}
}
static void
hidpp20drv_read_led_8071(struct ratbag_led *led, struct hidpp20drv_data* drv_data)
{
struct hidpp20_profile *profile;
struct hidpp20_led *h_led;
struct hidpp20_rgb_device_info device_info;
struct hidpp20_rgb_cluster_info cluster_info;
int rc;
hidpp20_rgb_effects_get_device_info(drv_data->dev, &device_info);
cluster_info = drv_data->led_infos.color_leds_8071[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
switch (h_led->mode) {
case HIDPP20_LED_ON:
led->mode = RATBAG_LED_ON;
break;
case HIDPP20_LED_CYCLE:
led->mode = RATBAG_LED_CYCLE;
break;
case HIDPP20_LED_BREATHING:
led->mode = RATBAG_LED_BREATHING;
break;
default:
led->mode = RATBAG_LED_OFF;
break;
}
/* pre-filled, only override if unknown */
if (led->type == RATBAG_LED_TYPE_UNKNOWN)
led->type = hidpp20_led_get_location_mapping(cluster_info.location);
led->color.red = h_led->color.red;
led->color.green = h_led->color.green;
led->color.blue = h_led->color.blue;
led->ms = h_led->period;
led->brightness = h_led->brightness * 255 / 100;
if (device_info.ext_caps & HIDPP20_COLOR_LED_INFO_EXT_CAP_MONOCHROME_ONLY)
led->colordepth = RATBAG_LED_COLORDEPTH_MONOCHROME;
else
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
for (int i = 0; i < cluster_info.num_effects; i++) {
struct hidpp20_rgb_effect_info ei;
rc = hidpp20_rgb_effects_get_effect_info(drv_data->dev,
cluster_info.index,
i, &ei);
if (rc < 0)
break;
switch (ei.effect_id) {
case HIDPP20_COLOR_LED_ZONE_EFFECT_DISABLED:
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_FIXED:
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_CYCLING:
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
break;
case HIDPP20_COLOR_LED_ZONE_EFFECT_WAVE:
case HIDPP20_COLOR_LED_ZONE_EFFECT_BREATHING:
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
break;
default:
log_debug(led->profile->device->ratbag,
"%s: Unsupported effect (%d)\n",
led->profile->device->name,
ei.effect_id);
break;
}
}
@@ -413,6 +500,8 @@ hidpp20drv_read_led(struct ratbag_led *led)
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
hidpp20drv_read_led_8070(led, drv_data);
else if (drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071)
hidpp20drv_read_led_8071(led, drv_data);
else if (drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300)
hidpp20drv_read_led_1300(led, drv_data);
}
@@ -609,15 +698,17 @@ hidpp20drv_update_led_1300(struct ratbag_led *led, struct hidpp20drv_data *data)
}
static int
hidpp20drv_update_led_8070(struct ratbag_led *led, struct ratbag_profile* profile,
struct hidpp20drv_data *drv_data)
hidpp20drv_update_led_8070_8071(struct ratbag_led *led, struct ratbag_profile* profile,
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;
@@ -642,6 +733,13 @@ hidpp20drv_update_led_8070(struct ratbag_led *led, struct ratbag_profile* profil
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;
}
@@ -652,8 +750,9 @@ hidpp20drv_update_led(struct ratbag_led *led)
struct ratbag_device *device = profile->device;
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
return hidpp20drv_update_led_8070(led, profile, drv_data);
if ((drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070) |
(drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071))
return hidpp20drv_update_led_8070_8071(led, profile, drv_data);
if (drv_data->capabilities & HIDPP_CAP_LED_SW_CONTROL_1300)
return hidpp20drv_update_led_1300(led, drv_data);
@@ -743,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);
@@ -764,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;
@@ -780,7 +913,8 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
unsigned int default_rate = 500;
int rc;
if (drv_data->capabilities & HIDPP_CAP_RESOLUTION_2200) {
if ((drv_data->capabilities & HIDPP_CAP_RESOLUTION_2200) &&
(drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201) == 0) {
uint16_t resolution;
uint8_t flags;
@@ -849,6 +983,9 @@ hidpp20drv_update_resolution_dpi_8100(struct ratbag_resolution *resolution,
h_profile = &drv_data->profiles->profiles[profile->index];
h_profile->dpi[resolution->index] = dpi;
if (resolution->is_default)
h_profile->default_dpi = resolution->index;
return RATBAG_SUCCESS;
}
@@ -860,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)
@@ -876,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]) {
@@ -891,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
@@ -918,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;
}
@@ -971,19 +1126,15 @@ hidpp20drv_read_kbd_reprogrammable_key(struct ratbag_device *device)
}
static int
hidpp20drv_read_color_leds(struct ratbag_device *device)
hidpp20drv_read_color_leds_8070(struct hidpp20drv_data *drv_data)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
int rc;
if (!(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070))
return 0;
free(drv_data->led_infos.color_leds);
drv_data->led_infos.color_leds = NULL;
free(drv_data->led_infos.color_leds_8070);
drv_data->led_infos.color_leds_8070 = NULL;
drv_data->num_leds = 0;
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos.color_leds);
rc = hidpp20_color_led_effects_get_zone_infos(drv_data->dev, &drv_data->led_infos.color_leds_8070);
if (rc > 0) {
drv_data->num_leds = rc;
@@ -993,6 +1144,38 @@ hidpp20drv_read_color_leds(struct ratbag_device *device)
return rc;
}
static int
hidpp20drv_read_color_leds_8071(struct hidpp20drv_data *drv_data)
{
int rc;
free(drv_data->led_infos.color_leds_8071);
drv_data->led_infos.color_leds_8071 = NULL;
drv_data->num_leds = 0;
rc = hidpp20_rgb_effects_get_cluster_infos(drv_data->dev, &drv_data->led_infos.color_leds_8071);
if (rc > 0) {
drv_data->num_leds = rc;
rc = 0;
}
return rc;
}
static int
hidpp20drv_read_color_leds(struct ratbag_device *device)
{
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
if(drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
return hidpp20drv_read_color_leds_8070(drv_data);
else if (drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071)
return hidpp20drv_read_color_leds_8071(drv_data);
return 0;
}
static int
hidpp20drv_read_leds(struct ratbag_device *device)
{
@@ -1068,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)
{
@@ -1195,15 +1432,18 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
break;
}
case HIDPP_PAGE_COLOR_LED_EFFECTS: {
/* The 8070 feauture implemented in the G602 doesn't follow the spec,
* so we ignore it */
if (ratbag_device_data_hidpp20_get_quirk(device->data) == HIDPP20_QUIRK_G602)
break;
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;
break;
}
case HIDPP_PAGE_LED_SW_CONTROL: {
@@ -1221,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: {
@@ -1258,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\n");
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\n");
return RATBAG_ERROR_DEVICE;
}
}
list_for_each(button, &profile->buttons, link) {
@@ -1286,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\n");
return RATBAG_ERROR_DEVICE;
}
}
list_for_each(led, &profile->leds, link) {
@@ -1295,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\n");
return RATBAG_ERROR_DEVICE;
}
}
}
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
@@ -1307,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\n");
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);
}
}
}
@@ -1348,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
@@ -1380,9 +1621,10 @@ hidpp20drv_remove(struct ratbag_device *device)
if (drv_data->profiles)
hidpp20_onboard_profiles_destroy(drv_data->profiles);
free(drv_data->led_infos.color_leds);
free(drv_data->led_infos.color_leds_8070);
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);
@@ -1393,6 +1635,7 @@ hidpp20drv_init_device(struct ratbag_device *device,
struct hidpp20drv_data *drv_data)
{
struct ratbag_profile *profile;
bool active_profile = false;
ratbag_device_init_profiles(device,
drv_data->num_profiles,
@@ -1402,6 +1645,18 @@ hidpp20drv_init_device(struct ratbag_device *device,
ratbag_device_for_each_profile(device, profile)
hidpp20drv_read_profile(profile);
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
/* Fallback to the first profile if no profile is active */
list_for_each(profile, &device->profiles, link)
if (profile->is_active)
active_profile = true;
if (!active_profile && device->num_profiles >= 1) {
profile = ratbag_device_get_profile(device, 0);
profile->is_active = true;
}
}
}
static int
@@ -1434,19 +1689,24 @@ 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;
}
dev->quirk = ratbag_device_data_hidpp20_get_quirk(device->data);
drv_data->dev = dev;
log_debug(device->ratbag, "'%s' is using protocol v%d.%d\n", ratbag_device_get_name(device), dev->proto_major, dev->proto_minor);
if(dev->quirk != HIDPP20_QUIRK_NONE)
log_debug(device->ratbag, "'%s' is quirked (%s)\n", ratbag_device_get_name(device), hidpp20_get_quirk_string(dev->quirk));
/* 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

@@ -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",
.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

@@ -267,9 +267,8 @@ roccat_is_ready(struct ratbag_device *device)
uint8_t buf[3] = { 0 };
int rc;
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_CONFIGURE_PROFILE,
buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_CONFIGURE_PROFILE,
buf, sizeof(buf));
if (rc < 0)
return rc;
if (rc != sizeof(buf))
@@ -315,8 +314,8 @@ roccat_current_profile(struct ratbag_device *device)
uint8_t buf[3];
int ret;
ret = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_PROFILE, buf,
sizeof(buf), HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
ret = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_PROFILE, buf,
sizeof(buf));
if (ret < 0)
return ret;
@@ -335,8 +334,8 @@ roccat_set_current_profile(struct ratbag_device *device, unsigned int index)
if (index > ROCCAT_PROFILE_MAX)
return -EINVAL;
ret = ratbag_hidraw_raw_request(device, buf[0], buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
ret = ratbag_hidraw_set_feature_report(device, buf[0], buf,
sizeof(buf));
if (ret < 0)
return ret;
@@ -362,8 +361,8 @@ roccat_set_config_profile(struct ratbag_device *device, uint8_t profile, uint8_t
if (profile > ROCCAT_PROFILE_MAX)
return -EINVAL;
ret = ratbag_hidraw_raw_request(device, buf[0], buf, sizeof(buf),
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
ret = ratbag_hidraw_set_feature_report(device, buf[0], buf,
sizeof(buf));
if (ret < 0)
return ret;
@@ -409,9 +408,8 @@ roccat_write_profile(struct ratbag_profile *profile)
*crc = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_PROFILE);
roccat_set_config_profile(device, index, ROCCAT_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
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;
@@ -468,9 +466,8 @@ roccat_read_button(struct ratbag_button *button)
macro = &drv_data->macros[button->profile->index][button->index];
buf = (uint8_t*)macro;
buf[0] = ROCCAT_REPORT_ID_MACRO;
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
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",
@@ -594,9 +591,8 @@ roccat_write_macro(struct ratbag_button *button,
macro->length = count;
macro->checksum = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_MACRO);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_MACRO,
buf, ROCCAT_REPORT_SIZE_MACRO);
if (rc < 0)
return rc;
@@ -673,9 +669,8 @@ roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
settings_report->checksum = roccat_compute_crc(buf, ROCCAT_REPORT_SIZE_SETTINGS);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
rc = ratbag_hidraw_set_feature_report(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS);
if (rc < 0)
return rc;
@@ -713,9 +708,8 @@ roccat_read_profile(struct ratbag_profile *profile)
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_raw_request(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_SETTINGS,
buf, ROCCAT_REPORT_SIZE_SETTINGS);
if (rc < ROCCAT_REPORT_SIZE_SETTINGS)
return;
@@ -756,9 +750,8 @@ roccat_read_profile(struct ratbag_profile *profile)
buf = drv_data->profiles[profile->index];
roccat_set_config_profile(device, profile->index, ROCCAT_CONFIG_KEY_MAPPING);
rc = ratbag_hidraw_raw_request(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
rc = ratbag_hidraw_get_feature_report(device, ROCCAT_REPORT_ID_KEY_MAPPING,
buf, ROCCAT_REPORT_SIZE_PROFILE);
msleep(10);

View File

@@ -701,7 +701,7 @@ construct_cycle_buffer(struct steelseries_led_cycle *cycle,
/* this seems to be the minimum allowed */
duration = max(buf[spec->point_count_idx] * 330, cycle->duration);
hidpp_set_unaligned_le_u16(&buf[spec->duration_idx], duration);
set_unaligned_le_u16(&buf[spec->duration_idx], duration);
}
static int

View File

@@ -214,7 +214,10 @@ test_read_profile(struct ratbag_profile *profile)
res->is_default = r->dflt;
if (r->dflt)
default_set = true;
ratbag_resolution_set_cap(res, r->caps);
for (size_t j = 0; j < ARRAY_LENGTH(r->caps) && r->caps[j]; j++) {
ratbag_resolution_set_cap(res, r->caps[j]);
}
}
/* special case triggered by the test suite when num_resolutions is 0 */

View File

@@ -234,7 +234,7 @@ hidpp20_1b04_get_physical_mapping_name(uint16_t value)
}
enum ratbag_led_type
hidpp20_8070_get_location_mapping(uint16_t value)
hidpp20_led_get_location_mapping(uint16_t value)
{
const struct hidpp20_8070_location_mapping *map;
@@ -247,7 +247,7 @@ hidpp20_8070_get_location_mapping(uint16_t value)
}
const char *
hidpp20_8070_get_location_mapping_name(uint16_t value)
hidpp20_led_get_location_mapping_name(uint16_t value)
{
const struct hidpp20_8070_location_mapping *map;
@@ -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,
@@ -322,6 +349,21 @@ hidpp_log(struct hidpp_device *dev,
va_end(args);
}
char *
hidpp_buffer_to_string(const uint8_t *buf, size_t len)
{
size_t dstlen = len * 6;
char *dst = zalloc(dstlen);
unsigned int n = 0;
char *sep = "";
for (unsigned i = 0; i < len; ++i) {
n += snprintf_safe(&dst[n], dstlen - n, "%s%02x", sep, buf[i] & 0xFF);
sep = " ";
}
return dst;
}
void
hidpp_log_buffer(struct hidpp_device *dev,
enum hidpp_log_priority priority,
@@ -329,23 +371,10 @@ hidpp_log_buffer(struct hidpp_device *dev,
uint8_t *buf, size_t len)
{
_cleanup_free_ char *output_buf = NULL;
char *sep = "";
unsigned int i, n;
unsigned int buf_len;
_cleanup_free_ char *bytes = NULL;
buf_len = header ? strlen(header) : 0;
buf_len += len * 3;
buf_len += 1; /* terminating '\0' */
output_buf = zalloc(buf_len);
n = 0;
if (header)
n += snprintf_safe(output_buf, buf_len - n, "%s", header);
for (i = 0; i < len; ++i) {
n += snprintf_safe(&output_buf[n], buf_len - n, "%s%02x", sep, buf[i] & 0xFF);
sep = " ";
}
bytes = hidpp_buffer_to_string(buf, len);
asprintf(&output_buf, "%s %s", header ? header : "", bytes);
hidpp_log(dev, priority, "%s\n", output_buf);
}
@@ -361,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

@@ -32,6 +32,8 @@
#include <stdarg.h>
#include <stddef.h>
#include "libratbag-util.h"
#define HIDPP_RECEIVER_IDX 0xFF
#define HIDPP_WIRED_DEVICE_IDX 0x00
@@ -93,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
@@ -118,10 +130,10 @@ enum ratbag_button_type
hidpp20_1b04_get_physical_mapping(uint16_t value);
enum ratbag_led_type
hidpp20_8070_get_location_mapping(uint16_t value);
hidpp20_led_get_location_mapping(uint16_t value);
const char *
hidpp20_8070_get_location_mapping_name(uint16_t value);
hidpp20_led_get_location_mapping_name(uint16_t value);
const char *
hidpp20_1b04_get_logical_mapping_name(uint16_t value);
@@ -138,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,
@@ -145,6 +162,9 @@ hidpp_log(struct hidpp_device *dev,
...)
__attribute__ ((format (printf, 3, 4)));
char *
hidpp_buffer_to_string(const uint8_t *buf, size_t len);
void
hidpp_log_buffer(struct hidpp_device *dev,
enum hidpp_log_priority priority,
@@ -163,62 +183,30 @@ hidpp_log_buffer(struct hidpp_device *dev,
uint16_t hidpp_crc_ccitt(uint8_t *data, unsigned int length);
static inline uint16_t
hidpp_get_unaligned_be_u16(uint8_t *buf)
{
return (buf[0] << 8) | buf[1];
}
static inline uint16_t
hidpp_be_u16_to_cpu(uint16_t data)
{
return hidpp_get_unaligned_be_u16((uint8_t *)&data);
}
static inline void
hidpp_set_unaligned_be_u16(uint8_t *buf, uint16_t value)
{
buf[0] = value >> 8;
buf[1] = value & 0xFF;
return get_unaligned_be_u16((uint8_t *)&data);
}
static inline uint16_t
hidpp_cpu_to_be_u16(uint16_t data)
{
uint16_t result;
hidpp_set_unaligned_be_u16((uint8_t *)&result, data);
set_unaligned_be_u16((uint8_t *)&result, data);
return result;
}
static inline uint16_t
hidpp_get_unaligned_le_u16(uint8_t *buf)
{
return (buf[1] << 8) | buf[0];
}
static inline uint16_t
hidpp_le_u16_to_cpu(uint16_t data)
{
return hidpp_get_unaligned_le_u16((uint8_t *)&data);
}
static inline void
hidpp_set_unaligned_le_u16(uint8_t *buf, uint16_t value)
{
buf[0] = value & 0xFF;
buf[1] = value >> 8;
return get_unaligned_le_u16((uint8_t *)&data);
}
static inline uint16_t
hidpp_cpu_to_le_u16(uint16_t data)
{
uint16_t result;
hidpp_set_unaligned_le_u16((uint8_t *)&result, data);
set_unaligned_le_u16((uint8_t *)&result, data);
return result;
}
static inline uint32_t
hidpp_get_unaligned_be_u32(uint8_t *buf)
{
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}

23
src/hidpp.i Normal file
View File

@@ -0,0 +1,23 @@
/* wrapping libratbag functions from hidpp*.h using SWIG. */
%module hidpp
%{
/* the resulting C file should be built as a python extension */
#define SWIG_FILE_WITH_INIT
/* Includes the header in the wrapper code */
#include <shared.h>
#include <hidpp-generic.h>
#include <hidpp20.h>
%}
#define __attribute__(x)
#define _Static_assert(a,b)
/* Parse the header file to generate wrappers */
%include "stdint.i"
%include "cpointer.i"
%pointer_functions(int, intp);
%pointer_functions(uint8_t, uint8_tp);
%pointer_functions(uint16_t, uint16_tp);
%include "hidpp-generic.h"
%include "hidpp20.h"

View File

@@ -161,7 +161,6 @@ hidpp10_build_dpi_table_from_dpi_info(struct hidpp10_device *dev,
return 0;
}
static int
hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
{
@@ -171,6 +170,7 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
int ret;
uint8_t hidpp_err = 0;
int command_size;
_cleanup_free_ char *rxdata = NULL, *txdata = NULL;
switch (msg->msg.report_id) {
case REPORT_ID_SHORT:
@@ -186,9 +186,19 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
/* create the expected header */
expected_header = *msg;
txdata = hidpp_buffer_to_string(&msg->data[4], command_size - 4);
hidpp_log_raw(&dev->base, "hidpp10 tx: %02x | %02x | %02x | %02x | %s\n",
msg->msg.report_id,
msg->msg.device_idx,
msg->msg.sub_id,
msg->msg.address,
txdata);
/* response message length doesn't depend on request length */
#if 0
hidpp_log_buf_raw(&dev->base, " expected_header: ?? ", &expected_header.data[1], 3);
hidpp_log_buf_raw(&dev->base, " expected_error_dev: ", expected_error_dev.data, SHORT_MESSAGE_LENGTH);
#endif
/* Send the message to the Device */
ret = hidpp_write_command(&dev->base, msg->data, command_size);
@@ -238,8 +248,15 @@ hidpp10_request_command(struct hidpp10_device *dev, union hidpp10_message *msg)
goto out_err;
}
rxdata = hidpp_buffer_to_string(&read_buffer.data[4], ret - 4);
hidpp_log_raw(&dev->base, "hidpp10 rx: %02x | %02x | %02x | %02x | %s\n",
read_buffer.msg.report_id,
read_buffer.msg.device_idx,
read_buffer.msg.sub_id,
read_buffer.msg.address,
rxdata);
if (!hidpp_err) {
hidpp_log_buf_raw(&dev->base, " received: ", read_buffer.data, ret);
/* copy the answer for the caller */
*msg = read_buffer;
}
@@ -277,7 +294,8 @@ hidpp10_get_hidpp_notifications(struct hidpp10_device *dev,
union hidpp10_message notifications = CMD_HIDPP_NOTIFICATIONS(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching HID++ notifications\n");
hidpp_log_raw(&dev->base, "Fetching HID++ notifications (%#02x)\n",
__CMD_HIDPP_NOTIFICATIONS);
res = hidpp10_request_command(dev, &notifications);
if (res)
@@ -298,7 +316,8 @@ hidpp10_set_hidpp_notifications(struct hidpp10_device *dev,
union hidpp10_message notifications = CMD_HIDPP_NOTIFICATIONS(idx, SET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Setting HID++ notifications\n");
hidpp_log_raw(&dev->base, "Setting HID++ notifications (%#02x\n",
__CMD_HIDPP_NOTIFICATIONS);
notifications.msg.parameters[0] = reporting_flags & 0xFF;
notifications.msg.parameters[1] = (reporting_flags >> 8) & 0x1F;
@@ -333,7 +352,8 @@ hidpp10_get_individual_features(struct hidpp10_device *dev,
union hidpp10_message features = CMD_ENABLE_INDIVIDUAL_FEATURES(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching individual features\n");
hidpp_log_raw(&dev->base, "Fetching individual features (%#02x)\n",
__CMD_ENABLE_INDIVIDUAL_FEATURES);
res = hidpp10_request_command(dev, &features);
if (res)
@@ -356,7 +376,8 @@ hidpp10_set_individual_features(struct hidpp10_device *dev,
union hidpp10_message mode = CMD_ENABLE_INDIVIDUAL_FEATURES(idx, SET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Setting individual features\n");
hidpp_log_raw(&dev->base, "Setting individual features (%#02x)\n",
__CMD_ENABLE_INDIVIDUAL_FEATURES);
mode.msg.device_idx = dev->index;
@@ -700,7 +721,7 @@ hidpp10_write_profile_directory(struct hidpp10_device *dev)
}
crc = hidpp_crc_ccitt(bytes, HIDPP10_PAGE_SIZE - 2);
hidpp_set_unaligned_be_u16(&bytes[HIDPP10_PAGE_SIZE - 2], crc);
set_unaligned_be_u16(&bytes[HIDPP10_PAGE_SIZE - 2], crc);
res = hidpp10_send_hot_payload(dev,
0x00, 0x0000, /* destination: RAM */
@@ -782,7 +803,8 @@ hidpp10_get_current_profile(struct hidpp10_device *dev, uint8_t *current_profile
unsigned i;
uint8_t type, page, offset;
hidpp_log_raw(&dev->base, "Fetching current profile\n");
hidpp_log_raw(&dev->base, "Fetching current profile (%#02x)\n",
__CMD_PROFILE);
res = hidpp10_request_command(dev, &profile);
if (res)
@@ -811,6 +833,20 @@ hidpp10_get_current_profile(struct hidpp10_device *dev, uint8_t *current_profile
"unable to find the profile at (%d,%d) in the directory\n",
page, offset);
break;
case PROFILE_TYPE_FACTORY:
/* Factory profile is selected and profile switching is
* disabled. Let's switch to the first profile because the
* factory profile doesn't help anywone */
res = hidpp10_set_current_profile(dev, 0);
if (res == 0) {
hidpp_log_info(&dev->base, "switched from factory profile to 0\n");
*current_profile = 0;
return 0;
}
hidpp_log_error(&dev->base,
"current profile is factory profile but switching to 0 failed.\n");
break;
default:
hidpp_log_error(&dev->base,
"Unexpected value: %02x\n",
@@ -829,7 +865,8 @@ hidpp10_set_internal_current_profile(struct hidpp10_device *dev,
union hidpp10_message profile = CMD_PROFILE(idx, SET_REGISTER_REQ);
int8_t page, offset;
hidpp_log_raw(&dev->base, "Setting current profile\n");
hidpp_log_raw(&dev->base, "Setting current profile (%#02x)\n",
__CMD_PROFILE);
profile.msg.parameters[0] = profile_type;
@@ -964,9 +1001,9 @@ hidpp10_fill_dpi_modes_16(struct hidpp10_device *dev,
struct _hidpp10_dpi_mode_16 *dpi = &dpi_list[i];
be = (uint8_t*)&dpi->xres;
profile->dpi_modes[i].xres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_be_u16(be));
profile->dpi_modes[i].xres = hidpp10_get_dpi_value(dev, get_unaligned_be_u16(be));
be = (uint8_t*)&dpi->yres;
profile->dpi_modes[i].yres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_be_u16(be));
profile->dpi_modes[i].yres = hidpp10_get_dpi_value(dev, get_unaligned_be_u16(be));
profile->dpi_modes[i].led[0] = dpi->led1 == 0x2;
profile->dpi_modes[i].led[1] = dpi->led2 == 0x2;
@@ -988,9 +1025,9 @@ hidpp10_write_dpi_modes_16(struct hidpp10_device *dev,
struct _hidpp10_dpi_mode_16 *dpi = &dpi_list[i];
be = (uint8_t*)&dpi->xres;
hidpp_set_unaligned_be_u16(be, hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].xres));
set_unaligned_be_u16(be, hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].xres));
be = (uint8_t*)&dpi->yres;
hidpp_set_unaligned_be_u16(be, hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].yres));
set_unaligned_be_u16(be, hidpp10_get_dpi_mapping(dev, profile->dpi_modes[i].yres));
dpi->led1 = profile->dpi_modes[i].led[0] ? 0x02 : 0x01;
dpi->led2 = profile->dpi_modes[i].led[1] ? 0x02 : 0x01;
@@ -1532,57 +1569,62 @@ hidpp10_read_profile(struct hidpp10_device *dev, uint8_t number)
}
profile->initialized = 1;
hidpp_log_buf_raw(&dev->base,
"+++++++++++++++++++ Profile data: +++++++++++++++++ \n",
data->data, 78);
hidpp_log_raw(&dev->base,
"+++++++++++++++++++ Profile data: +++++++++++++++++ \n");
for (size_t x = 0; x < 78; x += 8) {
hidpp_log_buf_raw(&dev->base, NULL, &data->data[x], min(8, 78 - x));
}
hidpp_log_raw(&dev->base,
"+++++++++++++++++++ Profile data end +++++++++++++++++ \n");
}
hidpp_log_raw(&dev->base, "Profile %d:\n", number);
for (i = 0; i < 5; i++) {
hidpp_log_raw(&dev->base,
"DPI mode: %dx%d dpi\n",
" DPI mode: %dx%d dpi\n",
profile->dpi_modes[i].xres,
profile->dpi_modes[i].yres);
hidpp_log_raw(&dev->base,
"LED status: 1:%s 2:%s 3:%s 4:%s\n",
" LED status: 1:%s 2:%s 3:%s 4:%s\n",
profile->dpi_modes[i].led[0] ? "on" : "off",
profile->dpi_modes[i].led[1] ? "on" : "off",
profile->dpi_modes[i].led[2] ? "on" : "off",
profile->dpi_modes[i].led[3] ? "on" : "off");
}
hidpp_log_raw(&dev->base, "Angle correction: %d\n", profile->angle_correction);
hidpp_log_raw(&dev->base, "Default DPI mode: %d\n", profile->default_dpi_mode);
hidpp_log_raw(&dev->base, "Refresh rate: %d\n", profile->refresh_rate);
hidpp_log_raw(&dev->base, " Angle correction: %d\n", profile->angle_correction);
hidpp_log_raw(&dev->base, " Default DPI mode: %d\n", profile->default_dpi_mode);
hidpp_log_raw(&dev->base, " Refresh rate: %d\n", profile->refresh_rate);
for (i = 0; i < 13; i++) {
union hidpp10_button *button = &profile->buttons[i];
switch (button->any.type) {
case PROFILE_BUTTON_TYPE_BUTTON:
hidpp_log_raw(&dev->base,
"Button %zd: button %d\n",
" Button %zd: button %d\n",
i,
button->button.button);
break;
case PROFILE_BUTTON_TYPE_KEYS:
hidpp_log_raw(&dev->base,
"Button %zd: key %d modifier %x\n",
" Button %zd: key %d modifier %x\n",
i,
button->keys.key,
button->keys.modifier_flags);
break;
case PROFILE_BUTTON_TYPE_SPECIAL:
hidpp_log_raw(&dev->base,
"Button %zd: special %x\n",
" Button %zd: special %x\n",
i,
button->special.special);
break;
case PROFILE_BUTTON_TYPE_CONSUMER_CONTROL:
hidpp_log_raw(&dev->base,
"Button %zd: consumer: %x\n",
" Button %zd: consumer: %x\n",
i,
button->consumer_control.consumer_control);
break;
case PROFILE_BUTTON_TYPE_DISABLED:
hidpp_log_raw(&dev->base, "Button %zd: disabled\n", i);
hidpp_log_raw(&dev->base, " Button %zd: disabled\n", i);
break;
default:
/* FIXME: this is the page number for the macro,
@@ -1757,7 +1799,7 @@ hidpp10_set_profile(struct hidpp10_device *dev, uint8_t number, struct hidpp10_p
}
crc = hidpp_crc_ccitt(page_data, HIDPP10_PAGE_SIZE - 2);
hidpp_set_unaligned_be_u16(&page_data[HIDPP10_PAGE_SIZE - 2], crc);
set_unaligned_be_u16(&page_data[HIDPP10_PAGE_SIZE - 2], crc);
/*
* writing the data in several steps to prevent shroedinger state
@@ -1851,7 +1893,8 @@ hidpp10_get_led_status(struct hidpp10_device *dev,
uint8_t *status = led_status.msg.parameters;
int res;
hidpp_log_raw(&dev->base, "Fetching LED status\n");
hidpp_log_raw(&dev->base, "Fetching LED status (%#02x)\n",
__CMD_LED_STATUS);
res = hidpp10_request_command(dev, &led_status);
if (res)
@@ -1877,7 +1920,8 @@ hidpp10_set_led_status(struct hidpp10_device *dev,
int res;
int i;
hidpp_log_raw(&dev->base, "Setting LED status\n");
hidpp_log_raw(&dev->base, "Setting LED status (%#02x)\n",
__CMD_LED_STATUS);
for (i = 0; i < 6; i++) {
switch (led[i]) {
@@ -1928,7 +1972,8 @@ hidpp10_get_led_intensity(struct hidpp10_device *dev,
uint8_t *intensity = led_intensity.msg.parameters;
int res;
hidpp_log_raw(&dev->base, "Fetching LED intensity\n");
hidpp_log_raw(&dev->base, "Fetching LED intensity (%#02x)\n",
__CMD_LED_INTENSITY);
res = hidpp10_request_command(dev, &led_intensity);
if (res)
@@ -1953,7 +1998,8 @@ hidpp10_set_led_intensity(struct hidpp10_device *dev,
uint8_t *intensity = led_intensity.msg.parameters;
int res;
hidpp_log_raw(&dev->base, "Setting LED intensity\n");
hidpp_log_raw(&dev->base, "Setting LED intensity (%#02x)\n",
__CMD_LED_INTENSITY);
intensity[0] = led_intensity_in_percent[0]/10 & 0xF;
intensity[0] |= (led_intensity_in_percent[1]/10 & 0xF) << 4;
@@ -1992,7 +2038,8 @@ hidpp10_get_led_color(struct hidpp10_device *dev,
union hidpp10_message led_color = CMD_LED_COLOR(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching LED color\n");
hidpp_log_raw(&dev->base, "Fetching LED color (%#02x)\n",
__CMD_LED_COLOR);
res = hidpp10_request_command(dev, &led_color);
if (res)
@@ -2015,7 +2062,8 @@ hidpp10_set_led_color(struct hidpp10_device *dev,
union hidpp10_message led_color = CMD_LED_COLOR(idx, SET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Setting LED color\n");
hidpp_log_raw(&dev->base, "Setting LED color (%#02x)\n",
__CMD_LED_COLOR);
led_color.msg.parameters[0] = red;
led_color.msg.parameters[1] = green;
@@ -2048,7 +2096,8 @@ hidpp10_get_optical_sensor_settings(struct hidpp10_device *dev,
union hidpp10_message sensor = CMD_OPTICAL_SENSOR_SETTINGS(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching optical sensor settings\n");
hidpp_log_raw(&dev->base, "Fetching optical sensor settings (%#02x)\n",
__CMD_OPTICAL_SENSOR_SETTINGS);
res = hidpp10_request_command(dev, &sensor);
if (res)
@@ -2086,7 +2135,8 @@ hidpp10_get_current_resolution(struct hidpp10_device *dev,
union hidpp10_message resolution_long = CMD_CURRENT_RESOLUTION(REPORT_ID_SHORT, idx, GET_LONG_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching current resolution\n");
hidpp_log_raw(&dev->base, "Fetching current resolution (%#02x)\n",
__CMD_CURRENT_RESOLUTION);
switch (dev->profile_type) {
case HIDPP10_PROFILE_G9:
@@ -2094,15 +2144,15 @@ hidpp10_get_current_resolution(struct hidpp10_device *dev,
if (res)
return res;
/* resolution is in 50dpi multiples */
*xres = *yres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution.data[4]));
*xres = *yres = hidpp10_get_dpi_value(dev, get_unaligned_le_u16(&resolution.data[4]));
break;
default:
res = hidpp10_request_command(dev, &resolution_long);
if (res)
return res;
/* resolution is in 50dpi multiples */
*xres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution_long.data[4]));
*yres = hidpp10_get_dpi_value(dev, hidpp_get_unaligned_le_u16(&resolution_long.data[6]));
*xres = hidpp10_get_dpi_value(dev, get_unaligned_le_u16(&resolution_long.data[4]));
*yres = hidpp10_get_dpi_value(dev, get_unaligned_le_u16(&resolution_long.data[6]));
}
return 0;
@@ -2118,14 +2168,17 @@ hidpp10_set_current_resolution(struct hidpp10_device *dev,
union hidpp10_message resolution_long = CMD_CURRENT_RESOLUTION(REPORT_ID_LONG, idx, SET_LONG_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Setting current resolution (%#02x)\n",
__CMD_CURRENT_RESOLUTION);
switch (dev->profile_type) {
case HIDPP10_PROFILE_G9:
resolution.data[4] = hidpp10_get_dpi_mapping(dev, xres);
res = hidpp10_request_command(dev, &resolution);
break;
default:
hidpp_set_unaligned_le_u16(&resolution_long.data[4], hidpp10_get_dpi_mapping(dev, xres));
hidpp_set_unaligned_le_u16(&resolution_long.data[6], hidpp10_get_dpi_mapping(dev, yres));
set_unaligned_le_u16(&resolution_long.data[4], hidpp10_get_dpi_mapping(dev, xres));
set_unaligned_le_u16(&resolution_long.data[6], hidpp10_get_dpi_mapping(dev, yres));
res = hidpp10_request_command(dev, &resolution_long);
break;
@@ -2157,7 +2210,8 @@ hidpp10_get_usb_refresh_rate(struct hidpp10_device *dev,
union hidpp10_message refresh = CMD_USB_REFRESH_RATE(idx, GET_REGISTER_REQ);
int res;
hidpp_log_raw(&dev->base, "Fetching USB refresh rate\n");
hidpp_log_raw(&dev->base, "Fetching USB refresh rate (%#02x)\n",
__CMD_USB_REFRESH_RATE);
res = hidpp10_request_command(dev, &refresh);
if (res)
@@ -2178,7 +2232,8 @@ hidpp10_set_usb_refresh_rate(struct hidpp10_device *dev,
unsigned idx = dev->index;
union hidpp10_message refresh = CMD_USB_REFRESH_RATE(idx, GET_REGISTER_REQ);
hidpp_log_raw(&dev->base, "Setting USB refresh rate\n");
hidpp_log_raw(&dev->base, "Setting USB refresh rate (%#02x)\n",
__CMD_USB_REFRESH_RATE);
refresh.msg.parameters[0] = 1000/rate;
@@ -2483,7 +2538,7 @@ hidpp10_read_page(struct hidpp10_device *dev, uint8_t page,
}
crc = hidpp_crc_ccitt(bytes, HIDPP10_PAGE_SIZE - 2);
read_crc = hidpp_get_unaligned_be_u16(&bytes[HIDPP10_PAGE_SIZE - 2]);
read_crc = get_unaligned_be_u16(&bytes[HIDPP10_PAGE_SIZE - 2]);
if (crc != read_crc)
return -EILSEQ; /* return illegal sequence */
@@ -2573,7 +2628,7 @@ hidpp10_get_pairing_information(struct hidpp10_device *dev,
return -1;
*report_interval = pairing_information.msg.string[2];
*wpid = hidpp_get_unaligned_be_u16(&pairing_information.msg.string[3]);
*wpid = get_unaligned_be_u16(&pairing_information.msg.string[3]);
*device_type = pairing_information.msg.string[7];
return 0;
@@ -2613,7 +2668,7 @@ hidpp10_get_extended_pairing_information(struct hidpp10_device *dev,
if (res)
return -1;
*serial = hidpp_get_unaligned_be_u32(&info.msg.string[1]);
*serial = get_unaligned_be_u32(&info.msg.string[1]);
return 0;
}
@@ -2665,7 +2720,7 @@ hidpp10_get_firmare_information(struct hidpp10_device *dev,
res = hidpp10_request_command(dev, &build_information);
if (res)
return res;
build = hidpp_get_unaligned_be_u16(&build_information.msg.string[1]);
build = get_unaligned_be_u16(&build_information.msg.string[1]);
*major_out = maj;
*minor_out = min;
@@ -2696,8 +2751,7 @@ hidpp10_get_device_info(struct hidpp10_device *dev)
hidpp10_get_optical_sensor_settings(dev, &reflect);
hidpp10_get_current_profile(dev, &current_profile);
return 0;
return hidpp10_get_current_profile(dev, &current_profile);
}
struct hidpp10_device*

View File

@@ -48,7 +48,6 @@ hidpp20_feature_get_name(uint16_t feature)
{
static char numeric[8];
const char *str;
#define CASE_RETURN_STRING(a) case a: return #a; break
switch(feature)
{
@@ -66,6 +65,7 @@ hidpp20_feature_get_name(uint16_t feature)
CASE_RETURN_STRING(HIDPP_PAGE_ADJUSTABLE_DPI);
CASE_RETURN_STRING(HIDPP_PAGE_ADJUSTABLE_REPORT_RATE);
CASE_RETURN_STRING(HIDPP_PAGE_COLOR_LED_EFFECTS);
CASE_RETURN_STRING(HIDPP_PAGE_RGB_EFFECTS);
CASE_RETURN_STRING(HIDPP_PAGE_ONBOARD_PROFILES);
CASE_RETURN_STRING(HIDPP_PAGE_MOUSE_BUTTON_SPY);
default:
@@ -98,11 +98,22 @@ hidpp20_sw_led_control_get_mode_string(const enum hidpp20_led_sw_ctrl_led_mode m
str = numeric;
break;
}
#undef CASE_RETURN_STRING
return str;
}
const char*
hidpp20_get_quirk_string(enum hidpp20_quirk quirk)
{
switch (quirk) {
CASE_RETURN_STRING(HIDPP20_QUIRK_NONE);
CASE_RETURN_STRING(HIDPP20_QUIRK_G305);
CASE_RETURN_STRING(HIDPP20_QUIRK_G602);
}
abort();
}
static int
hidpp20_request_command_allow_error(struct hidpp20_device *device, union hidpp20_message *msg,
bool allow_error)
@@ -122,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 */
@@ -245,7 +266,7 @@ hidpp_root_get_feature(struct hidpp20_device *device,
.msg.address = CMD_ROOT_GET_FEATURE,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], feature);
set_unaligned_be_u16(&msg.msg.parameters[0], feature);
rc = hidpp20_request_command(device, &msg);
if (rc)
@@ -333,7 +354,7 @@ hidpp20_feature_set_get_feature_id(struct hidpp20_device *device,
if (rc)
return rc;
*feature = hidpp_get_unaligned_be_u16(msg.msg.parameters);
*feature = get_unaligned_be_u16(msg.msg.parameters);
*type = msg.msg.parameters[2];
return 0;
@@ -432,10 +453,11 @@ hidpp20_batterylevel_get_battery_level(struct hidpp20_device *device,
}
/* -------------------------------------------------------------------------- */
/* 0x1000: Battery level status */
/* 0x1001: Battery voltage */
/* -------------------------------------------------------------------------- */
#define CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE 0x00
#define CMD_BATTERY_VOLTAGE_GET_SHOW_BATTERY_STATUS 0x10
int
hidpp20_batteryvoltage_get_battery_voltage(struct hidpp20_device *device,
@@ -460,7 +482,7 @@ hidpp20_batteryvoltage_get_battery_voltage(struct hidpp20_device *device,
if (rc)
return rc;
*voltage = (msg.msg.parameters[0] << 8) + msg.msg.parameters[1];
*voltage = get_unaligned_be_u16(msg.msg.parameters);
return msg.msg.parameters[2];
}
@@ -720,10 +742,10 @@ int hidpp20_led_sw_control_set_led_state(struct hidpp20_device* device,
return -EINVAL;
msg.msg.parameters[0] = state->index;
hidpp_set_unaligned_be_u16(&msg.msg.parameters[1], state->mode);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[3], state->blink.index);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[5], state->blink.on_time);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[7], state->blink.off_time);
set_unaligned_be_u16(&msg.msg.parameters[1], state->mode);
set_unaligned_be_u16(&msg.msg.parameters[3], state->blink.index);
set_unaligned_be_u16(&msg.msg.parameters[5], state->blink.on_time);
set_unaligned_be_u16(&msg.msg.parameters[7], state->blink.off_time);
rc = hidpp20_request_command(device, &msg);
@@ -777,8 +799,8 @@ hidpp20_kbd_reprogrammable_keys_get_info(struct hidpp20_device *device,
if (rc)
return rc;
control->control_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[0]);
control->task_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[2]);
control->control_id = get_unaligned_be_u16(&msg.msg.parameters[0]);
control->task_id = get_unaligned_be_u16(&msg.msg.parameters[2]);
control->flags = msg.msg.parameters[4];
return 0;
@@ -846,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,
@@ -871,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;
}
@@ -941,7 +966,7 @@ hidpp20_color_led_effects_get_zone_infos(struct hidpp20_device *device,
"led_info %d: location: %d type %s num_effects: %d persistency_caps: 0x%02x\n",
info->index,
info->location,
hidpp20_8070_get_location_mapping_name(info->location),
hidpp20_led_get_location_mapping_name(info->location),
info->num_effects,
info->persistency_caps);
}
@@ -983,9 +1008,253 @@ hidpp20_color_led_effect_get_zone_effect_info(struct hidpp20_device *device,
info->zone_index = msg.msg.parameters[0];
info->zone_effect_index = msg.msg.parameters[1];
info->effect_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[2]);
info->effect_caps = hidpp_get_unaligned_be_u16(&msg.msg.parameters[4]);
info->effect_period = hidpp_get_unaligned_be_u16(&msg.msg.parameters[6]);
info->effect_id = get_unaligned_be_u16(&msg.msg.parameters[2]);
info->effect_caps = get_unaligned_be_u16(&msg.msg.parameters[4]);
info->effect_period = get_unaligned_be_u16(&msg.msg.parameters[6]);
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_SHORT,
.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 */
/* -------------------------------------------------------------------------- */
#define CMD_RGB_EFFECTS_GET_INFO 0x00
#define CMD_RGB_EFFECTS_SET_RGB_CLUSTER_EFFECT 0x10
#define CMD_RGB_EFFECTS_SET_MULTI_LED_RGB_CLUSTER_PATTERN 0x20
#define CMD_RGB_EFFECTS_MANAGE_NV_CONFIG 0x30
#define CMD_RGB_EFFECTS_MANAGE_RGB_LED_BIN_INFO 0x40
#define CMD_RGB_EFFECTS_MANAGE_SW_CONTROL 0x50
#define CMD_RGB_EFFECTS_SET_EFFECT_SYNC_CORRECTION 0x60
#define CMD_RGB_EFFECTS_MANAGE_RGB_POWER_MODE_CONFIG 0x70
#define CMD_RGB_EFFECTS_MANAGE_RGB_POWER_MODE 0x80
int
hidpp20_rgb_effects_get_device_info(struct hidpp20_device *device,
struct hidpp20_rgb_device_info *info)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
.msg.parameters[1] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
.msg.parameters[2] = HIDPP20_RGB_EFFECTS_TOI_GENERAL,
};
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_RGB_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info->cluster_index = msg.msg.parameters[0];
info->effect_index = msg.msg.parameters[1];
info->cluster_count = msg.msg.parameters[2];
info->nv_caps = get_unaligned_be_u16(&msg.msg.parameters[3]);
info->ext_caps = get_unaligned_be_u16(&msg.msg.parameters[4]);
return 0;
}
int
hidpp20_rgb_effects_get_cluster_info(struct hidpp20_device *device,
uint8_t cluster_index,
struct hidpp20_rgb_cluster_info *info)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = cluster_index,
.msg.parameters[1] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
.msg.parameters[2] = HIDPP20_RGB_EFFECTS_TOI_GENERAL,
};
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_RGB_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info->index = msg.msg.parameters[0];
info->effect_index = msg.msg.parameters[1];
info->location = get_unaligned_be_u16(&msg.msg.parameters[2]);
info->num_effects = msg.msg.parameters[4];
info->persistency_caps = msg.msg.parameters[5];
return 0;
}
int
hidpp20_rgb_effects_get_cluster_infos(struct hidpp20_device *device,
struct hidpp20_rgb_cluster_info **infos_list)
{
struct hidpp20_rgb_cluster_info *i_list, *info;
struct hidpp20_rgb_device_info device_info = {0};
uint8_t num_infos;
unsigned i;
int rc;
rc = hidpp20_rgb_effects_get_device_info(device, &device_info);
if (rc < 0)
return rc;
num_infos = device_info.cluster_count;
if (num_infos == 0) {
*infos_list = NULL;
return 0;
}
i_list = zalloc(num_infos * sizeof(struct hidpp20_rgb_cluster_info));
for (i = 0; i < num_infos; i++) {
info = &i_list[i];
info->index = i;
rc = hidpp20_rgb_effects_get_cluster_info(device, i, info);
if (rc)
goto err;
hidpp_log_raw(&device->base,
"cluster_info %d: location: %d type %s num_effects: %d persistency_caps: 0x%02x\n",
info->index,
info->location,
hidpp20_led_get_location_mapping_name(info->location),
info->num_effects,
info->persistency_caps);
}
*infos_list = i_list;
return num_infos;
err:
free(i_list);
return rc;
}
int
hidpp20_rgb_effects_get_effect_info(struct hidpp20_device *device,
uint8_t cluster_index,
uint8_t effect_index,
struct hidpp20_rgb_effect_info *info)
{
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_LONG,
.msg.device_idx = device->index,
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
.msg.parameters[0] = cluster_index,
.msg.parameters[1] = effect_index,
.msg.parameters[2] = HIDPP20_RGB_EFFECTS_TOI_GENERAL,
};
uint8_t feature_index;
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_RGB_EFFECTS);
if (feature_index == 0)
return -ENOTSUP;
msg.msg.sub_id = feature_index;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
info->cluster_index = msg.msg.parameters[0];
info->effect_index = msg.msg.parameters[1];
info->effect_id = get_unaligned_be_u16(&msg.msg.parameters[2]);
info->capabilities = get_unaligned_be_u16(&msg.msg.parameters[4]);
info->effect_period = get_unaligned_be_u16(&msg.msg.parameters[6]);
return 0;
}
@@ -1035,8 +1304,8 @@ hidpp20_special_keys_buttons_get_info(struct hidpp20_device *device,
if (rc)
return rc;
control->control_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[0]);
control->task_id = hidpp_get_unaligned_be_u16(&msg.msg.parameters[2]);
control->control_id = get_unaligned_be_u16(&msg.msg.parameters[0]);
control->task_id = get_unaligned_be_u16(&msg.msg.parameters[2]);
control->flags = msg.msg.parameters[4];
control->position = msg.msg.parameters[5];
control->group = msg.msg.parameters[6];
@@ -1060,13 +1329,13 @@ hidpp20_special_keys_buttons_get_reporting(struct hidpp20_device *device,
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_REPORTING,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], control->control_id);
set_unaligned_be_u16(&msg.msg.parameters[0], control->control_id);
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
control->reporting.remapped = hidpp_get_unaligned_be_u16(&msg.msg.parameters[3]);
control->reporting.remapped = get_unaligned_be_u16(&msg.msg.parameters[3]);
control->reporting.raw_XY = !!(msg.msg.parameters[2] & 0x10);
control->reporting.persist = !!(msg.msg.parameters[2] & 0x04);
control->reporting.divert = !!(msg.msg.parameters[2] & 0x01);
@@ -1160,8 +1429,8 @@ hidpp20_special_key_mouse_set_control(struct hidpp20_device *device,
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_SET_REPORTING,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], control->control_id);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[3], control->reporting.remapped);
set_unaligned_be_u16(&msg.msg.parameters[0], control->control_id);
set_unaligned_be_u16(&msg.msg.parameters[3], control->reporting.remapped);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_SPECIAL_KEYS_BUTTONS);
@@ -1212,7 +1481,7 @@ hidpp20_mousepointer_get_mousepointer_info(struct hidpp20_device *device,
if (rc)
return rc;
*resolution = hidpp_get_unaligned_be_u16(msg.msg.parameters);
*resolution = get_unaligned_be_u16(msg.msg.parameters);
*flags = msg.msg.parameters[2];
return 0;
@@ -1260,6 +1529,11 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
if (device->quirk == HIDPP20_QUIRK_G602) {
msg.msg.parameters[0] = 1;
i = 0;
}
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
@@ -1268,8 +1542,11 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
sensor->index = msg.msg.parameters[0];
while (i < LONG_MESSAGE_LENGTH - 4U &&
hidpp_get_unaligned_be_u16(&msg.msg.parameters[i]) != 0) {
uint16_t value = hidpp_get_unaligned_be_u16(&msg.msg.parameters[i]);
get_unaligned_be_u16(&msg.msg.parameters[i]) != 0) {
uint16_t value = get_unaligned_be_u16(&msg.msg.parameters[i]);
if (device->quirk == HIDPP20_QUIRK_G602 && i == 2)
value += 0xe000;
if (value > 0xe000) {
sensor->dpi_steps = value - 0xe000;
@@ -1300,12 +1577,15 @@ hidpp20_adjustable_dpi_get_dpi(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
if (device->quirk == HIDPP20_QUIRK_G602)
msg.msg.parameters[0] = 1;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
sensor->dpi = hidpp_get_unaligned_be_u16(&msg.msg.parameters[1]);
sensor->default_dpi = hidpp_get_unaligned_be_u16(&msg.msg.parameters[3]);
sensor->dpi = get_unaligned_be_u16(&msg.msg.parameters[1]);
sensor->default_dpi = get_unaligned_be_u16(&msg.msg.parameters[3]);
return 0;
}
@@ -1379,7 +1659,10 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[1], dpi);
if (device->quirk == HIDPP20_QUIRK_G602)
msg.msg.parameters[0] = 1;
set_unaligned_be_u16(&msg.msg.parameters[1], dpi);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ADJUSTABLE_DPI);
@@ -1392,7 +1675,7 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
if (rc)
return rc;
returned_parameters = hidpp_get_unaligned_be_u16(&msg.msg.parameters[1]);
returned_parameters = get_unaligned_be_u16(&msg.msg.parameters[1]);
/* version 0 of the protocol does not echo the parameters */
if (returned_parameters != dpi && returned_parameters)
@@ -1406,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)
@@ -1435,6 +1720,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 */
/* -------------------------------------------------------------------------- */
@@ -1462,6 +1801,7 @@ int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *d
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G402 0x01
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G303 0x02
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900 0x03
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G915 0x04
#define HIDPP20_ONBOARD_PROFILES_MACRO_TYPE_G402 0x01
#define HIDPP20_USER_PROFILES_G402 0x0000
@@ -1480,7 +1820,9 @@ union hidpp20_internal_profile {
struct hidpp20_color profile_color;
uint8_t power_mode;
uint8_t angle_snapping;
uint8_t reserved[14];
uint8_t reserved[10];
uint16_t powersave_timeout;
uint16_t poweroff_timeout;
union hidpp20_button_binding buttons[16];
union hidpp20_button_binding alternate_buttons[16];
union {
@@ -1488,7 +1830,8 @@ union hidpp20_internal_profile {
uint8_t raw[16 * 3];
} name;
struct hidpp20_internal_led leds[2]; /* G303, g502, g900 only */
uint8_t free[24];
struct hidpp20_internal_led alt_leds[2];
uint8_t free[2];
uint16_t crc;
} __attribute__((packed)) profile;
};
@@ -1540,7 +1883,7 @@ hidpp20_onboard_profiles_read_sector(struct hidpp20_device *device,
.msg.address = CMD_ONBOARD_PROFILES_MEMORY_READ,
};
hidpp_log_debug(device, "Reading sector 0x%04x\n", sector);
hidpp_log_debug(&device->base, "Reading sector 0x%04x\n", sector);
feature_index = hidpp_root_get_feature_idx(device,
HIDPP_PAGE_ONBOARD_PROFILES);
@@ -1548,7 +1891,7 @@ hidpp20_onboard_profiles_read_sector(struct hidpp20_device *device,
return -ENOTSUP;
msg.msg.sub_id = feature_index;
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], sector);
set_unaligned_be_u16(&msg.msg.parameters[0], sector);
count = sector_size;
@@ -1559,7 +1902,7 @@ hidpp20_onboard_profiles_read_sector(struct hidpp20_device *device,
* less than 16 bytes to read we need to read from sector_size - 16
*/
offset = (sector_size - offset < 16) ? sector_size - 16 : offset;
hidpp_set_unaligned_be_u16(&msg.msg.parameters[2], offset);
set_unaligned_be_u16(&msg.msg.parameters[2], offset);
buf = msg;
rc = hidpp20_request_command(device, &buf);
if (rc)
@@ -1587,10 +1930,10 @@ hidpp20_onboard_profiles_is_sector_valid(struct hidpp20_device *device,
uint16_t crc, read_crc;
crc = hidpp_crc_ccitt(data, sector_size - 2);
read_crc = hidpp_get_unaligned_be_u16(&data[sector_size - 2]);
read_crc = get_unaligned_be_u16(&data[sector_size - 2]);
if (crc != read_crc)
hidpp_log_debug(device, "Invalid CRC (%04x != %04x)\n", read_crc, crc);
hidpp_log_debug(&device->base, "Invalid CRC (%04x != %04x)\n", read_crc, crc);
return crc == read_crc;
}
@@ -1610,9 +1953,9 @@ hidpp20_onboard_profiles_write_start(struct hidpp20_device *device,
.msg.address = CMD_ONBOARD_PROFILES_MEMORY_ADDR_WRITE,
};
hidpp_set_unaligned_be_u16(&msg.msg.parameters[0], sector);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[2], sub_address);
hidpp_set_unaligned_be_u16(&msg.msg.parameters[4], count);
set_unaligned_be_u16(&msg.msg.parameters[0], sector);
set_unaligned_be_u16(&msg.msg.parameters[2], sub_address);
set_unaligned_be_u16(&msg.msg.parameters[4], count);
rc = hidpp20_request_command(device, &msg);
if (rc)
@@ -1680,7 +2023,7 @@ hidpp20_onboard_profiles_write_sector(struct hidpp20_device *device,
if (write_crc) {
crc = hidpp_crc_ccitt(data, sector_size - 2);
hidpp_set_unaligned_be_u16(&data[sector_size - 2], crc);
set_unaligned_be_u16(&data[sector_size - 2], crc);
}
rc = hidpp20_onboard_profiles_write_start(device,
@@ -1877,7 +2220,8 @@ hidpp20_onboard_profiles_validate(struct hidpp20_device *device,
if ((info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G402) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G303) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900)) {
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G915)) {
hidpp_log_error(&device->base,
"Profile layout not supported: 0x%02x.\n",
info->profile_format_id);
@@ -2177,7 +2521,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,
@@ -2188,6 +2532,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;
}
@@ -2225,6 +2572,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]) {
@@ -2284,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;
@@ -2299,7 +2648,7 @@ hidpp20_buttons_from_cpu(struct hidpp20_profile *profile,
}
}
static void
void
hidpp20_onboard_profiles_read_led(struct hidpp20_led *led,
struct hidpp20_internal_led internal_led)
{
@@ -2315,6 +2664,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;
@@ -2322,11 +2675,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;
@@ -2371,12 +2731,18 @@ hidpp20_onboard_profiles_parse_profile(struct hidpp20_device *device,
profile->default_dpi = pdata->profile.default_dpi;
profile->switched_dpi = pdata->profile.switched_dpi;
profile->powersave_timeout = pdata->profile.powersave_timeout;
profile->poweroff_timeout = pdata->profile.poweroff_timeout;
for (i = 0; i < 5; i++) {
profile->dpi[i] = hidpp_get_unaligned_le_u16(&data[2 * i + 3]);
profile->dpi[i] = get_unaligned_le_u16(&data[2 * i + 3]);
}
for (i = 0; i < profiles_list->num_leds; i++)
{
hidpp20_onboard_profiles_read_led(&profile->leds[i], pdata->profile.leds[i]);
hidpp20_onboard_profiles_read_led(&profile->alt_leds[i], pdata->profile.alt_leds[i]);
}
hidpp20_buttons_to_cpu(device, profiles_list, profile, pdata->profile.buttons, profiles_list->num_buttons);
@@ -2419,6 +2785,15 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
HIDPP20_USER_PROFILES_G402,
profiles->sector_size,
data);
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.*/
read_userdata = false;
goto read_profiles;
}
if (rc < 0)
return rc; // ignore_clang_sa_mem_leak
@@ -2429,7 +2804,7 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
for (i = 0; i < profiles->num_profiles; i++) {
uint8_t *d = data + 4 * i;
addr = hidpp_get_unaligned_be_u16(d);
addr = get_unaligned_be_u16(d);
if(addr == HIDPP20_PROFILE_DIR_END)
break;
@@ -2447,14 +2822,15 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
profiles->profiles[i].enabled = !!d[HIDPP20_PROFILE_DIR_ENABLED];
}
} else {
hidpp_log_debug(device, "Profile directory has an invalid CRC... Reading ROM profiles.\n");
hidpp_log_debug(&device->base, "Profile directory has an invalid CRC... Reading ROM profiles.\n");
read_userdata = false;
}
read_profiles:
for (i = 0; i < profiles->num_profiles; i++) {
if (read_userdata) {
hidpp_log_debug(device, "Parsing profile %u\n", i);
hidpp_log_debug(&device->base, "Parsing profile %u\n", i);
rc = hidpp20_onboard_profiles_parse_profile(device,
profiles,
i,
@@ -2462,7 +2838,7 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
/* on fail to read the user profile fallback to the default profile */
if (rc)
hidpp_log_debug("Profile %u is bad. Falling back to the ROM settings.\n", i);
hidpp_log_debug(&device->base, "Profile %u is bad. Falling back to the ROM settings.\n", i);
else
continue;
}
@@ -2486,7 +2862,7 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
return profiles->num_profiles;
}
static void
void
hidpp20_onboard_profiles_write_led(struct hidpp20_internal_led *internal_led,
struct hidpp20_led *led)
{
@@ -2505,24 +2881,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;
}
}
@@ -2551,20 +2934,29 @@ hidpp20_onboard_profiles_write_profile(struct hidpp20_device *device,
pdata->profile.default_dpi = profile->default_dpi;
pdata->profile.switched_dpi = profile->switched_dpi;
pdata->profile.powersave_timeout = profile->powersave_timeout;
pdata->profile.poweroff_timeout = profile->poweroff_timeout;
for (i = 0; i < 5; i++) {
pdata->profile.dpi[i] = hidpp_cpu_to_le_u16(profile->dpi[i]);
}
for (i = 0; i < profiles_list->num_leds; i++)
{
hidpp20_onboard_profiles_write_led(&pdata->profile.leds[i], &profile->leds[i]);
/* we write the current led instead of the stored value */
hidpp20_onboard_profiles_write_led(&pdata->profile.alt_leds[i], &profile->leds[i]);
}
hidpp20_buttons_from_cpu(profile, pdata->profile.buttons, profiles_list->num_buttons);
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;
}
@@ -2647,7 +3039,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;
@@ -2660,6 +3052,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

@@ -51,6 +51,12 @@ struct hidpp20_feature {
uint8_t type;
};
enum hidpp20_quirk {
HIDPP20_QUIRK_NONE,
HIDPP20_QUIRK_G305,
HIDPP20_QUIRK_G602,
};
struct hidpp20_device {
struct hidpp_device base;
unsigned int index;
@@ -58,18 +64,25 @@ struct hidpp20_device {
unsigned proto_minor;
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);
#define CASE_RETURN_STRING(a) case a: return #a; break
const char *hidpp20_feature_get_name(uint16_t feature);
const char *hidpp20_get_quirk_string(enum hidpp20_quirk quirk);
/* -------------------------------------------------------------------------- */
/* generic hidpp20 device operations */
/* -------------------------------------------------------------------------- */
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);
@@ -420,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 */
/* -------------------------------------------------------------------------- */
@@ -458,6 +477,43 @@ hidpp20_color_led_effect_get_zone_effect_info(struct hidpp20_device *device,
uint8_t zone_effect_index,
struct hidpp20_color_led_zone_effect_info *info);
struct hidpp20_rgb_device_info;
int
hidpp20_rgb_effects_get_device_info(struct hidpp20_device *device,
struct hidpp20_rgb_device_info *info);
struct hidpp20_rgb_cluster_info;
int
hidpp20_rgb_effects_get_cluster_info(struct hidpp20_device *device,
uint8_t cluster_index,
struct hidpp20_rgb_cluster_info *info);
int
hidpp20_rgb_effects_get_cluster_infos(struct hidpp20_device *device,
struct hidpp20_rgb_cluster_info **infos_list);
struct hidpp20_rgb_effect_info;
int
hidpp20_rgb_effects_get_effect_info(struct hidpp20_device *device,
uint8_t cluster_index,
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,
@@ -491,8 +547,30 @@ enum hidpp20_color_led_zone_effect {
HIDPP20_COLOR_LED_ZONE_EFFECT_BOOT_UP = 8,
HIDPP20_COLOR_LED_ZONE_EFFECT_DEMO_MODE = 8,
HIDPP20_COLOR_LED_ZONE_EFFECT_BREATHING = 10,
HIDPP20_COLOR_LED_ZONE_EFFECT_RIPPLE = 11,
HIDPP20_COLOR_LED_ZONE_EFFECT_CUSTOM = 12,
};
/* -------------------------------------------------------------------------- */
/* 0x8071 - RGB Effects */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_RGB_EFFECTS 0x8071
#define HIDPP20_RGB_EFFECTS_INDEX_ALL 0xFF
/* type of info */
#define HIDPP20_RGB_EFFECTS_TOI_GENERAL 0x00
#define HIDPP20_RGB_EFFECTS_TOI_EFFECT 0x01
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_STATE 0x00
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_DEFAULTS 0x01
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_UUID_0_10 0x02
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_UUID_11_16 0x03
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_EFCT_NAME_0_10 0x04
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_EFCT_NAME_11_21 0x05
#define HIDPP20_RGB_EFFECTS_SLOT_INFO_EFCT_NAME_21_31 0x06
/* -------------------------------------------------------------------------- */
/* 0x8100 - Onboard Profiles */
/* -------------------------------------------------------------------------- */
@@ -540,6 +618,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 */
@@ -578,11 +658,6 @@ struct hidpp20_color_led_zone_info {
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_zone_info) == 5, "Invalid size");
union hidpp20_generic_led_zone_info {
struct hidpp20_led_sw_ctrl_led_info* leds;
struct hidpp20_color_led_zone_info* color_leds;
};
struct hidpp20_color_led_zone_effect_info {
uint8_t zone_index;
uint8_t zone_effect_index;
@@ -592,6 +667,41 @@ struct hidpp20_color_led_zone_effect_info {
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_color_led_zone_effect_info) == 8, "Invalid size");
struct hidpp20_rgb_device_info {
uint8_t cluster_index;
uint8_t effect_index;
uint8_t cluster_count;
/* we don't care about NV capabilities for libratbag, they just
* indicate sale demo effects */
uint16_t nv_caps;
uint16_t ext_caps;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_rgb_device_info) == 7, "Invalid size");
struct hidpp20_rgb_cluster_info {
uint8_t index;
uint8_t effect_index;
uint16_t location;
uint16_t num_effects;
uint16_t persistency_caps;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_rgb_cluster_info) == 8, "Invalid size");
struct hidpp20_rgb_effect_info {
uint8_t cluster_index;
uint8_t effect_index;
uint16_t effect_id;
uint16_t capabilities;
uint16_t effect_period;
} __attribute__((packed));
_Static_assert(sizeof(struct hidpp20_rgb_effect_info) == 8, "Invalid size");
union hidpp20_generic_led_zone_info {
struct hidpp20_led_sw_ctrl_led_info* leds;
struct hidpp20_color_led_zone_info* color_leds_8070;
struct hidpp20_rgb_cluster_info* color_leds_8071;
};
struct hidpp20_color {
uint8_t red;
uint8_t green;
@@ -609,7 +719,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 {
@@ -634,27 +748,48 @@ 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
@@ -692,6 +827,8 @@ struct hidpp20_profile {
uint16_t address;
uint8_t enabled;
char name[16 * 3];
uint16_t powersave_timeout;
uint16_t poweroff_timeout;
unsigned report_rate;
unsigned default_dpi;
unsigned switched_dpi;
@@ -700,6 +837,7 @@ struct hidpp20_profile {
union hidpp20_button_binding buttons[32];
union hidpp20_macro_data *macros[32];
struct hidpp20_led leds[HIDPP20_LED_COUNT];
struct hidpp20_led alt_leds[HIDPP20_LED_COUNT];
};
struct hidpp20_onboard_profiles_info {
@@ -827,6 +965,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

@@ -33,6 +33,7 @@
#include "libratbag.h"
#include "libratbag-private.h"
#include "libratbag-data.h"
#include "hidpp20.h"
#define GROUP_DEVICE "Device"
@@ -54,6 +55,7 @@ enum driver {
struct data_hidpp20 {
int index;
enum hidpp20_quirk quirk;
};
struct data_hidpp10 {
@@ -152,6 +154,7 @@ init_data_hidpp20(struct ratbag *ratbag,
const char *group = "Driver/hidpp20";
GError *error = NULL;
int num;
char *str;
data->hidpp20.index = -1;
@@ -160,6 +163,15 @@ init_data_hidpp20(struct ratbag *ratbag,
data->hidpp20.index = 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) {
if (streq(str, "G305"))
data->hidpp20.quirk = HIDPP20_QUIRK_G305;
else if(streq(str, "G602"))
data->hidpp20.quirk = HIDPP20_QUIRK_G602;
}
}
static void
@@ -334,6 +346,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;
@@ -562,6 +576,14 @@ ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data)
return data->hidpp20.index;
}
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP20);
return data->hidpp20.quirk;
}
/* SteelSeries */
int

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);
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data);
/* SteelSeries */
/**

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

@@ -1239,6 +1239,8 @@ ratbag_hidraw_parse_report_descriptor(struct ratbag_device *device)
if (rc < 0)
return rc;
log_debug(device->ratbag, "Parsing HID report descriptor\n");
i = 0;
usage_page = 0;
usage = 0;
@@ -1260,7 +1262,7 @@ ratbag_hidraw_parse_report_descriptor(struct ratbag_device *device)
switch (hid) {
case HID_REPORT_ID:
if (hidraw->reports) {
log_debug(device->ratbag, "HID report ID %02x\n", content);
log_debug(device->ratbag, "- HID report ID %02x\n", content);
hidraw->reports[hidraw->num_reports].report_id = content;
hidraw->reports[hidraw->num_reports].usage_page = usage_page;
hidraw->reports[hidraw->num_reports].usage = usage;
@@ -1586,6 +1588,23 @@ ratbag_hidraw_raw_request(struct ratbag_device *device, unsigned char reportnum,
return -EINVAL;
}
int
ratbag_hidraw_get_feature_report(struct ratbag_device *device, unsigned char reportnum,
uint8_t *buf, size_t len)
{
return ratbag_hidraw_raw_request(device, reportnum, buf, len,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
}
int
ratbag_hidraw_set_feature_report(struct ratbag_device *device, unsigned char reportnum,
uint8_t *buf, size_t len)
{
return ratbag_hidraw_raw_request(device, reportnum, buf, len,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
}
int
ratbag_hidraw_output_report(struct ratbag_device *device, uint8_t *buf, size_t len)
{

View File

@@ -101,7 +101,7 @@ void ratbag_close_hidraw_index(struct ratbag_device *device, int idx);
*
* @param device the ratbag device
* @param reportnum report ID
* @param buf in/out data to transfer
* @param buf[in/out] data to transfer
* @param len length of buf
* @param rtype HID report type
* @param reqtype HID_REQ_GET_REPORT or HID_REQ_SET_REPORT
@@ -114,6 +114,41 @@ int ratbag_hidraw_raw_request(struct ratbag_device *device, unsigned char report
uint8_t *buf, size_t len, unsigned char rtype,
int reqtype);
/**
* Get feature report from device.
*
* This call sends a HID_REQ_GET_REPORT to the device for the given feature
* and returns the data in the caller-allocated buffer buf of size len.
*
* @param device the ratbag device
* @param reportnum report ID
* @param buf[out] data returned from device
* @param len length of buf
*
* @return count of data transfered, or a negative errno on error
*
* Convenience wrapper around ratbag_hidraw_raw_request()
*/
int ratbag_hidraw_get_feature_report(struct ratbag_device *device, unsigned char reportnum,
uint8_t *buf, size_t len);
/**
* Set feature report on device.
*
* This call sends a HID_REQ_SET_REPORT to the device for the given feature
* and the buffer buf of size len.
*
* @param device the ratbag device
* @param reportnum report ID
* @param buf[in] data returned from device
* @param len length of buf
*
* @return count of data transfered, or a negative errno on error
*
* Convenience wrapper around ratbag_hidraw_raw_request()
*/
int ratbag_hidraw_set_feature_report(struct ratbag_device *device, unsigned char reportnum,
uint8_t *buf, size_t len);
/**
* Send output report to device
*

View File

@@ -295,6 +295,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;
@@ -571,7 +572,7 @@ ratbag_resolution_set_cap(struct ratbag_resolution *res,
{
assert(cap <= RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
res->capabilities = (1 << cap);
res->capabilities |= (1 << cap);
}
static inline void
@@ -585,14 +586,14 @@ 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;
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

@@ -53,7 +53,7 @@ struct ratbag_test_resolution {
int xres, yres;
bool active;
bool dflt;
uint32_t caps;
uint32_t caps[10];
int dpi_min, dpi_max;
};
@@ -73,7 +73,7 @@ struct ratbag_test_led {
};
struct ratbag_test_profile {
const char *name;
char *name;
struct ratbag_test_button buttons[RATBAG_TEST_MAX_BUTTONS];
struct ratbag_test_resolution resolutions[RATBAG_TEST_MAX_RESOLUTIONS];
struct ratbag_test_led leds[RATBAG_TEST_MAX_LEDS];

View File

@@ -328,3 +328,35 @@ err:
dpi_list_free(list);
return NULL;
}
static inline uint16_t
get_unaligned_be_u16(uint8_t *buf)
{
return (buf[0] << 8) | buf[1];
}
static inline void
set_unaligned_be_u16(uint8_t *buf, uint16_t value)
{
buf[0] = value >> 8;
buf[1] = value & 0xFF;
}
static inline uint16_t
get_unaligned_le_u16(uint8_t *buf)
{
return (buf[1] << 8) | buf[0];
}
static inline void
set_unaligned_le_u16(uint8_t *buf, uint16_t value)
{
buf[0] = value & 0xFF;
buf[1] = value >> 8;
}
static inline uint32_t
get_unaligned_be_u32(uint8_t *buf)
{
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}

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,
@@ -322,7 +334,7 @@ out:
}
static inline bool
ratbag_test_driver(struct ratbag_device *device,
ratbag_try_driver(struct ratbag_device *device,
const struct input_id *dev_id,
const char *driver_name,
const struct ratbag_test_device *test_device)
@@ -377,9 +389,9 @@ ratbag_driver_fallback_logitech(struct ratbag_device *device,
if (dev_id->vendor != USB_VENDOR_ID_LOGITECH)
return false;
rc = ratbag_test_driver(device, dev_id, "hidpp20", NULL);
rc = ratbag_try_driver(device, dev_id, "hidpp20", NULL);
if (!rc)
rc = ratbag_test_driver(device, dev_id, "hidpp10", NULL);
rc = ratbag_try_driver(device, dev_id, "hidpp10", NULL);
return rc;
}
@@ -401,7 +413,7 @@ ratbag_assign_driver(struct ratbag_device *device,
if (driver_name) {
log_debug(device->ratbag, "device assigned driver %s\n", driver_name);
rc = ratbag_test_driver(device, dev_id, driver_name, test_device);
rc = ratbag_try_driver(device, dev_id, driver_name, test_device);
} else {
rc = ratbag_driver_fallback_logitech(device, dev_id);
}
@@ -628,7 +640,7 @@ ratbag_create_button(struct ratbag_profile *profile, unsigned int index)
button->index = index;
button->type = RATBAG_BUTTON_TYPE_UNKNOWN;
list_insert(&profile->buttons, &button->link);
list_append(&profile->buttons, &button->link);
return button;
}
@@ -648,7 +660,7 @@ ratbag_create_led(struct ratbag_profile *profile, unsigned int index)
if (device->data)
led->type = ratbag_device_data_get_led_type(device->data, led->index);
list_insert(&profile->leds, &led->link);
list_append(&profile->leds, &led->link);
return led;
}
@@ -673,7 +685,7 @@ ratbag_create_resolution(struct ratbag_profile *profile, int index)
res->profile = profile;
res->index = index;
list_insert(&profile->resolutions, &res->link);
list_append(&profile->resolutions, &res->link);
profile->num_resolutions++;
}
@@ -697,7 +709,7 @@ ratbag_create_profile(struct ratbag_device *device,
profile->is_enabled = true;
profile->name = NULL;
list_insert(&device->profiles, &profile->link);
list_append(&device->profiles, &profile->link);
list_init(&profile->buttons);
list_init(&profile->leds);
list_init(&profile->resolutions);
@@ -937,6 +949,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 +1119,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

@@ -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,10 +0,0 @@
#!/bin/sh
GIT_DIR="$MESON_SOURCE_ROOT/.git"
export GIT_DIR
git log --format="format:%h" -1 2>/dev/null
if test $? -ne 0; then
echo "unknown"
fi

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 = {}
@@ -216,11 +214,11 @@ class Ratbagd(object):
def get_capabilities(type, object):
capabilities = []
for k in libratbag.__dict__.keys():
if k.startswith("RATBAG_{}_CAP_".format(type.upper())) and "CAP_NONE" not in k:
cap = getattr(libratbag, k)
func = getattr(libratbag, "ratbag_{}_has_capability".format(type.lower()))
if func(object, cap):
capabilities.append(cap)
if k.startswith("RATBAG_{}_CAP_".format(type.upper())) and "CAP_NONE" not in k:
cap = getattr(libratbag, k)
func = getattr(libratbag, "ratbag_{}_has_capability".format(type.lower()))
if func(object, cap):
capabilities.append(cap)
return capabilities
@@ -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.
@@ -25,6 +23,7 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
import argparse
import os
import time
import toolbox
@@ -33,11 +32,13 @@ import sys
from ratbagctl import RatbagErrorCapability # NOQA
def main(argv):
def main(argv, start_ratbagd=True):
if not os.geteuid() == 0:
sys.exit('Script must be run as root')
# replaced by meson
os.environ['LIBRATBAG_DATA_DIR'] = '@LIBRATBAG_DATA_DIR@'
os.environ['RATBAG_TEST'] = "1"
if not argv:
argv = ["list"]
@@ -52,9 +53,10 @@ def main(argv):
parser.print_help()
return
ratbagd_process = toolbox.start_ratbagd(verbosity=cmd.verbose)
if ratbagd_process is None:
sys.exit("Failed to start or connect to ratbagd")
if start_ratbagd:
ratbagd_process = toolbox.start_ratbagd(verbosity=cmd.verbose)
if ratbagd_process is None:
sys.exit("Failed to start or connect to ratbagd")
_ratbagd = toolbox.open_ratbagd()
if _ratbagd is not None:
@@ -75,14 +77,20 @@ def main(argv):
finally:
try:
if cmd.keepalive:
print('\nexport RATBAGCTL_DEVEL="{}"\n'.format(os.environ['RATBAGCTL_DEVEL']))
signal.pause()
except KeyboardInterrupt:
pass
except AttributeError: # in case the parsing failed and cmd is None
pass
toolbox.terminate_ratbagd(ratbagd_process)
if ratbagd_process:
toolbox.terminate_ratbagd(ratbagd_process)
if __name__ == "__main__":
main(sys.argv[1:])
parser = argparse.ArgumentParser(description='commandline tool to access the ratbagd.devel debug server')
parser.add_argument('--use-existing-ratbagd', dest='use_existing', action='store_true', default=False,
help='Don\'t start up ratbagd.devel, connect to the already running one')
args, remainder = parser.parse_known_args()
main(remainder, not args.use_existing)

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
@@ -31,6 +29,13 @@ import re
from ratbagd import Ratbagd, RatbagdDevice, RatbagdProfile, RatbagdMacro, RatbagdResolution, RatbagdButton, RatbagdLed, RatbagdUnavailable, RatbagError, RatbagErrorCode, RatbagErrorCapability # NOQA
def humanize(string):
return string.lower().replace('_', '-')
button_special_names = [humanize(e.name) for e in RatbagdButton.ActionSpecial if e.name != "INVALID"]
led_mode_names = [humanize(e.name) for e in RatbagdLed.Mode]
button_specials_strmap = {
**{e: e.name.lower().replace("_", "-") for e in RatbagdButton.ActionSpecial},
**{e.name.lower().replace("_", "-"): e for e in RatbagdButton.ActionSpecial}
@@ -202,6 +207,7 @@ def print_device(d, level):
p = d.profiles[0] # there should be always one
print(" " * level + "{} - {}".format(d.id, d.name))
print(" " * level + " Model: {}".format(d.model))
print(" " * level + " Number of Buttons: {}".format(len(p.buttons)))
print(" " * level + " Number of Leds: {}".format(len(p.leds)))
print(" " * level + "Number of Profiles: {}".format(len(d.profiles)))
@@ -949,27 +955,7 @@ active profile if none is given.""",
name: 'target_special',
metavar: 'S',
help_str: 'The new special value to assign',
choices: [
"unknown",
"doubleclick",
"wheel-left",
"wheel-right",
"wheel-up",
"wheel-down",
"ratchet-mode-switch",
"resolution-cycle-up",
"resolution-cycle-down",
"resolution-up",
"resolution-down",
"resolution-alternate",
"resolution-default",
"profile-cycle-up",
"profile-cycle-down",
"profile-up",
"profile-down",
"second-mode",
"battery-level",
]
choices: button_special_names
},
],
func: func_button_action_set_special,
@@ -1054,7 +1040,7 @@ active profile if none is given.""",
name: 'mode',
metavar: 'mode',
help_str: 'The mode to set as current',
choices: ['on', 'off', 'cycle', 'breathing'],
choices: led_mode_names,
},
],
},
@@ -1164,7 +1150,7 @@ class RatbagParser(object):
class RatbagParserSwitch(RatbagParser):
def __init__(self, type, name, group=None, switch=[], N_access=None, tag=None, func=None, help=None):
super(RatbagParserSwitch, self).__init__(type, name, group, tag, func, help)
super().__init__(type, name, group, tag, func, help)
self.switch = [classes[obj[of_type]](**obj) for obj in switch]
if N_access is not None:
self.N_access = RatbagParserNAccess(**N_access)
@@ -1218,7 +1204,7 @@ class RatbagParserSwitch(RatbagParser):
class RatbagParserNAccess(RatbagParserSwitch):
def __init__(self, type, name, group=None, switch=[], metavar=None, tag=None, func=None, help=None):
super(RatbagParserNAccess, self).__init__(type, name, group, switch, None, tag, func, help)
super().__init__(type, name, group, switch, None, tag, func, help)
self.metavar = metavar
def _sub_parse(self, parent, input_string, ns):
@@ -1244,7 +1230,7 @@ class RatbagParserNAccess(RatbagParserSwitch):
class RatbagParserSet(RatbagParserSwitch):
def __init__(self, type, name, group=None, switch=[], N_access=None, tag=None, func=None, help=None):
super(RatbagParserSet, self).__init__(type, name, group, switch, N_access, tag, func, help)
super().__init__(type, name, group, switch, N_access, tag, func, help)
def _add_to_subparsers(self, parent):
parser = parent.add_parser(self.name, help=self.help)
@@ -1276,7 +1262,7 @@ class RatbagParserSet(RatbagParserSwitch):
class RatbagParserCommand(RatbagParser):
def __init__(self, type, name, group=None, pos_args=[], tag=None, func=None, help=None):
super(RatbagParserCommand, self).__init__(type, name, group, tag, func, help)
super().__init__(type, name, group, tag, func, help)
self.pos_args = [classes[obj[of_type]](**obj) for obj in pos_args]
def _add_to_subparsers(self, parent):
@@ -1301,7 +1287,7 @@ class RatbagParserCommand(RatbagParser):
class RatbagParserArgument(RatbagParser):
def __init__(self, type, name, group=None, arg_type=None, metavar=None, nargs=None, choices=None, tag=None, func=None, help=None):
super(RatbagParserArgument, self).__init__(type, name, group, tag, func, help)
super().__init__(type, name, group, tag, func, help)
self.arg_type = arg_type
self.metavar = metavar
self.nargs = nargs
@@ -1313,7 +1299,7 @@ class RatbagParserArgument(RatbagParser):
class RatbagParserLink(RatbagParser):
def __init__(self, type, dest=None, group=None, tag=None, func=None, help=None):
super(RatbagParserLink, self).__init__(type, dest, group, tag, func, help)
super().__init__(type, dest, group, tag, func, help)
self.dest = dest
def get_dest(self):
@@ -1477,7 +1463,7 @@ def on_device_added(ratbagd, device):
def open_ratbagd(ratbagd_process=None, verbose=0):
try:
r = Ratbagd()
r = Ratbagd(@RATBAGD_API_VERSION@)
r.verbose = verbose
try:
r.connect('device-added', on_device_added)

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.
@@ -38,10 +36,20 @@ run_ratbagctl_in_subprocess = False
class TestRatbagCtl(unittest.TestCase):
json = None
@classmethod
def setUpClass(cls):
super(TestRatbagCtl, cls).setUpClass()
cls.reset_test_device()
super().setUpClass()
# it's a lot easier to debug a validataion failure here than
# with ratbagd failing
if cls.json is not None:
import json
json.loads(cls.json)
if cls.json is not None:
cls.load_test_device(cls.json)
cls.find_test_device()
@classmethod
@@ -50,7 +58,7 @@ class TestRatbagCtl(unittest.TestCase):
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True,
env={"RATBAGCTL_DEVEL": "org.freedesktop.ratbag_devel1_@ratbagd_sha@"})
env={"RATBAG_TEST": "1"})
(stdout, stderr) = ratbagctl.communicate()
return (ratbagctl.returncode,
@@ -71,9 +79,9 @@ class TestRatbagCtl(unittest.TestCase):
cmd.func(ratbagd, cmd)
except SystemExit as e:
returncode = e.code
except AttributeError as e:
except AttributeError:
returncode = 2
except argparse.ArgumentTypeError as e:
except argparse.ArgumentTypeError:
returncode = 2
output = sys.stdout.getvalue()
error = sys.stderr.getvalue()
@@ -95,6 +103,13 @@ class TestRatbagCtl(unittest.TestCase):
ratbagd._dbus_call("ResetTestDevice", "")
toolbox.sync_dbus()
@classmethod
def load_test_device(cls, json):
global ratbagd
rc = ratbagd._dbus_call("LoadTestDevice", "s", json)
assert(rc == 0)
toolbox.sync_dbus()
@classmethod
def find_test_device(cls):
rc, stdout, stderr = cls.run_ratbagctl_subprocess('list')
@@ -153,24 +168,42 @@ class TestRatbagCtlName(TestRatbagCtl):
class TestRatbagCtlProfile(TestRatbagCtl):
json = '''
{
"profiles": [
{
"is_active": false
},
{
"name": "test profile x2",
"is_active": false
},
{
"is_active": true
},
{
"is_active": false,
"is_disabled": true
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlProfile, cls).setUpClass()
cls.setProfile(0)
cls.setProfile(1)
cls.setProfile(0)
super().setUpClass()
def test_profile_name_get(self):
command = "profile 1 name get"
r = self.launch_good_test("test_device " + command)
self.assertEqual(r, 'test profile 2')
self.assertEqual(r, 'test profile x2')
self.launch_fail_test(command)
self.launch_fail_test("test_device " + command + " X")
self.launch_fail_test("test_device profile name get")
def test_profile_name_set(self):
r = self.launch_good_test("test_device profile 1 name get")
self.assertEqual(r, 'test profile 2')
self.assertEqual(r, 'test profile x2')
self.launch_good_test("test_device profile 1 name set banana")
r = self.launch_good_test("test_device profile 1 name get")
self.assertEqual(r, 'banana')
@@ -186,7 +219,7 @@ class TestRatbagCtlProfile(TestRatbagCtl):
def test_profile_active_get(self):
command = "profile active get"
r = self.launch_good_test("test_device " + command)
self.assertEqual(int(r), 0)
self.assertEqual(int(r), 2)
self.launch_fail_test(command)
self.launch_fail_test("test_device " + command + " X")
@@ -235,10 +268,41 @@ class TestRatbagCtlProfile(TestRatbagCtl):
class TestRatbagCtlResolution(TestRatbagCtl):
json = '''
{
"profiles": [
{
"is_active": false
},
{
"is_active": false
},
{
"is_active": true,
"resolutions": [
{ "is_active": false,
"is_default": true },
{ "xres": 1200, "yres": 1300,
"capabilities": [1],
"is_default": false,
"is_active": false },
{ "xres": 2300, "yres": 2400,
"capabilities": [1],
"is_active": true,
"is_default": false }
]
},
{
"is_active": false,
"is_disabled": true
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlResolution, cls).setUpClass()
cls.setProfile(1)
super().setUpClass()
def test_resolution_active_get(self):
command = "resolution active get"
@@ -267,13 +331,13 @@ class TestRatbagCtlResolution(TestRatbagCtl):
self.launch_fail_test("resolution 1 get X")
def test_profile_resolution(self):
r = self.launch_good_test("test_device profile 2 resolution 2 get")
self.assertEqual(r, "2: 2300x2400dpi")
r = self.launch_good_test("test_device profile 2 resolution 1 get")
self.assertEqual(r, "1: 1200x1300dpi")
def test_resolution_default_get(self):
command = "resolution default get"
r = self.launch_good_test("test_device " + command)
self.assertEqual(int(r), 1)
self.assertEqual(int(r), 0)
self.launch_fail_test("test_device " + command + " X")
self.launch_fail_test("test_device " + command + " 10")
@@ -290,10 +354,47 @@ class TestRatbagCtlResolution(TestRatbagCtl):
class TestRatbagCtlDPI(TestRatbagCtl):
json = '''
{
"profiles": [
{ "is_active": true,
"resolutions": [
{ "xres": 200,
"is_active": true,
"dpi_min": 50,
"dpi_max": 5000 },
{ "xres": 250,
"is_active": false },
{ "xres": 300,
"is_active": false }
]
},
{ "is_active": false,
"resolutions": [
{ "xres": 1000, "yres": 1100,
"capabilities": [1],
"is_active": false },
{ "xres": 1300, "yres": 1400,
"capabilities": [1],
"is_active": true }
]
},
{ "is_active": false,
"resolutions": [
{ "xres": 2100, "yres": 2200,
"capabilities": [1],
"is_active": true},
{ "xres": 2200, "yres": 2300,
"capabilities": [1],
"is_active": false }
]
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlDPI, cls).setUpClass()
cls.setProfile(0)
super().setUpClass()
def test_dpi_get(self):
command = "dpi get"
@@ -351,8 +452,8 @@ class TestRatbagCtlDPI(TestRatbagCtl):
self.setProfile(0)
r = self.launch_good_test("test_device profile 1 dpi get")
self.assertEqual(r, "1300x1400dpi")
r = self.launch_good_test("test_device profile 1 resolution 1 dpi get")
self.assertEqual(r, "1200x1300dpi")
r = self.launch_good_test("test_device profile 2 resolution 1 dpi get")
self.assertEqual(r, "2200x2300dpi")
r = self.launch_good_test("test_device resolution 2 dpi get")
self.assertEqual(int(r[:-3]), 300) # drop 'dpi' suffix
self.launch_fail_test("test_device profile 1 profile 1 dpi get")
@@ -360,10 +461,25 @@ class TestRatbagCtlDPI(TestRatbagCtl):
class TestRatbagCtlReportRate(TestRatbagCtl):
json = '''
{
"profiles": [
{ "is_active": true,
"rate": 1000,
"report_rates": [500, 1000]
},
{ "is_active": false,
"rate": 2000
},
{ "is_active": false,
"rate": 3000
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlReportRate, cls).setUpClass()
cls.setProfile(0)
super().setUpClass()
def test_rate_get(self):
command = "rate get"
@@ -407,10 +523,38 @@ class TestRatbagCtlReportRate(TestRatbagCtl):
class TestRatbagCtlButton(TestRatbagCtl):
json = '''
{
"profiles": [
{ "is_active": true,
"buttons": [
{ "button": 0 },
{ "action_type": "none" },
{ "action_type": "special",
"special": "profile-cycle-up" },
{ "action_type": "macro",
"macro": [ "+B", "-B", "t300" ] }
]
},
{ "is_active": false
},
{ "is_active": false,
"buttons": [
{ "action_type": "special",
"special": "profile-cycle-up" },
{ "action_type": "special" },
{ "button": 2,
"action_type": "button" },
{ "button": 3,
"action_type": "button" }
]
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlButton, cls).setUpClass()
cls.setProfile(0)
super().setUpClass()
def test_button_count(self):
command = "button count"
@@ -493,10 +637,57 @@ class TestRatbagCtlButton(TestRatbagCtl):
class TestRatbagCtlLED(TestRatbagCtl):
json = '''
{
"profiles": [
{ "is_active": true,
"leds": [
{ "type": 1,
"mode": 0,
"duration": 1000,
"brightness": 20 },
{ "type": 1,
"mode": 1,
"duration": 333,
"brightness": 40,
"color": [255, 0, 0] },
{ "type": 1,
"mode": 2,
"duration": 333,
"brightness": 40 }
]
},
{
"leds": [
{ "type": 1,
"mode": 1 },
{ "type": 1,
"mode": 0 },
{ "type": 1,
"mode": 1 }
]
},
{
"leds": [
{ "type": 1,
"mode": 1,
"color": [255, 0, 0],
"duration": 1000 },
{ "type": 1,
"mode": 2,
"brightness": 40,
"duration": 333 },
{ "type": 1,
"mode": 1 }
]
}
]
}
'''
@classmethod
def setUpClass(cls):
super(TestRatbagCtlLED, cls).setUpClass()
cls.setProfile(0)
super().setUpClass()
def test_led_n_get(self):
self.setProfile(0)
@@ -603,36 +794,51 @@ class TestRatbagCtlLED(TestRatbagCtl):
def setUpModule():
global ratbagd_process, ratbagd, parser
ratbagd_process = toolbox.start_ratbagd()
ratbagd_process = None
if start_ratbagd:
ratbagd_process = toolbox.start_ratbagd()
assert ratbagd_process is not None
assert ratbagd_process is not None
ratbagd = toolbox.open_ratbagd(ratbagd_process)
assert ratbagd is not None
try:
ratbagd = toolbox.open_ratbagd(ratbagd_process)
assert ratbagd is not None
except Exception as e:
if ratbagd_process is not None:
toolbox.terminate_ratbagd(ratbagd_process)
raise e
parser = toolbox.get_parser()
def tearDownModule():
global ratbagd_process
toolbox.terminate_ratbagd(ratbagd_process)
if start_ratbagd:
toolbox.terminate_ratbagd(ratbagd_process)
def parse(input_string):
global run_ratbagctl_in_subprocess
global run_ratbagctl_in_subprocess, start_ratbagd
parser_test = argparse.ArgumentParser(description="Testsuite for ratbagd/ratbagctl", add_help=False)
parser_test.add_argument("--subprocess", action='store_true', default=False)
parser_test.add_argument("--use-existing-ratbagd", dest='use_existing',
action='store_true', default=False,
help='Don\'t start up ratbagd.devel, connect to the already running one')
ns, rest = parser_test.parse_known_args(input_string)
run_ratbagctl_in_subprocess = ns.subprocess
start_ratbagd = not ns.use_existing
if start_ratbagd and not os.geteuid() == 0:
print('Script must be run as root', file=sys.stderr)
sys.exit(77)
return rest
def main(argv):
if not os.geteuid() == 0:
print('Script must be run as root', file=sys.stderr)
sys.exit(77)
os.environ['RATBAG_TEST'] = '1'
os.environ['LIBRATBAG_DATA_DIR'] = '@LIBRATBAG_DATA_DIR@'
resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
args = parse(argv)

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"),
@@ -62,6 +60,18 @@ class RatbagErrorCode(IntEnum):
IMPLEMENTATION = -1004
class RatbagdIncompatible(Exception):
"""ratbagd is incompatible with this client"""
def __init__(self, ratbagd_version, required_version):
super().__init__()
self.ratbagd_version = ratbagd_version
self.required_version = required_version
self.message = "ratbagd API version is {} but we require {}".format(ratbagd_version, required_version)
def __str__(self):
return self.message
class RatbagdUnavailable(Exception):
"""Signals DBus is unavailable or the ratbagd daemon is not available."""
pass
@@ -116,7 +126,7 @@ class _RatbagdDBus(GObject.GObject):
_dbus = None
def __init__(self, interface, object_path):
GObject.GObject.__init__(self)
super().__init__()
if _RatbagdDBus._dbus is None:
try:
@@ -125,8 +135,8 @@ class _RatbagdDBus(GObject.GObject):
raise RatbagdUnavailable(e.message)
ratbag1 = "org.freedesktop.ratbag1"
if os.environ.get('RATBAGCTL_DEVEL'):
ratbag1 = os.environ['RATBAGCTL_DEVEL']
if os.environ.get('RATBAG_TEST'):
ratbag1 = "org.freedesktop.ratbag_devel1"
if object_path is None:
object_path = "/" + ratbag1.replace('.', '/')
@@ -196,7 +206,7 @@ class _RatbagdDBus(GObject.GObject):
# Calls a method synchronously on the bus, using the given method name,
# type signature and values.
#
# It the result is valid, it is returned. Invalid results raise the
# If the result is valid, it is returned. Invalid results raise the
# appropriate RatbagError* or RatbagdDBus* exception, or GLib.Error if
# it is an unexpected exception that probably shouldn't be passed up to
# the UI.
@@ -238,10 +248,14 @@ class Ratbagd(_RatbagdDBus):
(GObject.SignalFlags.RUN_FIRST, None, ()),
}
def __init__(self):
_RatbagdDBus.__init__(self, "Manager", None)
result = self._get_dbus_property("Devices") or []
self._devices = [RatbagdDevice(objpath) for objpath in result]
def __init__(self, api_version):
super().__init__("Manager", None)
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):
@@ -261,6 +275,10 @@ class Ratbagd(_RatbagdDBus):
self.emit("device-removed", device)
self.notify("devices")
@GObject.Property
def api_version(self):
return self._get_dbus_property("APIVersion")
@GObject.Property
def devices(self):
"""A list of RatbagdDevice objects supported by ratbagd."""
@@ -291,7 +309,7 @@ class RatbagdDevice(_RatbagdDBus):
}
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Device", object_path)
super().__init__("Device", object_path)
# FIXME: if we start adding and removing objects from this list,
# things will break!
@@ -370,7 +388,7 @@ class RatbagdProfile(_RatbagdDBus):
CAP_WRITE_ONLY = 103
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Profile", object_path)
super().__init__("Profile", object_path)
self._dirty = False
self._active = self._get_dbus_property("IsActive")
@@ -410,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 []
@@ -502,7 +520,7 @@ class RatbagdProfile(_RatbagdDBus):
@GObject.Property
def is_active(self):
"""Returns True if the profile is currenly active, false otherwise."""
"""Returns True if the profile is currently active, false otherwise."""
return self._active
def set_active(self):
@@ -516,7 +534,7 @@ class RatbagdResolution(_RatbagdDBus):
"""Represents a ratbagd resolution."""
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Resolution", object_path)
super().__init__("Resolution", object_path)
self._active = self._get_dbus_property("IsActive")
self._default = self._get_dbus_property("IsDefault")
@@ -664,7 +682,7 @@ class RatbagdButton(_RatbagdDBus):
}
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Button", object_path)
super().__init__("Button", object_path)
def _on_properties_changed(self, proxy, changed_props, invalidated_props):
if "Mapping" in changed_props.keys():
@@ -784,7 +802,7 @@ class RatbagdMacro(GObject.Object):
}
def __init__(self, **kwargs):
GObject.Object.__init__(self, **kwargs)
super().__init__(**kwargs)
self._macro = []
def __str__(self):
@@ -811,7 +829,7 @@ class RatbagdMacro(GObject.Object):
@GObject.Property
def keys(self):
"""A list of (RatbagdButton.MACRO_*, value) tuples representing the
"""A list of (RatbagdButton.Macro.*, value) tuples representing the
current macro."""
return self._macro
@@ -821,7 +839,7 @@ class RatbagdMacro(GObject.Object):
libratbag format.
@param macro The macro in libratbag format, as
[(RatbagdButton.MACRO_*, value)].
[(RatbagdButton.Macro.*, value)].
"""
ratbagd_macro = RatbagdMacro()
@@ -838,7 +856,7 @@ class RatbagdMacro(GObject.Object):
def append(self, type, value):
"""Appends the given event to the current macro.
@param type The type of event, as one of RatbagdButton.MACRO_*.
@param type The type of event, as one of RatbagdButton.Macro.*.
@param value If the type denotes a key event, the X.Org or Gdk keycode
of the event, as int. Otherwise, the value of the timeout
in milliseconds, as int.
@@ -883,7 +901,7 @@ class RatbagdLed(_RatbagdDBus):
}
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Led", object_path)
super().__init__("Led", object_path)
@GObject.Property
def index(self):

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.
@@ -25,7 +23,6 @@
import imp
import os
import shutil
import subprocess
import sys
@@ -36,9 +33,6 @@ RATBAGCTL_NAME = 'ratbagctl'
RATBAGCTL_PATH = os.path.join('@MESON_BUILD_ROOT@', RATBAGCTL_NAME)
RATBAGCTL_DEVEL_NAME = 'ratbagctl.devel'
RATBAGCTL_DEVEL_PATH = os.path.join('@MESON_BUILD_ROOT@', RATBAGCTL_DEVEL_NAME)
DBUS_CONF_DIR = '/etc/dbus-1/system.d'
DBUS_CONF_NAME = 'org.freedesktop.ratbag_devel1.conf'
DBUS_CONF_PATH = os.path.join(DBUS_CONF_DIR, DBUS_CONF_NAME)
def import_non_standard_path(name, path):
@@ -61,20 +55,14 @@ def start_ratbagd(verbosity=0):
from gi.repository import Gio
import time
os.makedirs(DBUS_CONF_DIR, exist_ok=True)
# first copy the policy for the ratbagd daemon to be allowed to run
shutil.copy(os.path.join('@MESON_BUILD_ROOT@', DBUS_CONF_NAME),
DBUS_CONF_PATH)
# FIXME: kill any running ratbagd.devel
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')
@@ -88,15 +76,15 @@ def start_ratbagd(verbosity=0):
proxy = Gio.DBusProxy.new_sync(dbus,
Gio.DBusProxyFlags.NONE,
None,
"org.freedesktop.ratbag_devel1_@ratbagd_sha@",
"/org/freedesktop/ratbag_devel1_@ratbagd_sha@",
"org.freedesktop.ratbag_devel1_@ratbagd_sha@.Manager",
"org.freedesktop.ratbag_devel1",
"/org/freedesktop/ratbag_devel1",
"org.freedesktop.ratbag_devel1.Manager",
None)
name_owner = proxy.get_name_owner()
if name_owner is None:
time.sleep(0.2)
os.environ['RATBAGCTL_DEVEL'] = "org.freedesktop.ratbag_devel1_@ratbagd_sha@"
os.environ['RATBAG_TEST'] = "1"
if name_owner is None or ratbagd_process.poll() is not None:
return None
@@ -111,10 +99,6 @@ def terminate_ratbagd(ratbagd):
ratbagd.wait(5)
except subprocess.TimeoutExpired:
ratbagd.kill()
try:
os.unlink(DBUS_CONF_PATH)
except FileNotFoundError:
pass
def sync_dbus():
@@ -125,14 +109,11 @@ def sync_dbus():
ratbagctl = import_non_standard_path(RATBAGCTL_NAME, RATBAGCTL_PATH)
from ratbagctl import open_ratbagd, get_parser, RatbagError, RatbagErrorCapability
from ratbagctl import open_ratbagd, get_parser, RatbagError, RatbagErrorCapability # NOQA
__all__ = [
RATBAGCTL_NAME,
RATBAGCTL_PATH,
DBUS_CONF_DIR,
DBUS_CONF_NAME,
DBUS_CONF_PATH,
start_ratbagd,
terminate_ratbagd,
open_ratbagd,