Compare commits

...

194 Commits
v0.16 ... v0.17

Author SHA1 Message Date
Yaroslav Chvanov
925b2a2a0a libratbag 0.17 2022-11-09 10:43:27 +00:00
Yaroslav Chvanov
df42eeb1ca CI: update runners to use Node 16
Fixes warnings.
2022-11-09 10:43:05 +00:00
Yaroslav Chvanov
aab91b4b11 data: add another SinoWealth Generic Mouse (258a:0029) 2022-11-09 10:42:34 +00:00
Yaroslav Chvanov
c39640d943 asus: fix include order
It caused an `implicit function declaration` warning.
2022-10-12 22:16:19 +03:00
Kyoken
0c3cfe71a0 asus: remove extern 2022-10-12 21:20:30 +03:00
Kyoken
dcba13152f asus driver 2022-10-12 18:11:28 +03:00
Andreas Grosse
61da98ffd4 Add Support for G502 X over USB 2022-10-10 21:39:53 +02:00
PeakKS
98eec0941a Add support for G502 X Wireless 2022-09-11 19:21:27 -04:00
Yaroslav Chvanov
d8301414c7 libratbag-data: add a missing include
To fix `Declaration of X will not be visible outside of this function`
Clang warnings.
2022-08-02 12:12:42 +03:00
Yaroslav Chvanov
a4da425298 sinowealth: move device-specific data out of the driver
This moves device data out of the driver into device files. However, a
_single_ device file contains data for _several mice_ at the same time.
The reason for this is that different mice by SinoWealth may use the
same VID:PID pair. This is unfortunate, as we only allow a single
device file a given VID:PID pair.

Also move `sinowealth_led_format` enum into a header file and rename
its constants to fit in outside of the driver.
2022-08-02 12:12:42 +03:00
Yaroslav Chvanov
ea7b4b6821 sinowealth: null-terminate the buffer with firmware version 2022-08-02 12:12:42 +03:00
Yaroslav Chvanov
833468b292 sinowealth: use a constant instead of a magic number 2022-08-02 12:12:39 +03:00
Yaroslav Chvanov
cc954af7a7 steelseries: ensure that device has a version number set
Otherwise there would be hard to diagnose errors later on.
2022-08-01 07:55:40 +00:00
Yaroslav Chvanov
213ccce0a8 steelseries: add fallback button and LED count
Otherwise if there is a device file without them set, will try to create
UINT_MAX-1 instances of them and obviously fail with an out-of-memory
error.
2022-08-01 07:55:40 +00:00
Linus
bb6ab46a1a Fix presumed typo 2022-08-01 09:27:27 +10:00
listout
05c5815fb9 Use fprintf instead of error
hidpp files were using the error function from error.h header file.
Unfortunately not every libc comes with this particular header file, so
with this patch we won't be requiring to be rely on error function and
use fprintf function.

Thanks to developer Yaroslav Chvanov <staticssleever668> for the
original suggestion

Closes: https://github.com/libratbag/libratbag/issues/1253

Signed-off-by: listout <listout@protonmail.com>
2022-07-29 14:22:05 +00:00
Thomas Weißschuh
0fc6764b47 libratbag-data: fix possible NULL dereferences
This can happen when a device has not properly initialized and this
fields have not been set.
2022-07-21 09:09:04 +00:00
Peter Hutterer
07b40316fb test: add the Bolt receiver C548 to the receiver list
Listed here: https://pwr-solaar.github.io/Solaar/devices.html

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2022-06-08 12:23:44 +10:00
Sami Olmari
260e4adffc Add device-file for Logitech MX Anywhere 3B (#1313)
This variant stands for Logitech MX Anywhere 3 for Business.

Bluetooth match only, the receiver is not yet supported by the 
kernel and the device thus has the receiver's USB id

Signed-off-by: Sami Olmari <sami@olmari.fi>
2022-06-07 10:08:16 +10:00
Ilya Guterman
8c3c6598c3 hidpp20: verbose log the get current profile 2022-06-06 17:20:42 +03:00
Yaroslav Chvanov
1140875cd8 sinowealth: fix all devices being detected as generic device 2022-06-06 20:13:19 +10:00
Yaroslav Chvanov
f87bccb370 sinowealth: add data for Marvo Scorpion G961
I did not add the data entry for ratbagd though, as there has been
reported breakage with Ant Esports GM500, which uses the same VID:PID.
2022-06-06 07:54:17 +00:00
Yaroslav Chvanov
c1955598fe sinowealth: note that debounce time command doesn't work on shorter mice 2022-06-06 07:54:17 +00:00
Yaroslav Chvanov
d261458cae sinowealth: limit the resolution to sensor's upper bound 2022-06-06 07:54:17 +00:00
Yaroslav Chvanov
ef3822af82 sinowealth: fix crashing on mice with unknown sensors 2022-06-06 07:54:17 +00:00
Yaroslav Chvanov
2d8115d2bf sinowealth: detect sensor type automatically 2022-06-06 07:54:17 +00:00
Yaroslav Chvanov
4c102358a2 sinowealth: make device detection less imperative
..and rename sensor types along the way:
- Prefix them;
- Fix PMW sensors being named PWM.
2022-06-06 07:54:17 +00:00
Yaroslav Chvanov
d133d30f62 sinowealth: add numeric IDs to sensor types
These IDs match the ones in SinoWealth's configuration software.
2022-06-06 07:54:17 +00:00
Yaroslav Chvanov
16a1028e1d sinowealth: add sensor type field to configuration data 2022-06-06 07:54:17 +00:00
Yaroslav Chvanov
704d5d2e65 sinowealth: decrease minimal configuration size
Configuration size on 1007 mice is smaller for sure. With these changes
the driver has been reported to successfully work with Marvo Scorpion
G961.
2022-06-06 07:54:17 +00:00
Yaroslav Chvanov
bef26c1890 sinowealth: make dpi_to_raw return 8 bit number
It would otherwise get converted implicitly anyway.
2022-06-06 07:54:17 +00:00
Yaroslav Chvanov
85241fdd1b sinowealth: handle RGB_NOT_SUPPORTED explicitly
This should technically be unreachable but this is good for code
quality.
2022-06-06 07:54:17 +00:00
Yaroslav Chvanov
2dea887d2f sinowealth: prefix a function name 2022-06-06 07:54:17 +00:00
Yaroslav Chvanov
2004a3c13e sinowealth: fix a typo 2022-06-06 07:54:17 +00:00
Ilya Guterman
48bccf4c98 hidpp20: fetch active profile index only once
currently the active profile index is fetched for each profile,
this adds unnecessary overhead.

change the behavior to fetch the active profile only once during the driver data initialization.
2022-06-06 15:11:02 +10:00
Andreas Schwab
3ea0d44176 meson.build: use valgrind only if available 2022-05-06 15:33:46 +01:00
Ilya Guterman
3e1bc79aac hidpp20: fix set onboard mode 2022-04-29 01:03:45 +03:00
Yaroslav Chvanov
9715c5c150 libratbag-data: fix a memory leak
In driver data of hidpp10 and steelseries drivers `dpi_list` and
`dpi_range` were allocated with calloc, but never got freed.
2022-04-27 22:16:32 +03:00
Yaroslav Chvanov
07f0eafa57 sinowealth: add support for writing macros 2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
72f8708e0e sinowealth: add support for writing key button actions 2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
6ac5ba6dbb sinowealth: add support for changing button mappings 2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
45a8fd9ec3 sinowealth: add support for reading button bindings 2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
e9dff40258 sinowealth: add types for buttons
This button mapping reading and writing support is partially based on a
previous work of @phomes and @enkore. Thank you both very much!
2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
94f28df134 sinowealth: set button count from detected device 2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
4d646db24f sinowealth: fix rave lighting mode settings being shifted by one byte
Turns out I missed a field a bit lower and made an `unknown` section
bigger to compensate for that.
2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
e21366e5e5 sinowealth: mark color order on Genesis Xenon 770 as tested 2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
21ac98e5f9 sinowealth: fix DPI being off by 100 on PWM3327 sensor
Thanks @hrw for testing.
2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
567d666b96 sinowealth: use a union instead of doing pointer arithmetic on DPI slots
This makes the code a bit more self-explaining and less error-prone as
we don't have to think about memory addresses when modifying DPI.
Also fixes several `bugprone-implicit-widening-of-multiplication-result`
clang-tidy warnings.
2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
b62565da56 sinowealth: fix breathing LED mode on 0027 mice 2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
3ff3705dbf sinowealth: add partial support for reading BREATHING7 lighting mode
Only partial as libratbag can only have 1 color in breathing lighting
mode.
2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
2aa9f012fe sinowealth: fix active resolution slot reading and writing
It turns out the field with active resolution only counts active
resolution slots. This was probably missed before because probably no
one sets for instance slots 1 and 3 but disables slot 2. I already added
a comment explaining this behavior before because I noticed this while
trying to implement this in ratbag-python and be surprised it didn't
work, but I didn't notice that the implementation here was lacking.
2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
e7cc62a9c8 sinowealth: return an error code instead of doing an assert
While I doubt it will actually ever get hit, there is no reason to crash
the whole daemon because of this.
2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
0608a0156d sinowealth: don't redundantly unreference a profile
Not needed anymore as a `for_each` macro is now used to get profiles.
2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
3723c5177f sinowealth: add command ID for putting the device into DFU mode
This is not right now and will never be actually used, I put it here
just for the sake of completeness.
2022-04-21 00:37:44 +03:00
Yaroslav Chvanov
9d59c1992a libratbag: remove now unused deprecated driver methods 2022-04-16 02:24:40 +03:00
Yaroslav Chvanov
ee54001032 roccat-kone-pure: migrate to profile-oriented API 2022-04-16 02:24:40 +03:00
Yaroslav Chvanov
7f0d058b66 roccat: migrate to profile-oriented API 2022-04-16 02:24:40 +03:00
Yaroslav Chvanov
cd2ad37db7 etekcity: only write profile once 2022-04-16 02:24:40 +03:00
Yaroslav Chvanov
d08f1f21b7 etekcity: migrate to profile-oriented API 2022-04-16 02:24:40 +03:00
Yaroslav Chvanov
2d21c0dac1 test: migrate to profile-oriented API 2022-04-16 02:24:40 +03:00
Peter Hutterer
7016b50f1b roccat: remove an unnecessary #include
No obvious reason why we need to include that header and it appears to
be glibc-specific. Let's use stdint.h instead (which is already
included).

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2022-02-15 11:32:17 +10:00
Yaroslav Chvanov
217847adde steelseries: only write report rate if it's dirty 2022-02-11 14:32:14 +10:00
Yaroslav Chvanov
c12edcd4c7 steelseries: fix length of LED cycle output report being incorrect
It probably meant to use the macro directly, not `sizeof` of it (which
made it be 4 instead of 64).
2022-02-11 14:31:37 +10:00
Rafael Silva
ae53f6123b ibratbag-hidraw: add optional filter to hidraw read
Currently, these functions will read and timeout after 1s if
no packets were received. The issue with this is that a lot of times we
may catch unrelated packets, such as mouse or keyboard changes.
To solve this, this patch adds a new optional filter argument, when a filter
is passed the functions now will keep reading packets until a valid packet is found.
The filter argument is user written function that returns true or false.

Co-authored-by: Filipe Laíns <lains@riseup.net>
Signed-off-by: Rafael Silva <perigoso@riseup.net>
2022-02-04 14:27:08 +10:00
Yaroslav Chvanov
6dbaeaf506 steelseries: fix a memory leak
Found by valgrind. I also feel that there is another memory leak of the
same nature a bit higher in `init_data_hidpp10`.

Introduced by me. :^)
2022-02-01 07:30:01 +10:00
Yaroslav Chvanov
75c8cf83c3 sinowealth: fix several misspellings 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
706fe34382 sinowealth: explain how active_dpi works 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
5e57aa07ea sinowealth: add helpers for doing raw HID queries
Removes a lot of duplicated code.
There is one place, however, where these helpers were not used -
`sinowealth_read_raw_config`. It has some custom handling of data
because of unexpected behavior from mice.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
1f4bf8fe5f sinowealth: rename a struct member to show that it's bit field 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
dadfb06b98 sinowealth: put command buffer size into a macro 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
d3c37a6836 sinowealth: fix two struct fields having swapped places 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
1b04bcffba sinowealth: visually separate config report data specific to long mice 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
e5093e1009 sinowealth: prefix some of our data type names 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
132fc21212 sinowealth: sanity check output of all raw data getters
Well, of almost all, config getter's output is not checked as it may
have seemingly incorrect data in some cases.

Potentially some mice may not support some commands but look like they
do. You can see this guard in action if you run
`sinowealth_print_long_lod_and_anglesnapping` after any other command
that sets 6 byte long buffer on non-long mice.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
3793e32ebd sinowealth: put a macro into an enum 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
4e931cb712 sinowealth: remove a now unused macro 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
e1682171ce sinowealth: improve comments
- Add comments to almost all functions.
- Add comments to some data types and macros.
- Add missing periods where it's needed.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
e1334214b8 sinowealth: make the sensor maximum DPI getter return 0 on error 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
fd0954dbdc sinowealth: remove new-lines between data types and their size checks 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
aa1ab47f59 sinowealth: make update_profile_from_config a void function
It only ever returned `0`.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
7585d73ff5 sinowealth: rename macros with config size to make more sense 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
81ca281f2c sinowealth: don't mark inactive profile as active 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
48d654827e sinowealth: cache current profile index in driver data 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
6c9306b199 sinowealth: store config report in an array 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
a9c0060029 sinowealth: add a macro for profile count 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
a289612d85 sinowealth: initialize all variables
While none of them were actually used before being initialized, I got
tired of constantly seeing `uninitialized variable` warnings in my
editor.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
f7b7007897 sinowealth: declare a variable right before it's initialization
It's not in the beginning of the function anyway, so why not make it a
bit more concise.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
16333068b7 sinowealth: use for_each instead of manually getting profiles 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
eace92870a sinowealth: set the command before writing config
This will help if the config report has a different command in it for
some reason. This has happened to me when I tried using the second
profile.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
c176825051 sinowealth: use uint8_t instead of char for report IDs
Also move one of them up, this may make the function a bit more readable
if we ever decide to support the second mouse profile.

Fixes a clang-tidy warning.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
ba3547457c sinowealth: split commit into raw data update and raw write helpers
This will get handy if we decide to add multiple profile support.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
3f936eff68 data: add another name to Glorious Model O - O- 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
c25864c18a data: add SinoWealth Generic Mouse (258a:0027) 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
cf64aac5f1 sinowealth: set the RGB effect to unsupported if LED type is not defined 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
0a39552a65 sinowealth: add default sensor type and LED type 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
03d2d71f82 sinowealth: don't enable LED support if LED type is not defined 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
1edb6df25b sinowealth: define set_active_profile method in the driver 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
9c7494c5c0 sinowealth: select first profile before getting any data from the mouse 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
7f29c0423f sinowealth: add active profile setter 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
07307b85d3 sinowealth: add commands IDs for the second profile 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
219218118d sinowealth: print LOD and angle snapping value 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
1e2489c9e7 sinowealth: add LOD and angle snapping value getter
LOD - lift-off distance.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
479c4352af sinowealth: print debounce time 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
763655516c sinowealth: add debounce time getter 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
944c4f0af5 sinowealth: print profile index 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
268ccab111 sinowealth: add profile index getter
Thank you so much, @araghon007!
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
603b0ca1fc sinowealth: use RGB or RBG colors depending on device 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
4e50c506de sinowealth: format code 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
4351eb3d72 sinowealth: support different sensors 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
9f4a1ba405 sinowealth: print device model based on firmware version
sinowealth: detect Glorious Model O with old firmware
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
451894b1b4 sinowealth: make the firmware version printing helper return it instead 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
c6051cb9ea sinowealth: handle new RGB effects 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
34fafbc0f8 sinowealth: use enum types in config report directly
Although I'm not sure this if this would work on every compiler, having
a bit of extra type safety is always nice.
It does work with GCC and Clang at the very least.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
75d075cc67 sinowealth: make some utility functions take unsigned integers
To avoid implicit conversions.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
2f330b6466 sinowealth: use a struct to store lighting mode 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
c7adc9b9f6 sinowealth: print config data size 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
16a7b6ed18 sinowealth: use NULL instead of 0 for pointers 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
7ff73c8515 sinowealth: rename a config entry to make more sense 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
f11e31d671 sinowealth: add some hidden lighting types
These are not available in Glorious software.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
f69457c280 sinowealth: add breathing_mode to config report 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
756fee1202 sinowealth: sort values in an enum 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
d4126463e4 sinowealth: don't say that amount of breathing colors is constant
Based on information in gloryctl's documentation, it's not. I guess it
was marked as such because Glorious software doesn't allow to change it.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
00832cb9f0 sinowealth: add a lift off distance value to config report 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
866d4e6433 sinowealth: put some macros into enums 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
cbba00f997 sinowealth: use a map to convert report rates 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
d2e4fcc23e sinowealth: put report rate conversion comparison in order
And also not use some random number in the comparison.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
f0c66d0e1f sinowealth: separate config to real config and report_rate nibbles 2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
b2fb91e261 sinowealth: use correct bits for report rate
Turns out it takes the whole nibble, not just three bits.
2022-02-01 07:20:28 +10:00
Yaroslav Chvanov
1a64d2b85d Add a TODO to make ratbag_button_action_match support macro actions 2022-02-01 07:11:43 +10:00
Yaroslav Chvanov
8f5fdf8590 Fix ratbag_button_action_match not matching on type NONE 2022-02-01 07:11:43 +10:00
Yaroslav Chvanov
baad36060d util: fix a string of text being printed out of nowhere 2022-02-01 07:06:38 +10:00
Yaroslav Chvanov
7ecabea70d sinowealth: remove a redundant comment 2022-01-28 10:31:09 +10:00
Yaroslav Chvanov
c0c8d6c544 sinowealth: increase log level of printed firmware version 2022-01-28 10:31:09 +10:00
Yaroslav Chvanov
37587bb99c sinowealth: fix a memory leak
We were getting profiles, but never unreferenced them afterwards.
2022-01-28 10:31:09 +10:00
Yaroslav Chvanov
cc2fff9c06 sinowealth: rename some functions to be more clear
So that they would match button data reading later on.
2022-01-28 10:31:09 +10:00
Yaroslav Chvanov
b80d3ac0a3 sinowealth: use the detected LED count
I put this in a separate commit as 99% of this is just a change of
indentation levels, which doesn't look nice in git.
2022-01-28 10:31:09 +10:00
Yaroslav Chvanov
a757180d60 sinowealth: detect LED count at run-time 2022-01-28 10:31:09 +10:00
Yaroslav Chvanov
8c0124d654 sinowealth: move raw data reading from init function to probe function 2022-01-28 10:31:09 +10:00
Yaroslav Chvanov
b5d41e3657 sinowealth: put reading of raw configuration data into a helper 2022-01-28 10:31:09 +10:00
Yaroslav Chvanov
bfd2fc9969 sinowealth: print firmware version on profile init, not on reading 2022-01-28 10:31:09 +10:00
Yaroslav Chvanov
566d160de3 sinowealth: make init_profile return error codes 2022-01-28 10:31:09 +10:00
Kyoken
e0dc0a3a5b fix ratbag-command led setting 2022-01-24 12:55:12 +10:00
Peter Hutterer
5b7af0ec14 CI: actually test with the various meson args
Different variable names and no-one noticed.

This means we need to add a few dependencies that weren't needed before.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2022-01-22 12:09:34 +10:00
Peter Hutterer
44a9959344 roccat: name two fields correctly
In hindsight, these should've been obvious.
2022-01-20 19:32:14 +00:00
Peter Hutterer
755dbddfb3 meson.build: fix spelling of receiver 2022-01-20 15:47:11 +10:00
Peter Hutterer
1e3a85433a CI: add a github pages job to the CI
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2021-12-15 14:40:56 +10:00
Peter Hutterer
b3b3e3f838 Drop CircleCI
Right now, CircleCI fails with ssh errors because for some reason it
wants to clone the repo over ssh, not https. This is probably fixable
but CircleCI now limits how many runs we can do per month anyway so it's
borderline useless if we do actual developement.

Having to re-learn the Circle CI syntax to fix the current issue is not
an efficient use of our time, so let's just drop this completely, git
will keep the history of what we used to do in the CI for us.

02f3487f52 added a github workflow which
is comparatively incomplete but could be extended to do the previous
test runs. That's a more future-proof path anyway.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2021-12-15 14:40:56 +10:00
Peter Hutterer
f05b671dc1 Install all data files as subdir
This removes the need of listing every device in meson.build which is a
common enough mistake for new contributors.

