Re: [PATCH 3/3] iio: light: apple-ib-als: Add driver for ALS on iBridge chip.

From: Peter Meerwald-Stadler
Date: Mon Apr 22 2019 - 05:23:09 EST


On Sun, 21 Apr 2019, Ronald TschalÃr wrote:

> On 2016/2017 MacBook Pro's with a Touch Bar the ALS is attached to,
> and exposed via the iBridge device. This provides the driver for that
> sensor.

some comments below inline

> Signed-off-by: Ronald TschalÃr <ronald@xxxxxxxxxxxxx>
> ---
> drivers/iio/light/Kconfig | 12 +
> drivers/iio/light/Makefile | 1 +
> drivers/iio/light/apple-ib-als.c | 694 +++++++++++++++++++++++++++++++
> 3 files changed, 707 insertions(+)
> create mode 100644 drivers/iio/light/apple-ib-als.c
>
> diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
> index 36f458433480..49159fab1c0e 100644
> --- a/drivers/iio/light/Kconfig
> +++ b/drivers/iio/light/Kconfig
> @@ -64,6 +64,18 @@ config APDS9960
> To compile this driver as a module, choose M here: the
> module will be called apds9960
>
> +config APPLE_IBRIDGE_ALS
> + tristate "Apple iBridge ambient light sensor"
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> + depends on MFD_APPLE_IBRIDGE
> + help
> + Say Y here to build the driver for the Apple iBridge ALS
> + sensor.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called apple-ib-als.
> +
> config BH1750
> tristate "ROHM BH1750 ambient light sensor"
> depends on I2C
> diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> index 286bf3975372..144d918917f7 100644
> --- a/drivers/iio/light/Makefile
> +++ b/drivers/iio/light/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_ADJD_S311) += adjd_s311.o
> obj-$(CONFIG_AL3320A) += al3320a.o
> obj-$(CONFIG_APDS9300) += apds9300.o
> obj-$(CONFIG_APDS9960) += apds9960.o
> +obj-$(CONFIG_APPLE_IBRIDGE_ALS) += apple-ib-als.o
> obj-$(CONFIG_BH1750) += bh1750.o
> obj-$(CONFIG_BH1780) += bh1780.o
> obj-$(CONFIG_CM32181) += cm32181.o
> diff --git a/drivers/iio/light/apple-ib-als.c b/drivers/iio/light/apple-ib-als.c
> new file mode 100644
> index 000000000000..1718fcbe304f
> --- /dev/null
> +++ b/drivers/iio/light/apple-ib-als.c
> @@ -0,0 +1,694 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Apple Ambient Light Sensor Driver
> + *
> + * Copyright (c) 2017-2018 Ronald TschalÃr
> + */
> +
> +/*
> + * MacBookPro models with an iBridge chip (13,[23] and 14,[23]) have an
> + * ambient light sensor that is exposed via one of the USB interfaces on
> + * the iBridge as a standard HID light sensor. However, we cannot use the
> + * existing hid-sensor-als driver, for two reasons:
> + *
> + * 1. The hid-sensor-als driver is part of the hid-sensor-hub which in turn
> + * is a hid driver, but you can't have more than one hid driver per hid
> + * device, which is a problem because the touch bar also needs to
> + * register as a driver for this hid device.
> + *
> + * 2. While the hid-sensors-als driver stores sensor readings received via
> + * interrupt in an iio buffer, reads on the sysfs
> + * .../iio:deviceX/in_illuminance_YYY attribute result in a get of the
> + * feature report; however, in the case of this sensor here the
> + * illuminance field of that report is always 0. Instead, the input
> + * report needs to be requested.
> + */
> +
> +#define dev_fmt(fmt) "als: " fmt
> +
> +#include <linux/device.h>
> +#include <linux/hid.h>
> +#include <linux/hid-sensor-ids.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/mfd/apple-ibridge.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#define APPLEALS_DYN_SENS 0 /* our dynamic sensitivity */
> +#define APPLEALS_DEF_CHANGE_SENS APPLEALS_DYN_SENS
> +
> +struct appleals_device {
> + struct appleib_device *ib_dev;
> + struct device *log_dev;
> + struct hid_device *hid_dev;
> + struct hid_report *cfg_report;
> + struct hid_field *illum_field;
> + struct iio_dev *iio_dev;
> + struct iio_trigger *iio_trig;
> + int cur_sensitivity;
> + int cur_hysteresis;
> + bool events_enabled;
> +};
> +
> +static struct hid_driver appleals_hid_driver;
> +
> +/*
> + * This is a primitive way to get a relative sensitivity, one where we get
> + * notified when the value changes by a certain percentage rather than some
> + * absolute value. MacOS somehow manages to configure the sensor to work this
> + * way (with a 15% relative sensitivity), but I haven't been able to figure
> + * out how so far. So until we do, this provides a less-than-perfect
> + * simulation.
> + *
> + * When the brightness value is within one of the ranges, the sensitivity is
> + * set to that range's sensitivity. But in order to reduce flapping when the
> + * brightness is right on the border between two ranges, the ranges overlap
> + * somewhat (by at least one sensitivity), and sensitivity is only changed if
> + * the value leaves the current sensitivity's range.
> + *
> + * The values chosen for the map are somewhat arbitrary: a compromise of not
> + * too many ranges (and hence changing the sensitivity) but not too small or
> + * large of a percentage of the min and max values in the range (currently
> + * from 7.5% to 30%, i.e. within a factor of 2 of 15%), as well as just plain
> + * "this feels reasonable to me".
> + */
> +struct appleals_sensitivity_map {
> + int sensitivity;
> + int illum_low;
> + int illum_high;
> +};
> +
> +static struct appleals_sensitivity_map appleals_sensitivity_map[] = {

const?

> + { 1, 0, 14 },
> + { 3, 10, 40 },
> + { 9, 30, 120 },
> + { 27, 90, 360 },
> + { 81, 270, 1080 },
> + { 243, 810, 3240 },
> + { 729, 2430, 9720 },
> +};
> +
> +static int appleals_compute_sensitivity(int cur_illum, int cur_sens)
> +{
> + struct appleals_sensitivity_map *entry;
> + int i;
> +
> + /* see if we're still in current range */
> + for (i = 0; i < ARRAY_SIZE(appleals_sensitivity_map); i++) {
> + entry = &appleals_sensitivity_map[i];
> +
> + if (entry->sensitivity == cur_sens &&
> + entry->illum_low <= cur_illum &&
> + entry->illum_high >= cur_illum)
> + return cur_sens;
> + else if (entry->sensitivity > cur_sens)
> + break;
> + }
> +
> + /* not in current range, so find new sensitivity */
> + for (i = 0; i < ARRAY_SIZE(appleals_sensitivity_map); i++) {
> + entry = &appleals_sensitivity_map[i];
> +
> + if (entry->illum_low <= cur_illum &&
> + entry->illum_high >= cur_illum)
> + return entry->sensitivity;
> + }
> +
> + /* hmm, not in table, so assume we are above highest range */
> + i = ARRAY_SIZE(appleals_sensitivity_map) - 1;
> + return appleals_sensitivity_map[i].sensitivity;
> +}
> +
> +static int appleals_get_field_value_for_usage(struct hid_field *field,
> + unsigned int usage)
> +{
> + int u;
> +
> + if (!field)
> + return -1;
> +
> + for (u = 0; u < field->maxusage; u++) {
> + if (field->usage[u].hid == usage)
> + return u + field->logical_minimum;
> + }
> +
> + return -1;
> +}
> +
> +static __s32 appleals_get_field_value(struct appleals_device *als_dev,
> + struct hid_field *field)
> +{
> + hid_hw_request(als_dev->hid_dev, field->report, HID_REQ_GET_REPORT);
> + hid_hw_wait(als_dev->hid_dev);
> +
> + return field->value[0];
> +}
> +
> +static void appleals_set_field_value(struct appleals_device *als_dev,
> + struct hid_field *field, __s32 value)
> +{
> + hid_set_field(field, 0, value);
> + hid_hw_request(als_dev->hid_dev, field->report, HID_REQ_SET_REPORT);
> +}
> +
> +static int appleals_get_config(struct appleals_device *als_dev,
> + unsigned int field_usage, __s32 *value)
> +{
> + struct hid_field *field;
> +
> + field = appleib_find_report_field(als_dev->cfg_report, field_usage);
> + if (!field)
> + return -EINVAL;
> +
> + *value = appleals_get_field_value(als_dev, field);
> +
> + return 0;
> +}
> +
> +static int appleals_set_config(struct appleals_device *als_dev,
> + unsigned int field_usage, __s32 value)
> +{
> + struct hid_field *field;
> +
> + field = appleib_find_report_field(als_dev->cfg_report, field_usage);
> + if (!field)
> + return -EINVAL;
> +
> + appleals_set_field_value(als_dev, field, value);
> +
> + return 0;
> +}
> +
> +static int appleals_set_enum_config(struct appleals_device *als_dev,
> + unsigned int field_usage,
> + unsigned int value_usage)
> +{
> + struct hid_field *field;
> + int value;
> +
> + field = appleib_find_report_field(als_dev->cfg_report, field_usage);
> + if (!field)
> + return -EINVAL;
> +
> + value = appleals_get_field_value_for_usage(field, value_usage);

can return -1, not checked

> +
> + appleals_set_field_value(als_dev, field, value);
> +
> + return 0;
> +}
> +
> +static void appleals_update_dyn_sensitivity(struct appleals_device *als_dev,
> + __s32 value)
> +{
> + int new_sens;
> + int rc;
> +
> + new_sens = appleals_compute_sensitivity(value,
> + als_dev->cur_sensitivity);
> + if (new_sens != als_dev->cur_sensitivity) {
> + rc = appleals_set_config(als_dev,
> + HID_USAGE_SENSOR_LIGHT_ILLUM |
> + HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS,
> + new_sens);
> + if (!rc)
> + als_dev->cur_sensitivity = new_sens;
> + }
> +}
> +
> +static void appleals_push_new_value(struct appleals_device *als_dev,
> + __s32 value)
> +{
> + __s32 buf[2] = { value, value };
> +
> + iio_push_to_buffers(als_dev->iio_dev, buf);
> +
> + if (als_dev->cur_hysteresis == APPLEALS_DYN_SENS)
> + appleals_update_dyn_sensitivity(als_dev, value);
> +}
> +
> +static int appleals_hid_event(struct hid_device *hdev, struct hid_field *field,
> + struct hid_usage *usage, __s32 value)
> +{
> + struct appleals_device *als_dev =
> + appleib_get_drvdata(hid_get_drvdata(hdev),
> + &appleals_hid_driver);
> + int rc = 0;
> +
> + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_SENSOR)
> + return 0;
> +
> + if (usage->hid == HID_USAGE_SENSOR_LIGHT_ILLUM) {
> + appleals_push_new_value(als_dev, value);
> + rc = 1;
> + }
> +
> + return rc;
> +}
> +
> +static int appleals_enable_events(struct iio_trigger *trig, bool enable)
> +{
> + struct appleals_device *als_dev = iio_trigger_get_drvdata(trig);
> + int value;
> +
> + /* set the sensor's reporting state */
> + appleals_set_enum_config(als_dev, HID_USAGE_SENSOR_PROP_REPORT_STATE,
> + enable ? HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM :
> + HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM);
> + als_dev->events_enabled = enable;
> +
> + /* if the sensor was enabled, push an initial value */
> + if (enable) {
> + value = appleals_get_field_value(als_dev, als_dev->illum_field);
> + appleals_push_new_value(als_dev, value);
> + }
> +
> + return 0;
> +}
> +
> +static int appleals_read_raw(struct iio_dev *iio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct appleals_device *als_dev =
> + *(struct appleals_device **)iio_priv(iio_dev);
> + __s32 value;
> + int rc;
> +
> + *val = 0;
> + *val2 = 0;

no need to set these here

> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + case IIO_CHAN_INFO_PROCESSED:
> + *val = appleals_get_field_value(als_dev, als_dev->illum_field);
> + return IIO_VAL_INT;
> +
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + rc = appleals_get_config(als_dev,
> + HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,
> + &value);
> + if (rc)
> + return rc;
> +
> + /* interval is in ms; val is in HZ, val2 in ÂHZ */
> + value = 1000000000 / value;
> + *val = value / 1000000;
> + *val2 = value - (*val * 1000000);
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case IIO_CHAN_INFO_HYSTERESIS:
> + if (als_dev->cur_hysteresis == APPLEALS_DYN_SENS) {
> + *val = als_dev->cur_hysteresis;
> + return IIO_VAL_INT;
> + }
> +
> + rc = appleals_get_config(als_dev,
> + HID_USAGE_SENSOR_LIGHT_ILLUM |
> + HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS,
> + val);
> + if (!rc) {
> + als_dev->cur_sensitivity = *val;
> + als_dev->cur_hysteresis = *val;
> + }
> + return rc ? rc : IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int appleals_write_raw(struct iio_dev *iio_dev,
> + struct iio_chan_spec const *chan,
> + int val, int val2, long mask)
> +{
> + struct appleals_device *als_dev =
> + *(struct appleals_device **)iio_priv(iio_dev);
> + __s32 illum;
> + int rc;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + rc = appleals_set_config(als_dev,
> + HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,
> + 1000000000 / (val * 1000000 + val2));
> + break;

maybe return directly instead of at the end (matter of taste);
here and in the other cases below

> +
> + case IIO_CHAN_INFO_HYSTERESIS:
> + if (val == APPLEALS_DYN_SENS) {
> + if (als_dev->cur_hysteresis != APPLEALS_DYN_SENS) {
> + als_dev->cur_hysteresis = val;
> + illum = appleals_get_field_value(als_dev,
> + als_dev->illum_field);
> + appleals_update_dyn_sensitivity(als_dev, illum);
> + }
> + rc = 0;
> + break;
> + }
> +
> + rc = appleals_set_config(als_dev,
> + HID_USAGE_SENSOR_LIGHT_ILLUM |
> + HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS,
> + val);
> + if (!rc) {
> + als_dev->cur_sensitivity = val;
> + als_dev->cur_hysteresis = val;
> + }
> + break;
> +
> + default:
> + rc = -EINVAL;
> + }
> +
> + return rc;
> +}
> +
> +static const struct iio_chan_spec appleals_channels[] = {
> + {
> + .type = IIO_INTENSITY,
> + .modified = 1,
> + .channel2 = IIO_MOD_LIGHT_BOTH,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
> + BIT(IIO_CHAN_INFO_RAW),
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> + BIT(IIO_CHAN_INFO_HYSTERESIS),
> + .scan_type = {
> + .sign = 'u',
> + .realbits = 32,
> + .storagebits = 32,
> + },
> + .scan_index = 0,
> + },
> + {
> + .type = IIO_LIGHT,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
> + BIT(IIO_CHAN_INFO_RAW),
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> + BIT(IIO_CHAN_INFO_HYSTERESIS),
> + .scan_type = {
> + .sign = 'u',
> + .realbits = 32,
> + .storagebits = 32,
> + },
> + .scan_index = 1,
> + }
> +};
> +
> +static const struct iio_trigger_ops appleals_trigger_ops = {
> + .set_trigger_state = &appleals_enable_events,
> +};
> +
> +static const struct iio_info appleals_info = {
> + .read_raw = &appleals_read_raw,
> + .write_raw = &appleals_write_raw,
> +};
> +
> +static void appleals_config_sensor(struct appleals_device *als_dev,
> + bool events_enabled, int sensitivity)
> +{
> + struct hid_field *field;
> + __s32 val;
> +
> + /*
> + * We're (often) in a probe here, so need to enable input processing
> + * in that case, but only in that case.
> + */
> + if (appleib_in_hid_probe(als_dev->ib_dev))
> + hid_device_io_start(als_dev->hid_dev);
> +
> + /* power on the sensor */
> + field = appleib_find_report_field(als_dev->cfg_report,
> + HID_USAGE_SENSOR_PROY_POWER_STATE);
> + val = appleals_get_field_value_for_usage(field,
> + HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM);

what if -1?

> + hid_set_field(field, 0, val);
> +
> + /* configure reporting of change events */
> + field = appleib_find_report_field(als_dev->cfg_report,
> + HID_USAGE_SENSOR_PROP_REPORT_STATE);
> + val = appleals_get_field_value_for_usage(field,
> + events_enabled ?
> + HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM :
> + HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM);
> + hid_set_field(field, 0, val);
> +
> + /* report change events asap */
> + field = appleib_find_report_field(als_dev->cfg_report,
> + HID_USAGE_SENSOR_PROP_REPORT_INTERVAL);
> + hid_set_field(field, 0, field->logical_minimum);
> +
> + /*
> + * Set initial change sensitivity; if dynamic, enabling trigger will set
> + * it instead.
> + */
> + if (sensitivity != APPLEALS_DYN_SENS) {
> + field = appleib_find_report_field(als_dev->cfg_report,
> + HID_USAGE_SENSOR_LIGHT_ILLUM |
> + HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS);
> +
> + hid_set_field(field, 0, sensitivity);
> + }
> +
> + /* write the new config to the sensor */
> + hid_hw_request(als_dev->hid_dev, als_dev->cfg_report,
> + HID_REQ_SET_REPORT);
> +
> + if (appleib_in_hid_probe(als_dev->ib_dev))
> + hid_device_io_stop(als_dev->hid_dev);
> +};

