Skip to contentSkip to conversation

TAOS 258x: Device Driver

PATCH In review Jon Brenner 2 participants 3 messages Feb 25, 2011

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 TAOS 258x: Device Driver

From: J. August Brenner <[email protected]>

This driver supports the TAOS tsl258x family of ALS sensors. This driver provides ALS data (lux), and device configuration via ioctl, input_event, sysfs/iio methods. These methods are provided to support varied access requirements and integrated into a single driver. Thus, adverting the need for multiple releases of similar code. More significantly, this driver provides the capability to be fed 'glass coefficients' to accommodate varying system designs (bezels, faceplates, etc.).

---

Kconfig +7 -0
diff -Naurp drivers-orig/Kconfig drivers/Kconfig
@@ -13,6 +13,13 @@ config SENSORS_TSL2563
 	 This driver can also be built as a module.  If so, the module
 	 will be called tsl2563.
 
+config TAOS_258x
+	tristate "TAOS TSL258x device driver"
+	default m
+	help
+	  Provides support for the TAOS tsl258x family of devices.
+	  Access ALS data via iio, sysfs, input_event, and ioctl methods.
+
 config SENSORS_ISL29018
         tristate "ISL 29018 light and proximity sensor"
         depends on I2C
Makefile +1 -0
diff -Naurp drivers-orig/Makefile drivers/Makefile
@@ -3,4 +3,5 @@
 #
 
 obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o
+obj-$(CONFIG_TAOS_258x)	+= tsl258x.o
 obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o
tsl258x.c +12 -0
diff -Naurp drivers-orig/tsl258x.c drivers/tsl258x.c
@@ -0,0 +1,2055 @@
+/*
+ * Device driver for monitoring ambient light intensity (lux)
+ * within the TAOS tsl258x family of devices
+ *
+ * Copyright (c) 2011, 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/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/wait.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/firmware.h>
+#include <linux/string.h>
+#include <linux/kmod.h>
+#include <linux/ctype.h>
+#include <linux/input.h>
+#include <linux/time.h>
+#include <linux/jiffies.h>
+#include "../iio.h"
+ +/*........................................................................*/
+#define DRIVER_VERSION_ID "3.9"
+
+#define TAOS258x 2
+
+/* This build will be for..*/
+#define DRIVER_TYPE TAOS258x
+ +/*........................................................................*/ +/*Debug related*/
+#define TAOS_DEBUG 1
+/*#define TAOS_DEBUG_SMBUS 1 */
+#define TAOS_IRQ_DEBUG 1
+/*........................................................................*/ +/*User notification method*/
+/*#define ASYNC_NOTIFY 1 */
+#define EVENT_NOTIFY 1
+/*........................................................................*/ +/*Module defines*/
+/* Device name/id/address */
+#define DEVICE_NAME "taos"
+#ifdef TAOS258x
+#define DEVICE_ID "skateFN"
+#else
+#define DEVICE_ID "tritonFN"
+#endif
+
+#define ID_NAME_SIZE 10
+#define DEVICE_ADDR1 0x29
+#define DEVICE_ADDR2 0x39
+#define DEVICE_ADDR3 0x49
+#define MAX_NUM_DEVICES 3
+#define MAX_DEVICE_REGS 32
+#define I2C_MAX_ADAPTERS 8
+
+/* Triton register offsets */
+#ifdef TAOS258x
+#define TAOS_REG_MAX 8
+#else
+#define TAOS_REG_MAX 16
+#endif
+
+/* Power management */
+#define taos_suspend NULL
+#define taos_shutdown NULL
+#define taos_resume NULL
+
+#define TRUE 1
+#define FALSE 0
+
+#define MAXI2C 32
+#define CMD_ADDRESS 0x80
+#define TAOS_ERROR -1
+#define TAOS_SUCCESS 0
+
+#define MAX_SAMPLES_CAL 200
+
+#define LUX_UPDATE_PERIOD (HZ/2)
+
+/* power control */
+#define ON 1
+#define OFF 0
+
+
+/* sensor type */
+#define LIGHT 1
+#define PROXIMITY 2
+#define ALL 3
+
+struct taos_als_info {
+ u16 als_ch0;
+ u16 als_ch1;
+ u16 lux;
+};
+
+struct taos_settings {
+ int als_time;
+ int als_gain;
+ int als_gain_trim;
+ int als_cal_target;
+ int interrupts_enabled;
+ int als_thresh_low;
+ int als_thresh_high;
+ char als_persistence;
+};
+
+/* ioctl numbers */
+#define TAOS_IOCTL_MAGIC 0XCF
+#define TAOS_IOCTL_ALS_ON _IO(TAOS_IOCTL_MAGIC, 1)
+#define TAOS_IOCTL_ALS_OFF _IO(TAOS_IOCTL_MAGIC, 2)
+#define TAOS_IOCTL_ALS_DATA _IOR(TAOS_IOCTL_MAGIC, 3, short)
+#define TAOS_IOCTL_ALS_CALIBRATE _IO(TAOS_IOCTL_MAGIC, 4)
+#define TAOS_IOCTL_CONFIG_GET _IOR(TAOS_IOCTL_MAGIC, 5,\
+ struct taos_settings)
+#define TAOS_IOCTL_CONFIG_SET _IOW(TAOS_IOCTL_MAGIC, 6, \
+ struct taos_settings)
+#define TAOS_IOCTL_LUX_TABLE_GET _IOR(TAOS_IOCTL_MAGIC, 7, \
+ struct taos_lux)
+#define TAOS_IOCTL_LUX_TABLE_SET _IOW(TAOS_IOCTL_MAGIC, 8, \
+ struct taos_lux)
+#define TAOS_IOCTL_ID _IOR(TAOS_IOCTL_MAGIC, 12, char*)
+#define TAOS_IOCTL_SET_GAIN _IOW(TAOS_IOCTL_MAGIC, 15, int)
+#define TAOS_IOCTL_GET_ALS _IOR(TAOS_IOCTL_MAGIC, 16, \
+ struct taos_als_info)
+#define TAOS_IOCTL_INT_SET _IOW(TAOS_IOCTL_MAGIC, 17, int)
+
+/*Device Registers and Masks*/
+#define CNTRL 0x00
+#define STATUS 0x00
+#define ALS_TIME 0X01
+#define INTERRUPT 0x02
+#define ALS_MINTHRESHLO 0X03
+#define ALS_MINTHRESHHI 0X04
+#define ALS_MAXTHRESHLO 0X05
+#define ALS_MAXTHRESHHI 0X06
+#define GAIN 0x07
+#define REVID 0x11
+#define CHIPID 0x12
+#define SMB_4 0x13
+#define ALS_CHAN0LO 0x14
+#define ALS_CHAN0HI 0x15
+#define ALS_CHAN1LO 0x16
+#define ALS_CHAN1HI 0x17
+#define TMR_LO 0x18
+#define TMR_HI 0x19
+
+/* Skate cmd reg masks */
+#define CMD_REG 0x80
+#define CMD_BYTE_RW 0x00
+#define CMD_WORD_BLK_RW 0x20
+#define CMD_SPL_FN 0x60
+#define CMD_ALS_INTCLR 0X01
+
+/* Skate cntrl reg masks */
+#define CNTL_REG_CLEAR 0x00
+#define CNTL_ALS_INT_ENBL 0x10
+#define CNTL_WAIT_TMR_ENBL 0x08
+#define CNTL_ADC_ENBL 0x02
+#define CNTL_PWRON 0x01
+#define CNTL_ALSPON_ENBL 0x03
+#define CNTL_INTALSPON_ENBL 0x13
+
+/* Skate status reg masks */
+#define STA_ADCVALID 0x01
+#define STA_ALSINTR 0x10
+#define STA_ADCINTR 0x10
+
+/* Lux constants */
+#define MAX_LUX 65535
+
+/* Thresholds */
+#define ALS_THRESHOLD_LO_LIMIT 0x0010
+#define ALS_THRESHOLD_HI_LIMIT 0xFFFF
+
+/* Device default configuration */
+#define ALS_TIME_PARAM 100
+#define SCALE_FACTOR_PARAM 1
+#define GAIN_TRIM_PARAM 512
+#define GAIN_PARAM 1
+#define ALS_THRSH_HI_PARAM 0xFFFF
+#define ALS_THRSH_LO_PARAM 0
+
+
+/* Prototypes */
+static void taos_defaults(void);
+static int taos_chip_on(void);
+static int taos_chip_off(void);
+static int taos_get_lux(void);
+
+struct i2c_adapter *i2c_get_adapter(int id);
+
+static int taos_probe(struct i2c_client *clientp,
+ const struct i2c_device_id *idp);
+static int taos_remove(struct i2c_client *client);
+static int taos_open(struct inode *inode, struct file *file); +static int taos_release(struct inode *inode, struct file *file); +static int taos_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg);
+static int taos_read(struct file *file, char *buf, size_t count, loff_t *ppos);
+static int taos_write(struct file *file, const char *buf, size_t count,
+ loff_t *ppos);
+static loff_t taos_llseek(struct file *file, loff_t offset, int orig); +static int taos_fasync(int fd, struct file *file, int mode); +static int taos_device_name(unsigned char *bufp, char **device_name); +static int taos_als_calibrate(unsigned long als_cal_target); +static void taos_als_auto_update(struct work_struct *update);
+ +/*........................................................................*/ +/* Various /misc. emun*/
+enum {
+ TAOS_CHIP_UNKNOWN = 0, TAOS_CHIP_WORKING = 1, TAOS_CHIP_SLEEP = 2
+} TAOS_CHIP_WORKING_STATUS;
+ +/*........................................................................*/ +
+char driver_version_id[] = DRIVER_VERSION_ID;
+ +/*........................................................................*/ +
+/* First device number */
+static dev_t taos_dev_number;
+
+/* Class structure for this device */
+static struct class *taos_class;
+
+/* Module device table */
+static struct i2c_device_id taos_idtable[] = {
+ { DEVICE_ID, 0 },
+ { } };
+MODULE_DEVICE_TABLE(i2c, taos_idtable);
+
+/* Board and address info */
+static struct i2c_board_info taos_board_info[] = {
+ { I2C_BOARD_INFO(DEVICE_ID, DEVICE_ADDR1), },
+ { I2C_BOARD_INFO(DEVICE_ID, DEVICE_ADDR2), },
+ { I2C_BOARD_INFO(DEVICE_ID, DEVICE_ADDR3), }, };
+
+static unsigned short const taos_addr_list[4] = {
+ DEVICE_ADDR1,
+ DEVICE_ADDR2,
+ DEVICE_ADDR3,
+ I2C_CLIENT_END };
+
+/* Client and device */
+static struct i2c_client *client;
+static struct device *devp;
+static struct i2c_client *bad_clientp[MAX_NUM_DEVICES];
+static int device_found;
+
+/* Driver definition */
+static struct i2c_driver taos_driver = {
+ .driver = {
+ .owner = THIS_MODULE,

+    .name = DEVICE_NAME, },
+    .id_table = taos_idtable,
+    .probe = taos_probe,
+    .remove = __devexit_p(taos_remove),
+    .suspend = taos_suspend,
+    .shutdown = taos_shutdown,
+    .resume = taos_resume, };

+
+/* Per-device data */
+static struct tsl258x_chip {
+ struct i2c_client *client;
+ struct iio_dev *iio_dev;
+ struct delayed_work update_lux;
+ struct work_struct work_thresh;
+ s64 event_timestamp;
+ struct cdev cdev;
+ struct fasync_struct *async_queue;
+ unsigned int addr;
+ char taos_id;
+ char taos_name[ID_NAME_SIZE];
+ char valid;
+ unsigned long last_updated;
+
+} *chip;
+
+/* File operations */
+static const struct file_operations taos_fops = {
+ .owner = THIS_MODULE,
+ .open = taos_open,
+ .release = taos_release,
+ .read = taos_read,
+ .write = taos_write,
+ .llseek = taos_llseek,
+ .fasync = taos_fasync,
+ .ioctl = taos_ioctl, };
+
+/* ................ Als info ..................*/
+struct taos_als_info als_cur_info;
+EXPORT_SYMBOL(als_cur_info);
+
+/*Next two vars are also used to determine irq type - in lieu status info*/
+bool als_on = FALSE;
+
+static int device_released = 0x0000;
+static unsigned int irqnum = 0x0000;
+static DECLARE_WAIT_QUEUE_HEAD(taos_int_wait);
+
+/* Lux time scale */
+struct time_scale_factor {
+ unsigned int numerator;
+ unsigned int denominator;
+ unsigned int saturation;
+};
+
+/* ------ Mutex ------*/
+DEFINE_MUTEX(als_mutex);
+
+/*Structs & vars*/
+int taos_chip_status = TAOS_CHIP_UNKNOWN; /*Unknown = uninitialized to start*/
+int taos_cycle_type; /* what is the type of cycle being run: ALS, PROX, or BOTH
+ */
+struct taos_settings taos_settings; +EXPORT_SYMBOL(taos_settings);
+
+/* Device configuration */
+#define MAX_SETTING_MEMBERS 6
+static struct taos_settings *taos_settingsP; /*Pointer Needed for ioctl(s)*/
+
+/*More Prototypes*/
+static int taos_i2c_read(u8 reg, u8 *val, unsigned int len); +static int taos_i2c_write(u8 reg, u8 *val);
+static int taos_i2c_write_command(u8 reg);
+
+/* Work Queues used for bottom halves of self poll/push & IRQs */
+static struct workqueue_struct *taos_wq0;
+
+struct delayed_work *luxUd; +/*........................................................................*/ +/*
+ Initial values for device - this values can/will be changed by driver.
+ and applications as needed.
+ These values are dynamic.
+ */
+u8 taos_config[8] = { 0x00, 0xee, 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00 };
+/* cntrl atime intC Athl0 Athl1 Athh0 Athh1 gain*/
+
+/*
+ This structure is intentionally large to accommodate updates via
+ ioctl and proc input methods.
+ */
+struct taos_lux {
+ unsigned int ratio;
+ unsigned int ch0;
+ unsigned int ch1;
+} taos_device_lux[] = {
+ { 9830, 8520, 15729 },
+ { 12452, 10807, 23344 },
+ {14746, 6383, 11705 },
+ { 17695, 4063, 6554 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 } };
+
+struct taos_lux taos_lux;
+EXPORT_SYMBOL(taos_lux);
+
+int als_time_scale; /*computed, ratios lux due to als integration time*/
+int als_saturation; /*computed, set to 90% of full scale of als integration*/
+
+/*Index = (0 - 3) Used to validate the gain selection index*/
+#define MAX_GAIN_STAGES 4
+struct gainadj {
+ s16 ch0;
+ s16 ch1;
+} gainadj[] = {
+ { 1, 1 },
+ { 8, 8 },
+ { 16, 16 },
+ { 107, 115 } };
+
+struct taos_lux *taos_device_luxP;
+
+/*Input_dev*/
+static struct input_dev *taos_dev;
+
+/*sysfs - interface functions*/
+static ssize_t taos_device_id(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(device_id, S_IRUGO, taos_device_id, NULL);
+
+static ssize_t taos_power_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t taos_power_state_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(device_state, S_IRUGO | S_IWUSR,
+ taos_power_state_show,
+ taos_power_state_store);
+
+static ssize_t taos_gain_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t taos_gain_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_gain, S_IRUGO | S_IWUSR,
+ taos_gain_show,
+ taos_gain_store);
+
+static ssize_t taos_interrupt_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t taos_interrupt_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_interrupt, S_IRUGO | S_IWUSR,
+ taos_interrupt_show,
+ taos_interrupt_store);
+
+static ssize_t taos_als_time_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t taos_als_time_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_time, S_IRUGO | S_IWUSR,
+ taos_als_time_show,
+ taos_als_time_store);
+
+static ssize_t taos_als_trim_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t taos_als_trim_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_trim, S_IRUGO | S_IWUSR,
+ taos_als_trim_show,
+ taos_als_trim_store);
+
+static ssize_t taos_als_persistence_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t taos_als_persistence_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_persistence, S_IRUGO | S_IWUSR,
+ taos_als_persistence_show,
+ taos_als_persistence_store);
+
+static ssize_t taos_als_cal_target_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t taos_als_cal_target_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_target, S_IRUGO | S_IWUSR,
+ taos_als_cal_target_show,
+ taos_als_cal_target_store);
+
+static ssize_t taos_lux_show(struct device *dev, struct device_attribute *attr,
+ char *buf);
+static DEVICE_ATTR(lux, S_IRUGO, taos_lux_show, NULL);
+
+static ssize_t taos_do_calibrate(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, taos_do_calibrate); +
+static ssize_t taos_als_thresh_low_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t taos_als_thresh_low_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_lowT, S_IRUGO | S_IWUSR,
+ taos_als_thresh_low_show,
+ taos_als_thresh_low_store);
+
+static ssize_t taos_als_thresh_high_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t taos_als_thresh_high_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_highT, S_IRUGO | S_IWUSR,
+ taos_als_thresh_high_show,
+ taos_als_thresh_high_store);
+
+
+static struct attribute *sysfs_attrs_ctrl[] = {
+ &dev_attr_device_id.attr,

+    &dev_attr_device_state.attr,
+    &dev_attr_als_gain.attr,
+    &dev_attr_als_interrupt.attr,
+    &dev_attr_als_time.attr,
+    &dev_attr_als_trim.attr,
+    &dev_attr_als_persistence.attr,
+    &dev_attr_als_target.attr,
+    &dev_attr_lux.attr,
+    &dev_attr_calibrate.attr,
+    &dev_attr_als_lowT.attr,
+    &dev_attr_als_highT.attr,
+    NULL };

