2014-09-30 13:18:27 -04:00
/*
* HIDPP protocol for Logitech Unifying receivers
*
* Copyright ( c ) 2011 Logitech ( c )
* Copyright ( c ) 2012 - 2013 Google ( c )
* Copyright ( c ) 2013 - 2014 Red Hat Inc .
*/
/*
* 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 ; version 2 of the License .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/device.h>
# include <linux/hid.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/sched.h>
# include <linux/kfifo.h>
# include <linux/input/mt.h>
# include <asm/unaligned.h>
# include "hid-ids.h"
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Benjamin Tissoires <benjamin.tissoires@gmail.com> " ) ;
MODULE_AUTHOR ( " Nestor Lopez Casado <nlopezcasad@logitech.com> " ) ;
2015-03-26 12:41:57 -04:00
static bool disable_raw_mode ;
module_param ( disable_raw_mode , bool , 0644 ) ;
MODULE_PARM_DESC ( disable_raw_mode ,
" Disable Raw mode reporting for touchpads and keep firmware gestures. " ) ;
2015-09-03 09:08:30 -04:00
static bool disable_tap_to_click ;
module_param ( disable_tap_to_click , bool , 0644 ) ;
MODULE_PARM_DESC ( disable_tap_to_click ,
" Disable Tap-To-Click mode reporting for touchpads (only on the K400 currently). " ) ;
2014-09-30 13:18:27 -04:00
# define REPORT_ID_HIDPP_SHORT 0x10
# define REPORT_ID_HIDPP_LONG 0x11
# define HIDPP_REPORT_SHORT_LENGTH 7
# define HIDPP_REPORT_LONG_LENGTH 20
# define HIDPP_QUIRK_CLASS_WTP BIT(0)
2015-05-30 11:00:27 +02:00
# define HIDPP_QUIRK_CLASS_M560 BIT(1)
2015-09-03 09:08:30 -04:00
# define HIDPP_QUIRK_CLASS_K400 BIT(2)
2014-09-30 13:18:27 -04:00
2015-05-30 11:00:27 +02:00
/* bits 2..20 are reserved for classes */
2015-09-03 09:08:29 -04:00
# define HIDPP_QUIRK_CONNECT_EVENTS BIT(21)
2014-09-30 13:18:34 -04:00
# define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
2015-09-03 09:08:29 -04:00
# define HIDPP_QUIRK_NO_HIDINPUT BIT(23)
# define HIDPP_QUIRK_DELAYED_INIT (HIDPP_QUIRK_NO_HIDINPUT | \
HIDPP_QUIRK_CONNECT_EVENTS )
2014-09-30 13:18:32 -04:00
2014-09-30 13:18:27 -04:00
/*
* There are two hidpp protocols in use , the first version hidpp10 is known
* as register access protocol or RAP , the second version hidpp20 is known as
* feature access protocol or FAP
*
* Most older devices ( including the Unifying usb receiver ) use the RAP protocol
* where as most newer devices use the FAP protocol . Both protocols are
* compatible with the underlying transport , which could be usb , Unifiying , or
* bluetooth . The message lengths are defined by the hid vendor specific report
* descriptor for the HIDPP_SHORT report type ( total message lenth 7 bytes ) and
* the HIDPP_LONG report type ( total message length 20 bytes )
*
* The RAP protocol uses both report types , whereas the FAP only uses HIDPP_LONG
* messages . The Unifying receiver itself responds to RAP messages ( device index
* is 0xFF for the receiver ) , and all messages ( short or long ) with a device
* index between 1 and 6 are passed untouched to the corresponding paired
* Unifying device .
*
* The paired device can be RAP or FAP , it will receive the message untouched
* from the Unifiying receiver .
*/
struct fap {
u8 feature_index ;
u8 funcindex_clientid ;
u8 params [ HIDPP_REPORT_LONG_LENGTH - 4U ] ;
} ;
struct rap {
u8 sub_id ;
u8 reg_address ;
u8 params [ HIDPP_REPORT_LONG_LENGTH - 4U ] ;
} ;
struct hidpp_report {
u8 report_id ;
u8 device_index ;
union {
struct fap fap ;
struct rap rap ;
u8 rawbytes [ sizeof ( struct fap ) ] ;
} ;
} __packed ;
struct hidpp_device {
struct hid_device * hid_dev ;
struct mutex send_mutex ;
void * send_receive_buf ;
2015-01-08 14:37:12 -05:00
char * name ; /* will never be NULL and should not be freed */
2014-09-30 13:18:27 -04:00
wait_queue_head_t wait ;
bool answer_available ;
u8 protocol_major ;
u8 protocol_minor ;
void * private_data ;
2014-09-30 13:18:32 -04:00
struct work_struct work ;
struct kfifo delayed_work_fifo ;
atomic_t connected ;
struct input_dev * delayed_input ;
2014-09-30 13:18:27 -04:00
unsigned long quirks ;
} ;
2014-12-16 01:50:14 +01:00
/* HID++ 1.0 error codes */
2014-09-30 13:18:27 -04:00
# define HIDPP_ERROR 0x8f
# define HIDPP_ERROR_SUCCESS 0x00
# define HIDPP_ERROR_INVALID_SUBID 0x01
# define HIDPP_ERROR_INVALID_ADRESS 0x02
# define HIDPP_ERROR_INVALID_VALUE 0x03
# define HIDPP_ERROR_CONNECT_FAIL 0x04
# define HIDPP_ERROR_TOO_MANY_DEVICES 0x05
# define HIDPP_ERROR_ALREADY_EXISTS 0x06
# define HIDPP_ERROR_BUSY 0x07
# define HIDPP_ERROR_UNKNOWN_DEVICE 0x08
# define HIDPP_ERROR_RESOURCE_ERROR 0x09
# define HIDPP_ERROR_REQUEST_UNAVAILABLE 0x0a
# define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b
# define HIDPP_ERROR_WRONG_PIN_CODE 0x0c
2014-12-16 01:50:14 +01:00
/* HID++ 2.0 error codes */
# define HIDPP20_ERROR 0xff
2014-09-30 13:18:27 -04:00
2014-09-30 13:18:32 -04:00
static void hidpp_connect_event ( struct hidpp_device * hidpp_dev ) ;
2014-09-30 13:18:27 -04:00
static int __hidpp_send_report ( struct hid_device * hdev ,
struct hidpp_report * hidpp_report )
{
int fields_count , ret ;
switch ( hidpp_report - > report_id ) {
case REPORT_ID_HIDPP_SHORT :
fields_count = HIDPP_REPORT_SHORT_LENGTH ;
break ;
case REPORT_ID_HIDPP_LONG :
fields_count = HIDPP_REPORT_LONG_LENGTH ;
break ;
default :
return - ENODEV ;
}
/*
* set the device_index as the receiver , it will be overwritten by
* hid_hw_request if needed
*/
hidpp_report - > device_index = 0xff ;
ret = hid_hw_raw_request ( hdev , hidpp_report - > report_id ,
( u8 * ) hidpp_report , fields_count , HID_OUTPUT_REPORT ,
HID_REQ_SET_REPORT ) ;
return ret = = fields_count ? 0 : - 1 ;
}
2014-11-03 16:09:58 -05:00
/**
* hidpp_send_message_sync ( ) returns 0 in case of success , and something else
* in case of a failure .
* - If ' something else ' is positive , that means that an error has been raised
* by the protocol itself .
* - If ' something else ' is negative , that means that we had a classic error
* ( - ENOMEM , - EPIPE , etc . . . )
*/
2014-09-30 13:18:27 -04:00
static int hidpp_send_message_sync ( struct hidpp_device * hidpp ,
struct hidpp_report * message ,
struct hidpp_report * response )
{
int ret ;
mutex_lock ( & hidpp - > send_mutex ) ;
hidpp - > send_receive_buf = response ;
hidpp - > answer_available = false ;
/*
* So that we can later validate the answer when it arrives
* in hidpp_raw_event
*/
* response = * message ;
ret = __hidpp_send_report ( hidpp - > hid_dev , message ) ;
if ( ret ) {
dbg_hid ( " __hidpp_send_report returned err: %d \n " , ret ) ;
memset ( response , 0 , sizeof ( struct hidpp_report ) ) ;
goto exit ;
}
if ( ! wait_event_timeout ( hidpp - > wait , hidpp - > answer_available ,
5 * HZ ) ) {
dbg_hid ( " %s:timeout waiting for response \n " , __func__ ) ;
memset ( response , 0 , sizeof ( struct hidpp_report ) ) ;
ret = - ETIMEDOUT ;
}
if ( response - > report_id = = REPORT_ID_HIDPP_SHORT & &
2014-12-16 01:50:14 +01:00
response - > rap . sub_id = = HIDPP_ERROR ) {
ret = response - > rap . params [ 1 ] ;
dbg_hid ( " %s:got hidpp error %02X \n " , __func__ , ret ) ;
goto exit ;
}
if ( response - > report_id = = REPORT_ID_HIDPP_LONG & &
response - > fap . feature_index = = HIDPP20_ERROR ) {
2014-09-30 13:18:27 -04:00
ret = response - > fap . params [ 1 ] ;
2014-12-16 01:50:14 +01:00
dbg_hid ( " %s:got hidpp 2.0 error %02X \n " , __func__ , ret ) ;
2014-09-30 13:18:27 -04:00
goto exit ;
}
exit :
mutex_unlock ( & hidpp - > send_mutex ) ;
return ret ;
}
static int hidpp_send_fap_command_sync ( struct hidpp_device * hidpp ,
u8 feat_index , u8 funcindex_clientid , u8 * params , int param_count ,
struct hidpp_report * response )
{
2014-10-31 12:14:39 +03:00
struct hidpp_report * message ;
2014-09-30 13:18:27 -04:00
int ret ;
if ( param_count > sizeof ( message - > fap . params ) )
return - EINVAL ;
2014-10-31 12:14:39 +03:00
message = kzalloc ( sizeof ( struct hidpp_report ) , GFP_KERNEL ) ;
if ( ! message )
return - ENOMEM ;
2014-09-30 13:18:27 -04:00
message - > report_id = REPORT_ID_HIDPP_LONG ;
message - > fap . feature_index = feat_index ;
message - > fap . funcindex_clientid = funcindex_clientid ;
memcpy ( & message - > fap . params , params , param_count ) ;
ret = hidpp_send_message_sync ( hidpp , message , response ) ;
kfree ( message ) ;
return ret ;
}
2014-09-30 13:18:30 -04:00
static int hidpp_send_rap_command_sync ( struct hidpp_device * hidpp_dev ,
u8 report_id , u8 sub_id , u8 reg_address , u8 * params , int param_count ,
struct hidpp_report * response )
{
2014-10-31 12:14:39 +03:00
struct hidpp_report * message ;
2014-09-30 13:18:30 -04:00
int ret ;
if ( ( report_id ! = REPORT_ID_HIDPP_SHORT ) & &
( report_id ! = REPORT_ID_HIDPP_LONG ) )
return - EINVAL ;
if ( param_count > sizeof ( message - > rap . params ) )
return - EINVAL ;
2014-10-31 12:14:39 +03:00
message = kzalloc ( sizeof ( struct hidpp_report ) , GFP_KERNEL ) ;
if ( ! message )
return - ENOMEM ;
2014-09-30 13:18:30 -04:00
message - > report_id = report_id ;
message - > rap . sub_id = sub_id ;
message - > rap . reg_address = reg_address ;
memcpy ( & message - > rap . params , params , param_count ) ;
ret = hidpp_send_message_sync ( hidpp_dev , message , response ) ;
kfree ( message ) ;
return ret ;
}
2014-09-30 13:18:32 -04:00
static void delayed_work_cb ( struct work_struct * work )
{
struct hidpp_device * hidpp = container_of ( work , struct hidpp_device ,
work ) ;
hidpp_connect_event ( hidpp ) ;
}
2014-09-30 13:18:27 -04:00
static inline bool hidpp_match_answer ( struct hidpp_report * question ,
struct hidpp_report * answer )
{
return ( answer - > fap . feature_index = = question - > fap . feature_index ) & &
( answer - > fap . funcindex_clientid = = question - > fap . funcindex_clientid ) ;
}
static inline bool hidpp_match_error ( struct hidpp_report * question ,
struct hidpp_report * answer )
{
2014-12-16 01:50:14 +01:00
return ( ( answer - > rap . sub_id = = HIDPP_ERROR ) | |
( answer - > fap . feature_index = = HIDPP20_ERROR ) ) & &
2014-09-30 13:18:27 -04:00
( answer - > fap . funcindex_clientid = = question - > fap . feature_index ) & &
( answer - > fap . params [ 0 ] = = question - > fap . funcindex_clientid ) ;
}
2014-09-30 13:18:32 -04:00
static inline bool hidpp_report_is_connect_event ( struct hidpp_report * report )
{
return ( report - > report_id = = REPORT_ID_HIDPP_SHORT ) & &
( report - > rap . sub_id = = 0x41 ) ;
}
2014-12-11 17:39:59 -05:00
/**
* hidpp_prefix_name ( ) prefixes the current given name with " Logitech " .
*/
static void hidpp_prefix_name ( char * * name , int name_length )
{
# define PREFIX_LENGTH 9 /* "Logitech " */
int new_length ;
char * new_name ;
if ( name_length > PREFIX_LENGTH & &
strncmp ( * name , " Logitech " , PREFIX_LENGTH ) = = 0 )
/* The prefix has is already in the name */
return ;
new_length = PREFIX_LENGTH + name_length ;
new_name = kzalloc ( new_length , GFP_KERNEL ) ;
if ( ! new_name )
return ;
snprintf ( new_name , new_length , " Logitech %s " , * name ) ;
kfree ( * name ) ;
* name = new_name ;
}
2014-09-30 13:18:30 -04:00
/* -------------------------------------------------------------------------- */
/* HIDP++ 1.0 commands */
/* -------------------------------------------------------------------------- */
# define HIDPP_SET_REGISTER 0x80
# define HIDPP_GET_REGISTER 0x81
# define HIDPP_SET_LONG_REGISTER 0x82
# define HIDPP_GET_LONG_REGISTER 0x83
# define HIDPP_REG_PAIRING_INFORMATION 0xB5
# define DEVICE_NAME 0x40
static char * hidpp_get_unifying_name ( struct hidpp_device * hidpp_dev )
{
struct hidpp_report response ;
int ret ;
/* hid-logitech-dj is in charge of setting the right device index */
u8 params [ 1 ] = { DEVICE_NAME } ;
char * name ;
int len ;
ret = hidpp_send_rap_command_sync ( hidpp_dev ,
REPORT_ID_HIDPP_SHORT ,
HIDPP_GET_LONG_REGISTER ,
HIDPP_REG_PAIRING_INFORMATION ,
params , 1 , & response ) ;
if ( ret )
return NULL ;
len = response . rap . params [ 1 ] ;
2014-12-11 13:51:19 +01:00
if ( 2 + len > sizeof ( response . rap . params ) )
return NULL ;
2014-09-30 13:18:30 -04:00
name = kzalloc ( len + 1 , GFP_KERNEL ) ;
if ( ! name )
return NULL ;
memcpy ( name , & response . rap . params [ 2 ] , len ) ;
2014-12-11 17:39:59 -05:00
/* include the terminating '\0' */
hidpp_prefix_name ( & name , len + 1 ) ;
2014-09-30 13:18:30 -04:00
return name ;
}
2014-09-30 13:18:27 -04:00
/* -------------------------------------------------------------------------- */
/* 0x0000: Root */
/* -------------------------------------------------------------------------- */
# define HIDPP_PAGE_ROOT 0x0000
# define HIDPP_PAGE_ROOT_IDX 0x00
# define CMD_ROOT_GET_FEATURE 0x01
# define CMD_ROOT_GET_PROTOCOL_VERSION 0x11
static int hidpp_root_get_feature ( struct hidpp_device * hidpp , u16 feature ,
u8 * feature_index , u8 * feature_type )
{
struct hidpp_report response ;
int ret ;
u8 params [ 2 ] = { feature > > 8 , feature & 0x00FF } ;
ret = hidpp_send_fap_command_sync ( hidpp ,
HIDPP_PAGE_ROOT_IDX ,
CMD_ROOT_GET_FEATURE ,
params , 2 , & response ) ;
if ( ret )
return ret ;
* feature_index = response . fap . params [ 0 ] ;
* feature_type = response . fap . params [ 1 ] ;
return ret ;
}
static int hidpp_root_get_protocol_version ( struct hidpp_device * hidpp )
{
struct hidpp_report response ;
int ret ;
ret = hidpp_send_fap_command_sync ( hidpp ,
HIDPP_PAGE_ROOT_IDX ,
CMD_ROOT_GET_PROTOCOL_VERSION ,
NULL , 0 , & response ) ;
2014-11-03 16:09:59 -05:00
if ( ret = = HIDPP_ERROR_INVALID_SUBID ) {
2014-09-30 13:18:27 -04:00
hidpp - > protocol_major = 1 ;
hidpp - > protocol_minor = 0 ;
return 0 ;
}
2014-11-03 16:09:59 -05:00
/* the device might not be connected */
if ( ret = = HIDPP_ERROR_RESOURCE_ERROR )
return - EIO ;
2014-11-03 16:09:58 -05:00
if ( ret > 0 ) {
hid_err ( hidpp - > hid_dev , " %s: received protocol error 0x%02x \n " ,
__func__ , ret ) ;
return - EPROTO ;
}
2014-09-30 13:18:27 -04:00
if ( ret )
2014-11-03 16:09:58 -05:00
return ret ;
2014-09-30 13:18:27 -04:00
hidpp - > protocol_major = response . fap . params [ 0 ] ;
hidpp - > protocol_minor = response . fap . params [ 1 ] ;
return ret ;
}
static bool hidpp_is_connected ( struct hidpp_device * hidpp )
{
int ret ;
ret = hidpp_root_get_protocol_version ( hidpp ) ;
if ( ! ret )
hid_dbg ( hidpp - > hid_dev , " HID++ %u.%u device connected. \n " ,
hidpp - > protocol_major , hidpp - > protocol_minor ) ;
return ret = = 0 ;
}
/* -------------------------------------------------------------------------- */
/* 0x0005: GetDeviceNameType */
/* -------------------------------------------------------------------------- */
# define HIDPP_PAGE_GET_DEVICE_NAME_TYPE 0x0005
# define CMD_GET_DEVICE_NAME_TYPE_GET_COUNT 0x01
# define CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME 0x11
# define CMD_GET_DEVICE_NAME_TYPE_GET_TYPE 0x21
static int hidpp_devicenametype_get_count ( struct hidpp_device * hidpp ,
u8 feature_index , u8 * nameLength )
{
struct hidpp_report response ;
int ret ;
ret = hidpp_send_fap_command_sync ( hidpp , feature_index ,
CMD_GET_DEVICE_NAME_TYPE_GET_COUNT , NULL , 0 , & response ) ;
2014-11-03 16:09:58 -05:00
if ( ret > 0 ) {
hid_err ( hidpp - > hid_dev , " %s: received protocol error 0x%02x \n " ,
__func__ , ret ) ;
return - EPROTO ;
}
2014-09-30 13:18:27 -04:00
if ( ret )
2014-11-03 16:09:58 -05:00
return ret ;
2014-09-30 13:18:27 -04:00
* nameLength = response . fap . params [ 0 ] ;
return ret ;
}
static int hidpp_devicenametype_get_device_name ( struct hidpp_device * hidpp ,
u8 feature_index , u8 char_index , char * device_name , int len_buf )
{
struct hidpp_report response ;
int ret , i ;
int count ;
ret = hidpp_send_fap_command_sync ( hidpp , feature_index ,
CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME , & char_index , 1 ,
& response ) ;
2014-11-03 16:09:58 -05:00
if ( ret > 0 ) {
hid_err ( hidpp - > hid_dev , " %s: received protocol error 0x%02x \n " ,
__func__ , ret ) ;
return - EPROTO ;
}
2014-09-30 13:18:27 -04:00
if ( ret )
2014-11-03 16:09:58 -05:00
return ret ;
2014-09-30 13:18:27 -04:00
if ( response . report_id = = REPORT_ID_HIDPP_LONG )
count = HIDPP_REPORT_LONG_LENGTH - 4 ;
else
count = HIDPP_REPORT_SHORT_LENGTH - 4 ;
if ( len_buf < count )
count = len_buf ;
for ( i = 0 ; i < count ; i + + )
device_name [ i ] = response . fap . params [ i ] ;
return count ;
}
2014-12-11 13:51:17 +01:00
static char * hidpp_get_device_name ( struct hidpp_device * hidpp )
2014-09-30 13:18:27 -04:00
{
u8 feature_type ;
u8 feature_index ;
u8 __name_length ;
char * name ;
unsigned index = 0 ;
int ret ;
ret = hidpp_root_get_feature ( hidpp , HIDPP_PAGE_GET_DEVICE_NAME_TYPE ,
& feature_index , & feature_type ) ;
if ( ret )
2014-12-11 13:51:17 +01:00
return NULL ;
2014-09-30 13:18:27 -04:00
ret = hidpp_devicenametype_get_count ( hidpp , feature_index ,
& __name_length ) ;
if ( ret )
2014-12-11 13:51:17 +01:00
return NULL ;
2014-09-30 13:18:27 -04:00
name = kzalloc ( __name_length + 1 , GFP_KERNEL ) ;
if ( ! name )
2014-12-11 13:51:17 +01:00
return NULL ;
2014-09-30 13:18:27 -04:00
2014-12-11 13:51:18 +01:00
while ( index < __name_length ) {
ret = hidpp_devicenametype_get_device_name ( hidpp ,
2014-09-30 13:18:27 -04:00
feature_index , index , name + index ,
__name_length - index ) ;
2014-12-11 13:51:18 +01:00
if ( ret < = 0 ) {
kfree ( name ) ;
return NULL ;
}
index + = ret ;
}
2014-09-30 13:18:27 -04:00
2014-12-11 17:39:59 -05:00
/* include the terminating '\0' */
hidpp_prefix_name ( & name , __name_length + 1 ) ;
2014-09-30 13:18:27 -04:00
return name ;
}
2015-09-03 09:08:30 -04:00
/* -------------------------------------------------------------------------- */
/* 0x6010: Touchpad FW items */
/* -------------------------------------------------------------------------- */
# define HIDPP_PAGE_TOUCHPAD_FW_ITEMS 0x6010
# define CMD_TOUCHPAD_FW_ITEMS_SET 0x10
struct hidpp_touchpad_fw_items {
uint8_t presence ;
uint8_t desired_state ;
uint8_t state ;
uint8_t persistent ;
} ;
/**
* send a set state command to the device by reading the current items - > state
* field . items is then filled with the current state .
*/
static int hidpp_touchpad_fw_items_set ( struct hidpp_device * hidpp ,
u8 feature_index ,
struct hidpp_touchpad_fw_items * items )
{
struct hidpp_report response ;
int ret ;
u8 * params = ( u8 * ) response . fap . params ;
ret = hidpp_send_fap_command_sync ( hidpp , feature_index ,
CMD_TOUCHPAD_FW_ITEMS_SET , & items - > state , 1 , & response ) ;
if ( ret > 0 ) {
hid_err ( hidpp - > hid_dev , " %s: received protocol error 0x%02x \n " ,
__func__ , ret ) ;
return - EPROTO ;
}
if ( ret )
return ret ;
items - > presence = params [ 0 ] ;
items - > desired_state = params [ 1 ] ;
items - > state = params [ 2 ] ;
items - > persistent = params [ 3 ] ;
return 0 ;
}
2014-09-30 13:18:27 -04:00
/* -------------------------------------------------------------------------- */
/* 0x6100: TouchPadRawXY */
/* -------------------------------------------------------------------------- */
# define HIDPP_PAGE_TOUCHPAD_RAW_XY 0x6100
# define CMD_TOUCHPAD_GET_RAW_INFO 0x01
2014-09-30 13:18:33 -04:00
# define CMD_TOUCHPAD_SET_RAW_REPORT_STATE 0x21
# define EVENT_TOUCHPAD_RAW_XY 0x00
2014-09-30 13:18:27 -04:00
# define TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT 0x01
# define TOUCHPAD_RAW_XY_ORIGIN_UPPER_LEFT 0x03
struct hidpp_touchpad_raw_info {
u16 x_size ;
u16 y_size ;
u8 z_range ;
u8 area_range ;
u8 timestamp_unit ;
u8 maxcontacts ;
u8 origin ;
u16 res ;
} ;
struct hidpp_touchpad_raw_xy_finger {
u8 contact_type ;
u8 contact_status ;
u16 x ;
u16 y ;
u8 z ;
u8 area ;
u8 finger_id ;
} ;
struct hidpp_touchpad_raw_xy {
u16 timestamp ;
struct hidpp_touchpad_raw_xy_finger fingers [ 2 ] ;
u8 spurious_flag ;
u8 end_of_frame ;
u8 finger_count ;
u8 button ;
} ;
static int hidpp_touchpad_get_raw_info ( struct hidpp_device * hidpp ,
u8 feature_index , struct hidpp_touchpad_raw_info * raw_info )
{
struct hidpp_report response ;
int ret ;
u8 * params = ( u8 * ) response . fap . params ;
ret = hidpp_send_fap_command_sync ( hidpp , feature_index ,
CMD_TOUCHPAD_GET_RAW_INFO , NULL , 0 , & response ) ;
2014-11-03 16:09:58 -05:00
if ( ret > 0 ) {
hid_err ( hidpp - > hid_dev , " %s: received protocol error 0x%02x \n " ,
__func__ , ret ) ;
return - EPROTO ;
}
2014-09-30 13:18:27 -04:00
if ( ret )
2014-11-03 16:09:58 -05:00
return ret ;
2014-09-30 13:18:27 -04:00
raw_info - > x_size = get_unaligned_be16 ( & params [ 0 ] ) ;
raw_info - > y_size = get_unaligned_be16 ( & params [ 2 ] ) ;
raw_info - > z_range = params [ 4 ] ;
raw_info - > area_range = params [ 5 ] ;
raw_info - > maxcontacts = params [ 7 ] ;
raw_info - > origin = params [ 8 ] ;
/* res is given in unit per inch */
raw_info - > res = get_unaligned_be16 ( & params [ 13 ] ) * 2 / 51 ;
return ret ;
}
2014-09-30 13:18:33 -04:00
static int hidpp_touchpad_set_raw_report_state ( struct hidpp_device * hidpp_dev ,
u8 feature_index , bool send_raw_reports ,
bool sensor_enhanced_settings )
{
struct hidpp_report response ;
/*
* Params :
* bit 0 - enable raw
* bit 1 - 16 bit Z , no area
* bit 2 - enhanced sensitivity
* bit 3 - width , height ( 4 bits each ) instead of area
* bit 4 - send raw + gestures ( degrades smoothness )
* remaining bits - reserved
*/
u8 params = send_raw_reports | ( sensor_enhanced_settings < < 2 ) ;
return hidpp_send_fap_command_sync ( hidpp_dev , feature_index ,
CMD_TOUCHPAD_SET_RAW_REPORT_STATE , & params , 1 , & response ) ;
}
static void hidpp_touchpad_touch_event ( u8 * data ,
struct hidpp_touchpad_raw_xy_finger * finger )
{
u8 x_m = data [ 0 ] < < 2 ;
u8 y_m = data [ 2 ] < < 2 ;
finger - > x = x_m < < 6 | data [ 1 ] ;
finger - > y = y_m < < 6 | data [ 3 ] ;
finger - > contact_type = data [ 0 ] > > 6 ;
finger - > contact_status = data [ 2 ] > > 6 ;
finger - > z = data [ 4 ] ;
finger - > area = data [ 5 ] ;
finger - > finger_id = data [ 6 ] > > 4 ;
}
static void hidpp_touchpad_raw_xy_event ( struct hidpp_device * hidpp_dev ,
u8 * data , struct hidpp_touchpad_raw_xy * raw_xy )
{
memset ( raw_xy , 0 , sizeof ( struct hidpp_touchpad_raw_xy ) ) ;
raw_xy - > end_of_frame = data [ 8 ] & 0x01 ;
raw_xy - > spurious_flag = ( data [ 8 ] > > 1 ) & 0x01 ;
raw_xy - > finger_count = data [ 15 ] & 0x0f ;
raw_xy - > button = ( data [ 8 ] > > 2 ) & 0x01 ;
if ( raw_xy - > finger_count ) {
hidpp_touchpad_touch_event ( & data [ 2 ] , & raw_xy - > fingers [ 0 ] ) ;
hidpp_touchpad_touch_event ( & data [ 9 ] , & raw_xy - > fingers [ 1 ] ) ;
}
}
2014-09-30 13:18:27 -04:00
/* ************************************************************************** */
/* */
/* Device Support */
/* */
/* ************************************************************************** */
/* -------------------------------------------------------------------------- */
/* Touchpad HID++ devices */
/* -------------------------------------------------------------------------- */
2014-09-30 13:18:34 -04:00
# define WTP_MANUAL_RESOLUTION 39
2014-09-30 13:18:27 -04:00
struct wtp_data {
struct input_dev * input ;
u16 x_size , y_size ;
u8 finger_count ;
u8 mt_feature_index ;
u8 button_feature_index ;
u8 maxcontacts ;
bool flip_y ;
unsigned int resolution ;
} ;
static int wtp_input_mapping ( struct hid_device * hdev , struct hid_input * hi ,
struct hid_field * field , struct hid_usage * usage ,
unsigned long * * bit , int * max )
{
return - 1 ;
}
2014-09-30 13:18:32 -04:00
static void wtp_populate_input ( struct hidpp_device * hidpp ,
struct input_dev * input_dev , bool origin_is_hid_core )
2014-09-30 13:18:27 -04:00
{
struct wtp_data * wd = hidpp - > private_data ;
__set_bit ( EV_ABS , input_dev - > evbit ) ;
__set_bit ( EV_KEY , input_dev - > evbit ) ;
__clear_bit ( EV_REL , input_dev - > evbit ) ;
__clear_bit ( EV_LED , input_dev - > evbit ) ;
input_set_abs_params ( input_dev , ABS_MT_POSITION_X , 0 , wd - > x_size , 0 , 0 ) ;
input_abs_set_res ( input_dev , ABS_MT_POSITION_X , wd - > resolution ) ;
input_set_abs_params ( input_dev , ABS_MT_POSITION_Y , 0 , wd - > y_size , 0 , 0 ) ;
input_abs_set_res ( input_dev , ABS_MT_POSITION_Y , wd - > resolution ) ;
/* Max pressure is not given by the devices, pick one */
input_set_abs_params ( input_dev , ABS_MT_PRESSURE , 0 , 50 , 0 , 0 ) ;
input_set_capability ( input_dev , EV_KEY , BTN_LEFT ) ;
2014-09-30 13:18:34 -04:00
if ( hidpp - > quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS )
input_set_capability ( input_dev , EV_KEY , BTN_RIGHT ) ;
else
__set_bit ( INPUT_PROP_BUTTONPAD , input_dev - > propbit ) ;
2014-09-30 13:18:27 -04:00
input_mt_init_slots ( input_dev , wd - > maxcontacts , INPUT_MT_POINTER |
INPUT_MT_DROP_UNUSED ) ;
wd - > input = input_dev ;
}
static void wtp_touch_event ( struct wtp_data * wd ,
struct hidpp_touchpad_raw_xy_finger * touch_report )
{
int slot ;
if ( ! touch_report - > finger_id | | touch_report - > contact_type )
/* no actual data */
return ;
slot = input_mt_get_slot_by_key ( wd - > input , touch_report - > finger_id ) ;
input_mt_slot ( wd - > input , slot ) ;
input_mt_report_slot_state ( wd - > input , MT_TOOL_FINGER ,
touch_report - > contact_status ) ;
if ( touch_report - > contact_status ) {
input_event ( wd - > input , EV_ABS , ABS_MT_POSITION_X ,
touch_report - > x ) ;
input_event ( wd - > input , EV_ABS , ABS_MT_POSITION_Y ,
wd - > flip_y ? wd - > y_size - touch_report - > y :
touch_report - > y ) ;
input_event ( wd - > input , EV_ABS , ABS_MT_PRESSURE ,
touch_report - > area ) ;
}
}
static void wtp_send_raw_xy_event ( struct hidpp_device * hidpp ,
struct hidpp_touchpad_raw_xy * raw )
{
struct wtp_data * wd = hidpp - > private_data ;
int i ;
for ( i = 0 ; i < 2 ; i + + )
wtp_touch_event ( wd , & ( raw - > fingers [ i ] ) ) ;
2014-09-30 13:18:34 -04:00
if ( raw - > end_of_frame & &
! ( hidpp - > quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS ) )
2014-09-30 13:18:27 -04:00
input_event ( wd - > input , EV_KEY , BTN_LEFT , raw - > button ) ;
if ( raw - > end_of_frame | | raw - > finger_count < = 2 ) {
input_mt_sync_frame ( wd - > input ) ;
input_sync ( wd - > input ) ;
}
}
static int wtp_mouse_raw_xy_event ( struct hidpp_device * hidpp , u8 * data )
{
struct wtp_data * wd = hidpp - > private_data ;
u8 c1_area = ( ( data [ 7 ] & 0xf ) * ( data [ 7 ] & 0xf ) +
( data [ 7 ] > > 4 ) * ( data [ 7 ] > > 4 ) ) / 2 ;
u8 c2_area = ( ( data [ 13 ] & 0xf ) * ( data [ 13 ] & 0xf ) +
( data [ 13 ] > > 4 ) * ( data [ 13 ] > > 4 ) ) / 2 ;
struct hidpp_touchpad_raw_xy raw = {
. timestamp = data [ 1 ] ,
. fingers = {
{
. contact_type = 0 ,
. contact_status = ! ! data [ 7 ] ,
. x = get_unaligned_le16 ( & data [ 3 ] ) ,
. y = get_unaligned_le16 ( & data [ 5 ] ) ,
. z = c1_area ,
. area = c1_area ,
. finger_id = data [ 2 ] ,
} , {
. contact_type = 0 ,
. contact_status = ! ! data [ 13 ] ,
. x = get_unaligned_le16 ( & data [ 9 ] ) ,
. y = get_unaligned_le16 ( & data [ 11 ] ) ,
. z = c2_area ,
. area = c2_area ,
. finger_id = data [ 8 ] ,
}
} ,
. finger_count = wd - > maxcontacts ,
. spurious_flag = 0 ,
. end_of_frame = ( data [ 0 ] > > 7 ) = = 0 ,
. button = data [ 0 ] & 0x01 ,
} ;
wtp_send_raw_xy_event ( hidpp , & raw ) ;
return 1 ;
}
static int wtp_raw_event ( struct hid_device * hdev , u8 * data , int size )
{
struct hidpp_device * hidpp = hid_get_drvdata ( hdev ) ;
struct wtp_data * wd = hidpp - > private_data ;
2014-09-30 13:18:33 -04:00
struct hidpp_report * report = ( struct hidpp_report * ) data ;
struct hidpp_touchpad_raw_xy raw ;
2014-09-30 13:18:27 -04:00
2014-09-30 13:18:33 -04:00
if ( ! wd | | ! wd - > input )
2014-09-30 13:18:27 -04:00
return 1 ;
2014-09-30 13:18:33 -04:00
switch ( data [ 0 ] ) {
case 0x02 :
2014-12-16 16:55:22 +01:00
if ( size < 2 ) {
hid_err ( hdev , " Received HID report of bad size (%d) " ,
size ) ;
return 1 ;
}
2014-09-30 13:18:34 -04:00
if ( hidpp - > quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS ) {
input_event ( wd - > input , EV_KEY , BTN_LEFT ,
! ! ( data [ 1 ] & 0x01 ) ) ;
input_event ( wd - > input , EV_KEY , BTN_RIGHT ,
! ! ( data [ 1 ] & 0x02 ) ) ;
input_sync ( wd - > input ) ;
2014-12-16 01:50:16 +01:00
return 0 ;
2014-09-30 13:18:34 -04:00
} else {
if ( size < 21 )
return 1 ;
return wtp_mouse_raw_xy_event ( hidpp , & data [ 7 ] ) ;
}
2014-09-30 13:18:33 -04:00
case REPORT_ID_HIDPP_LONG :
2014-12-16 16:55:22 +01:00
/* size is already checked in hidpp_raw_event. */
2014-09-30 13:18:33 -04:00
if ( ( report - > fap . feature_index ! = wd - > mt_feature_index ) | |
( report - > fap . funcindex_clientid ! = EVENT_TOUCHPAD_RAW_XY ) )
return 1 ;
hidpp_touchpad_raw_xy_event ( hidpp , data + 4 , & raw ) ;
wtp_send_raw_xy_event ( hidpp , & raw ) ;
return 0 ;
}
return 0 ;
2014-09-30 13:18:27 -04:00
}
static int wtp_get_config ( struct hidpp_device * hidpp )
{
struct wtp_data * wd = hidpp - > private_data ;
struct hidpp_touchpad_raw_info raw_info = { 0 } ;
u8 feature_type ;
int ret ;
ret = hidpp_root_get_feature ( hidpp , HIDPP_PAGE_TOUCHPAD_RAW_XY ,
& wd - > mt_feature_index , & feature_type ) ;
if ( ret )
/* means that the device is not powered up */
return ret ;
ret = hidpp_touchpad_get_raw_info ( hidpp , wd - > mt_feature_index ,
& raw_info ) ;
if ( ret )
return ret ;
wd - > x_size = raw_info . x_size ;
wd - > y_size = raw_info . y_size ;
wd - > maxcontacts = raw_info . maxcontacts ;
wd - > flip_y = raw_info . origin = = TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT ;
wd - > resolution = raw_info . res ;
2014-09-30 13:18:34 -04:00
if ( ! wd - > resolution )
wd - > resolution = WTP_MANUAL_RESOLUTION ;
2014-09-30 13:18:27 -04:00
return 0 ;
}
static int wtp_allocate ( struct hid_device * hdev , const struct hid_device_id * id )
{
struct hidpp_device * hidpp = hid_get_drvdata ( hdev ) ;
struct wtp_data * wd ;
wd = devm_kzalloc ( & hdev - > dev , sizeof ( struct wtp_data ) ,
GFP_KERNEL ) ;
if ( ! wd )
return - ENOMEM ;
hidpp - > private_data = wd ;
return 0 ;
} ;
2014-12-16 17:06:01 -05:00
static int wtp_connect ( struct hid_device * hdev , bool connected )
2014-09-30 13:18:33 -04:00
{
struct hidpp_device * hidpp = hid_get_drvdata ( hdev ) ;
struct wtp_data * wd = hidpp - > private_data ;
int ret ;
if ( ! connected )
2014-12-16 17:06:01 -05:00
return 0 ;
2014-09-30 13:18:33 -04:00
if ( ! wd - > x_size ) {
ret = wtp_get_config ( hidpp ) ;
if ( ret ) {
hid_err ( hdev , " Can not get wtp config: %d \n " , ret ) ;
2014-12-16 17:06:01 -05:00
return ret ;
2014-09-30 13:18:33 -04:00
}
}
2014-12-16 17:06:01 -05:00
return hidpp_touchpad_set_raw_report_state ( hidpp , wd - > mt_feature_index ,
2014-09-30 13:18:33 -04:00
true , true ) ;
}
2015-05-30 11:00:27 +02:00
/* ------------------------------------------------------------------------- */
/* Logitech M560 devices */
/* ------------------------------------------------------------------------- */
/*
* Logitech M560 protocol overview
*
* The Logitech M560 mouse , is designed for windows 8. When the middle and / or
* the sides buttons are pressed , it sends some keyboard keys events
* instead of buttons ones .
* To complicate things further , the middle button keys sequence
* is different from the odd press and the even press .
*
* forward button - > Super_R
* backward button - > Super_L + ' d ' ( press only )
* middle button - > 1 st time : Alt_L + SuperL + XF86TouchpadOff ( press only )
* 2 nd time : left - click ( press only )
* NB : press - only means that when the button is pressed , the
* KeyPress / ButtonPress and KeyRelease / ButtonRelease events are generated
* together sequentially ; instead when the button is released , no event is
* generated !
*
* With the command
* 10 < xx > 0 a 3500 af03 ( where < xx > is the mouse id ) ,
* the mouse reacts differently :
* - it never sends a keyboard key event
* - for the three mouse button it sends :
* middle button press 11 < xx > 0 a 3500 af00 . . .
* side 1 button ( forward ) press 11 < xx > 0 a 3500 b000 . . .
* side 2 button ( backward ) press 11 < xx > 0 a 3500 ae00 . . .
* middle / side1 / side2 button release 11 < xx > 0 a 35000000. . .
*/
static const u8 m560_config_parameter [ ] = { 0x00 , 0xaf , 0x03 } ;
struct m560_private_data {
struct input_dev * input ;
} ;
/* how buttons are mapped in the report */
# define M560_MOUSE_BTN_LEFT 0x01
# define M560_MOUSE_BTN_RIGHT 0x02
# define M560_MOUSE_BTN_WHEEL_LEFT 0x08
# define M560_MOUSE_BTN_WHEEL_RIGHT 0x10
# define M560_SUB_ID 0x0a
# define M560_BUTTON_MODE_REGISTER 0x35
static int m560_send_config_command ( struct hid_device * hdev , bool connected )
{
struct hidpp_report response ;
struct hidpp_device * hidpp_dev ;
hidpp_dev = hid_get_drvdata ( hdev ) ;
if ( ! connected )
return - ENODEV ;
return hidpp_send_rap_command_sync (
hidpp_dev ,
REPORT_ID_HIDPP_SHORT ,
M560_SUB_ID ,
M560_BUTTON_MODE_REGISTER ,
( u8 * ) m560_config_parameter ,
sizeof ( m560_config_parameter ) ,
& response
) ;
}
static int m560_allocate ( struct hid_device * hdev )
{
struct hidpp_device * hidpp = hid_get_drvdata ( hdev ) ;
struct m560_private_data * d ;
d = devm_kzalloc ( & hdev - > dev , sizeof ( struct m560_private_data ) ,
GFP_KERNEL ) ;
if ( ! d )
return - ENOMEM ;
hidpp - > private_data = d ;
return 0 ;
} ;
static int m560_raw_event ( struct hid_device * hdev , u8 * data , int size )
{
struct hidpp_device * hidpp = hid_get_drvdata ( hdev ) ;
struct m560_private_data * mydata = hidpp - > private_data ;
/* sanity check */
if ( ! mydata | | ! mydata - > input ) {
hid_err ( hdev , " error in parameter \n " ) ;
return - EINVAL ;
}
if ( size < 7 ) {
hid_err ( hdev , " error in report \n " ) ;
return 0 ;
}
if ( data [ 0 ] = = REPORT_ID_HIDPP_LONG & &
data [ 2 ] = = M560_SUB_ID & & data [ 6 ] = = 0x00 ) {
/*
* m560 mouse report for middle , forward and backward button
*
* data [ 0 ] = 0x11
* data [ 1 ] = device - id
* data [ 2 ] = 0x0a
* data [ 5 ] = 0xaf - > middle
* 0xb0 - > forward
* 0xae - > backward
* 0x00 - > release all
* data [ 6 ] = 0x00
*/
switch ( data [ 5 ] ) {
case 0xaf :
input_report_key ( mydata - > input , BTN_MIDDLE , 1 ) ;
break ;
case 0xb0 :
input_report_key ( mydata - > input , BTN_FORWARD , 1 ) ;
break ;
case 0xae :
input_report_key ( mydata - > input , BTN_BACK , 1 ) ;
break ;
case 0x00 :
input_report_key ( mydata - > input , BTN_BACK , 0 ) ;
input_report_key ( mydata - > input , BTN_FORWARD , 0 ) ;
input_report_key ( mydata - > input , BTN_MIDDLE , 0 ) ;
break ;
default :
hid_err ( hdev , " error in report \n " ) ;
return 0 ;
}
input_sync ( mydata - > input ) ;
} else if ( data [ 0 ] = = 0x02 ) {
/*
* Logitech M560 mouse report
*
* data [ 0 ] = type ( 0x02 )
* data [ 1. .2 ] = buttons
* data [ 3. .5 ] = xy
* data [ 6 ] = wheel
*/
int v ;
input_report_key ( mydata - > input , BTN_LEFT ,
! ! ( data [ 1 ] & M560_MOUSE_BTN_LEFT ) ) ;
input_report_key ( mydata - > input , BTN_RIGHT ,
! ! ( data [ 1 ] & M560_MOUSE_BTN_RIGHT ) ) ;
if ( data [ 1 ] & M560_MOUSE_BTN_WHEEL_LEFT )
input_report_rel ( mydata - > input , REL_HWHEEL , - 1 ) ;
else if ( data [ 1 ] & M560_MOUSE_BTN_WHEEL_RIGHT )
input_report_rel ( mydata - > input , REL_HWHEEL , 1 ) ;
v = hid_snto32 ( hid_field_extract ( hdev , data + 3 , 0 , 12 ) , 12 ) ;
input_report_rel ( mydata - > input , REL_X , v ) ;
v = hid_snto32 ( hid_field_extract ( hdev , data + 3 , 12 , 12 ) , 12 ) ;
input_report_rel ( mydata - > input , REL_Y , v ) ;
v = hid_snto32 ( data [ 6 ] , 8 ) ;
input_report_rel ( mydata - > input , REL_WHEEL , v ) ;
input_sync ( mydata - > input ) ;
}
return 1 ;
}
static void m560_populate_input ( struct hidpp_device * hidpp ,
struct input_dev * input_dev , bool origin_is_hid_core )
{
struct m560_private_data * mydata = hidpp - > private_data ;
mydata - > input = input_dev ;
__set_bit ( EV_KEY , mydata - > input - > evbit ) ;
__set_bit ( BTN_MIDDLE , mydata - > input - > keybit ) ;
__set_bit ( BTN_RIGHT , mydata - > input - > keybit ) ;
__set_bit ( BTN_LEFT , mydata - > input - > keybit ) ;
__set_bit ( BTN_BACK , mydata - > input - > keybit ) ;
__set_bit ( BTN_FORWARD , mydata - > input - > keybit ) ;
__set_bit ( EV_REL , mydata - > input - > evbit ) ;
__set_bit ( REL_X , mydata - > input - > relbit ) ;
__set_bit ( REL_Y , mydata - > input - > relbit ) ;
__set_bit ( REL_WHEEL , mydata - > input - > relbit ) ;
__set_bit ( REL_HWHEEL , mydata - > input - > relbit ) ;
}
static int m560_input_mapping ( struct hid_device * hdev , struct hid_input * hi ,
struct hid_field * field , struct hid_usage * usage ,
unsigned long * * bit , int * max )
{
return - 1 ;
}
2015-09-03 09:08:30 -04:00
/* ------------------------------------------------------------------------- */
/* Logitech K400 devices */
/* ------------------------------------------------------------------------- */
/*
* The Logitech K400 keyboard has an embedded touchpad which is seen
* as a mouse from the OS point of view . There is a hardware shortcut to disable
* tap - to - click but the setting is not remembered accross reset , annoying some
* users .
*
* We can toggle this feature from the host by using the feature 0x6010 :
* Touchpad FW items
*/
struct k400_private_data {
u8 feature_index ;
} ;
static int k400_disable_tap_to_click ( struct hidpp_device * hidpp )
{
struct k400_private_data * k400 = hidpp - > private_data ;
struct hidpp_touchpad_fw_items items = { } ;
int ret ;
u8 feature_type ;
if ( ! k400 - > feature_index ) {
ret = hidpp_root_get_feature ( hidpp ,
HIDPP_PAGE_TOUCHPAD_FW_ITEMS ,
& k400 - > feature_index , & feature_type ) ;
if ( ret )
/* means that the device is not powered up */
return ret ;
}
ret = hidpp_touchpad_fw_items_set ( hidpp , k400 - > feature_index , & items ) ;
if ( ret )
return ret ;
return 0 ;
}
static int k400_allocate ( struct hid_device * hdev )
{
struct hidpp_device * hidpp = hid_get_drvdata ( hdev ) ;
struct k400_private_data * k400 ;
k400 = devm_kzalloc ( & hdev - > dev , sizeof ( struct k400_private_data ) ,
GFP_KERNEL ) ;
if ( ! k400 )
return - ENOMEM ;
hidpp - > private_data = k400 ;
return 0 ;
} ;
static int k400_connect ( struct hid_device * hdev , bool connected )
{
struct hidpp_device * hidpp = hid_get_drvdata ( hdev ) ;
if ( ! connected )
return 0 ;
if ( ! disable_tap_to_click )
return 0 ;
return k400_disable_tap_to_click ( hidpp ) ;
}
2014-09-30 13:18:27 -04:00
/* -------------------------------------------------------------------------- */
/* Generic HID++ devices */
/* -------------------------------------------------------------------------- */
static int hidpp_input_mapping ( struct hid_device * hdev , struct hid_input * hi ,
struct hid_field * field , struct hid_usage * usage ,
unsigned long * * bit , int * max )
{
struct hidpp_device * hidpp = hid_get_drvdata ( hdev ) ;
if ( hidpp - > quirks & HIDPP_QUIRK_CLASS_WTP )
return wtp_input_mapping ( hdev , hi , field , usage , bit , max ) ;
2015-05-30 11:00:27 +02:00
else if ( hidpp - > quirks & HIDPP_QUIRK_CLASS_M560 & &
field - > application ! = HID_GD_MOUSE )
return m560_input_mapping ( hdev , hi , field , usage , bit , max ) ;
2014-09-30 13:18:27 -04:00
return 0 ;
}
2014-09-30 13:18:32 -04:00
static void hidpp_populate_input ( struct hidpp_device * hidpp ,
struct input_dev * input , bool origin_is_hid_core )
{
if ( hidpp - > quirks & HIDPP_QUIRK_CLASS_WTP )
wtp_populate_input ( hidpp , input , origin_is_hid_core ) ;
2015-05-30 11:00:27 +02:00
else if ( hidpp - > quirks & HIDPP_QUIRK_CLASS_M560 )
m560_populate_input ( hidpp , input , origin_is_hid_core ) ;
2014-09-30 13:18:32 -04:00
}
2015-09-29 15:52:59 -07:00
static int hidpp_input_configured ( struct hid_device * hdev ,
2014-09-30 13:18:27 -04:00
struct hid_input * hidinput )
{
struct hidpp_device * hidpp = hid_get_drvdata ( hdev ) ;
2014-09-30 13:18:32 -04:00
struct input_dev * input = hidinput - > input ;
2014-09-30 13:18:27 -04:00
2014-09-30 13:18:32 -04:00
hidpp_populate_input ( hidpp , input , true ) ;
2015-09-29 15:52:59 -07:00
return 0 ;
2014-09-30 13:18:27 -04:00
}
static int hidpp_raw_hidpp_event ( struct hidpp_device * hidpp , u8 * data ,
int size )
{
struct hidpp_report * question = hidpp - > send_receive_buf ;
struct hidpp_report * answer = hidpp - > send_receive_buf ;
struct hidpp_report * report = ( struct hidpp_report * ) data ;
/*
* If the mutex is locked then we have a pending answer from a
2014-12-17 00:23:51 +01:00
* previously sent command .
2014-09-30 13:18:27 -04:00
*/
if ( unlikely ( mutex_is_locked ( & hidpp - > send_mutex ) ) ) {
/*
* Check for a correct hidpp20 answer or the corresponding
* error
*/
if ( hidpp_match_answer ( question , report ) | |
hidpp_match_error ( question , report ) ) {
* answer = * report ;
hidpp - > answer_available = true ;
wake_up ( & hidpp - > wait ) ;
/*
* This was an answer to a command that this driver sent
* We return 1 to hid - core to avoid forwarding the
* command upstream as it has been treated by the driver
*/
return 1 ;
}
}
2014-09-30 13:18:32 -04:00
if ( unlikely ( hidpp_report_is_connect_event ( report ) ) ) {
atomic_set ( & hidpp - > connected ,
! ( report - > rap . params [ 0 ] & ( 1 < < 6 ) ) ) ;
2015-09-03 09:08:29 -04:00
if ( ( hidpp - > quirks & HIDPP_QUIRK_CONNECT_EVENTS ) & &
2014-09-30 13:18:32 -04:00
( schedule_work ( & hidpp - > work ) = = 0 ) )
dbg_hid ( " %s: connect event already queued \n " , __func__ ) ;
return 1 ;
}
2014-09-30 13:18:27 -04:00
return 0 ;
}
static int hidpp_raw_event ( struct hid_device * hdev , struct hid_report * report ,
u8 * data , int size )
{
struct hidpp_device * hidpp = hid_get_drvdata ( hdev ) ;
2014-12-17 00:23:51 +01:00
int ret = 0 ;
2014-09-30 13:18:27 -04:00
2014-12-17 00:23:51 +01:00
/* Generic HID++ processing. */
2014-09-30 13:18:27 -04:00
switch ( data [ 0 ] ) {
case REPORT_ID_HIDPP_LONG :
if ( size ! = HIDPP_REPORT_LONG_LENGTH ) {
hid_err ( hdev , " received hid++ report of bad size (%d) " ,
size ) ;
return 1 ;
}
2014-12-17 00:23:51 +01:00
ret = hidpp_raw_hidpp_event ( hidpp , data , size ) ;
break ;
2014-09-30 13:18:27 -04:00
case REPORT_ID_HIDPP_SHORT :
if ( size ! = HIDPP_REPORT_SHORT_LENGTH ) {
hid_err ( hdev , " received hid++ report of bad size (%d) " ,
size ) ;
return 1 ;
}
2014-12-17 00:23:51 +01:00
ret = hidpp_raw_hidpp_event ( hidpp , data , size ) ;
break ;
2014-09-30 13:18:27 -04:00
}
2014-12-17 00:23:51 +01:00
/* If no report is available for further processing, skip calling
* raw_event of subclasses . */
if ( ret ! = 0 )
return ret ;
2014-09-30 13:18:27 -04:00
if ( hidpp - > quirks & HIDPP_QUIRK_CLASS_WTP )
return wtp_raw_event ( hdev , data , size ) ;
2015-05-30 11:00:27 +02:00
else if ( hidpp - > quirks & HIDPP_QUIRK_CLASS_M560 )
return m560_raw_event ( hdev , data , size ) ;
2014-09-30 13:18:27 -04:00
return 0 ;
}
2014-09-30 13:18:30 -04:00
static void hidpp_overwrite_name ( struct hid_device * hdev , bool use_unifying )
2014-09-30 13:18:27 -04:00
{
struct hidpp_device * hidpp = hid_get_drvdata ( hdev ) ;
char * name ;
2014-09-30 13:18:30 -04:00
if ( use_unifying )
/*
* the device is connected through an Unifying receiver , and
* might not be already connected .
* Ask the receiver for its name .
*/
name = hidpp_get_unifying_name ( hidpp ) ;
else
2014-12-11 13:51:17 +01:00
name = hidpp_get_device_name ( hidpp ) ;
2014-09-30 13:18:27 -04:00
if ( ! name )
hid_err ( hdev , " unable to retrieve the name of the device " ) ;
else
snprintf ( hdev - > name , sizeof ( hdev - > name ) , " %s " , name ) ;
kfree ( name ) ;
}
2014-09-30 13:18:32 -04:00
static int hidpp_input_open ( struct input_dev * dev )
{
struct hid_device * hid = input_get_drvdata ( dev ) ;
return hid_hw_open ( hid ) ;
}
static void hidpp_input_close ( struct input_dev * dev )
{
struct hid_device * hid = input_get_drvdata ( dev ) ;
hid_hw_close ( hid ) ;
}
static struct input_dev * hidpp_allocate_input ( struct hid_device * hdev )
{
struct input_dev * input_dev = devm_input_allocate_device ( & hdev - > dev ) ;
2015-01-08 14:37:12 -05:00
struct hidpp_device * hidpp = hid_get_drvdata ( hdev ) ;
2014-09-30 13:18:32 -04:00
if ( ! input_dev )
return NULL ;
input_set_drvdata ( input_dev , hdev ) ;
input_dev - > open = hidpp_input_open ;
input_dev - > close = hidpp_input_close ;
2015-01-08 14:37:12 -05:00
input_dev - > name = hidpp - > name ;
2014-09-30 13:18:32 -04:00
input_dev - > phys = hdev - > phys ;
input_dev - > uniq = hdev - > uniq ;
input_dev - > id . bustype = hdev - > bus ;
input_dev - > id . vendor = hdev - > vendor ;
input_dev - > id . product = hdev - > product ;
input_dev - > id . version = hdev - > version ;
input_dev - > dev . parent = & hdev - > dev ;
return input_dev ;
}
static void hidpp_connect_event ( struct hidpp_device * hidpp )
{
struct hid_device * hdev = hidpp - > hid_dev ;
int ret = 0 ;
bool connected = atomic_read ( & hidpp - > connected ) ;
struct input_dev * input ;
char * name , * devm_name ;
2014-12-16 17:06:01 -05:00
if ( hidpp - > quirks & HIDPP_QUIRK_CLASS_WTP ) {
ret = wtp_connect ( hdev , connected ) ;
if ( ret )
return ;
2015-05-30 11:00:27 +02:00
} else if ( hidpp - > quirks & HIDPP_QUIRK_CLASS_M560 ) {
ret = m560_send_config_command ( hdev , connected ) ;
if ( ret )
return ;
2015-09-03 09:08:30 -04:00
} else if ( hidpp - > quirks & HIDPP_QUIRK_CLASS_K400 ) {
ret = k400_connect ( hdev , connected ) ;
if ( ret )
return ;
2014-12-16 17:06:01 -05:00
}
2014-09-30 13:18:33 -04:00
2014-09-30 13:18:32 -04:00
if ( ! connected | | hidpp - > delayed_input )
return ;
2015-09-03 09:08:29 -04:00
/* the device is already connected, we can ask for its name and
* protocol */
2014-09-30 13:18:32 -04:00
if ( ! hidpp - > protocol_major ) {
ret = ! hidpp_is_connected ( hidpp ) ;
if ( ret ) {
hid_err ( hdev , " Can not get the protocol version. \n " ) ;
return ;
}
2015-09-03 09:08:29 -04:00
hid_info ( hdev , " HID++ %u.%u device connected. \n " ,
hidpp - > protocol_major , hidpp - > protocol_minor ) ;
2014-09-30 13:18:32 -04:00
}
2015-09-03 09:08:29 -04:00
if ( ! ( hidpp - > quirks & HIDPP_QUIRK_NO_HIDINPUT ) )
/* if HID created the input nodes for us, we can stop now */
return ;
2014-09-30 13:18:32 -04:00
2015-01-08 14:37:12 -05:00
if ( ! hidpp - > name | | hidpp - > name = = hdev - > name ) {
name = hidpp_get_device_name ( hidpp ) ;
if ( ! name ) {
hid_err ( hdev ,
" unable to retrieve the name of the device " ) ;
return ;
}
devm_name = devm_kasprintf ( & hdev - > dev , GFP_KERNEL , " %s " , name ) ;
kfree ( name ) ;
if ( ! devm_name )
return ;
hidpp - > name = devm_name ;
}
2014-09-30 13:18:32 -04:00
input = hidpp_allocate_input ( hdev ) ;
if ( ! input ) {
hid_err ( hdev , " cannot allocate new input device: %d \n " , ret ) ;
return ;
}
hidpp_populate_input ( hidpp , input , false ) ;
ret = input_register_device ( input ) ;
if ( ret )
input_free_device ( input ) ;
hidpp - > delayed_input = input ;
}
2014-09-30 13:18:27 -04:00
static int hidpp_probe ( struct hid_device * hdev , const struct hid_device_id * id )
{
struct hidpp_device * hidpp ;
int ret ;
bool connected ;
2014-09-30 13:18:32 -04:00
unsigned int connect_mask = HID_CONNECT_DEFAULT ;
2014-09-30 13:18:27 -04:00
hidpp = devm_kzalloc ( & hdev - > dev , sizeof ( struct hidpp_device ) ,
GFP_KERNEL ) ;
if ( ! hidpp )
return - ENOMEM ;
hidpp - > hid_dev = hdev ;
2015-01-08 14:37:12 -05:00
hidpp - > name = hdev - > name ;
2014-09-30 13:18:27 -04:00
hid_set_drvdata ( hdev , hidpp ) ;
hidpp - > quirks = id - > driver_data ;
2015-03-26 12:41:57 -04:00
if ( disable_raw_mode ) {
hidpp - > quirks & = ~ HIDPP_QUIRK_CLASS_WTP ;
2015-09-03 09:08:29 -04:00
hidpp - > quirks & = ~ HIDPP_QUIRK_CONNECT_EVENTS ;
hidpp - > quirks & = ~ HIDPP_QUIRK_NO_HIDINPUT ;
2015-03-26 12:41:57 -04:00
}
2014-09-30 13:18:27 -04:00
if ( hidpp - > quirks & HIDPP_QUIRK_CLASS_WTP ) {
ret = wtp_allocate ( hdev , id ) ;
if ( ret )
2015-05-30 11:00:27 +02:00
goto allocate_fail ;
} else if ( hidpp - > quirks & HIDPP_QUIRK_CLASS_M560 ) {
ret = m560_allocate ( hdev ) ;
if ( ret )
goto allocate_fail ;
2015-09-03 09:08:30 -04:00
} else if ( hidpp - > quirks & HIDPP_QUIRK_CLASS_K400 ) {
ret = k400_allocate ( hdev ) ;
if ( ret )
goto allocate_fail ;
2014-09-30 13:18:27 -04:00
}
2014-09-30 13:18:32 -04:00
INIT_WORK ( & hidpp - > work , delayed_work_cb ) ;
2014-09-30 13:18:27 -04:00
mutex_init ( & hidpp - > send_mutex ) ;
init_waitqueue_head ( & hidpp - > wait ) ;
ret = hid_parse ( hdev ) ;
if ( ret ) {
hid_err ( hdev , " %s:parse failed \n " , __func__ ) ;
goto hid_parse_fail ;
}
/* Allow incoming packets */
hid_device_io_start ( hdev ) ;
connected = hidpp_is_connected ( hidpp ) ;
2014-09-30 13:18:28 -04:00
if ( id - > group ! = HID_GROUP_LOGITECH_DJ_DEVICE ) {
if ( ! connected ) {
2015-04-05 14:06:29 +02:00
ret = - ENODEV ;
2014-09-30 13:18:28 -04:00
hid_err ( hdev , " Device not connected " ) ;
2014-12-11 13:51:20 +01:00
hid_device_io_stop ( hdev ) ;
2014-09-30 13:18:28 -04:00
goto hid_parse_fail ;
}
2014-09-30 13:18:27 -04:00
2014-09-30 13:18:28 -04:00
hid_info ( hdev , " HID++ %u.%u device connected. \n " ,
hidpp - > protocol_major , hidpp - > protocol_minor ) ;
}
2014-09-30 13:18:27 -04:00
2014-09-30 13:18:30 -04:00
hidpp_overwrite_name ( hdev , id - > group = = HID_GROUP_LOGITECH_DJ_DEVICE ) ;
2014-09-30 13:18:32 -04:00
atomic_set ( & hidpp - > connected , connected ) ;
2014-09-30 13:18:30 -04:00
2014-09-30 13:18:32 -04:00
if ( connected & & ( hidpp - > quirks & HIDPP_QUIRK_CLASS_WTP ) ) {
2014-09-30 13:18:27 -04:00
ret = wtp_get_config ( hidpp ) ;
if ( ret )
goto hid_parse_fail ;
}
/* Block incoming packets */
hid_device_io_stop ( hdev ) ;
2015-09-03 09:08:29 -04:00
if ( hidpp - > quirks & HIDPP_QUIRK_NO_HIDINPUT )
2014-09-30 13:18:32 -04:00
connect_mask & = ~ HID_CONNECT_HIDINPUT ;
ret = hid_hw_start ( hdev , connect_mask ) ;
2014-09-30 13:18:27 -04:00
if ( ret ) {
hid_err ( hdev , " %s:hid_hw_start returned error \n " , __func__ ) ;
goto hid_hw_start_fail ;
}
2015-09-03 09:08:29 -04:00
if ( hidpp - > quirks & HIDPP_QUIRK_CONNECT_EVENTS ) {
2014-09-30 13:18:32 -04:00
/* Allow incoming packets */
hid_device_io_start ( hdev ) ;
hidpp_connect_event ( hidpp ) ;
}
2014-09-30 13:18:27 -04:00
return ret ;
hid_hw_start_fail :
hid_parse_fail :
2014-09-30 13:18:32 -04:00
cancel_work_sync ( & hidpp - > work ) ;
2014-09-30 13:18:27 -04:00
mutex_destroy ( & hidpp - > send_mutex ) ;
2015-05-30 11:00:27 +02:00
allocate_fail :
2014-09-30 13:18:27 -04:00
hid_set_drvdata ( hdev , NULL ) ;
return ret ;
}
static void hidpp_remove ( struct hid_device * hdev )
{
struct hidpp_device * hidpp = hid_get_drvdata ( hdev ) ;
2014-09-30 13:18:32 -04:00
cancel_work_sync ( & hidpp - > work ) ;
2014-09-30 13:18:27 -04:00
mutex_destroy ( & hidpp - > send_mutex ) ;
hid_hw_stop ( hdev ) ;
}
static const struct hid_device_id hidpp_devices [ ] = {
2014-09-30 13:18:34 -04:00
{ /* wireless touchpad */
HID_DEVICE ( BUS_USB , HID_GROUP_LOGITECH_DJ_DEVICE ,
USB_VENDOR_ID_LOGITECH , 0x4011 ) ,
. driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT |
HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS } ,
2014-09-30 13:18:33 -04:00
{ /* wireless touchpad T650 */
HID_DEVICE ( BUS_USB , HID_GROUP_LOGITECH_DJ_DEVICE ,
USB_VENDOR_ID_LOGITECH , 0x4101 ) ,
. driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT } ,
2014-09-30 13:18:27 -04:00
{ /* wireless touchpad T651 */
HID_BLUETOOTH_DEVICE ( USB_VENDOR_ID_LOGITECH ,
USB_DEVICE_ID_LOGITECH_T651 ) ,
. driver_data = HIDPP_QUIRK_CLASS_WTP } ,
2015-05-30 11:00:27 +02:00
{ /* Mouse logitech M560 */
2014-09-30 13:18:35 -04:00
HID_DEVICE ( BUS_USB , HID_GROUP_LOGITECH_DJ_DEVICE ,
2015-05-30 11:00:27 +02:00
USB_VENDOR_ID_LOGITECH , 0x402d ) ,
. driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 } ,
2015-09-03 09:08:30 -04:00
{ /* Keyboard logitech K400 */
HID_DEVICE ( BUS_USB , HID_GROUP_LOGITECH_DJ_DEVICE ,
USB_VENDOR_ID_LOGITECH , 0x4024 ) ,
. driver_data = HIDPP_QUIRK_CONNECT_EVENTS | HIDPP_QUIRK_CLASS_K400 } ,
2014-09-30 13:18:28 -04:00
{ HID_DEVICE ( BUS_USB , HID_GROUP_LOGITECH_DJ_DEVICE ,
USB_VENDOR_ID_LOGITECH , HID_ANY_ID ) } ,
2014-09-30 13:18:27 -04:00
{ }
} ;
MODULE_DEVICE_TABLE ( hid , hidpp_devices ) ;
static struct hid_driver hidpp_driver = {
. name = " logitech-hidpp-device " ,
. id_table = hidpp_devices ,
. probe = hidpp_probe ,
. remove = hidpp_remove ,
. raw_event = hidpp_raw_event ,
. input_configured = hidpp_input_configured ,
. input_mapping = hidpp_input_mapping ,
} ;
module_hid_driver ( hidpp_driver ) ;