linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Sebastian Reichel <sebastian.reichel@collabora.com>
To: Dmitry Torokhov <dmitry.torokhov@gmail.com>, Ahmet Inan <inan@distec.de>
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	kernel@collabora.com,
	Sebastian Reichel <sebastian.reichel@collabora.com>
Subject: [PATCHv1 2/2] Input: EXC3000: Add support to query model and fw_version
Date: Thu,  7 Nov 2019 19:10:10 +0100	[thread overview]
Message-ID: <20191107181010.17211-2-sebastian.reichel@collabora.com> (raw)
In-Reply-To: <20191107181010.17211-1-sebastian.reichel@collabora.com>

Expose model and fw_version via sysfs. Also query the model
in probe to make sure, that the I2C communication with the
device works before successfully probing the driver.

Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
 drivers/input/touchscreen/exc3000.c | 136 ++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)

diff --git a/drivers/input/touchscreen/exc3000.c b/drivers/input/touchscreen/exc3000.c
index 7d695022082c..4c9f132629b9 100644
--- a/drivers/input/touchscreen/exc3000.c
+++ b/drivers/input/touchscreen/exc3000.c
@@ -41,6 +41,11 @@ struct exc3000_data {
 	struct touchscreen_properties prop;
 	struct timer_list timer;
 	u8 buf[2 * EXC3000_LEN_FRAME];
+	char model[11];
+	char fw_version[6];
+	struct completion wait_event;
+	struct mutex query_lock;
+	int query_result;
 };
 
 static void exc3000_report_slots(struct input_dev *input,
@@ -121,6 +126,35 @@ static int exc3000_read_data(struct i2c_client *client,
 	return 0;
 }
 
+static void exc3000_query_interrupt(struct exc3000_data *data)
+{
+	u8 *buf = data->buf;
+	int err;
+
+	data->query_result = 0;
+
+	err = i2c_master_recv(data->client, buf, EXC3000_LEN_FRAME);
+	if (err < 0) {
+		data->query_result = err;
+		goto out;
+	}
+
+	if (buf[0] == 0x42 && buf[4] == 'E') {
+		memcpy(data->model, buf+5, 10);
+		data->model[10] = '\0';
+		goto out;
+	} else if (buf[0] == 0x42 && buf[4] == 'D') {
+		memcpy(data->fw_version, buf+5, 5);
+		data->fw_version[5] = '\0';
+		goto out;
+	}
+
+	data->query_result = -EPROTO;
+
+out:
+	complete(&data->wait_event);
+}
+
 static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
 {
 	struct exc3000_data *data = dev_id;
@@ -129,6 +163,11 @@ static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
 	int slots, total_slots;
 	int error;
 
+	if (mutex_is_locked(&data->query_lock)) {
+		exc3000_query_interrupt(data);
+		goto out;
+	}
+
 	error = exc3000_read_data(data->client, buf, &total_slots);
 	if (error) {
 		/* Schedule a timer to release "stuck" contacts */
@@ -156,12 +195,92 @@ static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static int exc3000_get_fw_version(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct exc3000_data *data = dev_get_drvdata(dev);
+	static const u8 request[68] =
+		{0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'D', 0x00};
+	struct i2c_client *client = data->client;
+	int err;
+
+	mutex_lock(&data->query_lock);
+
+	data->query_result = -ETIMEDOUT;
+	reinit_completion(&data->wait_event);
+
+	err = i2c_master_send(client, request, sizeof(request));
+	if (err < 0) {
+		mutex_unlock(&data->query_lock);
+		return err;
+	}
+
+	wait_for_completion_interruptible_timeout(&data->wait_event, 1*HZ);
+	mutex_unlock(&data->query_lock);
+
+	if (data->query_result < 0)
+		return data->query_result;
+
+	return sprintf(buf, "%s\n", data->fw_version);
+}
+static DEVICE_ATTR(fw_version, S_IRUGO, exc3000_get_fw_version, NULL);
+
+static ssize_t exc3000_get_model(struct exc3000_data *data)
+{
+	static const u8 request[68] =
+		{0x67, 0x00, 0x42, 0x00, 0x03, 0x01, 'E', 0x00};
+	struct i2c_client *client = data->client;
+	int err;
+
+	mutex_lock(&data->query_lock);
+	data->query_result = -ETIMEDOUT;
+	reinit_completion(&data->wait_event);
+
+	err = i2c_master_send(client, request, sizeof(request));
+	if (err < 0) {
+		mutex_unlock(&data->query_lock);
+		return err;
+	}
+
+	wait_for_completion_interruptible_timeout(&data->wait_event, 1*HZ);
+	mutex_unlock(&data->query_lock);
+
+	return data->query_result;
+}
+
+static ssize_t exc3000_get_model_sysfs(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct exc3000_data *data = dev_get_drvdata(dev);
+	int err = exc3000_get_model(data);
+
+	if (err < 0)
+		return err;
+
+	return sprintf(buf, "%s\n", data->model);
+}
+static DEVICE_ATTR(model, S_IRUGO, exc3000_get_model_sysfs, NULL);
+
+static struct attribute *sysfs_attrs[] = {
+	&dev_attr_model.attr,
+	&dev_attr_fw_version.attr,
+	NULL
+};
+
+static struct attribute_group exc3000_attribute_group = {
+	.attrs = sysfs_attrs
+};
+
+static const struct attribute_group *exc3000_attribute_groups[] = {
+	&exc3000_attribute_group,
+	NULL
+};
+
 static int exc3000_probe(struct i2c_client *client,
 			 const struct i2c_device_id *id)
 {
 	struct exc3000_data *data;
 	struct input_dev *input;
 	int error;
+	int retry;
 
 	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
@@ -170,15 +289,19 @@ static int exc3000_probe(struct i2c_client *client,
 	data->client = client;
 	data->type = id->driver_data;
 	timer_setup(&data->timer, exc3000_timer, 0);
+	init_completion(&data->wait_event);
+	mutex_init(&data->query_lock);
 
 	input = devm_input_allocate_device(&client->dev);
 	if (!input)
 		return -ENOMEM;
 
 	data->input = input;
+	input_set_drvdata(input, data);
 
 	input->name = "EETI EXC3000 Touch Screen";
 	input->id.bustype = BUS_I2C;
+	input->dev.groups = exc3000_attribute_groups;
 
 	if (data->type == EETI_EXC80Hxx) {
 		input_set_abs_params(input, ABS_MT_POSITION_X, 0, 16383, 0, 0);
@@ -204,6 +327,19 @@ static int exc3000_probe(struct i2c_client *client,
 	if (error)
 		return error;
 
+	for (retry = 0; retry < 3; ++retry) {
+		error = exc3000_get_model(data);
+		if (!error) {
+			break;
+		}
+		dev_warn(&client->dev, "Retry %d get EETI EXC3000 model: %d\n", retry + 1, error);
+	}
+
+	if (error)
+		return error;
+
+	dev_dbg(&client->dev, "TS Model: %s", data->model);
+
 	return 0;
 }
 
-- 
2.24.0.rc1


  reply	other threads:[~2019-11-07 18:10 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-11-07 18:10 [PATCHv1 1/2] Input: EXC3000: add EXC80Hxx support Sebastian Reichel
2019-11-07 18:10 ` Sebastian Reichel [this message]
2019-11-13  0:23   ` [PATCHv1 2/2] Input: EXC3000: Add support to query model and fw_version Dmitry Torokhov
2019-11-20 11:47     ` Martin Fuzzey
2019-11-22 17:43   ` Martin Fuzzey
2019-11-13  0:19 ` [PATCHv1 1/2] Input: EXC3000: add EXC80Hxx support Dmitry Torokhov
2019-11-20 11:32 ` Martin Fuzzey

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191107181010.17211-2-sebastian.reichel@collabora.com \
    --to=sebastian.reichel@collabora.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=inan@distec.de \
    --cc=kernel@collabora.com \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).