+
+static struct attribute_group tsl258x_attribute_group[] = {
+ { .attrs = sysfs_attrs_ctrl }, };
+
+ +/*===========================================================================*/ +/*
+
+ FUNCTIONS BEGIN HERE
+
+ */ +/*===========================================================================*/ + +/*===========================================================================*/ +/**@defgroup Initialization Driver Initialization
+ * The sections applies to functions for driver initialization, instantiation,
+ * _exit, \n
+ * and user-land application invoking (ie. open).\n
+ * Also included in this section is the initial interrupt handler (handler
+ * bottom halves are in the respective
+ * sections).
+ * @{
+ */
+ +/*........................................................................*/ +/** Driver initialization - device probe is initiated here, to identify
+ * a valid device if present on any of the i2c buses and at any address.
+ * \param none
+ * \return: int (0 = OK)
+ * \note H/W Interrupt are device/product dependent.
+ * Attention is required to the definition and configuration.
+ */ +/*........................................................................*/ +static int __init taos_init(void)
+{
+ u32 err;
+ int i, j, k, ret = 0;
+ struct i2c_adapter *my_adap;
+ static int num_bad = 0x0000;
+
+#ifdef TAOS_DEBUG
+ printk(KERN_ERR "Loading TAOS Driver\n");
+ printk(KERN_INFO "\nRequesting a GPIO for irq\n");
+#endif +/*......................................................................*/
+ /*Create the work queue and allocate kernel memory for the
+ * scheduled workers*/
+ taos_wq0 = create_workqueue("taos_work_queue");
+ if (taos_wq0)
+ luxUd = kmalloc(sizeof(struct delayed_work), GFP_KERNEL);
+
+ /*Make sure we have what we need */
+ if ((!taos_wq0) || (!luxUd))
+ return -ENOMEM;
+ +/*.....................................................................*/
+ ret = alloc_chrdev_region(&taos_dev_number, 0, MAX_NUM_DEVICES,
+ DEVICE_NAME);
+ if (ret < 0) {
+ dev_err(devp, "taos_device_drvr: alloc_chrdev_region()"
+ "failed in taos_init()\n");
+ return ret;
+ }
+ taos_class = class_create(THIS_MODULE, DEVICE_NAME);
+ chip = kmalloc(sizeof(struct tsl258x_chip), GFP_KERNEL);
+ if (!chip) {
+ dev_err(devp, "taos_device_drvr: kmalloc for "
+ "struct tsl258x_chip failed in taos_init()\n");
+ ret = -ENOMEM;
+ goto exit_3;
+ }
+ memset(chip, 0, sizeof(struct tsl258x_chip));
+ cdev_init(&chip->cdev, &taos_fops);
+ chip->cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&chip->cdev, taos_dev_number, 1);
+ if (ret < 0) {
+ dev_err(devp, "taos_device_drvr: cdev_add() failed in "
+ "taos_init()\n");
+ goto exit_2;
+ }
+ device_create(taos_class, NULL, MKDEV(MAJOR(taos_dev_number), 0),
+ &taos_driver , DEVICE_NAME);
+ ret = i2c_add_driver(&taos_driver);
+ if (ret < 0) {
+ dev_err(devp, "taos_device_drvr: i2c_add_driver() failed in "
+ "taos_init()\n");
+ goto exit_1;
+ }
+ device_found = 0;
+ for (i = 0; i < I2C_MAX_ADAPTERS; i++) {
+ my_adap = i2c_get_adapter(i);
+ if (my_adap == NULL)
+ break;
+ for (j = 0; j < MAX_NUM_DEVICES; j++) {
+ client = i2c_new_probed_device(my_adap,
+ &taos_board_info[j], taos_addr_list);
+ if ((client) && (!device_found)) {
+ bad_clientp[num_bad] = client;
+ num_bad++;
+ }
+ if (device_found)
+ break;
+ }
+ if (num_bad) {
+ for (k = 0; k < num_bad; k++)
+ i2c_unregister_device(bad_clientp[k]);
+ num_bad = 0;
+ }
+ if (device_found)
+ break;
+ }
+ if (device_found) {
+ devp = &client->dev;
+ goto exit_4;
+ } else {
+ if (client)
+ i2c_unregister_device(client);
+ i2c_del_driver(&taos_driver);
+ device_destroy(taos_class, MKDEV(MAJOR(taos_dev_number), 0));
+ cdev_del(&chip->cdev);
+ kfree(chip);
+ class_destroy(taos_class);
+ unregister_chrdev_region(taos_dev_number, MAX_NUM_DEVICES);
+ printk(KERN_ERR "taos_device_drvr: taos_init() found no device\n");
+ return -ENODEV;
+ }
+
+exit_1: device_destroy(taos_class, MKDEV(MAJOR(taos_dev_number), 0)); +exit_2: cdev_del(&chip->cdev);
+ kfree(chip);
+exit_3: class_destroy(taos_class);
+ unregister_chrdev_region(taos_dev_number, MAX_NUM_DEVICES);
+
+exit_4:
+
+ printk(KERN_INFO "\n\nallocating iio device\n");
+ chip->iio_dev = iio_allocate_device();
+ if (!chip->iio_dev)
+ printk(KERN_INFO "iio allocation failed\n");
+
+ chip->iio_dev->attrs =
+ (struct attribute_group *)&tsl258x_attribute_group;
+ chip->iio_dev->dev.parent = &client->dev;
+ chip->iio_dev->dev_data = (void *)(chip);
+ chip->iio_dev->driver_module = THIS_MODULE;
+ chip->iio_dev->modes = INDIO_DIRECT_MODE;
+ err = iio_device_register(chip->iio_dev);
+
+ if (err)
+ printk(KERN_INFO "iio registration failed\n");
+
+ /*Make sure the chip is off*/
+ taos_chip_off();
+ /*Load up the V2 defaults (these are hard coded defaults for now)*/
+ taos_defaults();
+ /*Set any other defaults that are needed to initialize
+ * (such as taos_cycle_type).
+ Note: these settings can/will be changed based on user
requirements.*/
+ taos_cycle_type = LIGHT;
+ /*Initialize the work queues*/
+
+ INIT_DELAYED_WORK(luxUd, taos_als_auto_update);
+
+ /*...................................................................*/
+ /*This section populates the input structure and registers the
device*/
+#ifdef EVENT_NOTIFY
+ taos_dev = input_allocate_device();
+ if (!taos_dev) {
+ printk(KERN_ERR "__init: Not enough memory for input_dev\n");
+ return ret;
+ }
+
+ /*Since we now have the struct, populate.*/
+ taos_dev->name = DEVICE_ID;
+ taos_dev->id.bustype = BUS_I2C;
+ taos_dev->id.vendor = 0x0089;
+ taos_dev->id.product = 0x2581;
+ taos_dev->id.version = 3;
+ taos_dev->phys = "/dev/taos";
+
+ /*This device one value (LUX)
+ Other devices may have LUX and PROX.
+ Thus we use the ABS_X so ABS_Y is logically available.*/
+ set_bit(EV_ABS, taos_dev->evbit);
+ set_bit(ABS_X, taos_dev->absbit);
+
+ /*Only returns only positive LUX values
+ No noise filter, no flat level*/
+ taos_dev->absmin[ABS_X] = 0;
+ taos_dev->absmax[ABS_X] = MAX_LUX;
+ taos_dev->absfuzz[ABS_X] = 0;
+ taos_dev->absflat[ABS_X] = 0;
+
+ /*And register..*/
+ ret = input_register_device(taos_dev);
+ if (ret) {
+ printk(KERN_ERR "button.c: Failed to register input_dev\n");
+ goto err_free_dev;
+ }
+#endif /*End of input_event*/
+
+
+ /*invoke sysfs
+ invoke iio subsystem*/
+
+ err = sysfs_create_group(&chip->client->dev.kobj,
+ tsl258x_attribute_group);
+ if (err < 0) {
+ dev_err(&chip->client->dev, "Sysfs registration failed\n");
+ goto err_free_dev;
+ }
+
+ if (taos_wq0)
+ ret = queue_delayed_work(taos_wq0, luxUd, LUX_UPDATE_PERIOD);
+ else
+ printk(KERN_INFO "\nNO taos_wq0 for luxUd\n\n");
+
+ return ret; /*Normal return*/
+
+/*In the event we had a problem REGISTERING the device for input_events*/
+err_free_dev:
+ input_free_device(taos_dev);
+ return ret;
+}
+
+/**
+ * Driver exit
+ */
+static void __exit taos_exit(void)
+{
+ printk(KERN_INFO "TAOS driver __exit\n");
+
+ if (taos_wq0) {
+ flush_workqueue(taos_wq0);
+ destroy_workqueue(taos_wq0);
+ }
+
+ input_free_device(taos_dev);
+
+ if (client)
+ i2c_unregister_device(client);
+ i2c_del_driver(&taos_driver);
+ device_destroy(taos_class, MKDEV(MAJOR(taos_dev_number), 0));
+ cdev_del(&chip->cdev);
+ kfree(chip);
+ class_destroy(taos_class);
+ unregister_chrdev_region(taos_dev_number, MAX_NUM_DEVICES);
+
+}
+
+/**
+ * Client probe function - When a valid device is found, the driver's device
+ * data structure is updated, and initialization completes successfully.
+ */
+static int taos_probe(struct i2c_client *clientp,
+ const struct i2c_device_id *idp) {
+ int i, ret = 0;
+ unsigned char buf[MAX_DEVICE_REGS];
+ char *device_name;
+
+ if (device_found)
+ return -ENODEV;
+ if (!i2c_check_functionality(clientp->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&clientp->dev,
+ "taos_probe() - i2c smbus byte data "
+ "functions unsupported\n");
+ return -EOPNOTSUPP;
+ }
+ if (!i2c_check_functionality(clientp->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&clientp->dev,
+ "taos_probe() - i2c smbus word data "
+ "functions unsupported\n");
+ }
+ if (!i2c_check_functionality(clientp->adapter,
+ I2C_FUNC_SMBUS_BLOCK_DATA)) {
+ dev_err(&clientp->dev,
+ "taos_probe() - i2c smbus block data "
+ "functions unsupported\n");
+ }
+ chip->client = clientp;
+ i2c_set_clientdata(clientp, chip);
+ for (i = 0; i < MAX_DEVICE_REGS; i++) {
+ ret = i2c_smbus_write_byte(clientp, (CMD_REG | (CNTRL + i)));
+ if (ret < 0) {
+ dev_err(&clientp->dev, "i2c_smbus_write_byte() to cmd "
+ "reg failed in taos_probe(), err = %d\n", ret);
+ return ret;
+ }
+ buf[i] = i2c_smbus_read_byte(clientp);
+ }
+ ret = taos_device_name(buf, &device_name);
+ if (ret < 0) {
+ dev_info(&clientp->dev, "i2c device found - does not match "
+ "expected id in taos_probe() 1\n");
+ }
+ if (strcmp(device_name, DEVICE_ID)) {
+ dev_info(&clientp->dev, "i2c device found - does not match "
+ "expected id in taos_probe()2\n");
+ } else {
+ dev_info(&clientp->dev, "i2c device found - matches expected "
+ "id of %s in taos_probe()\n", device_name);
+ device_found = 1;
+ }
+ ret = i2c_smbus_write_byte(clientp, (CMD_REG | CNTRL));
+ if (ret < 0) {
+ dev_err(&clientp->dev, "i2c_smbus_write_byte() to cmd reg "
+ "failed in taos_probe(), err = %d\n", ret);
+ return ret;
+ }
+ strlcpy(clientp->name, DEVICE_ID, I2C_NAME_SIZE);
+ strlcpy(chip->taos_name, DEVICE_ID, ID_NAME_SIZE);
+ chip->valid = 0;
+
+ taos_settingsP = kmalloc(sizeof(struct taos_settings), GFP_KERNEL);
+ if (!(taos_settingsP)) {
+ dev_err(&clientp->dev,
+ "kmalloc for struct taos_settings failed in taos_probe()\n");
+ return -ENOMEM;
+ }
+
+ return ret;
+}
+
+/**
+ * Client remove
+ */
+static int __devexit taos_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+/**
+ * Device open function
+ */
+static int taos_open(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ struct tsl258x_chip *chip;
+
+
+ /*For debug - Make it ez to c on the console*/
+#ifdef TAOS_DEBUG
+ printk(KERN_INFO "***************************************************
\n");
+ printk(KERN_INFO "* *
\n");
+ printk(KERN_INFO "* TAOS DEVICE *
\n");
+ printk(KERN_INFO "* DRIVER *
\n");
+ printk(KERN_INFO "* OPEN *
\n");
+ printk(KERN_INFO "***************************************************
\n");
+#endif
+
+ chip = container_of(inode->i_cdev, struct tsl258x_chip, cdev);
+ if (strcmp(chip->taos_name, DEVICE_ID) != 0) {
+ dev_err(devp, "device name error in taos_open(), shows %s\n",
+ chip->taos_name);
+ return -ENODEV;
+ }
+
+ device_released = 0;
+ return ret;
+}
+
+/**
+ * Device release
+ */
+static int taos_release(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ struct tsl258x_chip *chip;
+
+ device_released = 1;
+ chip = container_of(inode->i_cdev, struct tsl258x_chip, cdev);
+ if (strcmp(chip->taos_name, DEVICE_ID) != 0) {
+ dev_err(devp, "device id incorrect in taos_release(), shows "
+ "%s\n", chip->taos_name);
+ ret = -ENODEV;
+ }
+ taos_fasync(-1, file, 0);
+ free_irq(irqnum, chip);
+ return ret;
+}
+
+/**
+ * Name verification - uses default register values to identify the Taos device
+ */
+static int taos_device_name(unsigned char *bufp, char **device_name) +{
+ if (((bufp[0x12] & 0xf0) == 0x00) || (bufp[0x12] == 0x08)) {
+ *device_name = "tritonFN";
+ return 1;
+ } else if (((bufp[0x12] & 0xf0) == 0x90)) {
+ *device_name = "skateFN";
+ return 1;
+ }
+ *device_name = "unknown";
+ printk(KERN_INFO "Identified %s\n", *device_name);
+ return 0;
+}
+
+/**
+ * Device read - reads are permitted only within the range of the accessible
+ * registers of the device. The data read is copied safely to user space.
+ */
+static int taos_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ int ret = 0;
+ struct tsl258x_chip *chip;
+ u8 my_buf[MAX_DEVICE_REGS];
+ u8 reg, i = 0, xfrd = 0;
+
+
+ /*Make sure we not in the middle of accessing the device*/
+ if (mutex_trylock(&als_mutex) == 0) {
+ printk(KERN_INFO "Can't get ALS mutex\n");
+ return -1; /*busy, so return LAST VALUE*/
+ }
+
+ if ((*ppos < 0) || (*ppos >= MAX_DEVICE_REGS) || ((*ppos + count)
+ > MAX_DEVICE_REGS)) {
+ dev_err(devp, "reg limit check failed in taos_read()\n");
+ mutex_unlock(&als_mutex);
+ return -EINVAL;
+ }
+ reg = (u8) *ppos;
+ chip = container_of(file->f_dentry->d_inode->i_cdev,
+ struct tsl258x_chip, cdev);
+ while (xfrd < count) {
+ ret = i2c_smbus_write_byte(chip->client, (CMD_REG | reg));
+ if (ret < 0) {
+ dev_err(devp, "i2c_smbus_write_byte to cmd reg failed "
+ "in taos_read(), err = %d\n", ret);
+ mutex_unlock(&als_mutex);
+ return ret;
+ }
+ my_buf[i++] = i2c_smbus_read_byte(chip->client);
+ reg++;
+ xfrd++;
+ }
+ ret = copy_to_user(buf, my_buf, xfrd);
+ if (ret) {
+ dev_err(devp, "copy_to_user failed in taos_read()\n");
+ mutex_unlock(&als_mutex);
+ return -ENODATA;
+ }
+
+ mutex_unlock(&als_mutex);
+ return (int) xfrd;
+}
+
+/**
+ * Device write - writes are permitted only within the range of the accessible
+ * registers of the device. The data written is copied safely from user space.
+ */
+static int taos_write(struct file *file, const char *buf, size_t count,
+ loff_t *ppos)
+{
+ int ret = 0;
+ struct tsl258x_chip *chip;
+ u8 my_buf[MAX_DEVICE_REGS];
+ u8 reg, i = 0, xfrd = 0;
+
+ if (mutex_trylock(&als_mutex) == 0) {
+ printk(KERN_INFO "Can't get ALS mutex\n");
+ return -1; /*busy, so return LAST VALUE*/
+ }
+
+ if ((*ppos < 0) || (*ppos >= MAX_DEVICE_REGS) || ((*ppos + count)
+ > MAX_DEVICE_REGS)) {
+ dev_err(devp, "reg limit check failed in taos_write()\n");
+ mutex_unlock(&als_mutex);
+ return -EINVAL;
+ }
+ reg = (u8) *ppos;
+ ret = copy_from_user(my_buf, buf, count);
+ if (ret) {
+ dev_err(devp, "copy_from_user failed in taos_write()\n");
+ mutex_unlock(&als_mutex);
+ return -ENODATA;
+ }
+ chip = container_of(file->f_dentry->d_inode->i_cdev,
+ struct tsl258x_chip, cdev);
+ while (xfrd < count) {
+ ret = i2c_smbus_write_byte_data(chip->client, (CMD_REG | reg),
+ my_buf[i++]);
+ if (ret < 0) {
+ dev_err(devp, "i2c_smbus_write_byte_data failed in "
+ "taos_write()\n");
+ mutex_unlock(&als_mutex);
+ return ret;
+ }
+ reg++;
+ xfrd++;
+ }
+
+ mutex_unlock(&als_mutex);
+ return (int) xfrd;
+}
+
+/**
+ * Device seek - seeks are permitted only within the range of the accessible
+ * registers of the device. The new offset is returned.
+ */
+static loff_t taos_llseek(struct file *file, loff_t offset, int orig) +{
+ loff_t new_pos;
+
+ if ((offset >= MAX_DEVICE_REGS) || (orig < 0) || (orig > 1)) {
+ dev_err(devp, "offset param limit or whence limit check failed "
+ "in taos_llseek()\n");
+ return -EINVAL;
+ }
+ switch (orig) {
+ case 0:
+ new_pos = offset;
+ break;
+ case 1:
+ new_pos = file->f_pos + offset;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ if ((new_pos < 0) || (new_pos >= MAX_DEVICE_REGS)) {
+ dev_err(devp, "new offset check failed in taos_llseek()\n");
+ return -EINVAL;
+ }
+ file->f_pos = new_pos;
+ return new_pos;
+}
+
+/**
+ * Fasync function - called when the FASYNC flag in the file descriptor of the
+ * device indicates that a new user program wants to be added to the wait queue
+ * of the device - calls fasync_helper() to add the new entry to the queue.
+ */
+static int taos_fasync(int fd, struct file *file, int mode)
+{
+ chip = container_of(file->f_dentry->d_inode->i_cdev, struct
+ tsl258x_chip, cdev);
+ return fasync_helper(fd, file, mode, &chip->async_queue);
+}
+
+/**
+ * Provides initial operational parameter defaults.\n
+ * These defaults may be changed by the following:\n
+ * - system deamon
+ * - user application (via ioctl)
+ * - directly writing to the taos procfs file
+ * - external kernel level modules / applications
+ */
+static void taos_defaults()
+{
+ /*Operational parameters*/
+ taos_cycle_type = LIGHT;
+ /*default is ALS only*/
+ taos_settings.als_time = 100;
+ /*must be a multiple of 50mS*/
+ taos_settings.als_gain = 0;
+ /*this is actually an index into the gain table*/
+ /*assume clear glass as default*/
+ taos_settings.als_gain_trim = 1000;
+ /*default gain trim to account for aperture effects*/
+ taos_settings.als_persistence = 0;
+ /*default number of 'out of bounds' b4 interrupt*/
+ taos_settings.als_cal_target = 130;
+ /*Known external ALS reading used for calibration*/
+ taos_settings.interrupts_enabled = 0;
+ /*Interrupts enabled (ALS) 0 = none*/
+
+ /*Initialize ALS data to defaults*/
+ als_cur_info.als_ch0 = 0;
+ als_cur_info.als_ch1 = 0;
+ als_cur_info.lux = 0;
+
+ taos_settings.als_thresh_high = ALS_THRESHOLD_HI_LIMIT;
+ taos_settings.als_thresh_low = ALS_THRESHOLD_LO_LIMIT;
+
+
+#ifdef TAOS_DEBUG
+printk(KERN_INFO "\nDEFAULTS LOADED\n");
+#endif
+}
+EXPORT_SYMBOL(taos_defaults); +/*........................................................................*/
+/*@} End of Initial*/
+
+ +/*===========================================================================*/ +/**@defgroup IOCTL User ioctl Functions
+ * The section applies to 'ioctl' calls used primarily to support user-land applications.\n
+ * @{
+ */
+/**
+ * Ioctl functions - each one is documented below.
+ */
+static int taos_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg) {
+ int ret = 0;
+ struct tsl258x_chip *chip;
+ int tmp;
+ /*u8 reg_val;*/
+
+ chip = container_of(inode->i_cdev, struct tsl258x_chip, cdev);
+ switch (cmd) {
+ /**
+ * - ALS_ON - called to set the device in ambient light sense mode.
+ * configured values of light integration time, initial interrupt
+ * filter, gain, and interrupt thresholds (if interrupt driven) are
+ * initialized. Then power, adc, (and interrupt if needed) are
enabled.
+ */
+ case TAOS_IOCTL_ALS_ON:

+	  taos_cycle_type = LIGHT;
+	  printk(KERN_INFO "ALS On\n");
+	  return taos_chip_on();

+ break;
+ /**
+ * - ALS_OFF - called to stop ambient light sense mode of operation.
+ * Clears the filter history, and clears the control register.
+ */
+ case TAOS_IOCTL_ALS_OFF:
+ taos_chip_off();
+ break;
+ /**
+ * - ALS_DATA - request for current ambient light data. If correctly
+ * enabled and valid data is available at the device, the function
+ * for lux conversion is called, and the result returned if valid.
+ */
+ case TAOS_IOCTL_ALS_DATA:
+ /*Are we actively updating the struct?*/
+ if ((taos_settings.interrupts_enabled
+ & CNTL_ALS_INT_ENBL) == 0x00)
+ return taos_get_lux(); /*No - so get fresh data*/
+ else
+ return als_cur_info.lux;
+ /*Yes - data is fresh as last change*/
+ break;
+
+ /**
+ * - ALS_CALIBRATE - called to run one calibration cycle, during
assembly.
+ * The lux value under a known intensity light source is used to obtain
+ * a "gain trim" multiplier which is to be used to normalize subsequent
+ * lux data read from the device, after the calibration is completed.
+ * This 'known intensity should be the value of als_cal_target.
+ * If not - make is so!
+ */
+ case TAOS_IOCTL_ALS_CALIBRATE:
+ return taos_als_calibrate(taos_settings.als_cal_target);
+ break;
+ /**
+ * - CONFIG-GET - returns the current device configuration values to
the
+ * caller. The user mode application can display or store configuration
+ * values, for future reconfiguration of the device, as needed.
+ * Refer to structure "taos_settings" in '.h' file.
+ */
+ case TAOS_IOCTL_CONFIG_GET:
+ ret = copy_to_user((struct taos_settings *) arg, &taos_settings,
+ sizeof(struct taos_settings));
+ if (ret) {
+ dev_err(devp,
+ "copy_to_user() failed in ioctl config_get\n");
+ return -ENODATA;
+ }
+ return ret;
+ break;
+ /**
+ * - GET_ALS - returns the current ALS structure values to the
+ * caller. The values returned represent the last call to
+ * taos_get_lux(). This ioctl can be used when the driver
+ * is operating in the interrupt mode.
+ * Refer to structure "als_cur_info" in '.h' file.
+ */
+ case TAOS_IOCTL_GET_ALS:
+ ret = copy_to_user((struct taos_als_info *) arg,
+ &als_cur_info,
+ sizeof(struct taos_als_info));
+ if (ret) {
+ dev_err(devp,
+ "copy_to_user() failed in ioctl to get "
+ "taos_als_info\n");
+ return -ENODATA;
+ }
+ return ret;
+ break;
+ /**
+ * - CONFIG-SET - used by a user mode application to set the desired
+ * values in the driver's in memory copy of the device configuration
+ * data. Light integration times are aligned optimally, and driver
+ * global variables dependent on configured values are updated here.
+ * Refer to structure "taos_settings" in '.h' file.
+ */
+ case TAOS_IOCTL_CONFIG_SET:
+ ret = copy_from_user(&taos_settings,
+ (struct taos_settings *) arg,
+ sizeof(struct taos_settings));
+ if (ret) {
+ dev_err(devp,
+ "copy_from_user() failed in ioctl config_set\n");
+ return -ENODATA;
+ }
+ return ret;
+ break;
+ /**
+ * - LUX_TABLE-GET - returns the current LUX coefficients table to the
+ * caller. The user mode application can display or store the table
+ * values, for future re-calibration of the device, as needed.
+ * Refer to structure "taos_lux" in '.h' file.
+ */
+ case TAOS_IOCTL_LUX_TABLE_GET:
+ ret = copy_to_user((struct taos_lux *) arg,
+ &taos_device_lux,
+ sizeof(taos_device_lux));
+ if (ret) {
+ dev_err(devp,
+ "copy_to_user() failed in ioctl "
+ "TAOS_IOCTL_LUX_TABLE_GET\n");
+ return -ENODATA;
+ }
+ return ret;
+ break;
+ /**
+ * - LUX TABLE-SET - used by a user mode application to set the desired
+ * LUX table values in the driver's in memory copy of the lux table
+ * Refer to structure "taos_lux" in '.h' file.
+ */
+ case TAOS_IOCTL_LUX_TABLE_SET:
+ ret = copy_from_user(&taos_lux,
+ (struct taos_lux *) arg,
+ sizeof(taos_device_lux));
+ if (ret) {
+ dev_err(devp, "copy_from_user() failed in ioctl "
+ "TAOS_IOCTL_LUX_TABLE_SET\n");
+ return -ENODATA;
+ }
+#ifdef ASYNC_NOTIFY
+ /*Notify the user-land app (if any)*/
+ if (chip->async_queue)
+ kill_fasync(&chip->async_queue, SIGIO, POLL_IN);
+#endif
+
+
+ return ret;
+ break;
+ /**
+ * - TAOS_IOCTL_ID - used to query driver name & version
+ */
+ case TAOS_IOCTL_ID:
+ ret = copy_to_user((char *) arg,
+ &driver_version_id,
+ sizeof(driver_version_id));
+ dev_info(devp, "%s\n", DRIVER_VERSION_ID);
+ return ret;
+ break;
+ /*
+ * - TAOS_IOCTL_SET_GAIN - used to set/change the device analog gain.
+ * The value passed into here from the user is the index into the
table.
+ * Index = (0 - 3) Used to validate the gain selection index
+ */
+ case TAOS_IOCTL_SET_GAIN:
+ get_user(tmp, (int *)arg);
+ if (tmp > MAX_GAIN_STAGES)
+ return -1;
+ taos_settings.als_gain = tmp;
+ ret = taos_i2c_write(CMD_REG|GAIN, (u8 *)&tmp);
+ if (ret < 0) {
+ dev_err(devp, "taos_i2c_write to turn on device "
+ "failed in taos_chip_on.\n");
+ return -1;
+ }
+ return ret;
+ break;
+
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ return ret;
+
+ }
+/*........................................................................*/
+/*@} end of IOCTL section*/
+
+
+/*===========================================================================*/
+/**@defgroup ALS Ambient Light Sense (ALS)
+ * The section applies to the ALS related functions.\n
+ * Other ALS releated functions may appear elsewhere in the code.\n
+ * @{
+ */
+
+/*........................................................................*/
+
+/**
+ * Reads and calculates current lux value.
+ * 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. The array
taos_device_luxP[]
+ * declared above 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.
+ *
+ * \param none
+ * \return int -1 = Failure, Lux Value = success
+ */
+static int taos_get_lux(void)
+{
+ u32 ch0, ch1; /* separated ch0/ch1 data from device */
+ u32 lux; /* raw lux calculated from device data */
+ u32 ratio;
+ u8 buf[5];
+ struct taos_lux *p;
+
+ int i, ret;
+
+ u32 ch0lux = 0x0000;
+ u32 ch1lux = 0x0000;
+
+ if (mutex_trylock(&als_mutex) == 0) {
+ printk(KERN_INFO "Can't get ALS mutex\n");
+ return als_cur_info.lux; /*busy, so return LAST VALUE*/
+ }
+
+ if (taos_chip_status != TAOS_CHIP_WORKING) {
+ /*device is not enabled*/
+ printk(KERN_INFO "device is not enabled\n");
+ mutex_unlock(&als_mutex);
+ return -1;
+ }
+
+ if ((taos_cycle_type & LIGHT) == 0) {
+ /*device not in ALS mode*/
+ printk(KERN_INFO "device not in ALS mode\n");
+ mutex_unlock(&als_mutex);
+ return -1;
+ }
+
+ ret = taos_i2c_read((CMD_REG), &buf[0], 1);
+ if (ret < 0) {
+ printk(KERN_INFO "taos_i2c_read() to CMD_REG reg failed in "
+ "taos_get_lux()\n");
+ mutex_unlock(&als_mutex);
+ return -1;
+ }
+ /* is data new & valid */
+ if (!(buf[0] & STA_ADCINTR)) {
+ printk(KERN_INFO "Data not valid, so return LAST VALUE\n");
+ mutex_unlock(&als_mutex);
+ return als_cur_info.lux; /*have no data, so return LAST VALUE*/
+ }
+
+ for (i = 0; i < 4; i++) {
+ ret = taos_i2c_read((CMD_REG | (ALS_CHAN0LO + i)), &buf[i], 1);
+ if (ret < 0) {
+ dev_err(devp,
+ "taos_i2c_read() to (ALS_CHAN0LO + i) failed in "
+ "taos_get_lux()\n");
+ mutex_unlock(&als_mutex);
+ return -1;
+ }
+ }
+
+ /* clear status, really interrupt status (interrupts are off), but
+ * we use the bit anyway */
+ ret = taos_i2c_write_command(CMD_REG | CMD_SPL_FN | CMD_ALS_INTCLR);
+ if (ret < 0) {
+ dev_err(devp,
+ "taos_i2c_write_command failed in "
+ "taos_chip_on, err = %d\n", ret);
+ mutex_unlock(&als_mutex);
+ return -1; /*have no data, so return fail*/
+ }
+
+ /* extract ALS/lux data */
+ ch0 = (buf[1] << 8) | buf[0];
+ ch1 = (buf[3] << 8) | buf[2];
+
+ als_cur_info.als_ch0 = ch0;
+ als_cur_info.als_ch1 = ch1;
+
+
+#ifdef TAOS_DEBUG
+ printk(KERN_INFO " ch0=%d/0x%x ch1=%d/0x%x\n", ch0, ch0, ch1, ch1);
+#endif
+
+ if ((ch0 >= als_saturation) || (ch1 >= als_saturation)) {
+return_max:
+ als_cur_info.lux = MAX_LUX;
+ mutex_unlock(&als_mutex);
+ return als_cur_info.lux;
+ }
+
+ if (ch0 == 0) {
+ als_cur_info.lux = 0;
+#ifdef TAOS_DEBUG
+ printk(KERN_INFO "ch0==0\n");
+#endif
+ mutex_unlock(&als_mutex);
+
+ return als_cur_info.lux;
+ /*have no data, so return LAST VALUE*/
+ }
+ /* calculate ratio */
+ ratio = (ch1 << 15) / ch0;
+ /* convert to unscaled lux using the pointer to the table */
+ for (p = (struct taos_lux *) taos_device_lux;
+ p->ratio != 0 && p->ratio < ratio; p++)
+ ;
+
+ if (p->ratio == 0) {
+#ifdef TAOS_DEBUG
+ dev_info(devp, "ratio = %04x p->ratio = %04x\n",
+ ratio, p->ratio);
+#endif
+ lux = 0;
+ } else {
+ ch0lux = ((ch0 * p->ch0) +
+ (gainadj[taos_settings.als_gain].ch0 >> 1))
+ / gainadj[taos_settings.als_gain].ch0;
+ ch1lux = ((ch1 * p->ch1) +
+ (gainadj[taos_settings.als_gain].ch1 >> 1))
+ / gainadj[taos_settings.als_gain].ch1;
+ lux = ch0lux - ch1lux;
+ }
+
+ /* note: lux is 31 bit max at this point */
+ if (ch1lux > ch0lux) {
+ printk(KERN_INFO "No Data - Return last value\n");
+ als_cur_info.lux = 0;
+ mutex_unlock(&als_mutex);
+ return als_cur_info.lux; /*have no data, so return LAST VALUE*/
+ }
+ /* adjust for active time scale */
+ lux = (lux + (als_time_scale >> 1)) / als_time_scale;
+ /* adjust for active gain scale */
+ lux >>= 13; /* tables have factor of 8192 builtin for accuracy */
+
+ lux = (lux * taos_settings.als_gain_trim + 500) / 1000;
+
+
+#ifdef TAOS_DEBUG
+ printk(KERN_INFO "lux=%d\n", lux);
+#endif
+ if (lux > MAX_LUX) /* check for overflow */
+ goto return_max;
+
+ als_cur_info.lux = lux;
+ /*Update the structure with the latest VALID lux.*/
+
+ mutex_unlock(&als_mutex);
+ return lux;
+}
+EXPORT_SYMBOL(taos_get_lux);
+
+/**
+ * Obtain single reading and calculate the als_gain_trim (later used to
derive actual lux)
+ *\param ulong als_cal_target ALS target (real world)
+ *\return int updated gain_trim value
+ */
+int taos_als_calibrate(unsigned long als_cal_target)
+{
+ u8 reg_val;
+ unsigned int gain_trim_val;
+ int ret;
+ int lux_val;
+
+
+ /*This may seem redundant but..
+ We need this next line in case we call this function from an
+ external application who has a different target value than
+ our defaults.
+ In which case, make 'that' our new default/settings.*/
+ taos_settings.als_cal_target = als_cal_target;
+
+ ret = i2c_smbus_write_byte(chip->client, (CMD_REG | CNTRL));
+ if (ret < 0) {
+ dev_err(devp,
+ "i2c_smbus_write_byte to cmd reg failed in "
+ "ioctl als_calibrate\n");
+ return ret;
+ }
+ reg_val = i2c_smbus_read_byte(chip->client);
+
+ if ((reg_val & (CNTL_ADC_ENBL | CNTL_PWRON))
+ != (CNTL_ADC_ENBL | CNTL_PWRON)) {
+ dev_err(devp,
+ "reg_val & (CNTL_ADC_ENBL | CNTL_PWRON)) "
+ "!= (CNTL_ADC_ENBL | CNTL_PWRON) in ioctl als_calibrate\n");
+ return -ENODATA;
+ }
+
+ ret = i2c_smbus_write_byte(chip->client, (CMD_REG | STATUS));
+ if (ret < 0) {
+ dev_err(devp,
+ "i2c_smbus_write_byte to cmd reg failed in "
+ "ioctl als_calibrate\n");
+ return ret;
+ }
+ reg_val = i2c_smbus_read_byte(chip->client);
+
+ if ((reg_val & STA_ADCVALID) != STA_ADCVALID) {
+ dev_err(devp,
+ "(reg_val & STA_ADCVALID) != STA_ADCVALID in "
+ "ioctl als_calibrate\n");
+ return -ENODATA;
+ }
+ lux_val = taos_get_lux();
+ if (lux_val < 0) {
+ dev_err(devp,
+ "taos_get_lux() returned bad value %d in "
+ "ioctl als_calibrate\n",
+ lux_val);
+ return lux_val;
+ }
+ gain_trim_val = (unsigned int) (((taos_settings.als_cal_target)
+ * taos_settings.als_gain_trim) / lux_val);
+
+ dev_info(devp, "\n\ntaos_settings.als_cal_target = %d\n"
+ "taos_settings.als_gain_trim = %d\nlux_val = %d\n",
+ taos_settings.als_cal_target,
+ taos_settings.als_gain_trim,
+ lux_val);
+
+ if ((gain_trim_val < 250) || (gain_trim_val > 4000)) {
+ dev_err(devp,
+ "ALS calibrate result %d out of range "
+ "in ioctl als_calibrate\n",
+ gain_trim_val);
+ return -ENODATA;
+ }
+ taos_settings.als_gain_trim = (int) gain_trim_val;
+
+ return (int) gain_trim_val;
+}
+EXPORT_SYMBOL(taos_als_calibrate);
+
+/*........................................................................*/
+/*@} End of ALS section*/
+
+/*===========================================================================*/
+/**@defgroup DEV_CTRL Device Control Functions
+ * @{
+ */
+
+/*........................................................................*/
+/**
+ * Turn the device on.
+ * Configuration and taos_cycle_type must be set before calling this
function.
+ * \param none.
+ * \return int 0 = success, < 0 = failure
+ */
+static int taos_chip_on()
+{
+ int i;
+ int ret = 0;
+ u8 *uP;
+ u8 utmp;
+ int als_count;
+ int als_time;
+
+
+ /*make sure taos_cycle_type is set.*/
+ if ((taos_cycle_type != LIGHT))
+ return -1;
+
+ /*and make sure we're not already on*/
+ if (taos_chip_status == TAOS_CHIP_WORKING) {
+ /*if forcing a register update - turn off, then on*/
+ printk(KERN_INFO "device is already enabled\n");
+ return -1;
+ }
+
+ /* . . . . . . . . SHADOW REGISTER
INITIALIZATION . . . . . . . . . . .
+ Note:
+ If interrupts are enabled, keeping persist at 0 should prime the
pump.
+ So don't set persist in shadow register. Just enable if required.*/
+ taos_config[INTERRUPT] = (taos_settings.interrupts_enabled & 0xF0);
+
+
+ /*determine als integration regster*/
+ als_count = (taos_settings.als_time * 100 + 135) / 270;
+ if (als_count == 0)
+ als_count = 1; /*ensure at least one cycle*/
+
+
+ /*convert back to time (encompasses overrides)*/
+ if (taos_cycle_type & LIGHT) {
+ als_time = (als_count * 27 + 5) / 10;
+ taos_config[ALS_TIME] = 256 - als_count;
+ } else
+ taos_config[ALS_TIME] = 0xff;
+
+
+ /*Set the gain based on taos_settings struct*/
+ taos_config[GAIN] = taos_settings.als_gain;
+
+
+ /*set globals re scaling and saturation*/
+ als_saturation = als_count * 922; /*90% of full scale*/
+ als_time_scale = (als_time + 25) / 50;
+
+
+ /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+ SKATE Specific power-on / adc enable sequence
+ Power on the device 1st.*/
+ utmp = CNTL_PWRON;
+ ret = taos_i2c_write(CMD_REG | CNTRL, &utmp);
+ if (ret < 0) {
+ dev_err(devp,
+ "taos_i2c_write to turn on device failed in "
+ "taos_chip_on.\n");
+ return -1;
+ }
+
+ /*User the following shadow copy for our delay before enabling ADC
+ Write ALL THE REGISTERS*/
+ for (i = 0, uP = taos_config; i < TAOS_REG_MAX; i++) {
+ ret = taos_i2c_write(CMD_REG + i, uP++);
+ if (ret < 0) {
+ dev_err(devp,
+ "taos_i2c_write to reg %d failed in "
+ "taos_chip_on.\n", i);
+ return -1;
+ }
+ }
+
+ mdelay(3);
+ /*NOW enable the ADC
+ initialize the desired mode of operation*/
+ utmp = CNTL_PWRON | CNTL_ADC_ENBL;
+ ret = taos_i2c_write(CMD_REG | CNTRL, &utmp);
+ if (ret < 0) {
+ dev_err(devp,
+ "taos_i2c_write to turn on device failed in "
+ "taos_chip_on.\n");
+ return -1;
+ }
+ taos_chip_status = TAOS_CHIP_WORKING;
+
+#ifdef TAOS_DEBUG
+ dev_info(devp, "chip_on() called\n\n");
+#endif
+
+ return ret; /*returns result of last i2cwrite*/
+}
+EXPORT_SYMBOL(taos_chip_on);
+
+/*........................................................................*/
+/**
+ * Turn the device OFF.
+ * \param none.
+ * \return int 0 = success, < 0 = failure
+ */
+static int taos_chip_off()
+{
+ int ret;
+ u8 utmp;
+
+
+ /*turn device off*/
+ taos_chip_status = TAOS_CHIP_SLEEP;
+ utmp = 0x00;
+ ret = taos_i2c_write(CMD_REG | CNTRL, &utmp);
+ als_on = FALSE;
+ /*init_done = 0x00; *//*used by ALS irq as one/first shot*/
+#ifdef TAOS_DEBUG
+ printk(KERN_INFO "chip_off() called\n");
+#endif
+
+ return ret;
+
+}
+EXPORT_SYMBOL(taos_chip_off);
+/*........................................................................*/
+/*@} End of Device Control Section*/
+
+/*===========================================================================*/
+
+/*===========================================================================*/
+/**@defgroup I2C-HAL I2C/SMBus Communication Functions
+ * The sections pertains to the driver/device communication functions\n
+ * facilitated via SMBus in an I2C manner.\n
+ * These functions represent several layers of abstraction.
+ * Also note, some i2c controllers do not support block read/write.
+ * @{
+ */
+
+/**
+ * Read a number of bytes starting at register (reg) location.
+ *
+ * \param unsigned char reg - device register
+ * \param unsigned char *val - location to store results
+ * \param unsigned int number of bytes to read
+ * \return TAOS_SUCCESS, or i2c_smbus_write_byte ERROR code
+ *
+ */
+static int taos_i2c_read(u8 reg, u8 *val, unsigned int len)
+{
+ int result;
+ int i;
+
+ if (len > MAXI2C)
+ len = MAXI2C;
+ for (i = 0; i < len; i++) {
+ /* select register to write */
+ if (reg >= 0) {
+ result = i2c_smbus_write_byte(chip->client,
+ (CMD_REG | (reg + i)));
+
+ if (result < 0) {
+ dev_err(devp,
+ "FAILED: i2c_smbus_write_byte_data in "
+ "taos_io_read()\n");
+ return result;
+ }
+ /* read the data */
+ *val = i2c_smbus_read_byte(chip->client);
+#ifdef TAOS_DEBUG_SMBUS
+ dev_info(devp,
+ "READ FROM REGISTER [%02X] -->%02x<--\n",
+ (CMD_REG | (reg + i)) , *data);
+#endif
+ val++;
+ }
+ }
+ return TAOS_SUCCESS;
+}
+
+/**
+ * This function is used to send a an register address followed by a
data value
+ * When calling this function remember to set the register MSBit
+ * (if applicable).
+ * \param unsigned char register
+ * \param unsigned char * pointer to data value(s) to be written
+ * \return TAOS_SUCCESS, or i2c_smbus_write_byte error code.
+ */
+static int taos_i2c_write(u8 reg, u8 *val)
+{
+ int retval;
+ retval = i2c_smbus_write_byte_data(chip->client, reg, (*val));
+ if (retval < 0) {
+ dev_err(devp, "FAILED: i2c_smbus_write_byte_data\n");
+ return retval;
+ }
+
+ return TAOS_SUCCESS;
+}
+
+/**
+ * This function is used to send a command to device command/control
register
+ * All bytes sent using this command have their MSBit set - it's a
command!
+ * \param unsigned char register to write
+ * \return TAOS_SUCCESS, or i2c_smbus_write_byte error code.
+ */
+static int taos_i2c_write_command(u8 reg)
+{
+ int result;
+ u8 *p;
+ u8 buf;
+
+ p = &buf;
+ *p = reg |= CMD_REG;
+ /*write the data*/
+ result = i2c_smbus_write_byte(chip->client, (*p));
+ if (result < 0) {
+ dev_err(devp, "FAILED: i2c_smbus_write_byte\n");
+ return result;
+ }
+ return TAOS_SUCCESS;
+}
+
+/*........................................................................*/
+/*@} //end of I2C-HAL section*/
+
+
+/**@defgroup group6 Sysfs Interface Functions
+ * The following functions are for access via the sysfs style
interface.\n
+ * Use 'cat' to read, 'echo >' to write\n
+ * @{
+ */
+
+/*........................................................................*/
+/*sysfs interface functions begin here*/
+
+static ssize_t taos_device_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s %s\n", DEVICE_ID, DRIVER_VERSION_ID);
+}
+
+static ssize_t taos_power_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", taos_chip_status);
+ return 0;
+}
+
+static ssize_t taos_power_state_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ unsigned long value;
+
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value == 1)
+ taos_chip_on();
+ else
+ taos_chip_off();
+
+ return len;
+}
+
+static ssize_t taos_gain_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", taos_settings.als_gain);
+ return 0;
+}
+
+static ssize_t taos_gain_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ unsigned long value;
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+ if (value) {
+ if (value > 4) {
+ printk(KERN_INFO "Invalid Gain Index\n");
+ return -1;
+ } else {
+ taos_settings.als_gain = value;
+ }
+ }
+ return len;
+}
+
+static ssize_t taos_interrupt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", taos_settings.interrupts_enabled);
+ return 0;
+}
+
+static ssize_t taos_interrupt_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ unsigned long value;
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value) {
+ if (value > 1) {
+ printk(KERN_INFO "Invalid: 0 or 1\n");
+ return -1;
+ } else {
+ taos_settings.interrupts_enabled = CNTL_ALS_INT_ENBL;
+ }
+ }
+ return len;
+}
+
+static ssize_t taos_als_time_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", taos_settings.als_time);
+ return 0;
+}
+
+static ssize_t taos_als_time_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ unsigned long value;
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value)
+ taos_settings.als_time = value;
+
+ return len;
+}
+
+static ssize_t taos_als_trim_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", taos_settings.als_gain_trim);
+ return 0;
+}
+
+static ssize_t taos_als_trim_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ unsigned long value;
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value)
+ taos_settings.als_gain_trim = value;
+
+ return len;
+}
+
+static ssize_t taos_als_persistence_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", taos_settings.als_persistence);
+ return 0;
+}
+
+static ssize_t taos_als_persistence_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ unsigned long value;
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value)
+ taos_settings.als_persistence = value;
+
+ return len;
+}
+
+static ssize_t taos_als_cal_target_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", taos_settings.als_cal_target);
+ return 0;
+}
+
+static ssize_t taos_als_cal_target_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ unsigned long value;
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value)
+ taos_settings.als_cal_target = value;
+
+ return len;
+}
+
+static ssize_t taos_lux_show(struct device *dev, struct
device_attribute *attr,
+ char *buf)
+{
+ int lux = 0;
+ if ((taos_settings.interrupts_enabled & CNTL_ALS_INT_ENBL) == 0x00)
+ lux = taos_get_lux(); /*get fresh data*/
+ else
+ lux = als_cur_info.lux; /*data is fresh as last change*/
+
+ return sprintf(buf, "%d\n", lux);
+ return 0;
+}
+
+static ssize_t taos_do_calibrate(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ unsigned long value;
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value == 1)
+ taos_als_calibrate(taos_settings.als_cal_target);
+
+ return len;
+}
+
+static ssize_t taos_als_thresh_low_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", taos_settings.als_thresh_low);
+ return 0;
+}
+
+static ssize_t taos_als_thresh_low_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ unsigned long value;
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value)
+ taos_settings.als_thresh_low = value;
+
+ return len;
+}
+
+static ssize_t taos_als_thresh_high_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", taos_settings.als_thresh_high);
+ return 0;
+}
+
+static ssize_t taos_als_thresh_high_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ unsigned long value;
+ if (strict_strtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value)
+ taos_settings.als_thresh_high = value;
+
+ return len;
+}
+
+/*@} End of Sysfs Interface Functions - gp6*/
+/*===========================================================================*/
+
+static void taos_als_auto_update(struct work_struct *update)
+{
+ if (taos_chip_status != TAOS_CHIP_WORKING) {
+ taos_chip_on();
+ mdelay(100);
+ }
+ taos_get_lux();
+ input_report_abs(taos_dev, ABS_X, als_cur_info.lux);
+ input_sync(taos_dev);
+
+ queue_delayed_work(taos_wq0, luxUd, LUX_UPDATE_PERIOD);
+
+}
+
+
+
+MODULE_AUTHOR("J. August Brenner<[email protected]>");
+MODULE_DESCRIPTION("TAOS 258x ambient light sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(taos_init);
+module_exit(taos_exit);
+ ���{.n�+�������+%�����ݶ��w��{.n�+����{��G�����{ay�ʇڙ�,j��f���h��������z_�(�階�ݢj"���m������G������?����&���~��iO��z��v�^�m���� ������?�I�

#1 lore raw

Lots of cc's added. Note that a driver like this that touches a number of subsystems needs to also go to their individual mailing lists as not everyone monitors lkml very closely.

Quoted 2,159 lines

From: J. August Brenner <[email protected]>

This driver supports the TAOS tsl258x family of ALS sensors. This driver provides ALS data (lux), and device configuration via ioctl, input_event, sysfs/iio methods. These methods are provided to support varied access requirements and integrated into a single driver. Thus, adverting the need for multiple releases of similar code. More significantly, this driver provides the capability to be fed 'glass coefficients' to accommodate varying system designs (bezels, faceplates, etc.).

---
diff -Naurp drivers-orig/Kconfig drivers/Kconfig

Kconfig +7 -0
@@ -13,6 +13,13 @@ config SENSORS_TSL2563
 	 This driver can also be built as a module.  If so, the module
 	 will be called tsl2563.
 
+config TAOS_258x
+	tristate "TAOS TSL258x device driver"
+	default m
+	help
+	  Provides support for the TAOS tsl258x family of devices.
+	  Access ALS data via iio, sysfs, input_event, and ioctl methods.
+
 config SENSORS_ISL29018
         tristate "ISL 29018 light and proximity sensor"
         depends on I2C

diff -Naurp drivers-orig/Makefile drivers/Makefile

Makefile +1 -0
@@ -3,4 +3,5 @@
 #
 
 obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o
+obj-$(CONFIG_TAOS_258x)	+= tsl258x.o
 obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o

diff -Naurp drivers-orig/tsl258x.c drivers/tsl258x.c

tsl258x.c +12 -0
@@ -0,0 +1,2055 @@
+/*
+ * Device driver for monitoring ambient light intensity (lux)
+ * within the TAOS tsl258x family of devices
+ *
+ * Copyright (c) 2011, 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.
+ */

tsl258x.c +211 -0
+
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/wait.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/firmware.h>
+#include <linux/string.h>
+#include <linux/kmod.h>
+#include <linux/ctype.h>
+#include <linux/input.h>
+#include <linux/time.h>
+#include <linux/jiffies.h>
+#include "../iio.h"
+
+/*........................................................................*/
+#define DRIVER_VERSION_ID	"3.9"
+
+#define TAOS258x		2
+
+/* This build will be for..*/
+#define DRIVER_TYPE		TAOS258x
+
+/*........................................................................*/
+/*Debug related*/
+#define TAOS_DEBUG			1
+/*#define TAOS_DEBUG_SMBUS		1	*/
+#define TAOS_IRQ_DEBUG			1
+/*........................................................................*/
+/*User notification method*/
+/*#define ASYNC_NOTIFY			1	*/
+#define EVENT_NOTIFY			1
+/*........................................................................*/
+/*Module defines*/
+/* Device name/id/address */
+#define DEVICE_NAME			"taos"
+#ifdef TAOS258x
+#define DEVICE_ID			"skateFN"
+#else
+#define DEVICE_ID			"tritonFN"
+#endif
+
+#define ID_NAME_SIZE			10
+#define DEVICE_ADDR1			0x29
+#define DEVICE_ADDR2			0x39
+#define DEVICE_ADDR3			0x49
+#define MAX_NUM_DEVICES			3
+#define MAX_DEVICE_REGS			32
+#define I2C_MAX_ADAPTERS		8
+
+/* Triton register offsets */
+#ifdef TAOS258x
+#define	TAOS_REG_MAX	8
+#else
+#define	TAOS_REG_MAX	16
+#endif
+
+/* Power management */
+#define taos_suspend			NULL
+#define taos_shutdown			NULL
+#define taos_resume			NULL
+
+#define TRUE				1
+#define FALSE				0
+
+#define	MAXI2C	32
+#define	CMD_ADDRESS	0x80
+#define	TAOS_ERROR	-1
+#define TAOS_SUCCESS	0
+
+#define	MAX_SAMPLES_CAL	200
+
+#define LUX_UPDATE_PERIOD	(HZ/2)
+
+/* power control */
+#define ON              1
+#define OFF		0
+
+
+/* sensor type */
+#define LIGHT		1
+#define PROXIMITY	2
+#define ALL			3
+
+struct taos_als_info {
+	u16 als_ch0;
+	u16 als_ch1;
+	u16 lux;
+};
+
+struct taos_settings {
+	int	als_time;
+	int	als_gain;
+	int	als_gain_trim;
+	int	als_cal_target;
+	int	interrupts_enabled;
+	int als_thresh_low;
+	int als_thresh_high;
+	char als_persistence;
+};
+
+/* ioctl numbers */
+#define TAOS_IOCTL_MAGIC		0XCF
+#define TAOS_IOCTL_ALS_ON		_IO(TAOS_IOCTL_MAGIC,  1)
+#define TAOS_IOCTL_ALS_OFF		_IO(TAOS_IOCTL_MAGIC,  2)
+#define TAOS_IOCTL_ALS_DATA		_IOR(TAOS_IOCTL_MAGIC, 3, short)
+#define TAOS_IOCTL_ALS_CALIBRATE	_IO(TAOS_IOCTL_MAGIC,  4)
+#define TAOS_IOCTL_CONFIG_GET		_IOR(TAOS_IOCTL_MAGIC, 5,\
+		struct taos_settings)
+#define TAOS_IOCTL_CONFIG_SET		_IOW(TAOS_IOCTL_MAGIC, 6, \
+		struct taos_settings)
+#define TAOS_IOCTL_LUX_TABLE_GET	_IOR(TAOS_IOCTL_MAGIC, 7, \
+		struct taos_lux)
+#define TAOS_IOCTL_LUX_TABLE_SET	_IOW(TAOS_IOCTL_MAGIC, 8, \
+		struct taos_lux)
+#define TAOS_IOCTL_ID			_IOR(TAOS_IOCTL_MAGIC, 12, char*)
+#define TAOS_IOCTL_SET_GAIN		_IOW(TAOS_IOCTL_MAGIC, 15, int)
+#define TAOS_IOCTL_GET_ALS		_IOR(TAOS_IOCTL_MAGIC, 16, \
+		struct taos_als_info)
+#define TAOS_IOCTL_INT_SET		_IOW(TAOS_IOCTL_MAGIC, 17, int)
+
+/*Device Registers and Masks*/
+#define CNTRL				0x00
+#define STATUS				0x00
+#define ALS_TIME			0X01
+#define INTERRUPT			0x02
+#define ALS_MINTHRESHLO		0X03
+#define ALS_MINTHRESHHI		0X04
+#define ALS_MAXTHRESHLO		0X05
+#define ALS_MAXTHRESHHI		0X06
+#define GAIN				0x07
+#define REVID				0x11
+#define CHIPID				0x12
+#define SMB_4				0x13
+#define ALS_CHAN0LO			0x14
+#define ALS_CHAN0HI			0x15
+#define ALS_CHAN1LO			0x16
+#define ALS_CHAN1HI			0x17
+#define TMR_LO				0x18
+#define TMR_HI				0x19
+
+/* Skate cmd reg masks */
+#define CMD_REG				0x80
+#define CMD_BYTE_RW			0x00
+#define CMD_WORD_BLK_RW		0x20
+#define CMD_SPL_FN			0x60
+#define CMD_ALS_INTCLR		0X01
+
+/* Skate cntrl reg masks */
+#define CNTL_REG_CLEAR		0x00
+#define CNTL_ALS_INT_ENBL	0x10
+#define CNTL_WAIT_TMR_ENBL	0x08
+#define CNTL_ADC_ENBL		0x02
+#define CNTL_PWRON			0x01
+#define CNTL_ALSPON_ENBL	0x03
+#define CNTL_INTALSPON_ENBL	0x13
+
+/* Skate status reg masks */
+#define STA_ADCVALID		0x01
+#define STA_ALSINTR			0x10
+#define STA_ADCINTR			0x10
+
+/* Lux constants */
+#define	MAX_LUX				65535
+
+/* Thresholds */
+#define ALS_THRESHOLD_LO_LIMIT		0x0010
+#define ALS_THRESHOLD_HI_LIMIT		0xFFFF
+
+/* Device default configuration */
+#define ALS_TIME_PARAM			100
+#define SCALE_FACTOR_PARAM		1
+#define GAIN_TRIM_PARAM			512
+#define GAIN_PARAM			1
+#define ALS_THRSH_HI_PARAM		0xFFFF
+#define ALS_THRSH_LO_PARAM		0
+
+
+/* Prototypes */
+static void taos_defaults(void);
+static int taos_chip_on(void);
+static int taos_chip_off(void);
+static int taos_get_lux(void);
+
+struct i2c_adapter *i2c_get_adapter(int id);
+
+static int taos_probe(struct i2c_client *clientp,
+    const struct i2c_device_id *idp);
+static int taos_remove(struct i2c_client *client);
+static int taos_open(struct inode *inode, struct file *file);
+static int taos_release(struct inode *inode, struct file *file);
+static int taos_ioctl(struct inode *inode, struct file *file, unsigned

int cmd,

tsl258x.c +2 -0
+    unsigned long arg);
+static int taos_read(struct file *file, char *buf, size_t count, loff_t

*ppos);

tsl258x.c +95 -0
+static int taos_write(struct file *file, const char *buf, size_t count,
+    loff_t *ppos);
+static loff_t taos_llseek(struct file *file, loff_t offset, int orig);
+static int taos_fasync(int fd, struct file *file, int mode);
+static int taos_device_name(unsigned char *bufp, char **device_name);
+static int taos_als_calibrate(unsigned long als_cal_target);
+static void taos_als_auto_update(struct work_struct *update);
+
+/*........................................................................*/
+/* Various /misc. emun*/
+enum {
+	TAOS_CHIP_UNKNOWN = 0, TAOS_CHIP_WORKING = 1, TAOS_CHIP_SLEEP = 2
+} TAOS_CHIP_WORKING_STATUS;
+
+/*........................................................................*/
+
+char driver_version_id[] = DRIVER_VERSION_ID;
+
+/*........................................................................*/
+
+/* First device number */
+static dev_t taos_dev_number;
+
+/* Class structure for this device */
+static struct class *taos_class;
+
+/* Module device table */
+static struct i2c_device_id taos_idtable[] = {
+	{ DEVICE_ID, 0 },
+	{ } };
+MODULE_DEVICE_TABLE(i2c, taos_idtable);
+
+/* Board and address info */
+static struct i2c_board_info taos_board_info[] = {
+	{ I2C_BOARD_INFO(DEVICE_ID, DEVICE_ADDR1), },
+	{ I2C_BOARD_INFO(DEVICE_ID, DEVICE_ADDR2), },
+	{ I2C_BOARD_INFO(DEVICE_ID, DEVICE_ADDR3), }, };
+
+static unsigned short const taos_addr_list[4] = {
+	DEVICE_ADDR1,
+	DEVICE_ADDR2,
+    DEVICE_ADDR3,
+    I2C_CLIENT_END };
+
+/* Client and device */
+static struct i2c_client *client;
+static struct device *devp;
+static struct i2c_client *bad_clientp[MAX_NUM_DEVICES];
+static int device_found;
+
+/* Driver definition */
+static struct i2c_driver taos_driver = {
+	.driver = {
+	.owner = THIS_MODULE,
+    .name = DEVICE_NAME, },
+    .id_table = taos_idtable,
+    .probe = taos_probe,
+    .remove = __devexit_p(taos_remove),
+    .suspend = taos_suspend,
+    .shutdown = taos_shutdown,
+    .resume = taos_resume, };
+
+/* Per-device data */
+static struct tsl258x_chip {
+	struct i2c_client *client;
+	struct iio_dev *iio_dev;
+	struct delayed_work update_lux;
+	struct work_struct work_thresh;
+	s64 event_timestamp;
+	struct cdev cdev;
+	struct fasync_struct *async_queue;
+	unsigned int addr;
+	char taos_id;
+	char taos_name[ID_NAME_SIZE];
+	char valid;
+	unsigned long last_updated;
+
+} *chip;
+
+/* File operations */
+static const struct file_operations taos_fops = {
+	.owner = THIS_MODULE,
+	.open =	taos_open,
+	.release = taos_release,
+	.read = taos_read,
+	.write = taos_write,
+	.llseek = taos_llseek,
+	.fasync = taos_fasync,
+	.ioctl = taos_ioctl, };
+
+/* ................ Als info ..................*/
+struct taos_als_info als_cur_info;
+EXPORT_SYMBOL(als_cur_info);
+
+/*Next two vars are also used to determine irq type - in lieu status

info*/

tsl258x.c +18 -0
+bool als_on = FALSE;
+
+static int device_released = 0x0000;
+static unsigned int irqnum = 0x0000;
+static DECLARE_WAIT_QUEUE_HEAD(taos_int_wait);
+
+/* Lux time scale */
+struct time_scale_factor {
+	unsigned int numerator;
+	unsigned int denominator;
+	unsigned int saturation;
+};
+
+/* ------ Mutex ------*/
+DEFINE_MUTEX(als_mutex);
+
+/*Structs & vars*/
+int taos_chip_status = TAOS_CHIP_UNKNOWN; /*Unknown = uninitialized to

start*/
+int taos_cycle_type; /* what is the type of cycle being run: ALS, PROX, or BOTH
+ */

tsl258x.c +6 -0
+struct taos_settings taos_settings;
+EXPORT_SYMBOL(taos_settings);
+
+/* Device configuration */
+#define MAX_SETTING_MEMBERS	6
+static struct taos_settings *taos_settingsP; /*Pointer Needed for

ioctl(s)*/

tsl258x.c +44 -0
+
+/*More Prototypes*/
+static int taos_i2c_read(u8 reg, u8 *val, unsigned int len);
+static int taos_i2c_write(u8 reg, u8 *val);
+static int taos_i2c_write_command(u8 reg);
+
+/*   Work Queues used for bottom halves of self poll/push & IRQs  */
+static struct workqueue_struct *taos_wq0;
+
+struct delayed_work *luxUd;
+/*........................................................................*/
+/*
+ Initial values for device - this values can/will be changed by driver.
+ and applications as needed.
+ These values are dynamic.
+ */
+u8 taos_config[8] = { 0x00, 0xee, 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00 };
+/*	                  cntrl atime intC  Athl0 Athl1 Athh0 Athh1 gain*/
+
+/*
+ This structure is intentionally large to accommodate updates via
+ ioctl and proc input methods.
+ */
+struct taos_lux {
+	unsigned int ratio;
+	unsigned int ch0;
+	unsigned int ch1;
+} taos_device_lux[] = {
+	{ 9830, 8520, 15729 },
+	{ 12452, 10807, 23344 },
+	{14746, 6383, 11705 },
+	{ 17695, 4063, 6554 },
+	{ 0, 0, 0 },
+	{ 0, 0, 0 },
+	{ 0, 0, 0 },
+	{ 0, 0, 0 },
+	{ 0, 0, 0 },
+	{ 0, 0, 0 },
+	{ 0, 0, 0 } };
+
+struct taos_lux taos_lux;
+EXPORT_SYMBOL(taos_lux);
+
+int als_time_scale; /*computed, ratios lux due to als integration

time*/
+int als_saturation; /*computed, set to 90% of full scale of als integration*/

tsl258x.c +79 -0
+
+/*Index = (0 - 3) Used to validate the gain selection index*/
+#define MAX_GAIN_STAGES	4
+struct gainadj {
+	s16 ch0;
+	s16 ch1;
+} gainadj[] = {
+	{ 1, 1 },
+	{ 8, 8 },
+	{ 16, 16 },
+	{ 107, 115 } };
+
+struct taos_lux *taos_device_luxP;
+
+/*Input_dev*/
+static struct input_dev *taos_dev;
+
+/*sysfs - interface functions*/
+static ssize_t taos_device_id(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(device_id, S_IRUGO, taos_device_id, NULL);
+
+static ssize_t taos_power_state_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_power_state_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(device_state, S_IRUGO | S_IWUSR,
+		taos_power_state_show,
+		taos_power_state_store);
+
+static ssize_t taos_gain_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_gain_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_gain, S_IRUGO | S_IWUSR,
+		taos_gain_show,
+		taos_gain_store);
+
+static ssize_t taos_interrupt_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_interrupt_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_interrupt, S_IRUGO | S_IWUSR,
+		taos_interrupt_show,
+		taos_interrupt_store);
+
+static ssize_t taos_als_time_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_als_time_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_time, S_IRUGO | S_IWUSR,
+		taos_als_time_show,
+		taos_als_time_store);
+
+static ssize_t taos_als_trim_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_als_trim_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_trim, S_IRUGO | S_IWUSR,
+		taos_als_trim_show,
+		taos_als_trim_store);
+
+static ssize_t taos_als_persistence_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_als_persistence_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_persistence, S_IRUGO | S_IWUSR,
+		taos_als_persistence_show,
+		taos_als_persistence_store);
+
+static ssize_t taos_als_cal_target_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_als_cal_target_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_target, S_IRUGO | S_IWUSR,
+		taos_als_cal_target_show,
+		taos_als_cal_target_store);
+
+static ssize_t taos_lux_show(struct device *dev, struct

device_attribute *attr,

tsl258x.c +54 -0
+    char *buf);
+static DEVICE_ATTR(lux, S_IRUGO, taos_lux_show, NULL);
+
+static ssize_t taos_do_calibrate(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, taos_do_calibrate);
+
+static ssize_t taos_als_thresh_low_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_als_thresh_low_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_lowT, S_IRUGO | S_IWUSR,
+		taos_als_thresh_low_show,
+		taos_als_thresh_low_store);
+
+static ssize_t taos_als_thresh_high_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_als_thresh_high_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_highT, S_IRUGO | S_IWUSR,
+	taos_als_thresh_high_show,
+	taos_als_thresh_high_store);
+
+
+static struct attribute *sysfs_attrs_ctrl[] = {
+	&dev_attr_device_id.attr,
+    &dev_attr_device_state.attr,
+    &dev_attr_als_gain.attr,
+    &dev_attr_als_interrupt.attr,
+    &dev_attr_als_time.attr,
+    &dev_attr_als_trim.attr,
+    &dev_attr_als_persistence.attr,
+    &dev_attr_als_target.attr,
+    &dev_attr_lux.attr,
+    &dev_attr_calibrate.attr,
+    &dev_attr_als_lowT.attr,
+    &dev_attr_als_highT.attr,
+    NULL };
+
+static struct attribute_group tsl258x_attribute_group[] = {
+	{ .attrs = sysfs_attrs_ctrl }, };
+
+
+/*===========================================================================*/
+/*
+
+ FUNCTIONS BEGIN HERE
+
+ */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/**@defgroup Initialization Driver Initialization
+ * The sections applies to functions for driver initialization,

instantiation,
+ * _exit, \n
+ * and user-land application invoking (ie. open).\n
+ * Also included in this section is the initial interrupt handler (handler
+ * bottom halves are in the respective
+ * sections).
+ * @{
+ */