no semicolon at the end of a function please

> +
> +static int appleals_config_iio(struct appleals_device *als_dev)
> +{
> + struct iio_dev *iio_dev;
> + struct iio_trigger *iio_trig;
> + int rc;
> +
> + /* create and register iio device */
> + iio_dev = iio_device_alloc(sizeof(als_dev));

how about using the devm_ variants?

> + if (!iio_dev)
> + return -ENOMEM;
> +
> + *(struct appleals_device **)iio_priv(iio_dev) = als_dev;
> +
> + iio_dev->channels = appleals_channels;
> + iio_dev->num_channels = ARRAY_SIZE(appleals_channels);
> + iio_dev->dev.parent = &als_dev->hid_dev->dev;
> + iio_dev->info = &appleals_info;
> + iio_dev->name = "als";
> + iio_dev->modes = INDIO_DIRECT_MODE;
> +
> + rc = iio_triggered_buffer_setup(iio_dev, &iio_pollfunc_store_time, NULL,
> + NULL);
> + if (rc) {
> + dev_err(als_dev->log_dev, "failed to set up iio triggers: %d\n",

just one trigger?

> + rc);
> + goto free_iio_dev;
> + }
> +
> + iio_trig = iio_trigger_alloc("%s-dev%d", iio_dev->name, iio_dev->id);
> + if (!iio_trig) {
> + rc = -ENOMEM;
> + goto clean_trig_buf;
> + }
> +
> + iio_trig->dev.parent = &als_dev->hid_dev->dev;
> + iio_trig->ops = &appleals_trigger_ops;
> + iio_trigger_set_drvdata(iio_trig, als_dev);
> +
> + rc = iio_trigger_register(iio_trig);
> + if (rc) {
> + dev_err(als_dev->log_dev, "failed to register iio trigger: %d\n",

some messages start lowercase, some uppercase (nitpicking)

> + rc);
> + goto free_iio_trig;
> + }
> +
> + als_dev->iio_trig = iio_trig;
> +
> + rc = iio_device_register(iio_dev);
> + if (rc) {
> + dev_err(als_dev->log_dev, "failed to register iio device: %d\n",
> + rc);
> + goto unreg_iio_trig;
> + }
> +
> + als_dev->iio_dev = iio_dev;
> +
> + return 0;
> +
> +unreg_iio_trig:
> + iio_trigger_unregister(iio_trig);
> +free_iio_trig:
> + iio_trigger_free(iio_trig);
> + als_dev->iio_trig = NULL;
> +clean_trig_buf:
> + iio_triggered_buffer_cleanup(iio_dev);
> +free_iio_dev:
> + iio_device_free(iio_dev);
> +
> + return rc;
> +}
> +
> +static int appleals_probe(struct hid_device *hdev,
> + const struct hid_device_id *id)
> +{
> + struct appleals_device *als_dev =
> + appleib_get_drvdata(hid_get_drvdata(hdev),
> + &appleals_hid_driver);
> + struct hid_field *state_field;
> + struct hid_field *illum_field;
> + int rc;
> +
> + /* find als fields and reports */
> + state_field = appleib_find_hid_field(hdev, HID_USAGE_SENSOR_ALS,
> + HID_USAGE_SENSOR_PROP_REPORT_STATE);
> + illum_field = appleib_find_hid_field(hdev, HID_USAGE_SENSOR_ALS,
> + HID_USAGE_SENSOR_LIGHT_ILLUM);
> + if (!state_field || !illum_field)
> + return -ENODEV;
> +
> + if (als_dev->hid_dev) {
> + dev_warn(als_dev->log_dev,
> + "Found duplicate ambient light sensor - ignoring\n");
> + return -EBUSY;
> + }
> +
> + dev_info(als_dev->log_dev, "Found ambient light sensor\n");

in general avoid logging for the OK case, it just clutters the log

> +
> + /* initialize device */
> + als_dev->hid_dev = hdev;
> + als_dev->cfg_report = state_field->report;
> + als_dev->illum_field = illum_field;
> +
> + als_dev->cur_hysteresis = APPLEALS_DEF_CHANGE_SENS;
> + als_dev->cur_sensitivity = APPLEALS_DEF_CHANGE_SENS;
> + appleals_config_sensor(als_dev, false, als_dev->cur_sensitivity);
> +
> + rc = appleals_config_iio(als_dev);
> + if (rc)
> + return rc;
> +
> + return 0;
> +}
> +
> +static void appleals_remove(struct hid_device *hdev)
> +{
> + struct appleals_device *als_dev =
> + appleib_get_drvdata(hid_get_drvdata(hdev),
> + &appleals_hid_driver);
> +

could be a lot less if devm_ were used?

> + if (als_dev->iio_dev) {
> + iio_device_unregister(als_dev->iio_dev);
> +
> + iio_trigger_unregister(als_dev->iio_trig);
> + iio_trigger_free(als_dev->iio_trig);
> + als_dev->iio_trig = NULL;
> +
> + iio_triggered_buffer_cleanup(als_dev->iio_dev);
> + iio_device_free(als_dev->iio_dev);
> + als_dev->iio_dev = NULL;
> + }
> +
> + als_dev->hid_dev = NULL;
> +}
> +
> +#ifdef CONFIG_PM
> +static int appleals_reset_resume(struct hid_device *hdev)
> +{
> + struct appleals_device *als_dev =
> + appleib_get_drvdata(hid_get_drvdata(hdev),
> + &appleals_hid_driver);
> +
> + appleals_config_sensor(als_dev, als_dev->events_enabled,
> + als_dev->cur_sensitivity);
> +
> + return 0;
> +}
> +#endif
> +
> +static struct hid_driver appleals_hid_driver = {
> + .name = "apple-ib-als",
> + .probe = appleals_probe,
> + .remove = appleals_remove,
> + .event = appleals_hid_event,
> +#ifdef CONFIG_PM
> + .reset_resume = appleals_reset_resume,
> +#endif
> +};
> +
> +static int appleals_platform_probe(struct platform_device *pdev)
> +{
> + struct appleib_platform_data *pdata = pdev->dev.platform_data;
> + struct appleib_device *ib_dev = pdata->ib_dev;
> + struct appleals_device *als_dev;
> + int rc;
> +
> + als_dev = kzalloc(sizeof(*als_dev), GFP_KERNEL);
> + if (!als_dev)
> + return -ENOMEM;
> +
> + als_dev->ib_dev = ib_dev;
> + als_dev->log_dev = pdata->log_dev;
> +
> + rc = appleib_register_hid_driver(ib_dev, &appleals_hid_driver, als_dev);
> + if (rc) {
> + dev_err(als_dev->log_dev, "Error registering hid driver: %d\n",
> + rc);
> + goto error;
> + }
> +
> + platform_set_drvdata(pdev, als_dev);
> +
> + return 0;
> +
> +error:
> + kfree(als_dev);
> + return rc;
> +}
> +
> +static int appleals_platform_remove(struct platform_device *pdev)
> +{
> + struct appleib_platform_data *pdata = pdev->dev.platform_data;
> + struct appleib_device *ib_dev = pdata->ib_dev;
> + struct appleals_device *als_dev = platform_get_drvdata(pdev);
> + int rc;
> +
> + rc = appleib_unregister_hid_driver(ib_dev, &appleals_hid_driver);
> + if (rc) {
> + dev_err(als_dev->log_dev,
> + "Error unregistering hid driver: %d\n", rc);
> + goto error;
> + }
> +
> + kfree(als_dev);
> +
> + return 0;
> +
> +error:
> + return rc;
> +}
> +
> +static const struct platform_device_id appleals_platform_ids[] = {
> + { .name = PLAT_NAME_IB_ALS },
> + { }
> +};
> +MODULE_DEVICE_TABLE(platform, appleals_platform_ids);
> +
> +static struct platform_driver appleals_platform_driver = {
> + .id_table = appleals_platform_ids,
> + .driver = {
> + .name = "apple-ib-als",
> + },
> + .probe = appleals_platform_probe,
> + .remove = appleals_platform_remove,
> +};
> +
> +module_platform_driver(appleals_platform_driver);
> +
> +MODULE_AUTHOR("Ronald TschalÃr");
> +MODULE_DESCRIPTION("Apple iBridge ALS driver");
> +MODULE_LICENSE("GPL v2");
>

--

Peter Meerwald-Stadler
Mobile: +43 664 24 44 418