/*
* Roccat Pyra driver for Linux
*
* Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
/*
* Roccat Pyra is a mobile gamer mouse which comes in wired and wireless
* variant. Wireless variant is not tested.
* Userland tools can be found at http://sourceforge.net/projects/roccat
*/
#include <linux/device.h>
#include <linux/input.h>
#include <linux/hid.h>
#include <linux/usb.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "hid-ids.h"
#include "hid-roccat.h"
#include "hid-roccat-pyra.h"
static void profile_activated(struct pyra_device *pyra,
unsigned int new_profile)
{
pyra->actual_profile = new_profile;
pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi;
}
static int pyra_send_control(struct usb_device *usb_dev, int value,
enum pyra_control_requests request)
{
int len;
struct pyra_control control;
if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
(value < 0 || value > 4))
return -EINVAL;
control.command = PYRA_COMMAND_CONTROL;
control.value = value;
control.request = request;
len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
USB_REQ_SET_CONFIGURATION,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
PYRA_USB_COMMAND_CONTROL, 0, (char *)&control,
sizeof(struct pyra_control),
USB_CTRL_SET_TIMEOUT);
if (len != sizeof(struct pyra_control))
return len;
return 0;
}
static int pyra_receive_control_status(struct usb_device *usb_dev)
{
int len;
struct pyra_control control;
do {
msleep(10);
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
USB_REQ_CLEAR_FEATURE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE |
USB_DIR_IN,
PYRA_USB_COMMAND_CONTROL, 0, (char *)&control,
sizeof(struct pyra_control),
USB_CTRL_SET_TIMEOUT);
/* requested too early, try again */
} while (len == -EPROTO);
if (len == sizeof(struct pyra_control) &&
control.command == PYRA_COMMAND_CONTROL &&
control.request == PYRA_CONTROL_REQUEST_STATUS &&
control.value == 1)
return 0;
else {
dev_err(&usb_dev->dev, "receive control status: "
"unknown response 0x%x 0x%x\n",
control.request, control.value);
return -EINVAL;
}
}
static int pyra_get_profile_settings(struct usb_device *usb_dev,
struct pyra_profile_settings *buf, int number)
{
int retval;
retval = pyra_send_control(usb_dev, number,
PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
if (retval)
return retval;
retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
USB_REQ_CLEAR_FEATURE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)buf,
sizeof(struct pyra_profile_settings),
USB_CTRL_SET_TIMEOUT);
if (retval != sizeof(struct pyra_profile_settings))
return retval;
return 0;
}
static int pyra_get_profile_buttons(struct usb_device *usb_dev,
struct pyra_profile_buttons *buf, int number)
{
int retval;
retval = pyra_send_control(usb_dev, number,
PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
if (retval)
return retval;
retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
USB_REQ_CLEAR_FEATURE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buf,
sizeof(struct pyra_profile_buttons),
USB_CTRL_SET_TIMEOUT);
if (retval != sizeof(struct pyra_profile_buttons))
return retval;
return 0;
}
static int pyra_get_settings(struct usb_device *usb_dev,
struct pyra_settings *buf)
{
int len;
len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
USB_REQ_CLEAR_FEATURE,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
PYRA_USB_COMMAND_SETTINGS, 0, buf,
sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT);
if (len != sizeof(struct pyra_settings))
return -EIO;
return 0;
}
static int pyra_get_info(struct usb_device