tsl258x.c +4 -0
+
+/*........................................................................*/
+/** Driver initialization - device probe is initiated here, to identify
+ * a valid device if present on any of the i2c buses and at any

address.
+ * \param none
+ * \return: int (0 = OK)
+ * \note H/W Interrupt are device/product dependent.
+ * Attention is required to the definition and configuration.
+ */

tsl258x.c +127 -0
+/*........................................................................*/
+static int __init taos_init(void)
+{
+	u32 err;
+	int i, j, k, ret = 0;
+	struct i2c_adapter *my_adap;
+	static int num_bad = 0x0000;
+
+#ifdef TAOS_DEBUG
+	printk(KERN_ERR "Loading TAOS Driver\n");
+	printk(KERN_INFO "\nRequesting a GPIO for irq\n");
+#endif
+/*......................................................................*/
+	/*Create the work queue and allocate kernel memory for the
+	 * scheduled workers*/
+	taos_wq0 = create_workqueue("taos_work_queue");
+	if (taos_wq0)
+		luxUd = kmalloc(sizeof(struct delayed_work), GFP_KERNEL);
+
+	/*Make sure we have what we need */
+	if ((!taos_wq0) || (!luxUd))
+		return -ENOMEM;
+
+/*.....................................................................*/
+	ret = alloc_chrdev_region(&taos_dev_number, 0, MAX_NUM_DEVICES,
+		DEVICE_NAME);
+	if (ret < 0) {
+		dev_err(devp, "taos_device_drvr: alloc_chrdev_region()"
+			"failed in taos_init()\n");
+		return ret;
+	}
+	taos_class = class_create(THIS_MODULE, DEVICE_NAME);
+	chip = kmalloc(sizeof(struct tsl258x_chip), GFP_KERNEL);
+	if (!chip) {
+		dev_err(devp, "taos_device_drvr: kmalloc for "
+			"struct tsl258x_chip failed in taos_init()\n");
+		ret = -ENOMEM;
+		goto exit_3;
+	}
+	memset(chip, 0, sizeof(struct tsl258x_chip));
+	cdev_init(&chip->cdev, &taos_fops);
+	chip->cdev.owner = THIS_MODULE;
+
+	ret = cdev_add(&chip->cdev, taos_dev_number, 1);
+	if (ret < 0) {
+		dev_err(devp, "taos_device_drvr: cdev_add() failed in "
+				"taos_init()\n");
+		goto exit_2;
+	}
+	device_create(taos_class, NULL, MKDEV(MAJOR(taos_dev_number), 0),
+			&taos_driver , DEVICE_NAME);
+	ret = i2c_add_driver(&taos_driver);
+	if (ret < 0) {
+		dev_err(devp, "taos_device_drvr: i2c_add_driver() failed in "
+				"taos_init()\n");
+		goto exit_1;
+	}
+	device_found = 0;
+	for (i = 0; i < I2C_MAX_ADAPTERS; i++) {
+		my_adap = i2c_get_adapter(i);
+		if (my_adap == NULL)
+			break;
+		for (j = 0; j < MAX_NUM_DEVICES; j++) {
+			client = i2c_new_probed_device(my_adap,
+					&taos_board_info[j], taos_addr_list);
+			if ((client) && (!device_found)) {
+				bad_clientp[num_bad] = client;
+				num_bad++;
+			}
+			if (device_found)
+				break;
+		}
+		if (num_bad) {
+			for (k = 0; k < num_bad; k++)
+				i2c_unregister_device(bad_clientp[k]);
+			num_bad = 0;
+		}
+		if (device_found)
+			break;
+	}
+	if (device_found) {
+		devp = &client->dev;
+		goto exit_4;
+	} else {
+		if (client)
+			i2c_unregister_device(client);
+		i2c_del_driver(&taos_driver);
+		device_destroy(taos_class, MKDEV(MAJOR(taos_dev_number), 0));
+		cdev_del(&chip->cdev);
+		kfree(chip);
+		class_destroy(taos_class);
+		unregister_chrdev_region(taos_dev_number, MAX_NUM_DEVICES);
+		printk(KERN_ERR "taos_device_drvr: taos_init() found no device\n");
+		return -ENODEV;
+	}
+
+exit_1: device_destroy(taos_class, MKDEV(MAJOR(taos_dev_number), 0));
+exit_2: cdev_del(&chip->cdev);
+	kfree(chip);
+exit_3: class_destroy(taos_class);
+	unregister_chrdev_region(taos_dev_number, MAX_NUM_DEVICES);
+
+exit_4:
+
+	printk(KERN_INFO "\n\nallocating iio device\n");
+	chip->iio_dev = iio_allocate_device();
+	if (!chip->iio_dev)
+		printk(KERN_INFO "iio allocation failed\n");
+
+	chip->iio_dev->attrs =
+			(struct attribute_group *)&tsl258x_attribute_group;
+	chip->iio_dev->dev.parent = &client->dev;
+	chip->iio_dev->dev_data = (void *)(chip);
+	chip->iio_dev->driver_module = THIS_MODULE;
+	chip->iio_dev->modes = INDIO_DIRECT_MODE;
+	err = iio_device_register(chip->iio_dev);
+
+	if (err)
+		printk(KERN_INFO "iio registration failed\n");
+
+	/*Make sure the chip is off*/
+	taos_chip_off();
+	/*Load up the V2 defaults (these are hard coded defaults for now)*/
+	taos_defaults();
+	/*Set any other defaults that are needed to initialize
+	 * (such as taos_cycle_type).
+	 Note: these settings can/will be changed based on user

requirements.*/

tsl258x.c +7 -0
+	taos_cycle_type = LIGHT;
+	/*Initialize the work queues*/
+
+	INIT_DELAYED_WORK(luxUd, taos_als_auto_update);
+
+	/*...................................................................*/
+	/*This section populates the input structure and registers the

device*/

tsl258x.c +55 -0
+#ifdef EVENT_NOTIFY
+	taos_dev = input_allocate_device();
+	if (!taos_dev) {
+		printk(KERN_ERR "__init: Not enough memory for input_dev\n");
+		return ret;
+	}
+
+	/*Since we now have the struct, populate.*/
+	taos_dev->name = DEVICE_ID;
+	taos_dev->id.bustype = BUS_I2C;
+	taos_dev->id.vendor = 0x0089;
+	taos_dev->id.product = 0x2581;
+	taos_dev->id.version = 3;
+	taos_dev->phys = "/dev/taos";
+
+	/*This device one value (LUX)
+	 Other devices may have LUX and PROX.
+	 Thus we use the ABS_X so ABS_Y is logically available.*/
+	set_bit(EV_ABS, taos_dev->evbit);
+	set_bit(ABS_X, taos_dev->absbit);
+
+	/*Only returns only positive LUX values
+	 No noise filter, no flat level*/
+	taos_dev->absmin[ABS_X] = 0;
+	taos_dev->absmax[ABS_X] = MAX_LUX;
+	taos_dev->absfuzz[ABS_X] = 0;
+	taos_dev->absflat[ABS_X] = 0;
+
+	/*And register..*/
+	ret = input_register_device(taos_dev);
+	if (ret) {
+		printk(KERN_ERR "button.c: Failed to register input_dev\n");
+		goto err_free_dev;
+	}
+#endif	/*End of input_event*/
+
+
+	/*invoke sysfs
+	 invoke iio subsystem*/
+
+	err = sysfs_create_group(&chip->client->dev.kobj,
+			tsl258x_attribute_group);
+	if (err < 0) {
+		dev_err(&chip->client->dev, "Sysfs registration failed\n");
+		goto err_free_dev;
+	}
+
+	if (taos_wq0)
+		ret = queue_delayed_work(taos_wq0, luxUd, LUX_UPDATE_PERIOD);
+	else
+		printk(KERN_INFO "\nNO taos_wq0 for luxUd\n\n");
+
+	return ret; /*Normal return*/
+
+/*In the event we had a problem REGISTERING the device for

input_events*/

tsl258x.c +32 -0
+err_free_dev:
+	input_free_device(taos_dev);
+	return ret;
+}
+
+/**
+ * Driver exit
+ */
+static void __exit taos_exit(void)
+{
+	printk(KERN_INFO "TAOS driver __exit\n");
+
+	if (taos_wq0) {
+		flush_workqueue(taos_wq0);
+		destroy_workqueue(taos_wq0);
+	}
+
+	input_free_device(taos_dev);
+
+	if (client)
+		i2c_unregister_device(client);
+	i2c_del_driver(&taos_driver);
+	device_destroy(taos_class, MKDEV(MAJOR(taos_dev_number), 0));
+	cdev_del(&chip->cdev);
+	kfree(chip);
+	class_destroy(taos_class);
+	unregister_chrdev_region(taos_dev_number, MAX_NUM_DEVICES);
+
+}
+
+/**
+ * Client probe function - When a valid device is found, the driver's

device
+ * data structure is updated, and initialization completes successfully.
+ */

