2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-11-24 20:46:24 +04:00
/*
* Roccat Isku driver for Linux
*
* Copyright ( c ) 2011 Stefan Achatz < erazor_de @ users . sourceforge . net >
*/
/*
*/
/*
* Roccat Isku is a gamer keyboard with macro keys that can be configured in
* 5 profiles .
*/
# 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-isku.h"
static void isku_profile_activated ( struct isku_device * isku , uint new_profile )
{
isku - > actual_profile = new_profile ;
}
static int isku_receive ( struct usb_device * usb_dev , uint command ,
void * buf , uint size )
{
2012-05-21 00:45:04 +04:00
return roccat_common2_receive ( usb_dev , command , buf , size ) ;
2011-11-24 20:46:24 +04:00
}
static int isku_get_actual_profile ( struct usb_device * usb_dev )
{
struct isku_actual_profile buf ;
int retval ;
retval = isku_receive ( usb_dev , ISKU_COMMAND_ACTUAL_PROFILE ,
& buf , sizeof ( struct isku_actual_profile ) ) ;
return retval ? retval : buf . actual_profile ;
}
static int isku_set_actual_profile ( struct usb_device * usb_dev , int new_profile )
{
struct isku_actual_profile buf ;
buf . command = ISKU_COMMAND_ACTUAL_PROFILE ;
buf . size = sizeof ( struct isku_actual_profile ) ;
buf . actual_profile = new_profile ;
2012-05-21 00:45:04 +04:00
return roccat_common2_send_with_status ( usb_dev ,
2012-05-21 00:44:59 +04:00
ISKU_COMMAND_ACTUAL_PROFILE , & buf ,
2011-11-24 20:46:24 +04:00
sizeof ( struct isku_actual_profile ) ) ;
}
static ssize_t isku_sysfs_show_actual_profile ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct isku_device * isku =
hid_get_drvdata ( dev_get_drvdata ( dev - > parent - > parent ) ) ;
return snprintf ( buf , PAGE_SIZE , " %d \n " , isku - > actual_profile ) ;
}
static ssize_t isku_sysfs_set_actual_profile ( struct device * dev ,
struct device_attribute * attr , char const * buf , size_t size )
{
struct isku_device * isku ;
struct usb_device * usb_dev ;
unsigned long profile ;
int retval ;
struct isku_roccat_report roccat_report ;
dev = dev - > parent - > parent ;
isku = hid_get_drvdata ( dev_get_drvdata ( dev ) ) ;
usb_dev = interface_to_usbdev ( to_usb_interface ( dev ) ) ;
2013-07-19 10:53:16 +04:00
retval = kstrtoul ( buf , 10 , & profile ) ;
2011-11-24 20:46:24 +04:00
if ( retval )
return retval ;
if ( profile > 4 )
return - EINVAL ;
mutex_lock ( & isku - > isku_lock ) ;
retval = isku_set_actual_profile ( usb_dev , profile ) ;
if ( retval ) {
mutex_unlock ( & isku - > isku_lock ) ;
return retval ;
}
isku_profile_activated ( isku , profile ) ;
roccat_report . event = ISKU_REPORT_BUTTON_EVENT_PROFILE ;
roccat_report . data1 = profile + 1 ;
roccat_report . data2 = 0 ;
roccat_report . profile = profile + 1 ;
roccat_report_event ( isku - > chrdev_minor , ( uint8_t const * ) & roccat_report ) ;
mutex_unlock ( & isku - > isku_lock ) ;
return size ;
}
2013-07-25 02:05:11 +04:00
static DEVICE_ATTR ( actual_profile , 0660 , isku_sysfs_show_actual_profile ,
isku_sysfs_set_actual_profile ) ;
2011-11-24 20:46:24 +04:00
2013-07-25 02:05:11 +04:00
static struct attribute * isku_attrs [ ] = {
& dev_attr_actual_profile . attr ,
NULL ,
2011-11-24 20:46:24 +04:00
} ;
static ssize_t isku_sysfs_read ( struct file * fp , struct kobject * kobj ,
char * buf , loff_t off , size_t count ,
size_t real_size , uint command )
{
2015-12-27 12:25:24 +03:00
struct device * dev = kobj_to_dev ( kobj ) - > parent - > parent ;
2011-11-24 20:46:24 +04:00
struct isku_device * isku = hid_get_drvdata ( dev_get_drvdata ( dev ) ) ;
struct usb_device * usb_dev = interface_to_usbdev ( to_usb_interface ( dev ) ) ;
int retval ;
if ( off > = real_size )
return 0 ;
2013-03-10 15:33:02 +04:00
if ( off ! = 0 | | count > real_size )
2011-11-24 20:46:24 +04:00
return - EINVAL ;
mutex_lock ( & isku - > isku_lock ) ;
2013-03-10 15:33:02 +04:00
retval = isku_receive ( usb_dev , command , buf , count ) ;
2011-11-24 20:46:24 +04:00
mutex_unlock ( & isku - > isku_lock ) ;
2013-03-10 15:33:02 +04:00
return retval ? retval : count ;
2011-11-24 20:46:24 +04:00
}
static ssize_t isku_sysfs_write ( struct file * fp , struct kobject * kobj ,
void const * buf , loff_t off , size_t count ,
size_t real_size , uint command )
{
2015-12-27 12:25:24 +03:00
struct device * dev = kobj_to_dev ( kobj ) - > parent - > parent ;
2011-11-24 20:46:24 +04:00
struct isku_device * isku = hid_get_drvdata ( dev_get_drvdata ( dev ) ) ;
struct usb_device * usb_dev = interface_to_usbdev ( to_usb_interface ( dev ) ) ;
int retval ;
2013-03-10 15:33:02 +04:00
if ( off ! = 0 | | count > real_size )
2011-11-24 20:46:24 +04:00
return - EINVAL ;
mutex_lock ( & isku - > isku_lock ) ;
2012-05-21 00:45:04 +04:00
retval = roccat_common2_send_with_status ( usb_dev , command ,
2013-03-10 15:33:02 +04:00
( void * ) buf , count ) ;
2011-11-24 20:46:24 +04:00
mutex_unlock ( & isku - > isku_lock ) ;
2013-03-10 15:33:02 +04:00
return retval ? retval : count ;
2011-11-24 20:46:24 +04:00
}
# define ISKU_SYSFS_W(thingy, THINGY) \
static ssize_t isku_sysfs_write_ # # thingy ( struct file * fp , struct kobject * kobj , \
struct bin_attribute * attr , char * buf , \
loff_t off , size_t count ) \
{ \
return isku_sysfs_write ( fp , kobj , buf , off , count , \
2012-11-11 09:21:02 +04:00
ISKU_SIZE_ # # THINGY , ISKU_COMMAND_ # # THINGY ) ; \
2011-11-24 20:46:24 +04:00
}
# define ISKU_SYSFS_R(thingy, THINGY) \
static ssize_t isku_sysfs_read_ # # thingy ( struct file * fp , struct kobject * kobj , \
struct bin_attribute * attr , char * buf , \
loff_t off , size_t count ) \
{ \
return isku_sysfs_read ( fp , kobj , buf , off , count , \
2012-11-11 09:21:02 +04:00
ISKU_SIZE_ # # THINGY , ISKU_COMMAND_ # # THINGY ) ; \
2011-11-24 20:46:24 +04:00
}
# define ISKU_SYSFS_RW(thingy, THINGY) \
ISKU_SYSFS_R ( thingy , THINGY ) \
ISKU_SYSFS_W ( thingy , THINGY )
2012-11-11 09:21:02 +04:00
# define ISKU_BIN_ATTR_RW(thingy, THINGY) \
2013-08-20 08:40:26 +04:00
ISKU_SYSFS_RW ( thingy , THINGY ) ; \
static struct bin_attribute bin_attr_ # # thingy = { \
2011-11-24 20:46:24 +04:00
. attr = { . name = # thingy , . mode = 0660 } , \
2012-11-11 09:21:02 +04:00
. size = ISKU_SIZE_ # # THINGY , \
2011-11-24 20:46:24 +04:00
. read = isku_sysfs_read_ # # thingy , \
. write = isku_sysfs_write_ # # thingy \
}
2012-11-11 09:21:02 +04:00
# define ISKU_BIN_ATTR_R(thingy, THINGY) \
2013-08-20 08:40:26 +04:00
ISKU_SYSFS_R ( thingy , THINGY ) ; \
static struct bin_attribute bin_attr_ # # thingy = { \
2011-11-24 20:46:24 +04:00
. attr = { . name = # thingy , . mode = 0440 } , \
2012-11-11 09:21:02 +04:00
. size = ISKU_SIZE_ # # THINGY , \
2011-11-24 20:46:24 +04:00
. read = isku_sysfs_read_ # # thingy , \
}
2012-11-11 09:21:02 +04:00
# define ISKU_BIN_ATTR_W(thingy, THINGY) \
2013-08-20 08:40:26 +04:00
ISKU_SYSFS_W ( thingy , THINGY ) ; \
static struct bin_attribute bin_attr_ # # thingy = { \
2011-11-24 20:46:24 +04:00
. attr = { . name = # thingy , . mode = 0220 } , \
2012-11-11 09:21:02 +04:00
. size = ISKU_SIZE_ # # THINGY , \
2011-11-24 20:46:24 +04:00
. write = isku_sysfs_write_ # # thingy \
}
2013-08-20 08:40:26 +04:00
ISKU_BIN_ATTR_RW ( macro , MACRO ) ;
ISKU_BIN_ATTR_RW ( keys_function , KEYS_FUNCTION ) ;
ISKU_BIN_ATTR_RW ( keys_easyzone , KEYS_EASYZONE ) ;
ISKU_BIN_ATTR_RW ( keys_media , KEYS_MEDIA ) ;
ISKU_BIN_ATTR_RW ( keys_thumbster , KEYS_THUMBSTER ) ;
ISKU_BIN_ATTR_RW ( keys_macro , KEYS_MACRO ) ;
ISKU_BIN_ATTR_RW ( keys_capslock , KEYS_CAPSLOCK ) ;
ISKU_BIN_ATTR_RW ( light , LIGHT ) ;
ISKU_BIN_ATTR_RW ( key_mask , KEY_MASK ) ;
ISKU_BIN_ATTR_RW ( last_set , LAST_SET ) ;
ISKU_BIN_ATTR_W ( talk , TALK ) ;
ISKU_BIN_ATTR_W ( talkfx , TALKFX ) ;
ISKU_BIN_ATTR_W ( control , CONTROL ) ;
ISKU_BIN_ATTR_W ( reset , RESET ) ;
ISKU_BIN_ATTR_R ( info , INFO ) ;
static struct bin_attribute * isku_bin_attributes [ ] = {
& bin_attr_macro ,
& bin_attr_keys_function ,
& bin_attr_keys_easyzone ,
& bin_attr_keys_media ,
& bin_attr_keys_thumbster ,
& bin_attr_keys_macro ,
& bin_attr_keys_capslock ,
& bin_attr_light ,
& bin_attr_key_mask ,
& bin_attr_last_set ,
& bin_attr_talk ,
& bin_attr_talkfx ,
& bin_attr_control ,
& bin_attr_reset ,
& bin_attr_info ,
NULL ,
} ;
static const struct attribute_group isku_group = {
. attrs = isku_attrs ,
. bin_attrs = isku_bin_attributes ,
} ;
static const struct attribute_group * isku_groups [ ] = {
& isku_group ,
NULL ,
2011-11-24 20:46:24 +04:00
} ;
2023-06-20 21:31:42 +03:00
static const struct class isku_class = {
. name = " isku " ,
. dev_groups = isku_groups ,
} ;
2011-11-24 20:46:24 +04:00
static int isku_init_isku_device_struct ( struct usb_device * usb_dev ,
struct isku_device * isku )
{
int retval ;
mutex_init ( & isku - > isku_lock ) ;
retval = isku_get_actual_profile ( usb_dev ) ;
if ( retval < 0 )
return retval ;
isku_profile_activated ( isku , retval ) ;
return 0 ;
}
static int isku_init_specials ( struct hid_device * hdev )
{
struct usb_interface * intf = to_usb_interface ( hdev - > dev . parent ) ;
struct usb_device * usb_dev = interface_to_usbdev ( intf ) ;
struct isku_device * isku ;
int retval ;
if ( intf - > cur_altsetting - > desc . bInterfaceProtocol
! = ISKU_USB_INTERFACE_PROTOCOL ) {
hid_set_drvdata ( hdev , NULL ) ;
return 0 ;
}
isku = kzalloc ( sizeof ( * isku ) , GFP_KERNEL ) ;
if ( ! isku ) {
hid_err ( hdev , " can't alloc device descriptor \n " ) ;
return - ENOMEM ;
}
hid_set_drvdata ( hdev , isku ) ;
retval = isku_init_isku_device_struct ( usb_dev , isku ) ;
if ( retval ) {
hid_err ( hdev , " couldn't init struct isku_device \n " ) ;
goto exit_free ;
}
2023-06-20 21:31:42 +03:00
retval = roccat_connect ( & isku_class , hdev ,
2011-11-24 20:46:24 +04:00
sizeof ( struct isku_roccat_report ) ) ;
if ( retval < 0 ) {
hid_err ( hdev , " couldn't init char dev \n " ) ;
} else {
isku - > chrdev_minor = retval ;
isku - > roccat_claimed = 1 ;
}
return 0 ;
exit_free :
kfree ( isku ) ;
return retval ;
}
static void isku_remove_specials ( struct hid_device * hdev )
{
struct usb_interface * intf = to_usb_interface ( hdev - > dev . parent ) ;
struct isku_device * isku ;
if ( intf - > cur_altsetting - > desc . bInterfaceProtocol
! = ISKU_USB_INTERFACE_PROTOCOL )
return ;
isku = hid_get_drvdata ( hdev ) ;
if ( isku - > roccat_claimed )
roccat_disconnect ( isku - > chrdev_minor ) ;
kfree ( isku ) ;
}
static int isku_probe ( struct hid_device * hdev ,
const struct hid_device_id * id )
{
int retval ;
2021-12-01 21:35:03 +03:00
if ( ! hid_is_usb ( hdev ) )
return - EINVAL ;
2011-11-24 20:46:24 +04:00
retval = hid_parse ( hdev ) ;
if ( retval ) {
hid_err ( hdev , " parse failed \n " ) ;
goto exit ;
}
retval = hid_hw_start ( hdev , HID_CONNECT_DEFAULT ) ;
if ( retval ) {
hid_err ( hdev , " hw start failed \n " ) ;
goto exit ;
}
retval = isku_init_specials ( hdev ) ;
if ( retval ) {
hid_err ( hdev , " couldn't install keyboard \n " ) ;
goto exit_stop ;
}
return 0 ;
exit_stop :
hid_hw_stop ( hdev ) ;
exit :
return retval ;
}
static void isku_remove ( struct hid_device * hdev )
{
isku_remove_specials ( hdev ) ;
hid_hw_stop ( hdev ) ;
}
static void isku_keep_values_up_to_date ( struct isku_device * isku ,
u8 const * data )
{
struct isku_report_button const * button_report ;
switch ( data [ 0 ] ) {
case ISKU_REPORT_NUMBER_BUTTON :
button_report = ( struct isku_report_button const * ) data ;
switch ( button_report - > event ) {
case ISKU_REPORT_BUTTON_EVENT_PROFILE :
isku_profile_activated ( isku , button_report - > data1 - 1 ) ;
break ;
}
break ;
}
}
static void isku_report_to_chrdev ( struct isku_device const * isku ,
u8 const * data )
{
struct isku_roccat_report roccat_report ;
struct isku_report_button const * button_report ;
if ( data [ 0 ] ! = ISKU_REPORT_NUMBER_BUTTON )
return ;
button_report = ( struct isku_report_button const * ) data ;
roccat_report . event = button_report - > event ;
roccat_report . data1 = button_report - > data1 ;
roccat_report . data2 = button_report - > data2 ;
roccat_report . profile = isku - > actual_profile + 1 ;
roccat_report_event ( isku - > chrdev_minor ,
( uint8_t const * ) & roccat_report ) ;
}
static int isku_raw_event ( struct hid_device * hdev ,
struct hid_report * report , u8 * data , int size )
{
struct usb_interface * intf = to_usb_interface ( hdev - > dev . parent ) ;
struct isku_device * isku = hid_get_drvdata ( hdev ) ;
if ( intf - > cur_altsetting - > desc . bInterfaceProtocol
! = ISKU_USB_INTERFACE_PROTOCOL )
return 0 ;
if ( isku = = NULL )
return 0 ;
isku_keep_values_up_to_date ( isku , data ) ;
if ( isku - > roccat_claimed )
isku_report_to_chrdev ( isku , data ) ;
return 0 ;
}
static const struct hid_device_id isku_devices [ ] = {
{ HID_USB_DEVICE ( USB_VENDOR_ID_ROCCAT , USB_DEVICE_ID_ROCCAT_ISKU ) } ,
2013-03-10 15:33:02 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_ROCCAT , USB_DEVICE_ID_ROCCAT_ISKUFX ) } ,
2011-11-24 20:46:24 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( hid , isku_devices ) ;
static struct hid_driver isku_driver = {
. name = " isku " ,
. id_table = isku_devices ,
. probe = isku_probe ,
. remove = isku_remove ,
. raw_event = isku_raw_event
} ;
static int __init isku_init ( void )
{
int retval ;
2023-06-20 21:31:42 +03:00
retval = class_register ( & isku_class ) ;
if ( retval )
return retval ;
2011-11-24 20:46:24 +04:00
retval = hid_register_driver ( & isku_driver ) ;
if ( retval )
2023-06-20 21:31:42 +03:00
class_unregister ( & isku_class ) ;
2011-11-24 20:46:24 +04:00
return retval ;
}
static void __exit isku_exit ( void )
{
hid_unregister_driver ( & isku_driver ) ;
2023-06-20 21:31:42 +03:00
class_unregister ( & isku_class ) ;
2011-11-24 20:46:24 +04:00
}
module_init ( isku_init ) ;
module_exit ( isku_exit ) ;
MODULE_AUTHOR ( " Stefan Achatz " ) ;
2013-03-10 15:33:02 +04:00
MODULE_DESCRIPTION ( " USB Roccat Isku/FX driver " ) ;
2011-11-24 20:46:24 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;