All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.