And move the validation tests over to test/ so we don't have to list
them in the exclude_files of the install_subdir() command.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2021-12-13 10:39:42 +10:00
Peter Hutterer
bfadfc3c91 Change the data validation tests to take the directory instead of files
Instead of passing a million files, let's pass the directory and do a
pathlib glob instead.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2021-12-13 10:39:42 +10:00
Peter Hutterer
96d93ae387 meson.build: require meson 0.50.0
Released March 2019 we've already relied on some of its features anyway,
let's get rid of the warnings.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2021-12-13 10:38:59 +10:00
Peter Hutterer
03891189da hidpp20: add missing #define to the feature report strings (#1237)
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2021-12-06 08:46:40 +10:00
Peter Hutterer
02f3487f52 Add a GitHub Workflow as CI run (#1238)
CircleCI is limiting how many runs we can do, which results in pipelines
randomly failing when we've used up the credits for this month.

Let's add a github workflow instead. Unfortunately github workflows are
a bit of a toy api, no support for YAML anchors or extending other jobs,
so there's a lot of duplication needed to get a job to do something.

The actions added in this patch help a bit to make the jobs easier to
understand (copied from libwacom).

This only runs on Ubuntu right now.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2021-12-06 08:43:14 +10:00
Ilya guterman
d2f1ff0ca2 tests: test for additonal lightspeed receviers pids
PR #1225
2021-11-06 16:53:01 +00:00
Rafael Silva
d5a7ad3730 device: g635: Set buttons as 0 2021-11-06 03:27:32 +02:00
Carlos Mogas da Silva
e98f86fa2a Add Logitech G635 Headset 2021-11-06 03:27:32 +02:00
Rafael Silva
fb14cb576b README: add uninstall instruction 2021-11-04 09:35:30 +00:00
Ilya Guterman
2c83f9c3ef add static assert 2021-11-04 03:36:41 +02:00
Ilya Guterman
b71f439ed6 fix ci failures 2021-11-04 03:36:41 +02:00
Ilya Guterman
dff102be43 steelseries: prefix hidraw writes with 0 2021-11-04 03:36:41 +02:00
mtayllan
6bb92dc313 Set buttons as 0 for logitech g733 2021-11-01 08:25:03 +02:00
mtayllan
92c5923255 Add support to Logitech G733 2021-11-01 08:25:03 +02:00
staticssleever668
6c8a2d36fe sinowealth: use proper config size when writing 2021-10-26 11:57:53 +03:00
staticssleever668
875aae6985 sinowealth: add support for mice with longer configuration data 2021-10-26 11:57:53 +03:00
Ilya Guterman
a55297e0fa fix button count and default report rate always zero 2021-10-20 11:29:55 +03:00
staticssleever668
fa95abbc33 hidpp20: give some structures names
Fixes SWIG's 503 warnings.
2021-10-16 22:43:37 +03:00
staticssleever668
48d1b21036 driver-hidpp20: cast an internal log priority enumeration to the general one
Fixes an 'enum-conversion' warning by clang.
2021-10-16 22:43:37 +03:00
staticssleever668
904aedc555 driver-hidpp10: cast an internal log priority enumeration to the general one
Fixes an 'enum-conversion' warning by clang.
2021-10-16 22:43:37 +03:00
staticssleever668
4fa8663cfb driver-hidpp10: remove a redundant character array null check
It is never null. Fixes a 'pointer-bool-conversion' warning by clang.
2021-10-16 22:43:37 +03:00
staticssleever668
58086882a3 ratbagd: initialize a variable
Fixes a 'sometimes-uninitialized' warning by clang.
2021-10-16 22:43:37 +03:00
Tayllan
aef92645b7 hidpp20: allow setting a default buttons count
PR #1212
2021-10-16 20:08:44 +01:00
staticssleever668
514ce5419f data: add SteelSeries Rival 100/105 2021-10-16 19:31:11 +01:00
staticssleever668
2a0a3cf203 steelseries: add support for changing LEDs on Rival 100 2021-10-16 19:31:11 +01:00
staticssleever668
f96ba13f07 libratbag-data: add 'Rival100' quirk to SteelSeries driver 2021-10-16 19:31:11 +01:00
staticssleever668
724d31c8fe steelseries: make settings unique to SteelSeries Sensei RAW a quirk 2021-10-16 19:31:11 +01:00
staticssleever668
66e44d76b9 steelseries: add 'Quirk' setting 2021-10-16 19:31:11 +01:00
Filipe Laíns
e908871e82 hidpp20: allow setting a default report rate (#969)
In case we can't get the report rate value from the device, allow
setting in the device file.

Signed-off-by: Filipe Laíns <lains@archlinux.org>
2021-10-11 02:14:02 +03:00
Kristian Klausen
9732aa009e steelseries: only use the HID vendor device
Rival mice are composite devices with multiple HID devices and only the
HID vendor device can be used to configure the device.

Tested with a SteelSeries Rival 300 (1038:1710).

Fix #888
2021-10-10 21:10:20 +01:00
Ilya guterman
92299d4673 data: support steelseries rival variations
PR #1209
2021-10-10 18:51:35 +01:00
LittleWhite-tb
5c3df3b218 Support for Roccat Kone EMP (#1131) 2021-10-07 16:00:36 +10:00
Filipe Laíns
62f26e797e Revert "data: Add (old) Logitech Performance Mouse MX"
This reverts commit d47f2ed946.

Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-07-24 22:25:38 +01:00
Filipe Laíns
d460743bd7 Revert "data: add additional identifier for MX Anywhere 2"
This reverts commit 2ce254e51b.

Signed-off-by: Filipe Laíns <lains@riseup.net>
2021-07-24 22:25:25 +01:00
strangemax
d47f2ed946 data: Add (old) Logitech Performance Mouse MX
PR #1146
2021-07-24 22:19:17 +01:00
João Ferreira
2ce254e51b data: add additional identifier for MX Anywhere 2
PR #1155
2021-07-24 22:18:19 +01:00
staticssleever668
c7c9c908e1 ratbagd-profile: allow report rates up to 8000 2021-07-24 22:17:19 +01:00
staticssleever668
b339370b6a ratbagd-profile: do not align report rates to 100 2021-07-24 22:17:19 +01:00
staticssleever668
37ae638a7c steelseries: improve v1 LED breathing duration configuration
SteelSeries mice using V1 driver don't support changing LED breathing
duration precisely, so it was always locked on 5 seconds. Now we try to
match desired duration with factory preset values: 3, 5 and 7 seconds.
2021-07-24 22:05:49 +01:00
staticssleever668
6a071dc1ee sinowealth: expose all eight resolution slots
It was limited to six because Glorious' software exposes only so many of
them, but SinoWealth mice firmware allows up to eight.
2021-07-24 22:05:16 +01:00
staticssleever668
7c9a75da9e sinowealth: add support for changing report rate 2021-07-24 22:04:55 +01:00
staticssleever668
dd1c72a1d0 sinowealth: add support for reading current report rate 2021-07-24 22:04:55 +01:00
staticssleever668
8d7cbcba4f steelseries: fix report rate changing for version 1 devices
Judging by rivalcfg's data for Sensei Raw, Rival 100, Rival (300) and Kinzu v2 (all are version 1 devices in our data), it should have been like that from the beginning.
Confirmed on Rival 100.
2021-07-24 22:04:14 +01:00
staticssleever668
4075227eb7 sinowealth: add support for changing active resolution number 2021-07-24 22:03:44 +01:00
Stephan Lachnit
abcec1d349 Register Roccat Kone Pure driver
Signed-off-by: Stephan Lachnit <stephanlachnit@protonmail.com>
2021-07-24 21:25:56 +01:00
Stephen Kitt
206bc5996d man: add both variants in ratbagctl.1 synopsis
PR #1188
2021-06-30 19:23:30 +01:00
Stephen Kitt
366f19abdd openinput: fix "Unknown" typo
PR #1187
2021-06-30 19:21:47 +01:00
69 changed files with 5428 additions and 1069 deletions

View File

@@ -1,269 +0,0 @@
libratbag_references:
build_dependencies: &build_dependencies
FEDORA_DEP_BUILD: gcc gcc-c++ meson dbus-daemon glib2-devel json-glib-devel libevdev-devel libudev-devel libunistring-devel python3-devel python3-evdev swig
FEDORA_DEP_TEST: check-devel python3-gobject python3-lxml valgrind diffutils
FEDORA_DEP_DOC: python3-sphinx python3-sphinx_rtd_theme
UBUNTU_DEP_BUILD: gcc g++ meson pkg-config systemd libevdev-dev libglib2.0-dev libjson-glib-dev libsystemd-dev libudev-dev libunistring-dev python3-dev python3-evdev swig
UBUNTU_DEP_TEST: check python3-gi python3-lxml valgrind
ARCH_DEP_BUILD: meson swig glib2 libevdev libudev.so libunistring json-glib python python-evdev python-gobject
ARCH_DEP_TEST: check valgrind python-lxml
default_settings: &default_settings
working_directory: ~/libratbag
environment:
LANG: C.UTF-8
build_default: &build_default
name: Build
command: |
rm -rf build
meson build ${MESON_PARAMS}
meson configure build
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
name: Build and test
environment:
NINJA_ARGS: test
build_with_docs: &build_with_docs
run:
<<: *build_default
name: Build with documentation
environment:
MESON_PARAMS: -Ddocumentation=true
install: &install
run:
name: Installing
command: ninja -C build install
check_install: &check_install
run:
name: Checking installation
command: |
PREFIX=/usr/local/
diff -u <(cd data/devices; ls *.device) <(cd $PREFIX/share/libratbag; ls *.device)
check_uninstall: &check_uninstall
run:
name: Checking if any files are left after uninstall
command: |
PREFIX=/root/test_install
meson build_install --prefix=$PREFIX
ninja -C build_install install
ninja -C build_install uninstall
if [ -d $PREFIX ]
then
tree $PREFIX
exit 1
fi
export_logs: &export_logs
store_artifacts:
path: ~/libratbag/build/meson-logs
start_dbus: &start_dbus
run:
name: Start dbus daemon
command: |
mkdir /run/dbus
/usr/bin/dbus-daemon --system --fork
save_date: &save_date
run:
name: Save date
command: date +%F > /tmp/date
fedora_save_cache: &fedora_save_cache
save_cache:
key: fedora-cache-v1-{{ checksum "/tmp/date" }}
paths:
- /var/cache/dnf
fedora_fetch_cache: &fedora_fetch_cache
restore_cache:
keys:
- fedora-cache-v1
fedora_install: &fedora_install
run:
name: Install prerequisites
command: |
echo keepcache=1 >> /etc/dnf/dnf.conf
dnf install -y git ${FEDORA_DEP_BUILD} ${FEDORA_DEP_TEST}
sed -i 's/systemd//' /etc/nsswitch.conf
fedora_settings: &fedora_settings
<<: *default_settings
steps:
- *save_date
- *fedora_fetch_cache
- *fedora_install
- checkout
- *start_dbus
- *build_and_test
- *install
- *check_install
- *check_uninstall
- *build_buildtype_plain
- *build_buildtype_release
- *export_logs
- *fedora_save_cache
environment:
*build_dependencies
arch_save_cache: &arch_save_cache
save_cache:
key: arch-cache-v1-{{ checksum "/tmp/date" }}
paths:
- /var/cache/pacman/pkg
arch_fetch_cache: &arch_fetch_cache
restore_cache:
keys:
- arch-cache-v1
arch_settings: &arch_settings
<<: *default_settings
steps:
- *save_date
- *arch_fetch_cache
- run:
name: Install prerequisites
command: pacman -Syu --noconfirm --noprogressbar --needed base-devel git tree ${ARCH_DEP_BUILD} ${ARCH_DEP_TEST}
- *start_dbus
- checkout
- *build_and_test
- *install
- *check_install
- *check_uninstall
- *export_logs
- *arch_save_cache
environment:
*build_dependencies
ubuntu_settings: &ubuntu_settings
<<: *default_settings
steps:
- run:
name: Install prerequisites
command: |
export DEBIAN_FRONTEND=noninteractive
apt-get update
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
steps:
- *fedora_fetch_cache
- *fedora_install
- run:
name: Install documentation build-deps
command: dnf install -y ${FEDORA_DEP_DOC}
- checkout
- *build_with_docs
- *export_logs
- store_artifacts:
path: ~/libratbag/build/doc/html
- persist_to_workspace:
root: build
paths:
- doc/html/*
environment:
*build_dependencies
docs_deploy: &docs_deploy
<<: *default_settings
steps:
- *fedora_fetch_cache
- run:
name: Install prerequisites
command: dnf install -y git tree
- checkout
- attach_workspace:
at: build
- run:
name: Setup the deploy SSH key
command: |
ssh-add -L | tail -n1 > ~/.ssh/deploy.pub
cat <<EOF > ~/.ssh/config
Host github.com
IdentityFile /root/.ssh/deploy.pub
IdentitiesOnly yes
EOF
- run:
name: Clone doc repository
command: |
cd ~
git clone ssh://git@github.com/${CIRCLE_PROJECT_REPONAME}/${CIRCLE_PROJECT_REPONAME}.github.io
- run:
name: Copy the doc, commit and push
command: |
set +e
cd ~/${CIRCLE_PROJECT_REPONAME}.github.io
\cp -r ~/${CIRCLE_PROJECT_REPONAME}/build/doc/html/* .
if ! git diff-index --quiet HEAD --; then
git config --global user.email "libratbag@librabtag.github.io"
git config --global user.name "The libratbag crew"
git add .
git commit -m "update docs from ${CIRCLE_SHA1}"
git push
fi
version: 2
jobs:
fedora_32:
<<: *fedora_settings
docker:
- image: fedora:32
arch:
<<: *arch_settings
docker:
- image: archlinux
ubuntu_20_04:
<<: *ubuntu_settings
docker:
- image: ubuntu:20.04
doc_build:
<<: *doc_build
docker:
- image: fedora:32
doc_deploy:
<<: *docs_deploy
docker:
- image: fedora:32
workflows:
version: 2
compile_and_test:
jobs:
- ubuntu_20_04
# - arch
- fedora_32
- doc_build
- doc_deploy:
requires:
- doc_build
filters:
branches:
only: master

39
.github/actions/meson/action.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
# A Github Action to build a repository with meson
name: "Setup and run meson"
description: "Setup and run meson"
inputs:
builddir:
description: "The build directory"
default: "builddir"
srcdir:
description: "The source directory"
default: "."
meson_args:
description: "Arguments passed to meson setup"
default: ""
meson_skip_test:
description: "Set to a nonempty string to skip meson test"
default: ""
meson_test_args:
description: "Arguments passed to meson test"
default: ""
meson_precmd:
description: "Set to 'sudo' to run with sudo"
default: ""
ninja_args:
description: "Arguments passed to ninja"
default: ""
ninja_precmd:
description: "Set to 'sudo' to run with sudo"
default: ""
runs:
using: "composite"
steps:
- run: |
${{inputs.meson_precmd}} meson setup ${{inputs.builddir}} ${{inputs.srcdir}} ${{inputs.meson_args}}
${{inputs.meson_precmd}} meson configure ${{inputs.builddir}}
${{inputs.ninja_precmd}} ninja -C ${{inputs.builddir}} ${{inputs.ninja_args}}
if [[ -z "${{inputs.meson_skip_test}}" ]]; then
${{inputs.meson_precmd}} meson test -C ${{inputs.builddir}} --print-errorlogs ${{inputs.meson_test_args}};
fi
shell: bash

17
.github/actions/pkginstall/action.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
name: "Install packages"
description: "Install a set of packages from sources"
inputs:
apt:
description: "The package list to install with apt"
pip:
description: "The package list to install with pip"
pip_precmd:
description: "Set to the string 'sudo' to run through sudo"
default: ""
runs:
using: "composite"
steps:
- run: sudo ${{ github.action_path }}/install-apt.sh ${{ inputs.apt }}
shell: bash
- run: ${{inputs.pip_precmd}} ${{ github.action_path }}/install-pip.sh ${{ inputs.pip }}
shell: bash

14
.github/actions/pkginstall/install-apt.sh vendored Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/bash -x
# If called without arguments, just skip the rest
if [[ -z "$@" ]]; then
exit
fi
# Don't care about these bits
echo 'path-exclude=/usr/share/doc/*' > /etc/dpkg/dpkg.cfg.d/99-exclude-cruft
echo 'path-exclude=/usr/share/locale/*' >> /etc/dpkg/dpkg.cfg.d/99-exclude-cruft
echo 'path-exclude=/usr/share/man/*' >> /etc/dpkg/dpkg.cfg.d/99-exclude-cruft
apt-get update
apt-get install -yq --no-install-suggests --no-install-recommends $@

9
.github/actions/pkginstall/install-pip.sh vendored Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash -x
# If called without arguments, just skip the rest
if [[ -z "$@" ]]; then
exit
fi
python -m pip install --upgrade pip
python -m pip install --upgrade "$@"

50
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: linux
on: [ push, pull_request ]
env:
UBUNTU_DEP_BUILD: |
gcc g++ pkg-config systemd libevdev-dev libglib2.0-dev
libjson-glib-dev libsystemd-dev libudev-dev libunistring-dev
python3-dev python3-evdev swig python3-sphinx
UBUNTU_DEP_TEST: check python3-gi python3-lxml valgrind
PIP_PACKAGES: meson ninja libevdev pyudev pytest sphinx_rtd_theme
jobs:
build-and-dist:
runs-on: ubuntu-20.04
strategy:
matrix:
meson_args:
- ''
- '-Dbuildtype=plain'
- '-Dbuildtype=release'
- '-Ddocumentation=true'
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/pkginstall
with:
apt: $UBUNTU_DEP_BUILD $UBUNTU_DEP_TEST
pip: $PIP_PACKAGES
- name: meson test ${{matrix.meson_args}}
uses: ./.github/actions/meson
with:
meson_args: --prefix=$PWD/_instdir ${{matrix.meson_args}}
ninja_args: install
- name: check installation of data files
run: diff -u <(cd data/devices; ls *.device) <(cd _instdir/share/libratbag; ls *.device)
- name: ninja uninstall
uses: ./.github/actions/meson
with:
meson_args: --prefix=$PWD/_instdir
ninja_args: uninstall
- name: check if any files are left after uninstall
run: (test -d _instdir && tree _instdir && exit 1) || exit 0
# Capture all the meson logs, even if we failed
- uses: actions/upload-artifact@v3
if: ${{ always() }} # even if we fail
with:
name: meson test logs
path: |
builddir/meson-logs/testlog*.txt
builddir/meson-logs/meson-log.txt

31
.github/workflows/pages.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: pages
on:
push:
branches:
- master
env:
UBUNTU_DEP_BUILD: gcc g++ pkg-config systemd libevdev-dev libglib2.0-dev libjson-glib-dev libsystemd-dev libudev-dev libunistring-dev python3-dev python3-evdev python3-pip swig python3-sphinx python3-sphinx-rtd-theme
PIP_PACKAGES: meson ninja
jobs:
deploy:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/pkginstall
with:
apt: $UBUNTU_DEP_BUILD
pip: $PIP_PACKAGES
- name: Build docs
uses: ./.github/actions/meson
with:
meson_args: -Ddocumentation=true -Dtests=false
meson_skip_test: yes
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./builddir/doc/html/
allow_empty_commit: true

View File

@@ -45,6 +45,10 @@ And to build or re-build after code-changes, run:
ninja -C builddir
sudo ninja -C builddir install
To remove/uninstal simply run:
sudo ninja -C builddir uninstall
Note: `builddir` is the build output directory and can be changed to any
other directory name. To set configure-time options, use e.g.
@@ -169,5 +173,3 @@ libratbag is licensed under the MIT license.
> Software is furnished to do so, subject to the following conditions: [...]
See the COPYING file for the full license information.
[![Build Status](https://circleci.com/gh/libratbag/libratbag.svg?style=shield&circle-token=d7c782e10d2d934b176da754f11b5105ea074f4a)](https://circleci.com/gh/libratbag/libratbag)

View File

@@ -0,0 +1,11 @@
[Device]
Name=ASUS ROG GX860 Buzzard Mouse
DeviceMatch=usb:0b05:1816
Driver=asus
[Driver/asus]
Profiles=3
Buttons=10
Leds=0
Dpis=2
DpiRange=100:8200@100

View File

@@ -0,0 +1,13 @@
[Device]
Name=ASUS ROG Chakram
DeviceMatch=usb:0b05:18e3;usb:0b05:18e5
Driver=asus
[Driver/asus]
Profiles=3
Buttons=5
Leds=3
Dpis=4
Wireless=1
DpiRange=100:16000@100
Quirks=DOUBLE_DPI;STRIX_PROFILE

View File

@@ -0,0 +1,13 @@
[Device]
Name=ASUS ROG Gladius II Origin PNK LTD
DeviceMatch=usb:0b05:18cd
Driver=asus
[Driver/asus]
Profiles=3
Buttons=8
Leds=3
Dpis=2
DpiRange=100:12000@100
ButtonMapping=0;1;2;7;8;5;3;4
Quirks=DOUBLE_DPI

View File

@@ -0,0 +1,14 @@
# "ASUS ROG Gladius II" without "DPI target button"
[Device]
Name=ASUS ROG Gladius II Origin
DeviceMatch=usb:0b05:1877
Driver=asus
[Driver/asus]
Profiles=3
Buttons=8
Leds=3
Dpis=2
DpiRange=100:12000@100
ButtonMapping=0;1;2;7;8;5;3;4
Quirks=DOUBLE_DPI

View File

@@ -0,0 +1,12 @@
[Device]
Name=ASUS ROG Gladius II
DeviceMatch=usb:0b05:1845
Driver=asus
[Driver/asus]
Profiles=3
Buttons=8
Leds=3
Dpis=2
DpiRange=100:12000@100
ButtonMapping=0;1;2;7;8;5;3;4

View File

@@ -0,0 +1,13 @@
[Device]
Name=ASUS ROG Keris Wireless
DeviceMatch=usb:0b05:195e;usb:0b05:1960
Driver=asus
[Driver/asus]
Profiles=3
Buttons=8
Leds=2
Dpis=4
Wireless=1
DpiRange=100:16000@100
Quirks=DOUBLE_DPI

View File

@@ -0,0 +1,14 @@
[Device]
Name=ASUS ROG Pugio
DeviceMatch=usb:0b05:1846
Driver=asus
[Driver/asus]
Profiles=3
Buttons=10
Leds=3
Dpis=2
DpiRange=100:7200@100
# Mappings the same as the Gladius II but with an extra 2 buttons
ButtonMapping=0;1;2;7;8;5;3;4;9;10

View File

@@ -0,0 +1,13 @@
[Device]
Name=ASUS ROG Strix Carry
DeviceMatch=usb:0b05:18b4
Driver=asus
[Driver/asus]
Profiles=3
Buttons=8
Leds=0
Dpis=2
Wireless=1
DpiRange=100:7200@100
Quirks=STRIX_PROFILE

View File

@@ -0,0 +1,11 @@
[Device]
Name=ASUS ROG Strix Impact
DeviceMatch=usb:0b05:1847
Driver=asus
[Driver/asus]
Profiles=0
Buttons=6
Leds=1
Dpis=2
DpiRange=100:5000@100

View File

@@ -0,0 +1,12 @@
[Device]
Name=ASUS ROG Strix Impact II Wireless
DeviceMatch=usb:0b05:1947;usb:0b05:1949
Driver=asus
[Driver/asus]
Profiles=3
Buttons=8
Leds=3
Dpis=4
Wireless=1
DpiRange=100:16000@100

View File

@@ -52,3 +52,29 @@ Leds=1
# The HID++ 2.0 device index
# Should be unset unless required
# DeviceIndex=1
[Driver/asus]
# Number of profiles
Profiles=3
# Number of buttons
Buttons=8
# Number of LEDs
Leds=3
# Number of DPI presets
Dpis=2
# Set if device is wireless
# Wireless=1
# DPI range in format min:max@step
DpiRange=100:16000@100
# Button index mapping
# ButtonMapping=0;1;2;6;7;5;3;4
# Device quirks
# Quirk=DOUBLE_DPI;STRIX_PROFILE

View File

@@ -3,3 +3,9 @@ Name=Glorious Model D
DeviceMatch=usb:258a:0033
Driver=sinowealth
[Driver/sinowealth/devices/GloriousModelD]
ButtonCount=6
DeviceName=Glorious Model D
FwVersion=V102
LedType=RBG
SensorType=PMW3360

View File

@@ -1,5 +1,11 @@
[Device]
Name=Glorious Model O
Name=Glorious Model O/O-
DeviceMatch=usb:258a:0036
Driver=sinowealth
[Driver/sinowealth/devices/GloriousModelO]
ButtonCount=6
DeviceName=Glorious Model O/O- (updated firmware)
FwVersion=V103
LedType=RBG
SensorType=PMW3360

View File

@@ -0,0 +1,4 @@
# Logitech MX Anywhere 3B
[Device]
Name=Logitech MX Anywhere 3B
DeviceMatch=bluetooth:046d:b02d

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech G502 X Wireless
DeviceMatch=usb:046d:c098
Driver=hidpp20

View File

@@ -0,0 +1,4 @@
[Device]
Name=Logitech G502 X
DeviceMatch=usb:046d:c099
Driver=hidpp20

View File

@@ -0,0 +1,8 @@
[Device]
Name=Logitech G635
DeviceMatch=usb:046d:0a89
LedTypes=logo;side
Driver=hidpp20
[Driver/hidpp20]
Buttons=0

View File

@@ -0,0 +1,7 @@
[Device]
Name=Logitech G733 Gaming Headset
DeviceMatch=usb:046d:0ab5
Driver=hidpp20
[Driver/hidpp20]
Buttons=0

View File

@@ -0,0 +1,4 @@
[Device]
Name=Roccat Kone EMP
DeviceMatch=usb:1e7d:2e24
Driver=roccat-kone-emp

View File

@@ -0,0 +1,38 @@
[Device]
DeviceMatch=usb:258a:0027
Driver=sinowealth
Name=SinoWealth Generic Mouse (0027)
[Driver/sinowealth/devices/DreamMachinesDM5Blink]
ButtonCount=8
DeviceName=DreamMachines DM5 Blink
FwVersion=3106
LedType=RGB
SensorType=PMW3389
[Driver/sinowealth/devices/GWolvesHatiHTM]
ButtonCount=6
# Can be both `Ace` and `Classic` version.
DeviceName=G-Wolves Hati HT-M Wired
FwVersion=V161
LedType=None
# Can also be PMW3389
SensorType=PMW3360
[Driver/sinowealth/devices/GenesysXenon770]
# There are 5 main buttons.
# Side buttons are detachable.
# There can be either 3 or 9 buttons on the side.
# Going for the maximum count: 5 + 9 = 14.
ButtonCount=14
DeviceName=Genesys Xenon 770
FwVersion=3110
LedType=RGB
SensorType=PMW3327
[Driver/sinowealth/devices/GloriousModelOOld]
ButtonCount=6
DeviceName=Glorious Model O (old firmware)
FwVersion=V102
LedType=RBG
SensorType=PMW3360

View File

@@ -0,0 +1,12 @@
[Device]
DeviceMatch=usb:258a:0029
Driver=sinowealth
Name=SinoWealth Generic Mouse (0029)
# Configuration data size is 131 bytes.
[Driver/sinowealth/devices/MachenikeM620]
ButtonCount=6
DeviceName=Machenike M620
FwVersion=V127
LedType=RGB
SensorType=PMW3389

View File

@@ -0,0 +1,20 @@
[Device]
DeviceMatch=usb:258a:1007
Driver=sinowealth
Name=SinoWealth Generic Mouse (1007)
# This does NOT work. See libratbag/libratbag#1296.
# For future: ID of A3050 sensor is 0x2, and maximum DPI is 4000.
#[Driver/sinowealth/devices/AntEsportsGM500]
#ButtonCount=7
#DeviceName=Ant Esports GM500
#FwVersion=0910
#LedType=RBG
#SensorType=A3050
[Driver/sinowealth/devices/MarvoScorpionG961]
ButtonCount=6
DeviceName=Marvo Scorpion G961
FwVersion=9677
LedType=RGB
SensorType=PMW3327

View File

@@ -0,0 +1,13 @@
[Device]
# 100; 100 (Dell China); 100 Dota 2 Edition (retail); 100 Dota 2 Edition (Lenovo); 105.
DeviceMatch=usb:1038:1702;usb:1038:170a;usb:1038:170b;usb:1038:170c;usb:1038:1814
Driver=steelseries
Name=SteelSeries Rival 100/105
[Driver/steelseries]
# Actually 6.
Buttons=0
DeviceVersion=1
DpiList=250;500;1000;1250;1500;1750;2000;4000
Leds=1
Quirk=Rival100

View File

@@ -1,6 +1,6 @@
[Device]
Name=SteelSeries Rival
DeviceMatch=usb:1038:1384
DeviceMatch=usb:1038:1384;usb:1038:1392;usb:1038:1710;usb:1038:1712;usb:1038:171c;usb:1038:1394;usb:1038:171a;usb:1038:1716;usb:1038:1714;usb:1038:1718
Driver=steelseries
[Driver/steelseries]

View File

@@ -10,5 +10,4 @@ Buttons=8
Leds=1
DpiRange=90:5670@90
MacroLength=1
MonoLed=1
ShortButton=1
Quirk=SenseiRAW

View File

@@ -1,8 +1,8 @@
project('libratbag', 'c',
version : '0.16',
version : '0.17',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
meson_version : '>= 0.40.0')
meson_version : '>= 0.50.0')
# The DBus API version. Increase this every time the DBus API changes.
# No backwards/forwards guarantee, clients are expected to understand
@@ -121,6 +121,18 @@ lib_libhidpp = static_library('hidpp',
dependencies : deps_libhidpp)
dep_libhidpp = declare_dependency(link_with: lib_libhidpp)
### libasus.a ####
src_libasus = [
'src/asus.c',
]
deps_libasus = [ ]
lib_libasus = static_library('asus',
src_libasus,
dependencies : deps_libasus)
dep_libasus = declare_dependency(link_with: lib_libasus)
### liblur ####
#
# liblur is the library to handle logitech unifying receivers.
@@ -135,6 +147,7 @@ src_liblur = [
deps_liblur = [
dep_libutil,
dep_libhidpp,
dep_libasus,
]
lur_mapfile = 'src/liblur.sym'
@@ -166,17 +179,21 @@ pkgconfig.generate (
src_libratbag = [
'src/libratbag-enums.h',
'src/libratbag.h',
'src/driver-asus.c',
'src/driver-etekcity.c',
'src/driver-hidpp20.c',
'src/driver-hidpp10.c',
'src/driver-logitech-g300.c',
'src/driver-logitech-g600.c',
'src/driver-roccat.c',
'src/driver-roccat-kone-emp.c',
'src/driver-roccat-kone-pure.c',
'src/driver-gskill.c',
'src/driver-openinput.c',
'src/driver-steelseries.c',
'src/driver-steelseries.h',
'src/driver-sinowealth.c',
'src/driver-sinowealth.h',
'src/driver-sinowealth-nubwo.c',
'src/driver-test.c',
'src/libratbag.c',
@@ -198,6 +215,7 @@ deps_libratbag = [
dep_json_glib,
dep_libutil,
dep_libhidpp,
dep_libasus,
]
lib_libratbag = static_library('ratbag',
@@ -279,99 +297,19 @@ man_lur_command = configure_file (
)
#### data files ####
# list ordered by git ls-files data/devices/*.device
data_files = files(
'data/devices/etekcity-scroll-alpha.device',
'data/devices/gskill-MX-780.device',
'data/devices/logitech-M325.device',
'data/devices/logitech-M500s.device',
'data/devices/logitech-M545.device',
'data/devices/logitech-M570.device',
'data/devices/logitech-M585-M590.device',
'data/devices/logitech-M705.device',
'data/devices/logitech-M720.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-x-wireless-superlight.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',
'data/devices/logitech-g403.device',
'data/devices/logitech-g5-2007.device',
'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.device',
'data/devices/logitech-g703-hero.device',
'data/devices/logitech-g703.device',
'data/devices/logitech-g9.device',
'data/devices/logitech-g900.device',
'data/devices/logitech-g903-hero.device',
'data/devices/logitech-g903.device',
'data/devices/logitech-g815.device',
'data/devices/logitech-g910.device',
'data/devices/logitech-g915.device',
'data/devices/logitech-g915-tkl.device',
'data/devices/logitech-g935.device',
'data/devices/logitech-g9x-Call-of-Duty-MW3-Edition.device',
'data/devices/logitech-g9x-Original.device',
'data/devices/logitech-Marathon-M705.device',
'data/devices/logitech-MX-Anywhere2.device',
'data/devices/logitech-MX-Anywhere2S.device',
'data/devices/logitech-MX-Anywhere-3.device',
'data/devices/logitech-MX-Vertical.device',
'data/devices/openinput.device',
'data/devices/roccat-kone-pure.device',
'data/devices/roccat-kone-xtd.device',
'data/devices/steelseries-kinzu-v2.device',
'data/devices/steelseries-kinzu-v2-pro.device',
'data/devices/steelseries-kinzu-v3.device',
'data/devices/steelseries-rival-310.device',
'data/devices/steelseries-rival-600.device',
'data/devices/steelseries-rival.device',
'data/devices/steelseries-sensei-310.device',
'data/devices/steelseries-sensei-raw.device',
'data/devices/glorious-model-d.device',
'data/devices/glorious-model-o.device',
'data/devices/nubwo-x7-spectrum.device',
)
install_subdir('data/devices',
strip_directory : true,
exclude_files : ['device.example', 'README.md'],
install_dir : join_paths(get_option('datadir'), 'libratbag'))
install_data(data_files,
install_dir : join_paths(get_option('datadir'), 'libratbag'))
data_parse_test = find_program(join_paths(meson.source_root(), 'test/data-parse-test.py'))
test('data-parse-test', data_parse_test, args: 'data/devices')
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(), 'test/duplicate-check.py'))
test('duplicate-test', duplicate_test, args : 'data/devices')
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)
receiver_id_test = find_program(join_paths(meson.source_root(), 'test/receiver-check.py'))
test('receiver-id-test', receiver_id_test, args : 'data/devices')
#### tests ####
enable_tests = get_option('tests')
@@ -407,16 +345,18 @@ if enable_tests
test('test-util', test_util)
test('test-iconv-helper', test_iconv_helper)
valgrind = find_program('valgrind')
valgrind_suppressions_file = join_paths(meson.source_root(), 'test', 'valgrind.suppressions')
add_test_setup('valgrind',
exe_wrapper : [
valgrind,
'--leak-check=full',
'--quiet',
'--error-exitcode=3',
'--suppressions=' + valgrind_suppressions_file ],
timeout_multiplier: 5)
valgrind = find_program('valgrind', required : false)
if valgrind.found()
valgrind_suppressions_file = join_paths(meson.source_root(), 'test', 'valgrind.suppressions')
add_test_setup('valgrind',
exe_wrapper : [
valgrind,
'--leak-check=full',
'--quiet',
'--error-exitcode=3',
'--suppressions=' + valgrind_suppressions_file ],
timeout_multiplier: 5)
endif
endif
#### ratbagd ####
@@ -464,10 +404,9 @@ install_man('ratbagd/ratbagd.8')
# (org.freedesktop.ratbag_devel1). This server is used by ratbagdctl.devel.
#
config_ratbagd_devel = configuration_data()
dbus_devel_policy = configure_file(input : 'dbus/org.freedesktop.ratbag_devel1.conf.in',
output : 'org.freedesktop.ratbag_devel1.conf',
configuration : config_ratbagd_devel)
copy: true)
# This is a hack. We always install the devel policy file into
# /etc/dbus-1/system.d, independent of any prefixes we use otherwise.
@@ -555,18 +494,12 @@ configure_file(input : 'dbus/org.freedesktop.ratbag1.conf.in',
# This is a special idiomatic construct in meson to get a fast dependency that
# doesn't exist and isn't logged as "not found".
dep_python3 = dependency('', required : false)
if meson.version().version_compare('<0.48.0')
python = import('python3')
py3 = python.find_python()
py_version = python.language_version()
else
pymod = import('python')
py3 = pymod.find_installation()
if meson.version().version_compare('>= 0.53.0')
dep_python3 = py3.dependency(embed : true)
endif
py_version = py3.language_version()
pymod = import('python')
py3 = pymod.find_installation()
if meson.version().version_compare('>= 0.53.0')
dep_python3 = py3.dependency(embed : true)
endif
py_version = py3.language_version()
# From python 3.8 we neeed python3-embed
embed = py_version.version_compare('>= 3.8') ? '-embed' : ''

View File

@@ -241,7 +241,7 @@ special_lookup(const char *string)
{ "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-cycle-down", 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},

View File

@@ -507,10 +507,8 @@ ratbagd_profile_set_report_rate(sd_bus *bus,
return r;
/* basic sanity check */
if (rate > 5000)
rate = 5000;
else if (rate % 100)
rate = rate - (rate % 100);
if (rate > 8000)
rate = 8000;
r = ratbag_profile_set_report_rate(profile->lib_profile, rate);
if (r == 0) {

View File

@@ -614,7 +614,7 @@ static void remove_ratbagd_devel_dbus_policy(void)
int main(int argc, char *argv[])
{
struct ratbagd *ctx = NULL;
int r;
int r = 0;
#if DISABLE_COREDUMP
const struct rlimit corelimit = { 0, 0 };

496
src/asus.c Normal file
View File

@@ -0,0 +1,496 @@
/*
* Copyright (C) 2021 Kyoken, kyoken@kyoken.ninja
*
* 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 "asus.h"
#include <assert.h>
#include "libratbag-data.h"
#include "libratbag-private.h"
/* ASUS commands */
#define ASUS_CMD_GET_LED_DATA 0x0312 /* get all LEDs */
#define ASUS_CMD_GET_SETTINGS 0x0412 /* dpi, rate, button response, angle snapping */
#define ASUS_CMD_GET_BUTTON_DATA 0x0512 /* get all buttons */
#define ASUS_CMD_GET_PROFILE_DATA 0x0012 /* get current profile info */
#define ASUS_CMD_SET_LED 0x2851 /* set single led */
#define ASUS_CMD_SET_SETTING 0x3151 /* dpi / rate / button response / angle snapping */
#define ASUS_CMD_SET_BUTTON 0x2151 /* set single button */
#define ASUS_CMD_SET_PROFILE 0x0250 /* switch profile */
#define ASUS_CMD_SAVE 0x0350 /* save settings */
/* fields order in _asus_dpiX_data, used for setting with ASUS_CMD_SET_SETTING */
#define ASUS_FIELD_RATE 0
#define ASUS_FIELD_RESPONSE 1
#define ASUS_FIELD_SNAPPING 2
/* order is mandatory because index is mapped from ButtonMapping configuration property */
static struct asus_button ASUS_BUTTON_MAPPING[] = {
{ 0xf0, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 1, 0 }, /* left */
{ 0xf1, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 2, 0 }, /* right (button 3 in xev) */
{ 0xf2, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 3, 0 }, /* middle (button 2 in xev) */
{ 0xe4, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 4, 0 }, /* backward (on left side) */
{ 0xe5, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 5, 0 }, /* forward (on left side) */
{ 0xe6, RATBAG_BUTTON_ACTION_TYPE_SPECIAL, 0, RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP },
{ 0xe8, RATBAG_BUTTON_ACTION_TYPE_SPECIAL, 0, RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP },
{ 0xe9, RATBAG_BUTTON_ACTION_TYPE_SPECIAL, 0, RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN },
{ 0xe1, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 4, 0 }, /* backward on right side */
{ 0xe2, RATBAG_BUTTON_ACTION_TYPE_BUTTON, 5, 0 }, /* forward on right side */
{ 0xff, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* placeholder */
{ 0xff, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* placeholder */
{ 0xff, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* placeholder */
{ 0xff, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* placeholder */
{ 0xff, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* placeholder */
{ 0xff, RATBAG_BUTTON_ACTION_TYPE_NONE, 0, 0 }, /* placeholder */
};
/* key mapping, the index is actual ASUS code */
static unsigned char ASUS_KEY_MAPPING[] = {
/* 00 */ 0, 0, 0, 0,
/* 04 */ KEY_A, KEY_B, KEY_C, KEY_D,
/* 08 */ KEY_E, KEY_F, KEY_G, KEY_H,
/* 0C */ KEY_I, KEY_J, KEY_K, KEY_L,
/* 0E */ KEY_M, KEY_N, KEY_O, KEY_P,
/* 14 */ KEY_Q, KEY_R, KEY_S, KEY_T,
/* 18 */ KEY_U, KEY_V, KEY_W, KEY_X,
/* 1C */ KEY_Y, KEY_Z, KEY_1, KEY_2,
/* 1E */ KEY_3, KEY_4, KEY_5, KEY_6,
/* 24 */ KEY_7, KEY_8, KEY_9, KEY_0,
/* 28 */ KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB,
/* 2C */ KEY_SPACE, KEY_MINUS, KEY_KPPLUS, 0,
/* 2E */ 0, 0, 0, 0,
/* 34 */ 0, KEY_GRAVE, KEY_EQUAL, 0,
/* 38 */ KEY_SLASH, 0, KEY_F1, KEY_F2,
/* 3C */ KEY_F3, KEY_F4, KEY_F5, KEY_F6,
/* 3E */ KEY_F7, KEY_F8, KEY_F9, KEY_F10,
/* 44 */ KEY_F11, KEY_F12, 0, 0,
/* 48 */ 0, 0, KEY_HOME, KEY_PAGEUP,
/* 4C */ KEY_DELETE, 0, KEY_PAGEDOWN, KEY_RIGHT,
/* 4E */ KEY_LEFT, KEY_DOWN, KEY_UP, 0,
/* 54 */ 0, 0, 0, 0,
/* 58 */ 0, KEY_KP1, KEY_KP2, KEY_KP3,
/* 5C */ KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7,
/* 5E */ KEY_KP8, KEY_KP9, 0,
};
static unsigned int ASUS_LED_TYPE[] = {
RATBAG_LED_TYPE_LOGO, /* Mouse logo */
RATBAG_LED_TYPE_WHEEL, /* Mouse wheel */
RATBAG_LED_TYPE_SIDE, /* Bottom of the mouse */
};
static unsigned int ASUS_POLLING_RATES[] = { 125, 250, 500, 1000 };
/* search for ASUS button by ratbag types */
struct asus_button *
asus_find_button_by_action(struct ratbag_button_action action)
{
struct asus_button *asus_button;
for (unsigned int i = 0; i < ARRAY_LENGTH(ASUS_BUTTON_MAPPING); i++) {
asus_button = &ASUS_BUTTON_MAPPING[i];
if ((action.type == RATBAG_BUTTON_ACTION_TYPE_BUTTON && asus_button->button == action.action.button) ||
(action.type == RATBAG_BUTTON_ACTION_TYPE_SPECIAL && asus_button->special == action.action.special))
return asus_button;
}
return NULL;
}
/* search for ASUS button by ASUS button code */
struct asus_button *
asus_find_button_by_code(uint8_t asus_code)
{
for (unsigned int i = 0; i < ARRAY_LENGTH(ASUS_BUTTON_MAPPING); i++) {
if (ASUS_BUTTON_MAPPING[i].asus_code == asus_code)
return &ASUS_BUTTON_MAPPING[i];
}
return NULL;
}
/* search for ASUS key code by Linux key code */
int
asus_find_key_code(unsigned int linux_code)
{
for (unsigned int i = 0; i < ARRAY_LENGTH(ASUS_KEY_MAPPING); i++) {
if (ASUS_KEY_MAPPING[i] == linux_code)
return i;
}
return -1;
}
/* convert ASUS key code to Linux key code */
int
asus_get_linux_key_code(uint8_t asus_code) {
assert(asus_code <= ARRAY_LENGTH(ASUS_KEY_MAPPING));
return ASUS_KEY_MAPPING[asus_code];
}
int
asus_query(struct ratbag_device *device,
union asus_request *request, union asus_response *response)
{
int rc;
rc = ratbag_hidraw_output_report(device, request->raw, ASUS_PACKET_SIZE);
if (rc < 0)
return rc;
memset(response, 0, sizeof(union asus_response));
rc = ratbag_hidraw_read_input_report(device, response->raw, ASUS_PACKET_SIZE, NULL);
if (rc < 0)
return rc;
/* invalid state, disconnected or sleeping */
if (response->data.code == 0xaaff) {
return ASUS_STATUS_ERROR;
}
return 0;
}
void
asus_setup_profile(struct ratbag_device *device, struct ratbag_profile *profile)
{
ratbag_profile_set_report_rate_list(
profile, ASUS_POLLING_RATES, ARRAY_LENGTH(ASUS_POLLING_RATES));
}
void
asus_setup_button(struct ratbag_device *device, struct ratbag_button *button)
{
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY);
}
void
asus_setup_resolution(struct ratbag_device *device, struct ratbag_resolution *resolution)
{
const struct dpi_range *dpirange = ratbag_device_data_asus_get_dpi_range(device->data);
if (!dpirange)
return;
ratbag_resolution_set_dpi_list_from_range(
resolution, dpirange->min, dpirange->max);
}
void
asus_setup_led(struct ratbag_device *device, struct ratbag_led *led)
{
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
led->type = ASUS_LED_TYPE[led->index];
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
}
int
asus_save_profile(struct ratbag_device *device)
{
int rc;
union asus_response response;
union asus_request request = {
.data.cmd = ASUS_CMD_SAVE,
};
rc = asus_query(device, &request, &response);
if (rc)
return rc;
return 0;
}
int
asus_get_profile_data(struct ratbag_device *device, struct asus_profile_data *data)
{
int rc;
uint32_t quirks = ratbag_device_data_asus_get_quirks(device->data);
union asus_response response;
union asus_request request = {
.data.cmd = ASUS_CMD_GET_PROFILE_DATA,
};
rc = asus_query(device, &request, &response);
if (rc)
return rc;
if (quirks & ASUS_QUIRK_STRIX_PROFILE)
data->profile_id = response.data.results[7];
else
data->profile_id = response.data.results[8];
data->version_primary_major = response.data.results[13];
data->version_primary_minor = response.data.results[12];
data->version_primary_build = response.data.results[11];
data->version_secondary_major = response.data.results[4];
data->version_secondary_minor = response.data.results[3];
data->version_secondary_build = response.data.results[2];
return 0;
}
int
asus_set_profile(struct ratbag_device *device, unsigned int index)
{
int rc;
union asus_response response;
union asus_request request = {
.data.cmd = ASUS_CMD_SET_PROFILE,
.data.params[0] = index,
};
rc = asus_query(device, &request, &response);
if (rc)
return rc;
return 0;
}
/* read button bindings using ButtonMapping configuration property */
int
asus_get_binding_data(struct ratbag_device *device, union asus_binding_data *data)
{
int rc;
union asus_response response;
union asus_request request = {
.data.cmd = ASUS_CMD_GET_BUTTON_DATA,
};
rc = asus_query(device, &request, &response);
if (rc)
return rc;
memcpy(data->raw, response.raw, sizeof(data->raw));
return 0;
}
/* set button binding using ASUS code of the button */
int
asus_set_button_action(struct ratbag_device *device, unsigned int asus_index, uint8_t asus_code, uint8_t asus_type)
{
int rc;
union asus_response response;
union asus_request request = {
.data.cmd = ASUS_CMD_SET_BUTTON,
};
/* source (physical mouse button) */
request.data.params[2] = ASUS_BUTTON_MAPPING[asus_index].asus_code;
request.data.params[3] = ASUS_BUTTON_ACTION_TYPE_BUTTON;
/* destination (mouse button or keyboard key action) */
request.data.params[4] = asus_code;
request.data.params[5] = asus_type;
rc = asus_query(device, &request, &response);
if (rc)
return rc;
return 0;
}
int
asus_get_resolution_data(struct ratbag_device *device, union asus_resolution_data *data)
{
int rc;
uint32_t quirks = ratbag_device_data_asus_get_quirks(device->data);
union asus_response response;
unsigned int dpi_count = ratbag_device_get_profile(device, 0)->num_resolutions;
unsigned int i;
union asus_request request = {
.data.cmd = ASUS_CMD_GET_SETTINGS,
};
rc = asus_query(device, &request, &response);
if (rc)
return rc;
memcpy(data->raw, response.raw, sizeof(data->raw));
/* convert DPI rates */
switch (dpi_count) {
case 2: /* 2 DPI presets */
for (i = 0; i < dpi_count; i++) {
data->data2.dpi[i] = data->data2.dpi[i] * 50 + 50;
if (quirks & ASUS_QUIRK_DOUBLE_DPI)
data->data2.dpi[i] *= 2;
}
data->data2.rate = ASUS_POLLING_RATES[data->data2.rate];
break;
case 4: /* 4 DPI presets */
for (i = 0; i < dpi_count; i++) {
data->data4.dpi[i] = data->data4.dpi[i] * 50 + 50;
if (quirks & ASUS_QUIRK_DOUBLE_DPI)
data->data4.dpi[i] *= 2;
}
data->data4.rate = ASUS_POLLING_RATES[data->data4.rate];
break;
default:
break;
}
return 0;
}
/* set DPI for the specified preset */
int
asus_set_dpi(struct ratbag_device *device, unsigned int index, unsigned int dpi)
{
int rc;
uint32_t quirks = ratbag_device_data_asus_get_quirks(device->data);
union asus_response response;
unsigned int idpi;
idpi = dpi;
if (quirks & ASUS_QUIRK_DOUBLE_DPI)
idpi /= 2;
union asus_request request = {
.data.cmd = ASUS_CMD_SET_SETTING,
.data.params[0] = index,
.data.params[2] = (idpi - 50) / 50,
};
rc = asus_query(device, &request, &response);
if (rc)
return rc;
return 0;
}
/* set polling rate in Hz */
int
asus_set_polling_rate(struct ratbag_device *device, unsigned int hz)
{
int rc;
union asus_response response;
unsigned int dpi_count = ratbag_device_get_profile(device, 0)->num_resolutions;
unsigned int i;
union asus_request request = {
.data.cmd = ASUS_CMD_SET_SETTING,
.data.params[0] = dpi_count + ASUS_FIELD_RATE, /* field index to set */
};
for (i = 0; i < ARRAY_LENGTH(ASUS_POLLING_RATES); i++) {
if (ASUS_POLLING_RATES[i] == hz) {
request.data.params[2] = i;
break;
}
}
rc = asus_query(device, &request, &response);
if (rc)
return rc;
return 0;
}
/* set button response/debounce in ms (from 4 to 32 with step of 4) */
int
asus_set_button_response(struct ratbag_device *device, unsigned int ms)
{
assert(ms >= 4);
int rc;
unsigned int dpi_count = ratbag_device_get_profile(device, 0)->num_resolutions;
union asus_response response;
union asus_request request = {
.data.cmd = ASUS_CMD_SET_SETTING,
.data.params[0] = dpi_count + ASUS_FIELD_RESPONSE, /* field index to set */
.data.params[2] = ms / 4 - 1,
};
rc = asus_query(device, &request, &response);
if (rc)
return rc;
return 0;
}
int
asus_set_angle_snapping(struct ratbag_device *device, bool is_enabled)
{
int rc;
union asus_response response;
unsigned int dpi_count = ratbag_device_get_profile(device, 0)->num_resolutions;
union asus_request request = {
.data.cmd = ASUS_CMD_SET_SETTING,
.data.params[0] = dpi_count + ASUS_FIELD_SNAPPING, /* field index to set */
.data.params[2] = is_enabled ? 1 : 0,
};
rc = asus_query(device, &request, &response);
if (rc)
return rc;
return 0;
}
int
asus_get_led_data(struct ratbag_device *device, union asus_led_data *data)
{
int rc;
union asus_response response;
union asus_request request = {
.data.cmd = ASUS_CMD_GET_LED_DATA,
};
rc = asus_query(device, &request, &response);
if (rc)
return rc;
memcpy(data->raw, response.raw, sizeof(data->raw));
return 0;
}
/* set LED mode, brightness (0-4) and color */
int
asus_set_led(struct ratbag_device *device,
uint8_t index, uint8_t mode, uint8_t brightness,
struct ratbag_color color)
{
int rc;
union asus_response response;
union asus_request request = {
.data.cmd = ASUS_CMD_SET_LED,
.data.params[0] = index,
.data.params[2] = mode,
.data.params[3] = brightness,
.data.params[4] = color.red,
.data.params[5] = color.green,
.data.params[6] = color.blue,
};
rc = asus_query(device, &request, &response);
if (rc)
return rc;
return 0;
}

214
src/asus.h Normal file
View File

@@ -0,0 +1,214 @@
#pragma once
#include <linux/input.h>
#include <stdint.h>
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
#define ASUS_QUIRK_DOUBLE_DPI 1 << 0
#define ASUS_QUIRK_STRIX_PROFILE 1 << 1
#define ASUS_PACKET_SIZE 64
#define ASUS_BUTTON_ACTION_TYPE_KEY 0 /* keyboard key */
#define ASUS_BUTTON_ACTION_TYPE_BUTTON 1 /* mouse button */
#define ASUS_BUTTON_CODE_DISABLED 0xff /* disabled mouse button */
#define ASUS_STATUS_ERROR 0xffaa /* invalid state/request, disconnected or sleeping */
/* maximum number of buttons across all ASUS devices */
#define ASUS_MAX_NUM_BUTTON 16
/* maximum number of DPI presets across all ASUS devices */
/* for 4 DPI devices: 0 - red, 1 - purple, 2 - blue (default), 3 - green */
/* for 2 DPI devices: 0 - main (default), 1 - alternative */
#define ASUS_MAX_NUM_DPI 4
/* maximum number of LEDs across all ASUS devices */
#define ASUS_MAX_NUM_LED 3
/* base request */
struct _asus_request {
uint16_t cmd;
uint8_t params[ASUS_PACKET_SIZE - 2];
} __attribute__((packed));
_Static_assert(sizeof(struct _asus_request) == ASUS_PACKET_SIZE, "The size of `_asus_request` is wrong.");
union asus_request {
struct _asus_request data;
uint8_t raw[ASUS_PACKET_SIZE];
};
/* base response */
struct _asus_response {
uint16_t code;
uint8_t results[ASUS_PACKET_SIZE - 2];
} __attribute__((packed));
_Static_assert(sizeof(struct _asus_response) == ASUS_PACKET_SIZE, "The size of `_asus_response` is wrong.");
union asus_response {
struct _asus_response data;
uint8_t raw[ASUS_PACKET_SIZE];
};
/* current profile ID and firmware info */
struct asus_profile_data {
unsigned int profile_id;
uint8_t version_primary_major;
uint8_t version_primary_minor;
uint8_t version_primary_build;
uint8_t version_secondary_major;
uint8_t version_secondary_minor;
uint8_t version_secondary_build;
} __attribute__((packed));
/* button bindings */
struct _asus_binding {
uint8_t action; /* ASUS code (for both keyboard keys and mouse buttons) */
uint8_t type; /* ASUS action type */
} __attribute__((packed));
struct _asus_binding_data {
uint32_t pad;
struct _asus_binding binding[ASUS_MAX_NUM_BUTTON];
} __attribute__((packed));
union asus_binding_data {
struct _asus_binding_data data;
uint8_t raw[ASUS_PACKET_SIZE];
};
/* DPI data */
struct _asus_dpi2_data {
uint32_t pad;
uint16_t dpi[2]; /* DPI presets */
uint16_t rate; /* polling rate */
uint16_t response; /* button response */
uint16_t snapping; /* angle snapping (on/off) */
} __attribute__((packed)); /* struct for storing 2 DPI presets and extra settings */
struct _asus_dpi4_data {
uint32_t pad;
uint16_t dpi[4];
uint16_t rate;
uint16_t response;
uint16_t snapping;
} __attribute__((packed)); /* struct for storing 4 DPI presets and extra settings */
union asus_resolution_data {
struct _asus_dpi2_data data2; /* data for 2 DPI presets */
struct _asus_dpi4_data data4; /* data for 4 DPI presets */
uint8_t raw[sizeof(struct _asus_dpi4_data)];
};
/* LED data */
struct _asus_led {
uint8_t mode;
uint8_t brightness; /* 0-4 */
uint8_t r;
uint8_t g;
uint8_t b;
} __attribute__((packed));
struct _asus_led_data {
uint32_t pad;
struct _asus_led led[ASUS_MAX_NUM_LED]; /* LEDs */
} __attribute__((packed));
union asus_led_data {
struct _asus_led_data data;
uint8_t raw[sizeof(struct _asus_led_data)];
};
/* button define */
struct asus_button {
uint8_t asus_code; /* used for button action */
enum ratbag_button_action_type type;
uint8_t button; /* mouse button number, optional */
enum ratbag_button_action_special special; /* special action, optional */
};
struct asus_button *
asus_find_button_by_action(struct ratbag_button_action action);
struct asus_button *
asus_find_button_by_code(uint8_t asus_code);
int
asus_find_key_code(unsigned int linux_code);
int
asus_get_linux_key_code(uint8_t asus_code);
/* initializers of ratbag data */
void
asus_setup_profile(struct ratbag_device *device, struct ratbag_profile *profile);
void
asus_setup_button(struct ratbag_device *device, struct ratbag_button *button);
void
asus_setup_resolution(struct ratbag_device *device, struct ratbag_resolution *resolution);
void
asus_setup_led(struct ratbag_device *device, struct ratbag_led *led);
/* generic i/o */
int
asus_query(struct ratbag_device *device, union asus_request *request, union asus_response *response);
/* commit */
int
asus_save_profile(struct ratbag_device *device);
/* profiles */
int
asus_get_profile_data(struct ratbag_device *device, struct asus_profile_data *data);
int
asus_set_profile(struct ratbag_device *device, unsigned int index);
/* button bindings */
int
asus_get_binding_data(struct ratbag_device *device, union asus_binding_data *data);
int
asus_set_button_action(struct ratbag_device *device, unsigned int index,
uint8_t asus_code, uint8_t asus_type);
/* resolution settings */
int
asus_get_resolution_data(struct ratbag_device *device, union asus_resolution_data *data);
int
asus_set_dpi(struct ratbag_device *device, unsigned int index, unsigned int dpi);
int
asus_set_polling_rate(struct ratbag_device *device, unsigned int hz);
int
asus_set_button_response(struct ratbag_device *device, unsigned int ms);
int
asus_set_angle_snapping(struct ratbag_device *device, bool is_enabled);
/* LED settings */
int
asus_get_led_data(struct ratbag_device *device, union asus_led_data *data);
int
asus_set_led(struct ratbag_device *device,
uint8_t index, uint8_t mode, uint8_t brightness,
struct ratbag_color color);

517
src/driver-asus.c Normal file
View File

@@ -0,0 +1,517 @@
/*
* Copyright (C) 2021 Kyoken, kyoken@kyoken.ninja
*
* 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 <math.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
#include "libratbag-data.h"
#include "asus.h"
/* ButtonMapping configuration property defaults, stores indices for ASUS_BUTTON_MAPPING */
static int8_t ASUS_BUTTON_MAPPING_INDEX[] = {
0, /* left */
1, /* right (button 3 in xev) */
2, /* middle (button 2 in xev) */
6, /* wheel up */
7, /* wheel down */
5, /* DPI */
3, /* backward */
4, /* forward */
-1, /* placeholder */
-1, /* placeholder */
-1, /* placeholder */
-1, /* placeholder */
-1, /* placeholder */
-1, /* placeholder */
-1, /* placeholder */
-1, /* placeholder */
};
static unsigned int ASUS_LED_MODE[] = {
RATBAG_LED_ON,
RATBAG_LED_BREATHING,
RATBAG_LED_CYCLE,
RATBAG_LED_ON, /* rainbow wave */
RATBAG_LED_ON, /* reactive - react to clicks */
RATBAG_LED_ON, /* custom - depends on mouse type */
RATBAG_LED_ON, /* battery - battery indicator */
};
struct asus_data {
uint8_t is_ready;
};
static int
asus_driver_load_profile(struct ratbag_device *device, struct ratbag_profile *profile)
{
int rc;
const int8_t *bmi = ratbag_device_data_asus_get_button_mapping(device->data);
struct _asus_binding *asus_binding;
struct _asus_led *asus_led;
struct asus_button *asus_button;
struct ratbag_button *button;
struct ratbag_led *led;
struct ratbag_resolution *resolution;
union asus_binding_data binding_data;
union asus_led_data led_data;
union asus_resolution_data resolution_data;
unsigned int dpi_count = ratbag_device_get_profile(device, 0)->num_resolutions;
/* get buttons */
log_debug(device->ratbag, "Loading buttons data\n");
rc = asus_get_binding_data(device, &binding_data);
if (rc)
return rc;
ratbag_profile_for_each_button(profile, button) {
int asus_index = bmi[button->index] != -1 ? bmi[button->index] : ASUS_BUTTON_MAPPING_INDEX[button->index];
if (asus_index == -1) {
log_debug(device->ratbag, "No mapping for button %d\n", button->index);
continue;
}
asus_binding = &binding_data.data.binding[asus_index];
/* disabled */
if (asus_binding->action == ASUS_BUTTON_CODE_DISABLED) {
button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
continue;
}
/* got action */
switch (asus_binding->type) {
case ASUS_BUTTON_ACTION_TYPE_KEY:
button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
/* ASUS code to Linux code */
button->action.action.key.key = asus_get_linux_key_code(asus_binding->action);
break;
case ASUS_BUTTON_ACTION_TYPE_BUTTON:
asus_button = asus_find_button_by_code(asus_binding->action);
if (asus_button != NULL) { /* found button to bind to */
button->action.type = asus_button->type;
if (asus_button->type == RATBAG_BUTTON_ACTION_TYPE_BUTTON)
button->action.action.button = asus_button->button;
else if (asus_button->type == RATBAG_BUTTON_ACTION_TYPE_SPECIAL)
button->action.action.special = asus_button->special;
} else {
log_debug(device->ratbag, "Unknown action code %02x\n", asus_binding->action);
}
break;
default:
break;
}
}
/* get DPIs */
log_debug(device->ratbag, "Loading resolutions data\n");
rc = asus_get_resolution_data(device, &resolution_data);
if (rc)
return rc;
switch (dpi_count) {
case 2: /* 2 DPI presets */
ratbag_profile_set_report_rate(profile, resolution_data.data2.rate);
ratbag_profile_for_each_resolution(profile, resolution)
ratbag_resolution_set_resolution(
resolution,
resolution_data.data2.dpi[resolution->index],
resolution_data.data2.dpi[resolution->index]);
break;
case 4: /* 4 DPI presets */
ratbag_profile_set_report_rate(profile, resolution_data.data4.rate);
ratbag_profile_for_each_resolution(profile, resolution)
ratbag_resolution_set_resolution(
resolution,
resolution_data.data4.dpi[resolution->index],
resolution_data.data4.dpi[resolution->index]);
break;
default:
break;
}
/* get LEDs */
log_debug(device->ratbag, "Loading LEDs data\n");
rc = asus_get_led_data(device, &led_data);
if (rc)
return rc;
ratbag_profile_for_each_led(profile, led) {
asus_led = &led_data.data.led[led->index];
led->mode = ASUS_LED_MODE[asus_led->mode];
/* convert brightness from 0-4 to 0-256 */
led->brightness = asus_led->brightness * 64;
led->color.red = asus_led->r;
led->color.green = asus_led->g;
led->color.blue = asus_led->b;
}
return 0;
}
static int
asus_driver_save_profile(struct ratbag_device *device, struct ratbag_profile *profile)
{
int rc = 0;
const int8_t *bmi = ratbag_device_data_asus_get_button_mapping(device->data);
struct asus_button *asus_button;
struct ratbag_button *button;
struct ratbag_led *led;
struct ratbag_resolution *resolution;
/* set buttons */
ratbag_profile_for_each_button(profile, button) {
if (!button->dirty)
continue;
int asus_index = bmi[button->index] != -1 ? bmi[button->index] : ASUS_BUTTON_MAPPING_INDEX[button->index];
int asus_code;
if (asus_index == -1) {
log_debug(device->ratbag, "No mapping for button %d\n", button->index);
continue;
}
log_debug(device->ratbag, "Button %d (%d) changed\n", button->index, asus_index);
switch (button->action.type) {
case RATBAG_BUTTON_ACTION_TYPE_NONE:
rc = asus_set_button_action(
device, asus_index,
ASUS_BUTTON_CODE_DISABLED,
ASUS_BUTTON_ACTION_TYPE_BUTTON);
break;
case RATBAG_BUTTON_ACTION_TYPE_KEY:
/* Linux code to ASUS code */
asus_code = asus_find_key_code(button->action.action.key.key);
if (asus_code >= 0)
rc = asus_set_button_action(
device, asus_index,
asus_code,
ASUS_BUTTON_ACTION_TYPE_KEY);
break;
case RATBAG_BUTTON_ACTION_TYPE_BUTTON:
case RATBAG_BUTTON_ACTION_TYPE_SPECIAL:
asus_button = asus_find_button_by_action(button->action);
if (asus_button != NULL) /* found button to bind to */
rc = asus_set_button_action(
device, asus_index,
asus_button->asus_code,
ASUS_BUTTON_ACTION_TYPE_BUTTON);
break;
default:
break;
}
if (rc)
return rc;
}
/* set extra settings */
if (profile->rate_dirty) {
log_debug(device->ratbag, "Polling rate changed to %d Hz\n", profile->hz);
rc = asus_set_polling_rate(device, profile->hz);
if (rc)
return rc;
}
/* set DPIs */
ratbag_profile_for_each_resolution(profile, resolution) {
if (!resolution->dirty)
continue;
log_debug(
device->ratbag, "Resolution %d changed to %d\n",
resolution->index, resolution->dpi_x);
rc = asus_set_dpi(device, resolution->index, resolution->dpi_x);
if (rc)
return rc;
}
/* set LEDs */
ratbag_profile_for_each_led(profile, led) {
if (!led->dirty)
continue;
log_debug(device->ratbag, "LED %d changed\n", led->index);
uint8_t led_mode = 0;
for (unsigned int i = 0; i < ARRAY_LENGTH(ASUS_LED_MODE); i++) {
if (ASUS_LED_MODE[i] == led->mode) {
led_mode = i;
break;
}
}
/* convert brightness from 0-256 to 0-4 */
uint8_t led_brightness = (uint8_t)round((double)led->brightness / 64.0);
rc = asus_set_led(device, led->index, led_mode, led_brightness, led->color);
if (rc)
return rc;
}
return 0;
}
static int
asus_driver_load_profiles(struct ratbag_device *device)
{
int rc;
struct asus_profile_data profile_data;
struct ratbag_profile *profile;
unsigned int current_profile_id;
/* get current profile id */
rc = asus_get_profile_data(device, &profile_data);
if (rc)
return rc;
current_profile_id = profile_data.profile_id;
log_debug(device->ratbag, "Initial profile is %d\n", current_profile_id);
log_debug(
device->ratbag, "Primary version %02X.%02X.%02X\n",
profile_data.version_primary_major,
profile_data.version_primary_minor,
profile_data.version_primary_build);
log_debug(
device->ratbag, "Secondary version %02X.%02X.%02X\n",
profile_data.version_secondary_major,
profile_data.version_secondary_minor,
profile_data.version_secondary_build);
/* read ratbag profiles */
ratbag_device_for_each_profile(device, profile) {
if (profile->index == current_profile_id) { /* profile is already selected */
profile->is_active = true;
} else { /* switch profile */
profile->is_active = false;
log_debug(device->ratbag, "Switching to profile %d\n", profile->index);
rc = asus_set_profile(device, profile->index);
if (rc)
return rc;
}
rc = asus_driver_load_profile(device, profile);
if (rc)
return rc;
}
/* back to initial profile */
if (device->num_profiles > 1) {
log_debug(device->ratbag, "Switching back to initial profile %d\n", current_profile_id);
rc = asus_set_profile(device, current_profile_id);
if (rc)
return rc;
}
return 0;
}
static int
asus_driver_save_profiles(struct ratbag_device *device)
{
int rc;
struct asus_profile_data profile_data;
struct ratbag_profile *profile;
unsigned int current_profile_id;
/* get current profile id */
rc = asus_get_profile_data(device, &profile_data);
if (rc)
return rc;
current_profile_id = profile_data.profile_id;
log_debug(device->ratbag, "Initial profile is %d\n", current_profile_id);
ratbag_device_for_each_profile(device, profile) {
if (!profile->dirty)
continue;
log_debug(device->ratbag, "Profile %d changed\n", profile->index);
/* switch profile */
if (profile->index != current_profile_id) {
log_debug(device->ratbag, "Switching to profile %d\n", profile->index);
rc = asus_set_profile(device, profile->index);
if (rc)
return rc;
}
rc = asus_driver_save_profile(device, profile);
if (rc)
return rc;
/* save profile */
log_debug(device->ratbag, "Saving profile\n");
rc = asus_save_profile(device);
if (rc)
return rc;
}
/* back to initial profile */
if (device->num_profiles > 1) {
log_debug(device->ratbag, "Switching back to initial profile %d\n", current_profile_id);
rc = asus_set_profile(device, current_profile_id);
if (rc)
return rc;
}
return 0;
}
static int
asus_driver_probe(struct ratbag_device *device)
{
int rc;
unsigned int profile_count, dpi_count, button_count, led_count;
struct asus_data *drv_data;
struct ratbag_profile *profile;
struct ratbag_button *button;
struct ratbag_resolution *resolution;
struct ratbag_led *led;
rc = ratbag_open_hidraw(device);
if (rc)
return rc;
/* create device state data */
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
drv_data->is_ready = 1;
/* get device properties from configuration file */
profile_count = ratbag_device_data_asus_get_profile_count(device->data);
dpi_count = ratbag_device_data_asus_get_dpi_count(device->data);
button_count = ratbag_device_data_asus_get_button_count(device->data);
led_count = ratbag_device_data_asus_get_led_count(device->data);
/* init profiles */
ratbag_device_init_profiles(
device,
max(profile_count, 1),
max(dpi_count, 2),
max(button_count, 8),
max(led_count, 0));
/* setup profiles */
ratbag_device_for_each_profile(device, profile) {
if (profile->index == 0) {
profile->is_active = true;
}
asus_setup_profile(device, profile);
ratbag_profile_for_each_button(profile, button)
asus_setup_button(device, button);
ratbag_profile_for_each_resolution(profile, resolution)
asus_setup_resolution(device, resolution);
ratbag_profile_for_each_led(profile, led)
asus_setup_led(device, led);
}
/* load profiles */
rc = asus_driver_load_profiles(device);
if (rc == ASUS_STATUS_ERROR) { /* mouse in invalid state */
drv_data->is_ready = 0;
} else if (rc) { /* other errors */
log_error(
device->ratbag, "Can't talk to the mouse: '%s' (%d)\n",
strerror(-rc), rc);
free(drv_data);
ratbag_set_drv_data(device, NULL);
return -ENODEV;
}
return 0;
}
static void
asus_driver_remove(struct ratbag_device *device)
{
ratbag_close_hidraw(device);
free(ratbag_get_drv_data(device));
}
static int
asus_driver_commit(struct ratbag_device *device)
{
int rc;
struct asus_data *drv_data;
/* check last device state */
drv_data = ratbag_get_drv_data(device);
if (!drv_data->is_ready) { /* device was not ready */
log_error(device->ratbag, "Device was not ready, trying to reload\n");
rc = asus_driver_load_profiles(device);
if (rc) {
log_error(device->ratbag, "Device reloading failed (%d)\n", rc);
if (rc != ASUS_STATUS_ERROR)
return rc;
} else {
drv_data->is_ready = 1;
log_error(device->ratbag, "Device was successfully reloaded\n");
}
return RATBAG_ERROR_DEVICE; /* fail in any case because we tried to rollback instead of commit */
}
/* save profiles */
rc = asus_driver_save_profiles(device);
if (rc) {
log_error(device->ratbag, "Commit failed (%d)\n", rc);
if (rc != ASUS_STATUS_ERROR)
return rc;
return RATBAG_ERROR_DEVICE;
}
return 0;
}
struct ratbag_driver asus_driver = {
.name = "ASUS",
.id = "asus",
.probe = asus_driver_probe,
.remove = asus_driver_remove,
.commit = asus_driver_commit,
.set_active_profile = asus_set_profile,
};

View File

@@ -496,9 +496,9 @@ etekcity_read_profile(struct ratbag_profile *profile)
}
static int
etekcity_write_macro(struct ratbag_button *button,
const struct ratbag_button_action *action)
etekcity_write_macro(struct ratbag_button *button)
{
const struct ratbag_button_action *action = &button->action;
struct ratbag_device *device;
struct etekcity_macro *macro;
struct etekcity_data *drv_data;
@@ -555,9 +555,9 @@ etekcity_write_macro(struct ratbag_button *button,
}
static int
etekcity_write_button(struct ratbag_button *button,
const struct ratbag_button_action *action)
etekcity_write_button(struct ratbag_button *button)
{
const struct ratbag_button_action *action = &button->action;
struct ratbag_profile *profile = button->profile;
struct ratbag_device *device = profile->device;
struct etekcity_data *drv_data = ratbag_get_drv_data(device);
@@ -572,15 +572,7 @@ etekcity_write_button(struct ratbag_button *button,
*data = rc;
rc = etekcity_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 = etekcity_write_macro(button, action);
rc = etekcity_write_macro(button);
if (rc) {
log_error(device->ratbag,
"unable to write the macro to the device: '%s' (%d)\n",
@@ -592,8 +584,7 @@ etekcity_write_button(struct ratbag_button *button,
}
static int
etekcity_write_resolution_dpi(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
etekcity_write_resolution(struct ratbag_resolution *resolution)
{
struct ratbag_profile *profile = resolution->profile;
struct ratbag_device *device = profile->device;
@@ -602,6 +593,9 @@ etekcity_write_resolution_dpi(struct ratbag_resolution *resolution,
uint8_t *buf;
int rc;
const unsigned int dpi_x = resolution->dpi_x;
const unsigned int dpi_y = resolution->dpi_y;
if (dpi_x < 50 || dpi_x > 8200 || dpi_x % 50)
return -EINVAL;
if (dpi_y < 50 || dpi_y > 8200 || dpi_y % 50)
@@ -705,13 +699,49 @@ etekcity_remove(struct ratbag_device *device)
free(ratbag_get_drv_data(device));
}
static int
etekcity_commit(struct ratbag_device *device)
{
int rc = 0;
struct ratbag_button *button = NULL;
struct ratbag_profile *profile = NULL;
struct ratbag_resolution *resolution = NULL;
ratbag_device_for_each_profile(device, profile) {
if (!profile->dirty)
continue;
ratbag_profile_for_each_resolution(profile, resolution) {
if (!resolution->dirty)
continue;
rc = etekcity_write_resolution(resolution);
if (rc)
return rc;
}
ratbag_profile_for_each_button(profile, button) {
if (!button->dirty)
continue;
rc = etekcity_write_button(button);
if (rc)
return rc;
}
rc = etekcity_write_profile(profile);
if (rc)
return rc;
}
return 0;
}
struct ratbag_driver etekcity_driver = {
.name = "EtekCity",
.id = "etekcity",
.probe = etekcity_probe,
.remove = etekcity_remove,
.write_profile = etekcity_write_profile,
.commit = etekcity_commit,
.set_active_profile = etekcity_set_current_profile,
.write_button = etekcity_write_button,
.write_resolution_dpi = etekcity_write_resolution_dpi,
};

View File

@@ -448,7 +448,7 @@ hidpp10drv_read_profile(struct ratbag_profile *profile)
free(profile->name);
profile->name = NULL;
}
if (p.name && p.name[0] != '\0')
if (p.name[0] != '\0')
profile->name = strdup_safe((char*)p.name);
rc = hidpp10_get_current_profile(hidpp10, &idx);
@@ -548,7 +548,7 @@ hidpp10_log(void *userdata, enum hidpp_log_priority priority, const char *format
{
struct ratbag_device *device = userdata;
log_msg_va(device->ratbag, priority, format, args);
log_msg_va(device->ratbag, (enum ratbag_log_priority)priority, format, args);
}
static int

View File

@@ -957,7 +957,7 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
if (drv_data->capabilities & HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060) {
rc = hidpp20drv_read_report_rate_8060(device);
if (rc < 0)
if (rc < 0 && drv_data->report_rates[0] == 0)
return rc;
ratbag_profile_set_report_rate_list(profile,
@@ -1096,6 +1096,7 @@ hidpp20drv_read_special_key_mouse(struct ratbag_device *device)
drv_data->controls = NULL;
drv_data->num_controls = 0;
rc = hidpp20_special_key_mouse_get_controls(drv_data->dev, &drv_data->controls);
log_raw(device->ratbag, "num_control %d\n", rc);
if (rc > 0) {
drv_data->num_controls = rc;
rc = 0;
@@ -1208,9 +1209,7 @@ hidpp20drv_read_profile_8100(struct ratbag_profile *profile)
profile->is_enabled = drv_data->profiles->profiles[profile->index].enabled;
profile->is_active = false;
if ((int)profile->index == hidpp20drv_current_profile(device))
profile->is_active = true;
profile->is_active = (profile->index == drv_data->profiles->active_profile_index);
if (profile->is_active)
dpi_index = hidpp20_onboard_profiles_get_current_dpi_index(drv_data->dev);
@@ -1604,7 +1603,7 @@ hidpp20_log(void *userdata, enum hidpp_log_priority priority, const char *format
{
struct ratbag_device *device = userdata;
log_msg_va(device->ratbag, priority, format, args);
log_msg_va(device->ratbag, (enum ratbag_log_priority)priority, format, args);
}
static void
@@ -1642,6 +1641,14 @@ hidpp20drv_init_device(struct ratbag_device *device,
if (num >= 0)
drv_data->num_leds = num;
num = ratbag_device_data_hidpp20_get_button_count(device->data);
if (num >= 0)
drv_data->num_buttons = num;
num = ratbag_device_data_hidpp20_get_report_rate(device->data);
if (num >= 0)
drv_data->report_rates[0] = num;
ratbag_device_init_profiles(device,
drv_data->num_profiles,
drv_data->num_resolutions,

View File

@@ -139,7 +139,7 @@ openinput_get_error_string(struct oi_report_t *report)
xasprintf(&str, "Unsupported function (0x%02x, 0x%02x)", report->data[0], report->data[1]);
break;
default:
xasprintf(&str, "Unkown error (%u)", report->function);
xasprintf(&str, "Unknown error (%u)", report->function);
break;
}
@@ -159,6 +159,22 @@ openinput_get_report_size(unsigned int report)
}
}
static bool
openinput_report_filter(uint8_t *buf, size_t len)
{
if (len < 1)
return false;
switch(buf[0]) {
case OI_REPORT_SHORT:
return len == OI_REPORT_SHORT_SIZE;
case OI_REPORT_LONG:
return len == OI_REPORT_LONG_SIZE;
default:
return false;
}
}
static int
openinput_send_report(struct ratbag_device *device, struct oi_report_t *report)
{
@@ -175,7 +191,7 @@ openinput_send_report(struct ratbag_device *device, struct oi_report_t *report)
return ret;
}
ret = ratbag_hidraw_read_input_report(device, buffer, OI_REPORT_MAX_SIZE);
ret = ratbag_hidraw_read_input_report(device, buffer, OI_REPORT_MAX_SIZE, openinput_report_filter);
if (ret < 0) {
log_error(device->ratbag, "openinput: failed to read data from device (%s)\n",
strerror(-ret));

1131
src/driver-roccat-kone-emp.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -603,9 +603,9 @@ roccat_write_macro(struct ratbag_button *button,
}
static int
roccat_write_button(struct ratbag_button *button,
const struct ratbag_button_action *action)
roccat_write_button(struct ratbag_button *button)
{
const struct ratbag_button_action *action = &button->action;
struct ratbag_profile *profile = button->profile;
struct ratbag_device *device = profile->device;
struct roccat_data *drv_data = ratbag_get_drv_data(device);
@@ -639,8 +639,7 @@ roccat_write_button(struct ratbag_button *button,
}
static int
roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
roccat_write_resolution(struct ratbag_resolution *resolution)
{
struct ratbag_profile *profile = resolution->profile;
struct ratbag_device *device = profile->device;
@@ -649,7 +648,10 @@ roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
uint8_t *buf;
int rc;
if (dpi_x < 100 || dpi_x > 5000 || dpi_x % 50)
const unsigned int dpi_x = resolution->dpi_x;
const unsigned int dpi_y = resolution->dpi_y;
if (dpi_x < 200 || dpi_x > 8200 || dpi_x % 50)
return -EINVAL;
if (dpi_y < 100 || dpi_y > 5000 || dpi_y % 50)
return -EINVAL;
@@ -833,13 +835,50 @@ roccat_remove(struct ratbag_device *device)
free(ratbag_get_drv_data(device));
}
struct ratbag_driver roccat_driver = {
static int
roccat_commit(struct ratbag_device *device)
{
int rc = 0;
struct ratbag_button *button = NULL;
struct ratbag_profile *profile = NULL;
struct ratbag_resolution *resolution = NULL;
ratbag_device_for_each_profile(device, profile) {
if (!profile->dirty)
continue;
rc = roccat_write_profile(profile);
if (rc)
return rc;
ratbag_profile_for_each_resolution(profile, resolution) {
if (!resolution->dirty)
continue;
rc = roccat_write_resolution(resolution);
if (rc)
return rc;
}
ratbag_profile_for_each_button(profile, button) {
if (!button->dirty)
continue;
rc = roccat_write_button(button);
if (rc)
return rc;
}
}
return 0;
}
struct ratbag_driver roccat_kone_pure_driver = {
.name = "Roccat Kone Pure",
.id = "roccat-kone-pure",
.probe = roccat_probe,
.remove = roccat_remove,
.write_profile = roccat_write_profile,
.commit = roccat_commit,
.set_active_profile = roccat_set_current_profile,
.write_button = roccat_write_button,
.write_resolution_dpi = roccat_write_resolution_dpi,
};

View File

@@ -57,7 +57,7 @@
struct roccat_settings_report {
uint8_t reportID;
uint8_t twoB;
uint8_t report_length;
uint8_t profileID;
uint8_t x_y_linked;
uint8_t x_sensitivity; /* 0x06 means 0 */
@@ -78,8 +78,7 @@ struct roccat_settings_report {
struct roccat_macro {
uint8_t reportID;
uint8_t twentytwo;
uint8_t height;
uint16_t report_length;
uint8_t profile;
uint8_t button_index;
uint8_t active;
@@ -581,8 +580,7 @@ roccat_write_macro(struct ratbag_button *button,
}
macro->reportID = ROCCAT_REPORT_ID_MACRO;
macro->twentytwo = 0x22;
macro->height = 0x08;
macro->report_length = 0x0822;
macro->profile = button->profile->index;
macro->button_index = button->index;
macro->active = 0x01;
@@ -609,9 +607,9 @@ roccat_write_macro(struct ratbag_button *button,
}
static int
roccat_write_button(struct ratbag_button *button,
const struct ratbag_button_action *action)
roccat_write_button(struct ratbag_button *button)
{
const struct ratbag_button_action *action = &button->action;
struct ratbag_profile *profile = button->profile;
struct ratbag_device *device = profile->device;
struct roccat_data *drv_data = ratbag_get_drv_data(device);
@@ -645,8 +643,7 @@ roccat_write_button(struct ratbag_button *button,
}
static int
roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y)
roccat_write_resolution(struct ratbag_resolution *resolution)
{
struct ratbag_profile *profile = resolution->profile;
struct ratbag_device *device = profile->device;
@@ -655,6 +652,9 @@ roccat_write_resolution_dpi(struct ratbag_resolution *resolution,
uint8_t *buf;
int rc;
const unsigned int dpi_x = resolution->dpi_x;
const unsigned int dpi_y = resolution->dpi_y;
if (dpi_x < 200 || dpi_x > 8200 || dpi_x % 50)
return -EINVAL;
if (dpi_y < 200 || dpi_y > 8200 || dpi_y % 50)
@@ -835,13 +835,50 @@ roccat_remove(struct ratbag_device *device)
free(ratbag_get_drv_data(device));
}
static int
roccat_commit(struct ratbag_device *device)
{
int rc = 0;
struct ratbag_button *button = NULL;
struct ratbag_profile *profile = NULL;
struct ratbag_resolution *resolution = NULL;
ratbag_device_for_each_profile(device, profile) {
if (!profile->dirty)
continue;
rc = roccat_write_profile(profile);
if (rc)
return rc;
ratbag_profile_for_each_resolution(profile, resolution) {
if (!resolution->dirty)
continue;
rc = roccat_write_resolution(resolution);
if (rc)
return rc;
}
ratbag_profile_for_each_button(profile, button) {
if (!button->dirty)
continue;
rc = roccat_write_button(button);
if (rc)
return rc;
}
}
return 0;
}
struct ratbag_driver roccat_driver = {
.name = "Roccat Kone XTD",
.id = "roccat",
.probe = roccat_probe,
.remove = roccat_remove,
.write_profile = roccat_write_profile,
.commit = roccat_commit,
.set_active_profile = roccat_set_current_profile,
.write_button = roccat_write_button,
.write_resolution_dpi = roccat_write_resolution_dpi,
};

File diff suppressed because it is too large Load Diff

41
src/driver-sinowealth.h Normal file
View File

@@ -0,0 +1,41 @@
/*
* Copyright © 2022 Yaroslav Chvanov
*
* 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-util.h"
enum sinowealth_led_format {
SINOWEALTH_LED_TYPE_NONE,
SINOWEALTH_LED_TYPE_RBG,
SINOWEALTH_LED_TYPE_RGB,
};
struct sinowealth_device_data {
char *fw_version;
char *device_name;
enum sinowealth_led_format led_type;
unsigned int button_count;
struct list link;
};

View File

@@ -30,6 +30,7 @@
#include <stdint.h>
#include <string.h>
#include "driver-steelseries.h"
#include "libratbag-private.h"
#include "libratbag-hidraw.h"
#include "libratbag-data.h"
@@ -41,6 +42,7 @@
#define STEELSERIES_INPUT_HIDRAW 1
/* not sure these two are used for */
#define STEELSERIES_REPORT_ID 0x00 // steelseries doesn't use numbered reports
#define STEELSERIES_REPORT_ID_1 0x01
#define STEELSERIES_REPORT_ID_2 0x02
@@ -53,14 +55,16 @@
#define STEELSERIES_ID_LED_INTENSITY_SHORT 0x05
#define STEELSERIES_ID_LED_EFFECT_SHORT 0x07
#define STEELSERIES_ID_LED_COLOR_SHORT 0x08
#define STEELSERIES_ID_LED_COLOR_SHORT_RIVAL100 0x05
#define STEELSERIES_ID_SAVE_SHORT 0x09
#define STEELSERIES_ID_FIRMWARE_PROTOCOL1 0x10
#define STEELSERIES_ID_BUTTONS 0x31
#define STEELSERIES_ID_DPI 0x53
#define STEELSERIES_ID_REPORT_RATE 0x54
#define STEELSERIES_ID_LED 0x5b
#define STEELSERIES_ID_SAVE 0x59
#define STEELSERIES_ID_FIRMWARE 0x90
#define STEELSERIES_ID_FIRMWARE_PROTOCOL2 0x90
#define STEELSERIES_ID_SETTTINGS 0x92
#define STEELSERIES_ID_DPI_PROTOCOL3 0x03
@@ -112,11 +116,31 @@ struct steelseries_led_cycle_spec {
int point_count_idx; /* index of the point counter field */
};
struct _steelseries_message {
uint8_t report_id;
uint8_t parameters[STEELSERIES_REPORT_SIZE - 1];
} __attribute__((packed));
union steelseries_message {
struct _steelseries_message msg;
uint8_t data[STEELSERIES_REPORT_SIZE];
};
_Static_assert(sizeof(union steelseries_message) == STEELSERIES_REPORT_SIZE,
"Size of union steelseries_message is wrong");
static int
steelseries_test_hidraw(struct ratbag_device *device)
{
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
/* Rival mice are composite devices with multiple HID devices
* and only the HID vendor device can be used to configure the
* device.
*/
if (!ratbag_hidraw_has_vendor_page(device))
return false;
if (device_version > 1)
return ratbag_hidraw_has_report(device, STEELSERIES_REPORT_ID_1);
else
@@ -179,23 +203,38 @@ steelseries_get_firmware_version(struct ratbag_device *device)
{
struct steelseries_data *drv_data = device->drv_data;
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
size_t buf_len = STEELSERIES_REPORT_SIZE;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
size_t msg_len;
uint8_t buf[2] = {0};
int ret;
if (device_version == 2)
buf[0] = STEELSERIES_ID_FIRMWARE;
else if (device_version == 3)
buf[0] = STEELSERIES_ID_FIRMWARE_PROTOCOL3;
else
union steelseries_message msg = {
.msg.report_id = STEELSERIES_REPORT_ID,
.msg.parameters = {0},
};
switch (device_version) {
case 1:
msg.msg.parameters[0] = STEELSERIES_ID_FIRMWARE_PROTOCOL1;
msg_len = STEELSERIES_REPORT_SIZE_SHORT;
break;
case 2:
msg.msg.parameters[0] = STEELSERIES_ID_FIRMWARE_PROTOCOL2;
msg_len = STEELSERIES_REPORT_SIZE;
break;
case 3:
msg.msg.parameters[0] = STEELSERIES_ID_FIRMWARE_PROTOCOL3;
msg_len = STEELSERIES_REPORT_SIZE;
break;
default:
return -ENOTSUP;
}
msleep(10);
ret = ratbag_hidraw_output_report(device, buf, buf_len);
ret = ratbag_hidraw_output_report(device, msg.data, msg_len);
if (ret < 0)
return ret;
ret = ratbag_hidraw_read_input_report_index(device, buf, buf_len, STEELSERIES_INPUT_HIDRAW);
ret = ratbag_hidraw_read_input_report_index(device, buf, sizeof(buf), STEELSERIES_INPUT_HIDRAW, NULL);
if (ret < 0)
return ret;
@@ -213,24 +252,33 @@ steelseries_read_settings(struct ratbag_device *device)
struct ratbag_resolution *resolution;
struct ratbag_led *led;
size_t buf_len = STEELSERIES_REPORT_SIZE;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
int ret;
unsigned int active_resolution;
if (device_version == 2)
buf[0] = STEELSERIES_ID_SETTTINGS;
else if (device_version == 3)
buf[0] = STEELSERIES_ID_SETTTINGS_PROTOCOL3;
else
union steelseries_message msg = {
.msg.report_id = STEELSERIES_REPORT_ID,
.msg.parameters = {0},
};
switch (device_version) {
case 2:
msg.msg.parameters[0] = STEELSERIES_ID_SETTTINGS;
break;
case 3:
msg.msg.parameters[0] = STEELSERIES_ID_SETTTINGS_PROTOCOL3;
break;
case 1:
default:
return -ENOTSUP;
}
msleep(10);
ret = ratbag_hidraw_output_report(device, buf, buf_len);
ret = ratbag_hidraw_output_report(device, msg.data, STEELSERIES_REPORT_SIZE);
if (ret < 0)
return ret;
ret = ratbag_hidraw_read_input_report_index(device, buf, buf_len, STEELSERIES_INPUT_HIDRAW);
ret = ratbag_hidraw_read_input_report_index(device, buf, STEELSERIES_REPORT_SIZE, STEELSERIES_INPUT_HIDRAW, NULL);
if (ret < 0)
return ret;
@@ -270,7 +318,7 @@ steelseries_probe(struct ratbag_device *device)
struct ratbag_resolution *resolution;
struct ratbag_button *button;
struct ratbag_led *led;
int rc, button_count, led_count, device_version, mono_led, short_button;
int rc, button_count, led_count, device_version, quirk;
struct dpi_list *dpilist = NULL;
struct dpi_range *dpirange = NULL;
@@ -287,12 +335,22 @@ steelseries_probe(struct ratbag_device *device)
return rc;
device_version = ratbag_device_data_steelseries_get_device_version(device->data);
if (device_version == -1) {
log_error(device->ratbag, "Device version not set\n");
return -EINVAL;
}
button_count = ratbag_device_data_steelseries_get_button_count(device->data);
if (button_count == -1)
button_count = 0;
led_count = ratbag_device_data_steelseries_get_led_count(device->data);
if (led_count == -1)
led_count = 0;
dpirange = ratbag_device_data_steelseries_get_dpi_range(device->data);
dpilist = ratbag_device_data_steelseries_get_dpi_list(device->data);
mono_led = ratbag_device_data_steelseries_get_mono_led(device->data);
short_button = ratbag_device_data_steelseries_get_short_button(device->data);
quirk = ratbag_device_data_steelseries_get_quirk(device->data);
ratbag_device_init_profiles(device,
STEELSERIES_NUM_PROFILES,
@@ -336,7 +394,7 @@ steelseries_probe(struct ratbag_device *device)
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_BUTTON);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_SPECIAL);
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_KEY);
if (short_button == 0) {
if (quirk != STEELSERIES_QUIRK_SENSEIRAW) {
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
}
@@ -346,7 +404,7 @@ steelseries_probe(struct ratbag_device *device)
ratbag_profile_for_each_led(profile, led) {
led->type = led->index == 0 ? RATBAG_LED_TYPE_LOGO : RATBAG_LED_TYPE_WHEEL;
led->mode = RATBAG_LED_ON;
if (mono_led) {
if (quirk == STEELSERIES_QUIRK_SENSEIRAW) {
led->colordepth = RATBAG_LED_COLORDEPTH_MONOCHROME;
led->brightness = 255;
} else {
@@ -385,15 +443,19 @@ steelseries_write_dpi(struct ratbag_resolution *resolution)
struct dpi_list *dpilist = NULL;
struct dpi_range *dpirange = NULL;
int ret;
int i = 0;
size_t buf_len;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
union steelseries_message msg = {
.msg.report_id = STEELSERIES_REPORT_ID,
.msg.parameters = {0},
};
dpirange = ratbag_device_data_steelseries_get_dpi_range(device->data);
dpilist = ratbag_device_data_steelseries_get_dpi_list(device->data);
if (device_version == 1) {
int i = 0;
switch (device_version) {
case 1:
/* when using lists the entries are enumerated in reverse */
if (dpilist) {
for (i = 0; i < (int)resolution->ndpis; i++) {
@@ -406,27 +468,30 @@ steelseries_write_dpi(struct ratbag_resolution *resolution)
}
buf_len = STEELSERIES_REPORT_SIZE_SHORT;
buf[0] = STEELSERIES_ID_DPI_SHORT;
buf[1] = resolution->index + 1;
buf[2] = i;
} else if (device_version == 2) {
msg.msg.parameters[0] = STEELSERIES_ID_DPI_SHORT;
msg.msg.parameters[1] = resolution->index + 1;
msg.msg.parameters[2] = i;
break;
case 2:
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_DPI;
buf[2] = resolution->index + 1;
buf[3] = resolution->dpi_x / dpirange->step - 1;
buf[6] = 0x42; /* not sure if needed */
} else if (device_version == 3) {
msg.msg.parameters[0] = STEELSERIES_ID_DPI;
msg.msg.parameters[2] = resolution->index + 1;
msg.msg.parameters[3] = resolution->dpi_x / dpirange->step - 1;
msg.msg.parameters[6] = 0x42; /* not sure if needed */
break;
case 3:
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_DPI_PROTOCOL3;
buf[2] = resolution->index + 1;
buf[3] = resolution->dpi_x / dpirange->step - 1;
buf[5] = 0x42; /* not sure if needed */
} else {
msg.msg.parameters[0] = STEELSERIES_ID_DPI_PROTOCOL3;
msg.msg.parameters[2] = resolution->index + 1;
msg.msg.parameters[3] = resolution->dpi_x / dpirange->step - 1;
msg.msg.parameters[5] = 0x42; /* not sure if needed */
break;
default:
return -ENOTSUP;
}
msleep(10);
ret = ratbag_hidraw_output_report(device, buf, buf_len);
ret = ratbag_hidraw_output_report(device, msg.data, buf_len);
if (ret < 0)
return ret;
@@ -440,26 +505,49 @@ steelseries_write_report_rate(struct ratbag_profile *profile)
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
int ret;
size_t buf_len;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
uint8_t reported_rate = 0;
union steelseries_message msg = {
.msg.report_id = STEELSERIES_REPORT_ID,
.msg.parameters = {0},
};
switch (device_version) {
case 1:
if (profile->hz >= 1000) {
profile->hz = 1000;
reported_rate = 0x01;
} else if (profile->hz >= 375) {
profile->hz = 500;
reported_rate = 0x02;
} else if (profile->hz <= 125) {
profile->hz = 125;
reported_rate = 0x04;
} else {
profile->hz = 250;
reported_rate = 0x03;
}
if (device_version == 1) {
buf_len = STEELSERIES_REPORT_SIZE_SHORT;
buf[0] = STEELSERIES_ID_REPORT_RATE_SHORT;
buf[2] = 1000 / profile->hz;
} else if (device_version == 2) {
msg.msg.parameters[0] = STEELSERIES_ID_REPORT_RATE_SHORT;
msg.msg.parameters[2] = reported_rate;
break;
case 2:
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_REPORT_RATE;
buf[2] = 1000 / profile->hz;
} else if (device_version == 3) {
msg.msg.parameters[0] = STEELSERIES_ID_REPORT_RATE;
msg.msg.parameters[2] = 1000 / profile->hz;
break;
case 3:
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_REPORT_RATE_PROTOCOL3;
buf[2] = 1000 / profile->hz;
} else {
msg.msg.parameters[0] = STEELSERIES_ID_REPORT_RATE_PROTOCOL3;
msg.msg.parameters[2] = 1000 / profile->hz;
break;
default:
return -ENOTSUP;
}
msleep(10);
ret = ratbag_hidraw_output_report(device, buf, buf_len);
ret = ratbag_hidraw_output_report(device, msg.data, buf_len);
if (ret < 0)
return ret;
@@ -477,15 +565,17 @@ steelseries_write_buttons(struct ratbag_profile *profile)
if (ratbag_device_data_steelseries_get_macro_length(device->data) == 0)
return 0;
int short_button = ratbag_device_data_steelseries_get_short_button(device->data);
int button_size = (short_button == 0) ? 5 : 3;
int report_size = (short_button == 0) ? STEELSERIES_REPORT_LONG_SIZE : STEELSERIES_REPORT_SIZE_SHORT;
int max_modifiers = (short_button == 0) ? 3 : 0;
const bool is_senseiraw = ratbag_device_data_steelseries_get_quirk(device->data) == STEELSERIES_QUIRK_SENSEIRAW;
const int button_size = is_senseiraw ? 3 : 5;
const int report_size = is_senseiraw ? STEELSERIES_REPORT_SIZE_SHORT : STEELSERIES_REPORT_LONG_SIZE;
const int max_modifiers = is_senseiraw ? 0 : 3;
uint8_t buf[report_size];
memset(buf, 0, report_size);
union steelseries_message msg = {
.msg.report_id = STEELSERIES_REPORT_ID,
.msg.parameters = {0},
};
buf[0] = STEELSERIES_ID_BUTTONS;
msg.msg.parameters[0] = STEELSERIES_ID_BUTTONS;
ratbag_profile_for_each_button(profile, button) {
struct ratbag_button_action *action = &button->action;
@@ -498,7 +588,7 @@ steelseries_write_buttons(struct ratbag_profile *profile)
switch (action->type) {
case RATBAG_BUTTON_ACTION_TYPE_BUTTON:
buf[idx] = action->action.button;
msg.msg.parameters[idx] = action->action.button;
break;
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
@@ -515,48 +605,48 @@ steelseries_write_buttons(struct ratbag_profile *profile)
code = ratbag_hidraw_get_keyboard_usage_from_keycode(
device, key);
if (code) {
if (short_button) {
buf[idx] = STEELSERIES_BUTTON_KEY;
if (is_senseiraw) {
msg.msg.parameters[idx] = STEELSERIES_BUTTON_KEY;
} else {
buf[idx] = STEELSERIES_BUTTON_KBD;
msg.msg.parameters[idx] = STEELSERIES_BUTTON_KBD;
if (modifiers & MODIFIER_LEFTCTRL)
buf[++idx] = 0xE0;
msg.msg.parameters[++idx] = 0xE0;
if (modifiers & MODIFIER_LEFTSHIFT)
buf[++idx] = 0xE1;
msg.msg.parameters[++idx] = 0xE1;
if (modifiers & MODIFIER_LEFTALT)
buf[++idx] = 0xE2;
msg.msg.parameters[++idx] = 0xE2;
if (modifiers & MODIFIER_LEFTMETA)
buf[++idx] = 0xE3;
msg.msg.parameters[++idx] = 0xE3;
if (modifiers & MODIFIER_RIGHTCTRL)
buf[++idx] = 0xE4;
msg.msg.parameters[++idx] = 0xE4;
if (modifiers & MODIFIER_RIGHTSHIFT)
buf[++idx] = 0xE5;
msg.msg.parameters[++idx] = 0xE5;
if (modifiers & MODIFIER_RIGHTALT)
buf[++idx] = 0xE6;
msg.msg.parameters[++idx] = 0xE6;
if (modifiers & MODIFIER_RIGHTMETA)
buf[++idx] = 0xE7;
msg.msg.parameters[++idx] = 0xE7;
}
buf[idx + 1] = code;
msg.msg.parameters[idx + 1] = code;
} else {
code = ratbag_hidraw_get_consumer_usage_from_keycode(
device, action->action.key.key);
buf[idx] = STEELSERIES_BUTTON_CONSUMER;
buf[idx + 1] = code;
msg.msg.parameters[idx] = STEELSERIES_BUTTON_CONSUMER;
msg.msg.parameters[idx + 1] = code;
}
break;
case RATBAG_BUTTON_ACTION_TYPE_SPECIAL:
switch (action->action.special) {
case RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_CYCLE_UP:
buf[idx] = STEELSERIES_BUTTON_RES_CYCLE;
msg.msg.parameters[idx] = STEELSERIES_BUTTON_RES_CYCLE;
break;
case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_UP:
buf[idx] = STEELSERIES_BUTTON_WHEEL_UP;
msg.msg.parameters[idx] = STEELSERIES_BUTTON_WHEEL_UP;
break;
case RATBAG_BUTTON_ACTION_SPECIAL_WHEEL_DOWN:
buf[idx] = STEELSERIES_BUTTON_WHEEL_DOWN;
msg.msg.parameters[idx] = STEELSERIES_BUTTON_WHEEL_DOWN;
break;
default:
break;
@@ -566,7 +656,7 @@ steelseries_write_buttons(struct ratbag_profile *profile)
case RATBAG_BUTTON_ACTION_TYPE_KEY:
case RATBAG_BUTTON_ACTION_TYPE_NONE:
default:
buf[idx] = STEELSERIES_BUTTON_OFF;
msg.msg.parameters[idx] = STEELSERIES_BUTTON_OFF;
break;
}
}
@@ -574,9 +664,10 @@ steelseries_write_buttons(struct ratbag_profile *profile)
msleep(10);
if (device_version == 3)
ret = ratbag_hidraw_raw_request(device, STEELSERIES_ID_BUTTONS,
buf,sizeof(buf),HID_FEATURE_REPORT,HID_REQ_SET_REPORT);
msg.msg.parameters, report_size - 1,
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
else
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
ret = ratbag_hidraw_output_report(device, msg.data, report_size);
if (ret < 0)
return ret;
@@ -588,19 +679,34 @@ static int
steelseries_write_led_v1(struct ratbag_led *led)
{
struct ratbag_device *device = led->profile->device;
uint8_t buf[STEELSERIES_REPORT_SIZE_SHORT] = {0};
const enum steelseries_quirk quirk = ratbag_device_data_steelseries_get_quirk(device->data);
int ret;
buf[0] = STEELSERIES_ID_LED_EFFECT_SHORT;
buf[1] = led->index + 1;
union steelseries_message msg = {
.msg.report_id = STEELSERIES_REPORT_ID,
.msg.parameters = {0},
};
msg.msg.parameters[0] = STEELSERIES_ID_LED_EFFECT_SHORT;
msg.msg.parameters[1] = quirk == STEELSERIES_QUIRK_RIVAL100 ? 0x00 : led->index + 1;
switch(led->mode) {
case RATBAG_LED_OFF:
case RATBAG_LED_ON:
buf[2] = 0x01;
msg.msg.parameters[2] = 0x01;
break;
case RATBAG_LED_BREATHING:
buf[2] = 0x03; /* 0x02: slow, 0x03: medium, 0x04: fast */
/* 0x2/3/4 - speed (by eye it's 3, 5 and 7 seconds) */
if (led->ms <= 3000) {
led->ms = 3000;
msg.msg.parameters[2] = 0x04;
} else if (led->ms <= 5000) {
led->ms = 5000;
msg.msg.parameters[2] = 0x03;
} else {
led->ms = 7000;
msg.msg.parameters[2] = 0x02;
}
break;
case RATBAG_LED_CYCLE:
/* Cycle mode is not supported on this version */
@@ -609,31 +715,36 @@ steelseries_write_led_v1(struct ratbag_led *led)
}
msleep(10);
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
ret = ratbag_hidraw_output_report(device, msg.data, STEELSERIES_REPORT_SIZE_SHORT);
if (ret < 0)
return ret;
int mono_led = ratbag_device_data_steelseries_get_mono_led(device->data);
memset(msg.data, 0, STEELSERIES_REPORT_SIZE); // reset the msg buffer before reusing
if (mono_led) {
buf[0] = STEELSERIES_ID_LED_INTENSITY_SHORT;
buf[1] = led->index + 1;
if (quirk == STEELSERIES_QUIRK_SENSEIRAW) {
msg.msg.parameters[0] = STEELSERIES_ID_LED_INTENSITY_SHORT;
msg.msg.parameters[1] = led->index + 1;
if (led->mode == RATBAG_LED_OFF || led->brightness == 0) {
buf[2] = 1;
msg.msg.parameters[2] = 1;
} else {
//split the brightness into roughly 3 equal intensities
buf[2] = (led->brightness / 86) + 2;
msg.msg.parameters[2] = (led->brightness / 86) + 2;
}
} else {
buf[0] = STEELSERIES_ID_LED_COLOR_SHORT;
buf[1] = led->index + 1;
buf[2] = led->color.red;
buf[3] = led->color.green;
buf[4] = led->color.blue;
if (quirk != STEELSERIES_QUIRK_RIVAL100) {
msg.msg.parameters[0] = STEELSERIES_ID_LED_COLOR_SHORT;
msg.msg.parameters[1] = led->index + 1;
} else {
msg.msg.parameters[0] = STEELSERIES_ID_LED_COLOR_SHORT_RIVAL100;
msg.msg.parameters[1] = 0x00;
}
msg.msg.parameters[2] = led->color.red;
msg.msg.parameters[3] = led->color.green;
msg.msg.parameters[4] = led->color.blue;
}
msleep(10);
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
ret = ratbag_hidraw_output_report(device, msg.data, STEELSERIES_REPORT_SIZE_SHORT);
if (ret < 0)
return ret;
@@ -709,10 +820,14 @@ steelseries_write_led_cycle(struct ratbag_led *led,
struct steelseries_led_cycle_spec *cycle_spec)
{
struct ratbag_device *device = led->profile->device;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
int ret;
union steelseries_message msg = {
.msg.report_id = STEELSERIES_REPORT_ID,
.msg.parameters = {0},
};
struct steelseries_led_cycle cycle;
struct steelseries_point point[4];
@@ -781,14 +896,15 @@ steelseries_write_led_cycle(struct ratbag_led *led,
return -EINVAL;
}
construct_cycle_buffer(&cycle, cycle_spec, buf, sizeof(buf));
construct_cycle_buffer(&cycle, cycle_spec, msg.msg.parameters, sizeof(msg.msg.parameters));
msleep(10);
if (device_version == 3)
ret = ratbag_hidraw_raw_request(device, cycle_spec->cmd_val, buf,
sizeof(buf), cycle_spec->hid_report_type, HID_REQ_SET_REPORT);
ret = ratbag_hidraw_raw_request(device, cycle_spec->cmd_val, msg.msg.parameters,
sizeof(msg.msg.parameters), cycle_spec->hid_report_type,
HID_REQ_SET_REPORT);
else
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
ret = ratbag_hidraw_output_report(device, msg.data, sizeof(msg.data));
if (ret < 0)
return ret;
@@ -855,23 +971,27 @@ steelseries_write_save(struct ratbag_device *device)
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
int ret;
size_t buf_len;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
union steelseries_message msg = {
.msg.report_id = STEELSERIES_REPORT_ID,
.msg.parameters = {0},
};
if (device_version == 1) {
buf_len = STEELSERIES_REPORT_SIZE_SHORT;
buf[0] = STEELSERIES_ID_SAVE_SHORT;
msg.msg.parameters[0] = STEELSERIES_ID_SAVE_SHORT;
} else if (device_version == 2) {
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_SAVE;
msg.msg.parameters[0] = STEELSERIES_ID_SAVE;
} else if (device_version == 3) {
buf_len = STEELSERIES_REPORT_SIZE;
buf[0] = STEELSERIES_ID_SAVE_PROTOCOL3;
msg.msg.parameters[0] = STEELSERIES_ID_SAVE_PROTOCOL3;
} else {
return -ENOTSUP;
}
msleep(20);
ret = ratbag_hidraw_output_report(device, buf, buf_len);
ret = ratbag_hidraw_output_report(device, msg.data, buf_len);
if (ret < 0)
return ret;
@@ -887,9 +1007,11 @@ steelseries_write_profile(struct ratbag_profile *profile)
int rc;
bool buttons_dirty = false;
rc = steelseries_write_report_rate(profile);
if (rc != 0)
return rc;
if (profile->rate_dirty) {
rc = steelseries_write_report_rate(profile);
if (rc != 0)
return rc;
}
ratbag_profile_for_each_resolution(profile, resolution) {
if (!resolution->dirty)

7
src/driver-steelseries.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
enum steelseries_quirk {
STEELSERIES_QURIK_NONE,
STEELSERIES_QUIRK_RIVAL100,
STEELSERIES_QUIRK_SENSEIRAW,
};

View File

@@ -29,14 +29,6 @@
#include "libratbag-private.h"
#include "libratbag-test.h"
static int
test_write_profile(struct ratbag_profile *profile)
{
/* check if the device is still valid */
assert(ratbag_get_drv_data(profile->device) != NULL);
return 0;
}
static int
test_set_active_profile(struct ratbag_device *device, unsigned int index)
{
@@ -136,26 +128,6 @@ test_read_led(struct ratbag_led *led)
led->type = d->profiles[0].leds[led->index].type;
}
static int
test_write_button(struct ratbag_button *button,
const struct ratbag_button_action *action)
{
/* check if the device is still valid */
assert(ratbag_get_drv_data(button->profile->device) != NULL);
return 0;
}
static int
test_write_led(struct ratbag_led *led,
enum ratbag_led_mode mode,
struct ratbag_color color, unsigned int ms,
unsigned int brightness)
{
/* check if the device is still valid */
assert(ratbag_get_drv_data(led->profile->device) != NULL);
return 0;
}
static int
test_fake_probe(struct ratbag_device *device)
{
@@ -285,15 +257,21 @@ test_remove(struct ratbag_device *device)
free(d);
}
static int
test_commit(struct ratbag_device *device)
{
/* check if the device is still valid */
assert(ratbag_get_drv_data(device) != NULL);
return 0;
}
struct ratbag_driver test_driver = {
.name = "Test driver",
.id = "test_driver",
.probe = test_fake_probe,
.test_probe = test_probe,
.remove = test_remove,
.write_profile = test_write_profile,
.commit = test_commit,
.set_active_profile = test_set_active_profile,
.write_button = test_write_button,
.write_resolution_dpi = NULL,
.write_led = test_write_led,
};

View File

@@ -58,6 +58,7 @@ hidpp20_feature_get_name(uint16_t feature)
CASE_RETURN_STRING(HIDPP_PAGE_RESET);
CASE_RETURN_STRING(HIDPP_PAGE_BATTERY_LEVEL_STATUS);
CASE_RETURN_STRING(HIDPP_PAGE_BATTERY_VOLTAGE);
CASE_RETURN_STRING(HIDPP_PAGE_LED_SW_CONTROL);
CASE_RETURN_STRING(HIDPP_PAGE_KBD_REPROGRAMMABLE_KEYS);
CASE_RETURN_STRING(HIDPP_PAGE_SPECIAL_KEYS_BUTTONS);
CASE_RETURN_STRING(HIDPP_PAGE_WIRELESS_DEVICE_STATUS);
@@ -2082,7 +2083,7 @@ hidpp20_onboard_profiles_set_onboard_mode(struct hidpp20_device *device,
.msg.report_id = REPORT_ID_SHORT,
.msg.device_idx = device->index,
.msg.address = CMD_ONBOARD_PROFILES_SET_ONBOARD_MODE,
.msg.parameters[1] = onboard_mode,
.msg.parameters[0] = onboard_mode,
};
feature_index = hidpp_root_get_feature_idx(device,
@@ -2102,7 +2103,7 @@ hidpp20_onboard_profiles_set_onboard_mode(struct hidpp20_device *device,
int
hidpp20_onboard_profiles_get_current_profile(struct hidpp20_device *device)
{
uint8_t feature_index;
uint8_t feature_index, unknown_0, active_profile_index;
int rc;
union hidpp20_message msg = {
.msg.report_id = REPORT_ID_SHORT,
@@ -2121,7 +2122,15 @@ hidpp20_onboard_profiles_get_current_profile(struct hidpp20_device *device)
if (rc)
return rc;
return msg.msg.parameters[1];
unknown_0 = msg.msg.parameters[0];
active_profile_index = msg.msg.parameters[1];
hidpp_log_raw(
&device->base,
"current active profile is index: %d unknown_0: %d\n",
active_profile_index, unknown_0);
return active_profile_index;
}
int
@@ -2243,8 +2252,7 @@ hidpp20_onboard_profiles_allocate(struct hidpp20_device *device,
{
struct hidpp20_onboard_profiles_info info = { 0 };
struct hidpp20_profiles *profiles;
int onboard_mode;
int rc;
int onboard_mode, active_profile_index, rc;
rc = hidpp20_onboard_profiles_get_profiles_desc(device, &info);
if (rc)
@@ -2268,6 +2276,12 @@ hidpp20_onboard_profiles_allocate(struct hidpp20_device *device,
return rc;
}
rc = hidpp20_onboard_profiles_get_current_profile(device);
if (rc < 0)
return rc;
active_profile_index = rc;
profiles = zalloc(sizeof(struct hidpp20_profiles));
profiles->profiles = zalloc(info.profile_count * sizeof(struct hidpp20_profile));
profiles->sector_size = info.sector_size;
@@ -2279,6 +2293,7 @@ hidpp20_onboard_profiles_allocate(struct hidpp20_device *device,
profiles->num_leds = HIDPP20_LED_COUNT;
profiles->has_g_shift = (info.mechanical_layout & 0x03) == 0x02;
profiles->has_dpi_shift = ((info.mechanical_layout & 0x0c) >> 2) == 0x02;
profiles->active_profile_index = active_profile_index;
switch(info.various_info & 0x07) {
case 1:
profiles->corded = 1;

View File

@@ -210,21 +210,21 @@ struct hidpp20_led_sw_ctrl_led_state {
uint8_t index;
uint16_t mode;
union {
struct {
struct hidpp20_led_breathing {
uint16_t brightness;
uint16_t period;
uint16_t timeout;
} breathing;
struct {
struct hidpp20_led_traveling {
uint16_t unused;
uint16_t delay;
} traveling;
struct {
struct hidpp20_led_blink {
uint16_t index;
uint16_t on_time;
uint16_t off_time;
} blink;
struct {
struct hidpp20_led_on {
/* Logical information to display on the LED
* Meaning and value range depend on the LED
*/
@@ -915,6 +915,7 @@ struct hidpp20_profiles {
uint8_t wireless;
uint8_t sector_count;
uint16_t sector_size;
uint8_t active_profile_index;
struct hidpp20_profile *profiles;
};

View File

@@ -30,10 +30,14 @@
#include <glib.h>
#include <limits.h>
#include "asus.h"
#include "driver-sinowealth.h"
#include "driver-steelseries.h"
#include "libratbag.h"
#include "libratbag-private.h"
#include "libratbag-data.h"
#include "hidpp20.h"
#include "shared-macro.h"
#include "usb-ids.h"
#define GROUP_DEVICE "Device"
@@ -47,11 +51,14 @@ enum driver {
HIDPP10,
HIDPP20,
ROCCAT,
ROCCAT_KONE_PURE,
ROCCAT_EMP,
ETEKCITY,
GSKILL,
LOGITECH_G300,
LOGITECH_G600,
STEELSERIES,
ASUS,
SINOWEALTH,
SINOWEALTH_NUBWO,
OPENINPUT,
@@ -61,6 +68,8 @@ struct data_hidpp20 {
int index;
enum hidpp20_quirk quirk;
int led_count;
int report_rate;
int button_count;
};
struct data_hidpp10 {
@@ -73,6 +82,10 @@ struct data_hidpp10 {
int led_count;
};
struct data_sinowealth {
struct list supported_devices;
};
struct data_steelseries {
int device_version;
int button_count;
@@ -80,8 +93,18 @@ struct data_steelseries {
struct dpi_list *dpi_list;
struct dpi_range *dpi_range;
int macro_length;
int mono_led;
int short_button;
enum steelseries_quirk quirk;
};
struct data_asus {
int profile_count;
int button_count;
int8_t button_mapping[ASUS_MAX_NUM_BUTTON];
int led_count;
int dpi_count;
int is_wireless;
struct dpi_range *dpi_range;
uint32_t quirks;
};
struct ratbag_device_data {
@@ -94,7 +117,9 @@ struct ratbag_device_data {
union {
struct data_hidpp20 hidpp20;
struct data_hidpp10 hidpp10;
struct data_sinowealth sinowealth;
struct data_steelseries steelseries;
struct data_asus asus;
};
enum ratbag_led_type led_types[20];
@@ -161,9 +186,18 @@ init_data_hidpp20(struct ratbag *ratbag,
int num;
char *str;
data->hidpp20.button_count = -1;
data->hidpp20.index = -1;
data->hidpp20.led_count = -1;
data->hidpp20.report_rate = -1;
num = g_key_file_get_integer(keyfile, group, "Buttons", &error);
if (num > 0 || !error)
data->hidpp20.button_count = num;
if (error)
g_error_free(error);
error = NULL;
num = g_key_file_get_integer(keyfile, group, "DeviceIndex", &error);
if (num != 0 || !error)
data->hidpp20.index = num;
@@ -177,6 +211,13 @@ init_data_hidpp20(struct ratbag *ratbag,
if (error)
g_error_free(error);
error = NULL;
num = g_key_file_get_integer(keyfile, group, "ReportRate", &error);
if (num > 0 || !error)
data->hidpp20.report_rate = 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) {
@@ -187,6 +228,56 @@ init_data_hidpp20(struct ratbag *ratbag,
}
}
static void
init_data_sinowealth(struct ratbag *ratbag,
GKeyFile *keyfile,
struct ratbag_device_data *data)
{
const char *devices_group = "Driver/sinowealth/devices/";
GError *error = NULL;
size_t group_count = 0;
_cleanup_(g_strfreevp) char **groups = g_key_file_get_groups(keyfile, &group_count);
list_init(&data->sinowealth.supported_devices);
for (size_t i = 0; i < group_count; ++i) {
const char *device_group = groups[i];
if (startswith(device_group, devices_group) == NULL)
continue;
struct sinowealth_device_data *device = zalloc(sizeof(struct sinowealth_device_data));
device->button_count = g_key_file_get_integer(keyfile, device_group, "ButtonCount", &error);
g_clear_error(&error);
device->device_name = g_key_file_get_string(keyfile, device_group, "DeviceName", &error);
g_clear_error(&error);
device->fw_version = g_key_file_get_string(keyfile, device_group, "FwVersion", &error);
g_clear_error(&error);
_cleanup_free_ char *led_type_str = g_key_file_get_string(keyfile, device_group, "LedType", &error);
if (led_type_str) {
if (streq(led_type_str, "RGB")) {
device->led_type = SINOWEALTH_LED_TYPE_RGB;
} else if (streq(led_type_str, "RBG")) {
device->led_type = SINOWEALTH_LED_TYPE_RBG;
} else if (streq(led_type_str, "None")) {
device->led_type = SINOWEALTH_LED_TYPE_NONE;
} else {
log_error(ratbag, "Unknown LED type '%s' in group '%s'\n", led_type_str, device_group);
device->led_type = SINOWEALTH_LED_TYPE_NONE;
}
}
g_clear_error(&error);
list_insert(&data->sinowealth.supported_devices, &device->link);
}
}
static void
init_data_steelseries(struct ratbag *ratbag,
GKeyFile *keyfile,
@@ -194,7 +285,8 @@ init_data_steelseries(struct ratbag *ratbag,
{
const char *group = "Driver/steelseries";
GError *error = NULL;
_cleanup_(freep) char *str = NULL;
_cleanup_(freep) char *dpi_range = NULL;
_cleanup_(freep) char *quirk = NULL;
int num;
data->steelseries.device_version = -1;
@@ -202,8 +294,7 @@ init_data_steelseries(struct ratbag *ratbag,
data->steelseries.led_count = -1;
data->steelseries.dpi_list = NULL;
data->steelseries.dpi_range = NULL;
data->steelseries.mono_led = 0;
data->steelseries.short_button = 0;
data->steelseries.quirk = STEELSERIES_QURIK_NONE;
num = g_key_file_get_integer(keyfile, group, "Buttons", &error);
if (num != 0 || !error)
@@ -225,13 +316,13 @@ init_data_steelseries(struct ratbag *ratbag,
if (error)
g_error_free(error);
str = g_key_file_get_string(keyfile, group, "DpiRange", NULL);
if (str) {
data->steelseries.dpi_range = dpi_range_from_string(str);
dpi_range = g_key_file_get_string(keyfile, group, "DpiRange", NULL);
if (dpi_range) {
data->steelseries.dpi_range = dpi_range_from_string(dpi_range);
} else {
str = g_key_file_get_string(keyfile, group, "DpiList", NULL);
if (str)
data->steelseries.dpi_list = dpi_list_from_string(str);
dpi_range = g_key_file_get_string(keyfile, group, "DpiList", NULL);
if (dpi_range)
data->steelseries.dpi_list = dpi_list_from_string(dpi_range);
}
error = NULL;
@@ -241,19 +332,86 @@ init_data_steelseries(struct ratbag *ratbag,
if (error)
g_error_free(error);
error = NULL;
num = g_key_file_get_integer(keyfile, group, "MonoLed", &error);
if (num > 0 || !error)
data->steelseries.mono_led = num;
if (error)
g_error_free(error);
quirk = g_key_file_get_string(keyfile, group, "Quirk", NULL);
if (quirk) {
if (streq(quirk, "Rival100"))
data->steelseries.quirk = STEELSERIES_QUIRK_RIVAL100;
if (streq(quirk, "SenseiRAW"))
data->steelseries.quirk = STEELSERIES_QUIRK_SENSEIRAW;
}
}
error = NULL;
num = g_key_file_get_integer(keyfile, group, "ShortButton", &error);
if (num > 0 || !error)
data->steelseries.short_button = num;
if (error)
g_error_free(error);
static void
init_data_asus(struct ratbag *ratbag,
GKeyFile *keyfile,
struct ratbag_device_data *data)
{
const char *group = "Driver/asus";
GError *error = NULL;
data->asus.profile_count = -1;
data->asus.button_count = -1;
data->asus.led_count = -1;
data->asus.dpi_count = -1;
data->asus.dpi_range = NULL;
data->asus.is_wireless = -1;
data->asus.quirks = 0;
for (unsigned int i = 0; i < ASUS_MAX_NUM_BUTTON; i++)
data->asus.button_mapping[i] = -1;
int profiles = g_key_file_get_integer(keyfile, group, "Profiles", &error);
if (!error && profiles >= 0)
data->asus.profile_count = profiles;
g_clear_error(&error);
int buttons = g_key_file_get_integer(keyfile, group, "Buttons", &error);
if (!error && buttons >= 0 && buttons <= ASUS_MAX_NUM_BUTTON)
data->asus.button_count = buttons;
g_clear_error(&error);
gsize button_mapping_count = 0;
_cleanup_(g_strfreevp) char **button_mapping = g_key_file_get_string_list(keyfile, group, "ButtonMapping", &button_mapping_count, &error);
if (!error && button_mapping) {
for (unsigned int i = 0; i < button_mapping_count; i++) {
data->asus.button_mapping[i] = (int8_t) strtoul(button_mapping[i], NULL, 10);
}
}
g_clear_error(&error);
int leds = g_key_file_get_integer(keyfile, group, "Leds", &error);
if (!error && leds >= 0 && leds <= ASUS_MAX_NUM_LED)
data->asus.led_count = leds;
g_clear_error(&error);
int dpis = g_key_file_get_integer(keyfile, group, "Dpis", &error);
if (!error && dpis >= 2 && dpis <= ASUS_MAX_NUM_DPI)
data->asus.dpi_count = dpis;
g_clear_error(&error);
_cleanup_(freep) char *dpi_range = g_key_file_get_string(keyfile, group, "DpiRange", &error);
if (!error && dpi_range)
data->asus.dpi_range = dpi_range_from_string(dpi_range);
g_clear_error(&error);
int wireless = g_key_file_get_integer(keyfile, group, "Wireless", &error);
if (!error && (wireless == 0 || wireless == 1))
data->asus.is_wireless = wireless;
g_clear_error(&error);
gsize quirks_count = 0;
_cleanup_(g_strfreevp) char **quirks = g_key_file_get_string_list(keyfile, group, "Quirks", &quirks_count, &error);
if (!error && quirks) {
for (unsigned int i = 0; i < quirks_count; i++) {
if (streq(quirks[i], "DOUBLE_DPI")) {
data->asus.quirks |= ASUS_QUIRK_DOUBLE_DPI;
} else if (streq(quirks[i], "STRIX_PROFILE")) {
data->asus.quirks |= ASUS_QUIRK_STRIX_PROFILE;
} else {
log_debug(ratbag, "%s is invalid quirk. Ignoring...\n", quirks[i]);
}
}
}
g_clear_error(&error);
}
static const struct driver_map {
@@ -266,12 +424,15 @@ static const struct driver_map {
{ HIDPP10, "hidpp10", init_data_hidpp10 },
{ HIDPP20, "hidpp20", init_data_hidpp20 },
{ ROCCAT, "roccat", NULL },
{ ROCCAT_KONE_PURE, "roccat-kone-pure", NULL },
{ ROCCAT_EMP, "roccat-kone-emp", NULL },
{ ETEKCITY, "etekcity", NULL},
{ GSKILL, "gskill", NULL },
{ LOGITECH_G300, "logitech_g300", NULL},
{ LOGITECH_G600, "logitech_g600", NULL},
{ STEELSERIES, "steelseries", init_data_steelseries },
{ SINOWEALTH, "sinowealth", NULL },
{ ASUS, "asus", init_data_asus },
{ SINOWEALTH, "sinowealth", init_data_sinowealth },
{ SINOWEALTH_NUBWO, "sinowealth_nubwo", NULL},
{ OPENINPUT, "openinput", NULL },
};
@@ -309,8 +470,34 @@ ratbag_device_data_destroy(struct ratbag_device_data *data)
{
switch (data->drivertype) {
case HIDPP10:
if (data->hidpp10.dpi_list) {
free(data->hidpp10.dpi_list->entries);
free(data->hidpp10.dpi_list);
}
free(data->hidpp10.dpi_range);
free(data->hidpp10.profile_type);
break;
case SINOWEALTH: {
struct sinowealth_device_data *device_data = NULL;
struct sinowealth_device_data *device_data_next = NULL;
list_for_each_safe(device_data, device_data_next, &data->sinowealth.supported_devices, link) {
free(device_data->device_name);
free(device_data->fw_version);
free(device_data);
}
break;
}
case STEELSERIES:
if (data->steelseries.dpi_list) {
free(data->steelseries.dpi_list->entries);
free(data->steelseries.dpi_list);
}
free(data->steelseries.dpi_range);
break;
default:
break;
}
@@ -594,6 +781,14 @@ ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data)
return data->hidpp20.index;
}
int
ratbag_device_data_hidpp20_get_button_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP20);
return data->hidpp20.button_count;
}
int
ratbag_device_data_hidpp20_get_led_count(const struct ratbag_device_data *data)
{
@@ -602,6 +797,14 @@ ratbag_device_data_hidpp20_get_led_count(const struct ratbag_device_data *data)
return data->hidpp20.led_count;
}
int
ratbag_device_data_hidpp20_get_report_rate(const struct ratbag_device_data *data)
{
assert(data->drivertype == HIDPP20);
return data->hidpp20.report_rate;
}
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data)
{
@@ -610,6 +813,16 @@ ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data)
return data->hidpp20.quirk;
}
/* SinoWealth */
const struct list *
ratbag_device_data_sinowealth_get_supported_devices(const struct ratbag_device_data *data)
{
assert(data->drivertype == SINOWEALTH);
return &data->sinowealth.supported_devices;
}
/* SteelSeries */
int
@@ -660,18 +873,68 @@ ratbag_device_data_steelseries_get_macro_length(const struct ratbag_device_data
return data->steelseries.macro_length;
}
int
ratbag_device_data_steelseries_get_mono_led(const struct ratbag_device_data *data)
enum steelseries_quirk
ratbag_device_data_steelseries_get_quirk(const struct ratbag_device_data *data)
{
assert(data->drivertype == STEELSERIES);
return data->steelseries.mono_led;
return data->steelseries.quirk;
}
/* ASUS */
int
ratbag_device_data_asus_get_profile_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == ASUS);
return data->asus.profile_count;
}
int
ratbag_device_data_steelseries_get_short_button(const struct ratbag_device_data *data)
ratbag_device_data_asus_get_button_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == STEELSERIES);
return data->steelseries.short_button;
assert(data->drivertype == ASUS);
return data->asus.button_count;
}
const int8_t *
ratbag_device_data_asus_get_button_mapping(const struct ratbag_device_data *data)
{
assert(data->drivertype == ASUS);
return data->asus.button_mapping;
}
int
ratbag_device_data_asus_get_led_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == ASUS);
return data->asus.led_count;
}
int
ratbag_device_data_asus_get_dpi_count(const struct ratbag_device_data *data)
{
assert(data->drivertype == ASUS);
return data->asus.dpi_count;
}
struct dpi_range *
ratbag_device_data_asus_get_dpi_range(const struct ratbag_device_data *data)
{
assert(data->drivertype == ASUS);
return data->asus.dpi_range;
}
int
ratbag_device_data_asus_is_wireless(const struct ratbag_device_data *data)
{
assert(data->drivertype == ASUS);
return data->asus.is_wireless;
}
uint32_t
ratbag_device_data_asus_get_quirks(const struct ratbag_device_data *data)
{
assert(data->drivertype == ASUS);
return data->asus.quirks;
}

View File

@@ -23,6 +23,8 @@
#pragma once
#include "libratbag-private.h"
struct ratbag_device_data;
struct ratbag_device_data *
@@ -80,12 +82,28 @@ ratbag_device_data_hidpp10_get_led_count(const struct ratbag_device_data *data);
int
ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data);
int
ratbag_device_data_hidpp20_get_button_count(const struct ratbag_device_data *data);
int
ratbag_device_data_hidpp20_get_led_count(const struct ratbag_device_data *data);
int
ratbag_device_data_hidpp20_get_report_rate(const struct ratbag_device_data *data);
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data);
/* SinoWealth */
/*
* NOTE: Don't forget to check for NULL in fields of each device data entry.
*
* @return List of data for supported devices.
*/
const struct list *
ratbag_device_data_sinowealth_get_supported_devices(const struct ratbag_device_data *data);
/* SteelSeries */
/**
@@ -115,8 +133,58 @@ ratbag_device_data_steelseries_get_dpi_range(const struct ratbag_device_data *da
int
ratbag_device_data_steelseries_get_macro_length(const struct ratbag_device_data *data);
int
ratbag_device_data_steelseries_get_mono_led(const struct ratbag_device_data *data);
/**
* @return Quirk
*/
enum steelseries_quirk
ratbag_device_data_steelseries_get_quirk(const struct ratbag_device_data *data);
/* ASUS */
/**
* @return Number of profiles
*/
int
ratbag_device_data_steelseries_get_short_button(const struct ratbag_device_data *data);
ratbag_device_data_asus_get_profile_count(const struct ratbag_device_data *data);
/**
* @return Number of buttons
*/
int
ratbag_device_data_asus_get_button_count(const struct ratbag_device_data *data);
/**
* @return Array of button indices, which are used for reading and writing button actions
*/
const int8_t *
ratbag_device_data_asus_get_button_mapping(const struct ratbag_device_data *data);
/**
* @return Number of LEDs
*/
int
ratbag_device_data_asus_get_led_count(const struct ratbag_device_data *data);
/**
* @return Number of DPI presets
*/
int
ratbag_device_data_asus_get_dpi_count(const struct ratbag_device_data *data);
/**
* @return NULL if not set
*/
struct dpi_range *
ratbag_device_data_asus_get_dpi_range(const struct ratbag_device_data *data);
/**
* @return 1 if wireless, 0 otherwise
*/
int
ratbag_device_data_asus_is_wireless(const struct ratbag_device_data *data);
/**
* @return Quirks bitmask
*/
uint32_t
ratbag_device_data_asus_get_quirks(const struct ratbag_device_data *data);

View File

@@ -1520,6 +1520,21 @@ ratbag_hidraw_get_usage(struct ratbag_device *device, unsigned int report_id)
return report->usage;
}
unsigned int
ratbag_hidraw_has_vendor_page(struct ratbag_device *device)
{
unsigned i;
if (ratbag_hidraw_get_usage_page(device, 0) >= 0xff00)
return 1;
for (i = 0; i < device->hidraw[0].num_reports; i++) {
if (ratbag_hidraw_get_usage_page(device, device->hidraw[0].reports[i].report_id) >= 0xff00)
return 1;
}
return 0;
}
void
ratbag_close_hidraw(struct ratbag_device *device)
{
@@ -1627,16 +1642,19 @@ ratbag_hidraw_output_report(struct ratbag_device *device, uint8_t *buf, size_t l
}
int
ratbag_hidraw_read_input_report(struct ratbag_device *device, uint8_t *buf, size_t len)
ratbag_hidraw_read_input_report(struct ratbag_device *device, uint8_t *buf, size_t len,
ratbagd_hidraw_filter_t filter)
{
return ratbag_hidraw_read_input_report_index(device, buf, len, 0);
return ratbag_hidraw_read_input_report_index(device, buf, len, 0, filter);
}
int
ratbag_hidraw_read_input_report_index(struct ratbag_device *device, uint8_t *buf, size_t len, int hidrawno)
ratbag_hidraw_read_input_report_index(struct ratbag_device *device, uint8_t *buf, size_t len, int hidrawno,
ratbagd_hidraw_filter_t filter)
{
int rc;
int rc, nfds;
struct pollfd fds;
int ts, ts_end;
assert(hidrawno >= 0 && hidrawno < MAX_HIDRAW);
@@ -1646,17 +1664,32 @@ ratbag_hidraw_read_input_report_index(struct ratbag_device *device, uint8_t *buf
fds.fd = device->hidraw[hidrawno].fd;
fds.events = POLLIN;
rc = poll(&fds, 1, 1000);
if (rc == -1)
return -errno;
/* convert to ms */
ts = now(CLOCK_MONOTONIC_RAW) / 1000 / 1000;
ts_end = ts + 1000; /* end in now + 1000ms */
if (rc == 0)
return -ETIMEDOUT;
while (ts < ts_end) {
nfds = poll(&fds, 1, ts_end - ts); /* poll for the remainder of timeout */
rc = read(device->hidraw[hidrawno].fd, buf, len);
if (nfds < 0)
return -errno;
if (rc > 0)
log_buf_raw(device->ratbag, "input report: ", buf, rc);
if (nfds > 0) {
rc = read(device->hidraw[hidrawno].fd, buf, len);
return rc >= 0 ? rc : -errno;
if (rc < 0)
return -errno;
if (rc > 0) {
if(!filter || (filter && filter(buf, rc))) {
log_buf_raw(device->ratbag, "input report: ", buf, rc);
return rc;
}
}
}
ts = now(CLOCK_MONOTONIC_RAW) / 1000 / 1000;
}
return -ETIMEDOUT;
}

View File

@@ -47,6 +47,8 @@ struct ratbag_hidraw {
char *sysname;
};
typedef bool (*ratbagd_hidraw_filter_t)(uint8_t *buf, size_t len);
/**
* Open the hidraw device associated with the device.
*
@@ -162,27 +164,32 @@ int ratbag_hidraw_output_report(struct ratbag_device *device, uint8_t *buf, size
/**
* Read an input report from the device
* Optional filter function can be provided, when the filter returns false the packet is ignored
*
* @param device the ratbag device
* @param[out] buf resulting raw data
* @param len length of buf
* @param filter filter function
*
* @return count of data transfered, or a negative errno on error
*/
int ratbag_hidraw_read_input_report(struct ratbag_device *device, uint8_t *buf, size_t len);
int ratbag_hidraw_read_input_report(struct ratbag_device *device, uint8_t *buf, size_t len,
ratbagd_hidraw_filter_t filter);
/**
* Read an input report from the device from a specific hidraw index
* Optional filter function can be provided, when the filter returns false the packet is ignored
*
* @param device the ratbag device
* @param[out] buf resulting raw data
* @param len length of buf
* @param interal index of hidraw array
* @param hidrawno index of hidraw array
* @param filter filter function
*
* @return count of data transfered, or a negative errno on error
*/
int ratbag_hidraw_read_input_report_index(struct ratbag_device *device, uint8_t *buf, size_t len, int hidrawno);
int ratbag_hidraw_read_input_report_index(struct ratbag_device *device, uint8_t *buf, size_t len, int hidrawno,
ratbagd_hidraw_filter_t filter);
/**
* Tells if a given device has the specified report ID.
@@ -219,6 +226,16 @@ ratbag_hidraw_get_usage_page(struct ratbag_device *device, unsigned int report_i
unsigned int
ratbag_hidraw_get_usage(struct ratbag_device *device, unsigned int report_id);
/**
* Tells if a given device has a vendor usage page
*
* @param device the ratbag device which hidraw node is opened
*
* @return 1 if the device has a vendor usage page, 0 otherwise
*/
unsigned int
ratbag_hidraw_has_vendor_page(struct ratbag_device *device);
/**
* Gives the input key code associated to the keyboard HID usage.
*

View File

@@ -197,13 +197,6 @@ struct ratbag_driver {
*/
int (*commit)(struct ratbag_device *device);
/*
* FIXME: This function is deprecated and should be removed. Once
* we've updated all the device drivers to stop using it we'll remove
* it. Look at commit() instead.
*/
int (*write_profile)(struct ratbag_profile *profile);
/**
* Called to mark a previously writen profile as active.
*
@@ -212,31 +205,6 @@ struct ratbag_driver {
*/
int (*set_active_profile)(struct ratbag_device *device, unsigned int index);
/*
* FIXME: This function is deprecated and should be removed. Once
* we've updated all the device drivers to stop using it we'll remove
* it. Look at commit() instead.
*/
int (*write_button)(struct ratbag_button *button,
const struct ratbag_button_action *action);
/*
* FIXME: This function is deprecated and should be removed. Once
* we've updated all the device drivers to stop using it we'll remove
* it. Look at commit() instead.
*/
int (*write_resolution_dpi)(struct ratbag_resolution *resolution,
int dpi_x, int dpi_y);
/*
* FIXME: This function is deprecated and should be removed. Once
* we've updated all the device drivers to stop using it we'll remove
* it. Look at commit() instead.
*/
int (*write_led)(struct ratbag_led *led, enum ratbag_led_mode mode,
struct ratbag_color color, unsigned int ms,
unsigned int brightness);
/* private */
int (*test_probe)(struct ratbag_device *device, const void *data);
@@ -462,6 +430,8 @@ ratbag_button_action_match(const struct ratbag_button_action *action,
return 0;
switch (action->type) {
case RATBAG_BUTTON_ACTION_TYPE_NONE:
return 1;
case RATBAG_BUTTON_ACTION_TYPE_BUTTON:
return match->action.button == action->action.button;
case RATBAG_BUTTON_ACTION_TYPE_KEY:
@@ -469,6 +439,7 @@ ratbag_button_action_match(const struct ratbag_button_action *action,
case RATBAG_BUTTON_ACTION_TYPE_SPECIAL:
return match->action.special == action->action.special;
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
/* TODO: check if events match. */
return 1;
default:
break;
@@ -593,8 +564,11 @@ 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 roccat_kone_pure_driver;
extern struct ratbag_driver roccat_emp_driver;
extern struct ratbag_driver gskill_driver;
extern struct ratbag_driver steelseries_driver;
extern struct ratbag_driver asus_driver;
extern struct ratbag_driver sinowealth_driver;
extern struct ratbag_driver sinowealth_nubwo_driver;
extern struct ratbag_driver openinput_driver;

View File

@@ -282,7 +282,6 @@ dpi_list_from_string(const char *str)
if (!str || str[0] == '\0')
return NULL;
printf("%s: ", str);
/* first, count how many elements do we have in the table */
count = 1;

View File

@@ -600,8 +600,11 @@ ratbag_create_context(const struct ratbag_interface *interface,
ratbag_register_driver(ratbag, &logitech_g300_driver);
ratbag_register_driver(ratbag, &logitech_g600_driver);
ratbag_register_driver(ratbag, &roccat_driver);
ratbag_register_driver(ratbag, &roccat_kone_pure_driver);
ratbag_register_driver(ratbag, &roccat_emp_driver);
ratbag_register_driver(ratbag, &gskill_driver);
ratbag_register_driver(ratbag, &steelseries_driver);
ratbag_register_driver(ratbag, &asus_driver);
ratbag_register_driver(ratbag, &sinowealth_driver);
ratbag_register_driver(ratbag, &sinowealth_nubwo_driver);
ratbag_register_driver(ratbag, &openinput_driver);
@@ -861,75 +864,6 @@ ratbag_device_get_num_leds(struct ratbag_device *device)
return device->num_leds;
}
static inline enum ratbag_error_code
write_led_helper(struct ratbag_device *device, struct ratbag_led *led)
{
return device->driver->write_led(led, led->mode, led->color, led->ms, led->brightness);
}
/* FIXME: This is a temporary fix for all of the drivers that have yet to be
* converted to the new profile-oriented API. Once all of the drivers have been
* converted, this code should be removed.
*/
static enum ratbag_error_code
ratbag_old_write_profile(struct ratbag_device *device)
{
struct ratbag_profile *profile;
struct ratbag_button *button;
struct ratbag_led *led;
struct ratbag_resolution *resolution;
int rc;
assert(device->driver->write_profile);
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty)
continue;
rc = device->driver->write_profile(profile);
if (rc)
return RATBAG_ERROR_DEVICE;
if (device->driver->write_resolution_dpi) {
ratbag_profile_for_each_resolution(profile, resolution) {
rc = device->driver->write_resolution_dpi(
resolution, resolution->dpi_x,
resolution->dpi_y);
if (rc)
return RATBAG_ERROR_DEVICE;
}
}
if (device->driver->write_button) {
list_for_each(button, &profile->buttons, link) {
struct ratbag_button_action action =
button->action;
if (!button->dirty)
continue;
rc = device->driver->write_button(button,
&action);
if (rc)
return RATBAG_ERROR_DEVICE;
}
}
if (device->driver->write_led) {
list_for_each(led, &profile->leds, link) {
if (!led->dirty)
continue;
rc = write_led_helper(device, led);
if (rc)
return RATBAG_ERROR_DEVICE;
}
}
}
return RATBAG_SUCCESS;
}
LIBRATBAG_EXPORT enum ratbag_error_code
ratbag_device_commit(struct ratbag_device *device)
{
@@ -939,15 +873,9 @@ ratbag_device_commit(struct ratbag_device *device)
struct ratbag_resolution *resolution;
int rc;
if (!device->driver->commit) {
rc = ratbag_old_write_profile(device);
if (rc != RATBAG_SUCCESS)
return rc;
} else {
rc = device->driver->commit(device);
if (rc)
return RATBAG_ERROR_DEVICE;
}
rc = device->driver->commit(device);
if (rc)
return RATBAG_ERROR_DEVICE;
list_for_each(profile, &device->profiles, link) {
profile->dirty = false;

View File

@@ -26,6 +26,7 @@
import argparse
import configparser
import pathlib
import re
@@ -168,7 +169,7 @@ def check_section_hidpp10(section):
def check_section_hidpp20(section):
permitted = ['DeviceIndex', 'Leds', 'Quirk']
permitted = ['Buttons', 'DeviceIndex', 'Leds', 'ReportRate', 'Quirk']
for key in section.keys():
assertIn(key, permitted)
@@ -220,8 +221,8 @@ def parse_data_file(path):
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Device data-file checker")
parser.add_argument('file', nargs='+')
parser.add_argument("directory")
args = parser.parse_args()
for path in args.file:
for path in pathlib.Path(args.directory).glob("*.device"):
validate_data_file_name(path)
parse_data_file(path)

View File

@@ -24,6 +24,7 @@
import argparse
import os
import pathlib
import sys
import configparser
@@ -40,11 +41,11 @@ def parse_data_file(path):
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Device duplicate match checker')
parser.add_argument('file', nargs='+')
parser.add_argument("directory")
args = parser.parse_args()
device_matches = {}
duplicates = False
for path in args.file:
for path in pathlib.Path(args.directory).glob("*.device"):
matches = parse_data_file(path)
fname = os.path.basename(path)
for m in matches:

View File

@@ -24,12 +24,14 @@
import argparse
import os
import pathlib
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
# https://pwr-solaar.github.io/Solaar/devices.html
logitech_receivers = [
0xc50c, # USB_DEVICE_ID_S510_RECEIVER
0xc517, # USB_DEVICE_ID_S510_RECEIVER_2
@@ -43,6 +45,9 @@ logitech_receivers = [
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
0xc545, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_2
0xc547, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_3
0xc548, # Bolt receiver
]
RECEIVERS = ['usb:046d:{}'.format(r) for r in logitech_receivers]
@@ -59,10 +64,10 @@ def parse_data_file(path):
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Checks that no unifying receiver ID is in the device files')
parser.add_argument('file', nargs='+')
parser.add_argument("directory")
args = parser.parse_args()
receiver_found = False
for path in args.file:
for path in pathlib.Path(args.directory).glob("*.device"):
matches = parse_data_file(path)
fname = os.path.basename(path)
for m in matches:

View File

@@ -23,7 +23,6 @@
#include <config.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <hidpp10.h>
@@ -94,8 +93,10 @@ main(int argc, char **argv)
path = argv[argc - 1];
fd = open(path, O_RDWR);
if (fd < 0)
error(1, errno, "Failed to open path %s", path);
if (fd < 0) {
fprintf(stderr, "Failed to open path '%s': %s", path, strerror(errno));
exit(3);
}
hidpp_device_init(&base, fd);
rc = hidpp10_device_new(&base, HIDPP_WIRED_DEVICE_IDX, HIDPP10_PROFILE_UNKNOWN, 5, &dev);

View File

@@ -23,7 +23,6 @@
#include <config.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <hidpp20.h>
@@ -111,13 +110,17 @@ main(int argc, char **argv)
path = argv[argc - 1];
fd = open(path, O_RDWR);
if (fd < 0)
error(1, errno, "Failed to open path %s", path);
if (fd < 0) {
fprintf(stderr, "Failed to open path '%s': %s", path, strerror(errno));
exit(3);
}
hidpp_device_init(&base, fd);
dev = hidpp20_device_new(&base, 0xff, NULL, 0);
if (!dev)
error(1, 0, "Failed to open %s as a HID++ 2.0 device", path);
if (!dev) {
fprintf(stderr, "Failed to open %s as a HID++ 2.0 device", path);
exit(3);
}
hidpp20_onboard_profiles_get_profiles_desc(dev, &info);

View File

@@ -23,7 +23,6 @@
#include <config.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <hidpp20.h>
@@ -87,13 +86,17 @@ main(int argc, char **argv)
path = argv[argc - 1];
fd = open(path, O_RDWR);
if (fd < 0)
error(1, errno, "Failed to open path %s", path);
if (fd < 0) {
fprintf(stderr, "Failed to open path '%s': %s", path, strerror(errno));
exit(3);
}
hidpp_device_init(&base, fd);
dev = hidpp20_device_new(&base, 0xff, NULL, 0);
if (!dev)
error(1, 0, "Failed to open %s as a HID++ 2.0 device", path);
if (!dev) {
fprintf(stderr, "Failed to open %s as a HID++ 2.0 device", path);
exit(3);
}
hidpp20_onboard_profiles_get_profiles_desc(dev, &info);

View File

@@ -808,7 +808,10 @@ class RatbagdLed(metaclass=MetaRatbag):
@param color An RGB color, as an integer triplet with values 0-255.
"""
libratbag.ratbag_led_set_color(self._led, libratbag.ratbag_color(*color))
# libratbag.ratbag_led_set_color(self._led, libratbag.ratbag_color(*color))
rc = libratbag.ratbag_color()
rc.red, rc.green, rc.blue = color
libratbag.ratbag_led_set_color(self._led, rc)
@property
def colordepth(self):

View File

@@ -2,7 +2,13 @@
.SH NAME
ratbagctl \- inspect and modify configurable mice
.SH SYNOPSIS
.B ratbagctl [OPTIONS] {COMMAND} ... device
.B ratbagctl
.RI [< options >]
.B list
.br
.B ratbagctl
.RI [< options >]
.RI < device "> <" command "> ..."
.SH DESCRIPTION
.PP
The
@@ -21,8 +27,8 @@ Print debugging output. Multiple -v options increase the verbosity. For example,
will show the protocol output.
.TP 8
.B \-\-nocommit
Do not immediately write the settings to the mouse. This allows to set
multiple parameters in a script, and the last call to
Do not immediately write the settings to the mouse. This allows
multiple parameters to be set in a script, and the last call to
.B ratbagctl
will write them all.
.TP 8