2012-02-22 05:10:06 +04:00
/*
2014-05-20 22:28:00 +04:00
* HID driver for Saitek devices .
*
* PS1000 ( USB gamepad ) :
2012-02-22 05:10:06 +04:00
* Fixes the HID report descriptor by removing a non - existent axis and
* clearing the constant bit on the input reports for buttons and d - pad .
* ( This module is based on " hid-ortek " . )
* Copyright ( c ) 2012 Andreas Hübner
2014-05-20 22:28:00 +04:00
*
2014-11-05 17:13:51 +03:00
* R . A . T .7 , R . A . T .9 , M . M . O .7 ( USB gaming mice ) :
2014-05-20 22:28:00 +04:00
* Fixes the mode button which cycles through three constantly pressed
* buttons . All three press events are mapped to one button and the
* missing release event is generated immediately .
*
2012-02-22 05:10:06 +04:00
*/
/*
* 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 .
*/
# include <linux/device.h>
# include <linux/hid.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include "hid-ids.h"
2014-05-20 22:28:00 +04:00
# define SAITEK_FIX_PS1000 0x0001
# define SAITEK_RELEASE_MODE_RAT7 0x0002
# define SAITEK_RELEASE_MODE_MMO7 0x0004
struct saitek_sc {
unsigned long quirks ;
int mode ;
} ;
static int saitek_probe ( struct hid_device * hdev ,
const struct hid_device_id * id )
{
unsigned long quirks = id - > driver_data ;
struct saitek_sc * ssc ;
int ret ;
ssc = devm_kzalloc ( & hdev - > dev , sizeof ( * ssc ) , GFP_KERNEL ) ;
if ( ssc = = NULL ) {
hid_err ( hdev , " can't alloc saitek descriptor \n " ) ;
return - ENOMEM ;
}
ssc - > quirks = quirks ;
ssc - > mode = - 1 ;
hid_set_drvdata ( hdev , ssc ) ;
ret = hid_parse ( hdev ) ;
if ( ret ) {
hid_err ( hdev , " parse failed \n " ) ;
return ret ;
}
ret = hid_hw_start ( hdev , HID_CONNECT_DEFAULT ) ;
if ( ret ) {
hid_err ( hdev , " hw start failed \n " ) ;
return ret ;
}
return 0 ;
}
2012-02-22 05:10:06 +04:00
static __u8 * saitek_report_fixup ( struct hid_device * hdev , __u8 * rdesc ,
unsigned int * rsize )
{
2014-05-20 22:28:00 +04:00
struct saitek_sc * ssc = hid_get_drvdata ( hdev ) ;
if ( ( ssc - > quirks & SAITEK_FIX_PS1000 ) & & * rsize = = 137 & &
rdesc [ 20 ] = = 0x09 & & rdesc [ 21 ] = = 0x33 & &
rdesc [ 94 ] = = 0x81 & & rdesc [ 95 ] = = 0x03 & &
rdesc [ 110 ] = = 0x81 & & rdesc [ 111 ] = = 0x03 ) {
2012-02-22 05:10:06 +04:00
hid_info ( hdev , " Fixing up Saitek PS1000 report descriptor \n " ) ;
/* convert spurious axis to a "noop" Logical Minimum (0) */
rdesc [ 20 ] = 0x15 ;
rdesc [ 21 ] = 0x00 ;
/* clear constant bit on buttons and d-pad */
rdesc [ 95 ] = 0x02 ;
rdesc [ 111 ] = 0x02 ;
}
return rdesc ;
}
2014-05-20 22:28:00 +04:00
static int saitek_raw_event ( struct hid_device * hdev ,
struct hid_report * report , u8 * raw_data , int size )
{
struct saitek_sc * ssc = hid_get_drvdata ( hdev ) ;
if ( ssc - > quirks & SAITEK_RELEASE_MODE_RAT7 & & size = = 7 ) {
/* R.A.T.7 uses bits 13, 14, 15 for the mode */
int mode = - 1 ;
if ( raw_data [ 1 ] & 0x01 )
mode = 0 ;
else if ( raw_data [ 1 ] & 0x02 )
mode = 1 ;
else if ( raw_data [ 1 ] & 0x04 )
mode = 2 ;
/* clear mode bits */
raw_data [ 1 ] & = ~ 0x07 ;
if ( mode ! = ssc - > mode ) {
hid_dbg ( hdev , " entered mode %d \n " , mode ) ;
if ( ssc - > mode ! = - 1 ) {
/* use bit 13 as the mode button */
raw_data [ 1 ] | = 0x04 ;
}
ssc - > mode = mode ;
}
} else if ( ssc - > quirks & SAITEK_RELEASE_MODE_MMO7 & & size = = 8 ) {
/* M.M.O.7 uses bits 8, 22, 23 for the mode */
int mode = - 1 ;
if ( raw_data [ 1 ] & 0x80 )
mode = 0 ;
else if ( raw_data [ 2 ] & 0x01 )
mode = 1 ;
else if ( raw_data [ 2 ] & 0x02 )
mode = 2 ;
/* clear mode bits */
raw_data [ 1 ] & = ~ 0x80 ;
raw_data [ 2 ] & = ~ 0x03 ;
if ( mode ! = ssc - > mode ) {
hid_dbg ( hdev , " entered mode %d \n " , mode ) ;
if ( ssc - > mode ! = - 1 ) {
/* use bit 8 as the mode button, bits 22
* and 23 do not represent buttons
* according to the HID report descriptor
*/
raw_data [ 1 ] | = 0x80 ;
}
ssc - > mode = mode ;
}
}
return 0 ;
}
static int saitek_event ( struct hid_device * hdev , struct hid_field * field ,
struct hid_usage * usage , __s32 value )
{
struct saitek_sc * ssc = hid_get_drvdata ( hdev ) ;
struct input_dev * input = field - > hidinput - > input ;
if ( usage - > type = = EV_KEY & & value & &
( ( ( ssc - > quirks & SAITEK_RELEASE_MODE_RAT7 ) & &
usage - > code - BTN_MOUSE = = 10 ) | |
( ( ssc - > quirks & SAITEK_RELEASE_MODE_MMO7 ) & &
usage - > code - BTN_MOUSE = = 15 ) ) ) {
input_report_key ( input , usage - > code , 1 ) ;
/* report missing release event */
input_report_key ( input , usage - > code , 0 ) ;
return 1 ;
}
return 0 ;
}
2012-02-22 05:10:06 +04:00
static const struct hid_device_id saitek_devices [ ] = {
2014-05-20 22:28:00 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_SAITEK , USB_DEVICE_ID_SAITEK_PS1000 ) ,
. driver_data = SAITEK_FIX_PS1000 } ,
2015-09-03 18:49:26 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_MADCATZ , USB_DEVICE_ID_MADCATZ_RAT5 ) ,
. driver_data = SAITEK_RELEASE_MODE_RAT7 } ,
2015-01-29 16:58:09 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_SAITEK , USB_DEVICE_ID_SAITEK_RAT7_OLD ) ,
. driver_data = SAITEK_RELEASE_MODE_RAT7 } ,
2014-05-20 22:28:00 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_SAITEK , USB_DEVICE_ID_SAITEK_RAT7 ) ,
. driver_data = SAITEK_RELEASE_MODE_RAT7 } ,
2016-07-30 14:53:39 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_SAITEK , USB_DEVICE_ID_SAITEK_RAT9 ) ,
. driver_data = SAITEK_RELEASE_MODE_RAT7 } ,
2014-11-05 17:13:51 +03:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_MADCATZ , USB_DEVICE_ID_MADCATZ_RAT9 ) ,
. driver_data = SAITEK_RELEASE_MODE_RAT7 } ,
2014-05-20 22:28:00 +04:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_SAITEK , USB_DEVICE_ID_SAITEK_MMO7 ) ,
. driver_data = SAITEK_RELEASE_MODE_MMO7 } ,
2012-02-22 05:10:06 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( hid , saitek_devices ) ;
static struct hid_driver saitek_driver = {
. name = " saitek " ,
. id_table = saitek_devices ,
2014-05-20 22:28:00 +04:00
. probe = saitek_probe ,
. report_fixup = saitek_report_fixup ,
. raw_event = saitek_raw_event ,
. event = saitek_event ,
2012-02-22 05:10:06 +04:00
} ;
2012-12-18 02:28:26 +04:00
module_hid_driver ( saitek_driver ) ;
2012-02-22 05:10:06 +04:00
MODULE_LICENSE ( " GPL " ) ;