mirror of
https://github.com/libratbag/libratbag.git
synced 2026-11-07 23:23:52 +01:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5357d4cbc2 | ||
|
|
26c534cc74 | ||
|
|
904c7988bb | ||
|
|
7d255a2f93 | ||
|
|
d0a5cb1f0e | ||
|
|
71b1d66287 | ||
|
|
32ebbca42f | ||
|
|
a0afbfeb3f | ||
|
|
9dfd4fd03a | ||
|
|
b0ea75979a | ||
|
|
a5c1ca202e | ||
|
|
8d40ad6c0c | ||
|
|
72d11de597 | ||
|
|
47cc5fce92 | ||
|
|
5da791b268 | ||
|
|
e63802b01a | ||
|
|
cd89b123ce | ||
|
|
f6546b3222 | ||
|
|
cd8ddc8444 | ||
|
|
cdb3226ef1 |
@@ -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
36
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal 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
11
.gitignore
vendored
@@ -1,11 +0,0 @@
|
||||
*.o
|
||||
*.pc
|
||||
*.swp
|
||||
*~
|
||||
*.sig
|
||||
*.tar.*
|
||||
*.announce
|
||||
*.patch
|
||||
*.rej
|
||||
*.trs
|
||||
tags
|
||||
@@ -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)
|
||||
|
||||
5
data/devices/logitech-M585-M590.device
Normal file
5
data/devices/logitech-M585-M590.device
Normal 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
75
data/devices/receiver-check.py
Executable 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)
|
||||
4
data/devices/roccat-kone-pure.device
Normal file
4
data/devices/roccat-kone-pure.device
Normal file
@@ -0,0 +1,4 @@
|
||||
[Device]
|
||||
Name=Roccat Kone Pure
|
||||
DeviceMatch=usb:1e7d:2dc2
|
||||
Driver=roccat-kone-pure
|
||||
12
meson.build
12
meson.build
@@ -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
12
ratbagd/.gitignore
vendored
@@ -1,12 +0,0 @@
|
||||
*.a
|
||||
*.log
|
||||
*.o
|
||||
*.stamp
|
||||
*.swp
|
||||
*.trs
|
||||
*~
|
||||
.dirstamp
|
||||
.libs/
|
||||
/*.tar.xz
|
||||
tags
|
||||
__pycache__
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
845
src/driver-roccat-kone-pure.c
Normal file
845
src/driver-roccat-kone-pure.c
Normal 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,
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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 []
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user