Compare commits

..

47 Commits
v0.10 ... v0.12

Author SHA1 Message Date
Filipe Laíns
5357d4cbc2 meson: libratbag 0.12
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-18 19:25:30 +00:00
Filipe Laíns
26c534cc74 hidpp20: check supported report types
Fixes #854

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

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

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

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

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

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

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

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

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

Related to #862

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-12-10 22:48:53 +00:00
Filipe Laíns
47cc5fce92 github: add issue templates
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-05 10:01:59 +00:00
Filipe Laíns
5da791b268 ratbagd: support the old --verbose=raw syntax
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-05 09:44:35 +00:00
Filipe Laíns
e63802b01a ratbagd: make raw the default verbosity level (#857)
Signed-off-by: Filipe Laíns <lains@archlinux.org>
2019-12-05 08:07:46 +10:00
Jeff Huang
cd89b123ce Add Logitech M585/M590 support. (#845) 2019-11-27 15:52:18 +10:00
Peter Hutterer
f6546b3222 circle CI: add diff as fedora package
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-11-18 08:50:36 +10:00
Peter Hutterer
cd8ddc8444 circle CI: switch to Fedora 31
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-11-18 07:16:22 +10:00
Luke
cdb3226ef1 meson: don't use brackets, parenthesis and braces in filenames
Renamed filenames to avoid brackets, and added a check to data-parse-test.py to
check for brackets, parentheses, and braces so they aren't shipped in filenames
in the future.
2019-11-17 00:11:42 +00:00
Peter Hutterer
ce377fc36c meson.build: libratbag 0.11
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-11-05 11:06:27 +10:00
Mateusz Mojsiejuk
fe1308fe38 data: add Steelseries Kinzu V3 2019-11-02 21:21:41 +00:00
FFY00
983d3e2a88 data: fix G502 Hero Wireless .device file
Signed-off-by: FFY00 <lains@archlinux.org>
2019-10-23 18:47:17 +01:00
Filipe Laíns
3a3d9e1148 devices: add Logitech G604
Fixes #827

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

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

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2019-08-04 17:17:45 +02:00
39 changed files with 1334 additions and 188 deletions

View File

@@ -78,7 +78,7 @@ fedora_prep_cache: &fedora_prep_cache
steps:
- run:
name: Initializing Fedora dnf cache
command: dnf install -y --downloadonly libsolv tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml libunistring-devel dbus-daemon json-glib-devel
command: dnf install -y --downloadonly libsolv tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml libunistring-devel dbus-daemon json-glib-devel diffutils
- persist_to_workspace:
root: /var/cache/
paths:
@@ -94,7 +94,7 @@ fedora_install: &fedora_install
command: |
echo keepcache=1 >> /etc/dnf/dnf.conf
dnf upgrade -y libsolv
dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml python3-devel swig libunistring-devel dbus-daemon json-glib-devel
dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml python3-devel swig libunistring-devel dbus-daemon json-glib-devel diffutils
sed -i 's/systemd//' /etc/nsswitch.conf
fedora_settings: &fedora_settings
@@ -193,18 +193,14 @@ docs_deploy: &docs_deploy
version: 2
jobs:
fedora_rawhide:
<<: *fedora_settings
docker:
- image: fedora:rawhide
fedora_cache:
<<: *fedora_prep_cache
docker:
- image: fedora:30
fedora_latest:
- image: fedora:31
fedora_31:
<<: *fedora_settings
docker:
- image: fedora:30
- image: fedora:31
ubuntu_19_04:
<<: *ubuntu_settings
docker:
@@ -212,21 +208,20 @@ jobs:
doc_build:
<<: *doc_build
docker:
- image: fedora:30
- image: fedora:31
doc_deploy:
<<: *docs_deploy
docker:
- image: fedora:30
- image: fedora:31
workflows:
version: 2
compile_and_test:
jobs:
# - fedora_rawhide
- fedora_cache
- ubuntu_19_04
- fedora_latest:
- fedora_31:
requires:
- fedora_cache
- doc_build:

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

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

View File

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

11
.gitignore vendored
View File

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

View File

@@ -27,6 +27,7 @@
import argparse
import configparser
import re
def assertIn(element, l):
@@ -169,7 +170,7 @@ def check_section_hidpp10(section):
def check_section_hidpp20(section):
permitted = ['DeviceIndex']
permitted = ['DeviceIndex', 'Quirk']
for key in section.keys():
assertIn(key, permitted)
@@ -190,6 +191,15 @@ def check_section_driver(driver, section):
assert('Unsupported driver section {}'.format(driver))
def validate_data_file_name(path):
# Matching any of the characters in the regular expression will throw an
# error. Currently only tests the square brackets [], parentheses, and curly
# braces.
illegal_characters_regex = '([\[\]\{\}\(\)])'
found_characters = re.findall(illegal_characters_regex, path)
if found_characters:
raise AssertionError("data file name '{}' contains illegal characters: '{}'".format(path, ''.join(found_characters)))
def parse_data_file(path):
print('Parsing file {}'.format(path))
data = configparser.ConfigParser(strict=True)
@@ -216,4 +226,5 @@ if __name__ == "__main__":
parser.add_argument('file', nargs='+')
args = parser.parse_args()
for path in args.file:
validate_data_file_name(path)
parse_data_file(path)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

@@ -0,0 +1,75 @@
#!/usr/bin/env python3
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# Copyright © 2018 Red Hat, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
import argparse
import os
import sys
import configparser
# see the IDs from
# https://github.com/torvalds/linux/blob/master/drivers/hid/hid-ids.h#L772
# https://github.com/torvalds/linux/blob/master/drivers/hid/hid-logitech-dj.c#L1826
logitech_receivers = [
0xc50c, # USB_DEVICE_ID_S510_RECEIVER
0xc517, # USB_DEVICE_ID_S510_RECEIVER_2
0xc512, # USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500
0xc513, # USB_DEVICE_ID_MX3000_RECEIVER
0xc51b, # USB_DEVICE_ID_LOGITECH_27MHZ_MOUSE_RECEIVER
0xc52b, # USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER
0xc52f, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER
0xc532, # USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2
0xc534, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2
0xc539, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1
0xc53f, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1
0xc53a, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY
]
RECEIVERS = ['usb:046d:{}'.format(r) for r in logitech_receivers]
def parse_data_file(path):
data = configparser.ConfigParser(strict=True)
# Don't convert to lowercase
data.optionxform = lambda option: option
data.read(path)
matches = data['Device']['DeviceMatch']
return matches.split(';')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Checks that no unifying receiver ID is in the device files')
parser.add_argument('file', nargs='+')
args = parser.parse_args()
receiver_found = False
for path in args.file:
matches = parse_data_file(path)
fname = os.path.basename(path)
for m in matches:
if m in RECEIVERS:
print('Receiver ID {} found in file {}'.format(m, fname))
receiver_found = True
if receiver_found:
sys.exit(1)

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
project('libratbag', 'c',
version : '0.10',
version : '0.12',
license : 'MIT/Expat',
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
meson_version : '>= 0.40.0')
@@ -172,6 +172,7 @@ src_libratbag = [
'src/driver-logitech-g300.c',
'src/driver-logitech-g600.c',
'src/driver-roccat.c',
'src/driver-roccat-kone-pure.c',
'src/driver-gskill.c',
'src/driver-steelseries.c',
'src/driver-test.c',
@@ -281,10 +282,12 @@ data_files = files(
'data/devices/gskill-MX-780.device',
'data/devices/logitech-M325.device',
'data/devices/logitech-M570.device',
'data/devices/logitech-M585-M590.device',
'data/devices/logitech-M705.device',
'data/devices/logitech-MX-Ergo.device',
'data/devices/logitech-MX-Master.device',
'data/devices/logitech-MX-Master-2S.device',
'data/devices/logitech-MX-Master-3.device',
'data/devices/logitech-MX518.device',
'data/devices/logitech-T650.device',
'data/devices/logitech-Wireless-Touchpad.device',
@@ -294,6 +297,7 @@ data_files = files(
'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',
@@ -302,12 +306,14 @@ data_files = files(
'data/devices/logitech-g5.device',
'data/devices/logitech-g500.device',
'data/devices/logitech-g500s.device',
'data/devices/logitech-g502-hero-wireless.device',
'data/devices/logitech-g502-hero.device',
'data/devices/logitech-g502-proteus-core.device',
'data/devices/logitech-g502-proteus-spectrum.device',
'data/devices/logitech-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',
@@ -318,14 +324,16 @@ data_files = files(
'data/devices/logitech-g900.device',
'data/devices/logitech-g903-hero.device',
'data/devices/logitech-g903.device',
'data/devices/logitech-g9x-[Call-of-Duty-MW3-Edition].device',
'data/devices/logitech-g9x-[Original].device',
'data/devices/logitech-g9x-Call-of-Duty-MW3-Edition.device',
'data/devices/logitech-g9x-Original.device',
'data/devices/logitech-Marathon-M705.device',
'data/devices/logitech-MX-Anywhere2.device',
'data/devices/logitech-MX-Anywhere2S.device',
'data/devices/logitech-MX-Vertical.device',
'data/devices/roccat-kone-pure.device',
'data/devices/roccat-kone-xtd.device',
'data/devices/steelseries-kinzu-v2.device',
'data/devices/steelseries-kinzu-v3.device',
'data/devices/steelseries-rival-310.device',
'data/devices/steelseries-rival-600.device',
'data/devices/steelseries-rival.device',
@@ -342,6 +350,9 @@ test('data-parse-test', data_parse_test, args: data_files)
duplicate_test = find_program(join_paths(meson.source_root(), 'data/devices/duplicate-check.py'))
test('duplicate-test', duplicate_test, args : data_files)
reciever_id_test = find_program(join_paths(meson.source_root(), 'data/devices/receiver-check.py'))
test('receiver-id-test', reciever_id_test, args : data_files)
#### tests ####
enable_tests = get_option('tests')
if enable_tests
@@ -615,6 +626,19 @@ shared_library(
install: false,
)
i_source = join_paths(meson.source_root(), 'src', 'hidpp.i')
c_source = swig_gen.process(i_source)
shared_library(
'hidpp',
c_source,
name_prefix: '_',
c_args : ['-Wno-missing-prototypes', '-Wno-format-nonliteral'],
extra_files: [ i_source ] + src_libratbag ,
dependencies: deps_libratbag + wrapper_deps,
include_directories : include_directories('src', 'tools'),
install: false,
)
# ratbagc is the layer that maps ratbagctl to the swig bindings
ratbagc_py_conf = configuration_data()
ratbagc_py_conf.set('LIBRATBAG_DATA_DIR', libratbag_data_dir_devel)

12
ratbagd/.gitignore vendored
View File

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

View File

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

View File

@@ -356,6 +356,7 @@ static int ratbagd_new(struct ratbagd **out)
if (r < 0)
return r;
log_verbose("Initializing libratbag\n");
ctx->lib_ctx = ratbag_create_context(&ratbagd_lib_interface, ctx);
if (!ctx->lib_ctx)
return -ENOMEM;
@@ -520,6 +521,8 @@ static int ratbagd_run(struct ratbagd *ctx)
ctx);
sd_event_add_post(ctx->event, NULL, before_idle_cb, ctx);
log_verbose("DBus server ready\n");
return sd_event_loop(ctx->event);
}
@@ -617,12 +620,12 @@ int main(int argc, char *argv[])
return 0;
} else if (streq(argv[1], "--quiet")) {
log_level = LL_QUIET;
} else if (streq(argv[1], "--verbose=raw")) {
} else if (streq(argv[1], "--verbose") || streq(argv[1], "--verbose=raw")) {
log_level = LL_RAW;
} else if (streq(argv[1], "--verbose")) {
} else if (streq(argv[1], "--verbose=debug")) {
log_level = LL_VERBOSE;
} else {
fprintf(stderr, "Usage: %s [--version | --quiet | --verbose[=raw]]\n",
fprintf(stderr, "Usage: %s [--version | --quiet | --verbose[=debug]]\n",
program_invocation_short_name);
r = -EINVAL;
goto exit;

View File

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

View File

@@ -412,11 +412,11 @@ hidpp20drv_read_led_8071(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
struct hidpp20_profile *profile;
struct hidpp20_led *h_led;
struct hidpp20_rgb_device_info device_info;
struct hidpp20_rgb_cluster_info *cluster_info;
struct hidpp20_rgb_cluster_info cluster_info;
int rc;
hidpp20_rgb_effects_get_device_info(drv_data->dev, &device_info);
cluster_info = &drv_data->led_infos.color_leds_8071[led->index];
cluster_info = drv_data->led_infos.color_leds_8071[led->index];
profile = &drv_data->profiles->profiles[led->profile->index];
h_led = &profile->leds[led->index];
@@ -437,7 +437,7 @@ hidpp20drv_read_led_8071(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
/* pre-filled, only override if unknown */
if (led->type == RATBAG_LED_TYPE_UNKNOWN)
led->type = hidpp20_led_get_location_mapping(cluster_info->location);
led->type = hidpp20_led_get_location_mapping(cluster_info.location);
led->color.red = h_led->color.red;
led->color.green = h_led->color.green;
led->color.blue = h_led->color.blue;
@@ -449,10 +449,10 @@ hidpp20drv_read_led_8071(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
else
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
for (int i = 0; i < cluster_info->num_effects; i++) {
for (int i = 0; i < cluster_info.num_effects; i++) {
struct hidpp20_rgb_effect_info ei;
rc = hidpp20_rgb_effects_get_effect_info(drv_data->dev,
cluster_info->index,
cluster_info.index,
i, &ei);
if (rc < 0)
break;
@@ -859,7 +859,8 @@ hidpp20drv_read_resolution_dpi(struct ratbag_profile *profile)
unsigned int default_rate = 500;
int rc;
if (drv_data->capabilities & HIDPP_CAP_RESOLUTION_2200) {
if ((drv_data->capabilities & HIDPP_CAP_RESOLUTION_2200) &&
(drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201) == 0) {
uint16_t resolution;
uint8_t flags;
@@ -928,6 +929,9 @@ hidpp20drv_update_resolution_dpi_8100(struct ratbag_resolution *resolution,
h_profile = &drv_data->profiles->profiles[profile->index];
h_profile->dpi[resolution->index] = dpi;
if (resolution->is_default)
h_profile->default_dpi = resolution->index;
return RATBAG_SUCCESS;
}
@@ -1302,6 +1306,11 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
break;
}
case HIDPP_PAGE_COLOR_LED_EFFECTS: {
/* The 8070 feauture implemented in the G602 doesn't follow the spec,
* so we ignore it */
if (ratbag_device_data_hidpp20_get_quirk(device->data) == HIDPP20_QUIRK_G602)
break;
log_debug(ratbag, "device has color effects\n");
drv_data->capabilities |= HIDPP_CAP_COLOR_LED_EFFECTS_8070;
@@ -1500,7 +1509,6 @@ hidpp20drv_remove(struct ratbag_device *device)
if (drv_data->profiles)
hidpp20_onboard_profiles_destroy(drv_data->profiles);
free(drv_data->led_infos.color_leds_8070);
free(drv_data->led_infos.color_leds_8071);
free(drv_data->controls);
free(drv_data->sensors);
if (drv_data->dev)
@@ -1513,6 +1521,7 @@ hidpp20drv_init_device(struct ratbag_device *device,
struct hidpp20drv_data *drv_data)
{
struct ratbag_profile *profile;
bool active_profile = false;
ratbag_device_init_profiles(device,
drv_data->num_profiles,
@@ -1522,6 +1531,18 @@ hidpp20drv_init_device(struct ratbag_device *device,
ratbag_device_for_each_profile(device, profile)
hidpp20drv_read_profile(profile);
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
/* Fallback to the first profile if no profile is active */
list_for_each(profile, &device->profiles, link)
if (profile->is_active)
active_profile = true;
if (!active_profile && device->num_profiles >= 1) {
profile = ratbag_device_get_profile(device, 0);
profile->is_active = true;
}
}
}
static int
@@ -1554,16 +1575,21 @@ hidpp20drv_probe(struct ratbag_device *device)
* If there is a special need like for G900, we can add this in the
* device data file.
*/
dev = hidpp20_device_new(&base, device_idx);
dev = hidpp20_device_new(&base, device_idx, (struct hidpp_hid_report*) device->hidraw[0].reports, device->hidraw[0].num_reports);
if (!dev) {
rc = -ENODEV;
goto err;
}
dev->quirk = ratbag_device_data_hidpp20_get_quirk(device->data);
drv_data->dev = dev;
log_debug(device->ratbag, "'%s' is using protocol v%d.%d\n", ratbag_device_get_name(device), dev->proto_major, dev->proto_minor);
if(dev->quirk != HIDPP20_QUIRK_NONE)
log_debug(device->ratbag, "'%s' is quirked (%s)\n", ratbag_device_get_name(device), hidpp20_get_quirk_string(dev->quirk));
/* add some defaults that will be overwritten by the device */
drv_data->num_profiles = 1;
drv_data->num_resolutions = 1;

View File

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

View File

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

View File

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

23
src/hidpp.i Normal file
View File

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

View File

@@ -833,6 +833,20 @@ hidpp10_get_current_profile(struct hidpp10_device *dev, uint8_t *current_profile
"unable to find the profile at (%d,%d) in the directory\n",
page, offset);
break;
case PROFILE_TYPE_FACTORY:
/* Factory profile is selected and profile switching is
* disabled. Let's switch to the first profile because the
* factory profile doesn't help anywone */
res = hidpp10_set_current_profile(dev, 0);
if (res == 0) {
hidpp_log_info(&dev->base, "switched from factory profile to 0\n");
*current_profile = 0;
return 0;
}
hidpp_log_error(&dev->base,
"current profile is factory profile but switching to 0 failed.\n");
break;
default:
hidpp_log_error(&dev->base,
"Unexpected value: %02x\n",
@@ -2737,8 +2751,7 @@ hidpp10_get_device_info(struct hidpp10_device *dev)
hidpp10_get_optical_sensor_settings(dev, &reflect);
hidpp10_get_current_profile(dev, &current_profile);
return 0;
return hidpp10_get_current_profile(dev, &current_profile);
}
struct hidpp10_device*

View File

@@ -48,7 +48,6 @@ hidpp20_feature_get_name(uint16_t feature)
{
static char numeric[8];
const char *str;
#define CASE_RETURN_STRING(a) case a: return #a; break
switch(feature)
{
@@ -99,11 +98,22 @@ hidpp20_sw_led_control_get_mode_string(const enum hidpp20_led_sw_ctrl_led_mode m
str = numeric;
break;
}
#undef CASE_RETURN_STRING
return str;
}
const char*
hidpp20_get_quirk_string(enum hidpp20_quirk quirk)
{
switch (quirk) {
CASE_RETURN_STRING(HIDPP20_QUIRK_NONE);
CASE_RETURN_STRING(HIDPP20_QUIRK_G305);
CASE_RETURN_STRING(HIDPP20_QUIRK_G602);
}
abort();
}
static int
hidpp20_request_command_allow_error(struct hidpp20_device *device, union hidpp20_message *msg,
bool allow_error)
@@ -123,6 +133,16 @@ hidpp20_request_command_allow_error(struct hidpp20_device *device, union hidpp20
}
msg->msg.address |= DEVICE_SW_ID;
/* some mice don't support short reports */
if (msg->msg.report_id == REPORT_ID_SHORT && !(device->base.supported_report_types & HIDPP_REPORT_SHORT))
msg->msg.report_id = REPORT_ID_LONG;
/* sanity check */
if (msg->msg.report_id == REPORT_ID_LONG && !(device->base.supported_report_types & HIDPP_REPORT_LONG)) {
hidpp_log_error(&device->base, "hidpp20: trying to use unsupported report type\n");
return -EINVAL;
}
msg_len = msg->msg.report_id == REPORT_ID_SHORT ? SHORT_MESSAGE_LENGTH : LONG_MESSAGE_LENGTH;
/* Send the message to the Device */
@@ -1073,8 +1093,8 @@ hidpp20_rgb_effects_get_cluster_info(struct hidpp20_device *device,
info->effect_index = msg.msg.parameters[1];
info->location = get_unaligned_be_u16(&msg.msg.parameters[2]);
info->num_effects = get_unaligned_be_u16(&msg.msg.parameters[4]);
info->persistency_caps = get_unaligned_be_u16(&msg.msg.parameters[6]);
info->num_effects = msg.msg.parameters[4];
info->persistency_caps = msg.msg.parameters[5];
return 0;
}
@@ -1432,6 +1452,11 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
if (device->quirk == HIDPP20_QUIRK_G602) {
msg.msg.parameters[0] = 1;
i = 0;
}
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
@@ -1443,6 +1468,9 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
get_unaligned_be_u16(&msg.msg.parameters[i]) != 0) {
uint16_t value = get_unaligned_be_u16(&msg.msg.parameters[i]);
if (device->quirk == HIDPP20_QUIRK_G602 && i == 2)
value += 0xe000;
if (value > 0xe000) {
sensor->dpi_steps = value - 0xe000;
} else {
@@ -1472,6 +1500,9 @@ hidpp20_adjustable_dpi_get_dpi(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
if (device->quirk == HIDPP20_QUIRK_G602)
msg.msg.parameters[0] = 1;
rc = hidpp20_request_command(device, &msg);
if (rc)
return rc;
@@ -1551,6 +1582,9 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
.msg.parameters[0] = sensor->index,
};
if (device->quirk == HIDPP20_QUIRK_G602)
msg.msg.parameters[0] = 1;
set_unaligned_be_u16(&msg.msg.parameters[1], dpi);
feature_index = hidpp_root_get_feature_idx(device,
@@ -1634,7 +1668,7 @@ int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *d
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G402 0x01
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G303 0x02
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900 0x03
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G903_HERO 0x04
#define HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G915 0x04
#define HIDPP20_ONBOARD_PROFILES_MACRO_TYPE_G402 0x01
#define HIDPP20_USER_PROFILES_G402 0x0000
@@ -2054,7 +2088,7 @@ hidpp20_onboard_profiles_validate(struct hidpp20_device *device,
if ((info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G402) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G303) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G900) &&
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G903_HERO)) {
(info->profile_format_id != HIDPP20_ONBOARD_PROFILES_PROFILE_TYPE_G915)) {
hidpp_log_error(&device->base,
"Profile layout not supported: 0x%02x.\n",
info->profile_format_id);
@@ -2354,7 +2388,7 @@ hidpp20_onboard_profiles_write_dict(struct hidpp20_device *device,
memset(data + buffer_index, 0xff, sector_size - buffer_index);
hidpp_log_buf_debug(&device->base,
hidpp_log_buf_raw(&device->base,
"dictionary: ",
data,
hidpp20_onboard_profiles_compute_dict_size(device,
@@ -2602,6 +2636,15 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
HIDPP20_USER_PROFILES_G402,
profiles->sector_size,
data);
if (rc && device->quirk == HIDPP20_QUIRK_G305) {
/* The G305 has a bug where it throws an ERR_INVALID_ARGUMENT
if the sector has not been written to yet. If this happens
we will read the ROM profiles.*/
read_userdata = false;
goto read_profiles;
}
if (rc < 0)
return rc; // ignore_clang_sa_mem_leak
@@ -2635,6 +2678,7 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
read_userdata = false;
}
read_profiles:
for (i = 0; i < profiles->num_profiles; i++) {
if (read_userdata) {
hidpp_log_debug(&device->base, "Parsing profile %u\n", i);
@@ -2837,7 +2881,7 @@ hidpp20_onboard_profiles_get_code_from_special(enum ratbag_button_action_special
/* -------------------------------------------------------------------------- */
struct hidpp20_device *
hidpp20_device_new(const struct hidpp_device *base, unsigned int idx)
hidpp20_device_new(const struct hidpp_device *base, unsigned int idx, struct hidpp_hid_report *reports, unsigned int num_reports)
{
struct hidpp20_device *dev;
int rc;
@@ -2850,6 +2894,12 @@ hidpp20_device_new(const struct hidpp_device *base, unsigned int idx)
dev->proto_major = 1;
dev->proto_minor = 0;
hidpp_get_supported_report_types(&(dev->base), reports, num_reports);
if (!(dev->base.supported_report_types & HIDPP_REPORT_SHORT) &&
!(dev->base.supported_report_types & HIDPP_REPORT_LONG))
goto err;
rc = hidpp20_root_get_protocol_version(dev, &dev->proto_major, &dev->proto_minor);
if (rc) {
/* communication error, best to ignore the device */

View File

@@ -51,6 +51,12 @@ struct hidpp20_feature {
uint8_t type;
};
enum hidpp20_quirk {
HIDPP20_QUIRK_NONE,
HIDPP20_QUIRK_G305,
HIDPP20_QUIRK_G602,
};
struct hidpp20_device {
struct hidpp_device base;
unsigned int index;
@@ -58,18 +64,24 @@ struct hidpp20_device {
unsigned proto_minor;
unsigned feature_count;
struct hidpp20_feature *feature_list;
enum hidpp20_quirk quirk;
};
int hidpp20_request_command(struct hidpp20_device *dev, union hidpp20_message *msg);
#define CASE_RETURN_STRING(a) case a: return #a; break
const char *hidpp20_feature_get_name(uint16_t feature);
const char *hidpp20_get_quirk_string(enum hidpp20_quirk quirk);
/* -------------------------------------------------------------------------- */
/* generic hidpp20 device operations */
/* -------------------------------------------------------------------------- */
struct hidpp20_device *
hidpp20_device_new(const struct hidpp_device *base, unsigned int idx);
hidpp20_device_new(const struct hidpp_device *base, unsigned int idx,
struct hidpp_hid_report *reports, unsigned int num_reports);
void
hidpp20_device_destroy(struct hidpp20_device *device);

View File

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

View File

@@ -80,6 +80,9 @@ ratbag_device_data_hidpp10_get_led_count(const struct ratbag_device_data *data);
int
ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data);
enum hidpp20_quirk
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data);
/* SteelSeries */
/**

View File

@@ -71,17 +71,29 @@ ratbag_default_log_func(struct ratbag *ratbag,
const char *format, va_list args)
{
const char *prefix;
FILE *out = stdout;
switch(priority) {
case RATBAG_LOG_PRIORITY_RAW: prefix = "raw"; break;
case RATBAG_LOG_PRIORITY_DEBUG: prefix = "debug"; break;
case RATBAG_LOG_PRIORITY_INFO: prefix = "info"; break;
case RATBAG_LOG_PRIORITY_ERROR: prefix = "error"; break;
default: prefix="<invalid priority>"; break;
case RATBAG_LOG_PRIORITY_RAW:
prefix = "raw";
break;
case RATBAG_LOG_PRIORITY_DEBUG:
prefix = "debug";
break;
case RATBAG_LOG_PRIORITY_INFO:
prefix = "info";
break;
case RATBAG_LOG_PRIORITY_ERROR:
prefix = "error";
out = stderr;
break;
default:
prefix="<invalid priority>";
break;
}
fprintf(stderr, "ratbag %s: ", prefix);
vfprintf(stderr, format, args);
fprintf(out, "ratbag %s: ", prefix);
vfprintf(out, format, args);
}
void
@@ -322,7 +334,7 @@ out:
}
static inline bool
ratbag_test_driver(struct ratbag_device *device,
ratbag_try_driver(struct ratbag_device *device,
const struct input_id *dev_id,
const char *driver_name,
const struct ratbag_test_device *test_device)
@@ -377,9 +389,9 @@ ratbag_driver_fallback_logitech(struct ratbag_device *device,
if (dev_id->vendor != USB_VENDOR_ID_LOGITECH)
return false;
rc = ratbag_test_driver(device, dev_id, "hidpp20", NULL);
rc = ratbag_try_driver(device, dev_id, "hidpp20", NULL);
if (!rc)
rc = ratbag_test_driver(device, dev_id, "hidpp10", NULL);
rc = ratbag_try_driver(device, dev_id, "hidpp10", NULL);
return rc;
}
@@ -401,7 +413,7 @@ ratbag_assign_driver(struct ratbag_device *device,
if (driver_name) {
log_debug(device->ratbag, "device assigned driver %s\n", driver_name);
rc = ratbag_test_driver(device, dev_id, driver_name, test_device);
rc = ratbag_try_driver(device, dev_id, driver_name, test_device);
} else {
rc = ratbag_driver_fallback_logitech(device, dev_id);
}

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# Copyright 2017 Red Hat, Inc.
# Copyright 2017-2019 Red Hat, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@@ -324,7 +324,7 @@ class RatbagdProfile(metaclass=MetaRatbag):
"""The capabilities of this profile as an array. Capabilities not
present on the profile are not in the list. Thus use e.g.
if RatbagdProfile.CAP_WRITABLE_NAME is in profile.capabilities:
if RatbagdProfile.CAP_WRITABLE_NAME in profile.capabilities:
do something
"""
return self._capabilities

View File

@@ -374,6 +374,7 @@ class TestRatbagCtlDPI(TestRatbagCtl):
{ "is_active": false,
"resolutions": [
{ "xres": 1000, "yres": 1100,
"capabilities": [1],
"is_active": false },
{ "xres": 1300, "yres": 1400,
"capabilities": [1],

View File

@@ -1,6 +1,4 @@
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# Copyright 2016 Red Hat, Inc.
# Copyright 2016-2019 Red Hat, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
@@ -428,7 +426,7 @@ class RatbagdProfile(_RatbagdDBus):
"""The capabilities of this profile as an array. Capabilities not
present on the profile are not in the list. Thus use e.g.
if RatbagdProfile.CAP_WRITABLE_NAME is in profile.capabilities:
if RatbagdProfile.CAP_WRITABLE_NAME in profile.capabilities:
do something
"""
return self._get_dbus_property("Capabilities") or []
@@ -520,7 +518,7 @@ class RatbagdProfile(_RatbagdDBus):
@GObject.Property
def is_active(self):
"""Returns True if the profile is currenly active, false otherwise."""
"""Returns True if the profile is currently active, false otherwise."""
return self._active
def set_active(self):

View File

@@ -62,9 +62,9 @@ def start_ratbagd(verbosity=0):
args = [os.path.join('@MESON_BUILD_ROOT@', "ratbagd.devel")]
if verbosity >= 3:
args.append('--verbose=raw')
elif verbosity >= 2:
args.append('--verbose')
elif verbosity >= 2:
args.append('--verbose=debug')
elif verbosity == 0:
args.append('--quiet')