2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-10-31 17:34:42 -07:00
/*
* Plantronics USB HID Driver
*
* Copyright ( c ) 2014 JD Cole < jd . cole @ plantronics . com >
2018-04-30 13:32:46 -07:00
* Copyright ( c ) 2015 - 2018 Terry Junge < terry . junge @ plantronics . com >
2014-10-31 17:34:42 -07:00
*/
/*
*/
# include "hid-ids.h"
# include <linux/hid.h>
# include <linux/module.h>
2021-02-07 16:47:40 +02:00
# include <linux/jiffies.h>
2014-10-31 17:34:42 -07:00
2015-06-08 14:27:57 -07:00
# define PLT_HID_1_0_PAGE 0xffa00000
# define PLT_HID_2_0_PAGE 0xffa20000
# define PLT_BASIC_TELEPHONY 0x0003
# define PLT_BASIC_EXCEPTION 0x0005
# define PLT_VOL_UP 0x00b1
# define PLT_VOL_DOWN 0x00b2
# define PLT1_VOL_UP (PLT_HID_1_0_PAGE | PLT_VOL_UP)
# define PLT1_VOL_DOWN (PLT_HID_1_0_PAGE | PLT_VOL_DOWN)
# define PLT2_VOL_UP (PLT_HID_2_0_PAGE | PLT_VOL_UP)
# define PLT2_VOL_DOWN (PLT_HID_2_0_PAGE | PLT_VOL_DOWN)
# define PLT_DA60 0xda60
# define PLT_BT300_MIN 0x0413
# define PLT_BT300_MAX 0x0418
# define PLT_ALLOW_CONSUMER (field->application == HID_CP_CONSUMERCONTROL && \
( usage - > hid & HID_USAGE_PAGE ) = = HID_UP_CONSUMER )
2021-02-07 16:47:40 +02:00
# define PLT_QUIRK_DOUBLE_VOLUME_KEYS BIT(0)
# define PLT_DOUBLE_KEY_TIMEOUT 5 /* ms */
struct plt_drv_data {
unsigned long device_type ;
unsigned long last_volume_key_ts ;
u32 quirks ;
} ;
2014-10-31 17:34:42 -07:00
static int plantronics_input_mapping ( struct hid_device * hdev ,
struct hid_input * hi ,
struct hid_field * field ,
struct hid_usage * usage ,
unsigned long * * bit , int * max )
{
2015-06-08 14:27:57 -07:00
unsigned short mapped_key ;
2021-02-07 16:47:40 +02:00
struct plt_drv_data * drv_data = hid_get_drvdata ( hdev ) ;
unsigned long plt_type = drv_data - > device_type ;
2015-06-08 14:27:57 -07:00
2018-04-30 13:32:46 -07:00
/* special case for PTT products */
if ( field - > application = = HID_GD_JOYSTICK )
goto defaulted ;
2015-06-08 14:27:57 -07:00
/* handle volume up/down mapping */
/* non-standard types or multi-HID interfaces - plt_type is PID */
if ( ! ( plt_type & HID_USAGE_PAGE ) ) {
switch ( plt_type ) {
case PLT_DA60 :
if ( PLT_ALLOW_CONSUMER )
goto defaulted ;
goto ignored ;
default :
if ( PLT_ALLOW_CONSUMER )
goto defaulted ;
}
}
/* handle standard types - plt_type is 0xffa0uuuu or 0xffa2uuuu */
/* 'basic telephony compliant' - allow default consumer page map */
else if ( ( plt_type & HID_USAGE ) > = PLT_BASIC_TELEPHONY & &
( plt_type & HID_USAGE ) ! = PLT_BASIC_EXCEPTION ) {
if ( PLT_ALLOW_CONSUMER )
goto defaulted ;
}
/* not 'basic telephony' - apply legacy mapping */
/* only map if the field is in the device's primary vendor page */
else if ( ! ( ( field - > application ^ plt_type ) & HID_USAGE_PAGE ) ) {
switch ( usage - > hid ) {
case PLT1_VOL_UP :
case PLT2_VOL_UP :
mapped_key = KEY_VOLUMEUP ;
goto mapped ;
case PLT1_VOL_DOWN :
case PLT2_VOL_DOWN :
mapped_key = KEY_VOLUMEDOWN ;
goto mapped ;
}
2014-10-31 17:34:42 -07:00
}
2015-06-08 14:27:57 -07:00
/*
* Future mapping of call control or other usages ,
* if and when keys are defined would go here
* otherwise , ignore everything else that was not mapped
*/
2014-10-31 17:34:42 -07:00
2015-06-08 14:27:57 -07:00
ignored :
2014-10-31 17:34:42 -07:00
return - 1 ;
2015-06-08 14:27:57 -07:00
defaulted :
hid_dbg ( hdev , " usage: %08x (appl: %08x) - defaulted \n " ,
usage - > hid , field - > application ) ;
return 0 ;
mapped :
hid_map_usage_clear ( hi , usage , bit , max , EV_KEY , mapped_key ) ;
hid_dbg ( hdev , " usage: %08x (appl: %08x) - mapped to key %d \n " ,
usage - > hid , field - > application , mapped_key ) ;
return 1 ;
}
2021-02-07 16:47:40 +02:00
static int plantronics_event ( struct hid_device * hdev , struct hid_field * field ,
struct hid_usage * usage , __s32 value )
{
struct plt_drv_data * drv_data = hid_get_drvdata ( hdev ) ;
if ( drv_data - > quirks & PLT_QUIRK_DOUBLE_VOLUME_KEYS ) {
unsigned long prev_ts , cur_ts ;
/* Usages are filtered in plantronics_usages. */
if ( ! value ) /* Handle key presses only. */
return 0 ;
prev_ts = drv_data - > last_volume_key_ts ;
cur_ts = jiffies ;
if ( jiffies_to_msecs ( cur_ts - prev_ts ) < = PLT_DOUBLE_KEY_TIMEOUT )
return 1 ; /* Ignore the repeated key. */
drv_data - > last_volume_key_ts = cur_ts ;
}
return 0 ;
}
2015-06-08 14:27:57 -07:00
static unsigned long plantronics_device_type ( struct hid_device * hdev )
{
unsigned i , col_page ;
unsigned long plt_type = hdev - > product ;
/* multi-HID interfaces? - plt_type is PID */
if ( plt_type > = PLT_BT300_MIN & & plt_type < = PLT_BT300_MAX )
goto exit ;
/* determine primary vendor page */
for ( i = 0 ; i < hdev - > maxcollection ; i + + ) {
col_page = hdev - > collection [ i ] . usage & HID_USAGE_PAGE ;
if ( col_page = = PLT_HID_2_0_PAGE ) {
plt_type = hdev - > collection [ i ] . usage ;
break ;
}
if ( col_page = = PLT_HID_1_0_PAGE )
plt_type = hdev - > collection [ i ] . usage ;
}
exit :
hid_dbg ( hdev , " plt_type decoded as: %08lx \n " , plt_type ) ;
return plt_type ;
}
static int plantronics_probe ( struct hid_device * hdev ,
const struct hid_device_id * id )
{
2021-02-07 16:47:40 +02:00
struct plt_drv_data * drv_data ;
2015-06-08 14:27:57 -07:00
int ret ;
2021-02-07 16:47:40 +02:00
drv_data = devm_kzalloc ( & hdev - > dev , sizeof ( * drv_data ) , GFP_KERNEL ) ;
if ( ! drv_data )
return - ENOMEM ;
2015-06-08 14:27:57 -07:00
ret = hid_parse ( hdev ) ;
if ( ret ) {
hid_err ( hdev , " parse failed \n " ) ;
goto err ;
}
2021-02-07 16:47:40 +02:00
drv_data - > device_type = plantronics_device_type ( hdev ) ;
drv_data - > quirks = id - > driver_data ;
drv_data - > last_volume_key_ts = jiffies - msecs_to_jiffies ( PLT_DOUBLE_KEY_TIMEOUT ) ;
hid_set_drvdata ( hdev , drv_data ) ;
2015-06-08 14:27:57 -07:00
ret = hid_hw_start ( hdev , HID_CONNECT_DEFAULT |
HID_CONNECT_HIDINPUT_FORCE | HID_CONNECT_HIDDEV_FORCE ) ;
if ( ret )
hid_err ( hdev , " hw start failed \n " ) ;
err :
return ret ;
2014-10-31 17:34:42 -07:00
}
static const struct hid_device_id plantronics_devices [ ] = {
2021-02-07 16:47:40 +02:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_PLANTRONICS ,
USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3220_SERIES ) ,
. driver_data = PLT_QUIRK_DOUBLE_VOLUME_KEYS } ,
2014-10-31 17:34:42 -07:00
{ HID_USB_DEVICE ( USB_VENDOR_ID_PLANTRONICS , HID_ANY_ID ) } ,
{ }
} ;
MODULE_DEVICE_TABLE ( hid , plantronics_devices ) ;
2021-02-07 16:47:40 +02:00
static const struct hid_usage_id plantronics_usages [ ] = {
{ HID_CP_VOLUMEUP , EV_KEY , HID_ANY_ID } ,
{ HID_CP_VOLUMEDOWN , EV_KEY , HID_ANY_ID } ,
{ HID_TERMINATOR , HID_TERMINATOR , HID_TERMINATOR }
} ;
2014-10-31 17:34:42 -07:00
static struct hid_driver plantronics_driver = {
. name = " plantronics " ,
. id_table = plantronics_devices ,
2021-02-07 16:47:40 +02:00
. usage_table = plantronics_usages ,
2014-10-31 17:34:42 -07:00
. input_mapping = plantronics_input_mapping ,
2021-02-07 16:47:40 +02:00
. event = plantronics_event ,
2015-06-08 14:27:57 -07:00
. probe = plantronics_probe ,
2014-10-31 17:34:42 -07:00
} ;
module_hid_driver ( plantronics_driver ) ;
MODULE_AUTHOR ( " JD Cole <jd.cole@plantronics.com> " ) ;
MODULE_AUTHOR ( " Terry Junge <terry.junge@plantronics.com> " ) ;
MODULE_DESCRIPTION ( " Plantronics USB HID Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;