[PATCH] ACPI ALS driver for iio introduced.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: marxin <marxin.liska@xxxxxxxxx>

---
 drivers/iio/industrialio-buffer.c    |    4 +-
 drivers/staging/iio/light/Kconfig    |    6 +
 drivers/staging/iio/light/Makefile   |    1 +
 drivers/staging/iio/light/acpi-als.c |  486 ++++++++++++++++++++++++++++++++++
 4 files changed, 495 insertions(+), 2 deletions(-)
 create mode 100644 drivers/staging/iio/light/acpi-als.c

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index aaadd32..b8b377c 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -119,8 +119,8 @@ static ssize_t iio_scan_el_show(struct device *dev,
 	int ret;
 	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
 
-	ret = test_bit(to_iio_dev_attr(attr)->address,
-		       indio_dev->buffer->scan_mask);
+	ret = !!(test_bit(to_iio_dev_attr(attr)->address,
+		       indio_dev->buffer->scan_mask));
 
 	return sprintf(buf, "%d\n", ret);
 }
diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
index 4bed30e..a164ecc 100644
--- a/drivers/staging/iio/light/Kconfig
+++ b/drivers/staging/iio/light/Kconfig
@@ -50,4 +50,10 @@ config TSL2x7x
 	 tmd2672, tsl2772, tmd2772 devices.
 	 Provides iio_events and direct access via sysfs.
 
+config ACPI_ALS
+	tristate "ACPI Ambient Light Sensor"
+	help
+	 Support for ACPI0008 Light Sensor.
+	 Provides direct access via sysfs.
+
 endmenu
diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile
index 141af1e..13090e6 100644
--- a/drivers/staging/iio/light/Makefile
+++ b/drivers/staging/iio/light/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o
 obj-$(CONFIG_SENSORS_ISL29028)	+= isl29028.o
 obj-$(CONFIG_TSL2583)	+= tsl2583.o
 obj-$(CONFIG_TSL2x7x)	+= tsl2x7x_core.o
