diff options
Diffstat (limited to 'drivers/misc/m4sensorhub_als.c')
| -rw-r--r-- | drivers/misc/m4sensorhub_als.c | 527 | 
1 files changed, 346 insertions, 181 deletions
diff --git a/drivers/misc/m4sensorhub_als.c b/drivers/misc/m4sensorhub_als.c index 92d6b2b6c19..dfb76611e6d 100644 --- a/drivers/misc/m4sensorhub_als.c +++ b/drivers/misc/m4sensorhub_als.c @@ -1,5 +1,5 @@  /* - *  Copyright (C) 2012 Motorola, Inc. + *  Copyright (C) 2012-2014 Motorola, 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 @@ -23,270 +23,435 @@  #include <linux/module.h>  #include <linux/types.h>  #include <linux/kernel.h> -#include <linux/miscdevice.h>  #include <linux/platform_device.h> -#include <linux/proc_fs.h> -#include <linux/input.h> +#include <linux/fs.h>  #include <linux/m4sensorhub.h> -#include <linux/uaccess.h> +#include <linux/input.h>  #include <linux/slab.h> -#define ALS_CLIENT_DRIVER_NAME	"m4sensorhub_als" +#define m4als_err(format, args...)  KDEBUG(M4SH_ERROR, format, ## args) + +#define M4ALS_DRIVER_NAME           "m4sensorhub_als" + +#define M4ALS_IRQ_ENABLED_BIT       0 -struct als_client { -	struct m4sensorhub_data *m4sensorhub; -	u16 als_signal; +struct m4als_driver_data { +	struct platform_device      *pdev; +	struct m4sensorhub_data     *m4; +	struct mutex                mutex; /* controls driver entry points */ +	struct input_dev            *indev; + +	uint16_t        luminosity; +	int16_t         samplerate; + +	uint16_t        status;  }; -static struct als_client *misc_als_data; -static int als_client_open(struct inode *inode, struct file *file) +static void m4als_isr(enum m4sensorhub_irqs int_event, void *handle)  {  	int err = 0; +	struct m4als_driver_data *dd = handle; +	int size = 0; +	uint16_t luminosity = 0; + +	mutex_lock(&(dd->mutex)); -	err = nonseekable_open(inode, file); +	size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_LIGHTSENSOR_SIGNAL); +	if (size < 0) { +		m4als_err("%s: Reading from invalid register %d.\n", +			  __func__, size); +		err = size; +		goto m4als_isr_fail; +	} + +	err = m4sensorhub_reg_read(dd->m4, M4SH_REG_LIGHTSENSOR_SIGNAL, +		(char *)&luminosity);  	if (err < 0) { -		KDEBUG(M4SH_ERROR, "%s failed\n", __func__); -		return err; +		m4als_err("%s: Failed to read luminosity data.\n", __func__); +		goto m4als_isr_fail; +	} else if (err != size) { +		m4als_err("%s: Read %d bytes instead of %d.\n", +			  __func__, err, size); +		goto m4als_isr_fail;  	} -	file->private_data = misc_als_data; -	return 0; +	dd->luminosity = luminosity; + +	input_event(dd->indev, EV_MSC, MSC_RAW, dd->luminosity); +	input_sync(dd->indev); + +m4als_isr_fail: +	if (err < 0) +		m4als_err("%s: Failed with error code %d.\n", __func__, err); + +	mutex_unlock(&(dd->mutex)); + +	return;  } -static int als_client_close(struct inode *inode, struct file *file) +static int m4als_set_samplerate(struct m4als_driver_data *dd, int16_t rate)  { -	KDEBUG(M4SH_DEBUG, "als_client in %s\n", __func__); -	return 0; -} +	int err = 0; +	int size = 0; +	if (rate == dd->samplerate) +		goto m4als_set_samplerate_fail; -static void m4_read_als_data(struct als_client *als_client_data) -{ -	m4sensorhub_reg_read( -			als_client_data->m4sensorhub, -			M4SH_REG_LIGHTSENSOR_SIGNAL, -			(char *)&als_client_data->als_signal -			); +	size = m4sensorhub_reg_getsize(dd->m4, M4SH_REG_LIGHTSENSOR_SAMPLERATE); +	if (size < 0) { +		m4als_err("%s: Writing to invalid register %d.\n", +			  __func__, size); +		err = size; +		goto m4als_set_samplerate_fail; +	} + +	err = m4sensorhub_reg_write(dd->m4, M4SH_REG_LIGHTSENSOR_SAMPLERATE, +		(char *)&rate, m4sh_no_mask); +	if (err < 0) { +		m4als_err("%s: Failed to set sample rate.\n", __func__); +		goto m4als_set_samplerate_fail; +	} else if (err != size) { +		m4als_err("%s: Wrote %d bytes instead of %d.\n", +			  __func__, err, size); +		goto m4als_set_samplerate_fail; +	} + +	dd->samplerate = rate; + +m4als_set_samplerate_fail: +	return err;  } -static void m4_handle_als_irq(enum m4sensorhub_irqs int_event, -					void *als_data) +static ssize_t m4als_enable_show(struct device *dev, +		struct device_attribute *attr, char *buf)  { -	struct als_client *als_client_data = als_data; -	m4_read_als_data(als_client_data); +	struct m4als_driver_data *dd = dev_get_drvdata(dev); + +	if (dd->status & (1 << M4ALS_IRQ_ENABLED_BIT)) +		return sprintf(buf, "Sensor is ENABLED.\n"); +	else +		return sprintf(buf, "Sensor is DISABLED.\n");  } +static ssize_t m4als_enable_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t size) +{ +	int err = 0; +	struct m4als_driver_data *dd = dev_get_drvdata(dev); +	int value = 0; -static const struct file_operations als_client_fops = { -	.owner = THIS_MODULE, -	.open  = als_client_open, -	.release = als_client_close, -}; +	mutex_lock(&(dd->mutex)); -static struct miscdevice als_client_miscdrv = { -	.minor = MISC_DYNAMIC_MINOR, -	.name  = ALS_CLIENT_DRIVER_NAME, -	.fops = &als_client_fops, -}; +	err = kstrtoint(buf, 10, &value); +	if (err < 0) { +		m4als_err("%s: Failed to convert value.\n", __func__); +		goto m4als_enable_store_exit; +	} -static ssize_t als_set_samplerate(struct device *dev, -				struct device_attribute *attr, -				const char *buf, size_t count) -{ -	int samplerate, ret; -	struct platform_device *pdev = to_platform_device(dev); -	struct als_client *als_client_data = platform_get_drvdata(pdev); +	switch (value) { +	case 0: +		if (dd->status & (1 << M4ALS_IRQ_ENABLED_BIT)) { +			err = m4sensorhub_irq_disable(dd->m4, +				M4SH_IRQ_LIGHTSENSOR_DATA_READY); +			if (err < 0) { +				m4als_err("%s: Failed to disable interrupt.\n", +					  __func__); +				goto m4als_enable_store_exit; +			} +			dd->status = dd->status & ~(1 << M4ALS_IRQ_ENABLED_BIT); +		} +		break; + +	case 1: +		if (!(dd->status & (1 << M4ALS_IRQ_ENABLED_BIT))) { +			err = m4sensorhub_irq_enable(dd->m4, +				M4SH_IRQ_LIGHTSENSOR_DATA_READY); +			if (err < 0) { +				m4als_err("%s: Failed to enable interrupt.\n", +					  __func__); +				goto m4als_enable_store_exit; +			} +			dd->status = dd->status | (1 << M4ALS_IRQ_ENABLED_BIT); +		} +		break; + +	default: +		m4als_err("%s: Invalid value %d passed.\n", __func__, value); +		err = -EINVAL; +		goto m4als_enable_store_exit; +	} + +m4als_enable_store_exit: +	if (err < 0) +		m4als_err("%s: Failed with error code %d.\n", __func__, err); -	sscanf(buf, "%d", &samplerate); -	/* set sample rate */ -	ret = m4sensorhub_reg_write( -				als_client_data->m4sensorhub, -				M4SH_REG_LIGHTSENSOR_SAMPLERATE, -				(char *)&samplerate, m4sh_no_mask -				); -	if (ret < 0) -		KDEBUG( -			M4SH_ERROR, "Failed to set als samplerate to %d\n", -			samplerate -			); -	/* enable interrupt */ -	ret = m4sensorhub_irq_enable( -				als_client_data->m4sensorhub, -				M4SH_IRQ_LIGHTSENSOR_DATA_READY -				); -	if (ret < 0) -		KDEBUG(M4SH_ERROR, "Error enabling als int(%d)\n", ret); +	mutex_unlock(&(dd->mutex)); -	return count; +	return size;  } +static DEVICE_ATTR(als_enable, S_IRUSR | S_IWUSR, +		m4als_enable_show, m4als_enable_store); -static ssize_t als_get_signal(struct device *dev, -			struct device_attribute *attr, char *buf) +static ssize_t m4als_setrate_show(struct device *dev, +		struct device_attribute *attr, char *buf)  { -	struct platform_device *pdev = to_platform_device(dev); -	struct als_client *als_client_data = platform_get_drvdata(pdev); +	struct m4als_driver_data *dd = dev_get_drvdata(dev); -	return sprintf(buf, "%ud\n", als_client_data->als_signal); +	return sprintf(buf, "Current rate: %hd\n", dd->samplerate);  } +static ssize_t m4als_setrate_store(struct device *dev, +		struct device_attribute *attr, const char *buf, size_t size) +{ +	int err = 0; +	struct m4als_driver_data *dd = dev_get_drvdata(dev); +	int value = 0; -static DEVICE_ATTR(samplerate, 0222, NULL, als_set_samplerate); -static DEVICE_ATTR(signal, 0444, als_get_signal, NULL); - -static struct attribute *als_attributes[] = { -	&dev_attr_samplerate.attr, -	&dev_attr_signal.attr, -	NULL -}; +	mutex_lock(&(dd->mutex)); -static const struct attribute_group als_attribute_group = { -	.attrs = als_attributes, -}; +	err = kstrtoint(buf, 10, &value); +	if (err < 0) { +		m4als_err("%s: Failed to convert value.\n", __func__); +		goto m4als_enable_store_exit; +	} -static int als_driver_init(struct init_calldata *p_arg) -{ -	int ret; -	struct m4sensorhub_data *m4sensorhub = p_arg->p_m4sensorhub_data; +	if ((value < -32768) || (value > 32767)) { +		m4als_err("%s: Value of %d is outside range of int16_t.\n", +			  __func__, value); +		err = -EOVERFLOW; +		goto m4als_enable_store_exit; +	} -	ret = m4sensorhub_irq_register(m4sensorhub, -					M4SH_IRQ_LIGHTSENSOR_DATA_READY, -					m4_handle_als_irq, -					misc_als_data); -	if (ret < 0) { -		KDEBUG( -			M4SH_ERROR, -			"Error registering int %d (%d)\n", -			M4SH_IRQ_PASSIVE_BUFFER_FULL, ret -			); +	err = m4als_set_samplerate(dd, value); +	if (err < 0) { +		m4als_err("%s: Failed to set sample rate.\n", __func__); +		goto m4als_enable_store_exit;  	} -	return ret; +m4als_enable_store_exit: +	if (err < 0) +		m4als_err("%s: Failed with error code %d.\n", __func__, err); + +	mutex_unlock(&(dd->mutex)); + +	return size;  } +static DEVICE_ATTR(als_setrate, S_IRUSR | S_IWUSR, +		m4als_setrate_show, m4als_setrate_store); -static int als_client_probe(struct platform_device *pdev) +static ssize_t m4als_luminosity_show(struct device *dev, +		struct device_attribute *attr, char *buf)  { -	int ret = -1; -	struct als_client *als_client_data; -	struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata(); +	struct m4als_driver_data *dd = dev_get_drvdata(dev); -	if (!m4sensorhub) -		return -EFAULT; +	return sprintf(buf, "Current luminosity: %hd\n", dd->luminosity); +} +static DEVICE_ATTR(luminosity, S_IRUGO, m4als_luminosity_show, NULL); -	als_client_data = kzalloc(sizeof(*als_client_data), -						GFP_KERNEL); -	if (!als_client_data) -		return -ENOMEM; +static int m4als_create_sysfs(struct m4als_driver_data *dd) +{ +	int err = 0; -	als_client_data->m4sensorhub = m4sensorhub; -	platform_set_drvdata(pdev, als_client_data); +	err = device_create_file(&(dd->pdev->dev), &dev_attr_als_enable); +	if (err < 0) { +		m4als_err("%s: Failed to create als_enable %s %d.\n", +			  __func__, "with error", err); +		goto m4als_create_sysfs_als_enable_fail; +	} -	ret = misc_register(&als_client_miscdrv); -	if (ret < 0) { -		KDEBUG(M4SH_ERROR, "Error registering %s driver\n", __func__); -		goto free_mem; +	err = device_create_file(&(dd->pdev->dev), &dev_attr_als_setrate); +	if (err < 0) { +		m4als_err("%s: Failed to create als_setrate %s %d.\n", +			  __func__, "with error", err); +		goto m4als_create_sysfs_als_setrate_fail;  	} -	misc_als_data = als_client_data; -	ret = m4sensorhub_register_initcall(als_driver_init, -							als_client_data); -	if (ret < 0) { -		KDEBUG(M4SH_ERROR, "Unable to register init function" -			"for als client = %d\n", ret); -		goto unregister_misc_device; + +	err = device_create_file(&(dd->pdev->dev), &dev_attr_luminosity); +	if (err < 0) { +		m4als_err("%s: Failed to create luminosity %s %d.\n", +			  __func__, "with error", err); +		goto m4als_create_sysfs_luminosity_fail;  	} -	ret = sysfs_create_group(&pdev->dev.kobj, &als_attribute_group); -	if (ret) -		goto unregister_initcall; +	goto m4als_create_sysfs_als_enable_fail; -	KDEBUG(M4SH_INFO, "Initialized %s driver\n", __func__); -	return 0; -unregister_initcall: -	m4sensorhub_unregister_initcall(als_driver_init); -unregister_misc_device: -	misc_als_data = NULL; -	misc_deregister(&als_client_miscdrv); -free_mem: -	platform_set_drvdata(pdev, NULL); -	als_client_data->m4sensorhub = NULL; -	kfree(als_client_data); -	als_client_data = NULL; -	return ret; +m4als_create_sysfs_luminosity_fail: +	device_remove_file(&(dd->pdev->dev), &dev_attr_als_setrate); +m4als_create_sysfs_als_setrate_fail: +	device_remove_file(&(dd->pdev->dev), &dev_attr_als_enable); +m4als_create_sysfs_als_enable_fail: +	return err;  } -static int __exit als_client_remove(struct platform_device *pdev) +static int m4als_remove_sysfs(struct m4als_driver_data *dd)  { -	struct als_client *als_client_data = -						platform_get_drvdata(pdev); +	int err = 0; -	m4sensorhub_irq_disable(als_client_data->m4sensorhub, -				M4SH_IRQ_LIGHTSENSOR_DATA_READY); -	m4sensorhub_irq_unregister( -				als_client_data->m4sensorhub, -				M4SH_IRQ_LIGHTSENSOR_DATA_READY -				); -	m4sensorhub_unregister_initcall(als_driver_init); +	device_remove_file(&(dd->pdev->dev), &dev_attr_als_enable); +	device_remove_file(&(dd->pdev->dev), &dev_attr_als_setrate); +	device_remove_file(&(dd->pdev->dev), &dev_attr_luminosity); -	misc_als_data = NULL; -	misc_deregister(&als_client_miscdrv); -	platform_set_drvdata(pdev, NULL); -	als_client_data->m4sensorhub = NULL; -	kfree(als_client_data); -	als_client_data = NULL; -	return 0; +	return err;  } -static void als_client_shutdown(struct platform_device *pdev) +static int m4als_create_m4eventdev(struct m4als_driver_data *dd)  { -	return; +	int err = 0; + +	dd->indev = input_allocate_device(); +	if (dd->indev == NULL) { +		m4als_err("%s: Failed to allocate input device.\n", +			  __func__); +		err = -ENODATA; +		goto m4als_create_m4eventdev_fail; +	} + +	dd->indev->name = M4ALS_DRIVER_NAME; +	input_set_drvdata(dd->indev, dd); +	input_set_capability(dd->indev, EV_MSC, MSC_RAW); + +	err = input_register_device(dd->indev); +	if (err < 0) { +		m4als_err("%s: Failed to register input device.\n", +			  __func__); +		input_free_device(dd->indev); +		dd->indev = NULL; +		goto m4als_create_m4eventdev_fail; +	} + +m4als_create_m4eventdev_fail: +	return err; +} + +static int m4als_driver_init(struct init_calldata *p_arg) +{ +	struct m4als_driver_data *dd = p_arg->p_data; +	int err = 0; + +	mutex_lock(&(dd->mutex)); + +	err = m4als_create_m4eventdev(dd); +	if (err < 0) { +		m4als_err("%s: Failed to create M4 event device.\n", __func__); +		goto m4als_driver_init_fail; +	} + +	err = m4als_create_sysfs(dd); +	if (err < 0) { +		m4als_err("%s: Failed to create sysfs.\n", __func__); +		goto m4als_driver_init_sysfs_fail; +	} + +	err = m4sensorhub_irq_register(dd->m4, M4SH_IRQ_LIGHTSENSOR_DATA_READY, +		m4als_isr, dd); +	if (err < 0) { +		m4als_err("%s: Failed to register M4 IRQ.\n", __func__); +		goto m4als_driver_init_irq_fail; +	} + +	goto m4als_driver_init_exit; + +m4als_driver_init_irq_fail: +	m4als_remove_sysfs(dd); +m4als_driver_init_sysfs_fail: +	input_unregister_device(dd->indev); +m4als_driver_init_fail: +	m4als_err("%s: Init failed with error code %d.\n", __func__, err); +m4als_driver_init_exit: +	mutex_unlock(&(dd->mutex)); +	return err;  } -#ifdef CONFIG_PM -static int als_client_suspend(struct platform_device *pdev, -				pm_message_t message) + +static int m4als_probe(struct platform_device *pdev)  { +	struct m4als_driver_data *dd = NULL; +	int err = 0; + +	dd = kzalloc(sizeof(*dd), GFP_KERNEL); +	if (dd == NULL) { +		m4als_err("%s: Failed to allocate driver data.\n", __func__); +		err = -ENOMEM; +		goto m4als_probe_fail_nodd; +	} + +	dd->pdev = pdev; +	mutex_init(&(dd->mutex)); +	platform_set_drvdata(pdev, dd); + +	dd->m4 = m4sensorhub_client_get_drvdata(); +	if (dd->m4 == NULL) { +		m4als_err("%s: M4 sensor data is NULL.\n", __func__); +		err = -ENODATA; +		goto m4als_probe_fail; +	} + +	err = m4sensorhub_register_initcall(m4als_driver_init, dd); +	if (err < 0) { +		m4als_err("%s: Failed to register initcall.\n", __func__); +		goto m4als_probe_fail; +	} +  	return 0; + +m4als_probe_fail: +	mutex_destroy(&(dd->mutex)); +	kfree(dd); +m4als_probe_fail_nodd: +	m4als_err("%s: Probe failed with error code %d.\n", __func__, err); +	return err;  } -static int als_client_resume(struct platform_device *pdev) +static int __exit m4als_remove(struct platform_device *pdev)  { +	struct m4als_driver_data *dd = platform_get_drvdata(pdev); + +	mutex_lock(&(dd->mutex)); +	m4als_remove_sysfs(dd); +	if (dd->status & (1 << M4ALS_IRQ_ENABLED_BIT)) { +		m4sensorhub_irq_disable(dd->m4, M4SH_IRQ_PRESSURE_DATA_READY); +		dd->status = dd->status & ~(1 << M4ALS_IRQ_ENABLED_BIT); +	} +	m4sensorhub_irq_unregister(dd->m4, M4SH_IRQ_PRESSURE_DATA_READY); +	m4sensorhub_unregister_initcall(m4als_driver_init); +	if (dd->indev != NULL) +		input_unregister_device(dd->indev); +	mutex_destroy(&(dd->mutex)); +	kfree(dd); +  	return 0;  } -#else -#define als_client_suspend NULL -#define als_client_resume  NULL -#endif  static struct of_device_id m4als_match_tbl[] = {  	{ .compatible = "mot,m4als" },  	{},  }; -static struct platform_driver als_client_driver = { -	.probe		= als_client_probe, -	.remove		= __exit_p(als_client_remove), -	.shutdown	= als_client_shutdown, -	.suspend	= als_client_suspend, -	.resume		= als_client_resume, +static struct platform_driver m4als_driver = { +	.probe		= m4als_probe, +	.remove		= __exit_p(m4als_remove), +	.shutdown	= NULL, +	.suspend	= NULL, +	.resume		= NULL,  	.driver		= { -		.name	= ALS_CLIENT_DRIVER_NAME, +		.name	= M4ALS_DRIVER_NAME,  		.owner	= THIS_MODULE,  		.of_match_table = of_match_ptr(m4als_match_tbl),  	},  }; -static int __init als_client_init(void) +static int __init m4als_init(void)  { -	return platform_driver_register(&als_client_driver); +	return platform_driver_register(&m4als_driver);  } -static void __exit als_client_exit(void) +static void __exit m4als_exit(void)  { -	platform_driver_unregister(&als_client_driver); +	platform_driver_unregister(&m4als_driver);  } -module_init(als_client_init); -module_exit(als_client_exit); +module_init(m4als_init); +module_exit(m4als_exit); -MODULE_ALIAS("platform:als_client"); -MODULE_DESCRIPTION("M4 Sensor Hub Passive mode client driver"); +MODULE_ALIAS("platform:m4als"); +MODULE_DESCRIPTION("M4 Sensor Hub Ambient Light client driver");  MODULE_AUTHOR("Motorola");  MODULE_LICENSE("GPL"); -  |