tsl258x.c +91 -0
+static int taos_probe(struct i2c_client *clientp,
+    const struct i2c_device_id *idp) {
+	int i, ret = 0;
+	unsigned char buf[MAX_DEVICE_REGS];
+	char *device_name;
+
+	if (device_found)
+		return -ENODEV;
+	if (!i2c_check_functionality(clientp->adapter,
+		I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&clientp->dev,
+			"taos_probe() - i2c smbus byte data "
+			"functions unsupported\n");
+		return -EOPNOTSUPP;
+	}
+	if (!i2c_check_functionality(clientp->adapter,
+		I2C_FUNC_SMBUS_WORD_DATA)) {
+		dev_warn(&clientp->dev,
+			"taos_probe() - i2c smbus word data "
+			"functions unsupported\n");
+	}
+	if (!i2c_check_functionality(clientp->adapter,
+		I2C_FUNC_SMBUS_BLOCK_DATA)) {
+		dev_err(&clientp->dev,
+			"taos_probe() - i2c smbus block data "
+			"functions unsupported\n");
+	}
+	chip->client = clientp;
+	i2c_set_clientdata(clientp, chip);
+	for (i = 0; i < MAX_DEVICE_REGS; i++) {
+		ret = i2c_smbus_write_byte(clientp, (CMD_REG | (CNTRL + i)));
+		if (ret < 0) {
+			dev_err(&clientp->dev, "i2c_smbus_write_byte() to cmd "
+				"reg failed in taos_probe(), err = %d\n", ret);
+			return ret;
+		}
+		buf[i] = i2c_smbus_read_byte(clientp);
+	}
+	ret = taos_device_name(buf, &device_name);
+	if (ret < 0) {
+		dev_info(&clientp->dev, "i2c device found - does not match "
+			"expected id in taos_probe() 1\n");
+	}
+	if (strcmp(device_name, DEVICE_ID)) {
+		dev_info(&clientp->dev, "i2c device found - does not match "
+			"expected id in taos_probe()2\n");
+	} else {
+		dev_info(&clientp->dev, "i2c device found - matches expected "
+			"id of %s in taos_probe()\n", device_name);
+		device_found = 1;
+	}
+	ret = i2c_smbus_write_byte(clientp, (CMD_REG | CNTRL));
+	if (ret < 0) {
+		dev_err(&clientp->dev, "i2c_smbus_write_byte() to cmd reg "
+			"failed in taos_probe(), err = %d\n", ret);
+		return ret;
+	}
+	strlcpy(clientp->name, DEVICE_ID, I2C_NAME_SIZE);
+	strlcpy(chip->taos_name, DEVICE_ID, ID_NAME_SIZE);
+	chip->valid = 0;
+
+	taos_settingsP = kmalloc(sizeof(struct taos_settings), GFP_KERNEL);
+	if (!(taos_settingsP)) {
+		dev_err(&clientp->dev,
+		"kmalloc for struct taos_settings failed in taos_probe()\n");
+		return -ENOMEM;
+	}
+
+	return ret;
+}
+
+/**
+ * Client remove
+ */
+static int __devexit taos_remove(struct i2c_client *client)
+{
+	return 0;
+}
+
+/**
+ * Device open function
+ */
+static int taos_open(struct inode *inode, struct file *file)
+{
+	int ret = 0;
+	struct tsl258x_chip *chip;
+
+
+	/*For debug - Make it ez to c on the console*/
+#ifdef TAOS_DEBUG
+	printk(KERN_INFO "***************************************************

\n");

tsl258x.c +1 -0
+	printk(KERN_INFO "*                                                 *

\n");

tsl258x.c +1 -0
+	printk(KERN_INFO "*                  TAOS DEVICE                    *

\n");

tsl258x.c +1 -0
+	printk(KERN_INFO "*                    DRIVER                       *

\n");

tsl258x.c +1 -0
+	printk(KERN_INFO "*                     OPEN                        *

\n");

tsl258x.c +1 -0
+	printk(KERN_INFO "***************************************************

\n");

tsl258x.c +35 -0
+#endif
+
+	chip = container_of(inode->i_cdev, struct tsl258x_chip, cdev);
+	if (strcmp(chip->taos_name, DEVICE_ID) != 0) {
+		dev_err(devp, "device name error in taos_open(), shows %s\n",
+		    chip->taos_name);
+		return -ENODEV;
+	}
+
+	device_released = 0;
+	return ret;
+}
+
+/**
+ * Device release
+ */
+static int taos_release(struct inode *inode, struct file *file)
+{
+	int ret = 0;
+	struct tsl258x_chip *chip;
+
+	device_released = 1;
+	chip = container_of(inode->i_cdev, struct tsl258x_chip, cdev);
+	if (strcmp(chip->taos_name, DEVICE_ID) != 0) {
+		dev_err(devp, "device id incorrect in taos_release(), shows "
+			"%s\n", chip->taos_name);
+		ret = -ENODEV;
+	}
+	taos_fasync(-1, file, 0);
+	free_irq(irqnum, chip);
+	return ret;
+}
+
+/**
+ * Name verification - uses default register values to identify the

Taos device
+ */

tsl258x.c +16 -0
+static int taos_device_name(unsigned char *bufp, char **device_name)
+{
+	if (((bufp[0x12] & 0xf0) == 0x00) || (bufp[0x12] == 0x08)) {
+		*device_name = "tritonFN";
+		return 1;
+	} else if (((bufp[0x12] & 0xf0) == 0x90)) {
+		*device_name = "skateFN";
+		return 1;
+	}
+	*device_name = "unknown";
+	printk(KERN_INFO "Identified %s\n", *device_name);
+	return 0;
+}
+
+/**
+ * Device read - reads are permitted only within the range of the

accessible
+ * registers of the device. The data read is copied safely to user space.
+ */
+static int taos_read(struct file *file, char *buf, size_t count, loff_t *ppos)

tsl258x.c +47 -0
+{
+	int ret = 0;
+	struct tsl258x_chip *chip;
+	u8 my_buf[MAX_DEVICE_REGS];
+	u8 reg, i = 0, xfrd = 0;
+
+
+	/*Make sure we not in the middle of accessing the device*/
+	if (mutex_trylock(&als_mutex) == 0) {
+		printk(KERN_INFO "Can't get ALS mutex\n");
+		return -1; /*busy, so return LAST VALUE*/
+	}
+
+	if ((*ppos < 0) || (*ppos >= MAX_DEVICE_REGS) || ((*ppos + count)
+			> MAX_DEVICE_REGS)) {
+		dev_err(devp, "reg limit check failed in taos_read()\n");
+		mutex_unlock(&als_mutex);
+		return -EINVAL;
+	}
+	reg = (u8) *ppos;
+	chip = container_of(file->f_dentry->d_inode->i_cdev,
+			struct tsl258x_chip, cdev);
+	while (xfrd < count) {
+		ret = i2c_smbus_write_byte(chip->client, (CMD_REG | reg));
+		if (ret < 0) {
+			dev_err(devp, "i2c_smbus_write_byte to cmd reg failed "
+				"in taos_read(), err = %d\n", ret);
+			mutex_unlock(&als_mutex);
+			return ret;
+		}
+		my_buf[i++] = i2c_smbus_read_byte(chip->client);
+		reg++;
+		xfrd++;
+	}
+	ret = copy_to_user(buf, my_buf, xfrd);
+	if (ret) {
+		dev_err(devp, "copy_to_user failed in taos_read()\n");
+		mutex_unlock(&als_mutex);
+		return -ENODATA;
+	}
+
+	mutex_unlock(&als_mutex);
+	return (int) xfrd;
+}
+
+/**
+ * Device write - writes are permitted only within the range of the

accessible
+ * registers of the device. The data written is copied safely from user space.
+ */

tsl258x.c +47 -0
+static int taos_write(struct file *file, const char *buf, size_t count,
+    loff_t *ppos)
+{
+	int ret = 0;
+	struct tsl258x_chip *chip;
+	u8 my_buf[MAX_DEVICE_REGS];
+	u8 reg, i = 0, xfrd = 0;
+
+	if (mutex_trylock(&als_mutex) == 0) {
+		printk(KERN_INFO "Can't get ALS mutex\n");
+		return -1; /*busy, so return LAST VALUE*/
+	}
+
+	if ((*ppos < 0) || (*ppos >= MAX_DEVICE_REGS) || ((*ppos + count)
+			> MAX_DEVICE_REGS)) {
+		dev_err(devp, "reg limit check failed in taos_write()\n");
+		mutex_unlock(&als_mutex);
+		return -EINVAL;
+	}
+	reg = (u8) *ppos;
+	ret = copy_from_user(my_buf, buf, count);
+	if (ret) {
+		dev_err(devp, "copy_from_user failed in taos_write()\n");
+		mutex_unlock(&als_mutex);
+		return -ENODATA;
+		}
+	chip = container_of(file->f_dentry->d_inode->i_cdev,
+		struct tsl258x_chip, cdev);
+	while (xfrd < count) {
+		ret = i2c_smbus_write_byte_data(chip->client, (CMD_REG | reg),
+		    my_buf[i++]);
+		if (ret < 0) {
+			dev_err(devp, "i2c_smbus_write_byte_data failed in "
+				"taos_write()\n");
+			mutex_unlock(&als_mutex);
+			return ret;
+		}
+		reg++;
+		xfrd++;
+	}
+
+	mutex_unlock(&als_mutex);
+	return (int) xfrd;
+}
+
+/**
+ * Device seek - seeks are permitted only within the range of the

accessible
+ * registers of the device. The new offset is returned.
+ */

tsl258x.c +30 -0
+static loff_t taos_llseek(struct file *file, loff_t offset, int orig)
+{
+	loff_t new_pos;
+
+	if ((offset >= MAX_DEVICE_REGS) || (orig < 0) || (orig > 1)) {
+		dev_err(devp, "offset param limit or whence limit check failed "
+			"in taos_llseek()\n");
+		return -EINVAL;
+	}
+	switch (orig) {
+	case 0:
+		new_pos = offset;
+		break;
+	case 1:
+		new_pos = file->f_pos + offset;
+		break;
+	default:
+		return -EINVAL;
+		break;
+	}
+	if ((new_pos < 0) || (new_pos >= MAX_DEVICE_REGS)) {
+		dev_err(devp, "new offset check failed in taos_llseek()\n");
+		return -EINVAL;
+	}
+	file->f_pos = new_pos;
+	return new_pos;
+}
+
+/**
+ * Fasync function - called when the FASYNC flag in the file descriptor

of the
+ * device indicates that a new user program wants to be added to the wait queue
+ * of the device - calls fasync_helper() to add the new entry to the queue.
+ */

tsl258x.c +55 -0
+static int taos_fasync(int fd, struct file *file, int mode)
+{
+	chip = container_of(file->f_dentry->d_inode->i_cdev, struct
+			tsl258x_chip, cdev);
+	return fasync_helper(fd, file, mode, &chip->async_queue);
+}
+
+/**
+ * Provides initial operational parameter defaults.\n
+ * These defaults may be changed by the following:\n
+ * - system deamon
+ * - user application (via ioctl)
+ * - directly writing to the taos procfs file
+ * - external kernel level modules / applications
+ */
+static void taos_defaults()
+{
+	/*Operational parameters*/
+	taos_cycle_type = LIGHT;
+	/*default is ALS only*/
+	taos_settings.als_time = 100;
+	/*must be a multiple of 50mS*/
+	taos_settings.als_gain = 0;
+	/*this is actually an index into the gain table*/
+	/*assume clear glass as default*/
+	taos_settings.als_gain_trim = 1000;
+	/*default gain trim to account for aperture effects*/
+	taos_settings.als_persistence = 0;
+	/*default number of 'out of bounds' b4 interrupt*/
+	taos_settings.als_cal_target = 130;
+	/*Known external ALS reading used for calibration*/
+	taos_settings.interrupts_enabled = 0;
+	/*Interrupts enabled (ALS) 0 = none*/
+
+	/*Initialize ALS data to defaults*/
+	als_cur_info.als_ch0 = 0;
+	als_cur_info.als_ch1 = 0;
+	als_cur_info.lux = 0;
+
+	taos_settings.als_thresh_high = ALS_THRESHOLD_HI_LIMIT;
+	taos_settings.als_thresh_low = ALS_THRESHOLD_LO_LIMIT;
+
+
+#ifdef TAOS_DEBUG
+printk(KERN_INFO "\nDEFAULTS LOADED\n");
+#endif
+}
+EXPORT_SYMBOL(taos_defaults);
+/*........................................................................*/
+/*@}	End of Initial*/
+
+
+/*===========================================================================*/
+/**@defgroup IOCTL User ioctl Functions
+ * The section applies to 'ioctl' calls used primarily to support

user-land applications.\n
+ * @{
+ */

tsl258x.c +4 -0
+/**
+ * Ioctl functions - each one is documented below.
+ */
+static int taos_ioctl(struct inode *inode, struct file *file, unsigned

int cmd,

tsl258x.c +13 -0
+    unsigned long arg) {
+	int ret = 0;
+	struct tsl258x_chip *chip;
+	int tmp;
+	/*u8 reg_val;*/
+
+	chip = container_of(inode->i_cdev, struct tsl258x_chip, cdev);
+	switch (cmd) {
+	/**
+	 * - ALS_ON - called to set the device in ambient light sense mode.
+	 * configured values of light integration time, initial interrupt
+	 * filter, gain, and interrupt thresholds (if interrupt driven) are
+	 * initialized. Then power, adc, (and interrupt if needed) are

enabled.

tsl258x.c +30 -0
+	 */
+	case TAOS_IOCTL_ALS_ON:
+	  taos_cycle_type = LIGHT;
+	  printk(KERN_INFO "ALS On\n");
+	  return taos_chip_on();
+	break;
+	/**
+	* - ALS_OFF - called to stop ambient light sense mode of operation.
+	* Clears the filter history, and clears the control register.
+	*/
+	case TAOS_IOCTL_ALS_OFF:
+		taos_chip_off();
+		break;
+	/**
+	* - ALS_DATA - request for current ambient light data. If correctly
+	* enabled and valid data is available at the device, the function
+	* for lux conversion is called, and the result returned if valid.
+	*/
+	case TAOS_IOCTL_ALS_DATA:
+		/*Are we actively updating the struct?*/
+		if ((taos_settings.interrupts_enabled
+				& CNTL_ALS_INT_ENBL) == 0x00)
+			return taos_get_lux(); /*No - so get fresh data*/
+		else
+			return als_cur_info.lux;
+			/*Yes - data is fresh as last change*/
+		break;
+
+	/**
+	* - ALS_CALIBRATE - called to run one calibration cycle, during

assembly.

tsl258x.c +11 -0
+	* The lux value under a known intensity light source is used to obtain
+	* a "gain trim" multiplier which is to be used to normalize subsequent
+	* lux data read from the device, after the calibration is completed.
+	* This 'known intensity should be the value of als_cal_target.
+	* If not - make is so!
+	*/
+	case TAOS_IOCTL_ALS_CALIBRATE:
+		return taos_als_calibrate(taos_settings.als_cal_target);
+		break;
+	/**
+	* - CONFIG-GET - returns the current device configuration values to

the

tsl258x.c +105 -0
+	* caller. The user mode application can display or store configuration
+	* values, for future reconfiguration of the device, as needed.
+	* Refer to structure "taos_settings" in '.h' file.
+	*/
+	case TAOS_IOCTL_CONFIG_GET:
+		ret = copy_to_user((struct taos_settings *) arg, &taos_settings,
+			sizeof(struct taos_settings));
+		if (ret) {
+			dev_err(devp,
+				"copy_to_user() failed in ioctl config_get\n");
+			return -ENODATA;
+		}
+		return ret;
+		break;
+	/**
+	* - GET_ALS - returns the current ALS structure values to the
+	* caller. The values returned represent the last call to
+	* taos_get_lux().  This ioctl can be used when the driver
+	* is operating in the interrupt mode.
+	* Refer to structure "als_cur_info" in '.h' file.
+	*/
+	case TAOS_IOCTL_GET_ALS:
+		ret = copy_to_user((struct taos_als_info *) arg,
+			&als_cur_info,
+			sizeof(struct taos_als_info));
+		if (ret) {
+			dev_err(devp,
+			"copy_to_user() failed in ioctl to get "
+				"taos_als_info\n");
+			return -ENODATA;
+			}
+		return ret;
+		break;
+	/**
+	* - CONFIG-SET - used by a user mode application to set the desired
+	* values in the driver's in memory copy of the device configuration
+	* data. Light integration times are aligned optimally, and driver
+	* global variables dependent on configured values are updated here.
+	* Refer to structure "taos_settings" in '.h' file.
+	*/
+	case TAOS_IOCTL_CONFIG_SET:
+		ret = copy_from_user(&taos_settings,
+			(struct taos_settings *) arg,
+			sizeof(struct taos_settings));
+		if (ret) {
+			dev_err(devp,
+			"copy_from_user() failed in ioctl config_set\n");
+			return -ENODATA;
+			}
+		return ret;
+		break;
+	/**
+	* - LUX_TABLE-GET - returns the current LUX coefficients table to the
+	* caller. The user mode application can display or store the table
+	* values, for future re-calibration of the device, as needed.
+	* Refer to structure "taos_lux" in '.h' file.
+	*/
+	case TAOS_IOCTL_LUX_TABLE_GET:
+		ret = copy_to_user((struct taos_lux *) arg,
+			&taos_device_lux,
+			sizeof(taos_device_lux));
+		if (ret) {
+			dev_err(devp,
+			"copy_to_user() failed in ioctl "
+			"TAOS_IOCTL_LUX_TABLE_GET\n");
+			return -ENODATA;
+			}
+		return ret;
+		break;
+	/**
+	* - LUX TABLE-SET - used by a user mode application to set the desired
+	* LUX table values in the driver's in memory copy of the lux table
+	* Refer to structure "taos_lux" in '.h' file.
+	*/
+	case TAOS_IOCTL_LUX_TABLE_SET:
+		ret = copy_from_user(&taos_lux,
+			(struct taos_lux *) arg,
+			sizeof(taos_device_lux));
+		if (ret) {
+			dev_err(devp, "copy_from_user() failed in ioctl "
+				"TAOS_IOCTL_LUX_TABLE_SET\n");
+			return -ENODATA;
+			}
+#ifdef ASYNC_NOTIFY
+		/*Notify the user-land app (if any)*/
+		if (chip->async_queue)
+			kill_fasync(&chip->async_queue, SIGIO, POLL_IN);
+#endif
+
+
+		return ret;
+		break;
+	/**
+	* - TAOS_IOCTL_ID - used to query driver name & version
+	*/
+	case TAOS_IOCTL_ID:
+		ret = copy_to_user((char *) arg,
+			&driver_version_id,
+			sizeof(driver_version_id));
+		dev_info(devp, "%s\n", DRIVER_VERSION_ID);
+		return ret;
+		break;
+	/*
+	* - TAOS_IOCTL_SET_GAIN - used to set/change the device analog gain.
+	* The value passed into here from the user is the index into the

table.

tsl258x.c +42 -0
+	* Index = (0 - 3) Used to validate the gain selection index
+	*/
+	case TAOS_IOCTL_SET_GAIN:
+		get_user(tmp, (int *)arg);
+		if (tmp > MAX_GAIN_STAGES)
+			return -1;
+		taos_settings.als_gain = tmp;
+		ret = taos_i2c_write(CMD_REG|GAIN, (u8 *)&tmp);
+		if (ret < 0) {
+			dev_err(devp, "taos_i2c_write to turn on device "
+				"failed in taos_chip_on.\n");
+			return -1;
+			}
+		return ret;
+		break;
+
+	default:
+		return -EINVAL;
+		break;
+		}
+
+		return ret;
+
+	}
+/*........................................................................*/
+/*@}	end of IOCTL section*/
+
+
+/*===========================================================================*/
+/**@defgroup ALS Ambient Light Sense (ALS)
+ * The section applies to the ALS related functions.\n
+ * Other ALS releated functions may appear elsewhere in the code.\n
+ * @{
+ */
+
+/*........................................................................*/
+
+/**
+ * Reads and calculates current lux value.
+ * 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. The array taos_device_luxP[]
+ * declared above 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.
+ *
+ * \param none
+ * \return int -1 = Failure, Lux Value = success
+ */

tsl258x.c +151 -0
+static int taos_get_lux(void)
+{
+	u32 ch0, ch1; /* separated ch0/ch1 data from device */
+	u32 lux; /* raw lux calculated from device data */
+	u32 ratio;
+	u8 buf[5];
+	struct taos_lux *p;
+
+	int i, ret;
+
+	u32 ch0lux = 0x0000;
+	u32 ch1lux = 0x0000;
+
+	if (mutex_trylock(&als_mutex) == 0) {
+		printk(KERN_INFO "Can't get ALS mutex\n");
+		return als_cur_info.lux; /*busy, so return LAST VALUE*/
+	}
+
+	if (taos_chip_status != TAOS_CHIP_WORKING) {
+		/*device is not enabled*/
+		printk(KERN_INFO "device is not enabled\n");
+		mutex_unlock(&als_mutex);
+		return -1;
+	}
+
+	if ((taos_cycle_type & LIGHT) == 0) {
+		/*device not in ALS mode*/
+		printk(KERN_INFO "device not in ALS mode\n");
+		mutex_unlock(&als_mutex);
+		return -1;
+	}
+
+	ret = taos_i2c_read((CMD_REG), &buf[0], 1);
+	if (ret < 0) {
+		printk(KERN_INFO "taos_i2c_read() to CMD_REG reg failed in "
+		"taos_get_lux()\n");
+		mutex_unlock(&als_mutex);
+		return -1;
+	}
+	/* is data new & valid */
+	if (!(buf[0] & STA_ADCINTR)) {
+		printk(KERN_INFO "Data not valid, so return LAST VALUE\n");
+		mutex_unlock(&als_mutex);
+		return als_cur_info.lux; /*have no data, so return LAST VALUE*/
+	}
+
+	for (i = 0; i < 4; i++) {
+		ret = taos_i2c_read((CMD_REG | (ALS_CHAN0LO + i)), &buf[i], 1);
+		if (ret < 0) {
+			dev_err(devp,
+			"taos_i2c_read() to (ALS_CHAN0LO + i) failed in "
+			"taos_get_lux()\n");
+			mutex_unlock(&als_mutex);
+			return -1;
+		}
+	}
+
+	/* clear status, really interrupt status (interrupts are off), but
+	 *  we use the bit anyway */
+	ret = taos_i2c_write_command(CMD_REG | CMD_SPL_FN | CMD_ALS_INTCLR);
+	if (ret < 0) {
+		dev_err(devp,
+		"taos_i2c_write_command failed in "
+		"taos_chip_on, err = %d\n", ret);
+		mutex_unlock(&als_mutex);
+		return -1; /*have no data, so return fail*/
+	}
+
+	/* extract ALS/lux data */
+	ch0 = (buf[1] << 8) | buf[0];
+	ch1 = (buf[3] << 8) | buf[2];
+
+	als_cur_info.als_ch0 = ch0;
+	als_cur_info.als_ch1 = ch1;
+
+
+#ifdef TAOS_DEBUG
+	printk(KERN_INFO " ch0=%d/0x%x ch1=%d/0x%x\n", ch0, ch0, ch1, ch1);
+#endif
+
+	if ((ch0 >= als_saturation) || (ch1 >= als_saturation)) {
+return_max:
+		als_cur_info.lux = MAX_LUX;
+		mutex_unlock(&als_mutex);
+		return als_cur_info.lux;
+	}
+
+	if (ch0 == 0) {
+		als_cur_info.lux = 0;
+#ifdef TAOS_DEBUG
+		printk(KERN_INFO "ch0==0\n");
+#endif
+		mutex_unlock(&als_mutex);
+
+		return als_cur_info.lux;
+		/*have no data, so return LAST VALUE*/
+	}
+	/* calculate ratio */
+	ratio = (ch1 << 15) / ch0;
+	/* convert to unscaled lux using the pointer to the table */
+	for (p = (struct taos_lux *) taos_device_lux;
+			p->ratio != 0 && p->ratio < ratio; p++)
+		;
+
+	if (p->ratio == 0) {
+#ifdef TAOS_DEBUG
+		dev_info(devp, "ratio = %04x   p->ratio = %04x\n",
+			ratio, p->ratio);
+#endif
+		lux = 0;
+	} else {
+		ch0lux = ((ch0 * p->ch0) +
+				(gainadj[taos_settings.als_gain].ch0 >> 1))
+				/ gainadj[taos_settings.als_gain].ch0;
+		ch1lux = ((ch1 * p->ch1) +
+				(gainadj[taos_settings.als_gain].ch1 >> 1))
+				/ gainadj[taos_settings.als_gain].ch1;
+		lux = ch0lux - ch1lux;
+	}
+
+	/* note: lux is 31 bit max at this point */
+	if (ch1lux > ch0lux) {
+		printk(KERN_INFO "No Data - Return last value\n");
+		als_cur_info.lux = 0;
+		mutex_unlock(&als_mutex);
+		return als_cur_info.lux; /*have no data, so return LAST VALUE*/
+	}
+	/* adjust for active time scale */
+	lux = (lux + (als_time_scale >> 1)) / als_time_scale;
+	/* adjust for active gain scale */
+	lux >>= 13; /* tables have factor of 8192 builtin for accuracy */
+
+	lux = (lux * taos_settings.als_gain_trim + 500) / 1000;
+
+
+#ifdef TAOS_DEBUG
+	printk(KERN_INFO "lux=%d\n", lux);
+#endif
+	if (lux > MAX_LUX) /* check for overflow */
+		goto return_max;
+
+	als_cur_info.lux = lux;
+	/*Update the structure with the latest VALID lux.*/
+
+	mutex_unlock(&als_mutex);
+	return lux;
+}
+EXPORT_SYMBOL(taos_get_lux);
+
+/**
+ * Obtain single reading and calculate the als_gain_trim (later used to

derive actual lux)
+ *\param ulong als_cal_target ALS target (real world)
+ *\return int updated gain_trim value
+ */

tsl258x.c +89 -0
+int taos_als_calibrate(unsigned long als_cal_target)
+{
+	u8 reg_val;
+	unsigned int gain_trim_val;
+	int ret;
+	int lux_val;
+
+
+	/*This may seem redundant but..
+	 We need this next line in case we call this function from an
+	 external application who has a different target value than
+	 our defaults.
+	 In which case, make 'that' our new default/settings.*/
+	taos_settings.als_cal_target = als_cal_target;
+
+	ret = i2c_smbus_write_byte(chip->client, (CMD_REG | CNTRL));
+	if (ret < 0) {
+		dev_err(devp,
+		"i2c_smbus_write_byte to cmd reg failed in "
+		"ioctl als_calibrate\n");
+		return ret;
+	}
+	reg_val = i2c_smbus_read_byte(chip->client);
+
+	if ((reg_val & (CNTL_ADC_ENBL | CNTL_PWRON))
+			!= (CNTL_ADC_ENBL | CNTL_PWRON)) {
+		dev_err(devp,
+		"reg_val & (CNTL_ADC_ENBL | CNTL_PWRON)) "
+		"!= (CNTL_ADC_ENBL | CNTL_PWRON) in ioctl als_calibrate\n");
+		return -ENODATA;
+	}
+
+	ret = i2c_smbus_write_byte(chip->client, (CMD_REG | STATUS));
+	if (ret < 0) {
+		dev_err(devp,
+		"i2c_smbus_write_byte to cmd reg failed in "
+		"ioctl als_calibrate\n");
+		return ret;
+	}
+	reg_val = i2c_smbus_read_byte(chip->client);
+
+	if ((reg_val & STA_ADCVALID) != STA_ADCVALID) {
+		dev_err(devp,
+		"(reg_val & STA_ADCVALID) != STA_ADCVALID in "
+		"ioctl als_calibrate\n");
+		return -ENODATA;
+	}
+	lux_val = taos_get_lux();
+	if (lux_val < 0) {
+		dev_err(devp,
+		"taos_get_lux() returned bad value %d in "
+		"ioctl als_calibrate\n",
+		    lux_val);
+		return lux_val;
+	}
+	gain_trim_val = (unsigned int) (((taos_settings.als_cal_target)
+			* taos_settings.als_gain_trim) / lux_val);
+
+	dev_info(devp, "\n\ntaos_settings.als_cal_target = %d\n"
+		"taos_settings.als_gain_trim = %d\nlux_val = %d\n",
+		taos_settings.als_cal_target,
+		taos_settings.als_gain_trim,
+		lux_val);
+
+	if ((gain_trim_val < 250) || (gain_trim_val > 4000)) {
+		dev_err(devp,
+		"ALS calibrate result %d out of range "
+		"in ioctl als_calibrate\n",
+		    gain_trim_val);
+		return -ENODATA;
+	}
+	taos_settings.als_gain_trim = (int) gain_trim_val;
+
+	return (int) gain_trim_val;
+}
+EXPORT_SYMBOL(taos_als_calibrate);
+
+/*........................................................................*/
+/*@} End of ALS section*/
+
+/*===========================================================================*/
+/**@defgroup DEV_CTRL Device Control Functions
+ * @{
+ */
+
+/*........................................................................*/
+/**
+ * Turn the device on.
+ * Configuration and taos_cycle_type must be set before calling this

function.
+ * \param none.
+ * \return int 0 = success, < 0 = failure
+ */

tsl258x.c +22 -0
+static int taos_chip_on()
+{
+	int i;
+	int ret = 0;
+	u8 *uP;
+	u8 utmp;
+	int als_count;
+	int als_time;
+
+
+	/*make sure taos_cycle_type is set.*/
+	if ((taos_cycle_type != LIGHT))
+		return -1;
+
+	/*and make sure we're not already on*/
+	if (taos_chip_status == TAOS_CHIP_WORKING) {
+		/*if forcing a register update - turn off, then on*/
+		printk(KERN_INFO "device is already enabled\n");
+		return -1;
+	}
+
+	/* . . . . . . . . SHADOW REGISTER

INITIALIZATION . . . . . . . . . . .

tsl258x.c +2 -0
+	 Note:
+	 If interrupts are enabled, keeping persist at 0 should prime the

pump.

tsl258x.c +155 -0
+	 So don't set persist in shadow register. Just enable if required.*/
+	taos_config[INTERRUPT] = (taos_settings.interrupts_enabled & 0xF0);
+
+
+	/*determine als integration regster*/
+	als_count = (taos_settings.als_time * 100 + 135) / 270;
+	if (als_count == 0)
+		als_count = 1; /*ensure at least one cycle*/
+
+
+	/*convert back to time (encompasses overrides)*/
+	if (taos_cycle_type & LIGHT) {
+		als_time = (als_count * 27 + 5) / 10;
+		taos_config[ALS_TIME] = 256 - als_count;
+	} else
+		taos_config[ALS_TIME] = 0xff;
+
+
+	/*Set the gain based on taos_settings struct*/
+	taos_config[GAIN] = taos_settings.als_gain;
+
+
+	/*set globals re scaling and saturation*/
+	als_saturation = als_count * 922; /*90% of full scale*/
+	als_time_scale = (als_time + 25) / 50;
+
+
+	/* . . . . . . . . . . . . .  . . . . .  . . . . . . . . . . .
+	 SKATE Specific power-on / adc enable sequence
+	 Power on the device 1st.*/
+	utmp = CNTL_PWRON;
+	ret = taos_i2c_write(CMD_REG | CNTRL, &utmp);
+	if (ret < 0) {
+		dev_err(devp,
+		"taos_i2c_write to turn on device failed in "
+		"taos_chip_on.\n");
+		return -1;
+	}
+
+	/*User the following shadow copy for our delay before enabling ADC
+	 Write ALL THE REGISTERS*/
+	for (i = 0, uP = taos_config; i < TAOS_REG_MAX; i++) {
+		ret = taos_i2c_write(CMD_REG + i, uP++);
+		if (ret < 0) {
+			dev_err(devp,
+			"taos_i2c_write to reg %d failed in "
+			"taos_chip_on.\n", i);
+			return -1;
+		}
+	}
+
+	mdelay(3);
+	/*NOW enable the ADC
+	 initialize the desired mode of operation*/
+	utmp = CNTL_PWRON | CNTL_ADC_ENBL;
+	ret = taos_i2c_write(CMD_REG | CNTRL, &utmp);
+	if (ret < 0) {
+		dev_err(devp,
+		"taos_i2c_write to turn on device failed in "
+		"taos_chip_on.\n");
+		return -1;
+	}
+	taos_chip_status = TAOS_CHIP_WORKING;
+
+#ifdef TAOS_DEBUG
+	dev_info(devp, "chip_on() called\n\n");
+#endif
+
+	return ret; /*returns result of last i2cwrite*/
+}
+EXPORT_SYMBOL(taos_chip_on);
+
+/*........................................................................*/
+/**
+ * Turn the device OFF.
+ * \param	none.
+ * \return	int	0 = success, < 0 = failure
+ */
+static int taos_chip_off()
+{
+	int ret;
+	u8 utmp;
+
+
+	/*turn device off*/
+	taos_chip_status = TAOS_CHIP_SLEEP;
+	utmp = 0x00;
+	ret = taos_i2c_write(CMD_REG | CNTRL, &utmp);
+	als_on = FALSE;
+	/*init_done = 0x00; *//*used by ALS irq as one/first shot*/
+#ifdef TAOS_DEBUG
+	printk(KERN_INFO "chip_off() called\n");
+#endif
+
+	return ret;
+
+}
+EXPORT_SYMBOL(taos_chip_off);
+/*........................................................................*/
+/*@} End of Device Control Section*/
+
+/*===========================================================================*/
+
+/*===========================================================================*/
+/**@defgroup I2C-HAL I2C/SMBus Communication Functions
+ * The sections pertains to the driver/device communication functions\n
+ * facilitated via SMBus in an I2C manner.\n
+ * These functions represent several layers of abstraction.
+ * Also note, some i2c controllers do not support block read/write.
+ * @{
+ */
+
+/**
+ * Read a number of bytes starting at register (reg) location.
+ *
+ * \param 	unsigned char 	reg - device register
+ * \param 	unsigned char	*val - location to store results
+ * \param 	unsigned int 	number of bytes to read
+ * \return	TAOS_SUCCESS, or i2c_smbus_write_byte ERROR code
+ *
+ */
+static int taos_i2c_read(u8 reg, u8 *val, unsigned int len)
+{
+	int result;
+	int i;
+
+	if (len > MAXI2C)
+		len = MAXI2C;
+	for (i = 0; i < len; i++) {
+		/* select register to write */
+		if (reg >= 0) {
+			result = i2c_smbus_write_byte(chip->client,
+				(CMD_REG | (reg + i)));
+
+			if (result < 0) {
+				dev_err(devp,
+				"FAILED: i2c_smbus_write_byte_data in "
+				"taos_io_read()\n");
+				return result;
+			}
+			/* read the data */
+			*val = i2c_smbus_read_byte(chip->client);
+#ifdef TAOS_DEBUG_SMBUS
+			dev_info(devp,
+				"READ FROM REGISTER [%02X] -->%02x<--\n",
+				(CMD_REG | (reg + i)) , *data);
+#endif
+			val++;
+		}
+	}
+	return TAOS_SUCCESS;
+}
+
+/**
+ * This function is used to send a an register address followed by a

data value
+ * When calling this function remember to set the register MSBit
+ * (if applicable).
+ * \param unsigned char register
+ * \param unsigned char * pointer to data value(s) to be written
+ * \return TAOS_SUCCESS, or i2c_smbus_write_byte error code.
+ */

tsl258x.c +14 -0
+static int taos_i2c_write(u8 reg, u8 *val)
+{
+	int retval;
+	retval = i2c_smbus_write_byte_data(chip->client, reg, (*val));
+	if (retval < 0) {
+		dev_err(devp, "FAILED: i2c_smbus_write_byte_data\n");
+		return retval;
+	}
+
+	return TAOS_SUCCESS;
+}
+
+/**
+ * This function is used to send a command to device command/control

register
+ * All bytes sent using this command have their MSBit set - it's a command!
+ * \param unsigned char register to write
+ * \return TAOS_SUCCESS, or i2c_smbus_write_byte error code.
+ */

tsl258x.c +23 -0
+static int taos_i2c_write_command(u8 reg)
+{
+	int result;
+	u8 *p;
+	u8 buf;
+
+	p = &buf;
+	*p = reg |= CMD_REG;
+	/*write the data*/
+	result = i2c_smbus_write_byte(chip->client, (*p));
+	if (result < 0) {
+		dev_err(devp, "FAILED: i2c_smbus_write_byte\n");
+		return result;
+	}
+	return TAOS_SUCCESS;
+}
+
+/*........................................................................*/
+/*@}	//end of I2C-HAL section*/
+
+
+/**@defgroup group6 Sysfs Interface Functions
+ * The following functions are for access via the sysfs style

interface.\n
+ * Use 'cat' to read, 'echo >' to write\n
+ * @{
+ */

tsl258x.c +163 -0
+
+/*........................................................................*/
+/*sysfs interface functions begin here*/
+
+static ssize_t taos_device_id(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%s %s\n", DEVICE_ID, DRIVER_VERSION_ID);
+}
+
+static ssize_t taos_power_state_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_chip_status);
+	return 0;
+}
+
+static ssize_t taos_power_state_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value == 1)
+		taos_chip_on();
+	else
+		taos_chip_off();
+
+	return len;
+}
+
+static ssize_t taos_gain_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_gain);
+	return 0;
+}
+
+static ssize_t taos_gain_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+	if (value) {
+		if (value > 4) {
+			printk(KERN_INFO "Invalid Gain Index\n");
+			return -1;
+		} else {
+			taos_settings.als_gain = value;
+		}
+	}
+	return len;
+}
+
+static ssize_t taos_interrupt_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.interrupts_enabled);
+	return 0;
+}
+
+static ssize_t taos_interrupt_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value) {
+		if (value > 1) {
+			printk(KERN_INFO "Invalid: 0 or 1\n");
+			return -1;
+		} else {
+			taos_settings.interrupts_enabled = CNTL_ALS_INT_ENBL;
+		}
+	}
+	return len;
+}
+
+static ssize_t taos_als_time_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_time);
+	return 0;
+}
+
+static ssize_t taos_als_time_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		taos_settings.als_time = value;
+
+	return len;
+}
+
+static ssize_t taos_als_trim_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_gain_trim);
+	return 0;
+}
+
+static ssize_t taos_als_trim_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		taos_settings.als_gain_trim = value;
+
+	return len;
+}
+
+static ssize_t taos_als_persistence_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_persistence);
+	return 0;
+}
+
+static ssize_t taos_als_persistence_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		taos_settings.als_persistence = value;
+
+	return len;
+}
+
+static ssize_t taos_als_cal_target_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_cal_target);
+	return 0;
+}
+
+static ssize_t taos_als_cal_target_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		taos_settings.als_cal_target = value;
+
+	return len;
+}
+
+static ssize_t taos_lux_show(struct device *dev, struct

device_attribute *attr,

tsl258x.c +91 -0
+    char *buf)
+{
+	int lux = 0;
+	if ((taos_settings.interrupts_enabled & CNTL_ALS_INT_ENBL) == 0x00)
+		lux = taos_get_lux(); /*get fresh data*/
+	else
+		lux = als_cur_info.lux; /*data is fresh as last change*/
+
+	return sprintf(buf, "%d\n", lux);
+	return 0;
+}
+
+static ssize_t taos_do_calibrate(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value == 1)
+		taos_als_calibrate(taos_settings.als_cal_target);
+
+	return len;
+}
+
+static ssize_t taos_als_thresh_low_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_thresh_low);
+	return 0;
+}
+
+static ssize_t taos_als_thresh_low_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		taos_settings.als_thresh_low = value;
+
+	return len;
+}
+
+static ssize_t taos_als_thresh_high_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_thresh_high);
+	return 0;
+}
+
+static ssize_t taos_als_thresh_high_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		taos_settings.als_thresh_high = value;
+
+	return len;
+}
+
+/*@}	End of Sysfs Interface Functions - gp6*/
+/*===========================================================================*/
+
+static void taos_als_auto_update(struct work_struct *update)
+{
+	if (taos_chip_status != TAOS_CHIP_WORKING) {
+		taos_chip_on();
+		mdelay(100);
+	}
+	taos_get_lux();
+	input_report_abs(taos_dev, ABS_X, als_cur_info.lux);
+	input_sync(taos_dev);
+
+	queue_delayed_work(taos_wq0, luxUd, LUX_UPDATE_PERIOD);
+
+}
+
+
+
+MODULE_AUTHOR("J. August Brenner<[email protected]>");
+MODULE_DESCRIPTION("TAOS 258x ambient light sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(taos_init);
+module_exit(taos_exit);
+
#2 lore raw
Quoted 7 lines · Jonathan Cameron

Lots of cc's added. Note that a driver like this that touches a number of subsystems needs to also go to their individual mailing lists as not everyone monitors lkml very closely.

Quoted 2 lines

From: J. August Brenner <[email protected]>

Hi Jon,

Various comments inline. Note quite a few are general patch submission issues that imply you haven't read the stuff on submitting patches under Documenation in the kernel tree. Please do so. Lots of trivial cleanup needed as well. In particular please check your comments....

There's quite a bit here, so I'd imagine we'll need to go through a couple of iterations. Please look at a few other in kernel drivers. Tsl2563 is reasonably clean and will show what we would normally expect wrt to i2c registration etc. There is one registered in arch/arm/mach-pxa/imote2.c that should show how the board file should interact with the device driver.

Quoted 4 lines
Quoted 4 lines

This driver supports the TAOS tsl258x family of ALS sensors. This driver provides ALS data (lux), and device configuration via ioctl, input_event, sysfs/iio methods. These methods are provided to support varied access requirements and integrated into a single driver.

I'll be interested to hear what other peoples view is on this...

Even if the rest is fine, that IOCTL interface indicates to me that there are defficiencies in the other interfaces (i.e. they don't support something you need in their interfaces). If that is the case please identify what additions are needed to them rather than providing yet another control method.

Thus, a fair bit to do to clean this driver up, but it's a good start and it will be good to expand a range of als drivers in tree.

Thanks,

Jonathan

Quoted 7 lines
Quoted 7 lines

Thus, adverting the need for multiple releases of similar code. More significantly, this driver provides the capability to be fed 'glass coefficients' to accommodate varying system designs (bezels, faceplates, etc.).

Please add stats (--with-stats for git format-patch). Gives an idea of where things are to everyone.

Quoted 4 lines

Kconfig

Quoted 4 lines

---
diff -Naurp drivers-orig/Kconfig drivers/Kconfig

Kconfig +0 -0

Diffs should be wrt to whole kernel tree rather than somewhere inside it. Makes them easier to apply. I'm guessing this in drivers/staging/iio/light given the other stuff present but that isn't clear from the patch.

Quoted 76 lines
Quoted 76 lines
Kconfig +7 -0
@@ -13,6 +13,13 @@ config SENSORS_TSL2563
 	 This driver can also be built as a module.  If so, the module
 	 will be called tsl2563.
 
+config TAOS_258x
+	tristate "TAOS TSL258x device driver"
+	default m
+	help
+	  Provides support for the TAOS tsl258x family of devices.
+	  Access ALS data via iio, sysfs, input_event, and ioctl methods.
+
 config SENSORS_ISL29018
         tristate "ISL 29018 light and proximity sensor"
         depends on I2C

diff -Naurp drivers-orig/Makefile drivers/Makefile

Makefile +1 -0
@@ -3,4 +3,5 @@
 #
 
 obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o
+obj-$(CONFIG_TAOS_258x)	+= tsl258x.o
 obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o

diff -Naurp drivers-orig/tsl258x.c drivers/tsl258x.c

tsl258x.c +12 -0
@@ -0,0 +1,2055 @@
+/*
+ * Device driver for monitoring ambient light intensity (lux)
+ * within the TAOS tsl258x family of devices
+ *
+ * Copyright (c) 2011, 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.
+ */

tsl258x.c +24 -0
+
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/wait.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+#include <linux/workqueue.h>
+#include <linux/firmware.h>
+#include <linux/string.h>
+#include <linux/kmod.h>
+#include <linux/ctype.h>
+#include <linux/input.h>
+#include <linux/time.h>
+#include <linux/jiffies.h>

That's an awful lot of headers. I can't see any use of ioport.h for starters. Please verify which ones are actually needed and get rid of the rest.

Quoted 2 lines

tsl258x.c

Quoted 2 lines
tsl258x.c +2 -0
+#include "../iio.h"
+

Comment lines like this just tend to break up the code without adding anything. Probably best to clean them out.

Quoted 2 lines

tsl258x.c

Quoted 2 lines
tsl258x.c +2 -0
+/*........................................................................*/
+#define DRIVER_VERSION_ID	"3.9"

The problem with having version numbers in driver is that they tend not to be kept up to date. Once a driver merges it's api is maintained pretty much for ever anyway. I guess this is because you have legacy userspace code to support?

Quoted 1 line
Quoted 1 line

+

Err. This is one of the stranger defines I've seen. Definitely needs explanation if you keep it!

Quoted 4 lines

tsl258x.c

Quoted 4 lines
tsl258x.c +4 -0
+#define TAOS258x		2
+
+/* This build will be for..*/
+#define DRIVER_TYPE		TAOS258x

Mysterious and seems to be unusued. Please get rid of this.

Quoted 3 lines

tsl258x.c

Quoted 3 lines
tsl258x.c +3 -0
+
+/*........................................................................*/
+/*Debug related*/

Please use standard kernel interfaces for debugging log messages etc. This approach obviously makes a log of sense when developing drivers, but for something that is going for a merge they need to go away.

Quoted 10 lines

tsl258x.c

Quoted 10 lines
tsl258x.c +10 -0
+#define TAOS_DEBUG			1
+/*#define TAOS_DEBUG_SMBUS		1	*/
+#define TAOS_IRQ_DEBUG			1
+/*........................................................................*/
+/*User notification method*/
+/*#define ASYNC_NOTIFY			1	*/
+#define EVENT_NOTIFY			1
+/*........................................................................*/
+/*Module defines*/
+/* Device name/id/address */

I've litle idea what is going on here, but I doubt this makes sense in a driver submitted for mainline inclusion...

Quoted 1 line
Quoted 1 line

+#define DEVICE_NAME "taos"

That's hardly an informative name for the device... Seeing as it's fixed (I think) please also encode a better name directly where it is used.

Quoted 5 lines
Quoted 5 lines
tsl258x.c +5 -0
+#ifdef TAOS258x
+#define DEVICE_ID			"skateFN"
+#else
+#define DEVICE_ID			"tritonFN"
+#endif
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+
+#define ID_NAME_SIZE			10
+#define DEVICE_ADDR1			0x29
+#define DEVICE_ADDR2			0x39
+#define DEVICE_ADDR3			0x49
+#define MAX_NUM_DEVICES			3
+#define MAX_DEVICE_REGS			32
+#define I2C_MAX_ADAPTERS		8

Does this driver include both the taos and an i2c adapter for it? If so they should be separate drivers....

Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+
+/* Triton register offsets */
+#ifdef TAOS258x
+#define	TAOS_REG_MAX	8
+#else
+#define	TAOS_REG_MAX	16
+#endif
+

Don't define these if you aren't going to have any implementation.

Quoted 5 lines

tsl258x.c

Quoted 5 lines
tsl258x.c +5 -0
+/* Power management */
+#define taos_suspend			NULL
+#define taos_shutdown			NULL
+#define taos_resume			NULL
+

Never do this... It just obscures what is going on for no benefit.

Quoted 3 lines

tsl258x.c

Quoted 3 lines
tsl258x.c +3 -0
+#define TRUE				1
+#define FALSE				0
+

No idea what this is, so probably needs a more descriptive name.

Quoted 1 line
Quoted 1 line

+#define MAXI2C 32

None of these are used so get rid of them.

Quoted 6 lines

tsl258x.c

Quoted 6 lines
tsl258x.c +6 -0
+#define	CMD_ADDRESS	0x80
+#define	TAOS_ERROR	-1
+#define TAOS_SUCCESS	0
+
+#define	MAX_SAMPLES_CAL	200
+

Somewhat cryptic...

Quoted 2 lines

tsl258x.c

Quoted 2 lines
tsl258x.c +2 -0
+#define LUX_UPDATE_PERIOD	(HZ/2)
+

On is clearly 1 and off clearly 0 so don't do this.

Quoted 6 lines

tsl258x.c

Quoted 6 lines
tsl258x.c +6 -0
+/* power control */
+#define ON              1
+#define OFF		0
+
+
+/* sensor type */

add a prefix to these names to avoid possible clashes.

Quoted 8 lines
Quoted 8 lines
tsl258x.c +8 -0
+#define LIGHT		1
+#define PROXIMITY	2
+#define ALL			3
+
+struct taos_als_info {
+	u16 als_ch0;
+	u16 als_ch1;
+	u16 lux;
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+};
+
+struct taos_settings {
+	int	als_time;
+	int	als_gain;
+	int	als_gain_trim;
+	int	als_cal_target;
+	int	interrupts_enabled;

Odd spacing issue here, please make it consistent.

Quoted 5 lines

tsl258x.c

Quoted 5 lines
tsl258x.c +5 -0
+	int als_thresh_low;
+	int als_thresh_high;
+	char als_persistence;
+};
+

As stated above. The need for this interface implies deficiencies in the other subystems. Lets fix them rather than having this ioctl stuff working around them...

Quoted 12 lines
Quoted 12 lines
tsl258x.c +12 -0
+/* ioctl numbers */
+#define TAOS_IOCTL_MAGIC		0XCF
+#define TAOS_IOCTL_ALS_ON		_IO(TAOS_IOCTL_MAGIC,  1)
+#define TAOS_IOCTL_ALS_OFF		_IO(TAOS_IOCTL_MAGIC,  2)
+#define TAOS_IOCTL_ALS_DATA		_IOR(TAOS_IOCTL_MAGIC, 3, short)
+#define TAOS_IOCTL_ALS_CALIBRATE	_IO(TAOS_IOCTL_MAGIC,  4)
+#define TAOS_IOCTL_CONFIG_GET		_IOR(TAOS_IOCTL_MAGIC, 5,\
+		struct taos_settings)
+#define TAOS_IOCTL_CONFIG_SET		_IOW(TAOS_IOCTL_MAGIC, 6, \
+		struct taos_settings)
+#define TAOS_IOCTL_LUX_TABLE_GET	_IOR(TAOS_IOCTL_MAGIC, 7, \
+		struct taos_lux)
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+#define TAOS_IOCTL_LUX_TABLE_SET	_IOW(TAOS_IOCTL_MAGIC, 8, \
+		struct taos_lux)
+#define TAOS_IOCTL_ID			_IOR(TAOS_IOCTL_MAGIC, 12, char*)
+#define TAOS_IOCTL_SET_GAIN		_IOW(TAOS_IOCTL_MAGIC, 15, int)
+#define TAOS_IOCTL_GET_ALS		_IOR(TAOS_IOCTL_MAGIC, 16, \
+		struct taos_als_info)
+#define TAOS_IOCTL_INT_SET		_IOW(TAOS_IOCTL_MAGIC, 17, int)
+

Better to name these all with a PREFIX of say TAOS_258X as then you avoid any accidental name clashes...

Quoted 74 lines
Quoted 74 lines
tsl258x.c +71 -0
+/*Device Registers and Masks*/
+#define CNTRL				0x00
+#define STATUS				0x00
+#define ALS_TIME			0X01
+#define INTERRUPT			0x02
+#define ALS_MINTHRESHLO		0X03
+#define ALS_MINTHRESHHI		0X04
+#define ALS_MAXTHRESHLO		0X05
+#define ALS_MAXTHRESHHI		0X06
+#define GAIN				0x07
+#define REVID				0x11
+#define CHIPID				0x12
+#define SMB_4				0x13
+#define ALS_CHAN0LO			0x14
+#define ALS_CHAN0HI			0x15
+#define ALS_CHAN1LO			0x16
+#define ALS_CHAN1HI			0x17
+#define TMR_LO				0x18
+#define TMR_HI				0x19
+
+/* Skate cmd reg masks */
+#define CMD_REG				0x80
+#define CMD_BYTE_RW			0x00
+#define CMD_WORD_BLK_RW		0x20
+#define CMD_SPL_FN			0x60
+#define CMD_ALS_INTCLR		0X01
+
+/* Skate cntrl reg masks */
+#define CNTL_REG_CLEAR		0x00
+#define CNTL_ALS_INT_ENBL	0x10
+#define CNTL_WAIT_TMR_ENBL	0x08
+#define CNTL_ADC_ENBL		0x02
+#define CNTL_PWRON			0x01
+#define CNTL_ALSPON_ENBL	0x03
+#define CNTL_INTALSPON_ENBL	0x13
+
+/* Skate status reg masks */
+#define STA_ADCVALID		0x01
+#define STA_ALSINTR			0x10
+#define STA_ADCINTR			0x10
+
+/* Lux constants */
+#define	MAX_LUX				65535
+
+/* Thresholds */
+#define ALS_THRESHOLD_LO_LIMIT		0x0010
+#define ALS_THRESHOLD_HI_LIMIT		0xFFFF
+
+/* Device default configuration */
+#define ALS_TIME_PARAM			100
+#define SCALE_FACTOR_PARAM		1
+#define GAIN_TRIM_PARAM			512
+#define GAIN_PARAM			1
+#define ALS_THRSH_HI_PARAM		0xFFFF
+#define ALS_THRSH_LO_PARAM		0
+
+
+/* Prototypes */
+static void taos_defaults(void);
+static int taos_chip_on(void);
+static int taos_chip_off(void);
+static int taos_get_lux(void);
+
+struct i2c_adapter *i2c_get_adapter(int id);
+
+static int taos_probe(struct i2c_client *clientp,
+    const struct i2c_device_id *idp);
+static int taos_remove(struct i2c_client *client);
+static int taos_open(struct inode *inode, struct file *file);
+static int taos_release(struct inode *inode, struct file *file);
+static int taos_ioctl(struct inode *inode, struct file *file, unsigned

int cmd,

tsl258x.c +2 -0
+    unsigned long arg);
+static int taos_read(struct file *file, char *buf, size_t count, loff_t
Quoted 8 lines

tsl258x.c

Quoted 8 lines

*ppos);

