// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Roccat Kone driver for Linux
*
* Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
*/
/*
*/
/*
* Roccat Kone is a gamer mouse which consists of a mouse part and a keyboard
* part. The keyboard part enables the mouse to execute stored macros with mixed
* key- and button-events.
*
* TODO implement on-the-fly polling-rate change
* The windows driver has the ability to change the polling rate of the
* device on the press of a mousebutton.
* Is it possible to remove and reinstall the urb in raw-event- or any
* other handler, or to defer this action to be executed somewhere else?
*
* TODO is it possible to overwrite group for sysfs attributes via udev?
*/
#include <linux/device.h>
#include <linux/input.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/hid-roccat.h>
#include "hid-ids.h"
#include "hid-roccat-common.h"
#include "hid-roccat-kone.h"
static uint profile_numbers[5] = {0, 1, 2, 3, 4};
static void kone_profile_activated(struct kone_device *kone, uint new_profile)
{
kone->actual_profile = new_profile;
kone->actual_dpi = kone->profiles[new_profile - 1].startup_dpi;
}
static void kone_profile_report(struct kone_device *kone, uint new_profile)
{
struct kone_roccat_report roccat_report;
roccat_report.event = kone_mouse_event_switch_profile;
roccat_report.value = new_profile;
roccat_report.key = 0;
roccat_report_event(kone->chrdev_minor, (uint8_t *)&roccat_report);
}
static int kone_receive(struct usb_device *usb_dev, uint usb_command,
void *data, uint size)
{
char *buf;
int len;
buf = kmalloc(size, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
HID_REQ_GET_REPORT,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT);
memcpy(data, buf, size);
kfree(buf);
return ((len < 0) ? len : ((len != size) ? -EIO : 0));
}
static int kone_send(struct usb_device *usb_dev, uint usb_command,
void const *data, uint size)
{
char *buf;
int len;
buf = kmemdup(data, size, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
HID_REQ_SET_REPORT,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT);
kfree(buf);
return ((len < 0) ? len : ((len != size) ? -EIO : 0));
}
static void kone_set_settings_checksum(struct kone_settings *settings)
{
uint16_t checksum = 0;
unsigned char *address = (unsigned char *)settings;
int i;
for (i = 0; i < sizeof(struct kone_settings) - 2; ++i, ++address)
checksum += *address;
settings->checksum = cpu_to_le16(checksum);
}
/*
* Checks success after writing data to mouse
* On success returns 0
* On failure returns errno
*/
static int kone_check_write(struct usb_device *usb_dev)
{
int retval;
uint8_t data;
do {
/*
* Mouse needs 50 msecs until it says ok, but there are
* 30 more msecs needed for next write to work.
*/
msleep(80);
retval = kone_receive(usb_dev,
kone_command_confirm_write, &data, 1);
if (retval)
return retval;
/*
* value of 3 seems to mean something like
* "not finished yet, but it looks good"
* So check again after a moment.
*/
} while (data == 3);
if (data == 1) /* everything alright */
return 0;
/* unknown answer */
dev_err(&usb_dev->dev, "got retval %d when checking write\n", data);
return -EIO;
}
/*
* Reads settings from mouse and stores it in @buf
* On success returns 0
* On failure returns errno
*/
static int kone_get_settings(struct usb_device *usb_dev,
struct kone_settings *buf)
{
return kone_receive(usb_dev, kone_command_settings, buf,
sizeof(struct kone_settings));
}
/*
* Writes settings from @buf to mouse
* On success returns 0
* On failure returns errno
*/
static int kone_set_settings(struct usb_device *usb_dev,
struct kone_settings const *settings)
{
int retval;
retval = kone_send(usb_dev, kone_command_settings,
settings, sizeof(struct kone_settings));
if (retval)
return retval;
return kone_check_write(usb_dev);
}
/*
* Reads profile data from mouse and stores it in @buf
* @number: profile number to read
* On success returns 0
* On failure returns errno
*/
static int kone_get_profile(struct usb_device