* [PATCH V5] TAOS tsl2x7x
@ 2012-04-02 16:50 Jon Brenner
2012-04-04 8:35 ` Jonathan Cameron
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Jon Brenner @ 2012-04-02 16:50 UTC (permalink / raw)
To: Jonathan Cameron; +Cc: linux-iio, Linux Kernel
TAOS device driver (version 5) for the tsl/tmd 2771 and 2772 device families (inc. all variants).
Signed-off-by: Jon Brenner <jbrenner@taosinc.com>
---
.../light/sysfs-bus-iio-light-tsl2583 | 6 +
.../light/sysfs-bus-iio-light-tsl2x7x | 14 +
drivers/staging/iio/Documentation/sysfs-bus-iio | 7 +
.../staging/iio/Documentation/sysfs-bus-iio-light | 8 +-
.../iio/Documentation/sysfs-bus-iio-light-tsl2583 | 20 -
drivers/staging/iio/light/Kconfig | 8 +
drivers/staging/iio/light/Makefile | 2 +
drivers/staging/iio/light/tsl2x7x.h | 99 ++
drivers/staging/iio/light/tsl2x7x_core.c | 1830 ++++++++++++++++++++
9 files changed, 1970 insertions(+), 24 deletions(-)
diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
new file mode 100644
index 0000000..8f2a038
--- /dev/null
+++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
@@ -0,0 +1,6 @@
+What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property causes an internal calibration of the als gain trim
+ value which is later used in calculating illuminance in lux.
diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
new file mode 100644
index 0000000..275ae54
--- /dev/null
+++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
@@ -0,0 +1,14 @@
+What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property causes an internal calibration of the als gain trim
+ value which is later used in calculating illuminance in lux.
+
+What: /sys/bus/iio/devices/device[n]/proximity_calibrate
+KernelVersion: 3.3-rc1
+Contact: linux-iio@vger.kernel.org
+Description:
+ Causes an recalculation and adjustment to the
+ proximity_thresh_rising_value.
+
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio b/drivers/staging/iio/Documentation/sysfs-bus-iio
index 46a995d..5b2b5d3 100644
--- a/drivers/staging/iio/Documentation/sysfs-bus-iio
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio
@@ -258,6 +258,8 @@ What /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale
What /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibscale
What /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
What /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale
+what /sys/bus/iio/devices/iio:deviceX/illuminance0_calibscale
+what /sys/bus/iio/devices/iio:deviceX/proximity_calibscale
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
@@ -457,6 +459,10 @@ What: /sys/.../events/in_voltageY_raw_thresh_falling_value
What: /sys/.../events/in_voltageY_raw_thresh_falling_value
What: /sys/.../events/in_tempY_raw_thresh_falling_value
What: /sys/.../events/in_tempY_raw_thresh_falling_value
+What: /sys/.../events/illuminance0_thresh_falling_value
+what: /sys/.../events/illuminance0_thresh_rising_value
+what: /sys/.../events/proximity_thresh_falling_value
+what: /sys/.../events/proximity_thresh_rising_value
KernelVersion: 2.6.37
Contact: linux-iio@vger.kernel.org
Description:
@@ -739,3 +745,4 @@ Description:
system. To minimize the current consumption of the system,
the bridge can be disconnected (when it is not being used
using the bridge_switch_en attribute.
+
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
index edbf470..4385c70 100644
--- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
@@ -76,10 +76,10 @@ Contact: linux-iio@vger.kernel.org
Description:
This property gets/sets the sensors ADC analog integration time.
-What: /sys/bus/iio/devices/device[n]/illuminance0_calibscale
+What: /sys/bus/iio/devices/device[n]/lux_table
KernelVersion: 2.6.37
Contact: linux-iio@vger.kernel.org
Description:
- Hardware or software applied calibration scale factor assumed
- to account for attenuation due to industrial design (glass
- filters or aperture holes).
+ This property gets/sets the table of coefficients
+ used in calculating illuminance in lux.
+
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
deleted file mode 100644
index 660781d..0000000
--- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
+++ /dev/null
@@ -1,20 +0,0 @@
-What: /sys/bus/iio/devices/device[n]/lux_table
-KernelVersion: 2.6.37
-Contact: linux-iio@vger.kernel.org
-Description:
- This property gets/sets the table of coefficients
- used in calculating illuminance in lux.
-
-What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate
-KernelVersion: 2.6.37
-Contact: linux-iio@vger.kernel.org
-Description:
- This property causes an internal calibration of the als gain trim
- value which is later used in calculating illuminance in lux.
-
-What: /sys/bus/iio/devices/device[n]/illuminance0_input_target
-KernelVersion: 2.6.37
-Contact: linux-iio@vger.kernel.org
-Description:
- This property is the known externally illuminance (in lux).
- It is used in the process of calibrating the device accuracy.
diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
index e7e9159..976f790 100644
--- a/drivers/staging/iio/light/Kconfig
+++ b/drivers/staging/iio/light/Kconfig
@@ -31,4 +31,12 @@ config TSL2583
Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
Access ALS data via iio, sysfs.
+config TSL2x7x
+ tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors"
+ depends on I2C
+ help
+ Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672,
+ tmd2672, tsl2772, tmd2772 devices.
+ Provides iio_events and direct access via sysfs.
+
endmenu
diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile
index 3011fbf..0b8fb22 100644
--- a/drivers/staging/iio/light/Makefile
+++ b/drivers/staging/iio/light/Makefile
@@ -5,3 +5,5 @@
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
obj-$(CONFIG_TSL2583) += tsl2583.o
+obj-$(CONFIG_TSL2x7x) += tsl2x7x_core.o
+obj-$(CONFIG_TCS3x7x) += tcs3x7x_core.o
diff --git a/drivers/staging/iio/light/tsl2x7x.h b/drivers/staging/iio/light/tsl2x7x.h
new file mode 100644
index 0000000..fe9e853
--- /dev/null
+++ b/drivers/staging/iio/light/tsl2x7x.h
@@ -0,0 +1,99 @@
+/*
+ * Device driver for monitoring ambient light intensity (lux)
+ * and proximity (prox) within the TAOS TSL2X7X family of devices.
+ *
+ * Copyright (c) 2012, TAOS Corporation.
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __TSL2X7X_H
+#define __TSL2X7X_H
+#include <linux/pm.h>
+
+/* Max number of segments allowable in LUX table */
+#define TSL2X7X_MAX_LUX_TABLE_SIZE 9
+#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) * TSL2X7X_MAX_LUX_TABLE_SIZE)
+
+struct iio_dev;
+
+struct tsl2x7x_lux {
+ unsigned int ratio;
+ unsigned int ch0;
+ unsigned int ch1;
+};
+
+/**
+ * struct tsl2x7x_default_settings - power on defaults unless
+ * overridden by platform data.
+ * @als_time: ALS Integration time - multiple of 50mS
+ * @als_gain: Index into the ALS gain table.
+ * @prx_time: 5.2ms prox integration time -
+ * dec in 2.7ms periods
+ * @wait_time: Time between PRX and ALS cycles
+ * in 2.7 periods
+ * @prox_config: Prox configuration filters.
+ * @als_gain_trim: default gain trim to account for
+ * aperture effects.
+ * @als_cal_target: Known external ALS reading for
+ * calibration.
+ * @als_thresh_low: CH0 'low' count to trigger interrupt.
+ * @als_thresh_high: CH0 'high' count to trigger interrupt.
+ * @persistence: H/W Filters, Number of 'out of limits'
+ * ADC readings PRX/ALS.
+ * @interrupts_en: Enable/Disable - 0x00 = none, 0x10 = als,
+ * 0x20 = prx, 0x30 = bth
+ * @prox_thres_low: Low threshold proximity detection.
+ * @prox_thres_high: High threshold proximity detection
+ * @prox_max_samples_cal: Used for prox cal.
+ * @prox_pulse_count: Number if proximity emitter pulses
+ */
+struct tsl2x7x_settings {
+ int als_time;
+ int als_gain;
+ int als_gain_trim;
+ int wait_time;
+ int prx_time;
+ int prox_gain;
+ int prox_config;
+ int als_cal_target;
+ u8 interrupts_en;
+ u8 persistence;
+ int als_thresh_low;
+ int als_thresh_high;
+ int prox_thres_low;
+ int prox_thres_high;
+ int prox_pulse_count;
+ int prox_max_samples_cal;
+};
+
+/**
+ * struct tsl2X7X_platform_data - Platform callback, glass and defaults
+ * @platform_power: Suspend/resume platform callback
+ * @power_on: Power on callback
+ * @power_off: Power off callback
+ * @platform_lux_table: Device specific glass coefficents
+ * @platform_default_settings: Device specific power on defaults
+ * Platform PM functions.
+ */
+struct tsl2X7X_platform_data {
+ int (*platform_power)(struct device *dev, pm_message_t);
+ int (*power_on) (struct iio_dev *indio_dev);
+ int (*power_off) (struct i2c_client *dev);
+ struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE];
+ struct tsl2x7x_settings *platform_default_settings;
+};
+
+#endif /* __TSL2X7X_H */
diff --git a/drivers/staging/iio/light/tsl2x7x_core.c b/drivers/staging/iio/light/tsl2x7x_core.c
new file mode 100644
index 0000000..267faab
--- /dev/null
+++ b/drivers/staging/iio/light/tsl2x7x_core.c
@@ -0,0 +1,1830 @@
+/*
+ * Device driver for monitoring ambient light intensity in (lux)
+ * and proximity detection (prox) within the TAOS TSL2X7X family of devices.
+ *
+ * Copyright (c) 2012, TAOS Corporation.
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include "tsl2x7x.h"
+#include "../events.h"
+#include "../iio.h"
+#include "../sysfs.h"
+
+/* Cal defs*/
+#define PROX_STAT_CAL 0
+#define PROX_STAT_SAMP 1
+#define MAX_SAMPLES_CAL 200
+
+/* TSL2X7X Device ID */
+#define TRITON_ID 0x00
+#define SWORDFISH_ID 0x30
+#define HALIBUT_ID 0x20
+
+/* Lux calculation constants */
+#define TSL2X7X_LUX_CALC_OVER_FLOW 65535
+
+/* TAOS Register definitions - note:
+ * depending on device, some of these register are not used and the
+ * register address is benign.
+ */
+/* 2X7X register offsets */
+#define TSL2X7X_MAX_CONFIG_REG 16
+
+/* Device Registers and Masks */
+#define TSL2X7X_CNTRL 0x00
+#define TSL2X7X_ALS_TIME 0X01
+#define TSL2X7X_PRX_TIME 0x02
+#define TSL2X7X_WAIT_TIME 0x03
+#define TSL2X7X_ALS_MINTHRESHLO 0X04
+#define TSL2X7X_ALS_MINTHRESHHI 0X05
+#define TSL2X7X_ALS_MAXTHRESHLO 0X06
+#define TSL2X7X_ALS_MAXTHRESHHI 0X07
+#define TSL2X7X_PRX_MINTHRESHLO 0X08
+#define TSL2X7X_PRX_MINTHRESHHI 0X09
+#define TSL2X7X_PRX_MAXTHRESHLO 0X0A
+#define TSL2X7X_PRX_MAXTHRESHHI 0X0B
+#define TSL2X7X_PERSISTENCE 0x0C
+#define TSL2X7X_PRX_CONFIG 0x0D
+#define TSL2X7X_PRX_COUNT 0x0E
+#define TSL2X7X_GAIN 0x0F
+#define TSL2X7X_NOTUSED 0x10
+#define TSL2X7X_REVID 0x11
+#define TSL2X7X_CHIPID 0x12
+#define TSL2X7X_STATUS 0x13
+#define TSL2X7X_ALS_CHAN0LO 0x14
+#define TSL2X7X_ALS_CHAN0HI 0x15
+#define TSL2X7X_ALS_CHAN1LO 0x16
+#define TSL2X7X_ALS_CHAN1HI 0x17
+#define TSL2X7X_PRX_LO 0x18
+#define TSL2X7X_PRX_HI 0x19
+
+/* tsl2X7X cmd reg masks */
+#define TSL2X7X_CMD_REG 0x80
+#define TSL2X7X_CMD_SPL_FN 0x60
+
+#define TSL2X7X_CMD_PROX_INT_CLR 0X05
+#define TSL2X7X_CMD_ALS_INT_CLR 0x06
+#define TSL2X7X_CMD_PROXALS_INT_CLR 0X07
+
+/* tsl2X7X cntrl reg masks */
+#define TSL2X7X_CNTL_ADC_ENBL 0x02
+#define TSL2X7X_CNTL_PWR_ON 0x01
+
+/* tsl2X7X status reg masks */
+#define TSL2X7X_STA_ADC_VALID 0x01
+#define TSL2X7X_STA_PRX_VALID 0x02
+#define TSL2X7X_STA_ADC_PRX_VALID 0x03
+#define TSL2X7X_STA_ALS_INTR 0x10
+#define TSL2X7X_STA_ADC_INTR 0x10
+#define TSL2X7X_STA_PRX_INTR 0x20
+
+#define TSL2X7X_STA_ADC_INTR 0x10
+
+/* tsl2X7X cntrl reg masks */
+#define TSL2X7X_CNTL_REG_CLEAR 0x00
+#define TSL2X7X_CNTL_PROX_INT_ENBL 0X20
+#define TSL2X7X_CNTL_ALS_INT_ENBL 0X10
+#define TSL2X7X_CNTL_WAIT_TMR_ENBL 0X08
+#define TSL2X7X_CNTL_PROX_DET_ENBL 0X04
+#define TSL2X7X_CNTL_PWRON 0x01
+#define TSL2X7X_CNTL_ALSPON_ENBL 0x03
+#define TSL2X7X_CNTL_INTALSPON_ENBL 0x13
+#define TSL2X7X_CNTL_PROXPON_ENBL 0x0F
+#define TSL2X7X_CNTL_INTPROXPON_ENBL 0x2F
+
+/*Prox diode to use */
+#define TSL2X7X_DIODE0 0x10
+#define TSL2X7X_DIODE1 0x20
+#define TSL2X7X_DIODE_BOTH 0x30
+
+/* LED Power */
+#define TSL2X7X_mA100 0x00
+#define TSL2X7X_mA50 0x40
+#define TSL2X7X_mA25 0x80
+#define TSL2X7X_mA13 0xD0
+
+/*Common device IIO EventMask */
+#define TSL2X7X_EVENT_MASK \
+ (IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \
+ IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)),
+
+/* TAOS txx2x7x Device family members */
+enum {
+ tsl2571,
+ tsl2671,
+ tmd2671,
+ tsl2771,
+ tmd2771,
+ tsl2572,
+ tsl2672,
+ tmd2672,
+ tsl2772,
+ tmd2772
+};
+
+enum {
+ TSL2X7X_CHIP_UNKNOWN = 0,
+ TSL2X7X_CHIP_WORKING = 1,
+ TSL2X7X_CHIP_SUSPENDED = 2
+};
+
+/* Per-device data */
+struct tsl2x7x_als_info {
+ u16 als_ch0;
+ u16 als_ch1;
+ u16 lux;
+};
+
+struct prox_stat {
+ u16 min;
+ u16 max;
+ u16 mean;
+ unsigned long stddev;
+};
+
+struct tsl2x7x_chip_info {
+ int chan_table_elements;
+ struct iio_chan_spec channel[9];
+ const struct iio_info *info;
+};
+
+struct tsl2X7X_chip {
+ kernel_ulong_t id;
+ struct mutex prox_mutex;
+ struct mutex als_mutex;
+ struct i2c_client *client;
+ u16 prox_data;
+ struct tsl2x7x_als_info als_cur_info;
+ struct tsl2x7x_settings tsl2x7x_settings;
+ struct tsl2X7X_platform_data *pdata;
+ int als_time_scale;
+ int als_saturation;
+ int tsl2x7x_chip_status;
+ u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG];
+ const struct tsl2x7x_chip_info *chip_info;
+ const struct iio_info *info;
+ s64 event_timestamp;
+ /* This structure is intentionally large to accommodate
+ * updates via sysfs. */
+ /* Sized to 9 = max 8 segments + 1 termination segment */
+ struct tsl2x7x_lux tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE];
+};
+
+/* Different devices require different coefficents */
+static const struct tsl2x7x_lux tsl2x71_lux_table[] = {
+ { 14461, 611, 1211 },
+ { 18540, 352, 623 },
+ { 0, 0, 0 },
+};
+
+static const struct tsl2x7x_lux tmd2x71_lux_table[] = {
+ { 11635, 115, 256 },
+ { 15536, 87, 179 },
+ { 0, 0, 0 },
+};
+
+static const struct tsl2x7x_lux tsl2x72_lux_table[] = {
+ { 14013, 466, 917 },
+ { 18222, 310, 552 },
+ { 0, 0, 0 },
+};
+
+static const struct tsl2x7x_lux tmd2x72_lux_table[] = {
+ { 13218, 130, 262 },
+ { 17592, 92, 169 },
+ { 0, 0, 0 },
+};
+
+static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] = {
+ [tsl2571] = tsl2x71_lux_table,
+ [tsl2671] = tsl2x71_lux_table,
+ [tmd2671] = tmd2x71_lux_table,
+ [tsl2771] = tsl2x71_lux_table,
+ [tmd2771] = tmd2x71_lux_table,
+ [tsl2572] = tsl2x72_lux_table,
+ [tsl2672] = tsl2x72_lux_table,
+ [tmd2672] = tmd2x72_lux_table,
+ [tsl2772] = tsl2x72_lux_table,
+ [tmd2772] = tmd2x72_lux_table,
+};
+
+static const struct tsl2x7x_settings tsl2x7x_default_settings = {
+ .als_time = 200,
+ .als_gain = 0,
+ .prx_time = 0xfe, /*5.4 mS */
+ .prox_gain = 1,
+ .wait_time = 245,
+ .prox_config = 0,
+ .als_gain_trim = 1000,
+ .als_cal_target = 150,
+ .als_thresh_low = 200,
+ .als_thresh_high = 256,
+ .persistence = 0xFF,
+ .interrupts_en = 0x00,
+ .prox_thres_low = 0,
+ .prox_thres_high = 512,
+ .prox_max_samples_cal = 30,
+ .prox_pulse_count = 8
+};
+
+static const s16 tsl2X7X_als_gainadj[] = {
+ 1,
+ 8,
+ 16,
+ 120
+};
+
+static const s16 tsl2X7X_prx_gainadj[] = {
+ 1,
+ 2,
+ 4,
+ 8
+};
+
+/* Channel variations */
+enum {
+ ALS,
+ PRX,
+ ALSPRX,
+ PRX2,
+ ALSPRX2,
+};
+
+const u8 device_channel_config[] = {
+ ALS,
+ PRX,
+ PRX,
+ ALSPRX,
+ ALSPRX,
+ ALS,
+ PRX2,
+ PRX2,
+ ALSPRX2,
+ ALSPRX2
+};
+
+/*
+ * Read a number of bytes starting at register (reg) location.
+ * Return 0, or i2c_smbus_write_byte ERROR code.
+ */
+static int
+tsl2x7x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ int ret;
+
+ /* select register to write */
+ ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg));
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: failed to write register %x\n"
+ , __func__, reg);
+ return ret;
+ }
+ /* read the data */
+ *val = i2c_smbus_read_byte(client);
+
+ return 0;
+}
+
+/**
+ * tsl2x7x_get_lux() - Reads and calculates current lux value.
+ * @indio_dev: IIO device
+ *
+ * The raw ch0 and ch1 values of the ambient light sensed in the last
+ * integration cycle are read from the device.
+ * Time scale factor array values are adjusted based on the integration time.
+ * The raw values are multiplied by a scale factor, and device gain is obtained
+ * using gain index. Limit checks are done next, then the ratio of a multiple
+ * of ch1 value, to the ch0 value, is calculated. Array tsl2x7x_device_lux[]
+ * is then scanned to find the first ratio value that is just above the ratio
+ * we just calculated. The ch0 and ch1 multiplier constants in the array are
+ * then used along with the time scale factor array values, to calculate the
+ * lux.
+ */
+static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
+{
+ u16 ch0, ch1; /* separated ch0/ch1 data from device */
+ u32 lux; /* raw lux calculated from device data */
+ u64 lux64;
+ u32 ratio;
+ u8 buf[4];
+ struct tsl2x7x_lux *p;
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ int i, ret;
+ u32 ch0lux = 0;
+ u32 ch1lux = 0;
+
+ if (mutex_trylock(&chip->als_mutex) == 0) {
+ dev_info(&chip->client->dev, "tsl2x7x_get_lux device is busy\n");
+ return chip->als_cur_info.lux; /* busy, so return LAST VALUE */
+ }
+
+ if (chip->tsl2x7x_chip_status != TSL2X7X_CHIP_WORKING) {
+ /* device is not enabled */
+ dev_err(&chip->client->dev, "%s: device is not enabled\n",
+ __func__);
+ ret = -EBUSY ;
+ goto out_unlock;
+ }
+
+ ret = tsl2x7x_i2c_read(chip->client,
+ (TSL2X7X_CMD_REG | TSL2X7X_STATUS), &buf[0]);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: failed to read CMD_REG\n", __func__);
+ goto out_unlock;
+ }
+ /* is data new & valid */
+ if (!(buf[0] & TSL2X7X_STA_ADC_VALID)) {
+ dev_err(&chip->client->dev,
+ "%s: data not valid yet\n", __func__);
+ ret = chip->als_cur_info.lux; /* return LAST VALUE */
+ goto out_unlock;
+ }
+
+ for (i = 0; i < 4; i++) {
+ ret = tsl2x7x_i2c_read(chip->client,
+ (TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i)),
+ &buf[i]);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: failed to read. err=%x\n", __func__, ret);
+ goto out_unlock;
+ }
+ }
+
+ /* clear status, really interrupt status ( are off),
+ but we use the bit anyway */
+ ret = i2c_smbus_write_byte(chip->client,
+ (TSL2X7X_CMD_REG |
+ TSL2X7X_CMD_SPL_FN |
+ TSL2X7X_CMD_ALS_INT_CLR));
+
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "i2c_write_command failed in %s, err = %d\n",
+ __func__, ret);
+ goto out_unlock; /* have no data, so return failure */
+ }
+
+ /* extract ALS/lux data */
+ ch0 = le16_to_cpup((const __le16 *)&buf[0]);
+ ch1 = le16_to_cpup((const __le16 *)&buf[2]);
+
+ chip->als_cur_info.als_ch0 = ch0;
+ chip->als_cur_info.als_ch1 = ch1;
+
+ if ((ch0 >= chip->als_saturation) || (ch1 >= chip->als_saturation)) {
+ lux = TSL2X7X_LUX_CALC_OVER_FLOW;
+ goto return_max;
+ }
+
+ if (ch0 == 0) {
+ /* have no data, so return LAST VALUE */
+ ret = chip->als_cur_info.lux = 0;
+ goto out_unlock;
+ }
+ /* calculate ratio */
+ ratio = (ch1 << 15) / ch0;
+ /* convert to unscaled lux using the pointer to the table */
+ p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux;
+ while (p->ratio != 0 && p->ratio < ratio)
+ p++;
+
+ if (p->ratio == 0) {
+ lux = 0;
+ } else {
+ ch0lux = DIV_ROUND_UP((ch0 * p->ch0),
+ tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
+ ch1lux = DIV_ROUND_UP((ch1 * p->ch1),
+ tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
+ lux = ch0lux - ch1lux;
+ }
+
+ /* note: lux is 31 bit max at this point */
+ if (ch1lux > ch0lux) {
+ dev_dbg(&chip->client->dev, "Returning last value\n");
+ ret = chip->als_cur_info.lux;
+ goto out_unlock;
+ }
+
+ /* adjust for active time scale */
+ if (chip->als_time_scale == 0)
+ lux = 0;
+ else
+ lux = (lux + (chip->als_time_scale >> 1)) /
+ chip->als_time_scale;
+
+ /* adjust for active gain scale
+ * The tsl2x7x_device_lux tables have a factor of 256 built-in.
+ * User-specified gain provides a multiplier.
+ * Apply user-specified gain before shifting right to retain precision.
+ * Use 64 bits to avoid overflow on multiplication.
+ * Then go back to 32 bits before division to avoid using div_u64().
+ */
+ lux64 = lux;
+ lux64 = lux64 * chip->tsl2x7x_settings.als_gain_trim;
+ lux64 >>= 8;
+ lux = lux64;
+ lux = (lux + 500) / 1000;
+
+ if (lux > TSL2X7X_LUX_CALC_OVER_FLOW) /* check for overflow */
+ lux = TSL2X7X_LUX_CALC_OVER_FLOW;
+
+ /* Update the structure with the latest lux. */
+return_max:
+ chip->als_cur_info.lux = lux;
+ ret = lux;
+
+out_unlock:
+ mutex_unlock(&chip->als_mutex);
+
+ return ret;
+}
+
+/* Proximity poll function */
+static int tsl2x7x_prox_poll(struct iio_dev *indio_dev)
+{
+ int i;
+ int ret;
+ u8 status;
+ u8 chdata[2];
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ if (mutex_trylock(&chip->prox_mutex) == 0) {
+ dev_err(&chip->client->dev,
+ "%s: Can't get prox mutex\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = tsl2x7x_i2c_read(chip->client,
+ (TSL2X7X_CMD_REG | TSL2X7X_STATUS), &status);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: i2c err=%d\n", __func__, ret);
+ goto prox_poll_err;
+ }
+
+ if (chip->id < tsl2572) {
+ if (!(status & TSL2X7X_STA_ADC_VALID))
+ goto prox_poll_err;
+ } else if (!(status & TSL2X7X_STA_PRX_VALID))
+ goto prox_poll_err;
+
+ for (i = 0; i < 2; i++) {
+ ret = tsl2x7x_i2c_read(chip->client,
+ (TSL2X7X_CMD_REG |
+ (TSL2X7X_PRX_LO + i)), &chdata[i]);
+ if (ret < 0)
+ goto prox_poll_err;
+ }
+
+ chip->prox_data =
+ le16_to_cpup((const __le16 *)&chdata[0]);
+
+prox_poll_err:
+
+ mutex_unlock(&chip->prox_mutex);
+ return chip->prox_data;
+}
+
+/*
+ * Provides initial operational parameter defaults.
+ * These defaults may be changed through the device's sysfs files.
+ */
+static void tsl2x7x_defaults(struct tsl2X7X_chip *chip)
+{
+ /* If Operational settings defined elsewhere.. */
+ if (chip->pdata && chip->pdata->platform_default_settings != 0)
+ memcpy(&(chip->tsl2x7x_settings),
+ chip->pdata->platform_default_settings,
+ sizeof(tsl2x7x_default_settings));
+ else
+ memcpy(&(chip->tsl2x7x_settings),
+ &tsl2x7x_default_settings,
+ sizeof(tsl2x7x_default_settings));
+
+ /* Load up the proper lux table. */
+ if (chip->pdata && chip->pdata->platform_lux_table[0].ratio != 0)
+ memcpy(chip->tsl2x7x_device_lux,
+ chip->pdata->platform_lux_table,
+ sizeof(chip->pdata->platform_lux_table));
+ else
+ memcpy(chip->tsl2x7x_device_lux,
+ (struct tsl2x7x_lux *)tsl2x7x_default_lux_table_group[chip->id],
+ MAX_DEFAULT_TABLE_BYTES);
+
+}
+
+/*
+ * Obtain single reading and calculate the als_gain_trim
+ * (later used to derive actual lux).
+ * Return updated gain_trim value.
+ */
+static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev)
+{
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ u8 reg_val;
+ int gain_trim_val;
+ int ret;
+ int lux_val;
+
+ ret = i2c_smbus_write_byte(chip->client,
+ (TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: failed to write CNTRL register, ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ reg_val = i2c_smbus_read_byte(chip->client);
+ if ((reg_val & (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON))
+ != (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) {
+ dev_err(&chip->client->dev,
+ "%s: failed: ADC not enabled\n", __func__);
+ return -1;
+ }
+
+ ret = i2c_smbus_write_byte(chip->client,
+ (TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: failed to write ctrl reg: ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ reg_val = i2c_smbus_read_byte(chip->client);
+ if ((reg_val & TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) {
+ dev_err(&chip->client->dev,
+ "%s: failed: STATUS - ADC not valid.\n", __func__);
+ return -ENODATA;
+ }
+
+ lux_val = tsl2x7x_get_lux(indio_dev);
+ if (lux_val < 0) {
+ dev_err(&chip->client->dev,
+ "%s: failed to get lux\n", __func__);
+ return lux_val;
+ }
+
+ gain_trim_val = (((chip->tsl2x7x_settings.als_cal_target)
+ * chip->tsl2x7x_settings.als_gain_trim) / lux_val);
+ if ((gain_trim_val < 250) || (gain_trim_val > 4000))
+ return -ERANGE;
+
+ chip->tsl2x7x_settings.als_gain_trim = gain_trim_val;
+ dev_info(&chip->client->dev,
+ "%s als_calibrate completed\n", chip->client->name);
+
+ return (int) gain_trim_val;
+}
+
+/*
+ * Turn the device on.
+ * Configuration must be set before calling this function.
+ */
+static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
+{
+ int i;
+ int ret = 0;
+ u8 *dev_reg;
+ u8 utmp;
+ int als_count;
+ int als_time;
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ u8 reg_val = 0;
+
+ if (chip->pdata && chip->pdata->power_on)
+ chip->pdata->power_on(indio_dev);
+
+ /* Non calculated parameters */
+ chip->tsl2x7x_config[TSL2X7X_PRX_TIME] =
+ chip->tsl2x7x_settings.prx_time;
+ chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] =
+ chip->tsl2x7x_settings.wait_time;
+ chip->tsl2x7x_config[TSL2X7X_PRX_CONFIG] =
+ chip->tsl2x7x_settings.prox_config;
+
+ chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHLO] =
+ (chip->tsl2x7x_settings.als_thresh_low) & 0xFF;
+ chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHHI] =
+ (chip->tsl2x7x_settings.als_thresh_low >> 8) & 0xFF;
+ chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHLO] =
+ (chip->tsl2x7x_settings.als_thresh_high) & 0xFF;
+ chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHHI] =
+ (chip->tsl2x7x_settings.als_thresh_high >> 8) & 0xFF;
+ chip->tsl2x7x_config[TSL2X7X_PERSISTENCE] =
+ chip->tsl2x7x_settings.persistence;
+
+ chip->tsl2x7x_config[TSL2X7X_PRX_COUNT] =
+ chip->tsl2x7x_settings.prox_pulse_count;
+ chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHLO] =
+ chip->tsl2x7x_settings.prox_thres_low;
+ chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHLO] =
+ chip->tsl2x7x_settings.prox_thres_high;
+
+ /* and make sure we're not already on */
+ if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
+ /* if forcing a register update - turn off, then on */
+ dev_info(&chip->client->dev, "device is already enabled\n");
+ return -EINVAL;
+ }
+
+ /* determine als integration regster */
+ als_count = (chip->tsl2x7x_settings.als_time * 100 + 135) / 270;
+ if (als_count == 0)
+ als_count = 1; /* ensure at least one cycle */
+
+ /* convert back to time (encompasses overrides) */
+ als_time = (als_count * 27 + 5) / 10;
+ chip->tsl2x7x_config[TSL2X7X_ALS_TIME] = 256 - als_count;
+
+ /* Set the gain based on tsl2x7x_settings struct */
+ chip->tsl2x7x_config[TSL2X7X_GAIN] =
+ (chip->tsl2x7x_settings.als_gain |
+ (TSL2X7X_mA100 | TSL2X7X_DIODE1)
+ | ((chip->tsl2x7x_settings.prox_gain) << 2));
+
+ /* set chip struct re scaling and saturation */
+ chip->als_saturation = als_count * 922; /* 90% of full scale */
+ chip->als_time_scale = (als_time + 25) / 50;
+
+ /* TSL2X7X Specific power-on / adc enable sequence
+ * Power on the device 1st. */
+ utmp = TSL2X7X_CNTL_PWR_ON;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: failed on CNTRL reg.\n", __func__);
+ return -1;
+ }
+
+ /* Use the following shadow copy for our delay before enabling ADC.
+ * Write all the registers. */
+ for (i = 0, dev_reg = chip->tsl2x7x_config;
+ i < TSL2X7X_MAX_CONFIG_REG; i++) {
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2X7X_CMD_REG + i, *dev_reg++);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: failed on write to reg %d.\n", __func__, i);
+ return ret;
+ }
+ }
+
+ udelay(3000); /* Power-on settling time */
+
+ /* NOW enable the ADC
+ * initialize the desired mode of operation */
+ utmp = TSL2X7X_CNTL_PWR_ON |
+ TSL2X7X_CNTL_ADC_ENBL |
+ TSL2X7X_CNTL_PROX_DET_ENBL;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: failed on 2nd CTRL reg.\n", __func__);
+ return ret;
+ }
+
+ chip->tsl2x7x_chip_status = TSL2X7X_CHIP_WORKING;
+
+ if (chip->tsl2x7x_settings.interrupts_en != 0) {
+ dev_info(&chip->client->dev, "Setting Up Interrupt(s)\n");
+
+ reg_val = TSL2X7X_CNTL_PWR_ON | TSL2X7X_CNTL_ADC_ENBL;
+ if ((chip->tsl2x7x_settings.interrupts_en == 0x20) ||
+ (chip->tsl2x7x_settings.interrupts_en == 0x30))
+ reg_val |= TSL2X7X_CNTL_PROX_DET_ENBL;
+
+ reg_val |= chip->tsl2x7x_settings.interrupts_en;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ (TSL2X7X_CMD_REG | TSL2X7X_CNTRL), reg_val);
+ if (ret < 0)
+ dev_err(&chip->client->dev,
+ "%s: failed in tsl2x7x_IOCTL_INT_SET.\n",
+ __func__);
+
+ /* Clear out any initial interrupts */
+ ret = i2c_smbus_write_byte(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
+ TSL2X7X_CMD_PROXALS_INT_CLR);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: failed in tsl2x7x_chip_on\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int tsl2x7x_chip_off(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ /* turn device off */
+ chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
+
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_CNTRL, 0x00);
+
+ if (chip->pdata && chip->pdata->power_off)
+ chip->pdata->power_off(chip->client);
+
+ return ret;
+}
+
+/*
+ * Proximity calibration helper function
+ * runs through a collection of data samples,
+ * sets the min, max, mean, and std dev.
+ */
+static
+void tsl2x7x_prox_calculate(u16 *data, int length, struct prox_stat *statP)
+{
+ int i;
+ int sample_min, sample_max, sample_sum, sample_mean;
+ unsigned long stddev;
+ int tmp;
+
+ if (length == 0)
+ length = 1;
+
+ sample_sum = 0;
+ sample_min = INT_MAX;
+ sample_max = INT_MIN;
+ for (i = 0; i < length; i++) {
+ sample_sum += data[i];
+ if (data[i] < sample_min)
+ sample_min = data[i];
+ if (data[i] > sample_max)
+ sample_max = data[i];
+ }
+ sample_mean = sample_sum/length;
+ statP->min = sample_min;
+ statP->max = sample_max;
+ statP->mean = sample_mean;
+
+ sample_sum = 0;
+ for (i = 0; i < length; i++) {
+ tmp = data[i]-sample_mean;
+ sample_sum += tmp * tmp;
+ }
+ stddev = int_sqrt((long)sample_sum)/length;
+ statP->stddev = stddev;
+}
+
+/**
+ * Proximity calibration - collects a number of samples,
+ * calculates a standard deviation based on the samples, and
+ * sets the threshold accordingly.
+ */
+static void tsl2x7x_prox_cal(struct iio_dev *indio_dev)
+{
+ u16 prox_history[MAX_SAMPLES_CAL + 1];
+ int i;
+ struct prox_stat prox_stat_data[2];
+ struct prox_stat *calP;
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ u8 tmp_irq_settings;
+ u8 current_state = chip->tsl2x7x_chip_status;
+
+ if (chip->tsl2x7x_settings.prox_max_samples_cal > MAX_SAMPLES_CAL) {
+ dev_err(&chip->client->dev,
+ "%s: max prox samples cal is too big: %d\n",
+ __func__, chip->tsl2x7x_settings.prox_max_samples_cal);
+ chip->tsl2x7x_settings.prox_max_samples_cal = MAX_SAMPLES_CAL;
+ }
+
+ /* have to stop to change settings */
+ tsl2x7x_chip_off(indio_dev);
+
+ /* Enable proximity detection save just in case prox not wanted yet*/
+ tmp_irq_settings = chip->tsl2x7x_settings.interrupts_en;
+ chip->tsl2x7x_settings.interrupts_en |= TSL2X7X_CNTL_PROX_INT_ENBL;
+
+ /*turn on device if not already on*/
+ tsl2x7x_chip_on(indio_dev);
+
+ /*gather the samples*/
+ for (i = 0; i < chip->tsl2x7x_settings.prox_max_samples_cal; i++) {
+ mdelay(15);
+ tsl2x7x_prox_poll(indio_dev);
+ prox_history[i] = chip->prox_data;
+ dev_info(&chip->client->dev, "2 i=%d prox data= %d\n",
+ i, chip->prox_data);
+ }
+
+ tsl2x7x_chip_off(indio_dev);
+ calP = &prox_stat_data[PROX_STAT_CAL];
+ tsl2x7x_prox_calculate(prox_history,
+ chip->tsl2x7x_settings.prox_max_samples_cal, calP);
+ chip->tsl2x7x_settings.prox_thres_high = (calP->max << 1) - calP->mean;
+
+ dev_info(&chip->client->dev, " cal min=%d mean=%d max=%d\n",
+ calP->min, calP->mean, calP->max);
+ dev_info(&chip->client->dev,
+ "%s proximity threshold set to %d\n",
+ chip->client->name, chip->tsl2x7x_settings.prox_thres_high);
+
+ /* back to the way they were */
+ chip->tsl2x7x_settings.interrupts_en = tmp_irq_settings;
+ if (current_state == TSL2X7X_CHIP_WORKING)
+ tsl2x7x_chip_on(indio_dev);
+}
+
+static ssize_t tsl2x7x_power_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", chip->tsl2x7x_chip_status);
+}
+
+static ssize_t tsl2x7x_power_state_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ bool value;
+
+ if (strtobool(buf, &value))
+ return -EINVAL;
+
+ if (!value)
+ tsl2x7x_chip_off(indio_dev);
+ else
+ tsl2x7x_chip_on(indio_dev);
+
+ return len;
+}
+
+static ssize_t tsl2x7x_gain_available_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ if (chip->id > tsl2771)
+ return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 128");
+ else
+ return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 120");
+}
+
+static ssize_t tsl2x7x_prox_gain_available_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", "1 2 4 8");
+}
+
+static ssize_t tsl2x7x_als_time_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ chip->tsl2x7x_settings.als_time);
+}
+
+static ssize_t tsl2x7x_als_time_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ unsigned long value;
+
+ if (kstrtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if ((value < 50) || (value > 650))
+ return -EINVAL;
+
+ if (value % 50)
+ return -EINVAL;
+
+ chip->tsl2x7x_settings.als_time = value;
+
+ return len;
+}
+
+static IIO_CONST_ATTR(illuminance0_integration_time_available,
+ "50 100 150 200 250 300 350 400 450 500 550 600 650");
+
+static ssize_t tsl2x7x_als_cal_target_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ chip->tsl2x7x_settings.als_cal_target);
+}
+
+static ssize_t tsl2x7x_als_cal_target_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ unsigned long value;
+
+ if (kstrtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value)
+ chip->tsl2x7x_settings.als_cal_target = value;
+
+ return len;
+}
+
+/* sampling_frequency AKA persistence in data sheet */
+static ssize_t tsl2x7x_persistence_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ chip->tsl2x7x_settings.persistence);
+}
+
+static ssize_t tsl2x7x_persistence_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ unsigned long value;
+
+ if (kstrtoul(buf, 0, &value))
+ return -EINVAL;
+
+ chip->tsl2x7x_settings.persistence = value;
+
+ return len;
+}
+
+static IIO_CONST_ATTR(sampling_frequency_available,
+ "0x00 - 0xFF (0 - 255)");
+
+static ssize_t tsl2x7x_do_calibrate(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ bool value;
+
+ if (strtobool(buf, &value))
+ return -EINVAL;
+
+ if (value)
+ tsl2x7x_als_calibrate(indio_dev);
+
+ return len;
+}
+
+static ssize_t tsl2x7x_luxtable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ int i = 0;
+ int offset = 0;
+
+ while (i < (TSL2X7X_MAX_LUX_TABLE_SIZE * 3)) {
+ offset += snprintf(buf + offset, PAGE_SIZE, "%d,%d,%d,",
+ chip->tsl2x7x_device_lux[i].ratio,
+ chip->tsl2x7x_device_lux[i].ch0,
+ chip->tsl2x7x_device_lux[i].ch1);
+ if (chip->tsl2x7x_device_lux[i].ratio == 0) {
+ /* We just printed the first "0" entry.
+ * Now get rid of the extra "," and break. */
+ offset--;
+ break;
+ }
+ i++;
+ }
+
+ offset += snprintf(buf + offset, PAGE_SIZE, "\n");
+ return offset;
+}
+
+static ssize_t tsl2x7x_luxtable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ int value[ARRAY_SIZE(chip->tsl2x7x_device_lux)*3 + 1];
+ int n;
+
+ get_options(buf, ARRAY_SIZE(value), value);
+
+ /* We now have an array of ints starting at value[1], and
+ * enumerated by value[0].
+ * We expect each group of three ints is one table entry,
+ * and the last table entry is all 0.
+ */
+ n = value[0];
+ if ((n % 3) || n < 6 ||
+ n > ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) {
+ dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n);
+ return -EINVAL;
+ }
+
+ if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) {
+ dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n);
+ return -EINVAL;
+ }
+
+ if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING)
+ tsl2x7x_chip_off(indio_dev);
+
+ /* Zero out the table */
+ memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux));
+ memcpy(chip->tsl2x7x_device_lux, &value[1], (value[0] * 4));
+
+ return len;
+}
+
+static ssize_t tsl2x7x_do_prox_calibrate(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ bool value;
+
+ if (strtobool(buf, &value))
+ return -EINVAL;
+
+ if (value)
+ tsl2x7x_prox_cal(indio_dev);
+
+ return len;
+}
+
+static int tsl2x7x_read_interrupt_config(struct iio_dev *indio_dev,
+ u64 event_code)
+{
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_INTENSITY)
+ ret = !!(chip->tsl2x7x_settings.interrupts_en & 0x10);
+ else
+ ret = !!(chip->tsl2x7x_settings.interrupts_en & 0x20);
+
+ return ret;
+}
+
+static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev,
+ u64 event_code,
+ int val)
+{
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_INTENSITY) {
+ if (val)
+ chip->tsl2x7x_settings.interrupts_en |= 0x10;
+ else
+ chip->tsl2x7x_settings.interrupts_en &= 0x20;
+ } else {
+ if (val)
+ chip->tsl2x7x_settings.interrupts_en |= 0x20;
+ else
+ chip->tsl2x7x_settings.interrupts_en &= 0x10;
+ }
+
+ return 0;
+}
+
+static int tsl2x7x_write_thresh(struct iio_dev *indio_dev,
+ u64 event_code,
+ int val)
+{
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_INTENSITY) {
+ switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+ case IIO_EV_DIR_RISING:
+ chip->tsl2x7x_settings.als_thresh_high = val;
+ break;
+ case IIO_EV_DIR_FALLING:
+ chip->tsl2x7x_settings.als_thresh_low = val;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+ case IIO_EV_DIR_RISING:
+ chip->tsl2x7x_settings.prox_thres_high = val;
+ break;
+ case IIO_EV_DIR_FALLING:
+ chip->tsl2x7x_settings.prox_thres_low = val;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int tsl2x7x_read_thresh(struct iio_dev *indio_dev,
+ u64 event_code,
+ int *val)
+{
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_INTENSITY) {
+ switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+ case IIO_EV_DIR_RISING:
+ *val = chip->tsl2x7x_settings.als_thresh_high;
+ break;
+ case IIO_EV_DIR_FALLING:
+ *val = chip->tsl2x7x_settings.als_thresh_low;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+ case IIO_EV_DIR_RISING:
+ *val = chip->tsl2x7x_settings.prox_thres_high;
+ break;
+ case IIO_EV_DIR_FALLING:
+ *val = chip->tsl2x7x_settings.prox_thres_low;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int tsl2x7x_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ int ret = -EINVAL;
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ switch (mask) {
+ case 0:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ tsl2x7x_get_lux(indio_dev);
+ *val = chip->als_cur_info.lux;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_INTENSITY:
+ tsl2x7x_get_lux(indio_dev);
+ if (chan->channel == 0)
+ *val = chip->als_cur_info.als_ch0;
+ else
+ *val = chip->als_cur_info.als_ch1;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_PROXIMITY:
+ tsl2x7x_prox_poll(indio_dev);
+ *val = chip->prox_data;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (chan->type == IIO_LIGHT)
+ *val =
+ tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain];
+ else
+ *val =
+ tsl2X7X_prx_gainadj[chip->tsl2x7x_settings.prox_gain];
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ *val = chip->tsl2x7x_settings.als_gain_trim;
+ ret = IIO_VAL_INT;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int tsl2x7x_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (chan->type == IIO_INTENSITY) {
+ switch (val) {
+ case 1:
+ chip->tsl2x7x_settings.als_gain = 0;
+ break;
+ case 8:
+ chip->tsl2x7x_settings.als_gain = 1;
+ break;
+ case 16:
+ chip->tsl2x7x_settings.als_gain = 2;
+ break;
+ case 120:
+ if (chip->id > tsl2771)
+ return -EINVAL;
+ chip->tsl2x7x_settings.als_gain = 3;
+ break;
+ case 128:
+ if (chip->id < tsl2572)
+ return -EINVAL;
+ chip->tsl2x7x_settings.als_gain = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (val) {
+ case 1:
+ chip->tsl2x7x_settings.prox_gain = 0;
+ break;
+ case 2:
+ chip->tsl2x7x_settings.prox_gain = 1;
+ break;
+ case 4:
+ chip->tsl2x7x_settings.prox_gain = 2;
+ break;
+ case 8:
+ chip->tsl2x7x_settings.prox_gain = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ chip->tsl2x7x_settings.als_gain_trim = val;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
+ tsl2x7x_power_state_show, tsl2x7x_power_state_store);
+
+static DEVICE_ATTR(proximity_calibscale_available, S_IRUGO,
+ tsl2x7x_prox_gain_available_show, NULL);
+
+static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO,
+ tsl2x7x_gain_available_show, NULL);
+
+static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR,
+ tsl2x7x_als_time_show, tsl2x7x_als_time_store);
+
+static DEVICE_ATTR(illuminance0_target_input, S_IRUGO | S_IWUSR,
+ tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store);
+
+static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL,
+ tsl2x7x_do_calibrate);
+
+static DEVICE_ATTR(proximity_calibrate, S_IWUSR, NULL,
+ tsl2x7x_do_prox_calibrate);
+
+static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR,
+ tsl2x7x_luxtable_show, tsl2x7x_luxtable_store);
+
+static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
+ tsl2x7x_persistence_show, tsl2x7x_persistence_store);
+
+/* Use the default register values to identify the Taos device */
+static int tsl2x7x_device_id(unsigned char *id, int target)
+{
+ switch (target) {
+ case tsl2571:
+ case tsl2671:
+ case tsl2771:
+ return ((*id & 0xf0) == TRITON_ID);
+ break;
+ case tmd2671:
+ case tmd2771:
+ return ((*id & 0xf0) == HALIBUT_ID);
+ break;
+ case tsl2572:
+ case tsl2672:
+ case tmd2672:
+ case tsl2772:
+ case tmd2772:
+ return ((*id & 0xf0) == SWORDFISH_ID);
+ break;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Interrupt Event Handler */
+static irqreturn_t tsl2x7x_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ s64 timestamp = iio_get_time_ns();
+ int ret;
+ int value;
+
+ value = i2c_smbus_read_byte_data(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_STATUS);
+
+ /* What type of interrupt do we need to process */
+ if (value & TSL2X7X_STA_PRX_INTR) {
+ tsl2x7x_prox_poll(indio_dev);
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ timestamp);
+ }
+
+ if (value & TSL2X7X_STA_ALS_INTR) {
+ tsl2x7x_get_lux(indio_dev);
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ timestamp);
+ }
+ /* Clear interrupt now that we have handled it. */
+ ret = i2c_smbus_write_byte(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
+ TSL2X7X_CMD_PROXALS_INT_CLR);
+ if (ret < 0)
+ dev_err(&chip->client->dev,
+ "%s: Failed to clear irq from event handler. err = %d\n",
+ __func__, ret);
+
+ return IRQ_HANDLED;
+}
+
+static struct attribute *tsl2x7x_ALS_device_attrs[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_illuminance0_calibscale_available.attr,
+ &dev_attr_illuminance0_integration_time.attr,
+ &iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
+ &dev_attr_illuminance0_target_input.attr,
+ &dev_attr_illuminance0_calibrate.attr,
+ &dev_attr_illuminance0_lux_table.attr,
+ &dev_attr_sampling_frequency.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *tsl2x7x_PRX_device_attrs[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_sampling_frequency.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &dev_attr_proximity_calibrate.attr,
+ NULL
+};
+
+static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_illuminance0_calibscale_available.attr,
+ &dev_attr_illuminance0_integration_time.attr,
+ &iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
+ &dev_attr_illuminance0_target_input.attr,
+ &dev_attr_illuminance0_calibrate.attr,
+ &dev_attr_illuminance0_lux_table.attr,
+ &dev_attr_sampling_frequency.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &dev_attr_proximity_calibrate.attr,
+ NULL
+};
+
+static struct attribute *tsl2x7x_PRX2_device_attrs[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_sampling_frequency.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &dev_attr_proximity_calibrate.attr,
+ &dev_attr_proximity_calibscale_available.attr,
+ NULL
+};
+
+static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_illuminance0_calibscale_available.attr,
+ &dev_attr_illuminance0_integration_time.attr,
+ &iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
+ &dev_attr_illuminance0_target_input.attr,
+ &dev_attr_illuminance0_calibrate.attr,
+ &dev_attr_illuminance0_lux_table.attr,
+ &dev_attr_sampling_frequency.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &dev_attr_proximity_calibrate.attr,
+ &dev_attr_proximity_calibscale_available.attr,
+ NULL
+};
+
+static const struct attribute_group tsl2X7X_device_attr_group_tbl[] = {
+ [ALS] = {
+ .attrs = tsl2x7x_ALS_device_attrs,
+ },
+ [PRX] = {
+ .attrs = tsl2x7x_PRX_device_attrs,
+ },
+ [ALSPRX] = {
+ .attrs = tsl2x7x_ALSPRX_device_attrs,
+ },
+ [PRX2] = {
+ .attrs = tsl2x7x_PRX2_device_attrs,
+ },
+ [ALSPRX2] = {
+ .attrs = tsl2x7x_ALSPRX2_device_attrs,
+ },
+};
+
+static const struct iio_info tsl2X7X_device_info[] = {
+ [ALS] = {
+ .attrs = &tsl2X7X_device_attr_group_tbl[ALS],
+ .driver_module = THIS_MODULE,
+ .read_raw = &tsl2x7x_read_raw,
+ .write_raw = &tsl2x7x_write_raw,
+ .read_event_value = &tsl2x7x_read_thresh,
+ .write_event_value = &tsl2x7x_write_thresh,
+ .read_event_config = &tsl2x7x_read_interrupt_config,
+ .write_event_config = &tsl2x7x_write_interrupt_config,
+ },
+ [PRX] = {
+ .attrs = &tsl2X7X_device_attr_group_tbl[PRX],
+ .driver_module = THIS_MODULE,
+ .read_raw = &tsl2x7x_read_raw,
+ .write_raw = &tsl2x7x_write_raw,
+ .read_event_value = &tsl2x7x_read_thresh,
+ .write_event_value = &tsl2x7x_write_thresh,
+ .read_event_config = &tsl2x7x_read_interrupt_config,
+ .write_event_config = &tsl2x7x_write_interrupt_config,
+ },
+ [ALSPRX] = {
+ .attrs = &tsl2X7X_device_attr_group_tbl[ALSPRX],
+ .driver_module = THIS_MODULE,
+ .read_raw = &tsl2x7x_read_raw,
+ .write_raw = &tsl2x7x_write_raw,
+ .read_event_value = &tsl2x7x_read_thresh,
+ .write_event_value = &tsl2x7x_write_thresh,
+ .read_event_config = &tsl2x7x_read_interrupt_config,
+ .write_event_config = &tsl2x7x_write_interrupt_config,
+ },
+ [PRX2] = {
+ .attrs = &tsl2X7X_device_attr_group_tbl[PRX2],
+ .driver_module = THIS_MODULE,
+ .read_raw = &tsl2x7x_read_raw,
+ .write_raw = &tsl2x7x_write_raw,
+ .read_event_value = &tsl2x7x_read_thresh,
+ .write_event_value = &tsl2x7x_write_thresh,
+ .read_event_config = &tsl2x7x_read_interrupt_config,
+ .write_event_config = &tsl2x7x_write_interrupt_config,
+ },
+ [ALSPRX2] = {
+ .attrs = &tsl2X7X_device_attr_group_tbl[ALSPRX2],
+ .driver_module = THIS_MODULE,
+ .read_raw = &tsl2x7x_read_raw,
+ .write_raw = &tsl2x7x_write_raw,
+ .read_event_value = &tsl2x7x_read_thresh,
+ .write_event_value = &tsl2x7x_write_thresh,
+ .read_event_config = &tsl2x7x_read_interrupt_config,
+ .write_event_config = &tsl2x7x_write_interrupt_config,
+ },
+};
+
+static const struct tsl2x7x_chip_info tsl2x7x_chip_info_tbl[] = {
+ [ALS] = {
+ .channel = {
+ {
+ .type = IIO_LIGHT,
+ .indexed = 1,
+ .channel = 0,
+ .processed_val = 1,
+ }, {
+ .type = IIO_INTENSITY,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
+ IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
+ .event_mask = TSL2X7X_EVENT_MASK
+ }, {
+ .type = IIO_INTENSITY,
+ .indexed = 1,
+ .channel = 1,
+ },
+ },
+ .chan_table_elements = 3,
+ .info = &tsl2X7X_device_info[ALS],
+ },
+ [PRX] = {
+ .channel = {
+ {
+ .type = IIO_PROXIMITY,
+ .indexed = 1,
+ .channel = 0,
+ .event_mask = TSL2X7X_EVENT_MASK
+ },
+ },
+ .chan_table_elements = 1,
+ .info = &tsl2X7X_device_info[PRX],
+ },
+ [ALSPRX] = {
+ .channel = {
+ {
+ .type = IIO_LIGHT,
+ .indexed = 1,
+ .channel = 0,
+ .processed_val = 1,
+ }, {
+ .type = IIO_INTENSITY,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
+ IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
+ .event_mask = TSL2X7X_EVENT_MASK
+ }, {
+ .type = IIO_INTENSITY,
+ .indexed = 1,
+ .channel = 1,
+ }, {
+ .type = IIO_PROXIMITY,
+ .indexed = 1,
+ .channel = 0,
+ .event_mask = TSL2X7X_EVENT_MASK
+ },
+ },
+ .chan_table_elements = 4,
+ .info = &tsl2X7X_device_info[ALSPRX],
+ },
+ [PRX2] = {
+ .channel = {
+ {
+ .type = IIO_PROXIMITY,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask =
+ IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
+ .event_mask = TSL2X7X_EVENT_MASK
+ },
+ },
+ .chan_table_elements = 1,
+ .info = &tsl2X7X_device_info[PRX2],
+ },
+ [ALSPRX2] = {
+ .channel = {
+ {
+ .type = IIO_LIGHT,
+ .indexed = 1,
+ .channel = 0,
+ .processed_val = 1,
+ }, {
+ .type = IIO_INTENSITY,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
+ IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
+ .event_mask = TSL2X7X_EVENT_MASK
+ }, {
+ .type = IIO_INTENSITY,
+ .indexed = 1,
+ .channel = 1,
+ }, {
+ .type = IIO_PROXIMITY,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask =
+ IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
+ .event_mask = TSL2X7X_EVENT_MASK
+ },
+ },
+ .chan_table_elements = 4,
+ .info = &tsl2X7X_device_info[ALSPRX2],
+ },
+};
+
+/*
+ * Client probe function.
+ */
+static int __devinit tsl2x7x_probe(struct i2c_client *clientp,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ unsigned char device_id;
+ struct iio_dev *indio_dev;
+ struct tsl2X7X_chip *chip;
+
+ indio_dev = iio_allocate_device(sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+ chip->client = clientp;
+ i2c_set_clientdata(clientp, indio_dev);
+
+ ret = tsl2x7x_i2c_read(chip->client,
+ TSL2X7X_CHIPID, &device_id);
+ if (ret < 0)
+ goto fail1;
+
+ if ((!tsl2x7x_device_id(&device_id, id->driver_data)) ||
+ (tsl2x7x_device_id(&device_id, id->driver_data) == -EINVAL)) {
+ dev_info(&chip->client->dev,
+ "i2c device found does not match expected id in %s\n",
+ __func__);
+ goto fail1;
+ }
+
+ ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+ if (ret < 0) {
+ dev_err(&clientp->dev, "%s: write to cmd reg failed. err = %d\n",
+ __func__, ret);
+ goto fail1;
+ }
+
+ /* ALS and PROX functions can be invoked via user space poll
+ * or H/W interrupt. If busy return last sample. */
+ mutex_init(&chip->als_mutex);
+ mutex_init(&chip->prox_mutex);
+
+ chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN;
+ chip->pdata = clientp->dev.platform_data;
+ chip->id = id->driver_data;
+ chip->chip_info =
+ &tsl2x7x_chip_info_tbl[device_channel_config[id->driver_data]];
+
+ indio_dev->info = chip->chip_info->info;
+ indio_dev->dev.parent = &clientp->dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = chip->client->name;
+ indio_dev->channels = chip->chip_info->channel;
+ indio_dev->num_channels = chip->chip_info->chan_table_elements;
+
+ if (clientp->irq) {
+ ret = request_threaded_irq(clientp->irq,
+ NULL,
+ &tsl2x7x_event_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "TSL2X7X_event",
+ indio_dev);
+ if (ret) {
+ dev_err(&clientp->dev,
+ "%s: irq request failed", __func__);
+ goto fail2;
+ }
+ }
+
+ /* Load up the defaults */
+ tsl2x7x_defaults(chip);
+ /* Make sure the chip is on */
+ tsl2x7x_chip_on(indio_dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&clientp->dev,
+ "%s: iio registration failed\n", __func__);
+ goto fail1;
+ }
+
+ dev_info(&clientp->dev, "%s Light sensor found.\n", id->name);
+ return 0;
+
+fail1:
+ if (clientp->irq)
+ free_irq(clientp->irq, indio_dev);
+fail2:
+ iio_free_device(indio_dev);
+ return ret;
+}
+
+static int tsl2x7x_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
+ ret = tsl2x7x_chip_off(indio_dev);
+ chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
+ }
+
+ if (chip->pdata && chip->pdata->platform_power) {
+ pm_message_t pmm = {PM_EVENT_SUSPEND};
+ chip->pdata->platform_power(dev, pmm);
+ }
+
+ return ret;
+}
+
+static int tsl2x7x_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (chip->pdata && chip->pdata->platform_power) {
+ pm_message_t pmm = {PM_EVENT_RESUME};
+ chip->pdata->platform_power(dev, pmm);
+ }
+
+ if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_SUSPENDED)
+ ret = tsl2x7x_chip_on(indio_dev);
+
+ return ret;
+}
+
+static int __devexit tsl2x7x_remove(struct i2c_client *client)
+{
+ struct tsl2X7X_chip *chip = i2c_get_clientdata(client);
+ struct iio_dev *indio_dev = iio_priv_to_dev(chip);
+
+ tsl2x7x_chip_off(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (client->irq)
+ free_irq(client->irq, chip->client->name);
+
+ iio_free_device(indio_dev);
+
+ return 0;
+}
+
+static struct i2c_device_id tsl2x7x_idtable[] = {
+ { "tsl2571", tsl2571 },
+ { "tsl2671", tsl2671 },
+ { "tmd2671", tmd2671 },
+ { "tsl2771", tsl2771 },
+ { "tmd2771", tmd2771 },
+ { "tsl2572", tsl2572 },
+ { "tsl2672", tsl2672 },
+ { "tmd2672", tmd2672 },
+ { "tsl2772", tsl2772 },
+ { "tmd2772", tmd2772 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable);
+
+static const struct dev_pm_ops tsl2x7x_pm_ops = {
+ .suspend = tsl2x7x_suspend,
+ .resume = tsl2x7x_resume,
+};
+
+/* Driver definition */
+static struct i2c_driver tsl2x7x_driver = {
+ .driver = {
+ .name = "tsl2x7x",
+ .pm = &tsl2x7x_pm_ops,
+ },
+ .id_table = tsl2x7x_idtable,
+ .probe = tsl2x7x_probe,
+ .remove = __devexit_p(tsl2x7x_remove),
+};
+
+module_i2c_driver(tsl2x7x_driver);
+
+MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>");
+MODULE_DESCRIPTION("TAOS tsl2x7x ambient and proximity light sensor driver");
+MODULE_LICENSE("GPL");
--
1.7.4.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH V5] TAOS tsl2x7x 2012-04-02 16:50 [PATCH V5] TAOS tsl2x7x Jon Brenner @ 2012-04-04 8:35 ` Jonathan Cameron 2012-04-04 15:30 ` Jon Brenner 2012-04-04 16:16 ` Peter Meerwald 2012-04-10 17:36 ` Greg KH 2 siblings, 1 reply; 9+ messages in thread From: Jonathan Cameron @ 2012-04-04 8:35 UTC (permalink / raw) To: Jon Brenner; +Cc: linux-iio, Linux Kernel On 4/2/2012 5:50 PM, Jon Brenner wrote: > TAOS device driver (version 5) for the tsl/tmd 2771 and 2772 device families (inc. all variants). Hi Jon, Changes since last version? A few bits still to sort out in here I'm afraid... (getting there though!) My reviews tend to get more picky as the big stuff gets sorted out. On trivial extra blank line to clear out. Extra line for your next driver has snuck into the make file. Units don't look right for sampling frequency. Sorry, but that's an abi issue so even if it is fiddly to do the conversion to Hz it needs to be done. Would normally expect changes to events to get applied immediately. Here I think that only happens if you turn the device off and on again? For future reference (don't bother here!) make any documentation moves not directly dependent on the driver (such as those that will become used by multiple drivers) in a precursor patch. > > Signed-off-by: Jon Brenner<jbrenner@taosinc.com> > --- > .../light/sysfs-bus-iio-light-tsl2583 | 6 + > .../light/sysfs-bus-iio-light-tsl2x7x | 14 + > drivers/staging/iio/Documentation/sysfs-bus-iio | 7 + > .../staging/iio/Documentation/sysfs-bus-iio-light | 8 +- > .../iio/Documentation/sysfs-bus-iio-light-tsl2583 | 20 - > drivers/staging/iio/light/Kconfig | 8 + > drivers/staging/iio/light/Makefile | 2 + > drivers/staging/iio/light/tsl2x7x.h | 99 ++ > drivers/staging/iio/light/tsl2x7x_core.c | 1830 ++++++++++++++++++++ > 9 files changed, 1970 insertions(+), 24 deletions(-) > > diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 > new file mode 100644 > index 0000000..8f2a038 > --- /dev/null > +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 > @@ -0,0 +1,6 @@ > +What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate > +KernelVersion: 2.6.37 > +Contact: linux-iio@vger.kernel.org > +Description: > + This property causes an internal calibration of the als gain trim > + value which is later used in calculating illuminance in lux. > diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x > new file mode 100644 > index 0000000..275ae54 > --- /dev/null > +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x > @@ -0,0 +1,14 @@ > +What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate > +KernelVersion: 2.6.37 > +Contact: linux-iio@vger.kernel.org > +Description: > + This property causes an internal calibration of the als gain trim > + value which is later used in calculating illuminance in lux. Hmm.. could possibly move this into sysfs-bus-iio-light at some point given we clearly have two drivers using it. (fine for now though) > + > +What: /sys/bus/iio/devices/device[n]/proximity_calibrate > +KernelVersion: 3.3-rc1 > +Contact: linux-iio@vger.kernel.org > +Description: > + Causes an recalculation and adjustment to the > + proximity_thresh_rising_value. This one is interesting as there are other proximity sensors out there (not light based) so we may want to move this at some later point to a sysfs-bus-iio-proximity documentation file. > + > diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio b/drivers/staging/iio/Documentation/sysfs-bus-iio > index 46a995d..5b2b5d3 100644 > --- a/drivers/staging/iio/Documentation/sysfs-bus-iio > +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio > @@ -258,6 +258,8 @@ What /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale > What /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibscale > What /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale > What /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale > +what /sys/bus/iio/devices/iio:deviceX/illuminance0_calibscale > +what /sys/bus/iio/devices/iio:deviceX/proximity_calibscale > KernelVersion: 2.6.35 > Contact: linux-iio@vger.kernel.org > Description: > @@ -457,6 +459,10 @@ What: /sys/.../events/in_voltageY_raw_thresh_falling_value > What: /sys/.../events/in_voltageY_raw_thresh_falling_value > What: /sys/.../events/in_tempY_raw_thresh_falling_value > What: /sys/.../events/in_tempY_raw_thresh_falling_value Oops, clearly and error in the lines above (repeats of falling and no rising). I'll fix that up unless someone else gets there first. > +What: /sys/.../events/illuminance0_thresh_falling_value > +what: /sys/.../events/illuminance0_thresh_rising_value > +what: /sys/.../events/proximity_thresh_falling_value > +what: /sys/.../events/proximity_thresh_rising_value > KernelVersion: 2.6.37 > Contact: linux-iio@vger.kernel.org > Description: > @@ -739,3 +745,4 @@ Description: > system. To minimize the current consumption of the system, > the bridge can be disconnected (when it is not being used > using the bridge_switch_en attribute. > + loose this extra blank line. > diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light b/drivers/staging/iio/Documentation/sysfs-bus-iio-light > index edbf470..4385c70 100644 > --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light > +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light > @@ -76,10 +76,10 @@ Contact: linux-iio@vger.kernel.org > Description: > This property gets/sets the sensors ADC analog integration time. > > -What: /sys/bus/iio/devices/device[n]/illuminance0_calibscale > +What: /sys/bus/iio/devices/device[n]/lux_table > KernelVersion: 2.6.37 > Contact: linux-iio@vger.kernel.org > Description: > - Hardware or software applied calibration scale factor assumed > - to account for attenuation due to industrial design (glass > - filters or aperture holes). > + This property gets/sets the table of coefficients > + used in calculating illuminance in lux. > + > diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 > deleted file mode 100644 > index 660781d..0000000 > --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 > +++ /dev/null > @@ -1,20 +0,0 @@ > -What: /sys/bus/iio/devices/device[n]/lux_table > -KernelVersion: 2.6.37 > -Contact: linux-iio@vger.kernel.org > -Description: > - This property gets/sets the table of coefficients > - used in calculating illuminance in lux. > - > -What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate > -KernelVersion: 2.6.37 > -Contact: linux-iio@vger.kernel.org > -Description: > - This property causes an internal calibration of the als gain trim > - value which is later used in calculating illuminance in lux. > - > -What: /sys/bus/iio/devices/device[n]/illuminance0_input_target > -KernelVersion: 2.6.37 > -Contact: linux-iio@vger.kernel.org > -Description: > - This property is the known externally illuminance (in lux). > - It is used in the process of calibrating the device accuracy. > diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig > index e7e9159..976f790 100644 > --- a/drivers/staging/iio/light/Kconfig > +++ b/drivers/staging/iio/light/Kconfig > @@ -31,4 +31,12 @@ config TSL2583 > Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices. > Access ALS data via iio, sysfs. > > +config TSL2x7x > + tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors" > + depends on I2C > + help > + Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672, > + tmd2672, tsl2772, tmd2772 devices. > + Provides iio_events and direct access via sysfs. > + > endmenu > diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile > index 3011fbf..0b8fb22 100644 > --- a/drivers/staging/iio/light/Makefile > +++ b/drivers/staging/iio/light/Makefile > @@ -5,3 +5,5 @@ > obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o > obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o > obj-$(CONFIG_TSL2583) += tsl2583.o > +obj-$(CONFIG_TSL2x7x) += tsl2x7x_core.o > +obj-$(CONFIG_TCS3x7x) += tcs3x7x_core.o Really? > diff --git a/drivers/staging/iio/light/tsl2x7x.h b/drivers/staging/iio/light/tsl2x7x.h > new file mode 100644 > index 0000000..fe9e853 > --- /dev/null > +++ b/drivers/staging/iio/light/tsl2x7x.h > @@ -0,0 +1,99 @@ > +/* > + * Device driver for monitoring ambient light intensity (lux) > + * and proximity (prox) within the TAOS TSL2X7X family of devices. > + * > + * Copyright (c) 2012, TAOS Corporation. > + * > + * 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 > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc., > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. > + */ > + > +#ifndef __TSL2X7X_H > +#define __TSL2X7X_H > +#include<linux/pm.h> > + > +/* Max number of segments allowable in LUX table */ > +#define TSL2X7X_MAX_LUX_TABLE_SIZE 9 > +#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) * TSL2X7X_MAX_LUX_TABLE_SIZE) > + > +struct iio_dev; > + > +struct tsl2x7x_lux { > + unsigned int ratio; > + unsigned int ch0; > + unsigned int ch1; > +}; > + > +/** > + * struct tsl2x7x_default_settings - power on defaults unless > + * overridden by platform data. > + * @als_time: ALS Integration time - multiple of 50mS > + * @als_gain: Index into the ALS gain table. > + * @prx_time: 5.2ms prox integration time - > + * dec in 2.7ms periods > + * @wait_time: Time between PRX and ALS cycles > + * in 2.7 periods > + * @prox_config: Prox configuration filters. > + * @als_gain_trim: default gain trim to account for > + * aperture effects. > + * @als_cal_target: Known external ALS reading for > + * calibration. > + * @als_thresh_low: CH0 'low' count to trigger interrupt. > + * @als_thresh_high: CH0 'high' count to trigger interrupt. > + * @persistence: H/W Filters, Number of 'out of limits' > + * ADC readings PRX/ALS. > + * @interrupts_en: Enable/Disable - 0x00 = none, 0x10 = als, > + * 0x20 = prx, 0x30 = bth > + * @prox_thres_low: Low threshold proximity detection. > + * @prox_thres_high: High threshold proximity detection > + * @prox_max_samples_cal: Used for prox cal. > + * @prox_pulse_count: Number if proximity emitter pulses reorder the docs to match the structure. Pick which ever order makes most sense (don't worry about wasting a byte or two, clarity is more important on structures like this!) > + */ > +struct tsl2x7x_settings { > + int als_time; > + int als_gain; > + int als_gain_trim; > + int wait_time; > + int prx_time; > + int prox_gain; > + int prox_config; > + int als_cal_target; > + u8 interrupts_en; > + u8 persistence; > + int als_thresh_low; > + int als_thresh_high; > + int prox_thres_low; > + int prox_thres_high; > + int prox_pulse_count; > + int prox_max_samples_cal; > +}; > + > +/** > + * struct tsl2X7X_platform_data - Platform callback, glass and defaults > + * @platform_power: Suspend/resume platform callback > + * @power_on: Power on callback > + * @power_off: Power off callback > + * @platform_lux_table: Device specific glass coefficents > + * @platform_default_settings: Device specific power on defaults > + * Platform PM functions. > + */ > +struct tsl2X7X_platform_data { > + int (*platform_power)(struct device *dev, pm_message_t); > + int (*power_on) (struct iio_dev *indio_dev); > + int (*power_off) (struct i2c_client *dev); > + struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE]; > + struct tsl2x7x_settings *platform_default_settings; > +}; > + > +#endif /* __TSL2X7X_H */ > diff --git a/drivers/staging/iio/light/tsl2x7x_core.c b/drivers/staging/iio/light/tsl2x7x_core.c > new file mode 100644 > index 0000000..267faab > --- /dev/null > +++ b/drivers/staging/iio/light/tsl2x7x_core.c > @@ -0,0 +1,1830 @@ > +/* > + * Device driver for monitoring ambient light intensity in (lux) > + * and proximity detection (prox) within the TAOS TSL2X7X family of devices. > + * > + * Copyright (c) 2012, TAOS Corporation. > + * > + * 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 > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc., > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. > + */ > + > +#include<linux/kernel.h> > +#include<linux/i2c.h> > +#include<linux/errno.h> > +#include<linux/delay.h> > +#include<linux/mutex.h> > +#include<linux/interrupt.h> > +#include<linux/slab.h> > +#include<linux/module.h> > +#include<linux/version.h> > +#include "tsl2x7x.h" > +#include "../events.h" > +#include "../iio.h" > +#include "../sysfs.h" > + > +/* Cal defs*/ > +#define PROX_STAT_CAL 0 > +#define PROX_STAT_SAMP 1 > +#define MAX_SAMPLES_CAL 200 > + > +/* TSL2X7X Device ID */ > +#define TRITON_ID 0x00 > +#define SWORDFISH_ID 0x30 > +#define HALIBUT_ID 0x20 hmmm.. fish ;) > + > +/* Lux calculation constants */ > +#define TSL2X7X_LUX_CALC_OVER_FLOW 65535 > + > +/* TAOS Register definitions - note: > + * depending on device, some of these register are not used and the > + * register address is benign. > + */ > +/* 2X7X register offsets */ > +#define TSL2X7X_MAX_CONFIG_REG 16 > + > +/* Device Registers and Masks */ > +#define TSL2X7X_CNTRL 0x00 > +#define TSL2X7X_ALS_TIME 0X01 > +#define TSL2X7X_PRX_TIME 0x02 > +#define TSL2X7X_WAIT_TIME 0x03 > +#define TSL2X7X_ALS_MINTHRESHLO 0X04 > +#define TSL2X7X_ALS_MINTHRESHHI 0X05 > +#define TSL2X7X_ALS_MAXTHRESHLO 0X06 > +#define TSL2X7X_ALS_MAXTHRESHHI 0X07 > +#define TSL2X7X_PRX_MINTHRESHLO 0X08 > +#define TSL2X7X_PRX_MINTHRESHHI 0X09 > +#define TSL2X7X_PRX_MAXTHRESHLO 0X0A > +#define TSL2X7X_PRX_MAXTHRESHHI 0X0B > +#define TSL2X7X_PERSISTENCE 0x0C > +#define TSL2X7X_PRX_CONFIG 0x0D > +#define TSL2X7X_PRX_COUNT 0x0E > +#define TSL2X7X_GAIN 0x0F > +#define TSL2X7X_NOTUSED 0x10 > +#define TSL2X7X_REVID 0x11 > +#define TSL2X7X_CHIPID 0x12 > +#define TSL2X7X_STATUS 0x13 > +#define TSL2X7X_ALS_CHAN0LO 0x14 > +#define TSL2X7X_ALS_CHAN0HI 0x15 > +#define TSL2X7X_ALS_CHAN1LO 0x16 > +#define TSL2X7X_ALS_CHAN1HI 0x17 > +#define TSL2X7X_PRX_LO 0x18 > +#define TSL2X7X_PRX_HI 0x19 > + > +/* tsl2X7X cmd reg masks */ > +#define TSL2X7X_CMD_REG 0x80 > +#define TSL2X7X_CMD_SPL_FN 0x60 > + > +#define TSL2X7X_CMD_PROX_INT_CLR 0X05 > +#define TSL2X7X_CMD_ALS_INT_CLR 0x06 > +#define TSL2X7X_CMD_PROXALS_INT_CLR 0X07 > + > +/* tsl2X7X cntrl reg masks */ > +#define TSL2X7X_CNTL_ADC_ENBL 0x02 > +#define TSL2X7X_CNTL_PWR_ON 0x01 > + > +/* tsl2X7X status reg masks */ > +#define TSL2X7X_STA_ADC_VALID 0x01 > +#define TSL2X7X_STA_PRX_VALID 0x02 > +#define TSL2X7X_STA_ADC_PRX_VALID 0x03 Would prefer above defined as TSL2X7X_STA_ADC_VALID | TSL2X7X_STA_PRX_VALID (makes it obvious at a glance what is going on). > +#define TSL2X7X_STA_ALS_INTR 0x10 > +#define TSL2X7X_STA_ADC_INTR 0x10 above unused (and seems to be repeated) ? (repeated value was suspicious ) > +#define TSL2X7X_STA_PRX_INTR 0x20 > + > +#define TSL2X7X_STA_ADC_INTR 0x10 > + > +/* tsl2X7X cntrl reg masks */ > +#define TSL2X7X_CNTL_REG_CLEAR 0x00 > +#define TSL2X7X_CNTL_PROX_INT_ENBL 0X20 > +#define TSL2X7X_CNTL_ALS_INT_ENBL 0X10 > +#define TSL2X7X_CNTL_WAIT_TMR_ENBL 0X08 > +#define TSL2X7X_CNTL_PROX_DET_ENBL 0X04 > +#define TSL2X7X_CNTL_PWRON 0x01 > +#define TSL2X7X_CNTL_ALSPON_ENBL 0x03 > +#define TSL2X7X_CNTL_INTALSPON_ENBL 0x13 > +#define TSL2X7X_CNTL_PROXPON_ENBL 0x0F > +#define TSL2X7X_CNTL_INTPROXPON_ENBL 0x2F > + > +/*Prox diode to use */ > +#define TSL2X7X_DIODE0 0x10 > +#define TSL2X7X_DIODE1 0x20 > +#define TSL2X7X_DIODE_BOTH 0x30 > + > +/* LED Power */ > +#define TSL2X7X_mA100 0x00 > +#define TSL2X7X_mA50 0x40 > +#define TSL2X7X_mA25 0x80 > +#define TSL2X7X_mA13 0xD0 > + > +/*Common device IIO EventMask */ > +#define TSL2X7X_EVENT_MASK \ > + (IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \ > + IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)), > + > +/* TAOS txx2x7x Device family members */ > +enum { > + tsl2571, > + tsl2671, > + tmd2671, > + tsl2771, > + tmd2771, > + tsl2572, > + tsl2672, > + tmd2672, > + tsl2772, > + tmd2772 > +}; > + > +enum { > + TSL2X7X_CHIP_UNKNOWN = 0, > + TSL2X7X_CHIP_WORKING = 1, > + TSL2X7X_CHIP_SUSPENDED = 2 > +}; > + > +/* Per-device data */ > +struct tsl2x7x_als_info { > + u16 als_ch0; > + u16 als_ch1; > + u16 lux; > +}; > + > +struct prox_stat { > + u16 min; > + u16 max; > + u16 mean; > + unsigned long stddev; > +}; > + > +struct tsl2x7x_chip_info { > + int chan_table_elements; > + struct iio_chan_spec channel[9]; > + const struct iio_info *info; > +}; > + > +struct tsl2X7X_chip { > + kernel_ulong_t id; > + struct mutex prox_mutex; > + struct mutex als_mutex; > + struct i2c_client *client; > + u16 prox_data; > + struct tsl2x7x_als_info als_cur_info; > + struct tsl2x7x_settings tsl2x7x_settings; > + struct tsl2X7X_platform_data *pdata; > + int als_time_scale; > + int als_saturation; > + int tsl2x7x_chip_status; > + u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG]; > + const struct tsl2x7x_chip_info *chip_info; > + const struct iio_info *info; > + s64 event_timestamp; > + /* This structure is intentionally large to accommodate > + * updates via sysfs. */ > + /* Sized to 9 = max 8 segments + 1 termination segment */ > + struct tsl2x7x_lux tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE]; > +}; > + > +/* Different devices require different coefficents */ > +static const struct tsl2x7x_lux tsl2x71_lux_table[] = { > + { 14461, 611, 1211 }, > + { 18540, 352, 623 }, > + { 0, 0, 0 }, > +}; > + > +static const struct tsl2x7x_lux tmd2x71_lux_table[] = { > + { 11635, 115, 256 }, > + { 15536, 87, 179 }, > + { 0, 0, 0 }, > +}; > + > +static const struct tsl2x7x_lux tsl2x72_lux_table[] = { > + { 14013, 466, 917 }, > + { 18222, 310, 552 }, > + { 0, 0, 0 }, > +}; > + > +static const struct tsl2x7x_lux tmd2x72_lux_table[] = { > + { 13218, 130, 262 }, > + { 17592, 92, 169 }, > + { 0, 0, 0 }, > +}; > + > +static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] = { > + [tsl2571] = tsl2x71_lux_table, > + [tsl2671] = tsl2x71_lux_table, > + [tmd2671] = tmd2x71_lux_table, > + [tsl2771] = tsl2x71_lux_table, > + [tmd2771] = tmd2x71_lux_table, > + [tsl2572] = tsl2x72_lux_table, > + [tsl2672] = tsl2x72_lux_table, > + [tmd2672] = tmd2x72_lux_table, > + [tsl2772] = tsl2x72_lux_table, > + [tmd2772] = tmd2x72_lux_table, > +}; > + > +static const struct tsl2x7x_settings tsl2x7x_default_settings = { > + .als_time = 200, > + .als_gain = 0, > + .prx_time = 0xfe, /*5.4 mS */ > + .prox_gain = 1, > + .wait_time = 245, > + .prox_config = 0, > + .als_gain_trim = 1000, > + .als_cal_target = 150, > + .als_thresh_low = 200, > + .als_thresh_high = 256, > + .persistence = 0xFF, > + .interrupts_en = 0x00, > + .prox_thres_low = 0, > + .prox_thres_high = 512, > + .prox_max_samples_cal = 30, > + .prox_pulse_count = 8 > +}; > + > +static const s16 tsl2X7X_als_gainadj[] = { > + 1, > + 8, > + 16, > + 120 > +}; > + > +static const s16 tsl2X7X_prx_gainadj[] = { > + 1, > + 2, > + 4, > + 8 > +}; > + > +/* Channel variations */ > +enum { > + ALS, > + PRX, > + ALSPRX, > + PRX2, > + ALSPRX2, > +}; > + > +const u8 device_channel_config[] = { > + ALS, > + PRX, > + PRX, > + ALSPRX, > + ALSPRX, > + ALS, > + PRX2, > + PRX2, > + ALSPRX2, > + ALSPRX2 > +}; > + > +/* > + * Read a number of bytes starting at register (reg) location. > + * Return 0, or i2c_smbus_write_byte ERROR code. > + */ Preference for kernel doc on all function descriptions. (not crucial though). > +static int > +tsl2x7x_i2c_read(struct i2c_client *client, u8 reg, u8 *val) > +{ > + int ret; > + > + /* select register to write */ > + ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg)); > + if (ret< 0) { > + dev_err(&client->dev, "%s: failed to write register %x\n" > + , __func__, reg); > + return ret; > + } > + /* read the data */ > + *val = i2c_smbus_read_byte(client); No error handling on the i2c_smbus_read_byte. > + > + return 0; > +} > + > +/** > + * tsl2x7x_get_lux() - Reads and calculates current lux value. > + * @indio_dev: IIO device > + * > + * The raw ch0 and ch1 values of the ambient light sensed in the last > + * integration cycle are read from the device. > + * Time scale factor array values are adjusted based on the integration time. > + * The raw values are multiplied by a scale factor, and device gain is obtained > + * using gain index. Limit checks are done next, then the ratio of a multiple > + * of ch1 value, to the ch0 value, is calculated. Array tsl2x7x_device_lux[] > + * is then scanned to find the first ratio value that is just above the ratio > + * we just calculated. The ch0 and ch1 multiplier constants in the array are > + * then used along with the time scale factor array values, to calculate the > + * lux. > + */ > +static int tsl2x7x_get_lux(struct iio_dev *indio_dev) > +{ > + u16 ch0, ch1; /* separated ch0/ch1 data from device */ > + u32 lux; /* raw lux calculated from device data */ > + u64 lux64; > + u32 ratio; > + u8 buf[4]; > + struct tsl2x7x_lux *p; > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + int i, ret; > + u32 ch0lux = 0; > + u32 ch1lux = 0; > + > + if (mutex_trylock(&chip->als_mutex) == 0) { > + dev_info(&chip->client->dev, "tsl2x7x_get_lux device is busy\n"); Isn't this dev_info going to give you a lot of log entries? Does anyone care that the value is a little stale? > + return chip->als_cur_info.lux; /* busy, so return LAST VALUE */ > + } > + > + if (chip->tsl2x7x_chip_status != TSL2X7X_CHIP_WORKING) { > + /* device is not enabled */ > + dev_err(&chip->client->dev, "%s: device is not enabled\n", > + __func__); > + ret = -EBUSY ; > + goto out_unlock; > + } > + > + ret = tsl2x7x_i2c_read(chip->client, > + (TSL2X7X_CMD_REG | TSL2X7X_STATUS),&buf[0]); > + if (ret< 0) { > + dev_err(&chip->client->dev, > + "%s: failed to read CMD_REG\n", __func__); > + goto out_unlock; > + } > + /* is data new& valid */ > + if (!(buf[0]& TSL2X7X_STA_ADC_VALID)) { > + dev_err(&chip->client->dev, > + "%s: data not valid yet\n", __func__); > + ret = chip->als_cur_info.lux; /* return LAST VALUE */ > + goto out_unlock; > + } > + > + for (i = 0; i< 4; i++) { > + ret = tsl2x7x_i2c_read(chip->client, > + (TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i)), > + &buf[i]); > + if (ret< 0) { > + dev_err(&chip->client->dev, > + "%s: failed to read. err=%x\n", __func__, ret); > + goto out_unlock; > + } > + } > + > + /* clear status, really interrupt status ( are off), > + but we use the bit anyway */ Umm.. not sure I follow ( are off) bit.... > + ret = i2c_smbus_write_byte(chip->client, > + (TSL2X7X_CMD_REG | > + TSL2X7X_CMD_SPL_FN | > + TSL2X7X_CMD_ALS_INT_CLR)); > + Unwanted blank line here? > + if (ret< 0) { > + dev_err(&chip->client->dev, > + "i2c_write_command failed in %s, err = %d\n", > + __func__, ret); > + goto out_unlock; /* have no data, so return failure */ > + } > + > + /* extract ALS/lux data */ > + ch0 = le16_to_cpup((const __le16 *)&buf[0]); > + ch1 = le16_to_cpup((const __le16 *)&buf[2]); > + > + chip->als_cur_info.als_ch0 = ch0; > + chip->als_cur_info.als_ch1 = ch1; > + > + if ((ch0>= chip->als_saturation) || (ch1>= chip->als_saturation)) { > + lux = TSL2X7X_LUX_CALC_OVER_FLOW; > + goto return_max; > + } > + > + if (ch0 == 0) { > + /* have no data, so return LAST VALUE */ > + ret = chip->als_cur_info.lux = 0; That's not returning the last value as per comment???? > + goto out_unlock; > + } > + /* calculate ratio */ > + ratio = (ch1<< 15) / ch0; > + /* convert to unscaled lux using the pointer to the table */ > + p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux; > + while (p->ratio != 0&& p->ratio< ratio) > + p++; That's a rather large indent on the p++! > + > + if (p->ratio == 0) { > + lux = 0; > + } else { > + ch0lux = DIV_ROUND_UP((ch0 * p->ch0), > + tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]); > + ch1lux = DIV_ROUND_UP((ch1 * p->ch1), > + tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]); > + lux = ch0lux - ch1lux; > + } > + > + /* note: lux is 31 bit max at this point */ > + if (ch1lux> ch0lux) { Could say whn it's returning the last value? Would make debug comments moer helpful perhaps. > + dev_dbg(&chip->client->dev, "Returning last value\n"); > + ret = chip->als_cur_info.lux; > + goto out_unlock; > + } > + > + /* adjust for active time scale */ > + if (chip->als_time_scale == 0) > + lux = 0; > + else > + lux = (lux + (chip->als_time_scale>> 1)) / > + chip->als_time_scale; > + > + /* adjust for active gain scale > + * The tsl2x7x_device_lux tables have a factor of 256 built-in. > + * User-specified gain provides a multiplier. > + * Apply user-specified gain before shifting right to retain precision. > + * Use 64 bits to avoid overflow on multiplication. > + * Then go back to 32 bits before division to avoid using div_u64(). > + */ > + lux64 = lux; > + lux64 = lux64 * chip->tsl2x7x_settings.als_gain_trim; > + lux64>>= 8; > + lux = lux64; > + lux = (lux + 500) / 1000; > + > + if (lux> TSL2X7X_LUX_CALC_OVER_FLOW) /* check for overflow */ > + lux = TSL2X7X_LUX_CALC_OVER_FLOW; > + > + /* Update the structure with the latest lux. */ > +return_max: > + chip->als_cur_info.lux = lux; > + ret = lux; > + > +out_unlock: > + mutex_unlock(&chip->als_mutex); > + > + return ret; > +} > + > +/* Proximity poll function */ > +static int tsl2x7x_prox_poll(struct iio_dev *indio_dev) > +{ > + int i; > + int ret; > + u8 status; > + u8 chdata[2]; > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + > + if (mutex_trylock(&chip->prox_mutex) == 0) { > + dev_err(&chip->client->dev, > + "%s: Can't get prox mutex\n", __func__); > + return -EBUSY; > + } > + > + ret = tsl2x7x_i2c_read(chip->client, > + (TSL2X7X_CMD_REG | TSL2X7X_STATUS),&status); > + if (ret< 0) { > + dev_err(&chip->client->dev, > + "%s: i2c err=%d\n", __func__, ret); > + goto prox_poll_err; > + } > + I'd prefer a switch on the chip->id's to this less than comparison. Looks like something that might get accidentally broken in future. > + if (chip->id< tsl2572) { > + if (!(status& TSL2X7X_STA_ADC_VALID)) > + goto prox_poll_err; > + } else if (!(status& TSL2X7X_STA_PRX_VALID)) > + goto prox_poll_err; > + > + for (i = 0; i< 2; i++) { > + ret = tsl2x7x_i2c_read(chip->client, > + (TSL2X7X_CMD_REG | > + (TSL2X7X_PRX_LO + i)),&chdata[i]); > + if (ret< 0) > + goto prox_poll_err; > + } > + > + chip->prox_data = > + le16_to_cpup((const __le16 *)&chdata[0]); > + > +prox_poll_err: > + > + mutex_unlock(&chip->prox_mutex); > + return chip->prox_data; > +} > + > +/* > + * Provides initial operational parameter defaults. > + * These defaults may be changed through the device's sysfs files. > + */ > +static void tsl2x7x_defaults(struct tsl2X7X_chip *chip) > +{ > + /* If Operational settings defined elsewhere.. */ > + if (chip->pdata&& chip->pdata->platform_default_settings != 0) > + memcpy(&(chip->tsl2x7x_settings), > + chip->pdata->platform_default_settings, > + sizeof(tsl2x7x_default_settings)); > + else > + memcpy(&(chip->tsl2x7x_settings), > + &tsl2x7x_default_settings, > + sizeof(tsl2x7x_default_settings)); > + > + /* Load up the proper lux table. */ > + if (chip->pdata&& chip->pdata->platform_lux_table[0].ratio != 0) > + memcpy(chip->tsl2x7x_device_lux, > + chip->pdata->platform_lux_table, > + sizeof(chip->pdata->platform_lux_table)); > + else > + memcpy(chip->tsl2x7x_device_lux, > + (struct tsl2x7x_lux *)tsl2x7x_default_lux_table_group[chip->id], > + MAX_DEFAULT_TABLE_BYTES); Unwanted blank line. > + > +} > + > +/* > + * Obtain single reading and calculate the als_gain_trim > + * (later used to derive actual lux). > + * Return updated gain_trim value. kernel doc preferred. > + */ > +static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev) > +{ > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + u8 reg_val; > + int gain_trim_val; > + int ret; > + int lux_val; > + > + ret = i2c_smbus_write_byte(chip->client, > + (TSL2X7X_CMD_REG | TSL2X7X_CNTRL)); > + if (ret< 0) { > + dev_err(&chip->client->dev, > + "%s: failed to write CNTRL register, ret=%d\n", > + __func__, ret); > + return ret; > + } > + > + reg_val = i2c_smbus_read_byte(chip->client); > + if ((reg_val& (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) > + != (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) { > + dev_err(&chip->client->dev, > + "%s: failed: ADC not enabled\n", __func__); > + return -1; > + } > + > + ret = i2c_smbus_write_byte(chip->client, > + (TSL2X7X_CMD_REG | TSL2X7X_CNTRL)); > + if (ret< 0) { > + dev_err(&chip->client->dev, > + "%s: failed to write ctrl reg: ret=%d\n", > + __func__, ret); > + return ret; > + } > + > + reg_val = i2c_smbus_read_byte(chip->client); > + if ((reg_val& TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) { > + dev_err(&chip->client->dev, > + "%s: failed: STATUS - ADC not valid.\n", __func__); > + return -ENODATA; > + } > + > + lux_val = tsl2x7x_get_lux(indio_dev); > + if (lux_val< 0) { > + dev_err(&chip->client->dev, > + "%s: failed to get lux\n", __func__); > + return lux_val; > + } > + > + gain_trim_val = (((chip->tsl2x7x_settings.als_cal_target) > + * chip->tsl2x7x_settings.als_gain_trim) / lux_val); > + if ((gain_trim_val< 250) || (gain_trim_val> 4000)) > + return -ERANGE; > + > + chip->tsl2x7x_settings.als_gain_trim = gain_trim_val; > + dev_info(&chip->client->dev, > + "%s als_calibrate completed\n", chip->client->name); > + > + return (int) gain_trim_val; > +} > + > +/* > + * Turn the device on. > + * Configuration must be set before calling this function. > + */ > +static int tsl2x7x_chip_on(struct iio_dev *indio_dev) > +{ > + int i; > + int ret = 0; Can't immediately see a path where this isn't set anyway. > + u8 *dev_reg; > + u8 utmp; > + int als_count; > + int als_time; > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + u8 reg_val = 0; > + > + if (chip->pdata&& chip->pdata->power_on) > + chip->pdata->power_on(indio_dev); > + > + /* Non calculated parameters */ > + chip->tsl2x7x_config[TSL2X7X_PRX_TIME] = > + chip->tsl2x7x_settings.prx_time; > + chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] = > + chip->tsl2x7x_settings.wait_time; > + chip->tsl2x7x_config[TSL2X7X_PRX_CONFIG] = > + chip->tsl2x7x_settings.prox_config; > + > + chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHLO] = > + (chip->tsl2x7x_settings.als_thresh_low)& 0xFF; > + chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHHI] = > + (chip->tsl2x7x_settings.als_thresh_low>> 8)& 0xFF; > + chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHLO] = > + (chip->tsl2x7x_settings.als_thresh_high)& 0xFF; > + chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHHI] = > + (chip->tsl2x7x_settings.als_thresh_high>> 8)& 0xFF; > + chip->tsl2x7x_config[TSL2X7X_PERSISTENCE] = > + chip->tsl2x7x_settings.persistence; > + > + chip->tsl2x7x_config[TSL2X7X_PRX_COUNT] = > + chip->tsl2x7x_settings.prox_pulse_count; > + chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHLO] = > + chip->tsl2x7x_settings.prox_thres_low; > + chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHLO] = > + chip->tsl2x7x_settings.prox_thres_high; > + > + /* and make sure we're not already on */ > + if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) { > + /* if forcing a register update - turn off, then on */ > + dev_info(&chip->client->dev, "device is already enabled\n"); > + return -EINVAL; > + } > + > + /* determine als integration regster */ > + als_count = (chip->tsl2x7x_settings.als_time * 100 + 135) / 270; > + if (als_count == 0) > + als_count = 1; /* ensure at least one cycle */ > + > + /* convert back to time (encompasses overrides) */ > + als_time = (als_count * 27 + 5) / 10; > + chip->tsl2x7x_config[TSL2X7X_ALS_TIME] = 256 - als_count; > + > + /* Set the gain based on tsl2x7x_settings struct */ > + chip->tsl2x7x_config[TSL2X7X_GAIN] = > + (chip->tsl2x7x_settings.als_gain | > + (TSL2X7X_mA100 | TSL2X7X_DIODE1) > + | ((chip->tsl2x7x_settings.prox_gain)<< 2)); > + > + /* set chip struct re scaling and saturation */ > + chip->als_saturation = als_count * 922; /* 90% of full scale */ > + chip->als_time_scale = (als_time + 25) / 50; > + > + /* TSL2X7X Specific power-on / adc enable sequence > + * Power on the device 1st. */ > + utmp = TSL2X7X_CNTL_PWR_ON; > + ret = i2c_smbus_write_byte_data(chip->client, > + TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp); > + if (ret< 0) { > + dev_err(&chip->client->dev, > + "%s: failed on CNTRL reg.\n", __func__); > + return -1; > + } > + > + /* Use the following shadow copy for our delay before enabling ADC. > + * Write all the registers. */ > + for (i = 0, dev_reg = chip->tsl2x7x_config; > + i< TSL2X7X_MAX_CONFIG_REG; i++) { > + ret = i2c_smbus_write_byte_data(chip->client, > + TSL2X7X_CMD_REG + i, *dev_reg++); > + if (ret< 0) { > + dev_err(&chip->client->dev, > + "%s: failed on write to reg %d.\n", __func__, i); > + return ret; > + } > + } > + > + udelay(3000); /* Power-on settling time */ > + > + /* NOW enable the ADC > + * initialize the desired mode of operation */ > + utmp = TSL2X7X_CNTL_PWR_ON | > + TSL2X7X_CNTL_ADC_ENBL | > + TSL2X7X_CNTL_PROX_DET_ENBL; > + ret = i2c_smbus_write_byte_data(chip->client, > + TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp); > + if (ret< 0) { > + dev_err(&chip->client->dev, > + "%s: failed on 2nd CTRL reg.\n", __func__); > + return ret; > + } Indent on the bracket doesn't look right.. > + > + chip->tsl2x7x_chip_status = TSL2X7X_CHIP_WORKING; > + > + if (chip->tsl2x7x_settings.interrupts_en != 0) { > + dev_info(&chip->client->dev, "Setting Up Interrupt(s)\n"); > + > + reg_val = TSL2X7X_CNTL_PWR_ON | TSL2X7X_CNTL_ADC_ENBL; > + if ((chip->tsl2x7x_settings.interrupts_en == 0x20) || > + (chip->tsl2x7x_settings.interrupts_en == 0x30)) > + reg_val |= TSL2X7X_CNTL_PROX_DET_ENBL; > + > + reg_val |= chip->tsl2x7x_settings.interrupts_en; > + ret = i2c_smbus_write_byte_data(chip->client, > + (TSL2X7X_CMD_REG | TSL2X7X_CNTRL), reg_val); > + if (ret< 0) > + dev_err(&chip->client->dev, > + "%s: failed in tsl2x7x_IOCTL_INT_SET.\n", > + __func__); > + > + /* Clear out any initial interrupts */ > + ret = i2c_smbus_write_byte(chip->client, > + TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN | > + TSL2X7X_CMD_PROXALS_INT_CLR); > + if (ret< 0) { > + dev_err(&chip->client->dev, > + "%s: failed in tsl2x7x_chip_on\n", err. message will be tsl2x7x_chip_on: failed in tsl2x7x_chip_on Spot the redundant information! > + __func__); > + return ret; > + } > + } > + > + return ret; > +} > + > +static int tsl2x7x_chip_off(struct iio_dev *indio_dev) > +{ > + int ret; > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + > + /* turn device off */ > + chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED; > + > + ret = i2c_smbus_write_byte_data(chip->client, > + TSL2X7X_CMD_REG | TSL2X7X_CNTRL, 0x00); > + > + if (chip->pdata&& chip->pdata->power_off) > + chip->pdata->power_off(chip->client); > + > + return ret; > +} > + > +/* > + * Proximity calibration helper function > + * runs through a collection of data samples, > + * sets the min, max, mean, and std dev. > + */ > +static > +void tsl2x7x_prox_calculate(u16 *data, int length, struct prox_stat *statP) > +{ > + int i; > + int sample_min, sample_max, sample_sum, sample_mean; > + unsigned long stddev; > + int tmp; > + > + if (length == 0) > + length = 1; > + > + sample_sum = 0; > + sample_min = INT_MAX; > + sample_max = INT_MIN; > + for (i = 0; i< length; i++) { > + sample_sum += data[i]; > + if (data[i]< sample_min) > + sample_min = data[i]; kernel has min and max macros. Use them. e.g. sample_min = min(sample_min, data[i]); > + if (data[i]> sample_max) > + sample_max = data[i]; > + } > + sample_mean = sample_sum/length; > + statP->min = sample_min; > + statP->max = sample_max; > + statP->mean = sample_mean; Looks like a trivial gain in having local copies, why not just use the statP versions directly? > + > + sample_sum = 0; > + for (i = 0; i< length; i++) { > + tmp = data[i]-sample_mean; > + sample_sum += tmp * tmp; > + } > + stddev = int_sqrt((long)sample_sum)/length; > + statP->stddev = stddev; > +} > + > +/** > + * Proximity calibration - collects a number of samples, > + * calculates a standard deviation based on the samples, and > + * sets the threshold accordingly. kernel-doc plese. > + */ > +static void tsl2x7x_prox_cal(struct iio_dev *indio_dev) > +{ > + u16 prox_history[MAX_SAMPLES_CAL + 1]; > + int i; > + struct prox_stat prox_stat_data[2]; > + struct prox_stat *calP; > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + u8 tmp_irq_settings; > + u8 current_state = chip->tsl2x7x_chip_status; > + > + if (chip->tsl2x7x_settings.prox_max_samples_cal> MAX_SAMPLES_CAL) { > + dev_err(&chip->client->dev, > + "%s: max prox samples cal is too big: %d\n", > + __func__, chip->tsl2x7x_settings.prox_max_samples_cal); > + chip->tsl2x7x_settings.prox_max_samples_cal = MAX_SAMPLES_CAL; > + } > + > + /* have to stop to change settings */ > + tsl2x7x_chip_off(indio_dev); > + > + /* Enable proximity detection save just in case prox not wanted yet*/ > + tmp_irq_settings = chip->tsl2x7x_settings.interrupts_en; > + chip->tsl2x7x_settings.interrupts_en |= TSL2X7X_CNTL_PROX_INT_ENBL; > + > + /*turn on device if not already on*/ > + tsl2x7x_chip_on(indio_dev); > + > + /*gather the samples*/ > + for (i = 0; i< chip->tsl2x7x_settings.prox_max_samples_cal; i++) { > + mdelay(15); > + tsl2x7x_prox_poll(indio_dev); > + prox_history[i] = chip->prox_data; > + dev_info(&chip->client->dev, "2 i=%d prox data= %d\n", > + i, chip->prox_data); > + } > + > + tsl2x7x_chip_off(indio_dev); > + calP =&prox_stat_data[PROX_STAT_CAL]; > + tsl2x7x_prox_calculate(prox_history, > + chip->tsl2x7x_settings.prox_max_samples_cal, calP); > + chip->tsl2x7x_settings.prox_thres_high = (calP->max<< 1) - calP->mean; > + > + dev_info(&chip->client->dev, " cal min=%d mean=%d max=%d\n", > + calP->min, calP->mean, calP->max); > + dev_info(&chip->client->dev, > + "%s proximity threshold set to %d\n", > + chip->client->name, chip->tsl2x7x_settings.prox_thres_high); > + > + /* back to the way they were */ > + chip->tsl2x7x_settings.interrupts_en = tmp_irq_settings; > + if (current_state == TSL2X7X_CHIP_WORKING) > + tsl2x7x_chip_on(indio_dev); > +} > + > +static ssize_t tsl2x7x_power_state_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); Could do the above in one go. struct tsl2X7X_chip *chip = iio_priv(dev_get_drvdata(dev)); Lots more cases of this below. > + > + return snprintf(buf, PAGE_SIZE, "%d\n", chip->tsl2x7x_chip_status); > +} > + > +static ssize_t tsl2x7x_power_state_store(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t len) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + bool value; > + > + if (strtobool(buf,&value)) > + return -EINVAL; > + Seems to me that inverting the logic of this if would make things a tiny bit easier to read. > + if (!value) > + tsl2x7x_chip_off(indio_dev); > + else > + tsl2x7x_chip_on(indio_dev); > + > + return len; > +} > + > +static ssize_t tsl2x7x_gain_available_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + > + if (chip->id> tsl2771) Switch on partnumbers preferred. > + return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 128"); > + else > + return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 120"); > +} > + > +static ssize_t tsl2x7x_prox_gain_available_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + return snprintf(buf, PAGE_SIZE, "%s\n", "1 2 4 8"); > +} > + > +static ssize_t tsl2x7x_als_time_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + > + return snprintf(buf, PAGE_SIZE, "%d\n", > + chip->tsl2x7x_settings.als_time); > +} > + > +static ssize_t tsl2x7x_als_time_store(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t len) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + unsigned long value; > + > + if (kstrtoul(buf, 0,&value)) > + return -EINVAL; > + > + if ((value< 50) || (value> 650)) > + return -EINVAL; > + > + if (value % 50) > + return -EINVAL; > + > + chip->tsl2x7x_settings.als_time = value; > + > + return len; > +} > + > +static IIO_CONST_ATTR(illuminance0_integration_time_available, > + "50 100 150 200 250 300 350 400 450 500 550 600 650"); > + > +static ssize_t tsl2x7x_als_cal_target_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + > + return snprintf(buf, PAGE_SIZE, "%d\n", > + chip->tsl2x7x_settings.als_cal_target); > +} > + > +static ssize_t tsl2x7x_als_cal_target_store(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t len) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + unsigned long value; > + > + if (kstrtoul(buf, 0,&value)) > + return -EINVAL; > + > + if (value) > + chip->tsl2x7x_settings.als_cal_target = value; > + > + return len; > +} > + > +/* sampling_frequency AKA persistence in data sheet */ > +static ssize_t tsl2x7x_persistence_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + > + return snprintf(buf, PAGE_SIZE, "%d\n", > + chip->tsl2x7x_settings.persistence); > +} > + > +static ssize_t tsl2x7x_persistence_store(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t len) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + unsigned long value; > + > + if (kstrtoul(buf, 0,&value)) > + return -EINVAL; > + > + chip->tsl2x7x_settings.persistence = value; > + > + return len; > +} > + > +static IIO_CONST_ATTR(sampling_frequency_available, > + "0x00 - 0xFF (0 - 255)"); What units? This really should be converted into hz even if it's somewhat of a pain to do. > + > +static ssize_t tsl2x7x_do_calibrate(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t len) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + bool value; > + > + if (strtobool(buf,&value)) > + return -EINVAL; > + > + if (value) > + tsl2x7x_als_calibrate(indio_dev); > + > + return len; > +} > + > +static ssize_t tsl2x7x_luxtable_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + int i = 0; > + int offset = 0; > + > + while (i< (TSL2X7X_MAX_LUX_TABLE_SIZE * 3)) { > + offset += snprintf(buf + offset, PAGE_SIZE, "%d,%d,%d,", > + chip->tsl2x7x_device_lux[i].ratio, > + chip->tsl2x7x_device_lux[i].ch0, > + chip->tsl2x7x_device_lux[i].ch1); > + if (chip->tsl2x7x_device_lux[i].ratio == 0) { > + /* We just printed the first "0" entry. > + * Now get rid of the extra "," and break. */ > + offset--; > + break; > + } > + i++; > + } > + > + offset += snprintf(buf + offset, PAGE_SIZE, "\n"); > + return offset; > +} > + > +static ssize_t tsl2x7x_luxtable_store(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t len) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + int value[ARRAY_SIZE(chip->tsl2x7x_device_lux)*3 + 1]; > + int n; > + > + get_options(buf, ARRAY_SIZE(value), value); > + > + /* We now have an array of ints starting at value[1], and > + * enumerated by value[0]. > + * We expect each group of three ints is one table entry, > + * and the last table entry is all 0. > + */ > + n = value[0]; > + if ((n % 3) || n< 6 || > + n> ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) { > + dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n); > + return -EINVAL; > + } > + > + if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) { > + dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n); > + return -EINVAL; > + } > + > + if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) > + tsl2x7x_chip_off(indio_dev); > + > + /* Zero out the table */ > + memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux)); > + memcpy(chip->tsl2x7x_device_lux,&value[1], (value[0] * 4)); > + > + return len; > +} > + > +static ssize_t tsl2x7x_do_prox_calibrate(struct device *dev, > + struct device_attribute *attr, const char *buf, size_t len) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + bool value; > + > + if (strtobool(buf,&value)) > + return -EINVAL; > + > + if (value) > + tsl2x7x_prox_cal(indio_dev); > + > + return len; > +} > + > +static int tsl2x7x_read_interrupt_config(struct iio_dev *indio_dev, > + u64 event_code) > +{ > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + int ret; > + > + if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_INTENSITY) > + ret = !!(chip->tsl2x7x_settings.interrupts_en& 0x10); > + else > + ret = !!(chip->tsl2x7x_settings.interrupts_en& 0x20); > + > + return ret; > +} > + > +static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev, > + u64 event_code, > + int val) > +{ > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + > + if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_INTENSITY) { > + if (val) > + chip->tsl2x7x_settings.interrupts_en |= 0x10; > + else > + chip->tsl2x7x_settings.interrupts_en&= 0x20; > + } else { > + if (val) > + chip->tsl2x7x_settings.interrupts_en |= 0x20; > + else > + chip->tsl2x7x_settings.interrupts_en&= 0x10; > + } Would normally expect this to write the settings to the device. > + > + return 0; > +} > + > +static int tsl2x7x_write_thresh(struct iio_dev *indio_dev, > + u64 event_code, > + int val) > +{ > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + > + if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_INTENSITY) { > + switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { > + case IIO_EV_DIR_RISING: > + chip->tsl2x7x_settings.als_thresh_high = val; > + break; > + case IIO_EV_DIR_FALLING: > + chip->tsl2x7x_settings.als_thresh_low = val; > + break; > + default: > + return -EINVAL; > + } > + } else { > + switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { > + case IIO_EV_DIR_RISING: > + chip->tsl2x7x_settings.prox_thres_high = val; > + break; > + case IIO_EV_DIR_FALLING: > + chip->tsl2x7x_settings.prox_thres_low = val; > + break; > + default: > + return -EINVAL; > + } > + } > + > + return 0; > +} > + > +static int tsl2x7x_read_thresh(struct iio_dev *indio_dev, > + u64 event_code, > + int *val) > +{ > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + > + if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_INTENSITY) { > + switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { > + case IIO_EV_DIR_RISING: > + *val = chip->tsl2x7x_settings.als_thresh_high; > + break; > + case IIO_EV_DIR_FALLING: > + *val = chip->tsl2x7x_settings.als_thresh_low; > + break; > + default: > + return -EINVAL; > + } > + } else { > + switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { > + case IIO_EV_DIR_RISING: > + *val = chip->tsl2x7x_settings.prox_thres_high; > + break; > + case IIO_EV_DIR_FALLING: > + *val = chip->tsl2x7x_settings.prox_thres_low; > + break; > + default: > + return -EINVAL; > + } > + } > + > + return 0; > +} > + > +static int tsl2x7x_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int *val, > + int *val2, > + long mask) > +{ > + int ret = -EINVAL; > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + > + switch (mask) { > + case 0: > + switch (chan->type) { > + case IIO_LIGHT: > + tsl2x7x_get_lux(indio_dev); > + *val = chip->als_cur_info.lux; > + ret = IIO_VAL_INT; > + break; > + case IIO_INTENSITY: > + tsl2x7x_get_lux(indio_dev); > + if (chan->channel == 0) > + *val = chip->als_cur_info.als_ch0; > + else > + *val = chip->als_cur_info.als_ch1; > + ret = IIO_VAL_INT; > + break; > + case IIO_PROXIMITY: > + tsl2x7x_prox_poll(indio_dev); > + *val = chip->prox_data; > + ret = IIO_VAL_INT; > + break; > + default: > + return -EINVAL; > + break; > + } > + break; > + case IIO_CHAN_INFO_CALIBSCALE: > + if (chan->type == IIO_LIGHT) > + *val = > + tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]; > + else > + *val = > + tsl2X7X_prx_gainadj[chip->tsl2x7x_settings.prox_gain]; > + ret = IIO_VAL_INT; > + break; > + case IIO_CHAN_INFO_CALIBBIAS: > + *val = chip->tsl2x7x_settings.als_gain_trim; > + ret = IIO_VAL_INT; > + break; > + > + default: > + ret = -EINVAL; > + } > + > + return ret; > +} > + > +static int tsl2x7x_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int val, > + int val2, > + long mask) > +{ > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + > + switch (mask) { > + case IIO_CHAN_INFO_CALIBSCALE: > + if (chan->type == IIO_INTENSITY) { > + switch (val) { > + case 1: > + chip->tsl2x7x_settings.als_gain = 0; > + break; > + case 8: > + chip->tsl2x7x_settings.als_gain = 1; > + break; > + case 16: > + chip->tsl2x7x_settings.als_gain = 2; > + break; > + case 120: again, would prefer a switch on the chip->id's in question to relying on ordering in the array of chip ids. Yes it's more code, but less fragile... > + if (chip->id> tsl2771) > + return -EINVAL; > + chip->tsl2x7x_settings.als_gain = 3; > + break; > + case 128: > + if (chip->id< tsl2572) > + return -EINVAL; > + chip->tsl2x7x_settings.als_gain = 3; > + break; > + default: > + return -EINVAL; > + } > + } else { > + switch (val) { > + case 1: > + chip->tsl2x7x_settings.prox_gain = 0; > + break; > + case 2: > + chip->tsl2x7x_settings.prox_gain = 1; > + break; > + case 4: > + chip->tsl2x7x_settings.prox_gain = 2; > + break; > + case 8: > + chip->tsl2x7x_settings.prox_gain = 3; > + break; > + default: > + return -EINVAL; > + } > + } > + break; > + case IIO_CHAN_INFO_CALIBBIAS: > + chip->tsl2x7x_settings.als_gain_trim = val; > + break; > + > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, > + tsl2x7x_power_state_show, tsl2x7x_power_state_store); > + > +static DEVICE_ATTR(proximity_calibscale_available, S_IRUGO, > + tsl2x7x_prox_gain_available_show, NULL); > + > +static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO, > + tsl2x7x_gain_available_show, NULL); > + > +static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR, > + tsl2x7x_als_time_show, tsl2x7x_als_time_store); > + > +static DEVICE_ATTR(illuminance0_target_input, S_IRUGO | S_IWUSR, > + tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store); > + > +static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL, > + tsl2x7x_do_calibrate); > + > +static DEVICE_ATTR(proximity_calibrate, S_IWUSR, NULL, > + tsl2x7x_do_prox_calibrate); > + > +static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR, > + tsl2x7x_luxtable_show, tsl2x7x_luxtable_store); > + > +static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, > + tsl2x7x_persistence_show, tsl2x7x_persistence_store); > + > +/* Use the default register values to identify the Taos device */ > +static int tsl2x7x_device_id(unsigned char *id, int target) > +{ > + switch (target) { > + case tsl2571: > + case tsl2671: > + case tsl2771: > + return ((*id& 0xf0) == TRITON_ID); > + break; > + case tmd2671: > + case tmd2771: > + return ((*id& 0xf0) == HALIBUT_ID); > + break; > + case tsl2572: > + case tsl2672: > + case tmd2672: > + case tsl2772: > + case tmd2772: > + return ((*id& 0xf0) == SWORDFISH_ID); > + break; > + } > + > + return -EINVAL; > +} > + > +/* > + * Interrupt Event Handler */ > +static irqreturn_t tsl2x7x_event_handler(int irq, void *private) > +{ > + struct iio_dev *indio_dev = private; > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + s64 timestamp = iio_get_time_ns(); > + int ret; > + int value; > + > + value = i2c_smbus_read_byte_data(chip->client, > + TSL2X7X_CMD_REG | TSL2X7X_STATUS); > + > + /* What type of interrupt do we need to process */ > + if (value& TSL2X7X_STA_PRX_INTR) { > + tsl2x7x_prox_poll(indio_dev); missed this, put what does the prox_poll do here? Event seems to be cleared below... A comment to clarify why a reading is take would clear this up. > + iio_push_event(indio_dev, > + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, > + 0, > + IIO_EV_TYPE_THRESH, > + IIO_EV_DIR_EITHER), > + timestamp); > + } > + > + if (value& TSL2X7X_STA_ALS_INTR) { > + tsl2x7x_get_lux(indio_dev); > + iio_push_event(indio_dev, > + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, > + 0, > + IIO_EV_TYPE_THRESH, > + IIO_EV_DIR_EITHER), > + timestamp); > + } > + /* Clear interrupt now that we have handled it. */ > + ret = i2c_smbus_write_byte(chip->client, > + TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN | > + TSL2X7X_CMD_PROXALS_INT_CLR); > + if (ret< 0) > + dev_err(&chip->client->dev, > + "%s: Failed to clear irq from event handler. err = %d\n", > + __func__, ret); > + > + return IRQ_HANDLED; > +} > + > +static struct attribute *tsl2x7x_ALS_device_attrs[] = { > + &dev_attr_power_state.attr, > + &dev_attr_illuminance0_calibscale_available.attr, > + &dev_attr_illuminance0_integration_time.attr, > + &iio_const_attr_illuminance0_integration_time_available.dev_attr.attr, > + &dev_attr_illuminance0_target_input.attr, > + &dev_attr_illuminance0_calibrate.attr, > + &dev_attr_illuminance0_lux_table.attr, > + &dev_attr_sampling_frequency.attr, > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > + NULL > +}; > + > +static struct attribute *tsl2x7x_PRX_device_attrs[] = { > + &dev_attr_power_state.attr, > + &dev_attr_sampling_frequency.attr, > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > + &dev_attr_proximity_calibrate.attr, > + NULL > +}; > + > +static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = { > + &dev_attr_power_state.attr, > + &dev_attr_illuminance0_calibscale_available.attr, > + &dev_attr_illuminance0_integration_time.attr, > + &iio_const_attr_illuminance0_integration_time_available.dev_attr.attr, > + &dev_attr_illuminance0_target_input.attr, > + &dev_attr_illuminance0_calibrate.attr, > + &dev_attr_illuminance0_lux_table.attr, > + &dev_attr_sampling_frequency.attr, > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > + &dev_attr_proximity_calibrate.attr, > + NULL > +}; > + > +static struct attribute *tsl2x7x_PRX2_device_attrs[] = { > + &dev_attr_power_state.attr, > + &dev_attr_sampling_frequency.attr, > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > + &dev_attr_proximity_calibrate.attr, > + &dev_attr_proximity_calibscale_available.attr, > + NULL > +}; > + > +static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] = { > + &dev_attr_power_state.attr, > + &dev_attr_illuminance0_calibscale_available.attr, > + &dev_attr_illuminance0_integration_time.attr, > + &iio_const_attr_illuminance0_integration_time_available.dev_attr.attr, > + &dev_attr_illuminance0_target_input.attr, > + &dev_attr_illuminance0_calibrate.attr, > + &dev_attr_illuminance0_lux_table.attr, > + &dev_attr_sampling_frequency.attr, > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > + &dev_attr_proximity_calibrate.attr, > + &dev_attr_proximity_calibscale_available.attr, > + NULL > +}; > + > +static const struct attribute_group tsl2X7X_device_attr_group_tbl[] = { > + [ALS] = { > + .attrs = tsl2x7x_ALS_device_attrs, > + }, > + [PRX] = { > + .attrs = tsl2x7x_PRX_device_attrs, > + }, > + [ALSPRX] = { > + .attrs = tsl2x7x_ALSPRX_device_attrs, > + }, > + [PRX2] = { > + .attrs = tsl2x7x_PRX2_device_attrs, > + }, > + [ALSPRX2] = { > + .attrs = tsl2x7x_ALSPRX2_device_attrs, > + }, > +}; > + > +static const struct iio_info tsl2X7X_device_info[] = { > + [ALS] = { > + .attrs =&tsl2X7X_device_attr_group_tbl[ALS], > + .driver_module = THIS_MODULE, > + .read_raw =&tsl2x7x_read_raw, > + .write_raw =&tsl2x7x_write_raw, > + .read_event_value =&tsl2x7x_read_thresh, > + .write_event_value =&tsl2x7x_write_thresh, > + .read_event_config =&tsl2x7x_read_interrupt_config, > + .write_event_config =&tsl2x7x_write_interrupt_config, > + }, > + [PRX] = { > + .attrs =&tsl2X7X_device_attr_group_tbl[PRX], > + .driver_module = THIS_MODULE, > + .read_raw =&tsl2x7x_read_raw, > + .write_raw =&tsl2x7x_write_raw, > + .read_event_value =&tsl2x7x_read_thresh, > + .write_event_value =&tsl2x7x_write_thresh, > + .read_event_config =&tsl2x7x_read_interrupt_config, > + .write_event_config =&tsl2x7x_write_interrupt_config, > + }, > + [ALSPRX] = { > + .attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX], > + .driver_module = THIS_MODULE, > + .read_raw =&tsl2x7x_read_raw, > + .write_raw =&tsl2x7x_write_raw, > + .read_event_value =&tsl2x7x_read_thresh, > + .write_event_value =&tsl2x7x_write_thresh, > + .read_event_config =&tsl2x7x_read_interrupt_config, > + .write_event_config =&tsl2x7x_write_interrupt_config, > + }, > + [PRX2] = { > + .attrs =&tsl2X7X_device_attr_group_tbl[PRX2], > + .driver_module = THIS_MODULE, > + .read_raw =&tsl2x7x_read_raw, > + .write_raw =&tsl2x7x_write_raw, > + .read_event_value =&tsl2x7x_read_thresh, > + .write_event_value =&tsl2x7x_write_thresh, > + .read_event_config =&tsl2x7x_read_interrupt_config, > + .write_event_config =&tsl2x7x_write_interrupt_config, > + }, > + [ALSPRX2] = { > + .attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX2], > + .driver_module = THIS_MODULE, > + .read_raw =&tsl2x7x_read_raw, > + .write_raw =&tsl2x7x_write_raw, > + .read_event_value =&tsl2x7x_read_thresh, > + .write_event_value =&tsl2x7x_write_thresh, > + .read_event_config =&tsl2x7x_read_interrupt_config, > + .write_event_config =&tsl2x7x_write_interrupt_config, > + }, > +}; > + > +static const struct tsl2x7x_chip_info tsl2x7x_chip_info_tbl[] = { > + [ALS] = { > + .channel = { > + { > + .type = IIO_LIGHT, > + .indexed = 1, > + .channel = 0, > + .processed_val = 1, > + }, { > + .type = IIO_INTENSITY, > + .indexed = 1, > + .channel = 0, > + .info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | > + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, > + .event_mask = TSL2X7X_EVENT_MASK > + }, { > + .type = IIO_INTENSITY, > + .indexed = 1, > + .channel = 1, > + }, > + }, > + .chan_table_elements = 3, > + .info =&tsl2X7X_device_info[ALS], > + }, > + [PRX] = { > + .channel = { > + { > + .type = IIO_PROXIMITY, > + .indexed = 1, > + .channel = 0, > + .event_mask = TSL2X7X_EVENT_MASK > + }, > + }, > + .chan_table_elements = 1, > + .info =&tsl2X7X_device_info[PRX], > + }, > + [ALSPRX] = { > + .channel = { > + { > + .type = IIO_LIGHT, > + .indexed = 1, > + .channel = 0, > + .processed_val = 1, > + }, { > + .type = IIO_INTENSITY, > + .indexed = 1, > + .channel = 0, > + .info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | > + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, > + .event_mask = TSL2X7X_EVENT_MASK > + }, { > + .type = IIO_INTENSITY, > + .indexed = 1, > + .channel = 1, > + }, { > + .type = IIO_PROXIMITY, > + .indexed = 1, > + .channel = 0, > + .event_mask = TSL2X7X_EVENT_MASK > + }, > + }, > + .chan_table_elements = 4, > + .info =&tsl2X7X_device_info[ALSPRX], > + }, > + [PRX2] = { > + .channel = { > + { > + .type = IIO_PROXIMITY, > + .indexed = 1, > + .channel = 0, > + .info_mask = > + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, > + .event_mask = TSL2X7X_EVENT_MASK > + }, > + }, > + .chan_table_elements = 1, > + .info =&tsl2X7X_device_info[PRX2], > + }, > + [ALSPRX2] = { > + .channel = { > + { > + .type = IIO_LIGHT, > + .indexed = 1, > + .channel = 0, > + .processed_val = 1, > + }, { > + .type = IIO_INTENSITY, > + .indexed = 1, > + .channel = 0, > + .info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | > + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, > + .event_mask = TSL2X7X_EVENT_MASK > + }, { > + .type = IIO_INTENSITY, > + .indexed = 1, > + .channel = 1, > + }, { > + .type = IIO_PROXIMITY, > + .indexed = 1, > + .channel = 0, > + .info_mask = > + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, > + .event_mask = TSL2X7X_EVENT_MASK > + }, > + }, I think you still have space for 9 channels in the structure.... > + .chan_table_elements = 4, > + .info =&tsl2X7X_device_info[ALSPRX2], > + }, > +}; > + Kind of obvious comment and not in kernel-doc... > +/* > + * Client probe function. > + */ > +static int __devinit tsl2x7x_probe(struct i2c_client *clientp, > + const struct i2c_device_id *id) > +{ > + int ret; > + unsigned char device_id; > + struct iio_dev *indio_dev; > + struct tsl2X7X_chip *chip; > + > + indio_dev = iio_allocate_device(sizeof(*chip)); > + if (!indio_dev) > + return -ENOMEM; > + > + chip = iio_priv(indio_dev); > + chip->client = clientp; > + i2c_set_clientdata(clientp, indio_dev); > + > + ret = tsl2x7x_i2c_read(chip->client, > + TSL2X7X_CHIPID,&device_id); > + if (ret< 0) > + goto fail1; > + > + if ((!tsl2x7x_device_id(&device_id, id->driver_data)) || > + (tsl2x7x_device_id(&device_id, id->driver_data) == -EINVAL)) { > + dev_info(&chip->client->dev, > + "i2c device found does not match expected id in %s\n", > + __func__); Would be good to standardise error formatting across the driver. Sometimes you have the function name first, sometimes last. Pick one and go with it. > + goto fail1; > + } > + > + ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG | TSL2X7X_CNTRL)); > + if (ret< 0) { > + dev_err(&clientp->dev, "%s: write to cmd reg failed. err = %d\n", > + __func__, ret); > + goto fail1; > + } > + > + /* ALS and PROX functions can be invoked via user space poll > + * or H/W interrupt. If busy return last sample. */ > + mutex_init(&chip->als_mutex); > + mutex_init(&chip->prox_mutex); > + > + chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN; > + chip->pdata = clientp->dev.platform_data; > + chip->id = id->driver_data; > + chip->chip_info = > + &tsl2x7x_chip_info_tbl[device_channel_config[id->driver_data]]; > + > + indio_dev->info = chip->chip_info->info; > + indio_dev->dev.parent =&clientp->dev; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->name = chip->client->name; > + indio_dev->channels = chip->chip_info->channel; > + indio_dev->num_channels = chip->chip_info->chan_table_elements; > + > + if (clientp->irq) { > + ret = request_threaded_irq(clientp->irq, > + NULL, > + &tsl2x7x_event_handler, > + IRQF_TRIGGER_RISING | IRQF_ONESHOT, > + "TSL2X7X_event", > + indio_dev); > + if (ret) { > + dev_err(&clientp->dev, > + "%s: irq request failed", __func__); > + goto fail2; > + } > + } > + > + /* Load up the defaults */ > + tsl2x7x_defaults(chip); > + /* Make sure the chip is on */ > + tsl2x7x_chip_on(indio_dev); > + > + ret = iio_device_register(indio_dev); > + if (ret) { > + dev_err(&clientp->dev, > + "%s: iio registration failed\n", __func__); > + goto fail1; > + } > + > + dev_info(&clientp->dev, "%s Light sensor found.\n", id->name); > + return 0; > + > +fail1: > + if (clientp->irq) > + free_irq(clientp->irq, indio_dev); > +fail2: > + iio_free_device(indio_dev); convention puts a blank line before the return. Same above. > + return ret; > +} > + > +static int tsl2x7x_suspend(struct device *dev) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + int ret = 0; > + > + if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) { > + ret = tsl2x7x_chip_off(indio_dev); > + chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED; > + } > + > + if (chip->pdata&& chip->pdata->platform_power) { > + pm_message_t pmm = {PM_EVENT_SUSPEND}; > + chip->pdata->platform_power(dev, pmm); > + } > + > + return ret; > +} > + > +static int tsl2x7x_resume(struct device *dev) > +{ > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > + int ret = 0; > + > + if (chip->pdata&& chip->pdata->platform_power) { > + pm_message_t pmm = {PM_EVENT_RESUME}; > + chip->pdata->platform_power(dev, pmm); > + } > + > + if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_SUSPENDED) > + ret = tsl2x7x_chip_on(indio_dev); > + > + return ret; > +} > + > +static int __devexit tsl2x7x_remove(struct i2c_client *client) > +{ > + struct tsl2X7X_chip *chip = i2c_get_clientdata(client); > + struct iio_dev *indio_dev = iio_priv_to_dev(chip); > + > + tsl2x7x_chip_off(indio_dev); > + > + iio_device_unregister(indio_dev); > + if (client->irq) > + free_irq(client->irq, chip->client->name); > + > + iio_free_device(indio_dev); > + > + return 0; > +} > + > +static struct i2c_device_id tsl2x7x_idtable[] = { > + { "tsl2571", tsl2571 }, > + { "tsl2671", tsl2671 }, > + { "tmd2671", tmd2671 }, > + { "tsl2771", tsl2771 }, > + { "tmd2771", tmd2771 }, > + { "tsl2572", tsl2572 }, > + { "tsl2672", tsl2672 }, > + { "tmd2672", tmd2672 }, > + { "tsl2772", tsl2772 }, > + { "tmd2772", tmd2772 }, > + {} > +}; > + > +MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable); > + > +static const struct dev_pm_ops tsl2x7x_pm_ops = { > + .suspend = tsl2x7x_suspend, > + .resume = tsl2x7x_resume, > +}; > + > +/* Driver definition */ > +static struct i2c_driver tsl2x7x_driver = { > + .driver = { > + .name = "tsl2x7x", > + .pm =&tsl2x7x_pm_ops, > + }, > + .id_table = tsl2x7x_idtable, > + .probe = tsl2x7x_probe, > + .remove = __devexit_p(tsl2x7x_remove), > +}; > + > +module_i2c_driver(tsl2x7x_driver); > + > +MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>"); > +MODULE_DESCRIPTION("TAOS tsl2x7x ambient and proximity light sensor driver"); > +MODULE_LICENSE("GPL"); > -- > 1.7.4.1 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 9+ messages in thread
* RE: [PATCH V5] TAOS tsl2x7x 2012-04-04 8:35 ` Jonathan Cameron @ 2012-04-04 15:30 ` Jon Brenner 0 siblings, 0 replies; 9+ messages in thread From: Jon Brenner @ 2012-04-04 15:30 UTC (permalink / raw) To: Jonathan Cameron; +Cc: linux-iio, Linux Kernel SGkgSm9uYXRoYW4sDQpUaGFua3MgZm9yIHRoZSByZXZpZXcgLg0KDQpQbGVhc2Ugc2VlIHZhcmlv dXMgcmVzcG9uc2VzIC0gaW4gbGluZS4NCg0KTmV4dCBwYXRjaCB3aWxsIGJlIFY2IGFuZCB0aGUg bGFzdCAtIEkgaG9wZSENCkpvbiANCg0KPiAtLS0tLU9yaWdpbmFsIE1lc3NhZ2UtLS0tLQ0KPiBG cm9tOiBKb25hdGhhbiBDYW1lcm9uIFttYWlsdG86amljMjNAY2FtLmFjLnVrXQ0KPiBTZW50OiBX ZWRuZXNkYXksIEFwcmlsIDA0LCAyMDEyIDM6MzUgQU0NCj4gVG86IEpvbiBCcmVubmVyDQo+IENj OiBsaW51eC1paW87IExpbnV4IEtlcm5lbA0KPiBTdWJqZWN0OiBSZTogW1BBVENIIFY1XSBUQU9T IHRzbDJ4N3gNCj4gDQo+IE9uIDQvMi8yMDEyIDU6NTAgUE0sIEpvbiBCcmVubmVyIHdyb3RlOg0K PiA+IFRBT1MgZGV2aWNlIGRyaXZlciAodmVyc2lvbiA1KSBmb3IgdGhlIHRzbC90bWQgMjc3MSBh bmQgMjc3MiBkZXZpY2UgZmFtaWxpZXMNCj4gKGluYy4gYWxsIHZhcmlhbnRzKS4NCj4gSGkgSm9u LA0KPiANCj4gQ2hhbmdlcyBzaW5jZSBsYXN0IHZlcnNpb24/DQpDb3JyZWN0Lg0KPiANCj4gQSBm ZXcgYml0cyBzdGlsbCB0byBzb3J0IG91dCBpbiBoZXJlIEknbSBhZnJhaWQuLi4gKGdldHRpbmcg dGhlcmUgdGhvdWdoISkNCj4gTXkgcmV2aWV3cyB0ZW5kIHRvIGdldCBtb3JlIHBpY2t5IGFzIHRo ZSBiaWcgc3R1ZmYgZ2V0cyBzb3J0ZWQgb3V0Lg0KPiANCj4gT24gdHJpdmlhbCBleHRyYSBibGFu ayBsaW5lIHRvIGNsZWFyIG91dC4NCg0KPiBFeHRyYSBsaW5lIGZvciB5b3VyIG5leHQgZHJpdmVy IGhhcyBzbnVjayBpbnRvIHRoZSBtYWtlIGZpbGUuDQpZaWtlcyENCg0KPiBVbml0cyBkb24ndCBs b29rIHJpZ2h0IGZvciBzYW1wbGluZyBmcmVxdWVuY3kuICBTb3JyeSwgYnV0IHRoYXQncyBhbiBh YmkNCj4gaXNzdWUgc28gZXZlbiBpZiBpdCBpcw0KPiBmaWRkbHkgdG8gZG8gdGhlIGNvbnZlcnNp b24gdG8gSHogaXQgbmVlZHMgdG8gYmUgZG9uZS4NCj4gV291bGQgbm9ybWFsbHkgZXhwZWN0IGNo YW5nZXMgdG8gZXZlbnRzIHRvIGdldCBhcHBsaWVkIGltbWVkaWF0ZWx5LiBIZXJlDQo+IEkgdGhp bmsgdGhhdCBvbmx5DQo+IGhhcHBlbnMgaWYgeW91IHR1cm4gdGhlIGRldmljZSBvZmYgYW5kIG9u IGFnYWluPw0KVGhpcyBpcyBwZXIgY3VzdG9tZXIgcmVxdWVzdCAtIGFsbG93cyBjb21wbGV0ZSBy ZWNvbmZpZ3VyYXRpb24gd2l0aG91dCBtYW55IGRldmljZSBvbi9vZmZzLg0KIA0KPiANCj4gRm9y IGZ1dHVyZSByZWZlcmVuY2UgKGRvbid0IGJvdGhlciBoZXJlISkgbWFrZSBhbnkgZG9jdW1lbnRh dGlvbiBtb3Zlcw0KPiBub3QgZGlyZWN0bHkgZGVwZW5kZW50IG9uIHRoZQ0KPiBkcml2ZXIgKHN1 Y2ggYXMgdGhvc2UgdGhhdCB3aWxsIGJlY29tZSB1c2VkIGJ5IG11bHRpcGxlIGRyaXZlcnMpIGlu IGENCj4gcHJlY3Vyc29yIHBhdGNoLg0KT0sNCg0KPiA+DQo+ID4gU2lnbmVkLW9mZi1ieTogSm9u IEJyZW5uZXI8amJyZW5uZXJAdGFvc2luYy5jb20+DQo+ID4gLS0tDQo+ID4gICAuLi4vbGlnaHQv c3lzZnMtYnVzLWlpby1saWdodC10c2wyNTgzICAgICAgICAgICAgICB8ICAgIDYgKw0KPiA+ICAg Li4uL2xpZ2h0L3N5c2ZzLWJ1cy1paW8tbGlnaHQtdHNsMng3eCAgICAgICAgICAgICAgfCAgIDE0 ICsNCj4gPiAgIGRyaXZlcnMvc3RhZ2luZy9paW8vRG9jdW1lbnRhdGlvbi9zeXNmcy1idXMtaWlv ICAgIHwgICAgNyArDQo+ID4gICAuLi4vc3RhZ2luZy9paW8vRG9jdW1lbnRhdGlvbi9zeXNmcy1i dXMtaWlvLWxpZ2h0ICB8ICAgIDggKy0NCj4gPiAgIC4uLi9paW8vRG9jdW1lbnRhdGlvbi9zeXNm cy1idXMtaWlvLWxpZ2h0LXRzbDI1ODMgIHwgICAyMCAtDQo+ID4gICBkcml2ZXJzL3N0YWdpbmcv aWlvL2xpZ2h0L0tjb25maWcgICAgICAgICAgICAgICAgICB8ICAgIDggKw0KPiA+ICAgZHJpdmVy cy9zdGFnaW5nL2lpby9saWdodC9NYWtlZmlsZSAgICAgICAgICAgICAgICAgfCAgICAyICsNCj4g PiAgIGRyaXZlcnMvc3RhZ2luZy9paW8vbGlnaHQvdHNsMng3eC5oICAgICAgICAgICAgICAgIHwg ICA5OSArKw0KPiA+ICAgZHJpdmVycy9zdGFnaW5nL2lpby9saWdodC90c2wyeDd4X2NvcmUuYyAg ICAgICAgICAgfCAxODMwICsrKysrKysrKysrKysrKysrKysrDQo+ID4gICA5IGZpbGVzIGNoYW5n ZWQsIDE5NzAgaW5zZXJ0aW9ucygrKSwgMjQgZGVsZXRpb25zKC0pDQo+ID4NCj4gPiBkaWZmIC0t Z2l0IGEvZHJpdmVycy9zdGFnaW5nL2lpby9Eb2N1bWVudGF0aW9uL2xpZ2h0L3N5c2ZzLWJ1cy1p aW8tbGlnaHQtdHNsMjU4Mw0KPiBiL2RyaXZlcnMvc3RhZ2luZy9paW8vRG9jdW1lbnRhdGlvbi9s aWdodC9zeXNmcy1idXMtaWlvLWxpZ2h0LXRzbDI1ODMNCj4gPiBuZXcgZmlsZSBtb2RlIDEwMDY0 NA0KPiA+IGluZGV4IDAwMDAwMDAuLjhmMmEwMzgNCj4gPiAtLS0gL2Rldi9udWxsDQo+ID4gKysr IGIvZHJpdmVycy9zdGFnaW5nL2lpby9Eb2N1bWVudGF0aW9uL2xpZ2h0L3N5c2ZzLWJ1cy1paW8t bGlnaHQtdHNsMjU4Mw0KPiA+IEBAIC0wLDAgKzEsNiBAQA0KPiA+ICtXaGF0OgkJL3N5cy9idXMv aWlvL2RldmljZXMvZGV2aWNlW25dL2lsbHVtaW5hbmNlMF9jYWxpYnJhdGUNCj4gPiArS2VybmVs VmVyc2lvbjoJMi42LjM3DQo+ID4gK0NvbnRhY3Q6CWxpbnV4LWlpb0B2Z2VyLmtlcm5lbC5vcmcN Cj4gPiArRGVzY3JpcHRpb246DQo+ID4gKwkJVGhpcyBwcm9wZXJ0eSBjYXVzZXMgYW4gaW50ZXJu YWwgY2FsaWJyYXRpb24gb2YgdGhlIGFscyBnYWluIHRyaW0NCj4gPiArCQl2YWx1ZSB3aGljaCBp cyBsYXRlciB1c2VkIGluIGNhbGN1bGF0aW5nIGlsbHVtaW5hbmNlIGluIGx1eC4NCj4gPiBkaWZm IC0tZ2l0IGEvZHJpdmVycy9zdGFnaW5nL2lpby9Eb2N1bWVudGF0aW9uL2xpZ2h0L3N5c2ZzLWJ1 cy1paW8tbGlnaHQtdHNsMng3eA0KPiBiL2RyaXZlcnMvc3RhZ2luZy9paW8vRG9jdW1lbnRhdGlv bi9saWdodC9zeXNmcy1idXMtaWlvLWxpZ2h0LXRzbDJ4N3gNCj4gPiBuZXcgZmlsZSBtb2RlIDEw MDY0NA0KPiA+IGluZGV4IDAwMDAwMDAuLjI3NWFlNTQNCj4gPiAtLS0gL2Rldi9udWxsDQo+ID4g KysrIGIvZHJpdmVycy9zdGFnaW5nL2lpby9Eb2N1bWVudGF0aW9uL2xpZ2h0L3N5c2ZzLWJ1cy1p aW8tbGlnaHQtdHNsMng3eA0KPiA+IEBAIC0wLDAgKzEsMTQgQEANCj4gPiArV2hhdDoJCS9zeXMv YnVzL2lpby9kZXZpY2VzL2RldmljZVtuXS9pbGx1bWluYW5jZTBfY2FsaWJyYXRlDQo+ID4gK0tl cm5lbFZlcnNpb246CTIuNi4zNw0KPiA+ICtDb250YWN0OglsaW51eC1paW9Admdlci5rZXJuZWwu b3JnDQo+ID4gK0Rlc2NyaXB0aW9uOg0KPiA+ICsJCVRoaXMgcHJvcGVydHkgY2F1c2VzIGFuIGlu dGVybmFsIGNhbGlicmF0aW9uIG9mIHRoZSBhbHMgZ2FpbiB0cmltDQo+ID4gKwkJdmFsdWUgd2hp Y2ggaXMgbGF0ZXIgdXNlZCBpbiBjYWxjdWxhdGluZyBpbGx1bWluYW5jZSBpbiBsdXguDQo+IEht bS4uIGNvdWxkIHBvc3NpYmx5IG1vdmUgdGhpcyBpbnRvIHN5c2ZzLWJ1cy1paW8tbGlnaHQgYXQg c29tZSBwb2ludA0KPiBnaXZlbiB3ZSBjbGVhcmx5IGhhdmUgdHdvIGRyaXZlcnMNCj4gdXNpbmcg aXQuIChmaW5lIGZvciBub3cgdGhvdWdoKQ0KPiA+ICsNCj4gPiArV2hhdDoJCS9zeXMvYnVzL2lp by9kZXZpY2VzL2RldmljZVtuXS9wcm94aW1pdHlfY2FsaWJyYXRlDQo+ID4gK0tlcm5lbFZlcnNp b246CTMuMy1yYzENCj4gPiArQ29udGFjdDoJbGludXgtaWlvQHZnZXIua2VybmVsLm9yZw0KPiA+ ICtEZXNjcmlwdGlvbjoNCj4gPiArCQlDYXVzZXMgYW4gcmVjYWxjdWxhdGlvbiBhbmQgYWRqdXN0 bWVudCB0byB0aGUNCj4gPiArCQlwcm94aW1pdHlfdGhyZXNoX3Jpc2luZ192YWx1ZS4NCj4gVGhp cyBvbmUgaXMgaW50ZXJlc3RpbmcgYXMgdGhlcmUgYXJlIG90aGVyIHByb3hpbWl0eSBzZW5zb3Jz IG91dCB0aGVyZQ0KPiAobm90IGxpZ2h0IGJhc2VkKSBzbyB3ZQ0KPiBtYXkgd2FudCB0byBtb3Zl IHRoaXMgYXQgc29tZSBsYXRlciBwb2ludCB0byBhIHN5c2ZzLWJ1cy1paW8tcHJveGltaXR5DQo+ IGRvY3VtZW50YXRpb24gZmlsZS4NCj4gPiArDQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvc3Rh Z2luZy9paW8vRG9jdW1lbnRhdGlvbi9zeXNmcy1idXMtaWlvDQo+IGIvZHJpdmVycy9zdGFnaW5n L2lpby9Eb2N1bWVudGF0aW9uL3N5c2ZzLWJ1cy1paW8NCj4gPiBpbmRleCA0NmE5OTVkLi41YjJi NWQzIDEwMDY0NA0KPiA+IC0tLSBhL2RyaXZlcnMvc3RhZ2luZy9paW8vRG9jdW1lbnRhdGlvbi9z eXNmcy1idXMtaWlvDQo+ID4gKysrIGIvZHJpdmVycy9zdGFnaW5nL2lpby9Eb2N1bWVudGF0aW9u L3N5c2ZzLWJ1cy1paW8NCj4gPiBAQCAtMjU4LDYgKzI1OCw4IEBAIFdoYXQNCj4gCS9zeXMvYnVz L2lpby9kZXZpY2VzL2lpbzpkZXZpY2VYL2luX2FjY2VsX3pfY2FsaWJzY2FsZQ0KPiA+ICAgV2hh dA0KPiAJL3N5cy9idXMvaWlvL2RldmljZXMvaWlvOmRldmljZVgvaW5fYW5nbHZlbF94X2NhbGli c2NhbGUNCj4gPiAgIFdoYXQNCj4gCS9zeXMvYnVzL2lpby9kZXZpY2VzL2lpbzpkZXZpY2VYL2lu X2FuZ2x2ZWxfeV9jYWxpYnNjYWxlDQo+ID4gICBXaGF0DQo+IAkvc3lzL2J1cy9paW8vZGV2aWNl cy9paW86ZGV2aWNlWC9pbl9hbmdsdmVsX3pfY2FsaWJzY2FsZQ0KPiA+ICt3aGF0DQo+IAkvc3lz L2J1cy9paW8vZGV2aWNlcy9paW86ZGV2aWNlWC9pbGx1bWluYW5jZTBfY2FsaWJzY2FsZQ0KPiA+ ICt3aGF0CQkvc3lzL2J1cy9paW8vZGV2aWNlcy9paW86ZGV2aWNlWC9wcm94aW1pdHlfY2FsaWJz Y2FsZQ0KPiA+ICAgS2VybmVsVmVyc2lvbjoJMi42LjM1DQo+ID4gICBDb250YWN0OglsaW51eC1p aW9Admdlci5rZXJuZWwub3JnDQo+ID4gICBEZXNjcmlwdGlvbjoNCj4gPiBAQCAtNDU3LDYgKzQ1 OSwxMCBAQCBXaGF0Og0KPiAJL3N5cy8uLi4vZXZlbnRzL2luX3ZvbHRhZ2VZX3Jhd190aHJlc2hf ZmFsbGluZ192YWx1ZQ0KPiA+ICAgV2hhdDoJCS9zeXMvLi4uL2V2ZW50cy9pbl92b2x0YWdlWV9y YXdfdGhyZXNoX2ZhbGxpbmdfdmFsdWUNCj4gPiAgIFdoYXQ6CQkvc3lzLy4uLi9ldmVudHMvaW5f dGVtcFlfcmF3X3RocmVzaF9mYWxsaW5nX3ZhbHVlDQo+ID4gICBXaGF0OgkJL3N5cy8uLi4vZXZl bnRzL2luX3RlbXBZX3Jhd190aHJlc2hfZmFsbGluZ192YWx1ZQ0KPiBPb3BzLCBjbGVhcmx5IGFu ZCBlcnJvciBpbiB0aGUgbGluZXMgYWJvdmUgKHJlcGVhdHMgb2YgZmFsbGluZyBhbmQgbm8NCj4g cmlzaW5nKS4gSSdsbCBmaXggdGhhdCB1cCB1bmxlc3MNCj4gc29tZW9uZSBlbHNlIGdldHMgdGhl cmUgZmlyc3QuDQpPSyAtIHdpbGwgbm90IGNoYW5nZSBpbiB0aGlzIHBhdGNoLg0KDQo+ID4gK1do YXQ6CQkvc3lzLy4uLi9ldmVudHMvaWxsdW1pbmFuY2UwX3RocmVzaF9mYWxsaW5nX3ZhbHVlDQo+ ID4gK3doYXQ6CQkvc3lzLy4uLi9ldmVudHMvaWxsdW1pbmFuY2UwX3RocmVzaF9yaXNpbmdfdmFs dWUNCj4gPiArd2hhdDoJCS9zeXMvLi4uL2V2ZW50cy9wcm94aW1pdHlfdGhyZXNoX2ZhbGxpbmdf dmFsdWUNCj4gPiArd2hhdDoJCS9zeXMvLi4uL2V2ZW50cy9wcm94aW1pdHlfdGhyZXNoX3Jpc2lu Z192YWx1ZQ0KPiA+ICAgS2VybmVsVmVyc2lvbjoJMi42LjM3DQo+ID4gICBDb250YWN0OglsaW51 eC1paW9Admdlci5rZXJuZWwub3JnDQo+ID4gICBEZXNjcmlwdGlvbjoNCj4gPiBAQCAtNzM5LDMg Kzc0NSw0IEBAIERlc2NyaXB0aW9uOg0KPiA+ICAgCQlzeXN0ZW0uIFRvIG1pbmltaXplIHRoZSBj dXJyZW50IGNvbnN1bXB0aW9uIG9mIHRoZSBzeXN0ZW0sDQo+ID4gICAJCXRoZSBicmlkZ2UgY2Fu IGJlIGRpc2Nvbm5lY3RlZCAod2hlbiBpdCBpcyBub3QgYmVpbmcgdXNlZA0KPiA+ICAgCQl1c2lu ZyB0aGUgYnJpZGdlX3N3aXRjaF9lbiBhdHRyaWJ1dGUuDQo+ID4gKw0KPiBsb29zZSB0aGlzIGV4 dHJhIGJsYW5rIGxpbmUuDQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvc3RhZ2luZy9paW8vRG9j dW1lbnRhdGlvbi9zeXNmcy1idXMtaWlvLWxpZ2h0DQo+IGIvZHJpdmVycy9zdGFnaW5nL2lpby9E b2N1bWVudGF0aW9uL3N5c2ZzLWJ1cy1paW8tbGlnaHQNCj4gPiBpbmRleCBlZGJmNDcwLi40Mzg1 YzcwIDEwMDY0NA0KPiA+IC0tLSBhL2RyaXZlcnMvc3RhZ2luZy9paW8vRG9jdW1lbnRhdGlvbi9z eXNmcy1idXMtaWlvLWxpZ2h0DQo+ID4gKysrIGIvZHJpdmVycy9zdGFnaW5nL2lpby9Eb2N1bWVu dGF0aW9uL3N5c2ZzLWJ1cy1paW8tbGlnaHQNCj4gPiBAQCAtNzYsMTAgKzc2LDEwIEBAIENvbnRh Y3Q6CWxpbnV4LWlpb0B2Z2VyLmtlcm5lbC5vcmcNCj4gPiAgIERlc2NyaXB0aW9uOg0KPiA+ICAg CQlUaGlzIHByb3BlcnR5IGdldHMvc2V0cyB0aGUgc2Vuc29ycyBBREMgYW5hbG9nIGludGVncmF0 aW9uDQo+IHRpbWUuDQo+ID4NCj4gPiAtV2hhdDoJCS9zeXMvYnVzL2lpby9kZXZpY2VzL2Rldmlj ZVtuXS9pbGx1bWluYW5jZTBfY2FsaWJzY2FsZQ0KPiA+ICtXaGF0OgkJL3N5cy9idXMvaWlvL2Rl dmljZXMvZGV2aWNlW25dL2x1eF90YWJsZQ0KPiA+ICAgS2VybmVsVmVyc2lvbjoJMi42LjM3DQo+ ID4gICBDb250YWN0OglsaW51eC1paW9Admdlci5rZXJuZWwub3JnDQo+ID4gICBEZXNjcmlwdGlv bjoNCj4gPiAtCQlIYXJkd2FyZSBvciBzb2Z0d2FyZSBhcHBsaWVkIGNhbGlicmF0aW9uIHNjYWxl IGZhY3RvciBhc3N1bWVkDQo+ID4gLQkJdG8gYWNjb3VudCBmb3IgYXR0ZW51YXRpb24gZHVlIHRv IGluZHVzdHJpYWwgZGVzaWduIChnbGFzcw0KPiA+IC0JCWZpbHRlcnMgb3IgYXBlcnR1cmUgaG9s ZXMpLg0KPiA+ICsJCVRoaXMgcHJvcGVydHkgZ2V0cy9zZXRzIHRoZSB0YWJsZSBvZiBjb2VmZmlj aWVudHMNCj4gPiArCQl1c2VkIGluIGNhbGN1bGF0aW5nIGlsbHVtaW5hbmNlIGluIGx1eC4NCj4g PiArDQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvc3RhZ2luZy9paW8vRG9jdW1lbnRhdGlvbi9z eXNmcy1idXMtaWlvLWxpZ2h0LXRzbDI1ODMNCj4gYi9kcml2ZXJzL3N0YWdpbmcvaWlvL0RvY3Vt ZW50YXRpb24vc3lzZnMtYnVzLWlpby1saWdodC10c2wyNTgzDQo+ID4gZGVsZXRlZCBmaWxlIG1v ZGUgMTAwNjQ0DQo+ID4gaW5kZXggNjYwNzgxZC4uMDAwMDAwMA0KPiA+IC0tLSBhL2RyaXZlcnMv c3RhZ2luZy9paW8vRG9jdW1lbnRhdGlvbi9zeXNmcy1idXMtaWlvLWxpZ2h0LXRzbDI1ODMNCj4g PiArKysgL2Rldi9udWxsDQo+ID4gQEAgLTEsMjAgKzAsMCBAQA0KPiA+IC1XaGF0OgkJL3N5cy9i dXMvaWlvL2RldmljZXMvZGV2aWNlW25dL2x1eF90YWJsZQ0KPiA+IC1LZXJuZWxWZXJzaW9uOgky LjYuMzcNCj4gPiAtQ29udGFjdDoJbGludXgtaWlvQHZnZXIua2VybmVsLm9yZw0KPiA+IC1EZXNj cmlwdGlvbjoNCj4gPiAtCQlUaGlzIHByb3BlcnR5IGdldHMvc2V0cyB0aGUgdGFibGUgb2YgY29l ZmZpY2llbnRzDQo+ID4gLQkJdXNlZCBpbiBjYWxjdWxhdGluZyBpbGx1bWluYW5jZSBpbiBsdXgu DQo+ID4gLQ0KPiA+IC1XaGF0OgkJL3N5cy9idXMvaWlvL2RldmljZXMvZGV2aWNlW25dL2lsbHVt aW5hbmNlMF9jYWxpYnJhdGUNCj4gPiAtS2VybmVsVmVyc2lvbjoJMi42LjM3DQo+ID4gLUNvbnRh Y3Q6CWxpbnV4LWlpb0B2Z2VyLmtlcm5lbC5vcmcNCj4gPiAtRGVzY3JpcHRpb246DQo+ID4gLQkJ VGhpcyBwcm9wZXJ0eSBjYXVzZXMgYW4gaW50ZXJuYWwgY2FsaWJyYXRpb24gb2YgdGhlIGFscyBn YWluIHRyaW0NCj4gPiAtCQl2YWx1ZSB3aGljaCBpcyBsYXRlciB1c2VkIGluIGNhbGN1bGF0aW5n IGlsbHVtaW5hbmNlIGluIGx1eC4NCj4gPiAtDQo+ID4gLVdoYXQ6DQo+IAkvc3lzL2J1cy9paW8v ZGV2aWNlcy9kZXZpY2Vbbl0vaWxsdW1pbmFuY2UwX2lucHV0X3RhcmdldA0KPiA+IC1LZXJuZWxW ZXJzaW9uOgkyLjYuMzcNCj4gPiAtQ29udGFjdDoJbGludXgtaWlvQHZnZXIua2VybmVsLm9yZw0K PiA+IC1EZXNjcmlwdGlvbjoNCj4gPiAtCQlUaGlzIHByb3BlcnR5IGlzIHRoZSBrbm93biBleHRl cm5hbGx5IGlsbHVtaW5hbmNlIChpbiBsdXgpLg0KPiA+IC0JCUl0IGlzIHVzZWQgaW4gdGhlIHBy b2Nlc3Mgb2YgY2FsaWJyYXRpbmcgdGhlIGRldmljZSBhY2N1cmFjeS4NCj4gPiBkaWZmIC0tZ2l0 IGEvZHJpdmVycy9zdGFnaW5nL2lpby9saWdodC9LY29uZmlnIGIvZHJpdmVycy9zdGFnaW5nL2lp by9saWdodC9LY29uZmlnDQo+ID4gaW5kZXggZTdlOTE1OS4uOTc2Zjc5MCAxMDA2NDQNCj4gPiAt LS0gYS9kcml2ZXJzL3N0YWdpbmcvaWlvL2xpZ2h0L0tjb25maWcNCj4gPiArKysgYi9kcml2ZXJz L3N0YWdpbmcvaWlvL2xpZ2h0L0tjb25maWcNCj4gPiBAQCAtMzEsNCArMzEsMTIgQEAgY29uZmln IFRTTDI1ODMNCj4gPiAgIAkgUHJvdmlkZXMgc3VwcG9ydCBmb3IgdGhlIFRBT1MgdHNsMjU4MCwg dHNsMjU4MSBhbmQgdHNsMjU4MyBkZXZpY2VzLg0KPiA+ICAgCSBBY2Nlc3MgQUxTIGRhdGEgdmlh IGlpbywgc3lzZnMuDQo+ID4NCj4gPiArY29uZmlnIFRTTDJ4N3gNCj4gPiArCXRyaXN0YXRlICJU QU9TIFRTTC9UTUQyeDcxIGFuZCBUU0wvVE1EMng3MiBGYW1pbHkgb2YgbGlnaHQgYW5kDQo+IHBy b3hpbWl0eSBzZW5zb3JzIg0KPiA+ICsJZGVwZW5kcyBvbiBJMkMNCj4gPiArCWhlbHANCj4gPiAr CSBTdXBwb3J0IGZvcjogdHNsMjU3MSwgdHNsMjY3MSwgdG1kMjY3MSwgdHNsMjc3MSwgdG1kMjc3 MSwgdHNsMjU3MiwNCj4gdHNsMjY3MiwNCj4gPiArCSB0bWQyNjcyLCB0c2wyNzcyLCB0bWQyNzcy IGRldmljZXMuDQo+ID4gKwkgUHJvdmlkZXMgaWlvX2V2ZW50cyBhbmQgZGlyZWN0IGFjY2VzcyB2 aWEgc3lzZnMuDQo+ID4gKw0KPiA+ICAgZW5kbWVudQ0KPiA+IGRpZmYgLS1naXQgYS9kcml2ZXJz L3N0YWdpbmcvaWlvL2xpZ2h0L01ha2VmaWxlDQo+IGIvZHJpdmVycy9zdGFnaW5nL2lpby9saWdo dC9NYWtlZmlsZQ0KPiA+IGluZGV4IDMwMTFmYmYuLjBiOGZiMjIgMTAwNjQ0DQo+ID4gLS0tIGEv ZHJpdmVycy9zdGFnaW5nL2lpby9saWdodC9NYWtlZmlsZQ0KPiA+ICsrKyBiL2RyaXZlcnMvc3Rh Z2luZy9paW8vbGlnaHQvTWFrZWZpbGUNCj4gPiBAQCAtNSwzICs1LDUgQEANCj4gPiAgIG9iai0k KENPTkZJR19TRU5TT1JTX1RTTDI1NjMpCSs9IHRzbDI1NjMubw0KPiA+ICAgb2JqLSQoQ09ORklH X1NFTlNPUlNfSVNMMjkwMTgpCSs9IGlzbDI5MDE4Lm8NCj4gPiAgIG9iai0kKENPTkZJR19UU0wy NTgzKQkrPSB0c2wyNTgzLm8NCj4gPiArb2JqLSQoQ09ORklHX1RTTDJ4N3gpCSs9IHRzbDJ4N3hf Y29yZS5vDQo+ID4gK29iai0kKENPTkZJR19UQ1MzeDd4KQkrPSB0Y3MzeDd4X2NvcmUubw0KPiBS ZWFsbHk/DQpPb3BzDQoNCj4gPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9zdGFnaW5nL2lpby9saWdo dC90c2wyeDd4LmgNCj4gYi9kcml2ZXJzL3N0YWdpbmcvaWlvL2xpZ2h0L3RzbDJ4N3guaA0KPiA+ IG5ldyBmaWxlIG1vZGUgMTAwNjQ0DQo+ID4gaW5kZXggMDAwMDAwMC4uZmU5ZTg1Mw0KPiA+IC0t LSAvZGV2L251bGwNCj4gPiArKysgYi9kcml2ZXJzL3N0YWdpbmcvaWlvL2xpZ2h0L3RzbDJ4N3gu aA0KPiA+IEBAIC0wLDAgKzEsOTkgQEANCj4gPiArLyoNCj4gPiArICogRGV2aWNlIGRyaXZlciBm b3IgbW9uaXRvcmluZyBhbWJpZW50IGxpZ2h0IGludGVuc2l0eSAobHV4KQ0KPiA+ICsgKiBhbmQg cHJveGltaXR5IChwcm94KSB3aXRoaW4gdGhlIFRBT1MgVFNMMlg3WCBmYW1pbHkgb2YgZGV2aWNl cy4NCj4gPiArICoNCj4gPiArICogQ29weXJpZ2h0IChjKSAyMDEyLCBUQU9TIENvcnBvcmF0aW9u Lg0KPiA+ICsgKg0KPiA+ICsgKiBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsgeW91IGNh biByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeQ0KPiA+ICsgKiBpdCB1bmRlciB0aGUgdGVy bXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieQ0KPiA+ ICsgKiB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uOyBlaXRoZXIgdmVyc2lvbiAyIG9mIHRo ZSBMaWNlbnNlLCBvcg0KPiA+ICsgKiAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9u Lg0KPiA+ICsgKg0KPiA+ICsgKiBUaGlzIHByb2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhv cGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwgYnV0IFdJVEhPVVQNCj4gPiArICogQU5ZIFdBUlJB TlRZOyB3aXRob3V0IGV2ZW4gdGhlIGltcGxpZWQgd2FycmFudHkgb2YgTUVSQ0hBTlRBQklMSVRZ DQo+IG9yDQo+ID4gKyAqIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRo ZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQ0KPiBmb3INCj4gPiArICogbW9yZSBkZXRhaWxz Lg0KPiA+ICsgKg0KPiA+ICsgKiBZb3Ugc2hvdWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRo ZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhbG9uZw0KPiA+ICsgKiB3aXRoIHRoaXMgcHJv Z3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBJbmMu LA0KPiA+ICsgKiA1MSBGcmFua2xpbiBTdHJlZXQsIEZpZnRoIEZsb29yLCBCb3N0b24sIE1BCTAy MTEwLTEzMDEsIFVTQS4NCj4gPiArICovDQo+ID4gKw0KPiA+ICsjaWZuZGVmIF9fVFNMMlg3WF9I DQo+ID4gKyNkZWZpbmUgX19UU0wyWDdYX0gNCj4gPiArI2luY2x1ZGU8bGludXgvcG0uaD4NCj4g PiArDQo+ID4gKy8qIE1heCBudW1iZXIgb2Ygc2VnbWVudHMgYWxsb3dhYmxlIGluIExVWCB0YWJs ZSAqLw0KPiA+ICsjZGVmaW5lIFRTTDJYN1hfTUFYX0xVWF9UQUJMRV9TSVpFCQk5DQo+ID4gKyNk ZWZpbmUgTUFYX0RFRkFVTFRfVEFCTEVfQllURVMgKHNpemVvZihpbnQpICoNCj4gVFNMMlg3WF9N QVhfTFVYX1RBQkxFX1NJWkUpDQo+ID4gKw0KPiA+ICtzdHJ1Y3QgaWlvX2RldjsNCj4gPiArDQo+ ID4gK3N0cnVjdCB0c2wyeDd4X2x1eCB7DQo+ID4gKwl1bnNpZ25lZCBpbnQgcmF0aW87DQo+ID4g Kwl1bnNpZ25lZCBpbnQgY2gwOw0KPiA+ICsJdW5zaWduZWQgaW50IGNoMTsNCj4gPiArfTsNCj4g PiArDQo+ID4gKy8qKg0KPiA+ICsgKiBzdHJ1Y3QgdHNsMng3eF9kZWZhdWx0X3NldHRpbmdzIC0g cG93ZXIgb24gZGVmYXVsdHMgdW5sZXNzDQo+ID4gKyAqICAgICAgICAgICAgICAgICAgICAgICAg ICAgICAgICAgICBvdmVycmlkZGVuIGJ5IHBsYXRmb3JtIGRhdGEuDQo+ID4gKyAqICBAYWxzX3Rp bWU6ICAgICAgICAgICAgICBBTFMgSW50ZWdyYXRpb24gdGltZSAtIG11bHRpcGxlIG9mIDUwbVMN Cj4gPiArICogIEBhbHNfZ2FpbjogICAgICAgICAgICAgIEluZGV4IGludG8gdGhlIEFMUyBnYWlu IHRhYmxlLg0KPiA+ICsgKiAgQHByeF90aW1lOiAgICAgICAgICAgICAgNS4ybXMgcHJveCBpbnRl Z3JhdGlvbiB0aW1lIC0NCj4gPiArICogICAgICAgICAgICAgICAgICAgICAgICAgIGRlYyBpbiAy LjdtcyBwZXJpb2RzDQo+ID4gKyAqICBAd2FpdF90aW1lOiAgICAgICAgICAgICBUaW1lIGJldHdl ZW4gUFJYIGFuZCBBTFMgY3ljbGVzDQo+ID4gKyAqICAgICAgICAgICAgICAgICAgICAgICAgICBp biAyLjcgcGVyaW9kcw0KPiA+ICsgKiAgQHByb3hfY29uZmlnOiAgICAgICAgICAgUHJveCBjb25m aWd1cmF0aW9uIGZpbHRlcnMuDQo+ID4gKyAqICBAYWxzX2dhaW5fdHJpbTogICAgICAgICBkZWZh dWx0IGdhaW4gdHJpbSB0byBhY2NvdW50IGZvcg0KPiA+ICsgKiAgICAgICAgICAgICAgICAgICAg ICAgICAgYXBlcnR1cmUgZWZmZWN0cy4NCj4gPiArICogIEBhbHNfY2FsX3RhcmdldDogICAgICAg IEtub3duIGV4dGVybmFsIEFMUyByZWFkaW5nIGZvcg0KPiA+ICsgKiAgICAgICAgICAgICAgICAg ICAgICAgICAgY2FsaWJyYXRpb24uDQo+ID4gKyAqICBAYWxzX3RocmVzaF9sb3c6ICAgICAgICBD SDAgJ2xvdycgY291bnQgdG8gdHJpZ2dlciBpbnRlcnJ1cHQuDQo+ID4gKyAqICBAYWxzX3RocmVz aF9oaWdoOiAgICAgICBDSDAgJ2hpZ2gnIGNvdW50IHRvIHRyaWdnZXIgaW50ZXJydXB0Lg0KPiA+ ICsgKiAgQHBlcnNpc3RlbmNlOiAgICAgICAgICAgSC9XIEZpbHRlcnMsIE51bWJlciBvZiAnb3V0 IG9mIGxpbWl0cycNCj4gPiArICogICAgICAgICAgICAgICAgICAgICAgICAgIEFEQyByZWFkaW5n cyBQUlgvQUxTLg0KPiA+ICsgKiAgQGludGVycnVwdHNfZW46ICAgICAgICAgRW5hYmxlL0Rpc2Fi bGUgLSAweDAwID0gbm9uZSwgMHgxMCA9IGFscywNCj4gPiArICogICAgICAgICAgICAgICAgICAg ICAgICAgICAgICAgICAgICAgICAgICAgMHgyMCA9IHByeCwgIDB4MzAgPSBidGgNCj4gPiArICog IEBwcm94X3RocmVzX2xvdzogICAgICAgIExvdyB0aHJlc2hvbGQgcHJveGltaXR5IGRldGVjdGlv bi4NCj4gPiArICogIEBwcm94X3RocmVzX2hpZ2g6ICAgICAgIEhpZ2ggdGhyZXNob2xkIHByb3hp bWl0eSBkZXRlY3Rpb24NCj4gPiArICogIEBwcm94X21heF9zYW1wbGVzX2NhbDogIFVzZWQgZm9y IHByb3ggY2FsLg0KPiA+ICsgKiAgQHByb3hfcHVsc2VfY291bnQ6ICAgICAgTnVtYmVyIGlmIHBy b3hpbWl0eSBlbWl0dGVyIHB1bHNlcw0KPiByZW9yZGVyIHRoZSBkb2NzIHRvIG1hdGNoIHRoZSBz dHJ1Y3R1cmUuICBQaWNrIHdoaWNoIGV2ZXIgb3JkZXIgbWFrZXMNCj4gbW9zdCBzZW5zZQ0KPiAo ZG9uJ3Qgd29ycnkgYWJvdXQgd2FzdGluZyBhIGJ5dGUgb3IgdHdvLCBjbGFyaXR5IGlzIG1vcmUg aW1wb3J0YW50IG9uDQo+IHN0cnVjdHVyZXMNCj4gbGlrZSB0aGlzISkNCk9LDQoNCj4gPiArICov DQo+ID4gK3N0cnVjdCB0c2wyeDd4X3NldHRpbmdzIHsNCj4gPiArCWludCBhbHNfdGltZTsNCj4g PiArCWludCBhbHNfZ2FpbjsNCj4gPiArCWludCBhbHNfZ2Fpbl90cmltOw0KPiA+ICsJaW50IHdh aXRfdGltZTsNCj4gPiArCWludCBwcnhfdGltZTsNCj4gPiArCWludCBwcm94X2dhaW47DQo+ID4g KwlpbnQgcHJveF9jb25maWc7DQo+ID4gKwlpbnQgYWxzX2NhbF90YXJnZXQ7DQo+ID4gKwl1OCAg aW50ZXJydXB0c19lbjsNCj4gPiArCXU4ICBwZXJzaXN0ZW5jZTsNCj4gPiArCWludCBhbHNfdGhy ZXNoX2xvdzsNCj4gPiArCWludCBhbHNfdGhyZXNoX2hpZ2g7DQo+ID4gKwlpbnQgcHJveF90aHJl c19sb3c7DQo+ID4gKwlpbnQgcHJveF90aHJlc19oaWdoOw0KPiA+ICsJaW50IHByb3hfcHVsc2Vf Y291bnQ7DQo+ID4gKwlpbnQgcHJveF9tYXhfc2FtcGxlc19jYWw7DQo+ID4gK307DQo+ID4gKw0K PiA+ICsvKioNCj4gPiArICogc3RydWN0IHRzbDJYN1hfcGxhdGZvcm1fZGF0YSAtIFBsYXRmb3Jt IGNhbGxiYWNrLCBnbGFzcyBhbmQgZGVmYXVsdHMNCj4gPiArICogQHBsYXRmb3JtX3Bvd2VyOgkJ CQlTdXNwZW5kL3Jlc3VtZQ0KPiBwbGF0Zm9ybSBjYWxsYmFjaw0KPiA+ICsgKiBAcG93ZXJfb246 CQkJCQlQb3dlciBvbiBjYWxsYmFjaw0KPiA+ICsgKiBAcG93ZXJfb2ZmOgkJCQkJUG93ZXIgb2Zm IGNhbGxiYWNrDQo+ID4gKyAqIEBwbGF0Zm9ybV9sdXhfdGFibGU6CQkJRGV2aWNlIHNwZWNpZmlj IGdsYXNzDQo+IGNvZWZmaWNlbnRzDQo+ID4gKyAqIEBwbGF0Zm9ybV9kZWZhdWx0X3NldHRpbmdz OglEZXZpY2Ugc3BlY2lmaWMgcG93ZXIgb24gZGVmYXVsdHMNCj4gPiArICogUGxhdGZvcm0gUE0g ZnVuY3Rpb25zLg0KPiA+ICsgKi8NCj4gPiArc3RydWN0IHRzbDJYN1hfcGxhdGZvcm1fZGF0YSB7 DQo+ID4gKwlpbnQgKCpwbGF0Zm9ybV9wb3dlcikoc3RydWN0IGRldmljZSAqZGV2LCBwbV9tZXNz YWdlX3QpOw0KPiA+ICsJaW50ICgqcG93ZXJfb24pICAgICAgKHN0cnVjdCBpaW9fZGV2ICppbmRp b19kZXYpOw0KPiA+ICsJaW50ICgqcG93ZXJfb2ZmKSAgICAgKHN0cnVjdCBpMmNfY2xpZW50ICpk ZXYpOw0KPiA+ICsJc3RydWN0IHRzbDJ4N3hfbHV4IHBsYXRmb3JtX2x1eF90YWJsZVtUU0wyWDdY X01BWF9MVVhfVEFCTEVfU0laRV07DQo+ID4gKwlzdHJ1Y3QgdHNsMng3eF9zZXR0aW5ncyAqcGxh dGZvcm1fZGVmYXVsdF9zZXR0aW5nczsNCj4gPiArfTsNCj4gPiArDQo+ID4gKyNlbmRpZiAvKiBf X1RTTDJYN1hfSCAqLw0KPiA+IGRpZmYgLS1naXQgYS9kcml2ZXJzL3N0YWdpbmcvaWlvL2xpZ2h0 L3RzbDJ4N3hfY29yZS5jDQo+IGIvZHJpdmVycy9zdGFnaW5nL2lpby9saWdodC90c2wyeDd4X2Nv cmUuYw0KPiA+IG5ldyBmaWxlIG1vZGUgMTAwNjQ0DQo+ID4gaW5kZXggMDAwMDAwMC4uMjY3ZmFh Yg0KPiA+IC0tLSAvZGV2L251bGwNCj4gPiArKysgYi9kcml2ZXJzL3N0YWdpbmcvaWlvL2xpZ2h0 L3RzbDJ4N3hfY29yZS5jDQo+ID4gQEAgLTAsMCArMSwxODMwIEBADQo+ID4gKy8qDQo+ID4gKyAq IERldmljZSBkcml2ZXIgZm9yIG1vbml0b3JpbmcgYW1iaWVudCBsaWdodCBpbnRlbnNpdHkgaW4g KGx1eCkNCj4gPiArICogYW5kIHByb3hpbWl0eSBkZXRlY3Rpb24gKHByb3gpIHdpdGhpbiB0aGUg VEFPUyBUU0wyWDdYIGZhbWlseSBvZiBkZXZpY2VzLg0KPiA+ICsgKg0KPiA+ICsgKiBDb3B5cmln aHQgKGMpIDIwMTIsIFRBT1MgQ29ycG9yYXRpb24uDQo+ID4gKyAqDQo+ID4gKyAqIFRoaXMgcHJv Z3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9k aWZ5DQo+ID4gKyAqIGl0IHVuZGVyIHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGlj IExpY2Vuc2UgYXMgcHVibGlzaGVkIGJ5DQo+ID4gKyAqIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5k YXRpb247IGVpdGhlciB2ZXJzaW9uIDIgb2YgdGhlIExpY2Vuc2UsIG9yDQo+ID4gKyAqIChhdCB5 b3VyIG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uDQo+ID4gKyAqDQo+ID4gKyAqIFRoaXMgcHJv Z3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLCBi dXQgV0lUSE9VVA0KPiA+ICsgKiBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGll ZCB3YXJyYW50eSBvZiBNRVJDSEFOVEFCSUxJVFkNCj4gb3INCj4gPiArICogRklUTkVTUyBGT1Ig QSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNl DQo+IGZvcg0KPiA+ICsgKiBtb3JlIGRldGFpbHMuDQo+ID4gKyAqDQo+ID4gKyAqIFlvdSBzaG91 bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNl IGFsb25nDQo+ID4gKyAqIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBG cmVlIFNvZnR3YXJlIEZvdW5kYXRpb24sIEluYy4sDQo+ID4gKyAqIDUxIEZyYW5rbGluIFN0cmVl dCwgRmlmdGggRmxvb3IsIEJvc3RvbiwgTUEgICAgICAgIDAyMTEwLTEzMDEsIFVTQS4NCj4gPiAr ICovDQo+ID4gKw0KPiA+ICsjaW5jbHVkZTxsaW51eC9rZXJuZWwuaD4NCj4gPiArI2luY2x1ZGU8 bGludXgvaTJjLmg+DQo+ID4gKyNpbmNsdWRlPGxpbnV4L2Vycm5vLmg+DQo+ID4gKyNpbmNsdWRl PGxpbnV4L2RlbGF5Lmg+DQo+ID4gKyNpbmNsdWRlPGxpbnV4L211dGV4Lmg+DQo+ID4gKyNpbmNs dWRlPGxpbnV4L2ludGVycnVwdC5oPg0KPiA+ICsjaW5jbHVkZTxsaW51eC9zbGFiLmg+DQo+ID4g KyNpbmNsdWRlPGxpbnV4L21vZHVsZS5oPg0KPiA+ICsjaW5jbHVkZTxsaW51eC92ZXJzaW9uLmg+ DQo+ID4gKyNpbmNsdWRlICJ0c2wyeDd4LmgiDQo+ID4gKyNpbmNsdWRlICIuLi9ldmVudHMuaCIN Cj4gPiArI2luY2x1ZGUgIi4uL2lpby5oIg0KPiA+ICsjaW5jbHVkZSAiLi4vc3lzZnMuaCINCj4g PiArDQo+ID4gKy8qIENhbCBkZWZzKi8NCj4gPiArI2RlZmluZSBQUk9YX1NUQVRfQ0FMICAgICAg ICAwDQo+ID4gKyNkZWZpbmUgUFJPWF9TVEFUX1NBTVAgICAgICAgMQ0KPiA+ICsjZGVmaW5lIE1B WF9TQU1QTEVTX0NBTCAgICAgIDIwMA0KPiA+ICsNCj4gPiArLyogVFNMMlg3WCBEZXZpY2UgSUQg Ki8NCj4gPiArI2RlZmluZSBUUklUT05fSUQgICAgMHgwMA0KPiA+ICsjZGVmaW5lIFNXT1JERklT SF9JRCAweDMwDQo+ID4gKyNkZWZpbmUgSEFMSUJVVF9JRCAgIDB4MjANCj4gaG1tbS4uIGZpc2gg OykNCkFuZCBDaGlwcyAtIHV1bSA7XikNCg0KPiA+ICsNCj4gPiArLyogTHV4IGNhbGN1bGF0aW9u IGNvbnN0YW50cyAqLw0KPiA+ICsjZGVmaW5lIFRTTDJYN1hfTFVYX0NBTENfT1ZFUl9GTE9XICAg ICA2NTUzNQ0KPiA+ICsNCj4gPiArLyogVEFPUyBSZWdpc3RlciBkZWZpbml0aW9ucyAtIG5vdGU6 DQo+ID4gKyAqIGRlcGVuZGluZyBvbiBkZXZpY2UsIHNvbWUgb2YgdGhlc2UgcmVnaXN0ZXIgYXJl IG5vdCB1c2VkIGFuZCB0aGUNCj4gPiArICogcmVnaXN0ZXIgYWRkcmVzcyBpcyBiZW5pZ24uDQo+ ID4gKyAqLw0KPiA+ICsvKiAyWDdYIHJlZ2lzdGVyIG9mZnNldHMgKi8NCj4gPiArI2RlZmluZSBU U0wyWDdYX01BWF9DT05GSUdfUkVHICAgICAgICAgMTYNCj4gPiArDQo+ID4gKy8qIERldmljZSBS ZWdpc3RlcnMgYW5kIE1hc2tzICovDQo+ID4gKyNkZWZpbmUgVFNMMlg3WF9DTlRSTCAgICAgICAg ICAgICAgICAgIDB4MDANCj4gPiArI2RlZmluZSBUU0wyWDdYX0FMU19USU1FICAgICAgICAgICAg ICAgMFgwMQ0KPiA+ICsjZGVmaW5lIFRTTDJYN1hfUFJYX1RJTUUgICAgICAgICAgICAgICAweDAy DQo+ID4gKyNkZWZpbmUgVFNMMlg3WF9XQUlUX1RJTUUgICAgICAgICAgICAgIDB4MDMNCj4gPiAr I2RlZmluZSBUU0wyWDdYX0FMU19NSU5USFJFU0hMTyAgICAgICAgMFgwNA0KPiA+ICsjZGVmaW5l IFRTTDJYN1hfQUxTX01JTlRIUkVTSEhJICAgICAgICAwWDA1DQo+ID4gKyNkZWZpbmUgVFNMMlg3 WF9BTFNfTUFYVEhSRVNITE8gICAgICAgIDBYMDYNCj4gPiArI2RlZmluZSBUU0wyWDdYX0FMU19N QVhUSFJFU0hISSAgICAgICAgMFgwNw0KPiA+ICsjZGVmaW5lIFRTTDJYN1hfUFJYX01JTlRIUkVT SExPICAgICAgICAwWDA4DQo+ID4gKyNkZWZpbmUgVFNMMlg3WF9QUlhfTUlOVEhSRVNISEkgICAg ICAgIDBYMDkNCj4gPiArI2RlZmluZSBUU0wyWDdYX1BSWF9NQVhUSFJFU0hMTyAgICAgICAgMFgw QQ0KPiA+ICsjZGVmaW5lIFRTTDJYN1hfUFJYX01BWFRIUkVTSEhJICAgICAgICAwWDBCDQo+ID4g KyNkZWZpbmUgVFNMMlg3WF9QRVJTSVNURU5DRSAgICAgICAgICAgIDB4MEMNCj4gPiArI2RlZmlu ZSBUU0wyWDdYX1BSWF9DT05GSUcgICAgICAgICAgICAgMHgwRA0KPiA+ICsjZGVmaW5lIFRTTDJY N1hfUFJYX0NPVU5UICAgICAgICAgICAgICAweDBFDQo+ID4gKyNkZWZpbmUgVFNMMlg3WF9HQUlO ICAgICAgICAgICAgICAgICAgIDB4MEYNCj4gPiArI2RlZmluZSBUU0wyWDdYX05PVFVTRUQgICAg ICAgICAgICAgICAgMHgxMA0KPiA+ICsjZGVmaW5lIFRTTDJYN1hfUkVWSUQgICAgICAgICAgICAg ICAgICAweDExDQo+ID4gKyNkZWZpbmUgVFNMMlg3WF9DSElQSUQgICAgICAgICAgICAgICAgIDB4 MTINCj4gPiArI2RlZmluZSBUU0wyWDdYX1NUQVRVUyAgICAgICAgICAgICAgICAgMHgxMw0KPiA+ ICsjZGVmaW5lIFRTTDJYN1hfQUxTX0NIQU4wTE8gICAgICAgICAgICAweDE0DQo+ID4gKyNkZWZp bmUgVFNMMlg3WF9BTFNfQ0hBTjBISSAgICAgICAgICAgIDB4MTUNCj4gPiArI2RlZmluZSBUU0wy WDdYX0FMU19DSEFOMUxPICAgICAgICAgICAgMHgxNg0KPiA+ICsjZGVmaW5lIFRTTDJYN1hfQUxT X0NIQU4xSEkgICAgICAgICAgICAweDE3DQo+ID4gKyNkZWZpbmUgVFNMMlg3WF9QUlhfTE8gICAg ICAgICAgICAgICAgIDB4MTgNCj4gPiArI2RlZmluZSBUU0wyWDdYX1BSWF9ISSAgICAgICAgICAg ICAgICAgMHgxOQ0KPiA+ICsNCj4gPiArLyogdHNsMlg3WCBjbWQgcmVnIG1hc2tzICovDQo+ID4g KyNkZWZpbmUgVFNMMlg3WF9DTURfUkVHICAgICAgICAgICAgICAgIDB4ODANCj4gPiArI2RlZmlu ZSBUU0wyWDdYX0NNRF9TUExfRk4gICAgICAgICAgICAgMHg2MA0KPiA+ICsNCj4gPiArI2RlZmlu ZSBUU0wyWDdYX0NNRF9QUk9YX0lOVF9DTFIgICAgICAgMFgwNQ0KPiA+ICsjZGVmaW5lIFRTTDJY N1hfQ01EX0FMU19JTlRfQ0xSICAgICAgICAweDA2DQo+ID4gKyNkZWZpbmUgVFNMMlg3WF9DTURf UFJPWEFMU19JTlRfQ0xSICAgIDBYMDcNCj4gPiArDQo+ID4gKy8qIHRzbDJYN1ggY250cmwgcmVn IG1hc2tzICovDQo+ID4gKyNkZWZpbmUgVFNMMlg3WF9DTlRMX0FEQ19FTkJMICAgICAgICAgIDB4 MDINCj4gPiArI2RlZmluZSBUU0wyWDdYX0NOVExfUFdSX09OICAgICAgICAgICAgMHgwMQ0KPiA+ ICsNCj4gPiArLyogdHNsMlg3WCBzdGF0dXMgcmVnIG1hc2tzICovDQo+ID4gKyNkZWZpbmUgVFNM Mlg3WF9TVEFfQURDX1ZBTElEICAgICAgICAgIDB4MDENCj4gPiArI2RlZmluZSBUU0wyWDdYX1NU QV9QUlhfVkFMSUQgICAgICAgICAgMHgwMg0KPiA+ICsjZGVmaW5lIFRTTDJYN1hfU1RBX0FEQ19Q UlhfVkFMSUQgICAgICAweDAzDQo+IFdvdWxkIHByZWZlciBhYm92ZSBkZWZpbmVkIGFzIFRTTDJY N1hfU1RBX0FEQ19WQUxJRCB8DQo+IFRTTDJYN1hfU1RBX1BSWF9WQUxJRA0KPiAobWFrZXMgaXQg b2J2aW91cyBhdCBhIGdsYW5jZSB3aGF0IGlzIGdvaW5nIG9uKS4NCj4gPiArI2RlZmluZSBUU0wy WDdYX1NUQV9BTFNfSU5UUiAgICAgICAgICAgMHgxMA0KPiA+ICsjZGVmaW5lIFRTTDJYN1hfU1RB X0FEQ19JTlRSICAgICAgICAgICAweDEwDQo+IGFib3ZlIHVudXNlZCAoYW5kIHNlZW1zIHRvIGJl IHJlcGVhdGVkKSA/IChyZXBlYXRlZCB2YWx1ZSB3YXMgc3VzcGljaW91cyApDQo+ID4gKyNkZWZp bmUgVFNMMlg3WF9TVEFfUFJYX0lOVFIgICAgICAgICAgIDB4MjANCj4gPiArDQo+ID4gKyNkZWZp bmUgVFNMMlg3WF9TVEFfQURDX0lOVFIgICAgICAgICAgIDB4MTANCj4gPiArDQo+ID4gKy8qIHRz bDJYN1ggY250cmwgcmVnIG1hc2tzICovDQo+ID4gKyNkZWZpbmUgVFNMMlg3WF9DTlRMX1JFR19D TEVBUiAgICAgICAgIDB4MDANCj4gPiArI2RlZmluZSBUU0wyWDdYX0NOVExfUFJPWF9JTlRfRU5C TCAgICAgMFgyMA0KPiA+ICsjZGVmaW5lIFRTTDJYN1hfQ05UTF9BTFNfSU5UX0VOQkwgICAgICAw WDEwDQo+ID4gKyNkZWZpbmUgVFNMMlg3WF9DTlRMX1dBSVRfVE1SX0VOQkwgICAgIDBYMDgNCj4g PiArI2RlZmluZSBUU0wyWDdYX0NOVExfUFJPWF9ERVRfRU5CTCAgICAgMFgwNA0KPiA+ICsjZGVm aW5lIFRTTDJYN1hfQ05UTF9QV1JPTiAgICAgICAgICAgICAweDAxDQo+ID4gKyNkZWZpbmUgVFNM Mlg3WF9DTlRMX0FMU1BPTl9FTkJMICAgICAgIDB4MDMNCj4gPiArI2RlZmluZSBUU0wyWDdYX0NO VExfSU5UQUxTUE9OX0VOQkwgICAgMHgxMw0KPiA+ICsjZGVmaW5lIFRTTDJYN1hfQ05UTF9QUk9Y UE9OX0VOQkwgICAgICAweDBGDQo+ID4gKyNkZWZpbmUgVFNMMlg3WF9DTlRMX0lOVFBST1hQT05f RU5CTCAgIDB4MkYNCj4gPiArDQo+ID4gKy8qUHJveCBkaW9kZSB0byB1c2UgKi8NCj4gPiArI2Rl ZmluZSBUU0wyWDdYX0RJT0RFMCAgICAgICAgICAgICAgICAgMHgxMA0KPiA+ICsjZGVmaW5lIFRT TDJYN1hfRElPREUxICAgICAgICAgICAgICAgICAweDIwDQo+ID4gKyNkZWZpbmUgVFNMMlg3WF9E SU9ERV9CT1RIICAgICAgICAgICAgIDB4MzANCj4gPiArDQo+ID4gKy8qIExFRCBQb3dlciAqLw0K PiA+ICsjZGVmaW5lIFRTTDJYN1hfbUExMDAgICAgICAgICAgICAgICAgICAweDAwDQo+ID4gKyNk ZWZpbmUgVFNMMlg3WF9tQTUwICAgICAgICAgICAgICAgICAgIDB4NDANCj4gPiArI2RlZmluZSBU U0wyWDdYX21BMjUgICAgICAgICAgICAgICAgICAgMHg4MA0KPiA+ICsjZGVmaW5lIFRTTDJYN1hf bUExMyAgICAgICAgICAgICAgICAgICAweEQwDQo+ID4gKw0KPiA+ICsvKkNvbW1vbiBkZXZpY2Ug SUlPIEV2ZW50TWFzayAqLw0KPiA+ICsjZGVmaW5lIFRTTDJYN1hfRVZFTlRfTUFTSyBcDQo+ID4g KwkJKElJT19FVl9CSVQoSUlPX0VWX1RZUEVfVEhSRVNILCBJSU9fRVZfRElSX1JJU0lORykgfCBc DQo+ID4gKwkJSUlPX0VWX0JJVChJSU9fRVZfVFlQRV9USFJFU0gsIElJT19FVl9ESVJfRkFMTElO RykpLA0KPiA+ICsNCj4gPiArLyogVEFPUyB0eHgyeDd4IERldmljZSBmYW1pbHkgbWVtYmVycyAq Lw0KPiA+ICtlbnVtIHsNCj4gPiArCXRzbDI1NzEsDQo+ID4gKwl0c2wyNjcxLA0KPiA+ICsJdG1k MjY3MSwNCj4gPiArCXRzbDI3NzEsDQo+ID4gKwl0bWQyNzcxLA0KPiA+ICsJdHNsMjU3MiwNCj4g PiArCXRzbDI2NzIsDQo+ID4gKwl0bWQyNjcyLA0KPiA+ICsJdHNsMjc3MiwNCj4gPiArCXRtZDI3 NzINCj4gPiArfTsNCj4gPiArDQo+ID4gK2VudW0gew0KPiA+ICsJVFNMMlg3WF9DSElQX1VOS05P V04gPSAwLA0KPiA+ICsJVFNMMlg3WF9DSElQX1dPUktJTkcgPSAxLA0KPiA+ICsJVFNMMlg3WF9D SElQX1NVU1BFTkRFRCA9IDINCj4gPiArfTsNCj4gPiArDQo+ID4gKy8qIFBlci1kZXZpY2UgZGF0 YSAqLw0KPiA+ICtzdHJ1Y3QgdHNsMng3eF9hbHNfaW5mbyB7DQo+ID4gKwl1MTYgYWxzX2NoMDsN Cj4gPiArCXUxNiBhbHNfY2gxOw0KPiA+ICsJdTE2IGx1eDsNCj4gPiArfTsNCj4gPiArDQo+ID4g K3N0cnVjdCBwcm94X3N0YXQgew0KPiA+ICsJdTE2IG1pbjsNCj4gPiArCXUxNiBtYXg7DQo+ID4g Kwl1MTYgbWVhbjsNCj4gPiArCXVuc2lnbmVkIGxvbmcgc3RkZGV2Ow0KPiA+ICt9Ow0KPiA+ICsN Cj4gPiArc3RydWN0IHRzbDJ4N3hfY2hpcF9pbmZvIHsNCj4gPiArCWludCBjaGFuX3RhYmxlX2Vs ZW1lbnRzOw0KPiA+ICsJc3RydWN0IGlpb19jaGFuX3NwZWMJCWNoYW5uZWxbOV07DQo+ID4gKwlj b25zdCBzdHJ1Y3QgaWlvX2luZm8JCSppbmZvOw0KPiA+ICt9Ow0KPiA+ICsNCj4gPiArc3RydWN0 IHRzbDJYN1hfY2hpcCB7DQo+ID4gKwlrZXJuZWxfdWxvbmdfdCBpZDsNCj4gPiArCXN0cnVjdCBt dXRleCBwcm94X211dGV4Ow0KPiA+ICsJc3RydWN0IG11dGV4IGFsc19tdXRleDsNCj4gPiArCXN0 cnVjdCBpMmNfY2xpZW50ICpjbGllbnQ7DQo+ID4gKwl1MTYgcHJveF9kYXRhOw0KPiA+ICsJc3Ry dWN0IHRzbDJ4N3hfYWxzX2luZm8gYWxzX2N1cl9pbmZvOw0KPiA+ICsJc3RydWN0IHRzbDJ4N3hf c2V0dGluZ3MgdHNsMng3eF9zZXR0aW5nczsNCj4gPiArCXN0cnVjdCB0c2wyWDdYX3BsYXRmb3Jt X2RhdGEgKnBkYXRhOw0KPiA+ICsJaW50IGFsc190aW1lX3NjYWxlOw0KPiA+ICsJaW50IGFsc19z YXR1cmF0aW9uOw0KPiA+ICsJaW50IHRzbDJ4N3hfY2hpcF9zdGF0dXM7DQo+ID4gKwl1OCB0c2wy eDd4X2NvbmZpZ1tUU0wyWDdYX01BWF9DT05GSUdfUkVHXTsNCj4gPiArCWNvbnN0IHN0cnVjdCB0 c2wyeDd4X2NoaXBfaW5mbwkqY2hpcF9pbmZvOw0KPiA+ICsJY29uc3Qgc3RydWN0IGlpb19pbmZv ICppbmZvOw0KPiA+ICsJczY0IGV2ZW50X3RpbWVzdGFtcDsNCj4gPiArCS8qIFRoaXMgc3RydWN0 dXJlIGlzIGludGVudGlvbmFsbHkgbGFyZ2UgdG8gYWNjb21tb2RhdGUNCj4gPiArCSAqIHVwZGF0 ZXMgdmlhIHN5c2ZzLiAqLw0KPiA+ICsJLyogU2l6ZWQgdG8gOSA9IG1heCA4IHNlZ21lbnRzICsg MSB0ZXJtaW5hdGlvbiBzZWdtZW50ICovDQo+ID4gKwlzdHJ1Y3QgdHNsMng3eF9sdXggdHNsMng3 eF9kZXZpY2VfbHV4W1RTTDJYN1hfTUFYX0xVWF9UQUJMRV9TSVpFXTsNCj4gPiArfTsNCj4gPiAr DQo+ID4gKy8qIERpZmZlcmVudCBkZXZpY2VzIHJlcXVpcmUgZGlmZmVyZW50IGNvZWZmaWNlbnRz ICovDQo+ID4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgdHNsMng3eF9sdXggdHNsMng3MV9sdXhfdGFi bGVbXSA9IHsNCj4gPiArCXsgMTQ0NjEsICAgNjExLCAgIDEyMTEgfSwNCj4gPiArCXsgMTg1NDAs ICAgMzUyLCAgICA2MjMgfSwNCj4gPiArCXsgICAgIDAsICAgICAwLCAgICAgIDAgfSwNCj4gPiAr fTsNCj4gPiArDQo+ID4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgdHNsMng3eF9sdXggdG1kMng3MV9s dXhfdGFibGVbXSA9IHsNCj4gPiArCXsgMTE2MzUsICAgMTE1LCAgICAyNTYgfSwNCj4gPiArCXsg MTU1MzYsICAgIDg3LCAgICAxNzkgfSwNCj4gPiArCXsgICAgIDAsICAgICAwLCAgICAgIDAgfSwN Cj4gPiArfTsNCj4gPiArDQo+ID4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgdHNsMng3eF9sdXggdHNs Mng3Ml9sdXhfdGFibGVbXSA9IHsNCj4gPiArCXsgMTQwMTMsICAgNDY2LCAgIDkxNyB9LA0KPiA+ ICsJeyAxODIyMiwgICAzMTAsICAgNTUyIH0sDQo+ID4gKwl7ICAgICAwLCAgICAgMCwgICAgIDAg fSwNCj4gPiArfTsNCj4gPiArDQo+ID4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgdHNsMng3eF9sdXgg dG1kMng3Ml9sdXhfdGFibGVbXSA9IHsNCj4gPiArCXsgMTMyMTgsICAgMTMwLCAgIDI2MiB9LA0K PiA+ICsJeyAxNzU5MiwgICA5MiwgICAgMTY5IH0sDQo+ID4gKwl7ICAgICAwLCAgICAgMCwgICAg IDAgfSwNCj4gPiArfTsNCj4gPiArDQo+ID4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgdHNsMng3eF9s dXggKnRzbDJ4N3hfZGVmYXVsdF9sdXhfdGFibGVfZ3JvdXBbXSA9IHsNCj4gPiArCVt0c2wyNTcx XSA9CXRzbDJ4NzFfbHV4X3RhYmxlLA0KPiA+ICsJW3RzbDI2NzFdID0JdHNsMng3MV9sdXhfdGFi bGUsDQo+ID4gKwlbdG1kMjY3MV0gPQl0bWQyeDcxX2x1eF90YWJsZSwNCj4gPiArCVt0c2wyNzcx XSA9CXRzbDJ4NzFfbHV4X3RhYmxlLA0KPiA+ICsJW3RtZDI3NzFdID0JdG1kMng3MV9sdXhfdGFi bGUsDQo+ID4gKwlbdHNsMjU3Ml0gPQl0c2wyeDcyX2x1eF90YWJsZSwNCj4gPiArCVt0c2wyNjcy XSA9CXRzbDJ4NzJfbHV4X3RhYmxlLA0KPiA+ICsJW3RtZDI2NzJdID0JdG1kMng3Ml9sdXhfdGFi bGUsDQo+ID4gKwlbdHNsMjc3Ml0gPQl0c2wyeDcyX2x1eF90YWJsZSwNCj4gPiArCVt0bWQyNzcy XSA9CXRtZDJ4NzJfbHV4X3RhYmxlLA0KPiA+ICt9Ow0KPiA+ICsNCj4gPiArc3RhdGljIGNvbnN0 IHN0cnVjdCB0c2wyeDd4X3NldHRpbmdzIHRzbDJ4N3hfZGVmYXVsdF9zZXR0aW5ncyA9IHsNCj4g PiArCS5hbHNfdGltZSA9IDIwMCwNCj4gPiArCS5hbHNfZ2FpbiA9IDAsDQo+ID4gKwkucHJ4X3Rp bWUgPSAweGZlLCAvKjUuNCBtUyAqLw0KPiA+ICsJLnByb3hfZ2FpbiA9IDEsDQo+ID4gKwkud2Fp dF90aW1lID0gMjQ1LA0KPiA+ICsJLnByb3hfY29uZmlnID0gMCwNCj4gPiArCS5hbHNfZ2Fpbl90 cmltID0gMTAwMCwNCj4gPiArCS5hbHNfY2FsX3RhcmdldCA9IDE1MCwNCj4gPiArCS5hbHNfdGhy ZXNoX2xvdyA9IDIwMCwNCj4gPiArCS5hbHNfdGhyZXNoX2hpZ2ggPSAyNTYsDQo+ID4gKwkucGVy c2lzdGVuY2UgPSAweEZGLA0KPiA+ICsJLmludGVycnVwdHNfZW4gPSAweDAwLA0KPiA+ICsJLnBy b3hfdGhyZXNfbG93ICA9IDAsDQo+ID4gKwkucHJveF90aHJlc19oaWdoID0gNTEyLA0KPiA+ICsJ LnByb3hfbWF4X3NhbXBsZXNfY2FsID0gMzAsDQo+ID4gKwkucHJveF9wdWxzZV9jb3VudCA9IDgN Cj4gPiArfTsNCj4gPiArDQo+ID4gK3N0YXRpYyBjb25zdCBzMTYgdHNsMlg3WF9hbHNfZ2FpbmFk altdID0gew0KPiA+ICsJMSwNCj4gPiArCTgsDQo+ID4gKwkxNiwNCj4gPiArCTEyMA0KPiA+ICt9 Ow0KPiA+ICsNCj4gPiArc3RhdGljIGNvbnN0IHMxNiB0c2wyWDdYX3ByeF9nYWluYWRqW10gPSB7 DQo+ID4gKwkxLA0KPiA+ICsJMiwNCj4gPiArCTQsDQo+ID4gKwk4DQo+ID4gK307DQo+ID4gKw0K PiA+ICsvKiBDaGFubmVsIHZhcmlhdGlvbnMgKi8NCj4gPiArZW51bSB7DQo+ID4gKwlBTFMsDQo+ ID4gKwlQUlgsDQo+ID4gKwlBTFNQUlgsDQo+ID4gKwlQUlgyLA0KPiA+ICsJQUxTUFJYMiwNCj4g PiArfTsNCj4gPiArDQo+ID4gK2NvbnN0IHU4IGRldmljZV9jaGFubmVsX2NvbmZpZ1tdID0gew0K PiA+ICsJQUxTLA0KPiA+ICsJUFJYLA0KPiA+ICsJUFJYLA0KPiA+ICsJQUxTUFJYLA0KPiA+ICsJ QUxTUFJYLA0KPiA+ICsJQUxTLA0KPiA+ICsJUFJYMiwNCj4gPiArCVBSWDIsDQo+ID4gKwlBTFNQ UlgyLA0KPiA+ICsJQUxTUFJYMg0KPiA+ICt9Ow0KPiA+ICsNCj4gPiArLyoNCj4gPiArICogUmVh ZCBhIG51bWJlciBvZiBieXRlcyBzdGFydGluZyBhdCByZWdpc3RlciAocmVnKSBsb2NhdGlvbi4N Cj4gPiArICogUmV0dXJuIDAsIG9yIGkyY19zbWJ1c193cml0ZV9ieXRlIEVSUk9SIGNvZGUuDQo+ ID4gKyAqLw0KPiBQcmVmZXJlbmNlIGZvciBrZXJuZWwgZG9jIG9uIGFsbCBmdW5jdGlvbiBkZXNj cmlwdGlvbnMuIChub3QgY3J1Y2lhbA0KPiB0aG91Z2gpLg0KPiA+ICtzdGF0aWMgaW50DQo+ID4g K3RzbDJ4N3hfaTJjX3JlYWQoc3RydWN0IGkyY19jbGllbnQgKmNsaWVudCwgdTggcmVnLCB1OCAq dmFsKQ0KPiA+ICt7DQo+ID4gKwlpbnQgcmV0Ow0KPiA+ICsNCj4gPiArCS8qIHNlbGVjdCByZWdp c3RlciB0byB3cml0ZSAqLw0KPiA+ICsJcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGUoY2xpZW50 LCAoVFNMMlg3WF9DTURfUkVHIHwgcmVnKSk7DQo+ID4gKwlpZiAocmV0PCAgMCkgew0KPiA+ICsJ CWRldl9lcnIoJmNsaWVudC0+ZGV2LCAiJXM6IGZhaWxlZCB0byB3cml0ZSByZWdpc3RlciAleFxu Ig0KPiA+ICsJCQkJLCBfX2Z1bmNfXywgcmVnKTsNCj4gPiArCQlyZXR1cm4gcmV0Ow0KPiA+ICsJ fQ0KPiA+ICsJLyogcmVhZCB0aGUgZGF0YSAqLw0KPiA+ICsJKnZhbCA9IGkyY19zbWJ1c19yZWFk X2J5dGUoY2xpZW50KTsNCj4gTm8gZXJyb3IgaGFuZGxpbmcgb24gdGhlIGkyY19zbWJ1c19yZWFk X2J5dGUuDQo+ID4gKw0KPiA+ICsJcmV0dXJuIDA7DQo+ID4gK30NCj4gPiArDQo+ID4gKy8qKg0K PiA+ICsgKiB0c2wyeDd4X2dldF9sdXgoKSAtIFJlYWRzIGFuZCBjYWxjdWxhdGVzIGN1cnJlbnQg bHV4IHZhbHVlLg0KPiA+ICsgKiBAaW5kaW9fZGV2OglJSU8gZGV2aWNlDQo+ID4gKyAqDQo+ID4g KyAqIFRoZSByYXcgY2gwIGFuZCBjaDEgdmFsdWVzIG9mIHRoZSBhbWJpZW50IGxpZ2h0IHNlbnNl ZCBpbiB0aGUgbGFzdA0KPiA+ICsgKiBpbnRlZ3JhdGlvbiBjeWNsZSBhcmUgcmVhZCBmcm9tIHRo ZSBkZXZpY2UuDQo+ID4gKyAqIFRpbWUgc2NhbGUgZmFjdG9yIGFycmF5IHZhbHVlcyBhcmUgYWRq dXN0ZWQgYmFzZWQgb24gdGhlIGludGVncmF0aW9uIHRpbWUuDQo+ID4gKyAqIFRoZSByYXcgdmFs dWVzIGFyZSBtdWx0aXBsaWVkIGJ5IGEgc2NhbGUgZmFjdG9yLCBhbmQgZGV2aWNlIGdhaW4gaXMg b2J0YWluZWQNCj4gPiArICogdXNpbmcgZ2FpbiBpbmRleC4gTGltaXQgY2hlY2tzIGFyZSBkb25l IG5leHQsIHRoZW4gdGhlIHJhdGlvIG9mIGEgbXVsdGlwbGUNCj4gPiArICogb2YgY2gxIHZhbHVl LCB0byB0aGUgY2gwIHZhbHVlLCBpcyBjYWxjdWxhdGVkLiBBcnJheSB0c2wyeDd4X2RldmljZV9s dXhbXQ0KPiA+ICsgKiBpcyB0aGVuIHNjYW5uZWQgdG8gZmluZCB0aGUgZmlyc3QgcmF0aW8gdmFs dWUgdGhhdCBpcyBqdXN0IGFib3ZlIHRoZSByYXRpbw0KPiA+ICsgKiB3ZSBqdXN0IGNhbGN1bGF0 ZWQuIFRoZSBjaDAgYW5kIGNoMSBtdWx0aXBsaWVyIGNvbnN0YW50cyBpbiB0aGUgYXJyYXkgYXJl DQo+ID4gKyAqIHRoZW4gdXNlZCBhbG9uZyB3aXRoIHRoZSB0aW1lIHNjYWxlIGZhY3RvciBhcnJh eSB2YWx1ZXMsIHRvIGNhbGN1bGF0ZSB0aGUNCj4gPiArICogbHV4Lg0KPiA+ICsgKi8NCj4gPiAr c3RhdGljIGludCB0c2wyeDd4X2dldF9sdXgoc3RydWN0IGlpb19kZXYgKmluZGlvX2RldikNCj4g PiArew0KPiA+ICsJdTE2IGNoMCwgY2gxOyAvKiBzZXBhcmF0ZWQgY2gwL2NoMSBkYXRhIGZyb20g ZGV2aWNlICovDQo+ID4gKwl1MzIgbHV4OyAvKiByYXcgbHV4IGNhbGN1bGF0ZWQgZnJvbSBkZXZp Y2UgZGF0YSAqLw0KPiA+ICsJdTY0IGx1eDY0Ow0KPiA+ICsJdTMyIHJhdGlvOw0KPiA+ICsJdTgg YnVmWzRdOw0KPiA+ICsJc3RydWN0IHRzbDJ4N3hfbHV4ICpwOw0KPiA+ICsJc3RydWN0IHRzbDJY N1hfY2hpcCAqY2hpcCA9IGlpb19wcml2KGluZGlvX2Rldik7DQo+ID4gKwlpbnQgaSwgcmV0Ow0K PiA+ICsJdTMyIGNoMGx1eCA9IDA7DQo+ID4gKwl1MzIgY2gxbHV4ID0gMDsNCj4gPiArDQo+ID4g KwlpZiAobXV0ZXhfdHJ5bG9jaygmY2hpcC0+YWxzX211dGV4KSA9PSAwKSB7DQo+ID4gKwkJZGV2 X2luZm8oJmNoaXAtPmNsaWVudC0+ZGV2LCAidHNsMng3eF9nZXRfbHV4IGRldmljZSBpcw0KPiBi dXN5XG4iKTsNCj4gSXNuJ3QgdGhpcyBkZXZfaW5mbyBnb2luZyB0byBnaXZlIHlvdSBhIGxvdCBv ZiBsb2cgZW50cmllcz8gIERvZXMgYW55b25lDQo+IGNhcmUgdGhhdCB0aGUgdmFsdWUgaXMNCj4g YSBsaXR0bGUgc3RhbGU/DQpHdWVzcyBub3QuDQoNCj4gPiArCQlyZXR1cm4gY2hpcC0+YWxzX2N1 cl9pbmZvLmx1eDsgLyogYnVzeSwgc28gcmV0dXJuIExBU1QgVkFMVUUgKi8NCj4gPiArCX0NCj4g PiArDQo+ID4gKwlpZiAoY2hpcC0+dHNsMng3eF9jaGlwX3N0YXR1cyAhPSBUU0wyWDdYX0NISVBf V09SS0lORykgew0KPiA+ICsJCS8qIGRldmljZSBpcyBub3QgZW5hYmxlZCAqLw0KPiA+ICsJCWRl dl9lcnIoJmNoaXAtPmNsaWVudC0+ZGV2LCAiJXM6IGRldmljZSBpcyBub3QgZW5hYmxlZFxuIiwN Cj4gPiArCQkJCV9fZnVuY19fKTsNCj4gPiArCQlyZXQgPSAtRUJVU1kgOw0KPiA+ICsJCWdvdG8g b3V0X3VubG9jazsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlyZXQgPSB0c2wyeDd4X2kyY19yZWFk KGNoaXAtPmNsaWVudCwNCj4gPiArCQkoVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9TVEFUVVMp LCZidWZbMF0pOw0KPiA+ICsJaWYgKHJldDwgIDApIHsNCj4gPiArCQlkZXZfZXJyKCZjaGlwLT5j bGllbnQtPmRldiwNCj4gPiArCQkJIiVzOiBmYWlsZWQgdG8gcmVhZCBDTURfUkVHXG4iLCBfX2Z1 bmNfXyk7DQo+ID4gKwkJZ290byBvdXRfdW5sb2NrOw0KPiA+ICsJfQ0KPiA+ICsJLyogaXMgZGF0 YSBuZXcmICB2YWxpZCAqLw0KPiA+ICsJaWYgKCEoYnVmWzBdJiAgVFNMMlg3WF9TVEFfQURDX1ZB TElEKSkgew0KPiA+ICsJCWRldl9lcnIoJmNoaXAtPmNsaWVudC0+ZGV2LA0KPiA+ICsJCQkiJXM6 IGRhdGEgbm90IHZhbGlkIHlldFxuIiwgX19mdW5jX18pOw0KPiA+ICsJCXJldCA9IGNoaXAtPmFs c19jdXJfaW5mby5sdXg7IC8qIHJldHVybiBMQVNUIFZBTFVFICovDQo+ID4gKwkJZ290byBvdXRf dW5sb2NrOw0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCWZvciAoaSA9IDA7IGk8ICA0OyBpKyspIHsN Cj4gPiArCQlyZXQgPSB0c2wyeDd4X2kyY19yZWFkKGNoaXAtPmNsaWVudCwNCj4gPiArCQkJKFRT TDJYN1hfQ01EX1JFRyB8IChUU0wyWDdYX0FMU19DSEFOMExPICsgaSkpLA0KPiA+ICsJCQkmYnVm W2ldKTsNCj4gPiArCQlpZiAocmV0PCAgMCkgew0KPiA+ICsJCQlkZXZfZXJyKCZjaGlwLT5jbGll bnQtPmRldiwNCj4gPiArCQkJCSIlczogZmFpbGVkIHRvIHJlYWQuIGVycj0leFxuIiwgX19mdW5j X18sIHJldCk7DQo+ID4gKwkJCWdvdG8gb3V0X3VubG9jazsNCj4gPiArCQl9DQo+ID4gKwl9DQo+ ID4gKw0KPiA+ICsJLyogY2xlYXIgc3RhdHVzLCByZWFsbHkgaW50ZXJydXB0IHN0YXR1cyAoIGFy ZSBvZmYpLA0KPiA+ICsJYnV0IHdlIHVzZSB0aGUgYml0IGFueXdheSAqLw0KPiBVbW0uLiBub3Qg c3VyZSBJIGZvbGxvdyAoIGFyZSBvZmYpIGJpdC4uLi4NCj4gPiArCXJldCA9IGkyY19zbWJ1c193 cml0ZV9ieXRlKGNoaXAtPmNsaWVudCwNCj4gPiArCQkoVFNMMlg3WF9DTURfUkVHIHwNCj4gPiAr CQkJCVRTTDJYN1hfQ01EX1NQTF9GTiB8DQo+ID4gKwkJCQlUU0wyWDdYX0NNRF9BTFNfSU5UX0NM UikpOw0KPiA+ICsNCj4gVW53YW50ZWQgYmxhbmsgbGluZSBoZXJlPw0KPiA+ICsJaWYgKHJldDwg IDApIHsNCj4gPiArCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwNCj4gPiArCQkiaTJjX3dy aXRlX2NvbW1hbmQgZmFpbGVkIGluICVzLCBlcnIgPSAlZFxuIiwNCj4gPiArCQkJX19mdW5jX18s IHJldCk7DQo+ID4gKwkJZ290byBvdXRfdW5sb2NrOyAvKiBoYXZlIG5vIGRhdGEsIHNvIHJldHVy biBmYWlsdXJlICovDQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJLyogZXh0cmFjdCBBTFMvbHV4IGRh dGEgKi8NCj4gPiArCWNoMCA9IGxlMTZfdG9fY3B1cCgoY29uc3QgX19sZTE2ICopJmJ1ZlswXSk7 DQo+ID4gKwljaDEgPSBsZTE2X3RvX2NwdXAoKGNvbnN0IF9fbGUxNiAqKSZidWZbMl0pOw0KPiA+ ICsNCj4gPiArCWNoaXAtPmFsc19jdXJfaW5mby5hbHNfY2gwID0gY2gwOw0KPiA+ICsJY2hpcC0+ YWxzX2N1cl9pbmZvLmFsc19jaDEgPSBjaDE7DQo+ID4gKw0KPiA+ICsJaWYgKChjaDA+PSBjaGlw LT5hbHNfc2F0dXJhdGlvbikgfHwgKGNoMT49IGNoaXAtPmFsc19zYXR1cmF0aW9uKSkgew0KPiA+ ICsJCWx1eCA9IFRTTDJYN1hfTFVYX0NBTENfT1ZFUl9GTE9XOw0KPiA+ICsJCWdvdG8gcmV0dXJu X21heDsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlpZiAoY2gwID09IDApIHsNCj4gPiArCQkvKiBo YXZlIG5vIGRhdGEsIHNvIHJldHVybiBMQVNUIFZBTFVFICovDQo+ID4gKwkJcmV0ID0gY2hpcC0+ YWxzX2N1cl9pbmZvLmx1eCA9IDA7DQo+IFRoYXQncyBub3QgcmV0dXJuaW5nIHRoZSBsYXN0IHZh bHVlIGFzIHBlciBjb21tZW50Pz8/Pw0KPiA+ICsJCWdvdG8gb3V0X3VubG9jazsNCj4gPiArCX0N Cj4gPiArCS8qIGNhbGN1bGF0ZSByYXRpbyAqLw0KPiA+ICsJcmF0aW8gPSAoY2gxPDwgIDE1KSAv IGNoMDsNCj4gPiArCS8qIGNvbnZlcnQgdG8gdW5zY2FsZWQgbHV4IHVzaW5nIHRoZSBwb2ludGVy IHRvIHRoZSB0YWJsZSAqLw0KPiA+ICsJcCA9IChzdHJ1Y3QgdHNsMng3eF9sdXggKikgY2hpcC0+ dHNsMng3eF9kZXZpY2VfbHV4Ow0KPiA+ICsJd2hpbGUgKHAtPnJhdGlvICE9IDAmJiAgcC0+cmF0 aW88ICByYXRpbykNCj4gPiArCQkJcCsrOw0KPiBUaGF0J3MgYSByYXRoZXIgbGFyZ2UgaW5kZW50 IG9uIHRoZSBwKyshDQo+ID4gKw0KPiA+ICsJaWYgKHAtPnJhdGlvID09IDApIHsNCj4gPiArCQls dXggPSAwOw0KPiA+ICsJfSBlbHNlIHsNCj4gPiArCQljaDBsdXggPSBESVZfUk9VTkRfVVAoKGNo MCAqIHAtPmNoMCksDQo+ID4gKwkJCXRzbDJYN1hfYWxzX2dhaW5hZGpbY2hpcC0+dHNsMng3eF9z ZXR0aW5ncy5hbHNfZ2Fpbl0pOw0KPiA+ICsJCWNoMWx1eCA9IERJVl9ST1VORF9VUCgoY2gxICog cC0+Y2gxKSwNCj4gPiArCQkJdHNsMlg3WF9hbHNfZ2FpbmFkaltjaGlwLT50c2wyeDd4X3NldHRp bmdzLmFsc19nYWluXSk7DQo+ID4gKwkJbHV4ID0gY2gwbHV4IC0gY2gxbHV4Ow0KPiA+ICsJfQ0K PiA+ICsNCj4gPiArCS8qIG5vdGU6IGx1eCBpcyAzMSBiaXQgbWF4IGF0IHRoaXMgcG9pbnQgKi8N Cj4gPiArCWlmIChjaDFsdXg+ICBjaDBsdXgpIHsNCj4gQ291bGQgc2F5IHdobiBpdCdzIHJldHVy bmluZyB0aGUgbGFzdCB2YWx1ZT8gIFdvdWxkIG1ha2UgZGVidWcgY29tbWVudHMNCj4gbW9lciBo ZWxwZnVsIHBlcmhhcHMuDQo+ID4gKwkJZGV2X2RiZygmY2hpcC0+Y2xpZW50LT5kZXYsICJSZXR1 cm5pbmcgbGFzdCB2YWx1ZVxuIik7DQo+ID4gKwkJcmV0ID0gY2hpcC0+YWxzX2N1cl9pbmZvLmx1 eDsNCj4gPiArCQlnb3RvIG91dF91bmxvY2s7DQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJLyogYWRq dXN0IGZvciBhY3RpdmUgdGltZSBzY2FsZSAqLw0KPiA+ICsJaWYgKGNoaXAtPmFsc190aW1lX3Nj YWxlID09IDApDQo+ID4gKwkJbHV4ID0gMDsNCj4gPiArCWVsc2UNCj4gPiArCQlsdXggPSAobHV4 ICsgKGNoaXAtPmFsc190aW1lX3NjYWxlPj4gIDEpKSAvDQo+ID4gKwkJCWNoaXAtPmFsc190aW1l X3NjYWxlOw0KPiA+ICsNCj4gPiArCS8qIGFkanVzdCBmb3IgYWN0aXZlIGdhaW4gc2NhbGUNCj4g PiArCSAqIFRoZSB0c2wyeDd4X2RldmljZV9sdXggdGFibGVzIGhhdmUgYSBmYWN0b3Igb2YgMjU2 IGJ1aWx0LWluLg0KPiA+ICsJICogVXNlci1zcGVjaWZpZWQgZ2FpbiBwcm92aWRlcyBhIG11bHRp cGxpZXIuDQo+ID4gKwkgKiBBcHBseSB1c2VyLXNwZWNpZmllZCBnYWluIGJlZm9yZSBzaGlmdGlu ZyByaWdodCB0byByZXRhaW4gcHJlY2lzaW9uLg0KPiA+ICsJICogVXNlIDY0IGJpdHMgdG8gYXZv aWQgb3ZlcmZsb3cgb24gbXVsdGlwbGljYXRpb24uDQo+ID4gKwkgKiBUaGVuIGdvIGJhY2sgdG8g MzIgYml0cyBiZWZvcmUgZGl2aXNpb24gdG8gYXZvaWQgdXNpbmcgZGl2X3U2NCgpLg0KPiA+ICsJ ICovDQo+ID4gKwlsdXg2NCA9IGx1eDsNCj4gPiArCWx1eDY0ID0gbHV4NjQgKiBjaGlwLT50c2wy eDd4X3NldHRpbmdzLmFsc19nYWluX3RyaW07DQo+ID4gKwlsdXg2ND4+PSA4Ow0KPiA+ICsJbHV4 ID0gbHV4NjQ7DQo+ID4gKwlsdXggPSAobHV4ICsgNTAwKSAvIDEwMDA7DQo+ID4gKw0KPiA+ICsJ aWYgKGx1eD4gIFRTTDJYN1hfTFVYX0NBTENfT1ZFUl9GTE9XKSAvKiBjaGVjayBmb3Igb3ZlcmZs b3cgKi8NCj4gPiArCQlsdXggPSBUU0wyWDdYX0xVWF9DQUxDX09WRVJfRkxPVzsNCj4gPiArDQo+ ID4gKwkvKiBVcGRhdGUgdGhlIHN0cnVjdHVyZSB3aXRoIHRoZSBsYXRlc3QgbHV4LiAqLw0KPiA+ ICtyZXR1cm5fbWF4Og0KPiA+ICsJY2hpcC0+YWxzX2N1cl9pbmZvLmx1eCA9IGx1eDsNCj4gPiAr CXJldCA9IGx1eDsNCj4gPiArDQo+ID4gK291dF91bmxvY2s6DQo+ID4gKwltdXRleF91bmxvY2so JmNoaXAtPmFsc19tdXRleCk7DQo+ID4gKw0KPiA+ICsJcmV0dXJuIHJldDsNCj4gPiArfQ0KPiA+ ICsNCj4gPiArLyogUHJveGltaXR5IHBvbGwgZnVuY3Rpb24gKi8NCj4gPiArc3RhdGljIGludCB0 c2wyeDd4X3Byb3hfcG9sbChzdHJ1Y3QgaWlvX2RldiAqaW5kaW9fZGV2KQ0KPiA+ICt7DQo+ID4g KwlpbnQgaTsNCj4gPiArCWludCByZXQ7DQo+ID4gKwl1OCBzdGF0dXM7DQo+ID4gKwl1OCBjaGRh dGFbMl07DQo+ID4gKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9f ZGV2KTsNCj4gPiArDQo+ID4gKwlpZiAobXV0ZXhfdHJ5bG9jaygmY2hpcC0+cHJveF9tdXRleCkg PT0gMCkgew0KPiA+ICsJCWRldl9lcnIoJmNoaXAtPmNsaWVudC0+ZGV2LA0KPiA+ICsJCQkiJXM6 IENhbid0IGdldCBwcm94IG11dGV4XG4iLCBfX2Z1bmNfXyk7DQo+ID4gKwkJcmV0dXJuIC1FQlVT WTsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlyZXQgPSB0c2wyeDd4X2kyY19yZWFkKGNoaXAtPmNs aWVudCwNCj4gPiArCQkoVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9TVEFUVVMpLCZzdGF0dXMp Ow0KPiA+ICsJaWYgKHJldDwgIDApIHsNCj4gPiArCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRl diwNCj4gPiArCQkiJXM6IGkyYyBlcnI9JWRcbiIsIF9fZnVuY19fLCByZXQpOw0KPiA+ICsJCWdv dG8gcHJveF9wb2xsX2VycjsNCj4gPiArCX0NCj4gPiArDQo+IEknZCBwcmVmZXIgYSBzd2l0Y2gg b24gdGhlIGNoaXAtPmlkJ3MgdG8gdGhpcyBsZXNzIHRoYW4gY29tcGFyaXNvbi4gIExvb2tzDQo+ IGxpa2Ugc29tZXRoaW5nIHRoYXQgbWlnaHQgZ2V0IGFjY2lkZW50YWxseSBicm9rZW4gaW4gZnV0 dXJlLg0KPiA+ICsJaWYgKGNoaXAtPmlkPCAgdHNsMjU3Mikgew0KPiA+ICsJCWlmICghKHN0YXR1 cyYgIFRTTDJYN1hfU1RBX0FEQ19WQUxJRCkpDQo+ID4gKwkJCQlnb3RvIHByb3hfcG9sbF9lcnI7 DQo+ID4gKwl9IGVsc2UgaWYgKCEoc3RhdHVzJiAgVFNMMlg3WF9TVEFfUFJYX1ZBTElEKSkNCj4g PiArCQkJCWdvdG8gcHJveF9wb2xsX2VycjsNCj4gPiArDQo+ID4gKwlmb3IgKGkgPSAwOyBpPCAg MjsgaSsrKSB7DQo+ID4gKwkJcmV0ID0gdHNsMng3eF9pMmNfcmVhZChjaGlwLT5jbGllbnQsDQo+ ID4gKwkJCShUU0wyWDdYX0NNRF9SRUcgfA0KPiA+ICsJCQkJCShUU0wyWDdYX1BSWF9MTyArIGkp KSwmY2hkYXRhW2ldKTsNCj4gPiArCQlpZiAocmV0PCAgMCkNCj4gPiArCQkJZ290byBwcm94X3Bv bGxfZXJyOw0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCWNoaXAtPnByb3hfZGF0YSA9DQo+ID4gKwkJ CWxlMTZfdG9fY3B1cCgoY29uc3QgX19sZTE2ICopJmNoZGF0YVswXSk7DQo+ID4gKw0KPiA+ICtw cm94X3BvbGxfZXJyOg0KPiA+ICsNCj4gPiArCW11dGV4X3VubG9jaygmY2hpcC0+cHJveF9tdXRl eCk7DQo+ID4gKwlyZXR1cm4gY2hpcC0+cHJveF9kYXRhOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICsv Kg0KPiA+ICsgKiBQcm92aWRlcyBpbml0aWFsIG9wZXJhdGlvbmFsIHBhcmFtZXRlciBkZWZhdWx0 cy4NCj4gPiArICogVGhlc2UgZGVmYXVsdHMgbWF5IGJlIGNoYW5nZWQgdGhyb3VnaCB0aGUgZGV2 aWNlJ3Mgc3lzZnMgZmlsZXMuDQo+ID4gKyAqLw0KPiA+ICtzdGF0aWMgdm9pZCB0c2wyeDd4X2Rl ZmF1bHRzKHN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXApDQo+ID4gK3sNCj4gPiArCS8qIElmIE9w ZXJhdGlvbmFsIHNldHRpbmdzIGRlZmluZWQgZWxzZXdoZXJlLi4gKi8NCj4gPiArCWlmIChjaGlw LT5wZGF0YSYmICBjaGlwLT5wZGF0YS0+cGxhdGZvcm1fZGVmYXVsdF9zZXR0aW5ncyAhPSAwKQ0K PiA+ICsJCW1lbWNweSgmKGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MpLA0KPiA+ICsJCQljaGlwLT5w ZGF0YS0+cGxhdGZvcm1fZGVmYXVsdF9zZXR0aW5ncywNCj4gPiArCQkJc2l6ZW9mKHRzbDJ4N3hf ZGVmYXVsdF9zZXR0aW5ncykpOw0KPiA+ICsJZWxzZQ0KPiA+ICsJCW1lbWNweSgmKGNoaXAtPnRz bDJ4N3hfc2V0dGluZ3MpLA0KPiA+ICsJCQkmdHNsMng3eF9kZWZhdWx0X3NldHRpbmdzLA0KPiA+ ICsJCQlzaXplb2YodHNsMng3eF9kZWZhdWx0X3NldHRpbmdzKSk7DQo+ID4gKw0KPiA+ICsJLyog TG9hZCB1cCB0aGUgcHJvcGVyIGx1eCB0YWJsZS4gKi8NCj4gPiArCWlmIChjaGlwLT5wZGF0YSYm ICBjaGlwLT5wZGF0YS0+cGxhdGZvcm1fbHV4X3RhYmxlWzBdLnJhdGlvICE9IDApDQo+ID4gKwkJ bWVtY3B5KGNoaXAtPnRzbDJ4N3hfZGV2aWNlX2x1eCwNCj4gPiArCQkJY2hpcC0+cGRhdGEtPnBs YXRmb3JtX2x1eF90YWJsZSwNCj4gPiArCQkJc2l6ZW9mKGNoaXAtPnBkYXRhLT5wbGF0Zm9ybV9s dXhfdGFibGUpKTsNCj4gPiArCWVsc2UNCj4gPiArCQltZW1jcHkoY2hpcC0+dHNsMng3eF9kZXZp Y2VfbHV4LA0KPiA+ICsJCShzdHJ1Y3QgdHNsMng3eF9sdXggKil0c2wyeDd4X2RlZmF1bHRfbHV4 X3RhYmxlX2dyb3VwW2NoaXAtPmlkXSwNCj4gPiArCQkJCU1BWF9ERUZBVUxUX1RBQkxFX0JZVEVT KTsNCj4gVW53YW50ZWQgYmxhbmsgbGluZS4NCj4gPiArDQo+ID4gK30NCj4gPiArDQo+ID4gKy8q DQo+ID4gKyAqIE9idGFpbiBzaW5nbGUgcmVhZGluZyBhbmQgY2FsY3VsYXRlIHRoZSBhbHNfZ2Fp bl90cmltDQo+ID4gKyAqIChsYXRlciB1c2VkIHRvIGRlcml2ZSBhY3R1YWwgbHV4KS4NCj4gPiAr ICogUmV0dXJuIHVwZGF0ZWQgZ2Fpbl90cmltIHZhbHVlLg0KPiBrZXJuZWwgZG9jIHByZWZlcnJl ZC4NCj4gPiArICovDQo+ID4gK3N0YXRpYyBpbnQgdHNsMng3eF9hbHNfY2FsaWJyYXRlKHN0cnVj dCBpaW9fZGV2ICppbmRpb19kZXYpDQo+ID4gK3sNCj4gPiArCXN0cnVjdCB0c2wyWDdYX2NoaXAg KmNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+ICsJdTggcmVnX3ZhbDsNCj4gPiArCWlu dCBnYWluX3RyaW1fdmFsOw0KPiA+ICsJaW50IHJldDsNCj4gPiArCWludCBsdXhfdmFsOw0KPiA+ ICsNCj4gPiArCXJldCA9IGkyY19zbWJ1c193cml0ZV9ieXRlKGNoaXAtPmNsaWVudCwNCj4gPiAr CQkJKFRTTDJYN1hfQ01EX1JFRyB8IFRTTDJYN1hfQ05UUkwpKTsNCj4gPiArCWlmIChyZXQ8ICAw KSB7DQo+ID4gKwkJZGV2X2VycigmY2hpcC0+Y2xpZW50LT5kZXYsDQo+ID4gKwkJIiVzOiBmYWls ZWQgdG8gd3JpdGUgQ05UUkwgcmVnaXN0ZXIsIHJldD0lZFxuIiwNCj4gPiArCQlfX2Z1bmNfXywg cmV0KTsNCj4gPiArCQlyZXR1cm4gcmV0Ow0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCXJlZ192YWwg PSBpMmNfc21idXNfcmVhZF9ieXRlKGNoaXAtPmNsaWVudCk7DQo+ID4gKwlpZiAoKHJlZ192YWwm ICAoVFNMMlg3WF9DTlRMX0FEQ19FTkJMIHwgVFNMMlg3WF9DTlRMX1BXUl9PTikpDQo+ID4gKwkJ IT0gKFRTTDJYN1hfQ05UTF9BRENfRU5CTCB8IFRTTDJYN1hfQ05UTF9QV1JfT04pKSB7DQo+ID4g KwkJZGV2X2VycigmY2hpcC0+Y2xpZW50LT5kZXYsDQo+ID4gKwkJCSIlczogZmFpbGVkOiBBREMg bm90IGVuYWJsZWRcbiIsIF9fZnVuY19fKTsNCj4gPiArCQlyZXR1cm4gLTE7DQo+ID4gKwl9DQo+ ID4gKw0KPiA+ICsJcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGUoY2hpcC0+Y2xpZW50LA0KPiA+ ICsJCQkoVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9DTlRSTCkpOw0KPiA+ICsJaWYgKHJldDwg IDApIHsNCj4gPiArCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwNCj4gPiArCQkJIiVzOiBm YWlsZWQgdG8gd3JpdGUgY3RybCByZWc6IHJldD0lZFxuIiwNCj4gPiArCQkJX19mdW5jX18sIHJl dCk7DQo+ID4gKwkJcmV0dXJuIHJldDsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlyZWdfdmFsID0g aTJjX3NtYnVzX3JlYWRfYnl0ZShjaGlwLT5jbGllbnQpOw0KPiA+ICsJaWYgKChyZWdfdmFsJiAg VFNMMlg3WF9TVEFfQURDX1ZBTElEKSAhPSBUU0wyWDdYX1NUQV9BRENfVkFMSUQpIHsNCj4gPiAr CQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwNCj4gPiArCQkJIiVzOiBmYWlsZWQ6IFNUQVRV UyAtIEFEQyBub3QgdmFsaWQuXG4iLCBfX2Z1bmNfXyk7DQo+ID4gKwkJcmV0dXJuIC1FTk9EQVRB Ow0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCWx1eF92YWwgPSB0c2wyeDd4X2dldF9sdXgoaW5kaW9f ZGV2KTsNCj4gPiArCWlmIChsdXhfdmFsPCAgMCkgew0KPiA+ICsJCWRldl9lcnIoJmNoaXAtPmNs aWVudC0+ZGV2LA0KPiA+ICsJCSIlczogZmFpbGVkIHRvIGdldCBsdXhcbiIsIF9fZnVuY19fKTsN Cj4gPiArCQlyZXR1cm4gbHV4X3ZhbDsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlnYWluX3RyaW1f dmFsID0gICgoKGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2NhbF90YXJnZXQpDQo+ID4gKwkJ CSogY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfZ2Fpbl90cmltKSAvIGx1eF92YWwpOw0KPiA+ ICsJaWYgKChnYWluX3RyaW1fdmFsPCAgMjUwKSB8fCAoZ2Fpbl90cmltX3ZhbD4gIDQwMDApKQ0K PiA+ICsJCXJldHVybiAtRVJBTkdFOw0KPiA+ICsNCj4gPiArCWNoaXAtPnRzbDJ4N3hfc2V0dGlu Z3MuYWxzX2dhaW5fdHJpbSA9IGdhaW5fdHJpbV92YWw7DQo+ID4gKwlkZXZfaW5mbygmY2hpcC0+ Y2xpZW50LT5kZXYsDQo+ID4gKwkJIiVzIGFsc19jYWxpYnJhdGUgY29tcGxldGVkXG4iLCBjaGlw LT5jbGllbnQtPm5hbWUpOw0KPiA+ICsNCj4gPiArCXJldHVybiAoaW50KSBnYWluX3RyaW1fdmFs Ow0KPiA+ICt9DQo+ID4gKw0KPiA+ICsvKg0KPiA+ICsgKiBUdXJuIHRoZSBkZXZpY2Ugb24uDQo+ ID4gKyAqIENvbmZpZ3VyYXRpb24gbXVzdCBiZSBzZXQgYmVmb3JlIGNhbGxpbmcgdGhpcyBmdW5j dGlvbi4NCj4gPiArICovDQo+ID4gK3N0YXRpYyBpbnQgdHNsMng3eF9jaGlwX29uKHN0cnVjdCBp aW9fZGV2ICppbmRpb19kZXYpDQo+ID4gK3sNCj4gPiArCWludCBpOw0KPiA+ICsJaW50IHJldCA9 IDA7DQo+IENhbid0IGltbWVkaWF0ZWx5IHNlZSBhIHBhdGggd2hlcmUgdGhpcyBpc24ndCBzZXQg YW55d2F5Lg0KPiA+ICsJdTggKmRldl9yZWc7DQo+ID4gKwl1OCB1dG1wOw0KPiA+ICsJaW50IGFs c19jb3VudDsNCj4gPiArCWludCBhbHNfdGltZTsNCj4gPiArCXN0cnVjdCB0c2wyWDdYX2NoaXAg KmNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+ICsJdTggcmVnX3ZhbCA9IDA7DQo+ID4g Kw0KPiA+ICsJaWYgKGNoaXAtPnBkYXRhJiYgIGNoaXAtPnBkYXRhLT5wb3dlcl9vbikNCj4gPiAr CQljaGlwLT5wZGF0YS0+cG93ZXJfb24oaW5kaW9fZGV2KTsNCj4gPiArDQo+ID4gKwkvKiBOb24g Y2FsY3VsYXRlZCBwYXJhbWV0ZXJzICovDQo+ID4gKwljaGlwLT50c2wyeDd4X2NvbmZpZ1tUU0wy WDdYX1BSWF9USU1FXSA9DQo+ID4gKwkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MucHJ4X3RpbWU7 DQo+ID4gKwljaGlwLT50c2wyeDd4X2NvbmZpZ1tUU0wyWDdYX1dBSVRfVElNRV0gPQ0KPiA+ICsJ CQljaGlwLT50c2wyeDd4X3NldHRpbmdzLndhaXRfdGltZTsNCj4gPiArCWNoaXAtPnRzbDJ4N3hf Y29uZmlnW1RTTDJYN1hfUFJYX0NPTkZJR10gPQ0KPiA+ICsJCQljaGlwLT50c2wyeDd4X3NldHRp bmdzLnByb3hfY29uZmlnOw0KPiA+ICsNCj4gPiArCWNoaXAtPnRzbDJ4N3hfY29uZmlnW1RTTDJY N1hfQUxTX01JTlRIUkVTSExPXSA9DQo+ID4gKwkJKGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxz X3RocmVzaF9sb3cpJiAgMHhGRjsNCj4gPiArCWNoaXAtPnRzbDJ4N3hfY29uZmlnW1RTTDJYN1hf QUxTX01JTlRIUkVTSEhJXSA9DQo+ID4gKwkJKGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX3Ro cmVzaF9sb3c+PiAgOCkmICAweEZGOw0KPiA+ICsJY2hpcC0+dHNsMng3eF9jb25maWdbVFNMMlg3 WF9BTFNfTUFYVEhSRVNITE9dID0NCj4gPiArCQkoY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNf dGhyZXNoX2hpZ2gpJiAgMHhGRjsNCj4gPiArCWNoaXAtPnRzbDJ4N3hfY29uZmlnW1RTTDJYN1hf QUxTX01BWFRIUkVTSEhJXSA9DQo+ID4gKwkJKGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX3Ro cmVzaF9oaWdoPj4gIDgpJiAgMHhGRjsNCj4gPiArCWNoaXAtPnRzbDJ4N3hfY29uZmlnW1RTTDJY N1hfUEVSU0lTVEVOQ0VdID0NCj4gPiArCQljaGlwLT50c2wyeDd4X3NldHRpbmdzLnBlcnNpc3Rl bmNlOw0KPiA+ICsNCj4gPiArCWNoaXAtPnRzbDJ4N3hfY29uZmlnW1RTTDJYN1hfUFJYX0NPVU5U XSA9DQo+ID4gKwkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MucHJveF9wdWxzZV9jb3VudDsNCj4g PiArCWNoaXAtPnRzbDJ4N3hfY29uZmlnW1RTTDJYN1hfUFJYX01JTlRIUkVTSExPXSA9DQo+ID4g KwljaGlwLT50c2wyeDd4X3NldHRpbmdzLnByb3hfdGhyZXNfbG93Ow0KPiA+ICsJY2hpcC0+dHNs Mng3eF9jb25maWdbVFNMMlg3WF9QUlhfTUFYVEhSRVNITE9dID0NCj4gPiArCQkJY2hpcC0+dHNs Mng3eF9zZXR0aW5ncy5wcm94X3RocmVzX2hpZ2g7DQo+ID4gKw0KPiA+ICsJLyogYW5kIG1ha2Ug c3VyZSB3ZSdyZSBub3QgYWxyZWFkeSBvbiAqLw0KPiA+ICsJaWYgKGNoaXAtPnRzbDJ4N3hfY2hp cF9zdGF0dXMgPT0gVFNMMlg3WF9DSElQX1dPUktJTkcpIHsNCj4gPiArCQkvKiBpZiBmb3JjaW5n IGEgcmVnaXN0ZXIgdXBkYXRlIC0gdHVybiBvZmYsIHRoZW4gb24gKi8NCj4gPiArCQlkZXZfaW5m bygmY2hpcC0+Y2xpZW50LT5kZXYsICJkZXZpY2UgaXMgYWxyZWFkeSBlbmFibGVkXG4iKTsNCj4g PiArCQlyZXR1cm4gLUVJTlZBTDsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwkvKiBkZXRlcm1pbmUg YWxzIGludGVncmF0aW9uIHJlZ3N0ZXIgKi8NCj4gPiArCWFsc19jb3VudCA9IChjaGlwLT50c2wy eDd4X3NldHRpbmdzLmFsc190aW1lICogMTAwICsgMTM1KSAvIDI3MDsNCj4gPiArCWlmIChhbHNf Y291bnQgPT0gMCkNCj4gPiArCQlhbHNfY291bnQgPSAxOyAvKiBlbnN1cmUgYXQgbGVhc3Qgb25l IGN5Y2xlICovDQo+ID4gKw0KPiA+ICsJLyogY29udmVydCBiYWNrIHRvIHRpbWUgKGVuY29tcGFz c2VzIG92ZXJyaWRlcykgKi8NCj4gPiArCWFsc190aW1lID0gKGFsc19jb3VudCAqIDI3ICsgNSkg LyAxMDsNCj4gPiArCWNoaXAtPnRzbDJ4N3hfY29uZmlnW1RTTDJYN1hfQUxTX1RJTUVdID0gMjU2 IC0gYWxzX2NvdW50Ow0KPiA+ICsNCj4gPiArCS8qIFNldCB0aGUgZ2FpbiBiYXNlZCBvbiB0c2wy eDd4X3NldHRpbmdzIHN0cnVjdCAqLw0KPiA+ICsJY2hpcC0+dHNsMng3eF9jb25maWdbVFNMMlg3 WF9HQUlOXSA9DQo+ID4gKwkJKGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dhaW4gfA0KPiA+ ICsJCQkoVFNMMlg3WF9tQTEwMCB8IFRTTDJYN1hfRElPREUxKQ0KPiA+ICsJCQl8ICgoY2hpcC0+ dHNsMng3eF9zZXR0aW5ncy5wcm94X2dhaW4pPDwgIDIpKTsNCj4gPiArDQo+ID4gKwkvKiBzZXQg Y2hpcCBzdHJ1Y3QgcmUgc2NhbGluZyBhbmQgc2F0dXJhdGlvbiAqLw0KPiA+ICsJY2hpcC0+YWxz X3NhdHVyYXRpb24gPSBhbHNfY291bnQgKiA5MjI7IC8qIDkwJSBvZiBmdWxsIHNjYWxlICovDQo+ ID4gKwljaGlwLT5hbHNfdGltZV9zY2FsZSA9IChhbHNfdGltZSArIDI1KSAvIDUwOw0KPiA+ICsN Cj4gPiArCS8qIFRTTDJYN1ggU3BlY2lmaWMgcG93ZXItb24gLyBhZGMgZW5hYmxlIHNlcXVlbmNl DQo+ID4gKwkgKiBQb3dlciBvbiB0aGUgZGV2aWNlIDFzdC4gKi8NCj4gPiArCXV0bXAgPSBUU0wy WDdYX0NOVExfUFdSX09OOw0KPiA+ICsJcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGVfZGF0YShj aGlwLT5jbGllbnQsDQo+ID4gKwkJVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9DTlRSTCwgdXRt cCk7DQo+ID4gKwlpZiAocmV0PCAgMCkgew0KPiA+ICsJCWRldl9lcnIoJmNoaXAtPmNsaWVudC0+ ZGV2LA0KPiA+ICsJCQkiJXM6IGZhaWxlZCBvbiBDTlRSTCByZWcuXG4iLCBfX2Z1bmNfXyk7DQo+ ID4gKwkJcmV0dXJuIC0xOw0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCS8qIFVzZSB0aGUgZm9sbG93 aW5nIHNoYWRvdyBjb3B5IGZvciBvdXIgZGVsYXkgYmVmb3JlIGVuYWJsaW5nIEFEQy4NCj4gPiAr CSAqIFdyaXRlIGFsbCB0aGUgcmVnaXN0ZXJzLiAqLw0KPiA+ICsJZm9yIChpID0gMCwgZGV2X3Jl ZyA9IGNoaXAtPnRzbDJ4N3hfY29uZmlnOw0KPiA+ICsJCQlpPCAgVFNMMlg3WF9NQVhfQ09ORklH X1JFRzsgaSsrKSB7DQo+ID4gKwkJcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGVfZGF0YShjaGlw LT5jbGllbnQsDQo+ID4gKwkJCQlUU0wyWDdYX0NNRF9SRUcgKyBpLCAqZGV2X3JlZysrKTsNCj4g PiArCQlpZiAocmV0PCAgMCkgew0KPiA+ICsJCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwN Cj4gPiArCQkJIiVzOiBmYWlsZWQgb24gd3JpdGUgdG8gcmVnICVkLlxuIiwgX19mdW5jX18sIGkp Ow0KPiA+ICsJCQlyZXR1cm4gcmV0Ow0KPiA+ICsJCX0NCj4gPiArCX0NCj4gPiArDQo+ID4gKwl1 ZGVsYXkoMzAwMCk7CS8qIFBvd2VyLW9uIHNldHRsaW5nIHRpbWUgKi8NCj4gPiArDQo+ID4gKwkv KiBOT1cgZW5hYmxlIHRoZSBBREMNCj4gPiArCSAqIGluaXRpYWxpemUgdGhlIGRlc2lyZWQgbW9k ZSBvZiBvcGVyYXRpb24gKi8NCj4gPiArCXV0bXAgPSBUU0wyWDdYX0NOVExfUFdSX09OIHwNCj4g PiArCQkJVFNMMlg3WF9DTlRMX0FEQ19FTkJMIHwNCj4gPiArCQkJVFNMMlg3WF9DTlRMX1BST1hf REVUX0VOQkw7DQo+ID4gKwlyZXQgPSBpMmNfc21idXNfd3JpdGVfYnl0ZV9kYXRhKGNoaXAtPmNs aWVudCwNCj4gPiArCQkJVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9DTlRSTCwgdXRtcCk7DQo+ ID4gKwlpZiAocmV0PCAgMCkgew0KPiA+ICsJCWRldl9lcnIoJmNoaXAtPmNsaWVudC0+ZGV2LA0K PiA+ICsJCQkiJXM6IGZhaWxlZCBvbiAybmQgQ1RSTCByZWcuXG4iLCBfX2Z1bmNfXyk7DQo+ID4g KwkJcmV0dXJuIHJldDsNCj4gPiArCQl9DQo+IEluZGVudCBvbiB0aGUgYnJhY2tldCBkb2Vzbid0 IGxvb2sgcmlnaHQuLg0KPiA+ICsNCj4gPiArCWNoaXAtPnRzbDJ4N3hfY2hpcF9zdGF0dXMgPSBU U0wyWDdYX0NISVBfV09SS0lORzsNCj4gPiArDQo+ID4gKwlpZiAoY2hpcC0+dHNsMng3eF9zZXR0 aW5ncy5pbnRlcnJ1cHRzX2VuICE9IDApIHsNCj4gPiArCQlkZXZfaW5mbygmY2hpcC0+Y2xpZW50 LT5kZXYsICJTZXR0aW5nIFVwIEludGVycnVwdChzKVxuIik7DQo+ID4gKw0KPiA+ICsJCXJlZ192 YWwgPSBUU0wyWDdYX0NOVExfUFdSX09OIHwNCj4gVFNMMlg3WF9DTlRMX0FEQ19FTkJMOw0KPiA+ ICsJCWlmICgoY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5pbnRlcnJ1cHRzX2VuID09IDB4MjApIHx8 DQo+ID4gKwkJCShjaGlwLT50c2wyeDd4X3NldHRpbmdzLmludGVycnVwdHNfZW4gPT0gMHgzMCkp DQo+ID4gKwkJCXJlZ192YWwgfD0gVFNMMlg3WF9DTlRMX1BST1hfREVUX0VOQkw7DQo+ID4gKw0K PiA+ICsJCXJlZ192YWwgfD0gY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5pbnRlcnJ1cHRzX2VuOw0K PiA+ICsJCXJldCA9IGkyY19zbWJ1c193cml0ZV9ieXRlX2RhdGEoY2hpcC0+Y2xpZW50LA0KPiA+ ICsJCQkoVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9DTlRSTCksIHJlZ192YWwpOw0KPiA+ICsJ CWlmIChyZXQ8ICAwKQ0KPiA+ICsJCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwNCj4gPiAr CQkJCSIlczogZmFpbGVkIGluIHRzbDJ4N3hfSU9DVExfSU5UX1NFVC5cbiIsDQo+ID4gKwkJCQlf X2Z1bmNfXyk7DQo+ID4gKw0KPiA+ICsJCS8qIENsZWFyIG91dCBhbnkgaW5pdGlhbCBpbnRlcnJ1 cHRzICAqLw0KPiA+ICsJCXJldCA9IGkyY19zbWJ1c193cml0ZV9ieXRlKGNoaXAtPmNsaWVudCwN Cj4gPiArCQkJVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9DTURfU1BMX0ZOIHwNCj4gPiArCQkJ VFNMMlg3WF9DTURfUFJPWEFMU19JTlRfQ0xSKTsNCj4gPiArCQlpZiAocmV0PCAgMCkgew0KPiA+ ICsJCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwNCj4gPiArCQkJCSIlczogZmFpbGVkIGlu IHRzbDJ4N3hfY2hpcF9vblxuIiwNCj4gZXJyLiBtZXNzYWdlIHdpbGwgYmUgdHNsMng3eF9jaGlw X29uOiBmYWlsZWQgaW4gdHNsMng3eF9jaGlwX29uDQo+IFNwb3QgdGhlIHJlZHVuZGFudCBpbmZv cm1hdGlvbiENCj4gPiArCQkJCV9fZnVuY19fKTsNCj4gPiArCQlyZXR1cm4gcmV0Ow0KPiA+ICsJ CX0NCj4gPiArCX0NCj4gPiArDQo+ID4gKwlyZXR1cm4gcmV0Ow0KPiA+ICt9DQo+ID4gKw0KPiA+ ICtzdGF0aWMgaW50IHRzbDJ4N3hfY2hpcF9vZmYoc3RydWN0IGlpb19kZXYgKmluZGlvX2RldikN Cj4gPiArew0KPiA+ICsJaW50IHJldDsNCj4gPiArCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAg PSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+ICsNCj4gPiArCS8qIHR1cm4gZGV2aWNlIG9mZiAq Lw0KPiA+ICsJY2hpcC0+dHNsMng3eF9jaGlwX3N0YXR1cyA9IFRTTDJYN1hfQ0hJUF9TVVNQRU5E RUQ7DQo+ID4gKw0KPiA+ICsJcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGVfZGF0YShjaGlwLT5j bGllbnQsDQo+ID4gKwkJVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9DTlRSTCwgMHgwMCk7DQo+ ID4gKw0KPiA+ICsJaWYgKGNoaXAtPnBkYXRhJiYgIGNoaXAtPnBkYXRhLT5wb3dlcl9vZmYpDQo+ ID4gKwkJY2hpcC0+cGRhdGEtPnBvd2VyX29mZihjaGlwLT5jbGllbnQpOw0KPiA+ICsNCj4gPiAr CXJldHVybiByZXQ7DQo+ID4gK30NCj4gPiArDQo+ID4gKy8qDQo+ID4gKyAqIFByb3hpbWl0eSBj YWxpYnJhdGlvbiBoZWxwZXIgZnVuY3Rpb24NCj4gPiArICogcnVucyB0aHJvdWdoIGEgY29sbGVj dGlvbiBvZiBkYXRhIHNhbXBsZXMsDQo+ID4gKyAqIHNldHMgdGhlIG1pbiwgbWF4LCBtZWFuLCBh bmQgc3RkIGRldi4NCj4gPiArICovDQo+ID4gK3N0YXRpYw0KPiA+ICt2b2lkIHRzbDJ4N3hfcHJv eF9jYWxjdWxhdGUodTE2ICpkYXRhLCBpbnQgbGVuZ3RoLCBzdHJ1Y3QgcHJveF9zdGF0ICpzdGF0 UCkNCj4gPiArew0KPiA+ICsJaW50IGk7DQo+ID4gKwlpbnQgc2FtcGxlX21pbiwgc2FtcGxlX21h eCwgc2FtcGxlX3N1bSwgc2FtcGxlX21lYW47DQo+ID4gKwl1bnNpZ25lZCBsb25nIHN0ZGRldjsN Cj4gPiArCWludCB0bXA7DQo+ID4gKw0KPiA+ICsJaWYgKGxlbmd0aCA9PSAwKQ0KPiA+ICsJCWxl bmd0aCA9IDE7DQo+ID4gKw0KPiA+ICsJc2FtcGxlX3N1bSA9IDA7DQo+ID4gKwlzYW1wbGVfbWlu ID0gSU5UX01BWDsNCj4gPiArCXNhbXBsZV9tYXggPSBJTlRfTUlOOw0KPiA+ICsJZm9yIChpID0g MDsgaTwgIGxlbmd0aDsgaSsrKSB7DQo+ID4gKwkJc2FtcGxlX3N1bSArPSBkYXRhW2ldOw0KPiA+ ICsJCWlmIChkYXRhW2ldPCAgc2FtcGxlX21pbikNCj4gPiArCQkJc2FtcGxlX21pbiA9IGRhdGFb aV07DQo+IGtlcm5lbCBoYXMgbWluIGFuZCBtYXggbWFjcm9zLiBVc2UgdGhlbS4gIGUuZy4gc2Ft cGxlX21pbiA9DQo+IG1pbihzYW1wbGVfbWluLCBkYXRhW2ldKTsNCj4gDQo+ID4gKwkJaWYgKGRh dGFbaV0+ICBzYW1wbGVfbWF4KQ0KPiA+ICsJCQlzYW1wbGVfbWF4ID0gZGF0YVtpXTsNCj4gPiAr CX0NCj4gPiArCXNhbXBsZV9tZWFuID0gc2FtcGxlX3N1bS9sZW5ndGg7DQo+ID4gKwlzdGF0UC0+ bWluID0gc2FtcGxlX21pbjsNCj4gPiArCXN0YXRQLT5tYXggPSBzYW1wbGVfbWF4Ow0KPiA+ICsJ c3RhdFAtPm1lYW4gPSBzYW1wbGVfbWVhbjsNCj4gTG9va3MgbGlrZSBhIHRyaXZpYWwgZ2FpbiBp biBoYXZpbmcgbG9jYWwgY29waWVzLCB3aHkgbm90IGp1c3QgdXNlIHRoZQ0KPiBzdGF0UCB2ZXJz aW9ucyBkaXJlY3RseT8NCj4gPiArDQo+ID4gKwlzYW1wbGVfc3VtID0gMDsNCj4gPiArCWZvciAo aSA9IDA7IGk8ICBsZW5ndGg7IGkrKykgew0KPiA+ICsJCXRtcCA9IGRhdGFbaV0tc2FtcGxlX21l YW47DQo+ID4gKwkJc2FtcGxlX3N1bSArPSB0bXAgKiB0bXA7DQo+ID4gKwl9DQo+ID4gKwlzdGRk ZXYgPSBpbnRfc3FydCgobG9uZylzYW1wbGVfc3VtKS9sZW5ndGg7DQo+ID4gKwlzdGF0UC0+c3Rk ZGV2ID0gc3RkZGV2Ow0KPiA+ICt9DQo+ID4gKw0KPiA+ICsvKioNCj4gPiArICogUHJveGltaXR5 IGNhbGlicmF0aW9uIC0gY29sbGVjdHMgYSBudW1iZXIgb2Ygc2FtcGxlcywNCj4gPiArICogY2Fs Y3VsYXRlcyBhIHN0YW5kYXJkIGRldmlhdGlvbiBiYXNlZCBvbiB0aGUgc2FtcGxlcywgYW5kDQo+ ID4gKyAqIHNldHMgdGhlIHRocmVzaG9sZCBhY2NvcmRpbmdseS4NCj4ga2VybmVsLWRvYyBwbGVz ZS4NCj4gPiArICovDQo+ID4gK3N0YXRpYyB2b2lkIHRzbDJ4N3hfcHJveF9jYWwoc3RydWN0IGlp b19kZXYgKmluZGlvX2RldikNCj4gPiArew0KPiA+ICsJdTE2IHByb3hfaGlzdG9yeVtNQVhfU0FN UExFU19DQUwgKyAxXTsNCj4gPiArCWludCBpOw0KPiA+ICsJc3RydWN0IHByb3hfc3RhdCBwcm94 X3N0YXRfZGF0YVsyXTsNCj4gPiArCXN0cnVjdCBwcm94X3N0YXQgKmNhbFA7DQo+ID4gKwlzdHJ1 Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4gPiArCXU4IHRt cF9pcnFfc2V0dGluZ3M7DQo+ID4gKwl1OCBjdXJyZW50X3N0YXRlID0gY2hpcC0+dHNsMng3eF9j aGlwX3N0YXR1czsNCj4gPiArDQo+ID4gKwlpZiAoY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94 X21heF9zYW1wbGVzX2NhbD4gIE1BWF9TQU1QTEVTX0NBTCkNCj4gew0KPiA+ICsJCWRldl9lcnIo JmNoaXAtPmNsaWVudC0+ZGV2LA0KPiA+ICsJCQkiJXM6IG1heCBwcm94IHNhbXBsZXMgY2FsIGlz IHRvbyBiaWc6ICVkXG4iLA0KPiA+ICsJCQlfX2Z1bmNfXywgY2hpcC0NCj4gPnRzbDJ4N3hfc2V0 dGluZ3MucHJveF9tYXhfc2FtcGxlc19jYWwpOw0KPiA+ICsJCWNoaXAtPnRzbDJ4N3hfc2V0dGlu Z3MucHJveF9tYXhfc2FtcGxlc19jYWwgPQ0KPiBNQVhfU0FNUExFU19DQUw7DQo+ID4gKwl9DQo+ ID4gKw0KPiA+ICsJLyogaGF2ZSB0byBzdG9wIHRvIGNoYW5nZSBzZXR0aW5ncyAqLw0KPiA+ICsJ dHNsMng3eF9jaGlwX29mZihpbmRpb19kZXYpOw0KPiA+ICsNCj4gPiArCS8qIEVuYWJsZSBwcm94 aW1pdHkgZGV0ZWN0aW9uIHNhdmUganVzdCBpbiBjYXNlIHByb3ggbm90IHdhbnRlZCB5ZXQqLw0K PiA+ICsJdG1wX2lycV9zZXR0aW5ncyA9IGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuaW50ZXJydXB0 c19lbjsNCj4gPiArCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuaW50ZXJydXB0c19lbiB8PQ0KPiBU U0wyWDdYX0NOVExfUFJPWF9JTlRfRU5CTDsNCj4gPiArDQo+ID4gKwkvKnR1cm4gb24gZGV2aWNl IGlmIG5vdCBhbHJlYWR5IG9uKi8NCj4gPiArCXRzbDJ4N3hfY2hpcF9vbihpbmRpb19kZXYpOw0K PiA+ICsNCj4gPiArCS8qZ2F0aGVyIHRoZSBzYW1wbGVzKi8NCj4gPiArCWZvciAoaSA9IDA7IGk8 ICBjaGlwLT50c2wyeDd4X3NldHRpbmdzLnByb3hfbWF4X3NhbXBsZXNfY2FsOyBpKyspIHsNCj4g PiArCQltZGVsYXkoMTUpOw0KPiA+ICsJCXRzbDJ4N3hfcHJveF9wb2xsKGluZGlvX2Rldik7DQo+ ID4gKwkJcHJveF9oaXN0b3J5W2ldID0gY2hpcC0+cHJveF9kYXRhOw0KPiA+ICsJCWRldl9pbmZv KCZjaGlwLT5jbGllbnQtPmRldiwgIjIgaT0lZCBwcm94IGRhdGE9ICVkXG4iLA0KPiA+ICsJCQlp LCBjaGlwLT5wcm94X2RhdGEpOw0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCXRzbDJ4N3hfY2hpcF9v ZmYoaW5kaW9fZGV2KTsNCj4gPiArCWNhbFAgPSZwcm94X3N0YXRfZGF0YVtQUk9YX1NUQVRfQ0FM XTsNCj4gPiArCXRzbDJ4N3hfcHJveF9jYWxjdWxhdGUocHJveF9oaXN0b3J5LA0KPiA+ICsJCWNo aXAtPnRzbDJ4N3hfc2V0dGluZ3MucHJveF9tYXhfc2FtcGxlc19jYWwsIGNhbFApOw0KPiA+ICsJ Y2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X3RocmVzX2hpZ2ggPSAoY2FsUC0+bWF4PDwgIDEp IC0gY2FsUC0NCj4gPm1lYW47DQo+ID4gKw0KPiA+ICsJZGV2X2luZm8oJmNoaXAtPmNsaWVudC0+ ZGV2LCAiIGNhbCBtaW49JWQgbWVhbj0lZCBtYXg9JWRcbiIsDQo+ID4gKwkJY2FsUC0+bWluLCBj YWxQLT5tZWFuLCBjYWxQLT5tYXgpOw0KPiA+ICsJZGV2X2luZm8oJmNoaXAtPmNsaWVudC0+ZGV2 LA0KPiA+ICsJCSIlcyBwcm94aW1pdHkgdGhyZXNob2xkIHNldCB0byAlZFxuIiwNCj4gPiArCQlj aGlwLT5jbGllbnQtPm5hbWUsIGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MucHJveF90aHJlc19oaWdo KTsNCj4gPiArDQo+ID4gKwkvKiBiYWNrIHRvIHRoZSB3YXkgdGhleSB3ZXJlICovDQo+ID4gKwlj aGlwLT50c2wyeDd4X3NldHRpbmdzLmludGVycnVwdHNfZW4gPSB0bXBfaXJxX3NldHRpbmdzOw0K PiA+ICsJaWYgKGN1cnJlbnRfc3RhdGUgPT0gVFNMMlg3WF9DSElQX1dPUktJTkcpDQo+ID4gKwkJ dHNsMng3eF9jaGlwX29uKGluZGlvX2Rldik7DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBz c2l6ZV90IHRzbDJ4N3hfcG93ZXJfc3RhdGVfc2hvdyhzdHJ1Y3QgZGV2aWNlICpkZXYsDQo+ID4g KwlzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KPiA+ICt7DQo+ID4g KwlzdHJ1Y3QgaWlvX2RldiAqaW5kaW9fZGV2ID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQo+ID4g KwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4gQ291 bGQgZG8gdGhlIGFib3ZlIGluIG9uZSBnby4NCj4gc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9 IGlpb19wcml2KGRldl9nZXRfZHJ2ZGF0YShkZXYpKTsNClRoYW5rcyBmb3IgdGhlIHRpcCENCg0K PiBMb3RzIG1vcmUgY2FzZXMgb2YgdGhpcyBiZWxvdy4NCj4gPiArDQo+ID4gKwlyZXR1cm4gc25w cmludGYoYnVmLCBQQUdFX1NJWkUsICIlZFxuIiwgY2hpcC0+dHNsMng3eF9jaGlwX3N0YXR1cyk7 DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBzc2l6ZV90IHRzbDJ4N3hfcG93ZXJfc3RhdGVf c3RvcmUoc3RydWN0IGRldmljZSAqZGV2LA0KPiA+ICsJc3RydWN0IGRldmljZV9hdHRyaWJ1dGUg KmF0dHIsIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGxlbikNCj4gPiArew0KPiA+ICsJc3RydWN0 IGlpb19kZXYgKmluZGlvX2RldiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KPiA+ICsJYm9vbCB2 YWx1ZTsNCj4gPiArDQo+ID4gKwlpZiAoc3RydG9ib29sKGJ1ZiwmdmFsdWUpKQ0KPiA+ICsJCXJl dHVybiAtRUlOVkFMOw0KPiA+ICsNCj4gU2VlbXMgdG8gbWUgdGhhdCBpbnZlcnRpbmcgdGhlIGxv Z2ljIG9mIHRoaXMgaWYgd291bGQgbWFrZSB0aGluZ3MNCj4gYSB0aW55IGJpdCBlYXNpZXIgdG8g cmVhZC4NCj4gPiArCWlmICghdmFsdWUpDQo+ID4gKwkJdHNsMng3eF9jaGlwX29mZihpbmRpb19k ZXYpOw0KPiA+ICsJZWxzZQ0KPiA+ICsJCXRzbDJ4N3hfY2hpcF9vbihpbmRpb19kZXYpOw0KPiA+ ICsNCj4gPiArCXJldHVybiBsZW47DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBzc2l6ZV90 IHRzbDJ4N3hfZ2Fpbl9hdmFpbGFibGVfc2hvdyhzdHJ1Y3QgZGV2aWNlICpkZXYsDQo+ID4gKwlz dHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KPiA+ICt7DQo+ID4gKwlz dHJ1Y3QgaWlvX2RldiAqaW5kaW9fZGV2ID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQo+ID4gKwlz dHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4gPiArDQo+ ID4gKwlpZiAoY2hpcC0+aWQ+ICB0c2wyNzcxKQ0KPiBTd2l0Y2ggb24gcGFydG51bWJlcnMgcHJl ZmVycmVkLg0KPiA+ICsJCXJldHVybiBzbnByaW50ZihidWYsIFBBR0VfU0laRSwgIiVzXG4iLCAi MSA4IDE2IDEyOCIpOw0KPiA+ICsJZWxzZQ0KPiA+ICsJCXJldHVybiBzbnByaW50ZihidWYsIFBB R0VfU0laRSwgIiVzXG4iLCAiMSA4IDE2IDEyMCIpOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0 aWMgc3NpemVfdCB0c2wyeDd4X3Byb3hfZ2Fpbl9hdmFpbGFibGVfc2hvdyhzdHJ1Y3QgZGV2aWNl ICpkZXYsDQo+ID4gKwlzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0K PiA+ICt7DQo+ID4gKwkJcmV0dXJuIHNucHJpbnRmKGJ1ZiwgUEFHRV9TSVpFLCAiJXNcbiIsICIx IDIgNCA4Iik7DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBzc2l6ZV90IHRzbDJ4N3hfYWxz X3RpbWVfc2hvdyhzdHJ1Y3QgZGV2aWNlICpkZXYsDQo+ID4gKwlzdHJ1Y3QgZGV2aWNlX2F0dHJp YnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KPiA+ICt7DQo+ID4gKwlzdHJ1Y3QgaWlvX2RldiAqaW5k aW9fZGV2ID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQo+ID4gKwlzdHJ1Y3QgdHNsMlg3WF9jaGlw ICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4gPiArDQo+ID4gKwlyZXR1cm4gc25wcmlu dGYoYnVmLCBQQUdFX1NJWkUsICIlZFxuIiwNCj4gPiArCQkJY2hpcC0+dHNsMng3eF9zZXR0aW5n cy5hbHNfdGltZSk7DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBzc2l6ZV90IHRzbDJ4N3hf YWxzX3RpbWVfc3RvcmUoc3RydWN0IGRldmljZSAqZGV2LA0KPiA+ICsJc3RydWN0IGRldmljZV9h dHRyaWJ1dGUgKmF0dHIsIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGxlbikNCj4gPiArew0KPiA+ ICsJc3RydWN0IGlpb19kZXYgKmluZGlvX2RldiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KPiA+ ICsJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlpb19wcml2KGluZGlvX2Rldik7DQo+ID4g Kwl1bnNpZ25lZCBsb25nIHZhbHVlOw0KPiA+ICsNCj4gPiArCWlmIChrc3RydG91bChidWYsIDAs JnZhbHVlKSkNCj4gPiArCQlyZXR1cm4gLUVJTlZBTDsNCj4gPiArDQo+ID4gKwlpZiAoKHZhbHVl PCAgNTApIHx8ICh2YWx1ZT4gIDY1MCkpDQo+ID4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ID4gKw0K PiA+ICsJaWYgKHZhbHVlICUgNTApDQo+ID4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ID4gKw0KPiA+ ICsJIGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX3RpbWUgPSB2YWx1ZTsNCj4gPiArDQo+ID4g KwlyZXR1cm4gbGVuOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgSUlPX0NPTlNUX0FUVFIo aWxsdW1pbmFuY2UwX2ludGVncmF0aW9uX3RpbWVfYXZhaWxhYmxlLA0KPiA+ICsJCSI1MCAxMDAg MTUwIDIwMCAyNTAgMzAwIDM1MCA0MDAgNDUwIDUwMCA1NTAgNjAwIDY1MCIpOw0KPiA+ICsNCj4g PiArc3RhdGljIHNzaXplX3QgdHNsMng3eF9hbHNfY2FsX3RhcmdldF9zaG93KHN0cnVjdCBkZXZp Y2UgKmRldiwNCj4gPiArCXN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICphdHRyLCBjaGFyICpidWYp DQo+ID4gK3sNCj4gPiArCXN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYgPSBkZXZfZ2V0X2RydmRh dGEoZGV2KTsNCj4gPiArCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJpdihpbmRp b19kZXYpOw0KPiA+ICsNCj4gPiArCXJldHVybiBzbnByaW50ZihidWYsIFBBR0VfU0laRSwgIiVk XG4iLA0KPiA+ICsJCQljaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc19jYWxfdGFyZ2V0KTsNCj4g PiArfQ0KPiA+ICsNCj4gPiArc3RhdGljIHNzaXplX3QgdHNsMng3eF9hbHNfY2FsX3RhcmdldF9z dG9yZShzdHJ1Y3QgZGV2aWNlICpkZXYsDQo+ID4gKwlzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAq YXR0ciwgY29uc3QgY2hhciAqYnVmLCBzaXplX3QgbGVuKQ0KPiA+ICt7DQo+ID4gKwlzdHJ1Y3Qg aWlvX2RldiAqaW5kaW9fZGV2ID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQo+ID4gKwlzdHJ1Y3Qg dHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4gPiArCXVuc2lnbmVk IGxvbmcgdmFsdWU7DQo+ID4gKw0KPiA+ICsJaWYgKGtzdHJ0b3VsKGJ1ZiwgMCwmdmFsdWUpKQ0K PiA+ICsJCXJldHVybiAtRUlOVkFMOw0KPiA+ICsNCj4gPiArCWlmICh2YWx1ZSkNCj4gPiArCQlj aGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc19jYWxfdGFyZ2V0ID0gdmFsdWU7DQo+ID4gKw0KPiA+ ICsJcmV0dXJuIGxlbjsNCj4gPiArfQ0KPiA+ICsNCj4gPiArLyogc2FtcGxpbmdfZnJlcXVlbmN5 IEFLQSBwZXJzaXN0ZW5jZSBpbiBkYXRhIHNoZWV0ICovDQo+ID4gK3N0YXRpYyBzc2l6ZV90IHRz bDJ4N3hfcGVyc2lzdGVuY2Vfc2hvdyhzdHJ1Y3QgZGV2aWNlICpkZXYsDQo+ID4gKwlzdHJ1Y3Qg ZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KPiA+ICt7DQo+ID4gKwlzdHJ1Y3Qg aWlvX2RldiAqaW5kaW9fZGV2ID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQo+ID4gKwlzdHJ1Y3Qg dHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4gPiArDQo+ID4gKwly ZXR1cm4gc25wcmludGYoYnVmLCBQQUdFX1NJWkUsICIlZFxuIiwNCj4gPiArCQkJY2hpcC0+dHNs Mng3eF9zZXR0aW5ncy5wZXJzaXN0ZW5jZSk7DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBz c2l6ZV90IHRzbDJ4N3hfcGVyc2lzdGVuY2Vfc3RvcmUoc3RydWN0IGRldmljZSAqZGV2LA0KPiA+ ICsJc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmF0dHIsIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90 IGxlbikNCj4gPiArew0KPiA+ICsJc3RydWN0IGlpb19kZXYgKmluZGlvX2RldiA9IGRldl9nZXRf ZHJ2ZGF0YShkZXYpOw0KPiA+ICsJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlpb19wcml2 KGluZGlvX2Rldik7DQo+ID4gKwl1bnNpZ25lZCBsb25nIHZhbHVlOw0KPiA+ICsNCj4gPiArCWlm IChrc3RydG91bChidWYsIDAsJnZhbHVlKSkNCj4gPiArCQlyZXR1cm4gLUVJTlZBTDsNCj4gPiAr DQo+ID4gKwljaGlwLT50c2wyeDd4X3NldHRpbmdzLnBlcnNpc3RlbmNlID0gdmFsdWU7DQo+ID4g Kw0KPiA+ICsJcmV0dXJuIGxlbjsNCj4gPiArfQ0KPiA+ICsNCj4gPiArc3RhdGljIElJT19DT05T VF9BVFRSKHNhbXBsaW5nX2ZyZXF1ZW5jeV9hdmFpbGFibGUsDQo+ID4gKwkJIjB4MDAgLSAweEZG ICgwIC0gMjU1KSIpOw0KPiBXaGF0IHVuaXRzPyAgVGhpcyByZWFsbHkgc2hvdWxkIGJlIGNvbnZl cnRlZCBpbnRvIGh6IGV2ZW4gaWYgaXQncw0KPiBzb21ld2hhdCBvZiBhIHBhaW4gdG8gZG8uDQo+ ID4gKw0KPiA+ICtzdGF0aWMgc3NpemVfdCB0c2wyeDd4X2RvX2NhbGlicmF0ZShzdHJ1Y3QgZGV2 aWNlICpkZXYsDQo+ID4gKwlzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY29uc3QgY2hh ciAqYnVmLCBzaXplX3QgbGVuKQ0KPiA+ICt7DQo+ID4gKwlzdHJ1Y3QgaWlvX2RldiAqaW5kaW9f ZGV2ID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQo+ID4gKwlib29sIHZhbHVlOw0KPiA+ICsNCj4g PiArCWlmIChzdHJ0b2Jvb2woYnVmLCZ2YWx1ZSkpDQo+ID4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ ID4gKw0KPiA+ICsJaWYgKHZhbHVlKQ0KPiA+ICsJCXRzbDJ4N3hfYWxzX2NhbGlicmF0ZShpbmRp b19kZXYpOw0KPiA+ICsNCj4gPiArCXJldHVybiBsZW47DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0 YXRpYyBzc2l6ZV90IHRzbDJ4N3hfbHV4dGFibGVfc2hvdyhzdHJ1Y3QgZGV2aWNlICpkZXYsDQo+ ID4gKwlzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KPiA+ICt7DQo+ ID4gKwlzdHJ1Y3QgaWlvX2RldiAqaW5kaW9fZGV2ID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQo+ ID4gKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4g PiArCWludCBpID0gMDsNCj4gPiArCWludCBvZmZzZXQgPSAwOw0KPiA+ICsNCj4gPiArCXdoaWxl IChpPCAgKFRTTDJYN1hfTUFYX0xVWF9UQUJMRV9TSVpFICogMykpIHsNCj4gPiArCQlvZmZzZXQg Kz0gc25wcmludGYoYnVmICsgb2Zmc2V0LCBQQUdFX1NJWkUsICIlZCwlZCwlZCwiLA0KPiA+ICsJ CQljaGlwLT50c2wyeDd4X2RldmljZV9sdXhbaV0ucmF0aW8sDQo+ID4gKwkJCWNoaXAtPnRzbDJ4 N3hfZGV2aWNlX2x1eFtpXS5jaDAsDQo+ID4gKwkJCWNoaXAtPnRzbDJ4N3hfZGV2aWNlX2x1eFtp XS5jaDEpOw0KPiA+ICsJCWlmIChjaGlwLT50c2wyeDd4X2RldmljZV9sdXhbaV0ucmF0aW8gPT0g MCkgew0KPiA+ICsJCQkvKiBXZSBqdXN0IHByaW50ZWQgdGhlIGZpcnN0ICIwIiBlbnRyeS4NCj4g PiArCQkJICogTm93IGdldCByaWQgb2YgdGhlIGV4dHJhICIsIiBhbmQgYnJlYWsuICovDQo+ID4g KwkJCW9mZnNldC0tOw0KPiA+ICsJCQlicmVhazsNCj4gPiArCQl9DQo+ID4gKwkJaSsrOw0KPiA+ ICsJfQ0KPiA+ICsNCj4gPiArCW9mZnNldCArPSBzbnByaW50ZihidWYgKyBvZmZzZXQsIFBBR0Vf U0laRSwgIlxuIik7DQo+ID4gKwlyZXR1cm4gb2Zmc2V0Ow0KPiA+ICt9DQo+ID4gKw0KPiA+ICtz dGF0aWMgc3NpemVfdCB0c2wyeDd4X2x1eHRhYmxlX3N0b3JlKHN0cnVjdCBkZXZpY2UgKmRldiwN Cj4gPiArCXN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICphdHRyLCBjb25zdCBjaGFyICpidWYsIHNp emVfdCBsZW4pDQo+ID4gK3sNCj4gPiArCXN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYgPSBkZXZf Z2V0X2RydmRhdGEoZGV2KTsNCj4gPiArCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9f cHJpdihpbmRpb19kZXYpOw0KPiA+ICsJaW50IHZhbHVlW0FSUkFZX1NJWkUoY2hpcC0+dHNsMng3 eF9kZXZpY2VfbHV4KSozICsgMV07DQo+ID4gKwlpbnQgbjsNCj4gPiArDQo+ID4gKwlnZXRfb3B0 aW9ucyhidWYsIEFSUkFZX1NJWkUodmFsdWUpLCB2YWx1ZSk7DQo+ID4gKw0KPiA+ICsJLyogV2Ug bm93IGhhdmUgYW4gYXJyYXkgb2YgaW50cyBzdGFydGluZyBhdCB2YWx1ZVsxXSwgYW5kDQo+ID4g KwkgKiBlbnVtZXJhdGVkIGJ5IHZhbHVlWzBdLg0KPiA+ICsJICogV2UgZXhwZWN0IGVhY2ggZ3Jv dXAgb2YgdGhyZWUgaW50cyBpcyBvbmUgdGFibGUgZW50cnksDQo+ID4gKwkgKiBhbmQgdGhlIGxh c3QgdGFibGUgZW50cnkgaXMgYWxsIDAuDQo+ID4gKwkgKi8NCj4gPiArCW4gPSB2YWx1ZVswXTsN Cj4gPiArCWlmICgobiAlIDMpIHx8IG48ICA2IHx8DQo+ID4gKwkJCW4+ICAoKEFSUkFZX1NJWkUo Y2hpcC0+dHNsMng3eF9kZXZpY2VfbHV4KSAtIDEpICogMykpIHsNCj4gPiArCQlkZXZfaW5mbyhk ZXYsICJMVVggVEFCTEUgSU5QVVQgRVJST1IgMSBWYWx1ZVswXT0lZFxuIiwgbik7DQo+ID4gKwkJ cmV0dXJuIC1FSU5WQUw7DQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJaWYgKCh2YWx1ZVsobiAtIDIp XSB8IHZhbHVlWyhuIC0gMSldIHwgdmFsdWVbbl0pICE9IDApIHsNCj4gPiArCQlkZXZfaW5mbyhk ZXYsICJMVVggVEFCTEUgSU5QVVQgRVJST1IgMiBWYWx1ZVswXT0lZFxuIiwgbik7DQo+ID4gKwkJ cmV0dXJuIC1FSU5WQUw7DQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJaWYgKGNoaXAtPnRzbDJ4N3hf Y2hpcF9zdGF0dXMgPT0gVFNMMlg3WF9DSElQX1dPUktJTkcpDQo+ID4gKwkJdHNsMng3eF9jaGlw X29mZihpbmRpb19kZXYpOw0KPiA+ICsNCj4gPiArCS8qIFplcm8gb3V0IHRoZSB0YWJsZSAqLw0K PiA+ICsJbWVtc2V0KGNoaXAtPnRzbDJ4N3hfZGV2aWNlX2x1eCwgMCwgc2l6ZW9mKGNoaXAtPnRz bDJ4N3hfZGV2aWNlX2x1eCkpOw0KPiA+ICsJbWVtY3B5KGNoaXAtPnRzbDJ4N3hfZGV2aWNlX2x1 eCwmdmFsdWVbMV0sICh2YWx1ZVswXSAqIDQpKTsNCj4gPiArDQo+ID4gKwlyZXR1cm4gbGVuOw0K PiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgc3NpemVfdCB0c2wyeDd4X2RvX3Byb3hfY2FsaWJy YXRlKHN0cnVjdCBkZXZpY2UgKmRldiwNCj4gPiArCXN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICph dHRyLCBjb25zdCBjaGFyICpidWYsIHNpemVfdCBsZW4pDQo+ID4gK3sNCj4gPiArCXN0cnVjdCBp aW9fZGV2ICppbmRpb19kZXYgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsNCj4gPiArCWJvb2wgdmFs dWU7DQo+ID4gKw0KPiA+ICsJaWYgKHN0cnRvYm9vbChidWYsJnZhbHVlKSkNCj4gPiArCQlyZXR1 cm4gLUVJTlZBTDsNCj4gPiArDQo+ID4gKwlpZiAodmFsdWUpDQo+ID4gKwkJdHNsMng3eF9wcm94 X2NhbChpbmRpb19kZXYpOw0KPiA+ICsNCj4gPiArCXJldHVybiBsZW47DQo+ID4gK30NCj4gPiAr DQo+ID4gK3N0YXRpYyBpbnQgdHNsMng3eF9yZWFkX2ludGVycnVwdF9jb25maWcoc3RydWN0IGlp b19kZXYgKmluZGlvX2RldiwNCj4gPiArCQkJCQkgdTY0IGV2ZW50X2NvZGUpDQo+ID4gK3sNCj4g PiArCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+ ICsJaW50IHJldDsNCj4gPiArDQo+ID4gKwlpZiAoSUlPX0VWRU5UX0NPREVfRVhUUkFDVF9DSEFO X1RZUEUoZXZlbnRfY29kZSkgPT0NCj4gSUlPX0lOVEVOU0lUWSkNCj4gPiArCQlyZXQgPSAhIShj aGlwLT50c2wyeDd4X3NldHRpbmdzLmludGVycnVwdHNfZW4mICAweDEwKTsNCj4gPiArCWVsc2UN Cj4gPiArCQlyZXQgPSAhIShjaGlwLT50c2wyeDd4X3NldHRpbmdzLmludGVycnVwdHNfZW4mICAw eDIwKTsNCj4gPiArDQo+ID4gKwlyZXR1cm4gcmV0Ow0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0 aWMgaW50IHRzbDJ4N3hfd3JpdGVfaW50ZXJydXB0X2NvbmZpZyhzdHJ1Y3QgaWlvX2RldiAqaW5k aW9fZGV2LA0KPiA+ICsJCQkJCSAgdTY0IGV2ZW50X2NvZGUsDQo+ID4gKwkJCQkJICBpbnQgdmFs KQ0KPiA+ICt7DQo+ID4gKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5k aW9fZGV2KTsNCj4gPiArDQo+ID4gKwlpZiAoSUlPX0VWRU5UX0NPREVfRVhUUkFDVF9DSEFOX1RZ UEUoZXZlbnRfY29kZSkgPT0NCj4gSUlPX0lOVEVOU0lUWSkgew0KPiA+ICsJCWlmICh2YWwpDQo+ ID4gKwkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuaW50ZXJydXB0c19lbiB8PSAweDEwOw0KPiA+ ICsJCWVsc2UNCj4gPiArCQkJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5pbnRlcnJ1cHRzX2VuJj0g MHgyMDsNCj4gPiArCX0gZWxzZSB7DQo+ID4gKwkJaWYgKHZhbCkNCj4gPiArCQkJY2hpcC0+dHNs Mng3eF9zZXR0aW5ncy5pbnRlcnJ1cHRzX2VuIHw9IDB4MjA7DQo+ID4gKwkJZWxzZQ0KPiA+ICsJ CQljaGlwLT50c2wyeDd4X3NldHRpbmdzLmludGVycnVwdHNfZW4mPSAweDEwOw0KPiA+ICsJfQ0K PiBXb3VsZCBub3JtYWxseSBleHBlY3QgdGhpcyB0byB3cml0ZSB0aGUgc2V0dGluZ3MgdG8gdGhl IGRldmljZS4NCkFueSBjaGFuZ2VzIHRvIHBhcmFtZXRlcnMgcmVxdWlyZSBkZXZpY2Ugb2ZmL29u Lg0KPiA+ICsNCj4gPiArCXJldHVybiAwOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW50 IHRzbDJ4N3hfd3JpdGVfdGhyZXNoKHN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYsDQo+ID4gKwkJ CQkgIHU2NCBldmVudF9jb2RlLA0KPiA+ICsJCQkJICBpbnQgdmFsKQ0KPiA+ICt7DQo+ID4gKwlz dHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4gPiArDQo+ ID4gKwlpZiAoSUlPX0VWRU5UX0NPREVfRVhUUkFDVF9DSEFOX1RZUEUoZXZlbnRfY29kZSkgPT0N Cj4gSUlPX0lOVEVOU0lUWSkgew0KPiA+ICsJCXN3aXRjaCAoSUlPX0VWRU5UX0NPREVfRVhUUkFD VF9ESVIoZXZlbnRfY29kZSkpIHsNCj4gPiArCQljYXNlIElJT19FVl9ESVJfUklTSU5HOg0KPiA+ ICsJCQljaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc190aHJlc2hfaGlnaCA9IHZhbDsNCj4gPiAr CQkJYnJlYWs7DQo+ID4gKwkJY2FzZSBJSU9fRVZfRElSX0ZBTExJTkc6DQo+ID4gKwkJCWNoaXAt PnRzbDJ4N3hfc2V0dGluZ3MuYWxzX3RocmVzaF9sb3cgPSB2YWw7DQo+ID4gKwkJCWJyZWFrOw0K PiA+ICsJCWRlZmF1bHQ6DQo+ID4gKwkJCXJldHVybiAtRUlOVkFMOw0KPiA+ICsJCX0NCj4gPiAr CX0gZWxzZSB7DQo+ID4gKwkJc3dpdGNoIChJSU9fRVZFTlRfQ09ERV9FWFRSQUNUX0RJUihldmVu dF9jb2RlKSkgew0KPiA+ICsJCWNhc2UgSUlPX0VWX0RJUl9SSVNJTkc6DQo+ID4gKwkJCWNoaXAt PnRzbDJ4N3hfc2V0dGluZ3MucHJveF90aHJlc19oaWdoID0gdmFsOw0KPiA+ICsJCQlicmVhazsN Cj4gPiArCQljYXNlIElJT19FVl9ESVJfRkFMTElORzoNCj4gPiArCQkJY2hpcC0+dHNsMng3eF9z ZXR0aW5ncy5wcm94X3RocmVzX2xvdyA9IHZhbDsNCj4gPiArCQkJYnJlYWs7DQo+ID4gKwkJZGVm YXVsdDoNCj4gPiArCQkJcmV0dXJuIC1FSU5WQUw7DQo+ID4gKwkJfQ0KPiA+ICsJfQ0KPiA+ICsN Cj4gPiArCXJldHVybiAwOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW50IHRzbDJ4N3hf cmVhZF90aHJlc2goc3RydWN0IGlpb19kZXYgKmluZGlvX2RldiwNCj4gPiArCQkJICAgICAgIHU2 NCBldmVudF9jb2RlLA0KPiA+ICsJCQkgICAgICAgaW50ICp2YWwpDQo+ID4gK3sNCj4gPiArCXN0 cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+ICsNCj4g PiArCWlmIChJSU9fRVZFTlRfQ09ERV9FWFRSQUNUX0NIQU5fVFlQRShldmVudF9jb2RlKSA9PQ0K PiBJSU9fSU5URU5TSVRZKSB7DQo+ID4gKwkJc3dpdGNoIChJSU9fRVZFTlRfQ09ERV9FWFRSQUNU X0RJUihldmVudF9jb2RlKSkgew0KPiA+ICsJCWNhc2UgSUlPX0VWX0RJUl9SSVNJTkc6DQo+ID4g KwkJCSp2YWwgPSBjaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc190aHJlc2hfaGlnaDsNCj4gPiAr CQkJYnJlYWs7DQo+ID4gKwkJY2FzZSBJSU9fRVZfRElSX0ZBTExJTkc6DQo+ID4gKwkJCSp2YWwg PSBjaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc190aHJlc2hfbG93Ow0KPiA+ICsJCQlicmVhazsN Cj4gPiArCQlkZWZhdWx0Og0KPiA+ICsJCQlyZXR1cm4gLUVJTlZBTDsNCj4gPiArCQl9DQo+ID4g Kwl9IGVsc2Ugew0KPiA+ICsJCXN3aXRjaCAoSUlPX0VWRU5UX0NPREVfRVhUUkFDVF9ESVIoZXZl bnRfY29kZSkpIHsNCj4gPiArCQljYXNlIElJT19FVl9ESVJfUklTSU5HOg0KPiA+ICsJCQkqdmFs ID0gY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X3RocmVzX2hpZ2g7DQo+ID4gKwkJCWJyZWFr Ow0KPiA+ICsJCWNhc2UgSUlPX0VWX0RJUl9GQUxMSU5HOg0KPiA+ICsJCQkqdmFsID0gY2hpcC0+ dHNsMng3eF9zZXR0aW5ncy5wcm94X3RocmVzX2xvdzsNCj4gPiArCQkJYnJlYWs7DQo+ID4gKwkJ ZGVmYXVsdDoNCj4gPiArCQkJcmV0dXJuIC1FSU5WQUw7DQo+ID4gKwkJfQ0KPiA+ICsJfQ0KPiA+ ICsNCj4gPiArCXJldHVybiAwOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW50IHRzbDJ4 N3hfcmVhZF9yYXcoc3RydWN0IGlpb19kZXYgKmluZGlvX2RldiwNCj4gPiArCQkJICAgIHN0cnVj dCBpaW9fY2hhbl9zcGVjIGNvbnN0ICpjaGFuLA0KPiA+ICsJCQkgICAgaW50ICp2YWwsDQo+ID4g KwkJCSAgICBpbnQgKnZhbDIsDQo+ID4gKwkJCSAgICBsb25nIG1hc2spDQo+ID4gK3sNCj4gPiAr CWludCByZXQgPSAtRUlOVkFMOw0KPiA+ICsJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlp b19wcml2KGluZGlvX2Rldik7DQo+ID4gKw0KPiA+ICsJc3dpdGNoIChtYXNrKSB7DQo+ID4gKwlj YXNlIDA6DQo+ID4gKwkJc3dpdGNoIChjaGFuLT50eXBlKSB7DQo+ID4gKwkJY2FzZSBJSU9fTElH SFQ6DQo+ID4gKwkJCXRzbDJ4N3hfZ2V0X2x1eChpbmRpb19kZXYpOw0KPiA+ICsJCQkqdmFsID0g Y2hpcC0+YWxzX2N1cl9pbmZvLmx1eDsNCj4gPiArCQkJcmV0ID0gSUlPX1ZBTF9JTlQ7DQo+ID4g KwkJCWJyZWFrOw0KPiA+ICsJCWNhc2UgSUlPX0lOVEVOU0lUWToNCj4gPiArCQkJdHNsMng3eF9n ZXRfbHV4KGluZGlvX2Rldik7DQo+ID4gKwkJCWlmIChjaGFuLT5jaGFubmVsID09IDApDQo+ID4g KwkJCQkqdmFsID0gY2hpcC0+YWxzX2N1cl9pbmZvLmFsc19jaDA7DQo+ID4gKwkJCWVsc2UNCj4g PiArCQkJCSp2YWwgPSBjaGlwLT5hbHNfY3VyX2luZm8uYWxzX2NoMTsNCj4gPiArCQkJcmV0ID0g SUlPX1ZBTF9JTlQ7DQo+ID4gKwkJCWJyZWFrOw0KPiA+ICsJCWNhc2UgSUlPX1BST1hJTUlUWToN Cj4gPiArCQkJdHNsMng3eF9wcm94X3BvbGwoaW5kaW9fZGV2KTsNCj4gPiArCQkJKnZhbCA9IGNo aXAtPnByb3hfZGF0YTsNCj4gPiArCQkJcmV0ID0gSUlPX1ZBTF9JTlQ7DQo+ID4gKwkJCWJyZWFr Ow0KPiA+ICsJCWRlZmF1bHQ6DQo+ID4gKwkJCXJldHVybiAtRUlOVkFMOw0KPiA+ICsJCQlicmVh azsNCj4gPiArCQl9DQo+ID4gKwkJYnJlYWs7DQo+ID4gKwljYXNlIElJT19DSEFOX0lORk9fQ0FM SUJTQ0FMRToNCj4gPiArCQlpZiAoY2hhbi0+dHlwZSA9PSBJSU9fTElHSFQpDQo+ID4gKwkJCSp2 YWwgPQ0KPiA+ICsJCQl0c2wyWDdYX2Fsc19nYWluYWRqW2NoaXAtPnRzbDJ4N3hfc2V0dGluZ3Mu YWxzX2dhaW5dOw0KPiA+ICsJCWVsc2UNCj4gPiArCQkJKnZhbCA9DQo+ID4gKwkJCXRzbDJYN1hf cHJ4X2dhaW5hZGpbY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X2dhaW5dOw0KPiA+ICsJCXJl dCA9IElJT19WQUxfSU5UOw0KPiA+ICsJCWJyZWFrOw0KPiA+ICsJY2FzZSBJSU9fQ0hBTl9JTkZP X0NBTElCQklBUzoNCj4gPiArCQkqdmFsID0gY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfZ2Fp bl90cmltOw0KPiA+ICsJCXJldCA9IElJT19WQUxfSU5UOw0KPiA+ICsJCWJyZWFrOw0KPiA+ICsN Cj4gPiArCWRlZmF1bHQ6DQo+ID4gKwkJcmV0ID0gLUVJTlZBTDsNCj4gPiArCX0NCj4gPiArDQo+ ID4gKwlyZXR1cm4gcmV0Ow0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW50IHRzbDJ4N3hf d3JpdGVfcmF3KHN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYsDQo+ID4gKwkJCSAgICAgICBzdHJ1 Y3QgaWlvX2NoYW5fc3BlYyBjb25zdCAqY2hhbiwNCj4gPiArCQkJICAgICAgIGludCB2YWwsDQo+ ID4gKwkJCSAgICAgICBpbnQgdmFsMiwNCj4gPiArCQkJICAgICAgIGxvbmcgbWFzaykNCj4gPiAr ew0KPiA+ICsJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlpb19wcml2KGluZGlvX2Rldik7 DQo+ID4gKw0KPiA+ICsJc3dpdGNoIChtYXNrKSB7DQo+ID4gKwljYXNlIElJT19DSEFOX0lORk9f Q0FMSUJTQ0FMRToNCj4gPiArCQlpZiAoY2hhbi0+dHlwZSA9PSBJSU9fSU5URU5TSVRZKSB7DQo+ ID4gKwkJCXN3aXRjaCAodmFsKSB7DQo+ID4gKwkJCWNhc2UgMToNCj4gPiArCQkJCWNoaXAtPnRz bDJ4N3hfc2V0dGluZ3MuYWxzX2dhaW4gPSAwOw0KPiA+ICsJCQkJYnJlYWs7DQo+ID4gKwkJCWNh c2UgODoNCj4gPiArCQkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dhaW4gPSAxOw0KPiA+ ICsJCQkJYnJlYWs7DQo+ID4gKwkJCWNhc2UgMTY6DQo+ID4gKwkJCQljaGlwLT50c2wyeDd4X3Nl dHRpbmdzLmFsc19nYWluID0gMjsNCj4gPiArCQkJCWJyZWFrOw0KPiA+ICsJCQljYXNlIDEyMDoN Cj4gYWdhaW4sIHdvdWxkIHByZWZlciBhIHN3aXRjaCBvbiB0aGUgY2hpcC0+aWQncyBpbiBxdWVz dGlvbiB0byByZWx5aW5nIG9uDQo+IG9yZGVyaW5nIGluIHRoZSBhcnJheSBvZiBjaGlwIGlkcy4g IFllcyBpdCdzIG1vcmUgY29kZSwgYnV0IGxlc3MgZnJhZ2lsZS4uLg0KPiA+ICsJCQkJaWYgKGNo aXAtPmlkPiAgdHNsMjc3MSkNCj4gPiArCQkJCQlyZXR1cm4gLUVJTlZBTDsNCj4gPiArCQkJCWNo aXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dhaW4gPSAzOw0KPiA+ICsJCQkJYnJlYWs7DQo+ID4g KwkJCWNhc2UgMTI4Og0KPiA+ICsJCQkJaWYgKGNoaXAtPmlkPCAgdHNsMjU3MikNCj4gPiArCQkJ CQlyZXR1cm4gLUVJTlZBTDsNCj4gPiArCQkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dh aW4gPSAzOw0KPiA+ICsJCQkJYnJlYWs7DQo+ID4gKwkJCWRlZmF1bHQ6DQo+ID4gKwkJCQlyZXR1 cm4gLUVJTlZBTDsNCj4gPiArCQkJfQ0KPiA+ICsJCX0gZWxzZSB7DQo+ID4gKwkJCXN3aXRjaCAo dmFsKSB7DQo+ID4gKwkJCWNhc2UgMToNCj4gPiArCQkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3Mu cHJveF9nYWluID0gMDsNCj4gPiArCQkJCWJyZWFrOw0KPiA+ICsJCQljYXNlIDI6DQo+ID4gKwkJ CQljaGlwLT50c2wyeDd4X3NldHRpbmdzLnByb3hfZ2FpbiA9IDE7DQo+ID4gKwkJCQlicmVhazsN Cj4gPiArCQkJY2FzZSA0Og0KPiA+ICsJCQkJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X2dh aW4gPSAyOw0KPiA+ICsJCQkJYnJlYWs7DQo+ID4gKwkJCWNhc2UgODoNCj4gPiArCQkJCWNoaXAt PnRzbDJ4N3hfc2V0dGluZ3MucHJveF9nYWluID0gMzsNCj4gPiArCQkJCWJyZWFrOw0KPiA+ICsJ CQlkZWZhdWx0Og0KPiA+ICsJCQkJcmV0dXJuIC1FSU5WQUw7DQo+ID4gKwkJCX0NCj4gPiArCQl9 DQo+ID4gKwkJYnJlYWs7DQo+ID4gKwljYXNlIElJT19DSEFOX0lORk9fQ0FMSUJCSUFTOg0KPiA+ ICsJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dhaW5fdHJpbSA9IHZhbDsNCj4gPiArCQli cmVhazsNCj4gPiArDQo+ID4gKwlkZWZhdWx0Og0KPiA+ICsJCXJldHVybiAtRUlOVkFMOw0KPiA+ ICsJfQ0KPiA+ICsNCj4gPiArCXJldHVybiAwOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMg REVWSUNFX0FUVFIocG93ZXJfc3RhdGUsIFNfSVJVR08gfCBTX0lXVVNSLA0KPiA+ICsJCXRzbDJ4 N3hfcG93ZXJfc3RhdGVfc2hvdywgdHNsMng3eF9wb3dlcl9zdGF0ZV9zdG9yZSk7DQo+ID4gKw0K PiA+ICtzdGF0aWMgREVWSUNFX0FUVFIocHJveGltaXR5X2NhbGlic2NhbGVfYXZhaWxhYmxlLCBT X0lSVUdPLA0KPiA+ICsJCXRzbDJ4N3hfcHJveF9nYWluX2F2YWlsYWJsZV9zaG93LCBOVUxMKTsN Cj4gPiArDQo+ID4gK3N0YXRpYyBERVZJQ0VfQVRUUihpbGx1bWluYW5jZTBfY2FsaWJzY2FsZV9h dmFpbGFibGUsIFNfSVJVR08sDQo+ID4gKwkJdHNsMng3eF9nYWluX2F2YWlsYWJsZV9zaG93LCBO VUxMKTsNCj4gPiArDQo+ID4gK3N0YXRpYyBERVZJQ0VfQVRUUihpbGx1bWluYW5jZTBfaW50ZWdy YXRpb25fdGltZSwgU19JUlVHTyB8IFNfSVdVU1IsDQo+ID4gKwkJdHNsMng3eF9hbHNfdGltZV9z aG93LCB0c2wyeDd4X2Fsc190aW1lX3N0b3JlKTsNCj4gPiArDQo+ID4gK3N0YXRpYyBERVZJQ0Vf QVRUUihpbGx1bWluYW5jZTBfdGFyZ2V0X2lucHV0LCBTX0lSVUdPIHwgU19JV1VTUiwNCj4gPiAr CQl0c2wyeDd4X2Fsc19jYWxfdGFyZ2V0X3Nob3csIHRzbDJ4N3hfYWxzX2NhbF90YXJnZXRfc3Rv cmUpOw0KPiA+ICsNCj4gPiArc3RhdGljIERFVklDRV9BVFRSKGlsbHVtaW5hbmNlMF9jYWxpYnJh dGUsIFNfSVdVU1IsIE5VTEwsDQo+ID4gKwkJdHNsMng3eF9kb19jYWxpYnJhdGUpOw0KPiA+ICsN Cj4gPiArc3RhdGljIERFVklDRV9BVFRSKHByb3hpbWl0eV9jYWxpYnJhdGUsIFNfSVdVU1IsIE5V TEwsDQo+ID4gKwkJdHNsMng3eF9kb19wcm94X2NhbGlicmF0ZSk7DQo+ID4gKw0KPiA+ICtzdGF0 aWMgREVWSUNFX0FUVFIoaWxsdW1pbmFuY2UwX2x1eF90YWJsZSwgU19JUlVHTyB8IFNfSVdVU1Is DQo+ID4gKwkJdHNsMng3eF9sdXh0YWJsZV9zaG93LCB0c2wyeDd4X2x1eHRhYmxlX3N0b3JlKTsN Cj4gPiArDQo+ID4gK3N0YXRpYyBERVZJQ0VfQVRUUihzYW1wbGluZ19mcmVxdWVuY3ksIFNfSVJV R08gfCBTX0lXVVNSLA0KPiA+ICsJCXRzbDJ4N3hfcGVyc2lzdGVuY2Vfc2hvdywgdHNsMng3eF9w ZXJzaXN0ZW5jZV9zdG9yZSk7DQo+ID4gKw0KPiA+ICsvKiBVc2UgdGhlIGRlZmF1bHQgcmVnaXN0 ZXIgdmFsdWVzIHRvIGlkZW50aWZ5IHRoZSBUYW9zIGRldmljZSAqLw0KPiA+ICtzdGF0aWMgaW50 IHRzbDJ4N3hfZGV2aWNlX2lkKHVuc2lnbmVkIGNoYXIgKmlkLCBpbnQgdGFyZ2V0KQ0KPiA+ICt7 DQo+ID4gKwlzd2l0Y2ggKHRhcmdldCkgew0KPiA+ICsJY2FzZSB0c2wyNTcxOg0KPiA+ICsJY2Fz ZSB0c2wyNjcxOg0KPiA+ICsJY2FzZSB0c2wyNzcxOg0KPiA+ICsJCXJldHVybiAoKCppZCYgIDB4 ZjApID09IFRSSVRPTl9JRCk7DQo+ID4gKwlicmVhazsNCj4gPiArCWNhc2UgdG1kMjY3MToNCj4g PiArCWNhc2UgdG1kMjc3MToNCj4gPiArCQlyZXR1cm4gKCgqaWQmICAweGYwKSA9PSBIQUxJQlVU X0lEKTsNCj4gPiArCWJyZWFrOw0KPiA+ICsJY2FzZSB0c2wyNTcyOg0KPiA+ICsJY2FzZSB0c2wy NjcyOg0KPiA+ICsJY2FzZSB0bWQyNjcyOg0KPiA+ICsJY2FzZSB0c2wyNzcyOg0KPiA+ICsJY2Fz ZSB0bWQyNzcyOg0KPiA+ICsJCXJldHVybiAoKCppZCYgIDB4ZjApID09IFNXT1JERklTSF9JRCk7 DQo+ID4gKwlicmVhazsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlyZXR1cm4gLUVJTlZBTDsNCj4g PiArfQ0KPiA+ICsNCj4gPiArLyoNCj4gPiArICogSW50ZXJydXB0IEV2ZW50IEhhbmRsZXIgKi8N Cj4gPiArc3RhdGljIGlycXJldHVybl90IHRzbDJ4N3hfZXZlbnRfaGFuZGxlcihpbnQgaXJxLCB2 b2lkICpwcml2YXRlKQ0KPiA+ICt7DQo+ID4gKwlzdHJ1Y3QgaWlvX2RldiAqaW5kaW9fZGV2ID0g cHJpdmF0ZTsNCj4gPiArCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJpdihpbmRp b19kZXYpOw0KPiA+ICsJczY0IHRpbWVzdGFtcCA9IGlpb19nZXRfdGltZV9ucygpOw0KPiA+ICsJ aW50IHJldDsNCj4gPiArCWludCB2YWx1ZTsNCj4gPiArDQo+ID4gKwl2YWx1ZSA9IGkyY19zbWJ1 c19yZWFkX2J5dGVfZGF0YShjaGlwLT5jbGllbnQsDQo+ID4gKwkJVFNMMlg3WF9DTURfUkVHIHwg VFNMMlg3WF9TVEFUVVMpOw0KPiA+ICsNCj4gPiArCS8qIFdoYXQgdHlwZSBvZiBpbnRlcnJ1cHQg ZG8gd2UgbmVlZCB0byBwcm9jZXNzICovDQo+ID4gKwlpZiAodmFsdWUmICBUU0wyWDdYX1NUQV9Q UlhfSU5UUikgew0KPiA+ICsJCXRzbDJ4N3hfcHJveF9wb2xsKGluZGlvX2Rldik7DQo+IG1pc3Nl ZCB0aGlzLCBwdXQgd2hhdCBkb2VzIHRoZSBwcm94X3BvbGwgZG8gaGVyZT8gIEV2ZW50IHNlZW1z IHRvIGJlDQo+IGNsZWFyZWQgYmVsb3cuLi4gIEEgY29tbWVudCB0byBjbGFyaWZ5IHdoeSBhIHJl YWRpbmcgaXMgdGFrZSB3b3VsZCBjbGVhcg0KPiB0aGlzIHVwLg0KV2Ugd2FudCBmcmVzaCBkYXRh LiBEYXRhIHVzZXIgc2VlcyBpcyBpbiB0aGUgc3RydWN0LiAgMXN0IHRpbWUsIHN0cnVjdCBjb3Vs ZCBiZSBpbml0aWFsaXplZCB0byB6ZXJvIC1wb3NzaWJsZS4NCg0KPiA+ICsJCWlpb19wdXNoX2V2 ZW50KGluZGlvX2RldiwNCj4gPiArCQkJICAgICAgIElJT19VTk1PRF9FVkVOVF9DT0RFKElJT19Q Uk9YSU1JVFksDQo+ID4gKwkJCQkJCSAgICAwLA0KPiA+ICsJCQkJCQkgICAgSUlPX0VWX1RZUEVf VEhSRVNILA0KPiA+ICsJCQkJCQkgICAgSUlPX0VWX0RJUl9FSVRIRVIpLA0KPiA+ICsJCQkJCQkg ICAgdGltZXN0YW1wKTsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlpZiAodmFsdWUmICBUU0wyWDdY X1NUQV9BTFNfSU5UUikgew0KPiA+ICsJCXRzbDJ4N3hfZ2V0X2x1eChpbmRpb19kZXYpOw0KPiA+ ICsJCWlpb19wdXNoX2V2ZW50KGluZGlvX2RldiwNCj4gPiArCQkgICAgICAgSUlPX1VOTU9EX0VW RU5UX0NPREUoSUlPX0xJR0hULA0KPiA+ICsJCQkJCSAgICAwLA0KPiA+ICsJCQkJCSAgICBJSU9f RVZfVFlQRV9USFJFU0gsDQo+ID4gKwkJCQkJICAgIElJT19FVl9ESVJfRUlUSEVSKSwNCj4gPiAr CQkJCQkgICAgdGltZXN0YW1wKTsNCj4gPiArCX0NCj4gPiArCS8qIENsZWFyIGludGVycnVwdCBu b3cgdGhhdCB3ZSBoYXZlIGhhbmRsZWQgaXQuICovDQo+ID4gKwlyZXQgPSBpMmNfc21idXNfd3Jp dGVfYnl0ZShjaGlwLT5jbGllbnQsDQo+ID4gKwkJVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9D TURfU1BMX0ZOIHwNCj4gPiArCQlUU0wyWDdYX0NNRF9QUk9YQUxTX0lOVF9DTFIpOw0KPiA+ICsJ aWYgKHJldDwgIDApDQo+ID4gKwkJZGV2X2VycigmY2hpcC0+Y2xpZW50LT5kZXYsDQo+ID4gKwkJ CSIlczogRmFpbGVkIHRvIGNsZWFyIGlycSBmcm9tIGV2ZW50IGhhbmRsZXIuIGVyciA9ICVkXG4i LA0KPiA+ICsJCQlfX2Z1bmNfXywgcmV0KTsNCj4gPiArDQo+ID4gKwlyZXR1cm4gSVJRX0hBTkRM RUQ7DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBzdHJ1Y3QgYXR0cmlidXRlICp0c2wyeDd4 X0FMU19kZXZpY2VfYXR0cnNbXSA9IHsNCj4gPiArCSZkZXZfYXR0cl9wb3dlcl9zdGF0ZS5hdHRy LA0KPiA+ICsJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9jYWxpYnNjYWxlX2F2YWlsYWJsZS5hdHRy LA0KPiA+ICsJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9pbnRlZ3JhdGlvbl90aW1lLmF0dHIsDQo+ ID4gKwkmaWlvX2NvbnN0X2F0dHJfaWxsdW1pbmFuY2UwX2ludGVncmF0aW9uX3RpbWVfYXZhaWxh YmxlLmRldl9hdHRyLmF0dHIsDQo+ID4gKwkmZGV2X2F0dHJfaWxsdW1pbmFuY2UwX3RhcmdldF9p bnB1dC5hdHRyLA0KPiA+ICsJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9jYWxpYnJhdGUuYXR0ciwN Cj4gPiArCSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfbHV4X3RhYmxlLmF0dHIsDQo+ID4gKwkmZGV2 X2F0dHJfc2FtcGxpbmdfZnJlcXVlbmN5LmF0dHIsDQo+ID4gKwkmaWlvX2NvbnN0X2F0dHJfc2Ft cGxpbmdfZnJlcXVlbmN5X2F2YWlsYWJsZS5kZXZfYXR0ci5hdHRyLA0KPiA+ICsJTlVMTA0KPiA+ ICt9Ow0KPiA+ICsNCj4gPiArc3RhdGljIHN0cnVjdCBhdHRyaWJ1dGUgKnRzbDJ4N3hfUFJYX2Rl dmljZV9hdHRyc1tdID0gew0KPiA+ICsJJmRldl9hdHRyX3Bvd2VyX3N0YXRlLmF0dHIsDQo+ID4g KwkmZGV2X2F0dHJfc2FtcGxpbmdfZnJlcXVlbmN5LmF0dHIsDQo+ID4gKwkmaWlvX2NvbnN0X2F0 dHJfc2FtcGxpbmdfZnJlcXVlbmN5X2F2YWlsYWJsZS5kZXZfYXR0ci5hdHRyLA0KPiA+ICsJJmRl dl9hdHRyX3Byb3hpbWl0eV9jYWxpYnJhdGUuYXR0ciwNCj4gPiArCU5VTEwNCj4gPiArfTsNCj4g PiArDQo+ID4gK3N0YXRpYyBzdHJ1Y3QgYXR0cmlidXRlICp0c2wyeDd4X0FMU1BSWF9kZXZpY2Vf YXR0cnNbXSA9IHsNCj4gPiArCSZkZXZfYXR0cl9wb3dlcl9zdGF0ZS5hdHRyLA0KPiA+ICsJJmRl dl9hdHRyX2lsbHVtaW5hbmNlMF9jYWxpYnNjYWxlX2F2YWlsYWJsZS5hdHRyLA0KPiA+ICsJJmRl dl9hdHRyX2lsbHVtaW5hbmNlMF9pbnRlZ3JhdGlvbl90aW1lLmF0dHIsDQo+ID4gKwkmaWlvX2Nv bnN0X2F0dHJfaWxsdW1pbmFuY2UwX2ludGVncmF0aW9uX3RpbWVfYXZhaWxhYmxlLmRldl9hdHRy LmF0dHIsDQo+ID4gKwkmZGV2X2F0dHJfaWxsdW1pbmFuY2UwX3RhcmdldF9pbnB1dC5hdHRyLA0K PiA+ICsJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9jYWxpYnJhdGUuYXR0ciwNCj4gPiArCSZkZXZf YXR0cl9pbGx1bWluYW5jZTBfbHV4X3RhYmxlLmF0dHIsDQo+ID4gKwkmZGV2X2F0dHJfc2FtcGxp bmdfZnJlcXVlbmN5LmF0dHIsDQo+ID4gKwkmaWlvX2NvbnN0X2F0dHJfc2FtcGxpbmdfZnJlcXVl bmN5X2F2YWlsYWJsZS5kZXZfYXR0ci5hdHRyLA0KPiA+ICsJJmRldl9hdHRyX3Byb3hpbWl0eV9j YWxpYnJhdGUuYXR0ciwNCj4gPiArCU5VTEwNCj4gPiArfTsNCj4gPiArDQo+ID4gK3N0YXRpYyBz dHJ1Y3QgYXR0cmlidXRlICp0c2wyeDd4X1BSWDJfZGV2aWNlX2F0dHJzW10gPSB7DQo+ID4gKwkm ZGV2X2F0dHJfcG93ZXJfc3RhdGUuYXR0ciwNCj4gPiArCSZkZXZfYXR0cl9zYW1wbGluZ19mcmVx dWVuY3kuYXR0ciwNCj4gPiArCSZpaW9fY29uc3RfYXR0cl9zYW1wbGluZ19mcmVxdWVuY3lfYXZh aWxhYmxlLmRldl9hdHRyLmF0dHIsDQo+ID4gKwkmZGV2X2F0dHJfcHJveGltaXR5X2NhbGlicmF0 ZS5hdHRyLA0KPiA+ICsJJmRldl9hdHRyX3Byb3hpbWl0eV9jYWxpYnNjYWxlX2F2YWlsYWJsZS5h dHRyLA0KPiA+ICsJTlVMTA0KPiA+ICt9Ow0KPiA+ICsNCj4gPiArc3RhdGljIHN0cnVjdCBhdHRy aWJ1dGUgKnRzbDJ4N3hfQUxTUFJYMl9kZXZpY2VfYXR0cnNbXSA9IHsNCj4gPiArCSZkZXZfYXR0 cl9wb3dlcl9zdGF0ZS5hdHRyLA0KPiA+ICsJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9jYWxpYnNj YWxlX2F2YWlsYWJsZS5hdHRyLA0KPiA+ICsJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9pbnRlZ3Jh dGlvbl90aW1lLmF0dHIsDQo+ID4gKwkmaWlvX2NvbnN0X2F0dHJfaWxsdW1pbmFuY2UwX2ludGVn cmF0aW9uX3RpbWVfYXZhaWxhYmxlLmRldl9hdHRyLmF0dHIsDQo+ID4gKwkmZGV2X2F0dHJfaWxs dW1pbmFuY2UwX3RhcmdldF9pbnB1dC5hdHRyLA0KPiA+ICsJJmRldl9hdHRyX2lsbHVtaW5hbmNl MF9jYWxpYnJhdGUuYXR0ciwNCj4gPiArCSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfbHV4X3RhYmxl LmF0dHIsDQo+ID4gKwkmZGV2X2F0dHJfc2FtcGxpbmdfZnJlcXVlbmN5LmF0dHIsDQo+ID4gKwkm aWlvX2NvbnN0X2F0dHJfc2FtcGxpbmdfZnJlcXVlbmN5X2F2YWlsYWJsZS5kZXZfYXR0ci5hdHRy LA0KPiA+ICsJJmRldl9hdHRyX3Byb3hpbWl0eV9jYWxpYnJhdGUuYXR0ciwNCj4gPiArCSZkZXZf YXR0cl9wcm94aW1pdHlfY2FsaWJzY2FsZV9hdmFpbGFibGUuYXR0ciwNCj4gPiArCU5VTEwNCj4g PiArfTsNCj4gPiArDQo+ID4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgYXR0cmlidXRlX2dyb3VwIHRz bDJYN1hfZGV2aWNlX2F0dHJfZ3JvdXBfdGJsW10gPSB7DQo+ID4gKwlbQUxTXSA9IHsNCj4gPiAr CQkuYXR0cnMgPSB0c2wyeDd4X0FMU19kZXZpY2VfYXR0cnMsDQo+ID4gKwl9LA0KPiA+ICsJW1BS WF0gPSB7DQo+ID4gKwkJLmF0dHJzID0gdHNsMng3eF9QUlhfZGV2aWNlX2F0dHJzLA0KPiA+ICsJ fSwNCj4gPiArCVtBTFNQUlhdID0gew0KPiA+ICsJCS5hdHRycyA9IHRzbDJ4N3hfQUxTUFJYX2Rl dmljZV9hdHRycywNCj4gPiArCX0sDQo+ID4gKwlbUFJYMl0gPSB7DQo+ID4gKwkJLmF0dHJzID0g dHNsMng3eF9QUlgyX2RldmljZV9hdHRycywNCj4gPiArCX0sDQo+ID4gKwlbQUxTUFJYMl0gPSB7 DQo+ID4gKwkJLmF0dHJzID0gdHNsMng3eF9BTFNQUlgyX2RldmljZV9hdHRycywNCj4gPiArCX0s DQo+ID4gK307DQo+ID4gKw0KPiA+ICtzdGF0aWMgY29uc3Qgc3RydWN0IGlpb19pbmZvIHRzbDJY N1hfZGV2aWNlX2luZm9bXSA9IHsNCj4gPiArCVtBTFNdID0gew0KPiA+ICsJCS5hdHRycyA9JnRz bDJYN1hfZGV2aWNlX2F0dHJfZ3JvdXBfdGJsW0FMU10sDQo+ID4gKwkJLmRyaXZlcl9tb2R1bGUg PSBUSElTX01PRFVMRSwNCj4gPiArCQkucmVhZF9yYXcgPSZ0c2wyeDd4X3JlYWRfcmF3LA0KPiA+ ICsJCS53cml0ZV9yYXcgPSZ0c2wyeDd4X3dyaXRlX3JhdywNCj4gPiArCQkucmVhZF9ldmVudF92 YWx1ZSA9JnRzbDJ4N3hfcmVhZF90aHJlc2gsDQo+ID4gKwkJLndyaXRlX2V2ZW50X3ZhbHVlID0m dHNsMng3eF93cml0ZV90aHJlc2gsDQo+ID4gKwkJLnJlYWRfZXZlbnRfY29uZmlnID0mdHNsMng3 eF9yZWFkX2ludGVycnVwdF9jb25maWcsDQo+ID4gKwkJLndyaXRlX2V2ZW50X2NvbmZpZyA9JnRz bDJ4N3hfd3JpdGVfaW50ZXJydXB0X2NvbmZpZywNCj4gPiArCX0sDQo+ID4gKwlbUFJYXSA9IHsN Cj4gPiArCQkuYXR0cnMgPSZ0c2wyWDdYX2RldmljZV9hdHRyX2dyb3VwX3RibFtQUlhdLA0KPiA+ ICsJCS5kcml2ZXJfbW9kdWxlID0gVEhJU19NT0RVTEUsDQo+ID4gKwkJLnJlYWRfcmF3ID0mdHNs Mng3eF9yZWFkX3JhdywNCj4gPiArCQkud3JpdGVfcmF3ID0mdHNsMng3eF93cml0ZV9yYXcsDQo+ ID4gKwkJLnJlYWRfZXZlbnRfdmFsdWUgPSZ0c2wyeDd4X3JlYWRfdGhyZXNoLA0KPiA+ICsJCS53 cml0ZV9ldmVudF92YWx1ZSA9JnRzbDJ4N3hfd3JpdGVfdGhyZXNoLA0KPiA+ICsJCS5yZWFkX2V2 ZW50X2NvbmZpZyA9JnRzbDJ4N3hfcmVhZF9pbnRlcnJ1cHRfY29uZmlnLA0KPiA+ICsJCS53cml0 ZV9ldmVudF9jb25maWcgPSZ0c2wyeDd4X3dyaXRlX2ludGVycnVwdF9jb25maWcsDQo+ID4gKwl9 LA0KPiA+ICsJW0FMU1BSWF0gPSB7DQo+ID4gKwkJLmF0dHJzID0mdHNsMlg3WF9kZXZpY2VfYXR0 cl9ncm91cF90YmxbQUxTUFJYXSwNCj4gPiArCQkuZHJpdmVyX21vZHVsZSA9IFRISVNfTU9EVUxF LA0KPiA+ICsJCS5yZWFkX3JhdyA9JnRzbDJ4N3hfcmVhZF9yYXcsDQo+ID4gKwkJLndyaXRlX3Jh dyA9JnRzbDJ4N3hfd3JpdGVfcmF3LA0KPiA+ICsJCS5yZWFkX2V2ZW50X3ZhbHVlID0mdHNsMng3 eF9yZWFkX3RocmVzaCwNCj4gPiArCQkud3JpdGVfZXZlbnRfdmFsdWUgPSZ0c2wyeDd4X3dyaXRl X3RocmVzaCwNCj4gPiArCQkucmVhZF9ldmVudF9jb25maWcgPSZ0c2wyeDd4X3JlYWRfaW50ZXJy dXB0X2NvbmZpZywNCj4gPiArCQkud3JpdGVfZXZlbnRfY29uZmlnID0mdHNsMng3eF93cml0ZV9p bnRlcnJ1cHRfY29uZmlnLA0KPiA+ICsJfSwNCj4gPiArCVtQUlgyXSA9IHsNCj4gPiArCQkuYXR0 cnMgPSZ0c2wyWDdYX2RldmljZV9hdHRyX2dyb3VwX3RibFtQUlgyXSwNCj4gPiArCQkuZHJpdmVy X21vZHVsZSA9IFRISVNfTU9EVUxFLA0KPiA+ICsJCS5yZWFkX3JhdyA9JnRzbDJ4N3hfcmVhZF9y YXcsDQo+ID4gKwkJLndyaXRlX3JhdyA9JnRzbDJ4N3hfd3JpdGVfcmF3LA0KPiA+ICsJCS5yZWFk X2V2ZW50X3ZhbHVlID0mdHNsMng3eF9yZWFkX3RocmVzaCwNCj4gPiArCQkud3JpdGVfZXZlbnRf dmFsdWUgPSZ0c2wyeDd4X3dyaXRlX3RocmVzaCwNCj4gPiArCQkucmVhZF9ldmVudF9jb25maWcg PSZ0c2wyeDd4X3JlYWRfaW50ZXJydXB0X2NvbmZpZywNCj4gPiArCQkud3JpdGVfZXZlbnRfY29u ZmlnID0mdHNsMng3eF93cml0ZV9pbnRlcnJ1cHRfY29uZmlnLA0KPiA+ICsJfSwNCj4gPiArCVtB TFNQUlgyXSA9IHsNCj4gPiArCQkuYXR0cnMgPSZ0c2wyWDdYX2RldmljZV9hdHRyX2dyb3VwX3Ri bFtBTFNQUlgyXSwNCj4gPiArCQkuZHJpdmVyX21vZHVsZSA9IFRISVNfTU9EVUxFLA0KPiA+ICsJ CS5yZWFkX3JhdyA9JnRzbDJ4N3hfcmVhZF9yYXcsDQo+ID4gKwkJLndyaXRlX3JhdyA9JnRzbDJ4 N3hfd3JpdGVfcmF3LA0KPiA+ICsJCS5yZWFkX2V2ZW50X3ZhbHVlID0mdHNsMng3eF9yZWFkX3Ro cmVzaCwNCj4gPiArCQkud3JpdGVfZXZlbnRfdmFsdWUgPSZ0c2wyeDd4X3dyaXRlX3RocmVzaCwN Cj4gPiArCQkucmVhZF9ldmVudF9jb25maWcgPSZ0c2wyeDd4X3JlYWRfaW50ZXJydXB0X2NvbmZp ZywNCj4gPiArCQkud3JpdGVfZXZlbnRfY29uZmlnID0mdHNsMng3eF93cml0ZV9pbnRlcnJ1cHRf Y29uZmlnLA0KPiA+ICsJfSwNCj4gPiArfTsNCj4gPiArDQo+ID4gK3N0YXRpYyBjb25zdCBzdHJ1 Y3QgdHNsMng3eF9jaGlwX2luZm8gdHNsMng3eF9jaGlwX2luZm9fdGJsW10gPSB7DQo+ID4gKwlb QUxTXSA9IHsNCj4gPiArCQkuY2hhbm5lbCA9IHsNCj4gPiArCQkJew0KPiA+ICsJCQkudHlwZSA9 IElJT19MSUdIVCwNCj4gPiArCQkJLmluZGV4ZWQgPSAxLA0KPiA+ICsJCQkuY2hhbm5lbCA9IDAs DQo+ID4gKwkJCS5wcm9jZXNzZWRfdmFsID0gMSwNCj4gPiArCQkJfSwgew0KPiA+ICsJCQkudHlw ZSA9IElJT19JTlRFTlNJVFksDQo+ID4gKwkJCS5pbmRleGVkID0gMSwNCj4gPiArCQkJLmNoYW5u ZWwgPSAwLA0KPiA+ICsJCQkuaW5mb19tYXNrID0NCj4gSUlPX0NIQU5fSU5GT19DQUxJQlNDQUxF X1NFUEFSQVRFX0JJVCB8DQo+ID4gKwkJCQlJSU9fQ0hBTl9JTkZPX0NBTElCQklBU19TRVBBUkFU RV9CSVQsDQo+ID4gKwkJCS5ldmVudF9tYXNrID0gVFNMMlg3WF9FVkVOVF9NQVNLDQo+ID4gKwkJ CX0sIHsNCj4gPiArCQkJLnR5cGUgPSBJSU9fSU5URU5TSVRZLA0KPiA+ICsJCQkuaW5kZXhlZCA9 IDEsDQo+ID4gKwkJCS5jaGFubmVsID0gMSwNCj4gPiArCQkJfSwNCj4gPiArCQl9LA0KPiA+ICsJ LmNoYW5fdGFibGVfZWxlbWVudHMgPSAzLA0KPiA+ICsJLmluZm8gPSZ0c2wyWDdYX2RldmljZV9p bmZvW0FMU10sDQo+ID4gKwl9LA0KPiA+ICsJW1BSWF0gPSB7DQo+ID4gKwkJLmNoYW5uZWwgPSB7 DQo+ID4gKwkJCXsNCj4gPiArCQkJLnR5cGUgPSBJSU9fUFJPWElNSVRZLA0KPiA+ICsJCQkuaW5k ZXhlZCA9IDEsDQo+ID4gKwkJCS5jaGFubmVsID0gMCwNCj4gPiArCQkJLmV2ZW50X21hc2sgPSBU U0wyWDdYX0VWRU5UX01BU0sNCj4gPiArCQkJfSwNCj4gPiArCQl9LA0KPiA+ICsJLmNoYW5fdGFi bGVfZWxlbWVudHMgPSAxLA0KPiA+ICsJLmluZm8gPSZ0c2wyWDdYX2RldmljZV9pbmZvW1BSWF0s DQo+ID4gKwl9LA0KPiA+ICsJW0FMU1BSWF0gPSB7DQo+ID4gKwkJLmNoYW5uZWwgPSB7DQo+ID4g KwkJCXsNCj4gPiArCQkJLnR5cGUgPSBJSU9fTElHSFQsDQo+ID4gKwkJCS5pbmRleGVkID0gMSwN Cj4gPiArCQkJLmNoYW5uZWwgPSAwLA0KPiA+ICsJCQkucHJvY2Vzc2VkX3ZhbCA9IDEsDQo+ID4g KwkJCX0sIHsNCj4gPiArCQkJLnR5cGUgPSBJSU9fSU5URU5TSVRZLA0KPiA+ICsJCQkuaW5kZXhl ZCA9IDEsDQo+ID4gKwkJCS5jaGFubmVsID0gMCwNCj4gPiArCQkJLmluZm9fbWFzayA9DQo+IElJ T19DSEFOX0lORk9fQ0FMSUJTQ0FMRV9TRVBBUkFURV9CSVQgfA0KPiA+ICsJCQkJSUlPX0NIQU5f SU5GT19DQUxJQkJJQVNfU0VQQVJBVEVfQklULA0KPiA+ICsJCQkuZXZlbnRfbWFzayA9IFRTTDJY N1hfRVZFTlRfTUFTSw0KPiA+ICsJCQl9LCB7DQo+ID4gKwkJCS50eXBlID0gSUlPX0lOVEVOU0lU WSwNCj4gPiArCQkJLmluZGV4ZWQgPSAxLA0KPiA+ICsJCQkuY2hhbm5lbCA9IDEsDQo+ID4gKwkJ CX0sIHsNCj4gPiArCQkJLnR5cGUgPSBJSU9fUFJPWElNSVRZLA0KPiA+ICsJCQkuaW5kZXhlZCA9 IDEsDQo+ID4gKwkJCS5jaGFubmVsID0gMCwNCj4gPiArCQkJLmV2ZW50X21hc2sgPSBUU0wyWDdY X0VWRU5UX01BU0sNCj4gPiArCQkJfSwNCj4gPiArCQl9LA0KPiA+ICsJLmNoYW5fdGFibGVfZWxl bWVudHMgPSA0LA0KPiA+ICsJLmluZm8gPSZ0c2wyWDdYX2RldmljZV9pbmZvW0FMU1BSWF0sDQo+ ID4gKwl9LA0KPiA+ICsJW1BSWDJdID0gew0KPiA+ICsJCS5jaGFubmVsID0gew0KPiA+ICsJCQl7 DQo+ID4gKwkJCS50eXBlID0gSUlPX1BST1hJTUlUWSwNCj4gPiArCQkJLmluZGV4ZWQgPSAxLA0K PiA+ICsJCQkuY2hhbm5lbCA9IDAsDQo+ID4gKwkJCS5pbmZvX21hc2sgPQ0KPiA+ICsJCQkJSUlP X0NIQU5fSU5GT19DQUxJQlNDQUxFX1NFUEFSQVRFX0JJVCwNCj4gPiArCQkJLmV2ZW50X21hc2sg PSBUU0wyWDdYX0VWRU5UX01BU0sNCj4gPiArCQkJfSwNCj4gPiArCQl9LA0KPiA+ICsJLmNoYW5f dGFibGVfZWxlbWVudHMgPSAxLA0KPiA+ICsJLmluZm8gPSZ0c2wyWDdYX2RldmljZV9pbmZvW1BS WDJdLA0KPiA+ICsJfSwNCj4gPiArCVtBTFNQUlgyXSA9IHsNCj4gPiArCQkuY2hhbm5lbCA9IHsN Cj4gPiArCQkJew0KPiA+ICsJCQkudHlwZSA9IElJT19MSUdIVCwNCj4gPiArCQkJLmluZGV4ZWQg PSAxLA0KPiA+ICsJCQkuY2hhbm5lbCA9IDAsDQo+ID4gKwkJCS5wcm9jZXNzZWRfdmFsID0gMSwN Cj4gPiArCQkJfSwgew0KPiA+ICsJCQkudHlwZSA9IElJT19JTlRFTlNJVFksDQo+ID4gKwkJCS5p bmRleGVkID0gMSwNCj4gPiArCQkJLmNoYW5uZWwgPSAwLA0KPiA+ICsJCQkuaW5mb19tYXNrID0N Cj4gSUlPX0NIQU5fSU5GT19DQUxJQlNDQUxFX1NFUEFSQVRFX0JJVCB8DQo+ID4gKwkJCQlJSU9f Q0hBTl9JTkZPX0NBTElCQklBU19TRVBBUkFURV9CSVQsDQo+ID4gKwkJCS5ldmVudF9tYXNrID0g VFNMMlg3WF9FVkVOVF9NQVNLDQo+ID4gKwkJCX0sIHsNCj4gPiArCQkJLnR5cGUgPSBJSU9fSU5U RU5TSVRZLA0KPiA+ICsJCQkuaW5kZXhlZCA9IDEsDQo+ID4gKwkJCS5jaGFubmVsID0gMSwNCj4g PiArCQkJfSwgew0KPiA+ICsJCQkudHlwZSA9IElJT19QUk9YSU1JVFksDQo+ID4gKwkJCS5pbmRl eGVkID0gMSwNCj4gPiArCQkJLmNoYW5uZWwgPSAwLA0KPiA+ICsJCQkuaW5mb19tYXNrID0NCj4g PiArCQkJCUlJT19DSEFOX0lORk9fQ0FMSUJTQ0FMRV9TRVBBUkFURV9CSVQsDQo+ID4gKwkJCS5l dmVudF9tYXNrID0gVFNMMlg3WF9FVkVOVF9NQVNLDQo+ID4gKwkJCX0sDQo+ID4gKwkJfSwNCj4g SSB0aGluayB5b3Ugc3RpbGwgaGF2ZSBzcGFjZSBmb3IgOSBjaGFubmVscyBpbiB0aGUgc3RydWN0 dXJlLi4uLg0KWW91IGFyZSBjb3JyZWN0IFRoYXQncyB3aGF0IEkgZ2V0IGZvciBib3Jyb3dpbmcg Y29kZS4NCg0KPiA+ICsJLmNoYW5fdGFibGVfZWxlbWVudHMgPSA0LA0KPiA+ICsJLmluZm8gPSZ0 c2wyWDdYX2RldmljZV9pbmZvW0FMU1BSWDJdLA0KPiA+ICsJfSwNCj4gPiArfTsNCj4gPiArDQo+ IEtpbmQgb2Ygb2J2aW91cyBjb21tZW50IGFuZCBub3QgaW4ga2VybmVsLWRvYy4uLg0KPiA+ICsv Kg0KPiA+ICsgKiBDbGllbnQgcHJvYmUgZnVuY3Rpb24uDQo+ID4gKyAqLw0KPiA+ICtzdGF0aWMg aW50IF9fZGV2aW5pdCB0c2wyeDd4X3Byb2JlKHN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnRwLA0K PiA+ICsJY29uc3Qgc3RydWN0IGkyY19kZXZpY2VfaWQgKmlkKQ0KPiA+ICt7DQo+ID4gKwlpbnQg cmV0Ow0KPiA+ICsJdW5zaWduZWQgY2hhciBkZXZpY2VfaWQ7DQo+ID4gKwlzdHJ1Y3QgaWlvX2Rl diAqaW5kaW9fZGV2Ow0KPiA+ICsJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcDsNCj4gPiArDQo+ ID4gKwlpbmRpb19kZXYgPSBpaW9fYWxsb2NhdGVfZGV2aWNlKHNpemVvZigqY2hpcCkpOw0KPiA+ ICsJaWYgKCFpbmRpb19kZXYpDQo+ID4gKwkJcmV0dXJuIC1FTk9NRU07DQo+ID4gKw0KPiA+ICsJ Y2hpcCA9IGlpb19wcml2KGluZGlvX2Rldik7DQo+ID4gKwljaGlwLT5jbGllbnQgPSBjbGllbnRw Ow0KPiA+ICsJaTJjX3NldF9jbGllbnRkYXRhKGNsaWVudHAsIGluZGlvX2Rldik7DQo+ID4gKw0K PiA+ICsJcmV0ID0gdHNsMng3eF9pMmNfcmVhZChjaGlwLT5jbGllbnQsDQo+ID4gKwkJVFNMMlg3 WF9DSElQSUQsJmRldmljZV9pZCk7DQo+ID4gKwlpZiAocmV0PCAgMCkNCj4gPiArCQlnb3RvIGZh aWwxOw0KPiA+ICsNCj4gPiArCWlmICgoIXRzbDJ4N3hfZGV2aWNlX2lkKCZkZXZpY2VfaWQsIGlk LT5kcml2ZXJfZGF0YSkpIHx8DQo+ID4gKwkJKHRzbDJ4N3hfZGV2aWNlX2lkKCZkZXZpY2VfaWQs IGlkLT5kcml2ZXJfZGF0YSkgPT0gLUVJTlZBTCkpIHsNCj4gPiArCQlkZXZfaW5mbygmY2hpcC0+ Y2xpZW50LT5kZXYsDQo+ID4gKwkJCQkiaTJjIGRldmljZSBmb3VuZCBkb2VzIG5vdCBtYXRjaCBl eHBlY3RlZCBpZA0KPiBpbiAlc1xuIiwNCj4gPiArCQkJCV9fZnVuY19fKTsNCj4gV291bGQgYmUg Z29vZCB0byBzdGFuZGFyZGlzZSBlcnJvciBmb3JtYXR0aW5nIGFjcm9zcyB0aGUgZHJpdmVyLg0K PiBTb21ldGltZXMgeW91IGhhdmUgdGhlIGZ1bmN0aW9uDQo+IG5hbWUgZmlyc3QsIHNvbWV0aW1l cyBsYXN0LiAgUGljayBvbmUgYW5kIGdvIHdpdGggaXQuDQo+ID4gKwkJZ290byBmYWlsMTsNCj4g PiArCX0NCj4gPiArDQo+ID4gKwlyZXQgPSBpMmNfc21idXNfd3JpdGVfYnl0ZShjbGllbnRwLCAo VFNMMlg3WF9DTURfUkVHIHwNCj4gVFNMMlg3WF9DTlRSTCkpOw0KPiA+ICsJaWYgKHJldDwgIDAp IHsNCj4gPiArCQlkZXZfZXJyKCZjbGllbnRwLT5kZXYsICIlczogd3JpdGUgdG8gY21kIHJlZyBm YWlsZWQuIGVyciA9DQo+ICVkXG4iLA0KPiA+ICsJCQkJX19mdW5jX18sIHJldCk7DQo+ID4gKwkJ Z290byBmYWlsMTsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwkvKiBBTFMgYW5kIFBST1ggZnVuY3Rp b25zIGNhbiBiZSBpbnZva2VkIHZpYSB1c2VyIHNwYWNlIHBvbGwNCj4gPiArCSAqIG9yIEgvVyBp bnRlcnJ1cHQuIElmIGJ1c3kgcmV0dXJuIGxhc3Qgc2FtcGxlLiAqLw0KPiA+ICsJbXV0ZXhfaW5p dCgmY2hpcC0+YWxzX211dGV4KTsNCj4gPiArCW11dGV4X2luaXQoJmNoaXAtPnByb3hfbXV0ZXgp Ow0KPiA+ICsNCj4gPiArCWNoaXAtPnRzbDJ4N3hfY2hpcF9zdGF0dXMgPSBUU0wyWDdYX0NISVBf VU5LTk9XTjsNCj4gPiArCWNoaXAtPnBkYXRhID0gY2xpZW50cC0+ZGV2LnBsYXRmb3JtX2RhdGE7 DQo+ID4gKwljaGlwLT5pZCA9IGlkLT5kcml2ZXJfZGF0YTsNCj4gPiArCWNoaXAtPmNoaXBfaW5m byA9DQo+ID4gKwkJJnRzbDJ4N3hfY2hpcF9pbmZvX3RibFtkZXZpY2VfY2hhbm5lbF9jb25maWdb aWQtDQo+ID5kcml2ZXJfZGF0YV1dOw0KPiA+ICsNCj4gPiArCWluZGlvX2Rldi0+aW5mbyA9IGNo aXAtPmNoaXBfaW5mby0+aW5mbzsNCj4gPiArCWluZGlvX2Rldi0+ZGV2LnBhcmVudCA9JmNsaWVu dHAtPmRldjsNCj4gPiArCWluZGlvX2Rldi0+bW9kZXMgPSBJTkRJT19ESVJFQ1RfTU9ERTsNCj4g PiArCWluZGlvX2Rldi0+bmFtZSA9IGNoaXAtPmNsaWVudC0+bmFtZTsNCj4gPiArCWluZGlvX2Rl di0+Y2hhbm5lbHMgPSBjaGlwLT5jaGlwX2luZm8tPmNoYW5uZWw7DQo+ID4gKwlpbmRpb19kZXYt Pm51bV9jaGFubmVscyA9IGNoaXAtPmNoaXBfaW5mby0+Y2hhbl90YWJsZV9lbGVtZW50czsNCj4g PiArDQo+ID4gKwlpZiAoY2xpZW50cC0+aXJxKSB7DQo+ID4gKwkJcmV0ID0gcmVxdWVzdF90aHJl YWRlZF9pcnEoY2xpZW50cC0+aXJxLA0KPiA+ICsJCQkJCSAgIE5VTEwsDQo+ID4gKwkJCQkJJnRz bDJ4N3hfZXZlbnRfaGFuZGxlciwNCj4gPiArCQkJCQkgICBJUlFGX1RSSUdHRVJfUklTSU5HIHwN Cj4gSVJRRl9PTkVTSE9ULA0KPiA+ICsJCQkJCSAgICJUU0wyWDdYX2V2ZW50IiwNCj4gPiArCQkJ CQkgICBpbmRpb19kZXYpOw0KPiA+ICsJCWlmIChyZXQpIHsNCj4gPiArCQkJZGV2X2VycigmY2xp ZW50cC0+ZGV2LA0KPiA+ICsJCQkJIiVzOiBpcnEgcmVxdWVzdCBmYWlsZWQiLCBfX2Z1bmNfXyk7 DQo+ID4gKwkJCWdvdG8gZmFpbDI7DQo+ID4gKwkJfQ0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCS8q IExvYWQgdXAgdGhlIGRlZmF1bHRzICovDQo+ID4gKwl0c2wyeDd4X2RlZmF1bHRzKGNoaXApOw0K PiA+ICsJLyogTWFrZSBzdXJlIHRoZSBjaGlwIGlzIG9uICovDQo+ID4gKwl0c2wyeDd4X2NoaXBf b24oaW5kaW9fZGV2KTsNCj4gPiArDQo+ID4gKwlyZXQgPSBpaW9fZGV2aWNlX3JlZ2lzdGVyKGlu ZGlvX2Rldik7DQo+ID4gKwlpZiAocmV0KSB7DQo+ID4gKwkJZGV2X2VycigmY2xpZW50cC0+ZGV2 LA0KPiA+ICsJCQkiJXM6IGlpbyByZWdpc3RyYXRpb24gZmFpbGVkXG4iLCBfX2Z1bmNfXyk7DQo+ ID4gKwkJZ290byBmYWlsMTsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlkZXZfaW5mbygmY2xpZW50 cC0+ZGV2LCAiJXMgTGlnaHQgc2Vuc29yIGZvdW5kLlxuIiwgaWQtPm5hbWUpOw0KPiA+ICsJcmV0 dXJuIDA7DQo+ID4gKw0KPiA+ICtmYWlsMToNCj4gPiArCWlmIChjbGllbnRwLT5pcnEpDQo+ID4g KwkJZnJlZV9pcnEoY2xpZW50cC0+aXJxLCBpbmRpb19kZXYpOw0KPiA+ICtmYWlsMjoNCj4gPiAr CWlpb19mcmVlX2RldmljZShpbmRpb19kZXYpOw0KPiBjb252ZW50aW9uIHB1dHMgYSBibGFuayBs aW5lIGJlZm9yZSB0aGUgcmV0dXJuLiAgU2FtZSBhYm92ZS4NCj4gPiArCXJldHVybiByZXQ7DQo+ ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBpbnQgdHNsMng3eF9zdXNwZW5kKHN0cnVjdCBkZXZp Y2UgKmRldikNCj4gPiArew0KPiA+ICsJc3RydWN0IGlpb19kZXYgKmluZGlvX2RldiA9IGRldl9n ZXRfZHJ2ZGF0YShkZXYpOw0KPiA+ICsJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlpb19w cml2KGluZGlvX2Rldik7DQo+ID4gKwlpbnQgcmV0ID0gMDsNCj4gPiArDQo+ID4gKwlpZiAoY2hp cC0+dHNsMng3eF9jaGlwX3N0YXR1cyA9PSBUU0wyWDdYX0NISVBfV09SS0lORykgew0KPiA+ICsJ CXJldCA9IHRzbDJ4N3hfY2hpcF9vZmYoaW5kaW9fZGV2KTsNCj4gPiArCQljaGlwLT50c2wyeDd4 X2NoaXBfc3RhdHVzID0gVFNMMlg3WF9DSElQX1NVU1BFTkRFRDsNCj4gPiArCX0NCj4gPiArDQo+ ID4gKwlpZiAoY2hpcC0+cGRhdGEmJiAgY2hpcC0+cGRhdGEtPnBsYXRmb3JtX3Bvd2VyKSB7DQo+ ID4gKwkJcG1fbWVzc2FnZV90IHBtbSA9IHtQTV9FVkVOVF9TVVNQRU5EfTsNCj4gPiArCQljaGlw LT5wZGF0YS0+cGxhdGZvcm1fcG93ZXIoZGV2LCBwbW0pOw0KPiA+ICsJfQ0KPiA+ICsNCj4gPiAr CXJldHVybiByZXQ7DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBpbnQgdHNsMng3eF9yZXN1 bWUoc3RydWN0IGRldmljZSAqZGV2KQ0KPiA+ICt7DQo+ID4gKwlzdHJ1Y3QgaWlvX2RldiAqaW5k aW9fZGV2ID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQo+ID4gKwlzdHJ1Y3QgdHNsMlg3WF9jaGlw ICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4gPiArCWludCByZXQgPSAwOw0KPiA+ICsN Cj4gPiArCWlmIChjaGlwLT5wZGF0YSYmICBjaGlwLT5wZGF0YS0+cGxhdGZvcm1fcG93ZXIpIHsN Cj4gPiArCQlwbV9tZXNzYWdlX3QgcG1tID0ge1BNX0VWRU5UX1JFU1VNRX07DQo+ID4gKwkJY2hp cC0+cGRhdGEtPnBsYXRmb3JtX3Bvd2VyKGRldiwgcG1tKTsNCj4gPiArCX0NCj4gPiArDQo+ID4g KwlpZiAoY2hpcC0+dHNsMng3eF9jaGlwX3N0YXR1cyA9PSBUU0wyWDdYX0NISVBfU1VTUEVOREVE KQ0KPiA+ICsJCXJldCA9IHRzbDJ4N3hfY2hpcF9vbihpbmRpb19kZXYpOw0KPiA+ICsNCj4gPiAr CXJldHVybiByZXQ7DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBpbnQgX19kZXZleGl0IHRz bDJ4N3hfcmVtb3ZlKHN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQpDQo+ID4gK3sNCj4gPiArCXN0 cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpMmNfZ2V0X2NsaWVudGRhdGEoY2xpZW50KTsNCj4g PiArCXN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYgPSBpaW9fcHJpdl90b19kZXYoY2hpcCk7DQo+ ID4gKw0KPiA+ICsJdHNsMng3eF9jaGlwX29mZihpbmRpb19kZXYpOw0KPiA+ICsNCj4gPiArCWlp b19kZXZpY2VfdW5yZWdpc3RlcihpbmRpb19kZXYpOw0KPiA+ICsJaWYgKGNsaWVudC0+aXJxKQ0K PiA+ICsJCWZyZWVfaXJxKGNsaWVudC0+aXJxLCBjaGlwLT5jbGllbnQtPm5hbWUpOw0KPiA+ICsN Cj4gPiArCWlpb19mcmVlX2RldmljZShpbmRpb19kZXYpOw0KPiA+ICsNCj4gPiArCXJldHVybiAw Ow0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgc3RydWN0IGkyY19kZXZpY2VfaWQgdHNsMng3 eF9pZHRhYmxlW10gPSB7DQo+ID4gKwl7ICJ0c2wyNTcxIiwgdHNsMjU3MSB9LA0KPiA+ICsJeyAi dHNsMjY3MSIsIHRzbDI2NzEgfSwNCj4gPiArCXsgInRtZDI2NzEiLCB0bWQyNjcxIH0sDQo+ID4g Kwl7ICJ0c2wyNzcxIiwgdHNsMjc3MSB9LA0KPiA+ICsJeyAidG1kMjc3MSIsIHRtZDI3NzEgfSwN Cj4gPiArCXsgInRzbDI1NzIiLCB0c2wyNTcyIH0sDQo+ID4gKwl7ICJ0c2wyNjcyIiwgdHNsMjY3 MiB9LA0KPiA+ICsJeyAidG1kMjY3MiIsIHRtZDI2NzIgfSwNCj4gPiArCXsgInRzbDI3NzIiLCB0 c2wyNzcyIH0sDQo+ID4gKwl7ICJ0bWQyNzcyIiwgdG1kMjc3MiB9LA0KPiA+ICsJe30NCj4gPiAr fTsNCj4gPiArDQo+ID4gK01PRFVMRV9ERVZJQ0VfVEFCTEUoaTJjLCB0c2wyeDd4X2lkdGFibGUp Ow0KPiA+ICsNCj4gPiArc3RhdGljIGNvbnN0IHN0cnVjdCBkZXZfcG1fb3BzIHRzbDJ4N3hfcG1f b3BzID0gew0KPiA+ICsJLnN1c3BlbmQgPSB0c2wyeDd4X3N1c3BlbmQsDQo+ID4gKwkucmVzdW1l ICA9IHRzbDJ4N3hfcmVzdW1lLA0KPiA+ICt9Ow0KPiA+ICsNCj4gPiArLyogRHJpdmVyIGRlZmlu aXRpb24gKi8NCj4gPiArc3RhdGljIHN0cnVjdCBpMmNfZHJpdmVyIHRzbDJ4N3hfZHJpdmVyID0g ew0KPiA+ICsJLmRyaXZlciA9IHsNCj4gPiArCQkubmFtZSA9ICJ0c2wyeDd4IiwNCj4gPiArCQku cG0gPSZ0c2wyeDd4X3BtX29wcywNCj4gPiArCX0sDQo+ID4gKwkuaWRfdGFibGUgPSB0c2wyeDd4 X2lkdGFibGUsDQo+ID4gKwkucHJvYmUgPSB0c2wyeDd4X3Byb2JlLA0KPiA+ICsJLnJlbW92ZSA9 IF9fZGV2ZXhpdF9wKHRzbDJ4N3hfcmVtb3ZlKSwNCj4gPiArfTsNCj4gPiArDQo+ID4gK21vZHVs ZV9pMmNfZHJpdmVyKHRzbDJ4N3hfZHJpdmVyKTsNCj4gPiArDQo+ID4gK01PRFVMRV9BVVRIT1Io IkouIEF1Z3VzdCBCcmVubmVyPGpicmVubmVyQHRhb3NpbmMuY29tPiIpOw0KPiA+ICtNT0RVTEVf REVTQ1JJUFRJT04oIlRBT1MgdHNsMng3eCBhbWJpZW50IGFuZCBwcm94aW1pdHkgbGlnaHQgc2Vu c29yDQo+IGRyaXZlciIpOw0KPiA+ICtNT0RVTEVfTElDRU5TRSgiR1BMIik7DQo+ID4gLS0NCj4g PiAxLjcuNC4xDQo+ID4NCj4gPiAtLQ0KPiA+IFRvIHVuc3Vic2NyaWJlIGZyb20gdGhpcyBsaXN0 OiBzZW5kIHRoZSBsaW5lICJ1bnN1YnNjcmliZSBsaW51eC1paW8iIGluDQo+ID4gdGhlIGJvZHkg b2YgYSBtZXNzYWdlIHRvIG1ham9yZG9tb0B2Z2VyLmtlcm5lbC5vcmcNCj4gPiBNb3JlIG1ham9y ZG9tbyBpbmZvIGF0ICBodHRwOi8vdmdlci5rZXJuZWwub3JnL21ham9yZG9tby1pbmZvLmh0bWwN Cg0K ^ permalink raw reply [flat|nested] 9+ messages in thread
* RE: [PATCH V5] TAOS tsl2x7x @ 2012-04-04 15:30 ` Jon Brenner 0 siblings, 0 replies; 9+ messages in thread From: Jon Brenner @ 2012-04-04 15:30 UTC (permalink / raw) To: Jonathan Cameron; +Cc: linux-iio, Linux Kernel [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Type: text/plain; charset="utf-8", Size: 74157 bytes --] Hi Jonathan, Thanks for the review . Please see various responses - in line. Next patch will be V6 and the last - I hope! Jon > -----Original Message----- > From: Jonathan Cameron [mailto:jic23@cam.ac.uk] > Sent: Wednesday, April 04, 2012 3:35 AM > To: Jon Brenner > Cc: linux-iio; Linux Kernel > Subject: Re: [PATCH V5] TAOS tsl2x7x > > On 4/2/2012 5:50 PM, Jon Brenner wrote: > > TAOS device driver (version 5) for the tsl/tmd 2771 and 2772 device families > (inc. all variants). > Hi Jon, > > Changes since last version? Correct. > > A few bits still to sort out in here I'm afraid... (getting there though!) > My reviews tend to get more picky as the big stuff gets sorted out. > > On trivial extra blank line to clear out. > Extra line for your next driver has snuck into the make file. Yikes! > Units don't look right for sampling frequency. Sorry, but that's an abi > issue so even if it is > fiddly to do the conversion to Hz it needs to be done. > Would normally expect changes to events to get applied immediately. Here > I think that only > happens if you turn the device off and on again? This is per customer request - allows complete reconfiguration without many device on/offs. > > For future reference (don't bother here!) make any documentation moves > not directly dependent on the > driver (such as those that will become used by multiple drivers) in a > precursor patch. OK > > > > Signed-off-by: Jon Brenner<jbrenner@taosinc.com> > > --- > > .../light/sysfs-bus-iio-light-tsl2583 | 6 + > > .../light/sysfs-bus-iio-light-tsl2x7x | 14 + > > drivers/staging/iio/Documentation/sysfs-bus-iio | 7 + > > .../staging/iio/Documentation/sysfs-bus-iio-light | 8 +- > > .../iio/Documentation/sysfs-bus-iio-light-tsl2583 | 20 - > > drivers/staging/iio/light/Kconfig | 8 + > > drivers/staging/iio/light/Makefile | 2 + > > drivers/staging/iio/light/tsl2x7x.h | 99 ++ > > drivers/staging/iio/light/tsl2x7x_core.c | 1830 ++++++++++++++++++++ > > 9 files changed, 1970 insertions(+), 24 deletions(-) > > > > diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 > b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 > > new file mode 100644 > > index 0000000..8f2a038 > > --- /dev/null > > +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 > > @@ -0,0 +1,6 @@ > > +What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate > > +KernelVersion: 2.6.37 > > +Contact: linux-iio@vger.kernel.org > > +Description: > > + This property causes an internal calibration of the als gain trim > > + value which is later used in calculating illuminance in lux. > > diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x > b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x > > new file mode 100644 > > index 0000000..275ae54 > > --- /dev/null > > +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x > > @@ -0,0 +1,14 @@ > > +What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate > > +KernelVersion: 2.6.37 > > +Contact: linux-iio@vger.kernel.org > > +Description: > > + This property causes an internal calibration of the als gain trim > > + value which is later used in calculating illuminance in lux. > Hmm.. could possibly move this into sysfs-bus-iio-light at some point > given we clearly have two drivers > using it. (fine for now though) > > + > > +What: /sys/bus/iio/devices/device[n]/proximity_calibrate > > +KernelVersion: 3.3-rc1 > > +Contact: linux-iio@vger.kernel.org > > +Description: > > + Causes an recalculation and adjustment to the > > + proximity_thresh_rising_value. > This one is interesting as there are other proximity sensors out there > (not light based) so we > may want to move this at some later point to a sysfs-bus-iio-proximity > documentation file. > > + > > diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio > b/drivers/staging/iio/Documentation/sysfs-bus-iio > > index 46a995d..5b2b5d3 100644 > > --- a/drivers/staging/iio/Documentation/sysfs-bus-iio > > +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio > > @@ -258,6 +258,8 @@ What > /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale > > What > /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibscale > > What > /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale > > What > /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale > > +what > /sys/bus/iio/devices/iio:deviceX/illuminance0_calibscale > > +what /sys/bus/iio/devices/iio:deviceX/proximity_calibscale > > KernelVersion: 2.6.35 > > Contact: linux-iio@vger.kernel.org > > Description: > > @@ -457,6 +459,10 @@ What: > /sys/.../events/in_voltageY_raw_thresh_falling_value > > What: /sys/.../events/in_voltageY_raw_thresh_falling_value > > What: /sys/.../events/in_tempY_raw_thresh_falling_value > > What: /sys/.../events/in_tempY_raw_thresh_falling_value > Oops, clearly and error in the lines above (repeats of falling and no > rising). I'll fix that up unless > someone else gets there first. OK - will not change in this patch. > > +What: /sys/.../events/illuminance0_thresh_falling_value > > +what: /sys/.../events/illuminance0_thresh_rising_value > > +what: /sys/.../events/proximity_thresh_falling_value > > +what: /sys/.../events/proximity_thresh_rising_value > > KernelVersion: 2.6.37 > > Contact: linux-iio@vger.kernel.org > > Description: > > @@ -739,3 +745,4 @@ Description: > > system. To minimize the current consumption of the system, > > the bridge can be disconnected (when it is not being used > > using the bridge_switch_en attribute. > > + > loose this extra blank line. > > diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light > b/drivers/staging/iio/Documentation/sysfs-bus-iio-light > > index edbf470..4385c70 100644 > > --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light > > +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light > > @@ -76,10 +76,10 @@ Contact: linux-iio@vger.kernel.org > > Description: > > This property gets/sets the sensors ADC analog integration > time. > > > > -What: /sys/bus/iio/devices/device[n]/illuminance0_calibscale > > +What: /sys/bus/iio/devices/device[n]/lux_table > > KernelVersion: 2.6.37 > > Contact: linux-iio@vger.kernel.org > > Description: > > - Hardware or software applied calibration scale factor assumed > > - to account for attenuation due to industrial design (glass > > - filters or aperture holes). > > + This property gets/sets the table of coefficients > > + used in calculating illuminance in lux. > > + > > diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 > b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 > > deleted file mode 100644 > > index 660781d..0000000 > > --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 > > +++ /dev/null > > @@ -1,20 +0,0 @@ > > -What: /sys/bus/iio/devices/device[n]/lux_table > > -KernelVersion: 2.6.37 > > -Contact: linux-iio@vger.kernel.org > > -Description: > > - This property gets/sets the table of coefficients > > - used in calculating illuminance in lux. > > - > > -What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate > > -KernelVersion: 2.6.37 > > -Contact: linux-iio@vger.kernel.org > > -Description: > > - This property causes an internal calibration of the als gain trim > > - value which is later used in calculating illuminance in lux. > > - > > -What: > /sys/bus/iio/devices/device[n]/illuminance0_input_target > > -KernelVersion: 2.6.37 > > -Contact: linux-iio@vger.kernel.org > > -Description: > > - This property is the known externally illuminance (in lux). > > - It is used in the process of calibrating the device accuracy. > > diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig > > index e7e9159..976f790 100644 > > --- a/drivers/staging/iio/light/Kconfig > > +++ b/drivers/staging/iio/light/Kconfig > > @@ -31,4 +31,12 @@ config TSL2583 > > Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices. > > Access ALS data via iio, sysfs. > > > > +config TSL2x7x > > + tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and > proximity sensors" > > + depends on I2C > > + help > > + Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, > tsl2672, > > + tmd2672, tsl2772, tmd2772 devices. > > + Provides iio_events and direct access via sysfs. > > + > > endmenu > > diff --git a/drivers/staging/iio/light/Makefile > b/drivers/staging/iio/light/Makefile > > index 3011fbf..0b8fb22 100644 > > --- a/drivers/staging/iio/light/Makefile > > +++ b/drivers/staging/iio/light/Makefile > > @@ -5,3 +5,5 @@ > > obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o > > obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o > > obj-$(CONFIG_TSL2583) += tsl2583.o > > +obj-$(CONFIG_TSL2x7x) += tsl2x7x_core.o > > +obj-$(CONFIG_TCS3x7x) += tcs3x7x_core.o > Really? Oops > > diff --git a/drivers/staging/iio/light/tsl2x7x.h > b/drivers/staging/iio/light/tsl2x7x.h > > new file mode 100644 > > index 0000000..fe9e853 > > --- /dev/null > > +++ b/drivers/staging/iio/light/tsl2x7x.h > > @@ -0,0 +1,99 @@ > > +/* > > + * Device driver for monitoring ambient light intensity (lux) > > + * and proximity (prox) within the TAOS TSL2X7X family of devices. > > + * > > + * Copyright (c) 2012, TAOS Corporation. > > + * > > + * 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 > > + * the Free Software Foundation; either version 2 of the License, or > > + * (at your option) any later version. > > + * > > + * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc., > > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. > > + */ > > + > > +#ifndef __TSL2X7X_H > > +#define __TSL2X7X_H > > +#include<linux/pm.h> > > + > > +/* Max number of segments allowable in LUX table */ > > +#define TSL2X7X_MAX_LUX_TABLE_SIZE 9 > > +#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) * > TSL2X7X_MAX_LUX_TABLE_SIZE) > > + > > +struct iio_dev; > > + > > +struct tsl2x7x_lux { > > + unsigned int ratio; > > + unsigned int ch0; > > + unsigned int ch1; > > +}; > > + > > +/** > > + * struct tsl2x7x_default_settings - power on defaults unless > > + * overridden by platform data. > > + * @als_time: ALS Integration time - multiple of 50mS > > + * @als_gain: Index into the ALS gain table. > > + * @prx_time: 5.2ms prox integration time - > > + * dec in 2.7ms periods > > + * @wait_time: Time between PRX and ALS cycles > > + * in 2.7 periods > > + * @prox_config: Prox configuration filters. > > + * @als_gain_trim: default gain trim to account for > > + * aperture effects. > > + * @als_cal_target: Known external ALS reading for > > + * calibration. > > + * @als_thresh_low: CH0 'low' count to trigger interrupt. > > + * @als_thresh_high: CH0 'high' count to trigger interrupt. > > + * @persistence: H/W Filters, Number of 'out of limits' > > + * ADC readings PRX/ALS. > > + * @interrupts_en: Enable/Disable - 0x00 = none, 0x10 = als, > > + * 0x20 = prx, 0x30 = bth > > + * @prox_thres_low: Low threshold proximity detection. > > + * @prox_thres_high: High threshold proximity detection > > + * @prox_max_samples_cal: Used for prox cal. > > + * @prox_pulse_count: Number if proximity emitter pulses > reorder the docs to match the structure. Pick which ever order makes > most sense > (don't worry about wasting a byte or two, clarity is more important on > structures > like this!) OK > > + */ > > +struct tsl2x7x_settings { > > + int als_time; > > + int als_gain; > > + int als_gain_trim; > > + int wait_time; > > + int prx_time; > > + int prox_gain; > > + int prox_config; > > + int als_cal_target; > > + u8 interrupts_en; > > + u8 persistence; > > + int als_thresh_low; > > + int als_thresh_high; > > + int prox_thres_low; > > + int prox_thres_high; > > + int prox_pulse_count; > > + int prox_max_samples_cal; > > +}; > > + > > +/** > > + * struct tsl2X7X_platform_data - Platform callback, glass and defaults > > + * @platform_power: Suspend/resume > platform callback > > + * @power_on: Power on callback > > + * @power_off: Power off callback > > + * @platform_lux_table: Device specific glass > coefficents > > + * @platform_default_settings: Device specific power on defaults > > + * Platform PM functions. > > + */ > > +struct tsl2X7X_platform_data { > > + int (*platform_power)(struct device *dev, pm_message_t); > > + int (*power_on) (struct iio_dev *indio_dev); > > + int (*power_off) (struct i2c_client *dev); > > + struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE]; > > + struct tsl2x7x_settings *platform_default_settings; > > +}; > > + > > +#endif /* __TSL2X7X_H */ > > diff --git a/drivers/staging/iio/light/tsl2x7x_core.c > b/drivers/staging/iio/light/tsl2x7x_core.c > > new file mode 100644 > > index 0000000..267faab > > --- /dev/null > > +++ b/drivers/staging/iio/light/tsl2x7x_core.c > > @@ -0,0 +1,1830 @@ > > +/* > > + * Device driver for monitoring ambient light intensity in (lux) > > + * and proximity detection (prox) within the TAOS TSL2X7X family of devices. > > + * > > + * Copyright (c) 2012, TAOS Corporation. > > + * > > + * 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 > > + * the Free Software Foundation; either version 2 of the License, or > > + * (at your option) any later version. > > + * > > + * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc., > > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. > > + */ > > + > > +#include<linux/kernel.h> > > +#include<linux/i2c.h> > > +#include<linux/errno.h> > > +#include<linux/delay.h> > > +#include<linux/mutex.h> > > +#include<linux/interrupt.h> > > +#include<linux/slab.h> > > +#include<linux/module.h> > > +#include<linux/version.h> > > +#include "tsl2x7x.h" > > +#include "../events.h" > > +#include "../iio.h" > > +#include "../sysfs.h" > > + > > +/* Cal defs*/ > > +#define PROX_STAT_CAL 0 > > +#define PROX_STAT_SAMP 1 > > +#define MAX_SAMPLES_CAL 200 > > + > > +/* TSL2X7X Device ID */ > > +#define TRITON_ID 0x00 > > +#define SWORDFISH_ID 0x30 > > +#define HALIBUT_ID 0x20 > hmmm.. fish ;) And Chips - uum ;^) > > + > > +/* Lux calculation constants */ > > +#define TSL2X7X_LUX_CALC_OVER_FLOW 65535 > > + > > +/* TAOS Register definitions - note: > > + * depending on device, some of these register are not used and the > > + * register address is benign. > > + */ > > +/* 2X7X register offsets */ > > +#define TSL2X7X_MAX_CONFIG_REG 16 > > + > > +/* Device Registers and Masks */ > > +#define TSL2X7X_CNTRL 0x00 > > +#define TSL2X7X_ALS_TIME 0X01 > > +#define TSL2X7X_PRX_TIME 0x02 > > +#define TSL2X7X_WAIT_TIME 0x03 > > +#define TSL2X7X_ALS_MINTHRESHLO 0X04 > > +#define TSL2X7X_ALS_MINTHRESHHI 0X05 > > +#define TSL2X7X_ALS_MAXTHRESHLO 0X06 > > +#define TSL2X7X_ALS_MAXTHRESHHI 0X07 > > +#define TSL2X7X_PRX_MINTHRESHLO 0X08 > > +#define TSL2X7X_PRX_MINTHRESHHI 0X09 > > +#define TSL2X7X_PRX_MAXTHRESHLO 0X0A > > +#define TSL2X7X_PRX_MAXTHRESHHI 0X0B > > +#define TSL2X7X_PERSISTENCE 0x0C > > +#define TSL2X7X_PRX_CONFIG 0x0D > > +#define TSL2X7X_PRX_COUNT 0x0E > > +#define TSL2X7X_GAIN 0x0F > > +#define TSL2X7X_NOTUSED 0x10 > > +#define TSL2X7X_REVID 0x11 > > +#define TSL2X7X_CHIPID 0x12 > > +#define TSL2X7X_STATUS 0x13 > > +#define TSL2X7X_ALS_CHAN0LO 0x14 > > +#define TSL2X7X_ALS_CHAN0HI 0x15 > > +#define TSL2X7X_ALS_CHAN1LO 0x16 > > +#define TSL2X7X_ALS_CHAN1HI 0x17 > > +#define TSL2X7X_PRX_LO 0x18 > > +#define TSL2X7X_PRX_HI 0x19 > > + > > +/* tsl2X7X cmd reg masks */ > > +#define TSL2X7X_CMD_REG 0x80 > > +#define TSL2X7X_CMD_SPL_FN 0x60 > > + > > +#define TSL2X7X_CMD_PROX_INT_CLR 0X05 > > +#define TSL2X7X_CMD_ALS_INT_CLR 0x06 > > +#define TSL2X7X_CMD_PROXALS_INT_CLR 0X07 > > + > > +/* tsl2X7X cntrl reg masks */ > > +#define TSL2X7X_CNTL_ADC_ENBL 0x02 > > +#define TSL2X7X_CNTL_PWR_ON 0x01 > > + > > +/* tsl2X7X status reg masks */ > > +#define TSL2X7X_STA_ADC_VALID 0x01 > > +#define TSL2X7X_STA_PRX_VALID 0x02 > > +#define TSL2X7X_STA_ADC_PRX_VALID 0x03 > Would prefer above defined as TSL2X7X_STA_ADC_VALID | > TSL2X7X_STA_PRX_VALID > (makes it obvious at a glance what is going on). > > +#define TSL2X7X_STA_ALS_INTR 0x10 > > +#define TSL2X7X_STA_ADC_INTR 0x10 > above unused (and seems to be repeated) ? (repeated value was suspicious ) > > +#define TSL2X7X_STA_PRX_INTR 0x20 > > + > > +#define TSL2X7X_STA_ADC_INTR 0x10 > > + > > +/* tsl2X7X cntrl reg masks */ > > +#define TSL2X7X_CNTL_REG_CLEAR 0x00 > > +#define TSL2X7X_CNTL_PROX_INT_ENBL 0X20 > > +#define TSL2X7X_CNTL_ALS_INT_ENBL 0X10 > > +#define TSL2X7X_CNTL_WAIT_TMR_ENBL 0X08 > > +#define TSL2X7X_CNTL_PROX_DET_ENBL 0X04 > > +#define TSL2X7X_CNTL_PWRON 0x01 > > +#define TSL2X7X_CNTL_ALSPON_ENBL 0x03 > > +#define TSL2X7X_CNTL_INTALSPON_ENBL 0x13 > > +#define TSL2X7X_CNTL_PROXPON_ENBL 0x0F > > +#define TSL2X7X_CNTL_INTPROXPON_ENBL 0x2F > > + > > +/*Prox diode to use */ > > +#define TSL2X7X_DIODE0 0x10 > > +#define TSL2X7X_DIODE1 0x20 > > +#define TSL2X7X_DIODE_BOTH 0x30 > > + > > +/* LED Power */ > > +#define TSL2X7X_mA100 0x00 > > +#define TSL2X7X_mA50 0x40 > > +#define TSL2X7X_mA25 0x80 > > +#define TSL2X7X_mA13 0xD0 > > + > > +/*Common device IIO EventMask */ > > +#define TSL2X7X_EVENT_MASK \ > > + (IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \ > > + IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)), > > + > > +/* TAOS txx2x7x Device family members */ > > +enum { > > + tsl2571, > > + tsl2671, > > + tmd2671, > > + tsl2771, > > + tmd2771, > > + tsl2572, > > + tsl2672, > > + tmd2672, > > + tsl2772, > > + tmd2772 > > +}; > > + > > +enum { > > + TSL2X7X_CHIP_UNKNOWN = 0, > > + TSL2X7X_CHIP_WORKING = 1, > > + TSL2X7X_CHIP_SUSPENDED = 2 > > +}; > > + > > +/* Per-device data */ > > +struct tsl2x7x_als_info { > > + u16 als_ch0; > > + u16 als_ch1; > > + u16 lux; > > +}; > > + > > +struct prox_stat { > > + u16 min; > > + u16 max; > > + u16 mean; > > + unsigned long stddev; > > +}; > > + > > +struct tsl2x7x_chip_info { > > + int chan_table_elements; > > + struct iio_chan_spec channel[9]; > > + const struct iio_info *info; > > +}; > > + > > +struct tsl2X7X_chip { > > + kernel_ulong_t id; > > + struct mutex prox_mutex; > > + struct mutex als_mutex; > > + struct i2c_client *client; > > + u16 prox_data; > > + struct tsl2x7x_als_info als_cur_info; > > + struct tsl2x7x_settings tsl2x7x_settings; > > + struct tsl2X7X_platform_data *pdata; > > + int als_time_scale; > > + int als_saturation; > > + int tsl2x7x_chip_status; > > + u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG]; > > + const struct tsl2x7x_chip_info *chip_info; > > + const struct iio_info *info; > > + s64 event_timestamp; > > + /* This structure is intentionally large to accommodate > > + * updates via sysfs. */ > > + /* Sized to 9 = max 8 segments + 1 termination segment */ > > + struct tsl2x7x_lux tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE]; > > +}; > > + > > +/* Different devices require different coefficents */ > > +static const struct tsl2x7x_lux tsl2x71_lux_table[] = { > > + { 14461, 611, 1211 }, > > + { 18540, 352, 623 }, > > + { 0, 0, 0 }, > > +}; > > + > > +static const struct tsl2x7x_lux tmd2x71_lux_table[] = { > > + { 11635, 115, 256 }, > > + { 15536, 87, 179 }, > > + { 0, 0, 0 }, > > +}; > > + > > +static const struct tsl2x7x_lux tsl2x72_lux_table[] = { > > + { 14013, 466, 917 }, > > + { 18222, 310, 552 }, > > + { 0, 0, 0 }, > > +}; > > + > > +static const struct tsl2x7x_lux tmd2x72_lux_table[] = { > > + { 13218, 130, 262 }, > > + { 17592, 92, 169 }, > > + { 0, 0, 0 }, > > +}; > > + > > +static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] = { > > + [tsl2571] = tsl2x71_lux_table, > > + [tsl2671] = tsl2x71_lux_table, > > + [tmd2671] = tmd2x71_lux_table, > > + [tsl2771] = tsl2x71_lux_table, > > + [tmd2771] = tmd2x71_lux_table, > > + [tsl2572] = tsl2x72_lux_table, > > + [tsl2672] = tsl2x72_lux_table, > > + [tmd2672] = tmd2x72_lux_table, > > + [tsl2772] = tsl2x72_lux_table, > > + [tmd2772] = tmd2x72_lux_table, > > +}; > > + > > +static const struct tsl2x7x_settings tsl2x7x_default_settings = { > > + .als_time = 200, > > + .als_gain = 0, > > + .prx_time = 0xfe, /*5.4 mS */ > > + .prox_gain = 1, > > + .wait_time = 245, > > + .prox_config = 0, > > + .als_gain_trim = 1000, > > + .als_cal_target = 150, > > + .als_thresh_low = 200, > > + .als_thresh_high = 256, > > + .persistence = 0xFF, > > + .interrupts_en = 0x00, > > + .prox_thres_low = 0, > > + .prox_thres_high = 512, > > + .prox_max_samples_cal = 30, > > + .prox_pulse_count = 8 > > +}; > > + > > +static const s16 tsl2X7X_als_gainadj[] = { > > + 1, > > + 8, > > + 16, > > + 120 > > +}; > > + > > +static const s16 tsl2X7X_prx_gainadj[] = { > > + 1, > > + 2, > > + 4, > > + 8 > > +}; > > + > > +/* Channel variations */ > > +enum { > > + ALS, > > + PRX, > > + ALSPRX, > > + PRX2, > > + ALSPRX2, > > +}; > > + > > +const u8 device_channel_config[] = { > > + ALS, > > + PRX, > > + PRX, > > + ALSPRX, > > + ALSPRX, > > + ALS, > > + PRX2, > > + PRX2, > > + ALSPRX2, > > + ALSPRX2 > > +}; > > + > > +/* > > + * Read a number of bytes starting at register (reg) location. > > + * Return 0, or i2c_smbus_write_byte ERROR code. > > + */ > Preference for kernel doc on all function descriptions. (not crucial > though). > > +static int > > +tsl2x7x_i2c_read(struct i2c_client *client, u8 reg, u8 *val) > > +{ > > + int ret; > > + > > + /* select register to write */ > > + ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg)); > > + if (ret< 0) { > > + dev_err(&client->dev, "%s: failed to write register %x\n" > > + , __func__, reg); > > + return ret; > > + } > > + /* read the data */ > > + *val = i2c_smbus_read_byte(client); > No error handling on the i2c_smbus_read_byte. > > + > > + return 0; > > +} > > + > > +/** > > + * tsl2x7x_get_lux() - Reads and calculates current lux value. > > + * @indio_dev: IIO device > > + * > > + * The raw ch0 and ch1 values of the ambient light sensed in the last > > + * integration cycle are read from the device. > > + * Time scale factor array values are adjusted based on the integration time. > > + * The raw values are multiplied by a scale factor, and device gain is obtained > > + * using gain index. Limit checks are done next, then the ratio of a multiple > > + * of ch1 value, to the ch0 value, is calculated. Array tsl2x7x_device_lux[] > > + * is then scanned to find the first ratio value that is just above the ratio > > + * we just calculated. The ch0 and ch1 multiplier constants in the array are > > + * then used along with the time scale factor array values, to calculate the > > + * lux. > > + */ > > +static int tsl2x7x_get_lux(struct iio_dev *indio_dev) > > +{ > > + u16 ch0, ch1; /* separated ch0/ch1 data from device */ > > + u32 lux; /* raw lux calculated from device data */ > > + u64 lux64; > > + u32 ratio; > > + u8 buf[4]; > > + struct tsl2x7x_lux *p; > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + int i, ret; > > + u32 ch0lux = 0; > > + u32 ch1lux = 0; > > + > > + if (mutex_trylock(&chip->als_mutex) == 0) { > > + dev_info(&chip->client->dev, "tsl2x7x_get_lux device is > busy\n"); > Isn't this dev_info going to give you a lot of log entries? Does anyone > care that the value is > a little stale? Guess not. > > + return chip->als_cur_info.lux; /* busy, so return LAST VALUE */ > > + } > > + > > + if (chip->tsl2x7x_chip_status != TSL2X7X_CHIP_WORKING) { > > + /* device is not enabled */ > > + dev_err(&chip->client->dev, "%s: device is not enabled\n", > > + __func__); > > + ret = -EBUSY ; > > + goto out_unlock; > > + } > > + > > + ret = tsl2x7x_i2c_read(chip->client, > > + (TSL2X7X_CMD_REG | TSL2X7X_STATUS),&buf[0]); > > + if (ret< 0) { > > + dev_err(&chip->client->dev, > > + "%s: failed to read CMD_REG\n", __func__); > > + goto out_unlock; > > + } > > + /* is data new& valid */ > > + if (!(buf[0]& TSL2X7X_STA_ADC_VALID)) { > > + dev_err(&chip->client->dev, > > + "%s: data not valid yet\n", __func__); > > + ret = chip->als_cur_info.lux; /* return LAST VALUE */ > > + goto out_unlock; > > + } > > + > > + for (i = 0; i< 4; i++) { > > + ret = tsl2x7x_i2c_read(chip->client, > > + (TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i)), > > + &buf[i]); > > + if (ret< 0) { > > + dev_err(&chip->client->dev, > > + "%s: failed to read. err=%x\n", __func__, ret); > > + goto out_unlock; > > + } > > + } > > + > > + /* clear status, really interrupt status ( are off), > > + but we use the bit anyway */ > Umm.. not sure I follow ( are off) bit.... > > + ret = i2c_smbus_write_byte(chip->client, > > + (TSL2X7X_CMD_REG | > > + TSL2X7X_CMD_SPL_FN | > > + TSL2X7X_CMD_ALS_INT_CLR)); > > + > Unwanted blank line here? > > + if (ret< 0) { > > + dev_err(&chip->client->dev, > > + "i2c_write_command failed in %s, err = %d\n", > > + __func__, ret); > > + goto out_unlock; /* have no data, so return failure */ > > + } > > + > > + /* extract ALS/lux data */ > > + ch0 = le16_to_cpup((const __le16 *)&buf[0]); > > + ch1 = le16_to_cpup((const __le16 *)&buf[2]); > > + > > + chip->als_cur_info.als_ch0 = ch0; > > + chip->als_cur_info.als_ch1 = ch1; > > + > > + if ((ch0>= chip->als_saturation) || (ch1>= chip->als_saturation)) { > > + lux = TSL2X7X_LUX_CALC_OVER_FLOW; > > + goto return_max; > > + } > > + > > + if (ch0 == 0) { > > + /* have no data, so return LAST VALUE */ > > + ret = chip->als_cur_info.lux = 0; > That's not returning the last value as per comment???? > > + goto out_unlock; > > + } > > + /* calculate ratio */ > > + ratio = (ch1<< 15) / ch0; > > + /* convert to unscaled lux using the pointer to the table */ > > + p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux; > > + while (p->ratio != 0&& p->ratio< ratio) > > + p++; > That's a rather large indent on the p++! > > + > > + if (p->ratio == 0) { > > + lux = 0; > > + } else { > > + ch0lux = DIV_ROUND_UP((ch0 * p->ch0), > > + tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]); > > + ch1lux = DIV_ROUND_UP((ch1 * p->ch1), > > + tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]); > > + lux = ch0lux - ch1lux; > > + } > > + > > + /* note: lux is 31 bit max at this point */ > > + if (ch1lux> ch0lux) { > Could say whn it's returning the last value? Would make debug comments > moer helpful perhaps. > > + dev_dbg(&chip->client->dev, "Returning last value\n"); > > + ret = chip->als_cur_info.lux; > > + goto out_unlock; > > + } > > + > > + /* adjust for active time scale */ > > + if (chip->als_time_scale == 0) > > + lux = 0; > > + else > > + lux = (lux + (chip->als_time_scale>> 1)) / > > + chip->als_time_scale; > > + > > + /* adjust for active gain scale > > + * The tsl2x7x_device_lux tables have a factor of 256 built-in. > > + * User-specified gain provides a multiplier. > > + * Apply user-specified gain before shifting right to retain precision. > > + * Use 64 bits to avoid overflow on multiplication. > > + * Then go back to 32 bits before division to avoid using div_u64(). > > + */ > > + lux64 = lux; > > + lux64 = lux64 * chip->tsl2x7x_settings.als_gain_trim; > > + lux64>>= 8; > > + lux = lux64; > > + lux = (lux + 500) / 1000; > > + > > + if (lux> TSL2X7X_LUX_CALC_OVER_FLOW) /* check for overflow */ > > + lux = TSL2X7X_LUX_CALC_OVER_FLOW; > > + > > + /* Update the structure with the latest lux. */ > > +return_max: > > + chip->als_cur_info.lux = lux; > > + ret = lux; > > + > > +out_unlock: > > + mutex_unlock(&chip->als_mutex); > > + > > + return ret; > > +} > > + > > +/* Proximity poll function */ > > +static int tsl2x7x_prox_poll(struct iio_dev *indio_dev) > > +{ > > + int i; > > + int ret; > > + u8 status; > > + u8 chdata[2]; > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + > > + if (mutex_trylock(&chip->prox_mutex) == 0) { > > + dev_err(&chip->client->dev, > > + "%s: Can't get prox mutex\n", __func__); > > + return -EBUSY; > > + } > > + > > + ret = tsl2x7x_i2c_read(chip->client, > > + (TSL2X7X_CMD_REG | TSL2X7X_STATUS),&status); > > + if (ret< 0) { > > + dev_err(&chip->client->dev, > > + "%s: i2c err=%d\n", __func__, ret); > > + goto prox_poll_err; > > + } > > + > I'd prefer a switch on the chip->id's to this less than comparison. Looks > like something that might get accidentally broken in future. > > + if (chip->id< tsl2572) { > > + if (!(status& TSL2X7X_STA_ADC_VALID)) > > + goto prox_poll_err; > > + } else if (!(status& TSL2X7X_STA_PRX_VALID)) > > + goto prox_poll_err; > > + > > + for (i = 0; i< 2; i++) { > > + ret = tsl2x7x_i2c_read(chip->client, > > + (TSL2X7X_CMD_REG | > > + (TSL2X7X_PRX_LO + i)),&chdata[i]); > > + if (ret< 0) > > + goto prox_poll_err; > > + } > > + > > + chip->prox_data = > > + le16_to_cpup((const __le16 *)&chdata[0]); > > + > > +prox_poll_err: > > + > > + mutex_unlock(&chip->prox_mutex); > > + return chip->prox_data; > > +} > > + > > +/* > > + * Provides initial operational parameter defaults. > > + * These defaults may be changed through the device's sysfs files. > > + */ > > +static void tsl2x7x_defaults(struct tsl2X7X_chip *chip) > > +{ > > + /* If Operational settings defined elsewhere.. */ > > + if (chip->pdata&& chip->pdata->platform_default_settings != 0) > > + memcpy(&(chip->tsl2x7x_settings), > > + chip->pdata->platform_default_settings, > > + sizeof(tsl2x7x_default_settings)); > > + else > > + memcpy(&(chip->tsl2x7x_settings), > > + &tsl2x7x_default_settings, > > + sizeof(tsl2x7x_default_settings)); > > + > > + /* Load up the proper lux table. */ > > + if (chip->pdata&& chip->pdata->platform_lux_table[0].ratio != 0) > > + memcpy(chip->tsl2x7x_device_lux, > > + chip->pdata->platform_lux_table, > > + sizeof(chip->pdata->platform_lux_table)); > > + else > > + memcpy(chip->tsl2x7x_device_lux, > > + (struct tsl2x7x_lux *)tsl2x7x_default_lux_table_group[chip->id], > > + MAX_DEFAULT_TABLE_BYTES); > Unwanted blank line. > > + > > +} > > + > > +/* > > + * Obtain single reading and calculate the als_gain_trim > > + * (later used to derive actual lux). > > + * Return updated gain_trim value. > kernel doc preferred. > > + */ > > +static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev) > > +{ > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + u8 reg_val; > > + int gain_trim_val; > > + int ret; > > + int lux_val; > > + > > + ret = i2c_smbus_write_byte(chip->client, > > + (TSL2X7X_CMD_REG | TSL2X7X_CNTRL)); > > + if (ret< 0) { > > + dev_err(&chip->client->dev, > > + "%s: failed to write CNTRL register, ret=%d\n", > > + __func__, ret); > > + return ret; > > + } > > + > > + reg_val = i2c_smbus_read_byte(chip->client); > > + if ((reg_val& (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) > > + != (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) { > > + dev_err(&chip->client->dev, > > + "%s: failed: ADC not enabled\n", __func__); > > + return -1; > > + } > > + > > + ret = i2c_smbus_write_byte(chip->client, > > + (TSL2X7X_CMD_REG | TSL2X7X_CNTRL)); > > + if (ret< 0) { > > + dev_err(&chip->client->dev, > > + "%s: failed to write ctrl reg: ret=%d\n", > > + __func__, ret); > > + return ret; > > + } > > + > > + reg_val = i2c_smbus_read_byte(chip->client); > > + if ((reg_val& TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) { > > + dev_err(&chip->client->dev, > > + "%s: failed: STATUS - ADC not valid.\n", __func__); > > + return -ENODATA; > > + } > > + > > + lux_val = tsl2x7x_get_lux(indio_dev); > > + if (lux_val< 0) { > > + dev_err(&chip->client->dev, > > + "%s: failed to get lux\n", __func__); > > + return lux_val; > > + } > > + > > + gain_trim_val = (((chip->tsl2x7x_settings.als_cal_target) > > + * chip->tsl2x7x_settings.als_gain_trim) / lux_val); > > + if ((gain_trim_val< 250) || (gain_trim_val> 4000)) > > + return -ERANGE; > > + > > + chip->tsl2x7x_settings.als_gain_trim = gain_trim_val; > > + dev_info(&chip->client->dev, > > + "%s als_calibrate completed\n", chip->client->name); > > + > > + return (int) gain_trim_val; > > +} > > + > > +/* > > + * Turn the device on. > > + * Configuration must be set before calling this function. > > + */ > > +static int tsl2x7x_chip_on(struct iio_dev *indio_dev) > > +{ > > + int i; > > + int ret = 0; > Can't immediately see a path where this isn't set anyway. > > + u8 *dev_reg; > > + u8 utmp; > > + int als_count; > > + int als_time; > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + u8 reg_val = 0; > > + > > + if (chip->pdata&& chip->pdata->power_on) > > + chip->pdata->power_on(indio_dev); > > + > > + /* Non calculated parameters */ > > + chip->tsl2x7x_config[TSL2X7X_PRX_TIME] = > > + chip->tsl2x7x_settings.prx_time; > > + chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] = > > + chip->tsl2x7x_settings.wait_time; > > + chip->tsl2x7x_config[TSL2X7X_PRX_CONFIG] = > > + chip->tsl2x7x_settings.prox_config; > > + > > + chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHLO] = > > + (chip->tsl2x7x_settings.als_thresh_low)& 0xFF; > > + chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHHI] = > > + (chip->tsl2x7x_settings.als_thresh_low>> 8)& 0xFF; > > + chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHLO] = > > + (chip->tsl2x7x_settings.als_thresh_high)& 0xFF; > > + chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHHI] = > > + (chip->tsl2x7x_settings.als_thresh_high>> 8)& 0xFF; > > + chip->tsl2x7x_config[TSL2X7X_PERSISTENCE] = > > + chip->tsl2x7x_settings.persistence; > > + > > + chip->tsl2x7x_config[TSL2X7X_PRX_COUNT] = > > + chip->tsl2x7x_settings.prox_pulse_count; > > + chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHLO] = > > + chip->tsl2x7x_settings.prox_thres_low; > > + chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHLO] = > > + chip->tsl2x7x_settings.prox_thres_high; > > + > > + /* and make sure we're not already on */ > > + if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) { > > + /* if forcing a register update - turn off, then on */ > > + dev_info(&chip->client->dev, "device is already enabled\n"); > > + return -EINVAL; > > + } > > + > > + /* determine als integration regster */ > > + als_count = (chip->tsl2x7x_settings.als_time * 100 + 135) / 270; > > + if (als_count == 0) > > + als_count = 1; /* ensure at least one cycle */ > > + > > + /* convert back to time (encompasses overrides) */ > > + als_time = (als_count * 27 + 5) / 10; > > + chip->tsl2x7x_config[TSL2X7X_ALS_TIME] = 256 - als_count; > > + > > + /* Set the gain based on tsl2x7x_settings struct */ > > + chip->tsl2x7x_config[TSL2X7X_GAIN] = > > + (chip->tsl2x7x_settings.als_gain | > > + (TSL2X7X_mA100 | TSL2X7X_DIODE1) > > + | ((chip->tsl2x7x_settings.prox_gain)<< 2)); > > + > > + /* set chip struct re scaling and saturation */ > > + chip->als_saturation = als_count * 922; /* 90% of full scale */ > > + chip->als_time_scale = (als_time + 25) / 50; > > + > > + /* TSL2X7X Specific power-on / adc enable sequence > > + * Power on the device 1st. */ > > + utmp = TSL2X7X_CNTL_PWR_ON; > > + ret = i2c_smbus_write_byte_data(chip->client, > > + TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp); > > + if (ret< 0) { > > + dev_err(&chip->client->dev, > > + "%s: failed on CNTRL reg.\n", __func__); > > + return -1; > > + } > > + > > + /* Use the following shadow copy for our delay before enabling ADC. > > + * Write all the registers. */ > > + for (i = 0, dev_reg = chip->tsl2x7x_config; > > + i< TSL2X7X_MAX_CONFIG_REG; i++) { > > + ret = i2c_smbus_write_byte_data(chip->client, > > + TSL2X7X_CMD_REG + i, *dev_reg++); > > + if (ret< 0) { > > + dev_err(&chip->client->dev, > > + "%s: failed on write to reg %d.\n", __func__, i); > > + return ret; > > + } > > + } > > + > > + udelay(3000); /* Power-on settling time */ > > + > > + /* NOW enable the ADC > > + * initialize the desired mode of operation */ > > + utmp = TSL2X7X_CNTL_PWR_ON | > > + TSL2X7X_CNTL_ADC_ENBL | > > + TSL2X7X_CNTL_PROX_DET_ENBL; > > + ret = i2c_smbus_write_byte_data(chip->client, > > + TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp); > > + if (ret< 0) { > > + dev_err(&chip->client->dev, > > + "%s: failed on 2nd CTRL reg.\n", __func__); > > + return ret; > > + } > Indent on the bracket doesn't look right.. > > + > > + chip->tsl2x7x_chip_status = TSL2X7X_CHIP_WORKING; > > + > > + if (chip->tsl2x7x_settings.interrupts_en != 0) { > > + dev_info(&chip->client->dev, "Setting Up Interrupt(s)\n"); > > + > > + reg_val = TSL2X7X_CNTL_PWR_ON | > TSL2X7X_CNTL_ADC_ENBL; > > + if ((chip->tsl2x7x_settings.interrupts_en == 0x20) || > > + (chip->tsl2x7x_settings.interrupts_en == 0x30)) > > + reg_val |= TSL2X7X_CNTL_PROX_DET_ENBL; > > + > > + reg_val |= chip->tsl2x7x_settings.interrupts_en; > > + ret = i2c_smbus_write_byte_data(chip->client, > > + (TSL2X7X_CMD_REG | TSL2X7X_CNTRL), reg_val); > > + if (ret< 0) > > + dev_err(&chip->client->dev, > > + "%s: failed in tsl2x7x_IOCTL_INT_SET.\n", > > + __func__); > > + > > + /* Clear out any initial interrupts */ > > + ret = i2c_smbus_write_byte(chip->client, > > + TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN | > > + TSL2X7X_CMD_PROXALS_INT_CLR); > > + if (ret< 0) { > > + dev_err(&chip->client->dev, > > + "%s: failed in tsl2x7x_chip_on\n", > err. message will be tsl2x7x_chip_on: failed in tsl2x7x_chip_on > Spot the redundant information! > > + __func__); > > + return ret; > > + } > > + } > > + > > + return ret; > > +} > > + > > +static int tsl2x7x_chip_off(struct iio_dev *indio_dev) > > +{ > > + int ret; > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + > > + /* turn device off */ > > + chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED; > > + > > + ret = i2c_smbus_write_byte_data(chip->client, > > + TSL2X7X_CMD_REG | TSL2X7X_CNTRL, 0x00); > > + > > + if (chip->pdata&& chip->pdata->power_off) > > + chip->pdata->power_off(chip->client); > > + > > + return ret; > > +} > > + > > +/* > > + * Proximity calibration helper function > > + * runs through a collection of data samples, > > + * sets the min, max, mean, and std dev. > > + */ > > +static > > +void tsl2x7x_prox_calculate(u16 *data, int length, struct prox_stat *statP) > > +{ > > + int i; > > + int sample_min, sample_max, sample_sum, sample_mean; > > + unsigned long stddev; > > + int tmp; > > + > > + if (length == 0) > > + length = 1; > > + > > + sample_sum = 0; > > + sample_min = INT_MAX; > > + sample_max = INT_MIN; > > + for (i = 0; i< length; i++) { > > + sample_sum += data[i]; > > + if (data[i]< sample_min) > > + sample_min = data[i]; > kernel has min and max macros. Use them. e.g. sample_min = > min(sample_min, data[i]); > > > + if (data[i]> sample_max) > > + sample_max = data[i]; > > + } > > + sample_mean = sample_sum/length; > > + statP->min = sample_min; > > + statP->max = sample_max; > > + statP->mean = sample_mean; > Looks like a trivial gain in having local copies, why not just use the > statP versions directly? > > + > > + sample_sum = 0; > > + for (i = 0; i< length; i++) { > > + tmp = data[i]-sample_mean; > > + sample_sum += tmp * tmp; > > + } > > + stddev = int_sqrt((long)sample_sum)/length; > > + statP->stddev = stddev; > > +} > > + > > +/** > > + * Proximity calibration - collects a number of samples, > > + * calculates a standard deviation based on the samples, and > > + * sets the threshold accordingly. > kernel-doc plese. > > + */ > > +static void tsl2x7x_prox_cal(struct iio_dev *indio_dev) > > +{ > > + u16 prox_history[MAX_SAMPLES_CAL + 1]; > > + int i; > > + struct prox_stat prox_stat_data[2]; > > + struct prox_stat *calP; > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + u8 tmp_irq_settings; > > + u8 current_state = chip->tsl2x7x_chip_status; > > + > > + if (chip->tsl2x7x_settings.prox_max_samples_cal> MAX_SAMPLES_CAL) > { > > + dev_err(&chip->client->dev, > > + "%s: max prox samples cal is too big: %d\n", > > + __func__, chip- > >tsl2x7x_settings.prox_max_samples_cal); > > + chip->tsl2x7x_settings.prox_max_samples_cal = > MAX_SAMPLES_CAL; > > + } > > + > > + /* have to stop to change settings */ > > + tsl2x7x_chip_off(indio_dev); > > + > > + /* Enable proximity detection save just in case prox not wanted yet*/ > > + tmp_irq_settings = chip->tsl2x7x_settings.interrupts_en; > > + chip->tsl2x7x_settings.interrupts_en |= > TSL2X7X_CNTL_PROX_INT_ENBL; > > + > > + /*turn on device if not already on*/ > > + tsl2x7x_chip_on(indio_dev); > > + > > + /*gather the samples*/ > > + for (i = 0; i< chip->tsl2x7x_settings.prox_max_samples_cal; i++) { > > + mdelay(15); > > + tsl2x7x_prox_poll(indio_dev); > > + prox_history[i] = chip->prox_data; > > + dev_info(&chip->client->dev, "2 i=%d prox data= %d\n", > > + i, chip->prox_data); > > + } > > + > > + tsl2x7x_chip_off(indio_dev); > > + calP =&prox_stat_data[PROX_STAT_CAL]; > > + tsl2x7x_prox_calculate(prox_history, > > + chip->tsl2x7x_settings.prox_max_samples_cal, calP); > > + chip->tsl2x7x_settings.prox_thres_high = (calP->max<< 1) - calP- > >mean; > > + > > + dev_info(&chip->client->dev, " cal min=%d mean=%d max=%d\n", > > + calP->min, calP->mean, calP->max); > > + dev_info(&chip->client->dev, > > + "%s proximity threshold set to %d\n", > > + chip->client->name, chip->tsl2x7x_settings.prox_thres_high); > > + > > + /* back to the way they were */ > > + chip->tsl2x7x_settings.interrupts_en = tmp_irq_settings; > > + if (current_state == TSL2X7X_CHIP_WORKING) > > + tsl2x7x_chip_on(indio_dev); > > +} > > + > > +static ssize_t tsl2x7x_power_state_show(struct device *dev, > > + struct device_attribute *attr, char *buf) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > Could do the above in one go. > struct tsl2X7X_chip *chip = iio_priv(dev_get_drvdata(dev)); Thanks for the tip! > Lots more cases of this below. > > + > > + return snprintf(buf, PAGE_SIZE, "%d\n", chip->tsl2x7x_chip_status); > > +} > > + > > +static ssize_t tsl2x7x_power_state_store(struct device *dev, > > + struct device_attribute *attr, const char *buf, size_t len) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + bool value; > > + > > + if (strtobool(buf,&value)) > > + return -EINVAL; > > + > Seems to me that inverting the logic of this if would make things > a tiny bit easier to read. > > + if (!value) > > + tsl2x7x_chip_off(indio_dev); > > + else > > + tsl2x7x_chip_on(indio_dev); > > + > > + return len; > > +} > > + > > +static ssize_t tsl2x7x_gain_available_show(struct device *dev, > > + struct device_attribute *attr, char *buf) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + > > + if (chip->id> tsl2771) > Switch on partnumbers preferred. > > + return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 128"); > > + else > > + return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 120"); > > +} > > + > > +static ssize_t tsl2x7x_prox_gain_available_show(struct device *dev, > > + struct device_attribute *attr, char *buf) > > +{ > > + return snprintf(buf, PAGE_SIZE, "%s\n", "1 2 4 8"); > > +} > > + > > +static ssize_t tsl2x7x_als_time_show(struct device *dev, > > + struct device_attribute *attr, char *buf) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + > > + return snprintf(buf, PAGE_SIZE, "%d\n", > > + chip->tsl2x7x_settings.als_time); > > +} > > + > > +static ssize_t tsl2x7x_als_time_store(struct device *dev, > > + struct device_attribute *attr, const char *buf, size_t len) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + unsigned long value; > > + > > + if (kstrtoul(buf, 0,&value)) > > + return -EINVAL; > > + > > + if ((value< 50) || (value> 650)) > > + return -EINVAL; > > + > > + if (value % 50) > > + return -EINVAL; > > + > > + chip->tsl2x7x_settings.als_time = value; > > + > > + return len; > > +} > > + > > +static IIO_CONST_ATTR(illuminance0_integration_time_available, > > + "50 100 150 200 250 300 350 400 450 500 550 600 650"); > > + > > +static ssize_t tsl2x7x_als_cal_target_show(struct device *dev, > > + struct device_attribute *attr, char *buf) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + > > + return snprintf(buf, PAGE_SIZE, "%d\n", > > + chip->tsl2x7x_settings.als_cal_target); > > +} > > + > > +static ssize_t tsl2x7x_als_cal_target_store(struct device *dev, > > + struct device_attribute *attr, const char *buf, size_t len) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + unsigned long value; > > + > > + if (kstrtoul(buf, 0,&value)) > > + return -EINVAL; > > + > > + if (value) > > + chip->tsl2x7x_settings.als_cal_target = value; > > + > > + return len; > > +} > > + > > +/* sampling_frequency AKA persistence in data sheet */ > > +static ssize_t tsl2x7x_persistence_show(struct device *dev, > > + struct device_attribute *attr, char *buf) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + > > + return snprintf(buf, PAGE_SIZE, "%d\n", > > + chip->tsl2x7x_settings.persistence); > > +} > > + > > +static ssize_t tsl2x7x_persistence_store(struct device *dev, > > + struct device_attribute *attr, const char *buf, size_t len) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + unsigned long value; > > + > > + if (kstrtoul(buf, 0,&value)) > > + return -EINVAL; > > + > > + chip->tsl2x7x_settings.persistence = value; > > + > > + return len; > > +} > > + > > +static IIO_CONST_ATTR(sampling_frequency_available, > > + "0x00 - 0xFF (0 - 255)"); > What units? This really should be converted into hz even if it's > somewhat of a pain to do. > > + > > +static ssize_t tsl2x7x_do_calibrate(struct device *dev, > > + struct device_attribute *attr, const char *buf, size_t len) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + bool value; > > + > > + if (strtobool(buf,&value)) > > + return -EINVAL; > > + > > + if (value) > > + tsl2x7x_als_calibrate(indio_dev); > > + > > + return len; > > +} > > + > > +static ssize_t tsl2x7x_luxtable_show(struct device *dev, > > + struct device_attribute *attr, char *buf) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + int i = 0; > > + int offset = 0; > > + > > + while (i< (TSL2X7X_MAX_LUX_TABLE_SIZE * 3)) { > > + offset += snprintf(buf + offset, PAGE_SIZE, "%d,%d,%d,", > > + chip->tsl2x7x_device_lux[i].ratio, > > + chip->tsl2x7x_device_lux[i].ch0, > > + chip->tsl2x7x_device_lux[i].ch1); > > + if (chip->tsl2x7x_device_lux[i].ratio == 0) { > > + /* We just printed the first "0" entry. > > + * Now get rid of the extra "," and break. */ > > + offset--; > > + break; > > + } > > + i++; > > + } > > + > > + offset += snprintf(buf + offset, PAGE_SIZE, "\n"); > > + return offset; > > +} > > + > > +static ssize_t tsl2x7x_luxtable_store(struct device *dev, > > + struct device_attribute *attr, const char *buf, size_t len) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + int value[ARRAY_SIZE(chip->tsl2x7x_device_lux)*3 + 1]; > > + int n; > > + > > + get_options(buf, ARRAY_SIZE(value), value); > > + > > + /* We now have an array of ints starting at value[1], and > > + * enumerated by value[0]. > > + * We expect each group of three ints is one table entry, > > + * and the last table entry is all 0. > > + */ > > + n = value[0]; > > + if ((n % 3) || n< 6 || > > + n> ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) { > > + dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n); > > + return -EINVAL; > > + } > > + > > + if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) { > > + dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n); > > + return -EINVAL; > > + } > > + > > + if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) > > + tsl2x7x_chip_off(indio_dev); > > + > > + /* Zero out the table */ > > + memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux)); > > + memcpy(chip->tsl2x7x_device_lux,&value[1], (value[0] * 4)); > > + > > + return len; > > +} > > + > > +static ssize_t tsl2x7x_do_prox_calibrate(struct device *dev, > > + struct device_attribute *attr, const char *buf, size_t len) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + bool value; > > + > > + if (strtobool(buf,&value)) > > + return -EINVAL; > > + > > + if (value) > > + tsl2x7x_prox_cal(indio_dev); > > + > > + return len; > > +} > > + > > +static int tsl2x7x_read_interrupt_config(struct iio_dev *indio_dev, > > + u64 event_code) > > +{ > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + int ret; > > + > > + if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == > IIO_INTENSITY) > > + ret = !!(chip->tsl2x7x_settings.interrupts_en& 0x10); > > + else > > + ret = !!(chip->tsl2x7x_settings.interrupts_en& 0x20); > > + > > + return ret; > > +} > > + > > +static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev, > > + u64 event_code, > > + int val) > > +{ > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + > > + if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == > IIO_INTENSITY) { > > + if (val) > > + chip->tsl2x7x_settings.interrupts_en |= 0x10; > > + else > > + chip->tsl2x7x_settings.interrupts_en&= 0x20; > > + } else { > > + if (val) > > + chip->tsl2x7x_settings.interrupts_en |= 0x20; > > + else > > + chip->tsl2x7x_settings.interrupts_en&= 0x10; > > + } > Would normally expect this to write the settings to the device. Any changes to parameters require device off/on. > > + > > + return 0; > > +} > > + > > +static int tsl2x7x_write_thresh(struct iio_dev *indio_dev, > > + u64 event_code, > > + int val) > > +{ > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + > > + if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == > IIO_INTENSITY) { > > + switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { > > + case IIO_EV_DIR_RISING: > > + chip->tsl2x7x_settings.als_thresh_high = val; > > + break; > > + case IIO_EV_DIR_FALLING: > > + chip->tsl2x7x_settings.als_thresh_low = val; > > + break; > > + default: > > + return -EINVAL; > > + } > > + } else { > > + switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { > > + case IIO_EV_DIR_RISING: > > + chip->tsl2x7x_settings.prox_thres_high = val; > > + break; > > + case IIO_EV_DIR_FALLING: > > + chip->tsl2x7x_settings.prox_thres_low = val; > > + break; > > + default: > > + return -EINVAL; > > + } > > + } > > + > > + return 0; > > +} > > + > > +static int tsl2x7x_read_thresh(struct iio_dev *indio_dev, > > + u64 event_code, > > + int *val) > > +{ > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + > > + if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == > IIO_INTENSITY) { > > + switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { > > + case IIO_EV_DIR_RISING: > > + *val = chip->tsl2x7x_settings.als_thresh_high; > > + break; > > + case IIO_EV_DIR_FALLING: > > + *val = chip->tsl2x7x_settings.als_thresh_low; > > + break; > > + default: > > + return -EINVAL; > > + } > > + } else { > > + switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { > > + case IIO_EV_DIR_RISING: > > + *val = chip->tsl2x7x_settings.prox_thres_high; > > + break; > > + case IIO_EV_DIR_FALLING: > > + *val = chip->tsl2x7x_settings.prox_thres_low; > > + break; > > + default: > > + return -EINVAL; > > + } > > + } > > + > > + return 0; > > +} > > + > > +static int tsl2x7x_read_raw(struct iio_dev *indio_dev, > > + struct iio_chan_spec const *chan, > > + int *val, > > + int *val2, > > + long mask) > > +{ > > + int ret = -EINVAL; > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + > > + switch (mask) { > > + case 0: > > + switch (chan->type) { > > + case IIO_LIGHT: > > + tsl2x7x_get_lux(indio_dev); > > + *val = chip->als_cur_info.lux; > > + ret = IIO_VAL_INT; > > + break; > > + case IIO_INTENSITY: > > + tsl2x7x_get_lux(indio_dev); > > + if (chan->channel == 0) > > + *val = chip->als_cur_info.als_ch0; > > + else > > + *val = chip->als_cur_info.als_ch1; > > + ret = IIO_VAL_INT; > > + break; > > + case IIO_PROXIMITY: > > + tsl2x7x_prox_poll(indio_dev); > > + *val = chip->prox_data; > > + ret = IIO_VAL_INT; > > + break; > > + default: > > + return -EINVAL; > > + break; > > + } > > + break; > > + case IIO_CHAN_INFO_CALIBSCALE: > > + if (chan->type == IIO_LIGHT) > > + *val = > > + tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]; > > + else > > + *val = > > + tsl2X7X_prx_gainadj[chip->tsl2x7x_settings.prox_gain]; > > + ret = IIO_VAL_INT; > > + break; > > + case IIO_CHAN_INFO_CALIBBIAS: > > + *val = chip->tsl2x7x_settings.als_gain_trim; > > + ret = IIO_VAL_INT; > > + break; > > + > > + default: > > + ret = -EINVAL; > > + } > > + > > + return ret; > > +} > > + > > +static int tsl2x7x_write_raw(struct iio_dev *indio_dev, > > + struct iio_chan_spec const *chan, > > + int val, > > + int val2, > > + long mask) > > +{ > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + > > + switch (mask) { > > + case IIO_CHAN_INFO_CALIBSCALE: > > + if (chan->type == IIO_INTENSITY) { > > + switch (val) { > > + case 1: > > + chip->tsl2x7x_settings.als_gain = 0; > > + break; > > + case 8: > > + chip->tsl2x7x_settings.als_gain = 1; > > + break; > > + case 16: > > + chip->tsl2x7x_settings.als_gain = 2; > > + break; > > + case 120: > again, would prefer a switch on the chip->id's in question to relying on > ordering in the array of chip ids. Yes it's more code, but less fragile... > > + if (chip->id> tsl2771) > > + return -EINVAL; > > + chip->tsl2x7x_settings.als_gain = 3; > > + break; > > + case 128: > > + if (chip->id< tsl2572) > > + return -EINVAL; > > + chip->tsl2x7x_settings.als_gain = 3; > > + break; > > + default: > > + return -EINVAL; > > + } > > + } else { > > + switch (val) { > > + case 1: > > + chip->tsl2x7x_settings.prox_gain = 0; > > + break; > > + case 2: > > + chip->tsl2x7x_settings.prox_gain = 1; > > + break; > > + case 4: > > + chip->tsl2x7x_settings.prox_gain = 2; > > + break; > > + case 8: > > + chip->tsl2x7x_settings.prox_gain = 3; > > + break; > > + default: > > + return -EINVAL; > > + } > > + } > > + break; > > + case IIO_CHAN_INFO_CALIBBIAS: > > + chip->tsl2x7x_settings.als_gain_trim = val; > > + break; > > + > > + default: > > + return -EINVAL; > > + } > > + > > + return 0; > > +} > > + > > +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, > > + tsl2x7x_power_state_show, tsl2x7x_power_state_store); > > + > > +static DEVICE_ATTR(proximity_calibscale_available, S_IRUGO, > > + tsl2x7x_prox_gain_available_show, NULL); > > + > > +static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO, > > + tsl2x7x_gain_available_show, NULL); > > + > > +static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR, > > + tsl2x7x_als_time_show, tsl2x7x_als_time_store); > > + > > +static DEVICE_ATTR(illuminance0_target_input, S_IRUGO | S_IWUSR, > > + tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store); > > + > > +static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL, > > + tsl2x7x_do_calibrate); > > + > > +static DEVICE_ATTR(proximity_calibrate, S_IWUSR, NULL, > > + tsl2x7x_do_prox_calibrate); > > + > > +static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR, > > + tsl2x7x_luxtable_show, tsl2x7x_luxtable_store); > > + > > +static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, > > + tsl2x7x_persistence_show, tsl2x7x_persistence_store); > > + > > +/* Use the default register values to identify the Taos device */ > > +static int tsl2x7x_device_id(unsigned char *id, int target) > > +{ > > + switch (target) { > > + case tsl2571: > > + case tsl2671: > > + case tsl2771: > > + return ((*id& 0xf0) == TRITON_ID); > > + break; > > + case tmd2671: > > + case tmd2771: > > + return ((*id& 0xf0) == HALIBUT_ID); > > + break; > > + case tsl2572: > > + case tsl2672: > > + case tmd2672: > > + case tsl2772: > > + case tmd2772: > > + return ((*id& 0xf0) == SWORDFISH_ID); > > + break; > > + } > > + > > + return -EINVAL; > > +} > > + > > +/* > > + * Interrupt Event Handler */ > > +static irqreturn_t tsl2x7x_event_handler(int irq, void *private) > > +{ > > + struct iio_dev *indio_dev = private; > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + s64 timestamp = iio_get_time_ns(); > > + int ret; > > + int value; > > + > > + value = i2c_smbus_read_byte_data(chip->client, > > + TSL2X7X_CMD_REG | TSL2X7X_STATUS); > > + > > + /* What type of interrupt do we need to process */ > > + if (value& TSL2X7X_STA_PRX_INTR) { > > + tsl2x7x_prox_poll(indio_dev); > missed this, put what does the prox_poll do here? Event seems to be > cleared below... A comment to clarify why a reading is take would clear > this up. We want fresh data. Data user sees is in the struct. 1st time, struct could be initialized to zero -possible. > > + iio_push_event(indio_dev, > > + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, > > + 0, > > + IIO_EV_TYPE_THRESH, > > + IIO_EV_DIR_EITHER), > > + timestamp); > > + } > > + > > + if (value& TSL2X7X_STA_ALS_INTR) { > > + tsl2x7x_get_lux(indio_dev); > > + iio_push_event(indio_dev, > > + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, > > + 0, > > + IIO_EV_TYPE_THRESH, > > + IIO_EV_DIR_EITHER), > > + timestamp); > > + } > > + /* Clear interrupt now that we have handled it. */ > > + ret = i2c_smbus_write_byte(chip->client, > > + TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN | > > + TSL2X7X_CMD_PROXALS_INT_CLR); > > + if (ret< 0) > > + dev_err(&chip->client->dev, > > + "%s: Failed to clear irq from event handler. err = %d\n", > > + __func__, ret); > > + > > + return IRQ_HANDLED; > > +} > > + > > +static struct attribute *tsl2x7x_ALS_device_attrs[] = { > > + &dev_attr_power_state.attr, > > + &dev_attr_illuminance0_calibscale_available.attr, > > + &dev_attr_illuminance0_integration_time.attr, > > + &iio_const_attr_illuminance0_integration_time_available.dev_attr.attr, > > + &dev_attr_illuminance0_target_input.attr, > > + &dev_attr_illuminance0_calibrate.attr, > > + &dev_attr_illuminance0_lux_table.attr, > > + &dev_attr_sampling_frequency.attr, > > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > > + NULL > > +}; > > + > > +static struct attribute *tsl2x7x_PRX_device_attrs[] = { > > + &dev_attr_power_state.attr, > > + &dev_attr_sampling_frequency.attr, > > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > > + &dev_attr_proximity_calibrate.attr, > > + NULL > > +}; > > + > > +static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = { > > + &dev_attr_power_state.attr, > > + &dev_attr_illuminance0_calibscale_available.attr, > > + &dev_attr_illuminance0_integration_time.attr, > > + &iio_const_attr_illuminance0_integration_time_available.dev_attr.attr, > > + &dev_attr_illuminance0_target_input.attr, > > + &dev_attr_illuminance0_calibrate.attr, > > + &dev_attr_illuminance0_lux_table.attr, > > + &dev_attr_sampling_frequency.attr, > > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > > + &dev_attr_proximity_calibrate.attr, > > + NULL > > +}; > > + > > +static struct attribute *tsl2x7x_PRX2_device_attrs[] = { > > + &dev_attr_power_state.attr, > > + &dev_attr_sampling_frequency.attr, > > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > > + &dev_attr_proximity_calibrate.attr, > > + &dev_attr_proximity_calibscale_available.attr, > > + NULL > > +}; > > + > > +static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] = { > > + &dev_attr_power_state.attr, > > + &dev_attr_illuminance0_calibscale_available.attr, > > + &dev_attr_illuminance0_integration_time.attr, > > + &iio_const_attr_illuminance0_integration_time_available.dev_attr.attr, > > + &dev_attr_illuminance0_target_input.attr, > > + &dev_attr_illuminance0_calibrate.attr, > > + &dev_attr_illuminance0_lux_table.attr, > > + &dev_attr_sampling_frequency.attr, > > + &iio_const_attr_sampling_frequency_available.dev_attr.attr, > > + &dev_attr_proximity_calibrate.attr, > > + &dev_attr_proximity_calibscale_available.attr, > > + NULL > > +}; > > + > > +static const struct attribute_group tsl2X7X_device_attr_group_tbl[] = { > > + [ALS] = { > > + .attrs = tsl2x7x_ALS_device_attrs, > > + }, > > + [PRX] = { > > + .attrs = tsl2x7x_PRX_device_attrs, > > + }, > > + [ALSPRX] = { > > + .attrs = tsl2x7x_ALSPRX_device_attrs, > > + }, > > + [PRX2] = { > > + .attrs = tsl2x7x_PRX2_device_attrs, > > + }, > > + [ALSPRX2] = { > > + .attrs = tsl2x7x_ALSPRX2_device_attrs, > > + }, > > +}; > > + > > +static const struct iio_info tsl2X7X_device_info[] = { > > + [ALS] = { > > + .attrs =&tsl2X7X_device_attr_group_tbl[ALS], > > + .driver_module = THIS_MODULE, > > + .read_raw =&tsl2x7x_read_raw, > > + .write_raw =&tsl2x7x_write_raw, > > + .read_event_value =&tsl2x7x_read_thresh, > > + .write_event_value =&tsl2x7x_write_thresh, > > + .read_event_config =&tsl2x7x_read_interrupt_config, > > + .write_event_config =&tsl2x7x_write_interrupt_config, > > + }, > > + [PRX] = { > > + .attrs =&tsl2X7X_device_attr_group_tbl[PRX], > > + .driver_module = THIS_MODULE, > > + .read_raw =&tsl2x7x_read_raw, > > + .write_raw =&tsl2x7x_write_raw, > > + .read_event_value =&tsl2x7x_read_thresh, > > + .write_event_value =&tsl2x7x_write_thresh, > > + .read_event_config =&tsl2x7x_read_interrupt_config, > > + .write_event_config =&tsl2x7x_write_interrupt_config, > > + }, > > + [ALSPRX] = { > > + .attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX], > > + .driver_module = THIS_MODULE, > > + .read_raw =&tsl2x7x_read_raw, > > + .write_raw =&tsl2x7x_write_raw, > > + .read_event_value =&tsl2x7x_read_thresh, > > + .write_event_value =&tsl2x7x_write_thresh, > > + .read_event_config =&tsl2x7x_read_interrupt_config, > > + .write_event_config =&tsl2x7x_write_interrupt_config, > > + }, > > + [PRX2] = { > > + .attrs =&tsl2X7X_device_attr_group_tbl[PRX2], > > + .driver_module = THIS_MODULE, > > + .read_raw =&tsl2x7x_read_raw, > > + .write_raw =&tsl2x7x_write_raw, > > + .read_event_value =&tsl2x7x_read_thresh, > > + .write_event_value =&tsl2x7x_write_thresh, > > + .read_event_config =&tsl2x7x_read_interrupt_config, > > + .write_event_config =&tsl2x7x_write_interrupt_config, > > + }, > > + [ALSPRX2] = { > > + .attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX2], > > + .driver_module = THIS_MODULE, > > + .read_raw =&tsl2x7x_read_raw, > > + .write_raw =&tsl2x7x_write_raw, > > + .read_event_value =&tsl2x7x_read_thresh, > > + .write_event_value =&tsl2x7x_write_thresh, > > + .read_event_config =&tsl2x7x_read_interrupt_config, > > + .write_event_config =&tsl2x7x_write_interrupt_config, > > + }, > > +}; > > + > > +static const struct tsl2x7x_chip_info tsl2x7x_chip_info_tbl[] = { > > + [ALS] = { > > + .channel = { > > + { > > + .type = IIO_LIGHT, > > + .indexed = 1, > > + .channel = 0, > > + .processed_val = 1, > > + }, { > > + .type = IIO_INTENSITY, > > + .indexed = 1, > > + .channel = 0, > > + .info_mask = > IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | > > + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, > > + .event_mask = TSL2X7X_EVENT_MASK > > + }, { > > + .type = IIO_INTENSITY, > > + .indexed = 1, > > + .channel = 1, > > + }, > > + }, > > + .chan_table_elements = 3, > > + .info =&tsl2X7X_device_info[ALS], > > + }, > > + [PRX] = { > > + .channel = { > > + { > > + .type = IIO_PROXIMITY, > > + .indexed = 1, > > + .channel = 0, > > + .event_mask = TSL2X7X_EVENT_MASK > > + }, > > + }, > > + .chan_table_elements = 1, > > + .info =&tsl2X7X_device_info[PRX], > > + }, > > + [ALSPRX] = { > > + .channel = { > > + { > > + .type = IIO_LIGHT, > > + .indexed = 1, > > + .channel = 0, > > + .processed_val = 1, > > + }, { > > + .type = IIO_INTENSITY, > > + .indexed = 1, > > + .channel = 0, > > + .info_mask = > IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | > > + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, > > + .event_mask = TSL2X7X_EVENT_MASK > > + }, { > > + .type = IIO_INTENSITY, > > + .indexed = 1, > > + .channel = 1, > > + }, { > > + .type = IIO_PROXIMITY, > > + .indexed = 1, > > + .channel = 0, > > + .event_mask = TSL2X7X_EVENT_MASK > > + }, > > + }, > > + .chan_table_elements = 4, > > + .info =&tsl2X7X_device_info[ALSPRX], > > + }, > > + [PRX2] = { > > + .channel = { > > + { > > + .type = IIO_PROXIMITY, > > + .indexed = 1, > > + .channel = 0, > > + .info_mask = > > + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, > > + .event_mask = TSL2X7X_EVENT_MASK > > + }, > > + }, > > + .chan_table_elements = 1, > > + .info =&tsl2X7X_device_info[PRX2], > > + }, > > + [ALSPRX2] = { > > + .channel = { > > + { > > + .type = IIO_LIGHT, > > + .indexed = 1, > > + .channel = 0, > > + .processed_val = 1, > > + }, { > > + .type = IIO_INTENSITY, > > + .indexed = 1, > > + .channel = 0, > > + .info_mask = > IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | > > + IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, > > + .event_mask = TSL2X7X_EVENT_MASK > > + }, { > > + .type = IIO_INTENSITY, > > + .indexed = 1, > > + .channel = 1, > > + }, { > > + .type = IIO_PROXIMITY, > > + .indexed = 1, > > + .channel = 0, > > + .info_mask = > > + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, > > + .event_mask = TSL2X7X_EVENT_MASK > > + }, > > + }, > I think you still have space for 9 channels in the structure.... You are correct That's what I get for borrowing code. > > + .chan_table_elements = 4, > > + .info =&tsl2X7X_device_info[ALSPRX2], > > + }, > > +}; > > + > Kind of obvious comment and not in kernel-doc... > > +/* > > + * Client probe function. > > + */ > > +static int __devinit tsl2x7x_probe(struct i2c_client *clientp, > > + const struct i2c_device_id *id) > > +{ > > + int ret; > > + unsigned char device_id; > > + struct iio_dev *indio_dev; > > + struct tsl2X7X_chip *chip; > > + > > + indio_dev = iio_allocate_device(sizeof(*chip)); > > + if (!indio_dev) > > + return -ENOMEM; > > + > > + chip = iio_priv(indio_dev); > > + chip->client = clientp; > > + i2c_set_clientdata(clientp, indio_dev); > > + > > + ret = tsl2x7x_i2c_read(chip->client, > > + TSL2X7X_CHIPID,&device_id); > > + if (ret< 0) > > + goto fail1; > > + > > + if ((!tsl2x7x_device_id(&device_id, id->driver_data)) || > > + (tsl2x7x_device_id(&device_id, id->driver_data) == -EINVAL)) { > > + dev_info(&chip->client->dev, > > + "i2c device found does not match expected id > in %s\n", > > + __func__); > Would be good to standardise error formatting across the driver. > Sometimes you have the function > name first, sometimes last. Pick one and go with it. > > + goto fail1; > > + } > > + > > + ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG | > TSL2X7X_CNTRL)); > > + if (ret< 0) { > > + dev_err(&clientp->dev, "%s: write to cmd reg failed. err = > %d\n", > > + __func__, ret); > > + goto fail1; > > + } > > + > > + /* ALS and PROX functions can be invoked via user space poll > > + * or H/W interrupt. If busy return last sample. */ > > + mutex_init(&chip->als_mutex); > > + mutex_init(&chip->prox_mutex); > > + > > + chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN; > > + chip->pdata = clientp->dev.platform_data; > > + chip->id = id->driver_data; > > + chip->chip_info = > > + &tsl2x7x_chip_info_tbl[device_channel_config[id- > >driver_data]]; > > + > > + indio_dev->info = chip->chip_info->info; > > + indio_dev->dev.parent =&clientp->dev; > > + indio_dev->modes = INDIO_DIRECT_MODE; > > + indio_dev->name = chip->client->name; > > + indio_dev->channels = chip->chip_info->channel; > > + indio_dev->num_channels = chip->chip_info->chan_table_elements; > > + > > + if (clientp->irq) { > > + ret = request_threaded_irq(clientp->irq, > > + NULL, > > + &tsl2x7x_event_handler, > > + IRQF_TRIGGER_RISING | > IRQF_ONESHOT, > > + "TSL2X7X_event", > > + indio_dev); > > + if (ret) { > > + dev_err(&clientp->dev, > > + "%s: irq request failed", __func__); > > + goto fail2; > > + } > > + } > > + > > + /* Load up the defaults */ > > + tsl2x7x_defaults(chip); > > + /* Make sure the chip is on */ > > + tsl2x7x_chip_on(indio_dev); > > + > > + ret = iio_device_register(indio_dev); > > + if (ret) { > > + dev_err(&clientp->dev, > > + "%s: iio registration failed\n", __func__); > > + goto fail1; > > + } > > + > > + dev_info(&clientp->dev, "%s Light sensor found.\n", id->name); > > + return 0; > > + > > +fail1: > > + if (clientp->irq) > > + free_irq(clientp->irq, indio_dev); > > +fail2: > > + iio_free_device(indio_dev); > convention puts a blank line before the return. Same above. > > + return ret; > > +} > > + > > +static int tsl2x7x_suspend(struct device *dev) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + int ret = 0; > > + > > + if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) { > > + ret = tsl2x7x_chip_off(indio_dev); > > + chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED; > > + } > > + > > + if (chip->pdata&& chip->pdata->platform_power) { > > + pm_message_t pmm = {PM_EVENT_SUSPEND}; > > + chip->pdata->platform_power(dev, pmm); > > + } > > + > > + return ret; > > +} > > + > > +static int tsl2x7x_resume(struct device *dev) > > +{ > > + struct iio_dev *indio_dev = dev_get_drvdata(dev); > > + struct tsl2X7X_chip *chip = iio_priv(indio_dev); > > + int ret = 0; > > + > > + if (chip->pdata&& chip->pdata->platform_power) { > > + pm_message_t pmm = {PM_EVENT_RESUME}; > > + chip->pdata->platform_power(dev, pmm); > > + } > > + > > + if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_SUSPENDED) > > + ret = tsl2x7x_chip_on(indio_dev); > > + > > + return ret; > > +} > > + > > +static int __devexit tsl2x7x_remove(struct i2c_client *client) > > +{ > > + struct tsl2X7X_chip *chip = i2c_get_clientdata(client); > > + struct iio_dev *indio_dev = iio_priv_to_dev(chip); > > + > > + tsl2x7x_chip_off(indio_dev); > > + > > + iio_device_unregister(indio_dev); > > + if (client->irq) > > + free_irq(client->irq, chip->client->name); > > + > > + iio_free_device(indio_dev); > > + > > + return 0; > > +} > > + > > +static struct i2c_device_id tsl2x7x_idtable[] = { > > + { "tsl2571", tsl2571 }, > > + { "tsl2671", tsl2671 }, > > + { "tmd2671", tmd2671 }, > > + { "tsl2771", tsl2771 }, > > + { "tmd2771", tmd2771 }, > > + { "tsl2572", tsl2572 }, > > + { "tsl2672", tsl2672 }, > > + { "tmd2672", tmd2672 }, > > + { "tsl2772", tsl2772 }, > > + { "tmd2772", tmd2772 }, > > + {} > > +}; > > + > > +MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable); > > + > > +static const struct dev_pm_ops tsl2x7x_pm_ops = { > > + .suspend = tsl2x7x_suspend, > > + .resume = tsl2x7x_resume, > > +}; > > + > > +/* Driver definition */ > > +static struct i2c_driver tsl2x7x_driver = { > > + .driver = { > > + .name = "tsl2x7x", > > + .pm =&tsl2x7x_pm_ops, > > + }, > > + .id_table = tsl2x7x_idtable, > > + .probe = tsl2x7x_probe, > > + .remove = __devexit_p(tsl2x7x_remove), > > +}; > > + > > +module_i2c_driver(tsl2x7x_driver); > > + > > +MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>"); > > +MODULE_DESCRIPTION("TAOS tsl2x7x ambient and proximity light sensor > driver"); > > +MODULE_LICENSE("GPL"); > > -- > > 1.7.4.1 > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-iio" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html ÿôèº{.nÇ+·®+%Ëÿ±éݶ\x17¥wÿº{.nÇ+·¥{±þG«éÿ{ayº\x1dÊÚë,j\a¢f£¢·hïêÿêçz_è®\x03(éÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?¨èÚ&£ø§~á¶iOæ¬z·vØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?I¥ ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH V5] TAOS tsl2x7x 2012-04-02 16:50 [PATCH V5] TAOS tsl2x7x Jon Brenner 2012-04-04 8:35 ` Jonathan Cameron @ 2012-04-04 16:16 ` Peter Meerwald 2012-04-10 17:36 ` Greg KH 2 siblings, 0 replies; 9+ messages in thread From: Peter Meerwald @ 2012-04-04 16:16 UTC (permalink / raw) To: Jon Brenner; +Cc: Jonathan Cameron, linux-iio, Linux Kernel just some nitpicking on comments below prox/PRX and als/ALS/lux are used interchangingly > +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x > + Causes an recalculation and adjustment to the > + proximity_thresh_rising_value. a recalculation > diff --git a/drivers/staging/iio/light/tsl2x7x.h b/drivers/staging/iio/light/tsl2x7x.h > + * struct tsl2x7x_default_settings - power on defaults unless > + * overridden by platform data. > + * @als_time: ALS Integration time - multiple of 50mS ms > + * @als_gain: Index into the ALS gain table. > + * @prx_time: 5.2ms prox integration time - > + * dec in 2.7ms periods what does 'dec' mean? > + * @wait_time: Time between PRX and ALS cycles > + * in 2.7 periods PRX -> prox? should ALS be lux? used inconsistently throughout > + * @als_gain_trim: default gain trim to account for > + * aperture effects. Default > + * @als_thresh_high: CH0 'high' count to trigger interrupt. > + * @persistence: H/W Filters, Number of 'out of limits' > + * ADC readings PRX/ALS. prox? > + * @interrupts_en: Enable/Disable - 0x00 = none, 0x10 = als, > + * 0x20 = prx, 0x30 = bth bth -> both? > + * @prox_max_samples_cal: Used for prox cal. cal -> calibration or calculation? > +++ b/drivers/staging/iio/light/tsl2x7x_core.c > +/* Cal defs*/ Calibration? > +static const struct tsl2x7x_settings tsl2x7x_default_settings = { > + .als_time = 200, > + .als_gain = 0, > + .prx_time = 0xfe, /*5.4 mS */ ms > + /* determine als integration regster */ register > + reg_val |= chip->tsl2x7x_settings.interrupts_en; > + ret = i2c_smbus_write_byte_data(chip->client, > + (TSL2X7X_CMD_REG | TSL2X7X_CNTRL), reg_val); > + if (ret < 0) > + dev_err(&chip->client->dev, > + "%s: failed in tsl2x7x_IOCTL_INT_SET.\n", > + __func__); why is this called tsl2x7x_IOCTL_INT_SET? misleading? > +/** > + * Proximity calibration - collects a number of samples, > + * calculates a standard deviation based on the samples, and > + * sets the threshold accordingly. > + */ a standard deviation -> the standard deviation > + /*turn on device if not already on*/ > + tsl2x7x_chip_on(indio_dev); /* Turn on ... */ > + /*gather the samples*/ /* Gather the samples */ > + if ((n % 3) || n < 6 || > + n > ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) { > + dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n); > + return -EINVAL; why uppercase? > + if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) { > + dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n); > + return -EINVAL; why uppercase? > +static int tsl2x7x_device_id(unsigned char *id, int target) > +{ > + switch (target) { > + case tsl2571: > + case tsl2671: > + case tsl2771: > + return ((*id & 0xf0) == TRITON_ID); > + break; break look weird here > + case tmd2671: > + case tmd2771: > + return ((*id & 0xf0) == HALIBUT_ID); > + break; break look weird here > + case tsl2572: > + case tsl2672: > + case tmd2672: > + case tsl2772: > + case tmd2772: > + return ((*id & 0xf0) == SWORDFISH_ID); > + break; break look weird here -- Peter Meerwald +43-664-2444418 (mobile) ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH V5] TAOS tsl2x7x 2012-04-02 16:50 [PATCH V5] TAOS tsl2x7x Jon Brenner 2012-04-04 8:35 ` Jonathan Cameron 2012-04-04 16:16 ` Peter Meerwald @ 2012-04-10 17:36 ` Greg KH 2012-04-10 19:03 ` Jonathan Cameron 2 siblings, 1 reply; 9+ messages in thread From: Greg KH @ 2012-04-10 17:36 UTC (permalink / raw) To: Jon Brenner; +Cc: Jonathan Cameron, linux-iio, Linux Kernel On Mon, Apr 02, 2012 at 11:50:39AM -0500, Jon Brenner wrote: > TAOS device driver (version 5) for the tsl/tmd 2771 and 2772 device families (inc. all variants). > > Signed-off-by: Jon Brenner <jbrenner@taosinc.com> > --- > .../light/sysfs-bus-iio-light-tsl2583 | 6 + > .../light/sysfs-bus-iio-light-tsl2x7x | 14 + > drivers/staging/iio/Documentation/sysfs-bus-iio | 7 + > .../staging/iio/Documentation/sysfs-bus-iio-light | 8 +- > .../iio/Documentation/sysfs-bus-iio-light-tsl2583 | 20 - > drivers/staging/iio/light/Kconfig | 8 + > drivers/staging/iio/light/Makefile | 2 + > drivers/staging/iio/light/tsl2x7x.h | 99 ++ > drivers/staging/iio/light/tsl2x7x_core.c | 1830 ++++++++++++++++++++ > 9 files changed, 1970 insertions(+), 24 deletions(-) > > diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 > new file mode 100644 > index 0000000..8f2a038 > --- /dev/null > +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 > @@ -0,0 +1,6 @@ > +What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate > +KernelVersion: 2.6.37 Really? Are we traveling back in time now? confused... I need an ack from Jonathan before I can take this. thanks, greg k-h ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH V5] TAOS tsl2x7x 2012-04-10 17:36 ` Greg KH @ 2012-04-10 19:03 ` Jonathan Cameron 2012-04-10 19:09 ` Greg KH 0 siblings, 1 reply; 9+ messages in thread From: Jonathan Cameron @ 2012-04-10 19:03 UTC (permalink / raw) To: Greg KH; +Cc: Jon Brenner, linux-iio, Linux Kernel On 04/10/2012 06:36 PM, Greg KH wrote: > On Mon, Apr 02, 2012 at 11:50:39AM -0500, Jon Brenner wrote: >> TAOS device driver (version 5) for the tsl/tmd 2771 and 2772 device families (inc. all variants). >> >> Signed-off-by: Jon Brenner <jbrenner@taosinc.com> >> --- >> .../light/sysfs-bus-iio-light-tsl2583 | 6 + >> .../light/sysfs-bus-iio-light-tsl2x7x | 14 + >> drivers/staging/iio/Documentation/sysfs-bus-iio | 7 + >> .../staging/iio/Documentation/sysfs-bus-iio-light | 8 +- >> .../iio/Documentation/sysfs-bus-iio-light-tsl2583 | 20 - >> drivers/staging/iio/light/Kconfig | 8 + >> drivers/staging/iio/light/Makefile | 2 + >> drivers/staging/iio/light/tsl2x7x.h | 99 ++ >> drivers/staging/iio/light/tsl2x7x_core.c | 1830 ++++++++++++++++++++ >> 9 files changed, 1970 insertions(+), 24 deletions(-) >> >> diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 >> new file mode 100644 >> index 0000000..8f2a038 >> --- /dev/null >> +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 >> @@ -0,0 +1,6 @@ >> +What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate >> +KernelVersion: 2.6.37 > > Really? Are we traveling back in time now? > > confused... > > I need an ack from Jonathan before I can take this. That is a little curious given the entry is already in that file as far as I can see (and was indeed introduced back in 2.6.37 given these are docs for a different driver). Something weird gone wrong moving that to the other file Jon? More importantly Jon is still working on this driver (we were exchanging emails about it a couple of days ago and I think I owe Jon a reply to the last one (oops) It was nearly there so I'd imagine you'll get a v6 or 7 in a few days... > > thanks, > > greg k-h ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH V5] TAOS tsl2x7x 2012-04-10 19:03 ` Jonathan Cameron @ 2012-04-10 19:09 ` Greg KH 0 siblings, 0 replies; 9+ messages in thread From: Greg KH @ 2012-04-10 19:09 UTC (permalink / raw) To: Jonathan Cameron; +Cc: Jon Brenner, linux-iio, Linux Kernel On Tue, Apr 10, 2012 at 08:03:39PM +0100, Jonathan Cameron wrote: > On 04/10/2012 06:36 PM, Greg KH wrote: > > On Mon, Apr 02, 2012 at 11:50:39AM -0500, Jon Brenner wrote: > >> TAOS device driver (version 5) for the tsl/tmd 2771 and 2772 device families (inc. all variants). > >> > >> Signed-off-by: Jon Brenner <jbrenner@taosinc.com> > >> --- > >> .../light/sysfs-bus-iio-light-tsl2583 | 6 + > >> .../light/sysfs-bus-iio-light-tsl2x7x | 14 + > >> drivers/staging/iio/Documentation/sysfs-bus-iio | 7 + > >> .../staging/iio/Documentation/sysfs-bus-iio-light | 8 +- > >> .../iio/Documentation/sysfs-bus-iio-light-tsl2583 | 20 - > >> drivers/staging/iio/light/Kconfig | 8 + > >> drivers/staging/iio/light/Makefile | 2 + > >> drivers/staging/iio/light/tsl2x7x.h | 99 ++ > >> drivers/staging/iio/light/tsl2x7x_core.c | 1830 ++++++++++++++++++++ > >> 9 files changed, 1970 insertions(+), 24 deletions(-) > >> > >> diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 > >> new file mode 100644 > >> index 0000000..8f2a038 > >> --- /dev/null > >> +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 > >> @@ -0,0 +1,6 @@ > >> +What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate > >> +KernelVersion: 2.6.37 > > > > Really? Are we traveling back in time now? > > > > confused... > > > > I need an ack from Jonathan before I can take this. > That is a little curious given the entry is already in that file as far > as I can see (and was indeed introduced back in 2.6.37 given these are > docs for a different driver). Something weird gone wrong moving that > to the other file Jon? > > More importantly Jon is still working on this driver (we were exchanging > emails about it a couple of days ago and I think I owe > Jon a reply to the last one (oops) It was nearly there so I'd imagine > you'll get a v6 or 7 in a few days... Ok, thanks for clearing it up. greg k-h ^ permalink raw reply [flat|nested] 9+ messages in thread
[parent not found: <c0e6301f-ce07-4659-9239-8dbd96dc5b59@email.android.com>]
* RE: [PATCH V5] TAOS tsl2x7x [not found] <c0e6301f-ce07-4659-9239-8dbd96dc5b59@email.android.com> @ 2012-04-04 16:23 ` Jonathan Cameron 0 siblings, 0 replies; 9+ messages in thread From: Jonathan Cameron @ 2012-04-04 16:23 UTC (permalink / raw) To: Jon Brenner; +Cc: linux-iio, Linux Kernel Jon Brenner <jbrenner@TAOSinc.com> wrote: >Hi Jonathan, >Thanks for the review . > >Please see various responses - in line. > >Next patch will be V6 and the last - I hope! >Jon > >> -----Original Message----- >> From: Jonathan Cameron [mailto:jic23@cam.ac.uk] >> Sent: Wednesday, April 04, 2012 3:35 AM >> To: Jon Brenner >> Cc: linux-iio; Linux Kernel >> Subject: Re: [PATCH V5] TAOS tsl2x7x >> >> On 4/2/2012 5:50 PM, Jon Brenner wrote: >> > TAOS device driver (version 5) for the tsl/tmd 2771 and 2772 device >families >> (inc. all variants). >> Hi Jon, >> >> Changes since last version? >Correct. >> >> A few bits still to sort out in here I'm afraid... (getting there >though!) >> My reviews tend to get more picky as the big stuff gets sorted out. >> >> On trivial extra blank line to clear out. > >> Extra line for your next driver has snuck into the make file. >Yikes! > >> Units don't look right for sampling frequency. Sorry, but that's an >abi >> issue so even if it is >> fiddly to do the conversion to Hz it needs to be done. >> Would normally expect changes to events to get applied immediately. >Here >> I think that only >> happens if you turn the device off and on again? >This is per customer request - allows complete reconfiguration without >many device on/offs. > To match other devices turn off then on again iff previously on. Works for me and your customer! >> >> For future reference (don't bother here!) make any documentation >moves >> not directly dependent on the >> driver (such as those that will become used by multiple drivers) in a >> precursor patch. >OK > >> > >> > Signed-off-by: Jon Brenner<jbrenner@taosinc.com> >> > --- >> > .../light/sysfs-bus-iio-light-tsl2583 | 6 + >> > .../light/sysfs-bus-iio-light-tsl2x7x | 14 + >> > drivers/staging/iio/Documentation/sysfs-bus-iio | 7 + >> > .../staging/iio/Documentation/sysfs-bus-iio-light | 8 +- >> > .../iio/Documentation/sysfs-bus-iio-light-tsl2583 | 20 - >> > drivers/staging/iio/light/Kconfig | 8 + >> > drivers/staging/iio/light/Makefile | 2 + >> > drivers/staging/iio/light/tsl2x7x.h | 99 ++ >> > drivers/staging/iio/light/tsl2x7x_core.c | 1830 >++++++++++++++++++++ >> > 9 files changed, 1970 insertions(+), 24 deletions(-) >> > >> > diff --git >a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 >> b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 >> > new file mode 100644 >> > index 0000000..8f2a038 >> > --- /dev/null >> > +++ >b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 >> > @@ -0,0 +1,6 @@ >> > +What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate >> > +KernelVersion: 2.6.37 >> > +Contact: linux-iio@vger.kernel.org >> > +Description: >> > + This property causes an internal calibration of the als gain >trim >> > + value which is later used in calculating illuminance in lux. >> > diff --git >a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x >> b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x >> > new file mode 100644 >> > index 0000000..275ae54 >> > --- /dev/null >> > +++ >b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x >> > @@ -0,0 +1,14 @@ >> > +What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate >> > +KernelVersion: 2.6.37 >> > +Contact: linux-iio@vger.kernel.org >> > +Description: >> > + This property causes an internal calibration of the als gain >trim >> > + value which is later used in calculating illuminance in lux. >> Hmm.. could possibly move this into sysfs-bus-iio-light at some point >> given we clearly have two drivers >> using it. (fine for now though) >> > + >> > +What: /sys/bus/iio/devices/device[n]/proximity_calibrate >> > +KernelVersion: 3.3-rc1 >> > +Contact: linux-iio@vger.kernel.org >> > +Description: >> > + Causes an recalculation and adjustment to the >> > + proximity_thresh_rising_value. >> This one is interesting as there are other proximity sensors out >there >> (not light based) so we >> may want to move this at some later point to a >sysfs-bus-iio-proximity >> documentation file. >> > + >> > diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio >> b/drivers/staging/iio/Documentation/sysfs-bus-iio >> > index 46a995d..5b2b5d3 100644 >> > --- a/drivers/staging/iio/Documentation/sysfs-bus-iio >> > +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio >> > @@ -258,6 +258,8 @@ What >> /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale >> > What >> /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibscale >> > What >> /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale >> > What >> /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale >> > +what >> /sys/bus/iio/devices/iio:deviceX/illuminance0_calibscale >> > +what /sys/bus/iio/devices/iio:deviceX/proximity_calibscale >> > KernelVersion: 2.6.35 >> > Contact: linux-iio@vger.kernel.org >> > Description: >> > @@ -457,6 +459,10 @@ What: >> /sys/.../events/in_voltageY_raw_thresh_falling_value >> > What: /sys/.../events/in_voltageY_raw_thresh_falling_value >> > What: /sys/.../events/in_tempY_raw_thresh_falling_value >> > What: /sys/.../events/in_tempY_raw_thresh_falling_value >> Oops, clearly and error in the lines above (repeats of falling and no >> rising). I'll fix that up unless >> someone else gets there first. >OK - will not change in this patch. > >> > +What: /sys/.../events/illuminance0_thresh_falling_value >> > +what: /sys/.../events/illuminance0_thresh_rising_value >> > +what: /sys/.../events/proximity_thresh_falling_value >> > +what: /sys/.../events/proximity_thresh_rising_value >> > KernelVersion: 2.6.37 >> > Contact: linux-iio@vger.kernel.org >> > Description: >> > @@ -739,3 +745,4 @@ Description: >> > system. To minimize the current consumption of the system, >> > the bridge can be disconnected (when it is not being used >> > using the bridge_switch_en attribute. >> > + >> loose this extra blank line. >> > diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light >> b/drivers/staging/iio/Documentation/sysfs-bus-iio-light >> > index edbf470..4385c70 100644 >> > --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light >> > +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light >> > @@ -76,10 +76,10 @@ Contact: linux-iio@vger.kernel.org >> > Description: >> > This property gets/sets the sensors ADC analog integration >> time. >> > >> > -What: /sys/bus/iio/devices/device[n]/illuminance0_calibscale >> > +What: /sys/bus/iio/devices/device[n]/lux_table >> > KernelVersion: 2.6.37 >> > Contact: linux-iio@vger.kernel.org >> > Description: >> > - Hardware or software applied calibration scale factor assumed >> > - to account for attenuation due to industrial design (glass >> > - filters or aperture holes). >> > + This property gets/sets the table of coefficients >> > + used in calculating illuminance in lux. >> > + >> > diff --git >a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 >> b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 >> > deleted file mode 100644 >> > index 660781d..0000000 >> > --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 >> > +++ /dev/null >> > @@ -1,20 +0,0 @@ >> > -What: /sys/bus/iio/devices/device[n]/lux_table >> > -KernelVersion: 2.6.37 >> > -Contact: linux-iio@vger.kernel.org >> > -Description: >> > - This property gets/sets the table of coefficients >> > - used in calculating illuminance in lux. >> > - >> > -What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate >> > -KernelVersion: 2.6.37 >> > -Contact: linux-iio@vger.kernel.org >> > -Description: >> > - This property causes an internal calibration of the als gain >trim >> > - value which is later used in calculating illuminance in lux. >> > - >> > -What: >> /sys/bus/iio/devices/device[n]/illuminance0_input_target >> > -KernelVersion: 2.6.37 >> > -Contact: linux-iio@vger.kernel.org >> > -Description: >> > - This property is the known externally illuminance (in lux). >> > - It is used in the process of calibrating the device accuracy. >> > diff --git a/drivers/staging/iio/light/Kconfig >b/drivers/staging/iio/light/Kconfig >> > index e7e9159..976f790 100644 >> > --- a/drivers/staging/iio/light/Kconfig >> > +++ b/drivers/staging/iio/light/Kconfig >> > @@ -31,4 +31,12 @@ config TSL2583 >> > Provides support for the TAOS tsl2580, tsl2581 and tsl2583 >devices. >> > Access ALS data via iio, sysfs. >> > >> > +config TSL2x7x >> > + tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and >> proximity sensors" >> > + depends on I2C >> > + help >> > + Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, >tsl2572, >> tsl2672, >> > + tmd2672, tsl2772, tmd2772 devices. >> > + Provides iio_events and direct access via sysfs. >> > + >> > endmenu >> > diff --git a/drivers/staging/iio/light/Makefile >> b/drivers/staging/iio/light/Makefile >> > index 3011fbf..0b8fb22 100644 >> > --- a/drivers/staging/iio/light/Makefile >> > +++ b/drivers/staging/iio/light/Makefile >> > @@ -5,3 +5,5 @@ >> > obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o >> > obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o >> > obj-$(CONFIG_TSL2583) += tsl2583.o >> > +obj-$(CONFIG_TSL2x7x) += tsl2x7x_core.o >> > +obj-$(CONFIG_TCS3x7x) += tcs3x7x_core.o >> Really? >Oops > >> > diff --git a/drivers/staging/iio/light/tsl2x7x.h >> b/drivers/staging/iio/light/tsl2x7x.h >> > new file mode 100644 >> > index 0000000..fe9e853 >> > --- /dev/null >> > +++ b/drivers/staging/iio/light/tsl2x7x.h >> > @@ -0,0 +1,99 @@ >> > +/* >> > + * Device driver for monitoring ambient light intensity (lux) >> > + * and proximity (prox) within the TAOS TSL2X7X family of devices. >> > + * >> > + * Copyright (c) 2012, TAOS Corporation. >> > + * >> > + * 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 >> > + * the Free Software Foundation; either version 2 of the License, >or >> > + * (at your option) any later version. >> > + * >> > + * This program is distributed in the hope that 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, write to the Free Software >Foundation, Inc., >> > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. >> > + */ >> > + >> > +#ifndef __TSL2X7X_H >> > +#define __TSL2X7X_H >> > +#include<linux/pm.h> >> > + >> > +/* Max number of segments allowable in LUX table */ >> > +#define TSL2X7X_MAX_LUX_TABLE_SIZE 9 >> > +#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) * >> TSL2X7X_MAX_LUX_TABLE_SIZE) >> > + >> > +struct iio_dev; >> > + >> > +struct tsl2x7x_lux { >> > + unsigned int ratio; >> > + unsigned int ch0; >> > + unsigned int ch1; >> > +}; >> > + >> > +/** >> > + * struct tsl2x7x_default_settings - power on defaults unless >> > + * overridden by platform data. >> > + * @als_time: ALS Integration time - multiple of >50mS >> > + * @als_gain: Index into the ALS gain table. >> > + * @prx_time: 5.2ms prox integration time - >> > + * dec in 2.7ms periods >> > + * @wait_time: Time between PRX and ALS cycles >> > + * in 2.7 periods >> > + * @prox_config: Prox configuration filters. >> > + * @als_gain_trim: default gain trim to account for >> > + * aperture effects. >> > + * @als_cal_target: Known external ALS reading for >> > + * calibration. >> > + * @als_thresh_low: CH0 'low' count to trigger interrupt. >> > + * @als_thresh_high: CH0 'high' count to trigger interrupt. >> > + * @persistence: H/W Filters, Number of 'out of limits' >> > + * ADC readings PRX/ALS. >> > + * @interrupts_en: Enable/Disable - 0x00 = none, 0x10 = >als, >> > + * 0x20 = prx, 0x30 = >bth >> > + * @prox_thres_low: Low threshold proximity detection. >> > + * @prox_thres_high: High threshold proximity detection >> > + * @prox_max_samples_cal: Used for prox cal. >> > + * @prox_pulse_count: Number if proximity emitter pulses >> reorder the docs to match the structure. Pick which ever order makes >> most sense >> (don't worry about wasting a byte or two, clarity is more important >on >> structures >> like this!) >OK > >> > + */ >> > +struct tsl2x7x_settings { >> > + int als_time; >> > + int als_gain; >> > + int als_gain_trim; >> > + int wait_time; >> > + int prx_time; >> > + int prox_gain; >> > + int prox_config; >> > + int als_cal_target; >> > + u8 interrupts_en; >> > + u8 persistence; >> > + int als_thresh_low; >> > + int als_thresh_high; >> > + int prox_thres_low; >> > + int prox_thres_high; >> > + int prox_pulse_count; >> > + int prox_max_samples_cal; >> > +}; >> > + >> > +/** >> > + * struct tsl2X7X_platform_data - Platform callback, glass and >defaults >> > + * @platform_power: Suspend/resume >> platform callback >> > + * @power_on: Power on callback >> > + * @power_off: Power off callback >> > + * @platform_lux_table: Device specific glass >> coefficents >> > + * @platform_default_settings: Device specific power on defaults >> > + * Platform PM functions. >> > + */ >> > +struct tsl2X7X_platform_data { >> > + int (*platform_power)(struct device *dev, pm_message_t); >> > + int (*power_on) (struct iio_dev *indio_dev); >> > + int (*power_off) (struct i2c_client *dev); >> > + struct tsl2x7x_lux >platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE]; >> > + struct tsl2x7x_settings *platform_default_settings; >> > +}; >> > + >> > +#endif /* __TSL2X7X_H */ >> > diff --git a/drivers/staging/iio/light/tsl2x7x_core.c >> b/drivers/staging/iio/light/tsl2x7x_core.c >> > new file mode 100644 >> > index 0000000..267faab >> > --- /dev/null >> > +++ b/drivers/staging/iio/light/tsl2x7x_core.c >> > @@ -0,0 +1,1830 @@ >> > +/* >> > + * Device driver for monitoring ambient light intensity in (lux) >> > + * and proximity detection (prox) within the TAOS TSL2X7X family >of devices. >> > + * >> > + * Copyright (c) 2012, TAOS Corporation. >> > + * >> > + * 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 >> > + * the Free Software Foundation; either version 2 of the License, >or >> > + * (at your option) any later version. >> > + * >> > + * This program is distributed in the hope that 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, write to the Free Software >Foundation, Inc., >> > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, >USA. >> > + */ >> > + >> > +#include<linux/kernel.h> >> > +#include<linux/i2c.h> >> > +#include<linux/errno.h> >> > +#include<linux/delay.h> >> > +#include<linux/mutex.h> >> > +#include<linux/interrupt.h> >> > +#include<linux/slab.h> >> > +#include<linux/module.h> >> > +#include<linux/version.h> >> > +#include "tsl2x7x.h" >> > +#include "../events.h" >> > +#include "../iio.h" >> > +#include "../sysfs.h" >> > + >> > +/* Cal defs*/ >> > +#define PROX_STAT_CAL 0 >> > +#define PROX_STAT_SAMP 1 >> > +#define MAX_SAMPLES_CAL 200 >> > + >> > +/* TSL2X7X Device ID */ >> > +#define TRITON_ID 0x00 >> > +#define SWORDFISH_ID 0x30 >> > +#define HALIBUT_ID 0x20 >> hmmm.. fish ;) >And Chips - uum ;^) > >> > + >> > +/* Lux calculation constants */ >> > +#define TSL2X7X_LUX_CALC_OVER_FLOW 65535 >> > + >> > +/* TAOS Register definitions - note: >> > + * depending on device, some of these register are not used and >the >> > + * register address is benign. >> > + */ >> > +/* 2X7X register offsets */ >> > +#define TSL2X7X_MAX_CONFIG_REG 16 >> > + >> > +/* Device Registers and Masks */ >> > +#define TSL2X7X_CNTRL 0x00 >> > +#define TSL2X7X_ALS_TIME 0X01 >> > +#define TSL2X7X_PRX_TIME 0x02 >> > +#define TSL2X7X_WAIT_TIME 0x03 >> > +#define TSL2X7X_ALS_MINTHRESHLO 0X04 >> > +#define TSL2X7X_ALS_MINTHRESHHI 0X05 >> > +#define TSL2X7X_ALS_MAXTHRESHLO 0X06 >> > +#define TSL2X7X_ALS_MAXTHRESHHI 0X07 >> > +#define TSL2X7X_PRX_MINTHRESHLO 0X08 >> > +#define TSL2X7X_PRX_MINTHRESHHI 0X09 >> > +#define TSL2X7X_PRX_MAXTHRESHLO 0X0A >> > +#define TSL2X7X_PRX_MAXTHRESHHI 0X0B >> > +#define TSL2X7X_PERSISTENCE 0x0C >> > +#define TSL2X7X_PRX_CONFIG 0x0D >> > +#define TSL2X7X_PRX_COUNT 0x0E >> > +#define TSL2X7X_GAIN 0x0F >> > +#define TSL2X7X_NOTUSED 0x10 >> > +#define TSL2X7X_REVID 0x11 >> > +#define TSL2X7X_CHIPID 0x12 >> > +#define TSL2X7X_STATUS 0x13 >> > +#define TSL2X7X_ALS_CHAN0LO 0x14 >> > +#define TSL2X7X_ALS_CHAN0HI 0x15 >> > +#define TSL2X7X_ALS_CHAN1LO 0x16 >> > +#define TSL2X7X_ALS_CHAN1HI 0x17 >> > +#define TSL2X7X_PRX_LO 0x18 >> > +#define TSL2X7X_PRX_HI 0x19 >> > + >> > +/* tsl2X7X cmd reg masks */ >> > +#define TSL2X7X_CMD_REG 0x80 >> > +#define TSL2X7X_CMD_SPL_FN 0x60 >> > + >> > +#define TSL2X7X_CMD_PROX_INT_CLR 0X05 >> > +#define TSL2X7X_CMD_ALS_INT_CLR 0x06 >> > +#define TSL2X7X_CMD_PROXALS_INT_CLR 0X07 >> > + >> > +/* tsl2X7X cntrl reg masks */ >> > +#define TSL2X7X_CNTL_ADC_ENBL 0x02 >> > +#define TSL2X7X_CNTL_PWR_ON 0x01 >> > + >> > +/* tsl2X7X status reg masks */ >> > +#define TSL2X7X_STA_ADC_VALID 0x01 >> > +#define TSL2X7X_STA_PRX_VALID 0x02 >> > +#define TSL2X7X_STA_ADC_PRX_VALID 0x03 >> Would prefer above defined as TSL2X7X_STA_ADC_VALID | >> TSL2X7X_STA_PRX_VALID >> (makes it obvious at a glance what is going on). >> > +#define TSL2X7X_STA_ALS_INTR 0x10 >> > +#define TSL2X7X_STA_ADC_INTR 0x10 >> above unused (and seems to be repeated) ? (repeated value was >suspicious ) >> > +#define TSL2X7X_STA_PRX_INTR 0x20 >> > + >> > +#define TSL2X7X_STA_ADC_INTR 0x10 >> > + >> > +/* tsl2X7X cntrl reg masks */ >> > +#define TSL2X7X_CNTL_REG_CLEAR 0x00 >> > +#define TSL2X7X_CNTL_PROX_INT_ENBL 0X20 >> > +#define TSL2X7X_CNTL_ALS_INT_ENBL 0X10 >> > +#define TSL2X7X_CNTL_WAIT_TMR_ENBL 0X08 >> > +#define TSL2X7X_CNTL_PROX_DET_ENBL 0X04 >> > +#define TSL2X7X_CNTL_PWRON 0x01 >> > +#define TSL2X7X_CNTL_ALSPON_ENBL 0x03 >> > +#define TSL2X7X_CNTL_INTALSPON_ENBL 0x13 >> > +#define TSL2X7X_CNTL_PROXPON_ENBL 0x0F >> > +#define TSL2X7X_CNTL_INTPROXPON_ENBL 0x2F >> > + >> > +/*Prox diode to use */ >> > +#define TSL2X7X_DIODE0 0x10 >> > +#define TSL2X7X_DIODE1 0x20 >> > +#define TSL2X7X_DIODE_BOTH 0x30 >> > + >> > +/* LED Power */ >> > +#define TSL2X7X_mA100 0x00 >> > +#define TSL2X7X_mA50 0x40 >> > +#define TSL2X7X_mA25 0x80 >> > +#define TSL2X7X_mA13 0xD0 >> > + >> > +/*Common device IIO EventMask */ >> > +#define TSL2X7X_EVENT_MASK \ >> > + (IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \ >> > + IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)), >> > + >> > +/* TAOS txx2x7x Device family members */ >> > +enum { >> > + tsl2571, >> > + tsl2671, >> > + tmd2671, >> > + tsl2771, >> > + tmd2771, >> > + tsl2572, >> > + tsl2672, >> > + tmd2672, >> > + tsl2772, >> > + tmd2772 >> > +}; >> > + >> > +enum { >> > + TSL2X7X_CHIP_UNKNOWN = 0, >> > + TSL2X7X_CHIP_WORKING = 1, >> > + TSL2X7X_CHIP_SUSPENDED = 2 >> > +}; >> > + >> > +/* Per-device data */ >> > +struct tsl2x7x_als_info { >> > + u16 als_ch0; >> > + u16 als_ch1; >> > + u16 lux; >> > +}; >> > + >> > +struct prox_stat { >> > + u16 min; >> > + u16 max; >> > + u16 mean; >> > + unsigned long stddev; >> > +}; >> > + >> > +struct tsl2x7x_chip_info { >> > + int chan_table_elements; >> > + struct iio_chan_spec channel[9]; >> > + const struct iio_info *info; >> > +}; >> > + >> > +struct tsl2X7X_chip { >> > + kernel_ulong_t id; >> > + struct mutex prox_mutex; >> > + struct mutex als_mutex; >> > + struct i2c_client *client; >> > + u16 prox_data; >> > + struct tsl2x7x_als_info als_cur_info; >> > + struct tsl2x7x_settings tsl2x7x_settings; >> > + struct tsl2X7X_platform_data *pdata; >> > + int als_time_scale; >> > + int als_saturation; >> > + int tsl2x7x_chip_status; >> > + u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG]; >> > + const struct tsl2x7x_chip_info *chip_info; >> > + const struct iio_info *info; >> > + s64 event_timestamp; >> > + /* This structure is intentionally large to accommodate >> > + * updates via sysfs. */ >> > + /* Sized to 9 = max 8 segments + 1 termination segment */ >> > + struct tsl2x7x_lux >tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE]; >> > +}; >> > + >> > +/* Different devices require different coefficents */ >> > +static const struct tsl2x7x_lux tsl2x71_lux_table[] = { >> > + { 14461, 611, 1211 }, >> > + { 18540, 352, 623 }, >> > + { 0, 0, 0 }, >> > +}; >> > + >> > +static const struct tsl2x7x_lux tmd2x71_lux_table[] = { >> > + { 11635, 115, 256 }, >> > + { 15536, 87, 179 }, >> > + { 0, 0, 0 }, >> > +}; >> > + >> > +static const struct tsl2x7x_lux tsl2x72_lux_table[] = { >> > + { 14013, 466, 917 }, >> > + { 18222, 310, 552 }, >> > + { 0, 0, 0 }, >> > +}; >> > + >> > +static const struct tsl2x7x_lux tmd2x72_lux_table[] = { >> > + { 13218, 130, 262 }, >> > + { 17592, 92, 169 }, >> > + { 0, 0, 0 }, >> > +}; >> > + >> > +static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] >= { >> > + [tsl2571] = tsl2x71_lux_table, >> > + [tsl2671] = tsl2x71_lux_table, >> > + [tmd2671] = tmd2x71_lux_table, >> > + [tsl2771] = tsl2x71_lux_table, >> > + [tmd2771] = tmd2x71_lux_table, >> > + [tsl2572] = tsl2x72_lux_table, >> > + [tsl2672] = tsl2x72_lux_table, >> > + [tmd2672] = tmd2x72_lux_table, >> > + [tsl2772] = tsl2x72_lux_table, >> > + [tmd2772] = tmd2x72_lux_table, >> > +}; >> > + >> > +static const struct tsl2x7x_settings tsl2x7x_default_settings = { >> > + .als_time = 200, >> > + .als_gain = 0, >> > + .prx_time = 0xfe, /*5.4 mS */ >> > + .prox_gain = 1, >> > + .wait_time = 245, >> > + .prox_config = 0, >> > + .als_gain_trim = 1000, >> > + .als_cal_target = 150, >> > + .als_thresh_low = 200, >> > + .als_thresh_high = 256, >> > + .persistence = 0xFF, >> > + .interrupts_en = 0x00, >> > + .prox_thres_low = 0, >> > + .prox_thres_high = 512, >> > + .prox_max_samples_cal = 30, >> > + .prox_pulse_count = 8 >> > +}; >> > + >> > +static const s16 tsl2X7X_als_gainadj[] = { >> > + 1, >> > + 8, >> > + 16, >> > + 120 >> > +}; >> > + >> > +static const s16 tsl2X7X_prx_gainadj[] = { >> > + 1, >> > + 2, >> > + 4, >> > + 8 >> > +}; >> > + >> > +/* Channel variations */ >> > +enum { >> > + ALS, >> > + PRX, >> > + ALSPRX, >> > + PRX2, >> > + ALSPRX2, >> > +}; >> > + >> > +const u8 device_channel_config[] = { >> > + ALS, >> > + PRX, >> > + PRX, >> > + ALSPRX, >> > + ALSPRX, >> > + ALS, >> > + PRX2, >> > + PRX2, >> > + ALSPRX2, >> > + ALSPRX2 >> > +}; >> > + >> > +/* >> > + * Read a number of bytes starting at register (reg) location. >> > + * Return 0, or i2c_smbus_write_byte ERROR code. >> > + * -- Sent from my Android phone with K-9 Mail. Please excuse my brevity. ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2012-04-10 19:09 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-04-02 16:50 [PATCH V5] TAOS tsl2x7x Jon Brenner
2012-04-04 8:35 ` Jonathan Cameron
2012-04-04 15:30 ` Jon Brenner
2012-04-04 15:30 ` Jon Brenner
2012-04-04 16:16 ` Peter Meerwald
2012-04-10 17:36 ` Greg KH
2012-04-10 19:03 ` Jonathan Cameron
2012-04-10 19:09 ` Greg KH
[not found] <c0e6301f-ce07-4659-9239-8dbd96dc5b59@email.android.com>
2012-04-04 16:23 ` Jonathan Cameron
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.