+obj-$(CONFIG_ACPI_ALS)	+= acpi-als.o
diff --git a/drivers/staging/iio/light/acpi-als.c b/drivers/staging/iio/light/acpi-als.c
new file mode 100644
index 0000000..9ba0fc4
--- /dev/null
+++ b/drivers/staging/iio/light/acpi-als.c
@@ -0,0 +1,486 @@
+/*
+ * ACPI Ambient Light Sensor Driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <trace/events/printk.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define PREFIX "ACPI: "
+
+#define ACPI_ALS_CLASS			"als"
+#define ACPI_ALS_DEVICE_NAME		"acpi-als"
+#define ACPI_ALS_NOTIFY_ILLUMINANCE	0x80
+
+#define ACPI_ALS_OUTPUTS		1
+
+#define _COMPONENT			ACPI_ALS_COMPONENT
+ACPI_MODULE_NAME("acpi-als");
+
+MODULE_AUTHOR("Martin Liska <marxin.liska@xxxxxxxxx>");
+MODULE_DESCRIPTION("ACPI Ambient Light Sensor Driver");
+MODULE_LICENSE("GPL");
+
+struct acpi_als_chip {
+    struct acpi_device		*device;
+    struct acpi_als_device	*acpi_als_sys;
+    struct mutex		lock;
+    struct iio_trigger		*trig;
+
+    int				illuminance;
+    int				polling;
+
+    int				count;
+    struct acpi_als_mapping	*mappings;
+};
+
+static int acpi_als_add(struct acpi_device *device);
+static int acpi_als_remove(struct acpi_device *device, int type);
+static void acpi_als_notify(struct acpi_device *device, u32 event);
+
+static const struct acpi_device_id acpi_als_device_ids[] = {
+    {"ACPI0008", 0},
+    {"", 0},
+};
+
+MODULE_DEVICE_TABLE(acpi, acpi_als_device_ids);
+
+static struct acpi_driver acpi_als_driver = {
+    .name = "acpi_als",
+    .class = ACPI_ALS_CLASS,
+    .ids = acpi_als_device_ids,
+    .ops = {
+        .add = acpi_als_add,
+        .remove = acpi_als_remove,
+        .notify = acpi_als_notify,
+    },
+};
+
+struct acpi_als_mapping {
+    int adjustment;
+    int illuminance;
+};
+
+#define ALS_INVALID_VALUE_LOW		0
+#define ALS_INVALID_VALUE_HIGH		-1
+
+/* --------------------------------------------------------------------------
+		Ambient Light Sensor device Management
+   -------------------------------------------------------------------------- */
+
+/*
+ * acpi_als_get_illuminance - get the current ambient light illuminance
+ */
+static int acpi_als_get_illuminance(struct acpi_als_chip *als)
+{
+    acpi_status status;
+    unsigned long long illuminance;
+
+    status = acpi_evaluate_integer(als->device->handle,
+                                   "_ALI", NULL, &illuminance);
+
+    if (ACPI_FAILURE(status)) {
+        ACPI_EXCEPTION((AE_INFO, status, "Error reading ALS illuminance"));
+        als->illuminance = ALS_INVALID_VALUE_LOW;
+        return -ENODEV;
+    }
+    als->illuminance = illuminance;
+
+    return 0;
+}
+
+/*
+ * acpi_als_get_mappings - get the ALS illuminance mappings
+ *
+ * Return a package of ALS illuminance to display adjustment mappings
+ * that can be used by OS to calibrate its ambient light policy
+ * for a given sensor configuration.
+ */
+static int acpi_als_get_mappings(struct acpi_als_chip *chip)
+{
+    int result = 0;
+    acpi_status status;
+    struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+    union acpi_object *alr;
+    int i, j;
+
+    /* Free the old mappings */
+    kfree(chip->mappings);
+    chip->mappings = NULL;
+
+    status =
+        acpi_evaluate_object(chip->device->handle, "_ALR", NULL, &buffer);
+    if (ACPI_FAILURE(status)) {
+        ACPI_EXCEPTION((AE_INFO, status, "Error reading ALS mappings"));
+        return -ENODEV;
+    }
+
+    alr = buffer.pointer;
+    if (!alr || (alr->type != ACPI_TYPE_PACKAGE)) {
+        printk(KERN_ERR PREFIX "Invalid _ALR data\n");
+        result = -EFAULT;
+        goto end;
+    }
+
+    ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d illuminance mappings\n",
+                      alr->package.count));
+
+    chip->count = alr->package.count;
+
+    if (!chip->count)
+        return 0;
+
+    chip->mappings =
+        kmalloc(sizeof(struct acpi_als_mapping) * chip->count, GFP_KERNEL);
+    if (!chip->mappings) {
+        result = -ENOMEM;
+        goto end;
+    }
+
+    for (i = 0, j = 0; i < chip->count; i++) {
+        struct acpi_als_mapping *mapping = &(chip->mappings[j]);
+        union acpi_object *element = &(alr->package.elements[i]);
+
+        if (element->type != ACPI_TYPE_PACKAGE)
+            continue;
+
+        if (element->package.count != 2)
+            continue;
+
+        if (element->package.elements[0].type != ACPI_TYPE_INTEGER ||
+            element->package.elements[1].type != ACPI_TYPE_INTEGER)
+            continue;
+
+        mapping->adjustment =
+            element->package.elements[0].integer.value;
+        mapping->illuminance =
+            element->package.elements[1].integer.value;
+        j++;
+
+        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Mapping [%d]: "
+                          "adjustment [%d] illuminance[%d]\n",
+                          i, mapping->adjustment,
+                          mapping->illuminance));
+    }
+
+end:
+    kfree(buffer.pointer);
+    return result;
+}
+
+/*
+ * acpi_als_get_polling - get a recommended polling frequency
+ * 			  for the Ambient Light Sensor device
+ */
+static int acpi_als_get_polling(struct acpi_als_chip *chip)
+{
+    acpi_status status;
+    unsigned long long polling;
+
+    status =
+        acpi_evaluate_integer(chip->device->handle, "_ALP", NULL, &polling);
+    if (ACPI_FAILURE(status)) {
+        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_ALP not available\n"));
+        return -ENODEV;
+    }
+
+    chip->polling = polling;
+    return 0;
+}
+
+/*
+ * get_illuminance - wrapper for getting the currect ambient light illuminance
+ */
+static int get_illuminance(struct acpi_als_chip *als, int *illuminance)
+{
+    int result;
+
+    result = acpi_als_get_illuminance(als);
+    if (!result)
+        *illuminance = als->illuminance;
+
+    return result;
+}
+
+static void acpi_als_notify(struct acpi_device *device, u32 event)
+{
+	int illuminance;
+	struct iio_dev *indio_dev = acpi_driver_data(device);
+	struct acpi_als_chip *chip = iio_priv(indio_dev);
+
+	s64 time_ns = iio_get_time_ns();
+	int len = sizeof(int);
+	u8 data[sizeof(s64) + ACPI_ALS_OUTPUTS * len];
+
+	switch(event) {
+	case ACPI_ALS_NOTIFY_ILLUMINANCE:
+		get_illuminance(chip, &illuminance);
+		*(int *)((u8 *)data) = illuminance;
+		*(s64 *)((u8 *)data + ALIGN(len, sizeof(s64))) = time_ns;
+		break;
+	default:
+		return;
+	}
+
+	if (iio_buffer_enabled(indio_dev))
+		iio_push_to_buffers(indio_dev, data);
+
+	return;
+}
+
+static int acpi_als_read_raw(struct iio_dev *indio_dev,
+                             struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+    struct acpi_als_chip *chip = iio_priv(indio_dev);
+    int ret = -EINVAL;
+
+    mutex_lock(&chip->lock);
+
+    switch (mask) {
+    case IIO_CHAN_INFO_RAW:
+    case IIO_CHAN_INFO_PROCESSED:
+        switch(chan->type) {
+        case IIO_LIGHT:
+            ret = get_illuminance(chip, val);
+            break;
+        default:
+            break;
+        }
+
+        if(!ret)
+            ret = IIO_VAL_INT;
+
+        break;
+    default:
+        dev_err(&chip->device->dev, "mask value 0x%08lx not supported\n", mask);
+        break;
+    }
+
+    mutex_unlock(&chip->lock);
+
+    return ret;
+}
+
+static const struct iio_chan_spec acpi_als_channels[] = {
+    {
+        .type = IIO_LIGHT,
+        .indexed = 1,
+        .channel = 1,
+        .scan_type.sign = 'u',
+        .scan_type.realbits = 10,
+        .scan_type.storagebits = 16,
+        .info_mask = IIO_CHAN_INFO_PROCESSED_SEPARATE_BIT |
+        IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+    },
+};
+
+static const struct iio_info acpi_als_info = {
+    .driver_module = THIS_MODULE,
+    .read_raw = &acpi_als_read_raw,
+    .write_raw = NULL,
+};
+
+static irqreturn_t acpi_als_trigger_handler(int irq, void *p)
+{
+
+    struct iio_poll_func *pf = p;
+    struct iio_dev *idev = pf->indio_dev;
+
+
+    struct acpi_als_chip *chip = iio_priv(idev);
+
+    printk("XXX: TRIGGER handler called :)");
+    iio_trigger_notify_done(chip->trig);
+    return IRQ_HANDLED;
+}
+
+/**
+ * acpi_als_data_rdy_trigger_set_state() set datardy interrupt state
+ **/
+static int acpi_als_data_rdy_trigger_set_state(struct iio_trigger *trig,
+        bool state)
+{
+
+    struct iio_dev *indio_dev = trig->private_data;
+    printk("XXX: set_state called\n");
+
+    dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
+
+    return 0;
+}
+
+static const struct iio_trigger_ops acpi_als_trigger_ops = {
+    .owner = THIS_MODULE,
+    .set_trigger_state = &acpi_als_data_rdy_trigger_set_state
+};
+
+// TODO
+static const struct iio_buffer_setup_ops acpi_als_buffer_ops = {
+    .preenable = &iio_sw_buffer_preenable, // TODO ?
+    .postenable = &iio_triggered_buffer_postenable,
+    .predisable = &iio_triggered_buffer_predisable,
+};
+
+static struct iio_trigger *acpi_als_allocate_trigger(struct iio_dev *idev) {
+    int ret;
+    struct iio_trigger *trig;
+
+    trig = iio_trigger_alloc("acpi-als");
+    if (trig == NULL)
+        return NULL;
+
+    trig->dev.parent = idev->dev.parent;
+    trig->private_data = idev;
+    trig->ops = &acpi_als_trigger_ops;
+
+
+    ret = iio_trigger_register(trig);
+    if (ret)
+        return NULL;
+
+    printk("XXX: acpi_als_allocate_trigger: %d\n", ret);
+    return trig;
+}
+
+static int acpi_als_trigger_init(struct iio_dev *idev)
+{
+    struct acpi_als_chip *chip = iio_priv(idev);
+    int ret;
+
+    chip->trig = devm_kzalloc(&idev->dev, sizeof(chip->trig), GFP_KERNEL);
+
+    if (chip->trig == NULL) {
+        ret = -ENOMEM;
+        goto error_ret;
+    }
+
+    chip->trig = acpi_als_allocate_trigger(idev);
+    if (chip->trig == NULL) {
+        dev_err(&idev->dev, "Could not allocate trigger!\n");
+        ret = -ENOMEM;
+        goto error_trigger;
+    }
+
+    return 0;
+
+error_trigger:
+    iio_trigger_free(chip->trig);
+error_ret:
+    return ret;
+}
+
+
+static int acpi_als_add(struct acpi_device *device)
+{
+    int result;
+    struct acpi_als_chip *chip;
+    struct iio_dev *indio_dev;
+
+    /*
+    if (unlikely(als_id >= 10)) {
+    	printk(KERN_WARNING PREFIX "Too many ALS device found\n");
+    	return -ENODEV;
+    }
+    */
+
+    indio_dev = iio_device_alloc(sizeof(*chip));
+    if (!indio_dev) {
+        dev_err(&device->dev, "iio allocation fails\n");
+        return -ENOMEM;
+    }
+
+    chip = iio_priv(indio_dev);
+
+    device->driver_data = indio_dev;
+    chip->device = device;
+    mutex_init(&chip->lock);
+
+    indio_dev->info = &acpi_als_info;
+    indio_dev->channels = acpi_als_channels;
+    indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
+    indio_dev->name = ACPI_ALS_DEVICE_NAME;
+    indio_dev->dev.parent = &device->dev;
+    indio_dev->modes = INDIO_DIRECT_MODE;
+
+    result = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+                                        &acpi_als_trigger_handler, NULL);
+
+    if(result) {
+        printk("Could not setup buffer for iio device\n");
+        goto exit_iio_free;
+    }
+
+    result = acpi_als_trigger_init(indio_dev);
+    if (result) {
+        printk("Couldn't setup the triggers.\n");
+        // TODO
+        //goto error_unregister_buffer;
+    }
+
+    result = iio_device_register(indio_dev);
+    if (result < 0) {
+        dev_err(&chip->device->dev, "iio registration fails with error %d\n",
+                result);
+        goto exit_iio_free;
+    }
+
+    printk("ACPI ALS initialized");
+
+    return 0;
+
+exit_iio_free:
+    iio_device_free(indio_dev);
+    return result;
+}
+
+static int acpi_als_remove(struct acpi_device *device, int type)
+{
+    struct iio_dev *indio_dev;
+
+    indio_dev = acpi_driver_data(device);
+    if(!indio_dev) {
+        dev_err(&device->dev, "could not get indio_dev for ACPI device\n");
+        return -1;
+    }
+
+    iio_device_unregister(indio_dev);
+    iio_device_free(indio_dev);
+
+    return 0;
+}
+
+static int __init acpi_als_init(void)
+{
+    return acpi_bus_register_driver(&acpi_als_driver);
+}
+
+static void __exit acpi_als_exit(void)
+{
+    acpi_bus_unregister_driver(&acpi_als_driver);
+}
+
+module_init(acpi_als_init);
+module_exit(acpi_als_exit);
-- 
1.7.8.6

--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux