2019-05-29 17:18:02 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-04-29 03:51:00 +04:00
/*
* HID Sensors Driver
* Copyright ( c ) 2014 , Intel Corporation .
*/
# include <linux/device.h>
# include <linux/platform_device.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/slab.h>
# include <linux/hid-sensor-hub.h>
# include <linux/iio/iio.h>
# include <linux/iio/sysfs.h>
# include <linux/iio/buffer.h>
# include "../common/hid-sensors/hid-sensor-trigger.h"
struct dev_rot_state {
struct hid_sensor_hub_callbacks callbacks ;
struct hid_sensor_common common_attributes ;
struct hid_sensor_hub_attribute_info quaternion ;
u32 sampled_vals [ 4 ] ;
2017-05-07 13:24:26 +03:00
int scale_pre_decml ;
int scale_post_decml ;
int scale_precision ;
int value_offset ;
2014-04-29 03:51:00 +04:00
} ;
/* Channel definitions */
static const struct iio_chan_spec dev_rot_channels [ ] = {
{
. type = IIO_ROT ,
. modified = 1 ,
. channel2 = IIO_MOD_QUATERNION ,
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) ,
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SAMP_FREQ ) |
2017-05-07 13:24:26 +03:00
BIT ( IIO_CHAN_INFO_OFFSET ) |
BIT ( IIO_CHAN_INFO_SCALE ) |
2014-04-29 03:51:00 +04:00
BIT ( IIO_CHAN_INFO_HYSTERESIS )
}
} ;
/* Adjust channel real bits based on report descriptor */
static void dev_rot_adjust_channel_bit_mask ( struct iio_chan_spec * chan ,
int size )
{
chan - > scan_type . sign = ' s ' ;
/* Real storage bits will change based on the report desc. */
chan - > scan_type . realbits = size * 8 ;
/* Maximum size of a sample to capture is u32 */
chan - > scan_type . storagebits = sizeof ( u32 ) * 8 ;
chan - > scan_type . repeat = 4 ;
}
/* Channel read_raw handler */
static int dev_rot_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int size , int * vals , int * val_len ,
long mask )
{
struct dev_rot_state * rot_state = iio_priv ( indio_dev ) ;
int ret_type ;
int i ;
vals [ 0 ] = 0 ;
vals [ 1 ] = 0 ;
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
if ( size > = 4 ) {
for ( i = 0 ; i < 4 ; + + i )
vals [ i ] = rot_state - > sampled_vals [ i ] ;
ret_type = IIO_VAL_INT_MULTIPLE ;
* val_len = 4 ;
} else
ret_type = - EINVAL ;
break ;
2017-05-07 13:24:26 +03:00
case IIO_CHAN_INFO_SCALE :
vals [ 0 ] = rot_state - > scale_pre_decml ;
vals [ 1 ] = rot_state - > scale_post_decml ;
return rot_state - > scale_precision ;
case IIO_CHAN_INFO_OFFSET :
* vals = rot_state - > value_offset ;
return IIO_VAL_INT ;
2014-04-29 03:51:00 +04:00
case IIO_CHAN_INFO_SAMP_FREQ :
ret_type = hid_sensor_read_samp_freq_value (
& rot_state - > common_attributes , & vals [ 0 ] , & vals [ 1 ] ) ;
break ;
case IIO_CHAN_INFO_HYSTERESIS :
ret_type = hid_sensor_read_raw_hyst_value (
& rot_state - > common_attributes , & vals [ 0 ] , & vals [ 1 ] ) ;
break ;
default :
ret_type = - EINVAL ;
break ;
}
return ret_type ;
}
/* Channel write_raw handler */
static int dev_rot_write_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * chan ,
int val ,
int val2 ,
long mask )
{
struct dev_rot_state * rot_state = iio_priv ( indio_dev ) ;
int ret ;
switch ( mask ) {
case IIO_CHAN_INFO_SAMP_FREQ :
ret = hid_sensor_write_samp_freq_value (
& rot_state - > common_attributes , val , val2 ) ;
break ;
case IIO_CHAN_INFO_HYSTERESIS :
ret = hid_sensor_write_raw_hyst_value (
& rot_state - > common_attributes , val , val2 ) ;
break ;
default :
ret = - EINVAL ;
}
return ret ;
}
static const struct iio_info dev_rot_info = {
. read_raw_multi = & dev_rot_read_raw ,
. write_raw = & dev_rot_write_raw ,
} ;
/* Function to push data to buffer */
static void hid_sensor_push_data ( struct iio_dev * indio_dev , u8 * data , int len )
{
dev_dbg ( & indio_dev - > dev , " hid_sensor_push_data >> \n " ) ;
iio_push_to_buffers ( indio_dev , ( u8 * ) data ) ;
dev_dbg ( & indio_dev - > dev , " hid_sensor_push_data << \n " ) ;
}
/* Callback handler to send event after all samples are received and captured */
static int dev_rot_proc_event ( struct hid_sensor_hub_device * hsdev ,
unsigned usage_id ,
void * priv )
{
struct iio_dev * indio_dev = platform_get_drvdata ( priv ) ;
struct dev_rot_state * rot_state = iio_priv ( indio_dev ) ;
2014-04-19 03:22:00 +04:00
dev_dbg ( & indio_dev - > dev , " dev_rot_proc_event \n " ) ;
if ( atomic_read ( & rot_state - > common_attributes . data_ready ) )
2014-04-29 03:51:00 +04:00
hid_sensor_push_data ( indio_dev ,
( u8 * ) rot_state - > sampled_vals ,
sizeof ( rot_state - > sampled_vals ) ) ;
return 0 ;
}
/* Capture samples in local storage */
static int dev_rot_capture_sample ( struct hid_sensor_hub_device * hsdev ,
unsigned usage_id ,
size_t raw_len , char * raw_data ,
void * priv )
{
struct iio_dev * indio_dev = platform_get_drvdata ( priv ) ;
struct dev_rot_state * rot_state = iio_priv ( indio_dev ) ;
if ( usage_id = = HID_USAGE_SENSOR_ORIENT_QUATERNION ) {
memcpy ( rot_state - > sampled_vals , raw_data ,
sizeof ( rot_state - > sampled_vals ) ) ;
dev_dbg ( & indio_dev - > dev , " Recd Quat len:%zu::%zu \n " , raw_len ,
sizeof ( rot_state - > sampled_vals ) ) ;
}
return 0 ;
}
/* Parse report which is specific to an usage id*/
static int dev_rot_parse_report ( struct platform_device * pdev ,
struct hid_sensor_hub_device * hsdev ,
struct iio_chan_spec * channels ,
unsigned usage_id ,
struct dev_rot_state * st )
{
int ret ;
ret = sensor_hub_input_get_attribute_info ( hsdev ,
HID_INPUT_REPORT ,
usage_id ,
HID_USAGE_SENSOR_ORIENT_QUATERNION ,
& st - > quaternion ) ;
if ( ret )
return ret ;
dev_rot_adjust_channel_bit_mask ( & channels [ 0 ] ,
st - > quaternion . size / 4 ) ;
dev_dbg ( & pdev - > dev , " dev_rot %x:%x \n " , st - > quaternion . index ,
st - > quaternion . report_id ) ;
dev_dbg ( & pdev - > dev , " dev_rot: attrib size %d \n " ,
st - > quaternion . size ) ;
2017-05-07 13:24:26 +03:00
st - > scale_precision = hid_sensor_format_scale (
hsdev - > usage ,
& st - > quaternion ,
& st - > scale_pre_decml , & st - > scale_post_decml ) ;
2014-04-29 03:51:00 +04:00
/* Set Sensitivity field ids, when there is no individual modifier */
if ( st - > common_attributes . sensitivity . index < 0 ) {
sensor_hub_input_get_attribute_info ( hsdev ,
HID_FEATURE_REPORT , usage_id ,
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
HID_USAGE_SENSOR_DATA_ORIENTATION ,
& st - > common_attributes . sensitivity ) ;
dev_dbg ( & pdev - > dev , " Sensitivity index:report %d:%d \n " ,
st - > common_attributes . sensitivity . index ,
st - > common_attributes . sensitivity . report_id ) ;
}
return 0 ;
}
/* Function to initialize the processing for usage id */
static int hid_dev_rot_probe ( struct platform_device * pdev )
{
int ret ;
2017-07-15 23:07:44 +03:00
char * name ;
2014-04-29 03:51:00 +04:00
struct iio_dev * indio_dev ;
struct dev_rot_state * rot_state ;
struct hid_sensor_hub_device * hsdev = pdev - > dev . platform_data ;
indio_dev = devm_iio_device_alloc ( & pdev - > dev ,
sizeof ( struct dev_rot_state ) ) ;
if ( indio_dev = = NULL )
return - ENOMEM ;
platform_set_drvdata ( pdev , indio_dev ) ;
rot_state = iio_priv ( indio_dev ) ;
rot_state - > common_attributes . hsdev = hsdev ;
rot_state - > common_attributes . pdev = pdev ;
2017-05-07 13:24:24 +03:00
switch ( hsdev - > usage ) {
case HID_USAGE_SENSOR_DEVICE_ORIENTATION :
name = " dev_rotation " ;
break ;
case HID_USAGE_SENSOR_RELATIVE_ORIENTATION :
name = " relative_orientation " ;
break ;
2017-05-07 13:24:25 +03:00
case HID_USAGE_SENSOR_GEOMAGNETIC_ORIENTATION :
name = " geomagnetic_orientation " ;
break ;
2017-05-07 13:24:24 +03:00
default :
return - EINVAL ;
}
ret = hid_sensor_parse_common_attributes ( hsdev , hsdev - > usage ,
2014-04-29 03:51:00 +04:00
& rot_state - > common_attributes ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to setup common attributes \n " ) ;
return ret ;
}
2015-05-13 21:06:13 +03:00
indio_dev - > channels = devm_kmemdup ( & pdev - > dev , dev_rot_channels ,
sizeof ( dev_rot_channels ) ,
GFP_KERNEL ) ;
if ( ! indio_dev - > channels ) {
2014-04-29 03:51:00 +04:00
dev_err ( & pdev - > dev , " failed to duplicate channels \n " ) ;
return - ENOMEM ;
}
2015-05-13 21:06:13 +03:00
ret = dev_rot_parse_report ( pdev , hsdev ,
( struct iio_chan_spec * ) indio_dev - > channels ,
2017-05-07 13:24:24 +03:00
hsdev - > usage , rot_state ) ;
2014-04-29 03:51:00 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " failed to setup attributes \n " ) ;
return ret ;
}
indio_dev - > num_channels = ARRAY_SIZE ( dev_rot_channels ) ;
indio_dev - > info = & dev_rot_info ;
indio_dev - > name = name ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
2014-04-19 03:22:00 +04:00
atomic_set ( & rot_state - > common_attributes . data_ready , 0 ) ;
2020-04-24 07:34:18 +03:00
2014-04-29 03:51:00 +04:00
ret = hid_sensor_setup_trigger ( indio_dev , name ,
& rot_state - > common_attributes ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " trigger setup failed \n " ) ;
2020-04-24 07:34:18 +03:00
return ret ;
2014-04-29 03:51:00 +04:00
}
ret = iio_device_register ( indio_dev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " device register failed \n " ) ;
goto error_remove_trigger ;
}
rot_state - > callbacks . send_event = dev_rot_proc_event ;
rot_state - > callbacks . capture_sample = dev_rot_capture_sample ;
rot_state - > callbacks . pdev = pdev ;
2017-05-07 13:24:24 +03:00
ret = sensor_hub_register_callback ( hsdev , hsdev - > usage ,
2014-04-29 03:51:00 +04:00
& rot_state - > callbacks ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " callback reg failed \n " ) ;
goto error_iio_unreg ;
}
return 0 ;
error_iio_unreg :
iio_device_unregister ( indio_dev ) ;
error_remove_trigger :
2020-04-24 07:34:18 +03:00
hid_sensor_remove_trigger ( indio_dev , & rot_state - > common_attributes ) ;
2014-04-29 03:51:00 +04:00
return ret ;
}
/* Function to deinitialize the processing for usage id */
static int hid_dev_rot_remove ( struct platform_device * pdev )
{
struct hid_sensor_hub_device * hsdev = pdev - > dev . platform_data ;
struct iio_dev * indio_dev = platform_get_drvdata ( pdev ) ;
struct dev_rot_state * rot_state = iio_priv ( indio_dev ) ;
2017-05-07 13:24:24 +03:00
sensor_hub_remove_callback ( hsdev , hsdev - > usage ) ;
2014-04-29 03:51:00 +04:00
iio_device_unregister ( indio_dev ) ;
2020-04-24 07:34:18 +03:00
hid_sensor_remove_trigger ( indio_dev , & rot_state - > common_attributes ) ;
2014-04-29 03:51:00 +04:00
return 0 ;
}
2015-05-01 18:53:37 +03:00
static const struct platform_device_id hid_dev_rot_ids [ ] = {
2014-04-29 03:51:00 +04:00
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
. name = " HID-SENSOR-20008a " ,
} ,
2017-05-07 13:24:24 +03:00
{
/* Relative orientation(AG) sensor */
. name = " HID-SENSOR-20008e " ,
} ,
2017-05-07 13:24:25 +03:00
{
/* Geomagnetic orientation(AM) sensor */
. name = " HID-SENSOR-2000c1 " ,
} ,
2014-04-29 03:51:00 +04:00
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( platform , hid_dev_rot_ids ) ;
static struct platform_driver hid_dev_rot_platform_driver = {
. id_table = hid_dev_rot_ids ,
. driver = {
. name = KBUILD_MODNAME ,
2016-10-25 04:06:03 +03:00
. pm = & hid_sensor_pm_ops ,
2014-04-29 03:51:00 +04:00
} ,
. probe = hid_dev_rot_probe ,
. remove = hid_dev_rot_remove ,
} ;
module_platform_driver ( hid_dev_rot_platform_driver ) ;
MODULE_DESCRIPTION ( " HID Sensor Device Rotation " ) ;
MODULE_AUTHOR ( " Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;