lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1324519802-23894-9-git-send-email-cheiny@synaptics.com>
Date:	Wed, 21 Dec 2011 18:09:59 -0800
From:	Christopher Heiny <cheiny@...aptics.com>
To:	Dmitry Torokhov <dmitry.torokhov@...il.com>
Cc:	Jean Delvare <khali@...ux-fr.org>,
	Linux Kernel <linux-kernel@...r.kernel.org>,
	Linux Input <linux-input@...r.kernel.org>,
	Christopher Heiny <cheiny@...aptics.com>,
	Joerie de Gram <j.de.gram@...il.com>,
	Linus Walleij <linus.walleij@...ricsson.com>,
	Naveen Kumar Gaddipati <naveen.gaddipati@...ricsson.com>
Subject: [RFC PATCH 8/11] input: RMI4 F54 - analog data reporting

Signed-off-by: Christopher Heiny <cheiny@...aptics.com>

Cc: Dmitry Torokhov <dmitry.torokhov@...il.com>
Cc: Linus Walleij <linus.walleij@...ricsson.com>
Cc: Naveen Kumar Gaddipati <naveen.gaddipati@...ricsson.com>
Cc: Joeri de Gram <j.de.gram@...il.com>

---

 drivers/input/rmi4/rmi_f54.c | 1506 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1506 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c
