2020-09-10 01:03:04 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* HID support for Vivaldi Keyboard
*
* Copyright 2020 Google LLC .
* Author : Sean O ' Brien < seobrien @ chromium . org >
*/
2022-01-08 02:23:05 +03:00
# include <linux/device.h>
2020-09-10 01:03:04 +03:00
# include <linux/hid.h>
2022-03-15 05:45:37 +03:00
# include <linux/input/vivaldi-fmap.h>
2022-01-08 02:23:05 +03:00
# include <linux/kernel.h>
2020-09-10 01:03:04 +03:00
# include <linux/module.h>
2022-01-08 02:23:05 +03:00
# include <linux/sysfs.h>
2020-09-10 01:03:04 +03:00
2022-03-15 05:45:37 +03:00
# define MIN_FN_ROW_KEY 1
# define MAX_FN_ROW_KEY VIVALDI_MAX_FUNCTION_ROW_KEYS
2020-09-10 01:03:04 +03:00
# define HID_VD_FN_ROW_PHYSMAP 0x00000001
# define HID_USAGE_FN_ROW_PHYSMAP (HID_UP_GOOGLEVENDOR | HID_VD_FN_ROW_PHYSMAP)
static ssize_t function_row_physmap_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct hid_device * hdev = to_hid_device ( dev ) ;
struct vivaldi_data * drvdata = hid_get_drvdata ( hdev ) ;
2022-03-15 05:45:37 +03:00
return vivaldi_function_row_physmap_show ( drvdata , buf ) ;
2020-09-10 01:03:04 +03:00
}
2022-01-08 02:23:05 +03:00
static DEVICE_ATTR_RO ( function_row_physmap ) ;
2020-09-10 01:03:04 +03:00
static struct attribute * sysfs_attrs [ ] = {
& dev_attr_function_row_physmap . attr ,
NULL
} ;
static const struct attribute_group input_attribute_group = {
. attrs = sysfs_attrs
} ;
static int vivaldi_probe ( struct hid_device * hdev ,
const struct hid_device_id * id )
{
struct vivaldi_data * drvdata ;
int ret ;
drvdata = devm_kzalloc ( & hdev - > dev , sizeof ( * drvdata ) , GFP_KERNEL ) ;
2021-12-15 11:36:05 +03:00
if ( ! drvdata )
return - ENOMEM ;
2020-09-10 01:03:04 +03:00
hid_set_drvdata ( hdev , drvdata ) ;
ret = hid_parse ( hdev ) ;
if ( ret )
return ret ;
return hid_hw_start ( hdev , HID_CONNECT_DEFAULT ) ;
}
static void vivaldi_feature_mapping ( struct hid_device * hdev ,
struct hid_field * field ,
struct hid_usage * usage )
{
struct vivaldi_data * drvdata = hid_get_drvdata ( hdev ) ;
2022-01-07 23:09:36 +03:00
struct hid_report * report = field - > report ;
2020-09-10 01:03:04 +03:00
int fn_key ;
int ret ;
u32 report_len ;
2022-01-07 23:09:36 +03:00
u8 * report_data , * buf ;
2020-09-10 01:03:04 +03:00
if ( field - > logical ! = HID_USAGE_FN_ROW_PHYSMAP | |
( usage - > hid & HID_USAGE_PAGE ) ! = HID_UP_ORDINAL )
return ;
2022-03-15 05:45:37 +03:00
fn_key = usage - > hid & HID_USAGE ;
2020-09-10 01:03:04 +03:00
if ( fn_key < MIN_FN_ROW_KEY | | fn_key > MAX_FN_ROW_KEY )
return ;
2022-03-15 05:45:37 +03:00
if ( fn_key > drvdata - > num_function_row_keys )
drvdata - > num_function_row_keys = fn_key ;
2020-09-10 01:03:04 +03:00
2022-01-07 23:09:36 +03:00
report_data = buf = hid_alloc_report_buf ( report , GFP_KERNEL ) ;
if ( ! report_data )
2020-09-10 01:03:04 +03:00
return ;
2022-01-07 23:09:36 +03:00
report_len = hid_report_len ( report ) ;
if ( ! report - > id ) {
/*
* hid_hw_raw_request ( ) will stuff report ID ( which will be 0 )
* into the first byte of the buffer even for unnumbered
* reports , so we need to account for this to avoid getting
* - EOVERFLOW in return .
* Note that hid_alloc_report_buf ( ) adds 7 bytes to the size
* so we can safely say that we have space for an extra byte .
*/
report_len + + ;
}
ret = hid_hw_raw_request ( hdev , report - > id , report_data ,
2020-09-10 01:03:04 +03:00
report_len , HID_FEATURE_REPORT ,
HID_REQ_GET_REPORT ) ;
if ( ret < 0 ) {
dev_warn ( & hdev - > dev , " failed to fetch feature %d \n " ,
field - > report - > id ) ;
goto out ;
}
2022-01-07 23:09:36 +03:00
if ( ! report - > id ) {
/*
* Undo the damage from hid_hw_raw_request ( ) for unnumbered
* reports .
*/
report_data + + ;
report_len - - ;
}
ret = hid_report_raw_event ( hdev , HID_FEATURE_REPORT , report_data ,
2020-09-10 01:03:04 +03:00
report_len , 0 ) ;
if ( ret ) {
dev_warn ( & hdev - > dev , " failed to report feature %d \n " ,
field - > report - > id ) ;
goto out ;
}
drvdata - > function_row_physmap [ fn_key - MIN_FN_ROW_KEY ] =
field - > value [ usage - > usage_index ] ;
out :
kfree ( buf ) ;
}
static int vivaldi_input_configured ( struct hid_device * hdev ,
struct hid_input * hidinput )
{
2022-02-26 04:18:58 +03:00
return devm_device_add_group ( & hdev - > dev , & input_attribute_group ) ;
2020-09-10 01:03:04 +03:00
}
static const struct hid_device_id vivaldi_table [ ] = {
{ HID_DEVICE ( HID_BUS_ANY , HID_GROUP_VIVALDI , HID_ANY_ID ,
HID_ANY_ID ) } ,
{ }
} ;
MODULE_DEVICE_TABLE ( hid , vivaldi_table ) ;
static struct hid_driver hid_vivaldi = {
. name = " hid-vivaldi " ,
. id_table = vivaldi_table ,
. probe = vivaldi_probe ,
. feature_mapping = vivaldi_feature_mapping ,
. input_configured = vivaldi_input_configured ,
} ;
module_hid_driver ( hid_vivaldi ) ;
MODULE_AUTHOR ( " Sean O'Brien " ) ;
MODULE_DESCRIPTION ( " HID vivaldi driver " ) ;
MODULE_LICENSE ( " GPL " ) ;