2008-11-19 17:54:46 +03:00
/*
2009-05-20 17:49:35 +04:00
* HID driver for N - Trig touchscreens
2008-11-19 17:54:46 +03:00
*
* Copyright ( c ) 2008 Rafi Rubin
2009-05-20 17:49:35 +04:00
* Copyright ( c ) 2009 Stephane Chatty
2008-11-19 17:54:46 +03: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 "hid-ids.h"
# define NTRIG_DUPLICATE_USAGES 0x001
# define nt_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
EV_KEY , ( c ) )
2009-05-20 17:49:35 +04:00
struct ntrig_data {
__s32 x , y , id , w , h ;
char reading_a_point , found_contact_id ;
} ;
/*
* this driver is aimed at two firmware versions in circulation :
* - dual pen / finger single touch
* - finger multitouch , pen not working
*/
2008-11-19 17:54:46 +03:00
static int ntrig_input_mapping ( struct hid_device * hdev , struct hid_input * hi ,
struct hid_field * field , struct hid_usage * usage ,
unsigned long * * bit , int * max )
{
2009-05-20 17:49:35 +04:00
switch ( usage - > hid & HID_USAGE_PAGE ) {
case HID_UP_GENDESK :
switch ( usage - > hid ) {
case HID_GD_X :
hid_map_usage ( hi , usage , bit , max ,
EV_ABS , ABS_MT_POSITION_X ) ;
input_set_abs_params ( hi - > input , ABS_X ,
field - > logical_minimum ,
field - > logical_maximum , 0 , 0 ) ;
return 1 ;
case HID_GD_Y :
hid_map_usage ( hi , usage , bit , max ,
EV_ABS , ABS_MT_POSITION_Y ) ;
input_set_abs_params ( hi - > input , ABS_Y ,
field - > logical_minimum ,
field - > logical_maximum , 0 , 0 ) ;
return 1 ;
}
return 0 ;
case HID_UP_DIGITIZER :
switch ( usage - > hid ) {
/* we do not want to map these for now */
case HID_DG_INVERT : /* value is always 0 */
case HID_DG_ERASER : /* value is always 0 */
case HID_DG_CONTACTID : /* value is useless */
case HID_DG_BARRELSWITCH : /* doubtful */
case HID_DG_INPUTMODE :
case HID_DG_DEVICEINDEX :
case HID_DG_CONTACTCOUNT :
case HID_DG_CONTACTMAX :
return - 1 ;
/* original mapping by Rafi Rubin */
case HID_DG_CONFIDENCE :
nt_map_key_clear ( BTN_TOOL_DOUBLETAP ) ;
return 1 ;
/* width/height mapped on TouchMajor/TouchMinor/Orientation */
case HID_DG_WIDTH :
hid_map_usage ( hi , usage , bit , max ,
EV_ABS , ABS_MT_TOUCH_MAJOR ) ;
return 1 ;
case HID_DG_HEIGHT :
hid_map_usage ( hi , usage , bit , max ,
EV_ABS , ABS_MT_TOUCH_MINOR ) ;
input_set_abs_params ( hi - > input , ABS_MT_ORIENTATION ,
0 , 1 , 0 , 0 ) ;
return 1 ;
}
return 0 ;
case 0xff000000 :
/* we do not want to map these: no input-oriented meaning */
return - 1 ;
2008-11-19 17:54:46 +03:00
}
2009-05-20 17:49:35 +04:00
2008-11-19 17:54:46 +03:00
return 0 ;
}
static int ntrig_input_mapped ( struct hid_device * hdev , struct hid_input * hi ,
struct hid_field * field , struct hid_usage * usage ,
unsigned long * * bit , int * max )
{
if ( usage - > type = = EV_KEY | | usage - > type = = EV_REL
| | usage - > type = = EV_ABS )
clear_bit ( usage - > code , * bit ) ;
return 0 ;
}
2009-05-20 17:49:35 +04:00
/*
* this function is called upon all reports
* so that we can filter contact point information ,
* decide whether we are in multi or single touch mode
* and call input_mt_sync after each point if necessary
*/
static int ntrig_event ( struct hid_device * hid , struct hid_field * field ,
struct hid_usage * usage , __s32 value )
{
struct input_dev * input = field - > hidinput - > input ;
struct ntrig_data * nd = hid_get_drvdata ( hid ) ;
if ( hid - > claimed & HID_CLAIMED_INPUT ) {
switch ( usage - > hid ) {
case HID_GD_X :
nd - > x = value ;
nd - > reading_a_point = 1 ;
break ;
case HID_GD_Y :
nd - > y = value ;
break ;
case HID_DG_CONTACTID :
nd - > id = value ;
/* we receive this only when in multitouch mode */
nd - > found_contact_id = 1 ;
break ;
case HID_DG_WIDTH :
nd - > w = value ;
break ;
case HID_DG_HEIGHT :
nd - > h = value ;
/*
* when in single touch mode , this is the last
* report received in a finger event . We want
* to emit a normal ( X , Y ) position
*/
if ( ! nd - > found_contact_id ) {
input_event ( input , EV_ABS , ABS_X , nd - > x ) ;
input_event ( input , EV_ABS , ABS_Y , nd - > y ) ;
}
break ;
case HID_DG_TIPPRESSURE :
/*
* when in single touch mode , this is the last
* report received in a pen event . We want
* to emit a normal ( X , Y ) position
*/
if ( ! nd - > found_contact_id ) {
input_event ( input , EV_ABS , ABS_X , nd - > x ) ;
input_event ( input , EV_ABS , ABS_Y , nd - > y ) ;
input_event ( input , EV_ABS , ABS_PRESSURE , value ) ;
}
break ;
case 0xff000002 :
/*
* we receive this when the device is in multitouch
* mode . The first of the three values tagged with
* this usage tells if the contact point is real
* or a placeholder
*/
if ( ! nd - > reading_a_point | | value ! = 1 )
break ;
/* emit a normal (X, Y) for the first point only */
if ( nd - > id = = 0 ) {
input_event ( input , EV_ABS , ABS_X , nd - > x ) ;
input_event ( input , EV_ABS , ABS_Y , nd - > y ) ;
}
input_event ( input , EV_ABS , ABS_MT_POSITION_X , nd - > x ) ;
input_event ( input , EV_ABS , ABS_MT_POSITION_Y , nd - > y ) ;
if ( nd - > w > nd - > h ) {
input_event ( input , EV_ABS ,
ABS_MT_ORIENTATION , 1 ) ;
input_event ( input , EV_ABS ,
ABS_MT_TOUCH_MAJOR , nd - > w ) ;
input_event ( input , EV_ABS ,
ABS_MT_TOUCH_MINOR , nd - > h ) ;
} else {
input_event ( input , EV_ABS ,
ABS_MT_ORIENTATION , 0 ) ;
input_event ( input , EV_ABS ,
ABS_MT_TOUCH_MAJOR , nd - > h ) ;
input_event ( input , EV_ABS ,
ABS_MT_TOUCH_MINOR , nd - > w ) ;
}
input_mt_sync ( field - > hidinput - > input ) ;
nd - > reading_a_point = 0 ;
nd - > found_contact_id = 0 ;
break ;
default :
/* fallback to the generic hidinput handling */
return 0 ;
}
}
/* we have handled the hidinput part, now remains hiddev */
if ( hid - > claimed & HID_CLAIMED_HIDDEV & & hid - > hiddev_hid_event )
hid - > hiddev_hid_event ( hid , field , usage , value ) ;
return 1 ;
}
static int ntrig_probe ( struct hid_device * hdev , const struct hid_device_id * id )
{
int ret ;
struct ntrig_data * nd ;
nd = kmalloc ( sizeof ( struct ntrig_data ) , GFP_KERNEL ) ;
if ( ! nd ) {
dev_err ( & hdev - > dev , " cannot allocate N-Trig data \n " ) ;
return - ENOMEM ;
}
nd - > reading_a_point = 0 ;
nd - > found_contact_id = 0 ;
hid_set_drvdata ( hdev , nd ) ;
ret = hid_parse ( hdev ) ;
if ( ! ret )
ret = hid_hw_start ( hdev , HID_CONNECT_DEFAULT ) ;
if ( ret )
kfree ( nd ) ;
return ret ;
}
static void ntrig_remove ( struct hid_device * hdev )
{
hid_hw_stop ( hdev ) ;
kfree ( hid_get_drvdata ( hdev ) ) ;
}
2008-11-19 17:54:46 +03:00
static const struct hid_device_id ntrig_devices [ ] = {
{ HID_USB_DEVICE ( USB_VENDOR_ID_NTRIG , USB_DEVICE_ID_NTRIG_TOUCH_SCREEN ) ,
. driver_data = NTRIG_DUPLICATE_USAGES } ,
{ }
} ;
MODULE_DEVICE_TABLE ( hid , ntrig_devices ) ;
2009-05-20 17:49:35 +04:00
static const struct hid_usage_id ntrig_grabbed_usages [ ] = {
{ HID_ANY_ID , HID_ANY_ID , HID_ANY_ID } ,
{ HID_ANY_ID - 1 , HID_ANY_ID - 1 , HID_ANY_ID - 1 }
} ;
2008-11-19 17:54:46 +03:00
static struct hid_driver ntrig_driver = {
. name = " ntrig " ,
. id_table = ntrig_devices ,
2009-05-20 17:49:35 +04:00
. probe = ntrig_probe ,
. remove = ntrig_remove ,
2008-11-19 17:54:46 +03:00
. input_mapping = ntrig_input_mapping ,
. input_mapped = ntrig_input_mapped ,
2009-05-20 17:49:35 +04:00
. usage_table = ntrig_grabbed_usages ,
. event = ntrig_event ,
2008-11-19 17:54:46 +03:00
} ;
static int ntrig_init ( void )
{
return hid_register_driver ( & ntrig_driver ) ;
}
static void ntrig_exit ( void )
{
hid_unregister_driver ( & ntrig_driver ) ;
}
module_init ( ntrig_init ) ;
module_exit ( ntrig_exit ) ;
MODULE_LICENSE ( " GPL " ) ;