2019-05-30 02:57:22 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-05-07 13:40:58 +04:00
/*
* HID driver for ELO usb touchscreen 4000 / 4500
*
* Copyright ( c ) 2013 Jiri Slaby
*
* Data parsing taken from elousb driver by Vojtech Pavlik .
*/
# include <linux/hid.h>
# include <linux/input.h>
# include <linux/module.h>
2013-05-07 13:40:59 +04:00
# include <linux/usb.h>
# include <linux/workqueue.h>
2013-05-07 13:40:58 +04:00
# include "hid-ids.h"
2013-05-07 13:40:59 +04:00
# define ELO_PERIODIC_READ_INTERVAL HZ
# define ELO_SMARTSET_CMD_TIMEOUT 2000 /* msec */
/* Elo SmartSet commands */
# define ELO_FLUSH_SMARTSET_RESPONSES 0x02 /* Flush all pending smartset responses */
# define ELO_SEND_SMARTSET_COMMAND 0x05 /* Send a smartset command */
# define ELO_GET_SMARTSET_RESPONSE 0x06 /* Get a smartset response */
# define ELO_DIAG 0x64 /* Diagnostics command */
# define ELO_SMARTSET_PACKET_SIZE 8
struct elo_priv {
struct usb_device * usbdev ;
struct delayed_work work ;
unsigned char buffer [ ELO_SMARTSET_PACKET_SIZE ] ;
} ;
static struct workqueue_struct * wq ;
static bool use_fw_quirk = true ;
module_param ( use_fw_quirk , bool , S_IRUGO ) ;
MODULE_PARM_DESC ( use_fw_quirk , " Do periodic pokes for broken M firmwares (default = true) " ) ;
2015-09-30 01:52:59 +03:00
static int elo_input_configured ( struct hid_device * hdev ,
2013-05-07 13:40:58 +04:00
struct hid_input * hidinput )
{
struct input_dev * input = hidinput - > input ;
2017-11-22 13:19:51 +03:00
/*
* ELO devices have one Button usage in GenDesk field , which makes
* hid - input map it to BTN_LEFT ; that confuses userspace , which then
* considers the device to be a mouse / touchpad instead of touchscreen .
*/
clear_bit ( BTN_LEFT , input - > keybit ) ;
2013-05-07 13:40:58 +04:00
set_bit ( BTN_TOUCH , input - > keybit ) ;
set_bit ( ABS_PRESSURE , input - > absbit ) ;
input_set_abs_params ( input , ABS_PRESSURE , 0 , 256 , 0 , 0 ) ;
2015-09-30 01:52:59 +03:00
return 0 ;
2013-05-07 13:40:58 +04:00
}
static void elo_process_data ( struct input_dev * input , const u8 * data , int size )
{
int press ;
input_report_abs ( input , ABS_X , ( data [ 3 ] < < 8 ) | data [ 2 ] ) ;
input_report_abs ( input , ABS_Y , ( data [ 5 ] < < 8 ) | data [ 4 ] ) ;
press = 0 ;
if ( data [ 1 ] & 0x80 )
press = ( data [ 7 ] < < 8 ) | data [ 6 ] ;
input_report_abs ( input , ABS_PRESSURE , press ) ;
if ( data [ 1 ] & 0x03 ) {
input_report_key ( input , BTN_TOUCH , 1 ) ;
input_sync ( input ) ;
}
if ( data [ 1 ] & 0x04 )
input_report_key ( input , BTN_TOUCH , 0 ) ;
input_sync ( input ) ;
}
static int elo_raw_event ( struct hid_device * hdev , struct hid_report * report ,
u8 * data , int size )
{
struct hid_input * hidinput ;
if ( ! ( hdev - > claimed & HID_CLAIMED_INPUT ) | | list_empty ( & hdev - > inputs ) )
return 0 ;
hidinput = list_first_entry ( & hdev - > inputs , struct hid_input , list ) ;
switch ( report - > id ) {
case 0 :
if ( data [ 0 ] = = ' T ' ) { /* Mandatory ELO packet marker */
elo_process_data ( hidinput - > input , data , size ) ;
return 1 ;
}
break ;
default : /* unknown report */
/* Unknown report type; pass upstream */
hid_info ( hdev , " unknown report type %d \n " , report - > id ) ;
break ;
}
return 0 ;
}
2013-05-07 13:40:59 +04:00
static int elo_smartset_send_get ( struct usb_device * dev , u8 command ,
void * data )
{
unsigned int pipe ;
u8 dir ;
if ( command = = ELO_SEND_SMARTSET_COMMAND ) {
pipe = usb_sndctrlpipe ( dev , 0 ) ;
dir = USB_DIR_OUT ;
} else if ( command = = ELO_GET_SMARTSET_RESPONSE ) {
pipe = usb_rcvctrlpipe ( dev , 0 ) ;
dir = USB_DIR_IN ;
} else
return - EINVAL ;
return usb_control_msg ( dev , pipe , command ,
dir | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
0 , 0 , data , ELO_SMARTSET_PACKET_SIZE ,
ELO_SMARTSET_CMD_TIMEOUT ) ;
}
static int elo_flush_smartset_responses ( struct usb_device * dev )
{
return usb_control_msg ( dev , usb_sndctrlpipe ( dev , 0 ) ,
ELO_FLUSH_SMARTSET_RESPONSES ,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
0 , 0 , NULL , 0 , USB_CTRL_SET_TIMEOUT ) ;
}
static void elo_work ( struct work_struct * work )
{
struct elo_priv * priv = container_of ( work , struct elo_priv , work . work ) ;
struct usb_device * dev = priv - > usbdev ;
unsigned char * buffer = priv - > buffer ;
int ret ;
ret = elo_flush_smartset_responses ( dev ) ;
if ( ret < 0 ) {
dev_err ( & dev - > dev , " initial FLUSH_SMARTSET_RESPONSES failed, error %d \n " ,
ret ) ;
goto fail ;
}
/* send Diagnostics command */
* buffer = ELO_DIAG ;
ret = elo_smartset_send_get ( dev , ELO_SEND_SMARTSET_COMMAND , buffer ) ;
if ( ret < 0 ) {
dev_err ( & dev - > dev , " send Diagnostics Command failed, error %d \n " ,
ret ) ;
goto fail ;
}
/* get the result */
ret = elo_smartset_send_get ( dev , ELO_GET_SMARTSET_RESPONSE , buffer ) ;
if ( ret < 0 ) {
dev_err ( & dev - > dev , " get Diagnostics Command response failed, error %d \n " ,
ret ) ;
goto fail ;
}
/* read the ack */
if ( * buffer ! = ' A ' ) {
ret = elo_smartset_send_get ( dev , ELO_GET_SMARTSET_RESPONSE ,
buffer ) ;
if ( ret < 0 ) {
dev_err ( & dev - > dev , " get acknowledge response failed, error %d \n " ,
ret ) ;
goto fail ;
}
}
fail :
ret = elo_flush_smartset_responses ( dev ) ;
if ( ret < 0 )
dev_err ( & dev - > dev , " final FLUSH_SMARTSET_RESPONSES failed, error %d \n " ,
ret ) ;
queue_delayed_work ( wq , & priv - > work , ELO_PERIODIC_READ_INTERVAL ) ;
}
/*
* Not all Elo devices need the periodic HID descriptor reads .
* Only firmware version M needs this .
*/
static bool elo_broken_firmware ( struct usb_device * dev )
{
2013-09-25 11:42:55 +04:00
struct usb_device * hub = dev - > parent ;
struct usb_device * child = NULL ;
u16 fw_lvl = le16_to_cpu ( dev - > descriptor . bcdDevice ) ;
u16 child_vid , child_pid ;
int i ;
if ( ! use_fw_quirk )
return false ;
if ( fw_lvl ! = 0x10d )
return false ;
/* iterate sibling devices of the touch controller */
usb_hub_for_each_child ( hub , i , child ) {
child_vid = le16_to_cpu ( child - > descriptor . idVendor ) ;
child_pid = le16_to_cpu ( child - > descriptor . idProduct ) ;
/*
* If one of the devices below is present attached as a sibling of
* the touch controller then this is a newer IBM 4820 monitor that
* does not need the IBM - requested workaround if fw level is
* 0x010d - aka ' M ' .
* No other HW can have this combination .
*/
if ( child_vid = = 0x04b3 ) {
switch ( child_pid ) {
case 0x4676 : /* 4820 21x Video */
case 0x4677 : /* 4820 51x Video */
case 0x4678 : /* 4820 2Lx Video */
case 0x4679 : /* 4820 5Lx Video */
return false ;
}
}
}
return true ;
2013-05-07 13:40:59 +04:00
}
2013-05-07 13:40:58 +04:00
static int elo_probe ( struct hid_device * hdev , const struct hid_device_id * id )
{
2013-05-07 13:40:59 +04:00
struct elo_priv * priv ;
2013-05-07 13:40:58 +04:00
int ret ;
2013-05-07 13:40:59 +04:00
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
INIT_DELAYED_WORK ( & priv - > work , elo_work ) ;
priv - > usbdev = interface_to_usbdev ( to_usb_interface ( hdev - > dev . parent ) ) ;
hid_set_drvdata ( hdev , priv ) ;
2013-05-07 13:40:58 +04:00
ret = hid_parse ( hdev ) ;
if ( ret ) {
hid_err ( hdev , " parse failed \n " ) ;
goto err_free ;
}
ret = hid_hw_start ( hdev , HID_CONNECT_DEFAULT ) ;
if ( ret ) {
hid_err ( hdev , " hw start failed \n " ) ;
goto err_free ;
}
2013-05-07 13:40:59 +04:00
if ( elo_broken_firmware ( priv - > usbdev ) ) {
hid_info ( hdev , " broken firmware found, installing workaround \n " ) ;
queue_delayed_work ( wq , & priv - > work , ELO_PERIODIC_READ_INTERVAL ) ;
}
2013-05-07 13:40:58 +04:00
return 0 ;
err_free :
2013-05-07 13:40:59 +04:00
kfree ( priv ) ;
2013-05-07 13:40:58 +04:00
return ret ;
}
static void elo_remove ( struct hid_device * hdev )
{
2013-05-07 13:40:59 +04:00
struct elo_priv * priv = hid_get_drvdata ( hdev ) ;
2013-05-07 13:40:58 +04:00
hid_hw_stop ( hdev ) ;
2016-05-31 15:48:15 +03:00
cancel_delayed_work_sync ( & priv - > work ) ;
2013-05-07 13:40:59 +04:00
kfree ( priv ) ;
2013-05-07 13:40:58 +04:00
}
static const struct hid_device_id elo_devices [ ] = {
{ HID_USB_DEVICE ( USB_VENDOR_ID_ELO , 0x0009 ) , } ,
{ HID_USB_DEVICE ( USB_VENDOR_ID_ELO , 0x0030 ) , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( hid , elo_devices ) ;
static struct hid_driver elo_driver = {
. name = " elo " ,
. id_table = elo_devices ,
. probe = elo_probe ,
. remove = elo_remove ,
. raw_event = elo_raw_event ,
. input_configured = elo_input_configured ,
} ;
static int __init elo_driver_init ( void )
{
2013-05-07 13:40:59 +04:00
int ret ;
wq = create_singlethread_workqueue ( " elousb " ) ;
if ( ! wq )
return - ENOMEM ;
ret = hid_register_driver ( & elo_driver ) ;
if ( ret )
destroy_workqueue ( wq ) ;
return ret ;
2013-05-07 13:40:58 +04:00
}
module_init ( elo_driver_init ) ;
static void __exit elo_driver_exit ( void )
{
hid_unregister_driver ( & elo_driver ) ;
2013-05-07 13:40:59 +04:00
destroy_workqueue ( wq ) ;
2013-05-07 13:40:58 +04:00
}
module_exit ( elo_driver_exit ) ;
MODULE_AUTHOR ( " Jiri Slaby <jslaby@suse.cz> " ) ;
MODULE_LICENSE ( " GPL " ) ;