diff --git a/data/devices/steelseries-rival-310.device b/data/devices/steelseries-rival-310.device
new file mode 100644
index 00000000..01e08127
--- /dev/null
+++ b/data/devices/steelseries-rival-310.device
@@ -0,0 +1,5 @@
+[Device]
+Name=SteelSeriesRival 310
+DeviceMatch=usb:1038:1720
+Driver=steelseries
+Svg=steelseries-rival310.svg
diff --git a/data/gnome/steelseries-rival310.svg b/data/gnome/steelseries-rival310.svg
new file mode 100644
index 00000000..2a750923
--- /dev/null
+++ b/data/gnome/steelseries-rival310.svg
@@ -0,0 +1,443 @@
+
+
+
+
diff --git a/meson.build b/meson.build
index 61eef95a..4fe0989c 100644
--- a/meson.build
+++ b/meson.build
@@ -132,6 +132,7 @@ src_libratbag = [
'src/driver-logitech-g300.c',
'src/driver-roccat.c',
'src/driver-gskill.c',
+ 'src/driver-steelseries.c',
'src/driver-test.c',
'src/libratbag.c',
'src/libratbag.h',
@@ -270,6 +271,7 @@ data_files = files(
'data/devices/logitech-T650.device',
'data/devices/logitech-Wireless-Touchpad.device',
'data/devices/roccat-kone-xtd.device',
+ 'data/devices/steelseries-rival-310.device',
)
install_data(data_files,
@@ -308,6 +310,7 @@ gnome_svg_files = files(
'data/gnome/logitech-g9.svg',
'data/gnome/logitech-g900.svg',
'data/gnome/roccat-kone-xtd.svg',
+ 'data/gnome/steelseries-rival310.svg',
)
install_data(gnome_svg_files,
install_dir : join_paths(get_option('datadir'), 'libratbag', 'gnome'))
diff --git a/src/driver-steelseries.c b/src/driver-steelseries.c
new file mode 100644
index 00000000..7cd284cc
--- /dev/null
+++ b/src/driver-steelseries.c
@@ -0,0 +1,273 @@
+/*
+ * 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
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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,
+};
diff --git a/src/libratbag-data.c b/src/libratbag-data.c
index 2e1a3c38..b1a96c3f 100644
--- a/src/libratbag-data.c
+++ b/src/libratbag-data.c
@@ -46,7 +46,8 @@ enum driver {
ROCCAT,
ETEKCITY,
GSKILL,
- LOGITECH_G300
+ LOGITECH_G300,
+ STEELSERIES,
};
struct data_hidpp20 {
@@ -161,6 +162,7 @@ static const struct driver_map {
{ ETEKCITY, "etekcity", NULL},
{ GSKILL, "gskill", NULL },
{ LOGITECH_G300, "logitech_g300", NULL},
+ { STEELSERIES, "steelseries", NULL },
};
const char *
diff --git a/src/libratbag-private.h b/src/libratbag-private.h
index fb96c688..8181a4da 100644
--- a/src/libratbag-private.h
+++ b/src/libratbag-private.h
@@ -578,6 +578,7 @@ struct ratbag_driver hidpp10_driver;
struct ratbag_driver logitech_g300_driver;
struct ratbag_driver roccat_driver;
struct ratbag_driver gskill_driver;
+struct ratbag_driver steelseries_driver;
struct ratbag_device*
ratbag_device_new(struct ratbag *ratbag, struct udev_device *udev_device,
diff --git a/src/libratbag.c b/src/libratbag.c
index 65185110..564b130c 100644
--- a/src/libratbag.c
+++ b/src/libratbag.c
@@ -585,6 +585,7 @@ ratbag_create_context(const struct ratbag_interface *interface,
ratbag_register_driver(ratbag, &logitech_g300_driver);
ratbag_register_driver(ratbag, &roccat_driver);
ratbag_register_driver(ratbag, &gskill_driver);
+ ratbag_register_driver(ratbag, &steelseries_driver);
return ratbag;
}