tsl258x.c +7 -0
+static int taos_write(struct file *file, const char *buf, size_t count,
+    loff_t *ppos);
+static loff_t taos_llseek(struct file *file, loff_t offset, int orig);
+static int taos_fasync(int fd, struct file *file, int mode);
+static int taos_device_name(unsigned char *bufp, char **device_name);
+static int taos_als_calibrate(unsigned long als_cal_target);
+static void taos_als_auto_update(struct work_struct *update);

Could avoid most of this by a spot of reordering of the code. Only have function prototypes if you absolutely need them. It just makes code reading a tiny bit harder.

Quoted 9 lines

tsl258x.c

Quoted 9 lines
tsl258x.c +9 -0
+
+/*........................................................................*/
+/* Various /misc. emun*/
+enum {
+	TAOS_CHIP_UNKNOWN = 0, TAOS_CHIP_WORKING = 1, TAOS_CHIP_SLEEP = 2
+} TAOS_CHIP_WORKING_STATUS;
+
+/*........................................................................*/
+

As stated above, you'll need to justify this thoroughly or get rid of it. Version numbers almost always rot as people don't remember to update them.

Quoted 14 lines
Quoted 14 lines
tsl258x.c +14 -0
+char driver_version_id[] = DRIVER_VERSION_ID;
+
+/*........................................................................*/
+
+/* First device number */
+static dev_t taos_dev_number;
+
+/* Class structure for this device */
+static struct class *taos_class;
+
+/* Module device table */
+static struct i2c_device_id taos_idtable[] = {
+	{ DEVICE_ID, 0 },
+	{ } };
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+MODULE_DEVICE_TABLE(i2c, taos_idtable);
+
+/* Board and address info */
+static struct i2c_board_info taos_board_info[] = {
+	{ I2C_BOARD_INFO(DEVICE_ID, DEVICE_ADDR1), },
+	{ I2C_BOARD_INFO(DEVICE_ID, DEVICE_ADDR2), },
+	{ I2C_BOARD_INFO(DEVICE_ID, DEVICE_ADDR3), }, };
+

So you have the board info inside the driver? Please explain. The driver should handle the device alone, not any information on a particular implementation on a particular i2c bus...

Quoted 52 lines
Quoted 52 lines
tsl258x.c +52 -0
+static unsigned short const taos_addr_list[4] = {
+	DEVICE_ADDR1,
+	DEVICE_ADDR2,
+    DEVICE_ADDR3,
+    I2C_CLIENT_END };
+
+/* Client and device */
+static struct i2c_client *client;
+static struct device *devp;
+static struct i2c_client *bad_clientp[MAX_NUM_DEVICES];
+static int device_found;
+
+/* Driver definition */
+static struct i2c_driver taos_driver = {
+	.driver = {
+	.owner = THIS_MODULE,
+    .name = DEVICE_NAME, },
+    .id_table = taos_idtable,
+    .probe = taos_probe,
+    .remove = __devexit_p(taos_remove),
+    .suspend = taos_suspend,
+    .shutdown = taos_shutdown,
+    .resume = taos_resume, };
+
+/* Per-device data */
+static struct tsl258x_chip {
+	struct i2c_client *client;
+	struct iio_dev *iio_dev;
+	struct delayed_work update_lux;
+	struct work_struct work_thresh;
+	s64 event_timestamp;
+	struct cdev cdev;
+	struct fasync_struct *async_queue;
+	unsigned int addr;
+	char taos_id;
+	char taos_name[ID_NAME_SIZE];
+	char valid;
+	unsigned long last_updated;
+
+} *chip;
+
+/* File operations */
+static const struct file_operations taos_fops = {
+	.owner = THIS_MODULE,
+	.open =	taos_open,
+	.release = taos_release,
+	.read = taos_read,
+	.write = taos_write,
+	.llseek = taos_llseek,
+	.fasync = taos_fasync,
+	.ioctl = taos_ioctl, };
+
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +5 -0
+/* ................ Als info ..................*/
+struct taos_als_info als_cur_info;
+EXPORT_SYMBOL(als_cur_info);
+
+/*Next two vars are also used to determine irq type - in lieu status

info*/

tsl258x.c +2 -0
+bool als_on = FALSE;
+

What are these doing global within the driver?

Quoted 11 lines

tsl258x.c

Quoted 11 lines
tsl258x.c +11 -0
+static int device_released = 0x0000;
+static unsigned int irqnum = 0x0000;
+static DECLARE_WAIT_QUEUE_HEAD(taos_int_wait);
+
+/* Lux time scale */
+struct time_scale_factor {
+	unsigned int numerator;
+	unsigned int denominator;
+	unsigned int saturation;
+};
+

That name is almost guaranteed to clash at some point! Why not put this in your nice tsl258x_chip structure above?

Quoted 11 lines

tsl258x.c

Quoted 11 lines
tsl258x.c +5 -0
+/* ------ Mutex ------*/
+DEFINE_MUTEX(als_mutex);
+
+/*Structs & vars*/
+int taos_chip_status = TAOS_CHIP_UNKNOWN; /*Unknown = uninitialized to

start*/
+int taos_cycle_type; /* what is the type of cycle being run: ALS, PROX, or BOTH
+ */

tsl258x.c +2 -0
+struct taos_settings taos_settings;
+EXPORT_SYMBOL(taos_settings);

This seems to restrict you to only one such sensor....

Quoted 3 lines

tsl258x.c

Quoted 3 lines
tsl258x.c +3 -0
+
+/* Device configuration */
+#define MAX_SETTING_MEMBERS	6

Unused...

Quoted 7 lines

tsl258x.c

Quoted 7 lines

+static struct taos_settings *taos_settingsP; /*Pointer Needed for ioctl(s)*/

tsl258x.c +5 -0
+
+/*More Prototypes*/
+static int taos_i2c_read(u8 reg, u8 *val, unsigned int len);
+static int taos_i2c_write(u8 reg, u8 *val);
+static int taos_i2c_write_command(u8 reg);

Bit of code reordering an you won't need these.

Quoted 3 lines

tsl258x.c

Quoted 3 lines
tsl258x.c +3 -0
+
+/*   Work Queues used for bottom halves of self poll/push & IRQs  */
+static struct workqueue_struct *taos_wq0;

Should be in an instance specific structure (along with a lot of other stuff that I haven't commented on). A driver should allow for multiple taos258x chips wired to a given machine.

Quoted 10 lines

tsl258x.c

Quoted 10 lines
tsl258x.c +10 -0
+
+struct delayed_work *luxUd;
+/*........................................................................*/
+/*
+ Initial values for device - this values can/will be changed by driver.
+ and applications as needed.
+ These values are dynamic.
+ */
+u8 taos_config[8] = { 0x00, 0xee, 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00 };
+/*	                  cntrl atime intC  Athl0 Athl1 Athh0 Athh1 gain*/

static const please

Quoted 9 lines

tsl258x.c

Quoted 9 lines
tsl258x.c +9 -0
+
+/*
+ This structure is intentionally large to accommodate updates via
+ ioctl and proc input methods.
+ */
+struct taos_lux {
+	unsigned int ratio;
+	unsigned int ch0;
+	unsigned int ch1;

Can just define the size and specify
initial elements. c99 means the rest will be
zero initialized anyway iirc.

Quoted 7 lines
Quoted 7 lines
tsl258x.c +7 -0
+} taos_device_lux[] = {
+	{ 9830, 8520, 15729 },
+	{ 12452, 10807, 23344 },
+	{14746, 6383, 11705 },
+	{ 17695, 4063, 6554 },
+	{ 0, 0, 0 },
+	{ 0, 0, 0 },
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+	{ 0, 0, 0 },
+	{ 0, 0, 0 },
+	{ 0, 0, 0 },
+	{ 0, 0, 0 },
+	{ 0, 0, 0 } };
+
+struct taos_lux taos_lux;
+EXPORT_SYMBOL(taos_lux);

Why export? Who else needs to know?

Quoted 5 lines
Quoted 5 lines
tsl258x.c +2 -0
+
+int als_time_scale; /*computed, ratios lux due to als integration

time*/
+int als_saturation; /*computed, set to 90% of full scale of als integration*/

Again, in device instance specific structure.

Quoted 11 lines

tsl258x.c

Quoted 11 lines
tsl258x.c +11 -0
+
+/*Index = (0 - 3) Used to validate the gain selection index*/
+#define MAX_GAIN_STAGES	4
+struct gainadj {
+	s16 ch0;
+	s16 ch1;
+} gainadj[] = {
+	{ 1, 1 },
+	{ 8, 8 },
+	{ 16, 16 },
+	{ 107, 115 } };

static const again. Please run sparse and checkpatch against this driver. It'll show up a lot of this sort of thing for you...

Quoted 5 lines

tsl258x.c

Quoted 5 lines
tsl258x.c +5 -0
+
+struct taos_lux *taos_device_luxP;
+
+/*Input_dev*/
+static struct input_dev *taos_dev;

Again in device instance specific structure.

Quoted 2 lines

tsl258x.c

Quoted 2 lines
tsl258x.c +2 -0
+
+/*sysfs - interface functions*/

Definitely don't need to have prototypes for these. Just reorder the code so they aren't needed.

Quoted 79 lines
Quoted 79 lines
tsl258x.c +61 -0
+static ssize_t taos_device_id(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static DEVICE_ATTR(device_id, S_IRUGO, taos_device_id, NULL);
+
+static ssize_t taos_power_state_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_power_state_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(device_state, S_IRUGO | S_IWUSR,
+		taos_power_state_show,
+		taos_power_state_store);
+
+static ssize_t taos_gain_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_gain_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_gain, S_IRUGO | S_IWUSR,
+		taos_gain_show,
+		taos_gain_store);
+
+static ssize_t taos_interrupt_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_interrupt_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_interrupt, S_IRUGO | S_IWUSR,
+		taos_interrupt_show,
+		taos_interrupt_store);
+
+static ssize_t taos_als_time_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_als_time_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_time, S_IRUGO | S_IWUSR,
+		taos_als_time_show,
+		taos_als_time_store);
+
+static ssize_t taos_als_trim_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_als_trim_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_trim, S_IRUGO | S_IWUSR,
+		taos_als_trim_show,
+		taos_als_trim_store);
+
+static ssize_t taos_als_persistence_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_als_persistence_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_persistence, S_IRUGO | S_IWUSR,
+		taos_als_persistence_show,
+		taos_als_persistence_store);
+
+static ssize_t taos_als_cal_target_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_als_cal_target_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_target, S_IRUGO | S_IWUSR,
+		taos_als_cal_target_show,
+		taos_als_cal_target_store);
+
+static ssize_t taos_lux_show(struct device *dev, struct

device_attribute *attr,

tsl258x.c +17 -0
+    char *buf);
+static DEVICE_ATTR(lux, S_IRUGO, taos_lux_show, NULL);
+
+static ssize_t taos_do_calibrate(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, taos_do_calibrate);
+
+static ssize_t taos_als_thresh_low_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
+static ssize_t taos_als_thresh_low_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_lowT, S_IRUGO | S_IWUSR,
+		taos_als_thresh_low_show,
+		taos_als_thresh_low_store);
+
+static ssize_t taos_als_thresh_high_show(struct device *dev,
+    struct device_attribute *attr, char *buf);
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+static ssize_t taos_als_thresh_high_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len);
+static DEVICE_ATTR(als_highT, S_IRUGO | S_IWUSR,
+	taos_als_thresh_high_show,
+	taos_als_thresh_high_store);
+
+
+static struct attribute *sysfs_attrs_ctrl[] = {

There are a number of 'new' abi elements here. Remotely general ones need full documentation in staging/iio/Documentation/sysfs-bus-iio-light Note some relevant elemetns are in sysfs-bus-iio as they are more general...

Quoted 4 lines

tsl258x.c

Quoted 4 lines
tsl258x.c +4 -0
+	&dev_attr_device_id.attr,
+    &dev_attr_device_state.attr,
+    &dev_attr_als_gain.attr,
+    &dev_attr_als_interrupt.attr,

What's this for example?

Quoted 2 lines

tsl258x.c

Quoted 2 lines
tsl258x.c +2 -0
+    &dev_attr_als_time.attr,
+    &dev_attr_als_trim.attr,

Again, what's this? I'd imagine Trim corresponds to als_calibbias...

Quoted 5 lines

tsl258x.c

Quoted 5 lines
tsl258x.c +5 -0
+    &dev_attr_als_persistence.attr,
+    &dev_attr_als_target.attr,
+    &dev_attr_lux.attr,
+    &dev_attr_calibrate.attr,
+    &dev_attr_als_lowT.attr,

These almost certainly have well defined abi elements in IIO, but without a close read I'm not sure what they do... Our events interface is pretty thorough so if we have missed a case someone needs then I want to know!

Quoted 5 lines

tsl258x.c

Quoted 5 lines
tsl258x.c +5 -0
+    &dev_attr_als_highT.attr,
+    NULL };
+
+static struct attribute_group tsl258x_attribute_group[] = {
+	{ .attrs = sysfs_attrs_ctrl }, };

Only one group, so don't make an array of them...

Quoted 6 lines

tsl258x.c

