forked from drath/libratbag
Compare commits
122 Commits
v0.11
...
firmware-v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84b1519ebd | ||
|
|
99b8c80aee | ||
|
|
f7c22ed88c | ||
|
|
aeda65cbfa | ||
|
|
ed2439e5e2 | ||
|
|
f407521e8f | ||
|
|
3c4fe23f70 | ||
|
|
9e2ac55dbc | ||
|
|
1e72ebaa7b | ||
|
|
920bc2b993 | ||
|
|
20b573668e | ||
|
|
49acc11db3 | ||
|
|
364e3fd965 | ||
|
|
bb230d390d | ||
|
|
93a076bed9 | ||
|
|
e7b50c9061 | ||
|
|
9d533bdc3e | ||
|
|
d7906b54b4 | ||
|
|
f1eba370ed | ||
|
|
7b5ab2ed99 | ||
|
|
d2d5471836 | ||
|
|
9cfc1e98e3 | ||
|
|
691661276f | ||
|
|
25ed99db84 | ||
|
|
02226d0428 | ||
|
|
f822fb0fa7 | ||
|
|
b88be0add0 | ||
|
|
9fc6d12486 | ||
|
|
0c64dad9e1 | ||
|
|
f61485c603 | ||
|
|
8c295479d8 | ||
|
|
e55b04ec7d | ||
|
|
1231415470 | ||
|
|
660da27021 | ||
|
|
481b6f029a | ||
|
|
768d93a45a | ||
|
|
1c6e039d01 | ||
|
|
d16df8d100 | ||
|
|
4b1e49d522 | ||
|
|
786cebcb0b | ||
|
|
d53454bcb3 | ||
|
|
a0def65b64 | ||
|
|
2a40279903 | ||
|
|
02c95422f0 | ||
|
|
4ce1cf2807 | ||
|
|
89168f2187 | ||
|
|
bb92337268 | ||
|
|
a8316c07f3 | ||
|
|
39769c57dd | ||
|
|
200a8af7a7 | ||
|
|
fcf9f7745d | ||
|
|
633c9fc55d | ||
|
|
ee73434ead | ||
|
|
d60b1c9d71 | ||
|
|
88a60f55b4 | ||
|
|
dba202ae8b | ||
|
|
6cdcce9de4 | ||
|
|
e2c3373145 | ||
|
|
02575a9008 | ||
|
|
1097b1ba68 | ||
|
|
77b67775da | ||
|
|
5b23c9ebae | ||
|
|
6d0974be25 | ||
|
|
ea9111a2a6 | ||
|
|
42787ee422 | ||
|
|
611dd9be0d | ||
|
|
fd20f9e642 | ||
|
|
9be5576967 | ||
|
|
9fe511fcc5 | ||
|
|
f0b69aab19 | ||
|
|
7610b80ced | ||
|
|
f0a05bb522 | ||
|
|
2669cc28b2 | ||
|
|
302702d94a | ||
|
|
e1136c4d30 | ||
|
|
0012853a1d | ||
|
|
afb85f868c | ||
|
|
b76fcdadde | ||
|
|
ba7c092ee2 | ||
|
|
3483bd8c14 | ||
|
|
0fb0eb3d42 | ||
|
|
5fb1f4429d | ||
|
|
5f5e294598 | ||
|
|
b4218737cd | ||
|
|
3875bb70fd | ||
|
|
e9d67b7235 | ||
|
|
8a476a8724 | ||
|
|
d67cbec5f8 | ||
|
|
f82c6f72b6 | ||
|
|
af30fc28cd | ||
|
|
3fb8a51456 | ||
|
|
ceaa3c3ad5 | ||
|
|
551b918ba8 | ||
|
|
3105428ba1 | ||
|
|
b3f2406180 | ||
|
|
902160f430 | ||
|
|
fd6d0b6198 | ||
|
|
5ed98fdb22 | ||
|
|
e0e73c2971 | ||
|
|
0d3b4f1b64 | ||
|
|
20b6e3deeb | ||
|
|
ffcdee912d | ||
|
|
5357d4cbc2 | ||
|
|
26c534cc74 | ||
|
|
904c7988bb | ||
|
|
7d255a2f93 | ||
|
|
d0a5cb1f0e | ||
|
|
71b1d66287 | ||
|
|
32ebbca42f | ||
|
|
a0afbfeb3f | ||
|
|
9dfd4fd03a | ||
|
|
b0ea75979a | ||
|
|
a5c1ca202e | ||
|
|
8d40ad6c0c | ||
|
|
72d11de597 | ||
|
|
47cc5fce92 | ||
|
|
5da791b268 | ||
|
|
e63802b01a | ||
|
|
cd89b123ce | ||
|
|
f6546b3222 | ||
|
|
cd8ddc8444 | ||
|
|
cdb3226ef1 |
@@ -1,5 +1,12 @@
|
||||
# vim: set expandtab shiftwidth=2 tabstop=8:
|
||||
libratbag_references:
|
||||
build_dependencies: &build_dependencies
|
||||
FEDORA_DEP_BUILD: gcc gcc-c++ meson dbus-daemon glib2-devel json-glib-devel libevdev-devel libudev-devel libunistring-devel python3-devel python3-evdev swig
|
||||
FEDORA_DEP_TEST: check-devel python3-gobject python3-lxml valgrind diffutils
|
||||
FEDORA_DEP_DOC: python3-sphinx python3-sphinx_rtd_theme
|
||||
UBUNTU_DEP_BUILD: gcc g++ meson pkg-config systemd libevdev-dev libglib2.0-dev libjson-glib-dev libsystemd-dev libudev-dev libunistring-dev python3-dev python3-evdev swig
|
||||
UBUNTU_DEP_TEST: check python3-gi python3-lxml valgrind
|
||||
ARCH_DEP_BUILD: meson swig glib2 libevdev libudev.so libunistring json-glib python python-evdev python-gobject
|
||||
ARCH_DEP_TEST: check valgrind python-lxml
|
||||
default_settings: &default_settings
|
||||
working_directory: ~/libratbag
|
||||
environment:
|
||||
@@ -52,10 +59,7 @@ libratbag_references:
|
||||
name: Checking if any files are left after uninstall
|
||||
command: |
|
||||
PREFIX=/root/test_install
|
||||
# workaround until https://github.com/mesonbuild/meson/pull/2033 is merged
|
||||
# and a new release appears
|
||||
git clone https://github.com/whot/meson -b wip/remove-directories-on-uninstall
|
||||
./meson/meson.py build_install --prefix=$PREFIX
|
||||
meson build_install --prefix=$PREFIX
|
||||
ninja -C build_install install
|
||||
ninja -C build_install uninstall
|
||||
if [ -d $PREFIX ]
|
||||
@@ -73,33 +77,34 @@ libratbag_references:
|
||||
mkdir /run/dbus
|
||||
/usr/bin/dbus-daemon --system --fork
|
||||
|
||||
fedora_prep_cache: &fedora_prep_cache
|
||||
<<: *default_settings
|
||||
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
|
||||
- persist_to_workspace:
|
||||
root: /var/cache/
|
||||
paths:
|
||||
- dnf/*
|
||||
save_date: &save_date
|
||||
run:
|
||||
name: Save date
|
||||
command: date +%F > /tmp/date
|
||||
|
||||
fedora_save_cache: &fedora_save_cache
|
||||
save_cache:
|
||||
key: fedora-cache-v1-{{ checksum "/tmp/date" }}
|
||||
paths:
|
||||
- /var/cache/dnf
|
||||
|
||||
fedora_fetch_cache: &fedora_fetch_cache
|
||||
attach_workspace:
|
||||
at: /var/cache/
|
||||
restore_cache:
|
||||
keys:
|
||||
- fedora-cache-v1
|
||||
|
||||
fedora_install: &fedora_install
|
||||
run:
|
||||
name: Install prerequisites
|
||||
command: |
|
||||
echo keepcache=1 >> /etc/dnf/dnf.conf
|
||||
dnf upgrade -y libsolv
|
||||
dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel valgrind python3-gobject python3-evdev glib2-devel python3-lxml python3-devel swig libunistring-devel dbus-daemon json-glib-devel
|
||||
dnf install -y git ${FEDORA_DEP_BUILD} ${FEDORA_DEP_TEST}
|
||||
sed -i 's/systemd//' /etc/nsswitch.conf
|
||||
|
||||
fedora_settings: &fedora_settings
|
||||
<<: *default_settings
|
||||
steps:
|
||||
- *save_date
|
||||
- *fedora_fetch_cache
|
||||
- *fedora_install
|
||||
- checkout
|
||||
@@ -111,7 +116,39 @@ fedora_settings: &fedora_settings
|
||||
- *build_buildtype_plain
|
||||
- *build_buildtype_release
|
||||
- *export_logs
|
||||
- *fedora_save_cache
|
||||
environment:
|
||||
*build_dependencies
|
||||
|
||||
arch_save_cache: &arch_save_cache
|
||||
save_cache:
|
||||
key: arch-cache-v1-{{ checksum "/tmp/date" }}
|
||||
paths:
|
||||
- /var/cache/pacman/pkg
|
||||
|
||||
arch_fetch_cache: &arch_fetch_cache
|
||||
restore_cache:
|
||||
keys:
|
||||
- arch-cache-v1
|
||||
|
||||
arch_settings: &arch_settings
|
||||
<<: *default_settings
|
||||
steps:
|
||||
- *save_date
|
||||
- *arch_fetch_cache
|
||||
- run:
|
||||
name: Install prerequisites
|
||||
command: pacman -Syu --noconfirm --noprogressbar --needed base-devel git tree ${ARCH_DEP_BUILD} ${ARCH_DEP_TEST}
|
||||
- *start_dbus
|
||||
- checkout
|
||||
- *build_and_test
|
||||
- *install
|
||||
- *check_install
|
||||
- *check_uninstall
|
||||
- *export_logs
|
||||
- *arch_save_cache
|
||||
environment:
|
||||
*build_dependencies
|
||||
|
||||
ubuntu_settings: &ubuntu_settings
|
||||
<<: *default_settings
|
||||
@@ -119,12 +156,9 @@ ubuntu_settings: &ubuntu_settings
|
||||
- run:
|
||||
name: Install prerequisites
|
||||
command: |
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y software-properties-common
|
||||
apt-get remove -y libnss-systemd
|
||||
add-apt-repository universe
|
||||
apt-get update
|
||||
apt-get install -y tree git gcc g++ pkg-config meson check libudev-dev libevdev-dev libsystemd-dev valgrind python3-gi python3-evdev libglib2.0-dev python3-lxml python3-dev swig systemd libunistring-dev libjson-glib-dev
|
||||
apt-get install -y git ${UBUNTU_DEP_BUILD} ${UBUNTU_DEP_TEST}
|
||||
- checkout
|
||||
- *start_dbus
|
||||
- *build_and_test
|
||||
@@ -134,6 +168,8 @@ ubuntu_settings: &ubuntu_settings
|
||||
- *build_buildtype_plain
|
||||
- *build_buildtype_release
|
||||
- *export_logs
|
||||
environment:
|
||||
*build_dependencies
|
||||
|
||||
doc_build: &doc_build
|
||||
<<: *default_settings
|
||||
@@ -142,7 +178,7 @@ doc_build: &doc_build
|
||||
- *fedora_install
|
||||
- run:
|
||||
name: Install documentation build-deps
|
||||
command: dnf install -y python3-sphinx python3-sphinx_rtd_theme
|
||||
command: dnf install -y ${FEDORA_DEP_DOC}
|
||||
- checkout
|
||||
- *build_with_docs
|
||||
- *export_logs
|
||||
@@ -152,6 +188,8 @@ doc_build: &doc_build
|
||||
root: build
|
||||
paths:
|
||||
- doc/html/*
|
||||
environment:
|
||||
*build_dependencies
|
||||
|
||||
docs_deploy: &docs_deploy
|
||||
<<: *default_settings
|
||||
@@ -193,48 +231,38 @@ docs_deploy: &docs_deploy
|
||||
|
||||
version: 2
|
||||
jobs:
|
||||
fedora_rawhide:
|
||||
fedora_31:
|
||||
<<: *fedora_settings
|
||||
docker:
|
||||
- image: fedora:rawhide
|
||||
fedora_cache:
|
||||
<<: *fedora_prep_cache
|
||||
- image: fedora:31
|
||||
arch:
|
||||
<<: *arch_settings
|
||||
docker:
|
||||
- image: fedora:30
|
||||
fedora_latest:
|
||||
<<: *fedora_settings
|
||||
docker:
|
||||
- image: fedora:30
|
||||
ubuntu_19_04:
|
||||
- image: archlinux
|
||||
ubuntu_20_04:
|
||||
<<: *ubuntu_settings
|
||||
docker:
|
||||
- image: ubuntu:19.04
|
||||
- image: ubuntu:20.04
|
||||
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:
|
||||
requires:
|
||||
- fedora_cache
|
||||
- doc_build:
|
||||
requires:
|
||||
- fedora_cache
|
||||
- ubuntu_20_04
|
||||
- arch
|
||||
- fedora_31
|
||||
- doc_build
|
||||
- doc_deploy:
|
||||
requires:
|
||||
- fedora_cache
|
||||
- doc_build
|
||||
filters:
|
||||
branches:
|
||||
|
||||
20
.editorconfig
Normal file
20
.editorconfig
Normal file
@@ -0,0 +1,20 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{c,h}]
|
||||
indent_style = tab
|
||||
tab_width = 8
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[{*.{py,py.in},tools/ratbagctl.*.in}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{yml,xsl}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
42
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
42
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
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`
|
||||
|
||||
Device info (if applicable):
|
||||
```
|
||||
$ ratbagctl <device> info
|
||||
OUTPUT 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
|
||||
@@ -3,9 +3,10 @@ libratbag
|
||||
|
||||
<img src="https://libratbag.github.io/_images/logo.svg" alt="" width="30%" align="right">
|
||||
|
||||
libratbag provides **ratbagd**, a DBus daemon to configure gaming mice.
|
||||
The daemon provides a generic way to access the various features exposed by
|
||||
these mice and abstracts away hardware-specific and kernel-specific quirks.
|
||||
libratbag provides **ratbagd**, a DBus daemon to configure input devices,
|
||||
mainly gaming mice. The daemon provides a generic way to access the various
|
||||
features exposed by these mice and abstracts away hardware-specific and
|
||||
kernel-specific quirks.
|
||||
|
||||
libratbag currently supports devices from Logitech, Etekcity, GSkill,
|
||||
Roccat, Steelseries. See [the device
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# vim: set expandtab shiftwidth=4 tabstop=4:
|
||||
#
|
||||
# Copyright © 2017 Red Hat, Inc.
|
||||
#
|
||||
@@ -27,6 +26,7 @@
|
||||
|
||||
import argparse
|
||||
import configparser
|
||||
import re
|
||||
|
||||
|
||||
def assertIn(element, l):
|
||||
@@ -57,7 +57,7 @@ def check_match_str(string):
|
||||
|
||||
|
||||
def check_ledtypes_str(string):
|
||||
permitted_types = ['logo', 'side', 'battery', 'dpi']
|
||||
permitted_types = ['logo', 'side', 'battery', 'dpi', 'switches']
|
||||
|
||||
types = string.split(';')
|
||||
for t in types:
|
||||
@@ -137,9 +137,8 @@ def check_section_hidpp10(section):
|
||||
pass
|
||||
|
||||
try:
|
||||
index = int(section['DeviceIndex'])
|
||||
# 10 is arbitrarily chosen
|
||||
assert(index > 0 and index < 10)
|
||||
index = int(section['DeviceIndex'], 16)
|
||||
assert(index > 0 and index <= 0xff)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@@ -169,14 +168,13 @@ def check_section_hidpp10(section):
|
||||
|
||||
|
||||
def check_section_hidpp20(section):
|
||||
permitted = ['DeviceIndex', 'Quirk']
|
||||
permitted = ['DeviceIndex', 'Leds', 'Quirk']
|
||||
for key in section.keys():
|
||||
assertIn(key, permitted)
|
||||
|
||||
try:
|
||||
index = int(section['DeviceIndex'])
|
||||
# 10 is arbitrarily chosen
|
||||
assert(index > 0 and index < 10)
|
||||
index = int(section['DeviceIndex'], 16)
|
||||
assert(index > 0 and index <= 0xff)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@@ -190,6 +188,15 @@ def check_section_driver(driver, section):
|
||||
assert('Unsupported driver section {}'.format(driver))
|
||||
|
||||
|
||||
def validate_data_file_name(path):
|
||||
# Matching any of the characters in the regular expression will throw an
|
||||
# error. Currently only tests the square brackets [], parentheses, and curly
|
||||
# braces.
|
||||
illegal_characters_regex = '([\[\]\{\}\(\)])'
|
||||
found_characters = re.findall(illegal_characters_regex, path)
|
||||
if found_characters:
|
||||
raise AssertionError("data file name '{}' contains illegal characters: '{}'".format(path, ''.join(found_characters)))
|
||||
|
||||
def parse_data_file(path):
|
||||
print('Parsing file {}'.format(path))
|
||||
data = configparser.ConfigParser(strict=True)
|
||||
@@ -216,4 +223,5 @@ if __name__ == "__main__":
|
||||
parser.add_argument('file', nargs='+')
|
||||
args = parser.parse_args()
|
||||
for path in args.file:
|
||||
validate_data_file_name(path)
|
||||
parse_data_file(path)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
# vim: set expandtab shiftwidth=4 tabstop=4:
|
||||
#
|
||||
# Copyright © 2018 Red Hat, Inc.
|
||||
#
|
||||
|
||||
5
data/devices/glorious-model-d.device
Normal file
5
data/devices/glorious-model-d.device
Normal file
@@ -0,0 +1,5 @@
|
||||
[Device]
|
||||
Name=Glorious Model D
|
||||
DeviceMatch=usb:258a:0033
|
||||
Driver=sinowealth
|
||||
|
||||
5
data/devices/glorious-model-o.device
Normal file
5
data/devices/glorious-model-o.device
Normal file
@@ -0,0 +1,5 @@
|
||||
[Device]
|
||||
Name=Glorious Model O
|
||||
DeviceMatch=usb:258a:0036
|
||||
Driver=sinowealth
|
||||
|
||||
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;bluetooth:046d:b01b
|
||||
Driver=hidpp20
|
||||
4
data/devices/logitech-M720.device
Normal file
4
data/devices/logitech-M720.device
Normal file
@@ -0,0 +1,4 @@
|
||||
[Device]
|
||||
Name=Logitech M720
|
||||
DeviceMatch=usb:046d:405e;bluetooth:046d:b015
|
||||
Driver=hidpp20
|
||||
@@ -1,4 +1,4 @@
|
||||
[Device]
|
||||
Name=Logitech MX Anywhere 2
|
||||
DeviceMatch=usb:046d:404a;bluetooth:046d:b013;bluetooth:046d:b018
|
||||
DeviceMatch=usb:046d:404a;bluetooth:046d:b013;bluetooth:046d:b018;bluetooth:046d:b01f
|
||||
Driver=hidpp20
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[Device]
|
||||
Name=Logitech MX Vertical
|
||||
DeviceMatch=usb:046d:407b
|
||||
DeviceMatch=bluetooth:046d:b020;usb:046d:407b
|
||||
Driver=hidpp20
|
||||
|
||||
8
data/devices/logitech-g-powerplay.device
Normal file
8
data/devices/logitech-g-powerplay.device
Normal file
@@ -0,0 +1,8 @@
|
||||
[Device]
|
||||
Name=Logitech G Powerplay
|
||||
DeviceMatch=usb:046d:405f
|
||||
LedTypes=logo
|
||||
Driver=hidpp20
|
||||
|
||||
[Driver/hidpp20]
|
||||
DeviceIndex=7
|
||||
@@ -1,6 +1,6 @@
|
||||
# G102 and G203 (USB)
|
||||
# G102, G103 and G203 (USB)
|
||||
[Device]
|
||||
Name=Logitech Gaming Mouse G102/G103
|
||||
DeviceMatch=usb:046d:c084
|
||||
Name=Logitech Gaming Mouse G102/G103/G203
|
||||
DeviceMatch=usb:046d:c084;usb:046d:c092
|
||||
Driver=hidpp20
|
||||
LedTypes=logo;side;
|
||||
|
||||
@@ -4,4 +4,5 @@ DeviceMatch=usb:046d:4074
|
||||
Driver=hidpp20
|
||||
|
||||
[Driver/hidpp20]
|
||||
Leds=0
|
||||
Quirk=G305
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
[Device]
|
||||
Name=Logitech G402 Gaming Mouse
|
||||
DeviceMatch=usb:046d:c07e
|
||||
LedTypes=logo
|
||||
Driver=hidpp20
|
||||
|
||||
@@ -7,3 +7,4 @@ Driver=hidpp10
|
||||
[Driver/hidpp10]
|
||||
DpiList=400;800;1600;2000
|
||||
Profiles=1
|
||||
ProfileType=G500
|
||||
|
||||
5
data/devices/logitech-g513.device
Normal file
5
data/devices/logitech-g513.device
Normal file
@@ -0,0 +1,5 @@
|
||||
[Device]
|
||||
Name=Logitech G513
|
||||
DeviceMatch=usb:046d:c33c
|
||||
LedTypes=switches
|
||||
Driver=hidpp20
|
||||
5
data/devices/logitech-g815.device
Normal file
5
data/devices/logitech-g815.device
Normal file
@@ -0,0 +1,5 @@
|
||||
[Device]
|
||||
Name=Logitech G815
|
||||
DeviceMatch=usb:046d:c33f
|
||||
LedTypes=logo;switches
|
||||
Driver=hidpp20
|
||||
8
data/devices/logitech-g910.device
Normal file
8
data/devices/logitech-g910.device
Normal file
@@ -0,0 +1,8 @@
|
||||
[Device]
|
||||
Name=Logitech G910
|
||||
DeviceMatch=usb:046d:c335
|
||||
LedTypes=logo;switches
|
||||
Driver=hidpp20
|
||||
|
||||
[Driver/hidpp20]
|
||||
DeviceIndex=ff
|
||||
8
data/devices/logitech-g915.device
Normal file
8
data/devices/logitech-g915.device
Normal file
@@ -0,0 +1,8 @@
|
||||
[Device]
|
||||
Name=Logitech G915
|
||||
DeviceMatch=usb:046d:c33e
|
||||
LedTypes=logo;switches
|
||||
Driver=hidpp20
|
||||
|
||||
[Driver/hidpp20]
|
||||
DeviceIndex=1
|
||||
5
data/devices/logitech-g935.device
Normal file
5
data/devices/logitech-g935.device
Normal file
@@ -0,0 +1,5 @@
|
||||
[Device]
|
||||
Name=Logitech G935
|
||||
DeviceMatch=usb:046d:0a87
|
||||
LedTypes=logo;side
|
||||
Driver=hidpp20
|
||||
@@ -4,8 +4,10 @@
|
||||
Name=G9x [Original]
|
||||
DeviceMatch=usb:046d:c066
|
||||
Driver=hidpp10
|
||||
LedTypes=dpi
|
||||
|
||||
[Driver/hidpp10]
|
||||
DpiRange=0:5700@23.53
|
||||
DpiRange=200:5700@50
|
||||
ProfileType=G500
|
||||
Profiles=5
|
||||
Leds=1
|
||||
5
data/devices/nubwo-x7-spectrum.device
Normal file
5
data/devices/nubwo-x7-spectrum.device
Normal file
@@ -0,0 +1,5 @@
|
||||
[Device]
|
||||
Name=Nubwo x7 spectrum
|
||||
DeviceMatch=usb:258a:0012
|
||||
Driver=sinowealth_nubwo
|
||||
|
||||
74
data/devices/receiver-check.py
Executable file
74
data/devices/receiver-check.py
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright © 2018 Red Hat, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the "Software"),
|
||||
# to deal in the Software without restriction, including without limitation
|
||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
# and/or sell copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice (including the next
|
||||
# paragraph) shall be included in all copies or substantial portions of the
|
||||
# Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
# DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import configparser
|
||||
|
||||
# see the IDs from
|
||||
# https://github.com/torvalds/linux/blob/master/drivers/hid/hid-ids.h#L772
|
||||
# https://github.com/torvalds/linux/blob/master/drivers/hid/hid-logitech-dj.c#L1826
|
||||
logitech_receivers = [
|
||||
0xc50c, # USB_DEVICE_ID_S510_RECEIVER
|
||||
0xc517, # USB_DEVICE_ID_S510_RECEIVER_2
|
||||
0xc512, # USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500
|
||||
0xc513, # USB_DEVICE_ID_MX3000_RECEIVER
|
||||
0xc51b, # USB_DEVICE_ID_LOGITECH_27MHZ_MOUSE_RECEIVER
|
||||
0xc52b, # USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER
|
||||
0xc52f, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER
|
||||
0xc532, # USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2
|
||||
0xc534, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2
|
||||
0xc539, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1
|
||||
0xc53f, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1
|
||||
0xc53a, # USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY
|
||||
]
|
||||
RECEIVERS = ['usb:046d:{}'.format(r) for r in logitech_receivers]
|
||||
|
||||
|
||||
def parse_data_file(path):
|
||||
data = configparser.ConfigParser(strict=True)
|
||||
# Don't convert to lowercase
|
||||
data.optionxform = lambda option: option
|
||||
data.read(path)
|
||||
|
||||
matches = data['Device']['DeviceMatch']
|
||||
return matches.split(';')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Checks that no unifying receiver ID is in the device files')
|
||||
parser.add_argument('file', nargs='+')
|
||||
args = parser.parse_args()
|
||||
receiver_found = False
|
||||
for path in args.file:
|
||||
matches = parse_data_file(path)
|
||||
fname = os.path.basename(path)
|
||||
for m in matches:
|
||||
if m in RECEIVERS:
|
||||
print('Receiver ID {} found in file {}'.format(m, fname))
|
||||
receiver_found = True
|
||||
|
||||
if receiver_found:
|
||||
sys.exit(1)
|
||||
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;usb:1e7d:2dbe
|
||||
Driver=roccat-kone-pure
|
||||
26
doc/dbus.rst
26
doc/dbus.rst
@@ -85,13 +85,13 @@ interact with ratbagd.
|
||||
:type: i
|
||||
:flags: read-only,constant
|
||||
|
||||
The DBus API version. This is a stopgap feature while the DBus API
|
||||
is still in flux. The version has no backwards or
|
||||
forward-compatibilty guarantees - your client must understand
|
||||
**exactly** the same version, it is not enough to support an older
|
||||
or a newer version.
|
||||
The DBus API version. This is a stopgap feature while the DBus API
|
||||
is still in flux. The version has no backwards or
|
||||
forward-compatibilty guarantees - your client must understand
|
||||
**exactly** the same version, it is not enough to support an older
|
||||
or a newer version.
|
||||
|
||||
This API will be removed once the DBus API is declared stable.
|
||||
This API will be removed once the DBus API is declared stable.
|
||||
|
||||
.. attribute:: Devices
|
||||
|
||||
@@ -151,6 +151,20 @@ known to ratbagd.
|
||||
Vendor or product IDs of 0 are valid IDs (e.g. used used by test
|
||||
devices).
|
||||
|
||||
.. attribute:: FirmwareMajor
|
||||
|
||||
:type: i
|
||||
:flags: read-only, constant
|
||||
|
||||
The device's firmware major version.
|
||||
|
||||
.. attribute:: FirmwareMinor
|
||||
|
||||
:type: i
|
||||
:flags: read-only, constant
|
||||
|
||||
The device's firmware minor version.
|
||||
|
||||
.. attribute:: Name
|
||||
|
||||
:type: s
|
||||
|
||||
56
meson.build
56
meson.build
@@ -1,5 +1,5 @@
|
||||
project('libratbag', 'c',
|
||||
version : '0.11',
|
||||
version : '0.15',
|
||||
license : 'MIT/Expat',
|
||||
default_options : [ 'c_std=gnu99', 'warning_level=2' ],
|
||||
meson_version : '>= 0.40.0')
|
||||
@@ -172,8 +172,11 @@ 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-sinowealth.c',
|
||||
'src/driver-sinowealth-nubwo.c',
|
||||
'src/driver-test.c',
|
||||
'src/libratbag.c',
|
||||
'src/libratbag.h',
|
||||
@@ -281,7 +284,9 @@ 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-M720.device',
|
||||
'data/devices/logitech-MX-Ergo.device',
|
||||
'data/devices/logitech-MX-Master.device',
|
||||
'data/devices/logitech-MX-Master-2S.device',
|
||||
@@ -289,6 +294,7 @@ data_files = files(
|
||||
'data/devices/logitech-MX518.device',
|
||||
'data/devices/logitech-T650.device',
|
||||
'data/devices/logitech-Wireless-Touchpad.device',
|
||||
'data/devices/logitech-g-powerplay.device',
|
||||
'data/devices/logitech-g-pro-wireless.device',
|
||||
'data/devices/logitech-g-pro.device',
|
||||
'data/devices/logitech-g102-g203.device',
|
||||
@@ -308,6 +314,7 @@ data_files = files(
|
||||
'data/devices/logitech-g502-hero.device',
|
||||
'data/devices/logitech-g502-proteus-core.device',
|
||||
'data/devices/logitech-g502-proteus-spectrum.device',
|
||||
'data/devices/logitech-g513.device',
|
||||
'data/devices/logitech-g600.device',
|
||||
'data/devices/logitech-g602.device',
|
||||
'data/devices/logitech-g603.device',
|
||||
@@ -322,12 +329,17 @@ data_files = files(
|
||||
'data/devices/logitech-g900.device',
|
||||
'data/devices/logitech-g903-hero.device',
|
||||
'data/devices/logitech-g903.device',
|
||||
'data/devices/logitech-g9x-[Call-of-Duty-MW3-Edition].device',
|
||||
'data/devices/logitech-g9x-[Original].device',
|
||||
'data/devices/logitech-g815.device',
|
||||
'data/devices/logitech-g910.device',
|
||||
'data/devices/logitech-g915.device',
|
||||
'data/devices/logitech-g935.device',
|
||||
'data/devices/logitech-g9x-Call-of-Duty-MW3-Edition.device',
|
||||
'data/devices/logitech-g9x-Original.device',
|
||||
'data/devices/logitech-Marathon-M705.device',
|
||||
'data/devices/logitech-MX-Anywhere2.device',
|
||||
'data/devices/logitech-MX-Anywhere2S.device',
|
||||
'data/devices/logitech-MX-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',
|
||||
@@ -336,6 +348,9 @@ data_files = files(
|
||||
'data/devices/steelseries-rival.device',
|
||||
'data/devices/steelseries-sensei-310.device',
|
||||
'data/devices/steelseries-sensei-raw.device',
|
||||
'data/devices/glorious-model-d.device',
|
||||
'data/devices/glorious-model-o.device',
|
||||
'data/devices/nubwo-x7-spectrum.device',
|
||||
)
|
||||
|
||||
install_data(data_files,
|
||||
@@ -347,6 +362,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
|
||||
@@ -526,12 +544,29 @@ configure_file(input : 'dbus/org.freedesktop.ratbag1.conf.in',
|
||||
|
||||
#### tools ####
|
||||
|
||||
# This is a special idiomatic construct in meson to get a fast dependency that
|
||||
# doesn't exist and isn't logged as "not found".
|
||||
dep_python3 = dependency('', required : false)
|
||||
if meson.version().version_compare('<0.48.0')
|
||||
python = import('python3')
|
||||
py3 = python.find_python()
|
||||
py_version = python.language_version()
|
||||
else
|
||||
pymod = import('python')
|
||||
py3 = pymod.find_installation()
|
||||
if meson.version().version_compare('>= 0.53.0')
|
||||
dep_python3 = py3.dependency(embed : true)
|
||||
endif
|
||||
py_version = py3.language_version()
|
||||
endif
|
||||
|
||||
# From python 3.8 we neeed python3-embed
|
||||
embed = py_version.version_compare('>= 3.8') ? '-embed' : ''
|
||||
if not dep_python3.found()
|
||||
dep_python3 = dependency('python-@0@@1@'.format(py_version, embed), required : false)
|
||||
if not dep_python3.found()
|
||||
dep_python3 = dependency('python3@0@'.format(embed))
|
||||
endif
|
||||
endif
|
||||
|
||||
config_ratbagctl = configuration_data()
|
||||
@@ -595,12 +630,6 @@ swig_gen = generator(
|
||||
'@INPUT@'],
|
||||
)
|
||||
|
||||
# From python 3.8 we neeed python3-embed
|
||||
dep_python3 = dependency('python3-embed', required: false)
|
||||
if not dep_python3.found()
|
||||
dep_python3 = dependency('python3')
|
||||
endif
|
||||
|
||||
wrapper_deps = [
|
||||
dep_python3,
|
||||
dep_libratbag,
|
||||
@@ -613,7 +642,9 @@ shared_library(
|
||||
'libratbag',
|
||||
c_source,
|
||||
name_prefix: '_',
|
||||
c_args : ['-Wno-missing-prototypes', '-Wno-format-nonliteral'],
|
||||
c_args : ['-Wno-missing-prototypes',
|
||||
'-Wno-format-nonliteral',
|
||||
'-Wno-missing-field-initializers'],
|
||||
extra_files: [ i_source ] + src_libratbag ,
|
||||
dependencies: deps_libratbag + wrapper_deps,
|
||||
include_directories : include_directories('src', 'tools'),
|
||||
@@ -626,7 +657,10 @@ shared_library(
|
||||
'hidpp',
|
||||
c_source,
|
||||
name_prefix: '_',
|
||||
c_args : ['-Wno-missing-prototypes', '-Wno-format-nonliteral'],
|
||||
c_args : ['-Wno-missing-prototypes',
|
||||
'-Wno-format-nonliteral',
|
||||
'-Wno-missing-field-initializers',
|
||||
'-Wno-strict-prototypes'],
|
||||
extra_files: [ i_source ] + src_libratbag ,
|
||||
dependencies: deps_libratbag + wrapper_deps,
|
||||
include_directories : include_directories('src', 'tools'),
|
||||
|
||||
12
ratbagd/.gitignore
vendored
12
ratbagd/.gitignore
vendored
@@ -1,12 +0,0 @@
|
||||
*.a
|
||||
*.log
|
||||
*.o
|
||||
*.stamp
|
||||
*.swp
|
||||
*.trs
|
||||
*~
|
||||
.dirstamp
|
||||
.libs/
|
||||
/*.tar.xz
|
||||
tags
|
||||
__pycache__
|
||||
@@ -172,6 +172,8 @@ static void ratbagd_device_commit_pending(void *data)
|
||||
int r;
|
||||
|
||||
r = ratbag_device_commit(device->lib_device);
|
||||
if (r)
|
||||
log_error("error committing device (%d)\n", r);
|
||||
if (r < 0)
|
||||
ratbagd_device_resync(device, device->ctx->bus);
|
||||
ratbagd_device_unref(device);
|
||||
@@ -218,9 +220,45 @@ ratbag_device_get_model(sd_bus *bus,
|
||||
return sd_bus_message_append(reply, "s", model);
|
||||
}
|
||||
|
||||
static int
|
||||
ratbag_device_get_firmware_major(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_device *device = userdata;
|
||||
int version = ratbag_device_get_firmware_major_version(device->lib_device);
|
||||
|
||||
CHECK_CALL(sd_bus_message_append(reply, "i", version));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ratbag_device_get_firmware_minor(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error)
|
||||
{
|
||||
struct ratbagd_device *device = userdata;
|
||||
int version = ratbag_device_get_firmware_minor_version(device->lib_device);
|
||||
|
||||
CHECK_CALL(sd_bus_message_append(reply, "i", version));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const sd_bus_vtable ratbagd_device_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_PROPERTY("Model", "s", ratbag_device_get_model, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("FirmwareMajor", "i", ratbag_device_get_firmware_major, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("FirmwareMinor", "i", ratbag_device_get_firmware_minor, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Name", "s", ratbagd_device_get_device_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Profiles", "ao", ratbagd_device_get_profiles, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_METHOD("Commit", "", "u", ratbagd_device_commit, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
|
||||
@@ -170,7 +170,7 @@ static void parse_led_member(JsonObject *obj, const gchar *name,
|
||||
if (streq(name, "type")) {
|
||||
gboolean v = json_object_get_int_member(obj, name);
|
||||
|
||||
if (v < 0 || v > RATBAG_LED_TYPE_WHEEL)
|
||||
if (v < 0 || v > RATBAG_LED_TYPE_SWITCHES)
|
||||
parser_error("type");
|
||||
|
||||
led->type = v;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <libratbag.h>
|
||||
#include <libudev.h>
|
||||
#include <stdio.h>
|
||||
@@ -356,6 +357,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 +522,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);
|
||||
}
|
||||
|
||||
@@ -544,6 +548,13 @@ static bool install_ratbagd_devel_dbus_policy(void)
|
||||
log_error("Failed to source policy file: %m\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mkdir_p(dirname(strdupa(DBUS_POLICY_DST)), 0755)) {
|
||||
log_error("Failed to create destination path: %m\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
out = open(DBUS_POLICY_DST, O_CREAT|O_WRONLY, 0644);
|
||||
if (out == -1) {
|
||||
log_error("Failed to open destination: %m\n");
|
||||
@@ -617,12 +628,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;
|
||||
|
||||
@@ -164,6 +164,7 @@ hidpp10drv_map_button(struct ratbag_device *device,
|
||||
{
|
||||
struct hidpp10_profile profile;
|
||||
int ret;
|
||||
unsigned int modifiers = 0;
|
||||
|
||||
ret = hidpp10_get_profile(hidpp10, button->profile->index, &profile);
|
||||
if (ret)
|
||||
@@ -178,6 +179,7 @@ hidpp10drv_map_button(struct ratbag_device *device,
|
||||
button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
|
||||
button->action.action.key.key = ratbag_hidraw_get_keycode_from_keyboard_usage(device,
|
||||
profile.buttons[button->index].keys.key);
|
||||
modifiers = profile.buttons[button->index].keys.modifier_flags;
|
||||
break;
|
||||
case PROFILE_BUTTON_TYPE_CONSUMER_CONTROL:
|
||||
button->action.type = RATBAG_BUTTON_ACTION_TYPE_KEY;
|
||||
@@ -199,6 +201,14 @@ hidpp10drv_map_button(struct ratbag_device *device,
|
||||
}
|
||||
}
|
||||
|
||||
if (button->action.type == RATBAG_BUTTON_ACTION_TYPE_KEY) {
|
||||
ret = ratbag_button_macro_new_from_keycode(button, button->action.action.key.key, modifiers);
|
||||
if (ret < 0) {
|
||||
log_error(device->ratbag, "hidpp10: error while reading button %d\n", button->index);
|
||||
button->action.type = RATBAG_BUTTON_ACTION_TYPE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
ratbag_button_enable_action_type(button, RATBAG_BUTTON_ACTION_TYPE_MACRO);
|
||||
}
|
||||
|
||||
@@ -299,6 +309,8 @@ hidpp10drv_write_button(struct hidpp10_profile *profile,
|
||||
struct hidpp10drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct hidpp10_device *hidpp10 = drv_data->dev;
|
||||
uint8_t code;
|
||||
unsigned int modifiers, key;
|
||||
int rc;
|
||||
|
||||
if (hidpp10->profile_type == HIDPP10_PROFILE_UNKNOWN)
|
||||
return -ENOTSUP;
|
||||
@@ -330,6 +342,26 @@ hidpp10drv_write_button(struct hidpp10_profile *profile,
|
||||
profile->buttons[button->index].special.special = code;
|
||||
break;
|
||||
case RATBAG_BUTTON_ACTION_TYPE_MACRO:
|
||||
rc = ratbag_action_keycode_from_macro(action, &key, &modifiers);
|
||||
if (rc < 0) {
|
||||
log_error(device->ratbag, "hidpp10: can't convert macro action to keycode in button %d\n", button->index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
code = ratbag_hidraw_get_keyboard_usage_from_keycode(device, key);
|
||||
if (code == 0) {
|
||||
code = ratbag_hidraw_get_consumer_usage_from_keycode(device, key);
|
||||
if (code == 0)
|
||||
return -EINVAL;
|
||||
|
||||
profile->buttons[button->index].consumer_control.type = PROFILE_BUTTON_TYPE_CONSUMER_CONTROL;
|
||||
profile->buttons[button->index].consumer_control.consumer_control = code;
|
||||
} else {
|
||||
profile->buttons[button->index].keys.type = PROFILE_BUTTON_TYPE_KEYS;
|
||||
profile->buttons[button->index].keys.key = code;
|
||||
profile->buttons[button->index].keys.modifier_flags = modifiers;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
@@ -541,7 +573,8 @@ hidpp10drv_commit(struct ratbag_device *device)
|
||||
return rc;
|
||||
|
||||
p.enabled = profile->is_enabled;
|
||||
strncpy_safe((char*)p.name, profile->name, 24);
|
||||
if (profile->name != NULL)
|
||||
strncpy_safe((char*)p.name, profile->name, 24);
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
p.dpi_modes[resolution->index].xres = resolution->dpi_x;
|
||||
@@ -558,8 +591,10 @@ hidpp10drv_commit(struct ratbag_device *device)
|
||||
continue;
|
||||
|
||||
rc = hidpp10drv_write_button(&p, button, &action);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp10: failed to update buttons (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
ratbag_profile_for_each_led(profile, led)
|
||||
@@ -567,8 +602,10 @@ hidpp10drv_commit(struct ratbag_device *device)
|
||||
|
||||
if (dev->profile_type != HIDPP10_PROFILE_UNKNOWN) {
|
||||
rc = hidpp10_set_profile(dev, profile->index, &p);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp10: failed to set profile (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the current resolution in case it changed */
|
||||
@@ -576,8 +613,10 @@ hidpp10drv_commit(struct ratbag_device *device)
|
||||
rc = hidpp10_set_current_resolution(dev,
|
||||
active_resolution->dpi_x,
|
||||
active_resolution->dpi_y);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp10: failed to set active resolution (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
|
||||
active_resolution = NULL;
|
||||
}
|
||||
@@ -631,9 +670,9 @@ hidpp10drv_probe(struct ratbag_device *device)
|
||||
* If there is a special need like for G700(s), then add a DeviceIndex
|
||||
* entry to the .device file.
|
||||
*/
|
||||
dev = hidpp10_device_new(&base, device_idx, type, profile_count);
|
||||
rc = hidpp10_device_new(&base, device_idx, type, profile_count, &dev);
|
||||
|
||||
if (!dev) {
|
||||
if (rc) {
|
||||
log_error(device->ratbag,
|
||||
"Failed to get HID++1.0 device for %s\n",
|
||||
device->name);
|
||||
@@ -714,14 +753,14 @@ static void
|
||||
hidpp10drv_remove(struct ratbag_device *device)
|
||||
{
|
||||
struct hidpp10drv_data *drv_data;
|
||||
struct hidpp10_device *dev;
|
||||
|
||||
ratbag_close_hidraw(device);
|
||||
|
||||
drv_data = ratbag_get_drv_data(device);
|
||||
dev = drv_data->dev;
|
||||
if (!drv_data)
|
||||
return;
|
||||
|
||||
hidpp10_device_destroy(dev);
|
||||
hidpp10_device_destroy(drv_data->dev);
|
||||
|
||||
free(drv_data);
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ struct hidpp20drv_data {
|
||||
unsigned num_controls;
|
||||
struct hidpp20_control_id *controls;
|
||||
struct hidpp20_profiles *profiles;
|
||||
struct hidpp20_led *leds;
|
||||
union hidpp20_generic_led_zone_info led_infos;
|
||||
|
||||
unsigned int report_rates[4];
|
||||
@@ -334,14 +335,24 @@ static void
|
||||
hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_data)
|
||||
{
|
||||
struct hidpp20_profile *profile;
|
||||
struct hidpp20_led *h_led;
|
||||
struct hidpp20_led h_led_val;
|
||||
struct hidpp20_led *h_led = &h_led_val;
|
||||
struct hidpp20_color_led_zone_info* led_info;
|
||||
struct hidpp20_color_led_info info;
|
||||
int rc;
|
||||
|
||||
led_info = &drv_data->led_infos.color_leds_8070[led->index];
|
||||
profile = &drv_data->profiles->profiles[led->profile->index];
|
||||
h_led = &profile->leds[led->index];
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
|
||||
profile = &drv_data->profiles->profiles[led->profile->index];
|
||||
h_led = &profile->leds[led->index];
|
||||
} else {
|
||||
rc = hidpp20_color_led_effects_get_zone_effect(drv_data->dev, led->index, h_led);
|
||||
if (rc) {
|
||||
log_debug(led->profile->device->ratbag,
|
||||
"Failed to read led settings\n");
|
||||
}
|
||||
}
|
||||
|
||||
switch (h_led->mode) {
|
||||
case HIDPP20_LED_ON:
|
||||
@@ -397,10 +408,10 @@ hidpp20drv_read_led_8070(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
|
||||
break;
|
||||
default:
|
||||
log_bug_libratbag(led->profile->device->ratbag,
|
||||
"%s: Unknown effect id %d\n",
|
||||
led->profile->device->name,
|
||||
ei.effect_id);
|
||||
log_debug(led->profile->device->ratbag,
|
||||
"%s: Unsupported effect (%d)\n",
|
||||
led->profile->device->name,
|
||||
ei.effect_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -472,10 +483,10 @@ hidpp20drv_read_led_8071(struct ratbag_led *led, struct hidpp20drv_data* drv_dat
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
|
||||
break;
|
||||
default:
|
||||
log_bug_libratbag(led->profile->device->ratbag,
|
||||
"%s: Unknown effect id %d\n",
|
||||
led->profile->device->name,
|
||||
ei.effect_id);
|
||||
log_debug(led->profile->device->ratbag,
|
||||
"%s: Unsupported effect (%d)\n",
|
||||
led->profile->device->name,
|
||||
ei.effect_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -564,7 +575,7 @@ hidpp20drv_update_button_8100(struct ratbag_button *button)
|
||||
code = ratbag_hidraw_get_keyboard_usage_from_keycode(device, key);
|
||||
if (code == 0) {
|
||||
subtype = HIDPP20_BUTTON_HID_TYPE_CONSUMER_CONTROL;
|
||||
code = ratbag_hidraw_get_consumer_usage_from_keycode(device, action->action.key.key);
|
||||
code = ratbag_hidraw_get_consumer_usage_from_keycode(device, key);
|
||||
if (code == 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -691,11 +702,13 @@ hidpp20drv_update_led_8070_8071(struct ratbag_led *led, struct ratbag_profile* p
|
||||
struct hidpp20drv_data *drv_data)
|
||||
{
|
||||
struct hidpp20_profile *h_profile;
|
||||
struct hidpp20_led *h_led;
|
||||
struct hidpp20_led h_led_val;
|
||||
struct hidpp20_led *h_led = &h_led_val;
|
||||
|
||||
h_profile = &drv_data->profiles->profiles[profile->index];
|
||||
|
||||
h_led = &(h_profile->leds[led->index]);
|
||||
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
|
||||
h_profile = &drv_data->profiles->profiles[profile->index];
|
||||
h_led = &(h_profile->leds[led->index]);
|
||||
}
|
||||
|
||||
if (!h_led)
|
||||
return -EINVAL;
|
||||
@@ -720,6 +733,13 @@ hidpp20drv_update_led_8070_8071(struct ratbag_led *led, struct ratbag_profile* p
|
||||
h_led->period = led->ms;
|
||||
h_led->brightness = led->brightness * 100 / 255;
|
||||
|
||||
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)) {
|
||||
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070)
|
||||
hidpp20_color_led_effects_set_zone_effect(drv_data->dev,
|
||||
led->index,
|
||||
h_led_val);
|
||||
}
|
||||
|
||||
return RATBAG_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -822,9 +842,12 @@ hidpp20drv_read_report_rate_8060(struct ratbag_device *device)
|
||||
{
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct ratbag *ratbag = device->ratbag;
|
||||
struct ratbag_profile *profile;
|
||||
uint8_t bitflags_ms;
|
||||
int nrates = 0;
|
||||
int rc;
|
||||
uint8_t rate_ms;
|
||||
unsigned rate_hz;
|
||||
|
||||
rc = hidpp20_adjustable_report_rate_get_report_rate_list(drv_data->dev,
|
||||
&bitflags_ms);
|
||||
@@ -843,6 +866,37 @@ hidpp20drv_read_report_rate_8060(struct ratbag_device *device)
|
||||
|
||||
drv_data->num_report_rates = nrates;
|
||||
|
||||
if (!hidpp20_adjustable_report_rate_get_report_rate(drv_data->dev, &rate_ms)) {
|
||||
switch (rate_ms)
|
||||
{
|
||||
case 1:
|
||||
rate_hz = 1000;
|
||||
break;
|
||||
case 2:
|
||||
case 3: /* 3ms = 333.(3)Hz, we round to 500Hz */
|
||||
rate_hz = 500;
|
||||
break;
|
||||
case 4:
|
||||
case 5: /* 5ms = 200Hz, we round to 250Hz */
|
||||
rate_hz = 250;
|
||||
break;
|
||||
case 6: /* 6ms = 166.(6)Hz, we round to 125Hz */
|
||||
case 7: /* 7ms = 142Hz, we round to 125Hz */
|
||||
case 8:
|
||||
rate_hz = 125;
|
||||
break;
|
||||
default:
|
||||
rate_hz = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rate_hz) {
|
||||
log_debug(ratbag, "report rate is %u\n", rate_hz);
|
||||
ratbag_device_for_each_profile(device, profile)
|
||||
profile->hz = rate_hz;
|
||||
}
|
||||
}
|
||||
|
||||
log_debug(ratbag, "device has %d report rates\n", nrates);
|
||||
|
||||
return 0;
|
||||
@@ -943,7 +997,7 @@ hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct hidpp20_sensor *sensor;
|
||||
int rc, i;
|
||||
int i;
|
||||
int dpi = dpi_x; /* dpi_x == dpi_y if we don't have the individual resolution cap */
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
|
||||
@@ -959,14 +1013,13 @@ hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
|
||||
sensor = &drv_data->sensors[0];
|
||||
|
||||
/* validate that the sensor accepts the given DPI */
|
||||
rc = -EINVAL;
|
||||
if (dpi < sensor->dpi_min || dpi > sensor->dpi_max)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
if (sensor->dpi_steps) {
|
||||
for (i = sensor->dpi_min; i < dpi; i += sensor->dpi_steps) {
|
||||
}
|
||||
if (i != dpi)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
} else {
|
||||
i = 0;
|
||||
while (sensor->dpi_list[i]) {
|
||||
@@ -974,13 +1027,24 @@ hidpp20drv_update_resolution_dpi(struct ratbag_resolution *resolution,
|
||||
break;
|
||||
}
|
||||
if (sensor->dpi_list[i] != dpi)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = hidpp20_adjustable_dpi_set_sensor_dpi(drv_data->dev, sensor, dpi);
|
||||
return hidpp20_adjustable_dpi_set_sensor_dpi(drv_data->dev, sensor, dpi);
|
||||
}
|
||||
|
||||
out:
|
||||
return rc;
|
||||
static int
|
||||
hidpp20drv_update_report_rate_8060(struct ratbag_profile *profile, int hz)
|
||||
{
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
int rc;
|
||||
|
||||
rc = hidpp20_adjustable_report_rate_set_report_rate(drv_data->dev, 1000/hz);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return RATBAG_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1001,12 +1065,20 @@ hidpp20drv_update_report_rate(struct ratbag_profile *profile, int hz)
|
||||
{
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
int rc;
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100)
|
||||
return hidpp20drv_update_report_rate_8100(profile, hz);
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060)
|
||||
return -ENOTSUP;
|
||||
if (drv_data->capabilities & HIDPP_CAP_ADJUSTIBLE_REPORT_RATE_8060) {
|
||||
rc = hidpp20drv_update_report_rate_8060(profile, hz);
|
||||
|
||||
/* re-populate the profile with the correct value if we fail */
|
||||
if (rc)
|
||||
hidpp20drv_read_report_rate_8060(profile->device);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
@@ -1179,6 +1251,60 @@ hidpp20drv_read_profile_8100(struct ratbag_profile *profile)
|
||||
ratbag_profile_set_report_rate(profile, p->report_rate);
|
||||
}
|
||||
|
||||
static int
|
||||
hidpp20drv_init_leds_8070_8071(struct ratbag_device *device)
|
||||
{
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct ratbag *ratbag = device->ratbag;
|
||||
|
||||
/* we only support 0x8071 via 0x8100 */
|
||||
if (!(drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) &&
|
||||
drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071) {
|
||||
log_debug(ratbag, "disabling 0x8071 (RGB Effects) feature because the device doesn't have 0x8100 (Onboard Memory Profiles)\n");
|
||||
drv_data->capabilities &= ~HIDPP_CAP_RGB_EFFECTS_8071;
|
||||
}
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070 ||
|
||||
drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071) {
|
||||
/* we read the profile once to get the correct number of
|
||||
* supported leds. */
|
||||
if (hidpp20drv_read_color_leds(device))
|
||||
return 0;
|
||||
|
||||
device->num_leds = drv_data->num_leds;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
hidpp20drv_init_profile_8100(struct ratbag_device *device)
|
||||
{
|
||||
struct hidpp20drv_data *drv_data = ratbag_get_drv_data(device);
|
||||
struct ratbag *ratbag = device->ratbag;
|
||||
int rc;
|
||||
|
||||
log_debug(ratbag, "initializing onboard profiles\n");
|
||||
rc = hidpp20_onboard_profiles_allocate(drv_data->dev, &drv_data->profiles);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = hidpp20_onboard_profiles_initialize(drv_data->dev, drv_data->profiles);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
drv_data->num_profiles = drv_data->profiles->num_profiles;
|
||||
drv_data->num_buttons = drv_data->profiles->num_buttons;
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_SWITCHABLE_RESOLUTION_2201)
|
||||
drv_data->num_resolutions = drv_data->profiles->num_modes;
|
||||
/* We ignore the profile's num_leds and require
|
||||
* HIDPP_PAGE_COLOR_LED_EFFECTS to succeed instead
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
hidpp20drv_read_profile(struct ratbag_profile *profile)
|
||||
{
|
||||
@@ -1313,25 +1439,11 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
|
||||
|
||||
log_debug(ratbag, "device has color effects\n");
|
||||
drv_data->capabilities |= HIDPP_CAP_COLOR_LED_EFFECTS_8070;
|
||||
|
||||
/* we read the profile once to get the correct number of
|
||||
* supported leds. */
|
||||
if (hidpp20drv_read_color_leds(device))
|
||||
return 0;
|
||||
|
||||
device->num_leds = drv_data->num_leds;
|
||||
break;
|
||||
}
|
||||
case HIDPP_PAGE_RGB_EFFECTS: {
|
||||
log_debug(ratbag, "device has color effects\n");
|
||||
drv_data->capabilities |= HIDPP_CAP_RGB_EFFECTS_8071;
|
||||
|
||||
/* we read the profile once to get the correct number of
|
||||
* supported leds. */
|
||||
if (hidpp20drv_read_color_leds(device))
|
||||
return 0;
|
||||
|
||||
device->num_leds = drv_data->num_leds;
|
||||
break;
|
||||
}
|
||||
case HIDPP_PAGE_LED_SW_CONTROL: {
|
||||
@@ -1349,22 +1461,6 @@ hidpp20drv_init_feature(struct ratbag_device *device, uint16_t feature)
|
||||
case HIDPP_PAGE_ONBOARD_PROFILES: {
|
||||
log_debug(ratbag, "device has onboard profiles\n");
|
||||
drv_data->capabilities |= HIDPP_CAP_ONBOARD_PROFILES_8100;
|
||||
|
||||
rc = hidpp20_onboard_profiles_allocate(drv_data->dev, &drv_data->profiles);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = hidpp20_onboard_profiles_initialize(drv_data->dev, drv_data->profiles);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
drv_data->num_profiles = drv_data->profiles->num_profiles;
|
||||
drv_data->num_resolutions = drv_data->profiles->num_modes;
|
||||
drv_data->num_buttons = drv_data->profiles->num_buttons;
|
||||
/* We ignore the profile's num_leds and require
|
||||
* HIDPP_PAGE_COLOR_LED_EFFECTS to succeed instead
|
||||
*/
|
||||
|
||||
break;
|
||||
}
|
||||
case HIDPP_PAGE_MOUSE_BUTTON_SPY: {
|
||||
@@ -1386,27 +1482,27 @@ hidpp20drv_commit(struct ratbag_device *device)
|
||||
struct ratbag_led *led;
|
||||
struct ratbag_resolution *resolution;
|
||||
int rc;
|
||||
unsigned int dpi_index = 0;
|
||||
|
||||
list_for_each(profile, &device->profiles, link) {
|
||||
if (!profile->dirty)
|
||||
continue;
|
||||
|
||||
rc = hidpp20drv_update_report_rate(profile, profile->hz);
|
||||
if (rc)
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
if (profile->rate_dirty) {
|
||||
rc = hidpp20drv_update_report_rate(profile, profile->hz);
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp20: failed to update report rate (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (resolution->is_active) {
|
||||
log_raw(device->ratbag, "dpi index: %d, profile %d\n", resolution->index, profile->index);
|
||||
dpi_index = resolution->index;
|
||||
}
|
||||
|
||||
rc = hidpp20drv_update_resolution_dpi(resolution,
|
||||
resolution->dpi_x,
|
||||
resolution->dpi_y);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp20: failed to update resolution (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each(button, &profile->buttons, link) {
|
||||
@@ -1414,8 +1510,10 @@ hidpp20drv_commit(struct ratbag_device *device)
|
||||
continue;
|
||||
|
||||
rc = hidpp20drv_update_button(button);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp20: failed to update button (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each(led, &profile->leds, link) {
|
||||
@@ -1423,10 +1521,11 @@ hidpp20drv_commit(struct ratbag_device *device)
|
||||
continue;
|
||||
|
||||
rc = hidpp20drv_update_led(led);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp20: failed to update led (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
|
||||
@@ -1435,17 +1534,18 @@ hidpp20drv_commit(struct ratbag_device *device)
|
||||
|
||||
rc = hidpp20_onboard_profiles_commit(drv_data->dev,
|
||||
drv_data->profiles);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
log_error(device->ratbag, "hidpp20: failed to commit profile (%d)\n", rc);
|
||||
return RATBAG_ERROR_DEVICE;
|
||||
}
|
||||
|
||||
list_for_each(profile, &device->profiles, link) {
|
||||
if (profile->is_active) {
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (resolution->is_active)
|
||||
dpi_index = resolution->index;
|
||||
hidpp20_onboard_profiles_set_current_dpi_index(drv_data->dev,
|
||||
resolution->index);
|
||||
}
|
||||
hidpp20_onboard_profiles_set_current_dpi_index(drv_data->dev,
|
||||
dpi_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1476,8 +1576,21 @@ hidpp20drv_20_probe(struct ratbag_device *device)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* initializations that depend on other features */
|
||||
if (drv_data->capabilities & HIDPP_CAP_COLOR_LED_EFFECTS_8070 ||
|
||||
drv_data->capabilities & HIDPP_CAP_RGB_EFFECTS_8071) {
|
||||
rc = hidpp20drv_init_leds_8070_8071(device);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (drv_data->capabilities & HIDPP_CAP_ONBOARD_PROFILES_8100) {
|
||||
rc = hidpp20drv_init_profile_8100(device);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1509,9 +1622,9 @@ 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);
|
||||
free(drv_data->leds);
|
||||
if (drv_data->dev)
|
||||
hidpp20_device_destroy(drv_data->dev);
|
||||
free(drv_data);
|
||||
@@ -1523,6 +1636,11 @@ hidpp20drv_init_device(struct ratbag_device *device,
|
||||
{
|
||||
struct ratbag_profile *profile;
|
||||
bool active_profile = false;
|
||||
int num;
|
||||
|
||||
num = ratbag_device_data_hidpp20_get_led_count(device->data);
|
||||
if (num >= 0)
|
||||
drv_data->num_leds = num;
|
||||
|
||||
ratbag_device_init_profiles(device,
|
||||
drv_data->num_profiles,
|
||||
@@ -1576,7 +1694,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;
|
||||
@@ -1593,7 +1711,7 @@ hidpp20drv_probe(struct ratbag_device *device)
|
||||
|
||||
/* add some defaults that will be overwritten by the device */
|
||||
drv_data->num_profiles = 1;
|
||||
drv_data->num_resolutions = 1;
|
||||
drv_data->num_resolutions = 0;
|
||||
drv_data->num_buttons = 8;
|
||||
drv_data->num_leds = 0;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#define LOGITECH_G300_BUTTON_MAX 8
|
||||
#define LOGITECH_G300_NUM_DPI 4
|
||||
#define LOGITECH_G300_NUM_LED 1
|
||||
#define LOGITECH_G300_DPI_MIN 250
|
||||
#define LOGITECH_G300_DPI_MIN 100
|
||||
#define LOGITECH_G300_DPI_MAX 2500
|
||||
|
||||
#define LOGITECH_G300_REPORT_ID_GET_ACTIVE 0xF0
|
||||
@@ -51,7 +51,7 @@
|
||||
#define LOGITECH_G300_REPORT_SIZE_PROFILE 35
|
||||
|
||||
struct logitech_g300_resolution {
|
||||
uint8_t dpi :7; /* Range 1-10. dpi = 250*value */
|
||||
uint8_t dpi :7; /* Range 1-10. dpi = 100*value */
|
||||
uint8_t is_default :1;
|
||||
} __attribute__((packed));
|
||||
|
||||
@@ -449,11 +449,10 @@ logitech_g300_read_profile(struct ratbag_profile *profile)
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
struct logitech_g300_resolution *res =
|
||||
&report->dpi_levels[resolution->index];
|
||||
unsigned int dpis[10] = { 250, 500, 750, 1000, 1250, 1500, 1750,
|
||||
2000, 2250, 2500 };
|
||||
unsigned int dpis[49] = { 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 1600, 1650, 1700, 1750, 1800, 1850, 1900, 1950, 2000, 2050, 2100, 2150, 2200, 2250, 2300, 2350, 2400, 2450, 2500, };
|
||||
|
||||
resolution->dpi_x = res->dpi * 250;
|
||||
resolution->dpi_y = res->dpi * 250;
|
||||
resolution->dpi_x = res->dpi * 50;
|
||||
resolution->dpi_y = res->dpi * 50;
|
||||
resolution->is_default = res->is_default;
|
||||
resolution->is_active = res->is_default;
|
||||
|
||||
@@ -546,7 +545,7 @@ logitech_g300_write_profile(struct ratbag_profile *profile)
|
||||
struct logitech_g300_resolution *res =
|
||||
&report->dpi_levels[resolution->index];
|
||||
|
||||
res->dpi = resolution->dpi_x / 250;
|
||||
res->dpi = resolution->dpi_x / 50;
|
||||
res->is_default = resolution->is_default;
|
||||
|
||||
if (profile->is_active && resolution->is_active)
|
||||
|
||||
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-kone-pure",
|
||||
.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,
|
||||
};
|
||||
319
src/driver-sinowealth-nubwo.c
Normal file
319
src/driver-sinowealth-nubwo.c
Normal file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright © 2020 Pipat Saengow
|
||||
*
|
||||
* 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 "libratbag-enums.h"
|
||||
#include "libratbag-hidraw.h"
|
||||
#include "libratbag-private.h"
|
||||
#include "libratbag-util.h"
|
||||
#include "shared-macro.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SINOWEALTHNUBWO_PERF_CMD_REPORTID 0x02
|
||||
#define SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID 0x03
|
||||
#define SINOWEALTHNUBWO_GET_FIRMWARE_CMD_REPORTID 0x04
|
||||
|
||||
#define SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE 256
|
||||
#define SINOWEALTHNUBWO_GET_FIRMWARE_MSGOFFSET 48
|
||||
|
||||
#define SINOWEALTHNUBWO_PERF_CMD_MSGSIZE 16
|
||||
|
||||
// Actually more but I only implemented one
|
||||
#define SINOWEALTHNUBWO_NUM_PROFILES 1
|
||||
#define SINOWEALTHNUBWO_NUM_RESOLUTIONS 1
|
||||
// Actually 8 but I am not going to implement macros.
|
||||
#define SINOWEALTHNUBWO_NUM_BUTTONS 0
|
||||
#define SINOWEALTHNUBWO_NUM_LEDS 1
|
||||
|
||||
//Magic set_feature that must be called before requesting firmware string
|
||||
static uint8_t PREFIRMWARE_QUERY_MSG[] = {0x02, 0x01, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
static unsigned int REPORT_RATES[] = { 125, 250, 333, 500, 1000 };
|
||||
static uint8_t REPORT_RATES_ENCODED[] = {0x08, 0x04, 0x03, 0x02, 0x01};
|
||||
static uint8_t REPORT_RATES_CMD[] = {0x02, 0x06, 0xbb, 0xaa, 0x28, 0x00, 0x01, 0x00};
|
||||
|
||||
static unsigned int DPILIST[] = { 1000, 2000, 3000, 5000, 15000};
|
||||
static uint8_t DPI_ENCODED[] = {0x04, 0x03, 0x02, 0x01, 0x00};
|
||||
static uint8_t DPI_CMD[] = { 0x02, 0x06, 0xbb, 0xaa, 0x32, 0x00, 0x01, 0x00 };
|
||||
|
||||
enum
|
||||
sinowealthnubwo_color_mode {
|
||||
SINOWEALTHNUBWO_COLOR_OFF = 0x00,
|
||||
SINOWEALTHNUBWO_COLOR_ON = 0x01,
|
||||
SINOWEALTHNUBWO_COLOR_BREATHING = 0x02,
|
||||
SINOWEALTHNUBWO_COLOR_COLOR_SHIFT = 0x03,
|
||||
SINOWEALTHNUBWO_COLOR_SPECTRUM = 0x04,
|
||||
SINOWEALTHNUBWO_COLOR_MARQUEE = 0x05,
|
||||
};
|
||||
|
||||
struct
|
||||
sinowealthnubwo_aesthetic_report {
|
||||
uint8_t report_id; //0x03
|
||||
uint8_t cmd[7];
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t color_mode;
|
||||
uint8_t padzero; // 0x00
|
||||
uint8_t brightness; // 0x01 to 0x03
|
||||
uint8_t tempo; // 0x05 0x03 0x01
|
||||
uint8_t padzero2[16*3+1];
|
||||
} __packed__;
|
||||
|
||||
static uint8_t AESTHETIC_CMD[] = {0x06, 0xbb, 0xaa, 0x2a, 0x00, 0x0a, 0x00};
|
||||
|
||||
static int
|
||||
sinowealthnubwo_test_hidraw(struct ratbag_device *device)
|
||||
{
|
||||
return
|
||||
ratbag_hidraw_has_report(device, SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID)
|
||||
&& ratbag_hidraw_has_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID)
|
||||
&& ratbag_hidraw_has_report(device, SINOWEALTHNUBWO_GET_FIRMWARE_CMD_REPORTID);
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealth_get_firmware_string(struct ratbag_device *device, char **output)
|
||||
{
|
||||
uint8_t buff[SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE + 1] = {0}; //Purposefully overalloacate to prevent buffer overrun
|
||||
int size;
|
||||
|
||||
size = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID, PREFIRMWARE_QUERY_MSG, ARRAY_LENGTH(PREFIRMWARE_QUERY_MSG));
|
||||
if (size < 0) {
|
||||
log_error(device->ratbag, "Error while sending pre-firmware request message: %d\n", size);
|
||||
return size;
|
||||
}
|
||||
|
||||
size = ratbag_hidraw_get_feature_report(device, SINOWEALTHNUBWO_GET_FIRMWARE_CMD_REPORTID, buff, SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE);
|
||||
if (size != SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE) {
|
||||
log_error(device->ratbag ,"Firmware report reply size mismatch expected %d got %d\n", SINOWEALTHNUBWO_GET_FIRMWARE_MSGSIZE, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
*output = strdup_ascii_only((char *) buff + SINOWEALTHNUBWO_GET_FIRMWARE_MSGOFFSET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealthnubwo_probe(struct ratbag_device *device)
|
||||
{
|
||||
int error;
|
||||
struct ratbag_profile *profile;
|
||||
struct ratbag_resolution *resolution;
|
||||
struct ratbag_led *led;
|
||||
|
||||
error = ratbag_find_hidraw(device, sinowealthnubwo_test_hidraw);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
char *fwstr;
|
||||
error = sinowealth_get_firmware_string(device, &fwstr);
|
||||
if (error)
|
||||
return error;
|
||||
log_info(device->ratbag, "Firmware: %s\n", fwstr);
|
||||
free(fwstr);
|
||||
|
||||
ratbag_device_init_profiles(device,
|
||||
SINOWEALTHNUBWO_NUM_PROFILES,
|
||||
SINOWEALTHNUBWO_NUM_RESOLUTIONS,
|
||||
SINOWEALTHNUBWO_NUM_BUTTONS,
|
||||
SINOWEALTHNUBWO_NUM_LEDS);
|
||||
|
||||
ratbag_device_for_each_profile(device, profile) {
|
||||
profile->is_active = true;
|
||||
|
||||
ratbag_profile_set_cap(profile, RATBAG_PROFILE_CAP_WRITE_ONLY);
|
||||
ratbag_profile_set_report_rate_list(profile, REPORT_RATES, ARRAY_LENGTH(REPORT_RATES));
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
ratbag_resolution_set_dpi_list(resolution, DPILIST, ARRAY_LENGTH(DPILIST));
|
||||
resolution->dpi_x = resolution->dpi_y = DPILIST[ARRAY_LENGTH(DPILIST)-1];
|
||||
resolution->is_active = true;
|
||||
resolution->is_default = true;
|
||||
}
|
||||
ratbag_profile_for_each_led(profile, led) {
|
||||
led->mode = RATBAG_LED_OFF;
|
||||
led->color.red = led->color.green = led->color.blue = 0xFF;
|
||||
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
|
||||
led->type = RATBAG_LED_TYPE_SIDE;
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
|
||||
//Actually more
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
encode_dpi(unsigned int dpi)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_LENGTH(DPILIST); i++)
|
||||
if (DPILIST[i] == dpi)
|
||||
return DPI_ENCODED[i];
|
||||
return DPI_ENCODED[0];
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
encode_report_rate(unsigned int reportrate)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_LENGTH(REPORT_RATES); i++)
|
||||
if (REPORT_RATES[i] == reportrate)
|
||||
return REPORT_RATES_ENCODED[i];
|
||||
return REPORT_RATES_ENCODED[0];
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealthnubwo_set_dpi(struct ratbag_device *device, int dpi)
|
||||
{
|
||||
uint8_t buf[SINOWEALTHNUBWO_PERF_CMD_MSGSIZE] = {0};
|
||||
memcpy(buf, DPI_CMD, ARRAY_LENGTH(DPI_CMD));
|
||||
buf[ARRAY_LENGTH(DPI_CMD)] = encode_dpi(dpi);
|
||||
int error = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID, buf, SINOWEALTHNUBWO_PERF_CMD_MSGSIZE);
|
||||
if (error < 0)
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealthnubwo_set_report_rate(struct ratbag_device *device, int reportrate)
|
||||
{
|
||||
uint8_t buf[SINOWEALTHNUBWO_PERF_CMD_MSGSIZE] = {0};
|
||||
memcpy(buf, REPORT_RATES_CMD, ARRAY_LENGTH(REPORT_RATES_CMD));
|
||||
buf[ARRAY_LENGTH(REPORT_RATES_CMD)] = encode_report_rate(reportrate);
|
||||
int error = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_PERF_CMD_REPORTID, buf, SINOWEALTHNUBWO_PERF_CMD_MSGSIZE);
|
||||
if (error < 0)
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum sinowealthnubwo_color_mode encode_color(enum ratbag_led_mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case RATBAG_LED_OFF:
|
||||
return SINOWEALTHNUBWO_COLOR_OFF;
|
||||
case RATBAG_LED_ON:
|
||||
return SINOWEALTHNUBWO_COLOR_ON;
|
||||
case RATBAG_LED_CYCLE:
|
||||
return SINOWEALTHNUBWO_COLOR_MARQUEE;
|
||||
case RATBAG_LED_BREATHING:
|
||||
return SINOWEALTHNUBWO_COLOR_BREATHING;
|
||||
default:
|
||||
return SINOWEALTHNUBWO_COLOR_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t normalize_duration(int duration)
|
||||
{
|
||||
const int MAX_DURATION = 10000;
|
||||
const uint8_t avail_dura[] = {0x01, 0x03, 0x05};
|
||||
const int selected = (duration * ((int) ARRAY_LENGTH(avail_dura)) - 1) / MAX_DURATION;
|
||||
return avail_dura[selected];
|
||||
}
|
||||
|
||||
static uint8_t normalize_brightness(int brightness)
|
||||
{
|
||||
const int MAX_BRIGHTNESS = 255;
|
||||
return 1 + (brightness * 3 - 1) / MAX_BRIGHTNESS;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealthnubwo_set_aesthetic(struct ratbag_device *device, struct ratbag_led *led) {
|
||||
struct sinowealthnubwo_aesthetic_report report;
|
||||
memset(&report, 0, sizeof(struct sinowealthnubwo_aesthetic_report));
|
||||
report.report_id = SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID;
|
||||
memcpy(report.cmd, AESTHETIC_CMD, ARRAY_LENGTH(AESTHETIC_CMD));
|
||||
report.r = led->color.red;
|
||||
report.g = led->color.green;
|
||||
report.b = led->color.blue;
|
||||
report.color_mode = encode_color(led->mode);
|
||||
report.tempo = normalize_duration(led->ms);
|
||||
report.brightness = normalize_brightness(led->brightness);
|
||||
|
||||
int size = ratbag_hidraw_set_feature_report(device, SINOWEALTHNUBWO_AESTHETIC_CMD_REPORTID, (uint8_t *) &report, sizeof(report));
|
||||
if (size < 0)
|
||||
return size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealthnubwo_write_profile(struct ratbag_device *device, struct ratbag_profile *profile)
|
||||
{
|
||||
struct ratbag_resolution *resolution;
|
||||
struct ratbag_led *led;
|
||||
int error;
|
||||
|
||||
log_debug(device->ratbag, "Writing updates\n");
|
||||
|
||||
log_debug(device->ratbag, "Setting report rate\n");
|
||||
error = sinowealthnubwo_set_report_rate(device, profile->hz);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (!resolution->dirty)
|
||||
continue;
|
||||
log_debug(device->ratbag, "Setting DPI\n");
|
||||
error = sinowealthnubwo_set_dpi(device, resolution->dpi_x);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
ratbag_profile_for_each_led(profile, led) {
|
||||
if (!led->dirty)
|
||||
continue;
|
||||
log_debug(device->ratbag, "Setting aesthetic\n");
|
||||
error = sinowealthnubwo_set_aesthetic(device, led);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealthnubwo_commit(struct ratbag_device *device)
|
||||
{
|
||||
struct ratbag_profile *profile;
|
||||
list_for_each(profile, &device->profiles, link) {
|
||||
if (!profile->dirty) continue;
|
||||
|
||||
int error = sinowealthnubwo_write_profile(device, profile);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sinowealthnubwo_remove(struct ratbag_device *device)
|
||||
{
|
||||
ratbag_close_hidraw(device);
|
||||
}
|
||||
|
||||
struct ratbag_driver sinowealth_nubwo_driver = {
|
||||
.name = "Sinowealth Nubwo",
|
||||
.id = "sinowealth_nubwo",
|
||||
.probe = sinowealthnubwo_probe,
|
||||
.remove = sinowealthnubwo_remove,
|
||||
.commit = sinowealthnubwo_commit,
|
||||
};
|
||||
480
src/driver-sinowealth.c
Normal file
480
src/driver-sinowealth.c
Normal file
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* Copyright © 2020 Marian Beermann
|
||||
*
|
||||
* 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 "libratbag-private.h"
|
||||
#include "libratbag-hidraw.h"
|
||||
|
||||
#define SINOWEALTH_REPORT_ID_CONFIG 0x4
|
||||
#define SINOWEALTH_REPORT_ID_CMD 0x5
|
||||
#define SINOWEALTH_CMD_FIRMWARE_VERSION 0x1
|
||||
#define SINOWEALTH_CMD_GET_CONFIG 0x11
|
||||
#define SINOWEALTH_CONFIG_SIZE 520
|
||||
#define SINOWEALTH_CONFIG_SIZE_USED 131
|
||||
|
||||
#define SINOWEALTH_XY_INDEPENDENT 0x80
|
||||
|
||||
/* The PC software only goes down to 400, but the PMW3360 doesn't care */
|
||||
#define SINOWEALTH_DPI_MIN 100
|
||||
#define SINOWEALTH_DPI_MAX 12000
|
||||
#define SINOWEALTH_DPI_STEP 100
|
||||
|
||||
/* other models might have up to eight */
|
||||
#define SINOWEALTH_NUM_DPIS 6
|
||||
|
||||
#define SINOWEALTH_RGB_BRIGHTNESS_BITS 0xF0
|
||||
#define SINOWEALTH_RGB_SPEED_BITS 0x0F
|
||||
|
||||
struct sinowealth_rgb8 {
|
||||
uint8_t r, g, b;
|
||||
} __attribute__((packed));
|
||||
|
||||
_Static_assert(sizeof(struct sinowealth_rgb8) == 3, "Invalid size");
|
||||
|
||||
struct sinowealth_rbg8 {
|
||||
uint8_t r, b, g;
|
||||
} __attribute__((packed));
|
||||
|
||||
_Static_assert(sizeof(struct sinowealth_rbg8) == 3, "Invalid size");
|
||||
|
||||
enum rgb_effect {
|
||||
RGB_OFF = 0,
|
||||
RGB_GLORIOUS = 0x1, /* unicorn mode */
|
||||
RGB_SINGLE = 0x2, /* single constant color */
|
||||
RGB_BREATHING = 0x5, /* Full RGB breathing */
|
||||
RGB_BREATHING7 = 0x3, /* breathing with seven user-defined colors */
|
||||
RGB_BREATHING1 = 0xa, /* single color breathing */
|
||||
RGB_TAIL = 0x4,
|
||||
RGB_RAVE = 0x7,
|
||||
RGB_WAVE = 0x9
|
||||
};
|
||||
|
||||
struct sinowealth_config_report {
|
||||
uint8_t report_id; /* SINOWEALTH_REPORT_ID_CONFIG */
|
||||
uint8_t command_id;
|
||||
uint8_t unknown1;
|
||||
/* always 0 when config is read from device,
|
||||
* has to be 0x7b when writing config to device
|
||||
*/
|
||||
uint8_t config_write;
|
||||
uint8_t unknown2[6];
|
||||
/* 0x80 - SINOWEALTH_XY_INDEPENDENT */
|
||||
uint8_t config;
|
||||
uint8_t dpi_count:4;
|
||||
uint8_t active_dpi:4;
|
||||
/* bit set: disabled, unset: enabled
|
||||
* this structure has support for eight DPI slots,
|
||||
* but the glorious software only exposes six
|
||||
*/
|
||||
uint8_t dpi_enabled;
|
||||
/* DPI/CPI is encoded in the way the PMW3360 sensor accepts it
|
||||
* value = (DPI - 100) / 100
|
||||
* If XY are identical, dpi[0-6] contain the sensitivities,
|
||||
* while in XY independent mode each entry takes two chars for X and Y.
|
||||
*/
|
||||
uint8_t dpi[16];
|
||||
struct sinowealth_rgb8 dpi_color[8];
|
||||
uint8_t rgb_effect; /* see enum rgb_effect */
|
||||
/* 0x40 - brightness (constant)
|
||||
* 0x1/2/3 - speed
|
||||
*/
|
||||
uint8_t glorious_mode;
|
||||
uint8_t glorious_direction;
|
||||
uint8_t single_mode;
|
||||
struct sinowealth_rbg8 single_color;
|
||||
/* 0x40 - brightness (constant)
|
||||
* 0x1/2/3 - speed
|
||||
*/
|
||||
uint8_t breathing7_mode;
|
||||
uint8_t breathing7_colorcount; /* 7, constant */
|
||||
struct sinowealth_rbg8 breathing7_colors[7];
|
||||
/* 0x10/20/30/40 - brightness
|
||||
* 0x1/2/3 - speed
|
||||
*/
|
||||
uint8_t tail_mode;
|
||||
uint8_t unknown3[33];
|
||||
/* 0x10/20/30/40 - brightness
|
||||
* 0x1/2/3 - speed
|
||||
*/
|
||||
uint8_t rave_mode;
|
||||
struct sinowealth_rbg8 rave_colors[2];
|
||||
/* 0x10/20/30/40 - brightness
|
||||
* 0x1/2/3 - speed
|
||||
*/
|
||||
uint8_t wave_mode;
|
||||
/* 0x1/2/3 - speed */
|
||||
uint8_t breathing1_mode;
|
||||
struct sinowealth_rbg8 breathing1_color;
|
||||
uint8_t unknown4;
|
||||
/* 0x1 - 2 mm
|
||||
* 0x2 - 3 mm
|
||||
*/
|
||||
uint8_t lift_off_distance;
|
||||
uint8_t padding[SINOWEALTH_CONFIG_SIZE - SINOWEALTH_CONFIG_SIZE_USED];
|
||||
} __attribute__((packed));
|
||||
|
||||
_Static_assert(sizeof(struct sinowealth_config_report) == SINOWEALTH_CONFIG_SIZE, "Invalid size");
|
||||
|
||||
struct sinowealth_data {
|
||||
/* this is kinda unnecessary at this time, but all the other drivers do it too ;) */
|
||||
struct sinowealth_config_report config;
|
||||
};
|
||||
|
||||
static int
|
||||
sinowealth_raw_to_dpi(int raw)
|
||||
{
|
||||
return (raw + 1) * 100;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealth_dpi_to_raw(int dpi)
|
||||
{
|
||||
assert(dpi >= SINOWEALTH_DPI_MIN && dpi <= SINOWEALTH_DPI_MAX);
|
||||
return dpi / 100 - 1;
|
||||
}
|
||||
|
||||
static struct ratbag_color
|
||||
sinowealth_raw_to_color(struct sinowealth_rgb8 raw)
|
||||
{
|
||||
return (struct ratbag_color) {.red = raw.r, .green = raw.g, .blue = raw.b};
|
||||
}
|
||||
|
||||
static struct sinowealth_rgb8
|
||||
sinowealth_color_to_raw(struct ratbag_color color)
|
||||
{
|
||||
return (struct sinowealth_rgb8) {.r = color.red, .g = color.green, .b = color.blue};
|
||||
}
|
||||
|
||||
static struct ratbag_color
|
||||
sinowealth_rbg_to_color(struct sinowealth_rbg8 raw)
|
||||
{
|
||||
return (struct ratbag_color) {.red = raw.r, .green = raw.g, .blue = raw.b};
|
||||
}
|
||||
|
||||
static struct sinowealth_rbg8
|
||||
sinowealth_color_to_rbg(struct ratbag_color color)
|
||||
{
|
||||
return (struct sinowealth_rbg8) {.r = color.red, .g = color.green, .b = color.blue};
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealth_rgb_mode_to_brightness(uint8_t mode)
|
||||
{
|
||||
/* brightness is 0-4 in the upper nibble of mode,
|
||||
* while libratbag uses 0-255.
|
||||
*/
|
||||
return min((mode >> 4) * 64, 255);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
sinowealth_brightness_to_rgb_mode(int brightness)
|
||||
{
|
||||
/* convert 0-255 to 0-4 in the upper nibble of mode */
|
||||
return ((brightness + 1) / 64) << 4;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealth_rgb_mode_to_duration(uint8_t mode)
|
||||
{
|
||||
switch (mode & SINOWEALTH_RGB_SPEED_BITS) {
|
||||
case 0: return 10000; /* static: does not translate to duration */
|
||||
case 1: return 1500;
|
||||
case 2: return 1000;
|
||||
case 3: return 500;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
sinowealth_duration_to_rgb_mode(int duration_ms)
|
||||
{
|
||||
uint8_t mode = 0;
|
||||
if (duration_ms <= 500) {
|
||||
mode |= 3;
|
||||
} else if (duration_ms <= 1000) {
|
||||
mode |= 2;
|
||||
} else {
|
||||
mode |= 1;
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
static void
|
||||
sinowealth_set_led_from_rgb_mode(struct ratbag_led *led, uint8_t mode)
|
||||
{
|
||||
led->brightness = sinowealth_rgb_mode_to_brightness(mode);
|
||||
led->ms = sinowealth_rgb_mode_to_duration(mode);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
sinowealth_led_to_rgb_mode(const struct ratbag_led *led)
|
||||
{
|
||||
uint8_t mode;
|
||||
mode = sinowealth_brightness_to_rgb_mode(led->brightness);
|
||||
mode |= sinowealth_duration_to_rgb_mode(led->ms);
|
||||
return mode;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealth_read_profile(struct ratbag_profile *profile)
|
||||
{
|
||||
struct ratbag_device *device = profile->device;
|
||||
struct sinowealth_data *drv_data = device->drv_data;
|
||||
struct sinowealth_config_report *config = &drv_data->config;
|
||||
struct ratbag_resolution *resolution;
|
||||
struct ratbag_led *led;
|
||||
unsigned int hz = 1000; /* TODO */
|
||||
int rc;
|
||||
|
||||
uint8_t cmd[6] = {SINOWEALTH_REPORT_ID_CMD, SINOWEALTH_CMD_GET_CONFIG};
|
||||
rc = ratbag_hidraw_set_feature_report(device, SINOWEALTH_REPORT_ID_CMD, cmd, sizeof(cmd));
|
||||
if (rc != sizeof(cmd)) {
|
||||
log_error(device->ratbag, "Error while sending read config command: %d\n", rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ratbag_hidraw_get_feature_report(device, SINOWEALTH_REPORT_ID_CONFIG,
|
||||
(uint8_t*) config, SINOWEALTH_CONFIG_SIZE);
|
||||
/* The GET_FEATURE report length has to be 520, but the actual data returned is less */
|
||||
if (rc != SINOWEALTH_CONFIG_SIZE_USED) {
|
||||
log_error(device->ratbag, "Could not read device configuration: %d\n", rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TODO */
|
||||
ratbag_profile_set_report_rate_list(profile, &hz, 1);
|
||||
ratbag_profile_set_report_rate(profile, hz);
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (config->config & SINOWEALTH_XY_INDEPENDENT) {
|
||||
resolution->dpi_x = sinowealth_raw_to_dpi(config->dpi[resolution->index * 2]);
|
||||
resolution->dpi_y = sinowealth_raw_to_dpi(config->dpi[resolution->index * 2 + 1]);
|
||||
} else {
|
||||
resolution->dpi_x = sinowealth_raw_to_dpi(config->dpi[resolution->index]);
|
||||
resolution->dpi_y = resolution->dpi_x;
|
||||
}
|
||||
if (config->dpi_enabled & (1<<resolution->index)) {
|
||||
/* DPI step is disabled, fake it by setting DPI to 0 */
|
||||
resolution->dpi_x = 0;
|
||||
resolution->dpi_y = 0;
|
||||
}
|
||||
resolution->is_active = resolution->index == config->active_dpi - 1;
|
||||
resolution->is_default = resolution->is_active;
|
||||
}
|
||||
|
||||
/* Body lighting */
|
||||
led = ratbag_profile_get_led(profile, 0);
|
||||
switch (config->rgb_effect) {
|
||||
case RGB_OFF:
|
||||
led->mode = RATBAG_LED_OFF;
|
||||
break;
|
||||
case RGB_SINGLE:
|
||||
led->mode = RATBAG_LED_ON;
|
||||
led->color = sinowealth_rbg_to_color(config->single_color);
|
||||
led->brightness = sinowealth_rgb_mode_to_brightness(config->single_mode);
|
||||
break;
|
||||
case RGB_GLORIOUS:
|
||||
case RGB_BREATHING:
|
||||
case RGB_BREATHING7:
|
||||
case RGB_TAIL:
|
||||
case RGB_RAVE:
|
||||
case RGB_WAVE:
|
||||
led->mode = RATBAG_LED_CYCLE;
|
||||
sinowealth_set_led_from_rgb_mode(led, config->glorious_mode);
|
||||
break;
|
||||
case RGB_BREATHING1:
|
||||
led->mode = RATBAG_LED_BREATHING;
|
||||
led->color = sinowealth_rbg_to_color(config->breathing1_color);
|
||||
sinowealth_set_led_from_rgb_mode(led, config->breathing1_mode);
|
||||
break;
|
||||
}
|
||||
ratbag_led_unref(led);
|
||||
|
||||
profile->is_active = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sinowealth_init_profile(struct ratbag_device *device)
|
||||
{
|
||||
struct ratbag_profile *profile;
|
||||
struct ratbag_resolution *resolution;
|
||||
struct ratbag_led *led;
|
||||
/* number of DPIs = all DPIs from min to max (inclusive) and "0 DPI" as a special value
|
||||
* to signal a disabled DPI step.
|
||||
*/
|
||||
int num_dpis = (SINOWEALTH_DPI_MAX - SINOWEALTH_DPI_MIN) / SINOWEALTH_DPI_STEP + 2;
|
||||
unsigned int dpis[num_dpis];
|
||||
|
||||
/* TODO: Button remapping */
|
||||
ratbag_device_init_profiles(device, 1, SINOWEALTH_NUM_DPIS, 0, 1);
|
||||
|
||||
profile = ratbag_device_get_profile(device, 0);
|
||||
|
||||
/* Generate DPI list */
|
||||
dpis[0] = 0; /* 0 DPI = disabled */
|
||||
for (int i = 1; i < num_dpis; i++) {
|
||||
dpis[i] = SINOWEALTH_DPI_MIN + (i - 1) * SINOWEALTH_DPI_STEP;
|
||||
}
|
||||
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
ratbag_resolution_set_dpi_list(resolution, dpis, num_dpis);
|
||||
ratbag_resolution_set_cap(resolution, RATBAG_RESOLUTION_CAP_SEPARATE_XY_RESOLUTION);
|
||||
}
|
||||
|
||||
/* Set up LED capabilities */
|
||||
led = ratbag_profile_get_led(profile, 0);
|
||||
led->type = RATBAG_LED_TYPE_SIDE;
|
||||
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_OFF);
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_ON);
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_CYCLE);
|
||||
ratbag_led_set_mode_capability(led, RATBAG_LED_BREATHING);
|
||||
ratbag_led_unref(led);
|
||||
|
||||
ratbag_profile_unref(profile);
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealth_test_hidraw(struct ratbag_device *device)
|
||||
{
|
||||
/* Only the keyboard interface has this report */
|
||||
return ratbag_hidraw_has_report(device, SINOWEALTH_REPORT_ID_CONFIG);
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealth_probe(struct ratbag_device *device)
|
||||
{
|
||||
int rc;
|
||||
struct sinowealth_data *drv_data = 0;
|
||||
struct ratbag_profile *profile = 0;
|
||||
|
||||
rc = ratbag_find_hidraw(device, sinowealth_test_hidraw);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
drv_data = zalloc(sizeof(*drv_data));
|
||||
ratbag_set_drv_data(device, drv_data);
|
||||
|
||||
sinowealth_init_profile(device);
|
||||
|
||||
profile = ratbag_device_get_profile(device, 0);
|
||||
rc = sinowealth_read_profile(profile);
|
||||
if (rc) {
|
||||
rc = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
free(drv_data);
|
||||
ratbag_set_drv_data(device, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
sinowealth_commit(struct ratbag_device *device)
|
||||
{
|
||||
struct ratbag_profile *profile = ratbag_device_get_profile(device, 0);
|
||||
struct sinowealth_data *drv_data = device->drv_data;
|
||||
struct sinowealth_config_report *config = &drv_data->config;
|
||||
struct ratbag_resolution *resolution;
|
||||
struct ratbag_led *led;
|
||||
int rc;
|
||||
uint8_t dpi_enabled = 0;
|
||||
|
||||
/* Check if any resolution requires independent XY DPIs */
|
||||
config->config &= ~SINOWEALTH_XY_INDEPENDENT;
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (resolution->dpi_x != resolution->dpi_y && resolution->dpi_x && resolution->dpi_y) {
|
||||
config->config |= SINOWEALTH_XY_INDEPENDENT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
config->dpi_count = 0;
|
||||
ratbag_profile_for_each_resolution(profile, resolution) {
|
||||
if (!resolution->dpi_x || !resolution->dpi_y)
|
||||
continue;
|
||||
if (config->config & SINOWEALTH_XY_INDEPENDENT) {
|
||||
config->dpi[resolution->index * 2] = sinowealth_dpi_to_raw(resolution->dpi_x);
|
||||
config->dpi[resolution->index * 2 + 1] = sinowealth_dpi_to_raw(resolution->dpi_y);
|
||||
} else {
|
||||
config->dpi[resolution->index] = sinowealth_dpi_to_raw(resolution->dpi_x);
|
||||
}
|
||||
dpi_enabled |= 1<<resolution->index;
|
||||
config->dpi_count++;
|
||||
}
|
||||
config->dpi_enabled = ~dpi_enabled; /* config->dpi_enabled is inverted */
|
||||
|
||||
/* Body lighting */
|
||||
led = ratbag_profile_get_led(profile, 0);
|
||||
switch(led->mode) {
|
||||
case RATBAG_LED_OFF:
|
||||
config->rgb_effect = RGB_OFF;
|
||||
break;
|
||||
case RATBAG_LED_ON:
|
||||
config->rgb_effect = RGB_SINGLE;
|
||||
config->single_color = sinowealth_color_to_rbg(led->color);
|
||||
break;
|
||||
case RATBAG_LED_CYCLE:
|
||||
config->rgb_effect = RGB_GLORIOUS;
|
||||
config->glorious_mode = sinowealth_led_to_rgb_mode(led);
|
||||
break;
|
||||
case RATBAG_LED_BREATHING:
|
||||
config->rgb_effect = RGB_BREATHING1;
|
||||
config->breathing1_color = sinowealth_color_to_rbg(led->color);
|
||||
config->breathing1_mode = sinowealth_led_to_rgb_mode(led);
|
||||
break;
|
||||
}
|
||||
ratbag_led_unref(led);
|
||||
|
||||
config->config_write = 0x7b; /* magic */
|
||||
|
||||
rc = ratbag_hidraw_set_feature_report(device, SINOWEALTH_REPORT_ID_CONFIG,
|
||||
(uint8_t*) config, SINOWEALTH_CONFIG_SIZE);
|
||||
if (rc != SINOWEALTH_CONFIG_SIZE) {
|
||||
log_error(device->ratbag, "Error while writing config: %d\n", rc);
|
||||
ratbag_profile_unref(profile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ratbag_profile_unref(profile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sinowealth_remove(struct ratbag_device *device)
|
||||
{
|
||||
ratbag_close_hidraw(device);
|
||||
free(ratbag_get_drv_data(device));
|
||||
}
|
||||
|
||||
struct ratbag_driver sinowealth_driver = {
|
||||
.name = "Sinowealth",
|
||||
.id = "sinowealth",
|
||||
.probe = sinowealth_probe,
|
||||
.remove = sinowealth_remove,
|
||||
.commit = sinowealth_commit
|
||||
};
|
||||
@@ -78,11 +78,6 @@
|
||||
#define STEELSERIES_BUTTON_KBD 0x51
|
||||
#define STEELSERIES_BUTTON_CONSUMER 0x61
|
||||
|
||||
struct steelseries_data {
|
||||
int firmware_major;
|
||||
int firmware_minor;
|
||||
};
|
||||
|
||||
struct steelseries_point {
|
||||
struct list link;
|
||||
|
||||
@@ -177,7 +172,6 @@ button_defaults_for_layout(struct ratbag_button *button, int button_count)
|
||||
static int
|
||||
steelseries_get_firmware_version(struct ratbag_device *device)
|
||||
{
|
||||
struct steelseries_data *drv_data = device->drv_data;
|
||||
int device_version = ratbag_device_data_steelseries_get_device_version(device->data);
|
||||
size_t buf_len = STEELSERIES_REPORT_SIZE;
|
||||
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
|
||||
@@ -199,8 +193,8 @@ steelseries_get_firmware_version(struct ratbag_device *device)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drv_data->firmware_major = buf[1];
|
||||
drv_data->firmware_minor = buf[0];
|
||||
device->firmware_major = buf[1];
|
||||
device->firmware_minor = buf[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -265,7 +259,6 @@ steelseries_read_settings(struct ratbag_device *device)
|
||||
static int
|
||||
steelseries_probe(struct ratbag_device *device)
|
||||
{
|
||||
struct steelseries_data *drv_data = NULL;
|
||||
struct ratbag_profile *profile = NULL;
|
||||
struct ratbag_resolution *resolution;
|
||||
struct ratbag_button *button;
|
||||
@@ -363,14 +356,11 @@ steelseries_probe(struct ratbag_device *device)
|
||||
}
|
||||
}
|
||||
|
||||
drv_data = zalloc(sizeof(*drv_data));
|
||||
ratbag_set_drv_data(device, drv_data);
|
||||
|
||||
rc = steelseries_get_firmware_version(device);
|
||||
if(rc == 0)
|
||||
log_debug(device->ratbag, "SteelSeries firmware version %d.%d\n",
|
||||
drv_data->firmware_major,
|
||||
drv_data->firmware_minor);
|
||||
device->firmware_major,
|
||||
device->firmware_minor);
|
||||
|
||||
steelseries_read_settings(device);
|
||||
|
||||
@@ -960,7 +950,6 @@ steelseries_remove(struct ratbag_device *device)
|
||||
{
|
||||
ratbag_close_hidraw_index(device, 0);
|
||||
ratbag_close_hidraw_index(device, 1);
|
||||
free(ratbag_get_drv_data(device));
|
||||
}
|
||||
|
||||
struct ratbag_driver steelseries_driver = {
|
||||
|
||||
@@ -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;
|
||||
unsigned supported_report_types;
|
||||
};
|
||||
|
||||
#define HIDPP_REPORT_SHORT (1 << 0)
|
||||
#define HIDPP_REPORT_LONG (1 << 1)
|
||||
|
||||
void
|
||||
hidpp_device_init(struct hidpp_device *dev, int fd);
|
||||
void
|
||||
@@ -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,
|
||||
|
||||
@@ -807,8 +807,12 @@ hidpp10_get_current_profile(struct hidpp10_device *dev, uint8_t *current_profile
|
||||
__CMD_PROFILE);
|
||||
|
||||
res = hidpp10_request_command(dev, &profile);
|
||||
if (res)
|
||||
return res;
|
||||
if (res) {
|
||||
/* Profiles not supported */
|
||||
hidpp_log_debug(&dev->base, "Profiles not supported\n");
|
||||
*current_profile = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
type = profile.msg.parameters[0];
|
||||
page = profile.msg.parameters[1];
|
||||
@@ -1144,6 +1148,9 @@ hidpp10_onboard_profiles_read_macro(struct hidpp10_device *device,
|
||||
}
|
||||
|
||||
if (rc == -ENOMEM) {
|
||||
if ((unsigned) offset + mem_index > 0xff)
|
||||
goto out_err;
|
||||
|
||||
offset += mem_index;
|
||||
if (offset & 0x01)
|
||||
offset--;
|
||||
@@ -1198,8 +1205,10 @@ hidpp10_onboard_profiles_parse_macro(struct hidpp10_device *device,
|
||||
hidpp_log_raw(&device->base, "*** macro starts at (0x%02x, 0x%04x) ***\n", page, offset);
|
||||
|
||||
rc = hidpp10_onboard_profiles_read_macro(device, page, offset, ¯o);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
hidpp_log_raw(&device->base, "hidpp10: failed to read macro\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
count = rc;
|
||||
|
||||
@@ -2754,12 +2763,14 @@ hidpp10_get_device_info(struct hidpp10_device *dev)
|
||||
return hidpp10_get_current_profile(dev, ¤t_profile);
|
||||
}
|
||||
|
||||
struct hidpp10_device*
|
||||
int
|
||||
hidpp10_device_new(const struct hidpp_device *base,
|
||||
int idx,
|
||||
enum hidpp10_profile_type type,
|
||||
unsigned int profile_count)
|
||||
unsigned int profile_count,
|
||||
struct hidpp10_device **out)
|
||||
{
|
||||
int rc;
|
||||
struct hidpp10_device *dev;
|
||||
|
||||
dev = zalloc(sizeof(*dev));
|
||||
@@ -2770,12 +2781,14 @@ hidpp10_device_new(const struct hidpp_device *base,
|
||||
dev->profile_count = profile_count;
|
||||
dev->profiles = zalloc(dev->profile_count * sizeof(struct hidpp10_profile));
|
||||
|
||||
if (hidpp10_get_device_info(dev) != 0) {
|
||||
if ((rc = hidpp10_get_device_info(dev)) != 0) {
|
||||
hidpp10_device_destroy(dev);
|
||||
dev = NULL;
|
||||
}
|
||||
|
||||
return dev;
|
||||
*out = dev;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -69,11 +69,12 @@ struct hidpp10_device {
|
||||
unsigned int profile_count;
|
||||
};
|
||||
|
||||
struct hidpp10_device*
|
||||
int
|
||||
hidpp10_device_new(const struct hidpp_device *base,
|
||||
int idx,
|
||||
enum hidpp10_profile_type type,
|
||||
unsigned int profile_count);
|
||||
unsigned int profile_count,
|
||||
struct hidpp10_device **out);
|
||||
|
||||
int
|
||||
hidpp10_device_read_profiles(struct hidpp10_device *dev);
|
||||
|
||||
249
src/hidpp20.c
249
src/hidpp20.c
@@ -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 */
|
||||
@@ -250,7 +260,7 @@ hidpp_root_get_feature(struct hidpp20_device *device,
|
||||
{
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.sub_id = HIDPP_PAGE_ROOT_IDX,
|
||||
.msg.address = CMD_ROOT_GET_FEATURE,
|
||||
@@ -277,7 +287,7 @@ hidpp20_root_get_protocol_version(struct hidpp20_device *device,
|
||||
{
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.sub_id = HIDPP_PAGE_ROOT_IDX,
|
||||
.msg.address = CMD_ROOT_GET_PROTOCOL_VERSION,
|
||||
@@ -311,7 +321,7 @@ hidpp20_feature_set_get_count(struct hidpp20_device *device, uint8_t reg)
|
||||
{
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.sub_id = reg,
|
||||
.msg.address = CMD_FEATURE_SET_GET_COUNT,
|
||||
@@ -333,7 +343,7 @@ hidpp20_feature_set_get_feature_id(struct hidpp20_device *device,
|
||||
{
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.sub_id = reg,
|
||||
.msg.address = CMD_FEATURE_SET_GET_FEATURE_ID,
|
||||
@@ -419,7 +429,7 @@ hidpp20_batterylevel_get_battery_level(struct hidpp20_device *device,
|
||||
{
|
||||
uint8_t feature_index;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.address = CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS,
|
||||
};
|
||||
@@ -556,7 +566,7 @@ int hidpp20_led_sw_control_get_led_count(struct hidpp20_device* device)
|
||||
{
|
||||
union hidpp20_message msg = {
|
||||
.msg = {
|
||||
.report_id = REPORT_ID_LONG,
|
||||
.report_id = REPORT_ID_SHORT,
|
||||
.address = CMD_LED_SW_CONTROL_GET_LED_COUNT,
|
||||
.device_idx = device->index,
|
||||
},
|
||||
@@ -584,7 +594,7 @@ int hidpp20_led_sw_control_get_led_info(struct hidpp20_device* device,
|
||||
{
|
||||
union hidpp20_message msg = {
|
||||
.msg = {
|
||||
.report_id = REPORT_ID_LONG,
|
||||
.report_id = REPORT_ID_SHORT,
|
||||
.address = CMD_LED_SW_CONTROL_GET_LED_INFO,
|
||||
.device_idx = device->index,
|
||||
},
|
||||
@@ -672,7 +682,7 @@ int hidpp20_led_sw_control_get_led_state(struct hidpp20_device* device,
|
||||
uint8_t feature_idx;
|
||||
union hidpp20_message msg = {
|
||||
.msg = {
|
||||
.report_id = REPORT_ID_LONG,
|
||||
.report_id = REPORT_ID_SHORT,
|
||||
.address = CMD_LED_SW_CONTROL_GET_LED_STATE,
|
||||
.device_idx = device->index,
|
||||
}
|
||||
@@ -757,7 +767,7 @@ static int
|
||||
hidpp20_kbd_reprogrammable_keys_get_count(struct hidpp20_device *device, uint8_t reg)
|
||||
{
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.sub_id = reg,
|
||||
.msg.address = CMD_KBD_REPROGRAMMABLE_KEYS_GET_COUNT,
|
||||
@@ -778,7 +788,7 @@ hidpp20_kbd_reprogrammable_keys_get_info(struct hidpp20_device *device,
|
||||
{
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.sub_id = reg,
|
||||
.msg.address = CMD_KBD_REPROGRAMMABLE_KEYS_GET_CTRL_ID_INFO,
|
||||
@@ -858,6 +868,8 @@ err:
|
||||
#define CMD_COLOR_LED_EFFECTS_GET_INFO 0x00
|
||||
#define CMD_COLOR_LED_EFFECTS_GET_ZONE_INFO 0x10
|
||||
#define CMD_COLOR_LED_EFFECTS_GET_ZONE_EFFECT_INFO 0x20
|
||||
#define CMD_COLOR_LED_EFFECTS_SET_ZONE_EFFECT 0x30
|
||||
#define CMD_COLOR_LED_EFFECTS_GET_ZONE_EFFECT 0xe0
|
||||
|
||||
int
|
||||
hidpp20_color_led_effects_get_info(struct hidpp20_device *device,
|
||||
@@ -883,6 +895,7 @@ hidpp20_color_led_effects_get_info(struct hidpp20_device *device,
|
||||
return rc;
|
||||
|
||||
*info = *(struct hidpp20_color_led_info *)msg.msg.parameters;
|
||||
device->led_ext_caps = info->ext_caps;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -895,7 +908,7 @@ hidpp20_color_led_effects_get_zone_info(struct hidpp20_device *device,
|
||||
int rc;
|
||||
struct hidpp20_color_led_zone_info *msg_info;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.sub_id = reg,
|
||||
.msg.address = CMD_COLOR_LED_EFFECTS_GET_ZONE_INFO,
|
||||
@@ -1002,6 +1015,80 @@ hidpp20_color_led_effect_get_zone_effect_info(struct hidpp20_device *device,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
hidpp20_color_led_effects_set_zone_effect(struct hidpp20_device *device,
|
||||
uint8_t zone_index,
|
||||
struct hidpp20_led led)
|
||||
{
|
||||
uint8_t feature_index;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.address = CMD_COLOR_LED_EFFECTS_SET_ZONE_EFFECT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.parameters[0] = zone_index,
|
||||
.msg.parameters[12] = 1, /* write to RAM and flash */
|
||||
};
|
||||
int rc;
|
||||
struct hidpp20_internal_led *internal_led = (struct hidpp20_internal_led*) &msg.msg.parameters[1];
|
||||
|
||||
hidpp20_onboard_profiles_write_led(internal_led, &led);
|
||||
|
||||
feature_index = hidpp_root_get_feature_idx(device,
|
||||
HIDPP_PAGE_COLOR_LED_EFFECTS);
|
||||
if (feature_index == 0)
|
||||
return -ENOTSUP;
|
||||
|
||||
msg.msg.sub_id = feature_index;
|
||||
|
||||
rc = hidpp20_request_command(device, &msg);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
hidpp20_color_led_effects_get_zone_effect(struct hidpp20_device *device,
|
||||
uint8_t zone_index,
|
||||
struct hidpp20_led *led)
|
||||
{
|
||||
uint8_t feature_index;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.address = CMD_COLOR_LED_EFFECTS_GET_ZONE_EFFECT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.parameters[0] = zone_index,
|
||||
};
|
||||
struct hidpp20_internal_led *internal_led;
|
||||
int rc;
|
||||
|
||||
/* hidpp20_color_led_effects_get_info() must be called first to set the capabilities */
|
||||
if (!(device->led_ext_caps & HIDPP20_COLOR_LED_INFO_EXT_CAP_HAS_ZONE_EFFECT))
|
||||
return -ENOTSUP;
|
||||
|
||||
feature_index = hidpp_root_get_feature_idx(device,
|
||||
HIDPP_PAGE_COLOR_LED_EFFECTS);
|
||||
if (feature_index == 0)
|
||||
return -ENOTSUP;
|
||||
|
||||
msg.msg.sub_id = feature_index;
|
||||
|
||||
rc = hidpp20_request_command(device, &msg);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (msg.msg.parameters[0] != zone_index)
|
||||
return -EPROTO;
|
||||
|
||||
internal_led = (struct hidpp20_internal_led*) &msg.msg.parameters[1];
|
||||
|
||||
hidpp20_onboard_profiles_read_led(led, *internal_led);
|
||||
|
||||
hidpp_log_debug(&device->base, "zone %u has effect %u\n", zone_index, led->mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x8071: RGB Effects */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@@ -1022,7 +1109,7 @@ hidpp20_rgb_effects_get_device_info(struct hidpp20_device *device,
|
||||
{
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
|
||||
.msg.parameters[0] = HIDPP20_RGB_EFFECTS_INDEX_ALL,
|
||||
@@ -1059,7 +1146,7 @@ hidpp20_rgb_effects_get_cluster_info(struct hidpp20_device *device,
|
||||
{
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
|
||||
.msg.parameters[0] = cluster_index,
|
||||
@@ -1142,7 +1229,7 @@ hidpp20_rgb_effects_get_effect_info(struct hidpp20_device *device,
|
||||
{
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.address = CMD_RGB_EFFECTS_GET_INFO,
|
||||
.msg.parameters[0] = cluster_index,
|
||||
@@ -1186,7 +1273,7 @@ hidpp20_special_keys_buttons_get_count(struct hidpp20_device *device, uint8_t re
|
||||
{
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.sub_id = reg,
|
||||
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_COUNT,
|
||||
@@ -1206,7 +1293,7 @@ hidpp20_special_keys_buttons_get_info(struct hidpp20_device *device,
|
||||
{
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.sub_id = reg,
|
||||
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_INFO,
|
||||
@@ -1236,7 +1323,7 @@ hidpp20_special_keys_buttons_get_reporting(struct hidpp20_device *device,
|
||||
{
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.sub_id = reg,
|
||||
.msg.address = CMD_SPECIAL_KEYS_BUTTONS_GET_REPORTING,
|
||||
@@ -1377,7 +1464,7 @@ hidpp20_mousepointer_get_mousepointer_info(struct hidpp20_device *device,
|
||||
{
|
||||
uint8_t feature_index;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.address = CMD_MOUSE_POINTER_BASIC_GET_INFO,
|
||||
};
|
||||
@@ -1414,7 +1501,7 @@ hidpp20_adjustable_dpi_get_count(struct hidpp20_device *device, uint8_t reg)
|
||||
{
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.sub_id = reg,
|
||||
.msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_COUNT,
|
||||
@@ -1435,7 +1522,7 @@ hidpp20_adjustable_dpi_get_dpi_list(struct hidpp20_device *device,
|
||||
int rc;
|
||||
unsigned i = 1, dpi_index = 0;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.sub_id = reg,
|
||||
.msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI_LIST,
|
||||
@@ -1483,7 +1570,7 @@ hidpp20_adjustable_dpi_get_dpi(struct hidpp20_device *device,
|
||||
{
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.sub_id = reg,
|
||||
.msg.address = CMD_ADJUSTABLE_DPI_GET_SENSOR_DPI,
|
||||
@@ -1566,7 +1653,7 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
|
||||
uint16_t returned_parameters;
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.address = CMD_ADJUSTABLE_DPI_SET_SENSOR_DPI,
|
||||
.msg.parameters[0] = sensor->index,
|
||||
@@ -1602,6 +1689,8 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define CMD_ADJUSTABLE_REPORT_RATE_GET_REPORT_RATE_LIST 0x00
|
||||
#define CMD_ADJUSTABLE_REPORT_RATE_GET_REPORT_RATE 0x10
|
||||
#define CMD_ADJUSTABLE_REPORT_RATE_SET_REPORT_RATE 0x20
|
||||
|
||||
int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *device,
|
||||
uint8_t *bitflags_ms)
|
||||
@@ -1609,10 +1698,9 @@ int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *d
|
||||
uint8_t feature_index;
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_LONG,
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.address = CMD_ADJUSTABLE_REPORT_RATE_GET_REPORT_RATE_LIST,
|
||||
.msg.parameters[0] = 0,
|
||||
};
|
||||
|
||||
feature_index = hidpp_root_get_feature_idx(device,
|
||||
@@ -1631,6 +1719,60 @@ int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *d
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hidpp20_adjustable_report_rate_get_report_rate(struct hidpp20_device *device,
|
||||
uint8_t *rate_ms)
|
||||
{
|
||||
uint8_t feature_index;
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.address = CMD_ADJUSTABLE_REPORT_RATE_GET_REPORT_RATE,
|
||||
.msg.parameters[0] = 0,
|
||||
};
|
||||
|
||||
feature_index = hidpp_root_get_feature_idx(device,
|
||||
HIDPP_PAGE_ADJUSTABLE_REPORT_RATE);
|
||||
if (feature_index == 0)
|
||||
return -ENOTSUP;
|
||||
|
||||
msg.msg.sub_id = feature_index;
|
||||
|
||||
rc = hidpp20_request_command(device, &msg);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*rate_ms = msg.msg.parameters[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hidpp20_adjustable_report_rate_set_report_rate(struct hidpp20_device *device,
|
||||
uint8_t rate_ms)
|
||||
{
|
||||
uint8_t feature_index;
|
||||
int rc;
|
||||
union hidpp20_message msg = {
|
||||
.msg.report_id = REPORT_ID_SHORT,
|
||||
.msg.device_idx = device->index,
|
||||
.msg.address = CMD_ADJUSTABLE_REPORT_RATE_SET_REPORT_RATE,
|
||||
.msg.parameters[0] = rate_ms,
|
||||
};
|
||||
|
||||
feature_index = hidpp_root_get_feature_idx(device,
|
||||
HIDPP_PAGE_ADJUSTABLE_REPORT_RATE);
|
||||
if (feature_index == 0)
|
||||
return -ENOTSUP;
|
||||
|
||||
msg.msg.sub_id = feature_index;
|
||||
|
||||
rc = hidpp20_request_command(device, &msg);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x8100 - Onboard Profiles */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@@ -2378,7 +2520,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,
|
||||
@@ -2389,6 +2531,9 @@ hidpp20_onboard_profiles_write_dict(struct hidpp20_device *device,
|
||||
sector_size,
|
||||
data,
|
||||
true);
|
||||
if (rc)
|
||||
hidpp_log_error(&device->base, "failed to write profile dictionary\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -2426,6 +2571,7 @@ hidpp20_buttons_to_cpu(struct hidpp20_device *device,
|
||||
break;
|
||||
case HIDPP20_BUTTON_SPECIAL:
|
||||
button->special.special = b->special.special;
|
||||
button->special.profile = b->special.profile;
|
||||
break;
|
||||
case HIDPP20_BUTTON_MACRO:
|
||||
if (profile->macros[i]) {
|
||||
@@ -2446,6 +2592,7 @@ hidpp20_buttons_to_cpu(struct hidpp20_device *device,
|
||||
case HIDPP20_BUTTON_DISABLED:
|
||||
break;
|
||||
default:
|
||||
memcpy(b, button, sizeof(*b));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2485,6 +2632,7 @@ hidpp20_buttons_from_cpu(struct hidpp20_profile *profile,
|
||||
break;
|
||||
case HIDPP20_BUTTON_SPECIAL:
|
||||
button->special.special = b->special.special;
|
||||
button->special.profile = b->special.profile;
|
||||
break;
|
||||
case HIDPP20_BUTTON_DISABLED:
|
||||
break;
|
||||
@@ -2495,12 +2643,13 @@ hidpp20_buttons_from_cpu(struct hidpp20_profile *profile,
|
||||
button->macro.zero = 0;
|
||||
break;
|
||||
default:
|
||||
memcpy(b, button, sizeof(*b));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
hidpp20_onboard_profiles_read_led(struct hidpp20_led *led,
|
||||
struct hidpp20_internal_led internal_led)
|
||||
{
|
||||
@@ -2516,6 +2665,10 @@ hidpp20_onboard_profiles_read_led(struct hidpp20_led *led,
|
||||
if (brightness == 0)
|
||||
brightness = 100;
|
||||
break;
|
||||
case HIDPP20_LED_STARLIGHT:
|
||||
led->color = internal_led.effect.starlight.color_sky;
|
||||
led->extra_color = internal_led.effect.starlight.color_star;
|
||||
break;
|
||||
case HIDPP20_LED_BREATHING:
|
||||
period = hidpp_be_u16_to_cpu(internal_led.effect.breath.period_or_speed);
|
||||
brightness = internal_led.effect.breath.intensity;
|
||||
@@ -2523,11 +2676,18 @@ hidpp20_onboard_profiles_read_led(struct hidpp20_led *led,
|
||||
brightness = 100;
|
||||
led->color = internal_led.effect.breath.color;
|
||||
break;
|
||||
case HIDPP20_LED_RIPPLE:
|
||||
period = hidpp_be_u16_to_cpu(internal_led.effect.breath.period_or_speed);
|
||||
led->color = internal_led.effect.ripple.color;
|
||||
break;
|
||||
case HIDPP20_LED_ON:
|
||||
led->color = internal_led.effect.fixed.color;
|
||||
break;
|
||||
case HIDPP20_LED_OFF:
|
||||
break;
|
||||
default:
|
||||
memcpy(led->original, &internal_led, sizeof(internal_led));
|
||||
break;
|
||||
}
|
||||
|
||||
led->period = period;
|
||||
@@ -2627,7 +2787,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.*/
|
||||
@@ -2703,7 +2863,7 @@ read_profiles:
|
||||
return profiles->num_profiles;
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
hidpp20_onboard_profiles_write_led(struct hidpp20_internal_led *internal_led,
|
||||
struct hidpp20_led *led)
|
||||
{
|
||||
@@ -2722,24 +2882,31 @@ hidpp20_onboard_profiles_write_led(struct hidpp20_internal_led *internal_led,
|
||||
else
|
||||
internal_led->effect.cycle.intensity = 0;
|
||||
break;
|
||||
case HIDPP20_LED_STARLIGHT:
|
||||
internal_led->effect.starlight.color_sky = led->color;
|
||||
internal_led->effect.starlight.color_star = led->extra_color;
|
||||
break;
|
||||
case HIDPP20_LED_BREATHING:
|
||||
internal_led->effect.breath.color.red = led->color.red;
|
||||
internal_led->effect.breath.color.blue = led->color.blue;
|
||||
internal_led->effect.breath.color.green = led->color.green;
|
||||
internal_led->effect.breath.color = led->color;
|
||||
internal_led->effect.breath.period_or_speed = hidpp_cpu_to_be_u16(period);
|
||||
if (brightness < 100)
|
||||
internal_led->effect.breath.intensity = brightness;
|
||||
else
|
||||
internal_led->effect.breath.intensity = 0;
|
||||
break;
|
||||
case HIDPP20_LED_RIPPLE:
|
||||
internal_led->effect.ripple.color = led->color;
|
||||
internal_led->effect.ripple.period = hidpp_cpu_to_be_u16(period);
|
||||
break;
|
||||
case HIDPP20_LED_ON:
|
||||
internal_led->effect.fixed.color.red = led->color.red;
|
||||
internal_led->effect.fixed.color.blue = led->color.blue;
|
||||
internal_led->effect.fixed.color.green = led->color.green;
|
||||
internal_led->effect.fixed.color = led->color;
|
||||
internal_led->effect.fixed.effect = 0;
|
||||
break;
|
||||
case HIDPP20_LED_OFF:
|
||||
break;
|
||||
default:
|
||||
memcpy(internal_led, led->original, sizeof(*internal_led));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2787,8 +2954,10 @@ hidpp20_onboard_profiles_write_profile(struct hidpp20_device *device,
|
||||
memcpy(pdata->profile.name.txt, profile->name, sizeof(profile->name));
|
||||
|
||||
rc = hidpp20_onboard_profiles_write_sector(device, sector, sector_size, data, true);
|
||||
if (rc < 0)
|
||||
if (rc < 0) {
|
||||
hidpp_log_error(&device->base, "failed to write profile\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2871,7 +3040,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 +3053,14 @@ hidpp20_device_new(const struct hidpp_device *base, unsigned int idx)
|
||||
dev->proto_major = 1;
|
||||
dev->proto_minor = 0;
|
||||
|
||||
dev->led_ext_caps = 0;
|
||||
|
||||
hidpp_get_supported_report_types(&(dev->base), reports, num_reports);
|
||||
|
||||
if (!(dev->base.supported_report_types & HIDPP_REPORT_SHORT) &&
|
||||
!(dev->base.supported_report_types & HIDPP_REPORT_LONG))
|
||||
goto err;
|
||||
|
||||
rc = hidpp20_root_get_protocol_version(dev, &dev->proto_major, &dev->proto_minor);
|
||||
if (rc) {
|
||||
/* communication error, best to ignore the device */
|
||||
|
||||
113
src/hidpp20.h
113
src/hidpp20.h
@@ -65,6 +65,7 @@ struct hidpp20_device {
|
||||
unsigned feature_count;
|
||||
struct hidpp20_feature *feature_list;
|
||||
enum hidpp20_quirk quirk;
|
||||
unsigned int led_ext_caps;
|
||||
};
|
||||
|
||||
int hidpp20_request_command(struct hidpp20_device *dev, union hidpp20_message *msg);
|
||||
@@ -80,7 +81,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);
|
||||
@@ -431,6 +433,12 @@ int hidpp20_adjustable_dpi_set_sensor_dpi(struct hidpp20_device *device,
|
||||
int hidpp20_adjustable_report_rate_get_report_rate_list(struct hidpp20_device *device,
|
||||
uint8_t *bitflags_ms);
|
||||
|
||||
int hidpp20_adjustable_report_rate_get_report_rate(struct hidpp20_device *device,
|
||||
uint8_t *rate_ms);
|
||||
|
||||
int hidpp20_adjustable_report_rate_set_report_rate(struct hidpp20_device *device,
|
||||
uint8_t rate_ms);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x8070v4 - Color LED effects */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
@@ -494,6 +502,18 @@ hidpp20_rgb_effects_get_effect_info(struct hidpp20_device *device,
|
||||
uint8_t effect_index,
|
||||
struct hidpp20_rgb_effect_info *info);
|
||||
|
||||
struct hidpp20_led;
|
||||
|
||||
int
|
||||
hidpp20_color_led_effects_set_zone_effect(struct hidpp20_device *device,
|
||||
uint8_t zone_index,
|
||||
struct hidpp20_led led);
|
||||
|
||||
int
|
||||
hidpp20_color_led_effects_get_zone_effect(struct hidpp20_device *device,
|
||||
uint8_t zone_index,
|
||||
struct hidpp20_led *led);
|
||||
|
||||
enum hidpp20_color_led_location {
|
||||
HIDPP20_COLOR_LED_LOCATION_UNDEFINED = 0,
|
||||
HIDPP20_COLOR_LED_LOCATION_PRIMARY,
|
||||
@@ -557,14 +577,42 @@ enum hidpp20_color_led_zone_effect {
|
||||
|
||||
#define HIDPP_PAGE_ONBOARD_PROFILES 0x8100
|
||||
|
||||
/* type */
|
||||
#define HIDPP20_BUTTON_HID_TYPE 0x80
|
||||
#define HIDPP20_BUTTON_SPECIAL 0x90
|
||||
|
||||
/* standard opcodes (0x80) */
|
||||
#define HIDPP20_BUTTON_HID_TYPE_NOOP 0x00
|
||||
#define HIDPP20_BUTTON_HID_TYPE_MOUSE 0x01
|
||||
#define HIDPP20_BUTTON_HID_TYPE_KEYBOARD 0x02
|
||||
#define HIDPP20_BUTTON_HID_TYPE_CONSUMER_CONTROL 0x03
|
||||
#define HIDPP20_BUTTON_SPECIAL 0x90
|
||||
|
||||
/* special opcodes (0x90) */
|
||||
#define HIDPP20_BUTTON_SPECIAL_NOOP 0x00
|
||||
#define HIDPP20_BUTTON_SPECIAL_TILT_LEFT 0x01
|
||||
#define HIDPP20_BUTTON_SPECIAL_TILT_RIGHT 0x02
|
||||
#define HIDPP20_BUTTON_SPECIAL_NEXT_DPI 0x03
|
||||
#define HIDPP20_BUTTON_SPECIAL_PREV_DPI 0x04
|
||||
#define HIDPP20_BUTTON_SPECIAL_CYCLE_DPI 0x05
|
||||
#define HIDPP20_BUTTON_SPECIAL_DEFAULT_DPI 0x06
|
||||
#define HIDPP20_BUTTON_SPECIAL_SHIFT_DPI 0x07
|
||||
#define HIDPP20_BUTTON_SPECIAL_NEXT_PROFILE 0x08
|
||||
#define HIDPP20_BUTTON_SPECIAL_PREV_PROFILE 0x09
|
||||
#define HIDPP20_BUTTON_SPECIAL_CYCLE_PROFILE 0x0a
|
||||
#define HIDPP20_BUTTON_SPECIAL_GSHIFT 0x0b
|
||||
/* memory model >= 3 */
|
||||
#define HIDPP20_BUTTON_SPECIAL_BATTERY_INDICATOR 0x0c
|
||||
#define HIDPP20_BUTTON_SPECIAL_ENABLE_PROFILE 0x0d
|
||||
#define HIDPP20_BUTTON_SPECIAL_PERFORMANCE_SWITCH 0x0e
|
||||
#define HIDPP20_BUTTON_SPECIAL_HOST 0x0f
|
||||
#define HIDPP20_BUTTON_SPECIAL_SCROLL_DOWN 0x10
|
||||
#define HIDPP20_BUTTON_SPECIAL_SCROLL_UP 0x11
|
||||
|
||||
/* buttons */
|
||||
#define HIDPP20_BUTTON_MACRO 0x00
|
||||
#define HIDPP20_BUTTON_DISABLED 0xFF
|
||||
|
||||
/* modifiers */
|
||||
#define HIDPP20_MODIFIER_KEY_CTRL 0x01
|
||||
#define HIDPP20_MODIFIER_KEY_SHIFT 0x02
|
||||
|
||||
@@ -598,6 +646,8 @@ union hidpp20_button_binding {
|
||||
struct {
|
||||
uint8_t type; /* HIDPP20_BUTTON_SPECIAL */
|
||||
uint8_t special;
|
||||
uint8_t reserved;
|
||||
uint8_t profile;
|
||||
} __attribute__((packed)) special;
|
||||
struct {
|
||||
uint8_t type; /* HIDPP20_BUTTON_MACRO */
|
||||
@@ -697,7 +747,11 @@ enum hidpp20_led_mode {
|
||||
HIDPP20_LED_OFF = 0x00,
|
||||
HIDPP20_LED_ON = 0x01,
|
||||
HIDPP20_LED_CYCLE = 0x03,
|
||||
HIDPP20_LED_COLOR_WAVE = 0x04,
|
||||
HIDPP20_LED_STARLIGHT = 0x05,
|
||||
HIDPP20_LED_BREATHING = 0x0a,
|
||||
HIDPP20_LED_RIPPLE = 0x0b,
|
||||
HIDPP20_LED_CUSTOM = 0x0c,
|
||||
};
|
||||
|
||||
enum hidpp20_led_waveform {
|
||||
@@ -722,35 +776,76 @@ struct hidpp20_internal_led {
|
||||
uint16_t period_or_speed; /* period in ms, speed is device dependent */
|
||||
uint8_t intensity; /* 1 - 100 percent, 0 means 100 */
|
||||
} __attribute__((packed)) cycle;
|
||||
struct hidpp20_led_starlight {
|
||||
struct hidpp20_color color_sky;
|
||||
struct hidpp20_color color_star;
|
||||
} __attribute__((packed)) starlight;
|
||||
struct hidpp20_led_breath {
|
||||
struct hidpp20_color color;
|
||||
uint16_t period_or_speed; /* period in ms, speed is device dependent */
|
||||
uint8_t waveform; /* enum hidpp20_led_waveform */
|
||||
uint8_t intensity; /* 1 - 100 percent, 0 means 100 */
|
||||
} __attribute__((packed)) breath;
|
||||
struct hidpp20_led_ripple {
|
||||
struct hidpp20_color color;
|
||||
uint8_t reserved;
|
||||
uint16_t period;
|
||||
} __attribute__((packed)) ripple;
|
||||
struct hidpp20_led_custom {
|
||||
uint8_t slot;
|
||||
uint16_t init_frame;
|
||||
uint16_t lenght;
|
||||
uint16_t frame_period;
|
||||
uint8_t intensity;
|
||||
} __attribute__((packed)) custom;
|
||||
uint8_t padding[10];
|
||||
} __attribute__((packed)) effect;
|
||||
};
|
||||
_Static_assert(sizeof(struct hidpp20_led_fixed) == 4, "Invalid size");
|
||||
_Static_assert(sizeof(struct hidpp20_led_cycle) == 8, "Invalid size");
|
||||
_Static_assert(sizeof(struct hidpp20_led_starlight) == 6, "Invalid size");
|
||||
_Static_assert(sizeof(struct hidpp20_led_breath) == 7, "Invalid size");
|
||||
_Static_assert(sizeof(struct hidpp20_internal_led) == 11, "Invalid size");
|
||||
_Static_assert(sizeof(struct hidpp20_led_ripple) == 6, "Invalid size");
|
||||
_Static_assert(sizeof(struct hidpp20_led_custom) == 8, "Invalid size");
|
||||
|
||||
typedef uint8_t percent_t;
|
||||
|
||||
struct hidpp20_led {
|
||||
enum hidpp20_led_mode mode;
|
||||
struct hidpp20_color color;
|
||||
struct hidpp20_color extra_color;
|
||||
uint16_t period;
|
||||
percent_t brightness;
|
||||
uint8_t original[sizeof(struct hidpp20_internal_led)];
|
||||
};
|
||||
|
||||
#define HIDPP20_MACRO_NOOP 0x01
|
||||
/* macro opcodes */
|
||||
|
||||
/* control commands */
|
||||
#define HIDPP20_MACRO_WAIT_FOR_RELEASE 0x01
|
||||
#define HIDPP20_MACRO_REPEAT_WHILE_PRESSED 0x02
|
||||
#define HIDPP20_MACRO_REPEAT_UNTIL_CANCELED 0x03
|
||||
#define HIDPP20_MACRO_END 0xff
|
||||
|
||||
/* delay & jump commands */
|
||||
#define HIDPP20_MACRO_NOOP 0x00 /* wait the duration of a macro step */
|
||||
#define HIDPP20_MACRO_DELAY 0x40
|
||||
#define HIDPP20_MACRO_JUMP 0x60
|
||||
|
||||
/* button & key event generation */
|
||||
#define HIDPP20_MACRO_BUTTON_DOWN 0x41
|
||||
#define HIDPP20_MACRO_BUTTON_UP 0x42
|
||||
#define HIDPP20_MACRO_KEY_PRESS 0x43
|
||||
#define HIDPP20_MACRO_KEY_RELEASE 0x44
|
||||
#define HIDPP20_MACRO_JUMP 0x60
|
||||
#define HIDPP20_MACRO_END 0xff
|
||||
#define HIDPP20_MACRO_CONS_DOWN 0x45
|
||||
#define HIDPP20_MACRO_CONS_UP 0x46
|
||||
|
||||
/* analog commands */
|
||||
#define HIDPP20_MACRO_ROLLER 0x20
|
||||
#define HIDPP20_MACRO_ACPAN 0x21
|
||||
#define HIDPP20_MACRO_XY 0x61
|
||||
|
||||
|
||||
union hidpp20_macro_data {
|
||||
struct {
|
||||
@@ -918,6 +1013,14 @@ hidpp20_onboard_profiles_allocate_sector(struct hidpp20_profiles *profiles)
|
||||
return zalloc(profiles->sector_size);
|
||||
}
|
||||
|
||||
void
|
||||
hidpp20_onboard_profiles_read_led(struct hidpp20_led *led,
|
||||
struct hidpp20_internal_led internal_led);
|
||||
|
||||
void
|
||||
hidpp20_onboard_profiles_write_led(struct hidpp20_internal_led *internal_led,
|
||||
struct hidpp20_led *led);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x8110 - Mouse Button Spy */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
16
src/liblur.c
16
src/liblur.c
@@ -140,20 +140,21 @@ hidraw_is_receiver(int fd)
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct hidpp10_device *
|
||||
hidpp10_init(int fd)
|
||||
static int
|
||||
hidpp10_init(int fd, struct hidpp10_device **out)
|
||||
{
|
||||
struct hidpp_device base;
|
||||
|
||||
hidpp_device_init(&base, fd);
|
||||
|
||||
return hidpp10_device_new(&base, HIDPP_RECEIVER_IDX,
|
||||
HIDPP10_PROFILE_UNKNOWN, 1);
|
||||
HIDPP10_PROFILE_UNKNOWN, 1, out);
|
||||
}
|
||||
|
||||
_EXPORT_ int
|
||||
lur_receiver_new_from_hidraw(int fd, void *userdata, struct lur_receiver **out)
|
||||
{
|
||||
int rc;
|
||||
struct lur_receiver *receiver;
|
||||
|
||||
if (!hidraw_is_receiver(fd))
|
||||
@@ -165,8 +166,8 @@ lur_receiver_new_from_hidraw(int fd, void *userdata, struct lur_receiver **out)
|
||||
receiver->userdata = userdata;
|
||||
list_init(&receiver->devices);
|
||||
|
||||
receiver->hidppdev = hidpp10_init(fd);
|
||||
if (!receiver->hidppdev)
|
||||
rc = hidpp10_init(fd, &receiver->hidppdev);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
*out = receiver;
|
||||
@@ -174,8 +175,7 @@ lur_receiver_new_from_hidraw(int fd, void *userdata, struct lur_receiver **out)
|
||||
|
||||
error:
|
||||
free(receiver);
|
||||
return -errno;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
_EXPORT_ int
|
||||
@@ -203,7 +203,7 @@ lur_receiver_enumerate(struct lur_receiver *lur,
|
||||
uint32_t serial;
|
||||
bool is_new_device = true;
|
||||
|
||||
d = hidpp10_device_new(&base, i, HIDPP10_PROFILE_UNKNOWN, 1);
|
||||
rc = hidpp10_device_new(&base, i, HIDPP10_PROFILE_UNKNOWN, 1, &d);
|
||||
if (!d)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -51,11 +51,14 @@ enum driver {
|
||||
LOGITECH_G300,
|
||||
LOGITECH_G600,
|
||||
STEELSERIES,
|
||||
SINOWEALTH,
|
||||
SINOWEALTH_NUBWO,
|
||||
};
|
||||
|
||||
struct data_hidpp20 {
|
||||
int index;
|
||||
enum hidpp20_quirk quirk;
|
||||
int led_count;
|
||||
};
|
||||
|
||||
struct data_hidpp10 {
|
||||
@@ -157,6 +160,7 @@ init_data_hidpp20(struct ratbag *ratbag,
|
||||
char *str;
|
||||
|
||||
data->hidpp20.index = -1;
|
||||
data->hidpp20.led_count = -1;
|
||||
|
||||
num = g_key_file_get_integer(keyfile, group, "DeviceIndex", &error);
|
||||
if (num != 0 || !error)
|
||||
@@ -164,6 +168,13 @@ init_data_hidpp20(struct ratbag *ratbag,
|
||||
if (error)
|
||||
g_error_free(error);
|
||||
|
||||
error = NULL;
|
||||
num = g_key_file_get_integer(keyfile, group, "Leds", &error);
|
||||
if (!error)
|
||||
data->hidpp20.led_count = num;
|
||||
if (error)
|
||||
g_error_free(error);
|
||||
|
||||
str = g_key_file_get_string(keyfile, group, "Quirk", NULL);
|
||||
data->hidpp20.quirk = HIDPP20_QUIRK_NONE;
|
||||
if (str) {
|
||||
@@ -258,6 +269,8 @@ static const struct driver_map {
|
||||
{ LOGITECH_G300, "logitech_g300", NULL},
|
||||
{ LOGITECH_G600, "logitech_g600", NULL},
|
||||
{ STEELSERIES, "steelseries", init_data_steelseries },
|
||||
{ SINOWEALTH, "sinowealth", NULL },
|
||||
{ SINOWEALTH_NUBWO, "sinowealth_nubwo", NULL},
|
||||
};
|
||||
|
||||
const char *
|
||||
@@ -346,6 +359,8 @@ parse_ledtypes(char **strv, enum ratbag_led_type *types, size_t ntypes)
|
||||
types[i] = RATBAG_LED_TYPE_SIDE;
|
||||
else if (streq(s, "wheel"))
|
||||
types[i] = RATBAG_LED_TYPE_WHEEL;
|
||||
else if (streq(s, "switches"))
|
||||
types[i] = RATBAG_LED_TYPE_SWITCHES;
|
||||
else
|
||||
return -1;
|
||||
|
||||
@@ -574,6 +589,14 @@ ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data)
|
||||
return data->hidpp20.index;
|
||||
}
|
||||
|
||||
int
|
||||
ratbag_device_data_hidpp20_get_led_count(const struct ratbag_device_data *data)
|
||||
{
|
||||
assert(data->drivertype == HIDPP20);
|
||||
|
||||
return data->hidpp20.led_count;
|
||||
}
|
||||
|
||||
enum hidpp20_quirk
|
||||
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data)
|
||||
{
|
||||
|
||||
@@ -80,6 +80,9 @@ ratbag_device_data_hidpp10_get_led_count(const struct ratbag_device_data *data);
|
||||
int
|
||||
ratbag_device_data_hidpp20_get_index(const struct ratbag_device_data *data);
|
||||
|
||||
int
|
||||
ratbag_device_data_hidpp20_get_led_count(const struct ratbag_device_data *data);
|
||||
|
||||
enum hidpp20_quirk
|
||||
ratbag_device_data_hidpp20_get_quirk(const struct ratbag_device_data *data);
|
||||
|
||||
|
||||
@@ -293,6 +293,7 @@ enum ratbag_led_type {
|
||||
RATBAG_LED_TYPE_BATTERY,
|
||||
RATBAG_LED_TYPE_DPI,
|
||||
RATBAG_LED_TYPE_WHEEL,
|
||||
RATBAG_LED_TYPE_SWITCHES,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
#include <linux/input.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
@@ -146,6 +147,9 @@ struct ratbag_device {
|
||||
unsigned num_buttons;
|
||||
unsigned num_leds;
|
||||
|
||||
int firmware_major;
|
||||
int firmware_minor;
|
||||
|
||||
void *drv_data;
|
||||
|
||||
struct list link;
|
||||
@@ -295,6 +299,7 @@ struct ratbag_profile {
|
||||
unsigned int hz; /**< report rate in Hz */
|
||||
unsigned int rates[8]; /**< report rates available */
|
||||
size_t nrates; /**< number of entries in rates */
|
||||
bool rate_dirty;
|
||||
|
||||
unsigned int num_resolutions;
|
||||
|
||||
@@ -585,14 +590,16 @@ ratbag_led_set_mode_capability(struct ratbag_led *led,
|
||||
}
|
||||
|
||||
/* list of all supported drivers */
|
||||
struct ratbag_driver etekcity_driver;
|
||||
struct ratbag_driver hidpp20_driver;
|
||||
struct ratbag_driver hidpp10_driver;
|
||||
struct ratbag_driver logitech_g300_driver;
|
||||
struct ratbag_driver logitech_g600_driver;
|
||||
struct ratbag_driver roccat_driver;
|
||||
struct ratbag_driver gskill_driver;
|
||||
struct ratbag_driver steelseries_driver;
|
||||
extern struct ratbag_driver etekcity_driver;
|
||||
extern struct ratbag_driver hidpp20_driver;
|
||||
extern struct ratbag_driver hidpp10_driver;
|
||||
extern struct ratbag_driver logitech_g300_driver;
|
||||
extern struct ratbag_driver logitech_g600_driver;
|
||||
extern struct ratbag_driver roccat_driver;
|
||||
extern struct ratbag_driver gskill_driver;
|
||||
extern struct ratbag_driver steelseries_driver;
|
||||
extern struct ratbag_driver sinowealth_driver;
|
||||
extern struct ratbag_driver sinowealth_nubwo_driver;
|
||||
|
||||
struct ratbag_device*
|
||||
ratbag_device_new(struct ratbag *ratbag, struct udev_device *udev_device,
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#include "libratbag-util.h"
|
||||
#include "libratbag-test.h"
|
||||
|
||||
struct ratbag_driver test_driver;
|
||||
extern struct ratbag_driver test_driver;
|
||||
|
||||
static inline void
|
||||
ratbag_register_test_drivers(struct ratbag *ratbag)
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <iconv.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "libratbag-util.h"
|
||||
#include "libratbag-private.h"
|
||||
@@ -188,3 +190,18 @@ err:
|
||||
iconv_close(converter);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mkdir_p(char *dir, mode_t mode)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (!dir)
|
||||
return EINVAL;
|
||||
|
||||
if (!stat(dir, &sb))
|
||||
return 0;
|
||||
|
||||
mkdir_p(dirname(strdupa(dir)), mode);
|
||||
|
||||
return mkdir(dir, mode);
|
||||
}
|
||||
|
||||
@@ -62,6 +62,8 @@ int list_empty(const struct list *list);
|
||||
pos = tmp, \
|
||||
tmp = container_of(pos->member.next, tmp, member))
|
||||
|
||||
int mkdir_p(char *dir, mode_t mode);
|
||||
|
||||
static inline char*
|
||||
strncpy_safe(char *dest, const char *src, size_t n)
|
||||
{
|
||||
|
||||
@@ -71,17 +71,29 @@ ratbag_default_log_func(struct ratbag *ratbag,
|
||||
const char *format, va_list args)
|
||||
{
|
||||
const char *prefix;
|
||||
FILE *out = stdout;
|
||||
|
||||
switch(priority) {
|
||||
case RATBAG_LOG_PRIORITY_RAW: prefix = "raw"; break;
|
||||
case RATBAG_LOG_PRIORITY_DEBUG: prefix = "debug"; break;
|
||||
case RATBAG_LOG_PRIORITY_INFO: prefix = "info"; break;
|
||||
case RATBAG_LOG_PRIORITY_ERROR: prefix = "error"; break;
|
||||
default: prefix="<invalid priority>"; break;
|
||||
case RATBAG_LOG_PRIORITY_RAW:
|
||||
prefix = "raw";
|
||||
break;
|
||||
case RATBAG_LOG_PRIORITY_DEBUG:
|
||||
prefix = "debug";
|
||||
break;
|
||||
case RATBAG_LOG_PRIORITY_INFO:
|
||||
prefix = "info";
|
||||
break;
|
||||
case RATBAG_LOG_PRIORITY_ERROR:
|
||||
prefix = "error";
|
||||
out = stderr;
|
||||
break;
|
||||
default:
|
||||
prefix="<invalid priority>";
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "ratbag %s: ", prefix);
|
||||
vfprintf(stderr, format, args);
|
||||
fprintf(out, "ratbag %s: ", prefix);
|
||||
vfprintf(out, format, args);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -256,7 +268,7 @@ ratbag_sanity_check_device(struct ratbag_device *device)
|
||||
}
|
||||
|
||||
nres = ratbag_profile_get_num_resolutions(profile);
|
||||
if (nres == 0 || nres > 16) {
|
||||
if (nres > 16) {
|
||||
log_bug_libratbag(ratbag,
|
||||
"%s: invalid number of resolutions (%d)\n",
|
||||
device->name,
|
||||
@@ -533,6 +545,18 @@ ratbag_device_get_product_id(const struct ratbag_device *device)
|
||||
return device->ids.product;
|
||||
}
|
||||
|
||||
LIBRATBAG_EXPORT int32_t
|
||||
ratbag_device_get_firmware_major_version(const struct ratbag_device *device)
|
||||
{
|
||||
return device->firmware_major;
|
||||
}
|
||||
|
||||
LIBRATBAG_EXPORT int32_t
|
||||
ratbag_device_get_firmware_minor_version(const struct ratbag_device *device)
|
||||
{
|
||||
return device->firmware_minor;
|
||||
}
|
||||
|
||||
LIBRATBAG_EXPORT uint32_t
|
||||
ratbag_device_get_product_version(const struct ratbag_device *device)
|
||||
{
|
||||
@@ -590,6 +614,8 @@ ratbag_create_context(const struct ratbag_interface *interface,
|
||||
ratbag_register_driver(ratbag, &roccat_driver);
|
||||
ratbag_register_driver(ratbag, &gskill_driver);
|
||||
ratbag_register_driver(ratbag, &steelseries_driver);
|
||||
ratbag_register_driver(ratbag, &sinowealth_driver);
|
||||
ratbag_register_driver(ratbag, &sinowealth_nubwo_driver);
|
||||
|
||||
return ratbag;
|
||||
}
|
||||
@@ -937,6 +963,8 @@ ratbag_device_commit(struct ratbag_device *device)
|
||||
list_for_each(profile, &device->profiles, link) {
|
||||
profile->dirty = false;
|
||||
|
||||
profile->rate_dirty = false;
|
||||
|
||||
list_for_each(button, &profile->buttons, link)
|
||||
button->dirty = false;
|
||||
|
||||
@@ -1105,6 +1133,7 @@ ratbag_profile_set_report_rate(struct ratbag_profile *profile,
|
||||
if (profile->hz != hz) {
|
||||
profile->hz = hz;
|
||||
profile->dirty = true;
|
||||
profile->rate_dirty = true;
|
||||
}
|
||||
|
||||
return RATBAG_SUCCESS;
|
||||
|
||||
@@ -421,6 +421,24 @@ ratbag_device_set_user_data(struct ratbag_device *device, void *userdata);
|
||||
void*
|
||||
ratbag_device_get_user_data(const struct ratbag_device *device);
|
||||
|
||||
/**
|
||||
* @ingroup device
|
||||
*
|
||||
* @param device A previously initialized ratbag device
|
||||
* @return The firmware major version of the device.
|
||||
*/
|
||||
int32_t
|
||||
ratbag_device_get_firmware_major_version(const struct ratbag_device *device);
|
||||
|
||||
/**
|
||||
* @ingroup device
|
||||
*
|
||||
* @param device A previously initialized ratbag device
|
||||
* @return The firmware minor version of the device.
|
||||
*/
|
||||
int32_t
|
||||
ratbag_device_get_firmware_minor_version(const struct ratbag_device *device);
|
||||
|
||||
/**
|
||||
* @ingroup device
|
||||
*
|
||||
|
||||
@@ -52,6 +52,28 @@
|
||||
%typemap(freearg) (unsigned int *rates, size_t nrates) = (unsigned int *resolutions, size_t nres);
|
||||
/* END OF custom typemap for handling ratbag_resolution_get_report_rate_list */
|
||||
|
||||
/* uintXX_t mapping: Python -> C */
|
||||
%typemap(in) uint8_t {
|
||||
$1 = (uint8_t) PyInt_AsLong($input);
|
||||
}
|
||||
%typemap(in) uint16_t {
|
||||
$1 = (uint16_t) PyInt_AsLong($input);
|
||||
}
|
||||
%typemap(in) uint32_t {
|
||||
$1 = (uint32_t) PyInt_AsLong($input);
|
||||
}
|
||||
|
||||
/* uintXX_t mapping: C -> Python */
|
||||
%typemap(out) uint8_t {
|
||||
$result = PyInt_FromLong((long) $1);
|
||||
}
|
||||
%typemap(out) uint16_t {
|
||||
$result = PyInt_FromLong((long) $1);
|
||||
}
|
||||
%typemap(out) uint32_t {
|
||||
$result = PyInt_FromLong((long) $1);
|
||||
}
|
||||
|
||||
/* Parse the header file to generate wrappers */
|
||||
%include "libratbag.h"
|
||||
%include "libratbag-enums.h"
|
||||
|
||||
@@ -98,7 +98,9 @@ main(int argc, char **argv)
|
||||
error(1, errno, "Failed to open path %s", path);
|
||||
|
||||
hidpp_device_init(&base, fd);
|
||||
dev = hidpp10_device_new(&base, HIDPP_WIRED_DEVICE_IDX, HIDPP10_PROFILE_UNKNOWN, 5);
|
||||
rc = hidpp10_device_new(&base, HIDPP_WIRED_DEVICE_IDX, HIDPP10_PROFILE_UNKNOWN, 5, &dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (argc == 2)
|
||||
rc = dump_all_pages(dev);
|
||||
|
||||
@@ -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,7 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# vim: set expandtab shiftwidth=4 tabstop=4:
|
||||
#
|
||||
# Copyright 2017 Red Hat, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# vim: set expandtab shiftwidth=4 tabstop=4:
|
||||
#
|
||||
# Copyright 2017 Red Hat, Inc.
|
||||
# Copyright 2017-2019 Red Hat, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the "Software"),
|
||||
@@ -137,7 +135,7 @@ class Ratbagd(object):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, apiversion):
|
||||
os.environ['LIBRATBAG_DATA_DIR'] = LIBRATBAG_DATA_DIR
|
||||
self._ratbag = libratbag.ratbag_create_context(libratbag.interface, None)
|
||||
self._devices = {}
|
||||
@@ -251,13 +249,13 @@ class RatbagdDevice(metaclass=MetaRatbag):
|
||||
@property
|
||||
def model(self):
|
||||
"""The unique identifier for this device model."""
|
||||
bus = libratbag.ratbag_device_get_bus(self._device)
|
||||
bus = libratbag.ratbag_device_get_bustype(self._device)
|
||||
if not bus:
|
||||
return "unknown"
|
||||
vid = libratbag.ratbag_device_get_vendor_id(self._device)
|
||||
pid = libratbag.ratbag_device_get_product_id(self._device)
|
||||
version = libratbag.ratbag_device_get_product_version(self._device)
|
||||
return "{}:{04x}:{04x}:{d}".format(bus, vid, pid, version)
|
||||
return "{}:{:04x}:{:04x}:{:d}".format(bus, vid, pid, version)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@@ -324,7 +322,7 @@ class RatbagdProfile(metaclass=MetaRatbag):
|
||||
"""The capabilities of this profile as an array. Capabilities not
|
||||
present on the profile are not in the list. Thus use e.g.
|
||||
|
||||
if RatbagdProfile.CAP_WRITABLE_NAME is in profile.capabilities:
|
||||
if RatbagdProfile.CAP_WRITABLE_NAME in profile.capabilities:
|
||||
do something
|
||||
"""
|
||||
return self._capabilities
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# vim: set expandtab shiftwidth=4 tabstop=4:
|
||||
#
|
||||
# This file is part of libratbag.
|
||||
#
|
||||
# Copyright 2017 Red Hat, Inc.
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# vim: set expandtab shiftwidth=4 tabstop=4:
|
||||
#
|
||||
# Copyright 2016 Red Hat, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
@@ -195,14 +193,18 @@ def print_profile(d, p, level):
|
||||
" (active)" if p.is_active else ""))
|
||||
if p.enabled:
|
||||
print(" " * level + "Name: {}".format(p.name or 'n/a'))
|
||||
print(" " * level + "Report Rate: {}Hz".format(p.report_rate))
|
||||
print(" " * level + "Resolutions:")
|
||||
for r in p.resolutions:
|
||||
print_resolution(d, p, r, level + 2)
|
||||
for b in p.buttons:
|
||||
print_button(d, p, b, level)
|
||||
for l in p.leds:
|
||||
print_led(d, p, l, level)
|
||||
if p.report_rate:
|
||||
print(" " * level + "Report Rate: {}Hz".format(p.report_rate))
|
||||
if p.resolutions:
|
||||
print(" " * level + "Resolutions:")
|
||||
for r in p.resolutions:
|
||||
print_resolution(d, p, r, level + 2)
|
||||
if p.buttons:
|
||||
for b in p.buttons:
|
||||
print_button(d, p, b, level)
|
||||
if p.leds:
|
||||
for l in p.leds:
|
||||
print_led(d, p, l, level)
|
||||
|
||||
|
||||
def print_device(d, level):
|
||||
@@ -210,6 +212,8 @@ def print_device(d, level):
|
||||
|
||||
print(" " * level + "{} - {}".format(d.id, d.name))
|
||||
print(" " * level + " Model: {}".format(d.model))
|
||||
if d.firmware_major > 0 or d.firmware_minor > 0:
|
||||
print(" " * level + " Firmware version: {}.{}".format(d.firmware_major, d.firmware_minor))
|
||||
print(" " * level + " Number of Buttons: {}".format(len(p.buttons)))
|
||||
print(" " * level + " Number of Leds: {}".format(len(p.leds)))
|
||||
print(" " * level + "Number of Profiles: {}".format(len(d.profiles)))
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# vim: set expandtab shiftwidth=4 tabstop=4:
|
||||
#
|
||||
# This file is part of libratbag.
|
||||
#
|
||||
# Copyright 2017 Red Hat, Inc.
|
||||
@@ -374,6 +372,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"),
|
||||
@@ -195,7 +193,7 @@ class _RatbagdDBus(GObject.GObject):
|
||||
# args to .Set are "interface name", "function name", value-variant
|
||||
val = GLib.Variant("{}".format(type), value)
|
||||
if readwrite:
|
||||
pval = GLib.Variant("(ssv)".format(type), (self._interface, property, val))
|
||||
pval = GLib.Variant("(ssv)", (self._interface, property, val))
|
||||
self._proxy.call_sync("org.freedesktop.DBus.Properties.Set",
|
||||
pval, Gio.DBusCallFlags.NO_AUTO_START,
|
||||
2000, None)
|
||||
@@ -252,11 +250,13 @@ class Ratbagd(_RatbagdDBus):
|
||||
|
||||
def __init__(self, api_version):
|
||||
super().__init__("Manager", None)
|
||||
result = self._get_dbus_property("Devices") or []
|
||||
self._devices = [RatbagdDevice(objpath) for objpath in result]
|
||||
self._proxy.connect("notify::g-name-owner", self._on_name_owner_changed)
|
||||
result = self._get_dbus_property("Devices")
|
||||
if result is None and not self._proxy.get_cached_property_names():
|
||||
raise RatbagdUnavailable("Make sure it is running and your user is in the required groups.")
|
||||
if self.api_version != api_version:
|
||||
raise RatbagdIncompatible(self.api_version or -1, api_version)
|
||||
self._devices = [RatbagdDevice(objpath) for objpath in result or []]
|
||||
self._proxy.connect("notify::g-name-owner", self._on_name_owner_changed)
|
||||
|
||||
def _on_name_owner_changed(self, *kwargs):
|
||||
self.emit("daemon-disappeared")
|
||||
@@ -342,6 +342,16 @@ class RatbagdDevice(_RatbagdDBus):
|
||||
"""The unique identifier for this device model."""
|
||||
return self._get_dbus_property("Model")
|
||||
|
||||
@GObject.Property
|
||||
def firmware_major(self):
|
||||
"""The firmware major version."""
|
||||
return self._get_dbus_property("FirmwareMajor")
|
||||
|
||||
@GObject.Property
|
||||
def firmware_minor(self):
|
||||
"""The firmware minor version."""
|
||||
return self._get_dbus_property("FirmwareMinor")
|
||||
|
||||
@GObject.Property
|
||||
def name(self):
|
||||
"""The device name, usually provided by the kernel."""
|
||||
@@ -428,7 +438,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 []
|
||||
|
||||
@@ -74,6 +74,9 @@ led_type_to_str(enum ratbag_led_type type)
|
||||
case RATBAG_LED_TYPE_WHEEL:
|
||||
str = "wheel";
|
||||
break;
|
||||
case RATBAG_LED_TYPE_SWITCHES:
|
||||
str = "switches";
|
||||
break;
|
||||
default:
|
||||
assert(!"Invalid LED type");
|
||||
break;
|
||||
|
||||
@@ -88,4 +88,4 @@ str_to_special_action(const char *str);
|
||||
struct ratbag_device *
|
||||
ratbag_cmd_open_device(struct ratbag *ratbag, const char *path);
|
||||
|
||||
const struct ratbag_interface interface;
|
||||
extern const struct ratbag_interface interface;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# vim: set expandtab shiftwidth=4 tabstop=4:
|
||||
#
|
||||
# This file is part of libratbag.
|
||||
#
|
||||
# Copyright 2017 Red Hat, Inc.
|
||||
@@ -62,9 +60,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