1
0
forked from drath/libratbag

Compare commits

..

20 Commits
v0.11 ... 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
27 changed files with 1114 additions and 67 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):
@@ -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

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

@@ -1,5 +1,5 @@
project('libratbag', 'c',
version : '0.11',
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,6 +282,7 @@ 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',
@@ -322,12 +324,13 @@ 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',
@@ -347,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

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

@@ -1509,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)
@@ -1576,7 +1575,7 @@ hidpp20drv_probe(struct ratbag_device *device)
* If there is a special need like for G900, we can add this in the
* device data file.
*/
dev = hidpp20_device_new(&base, device_idx);
dev = hidpp20_device_new(&base, device_idx, (struct hidpp_hid_report*) device->hidraw[0].reports, device->hidraw[0].num_reports);
if (!dev) {
rc = -ENODEV;
goto err;

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,

View File

@@ -133,6 +133,16 @@ hidpp20_request_command_allow_error(struct hidpp20_device *device, union hidpp20
}
msg->msg.address |= DEVICE_SW_ID;
/* some mice don't support short reports */
if (msg->msg.report_id == REPORT_ID_SHORT && !(device->base.supported_report_types & HIDPP_REPORT_SHORT))
msg->msg.report_id = REPORT_ID_LONG;
/* sanity check */
if (msg->msg.report_id == REPORT_ID_LONG && !(device->base.supported_report_types & HIDPP_REPORT_LONG)) {
hidpp_log_error(&device->base, "hidpp20: trying to use unsupported report type\n");
return -EINVAL;
}
msg_len = msg->msg.report_id == REPORT_ID_SHORT ? SHORT_MESSAGE_LENGTH : LONG_MESSAGE_LENGTH;
/* Send the message to the Device */
@@ -2378,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,
@@ -2627,7 +2637,7 @@ hidpp20_onboard_profiles_initialize(struct hidpp20_device *device,
profiles->sector_size,
data);
if (rc && device->quirk != HIDPP20_QUIRK_G305) {
if (rc && device->quirk == HIDPP20_QUIRK_G305) {
/* The G305 has a bug where it throws an ERR_INVALID_ARGUMENT
if the sector has not been written to yet. If this happens
we will read the ROM profiles.*/
@@ -2871,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;
@@ -2884,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

@@ -80,7 +80,8 @@ const char *hidpp20_get_quirk_string(enum hidpp20_quirk quirk);
/* -------------------------------------------------------------------------- */
struct hidpp20_device *
hidpp20_device_new(const struct hidpp_device *base, unsigned int idx);
hidpp20_device_new(const struct hidpp_device *base, unsigned int idx,
struct hidpp_hid_report *reports, unsigned int num_reports);
void
hidpp20_device_destroy(struct hidpp20_device *device);

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

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 []

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')