Quoted 6 lines
tsl258x.c +6 -0
+
+
+/*===========================================================================*/
+/*
+
+ FUNCTIONS BEGIN HERE

To much information ;) We can see that. Problem with standard templates is that they tend to have comments that you really want to get rid of for final code!

Quoted 9 lines
Quoted 9 lines
tsl258x.c +7 -0
+
+ */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/**@defgroup Initialization Driver Initialization
+ * The sections applies to functions for driver initialization,

instantiation,
+ * _exit, \n

new lines in a comment?

Quoted 4 lines
Quoted 4 lines

+ * and user-land application invoking (ie. open).\n
+ * Also included in this section is the initial interrupt handler (handler
+ * bottom halves are in the respective

Quoted 8 lines
Quoted 8 lines

+ * sections).
+ * @{
+ */

tsl258x.c +4 -0
+
+/*........................................................................*/
+/** Driver initialization - device probe is initiated here, to identify
+ * a valid device if present on any of the i2c buses and at any

address.

This is non standard. Please do your registration the same way pretty much every other i2c device does it, via a board file.
Or if you really can't do this you'll need to explain in some detail why not. I'm not going to even attempt to figure out what this is doing...

If documenting, please stick to kerneldoc format (not quite sure what format this is in...)

Quoted 15 lines
Quoted 15 lines

+ * \param none
+ * \return: int (0 = OK)
+ * \note H/W Interrupt are device/product dependent.
+ * Attention is required to the definition and configuration.
+ */

tsl258x.c +10 -0
+/*........................................................................*/
+static int __init taos_init(void)
+{
+	u32 err;
+	int i, j, k, ret = 0;
+	struct i2c_adapter *my_adap;
+	static int num_bad = 0x0000;
+
+#ifdef TAOS_DEBUG
+	printk(KERN_ERR "Loading TAOS Driver\n");
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+	printk(KERN_INFO "\nRequesting a GPIO for irq\n");
+#endif
+/*......................................................................*/
+	/*Create the work queue and allocate kernel memory for the
+	 * scheduled workers*/
+	taos_wq0 = create_workqueue("taos_work_queue");
+	if (taos_wq0)
+		luxUd = kmalloc(sizeof(struct delayed_work), GFP_KERNEL);

Just put the delayed work struct in the device instance specific data structure you are going to add and all will be simpler. In that case, the vast majority of what is here needs to be in probe (as it depends on an instance) rather than in the driver init function.

Quoted 76 lines
Quoted 76 lines
tsl258x.c +76 -0
+
+	/*Make sure we have what we need */
+	if ((!taos_wq0) || (!luxUd))
+		return -ENOMEM;
+
+/*.....................................................................*/
+	ret = alloc_chrdev_region(&taos_dev_number, 0, MAX_NUM_DEVICES,
+		DEVICE_NAME);
+	if (ret < 0) {
+		dev_err(devp, "taos_device_drvr: alloc_chrdev_region()"
+			"failed in taos_init()\n");
+		return ret;
+	}
+	taos_class = class_create(THIS_MODULE, DEVICE_NAME);
+	chip = kmalloc(sizeof(struct tsl258x_chip), GFP_KERNEL);
+	if (!chip) {
+		dev_err(devp, "taos_device_drvr: kmalloc for "
+			"struct tsl258x_chip failed in taos_init()\n");
+		ret = -ENOMEM;
+		goto exit_3;
+	}
+	memset(chip, 0, sizeof(struct tsl258x_chip));
+	cdev_init(&chip->cdev, &taos_fops);
+	chip->cdev.owner = THIS_MODULE;
+
+	ret = cdev_add(&chip->cdev, taos_dev_number, 1);
+	if (ret < 0) {
+		dev_err(devp, "taos_device_drvr: cdev_add() failed in "
+				"taos_init()\n");
+		goto exit_2;
+	}
+	device_create(taos_class, NULL, MKDEV(MAJOR(taos_dev_number), 0),
+			&taos_driver , DEVICE_NAME);
+	ret = i2c_add_driver(&taos_driver);
+	if (ret < 0) {
+		dev_err(devp, "taos_device_drvr: i2c_add_driver() failed in "
+				"taos_init()\n");
+		goto exit_1;
+	}
+	device_found = 0;
+	for (i = 0; i < I2C_MAX_ADAPTERS; i++) {
+		my_adap = i2c_get_adapter(i);
+		if (my_adap == NULL)
+			break;
+		for (j = 0; j < MAX_NUM_DEVICES; j++) {
+			client = i2c_new_probed_device(my_adap,
+					&taos_board_info[j], taos_addr_list);
+			if ((client) && (!device_found)) {
+				bad_clientp[num_bad] = client;
+				num_bad++;
+			}
+			if (device_found)
+				break;
+		}
+		if (num_bad) {
+			for (k = 0; k < num_bad; k++)
+				i2c_unregister_device(bad_clientp[k]);
+			num_bad = 0;
+		}
+		if (device_found)
+			break;
+	}
+	if (device_found) {
+		devp = &client->dev;
+		goto exit_4;
+	} else {
+		if (client)
+			i2c_unregister_device(client);
+		i2c_del_driver(&taos_driver);
+		device_destroy(taos_class, MKDEV(MAJOR(taos_dev_number), 0));
+		cdev_del(&chip->cdev);
+		kfree(chip);
+		class_destroy(taos_class);
+		unregister_chrdev_region(taos_dev_number, MAX_NUM_DEVICES);
+		printk(KERN_ERR "taos_device_drvr: taos_init() found no device\n");
+		return -ENODEV;
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+	}
+
+exit_1: device_destroy(taos_class, MKDEV(MAJOR(taos_dev_number), 0));
+exit_2: cdev_del(&chip->cdev);
+	kfree(chip);
+exit_3: class_destroy(taos_class);
+	unregister_chrdev_region(taos_dev_number, MAX_NUM_DEVICES);
+

Flow in this function probably wants rethinking... Its very confusing...

Quoted 2 lines

tsl258x.c

Quoted 2 lines
tsl258x.c +2 -0
+exit_4:
+

Should see this sort of explicit formatting. One newline at the end of a printk is quite enough!

Quoted 6 lines

tsl258x.c

Quoted 6 lines
tsl258x.c +6 -0
+	printk(KERN_INFO "\n\nallocating iio device\n");
+	chip->iio_dev = iio_allocate_device();
+	if (!chip->iio_dev)
+		printk(KERN_INFO "iio allocation failed\n");
+
+	chip->iio_dev->attrs =

The type cast here is a little disturbing and correctly shows that your taking the address of the address of a struct attribute_group is a bug... It might evaluate to the same value, but it definitely isn't the right way to use it.

Quoted 199 lines
Quoted 199 lines
tsl258x.c +17 -0
+			(struct attribute_group *)&tsl258x_attribute_group;
+	chip->iio_dev->dev.parent = &client->dev;
+	chip->iio_dev->dev_data = (void *)(chip);
+	chip->iio_dev->driver_module = THIS_MODULE;
+	chip->iio_dev->modes = INDIO_DIRECT_MODE;
+	err = iio_device_register(chip->iio_dev);
+
+	if (err)
+		printk(KERN_INFO "iio registration failed\n");
+
+	/*Make sure the chip is off*/
+	taos_chip_off();
+	/*Load up the V2 defaults (these are hard coded defaults for now)*/
+	taos_defaults();
+	/*Set any other defaults that are needed to initialize
+	 * (such as taos_cycle_type).
+	 Note: these settings can/will be changed based on user

requirements.*/

tsl258x.c +7 -0
+	taos_cycle_type = LIGHT;
+	/*Initialize the work queues*/
+
+	INIT_DELAYED_WORK(luxUd, taos_als_auto_update);
+
+	/*...................................................................*/
+	/*This section populates the input structure and registers the

device*/

tsl258x.c +55 -0
+#ifdef EVENT_NOTIFY
+	taos_dev = input_allocate_device();
+	if (!taos_dev) {
+		printk(KERN_ERR "__init: Not enough memory for input_dev\n");
+		return ret;
+	}
+
+	/*Since we now have the struct, populate.*/
+	taos_dev->name = DEVICE_ID;
+	taos_dev->id.bustype = BUS_I2C;
+	taos_dev->id.vendor = 0x0089;
+	taos_dev->id.product = 0x2581;
+	taos_dev->id.version = 3;
+	taos_dev->phys = "/dev/taos";
+
+	/*This device one value (LUX)
+	 Other devices may have LUX and PROX.
+	 Thus we use the ABS_X so ABS_Y is logically available.*/
+	set_bit(EV_ABS, taos_dev->evbit);
+	set_bit(ABS_X, taos_dev->absbit);
+
+	/*Only returns only positive LUX values
+	 No noise filter, no flat level*/
+	taos_dev->absmin[ABS_X] = 0;
+	taos_dev->absmax[ABS_X] = MAX_LUX;
+	taos_dev->absfuzz[ABS_X] = 0;
+	taos_dev->absflat[ABS_X] = 0;
+
+	/*And register..*/
+	ret = input_register_device(taos_dev);
+	if (ret) {
+		printk(KERN_ERR "button.c: Failed to register input_dev\n");
+		goto err_free_dev;
+	}
+#endif	/*End of input_event*/
+
+
+	/*invoke sysfs
+	 invoke iio subsystem*/
+
+	err = sysfs_create_group(&chip->client->dev.kobj,
+			tsl258x_attribute_group);
+	if (err < 0) {
+		dev_err(&chip->client->dev, "Sysfs registration failed\n");
+		goto err_free_dev;
+	}
+
+	if (taos_wq0)
+		ret = queue_delayed_work(taos_wq0, luxUd, LUX_UPDATE_PERIOD);
+	else
+		printk(KERN_INFO "\nNO taos_wq0 for luxUd\n\n");
+
+	return ret; /*Normal return*/
+
+/*In the event we had a problem REGISTERING the device for

input_events*/

tsl258x.c +32 -0
+err_free_dev:
+	input_free_device(taos_dev);
+	return ret;
+}
+
+/**
+ * Driver exit
+ */
+static void __exit taos_exit(void)
+{
+	printk(KERN_INFO "TAOS driver __exit\n");
+
+	if (taos_wq0) {
+		flush_workqueue(taos_wq0);
+		destroy_workqueue(taos_wq0);
+	}
+
+	input_free_device(taos_dev);
+
+	if (client)
+		i2c_unregister_device(client);
+	i2c_del_driver(&taos_driver);
+	device_destroy(taos_class, MKDEV(MAJOR(taos_dev_number), 0));
+	cdev_del(&chip->cdev);
+	kfree(chip);
+	class_destroy(taos_class);
+	unregister_chrdev_region(taos_dev_number, MAX_NUM_DEVICES);
+
+}
+
+/**
+ * Client probe function - When a valid device is found, the driver's

device
+ * data structure is updated, and initialization completes successfully.
+ */

tsl258x.c +81 -0
+static int taos_probe(struct i2c_client *clientp,
+    const struct i2c_device_id *idp) {
+	int i, ret = 0;
+	unsigned char buf[MAX_DEVICE_REGS];
+	char *device_name;
+
+	if (device_found)
+		return -ENODEV;
+	if (!i2c_check_functionality(clientp->adapter,
+		I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&clientp->dev,
+			"taos_probe() - i2c smbus byte data "
+			"functions unsupported\n");
+		return -EOPNOTSUPP;
+	}
+	if (!i2c_check_functionality(clientp->adapter,
+		I2C_FUNC_SMBUS_WORD_DATA)) {
+		dev_warn(&clientp->dev,
+			"taos_probe() - i2c smbus word data "
+			"functions unsupported\n");
+	}
+	if (!i2c_check_functionality(clientp->adapter,
+		I2C_FUNC_SMBUS_BLOCK_DATA)) {
+		dev_err(&clientp->dev,
+			"taos_probe() - i2c smbus block data "
+			"functions unsupported\n");
+	}
+	chip->client = clientp;
+	i2c_set_clientdata(clientp, chip);
+	for (i = 0; i < MAX_DEVICE_REGS; i++) {
+		ret = i2c_smbus_write_byte(clientp, (CMD_REG | (CNTRL + i)));
+		if (ret < 0) {
+			dev_err(&clientp->dev, "i2c_smbus_write_byte() to cmd "
+				"reg failed in taos_probe(), err = %d\n", ret);
+			return ret;
+		}
+		buf[i] = i2c_smbus_read_byte(clientp);
+	}
+	ret = taos_device_name(buf, &device_name);
+	if (ret < 0) {
+		dev_info(&clientp->dev, "i2c device found - does not match "
+			"expected id in taos_probe() 1\n");
+	}
+	if (strcmp(device_name, DEVICE_ID)) {
+		dev_info(&clientp->dev, "i2c device found - does not match "
+			"expected id in taos_probe()2\n");
+	} else {
+		dev_info(&clientp->dev, "i2c device found - matches expected "
+			"id of %s in taos_probe()\n", device_name);
+		device_found = 1;
+	}
+	ret = i2c_smbus_write_byte(clientp, (CMD_REG | CNTRL));
+	if (ret < 0) {
+		dev_err(&clientp->dev, "i2c_smbus_write_byte() to cmd reg "
+			"failed in taos_probe(), err = %d\n", ret);
+		return ret;
+	}
+	strlcpy(clientp->name, DEVICE_ID, I2C_NAME_SIZE);
+	strlcpy(chip->taos_name, DEVICE_ID, ID_NAME_SIZE);
+	chip->valid = 0;
+
+	taos_settingsP = kmalloc(sizeof(struct taos_settings), GFP_KERNEL);
+	if (!(taos_settingsP)) {
+		dev_err(&clientp->dev,
+		"kmalloc for struct taos_settings failed in taos_probe()\n");
+		return -ENOMEM;
+	}
+
+	return ret;
+}
+
+/**
+ * Client remove
+ */
+static int __devexit taos_remove(struct i2c_client *client)
+{
+	return 0;
+}
+
+/**
+ * Device open function
Quoted 8 lines

tsl258x.c

Quoted 8 lines

+ */

