1
0
forked from drath/libratbag
Files
libratbag-plus/src/driver-steelseries.c
Thomas Hindoe Paaboel Andersen 575b7bce98 add driver and svg for steelseries rival 310
The driver does not yet support button mappings or any
non-solid LED modes.

The device does not support reading out the current
configuration. On probe some default values are used
to get a usable profile.
2017-10-18 00:19:12 +02:00

274 lines
6.9 KiB
C

/*
* Copyright © 2017 Thomas Hindoe Paaboel Andersen.
*
* 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"
#include "hidpp-generic.h"
#define STEELSERIES_NUM_PROFILES 1
#define STEELSERIES_NUM_BUTTONS 6
#define STEELSERIES_NUM_DPI 2
#define STEELSERIES_NUM_LED 2
#define STEELSERIES_DPI_MIN 100
#define STEELSERIES_DPI_MAX 12000
/* not sure these are used for */
#define STEELSERIES_REPORT_ID_1 0x01
#define STEELSERIES_REPORT_ID_2 0x02
#define STEELSERIES_REPORT_SIZE 64
#define STEELSERIES_ID_DPI 0x53
#define STEELSERIES_ID_REPORT_RATE 0x54
#define STEELSERIES_ID_LED 0x5b
#define STEELSERIES_ID_SAVE 0x59
static int
steelseries_test_hidraw(struct ratbag_device *device)
{
return ratbag_hidraw_has_report(device, 0x01);
}
static int
steelseries_probe(struct ratbag_device *device)
{
struct ratbag_profile *profile = NULL;
struct ratbag_resolution *resolution;
struct ratbag_button *button;
struct ratbag_led *led;
int rc;
rc = ratbag_find_hidraw(device, steelseries_test_hidraw);
if (rc)
return rc;
ratbag_device_init_profiles(device,
STEELSERIES_NUM_PROFILES,
STEELSERIES_NUM_DPI,
STEELSERIES_NUM_BUTTONS,
STEELSERIES_NUM_LED);
/* set these caps manually as they are not assumed with only 1 profile */
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON);
ratbag_device_set_capability(device, RATBAG_DEVICE_CAP_BUTTON_KEY);
ratbag_device_unset_capability(device, RATBAG_DEVICE_CAP_QUERY_CONFIGURATION);
/* The device does not support reading the current settings. Fall back
to some sensible defaults */
ratbag_device_for_each_profile(device, profile) {
profile->is_active = true;
ratbag_profile_for_each_resolution(profile, resolution) {
if (resolution->index == 0) {
resolution->is_active = true;
resolution->is_default = true;
}
resolution->dpi_x = 1000 * (1 + resolution->index);
resolution->dpi_y = 1000 * (1 + resolution->index);
resolution->hz = 1000;
ratbag_resolution_set_dpi_list_from_range(resolution,
STEELSERIES_DPI_MIN,
STEELSERIES_DPI_MAX);
}
ratbag_profile_for_each_button(profile, button) {
/* TODO */
}
ratbag_profile_for_each_led(profile, led) {
led->type = led->index == 0 ? RATBAG_LED_TYPE_LOGO : RATBAG_LED_TYPE_WHEEL;
led->mode = RATBAG_LED_ON;
led->colordepth = RATBAG_LED_COLORDEPTH_RGB_888;
led->color.red = 0;
led->color.green = 0;
led->color.blue = 255;
}
}
return 0;
}
static int
steelseries_write_dpi(struct ratbag_resolution *resolution)
{
struct ratbag_device *device = resolution->profile->device;
int ret;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
buf[0] = STEELSERIES_ID_DPI;
buf[2] = resolution->index + 1;
buf[3] = resolution->dpi_x / 100 - 1;
buf[6] = 0x42; /* not sure if needed */
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
if (ret != sizeof(buf))
return ret;
return 0;
}
static int
steelseries_write_report_rate(struct ratbag_resolution *resolution)
{
struct ratbag_device *device = resolution->profile->device;
int ret;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
buf[0] = STEELSERIES_ID_REPORT_RATE;
buf[2] = 1000 / resolution->hz;
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
if (ret != sizeof(buf))
return ret;
return 0;
}
static int
steelseries_write_led(struct ratbag_led *led)
{
struct ratbag_device *device = led->profile->device;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
uint16_t rate;
int ret;
rate = led->index == 0 ? 10000 : 5000; /* 0x1027 or 0x8813 */
buf[0] = STEELSERIES_ID_LED;
buf[2] = led->index;
/* not sure why the rate is set for the steady color or why it is
different for the two LEDs */
hidpp_set_unaligned_le_u16(&buf[3], rate);
/* not sure if these four are needed either */
buf[15] = 0x01;
buf[17] = 0x01;
buf[19] = 0x01;
buf[27] = 0x01;
buf[28] = buf[31] = led->color.red;
buf[29] = buf[32] = led->color.green;
buf[30] = buf[33] = led->color.blue;
ret = ratbag_hidraw_output_report(device, buf, sizeof(buf));
if (ret != sizeof(buf))
return ret;
return 0;
}
static int
steelseries_write_profile(struct ratbag_profile *profile)
{
struct ratbag_resolution *resolution;
struct ratbag_button *button;
struct ratbag_led *led;
int rc;
ratbag_profile_for_each_resolution(profile, resolution) {
rc = steelseries_write_dpi(resolution);
if (rc != 0)
return rc;
/* The same hz is used for all resolutions. Only write once. */
if (resolution->index > 0)
continue;
rc = steelseries_write_report_rate(resolution);
if (rc != 0)
return rc;
}
ratbag_profile_for_each_button(profile, button) {
/* TODO */
}
ratbag_profile_for_each_led(profile, led) {
if (!led->dirty)
continue;
rc = steelseries_write_led(led);
if (rc != 0)
return rc;
}
return 0;
}
static int
steelseries_commit(struct ratbag_device *device)
{
struct ratbag_profile *profile;
uint8_t buf[STEELSERIES_REPORT_SIZE] = {0};
int rc = 0;
list_for_each(profile, &device->profiles, link) {
if (!profile->dirty)
continue;
log_debug(device->ratbag,
"Profile %d changed, rewriting\n", profile->index);
rc = steelseries_write_profile(profile);
if (rc)
return rc;
/* persist the current settings on the device */
buf[0] = STEELSERIES_ID_SAVE;
rc = ratbag_hidraw_output_report(device, buf, sizeof(buf));
if (rc != sizeof(buf))
return rc;
}
return 0;
}
static void
steelseries_remove(struct ratbag_device *device)
{
ratbag_close_hidraw(device);
}
struct ratbag_driver steelseries_driver = {
.name = "SteelSeries",
.id = "steelseries",
.probe = steelseries_probe,
.remove = steelseries_remove,
.commit = steelseries_commit,
};