2011-11-17 17:12:01 +04:00
/*
* HID driver for Nintendo Wiimote extension devices
* Copyright ( c ) 2011 David Herrmann
*/
/*
* 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/atomic.h>
# include <linux/module.h>
# include <linux/spinlock.h>
# include <linux/workqueue.h>
# include "hid-wiimote.h"
struct wiimote_ext {
struct wiimote_data * wdata ;
struct work_struct worker ;
2011-11-17 17:12:05 +04:00
struct input_dev * input ;
struct input_dev * mp_input ;
2011-11-17 17:12:01 +04:00
atomic_t opened ;
atomic_t mp_opened ;
bool plugged ;
2011-11-17 17:12:07 +04:00
bool mp_plugged ;
2011-11-17 17:12:01 +04:00
bool motionp ;
__u8 ext_type ;
} ;
enum wiiext_type {
WIIEXT_NONE , /* placeholder */
WIIEXT_CLASSIC , /* Nintendo classic controller */
WIIEXT_NUNCHUCK , /* Nintendo nunchuck controller */
} ;
2011-11-17 17:12:08 +04:00
enum wiiext_keys {
WIIEXT_KEY_C ,
WIIEXT_KEY_Z ,
2011-11-17 17:12:09 +04:00
WIIEXT_KEY_A ,
WIIEXT_KEY_B ,
WIIEXT_KEY_X ,
WIIEXT_KEY_Y ,
WIIEXT_KEY_ZL ,
WIIEXT_KEY_ZR ,
WIIEXT_KEY_PLUS ,
WIIEXT_KEY_MINUS ,
WIIEXT_KEY_HOME ,
WIIEXT_KEY_LEFT ,
WIIEXT_KEY_RIGHT ,
WIIEXT_KEY_UP ,
WIIEXT_KEY_DOWN ,
WIIEXT_KEY_LT ,
WIIEXT_KEY_RT ,
2011-11-17 17:12:08 +04:00
WIIEXT_KEY_COUNT
} ;
static __u16 wiiext_keymap [ ] = {
BTN_C , /* WIIEXT_KEY_C */
BTN_Z , /* WIIEXT_KEY_Z */
2011-11-17 17:12:09 +04:00
BTN_A , /* WIIEXT_KEY_A */
BTN_B , /* WIIEXT_KEY_B */
BTN_X , /* WIIEXT_KEY_X */
BTN_Y , /* WIIEXT_KEY_Y */
BTN_TL2 , /* WIIEXT_KEY_ZL */
BTN_TR2 , /* WIIEXT_KEY_ZR */
KEY_NEXT , /* WIIEXT_KEY_PLUS */
KEY_PREVIOUS , /* WIIEXT_KEY_MINUS */
BTN_MODE , /* WIIEXT_KEY_HOME */
KEY_LEFT , /* WIIEXT_KEY_LEFT */
KEY_RIGHT , /* WIIEXT_KEY_RIGHT */
KEY_UP , /* WIIEXT_KEY_UP */
KEY_DOWN , /* WIIEXT_KEY_DOWN */
BTN_TL , /* WIIEXT_KEY_LT */
BTN_TR , /* WIIEXT_KEY_RT */
2011-11-17 17:12:08 +04:00
} ;
2011-11-17 17:12:02 +04:00
/* diable all extensions */
static void ext_disable ( struct wiimote_ext * ext )
{
unsigned long flags ;
2011-11-17 17:12:03 +04:00
__u8 wmem = 0x55 ;
if ( ! wiimote_cmd_acquire ( ext - > wdata ) ) {
wiimote_cmd_write ( ext - > wdata , 0xa400f0 , & wmem , sizeof ( wmem ) ) ;
wiimote_cmd_release ( ext - > wdata ) ;
}
2011-11-17 17:12:02 +04:00
spin_lock_irqsave ( & ext - > wdata - > state . lock , flags ) ;
ext - > motionp = false ;
ext - > ext_type = WIIEXT_NONE ;
2011-11-17 17:12:03 +04:00
wiiproto_req_drm ( ext - > wdata , WIIPROTO_REQ_NULL ) ;
2011-11-17 17:12:02 +04:00
spin_unlock_irqrestore ( & ext - > wdata - > state . lock , flags ) ;
}
static bool motionp_read ( struct wiimote_ext * ext )
{
2011-11-17 17:12:03 +04:00
__u8 rmem [ 2 ] , wmem ;
ssize_t ret ;
bool avail = false ;
2011-11-17 17:12:05 +04:00
if ( ! atomic_read ( & ext - > mp_opened ) )
return false ;
2011-11-17 17:12:03 +04:00
if ( wiimote_cmd_acquire ( ext - > wdata ) )
return false ;
/* initialize motion plus */
wmem = 0x55 ;
ret = wiimote_cmd_write ( ext - > wdata , 0xa600f0 , & wmem , sizeof ( wmem ) ) ;
if ( ret )
goto error ;
/* read motion plus ID */
ret = wiimote_cmd_read ( ext - > wdata , 0xa600fe , rmem , 2 ) ;
if ( ret = = 2 | | rmem [ 1 ] = = 0x5 )
avail = true ;
error :
wiimote_cmd_release ( ext - > wdata ) ;
return avail ;
2011-11-17 17:12:02 +04:00
}
static __u8 ext_read ( struct wiimote_ext * ext )
{
2011-11-17 17:12:03 +04:00
ssize_t ret ;
__u8 rmem [ 2 ] , wmem ;
__u8 type = WIIEXT_NONE ;
2011-11-17 17:12:05 +04:00
if ( ! ext - > plugged | | ! atomic_read ( & ext - > opened ) )
2011-11-17 17:12:03 +04:00
return WIIEXT_NONE ;
if ( wiimote_cmd_acquire ( ext - > wdata ) )
return WIIEXT_NONE ;
/* initialize extension */
wmem = 0x55 ;
ret = wiimote_cmd_write ( ext - > wdata , 0xa400f0 , & wmem , sizeof ( wmem ) ) ;
if ( ! ret ) {
/* disable encryption */
wmem = 0x0 ;
wiimote_cmd_write ( ext - > wdata , 0xa400fb , & wmem , sizeof ( wmem ) ) ;
}
/* read extension ID */
ret = wiimote_cmd_read ( ext - > wdata , 0xa400fe , rmem , 2 ) ;
if ( ret = = 2 ) {
if ( rmem [ 0 ] = = 0 & & rmem [ 1 ] = = 0 )
type = WIIEXT_NUNCHUCK ;
else if ( rmem [ 0 ] = = 0x01 & & rmem [ 1 ] = = 0x01 )
type = WIIEXT_CLASSIC ;
}
wiimote_cmd_release ( ext - > wdata ) ;
return type ;
2011-11-17 17:12:02 +04:00
}
static void ext_enable ( struct wiimote_ext * ext , bool motionp , __u8 ext_type )
{
unsigned long flags ;
2011-11-17 17:12:03 +04:00
__u8 wmem ;
int ret ;
if ( motionp ) {
if ( wiimote_cmd_acquire ( ext - > wdata ) )
return ;
if ( ext_type = = WIIEXT_CLASSIC )
wmem = 0x07 ;
else if ( ext_type = = WIIEXT_NUNCHUCK )
wmem = 0x05 ;
else
wmem = 0x04 ;
ret = wiimote_cmd_write ( ext - > wdata , 0xa600fe , & wmem , sizeof ( wmem ) ) ;
wiimote_cmd_release ( ext - > wdata ) ;
if ( ret )
return ;
}
2011-11-17 17:12:02 +04:00
spin_lock_irqsave ( & ext - > wdata - > state . lock , flags ) ;
ext - > motionp = motionp ;
ext - > ext_type = ext_type ;
2011-11-17 17:12:03 +04:00
wiiproto_req_drm ( ext - > wdata , WIIPROTO_REQ_NULL ) ;
2011-11-17 17:12:02 +04:00
spin_unlock_irqrestore ( & ext - > wdata - > state . lock , flags ) ;
}
2011-11-17 17:12:01 +04:00
static void wiiext_worker ( struct work_struct * work )
{
struct wiimote_ext * ext = container_of ( work , struct wiimote_ext ,
worker ) ;
2011-11-17 17:12:02 +04:00
bool motionp ;
__u8 ext_type ;
ext_disable ( ext ) ;
motionp = motionp_read ( ext ) ;
ext_type = ext_read ( ext ) ;
ext_enable ( ext , motionp , ext_type ) ;
2011-11-17 17:12:01 +04:00
}
/* schedule work only once, otherwise mark for reschedule */
static void wiiext_schedule ( struct wiimote_ext * ext )
{
queue_work ( system_nrt_wq , & ext - > worker ) ;
}
/*
* Reacts on extension port events
* Whenever the driver gets an event from the wiimote that an extension has been
* plugged or unplugged , this funtion shall be called . It checks what extensions
* are connected and initializes and activates them .
* This can be called in atomic context . The initialization is done in a
* separate worker thread . The state . lock spinlock must be held by the caller .
*/
void wiiext_event ( struct wiimote_data * wdata , bool plugged )
{
if ( ! wdata - > ext )
return ;
if ( wdata - > ext - > plugged = = plugged )
return ;
wdata - > ext - > plugged = plugged ;
2011-11-17 17:12:07 +04:00
if ( ! plugged )
wdata - > ext - > mp_plugged = false ;
2011-11-17 17:12:01 +04:00
/*
* We need to call wiiext_schedule ( wdata - > ext ) here , however , the
* extension initialization logic is not fully understood and so
* automatic initialization is not supported , yet .
*/
}
/*
* Returns true if the current DRM mode should contain extension data and false
* if there is no interest in extension data .
* All supported extensions send 6 byte extension data so any DRM that contains
* extension bytes is fine .
* The caller must hold the state . lock spinlock .
*/
bool wiiext_active ( struct wiimote_data * wdata )
{
if ( ! wdata - > ext )
return false ;
return wdata - > ext - > motionp | | wdata - > ext - > ext_type ;
}
2011-11-17 17:12:06 +04:00
static void handler_motionp ( struct wiimote_ext * ext , const __u8 * payload )
{
2011-11-17 17:12:07 +04:00
__s32 x , y , z ;
bool plugged ;
/* | 8 7 6 5 4 3 | 2 | 1 |
* - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + - - - - - +
* 1 | Yaw Speed < 7 : 0 > |
* 2 | Roll Speed < 7 : 0 > |
* 3 | Pitch Speed < 7 : 0 > |
* - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + - - - - - +
* 4 | Yaw Speed < 13 : 8 > | Yaw | Pitch |
* - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + - - - - - +
* 5 | Roll Speed < 13 : 8 > | Roll | Ext |
* - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + - - - - - +
* 6 | Pitch Speed < 13 : 8 > | 1 | 0 |
* - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + - - - - - +
* The single bits Yaw , Roll , Pitch in the lower right corner specify
* whether the wiimote is rotating fast ( 0 ) or slow ( 1 ) . Speed for slow
* roation is 440 deg / s and for fast rotation 2000 deg / s . To get a
* linear scale we multiply by 2000 / 440 = ~ 4.5454 which is 18 for fast
* and 9 for slow .
* If the wiimote is not rotating the sensor reports 2 ^ 13 = 8192.
* Ext specifies whether an extension is connected to the motionp .
*/
x = payload [ 0 ] ;
y = payload [ 1 ] ;
z = payload [ 2 ] ;
x | = ( ( ( __u16 ) payload [ 3 ] ) < < 6 ) & 0xff00 ;
y | = ( ( ( __u16 ) payload [ 4 ] ) < < 6 ) & 0xff00 ;
z | = ( ( ( __u16 ) payload [ 5 ] ) < < 6 ) & 0xff00 ;
x - = 8192 ;
y - = 8192 ;
z - = 8192 ;
if ( ! ( payload [ 3 ] & 0x02 ) )
x * = 18 ;
else
x * = 9 ;
if ( ! ( payload [ 4 ] & 0x02 ) )
y * = 18 ;
else
y * = 9 ;
if ( ! ( payload [ 3 ] & 0x01 ) )
z * = 18 ;
else
z * = 9 ;
input_report_abs ( ext - > mp_input , ABS_RX , x ) ;
input_report_abs ( ext - > mp_input , ABS_RY , y ) ;
input_report_abs ( ext - > mp_input , ABS_RZ , z ) ;
input_sync ( ext - > mp_input ) ;
plugged = payload [ 5 ] & 0x01 ;
if ( plugged ! = ext - > mp_plugged )
ext - > mp_plugged = plugged ;
2011-11-17 17:12:06 +04:00
}
static void handler_nunchuck ( struct wiimote_ext * ext , const __u8 * payload )
{
2011-11-17 17:12:08 +04:00
__s16 x , y , z , bx , by ;
/* Byte | 8 7 | 6 5 | 4 3 | 2 | 1 |
* - - - - - + - - - - - - - - - - + - - - - - - - - - + - - - - - - - - - + - - - - + - - - - - +
* 1 | Button X < 7 : 0 > |
* 2 | Button Y < 7 : 0 > |
* - - - - - + - - - - - - - - - - + - - - - - - - - - + - - - - - - - - - + - - - - + - - - - - +
* 3 | Speed X < 9 : 2 > |
* 4 | Speed Y < 9 : 2 > |
* 5 | Speed Z < 9 : 2 > |
* - - - - - + - - - - - - - - - - + - - - - - - - - - + - - - - - - - - - + - - - - + - - - - - +
* 6 | Z < 1 : 0 > | Y < 1 : 0 > | X < 1 : 0 > | BC | BZ |
* - - - - - + - - - - - - - - - - + - - - - - - - - - + - - - - - - - - - + - - - - + - - - - - +
* Button X / Y is the analog stick . Speed X , Y and Z are the
* accelerometer data in the same format as the wiimote ' s accelerometer .
* The 6 th byte contains the LSBs of the accelerometer data .
* BC and BZ are the C and Z buttons : 0 means pressed
*
* If reported interleaved with motionp , then the layout changes . The
* 5 th and 6 th byte changes to :
* - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - +
* 5 | Speed Z < 9 : 3 > | EXT |
* - - - - - + - - - - - - - - + - - - - - + - - - - - + - - - - + - - - - + - - - - + - - - - - +
* 6 | Z < 2 : 1 > | Y < 1 > | X < 1 > | BC | BZ | 0 | 0 |
* - - - - - + - - - - - - - - + - - - - - + - - - - - + - - - - + - - - - + - - - - + - - - - - +
* All three accelerometer values lose their LSB . The other data is
* still available but slightly moved .
*
* Center data for button values is 128. Center value for accelerometer
* values it 512 / 0x200
*/
bx = payload [ 0 ] ;
by = payload [ 1 ] ;
bx - = 128 ;
by - = 128 ;
x = payload [ 2 ] < < 2 ;
y = payload [ 3 ] < < 2 ;
z = payload [ 4 ] < < 2 ;
if ( ext - > motionp ) {
x | = ( payload [ 5 ] > > 3 ) & 0x02 ;
y | = ( payload [ 5 ] > > 4 ) & 0x02 ;
z & = ~ 0x4 ;
z | = ( payload [ 5 ] > > 5 ) & 0x06 ;
} else {
x | = ( payload [ 5 ] > > 2 ) & 0x03 ;
y | = ( payload [ 5 ] > > 4 ) & 0x03 ;
z | = ( payload [ 5 ] > > 6 ) & 0x03 ;
}
x - = 0x200 ;
y - = 0x200 ;
z - = 0x200 ;
input_report_abs ( ext - > input , ABS_HAT0X , bx ) ;
input_report_abs ( ext - > input , ABS_HAT0Y , by ) ;
input_report_abs ( ext - > input , ABS_RX , x ) ;
input_report_abs ( ext - > input , ABS_RY , y ) ;
input_report_abs ( ext - > input , ABS_RZ , z ) ;
if ( ext - > motionp ) {
input_report_key ( ext - > input ,
wiiext_keymap [ WIIEXT_KEY_Z ] , ! ! ( payload [ 5 ] & 0x04 ) ) ;
input_report_key ( ext - > input ,
wiiext_keymap [ WIIEXT_KEY_C ] , ! ! ( payload [ 5 ] & 0x08 ) ) ;
} else {
input_report_key ( ext - > input ,
wiiext_keymap [ WIIEXT_KEY_Z ] , ! ! ( payload [ 5 ] & 0x01 ) ) ;
input_report_key ( ext - > input ,
wiiext_keymap [ WIIEXT_KEY_C ] , ! ! ( payload [ 5 ] & 0x02 ) ) ;
}
input_sync ( ext - > input ) ;
2011-11-17 17:12:06 +04:00
}
static void handler_classic ( struct wiimote_ext * ext , const __u8 * payload )
{
2011-11-17 17:12:09 +04:00
__s8 rx , ry , lx , ly , lt , rt ;
/* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
* - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - +
* 1 | RX < 5 : 4 > | LX < 5 : 0 > |
* 2 | RX < 3 : 2 > | LY < 5 : 0 > |
* - - - - - + - - - - - + - - - - - + - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* 3 | RX < 1 > | LT < 5 : 4 > | RY < 5 : 1 > |
* - - - - - + - - - - - + - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* 4 | LT < 3 : 1 > | RT < 5 : 1 > |
* - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - +
* 5 | BDR | BDD | BLT | B - | BH | B + | BRT | 1 |
* - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - +
* 6 | BZL | BB | BY | BA | BX | BZR | BDL | BDU |
* - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - +
* All buttons are 0 if pressed
* RX and RY are right analog stick
* LX and LY are left analog stick
* LT is left trigger , RT is right trigger
* BLT is 0 if left trigger is fully pressed
* BRT is 0 if right trigger is fully pressed
* BDR , BDD , BDL , BDU form the D - Pad with right , down , left , up buttons
* BZL is left Z button and BZR is right Z button
* B - , BH , B + are + , HOME and - buttons
* BB , BY , BA , BX are A , B , X , Y buttons
* LSB of RX , RY , LT , and RT are not transmitted and always 0.
*
* With motionp enabled it changes slightly to this :
* Byte | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
* - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - +
* 1 | RX < 4 : 3 > | LX < 5 : 1 > | BDU |
* 2 | RX < 2 : 1 > | LY < 5 : 1 > | BDL |
* - - - - - + - - - - - + - - - - - + - - - - - + - - - - - - - - - - - - - - - - - - - - - - - + - - - - - +
* 3 | RX < 0 > | LT < 4 : 3 > | RY < 4 : 0 > |
* - - - - - + - - - - - + - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* 4 | LT < 2 : 0 > | RT < 4 : 0 > |
* - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - +
* 5 | BDR | BDD | BLT | B - | BH | B + | BRT | EXT |
* - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - +
* 6 | BZL | BB | BY | BA | BX | BZR | 0 | 0 |
* - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - + - - - - - +
* Only the LSBs of LX and LY are lost . BDU and BDL are moved , the rest
* is the same as before .
*/
if ( ext - > motionp ) {
lx = payload [ 0 ] & 0x3e ;
ly = payload [ 0 ] & 0x3e ;
} else {
lx = payload [ 0 ] & 0x3f ;
ly = payload [ 0 ] & 0x3f ;
}
rx = ( payload [ 0 ] > > 3 ) & 0x14 ;
rx | = ( payload [ 1 ] > > 5 ) & 0x06 ;
rx | = ( payload [ 2 ] > > 7 ) & 0x01 ;
ry = payload [ 2 ] & 0x1f ;
rt = payload [ 3 ] & 0x1f ;
lt = ( payload [ 2 ] > > 2 ) & 0x18 ;
lt | = ( payload [ 3 ] > > 5 ) & 0x07 ;
rx < < = 1 ;
ry < < = 1 ;
rt < < = 1 ;
lt < < = 1 ;
input_report_abs ( ext - > input , ABS_HAT1X , lx - 0x20 ) ;
input_report_abs ( ext - > input , ABS_HAT1Y , ly - 0x20 ) ;
input_report_abs ( ext - > input , ABS_HAT2X , rx - 0x20 ) ;
input_report_abs ( ext - > input , ABS_HAT2Y , ry - 0x20 ) ;
input_report_abs ( ext - > input , ABS_HAT3X , rt - 0x20 ) ;
input_report_abs ( ext - > input , ABS_HAT3Y , lt - 0x20 ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_RIGHT ] ,
! ! ( payload [ 4 ] & 0x80 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_DOWN ] ,
! ! ( payload [ 4 ] & 0x40 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_LT ] ,
! ! ( payload [ 4 ] & 0x20 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_MINUS ] ,
! ! ( payload [ 4 ] & 0x10 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_HOME ] ,
! ! ( payload [ 4 ] & 0x08 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_PLUS ] ,
! ! ( payload [ 4 ] & 0x04 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_RT ] ,
! ! ( payload [ 4 ] & 0x02 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_ZL ] ,
! ! ( payload [ 5 ] & 0x80 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_B ] ,
! ! ( payload [ 5 ] & 0x40 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_Y ] ,
! ! ( payload [ 5 ] & 0x20 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_A ] ,
! ! ( payload [ 5 ] & 0x10 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_X ] ,
! ! ( payload [ 5 ] & 0x08 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_ZR ] ,
! ! ( payload [ 5 ] & 0x04 ) ) ;
if ( ext - > motionp ) {
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_UP ] ,
! ! ( payload [ 0 ] & 0x01 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_LEFT ] ,
! ! ( payload [ 1 ] & 0x01 ) ) ;
} else {
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_UP ] ,
! ! ( payload [ 5 ] & 0x01 ) ) ;
input_report_key ( ext - > input , wiiext_keymap [ WIIEXT_KEY_LEFT ] ,
! ! ( payload [ 5 ] & 0x02 ) ) ;
}
input_sync ( ext - > input ) ;
2011-11-17 17:12:06 +04:00
}
/* call this with state.lock spinlock held */
void wiiext_handle ( struct wiimote_data * wdata , const __u8 * payload )
{
struct wiimote_ext * ext = wdata - > ext ;
if ( ! ext )
return ;
if ( ext - > motionp & & ( payload [ 5 ] & 0x02 ) ) {
handler_motionp ( ext , payload ) ;
} else if ( ext - > ext_type = = WIIEXT_NUNCHUCK ) {
handler_nunchuck ( ext , payload ) ;
} else if ( ext - > ext_type = = WIIEXT_CLASSIC ) {
handler_classic ( ext , payload ) ;
}
}
2011-11-17 17:12:04 +04:00
static ssize_t wiiext_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct wiimote_data * wdata = dev_to_wii ( dev ) ;
__u8 type = WIIEXT_NONE ;
bool motionp = false ;
unsigned long flags ;
spin_lock_irqsave ( & wdata - > state . lock , flags ) ;
if ( wdata - > ext ) {
motionp = wdata - > ext - > motionp ;
type = wdata - > ext - > ext_type ;
}
spin_unlock_irqrestore ( & wdata - > state . lock , flags ) ;
if ( type = = WIIEXT_NUNCHUCK ) {
if ( motionp )
return sprintf ( buf , " motionp+nunchuck \n " ) ;
else
return sprintf ( buf , " nunchuck \n " ) ;
} else if ( type = = WIIEXT_CLASSIC ) {
if ( motionp )
return sprintf ( buf , " motionp+classic \n " ) ;
else
return sprintf ( buf , " classic \n " ) ;
} else {
if ( motionp )
return sprintf ( buf , " motionp \n " ) ;
else
return sprintf ( buf , " none \n " ) ;
}
}
static DEVICE_ATTR ( extension , S_IRUGO , wiiext_show , NULL ) ;
2011-11-17 17:12:05 +04:00
static int wiiext_input_open ( struct input_dev * dev )
{
struct wiimote_ext * ext = input_get_drvdata ( dev ) ;
int ret ;
ret = hid_hw_open ( ext - > wdata - > hdev ) ;
if ( ret )
return ret ;
atomic_inc ( & ext - > opened ) ;
wiiext_schedule ( ext ) ;
return 0 ;
}
static void wiiext_input_close ( struct input_dev * dev )
{
struct wiimote_ext * ext = input_get_drvdata ( dev ) ;
atomic_dec ( & ext - > opened ) ;
wiiext_schedule ( ext ) ;
hid_hw_close ( ext - > wdata - > hdev ) ;
}
static int wiiext_mp_open ( struct input_dev * dev )
{
struct wiimote_ext * ext = input_get_drvdata ( dev ) ;
int ret ;
ret = hid_hw_open ( ext - > wdata - > hdev ) ;
if ( ret )
return ret ;
atomic_inc ( & ext - > mp_opened ) ;
wiiext_schedule ( ext ) ;
return 0 ;
}
static void wiiext_mp_close ( struct input_dev * dev )
{
struct wiimote_ext * ext = input_get_drvdata ( dev ) ;
atomic_dec ( & ext - > mp_opened ) ;
wiiext_schedule ( ext ) ;
hid_hw_close ( ext - > wdata - > hdev ) ;
}
2011-11-17 17:12:01 +04:00
/* Initializes the extension driver of a wiimote */
int wiiext_init ( struct wiimote_data * wdata )
{
struct wiimote_ext * ext ;
unsigned long flags ;
2011-11-17 17:12:08 +04:00
int ret , i ;
2011-11-17 17:12:01 +04:00
ext = kzalloc ( sizeof ( * ext ) , GFP_KERNEL ) ;
if ( ! ext )
return - ENOMEM ;
ext - > wdata = wdata ;
INIT_WORK ( & ext - > worker , wiiext_worker ) ;
2011-11-17 17:12:05 +04:00
ext - > input = input_allocate_device ( ) ;
if ( ! ext - > input ) {
ret = - ENOMEM ;
goto err_input ;
}
input_set_drvdata ( ext - > input , ext ) ;
ext - > input - > open = wiiext_input_open ;
ext - > input - > close = wiiext_input_close ;
ext - > input - > dev . parent = & wdata - > hdev - > dev ;
ext - > input - > id . bustype = wdata - > hdev - > bus ;
ext - > input - > id . vendor = wdata - > hdev - > vendor ;
ext - > input - > id . product = wdata - > hdev - > product ;
ext - > input - > id . version = wdata - > hdev - > version ;
ext - > input - > name = WIIMOTE_NAME " Extension " ;
2011-11-17 17:12:08 +04:00
set_bit ( EV_KEY , ext - > input - > evbit ) ;
for ( i = 0 ; i < WIIEXT_KEY_COUNT ; + + i )
set_bit ( wiiext_keymap [ i ] , ext - > input - > keybit ) ;
set_bit ( EV_ABS , ext - > input - > evbit ) ;
set_bit ( ABS_HAT0X , ext - > input - > absbit ) ;
set_bit ( ABS_HAT0Y , ext - > input - > absbit ) ;
2011-11-17 17:12:09 +04:00
set_bit ( ABS_HAT1X , ext - > input - > absbit ) ;
set_bit ( ABS_HAT1Y , ext - > input - > absbit ) ;
set_bit ( ABS_HAT2X , ext - > input - > absbit ) ;
set_bit ( ABS_HAT2Y , ext - > input - > absbit ) ;
set_bit ( ABS_HAT3X , ext - > input - > absbit ) ;
set_bit ( ABS_HAT3Y , ext - > input - > absbit ) ;
2011-11-17 17:12:08 +04:00
input_set_abs_params ( ext - > input , ABS_HAT0X , - 120 , 120 , 2 , 4 ) ;
input_set_abs_params ( ext - > input , ABS_HAT0Y , - 120 , 120 , 2 , 4 ) ;
2011-11-17 17:12:09 +04:00
input_set_abs_params ( ext - > input , ABS_HAT1X , - 30 , 30 , 1 , 1 ) ;
input_set_abs_params ( ext - > input , ABS_HAT1Y , - 30 , 30 , 1 , 1 ) ;
input_set_abs_params ( ext - > input , ABS_HAT2X , - 30 , 30 , 1 , 1 ) ;
input_set_abs_params ( ext - > input , ABS_HAT2Y , - 30 , 30 , 1 , 1 ) ;
input_set_abs_params ( ext - > input , ABS_HAT3X , - 30 , 30 , 1 , 1 ) ;
input_set_abs_params ( ext - > input , ABS_HAT3Y , - 30 , 30 , 1 , 1 ) ;
2011-11-17 17:12:08 +04:00
set_bit ( ABS_RX , ext - > input - > absbit ) ;
set_bit ( ABS_RY , ext - > input - > absbit ) ;
set_bit ( ABS_RZ , ext - > input - > absbit ) ;
input_set_abs_params ( ext - > input , ABS_RX , - 500 , 500 , 2 , 4 ) ;
input_set_abs_params ( ext - > input , ABS_RY , - 500 , 500 , 2 , 4 ) ;
input_set_abs_params ( ext - > input , ABS_RZ , - 500 , 500 , 2 , 4 ) ;
2011-11-17 17:12:05 +04:00
ret = input_register_device ( ext - > input ) ;
if ( ret ) {
input_free_device ( ext - > input ) ;
goto err_input ;
}
ext - > mp_input = input_allocate_device ( ) ;
if ( ! ext - > mp_input ) {
ret = - ENOMEM ;
goto err_mp ;
}
input_set_drvdata ( ext - > mp_input , ext ) ;
ext - > mp_input - > open = wiiext_mp_open ;
ext - > mp_input - > close = wiiext_mp_close ;
ext - > mp_input - > dev . parent = & wdata - > hdev - > dev ;
ext - > mp_input - > id . bustype = wdata - > hdev - > bus ;
ext - > mp_input - > id . vendor = wdata - > hdev - > vendor ;
ext - > mp_input - > id . product = wdata - > hdev - > product ;
ext - > mp_input - > id . version = wdata - > hdev - > version ;
ext - > mp_input - > name = WIIMOTE_NAME " Motion+ " ;
2011-11-17 17:12:07 +04:00
set_bit ( EV_ABS , ext - > mp_input - > evbit ) ;
set_bit ( ABS_RX , ext - > mp_input - > absbit ) ;
set_bit ( ABS_RY , ext - > mp_input - > absbit ) ;
set_bit ( ABS_RZ , ext - > mp_input - > absbit ) ;
input_set_abs_params ( ext - > mp_input , ABS_RX , - 160000 , 160000 , 4 , 8 ) ;
input_set_abs_params ( ext - > mp_input , ABS_RY , - 160000 , 160000 , 4 , 8 ) ;
input_set_abs_params ( ext - > mp_input , ABS_RZ , - 160000 , 160000 , 4 , 8 ) ;
2011-11-17 17:12:05 +04:00
ret = input_register_device ( ext - > mp_input ) ;
if ( ret ) {
input_free_device ( ext - > mp_input ) ;
goto err_mp ;
}
2011-11-17 17:12:04 +04:00
ret = device_create_file ( & wdata - > hdev - > dev , & dev_attr_extension ) ;
if ( ret )
2011-11-17 17:12:05 +04:00
goto err_dev ;
2011-11-17 17:12:04 +04:00
2011-11-17 17:12:01 +04:00
spin_lock_irqsave ( & wdata - > state . lock , flags ) ;
wdata - > ext = ext ;
spin_unlock_irqrestore ( & wdata - > state . lock , flags ) ;
return 0 ;
2011-11-17 17:12:04 +04:00
2011-11-17 17:12:05 +04:00
err_dev :
input_unregister_device ( ext - > mp_input ) ;
err_mp :
input_unregister_device ( ext - > input ) ;
err_input :
2011-11-17 17:12:04 +04:00
kfree ( ext ) ;
return ret ;
2011-11-17 17:12:01 +04:00
}
/* Deinitializes the extension driver of a wiimote */
void wiiext_deinit ( struct wiimote_data * wdata )
{
struct wiimote_ext * ext = wdata - > ext ;
unsigned long flags ;
if ( ! ext )
return ;
/*
* We first unset wdata - > ext to avoid further input from the wiimote
* core . The worker thread does not access this pointer so it is not
* affected by this .
* We kill the worker after this so it does not get respawned during
* deinitialization .
*/
spin_lock_irqsave ( & wdata - > state . lock , flags ) ;
wdata - > ext = NULL ;
spin_unlock_irqrestore ( & wdata - > state . lock , flags ) ;
2011-11-17 17:12:04 +04:00
device_remove_file ( & wdata - > hdev - > dev , & dev_attr_extension ) ;
2011-11-17 17:12:05 +04:00
input_unregister_device ( ext - > mp_input ) ;
input_unregister_device ( ext - > input ) ;
2011-11-17 17:12:04 +04:00
2011-11-17 17:12:01 +04:00
cancel_work_sync ( & ext - > worker ) ;
kfree ( ext ) ;
}