2006-07-19 01:44:17 -04:00
/*
* Force feedback support for Zeroplus based devices
*
* Copyright ( c ) 2005 , 2006 Anssi Hannula < anssi . hannula @ gmail . com >
*/
/*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
2008-09-18 12:23:32 +02:00
# include <linux/hid.h>
2006-07-19 01:44:17 -04:00
# include <linux/input.h>
# include <linux/usb.h>
2008-09-18 12:23:32 +02:00
# include "hid-ids.h"
2009-05-15 15:46:44 +02:00
# ifdef CONFIG_ZEROPLUS_FF
2008-09-18 12:23:32 +02:00
# include "usbhid/usbhid.h"
2006-07-19 01:44:17 -04:00
struct zpff_device {
struct hid_report * report ;
} ;
2008-09-18 12:23:32 +02:00
static int zpff_play ( struct input_dev * dev , void * data ,
2006-07-19 01:44:17 -04:00
struct ff_effect * effect )
{
2007-05-09 10:17:31 +02:00
struct hid_device * hid = input_get_drvdata ( dev ) ;
2006-07-19 01:44:17 -04:00
struct zpff_device * zpff = data ;
int left , right ;
/*
* The following is specified the other way around in the Zeroplus
* datasheet but the order below is correct for the XFX Executioner ;
* however it is possible that the XFX Executioner is an exception
*/
left = effect - > u . rumble . strong_magnitude ;
right = effect - > u . rumble . weak_magnitude ;
2007-05-30 15:07:13 +02:00
dbg_hid ( " called with 0x%04x 0x%04x \n " , left , right ) ;
2006-07-19 01:44:17 -04:00
left = left * 0x7f / 0xffff ;
right = right * 0x7f / 0xffff ;
zpff - > report - > field [ 2 ] - > value [ 0 ] = left ;
zpff - > report - > field [ 3 ] - > value [ 0 ] = right ;
2007-05-30 15:07:13 +02:00
dbg_hid ( " running with 0x%02x 0x%02x \n " , left , right ) ;
2006-12-08 18:41:03 +01:00
usbhid_submit_report ( hid , zpff - > report , USB_DIR_OUT ) ;
2006-07-19 01:44:17 -04:00
return 0 ;
}
2008-09-18 12:23:32 +02:00
static int zpff_init ( struct hid_device * hid )
2006-07-19 01:44:17 -04:00
{
struct zpff_device * zpff ;
struct hid_report * report ;
struct hid_input * hidinput = list_entry ( hid - > inputs . next ,
struct hid_input , list ) ;
struct list_head * report_list =
& hid - > report_enum [ HID_OUTPUT_REPORT ] . report_list ;
struct input_dev * dev = hidinput - > input ;
int error ;
if ( list_empty ( report_list ) ) {
2008-09-18 12:23:34 +02:00
dev_err ( & hid - > dev , " no output report found \n " ) ;
2006-07-19 01:44:17 -04:00
return - ENODEV ;
}
report = list_entry ( report_list - > next , struct hid_report , list ) ;
if ( report - > maxfield < 4 ) {
2008-09-18 12:23:34 +02:00
dev_err ( & hid - > dev , " not enough fields in report \n " ) ;
2006-07-19 01:44:17 -04:00
return - ENODEV ;
}
zpff = kzalloc ( sizeof ( struct zpff_device ) , GFP_KERNEL ) ;
if ( ! zpff )
return - ENOMEM ;
set_bit ( FF_RUMBLE , dev - > ffbit ) ;
2008-09-18 12:23:32 +02:00
error = input_ff_create_memless ( dev , zpff , zpff_play ) ;
2006-07-19 01:44:17 -04:00
if ( error ) {
kfree ( zpff ) ;
return error ;
}
zpff - > report = report ;
zpff - > report - > field [ 0 ] - > value [ 0 ] = 0x00 ;
zpff - > report - > field [ 1 ] - > value [ 0 ] = 0x02 ;
zpff - > report - > field [ 2 ] - > value [ 0 ] = 0x00 ;
zpff - > report - > field [ 3 ] - > value [ 0 ] = 0x00 ;
2006-12-08 18:41:03 +01:00
usbhid_submit_report ( hid , zpff - > report , USB_DIR_OUT ) ;
2006-07-19 01:44:17 -04:00
2008-09-18 12:23:34 +02:00
dev_info ( & hid - > dev , " force feedback for Zeroplus based devices by "
2006-07-19 01:44:17 -04:00
" Anssi Hannula <anssi.hannula@gmail.com> \n " ) ;
return 0 ;
}
2009-05-15 15:46:44 +02:00
# else
static inline int zpff_init ( struct hid_device * hid )
{
return 0 ;
}
# endif
2008-09-18 12:23:32 +02:00
static int zp_probe ( struct hid_device * hdev , const struct hid_device_id * id )
{
int ret ;
ret = hid_parse ( hdev ) ;
if ( ret ) {
dev_err ( & hdev - > dev , " parse failed \n " ) ;
goto err ;
}
ret = hid_hw_start ( hdev , HID_CONNECT_DEFAULT & ~ HID_CONNECT_FF ) ;
if ( ret ) {
dev_err ( & hdev - > dev , " hw start failed \n " ) ;
goto err ;
}
zpff_init ( hdev ) ;
return 0 ;
err :
return ret ;
}
static const struct hid_device_id zp_devices [ ] = {
{ HID_USB_DEVICE ( USB_VENDOR_ID_ZEROPLUS , 0x0005 ) } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ZEROPLUS , 0x0030 ) } ,
{ }
} ;
MODULE_DEVICE_TABLE ( hid , zp_devices ) ;
static struct hid_driver zp_driver = {
. name = " zeroplus " ,
. id_table = zp_devices ,
. probe = zp_probe ,
} ;
2009-07-02 19:08:38 +02:00
static int __init zp_init ( void )
2008-09-18 12:23:32 +02:00
{
return hid_register_driver ( & zp_driver ) ;
}
2009-07-02 19:08:38 +02:00
static void __exit zp_exit ( void )
2008-09-18 12:23:32 +02:00
{
hid_unregister_driver ( & zp_driver ) ;
}
module_init ( zp_init ) ;
module_exit ( zp_exit ) ;
MODULE_LICENSE ( " GPL " ) ;