2019-05-27 08:55:05 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-16 15:20:36 -07:00
/*
* Copyright ( c ) 2000 - 2002 Vojtech Pavlik < vojtech @ ucw . cz >
2007-04-12 01:30:24 -04:00
* Copyright ( c ) 2001 - 2002 , 2007 Johann Deneux < johann . deneux @ gmail . com >
2005-04-16 15:20:36 -07:00
*
* USB / RS232 I - Force joysticks and wheels .
*/
2018-08-10 13:54:02 -07:00
# include <asm/unaligned.h>
2005-04-16 15:20:36 -07:00
# include "iforce.h"
static struct {
__s32 x ;
__s32 y ;
} iforce_hat_to_axis [ 16 ] = { { 0 , - 1 } , { 1 , - 1 } , { 1 , 0 } , { 1 , 1 } , { 0 , 1 } , { - 1 , 1 } , { - 1 , 0 } , { - 1 , - 1 } } ;
2018-07-24 10:46:47 -07:00
void iforce_dump_packet ( struct iforce * iforce , char * msg , u16 cmd , unsigned char * data )
2005-04-16 15:20:36 -07:00
{
2018-07-24 10:46:47 -07:00
dev_dbg ( iforce - > dev - > dev . parent , " %s %s cmd = %04x, data = %*ph \n " ,
__func__ , msg , cmd , LO ( cmd ) , data ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Send a packet of bytes to the device
*/
int iforce_send_packet ( struct iforce * iforce , u16 cmd , unsigned char * data )
{
/* Copy data to buffer */
int n = LO ( cmd ) ;
int c ;
int empty ;
int head , tail ;
unsigned long flags ;
/*
* Update head and tail of xmit buffer
*/
spin_lock_irqsave ( & iforce - > xmit_lock , flags ) ;
head = iforce - > xmit . head ;
tail = iforce - > xmit . tail ;
2007-04-12 01:30:24 -04:00
2005-04-16 15:20:36 -07:00
if ( CIRC_SPACE ( head , tail , XMIT_SIZE ) < n + 2 ) {
2008-08-14 09:37:34 -07:00
dev_warn ( & iforce - > dev - > dev ,
" not enough space in xmit buffer to send new packet \n " ) ;
2005-04-16 15:20:36 -07:00
spin_unlock_irqrestore ( & iforce - > xmit_lock , flags ) ;
return - 1 ;
}
empty = head = = tail ;
XMIT_INC ( iforce - > xmit . head , n + 2 ) ;
/*
* Store packet in xmit buffer
*/
iforce - > xmit . buf [ head ] = HI ( cmd ) ;
XMIT_INC ( head , 1 ) ;
iforce - > xmit . buf [ head ] = LO ( cmd ) ;
XMIT_INC ( head , 1 ) ;
c = CIRC_SPACE_TO_END ( head , tail , XMIT_SIZE ) ;
if ( n < c ) c = n ;
memcpy ( & iforce - > xmit . buf [ head ] ,
data ,
c ) ;
if ( n ! = c ) {
memcpy ( & iforce - > xmit . buf [ 0 ] ,
data + c ,
n - c ) ;
}
XMIT_INC ( head , n ) ;
spin_unlock_irqrestore ( & iforce - > xmit_lock , flags ) ;
/*
* If necessary , start the transmission
*/
2018-07-26 17:36:36 -07:00
if ( empty )
iforce - > xport_ops - > xmit ( iforce ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2018-07-24 17:32:24 -07:00
EXPORT_SYMBOL ( iforce_send_packet ) ;
2005-04-16 15:20:36 -07:00
/* Start or stop an effect */
int iforce_control_playback ( struct iforce * iforce , u16 id , unsigned int value )
{
unsigned char data [ 3 ] ;
data [ 0 ] = LO ( id ) ;
data [ 1 ] = ( value > 0 ) ? ( ( value > 1 ) ? 0x41 : 0x01 ) : 0 ;
data [ 2 ] = LO ( value ) ;
return iforce_send_packet ( iforce , FF_CMD_PLAY , data ) ;
}
/* Mark an effect that was being updated as ready. That means it can be updated
* again */
static int mark_core_as_ready ( struct iforce * iforce , unsigned short addr )
{
int i ;
2005-09-15 02:01:52 -05:00
2006-07-19 01:40:39 -04:00
if ( ! iforce - > dev - > ff )
return 0 ;
for ( i = 0 ; i < iforce - > dev - > ff - > max_effects ; + + i ) {
2005-04-16 15:20:36 -07:00
if ( test_bit ( FF_CORE_IS_USED , iforce - > core_effects [ i ] . flags ) & &
( iforce - > core_effects [ i ] . mod1_chunk . start = = addr | |
iforce - > core_effects [ i ] . mod2_chunk . start = = addr ) ) {
clear_bit ( FF_CORE_UPDATE , iforce - > core_effects [ i ] . flags ) ;
return 0 ;
}
}
2008-08-14 09:37:34 -07:00
dev_warn ( & iforce - > dev - > dev , " unused effect %04x updated !!! \n " , addr ) ;
2005-04-16 15:20:36 -07:00
return - 1 ;
}
2018-08-09 17:41:40 -07:00
static void iforce_report_hats_buttons ( struct iforce * iforce , u8 * data )
2005-04-16 15:20:36 -07:00
{
2005-09-15 02:01:52 -05:00
struct input_dev * dev = iforce - > dev ;
2005-04-16 15:20:36 -07:00
int i ;
2018-08-09 17:41:40 -07:00
input_report_abs ( dev , ABS_HAT0X , iforce_hat_to_axis [ data [ 6 ] > > 4 ] . x ) ;
input_report_abs ( dev , ABS_HAT0Y , iforce_hat_to_axis [ data [ 6 ] > > 4 ] . y ) ;
for ( i = 0 ; iforce - > type - > btn [ i ] > = 0 ; i + + )
input_report_key ( dev , iforce - > type - > btn [ i ] ,
data [ ( i > > 3 ) + 5 ] & ( 1 < < ( i & 7 ) ) ) ;
/* If there are untouched bits left, interpret them as the second hat */
if ( i < = 8 ) {
u8 btns = data [ 6 ] ;
if ( test_bit ( ABS_HAT1X , dev - > absbit ) ) {
if ( btns & BIT ( 3 ) )
input_report_abs ( dev , ABS_HAT1X , - 1 ) ;
else if ( btns & BIT ( 1 ) )
input_report_abs ( dev , ABS_HAT1X , 1 ) ;
else
input_report_abs ( dev , ABS_HAT1X , 0 ) ;
}
if ( test_bit ( ABS_HAT1Y , dev - > absbit ) ) {
if ( btns & BIT ( 0 ) )
input_report_abs ( dev , ABS_HAT1Y , - 1 ) ;
else if ( btns & BIT ( 2 ) )
input_report_abs ( dev , ABS_HAT1Y , 1 ) ;
else
input_report_abs ( dev , ABS_HAT1Y , 0 ) ;
}
}
}
2018-08-09 17:50:39 -07:00
void iforce_process_packet ( struct iforce * iforce ,
u8 packet_id , u8 * data , size_t len )
2018-08-09 17:41:40 -07:00
{
struct input_dev * dev = iforce - > dev ;
int i , j ;
2018-08-09 17:50:39 -07:00
switch ( packet_id ) {
2005-04-16 15:20:36 -07:00
2018-08-09 17:40:39 -07:00
case 0x01 : /* joystick position data */
2018-08-10 13:54:02 -07:00
input_report_abs ( dev , ABS_X ,
( __s16 ) get_unaligned_le16 ( data ) ) ;
input_report_abs ( dev , ABS_Y ,
( __s16 ) get_unaligned_le16 ( data + 2 ) ) ;
2018-08-09 17:41:40 -07:00
input_report_abs ( dev , ABS_THROTTLE , 255 - data [ 4 ] ) ;
2005-04-16 15:20:36 -07:00
2018-08-09 17:50:39 -07:00
if ( len > = 8 & & test_bit ( ABS_RUDDER , dev - > absbit ) )
2018-08-09 17:41:40 -07:00
input_report_abs ( dev , ABS_RUDDER , ( __s8 ) data [ 7 ] ) ;
iforce_report_hats_buttons ( iforce , data ) ;
2005-04-16 15:20:36 -07:00
2018-08-09 17:40:39 -07:00
input_sync ( dev ) ;
2018-08-09 17:41:40 -07:00
break ;
2005-04-16 15:20:36 -07:00
2018-08-09 17:41:40 -07:00
case 0x03 : /* wheel position data */
2018-08-10 13:54:02 -07:00
input_report_abs ( dev , ABS_WHEEL ,
( __s16 ) get_unaligned_le16 ( data ) ) ;
2018-08-09 17:41:40 -07:00
input_report_abs ( dev , ABS_GAS , 255 - data [ 2 ] ) ;
input_report_abs ( dev , ABS_BRAKE , 255 - data [ 3 ] ) ;
iforce_report_hats_buttons ( iforce , data ) ;
input_sync ( dev ) ;
2018-08-09 17:40:39 -07:00
break ;
2005-04-16 15:20:36 -07:00
2018-08-09 17:40:39 -07:00
case 0x02 : /* status report */
input_report_key ( dev , BTN_DEAD , data [ 0 ] & 0x02 ) ;
input_sync ( dev ) ;
2005-04-16 15:20:36 -07:00
2018-08-09 17:40:39 -07:00
/* Check if an effect was just started or stopped */
i = data [ 1 ] & 0x7f ;
if ( data [ 1 ] & 0x80 ) {
if ( ! test_and_set_bit ( FF_CORE_IS_PLAYED , iforce - > core_effects [ i ] . flags ) ) {
/* Report play event */
input_report_ff_status ( dev , i , FF_STATUS_PLAYING ) ;
2005-04-16 15:20:36 -07:00
}
2018-08-09 17:40:39 -07:00
} else if ( test_and_clear_bit ( FF_CORE_IS_PLAYED , iforce - > core_effects [ i ] . flags ) ) {
/* Report stop event */
input_report_ff_status ( dev , i , FF_STATUS_STOPPED ) ;
}
2018-08-09 17:41:40 -07:00
2018-08-09 17:50:39 -07:00
for ( j = 3 ; j < len ; j + = 2 )
2018-08-10 13:54:02 -07:00
mark_core_as_ready ( iforce , get_unaligned_le16 ( data + j ) ) ;
2018-08-09 17:41:40 -07:00
2018-08-09 17:40:39 -07:00
break ;
2005-04-16 15:20:36 -07:00
}
}
2018-07-24 17:32:24 -07:00
EXPORT_SYMBOL ( iforce_process_packet ) ;