new file mode 100644
index 0000000..5119184
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f54.c
@@ -0,0 +1,1506 @@
+
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/hrtimer.h>
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include "rmi_driver.h"
+/* Set this to 1 for raw hex dump of returned data. */
+#define RAW_HEX 0
+/* Set this to 1 for human readable dump of returned data. */
+#define HUMAN_READABLE 0
+/* The watchdog timer can be useful when debugging certain firmware related
+ * issues.
+ */
+#define F54_WATCHDOG 1
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+#define KERNEL_VERSION_ABOVE_2_6_32 1
+#endif
+
+/* define fn $54 commands */
+#define GET_REPORT                1
+#define FORCE_CAL                 2
+
+/* status */
+#define BUSY 1
+#define IDLE 0
+
+/* Offsets for data */
+#define RMI_F54_REPORT_DATA_OFFSET	3
+#define RMI_F54_FIFO_OFFSET		1
+#define RMI_F54_NUM_TX_OFFSET		1
+#define RMI_F54_NUM_RX_OFFSET		0
+
+/* Fixed sizes of reports */
+#define RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE 4
+#define RMI_54_HIGH_RESISTANCE_SIZE 6
+
+/* definitions for F54 Query Registers in ultra-portable unionstruct form */
+struct f54_ad_query {
+	/* query 0 */
+	u8 number_of_receiver_electrodes;
+
+	/* query 1 */
+	u8 number_of_transmitter_electrodes;
+
+	union {
+		struct {
+			/* query2 */
+			u8 f54_ad_query2_b0__1:2;
+			u8 has_baseline:1;
+			u8 has_image8:1;
+			u8 f54_ad_query2_b4__5:2;
+			u8 has_image16:1;
+			u8 f54_ad_query2_b7:1;
+		};
+		u8 f54_ad_query2;
+	};
+
+	/* query 3.0 and 3.1 */
+	u16 clock_rate;
+
+	/* query 4 */
+	u8 touch_controller_family;
+
+	/* query 5 */
+	union {
+		struct {
+			u8 has_pixel_touch_threshold_adjustment:1;
+			u8 f54_ad_query5_b1__7:7;
+		};
+		u8 f54_ad_query5;
+	};
+
+	/* query 6 */
+	union {
+		struct {
+		u8 has_sensor_assignment:1;
+		u8 has_interference_metric:1;
+		u8 has_sense_frequency_control:1;
+		u8 has_firmware_noise_mitigation:1;
+		u8 f54_ad_query6_b4:1;
+		u8 has_two_byte_report_rate:1;
+		u8 has_one_byte_report_rate:1;
+		u8 has_relaxation_control:1;
+		};
+		u8 f54_ad_query6;
+	};
+
+	/* query 7 */
+	union {
+		struct {
+			u8 curve_compensation_mode:2;
+			u8 f54_ad_query7_b2__7:6;
+		};
+		u8 f54_ad_query7;
+	};
+
+	/* query 8 */
+	union {
+		struct {
+		u8 f54_ad_query2_b0:1;
+		u8 has_iir_filter:1;
+		u8 has_cmn_removal:1;
+		u8 has_cmn_maximum:1;
+		u8 has_pixel_threshold_hysteresis:1;
+		u8 has_edge_compensation:1;
+		u8 has_perf_frequency_noisecontrol:1;
+		u8 f54_ad_query8_b7:1;
+		};
+		u8 f54_ad_query8;
+	};
+
+	u8 f54_ad_query9;
+	u8 f54_ad_query10;
+	u8 f54_ad_query11;
+
+	/* query 12 */
+	union {
+		struct {
+			u8 number_of_sensing_frequencies:4;
+			u8 f54_ad_query12_b4__7:4;
+		};
+		u8 f54_ad_query12;
+	};
+};
+
+/* define report types */
+enum f54_report_types {
+	/* The numbering should follow automatically, here for clarity */
+	F54_8BIT_IMAGE = 1,
+	F54_16BIT_IMAGE = 2,
+	F54_RAW_16BIT_IMAGE = 3,
+	F54_HIGH_RESISTANCE = 4,
+	F54_TX_TO_TX_SHORT = 5,
+	F54_RX_TO_RX1 = 7,
+	F54_TRUE_BASELINE = 9,
+	F54_FULL_RAW_CAP_MIN_MAX = 13,
+	F54_RX_OPENS1 = 14,
+	F54_TX_OPEN = 15,
+	F54_TX_TO_GROUND = 16,
+	F54_RX_TO_RX2 = 17,
+	F54_RX_OPENS2 = 18,
+	F54_FULL_RAW_CAP = 19,
+	F54_FULL_RAW_CAP_RX_COUPLING_COMP = 20
+};
+
+/* data specific to fn $54 that needs to be kept around */
+struct rmi_fn_54_data {
+	struct f54_ad_query query;
+	u8 cmd;
+	enum f54_report_types report_type;
+	u16 fifoindex;
+	signed char status;
+	bool no_auto_cal;
+	/*
+	 * May need to do something to make sure this reflects what is currently
+	 * in data.
+	 */
+	unsigned int report_size;
+	unsigned char *report_data;
+	unsigned int bufsize;
+	struct mutex data_mutex;
+	struct lock_class_key data_key;
+	struct mutex status_mutex;
+	struct lock_class_key status_key;
+#if F54_WATCHDOG
+	struct hrtimer watchdog;
+#endif
+	struct rmi_function_container *fc;
+	struct work_struct work;
+};
+
+/* sysfs functions */
+static ssize_t rmi_fn_54_report_type_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_report_type_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_get_report_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_force_cal_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_status_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_54_status_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count);
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+#else
+static ssize_t rmi_fn_54_data_read(struct kobject *kobj,
+#endif
+					struct bin_attribute *attributes,
+					char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_54_num_rx_electrodes_show(struct device *dev,
+				     struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_num_tx_electrodes_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_image16_show(struct device *dev,
+				     struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_image8_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_baseline_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_clock_rate_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_54_touch_controller_family_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_54_has_pixel_touch_threshold_adjustment_show(
+		struct device *dev, struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_sensor_assignment_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_interference_metric_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_sense_frequency_control_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_firmware_noise_mitigation_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_two_byte_report_rate_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_one_byte_report_rate_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_relaxation_control_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_curve_compensation_mode_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_iir_filter_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_cmn_removal_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_cmn_maximum_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_pixel_threshold_hysteresis_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_edge_compensation_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_perf_frequency_noisecontrol_show(
+		struct device *dev, struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_number_of_sensing_frequencies_show(struct device *dev,
+			struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_no_auto_cal_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_no_auto_cal_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_fifoindex_show(struct device *dev,
+				  struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_fifoindex_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count);
+
+
+static inline int rmi_f54_alloc_memory(struct rmi_function_container *fc);
+
+static inline void rmi_f54_free_memory(struct rmi_function_container *fc);
+
+static inline int rmi_f54_initialize(struct rmi_function_container *fc);
+
+static int rmi_f54_config(struct rmi_function_container *fc);
+
+static int rmi_f54_reset(struct rmi_function_container *fc);
+
+static inline int rmi_f54_create_sysfs(struct rmi_function_container *fc);
+
+
+static struct device_attribute attrs[] = {
+	__ATTR(report_type, RMI_RW_ATTR,
+		rmi_fn_54_report_type_show, rmi_fn_54_report_type_store),
+	__ATTR(get_report, RMI_WO_ATTR,
+		rmi_show_error, rmi_fn_54_get_report_store),
+	__ATTR(force_cal, RMI_WO_ATTR,
+		   rmi_show_error, rmi_fn_54_force_cal_store),
+	__ATTR(status, RMI_RW_ATTR,
+		rmi_fn_54_status_show, rmi_fn_54_status_store),
+	__ATTR(num_rx_electrodes, RMI_RO_ATTR,
+		rmi_fn_54_num_rx_electrodes_show, rmi_store_error),
+	__ATTR(num_tx_electrodes, RMI_RO_ATTR,
+		rmi_fn_54_num_tx_electrodes_show, rmi_store_error),
+	__ATTR(has_image16, RMI_RO_ATTR,
+		rmi_fn_54_has_image16_show, rmi_store_error),
+	__ATTR(has_image8, RMI_RO_ATTR,
+		rmi_fn_54_has_image8_show, rmi_store_error),
+	__ATTR(has_baseline, RMI_RO_ATTR,
+		rmi_fn_54_has_baseline_show, rmi_store_error),
+	__ATTR(clock_rate, RMI_RO_ATTR,
+		rmi_fn_54_clock_rate_show, rmi_store_error),
+	__ATTR(touch_controller_family, RMI_RO_ATTR,
+		rmi_fn_54_touch_controller_family_show, rmi_store_error),
+	__ATTR(has_pixel_touch_threshold_adjustment, RMI_RO_ATTR,
+		rmi_fn_54_has_pixel_touch_threshold_adjustment_show
+							, rmi_store_error),
+	__ATTR(has_sensor_assignment, RMI_RO_ATTR,
+		rmi_fn_54_has_sensor_assignment_show, rmi_store_error),
+	__ATTR(has_interference_metric, RMI_RO_ATTR,
+		rmi_fn_54_has_interference_metric_show, rmi_store_error),
+	__ATTR(has_sense_frequency_control, RMI_RO_ATTR,
+		rmi_fn_54_has_sense_frequency_control_show, rmi_store_error),
+	__ATTR(has_firmware_noise_mitigation, RMI_RO_ATTR,
+		rmi_fn_54_has_firmware_noise_mitigation_show, rmi_store_error),
+	__ATTR(has_two_byte_report_rate, RMI_RO_ATTR,
+		rmi_fn_54_has_two_byte_report_rate_show, rmi_store_error),
+	__ATTR(has_one_byte_report_rate, RMI_RO_ATTR,
+		rmi_fn_54_has_one_byte_report_rate_show, rmi_store_error),
+	__ATTR(has_relaxation_control, RMI_RO_ATTR,
+		rmi_fn_54_has_relaxation_control_show, rmi_store_error),
+	__ATTR(curve_compensation_mode, RMI_RO_ATTR,
+		rmi_fn_54_curve_compensation_mode_show, rmi_store_error),
+	__ATTR(has_iir_filter, RMI_RO_ATTR,
+		rmi_fn_54_has_iir_filter_show, rmi_store_error),
+	__ATTR(has_cmn_removal, RMI_RO_ATTR,
+		rmi_fn_54_has_cmn_removal_show, rmi_store_error),
+	__ATTR(has_cmn_maximum, RMI_RO_ATTR,
+		rmi_fn_54_has_cmn_maximum_show, rmi_store_error),
+	__ATTR(has_pixel_threshold_hysteresis, RMI_RO_ATTR,
+		rmi_fn_54_has_pixel_threshold_hysteresis_show, rmi_store_error),
+	__ATTR(has_edge_compensation, RMI_RO_ATTR,
+		rmi_fn_54_has_edge_compensation_show, rmi_store_error),
+	__ATTR(has_perf_frequency_noisecontrol, RMI_RO_ATTR,
+	      rmi_fn_54_has_perf_frequency_noisecontrol_show, rmi_store_error),
+	__ATTR(number_of_sensing_frequencies, RMI_RO_ATTR,
+		rmi_fn_54_number_of_sensing_frequencies_show, rmi_store_error),
+	__ATTR(no_auto_cal, RMI_RW_ATTR,
+		rmi_fn_54_no_auto_cal_show, rmi_fn_54_no_auto_cal_store),
+	__ATTR(fifoindex, RMI_RW_ATTR,
+		rmi_fn_54_fifoindex_show, rmi_fn_54_fifoindex_store),
+};
+
+struct bin_attribute dev_rep_data = {
+	.attr = {
+		 .name = "rep_data",
+		 .mode = RMI_RO_ATTR},
+	.size = 0,
+	.read = rmi_fn_54_data_read,
+};
+
+#if F54_WATCHDOG
+static enum hrtimer_restart clear_status(struct hrtimer *timer);
+
+static void clear_status_worker(struct work_struct *work);
+#endif
+
+
+
+static int rmi_f54_init(struct rmi_function_container *fc)
+{
+	int retval = 0;
+
+	dev_info(&fc->dev, "Intializing F54.");
+
+	retval = rmi_f54_alloc_memory(fc);
+	if (retval < 0)
+		goto error_exit;
+
+	retval = rmi_f54_initialize(fc);
+	if (retval < 0)
+		goto error_exit;
+
+	retval = rmi_f54_create_sysfs(fc);
+	if (retval < 0)
+		goto error_exit;
+
+	return retval;
+
+error_exit:
+	rmi_f54_free_memory(fc);
+
+	return retval;
+}
+
+
+
+static inline int rmi_f54_alloc_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *f54;
+
+	f54 = kzalloc(sizeof(struct rmi_fn_54_data), GFP_KERNEL);
+	if (!f54) {
+		dev_err(&fc->dev, "Failed to allocate rmi_fn_54_data.\n");
+		return -ENOMEM;
+	}
+	fc->data = f54;
+
+	return 0;
+}
+
+
+static inline void rmi_f54_free_memory(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *f54 = fc->data;
+
+	if (f54) {
+		kfree(f54->report_data);
+		kfree(f54);
+		fc->data = NULL;
+	}
+}
+
+
+static inline int rmi_f54_initialize(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *instance_data = fc->data;
+	int retval = 0;
+
+	instance_data->fc = fc;
+
+#if F54_WATCHDOG
+	/* Set up watchdog timer to catch unanswered get_report commands */
+	hrtimer_init(&instance_data->watchdog, CLOCK_MONOTONIC,
+							HRTIMER_MODE_REL);
+	instance_data->watchdog.function = clear_status;
+
+	/* work function to do unlocking */
+	INIT_WORK(&instance_data->work, clear_status_worker);
+#endif
+
+	/* Read F54 Query Data */
+	retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+		(u8 *)&instance_data->query, sizeof(instance_data->query));
+	if (retval < 0) {
+		dev_err(&fc->dev, "Could not read query registers"
+			" from 0x%04x\n", fc->fd.query_base_addr);
+		return retval;
+	}
+
+	__mutex_init(&instance_data->data_mutex, "data_mutex",
+		     &instance_data->data_key);
+
+	__mutex_init(&instance_data->status_mutex, "status_mutex",
+		     &instance_data->status_key);
+
+	instance_data->status = IDLE;
+
+	return 0;
+}
+
+
+static inline int rmi_f54_create_sysfs(struct rmi_function_container *fc)
+{
+	int attr_count = 0;
+	int rc;
+
+	dev_dbg(&fc->dev, "Creating sysfs files.");
+
+	/* Binary sysfs file to report the data back */
+	rc = sysfs_create_bin_file(&fc->dev.kobj, &dev_rep_data);
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to create sysfs file for F54 data "
+					"(error = %d).\n", rc);
+		return -ENODEV;
+	}
+
+	/* Set up sysfs device attributes. */
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+		if (sysfs_create_file
+		    (&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
+			dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+			     attrs[attr_count].attr.name);
+			rc = -ENODEV;
+			goto err_remove_sysfs;
+		}
+	}
+
+	return 0;
+
+err_remove_sysfs:
+	sysfs_remove_bin_file(&fc->dev.kobj, &dev_rep_data);
+
+	for (attr_count--; attr_count >= 0; attr_count--)
+		sysfs_remove_file(&fc->dev.kobj,
+				  &attrs[attr_count].attr);
+
+	return rc;
+}
+
+
+static int rmi_f54_config(struct rmi_function_container *fc)
+{
+	/*we shouldn't do anything here.
+	 * (reset should write a 1 to a "beenreset" sysfs file)
+     */
+	return 0;
+}
+
+
+
+static int rmi_f54_reset(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *data = fc->data;
+	struct rmi_driver *driver = fc->rmi_dev->driver;
+
+#if F54_WATCHDOG
+	hrtimer_cancel(&data->watchdog);
+#endif
+
+	mutex_lock(&data->status_mutex);
+	if (driver->restore_irq_mask) {
+		dev_dbg(&fc->dev, "Restoring interupts!\n");
+		driver->restore_irq_mask(fc->rmi_dev);
+	} else {
+		dev_err(&fc->dev, "No way to restore interrupts!\n");
+	}
+	data->status = -ECONNRESET;
+	mutex_unlock(&data->status_mutex);
+
+	return 0;
+}
+
+
+
+static void rmi_f54_remove(struct rmi_function_container *fc)
+{
+	struct rmi_fn_54_data *data = fc->data;
+	int attr_count = 0;
+
+	dev_info(&fc->dev, "Removing F54.");
+
+	#if F54_WATCHDOG
+	/* Stop timer */
+	hrtimer_cancel(&data->watchdog);
+	#endif
+
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
+		sysfs_remove_file(&fc->dev.kobj, &attrs[attr_count].attr);
+
+	sysfs_remove_bin_file(&fc->dev.kobj, &dev_rep_data);
+
+	rmi_f54_free_memory(fc);
+}
+
+
+static void set_report_size(struct rmi_fn_54_data *data)
+{
+	u8 rx = data->query.number_of_receiver_electrodes;
+	u8 tx = data->query.number_of_transmitter_electrodes;
+	switch (data->report_type) {
+	case F54_8BIT_IMAGE:
+		data->report_size = rx * tx;
+		break;
+	case F54_16BIT_IMAGE:
+	case F54_RAW_16BIT_IMAGE:
+	case F54_TRUE_BASELINE:
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+		data->report_size = 2 * rx * tx;
+		break;
+	case F54_HIGH_RESISTANCE:
+		data->report_size = RMI_54_HIGH_RESISTANCE_SIZE;
+		break;
+	case F54_FULL_RAW_CAP_MIN_MAX:
+		data->report_size = RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE;
+		break;
+	case F54_TX_TO_TX_SHORT:
+	case F54_TX_OPEN:
+	case F54_TX_TO_GROUND:
+		data->report_size =  (tx + 7) / 8;
+		break;
+	case F54_RX_TO_RX1:
+	case F54_RX_OPENS1:
+		if (rx < tx)
+			data->report_size = 2 * rx * rx;
+		else
+			data->report_size = 2 * rx * tx;
+		break;
+	case F54_RX_TO_RX2:
+	case F54_RX_OPENS2:
+		if (rx <= tx)
+			data->report_size = 0;
+		else
+			data->report_size = 2 * rx * (rx - tx);
+		break;
+	default:
+		data->report_size = 0;
+	}
+}
+
+int rmi_f54_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	struct rmi_driver *driver = fc->rmi_dev->driver;
+	char fifo[2];
+	struct rmi_fn_54_data *data = fc->data;
+	int error = 0;
+
+	set_report_size(data);
+	if (data->report_size == 0) {
+		dev_err(&fc->dev, "Invalid report type set in %s. "
+				"This should never happen.\n", __func__);
+		error = -EINVAL;
+		goto error_exit;
+	}
+	/*
+	 * We need to ensure the buffer is big enough. A Buffer size of 0 means
+	 * that the buffer has not been allocated.
+	 */
+	if (data->bufsize < data->report_size) {
+		mutex_lock(&data->data_mutex);
+		if (data->bufsize > 0)
+			kfree(data->report_data);
+		data->report_data = kzalloc(data->report_size, GFP_KERNEL);
+		if (!data->report_data) {
+			dev_err(&fc->dev, "Failed to allocate report_data.\n");
+			error = -ENOMEM;
+			data->bufsize = 0;
+			mutex_unlock(&data->data_mutex);
+			goto error_exit;
+		}
+		data->bufsize = data->report_size;
+		mutex_unlock(&data->data_mutex);
+	}
+	dev_vdbg(&fc->dev, "F54 Interrupt handler is running.\nSize: %d\n",
+		 data->report_size);
+	/* Write 0 to fifohi and fifolo. */
+	fifo[0] = 0;
+	fifo[1] = 0;
+	error = rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr
+				+ RMI_F54_FIFO_OFFSET, fifo,	sizeof(fifo));
+	if (error < 0)
+		dev_err(&fc->dev, "Failed to write fifo to zero!\n");
+	else
+		error = rmi_read_block(fc->rmi_dev,
+			fc->fd.data_base_addr + RMI_F54_REPORT_DATA_OFFSET,
+			data->report_data, data->report_size);
+	if (error < 0)
+		dev_err(&fc->dev, "F54 data read failed. Code: %d.\n", error);
+	else if (error != data->report_size) {
+		error = -EINVAL;
+		goto error_exit;
+	}
+#if RAW_HEX
+	int l;
+	/* Debugging: Print out the file in hex. */
+	pr_info("Report data (raw hex):\n");
+	for (l = 0; l < data->report_size; l += 2) {
+		pr_info("%03d: 0x%02x%02x\n", l/2,
+			data->report_data[l+1], data->report_data[l]);
+	}
+#endif
+#if HUMAN_READABLE
+	/* Debugging: Print out file in human understandable image */
+	switch (data->report_type) {
+	case F54_16BIT_IMAGE:
+	case F54_RAW_16BIT_IMAGE:
+	case F54_TRUE_BASELINE:
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+		pr_info("Report data (Image):\n");
+		int i, j, k;
+		char c[2];
+		short s;
+		k = 0;
+		for (i = 0; i < data->query.number_of_transmitter_electrodes;
+									i++) {
+			for (j = 0; j <
+			     data->query.number_of_receiver_electrodes; j++) {
+				c[0] = data->report_data[k];
+				c[1] = data->report_data[k+1];
+				memcpy(&s, &c, 2);
+				if (s < -64)
+					printk(".");
+				else if (s < 0)
+					printk("-");
+				else if (s > 64)
+					printk("*");
+				else if (s > 0)
+					printk("+");
+				else
+					printk("0");
+				k += 2;
+			}
+			pr_info("\n");
+		}
+		pr_info("EOF\n");
+		break;
+	default:
+		pr_info("Report type %d debug image not supported",
+							data->report_type);
+	}
+#endif
+	error = IDLE;
+error_exit:
+	mutex_lock(&data->status_mutex);
+	/* Turn back on other interupts, if it
+	 * appears that we turned them off. */
+	if (driver->restore_irq_mask) {
+		dev_dbg(&fc->dev, "Restoring interupts!\n");
+		driver->restore_irq_mask(fc->rmi_dev);
+	} else {
+		dev_err(&fc->dev, "No way to restore interrupts!\n");
+	}
+	data->status = error;
+	mutex_unlock(&data->status_mutex);
+	return data->status;
+}
+
+
+#if F54_WATCHDOG
+static void clear_status_worker(struct work_struct *work)
+{
+	struct rmi_fn_54_data *data = container_of(work,
+					struct rmi_fn_54_data, work);
+	struct rmi_function_container *fc = data->fc;
+	struct rmi_driver *driver = fc->rmi_dev->driver;
+	char command;
+	int result;
+
+	mutex_lock(&data->status_mutex);
+	if (data->status == BUSY) {
+		pr_debug("F54 Timout Occured: Determining status.\n");
+		result = rmi_read_block(fc->rmi_dev, fc->fd.command_base_addr,
+								&command, 1);
+		if (result < 0) {
+			dev_err(&fc->dev, "Could not read get_report register "
+				"from 0x%04x\n", fc->fd.command_base_addr);
+			data->status = -ETIMEDOUT;
+		} else {
+			if (command & GET_REPORT) {
+				dev_warn(&fc->dev, "Report type unsupported!");
+				data->status = -EINVAL;
+			} else {
+				data->status = -ETIMEDOUT;
+			}
+		}
+		if (driver->restore_irq_mask) {
+			dev_dbg(&fc->dev, "Restoring interupts!\n");
+			driver->restore_irq_mask(fc->rmi_dev);
+		} else {
+			dev_err(&fc->dev, "No way to restore interrupts!\n");
+		}
+	}
+	mutex_unlock(&data->status_mutex);
+}
+
+static enum hrtimer_restart clear_status(struct hrtimer *timer)
+{
+	struct rmi_fn_54_data *data = container_of(timer,
+					struct rmi_fn_54_data, watchdog);
+	schedule_work(&(data->work));
+	return HRTIMER_NORESTART;
+}
+#endif
+
+/* Check if report_type is valid */
+static bool is_report_type_valid(enum f54_report_types reptype)
+{
+	/* Basic checks on report_type to ensure we write a valid type
+	 * to the sensor.
+	 * TODO: Check Query3 to see if some specific reports are
+	 * available. This is currently listed as a reserved register.
+	 */
+	switch (reptype) {
+	case F54_8BIT_IMAGE:
+	case F54_16BIT_IMAGE:
+	case F54_RAW_16BIT_IMAGE:
+	case F54_HIGH_RESISTANCE:
+	case F54_TX_TO_TX_SHORT:
+	case F54_RX_TO_RX1:
+	case F54_TRUE_BASELINE:
+	case F54_FULL_RAW_CAP_MIN_MAX:
+	case F54_RX_OPENS1:
+	case F54_TX_OPEN:
+	case F54_TX_TO_GROUND:
+	case F54_RX_TO_RX2:
+	case F54_RX_OPENS2:
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+		return true;
+		break;
+	default:
+		return false;
+	}
+}
+
+/* SYSFS file show/store functions */
+static ssize_t rmi_fn_54_report_type_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->report_type);
+}
+
+static ssize_t rmi_fn_54_report_type_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count) {
+	int result;
+	unsigned long val;
+	unsigned char data;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	/* need to convert the string data to an actual value */
+	result = strict_strtoul(buf, 10, &val);
+	if (result)
+		return result;
+	if (!is_report_type_valid(val)) {
+		dev_err(dev, "%s : Report type %d is invalid.\n",
+					__func__, (u8) val);
+		return -EINVAL;
+	}
+	mutex_lock(&instance_data->status_mutex);
+	if (instance_data->status != BUSY) {
+		instance_data->report_type = (enum f54_report_types)val;
+		data = (char)val;
+		/* Write the Report Type back to the first Block
+		 * Data registers (F54_AD_Data0). */
+		result =
+		    rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr,
+								&data, 1);
+		mutex_unlock(&instance_data->status_mutex);
+		if (result < 0) {
+			dev_err(dev, "%s : Could not write report type to"
+				" 0x%x\n", __func__, fc->fd.data_base_addr);
+			return result;
+		}
+		return count;
+	} else {
+		dev_err(dev, "%s : Report type cannot be changed in the middle"
+				" of command.\n", __func__);
+		mutex_unlock(&instance_data->status_mutex);
+		return -EINVAL;
+	}
+}
+
+static ssize_t rmi_fn_54_get_report_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count) {
+	unsigned long val;
+	int error, result;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+	struct rmi_driver *driver;
+	u8 command;
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	driver = fc->rmi_dev->driver;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+	if (error)
+		return error;
+	/* Do nothing if not set to 1. This prevents accidental commands. */
+	if (val != 1)
+		return count;
+	command = (unsigned char)GET_REPORT;
+	/* Basic checks on report_type to ensure we write a valid type
+	 * to the sensor.
+	 * TODO: Check Query3 to see if some specific reports are
+	 * available. This is currently listed as a reserved register.
+	 */
+	if (!is_report_type_valid(instance_data->report_type)) {
+		dev_err(dev, "%s : Report type %d is invalid.\n",
+				__func__, instance_data->report_type);
+		return -EINVAL;
+	}
+	mutex_lock(&instance_data->status_mutex);
+	if (instance_data->status != IDLE) {
+		if (instance_data->status != BUSY) {
+			dev_err(dev, "F54 status is in an abnormal state: 0x%x",
+							instance_data->status);
+		}
+		mutex_unlock(&instance_data->status_mutex);
+		return count;
+	}
+	/* Store interrupts */
+	/* Do not exit if we fail to turn off interupts. We are likely
+	 * to still get useful data. The report data can, however, be
+	 * corrupted, and there may be unexpected behavior.
+	 */
+	dev_dbg(dev, "Storing and overriding interupts\n");
+	if (driver->store_irq_mask)
+		driver->store_irq_mask(fc->rmi_dev,
+					fc->irq_mask);
+	else
+		dev_err(dev, "No way to store interupts!\n");
+	instance_data->status = BUSY;
+
+	/* small delay to avoid race condition in firmare. This value is a bit
+	 * higher than absolutely necessary. Should be removed once issue is
+	 * resolved in firmware. */
+
+	mdelay(2);
+
+	/* Write the command to the command register */
+	result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+						&command, 1);
+	mutex_unlock(&instance_data->status_mutex);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write command to 0x%x\n",
+				__func__, fc->fd.command_base_addr);
+		return result;
+	}
+#if F54_WATCHDOG
+	/* start watchdog timer */
+	hrtimer_start(&instance_data->watchdog, ktime_set(1, 0),
+							HRTIMER_MODE_REL);
+#endif
+	return count;
+}
+
+
+static ssize_t rmi_fn_54_force_cal_store(struct device *dev,
+				   struct device_attribute *attr,
+				   const char *buf, size_t count) {
+	unsigned long val;
+	int error, result;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+	struct rmi_driver *driver;
+	u8 command;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	driver = fc->rmi_dev->driver;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+	if (error)
+		return error;
+	/* Do nothing if not set to 1. This prevents accidental commands. */
+	if (val != 1)
+		return count;
+
+	command = (unsigned char)FORCE_CAL;
+
+	if (instance_data->status == BUSY)
+		return -EBUSY;
+	/* Write the command to the command register */
+	result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+						&command, 1);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write command to 0x%x\n",
+				__func__, fc->fd.command_base_addr);
+		return result;
+	}
+	return count;
+}
+
+static ssize_t rmi_fn_54_status_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", instance_data->status);
+}
+
+
+
+static ssize_t rmi_fn_54_status_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	mutex_lock(&instance_data->status_mutex);
+	/* any write to status resets it */
+	instance_data->status = 0;
+	mutex_unlock(&instance_data->status_mutex);
+
+	return 0;
+}
+
+
+static ssize_t rmi_fn_54_num_rx_electrodes_show(struct device *dev,
+				     struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+				data->query.number_of_receiver_electrodes);
+}
+
+static ssize_t rmi_fn_54_num_tx_electrodes_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.number_of_transmitter_electrodes);
+}
+
+static ssize_t rmi_fn_54_has_image16_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_image16);
+}
+
+static ssize_t rmi_fn_54_has_image8_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_image8);
+}
+
+static ssize_t rmi_fn_54_has_baseline_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_baseline);
+}
+
+static ssize_t rmi_fn_54_clock_rate_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.clock_rate);
+}
+
+
+static ssize_t rmi_fn_54_touch_controller_family_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.touch_controller_family);
+}
+
+
+static ssize_t rmi_fn_54_has_pixel_touch_threshold_adjustment_show(
+		struct device *dev, struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_pixel_touch_threshold_adjustment);
+}
+
+static ssize_t rmi_fn_54_has_sensor_assignment_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_sensor_assignment);
+}
+
+static ssize_t rmi_fn_54_has_interference_metric_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_interference_metric);
+}
+
+static ssize_t rmi_fn_54_has_sense_frequency_control_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_sense_frequency_control);
+}
+
+static ssize_t rmi_fn_54_has_firmware_noise_mitigation_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_firmware_noise_mitigation);
+}
+
+static ssize_t rmi_fn_54_has_two_byte_report_rate_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_two_byte_report_rate);
+}
+
+static ssize_t rmi_fn_54_has_one_byte_report_rate_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_one_byte_report_rate);
+}
+
+static ssize_t rmi_fn_54_has_relaxation_control_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_relaxation_control);
+}
+
+static ssize_t rmi_fn_54_curve_compensation_mode_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.curve_compensation_mode);
+}
+
+static ssize_t rmi_fn_54_has_iir_filter_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_iir_filter);
+}
+
+static ssize_t rmi_fn_54_has_cmn_removal_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_cmn_removal);
+}
+
+static ssize_t rmi_fn_54_has_cmn_maximum_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_cmn_maximum);
+}
+
+static ssize_t rmi_fn_54_has_pixel_threshold_hysteresis_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_pixel_threshold_hysteresis);
+}
+
+static ssize_t rmi_fn_54_has_edge_compensation_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_edge_compensation);
+}
+
+static ssize_t rmi_fn_54_has_perf_frequency_noisecontrol_show(
+		struct device *dev, struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.has_perf_frequency_noisecontrol);
+}
+
+static ssize_t rmi_fn_54_number_of_sensing_frequencies_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *data;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+			data->query.number_of_sensing_frequencies);
+}
+
+
+static ssize_t rmi_fn_54_no_auto_cal_show(struct device *dev,
+				struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+				instance_data->no_auto_cal ? 1 : 0);
+}
+
+static ssize_t rmi_fn_54_no_auto_cal_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count) {
+	int result;
+	unsigned long val;
+	unsigned char data;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	/* need to convert the string data to an actual value */
+	result = strict_strtoul(buf, 10, &val);
+
+	/* if an error occured, return it */
+	if (result)
+		return result;
+	/* Do nothing if not 0 or 1. This prevents accidental commands. */
+	if (val > 1)
+		return count;
+	/* Read current control values */
+	result =
+	    rmi_read_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1);
+
+	/* if the current control registers are already set as we want them, do
+	 * nothing to them */
+	if ((data & 1) == val)
+		return count;
+	/* Write the control back to the control register (F54_AD_Ctrl0)
+	 * Ignores everything but bit 0 */
+	data = (data & ~1) | (val & 0x01); /* bit mask for lowest bit */
+	result =
+	    rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1);
+	if (result < 0) {
+		dev_err(dev, "%s : Could not write control to 0x%x\n",
+		       __func__, fc->fd.control_base_addr);
+		return result;
+	}
+	/* update our internal representation iff the write succeeds */
+	instance_data->no_auto_cal = (val == 1);
+	return count;
+}
+
+static ssize_t rmi_fn_54_fifoindex_show(struct device *dev,
+				  struct device_attribute *attr, char *buf) {
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+	struct rmi_driver *driver;
+	unsigned char temp_buf[2];
+	int retval;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	driver = fc->rmi_dev->driver;
+
+	/* Read fifoindex from device */
+	retval = rmi_read_block(fc->rmi_dev,
+				fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET,
+				temp_buf, ARRAY_SIZE(temp_buf));
+
+	if (retval < 0) {
+		dev_err(dev, "Could not read fifoindex from 0x%04x\n",
+		       fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET);
+		return retval;
+	}
+	batohs(&instance_data->fifoindex, temp_buf);
+	return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->fifoindex);
+}
+
+static ssize_t rmi_fn_54_fifoindex_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf,
+					size_t count)
+{
+	int error;
+	unsigned long val;
+	unsigned char data[2];
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+
+	/* need to convert the string data to an actual value */
+	error = strict_strtoul(buf, 10, &val);
+
+	if (error)
+		return error;
+
+	instance_data->fifoindex = val;
+
+	/* Write the FifoIndex back to the first data registers. */
+	hstoba(data, (unsigned short)val);
+
+	error = rmi_write_block(fc->rmi_dev,
+				fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET,
+				data,
+				ARRAY_SIZE(data));
+
+	if (error < 0) {
+		dev_err(dev, "%s : Could not write fifoindex to 0x%x\n",
+		       __func__, fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET);
+		return error;
+	}
+	return count;
+}
+
+/* Provide access to last report */
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+#else
+static ssize_t rmi_fn_54_data_read(struct kobject *kobj,
+#endif
+				struct bin_attribute *attributes,
+				char *buf, loff_t pos, size_t count)
+{
+	struct device *dev;
+	struct rmi_function_container *fc;
+	struct rmi_fn_54_data *instance_data;
+
+	dev = container_of(kobj, struct device, kobj);
+	fc = to_rmi_function_container(dev);
+	instance_data = fc->data;
+	mutex_lock(&instance_data->data_mutex);
+	if (count < instance_data->report_size) {
+		dev_err(dev,
+			"%s: F54 report size too large for buffer: %d."
+				" Need at least: %d for Report type: %d.\n",
+			__func__, count, instance_data->report_size,
+			instance_data->report_type);
+		mutex_unlock(&instance_data->data_mutex);
+		return -EINVAL;
+	}
+	if (instance_data->report_data) {
+		/* Copy data from instance_data to buffer */
+		memcpy(buf, instance_data->report_data,
+					instance_data->report_size);
+		mutex_unlock(&instance_data->data_mutex);
+		dev_dbg(dev, "%s: Presumably successful.", __func__);
+		return instance_data->report_size;
+	} else {
+		dev_err(dev, "%s: F54 report_data does not exist!\n", __func__);
+		mutex_unlock(&instance_data->data_mutex);
+		return -EINVAL;
+	}
+}
+
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x54,
+	.init = rmi_f54_init,
+	.config = rmi_f54_config,
+	.reset = rmi_f54_reset,
+	.attention = rmi_f54_attention,
+	.remove = rmi_f54_remove
+};
+
+static int __init rmi_f54_module_init(void)
+{
+	int error;
+
+	error = rmi_register_function_driver(&function_handler);
+	if (error < 0) {
+		pr_err("%s: register failed!\n", __func__);
+		return error;
+	}
+	return 0;
+}
+
+static void rmi_f54_module_exit(void)
+{
+	/* There is no function handler specific data to be freed here.
+	 * Function container specific data will be freed as part of the
+	 * unregistration process.
+	 */
+	rmi_unregister_function_driver(&function_handler);
+}
+
+module_init(rmi_f54_module_init);
+module_exit(rmi_f54_module_exit);
+
+MODULE_AUTHOR("Daniel Rosenberg <daniel.rosenberg@...aptics.com>");
+MODULE_DESCRIPTION("RMI F54 module");
+MODULE_LICENSE("GPL");
+
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