tsl258x.c +7 -0
+static int taos_open(struct inode *inode, struct file *file)
+{
+	int ret = 0;
+	struct tsl258x_chip *chip;
+
+
+	/*For debug - Make it ez to c on the console*/

This certainly should have gone from the code before submission. Don't want this in any production system kernel log!

Quoted 45 lines
Quoted 45 lines
tsl258x.c +2 -0
+#ifdef TAOS_DEBUG
+	printk(KERN_INFO "***************************************************

\n");

tsl258x.c +1 -0
+	printk(KERN_INFO "*                                                 *

\n");

tsl258x.c +1 -0
+	printk(KERN_INFO "*                  TAOS DEVICE                    *

\n");

tsl258x.c +1 -0
+	printk(KERN_INFO "*                    DRIVER                       *

\n");

tsl258x.c +1 -0
+	printk(KERN_INFO "*                     OPEN                        *

\n");

tsl258x.c +1 -0
+	printk(KERN_INFO "***************************************************

\n");

tsl258x.c +32 -0
+#endif
+
+	chip = container_of(inode->i_cdev, struct tsl258x_chip, cdev);
+	if (strcmp(chip->taos_name, DEVICE_ID) != 0) {
+		dev_err(devp, "device name error in taos_open(), shows %s\n",
+		    chip->taos_name);
+		return -ENODEV;
+	}
+
+	device_released = 0;
+	return ret;
+}
+
+/**
+ * Device release
+ */
+static int taos_release(struct inode *inode, struct file *file)
+{
+	int ret = 0;
+	struct tsl258x_chip *chip;
+
+	device_released = 1;
+	chip = container_of(inode->i_cdev, struct tsl258x_chip, cdev);
+	if (strcmp(chip->taos_name, DEVICE_ID) != 0) {
+		dev_err(devp, "device id incorrect in taos_release(), shows "
+			"%s\n", chip->taos_name);
+		ret = -ENODEV;
+	}
+	taos_fasync(-1, file, 0);
+	free_irq(irqnum, chip);
+	return ret;
+}
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +3 -0
+
+/**
+ * Name verification - uses default register values to identify the

Taos device
+ */

tsl258x.c +3 -0
+static int taos_device_name(unsigned char *bufp, char **device_name)
+{
+	if (((bufp[0x12] & 0xf0) == 0x00) || (bufp[0x12] == 0x08)) {

Are these internal names? If so please use part numbers instead. Much easier for everyone else!

Quoted 7 lines

tsl258x.c

Quoted 7 lines
tsl258x.c +7 -0
+		*device_name = "tritonFN";
+		return 1;
+	} else if (((bufp[0x12] & 0xf0) == 0x90)) {
+		*device_name = "skateFN";
+		return 1;
+	}
+	*device_name = "unknown";

That's probably a very bad sign...

Quoted 154 lines
Quoted 154 lines
tsl258x.c +6 -0
+	printk(KERN_INFO "Identified %s\n", *device_name);
+	return 0;
+}
+
+/**
+ * Device read - reads are permitted only within the range of the

accessible
+ * registers of the device. The data read is copied safely to user space.
+ */
+static int taos_read(struct file *file, char *buf, size_t count, loff_t *ppos)

tsl258x.c +47 -0
+{
+	int ret = 0;
+	struct tsl258x_chip *chip;
+	u8 my_buf[MAX_DEVICE_REGS];
+	u8 reg, i = 0, xfrd = 0;
+
+
+	/*Make sure we not in the middle of accessing the device*/
+	if (mutex_trylock(&als_mutex) == 0) {
+		printk(KERN_INFO "Can't get ALS mutex\n");
+		return -1; /*busy, so return LAST VALUE*/
+	}
+
+	if ((*ppos < 0) || (*ppos >= MAX_DEVICE_REGS) || ((*ppos + count)
+			> MAX_DEVICE_REGS)) {
+		dev_err(devp, "reg limit check failed in taos_read()\n");
+		mutex_unlock(&als_mutex);
+		return -EINVAL;
+	}
+	reg = (u8) *ppos;
+	chip = container_of(file->f_dentry->d_inode->i_cdev,
+			struct tsl258x_chip, cdev);
+	while (xfrd < count) {
+		ret = i2c_smbus_write_byte(chip->client, (CMD_REG | reg));
+		if (ret < 0) {
+			dev_err(devp, "i2c_smbus_write_byte to cmd reg failed "
+				"in taos_read(), err = %d\n", ret);
+			mutex_unlock(&als_mutex);
+			return ret;
+		}
+		my_buf[i++] = i2c_smbus_read_byte(chip->client);
+		reg++;
+		xfrd++;
+	}
+	ret = copy_to_user(buf, my_buf, xfrd);
+	if (ret) {
+		dev_err(devp, "copy_to_user failed in taos_read()\n");
+		mutex_unlock(&als_mutex);
+		return -ENODATA;
+	}
+
+	mutex_unlock(&als_mutex);
+	return (int) xfrd;
+}
+
+/**
+ * Device write - writes are permitted only within the range of the

accessible
+ * registers of the device. The data written is copied safely from user space.
+ */

tsl258x.c +47 -0
+static int taos_write(struct file *file, const char *buf, size_t count,
+    loff_t *ppos)
+{
+	int ret = 0;
+	struct tsl258x_chip *chip;
+	u8 my_buf[MAX_DEVICE_REGS];
+	u8 reg, i = 0, xfrd = 0;
+
+	if (mutex_trylock(&als_mutex) == 0) {
+		printk(KERN_INFO "Can't get ALS mutex\n");
+		return -1; /*busy, so return LAST VALUE*/
+	}
+
+	if ((*ppos < 0) || (*ppos >= MAX_DEVICE_REGS) || ((*ppos + count)
+			> MAX_DEVICE_REGS)) {
+		dev_err(devp, "reg limit check failed in taos_write()\n");
+		mutex_unlock(&als_mutex);
+		return -EINVAL;
+	}
+	reg = (u8) *ppos;
+	ret = copy_from_user(my_buf, buf, count);
+	if (ret) {
+		dev_err(devp, "copy_from_user failed in taos_write()\n");
+		mutex_unlock(&als_mutex);
+		return -ENODATA;
+		}
+	chip = container_of(file->f_dentry->d_inode->i_cdev,
+		struct tsl258x_chip, cdev);
+	while (xfrd < count) {
+		ret = i2c_smbus_write_byte_data(chip->client, (CMD_REG | reg),
+		    my_buf[i++]);
+		if (ret < 0) {
+			dev_err(devp, "i2c_smbus_write_byte_data failed in "
+				"taos_write()\n");
+			mutex_unlock(&als_mutex);
+			return ret;
+		}
+		reg++;
+		xfrd++;
+	}
+
+	mutex_unlock(&als_mutex);
+	return (int) xfrd;
+}
+
+/**
+ * Device seek - seeks are permitted only within the range of the

accessible
+ * registers of the device. The new offset is returned.
+ */

tsl258x.c +30 -0
+static loff_t taos_llseek(struct file *file, loff_t offset, int orig)
+{
+	loff_t new_pos;
+
+	if ((offset >= MAX_DEVICE_REGS) || (orig < 0) || (orig > 1)) {
+		dev_err(devp, "offset param limit or whence limit check failed "
+			"in taos_llseek()\n");
+		return -EINVAL;
+	}
+	switch (orig) {
+	case 0:
+		new_pos = offset;
+		break;
+	case 1:
+		new_pos = file->f_pos + offset;
+		break;
+	default:
+		return -EINVAL;
+		break;
+	}
+	if ((new_pos < 0) || (new_pos >= MAX_DEVICE_REGS)) {
+		dev_err(devp, "new offset check failed in taos_llseek()\n");
+		return -EINVAL;
+	}
+	file->f_pos = new_pos;
+	return new_pos;
+}
+
+/**
+ * Fasync function - called when the FASYNC flag in the file descriptor

of the
+ * device indicates that a new user program wants to be added to the wait queue
+ * of the device - calls fasync_helper() to add the new entry to the queue.
+ */

tsl258x.c +5 -0
+static int taos_fasync(int fd, struct file *file, int mode)
+{
+	chip = container_of(file->f_dentry->d_inode->i_cdev, struct
+			tsl258x_chip, cdev);
+	return fasync_helper(fd, file, mode, &chip->async_queue);
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+}
+
+/**
+ * Provides initial operational parameter defaults.\n
+ * These defaults may be changed by the following:\n
+ * - system deamon
+ * - user application (via ioctl)
+ * - directly writing to the taos procfs file

There's a procfs file? That's not going to go down well these days...

Quoted 27 lines
Quoted 27 lines

+ * - external kernel level modules / applications
+ */

tsl258x.c +25 -0
+static void taos_defaults()
+{
+	/*Operational parameters*/
+	taos_cycle_type = LIGHT;
+	/*default is ALS only*/
+	taos_settings.als_time = 100;
+	/*must be a multiple of 50mS*/
+	taos_settings.als_gain = 0;
+	/*this is actually an index into the gain table*/
+	/*assume clear glass as default*/
+	taos_settings.als_gain_trim = 1000;
+	/*default gain trim to account for aperture effects*/
+	taos_settings.als_persistence = 0;
+	/*default number of 'out of bounds' b4 interrupt*/
+	taos_settings.als_cal_target = 130;
+	/*Known external ALS reading used for calibration*/
+	taos_settings.interrupts_enabled = 0;
+	/*Interrupts enabled (ALS) 0 = none*/
+
+	/*Initialize ALS data to defaults*/
+	als_cur_info.als_ch0 = 0;
+	als_cur_info.als_ch1 = 0;
+	als_cur_info.lux = 0;
+
+	taos_settings.als_thresh_high = ALS_THRESHOLD_HI_LIMIT;
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+	taos_settings.als_thresh_low = ALS_THRESHOLD_LO_LIMIT;
+
+
+#ifdef TAOS_DEBUG
+printk(KERN_INFO "\nDEFAULTS LOADED\n");
+#endif
+}
+EXPORT_SYMBOL(taos_defaults);

Why export?

Quoted 198 lines
Quoted 198 lines
tsl258x.c +7 -0
+/*........................................................................*/
+/*@}	End of Initial*/
+
+
+/*===========================================================================*/
+/**@defgroup IOCTL User ioctl Functions
+ * The section applies to 'ioctl' calls used primarily to support

user-land applications.\n
+ * @{
+ */

tsl258x.c +4 -0
+/**
+ * Ioctl functions - each one is documented below.
+ */
+static int taos_ioctl(struct inode *inode, struct file *file, unsigned

int cmd,

tsl258x.c +13 -0
+    unsigned long arg) {
+	int ret = 0;
+	struct tsl258x_chip *chip;
+	int tmp;
+	/*u8 reg_val;*/
+
+	chip = container_of(inode->i_cdev, struct tsl258x_chip, cdev);
+	switch (cmd) {
+	/**
+	 * - ALS_ON - called to set the device in ambient light sense mode.
+	 * configured values of light integration time, initial interrupt
+	 * filter, gain, and interrupt thresholds (if interrupt driven) are
+	 * initialized. Then power, adc, (and interrupt if needed) are

enabled.

tsl258x.c +30 -0
+	 */
+	case TAOS_IOCTL_ALS_ON:
+	  taos_cycle_type = LIGHT;
+	  printk(KERN_INFO "ALS On\n");
+	  return taos_chip_on();
+	break;
+	/**
+	* - ALS_OFF - called to stop ambient light sense mode of operation.
+	* Clears the filter history, and clears the control register.
+	*/
+	case TAOS_IOCTL_ALS_OFF:
+		taos_chip_off();
+		break;
+	/**
+	* - ALS_DATA - request for current ambient light data. If correctly
+	* enabled and valid data is available at the device, the function
+	* for lux conversion is called, and the result returned if valid.
+	*/
+	case TAOS_IOCTL_ALS_DATA:
+		/*Are we actively updating the struct?*/
+		if ((taos_settings.interrupts_enabled
+				& CNTL_ALS_INT_ENBL) == 0x00)
+			return taos_get_lux(); /*No - so get fresh data*/
+		else
+			return als_cur_info.lux;
+			/*Yes - data is fresh as last change*/
+		break;
+
+	/**
+	* - ALS_CALIBRATE - called to run one calibration cycle, during

assembly.

tsl258x.c +11 -0
+	* The lux value under a known intensity light source is used to obtain
+	* a "gain trim" multiplier which is to be used to normalize subsequent
+	* lux data read from the device, after the calibration is completed.
+	* This 'known intensity should be the value of als_cal_target.
+	* If not - make is so!
+	*/
+	case TAOS_IOCTL_ALS_CALIBRATE:
+		return taos_als_calibrate(taos_settings.als_cal_target);
+		break;
+	/**
+	* - CONFIG-GET - returns the current device configuration values to

the

tsl258x.c +105 -0
+	* caller. The user mode application can display or store configuration
+	* values, for future reconfiguration of the device, as needed.
+	* Refer to structure "taos_settings" in '.h' file.
+	*/
+	case TAOS_IOCTL_CONFIG_GET:
+		ret = copy_to_user((struct taos_settings *) arg, &taos_settings,
+			sizeof(struct taos_settings));
+		if (ret) {
+			dev_err(devp,
+				"copy_to_user() failed in ioctl config_get\n");
+			return -ENODATA;
+		}
+		return ret;
+		break;
+	/**
+	* - GET_ALS - returns the current ALS structure values to the
+	* caller. The values returned represent the last call to
+	* taos_get_lux().  This ioctl can be used when the driver
+	* is operating in the interrupt mode.
+	* Refer to structure "als_cur_info" in '.h' file.
+	*/
+	case TAOS_IOCTL_GET_ALS:
+		ret = copy_to_user((struct taos_als_info *) arg,
+			&als_cur_info,
+			sizeof(struct taos_als_info));
+		if (ret) {
+			dev_err(devp,
+			"copy_to_user() failed in ioctl to get "
+				"taos_als_info\n");
+			return -ENODATA;
+			}
+		return ret;
+		break;
+	/**
+	* - CONFIG-SET - used by a user mode application to set the desired
+	* values in the driver's in memory copy of the device configuration
+	* data. Light integration times are aligned optimally, and driver
+	* global variables dependent on configured values are updated here.
+	* Refer to structure "taos_settings" in '.h' file.
+	*/
+	case TAOS_IOCTL_CONFIG_SET:
+		ret = copy_from_user(&taos_settings,
+			(struct taos_settings *) arg,
+			sizeof(struct taos_settings));
+		if (ret) {
+			dev_err(devp,
+			"copy_from_user() failed in ioctl config_set\n");
+			return -ENODATA;
+			}
+		return ret;
+		break;
+	/**
+	* - LUX_TABLE-GET - returns the current LUX coefficients table to the
+	* caller. The user mode application can display or store the table
+	* values, for future re-calibration of the device, as needed.
+	* Refer to structure "taos_lux" in '.h' file.
+	*/
+	case TAOS_IOCTL_LUX_TABLE_GET:
+		ret = copy_to_user((struct taos_lux *) arg,
+			&taos_device_lux,
+			sizeof(taos_device_lux));
+		if (ret) {
+			dev_err(devp,
+			"copy_to_user() failed in ioctl "
+			"TAOS_IOCTL_LUX_TABLE_GET\n");
+			return -ENODATA;
+			}
+		return ret;
+		break;
+	/**
+	* - LUX TABLE-SET - used by a user mode application to set the desired
+	* LUX table values in the driver's in memory copy of the lux table
+	* Refer to structure "taos_lux" in '.h' file.
+	*/
+	case TAOS_IOCTL_LUX_TABLE_SET:
+		ret = copy_from_user(&taos_lux,
+			(struct taos_lux *) arg,
+			sizeof(taos_device_lux));
+		if (ret) {
+			dev_err(devp, "copy_from_user() failed in ioctl "
+				"TAOS_IOCTL_LUX_TABLE_SET\n");
+			return -ENODATA;
+			}
+#ifdef ASYNC_NOTIFY
+		/*Notify the user-land app (if any)*/
+		if (chip->async_queue)
+			kill_fasync(&chip->async_queue, SIGIO, POLL_IN);
+#endif
+
+
+		return ret;
+		break;
+	/**
+	* - TAOS_IOCTL_ID - used to query driver name & version
+	*/
+	case TAOS_IOCTL_ID:
+		ret = copy_to_user((char *) arg,
+			&driver_version_id,
+			sizeof(driver_version_id));
+		dev_info(devp, "%s\n", DRIVER_VERSION_ID);
+		return ret;
+		break;
+	/*
+	* - TAOS_IOCTL_SET_GAIN - used to set/change the device analog gain.
+	* The value passed into here from the user is the index into the

table.

tsl258x.c +20 -0
+	* Index = (0 - 3) Used to validate the gain selection index
+	*/
+	case TAOS_IOCTL_SET_GAIN:
+		get_user(tmp, (int *)arg);
+		if (tmp > MAX_GAIN_STAGES)
+			return -1;
+		taos_settings.als_gain = tmp;
+		ret = taos_i2c_write(CMD_REG|GAIN, (u8 *)&tmp);
+		if (ret < 0) {
+			dev_err(devp, "taos_i2c_write to turn on device "
+				"failed in taos_chip_on.\n");
+			return -1;
+			}
+		return ret;
+		break;
+
+	default:
+		return -EINVAL;
+		break;
+		}
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+
+		return ret;
+
+	}
+/*........................................................................*/
+/*@}	end of IOCTL section*/
+
+

Wrong comment format and lots of obvious info. Please remove!

Quoted 34 lines
Quoted 34 lines
tsl258x.c +14 -0
+/*===========================================================================*/
+/**@defgroup ALS Ambient Light Sense (ALS)
+ * The section applies to the ALS related functions.\n
+ * Other ALS releated functions may appear elsewhere in the code.\n
+ * @{
+ */
+
+/*........................................................................*/
+
+/**
+ * Reads and calculates current lux value.
+ * 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. The array taos_device_luxP[]
+ * declared above 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.
+ *
+ * \param none
+ * \return int -1 = Failure, Lux Value = success
+ */

tsl258x.c +2 -0
+static int taos_get_lux(void)
+{
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+	u32 ch0, ch1; /* separated ch0/ch1 data from device */
+	u32 lux; /* raw lux calculated from device data */
+	u32 ratio;
+	u8 buf[5];
+	struct taos_lux *p;
+
+	int i, ret;
+

= 0; will do the job just as well.

Quoted 150 lines
Quoted 150 lines
tsl258x.c +141 -0
+	u32 ch0lux = 0x0000;
+	u32 ch1lux = 0x0000;
+
+	if (mutex_trylock(&als_mutex) == 0) {
+		printk(KERN_INFO "Can't get ALS mutex\n");
+		return als_cur_info.lux; /*busy, so return LAST VALUE*/
+	}
+
+	if (taos_chip_status != TAOS_CHIP_WORKING) {
+		/*device is not enabled*/
+		printk(KERN_INFO "device is not enabled\n");
+		mutex_unlock(&als_mutex);
+		return -1;
+	}
+
+	if ((taos_cycle_type & LIGHT) == 0) {
+		/*device not in ALS mode*/
+		printk(KERN_INFO "device not in ALS mode\n");
+		mutex_unlock(&als_mutex);
+		return -1;
+	}
+
+	ret = taos_i2c_read((CMD_REG), &buf[0], 1);
+	if (ret < 0) {
+		printk(KERN_INFO "taos_i2c_read() to CMD_REG reg failed in "
+		"taos_get_lux()\n");
+		mutex_unlock(&als_mutex);
+		return -1;
+	}
+	/* is data new & valid */
+	if (!(buf[0] & STA_ADCINTR)) {
+		printk(KERN_INFO "Data not valid, so return LAST VALUE\n");
+		mutex_unlock(&als_mutex);
+		return als_cur_info.lux; /*have no data, so return LAST VALUE*/
+	}
+
+	for (i = 0; i < 4; i++) {
+		ret = taos_i2c_read((CMD_REG | (ALS_CHAN0LO + i)), &buf[i], 1);
+		if (ret < 0) {
+			dev_err(devp,
+			"taos_i2c_read() to (ALS_CHAN0LO + i) failed in "
+			"taos_get_lux()\n");
+			mutex_unlock(&als_mutex);
+			return -1;
+		}
+	}
+
+	/* clear status, really interrupt status (interrupts are off), but
+	 *  we use the bit anyway */
+	ret = taos_i2c_write_command(CMD_REG | CMD_SPL_FN | CMD_ALS_INTCLR);
+	if (ret < 0) {
+		dev_err(devp,
+		"taos_i2c_write_command failed in "
+		"taos_chip_on, err = %d\n", ret);
+		mutex_unlock(&als_mutex);
+		return -1; /*have no data, so return fail*/
+	}
+
+	/* extract ALS/lux data */
+	ch0 = (buf[1] << 8) | buf[0];
+	ch1 = (buf[3] << 8) | buf[2];
+
+	als_cur_info.als_ch0 = ch0;
+	als_cur_info.als_ch1 = ch1;
+
+
+#ifdef TAOS_DEBUG
+	printk(KERN_INFO " ch0=%d/0x%x ch1=%d/0x%x\n", ch0, ch0, ch1, ch1);
+#endif
+
+	if ((ch0 >= als_saturation) || (ch1 >= als_saturation)) {
+return_max:
+		als_cur_info.lux = MAX_LUX;
+		mutex_unlock(&als_mutex);
+		return als_cur_info.lux;
+	}
+
+	if (ch0 == 0) {
+		als_cur_info.lux = 0;
+#ifdef TAOS_DEBUG
+		printk(KERN_INFO "ch0==0\n");
+#endif
+		mutex_unlock(&als_mutex);
+
+		return als_cur_info.lux;
+		/*have no data, so return LAST VALUE*/
+	}
+	/* calculate ratio */
+	ratio = (ch1 << 15) / ch0;
+	/* convert to unscaled lux using the pointer to the table */
+	for (p = (struct taos_lux *) taos_device_lux;
+			p->ratio != 0 && p->ratio < ratio; p++)
+		;
+
+	if (p->ratio == 0) {
+#ifdef TAOS_DEBUG
+		dev_info(devp, "ratio = %04x   p->ratio = %04x\n",
+			ratio, p->ratio);
+#endif
+		lux = 0;
+	} else {
+		ch0lux = ((ch0 * p->ch0) +
+				(gainadj[taos_settings.als_gain].ch0 >> 1))
+				/ gainadj[taos_settings.als_gain].ch0;
+		ch1lux = ((ch1 * p->ch1) +
+				(gainadj[taos_settings.als_gain].ch1 >> 1))
+				/ gainadj[taos_settings.als_gain].ch1;
+		lux = ch0lux - ch1lux;
+	}
+
+	/* note: lux is 31 bit max at this point */
+	if (ch1lux > ch0lux) {
+		printk(KERN_INFO "No Data - Return last value\n");
+		als_cur_info.lux = 0;
+		mutex_unlock(&als_mutex);
+		return als_cur_info.lux; /*have no data, so return LAST VALUE*/
+	}
+	/* adjust for active time scale */
+	lux = (lux + (als_time_scale >> 1)) / als_time_scale;
+	/* adjust for active gain scale */
+	lux >>= 13; /* tables have factor of 8192 builtin for accuracy */
+
+	lux = (lux * taos_settings.als_gain_trim + 500) / 1000;
+
+
+#ifdef TAOS_DEBUG
+	printk(KERN_INFO "lux=%d\n", lux);
+#endif
+	if (lux > MAX_LUX) /* check for overflow */
+		goto return_max;
+
+	als_cur_info.lux = lux;
+	/*Update the structure with the latest VALID lux.*/
+
+	mutex_unlock(&als_mutex);
+	return lux;
+}
+EXPORT_SYMBOL(taos_get_lux);
+
+/**
+ * Obtain single reading and calculate the als_gain_trim (later used to

derive actual lux)
+ *\param ulong als_cal_target ALS target (real world)
+ *\return int updated gain_trim value
+ */

tsl258x.c +5 -0
+int taos_als_calibrate(unsigned long als_cal_target)
+{
+	u8 reg_val;
+	unsigned int gain_trim_val;
+	int ret;
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+	int lux_val;
+
+
+	/*This may seem redundant but..
+	 We need this next line in case we call this function from an
+	 external application who has a different target value than
+	 our defaults.
+	 In which case, make 'that' our new default/settings.*/

Comment certainly makes me doubt this should be in a merged driver!

Quoted 241 lines
Quoted 241 lines
tsl258x.c +76 -0
+	taos_settings.als_cal_target = als_cal_target;
+
+	ret = i2c_smbus_write_byte(chip->client, (CMD_REG | CNTRL));
+	if (ret < 0) {
+		dev_err(devp,
+		"i2c_smbus_write_byte to cmd reg failed in "
+		"ioctl als_calibrate\n");
+		return ret;
+	}
+	reg_val = i2c_smbus_read_byte(chip->client);
+
+	if ((reg_val & (CNTL_ADC_ENBL | CNTL_PWRON))
+			!= (CNTL_ADC_ENBL | CNTL_PWRON)) {
+		dev_err(devp,
+		"reg_val & (CNTL_ADC_ENBL | CNTL_PWRON)) "
+		"!= (CNTL_ADC_ENBL | CNTL_PWRON) in ioctl als_calibrate\n");
+		return -ENODATA;
+	}
+
+	ret = i2c_smbus_write_byte(chip->client, (CMD_REG | STATUS));
+	if (ret < 0) {
+		dev_err(devp,
+		"i2c_smbus_write_byte to cmd reg failed in "
+		"ioctl als_calibrate\n");
+		return ret;
+	}
+	reg_val = i2c_smbus_read_byte(chip->client);
+
+	if ((reg_val & STA_ADCVALID) != STA_ADCVALID) {
+		dev_err(devp,
+		"(reg_val & STA_ADCVALID) != STA_ADCVALID in "
+		"ioctl als_calibrate\n");
+		return -ENODATA;
+	}
+	lux_val = taos_get_lux();
+	if (lux_val < 0) {
+		dev_err(devp,
+		"taos_get_lux() returned bad value %d in "
+		"ioctl als_calibrate\n",
+		    lux_val);
+		return lux_val;
+	}
+	gain_trim_val = (unsigned int) (((taos_settings.als_cal_target)
+			* taos_settings.als_gain_trim) / lux_val);
+
+	dev_info(devp, "\n\ntaos_settings.als_cal_target = %d\n"
+		"taos_settings.als_gain_trim = %d\nlux_val = %d\n",
+		taos_settings.als_cal_target,
+		taos_settings.als_gain_trim,
+		lux_val);
+
+	if ((gain_trim_val < 250) || (gain_trim_val > 4000)) {
+		dev_err(devp,
+		"ALS calibrate result %d out of range "
+		"in ioctl als_calibrate\n",
+		    gain_trim_val);
+		return -ENODATA;
+	}
+	taos_settings.als_gain_trim = (int) gain_trim_val;
+
+	return (int) gain_trim_val;
+}
+EXPORT_SYMBOL(taos_als_calibrate);
+
+/*........................................................................*/
+/*@} End of ALS section*/
+
+/*===========================================================================*/
+/**@defgroup DEV_CTRL Device Control Functions
+ * @{
+ */
+
+/*........................................................................*/
+/**
+ * Turn the device on.
+ * Configuration and taos_cycle_type must be set before calling this

function.
+ * \param none.
+ * \return int 0 = success, < 0 = failure
+ */

tsl258x.c +22 -0
+static int taos_chip_on()
+{
+	int i;
+	int ret = 0;
+	u8 *uP;
+	u8 utmp;
+	int als_count;
+	int als_time;
+
+
+	/*make sure taos_cycle_type is set.*/
+	if ((taos_cycle_type != LIGHT))
+		return -1;
+
+	/*and make sure we're not already on*/
+	if (taos_chip_status == TAOS_CHIP_WORKING) {
+		/*if forcing a register update - turn off, then on*/
+		printk(KERN_INFO "device is already enabled\n");
+		return -1;
+	}
+
+	/* . . . . . . . . SHADOW REGISTER

INITIALIZATION . . . . . . . . . . .

tsl258x.c +2 -0
+	 Note:
+	 If interrupts are enabled, keeping persist at 0 should prime the

pump.

tsl258x.c +135 -0
+	 So don't set persist in shadow register. Just enable if required.*/
+	taos_config[INTERRUPT] = (taos_settings.interrupts_enabled & 0xF0);
+
+
+	/*determine als integration regster*/
+	als_count = (taos_settings.als_time * 100 + 135) / 270;
+	if (als_count == 0)
+		als_count = 1; /*ensure at least one cycle*/
+
+
+	/*convert back to time (encompasses overrides)*/
+	if (taos_cycle_type & LIGHT) {
+		als_time = (als_count * 27 + 5) / 10;
+		taos_config[ALS_TIME] = 256 - als_count;
+	} else
+		taos_config[ALS_TIME] = 0xff;
+
+
+	/*Set the gain based on taos_settings struct*/
+	taos_config[GAIN] = taos_settings.als_gain;
+
+
+	/*set globals re scaling and saturation*/
+	als_saturation = als_count * 922; /*90% of full scale*/
+	als_time_scale = (als_time + 25) / 50;
+
+
+	/* . . . . . . . . . . . . .  . . . . .  . . . . . . . . . . .
+	 SKATE Specific power-on / adc enable sequence
+	 Power on the device 1st.*/
+	utmp = CNTL_PWRON;
+	ret = taos_i2c_write(CMD_REG | CNTRL, &utmp);
+	if (ret < 0) {
+		dev_err(devp,
+		"taos_i2c_write to turn on device failed in "
+		"taos_chip_on.\n");
+		return -1;
+	}
+
+	/*User the following shadow copy for our delay before enabling ADC
+	 Write ALL THE REGISTERS*/
+	for (i = 0, uP = taos_config; i < TAOS_REG_MAX; i++) {
+		ret = taos_i2c_write(CMD_REG + i, uP++);
+		if (ret < 0) {
+			dev_err(devp,
+			"taos_i2c_write to reg %d failed in "
+			"taos_chip_on.\n", i);
+			return -1;
+		}
+	}
+
+	mdelay(3);
+	/*NOW enable the ADC
+	 initialize the desired mode of operation*/
+	utmp = CNTL_PWRON | CNTL_ADC_ENBL;
+	ret = taos_i2c_write(CMD_REG | CNTRL, &utmp);
+	if (ret < 0) {
+		dev_err(devp,
+		"taos_i2c_write to turn on device failed in "
+		"taos_chip_on.\n");
+		return -1;
+	}
+	taos_chip_status = TAOS_CHIP_WORKING;
+
+#ifdef TAOS_DEBUG
+	dev_info(devp, "chip_on() called\n\n");
+#endif
+
+	return ret; /*returns result of last i2cwrite*/
+}
+EXPORT_SYMBOL(taos_chip_on);
+
+/*........................................................................*/
+/**
+ * Turn the device OFF.
+ * \param	none.
+ * \return	int	0 = success, < 0 = failure
+ */
+static int taos_chip_off()
+{
+	int ret;
+	u8 utmp;
+
+
+	/*turn device off*/
+	taos_chip_status = TAOS_CHIP_SLEEP;
+	utmp = 0x00;
+	ret = taos_i2c_write(CMD_REG | CNTRL, &utmp);
+	als_on = FALSE;
+	/*init_done = 0x00; *//*used by ALS irq as one/first shot*/
+#ifdef TAOS_DEBUG
+	printk(KERN_INFO "chip_off() called\n");
+#endif
+
+	return ret;
+
+}
+EXPORT_SYMBOL(taos_chip_off);
+/*........................................................................*/
+/*@} End of Device Control Section*/
+
+/*===========================================================================*/
+
+/*===========================================================================*/
+/**@defgroup I2C-HAL I2C/SMBus Communication Functions
+ * The sections pertains to the driver/device communication functions\n
+ * facilitated via SMBus in an I2C manner.\n
+ * These functions represent several layers of abstraction.
+ * Also note, some i2c controllers do not support block read/write.
+ * @{
+ */
+
+/**
+ * Read a number of bytes starting at register (reg) location.
+ *
+ * \param 	unsigned char 	reg - device register
+ * \param 	unsigned char	*val - location to store results
+ * \param 	unsigned int 	number of bytes to read
+ * \return	TAOS_SUCCESS, or i2c_smbus_write_byte ERROR code
+ *
+ */
+static int taos_i2c_read(u8 reg, u8 *val, unsigned int len)
+{
+	int result;
+	int i;
+
+	if (len > MAXI2C)
+		len = MAXI2C;
+	for (i = 0; i < len; i++) {
+		/* select register to write */
+		if (reg >= 0) {
+			result = i2c_smbus_write_byte(chip->client,
+				(CMD_REG | (reg + i)));
+
+			if (result < 0) {
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+				dev_err(devp,
+				"FAILED: i2c_smbus_write_byte_data in "
+				"taos_io_read()\n");
+				return result;
+			}
+			/* read the data */
+			*val = i2c_smbus_read_byte(chip->client);
+#ifdef TAOS_DEBUG_SMBUS

Use debug macros found in kernel for this rather than defining your own.

Quoted 11 lines
Quoted 11 lines
tsl258x.c +11 -0
+			dev_info(devp,
+				"READ FROM REGISTER [%02X] -->%02x<--\n",
+				(CMD_REG | (reg + i)) , *data);
+#endif
+			val++;
+		}
+	}
+	return TAOS_SUCCESS;
+}
+
+/**
Quoted 8 lines
Quoted 8 lines

+ * This function is used to send a an register address followed by a data value
+ * When calling this function remember to set the register MSBit
+ * (if applicable).
+ * \param unsigned char register
+ * \param unsigned char * pointer to data value(s) to be written
+ * \return TAOS_SUCCESS, or i2c_smbus_write_byte error code.
+ */

Don't do this. Just call the i2c_smbus_write inline within your code. The wrapper doesn't add any real value.

Quoted 49 lines
Quoted 49 lines
tsl258x.c +14 -0
+static int taos_i2c_write(u8 reg, u8 *val)
+{
+	int retval;
+	retval = i2c_smbus_write_byte_data(chip->client, reg, (*val));
+	if (retval < 0) {
+		dev_err(devp, "FAILED: i2c_smbus_write_byte_data\n");
+		return retval;
+	}
+
+	return TAOS_SUCCESS;
+}
+
+/**
+ * This function is used to send a command to device command/control

register
+ * All bytes sent using this command have their MSBit set - it's a command!
+ * \param unsigned char register to write
+ * \return TAOS_SUCCESS, or i2c_smbus_write_byte error code.
+ */

tsl258x.c +23 -0
+static int taos_i2c_write_command(u8 reg)
+{
+	int result;
+	u8 *p;
+	u8 buf;
+
+	p = &buf;
+	*p = reg |= CMD_REG;
+	/*write the data*/
+	result = i2c_smbus_write_byte(chip->client, (*p));
+	if (result < 0) {
+		dev_err(devp, "FAILED: i2c_smbus_write_byte\n");
+		return result;
+	}
+	return TAOS_SUCCESS;
+}
+
+/*........................................................................*/
+/*@}	//end of I2C-HAL section*/
+
+
+/**@defgroup group6 Sysfs Interface Functions
+ * The following functions are for access via the sysfs style

interface.\n
+ * Use 'cat' to read, 'echo >' to write\n
+ * @{
+ */

tsl258x.c +2 -0
+
+/*........................................................................*/
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+/*sysfs interface functions begin here*/
+
+static ssize_t taos_device_id(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%s %s\n", DEVICE_ID, DRIVER_VERSION_ID);
+}
+

Ideally this would use runtime pm to handle things rather than doing it explicitly, but this might be acceptable in the meantime...

Quoted 211 lines
Quoted 211 lines
tsl258x.c +153 -0
+static ssize_t taos_power_state_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_chip_status);
+	return 0;
+}
+
+static ssize_t taos_power_state_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value == 1)
+		taos_chip_on();
+	else
+		taos_chip_off();
+
+	return len;
+}
+
+static ssize_t taos_gain_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_gain);
+	return 0;
+}
+
+static ssize_t taos_gain_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+	if (value) {
+		if (value > 4) {
+			printk(KERN_INFO "Invalid Gain Index\n");
+			return -1;
+		} else {
+			taos_settings.als_gain = value;
+		}
+	}
+	return len;
+}
+
+static ssize_t taos_interrupt_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.interrupts_enabled);
+	return 0;
+}
+
+static ssize_t taos_interrupt_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value) {
+		if (value > 1) {
+			printk(KERN_INFO "Invalid: 0 or 1\n");
+			return -1;
+		} else {
+			taos_settings.interrupts_enabled = CNTL_ALS_INT_ENBL;
+		}
+	}
+	return len;
+}
+
+static ssize_t taos_als_time_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_time);
+	return 0;
+}
+
+static ssize_t taos_als_time_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		taos_settings.als_time = value;
+
+	return len;
+}
+
+static ssize_t taos_als_trim_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_gain_trim);
+	return 0;
+}
+
+static ssize_t taos_als_trim_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		taos_settings.als_gain_trim = value;
+
+	return len;
+}
+
+static ssize_t taos_als_persistence_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_persistence);
+	return 0;
+}
+
+static ssize_t taos_als_persistence_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		taos_settings.als_persistence = value;
+
+	return len;
+}
+
+static ssize_t taos_als_cal_target_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_cal_target);
+	return 0;
+}
+
+static ssize_t taos_als_cal_target_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		taos_settings.als_cal_target = value;
+
+	return len;
+}
+
+static ssize_t taos_lux_show(struct device *dev, struct

device_attribute *attr,

tsl258x.c +57 -0
+    char *buf)
+{
+	int lux = 0;
+	if ((taos_settings.interrupts_enabled & CNTL_ALS_INT_ENBL) == 0x00)
+		lux = taos_get_lux(); /*get fresh data*/
+	else
+		lux = als_cur_info.lux; /*data is fresh as last change*/
+
+	return sprintf(buf, "%d\n", lux);
+	return 0;
+}
+
+static ssize_t taos_do_calibrate(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value == 1)
+		taos_als_calibrate(taos_settings.als_cal_target);
+
+	return len;
+}
+
+static ssize_t taos_als_thresh_low_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_thresh_low);
+	return 0;
+}
+
+static ssize_t taos_als_thresh_low_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		taos_settings.als_thresh_low = value;
+
+	return len;
+}
+
+static ssize_t taos_als_thresh_high_show(struct device *dev,
+    struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", taos_settings.als_thresh_high);
+	return 0;
+}
+
+static ssize_t taos_als_thresh_high_store(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t len)
+{
+	unsigned long value;
+	if (strict_strtoul(buf, 0, &value))
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+		return -EINVAL;
+
+	if (value)
+		taos_settings.als_thresh_high = value;
+
+	return len;
+}
+

Irrelevant and cryptic comment. Please remove.

Quoted 7 lines
Quoted 7 lines
tsl258x.c +7 -0
+/*@}	End of Sysfs Interface Functions - gp6*/
+/*===========================================================================*/
+
+static void taos_als_auto_update(struct work_struct *update)
+{
+	if (taos_chip_status != TAOS_CHIP_WORKING) {
+		taos_chip_on();
Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+		mdelay(100);
+	}
+	taos_get_lux();
+	input_report_abs(taos_dev, ABS_X, als_cur_info.lux);
+	input_sync(taos_dev);
+
+	queue_delayed_work(taos_wq0, luxUd, LUX_UPDATE_PERIOD);
+

Bonus white line.

Quoted 8 lines

tsl258x.c

Quoted 8 lines
tsl258x.c +8 -0
+}
+
+
+
+MODULE_AUTHOR("J. August Brenner<[email protected]>");
+MODULE_DESCRIPTION("TAOS 258x ambient light sensor driver");
+MODULE_LICENSE("GPL");
+

These shoule be under the actual functions rather than stuck at the end like this. As a generaly rule, those two would be the last functions in the file as thats where everyone expects to find them.

Quoted 9 lines

tsl258x.c

Quoted 3 lines
tsl258x.c +3 -0
+module_init(taos_init);
+module_exit(taos_exit);
+

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