Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH 09/12] HID: uhid: rename uhid_raw_request to uhid_hid_raw_request
From: David Herrmann @ 2014-07-29 15:14 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, Benjamin Tissoires, David Herrmann
In-Reply-To: <1406646866-999-1-git-send-email-dh.herrmann@gmail.com>

We use strict prefixed in uhid.c:
  uhid_char_*: implement char-dev callbacks
  uhid_dev_*: implement uhid device management and runtime
  uhid_hid_*: implement hid-dev callbacks

uhid_raw_request is an hid callback, so rename it to uhid_hid_raw_request.

While at it, move it closer to it's extracted helpers and keep the same
order as in "struct hid_driver".

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/uhid.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 8f5e46b..8bf613e 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -203,6 +203,21 @@ unlock:
 	return ret ? ret : len;
 }
 
+static int uhid_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
+				__u8 *buf, size_t len, unsigned char rtype,
+				int reqtype)
+{
+	switch (reqtype) {
+	case HID_REQ_GET_REPORT:
+		return uhid_hid_get_report(hid, reportnum, buf, len, rtype);
+	case HID_REQ_SET_REPORT:
+		/* TODO: implement proper SET_REPORT functionality */
+		return -ENOSYS;
+	default:
+		return -EIO;
+	}
+}
+
 static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,
 			       unsigned char report_type)
 {
@@ -247,29 +262,14 @@ static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf,
 	return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT);
 }
 
-static int uhid_raw_request(struct hid_device *hid, unsigned char reportnum,
-			    __u8 *buf, size_t len, unsigned char rtype,
-			    int reqtype)
-{
-	switch (reqtype) {
-	case HID_REQ_GET_REPORT:
-		return uhid_hid_get_report(hid, reportnum, buf, len, rtype);
-	case HID_REQ_SET_REPORT:
-		/* TODO: implement proper SET_REPORT functionality */
-		return -ENOSYS;
-	default:
-		return -EIO;
-	}
-}
-
 static struct hid_ll_driver uhid_hid_driver = {
 	.start = uhid_hid_start,
 	.stop = uhid_hid_stop,
 	.open = uhid_hid_open,
 	.close = uhid_hid_close,
 	.parse = uhid_hid_parse,
+	.raw_request = uhid_hid_raw_request,
 	.output_report = uhid_hid_output_report,
-	.raw_request = uhid_raw_request,
 };
 
 #ifdef CONFIG_COMPAT
-- 
2.0.3


^ permalink raw reply related

* [PATCH 11/12] HID: uhid: report to user-space whether reports are numbered
From: David Herrmann @ 2014-07-29 15:14 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, Benjamin Tissoires, David Herrmann
In-Reply-To: <1406646866-999-1-git-send-email-dh.herrmann@gmail.com>

This makes UHID_START include a "dev_flags" field that describes details
of the hid-device in the kernel. The first flags we introduce describe
whether a given report-type uses numbered reports. This is useful for
transport layers that force report-numbers and therefore might have to
prefix kernel-provided HID-messages with the report-number.

Currently, only HoG needs this and the spec only talks about "global
report numbers". That is, it's a global boolean not a per-type boolean.
However, given the quirks we already have in kernel-space, a per-type
value seems much more appropriate.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/uhid.c        | 21 ++++++++++++++++++++-
 include/uapi/linux/uhid.h | 11 +++++++++++
 2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 1951148..f6ec5ea 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -92,8 +92,27 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event)
 static int uhid_hid_start(struct hid_device *hid)
 {
 	struct uhid_device *uhid = hid->driver_data;
+	struct uhid_event *ev;
+	unsigned long flags;
+
+	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
+	if (!ev)
+		return -ENOMEM;
+
+	ev->type = UHID_START;
 
-	return uhid_queue_event(uhid, UHID_START);
+	if (hid->report_enum[HID_FEATURE_REPORT].numbered)
+		ev->u.start.dev_flags |= UHID_DEV_NUMBERED_FEATURE_REPORTS;
+	if (hid->report_enum[HID_OUTPUT_REPORT].numbered)
+		ev->u.start.dev_flags |= UHID_DEV_NUMBERED_OUTPUT_REPORTS;
+	if (hid->report_enum[HID_INPUT_REPORT].numbered)
+		ev->u.start.dev_flags |= UHID_DEV_NUMBERED_INPUT_REPORTS;
+
+	spin_lock_irqsave(&uhid->qlock, flags);
+	uhid_queue(uhid, ev);
+	spin_unlock_irqrestore(&uhid->qlock, flags);
+
+	return 0;
 }
 
 static void uhid_hid_stop(struct hid_device *hid)
diff --git a/include/uapi/linux/uhid.h b/include/uapi/linux/uhid.h
index 62aac0e..aaa86d6 100644
--- a/include/uapi/linux/uhid.h
+++ b/include/uapi/linux/uhid.h
@@ -54,6 +54,16 @@ struct uhid_create2_req {
 	__u8 rd_data[HID_MAX_DESCRIPTOR_SIZE];
 } __attribute__((__packed__));
 
+enum uhid_dev_flag {
+	UHID_DEV_NUMBERED_FEATURE_REPORTS			= (1ULL << 0),
+	UHID_DEV_NUMBERED_OUTPUT_REPORTS			= (1ULL << 1),
+	UHID_DEV_NUMBERED_INPUT_REPORTS				= (1ULL << 2),
+};
+
+struct uhid_start_req {
+	__u64 dev_flags;
+};
+
 #define UHID_DATA_MAX 4096
 
 enum uhid_report_type {
@@ -182,6 +192,7 @@ struct uhid_event {
 		struct uhid_input2_req input2;
 		struct uhid_set_report_req set_report;
 		struct uhid_set_report_reply_req set_report_reply;
+		struct uhid_start_req start;
 	} u;
 } __attribute__((__packed__));
 
-- 
2.0.3


^ permalink raw reply related

* [PATCH 10/12] HID: uhid: implement SET_REPORT
From: David Herrmann @ 2014-07-29 15:14 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, Benjamin Tissoires, David Herrmann
In-Reply-To: <1406646866-999-1-git-send-email-dh.herrmann@gmail.com>

We so far lacked support for hid_hw_raw_request(..., HID_REQ_SET_REPORT);
Add support for it and simply forward the request to user-space. Note that
SET_REPORT is synchronous, just like GET_REPORT, even though it does not
provide any data back besides an error code.

If a transport layer does SET_REPORT asynchronously, they can just ACK it
immediately by writing an uhid_set_report_reply to uhid.

This patch re-uses the synchronous uhid-report infrastructure to query
user-space. Note that this means you cannot run SET_REPORT and GET_REPORT
in parallel. However, that has always been a restriction of HID and due to
its blocking nature, this is just fine. Maybe some future transport layer
supports parallel requests (very unlikely), however, until then lets not
over-complicate things and avoid request-lookup-tables.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 drivers/hid/uhid.c        | 206 +++++++++++++++++++++++++++++++---------------
 include/uapi/linux/uhid.h |  17 ++++
 2 files changed, 155 insertions(+), 68 deletions(-)

diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 8bf613e..1951148 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -49,6 +49,7 @@ struct uhid_device {
 	wait_queue_head_t report_wait;
 	bool report_running;
 	u32 report_id;
+	u32 report_type;
 	struct uhid_event report_buf;
 };
 
@@ -124,95 +125,166 @@ static int uhid_hid_parse(struct hid_device *hid)
 	return hid_parse_report(hid, uhid->rd_data, uhid->rd_size);
 }
 
+/* must be called with report_lock held */
+static int __uhid_report_queue_and_wait(struct uhid_device *uhid,
+					struct uhid_event *ev,
+					__u32 *report_id)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&uhid->qlock, flags);
+	*report_id = ++uhid->report_id;
+	uhid->report_type = ev->type;
+	uhid->report_running = true;
+	uhid_queue(uhid, ev);
+	spin_unlock_irqrestore(&uhid->qlock, flags);
+
+	ret = wait_event_interruptible_timeout(uhid->report_wait,
+				!uhid->report_running || !uhid->running,
+				5 * HZ);
+	if (!ret || !uhid->running || uhid->report_running)
+		ret = -EIO;
+	else if (ret < 0)
+		ret = -ERESTARTSYS;
+	else
+		ret = 0;
+
+	uhid->report_running = false;
+
+	return ret;
+}
+
+static void uhid_report_wake_up(struct uhid_device *uhid, u32 id,
+				const struct uhid_event *ev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uhid->qlock, flags);
+
+	/* id for old report; drop it silently */
+	if (uhid->report_type != ev->type || uhid->report_id != id)
+		goto unlock;
+	if (!uhid->report_running)
+		goto unlock;
+
+	memcpy(&uhid->report_buf, ev, sizeof(*ev));
+	uhid->report_running = false;
+	wake_up_interruptible(&uhid->report_wait);
+
+unlock:
+	spin_unlock_irqrestore(&uhid->qlock, flags);
+}
+
 static int uhid_hid_get_report(struct hid_device *hid, unsigned char rnum,
-			       __u8 *buf, size_t count, unsigned char rtype)
+			       u8 *buf, size_t count, u8 rtype)
 {
 	struct uhid_device *uhid = hid->driver_data;
-	__u8 report_type;
+	struct uhid_get_report_reply_req *req;
 	struct uhid_event *ev;
-	unsigned long flags;
 	int ret;
-	size_t uninitialized_var(len);
-	struct uhid_get_report_reply_req *req;
 
 	if (!uhid->running)
 		return -EIO;
 
-	switch (rtype) {
-	case HID_FEATURE_REPORT:
-		report_type = UHID_FEATURE_REPORT;
-		break;
-	case HID_OUTPUT_REPORT:
-		report_type = UHID_OUTPUT_REPORT;
-		break;
-	case HID_INPUT_REPORT:
-		report_type = UHID_INPUT_REPORT;
-		break;
-	default:
-		return -EINVAL;
-	}
+	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
+	if (!ev)
+		return -ENOMEM;
+
+	ev->type = UHID_GET_REPORT;
+	ev->u.get_report.rnum = rnum;
+	ev->u.get_report.rtype = rtype;
 
 	ret = mutex_lock_interruptible(&uhid->report_lock);
-	if (ret)
+	if (ret) {
+		kfree(ev);
 		return ret;
+	}
 
-	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
-	if (!ev) {
-		ret = -ENOMEM;
+	/* this _always_ takes ownership of @ev */
+	ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.get_report.id);
+	if (ret)
 		goto unlock;
+
+	req = &uhid->report_buf.u.get_report_reply;
+	if (req->err) {
+		ret = -EIO;
+	} else {
+		ret = min3(count, (size_t)req->size, (size_t)UHID_DATA_MAX);
+		memcpy(buf, req->data, ret);
 	}
 
-	spin_lock_irqsave(&uhid->qlock, flags);
-	ev->type = UHID_GET_REPORT;
-	ev->u.get_report.id = ++uhid->report_id;
-	ev->u.get_report.rnum = rnum;
-	ev->u.get_report.rtype = report_type;
+unlock:
+	mutex_unlock(&uhid->report_lock);
+	return ret;
+}
 
-	uhid->report_running = true;
-	uhid_queue(uhid, ev);
-	spin_unlock_irqrestore(&uhid->qlock, flags);
+static int uhid_hid_set_report(struct hid_device *hid, unsigned char rnum,
+			       const u8 *buf, size_t count, u8 rtype)
+{
+	struct uhid_device *uhid = hid->driver_data;
+	struct uhid_event *ev;
+	int ret;
 
-	ret = wait_event_interruptible_timeout(uhid->report_wait,
-				!uhid->report_running || !uhid->running,
-				5 * HZ);
+	if (!uhid->running || count > UHID_DATA_MAX)
+		return -EIO;
 
-	if (!ret || !uhid->running) {
-		ret = -EIO;
-	} else if (ret < 0) {
-		ret = -ERESTARTSYS;
-	} else {
-		spin_lock_irqsave(&uhid->qlock, flags);
-		req = &uhid->report_buf.u.get_report_reply;
+	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
+	if (!ev)
+		return -ENOMEM;
 
-		if (req->err) {
-			ret = -EIO;
-		} else {
-			ret = 0;
-			len = min(count,
-				min_t(size_t, req->size, UHID_DATA_MAX));
-			memcpy(buf, req->data, len);
-		}
+	ev->type = UHID_SET_REPORT;
+	ev->u.set_report.rnum = rnum;
+	ev->u.set_report.rtype = rtype;
+	ev->u.set_report.size = count;
+	memcpy(ev->u.set_report.data, buf, count);
 
-		spin_unlock_irqrestore(&uhid->qlock, flags);
+	ret = mutex_lock_interruptible(&uhid->report_lock);
+	if (ret) {
+		kfree(ev);
+		return ret;
 	}
 
-	uhid->report_running = false;
+	/* this _always_ takes ownership of @ev */
+	ret = __uhid_report_queue_and_wait(uhid, ev, &ev->u.set_report.id);
+	if (ret)
+		goto unlock;
+
+	if (uhid->report_buf.u.set_report_reply.err)
+		ret = -EIO;
+	else
+		ret = count;
 
 unlock:
 	mutex_unlock(&uhid->report_lock);
-	return ret ? ret : len;
+	return ret;
 }
 
 static int uhid_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
 				__u8 *buf, size_t len, unsigned char rtype,
 				int reqtype)
 {
+	u8 u_rtype;
+
+	switch (rtype) {
+	case HID_FEATURE_REPORT:
+		u_rtype = UHID_FEATURE_REPORT;
+		break;
+	case HID_OUTPUT_REPORT:
+		u_rtype = UHID_OUTPUT_REPORT;
+		break;
+	case HID_INPUT_REPORT:
+		u_rtype = UHID_INPUT_REPORT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
 	switch (reqtype) {
 	case HID_REQ_GET_REPORT:
-		return uhid_hid_get_report(hid, reportnum, buf, len, rtype);
+		return uhid_hid_get_report(hid, reportnum, buf, len, u_rtype);
 	case HID_REQ_SET_REPORT:
-		/* TODO: implement proper SET_REPORT functionality */
-		return -ENOSYS;
+		return uhid_hid_set_report(hid, reportnum, buf, len, u_rtype);
 	default:
 		return -EIO;
 	}
@@ -490,25 +562,20 @@ static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev)
 static int uhid_dev_get_report_reply(struct uhid_device *uhid,
 				     struct uhid_event *ev)
 {
-	unsigned long flags;
-
 	if (!uhid->running)
 		return -EINVAL;
 
-	spin_lock_irqsave(&uhid->qlock, flags);
-
-	/* id for old report; drop it silently */
-	if (uhid->report_id != ev->u.get_report_reply.id)
-		goto unlock;
-	if (!uhid->report_running)
-		goto unlock;
+	uhid_report_wake_up(uhid, ev->u.get_report_reply.id, ev);
+	return 0;
+}
 
-	memcpy(&uhid->report_buf, ev, sizeof(*ev));
-	uhid->report_running = false;
-	wake_up_interruptible(&uhid->report_wait);
+static int uhid_dev_set_report_reply(struct uhid_device *uhid,
+				     struct uhid_event *ev)
+{
+	if (!uhid->running)
+		return -EINVAL;
 
-unlock:
-	spin_unlock_irqrestore(&uhid->qlock, flags);
+	uhid_report_wake_up(uhid, ev->u.set_report_reply.id, ev);
 	return 0;
 }
 
@@ -637,6 +704,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
 	case UHID_GET_REPORT_REPLY:
 		ret = uhid_dev_get_report_reply(uhid, &uhid->input_buf);
 		break;
+	case UHID_SET_REPORT_REPLY:
+		ret = uhid_dev_set_report_reply(uhid, &uhid->input_buf);
+		break;
 	default:
 		ret = -EOPNOTSUPP;
 	}
diff --git a/include/uapi/linux/uhid.h b/include/uapi/linux/uhid.h
index 116536e..62aac0e 100644
--- a/include/uapi/linux/uhid.h
+++ b/include/uapi/linux/uhid.h
@@ -37,6 +37,8 @@ enum uhid_event_type {
 	UHID_GET_REPORT_REPLY,
 	UHID_CREATE2,
 	UHID_INPUT2,
+	UHID_SET_REPORT,
+	UHID_SET_REPORT_REPLY,
 };
 
 struct uhid_create2_req {
@@ -84,6 +86,19 @@ struct uhid_get_report_reply_req {
 	__u8 data[UHID_DATA_MAX];
 } __attribute__((__packed__));
 
+struct uhid_set_report_req {
+	__u32 id;
+	__u8 rnum;
+	__u8 rtype;
+	__u16 size;
+	__u8 data[UHID_DATA_MAX];
+} __attribute__((__packed__));
+
+struct uhid_set_report_reply_req {
+	__u32 id;
+	__u16 err;
+} __attribute__((__packed__));
+
 /*
  * Compat Layer
  * All these commands and requests are obsolete. You should avoid using them in
@@ -165,6 +180,8 @@ struct uhid_event {
 		struct uhid_get_report_reply_req get_report_reply;
 		struct uhid_create2_req create2;
 		struct uhid_input2_req input2;
+		struct uhid_set_report_req set_report;
+		struct uhid_set_report_reply_req set_report_reply;
 	} u;
 } __attribute__((__packed__));
 
-- 
2.0.3


^ permalink raw reply related

* [PATCH 12/12] HID: uhid: update documentation
From: David Herrmann @ 2014-07-29 15:14 UTC (permalink / raw)
  To: linux-input; +Cc: Jiri Kosina, Benjamin Tissoires, David Herrmann
In-Reply-To: <1406646866-999-1-git-send-email-dh.herrmann@gmail.com>

Remove legacy bits, refer people to hid-transport.txt and add descriptions
for all new features.

Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
 Documentation/hid/uhid.txt | 179 +++++++++++++++++++++++----------------------
 1 file changed, 92 insertions(+), 87 deletions(-)

diff --git a/Documentation/hid/uhid.txt b/Documentation/hid/uhid.txt
index 54c8f97..c8656dd 100644
--- a/Documentation/hid/uhid.txt
+++ b/Documentation/hid/uhid.txt
@@ -1,28 +1,13 @@
       UHID - User-space I/O driver support for HID subsystem
      ========================================================
 
-The HID subsystem needs two kinds of drivers. In this document we call them:
+UHID allows user-space to implement HID transport drivers. Please see
+hid-transport.txt for an introduction into HID transport drivers. This document
+relies heavily on the definitions declared there.
 
- 1. The "HID I/O Driver" is the driver that performs raw data I/O to the
-    low-level device. Internally, they register an hid_ll_driver structure with
-    the HID core. They perform device setup, read raw data from the device and
-    push it into the HID subsystem and they provide a callback so the HID
-    subsystem can send data to the device.
-
- 2. The "HID Device Driver" is the driver that parses HID reports and reacts on
-    them. There are generic drivers like "generic-usb" and "generic-bluetooth"
-    which adhere to the HID specification and provide the standardizes features.
-    But there may be special drivers and quirks for each non-standard device out
-    there. Internally, they use the hid_driver structure.
-
-Historically, the USB stack was the first subsystem to provide an HID I/O
-Driver. However, other standards like Bluetooth have adopted the HID specs and
-may provide HID I/O Drivers, too. The UHID driver allows to implement HID I/O
-Drivers in user-space and feed the data into the kernel HID-subsystem.
-
-This allows user-space to operate on the same level as USB-HID, Bluetooth-HID
-and similar. It does not provide a way to write HID Device Drivers, though. Use
-hidraw for this purpose.
+With UHID, a user-space transport driver can create kernel hid-devices for each
+device connected to the user-space controlled bus. The UHID API defines the I/O
+events provided from the kernel to user-space and vice versa.
 
 There is an example user-space application in ./samples/uhid/uhid-example.c
 
@@ -42,8 +27,9 @@ by setting O_NONBLOCK.
 struct uhid_event {
         __u32 type;
         union {
-                struct uhid_create_req create;
-                struct uhid_data_req data;
+                struct uhid_create2_req create2;
+                struct uhid_output_req output;
+                struct uhid_input2_req input2;
                 ...
         } u;
 };
@@ -54,8 +40,11 @@ multiple write()'s. A single event must always be sent as a whole. Furthermore,
 only a single event can be sent per read() or write(). Pending data is ignored.
 If you want to handle multiple events in a single syscall, then use vectored
 I/O with readv()/writev().
+The "type" field defines the payload. For each type, there is a
+payload-structure available in the union "u" (except for empty payloads). This
+payload contains management and/or device data.
 
-The first thing you should do is sending an UHID_CREATE event. This will
+The first thing you should do is sending an UHID_CREATE2 event. This will
 register the device. UHID will respond with an UHID_START event. You can now
 start sending data to and reading data from UHID. However, unless UHID sends the
 UHID_OPEN event, the internally attached HID Device Driver has no user attached.
@@ -69,12 +58,20 @@ ref-counting for you.
 You may decide to ignore UHID_OPEN/UHID_CLOSE, though. I/O is allowed even
 though the device may have no users.
 
-If you want to send data to the HID subsystem, you send an HID_INPUT event with
-your raw data payload. If the kernel wants to send data to the device, you will
-read an UHID_OUTPUT or UHID_OUTPUT_EV event.
+If you want to send data on the interrupt channel to the HID subsystem, you send
+an HID_INPUT2 event with your raw data payload. If the kernel wants to send data
+on the interrupt channel to the device, you will read an UHID_OUTPUT event.
+Data requests on the control channel are currently limited to GET_REPORT and
+SET_REPORT (no other data reports on the control channel are defined so far).
+Those requests are always synchronous. That means, the kernel sends
+UHID_GET_REPORT and UHID_SET_REPORT events and requires you to forward them to
+the device on the control channel. Once the device responds, you must forward
+the response via UHID_GET_REPORT_REPLY and UHID_SET_REPORT_REPLY to the kernel.
+The kernel blocks internal driver-execution during such round-trips (times out
+after a hard-coded period).
 
 If your device disconnects, you should send an UHID_DESTROY event. This will
-unregister the device. You can now send UHID_CREATE again to register a new
+unregister the device. You can now send UHID_CREATE2 again to register a new
 device.
 If you close() the fd, the device is automatically unregistered and destroyed
 internally.
@@ -82,73 +79,79 @@ internally.
 write()
 -------
 write() allows you to modify the state of the device and feed input data into
-the kernel. The following types are supported: UHID_CREATE, UHID_DESTROY and
-UHID_INPUT. The kernel will parse the event immediately and if the event ID is
+the kernel. The kernel will parse the event immediately and if the event ID is
 not supported, it will return -EOPNOTSUPP. If the payload is invalid, then
 -EINVAL is returned, otherwise, the amount of data that was read is returned and
-the request was handled successfully.
+the request was handled successfully. O_NONBLOCK does not affect write() as
+writes are always handled immediately in a non-blocking fashion. Future requests
+might make use of O_NONBLOCK, though.
 
-  UHID_CREATE:
+  UHID_CREATE2:
   This creates the internal HID device. No I/O is possible until you send this
-  event to the kernel. The payload is of type struct uhid_create_req and
+  event to the kernel. The payload is of type struct uhid_create2_req and
   contains information about your device. You can start I/O now.
 
-  UHID_CREATE2:
-  Same as UHID_CREATE, but the HID report descriptor data (rd_data) is an array
-  inside struct uhid_create2_req, instead of a pointer to a separate array.
-  Enables use from languages that don't support pointers, e.g. Python.
-
   UHID_DESTROY:
   This destroys the internal HID device. No further I/O will be accepted. There
   may still be pending messages that you can receive with read() but no further
   UHID_INPUT events can be sent to the kernel.
-  You can create a new device by sending UHID_CREATE again. There is no need to
+  You can create a new device by sending UHID_CREATE2 again. There is no need to
   reopen the character device.
 
-  UHID_INPUT:
-  You must send UHID_CREATE before sending input to the kernel! This event
-  contains a data-payload. This is the raw data that you read from your device.
-  The kernel will parse the HID reports and react on it.
-
   UHID_INPUT2:
-  Same as UHID_INPUT, but the data array is the last field of uhid_input2_req.
-  Enables userspace to write only the required bytes to kernel (ev.type +
-  ev.u.input2.size + the part of the data array that matters), instead of
-  the entire struct uhid_input2_req.
-
-  UHID_FEATURE_ANSWER:
-  If you receive a UHID_FEATURE request you must answer with this request. You
-  must copy the "id" field from the request into the answer. Set the "err" field
-  to 0 if no error occurred or to EIO if an I/O error occurred.
+  You must send UHID_CREATE2 before sending input to the kernel! This event
+  contains a data-payload. This is the raw data that you read from your device
+  on the interrupt channel. The kernel will parse the HID reports.
+
+  UHID_GET_REPORT_REPLY:
+  If you receive a UHID_GET_REPORT request you must answer with this request.
+  You  must copy the "id" field from the request into the answer. Set the "err"
+  field to 0 if no error occurred or to EIO if an I/O error occurred.
   If "err" is 0 then you should fill the buffer of the answer with the results
-  of the feature request and set "size" correspondingly.
+  of the GET_REPORT request and set "size" correspondingly.
+
+  UHID_SET_REPORT_REPLY:
+  This is the SET_REPORT equivalent of UHID_GET_REPORT_REPLY. Unlike GET_REPORT,
+  SET_REPORT never returns a data buffer, therefore, it's sufficient to set the
+  "id" and "err" fields correctly.
 
 read()
 ------
-read() will return a queued output report. These output reports can be of type
-UHID_START, UHID_STOP, UHID_OPEN, UHID_CLOSE, UHID_OUTPUT or UHID_OUTPUT_EV. No
-reaction is required to any of them but you should handle them according to your
-needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads.
+read() will return a queued output report. No reaction is required to any of
+them but you should handle them according to your needs.
 
   UHID_START:
   This is sent when the HID device is started. Consider this as an answer to
-  UHID_CREATE. This is always the first event that is sent.
+  UHID_CREATE2. This is always the first event that is sent. Note that this
+  event might not be available immediately after write(UHID_CREATE2) returns.
+  Device drivers might required delayed setups.
+  This event contains a payload of type uhid_start_req. The "dev_flags" field
+  describes special behaviors of a device. The following flags are defined:
+      UHID_DEV_NUMBERED_FEATURE_REPORTS:
+      UHID_DEV_NUMBERED_OUTPUT_REPORTS:
+      UHID_DEV_NUMBERED_INPUT_REPORTS:
+          Each of these flags defines whether a given report-type uses numbered
+          reports. If numbered reports are used for a type, all messages from
+          the kernel already have the report-number as prefix. Otherwise, no
+          prefix is added by the kernel.
+          For messages sent by user-space to the kernel, you must adjust the
+          prefixes according to these flags.
 
   UHID_STOP:
   This is sent when the HID device is stopped. Consider this as an answer to
   UHID_DESTROY.
-  If the kernel HID device driver closes the device manually (that is, you
-  didn't send UHID_DESTROY) then you should consider this device closed and send
-  an UHID_DESTROY event. You may want to reregister your device, though. This is
-  always the last message that is sent to you unless you reopen the device with
-  UHID_CREATE.
+  If you didn't destroy your device via UHID_DESTROY, but the kernel sends an
+  UHID_STOP event, this should usually be ignored. It means that the kernel
+  reloaded/changed the device driver loaded on your HID device (or some other
+  maintenance actions happened).
+  You can usually ignored any UHID_STOP events safely.
 
   UHID_OPEN:
   This is sent when the HID device is opened. That is, the data that the HID
   device provides is read by some other process. You may ignore this event but
   it is useful for power-management. As long as you haven't received this event
   there is actually no other process that reads your data so there is no need to
-  send UHID_INPUT events to the kernel.
+  send UHID_INPUT2 events to the kernel.
 
   UHID_CLOSE:
   This is sent when there are no more processes which read the HID data. It is
@@ -156,27 +159,29 @@ needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads.
 
   UHID_OUTPUT:
   This is sent if the HID device driver wants to send raw data to the I/O
-  device. You should read the payload and forward it to the device. The payload
-  is of type "struct uhid_data_req".
+  device on the interrupt channel. You should read the payload and forward it to
+  the device. The payload is of type "struct uhid_data_req".
   This may be received even though you haven't received UHID_OPEN, yet.
 
-  UHID_OUTPUT_EV (obsolete):
-  Same as UHID_OUTPUT but this contains a "struct input_event" as payload. This
-  is called for force-feedback, LED or similar events which are received through
-  an input device by the HID subsystem. You should convert this into raw reports
-  and send them to your device similar to events of type UHID_OUTPUT.
-  This is no longer sent by newer kernels. Instead, HID core converts it into a
-  raw output report and sends it via UHID_OUTPUT.
-
-  UHID_FEATURE:
-  This event is sent if the kernel driver wants to perform a feature request as
-  described in the HID specs. The report-type and report-number are available in
-  the payload.
-  The kernel serializes feature requests so there will never be two in parallel.
-  However, if you fail to respond with a UHID_FEATURE_ANSWER in a time-span of 5
-  seconds, then the requests will be dropped and a new one might be sent.
-  Therefore, the payload also contains an "id" field that identifies every
-  request.
-
-Document by:
-  David Herrmann <dh.herrmann@googlemail.com>
+  UHID_GET_REPORT:
+  This event is sent if the kernel driver wants to perform a GET_REPORT request
+  on the control channeld as described in the HID specs. The report-type and
+  report-number are available in the payload.
+  The kernel serializes GET_REPORT requests so there will never be two in
+  parallel. However, if you fail to respond with a UHID_GET_REPORT_REPLY, the
+  request might silently time out.
+  Once you read a GET_REPORT request, you shall forward it to the hid device and
+  remember the "id" field in the payload. Once your hid device responds to the
+  GET_REPORT (or if it fails), you must send a UHID_GET_REPORT_REPLY to the
+  kernel with the exact same "id" as in the request. If the request already
+  timed out, the kernel will ignore the response silently. The "id" field is
+  never re-used, so conflicts cannot happen.
+
+  UHID_SET_REPORT:
+  This is the SET_REPORT equivalent of UHID_GET_REPORT. On receipt, you shall
+  send a SET_REPORT request to your hid device. Once it replies, you must tell
+  the kernel about it via UHID_SET_REPORT_REPLY.
+  The same restrictions as for UHID_GET_REPORT apply.
+
+----------------------------------------------------
+Written 2012, David Herrmann <dh.herrmann@gmail.com>
-- 
2.0.3


^ permalink raw reply related

* Re: [PATCH 00/15] atmel_mxt_ts - device tree, bootloader, etc
From: Stephen Warren @ 2014-07-29 16:16 UTC (permalink / raw)
  To: Yufeng Shen, Nick Dyer
  Cc: Dmitry Torokhov, benson Leung, Daniel Kurtz, Henrik Rydberg,
	Joonyoung Shim, Alan Bowens, linux-input,
	linux-kernel@vger.kernel.org, Peter Meerwald, Olof Johansson,
	Sekhar Nori
In-Reply-To: <CAPDwgkPL1utj3DFoFbOCcJsASpf+12nvKg1KcseV_MhEz66eiw@mail.gmail.com>

On 07/28/2014 06:10 PM, Yufeng Shen wrote:
> On Mon, Jul 28, 2014 at 7:42 PM, Stephen Warren <swarren@wwwdotorg.org> wrote:
>> On 07/28/2014 03:23 PM, Stephen Warren wrote:
>>> On 07/28/2014 02:20 PM, Yufeng Shen wrote:
>>
>> ...
>>
>>>> Where did you get the configuration file ? It is possible that we rely
>>>> too much on mxt_start to turn on the T9.CTRL bit and have neglected
>>>> its setting in the config file.
>>>> If you can tell me where you get the config file I can do a check.
>>>
>>>
>>> It was already flashed into the touchpad when I received the board. I
>>> did try to track down the firmware/config files a few months ago, but
>>> didn't manage to; I was told since they were already flashed so I didn't
>>> need them. The board is Venice2.
>>
>> OK, I received the configuration and firmware file that's supposed to be in
>> the touchpad.
>>
>> I can see that the config file I was given has the "83" byte in the T9
>> configuration, and in fact /almost/ exactly matches the configuration I
>> have. I don't know why my T9 configuration was wrong before, but I suspect
>> it's not worth trying to track that down.
>>
>> Anyway, here's the diff between the two config files:
>>
>>> # diff -u mxt-save-after-t9-83-write.xml 224sl.raw
>>> --- mxt-save-after-t9-83-write.xml      2014-07-25 19:41:45.000000000
>>> +0000
>>> +++ 224sl.raw   2014-07-28 23:25:49.000000000 +0000
>>> @@ -1,8 +1,7 @@
>>>   OBP_RAW V1
>>>   82 01 10 AA 12 0C 16
>>>   F5AF33
>>> -000000
>>> -0025 0000 0082 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>> 00 00 00 00 00 00 00 00 00 00 00
>>> +E21E65
>>>   0026 0000 0008 00 00 00 00 00 00 00 00
>>>   0007 0000 0004 20 10 32 00
>>>   0008 0000 000A 1E 00 28 28 00 00 00 00 00 00
>>
>>
>> It seems that the T25(?) entry is missing in the new/expected configuration
>> file. I figured I'd try out the new/expected configuration file, so did:
>
> T37 (0x25) is DEBUG_DIAGNOSTIC object which the host can read debugging info
> from. It is not useful to have a initial config for it so usually CrOS
> system would just don't include configuration for this object.

OK, that makes sense.

I also tested mxt-app --zero to clear the config, the dumped it with 
--save to verify it was cleared, then --load 224sl.raw and then --save 
to verify it was programmed back correctly. That seemed to all work fine.

I then tried updating the firmware. This didn't work at all.

First I tried via mxt-app:

> root@localhost:~# ./obp-utils/mxt-app -d i2c-dev:1-004b --flash 130.1_1.0.170.bin
> Version:1.16-65-g0a4c
> Opening firmware file 130.1_1.0.170.bin
> Registered i2c-dev adapter:1 address:0x4b
> Chip detected
> Current firmware version: 1.0.AA
> Skipping version check
> Resetting in bootloader mode
> Registered i2c-dev adapter:1 address:0x25
> Error Remote I/O error (121) reading from i2c
> Bootloader read failure
> Bootloader not found

Then I power-cycled and tried via the atmel_mxt_ts modules' sysfs files:

> root@localhost:~# echo 1 > /sys/devices/soc0/7000c400.i2c/i2c-1/1-004b/update_fw
> [   38.495420] atmel_mxt_ts 1-004b: mxt_bootloader_read: i2c recv failed (-121)
> [   38.506208] atmel_mxt_ts 1-004b: mxt_bootloader_read: i2c recv failed (-121)
> [   38.513836] atmel_mxt_ts 1-004b: The firmware update failed(-121)
> -bash: echo: write error: Remote I/O error

I also found that removing the module (even without attempting a FW 
update) yields:

After attempted FW update via sysfs:

> root@localhost:~# rmmod ./atmel_mxt_ts.ko
> [   81.995672] Unable to handle kernel NULL pointer dereference at virtual address 00000364
> [   82.003828] pgd = e8cd0000
> [   82.006548] [00000364] *pgd=00000000
> [   82.010221] Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> [   82.015537] Modules linked in: atmel_mxt_ts(-)
> [   82.020007] CPU: 0 PID: 836 Comm: rmmod Not tainted 3.16.0-rc7-next-20140729-00011-gead0778e710c-dirty #7
> [   82.029559] task: e98ba140 ti: e8cc8000 task.ti: e8cc8000
> [   82.034961] PC is at input_unregister_device+0x8/0x70
> [   82.040010] LR is at mxt_remove+0x28/0x44 [atmel_mxt_ts]
> [   82.045315] pc : [<c039de7c>]    lr : [<bf000410>]    psr: 60000113
> [   82.045315] sp : e8cc9f08  ip : e97c7900  fp : 00000800
> [   82.056774] r10: 00000000  r9 : e8cc8000  r8 : c000e924
> [   82.061990] r7 : 00000081  r6 : ea1a7a54  r5 : bf003660  r4 : 00000000
> [   82.068505] r3 : 0000000c  r2 : 0000000a  r1 : 00000000  r0 : 00000000
> [   82.075024] Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
> [   82.082146] Control: 10c5387d  Table: a8cd006a  DAC: 00000015
> [   82.087882] Process rmmod (pid: 836, stack limit = 0xe8cc8240)
> [   82.093704] Stack: (0xe8cc9f08 to 0xe8cca000)
> [   82.098055] 9f00:                   e9a4e040 bf000410 ea1a7a20 c03bbe24 c03bbde0 c02dc12c
> [   82.106221] 9f20: bf003660 ea1a7a20 bf003660 c02dc910 bf003660 bf003704 00000800 c02dbfbc
> [   82.114386] 9f40: 00000000 c0081cdc e9f29018 00000000 bf003704 00000800 e8cc9f4c 656d7461
> [   82.122550] 9f60: 786d5f6c 73745f74 00000000 e98ba63c 00000000 c08c0d74 00000800 c0039d60
> [   82.130716] 9f80: e8c964c0 e8cc8000 e8cc8000 e8cc8000 c000e924 00010ef0 b6f3dd08 00000002
> [   82.138881] 9fa0: 00000000 c000e7a0 b6f3dd08 00000002 b6f3dd38 00000800 0cadcf00 0cadcf00
> [   82.147046] 9fc0: b6f3dd08 00000002 00000000 00000081 b6f3dd08 b6f3d008 beeac848 00000800
> [   82.155211] 9fe0: b6e65070 beeac5c4 b6ee02e9 b6e6507c 80000010 b6f3dd38 00000000 00000000
> [   82.163392] [<c039de7c>] (input_unregister_device) from [<bf000410>] (mxt_remove+0x28/0x44 [atmel_mxt_ts])
> [   82.173042] [<bf000410>] (mxt_remove [atmel_mxt_ts]) from [<c03bbe24>] (i2c_device_remove+0x44/0x5c)
> [   82.182171] [<c03bbe24>] (i2c_device_remove) from [<c02dc12c>] (__device_release_driver+0x70/0xc4)
> [   82.191122] [<c02dc12c>] (__device_release_driver) from [<c02dc910>] (driver_detach+0xac/0xb0)
> [   82.199726] [<c02dc910>] (driver_detach) from [<c02dbfbc>] (bus_remove_driver+0x4c/0x90)
> [   82.207810] [<c02dbfbc>] (bus_remove_driver) from [<c0081cdc>] (SyS_delete_module+0x108/0x194)
> [   82.216417] [<c0081cdc>] (SyS_delete_module) from [<c000e7a0>] (ret_fast_syscall+0x0/0x30)
> [   82.224672] Code: c089ecf0 c0786790 e92d4010 e1a04000 (e5d03364)
> [   82.231059] ---[ end trace e485a1b642f0d1d1 ]---
> Segmentation fault

After nothing but insmod:

> root@localhost:~# rmmod atmel_mxt_ts
> [   25.499625] Alignment trap: not handling instruction e1923f9f at [<c05ec6d8>]
> [   25.506763] Unhandled fault: alignment exception (0x001) at 0x6b6b6cc7
> [   25.513298] Internal error: : 1 [#1] PREEMPT SMP ARM
> [   25.518260] Modules linked in: atmel_mxt_ts(-)
> [   25.522724] CPU: 0 PID: 831 Comm: rmmod Not tainted 3.16.0-rc7-next-20140729-00011-gead0778e710c-dirty #7
> [   25.532277] task: ea205380 ti: e97d0000 task.ti: e97d0000
> [   25.537674] PC is at _raw_spin_lock_irqsave+0x2c/0x64
> [   25.542724] LR is at devres_remove+0x20/0x80
> [   25.546988] pc : [<c05ec6dc>]    lr : [<c02dec90>]    psr: 20000193
> [   25.546988] sp : e97d1ed0  ip : e9b5b5c0  fp : 00000800
> [   25.558446] r10: c039dee4  r9 : e97d0000  r8 : c039c278
> [   25.563662] r7 : e9a7d400  r6 : ea1a7a54  r5 : 6b6b6cc7  r4 : 6b6b6b6b
> [   25.570178] r3 : e97d0000  r2 : 6b6b6cc7  r1 : 00000001  r0 : 20000113
> [   25.576696] Flags: nzCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment user
> [   25.583905] Control: 10c5387d  Table: a996406a  DAC: 00000015
> [   25.589641] Process rmmod (pid: 831, stack limit = 0xe97d0240)
> [   25.595464] Stack: (0xe97d1ed0 to 0xe97d2000)
> [   25.599816] 1ec0:                                     e9a7d400 e9a7d400 00000000 ea1a7a54
> [   25.607983] 1ee0: 00000081 c000e924 00000000 c02df508 e9a7d400 c039de9c e8c16b80 bf0003a4
> [   25.616148] 1f00: 00000019 e8c16b80 bf003660 bf000418 ea1a7a20 c03bbe24 c03bbde0 c02dc12c
> [   25.624313] 1f20: bf003660 ea1a7a20 bf003660 c02dc910 bf003660 bf003704 00000800 c02dbfbc
> [   25.632478] 1f40: 00000000 c0081cdc e9fe0e78 00000000 bf003704 00000800 e97d1f4c 656d7461
> [   25.640644] 1f60: 786d5f6c 73745f74 00000000 ea20587c 00000000 c08c0d74 00000800 c0039d60
> [   25.648808] 1f80: e9b8d880 e97d0000 e97d0000 e97d0000 c000e924 00010ef0 b6fe7d08 00000002
> [   25.656973] 1fa0: 00000000 c000e7a0 b6fe7d08 00000002 b6fe7d38 00000800 7a392d00 7a392d00
> [   25.665138] 1fc0: b6fe7d08 00000002 00000000 00000081 b6fe7d08 b6fe7008 be857858 00000800
> [   25.673303] 1fe0: b6f0f070 be8575d4 b6f8a2e9 b6f0f07c 80000010 b6fe7d38 00000000 00000000
> [   25.681479] [<c05ec6dc>] (_raw_spin_lock_irqsave) from [<c02dec90>] (devres_remove+0x20/0x80)
> [   25.689999] [<c02dec90>] (devres_remove) from [<c02df508>] (devres_destroy+0x8/0x24)
> [   25.697738] [<c02df508>] (devres_destroy) from [<c039de9c>] (input_unregister_device+0x28/0x70)
> [   25.706435] [<c039de9c>] (input_unregister_device) from [<bf0003a4>] (mxt_free_object_table+0x14/0x58 [atmel_mxt_ts])
> [   25.717037] [<bf0003a4>] (mxt_free_object_table [atmel_mxt_ts]) from [<bf000418>] (mxt_remove+0x30/0x44 [atmel_mxt_ts])
> [   25.727813] [<bf000418>] (mxt_remove [atmel_mxt_ts]) from [<c03bbe24>] (i2c_device_remove+0x44/0x5c)
> [   25.736940] [<c03bbe24>] (i2c_device_remove) from [<c02dc12c>] (__device_release_driver+0x70/0xc4)
> [   25.745891] [<c02dc12c>] (__device_release_driver) from [<c02dc910>] (driver_detach+0xac/0xb0)
> [   25.754494] [<c02dc910>] (driver_detach) from [<c02dbfbc>] (bus_remove_driver+0x4c/0x90)
> [   25.762579] [<c02dbfbc>] (bus_remove_driver) from [<c0081cdc>] (SyS_delete_module+0x108/0x194)
> [   25.771184] [<c0081cdc>] (SyS_delete_module) from [<c000e7a0>] (ret_fast_syscall+0x0/0x30)
> [   25.779438] Code: e2811001 e5831004 f592f000 e1923f9f (e2831801)
> [   25.785524] ---[ end trace fd2f70b3c6f48889 ]---
> [   25.790136] note: rmmod[831] exited with preempt_count 1
> Segmentation fault

^ permalink raw reply

* Re: [PATCH 00/15] atmel_mxt_ts - device tree, bootloader, etc
From: Nick Dyer @ 2014-07-29 16:26 UTC (permalink / raw)
  To: Stephen Warren, Yufeng Shen
  Cc: Dmitry Torokhov, benson Leung, Daniel Kurtz, Henrik Rydberg,
	Joonyoung Shim, Alan Bowens, linux-input,
	linux-kernel@vger.kernel.org, Peter Meerwald, Olof Johansson,
	Sekhar Nori
In-Reply-To: <53D6DFE7.3040901@wwwdotorg.org>

On 29/07/14 00:42, Stephen Warren wrote:
> Anyway, here's the diff between the two config files:
> 
>> # diff -u mxt-save-after-t9-83-write.xml 224sl.raw
>> --- mxt-save-after-t9-83-write.xml    2014-07-25 19:41:45.000000000 +0000
>> +++ 224sl.raw    2014-07-28 23:25:49.000000000 +0000
>> @@ -1,8 +1,7 @@
>>  OBP_RAW V1
>>  82 01 10 AA 12 0C 16
>>  F5AF33
>> -000000
>> -0025 0000 0082 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>> +E21E65
>>  0026 0000 0008 00 00 00 00 00 00 00 00
>>  0007 0000 0004 20 10 32 00
>>  0008 0000 000A 1E 00 28 28 00 00 00 00 00 00
> 
> It seems that the T25(?) entry is missing in the new/expected configuration
> file. I figured I'd try out the new/expected configuration file, so did:

0x25 = 37. T37 is the diagnostic debug object. A change was made fairly
recently to the tools to save this object into the config files, which is
the reason why it is missing in one of your files. But the difference is
essentially meaningless for your purposes, and writing those zeros to the
chip won't affect anything.

> # ./obp-utils/mxt-app -d i2c-dev:1-004b --load 224sl.raw
> # ./obp-utils/mxt-app -d i2c-dev:1-004b --save
> mxt-save-after-loading-224sl.raw.xml
> 
> At this point, mxt-save-after-loading-224sl.raw.xml contains identical
> content to mxt-save-after-t9-83-write.xml (my previous backup). It looks
> like the new configuration isn't being loaded correctly, or perhaps
> configuration loading doesn't delete entries that are simply not in the new
> configuration file?
> 
> I subsequently did the following in case --save is reading from the NVRAM
> rather than RAM:

The --save command reads from RAM. There's no way of reading from NVRAM.

^ permalink raw reply

* Re: [PATCH 00/15] atmel_mxt_ts - device tree, bootloader, etc
From: Nick Dyer @ 2014-07-29 16:43 UTC (permalink / raw)
  To: Yufeng Shen, Stephen Warren
  Cc: Dmitry Torokhov, benson Leung, Daniel Kurtz, Henrik Rydberg,
	Joonyoung Shim, Alan Bowens, linux-input,
	linux-kernel@vger.kernel.org, Peter Meerwald, Olof Johansson,
	Sekhar Nori
In-Reply-To: <CAPDwgkPL1utj3DFoFbOCcJsASpf+12nvKg1KcseV_MhEz66eiw@mail.gmail.com>

On 29/07/14 01:10, Yufeng Shen wrote:
> T37 (0x25) is DEBUG_DIAGNOSTIC object which the host can read debugging info
> from. It is not useful to have a initial config for it so usually CrOS
> system would just
> don't include configuration for this object.
> 
> Nick, I want to confirm with you that does T37 contribute to config
> checksum computation?

You are correct.

>> At this point, mxt-save-after-loading-224sl.raw.xml contains identical
>> content to mxt-save-after-t9-83-write.xml (my previous backup). It looks
>> like the new configuration isn't being loaded correctly, or perhaps
>> configuration loading doesn't delete entries that are simply not in the new
>> configuration file?
> 
> Yeah, I would guess since T37 is not in the config, so whatever in the NVRAM
> stays the same and when you --save its original value gets dumped.

Yes. You need to call --zero to get a truly blank config, before loading.

^ permalink raw reply

* Re: [PATCH 00/15] atmel_mxt_ts - device tree, bootloader, etc
From: Nick Dyer @ 2014-07-29 17:06 UTC (permalink / raw)
  To: Stephen Warren, Yufeng Shen
  Cc: Dmitry Torokhov, benson Leung, Daniel Kurtz, Henrik Rydberg,
	Joonyoung Shim, Alan Bowens, linux-input,
	linux-kernel@vger.kernel.org, Peter Meerwald, Olof Johansson,
	Sekhar Nori
In-Reply-To: <53D7C8CC.1050201@wwwdotorg.org>

On 29/07/14 17:16, Stephen Warren wrote:
> I then tried updating the firmware. This didn't work at all.
> 
> First I tried via mxt-app:
> 
>> root@localhost:~# ./obp-utils/mxt-app -d i2c-dev:1-004b --flash
>> 130.1_1.0.170.bin
>> Version:1.16-65-g0a4c
>> Opening firmware file 130.1_1.0.170.bin
>> Registered i2c-dev adapter:1 address:0x4b
>> Chip detected
>> Current firmware version: 1.0.AA
>> Skipping version check
>> Resetting in bootloader mode
>> Registered i2c-dev adapter:1 address:0x25
>> Error Remote I/O error (121) reading from i2c
>> Bootloader read failure
>> Bootloader not found
> 
> Then I power-cycled and tried via the atmel_mxt_ts modules' sysfs files:
> 
>> root@localhost:~# echo 1 >
>> /sys/devices/soc0/7000c400.i2c/i2c-1/1-004b/update_fw
>> [   38.495420] atmel_mxt_ts 1-004b: mxt_bootloader_read: i2c recv failed
>> (-121)
>> [   38.506208] atmel_mxt_ts 1-004b: mxt_bootloader_read: i2c recv failed
>> (-121)
>> [   38.513836] atmel_mxt_ts 1-004b: The firmware update failed(-121)
>> -bash: echo: write error: Remote I/O error

OK - that's the same error in both cases, it has tried to switch the device
into bootloader mode, however it is not responding on the bootloader I2C
address.

Couple of things to check:
- is the device in deep sleep mode when you run the mxt-app command? can
you try doing "mxt-app [device] -W -T7 FFFF" which will make sure it is
definitely not sleeping first.
- if you do "mxt-app [device] -i" straight after the --flash failure, does
it respond (which would mean it hasn't actioned the reset-into-bootloader
command)

> I also found that removing the module (even without attempting a FW update)
> yields:
> 
> After attempted FW update via sysfs:
> 
>> root@localhost:~# rmmod ./atmel_mxt_ts.ko
>> [   81.995672] Unable to handle kernel NULL pointer dereference at virtual address 00000364

Not good. It's trying to free an input device which isn't registered at
that point. In a later patch in my series I add a guard around that
(mxt_free_input_device):
https://github.com/ndyer/linux/commit/bb4800ff8c185

^ permalink raw reply

* Re: [PATCH 2/2] Input - wacom: Remove passing id for wacom_set_report
From: Benjamin Tissoires @ 2014-07-29 18:12 UTC (permalink / raw)
  To: Przemo Firszt
  Cc: Benjamin Tissoires, Dmitry Torokhov, Jiri Kosina, Ping Cheng,
	Jason Gerecke, linux-kernel@vger.kernel.org, linux-input
In-Reply-To: <1406375969-4453-2-git-send-email-przemo@firszt.eu>

On Sat, Jul 26, 2014 at 7:59 AM, Przemo Firszt <przemo@firszt.eu> wrote:
> Every call of wacom_set_report was passing "id" as a separate parameter
> and buffer also passed the same information. We can use first u8 of the
> buffer instead of "id"
>
> Signed-off-by: Przemo Firszt <przemo@firszt.eu>
> ---

At first, I was not going to take this one without any other
approvers, because we had such a discussion in the hid subsystem about
the same problem with respect to hid_hw_raw_request().
But after a second thought, we control much things here, and this
patch would have saved me 30 min this morning while I was wondering
why your 1/2 fix was not working.

I'll take this through my patch submission to Dmitry.

Cheers,
Benjamin

>  drivers/hid/wacom_sys.c | 27 +++++++++++++--------------
>  1 file changed, 13 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
> index 51437e2..db17198 100644
> --- a/drivers/hid/wacom_sys.c
> +++ b/drivers/hid/wacom_sys.c
> @@ -36,13 +36,13 @@ static int wacom_get_report(struct hid_device *hdev, u8 type, u8 id,
>         return retval;
>  }
>
> -static int wacom_set_report(struct hid_device *hdev, u8 type, u8 id,
> -                           void *buf, size_t size, unsigned int retries)
> +static int wacom_set_report(struct hid_device *hdev, u8 type, u8 *buf,
> +                           size_t size, unsigned int retries)
>  {
>         int retval;
>
>         do {
> -               retval = hid_hw_raw_request(hdev, id, buf, size, type,
> +               retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,
>                                 HID_REQ_SET_REPORT);
>         } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
>
> @@ -251,8 +251,8 @@ static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
>                 rep_data[0] = report_id;
>                 rep_data[1] = mode;
>
> -               error = wacom_set_report(hdev, HID_FEATURE_REPORT,
> -                                        report_id, rep_data, length, 1);
> +               error = wacom_set_report(hdev, HID_FEATURE_REPORT, rep_data,
> +                                        length, 1);
>                 if (error >= 0)
>                         error = wacom_get_report(hdev, HID_FEATURE_REPORT,
>                                                  report_id, rep_data, length, 1);
> @@ -525,8 +525,8 @@ static int wacom_led_control(struct wacom *wacom)
>                 buf[4] = wacom->led.img_lum;
>         }
>
> -       retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
> -                                 WAC_CMD_LED_CONTROL, buf, 9, WAC_CMD_RETRIES);
> +       retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 9,
> +                                 WAC_CMD_RETRIES);
>         kfree(buf);
>
>         return retval;
> @@ -546,8 +546,8 @@ static int wacom_led_putimage(struct wacom *wacom, int button_id,
>         /* Send 'start' command */
>         buf[0] = WAC_CMD_ICON_START;
>         buf[1] = 1;
> -       retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
> -                                 WAC_CMD_ICON_START, buf, 2, WAC_CMD_RETRIES);
> +       retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 2,
> +                                 WAC_CMD_RETRIES);
>         if (retval < 0)
>                 goto out;
>
> @@ -557,9 +557,8 @@ static int wacom_led_putimage(struct wacom *wacom, int button_id,
>                 buf[2] = i;
>                 memcpy(buf + 3, img + i * chunk_len, chunk_len);
>
> -               retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
> -                                         WAC_CMD_ICON_XFER,
> -                                         buf, chunk_len + 3, WAC_CMD_RETRIES);
> +               retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf,
> +                                         chunk_len + 3, WAC_CMD_RETRIES);
>                 if (retval < 0)
>                         break;
>         }
> @@ -567,8 +566,8 @@ static int wacom_led_putimage(struct wacom *wacom, int button_id,
>         /* Send 'stop' */
>         buf[0] = WAC_CMD_ICON_START;
>         buf[1] = 0;
> -       wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, WAC_CMD_ICON_START,
> -                        buf, 2, WAC_CMD_RETRIES);
> +       wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 2,
> +                        WAC_CMD_RETRIES);
>
>  out:
>         kfree(buf);
> --
> 1.9.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 1/2] Input - Wacom: Fix transfer header problem
From: Benjamin Tissoires @ 2014-07-29 18:14 UTC (permalink / raw)
  To: Przemo Firszt
  Cc: Benjamin Tissoires, Dmitry Torokhov, Jiri Kosina, Ping Cheng,
	Jason Gerecke, linux-kernel@vger.kernel.org, linux-input
In-Reply-To: <1406375969-4453-1-git-send-email-przemo@firszt.eu>

On Sat, Jul 26, 2014 at 7:59 AM, Przemo Firszt <przemo@firszt.eu> wrote:
> Header of transfer of image is different depending on connection type.
> That patch should be probably merged with 462c52a8cbcc62c
> Input - wacom: Check for bluetooth protocol while setting OLEDs
>
> Signed-off-by: Przemo Firszt <przemo@firszt.eu>
> ---

Thanks Przemo. I'll merge this one in the v3 of the wacom/BT/USB merge.
I'll let you add your s-o-b line once the patches will be on the list (soonish).

Cheers,
Benjamin

>  drivers/hid/wacom_sys.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
> index 42f139f..51437e2 100644
> --- a/drivers/hid/wacom_sys.c
> +++ b/drivers/hid/wacom_sys.c
> @@ -19,6 +19,7 @@
>
>  #define WAC_CMD_LED_CONTROL    0x20
>  #define WAC_CMD_ICON_START     0x21
> +#define WAC_CMD_ICON_BT_XFER   0x26
>  #define WAC_CMD_ICON_XFER      0x23
>  #define WAC_CMD_RETRIES                10
>
> @@ -550,7 +551,7 @@ static int wacom_led_putimage(struct wacom *wacom, int button_id,
>         if (retval < 0)
>                 goto out;
>
> -       buf[0] = WAC_CMD_ICON_XFER;
> +       buf[0] = len == 256 ? WAC_CMD_ICON_BT_XFER : WAC_CMD_ICON_XFER;
>         buf[1] = button_id & 0x07;
>         for (i = 0; i < 4; i++) {
>                 buf[2] = i;
> --
> 1.9.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply

* [PATCH v3 0/7] Bluetooth Wacom merge into the USB driver
From: Benjamin Tissoires @ 2014-07-29 18:42 UTC (permalink / raw)
  To: Dmitry Torokhov, Jiri Kosina, Ping Cheng, Jason Gerecke,
	Przemo Firszt
  Cc: linux-kernel, linux-input

Hi guys,

so this patch series is intended to be applied on top of Dmitry's wacom branch.

This time, I had a closer look at the various moves, and checkpatch does not
complained on anything besides the "open brace { [which] should be on the
previous line".

In v3, the difference is that now, no functionality has been dropped thanks to
Przemo. The LEDs are working through the same API in BT and USB, and the OLEDs
has been unified. They use the same sysfs node, and the same scrambled API.
The only difference we have now is that BT expecta 1-bit images, while USB
4-bits images.

Przemo, I'll let you add your s-o-b line in 4/7.

Ping, I did not added your previous Acked-by (the patch set has been split since).
Feel free to comment on this part too :)

Cheers,
Benjamin

Benjamin Tissoires (6):
  Input - wacom: prepare the driver to include BT devices
  Input - wacom: handle Graphire BT tablets in wacom.ko
  Input - wacom: handle Intuos 4 BT in wacom.ko
  Input - wacom: Check for bluetooth protocol while setting OLEDs
  Input - wacom: add copyright note and bump version to 2.0
  HID: remove hid-wacom Bluetooth driver

Przemo Firszt (1):
  Input - wacom: Remove passing id for wacom_set_report

 drivers/hid/Kconfig     |  10 +-
 drivers/hid/Makefile    |   3 +-
 drivers/hid/hid-core.c  |   2 -
 drivers/hid/hid-wacom.c | 973 ------------------------------------------------
 drivers/hid/wacom.h     |   3 +
 drivers/hid/wacom_sys.c | 169 +++++++--
 drivers/hid/wacom_wac.c | 168 ++++++++-
 drivers/hid/wacom_wac.h |   5 +
 8 files changed, 323 insertions(+), 1010 deletions(-)
 delete mode 100644 drivers/hid/hid-wacom.c

-- 
2.0.3

^ permalink raw reply

* [PATCH v3 2/7] Input - wacom: handle Graphire BT tablets in wacom.ko
From: Benjamin Tissoires @ 2014-07-29 18:43 UTC (permalink / raw)
  To: Dmitry Torokhov, Jiri Kosina, Ping Cheng, Jason Gerecke,
	Przemo Firszt
  Cc: linux-kernel, linux-input
In-Reply-To: <1406659385-3256-1-git-send-email-benjamin.tissoires@redhat.com>

First, merge the Graphire BT tablet.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/hid-core.c  |  1 -
 drivers/hid/hid-wacom.c |  1 -
 drivers/hid/wacom_sys.c | 33 +++++++++++++++++
 drivers/hid/wacom_wac.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++--
 drivers/hid/wacom_wac.h |  2 ++
 5 files changed, 128 insertions(+), 4 deletions(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 0e4d290..d824a45 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1943,7 +1943,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
index 4874f4e..967c457 100644
--- a/drivers/hid/hid-wacom.c
+++ b/drivers/hid/hid-wacom.c
@@ -952,7 +952,6 @@ static void wacom_remove(struct hid_device *hdev)
 }
 
 static const struct hid_device_id wacom_devices[] = {
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
 
 	{ }
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 18154a5..c21e58b 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -265,6 +265,39 @@ static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
 static int wacom_bt_query_tablet_data(struct hid_device *hdev, u8 speed,
 		struct wacom_features *features)
 {
+	struct wacom *wacom = hid_get_drvdata(hdev);
+	int ret;
+	u8 rep_data[2];
+
+	switch (features->type) {
+	case GRAPHIRE_BT:
+		rep_data[0] = 0x03;
+		rep_data[1] = 0x00;
+		ret = wacom_set_report(hdev, HID_FEATURE_REPORT,
+					rep_data[0], rep_data, 2, 3);
+
+		if (ret >= 0) {
+			rep_data[0] = speed == 0 ? 0x05 : 0x06;
+			rep_data[1] = 0x00;
+
+			ret = wacom_set_report(hdev, HID_FEATURE_REPORT,
+						rep_data[0], rep_data, 2, 3);
+
+			if (ret >= 0) {
+				wacom->wacom_wac.bt_high_speed = speed;
+				return 0;
+			}
+		}
+
+		/*
+		 * Note that if the raw queries fail, it's not a hard failure
+		 * and it is safe to continue
+		 */
+		hid_warn(hdev, "failed to poke device, command %d, err %d\n",
+			 rep_data[0], ret);
+		break;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index cfd2ae1..ac891dc 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -30,6 +30,10 @@
  */
 #define WACOM_CONTACT_AREA_SCALE 2607
 
+/*percent of battery capacity for Graphire
+  8th value means AC online and show 100% capacity */
+static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 };
+
 static int wacom_penpartner_irq(struct wacom_wac *wacom)
 {
 	unsigned char *data = wacom->data;
@@ -263,11 +267,19 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
 	unsigned char *data = wacom->data;
 	struct input_dev *input = wacom->input;
 	struct input_dev *pad_input = wacom->pad_input;
+	int battery_capacity, ps_connected;
 	int prox;
 	int rw = 0;
 	int retval = 0;
 
-	if (data[0] != WACOM_REPORT_PENABLED) {
+	if (features->type == GRAPHIRE_BT) {
+		if (data[0] != WACOM_REPORT_PENABLED_BT) {
+			dev_dbg(input->dev.parent,
+				"%s: received unknown report #%d\n", __func__,
+				data[0]);
+			goto exit;
+		}
+	} else if (data[0] != WACOM_REPORT_PENABLED) {
 		dev_dbg(input->dev.parent,
 			"%s: received unknown report #%d\n", __func__, data[0]);
 		goto exit;
@@ -301,7 +313,12 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
 		input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
 		input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
 		if (wacom->tool[0] != BTN_TOOL_MOUSE) {
-			input_report_abs(input, ABS_PRESSURE, data[6] | ((data[7] & 0x03) << 8));
+			if (features->type == GRAPHIRE_BT)
+				input_report_abs(input, ABS_PRESSURE, data[6] |
+					(((__u16) (data[1] & 0x08)) << 5));
+			else
+				input_report_abs(input, ABS_PRESSURE, data[6] |
+					((data[7] & 0x03) << 8));
 			input_report_key(input, BTN_TOUCH, data[1] & 0x01);
 			input_report_key(input, BTN_STYLUS, data[1] & 0x02);
 			input_report_key(input, BTN_STYLUS2, data[1] & 0x04);
@@ -312,6 +329,20 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
 					features->type == WACOM_MO) {
 				input_report_abs(input, ABS_DISTANCE, data[6] & 0x3f);
 				rw = (data[7] & 0x04) - (data[7] & 0x03);
+			} else if (features->type == GRAPHIRE_BT) {
+				/* Compute distance between mouse and tablet */
+				rw = 44 - (data[6] >> 2);
+				rw = clamp_val(rw, 0, 31);
+				input_report_abs(input, ABS_DISTANCE, rw);
+				if (((data[1] >> 5) & 3) == 2) {
+					/* Mouse with wheel */
+					input_report_key(input, BTN_MIDDLE,
+							data[1] & 0x04);
+					rw = (data[6] & 0x01) ? -1 :
+						(data[6] & 0x02) ? 1 : 0;
+				} else {
+					rw = 0;
+				}
 			} else {
 				input_report_abs(input, ABS_DISTANCE, data[7] & 0x3f);
 				rw = -(signed char)data[6];
@@ -358,6 +389,31 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
 			retval = 1;
 		}
 		break;
+	case GRAPHIRE_BT:
+		prox = data[7] & 0x03;
+		if (prox || wacom->id[1]) {
+			wacom->id[1] = PAD_DEVICE_ID;
+			input_report_key(pad_input, BTN_0, (data[7] & 0x02));
+			input_report_key(pad_input, BTN_1, (data[7] & 0x01));
+			if (!prox)
+				wacom->id[1] = 0;
+			input_report_abs(pad_input, ABS_MISC, wacom->id[1]);
+			retval = 1;
+		}
+		break;
+	}
+
+	/* Store current battery capacity and power supply state */
+	if (features->type == GRAPHIRE_BT) {
+		rw = (data[7] >> 2 & 0x07);
+		battery_capacity = batcap_gr[rw];
+		ps_connected = rw == 7;
+		if ((wacom->battery_capacity != battery_capacity) ||
+		    (wacom->ps_connected != ps_connected)) {
+			wacom->battery_capacity = battery_capacity;
+			wacom->ps_connected = ps_connected;
+			wacom_notify_battery(wacom);
+		}
 	}
 exit:
 	return retval;
@@ -1418,6 +1474,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
 
 	case WACOM_G4:
 	case GRAPHIRE:
+	case GRAPHIRE_BT:
 	case WACOM_MO:
 		sync = wacom_graphire_irq(wacom_wac);
 		break;
@@ -1654,6 +1711,27 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
 		__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
 		break;
 
+	case GRAPHIRE_BT:
+		__clear_bit(ABS_MISC, input_dev->absbit);
+		input_set_abs_params(input_dev, ABS_DISTANCE, 0,
+					      features->distance_max,
+					      0, 0);
+
+		input_set_capability(input_dev, EV_REL, REL_WHEEL);
+
+		__set_bit(BTN_LEFT, input_dev->keybit);
+		__set_bit(BTN_RIGHT, input_dev->keybit);
+		__set_bit(BTN_MIDDLE, input_dev->keybit);
+
+		__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+		__set_bit(BTN_TOOL_PEN, input_dev->keybit);
+		__set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
+		__set_bit(BTN_STYLUS, input_dev->keybit);
+		__set_bit(BTN_STYLUS2, input_dev->keybit);
+
+		__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+		break;
+
 	case WACOM_24HD:
 		input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
 		input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0);
@@ -1862,6 +1940,11 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
 	input_set_abs_params(input_dev, ABS_Y, 0, 1, 0, 0);
 
 	switch (features->type) {
+	case GRAPHIRE_BT:
+		__set_bit(BTN_0, input_dev->keybit);
+		__set_bit(BTN_1, input_dev->keybit);
+		break;
+
 	case WACOM_MO:
 		__set_bit(BTN_BACK, input_dev->keybit);
 		__set_bit(BTN_LEFT, input_dev->keybit);
@@ -2031,6 +2114,9 @@ static const struct wacom_features wacom_features_0x00 =
 static const struct wacom_features wacom_features_0x10 =
 	{ "Wacom Graphire", 10206, 7422, 511, 63,
 	  GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+static const struct wacom_features wacom_features_0x81 =
+	{ "Wacom Graphire BT", 16704, 12064, 511, 32,
+	  GRAPHIRE_BT, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
 static const struct wacom_features wacom_features_0x11 =
 	{ "Wacom Graphire2 4x5", 10206, 7422, 511, 63,
 	  GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
@@ -2426,6 +2512,10 @@ static const struct wacom_features wacom_features_0x309 =
 	HID_DEVICE(BUS_USB, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
 	.driver_data = (kernel_ulong_t)&wacom_features_##prod
 
+#define BT_DEVICE_WACOM(prod)						\
+	HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
+	.driver_data = (kernel_ulong_t)&wacom_features_##prod
+
 #define USB_DEVICE_LENOVO(prod)					\
 	HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, prod),			\
 	.driver_data = (kernel_ulong_t)&wacom_features_##prod
@@ -2483,6 +2573,7 @@ const struct hid_device_id wacom_ids[] = {
 	{ USB_DEVICE_WACOM(0x69) },
 	{ USB_DEVICE_WACOM(0x6A) },
 	{ USB_DEVICE_WACOM(0x6B) },
+	{ BT_DEVICE_WACOM(0x81) },
 	{ USB_DEVICE_WACOM(0x84) },
 	{ USB_DEVICE_WACOM(0x90) },
 	{ USB_DEVICE_WACOM(0x93) },
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index 3aa55d9..0f3df34 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -46,6 +46,7 @@
 
 /* wacom data packet report IDs */
 #define WACOM_REPORT_PENABLED		2
+#define WACOM_REPORT_PENABLED_BT	3
 #define WACOM_REPORT_INTUOSREAD		5
 #define WACOM_REPORT_INTUOSWRITE	6
 #define WACOM_REPORT_INTUOSPAD		12
@@ -73,6 +74,7 @@
 enum {
 	PENPARTNER = 0,
 	GRAPHIRE,
+	GRAPHIRE_BT,
 	WACOM_G4,
 	PTU,
 	PL,
-- 
2.0.3

^ permalink raw reply related

* [PATCH v3 3/7] Input - wacom: handle Intuos 4 BT in wacom.ko
From: Benjamin Tissoires @ 2014-07-29 18:43 UTC (permalink / raw)
  To: Dmitry Torokhov, Jiri Kosina, Ping Cheng, Jason Gerecke,
	Przemo Firszt
  Cc: linux-kernel, linux-input
In-Reply-To: <1406659385-3256-1-git-send-email-benjamin.tissoires@redhat.com>

A good point of this change is that now, the Intuos4 bluetooth can handle
the different tools (artpen, airbrush, mice), and we get a common interface
between USB and BT for accessing the LEDs/OLEDs.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/hid-core.c  |  1 -
 drivers/hid/hid-wacom.c |  1 -
 drivers/hid/wacom_sys.c | 16 +++++++++++
 drivers/hid/wacom_wac.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/hid/wacom_wac.h |  1 +
 5 files changed, 90 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index d824a45..774e5b6 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1943,7 +1943,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_Q_PAD) },
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
index 967c457..db2d07d 100644
--- a/drivers/hid/hid-wacom.c
+++ b/drivers/hid/hid-wacom.c
@@ -952,7 +952,6 @@ static void wacom_remove(struct hid_device *hdev)
 }
 
 static const struct hid_device_id wacom_devices[] = {
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
 
 	{ }
 };
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index c21e58b..f5c9c56 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -296,6 +296,20 @@ static int wacom_bt_query_tablet_data(struct hid_device *hdev, u8 speed,
 		hid_warn(hdev, "failed to poke device, command %d, err %d\n",
 			 rep_data[0], ret);
 		break;
+	case INTUOS4WL:
+		if (speed == 1)
+			wacom->wacom_wac.bt_features &= ~0x20;
+		else
+			wacom->wacom_wac.bt_features |= 0x20;
+
+		rep_data[0] = 0x03;
+		rep_data[1] = wacom->wacom_wac.bt_features;
+
+		ret = wacom_set_report(hdev, HID_FEATURE_REPORT,
+					rep_data[0], rep_data, 2, 1);
+		if (ret >= 0)
+			wacom->wacom_wac.bt_high_speed = speed;
+		break;
 	}
 
 	return 0;
@@ -720,6 +734,7 @@ static int wacom_initialize_leds(struct wacom *wacom)
 	switch (wacom->wacom_wac.features.type) {
 	case INTUOS4S:
 	case INTUOS4:
+	case INTUOS4WL:
 	case INTUOS4L:
 		wacom->led.select[0] = 0;
 		wacom->led.select[1] = 0;
@@ -786,6 +801,7 @@ static void wacom_destroy_leds(struct wacom *wacom)
 	switch (wacom->wacom_wac.features.type) {
 	case INTUOS4S:
 	case INTUOS4:
+	case INTUOS4WL:
 	case INTUOS4L:
 		sysfs_remove_group(&wacom->hdev->dev.kobj,
 				   &intuos4_led_attr_group);
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index ac891dc..4670db8 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -34,6 +34,9 @@
   8th value means AC online and show 100% capacity */
 static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 };
 
+/*percent of battery capacity for Intuos4 WL, AC has a separate bit*/
+static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 };
+
 static int wacom_penpartner_irq(struct wacom_wac *wacom)
 {
 	unsigned char *data = wacom->data;
@@ -950,6 +953,58 @@ static int int_dist(int x1, int y1, int x2, int y2)
 	return int_sqrt(x*x + y*y);
 }
 
+static void wacom_intuos_bt_process_data(struct wacom_wac *wacom,
+		unsigned char *data)
+{
+	memcpy(wacom->data, data, 10);
+	wacom_intuos_irq(wacom);
+
+	input_sync(wacom->input);
+	if (wacom->pad_input)
+		input_sync(wacom->pad_input);
+}
+
+static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
+{
+	unsigned char data[WACOM_PKGLEN_MAX];
+	int i = 1;
+	unsigned power_raw, battery_capacity, bat_charging, ps_connected;
+
+	memcpy(data, wacom->data, len);
+
+	switch (data[0]) {
+	case 0x04:
+		wacom_intuos_bt_process_data(wacom, data + i);
+		i += 10;
+		/* fall through */
+	case 0x03:
+		wacom_intuos_bt_process_data(wacom, data + i);
+		i += 10;
+		wacom_intuos_bt_process_data(wacom, data + i);
+		i += 10;
+		power_raw = data[i];
+		bat_charging = (power_raw & 0x08) ? 1 : 0;
+		ps_connected = (power_raw & 0x10) ? 1 : 0;
+		battery_capacity = batcap_i4[power_raw & 0x07];
+		if ((wacom->battery_capacity != battery_capacity) ||
+		    (wacom->bat_charging != bat_charging) ||
+		    (wacom->ps_connected != ps_connected)) {
+			wacom->battery_capacity = battery_capacity;
+			wacom->bat_charging = bat_charging;
+			wacom->ps_connected = ps_connected;
+			wacom_notify_battery(wacom);
+		}
+
+		break;
+	default:
+		dev_dbg(wacom->input->dev.parent,
+				"Unknown report: %d,%d size:%li\n",
+				data[0], data[1], len);
+		return 0;
+	}
+	return 0;
+}
+
 static int wacom_24hdt_irq(struct wacom_wac *wacom)
 {
 	struct input_dev *input = wacom->input;
@@ -1509,6 +1564,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
 		sync = wacom_intuos_irq(wacom_wac);
 		break;
 
+	case INTUOS4WL:
+		sync = wacom_intuos_bt_irq(wacom_wac, len);
+		break;
+
 	case WACOM_24HDT:
 		sync = wacom_24hdt_irq(wacom_wac);
 		break;
@@ -1800,6 +1859,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
 		break;
 
 	case INTUOS4:
+	case INTUOS4WL:
 	case INTUOS4L:
 	case INTUOS4S:
 		input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
@@ -2062,6 +2122,15 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
 		input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
 		break;
 
+	case INTUOS4WL:
+		/*
+		 * For Bluetooth devices, the udev rule does not work correctly
+		 * for pads unless we add a stylus capability, which forces
+		 * ID_INPUT_TABLET to be set.
+		 */
+		__set_bit(BTN_STYLUS, input_dev->keybit);
+		/* fall through */
+
 	case INTUOS4:
 	case INTUOS4L:
 		__set_bit(BTN_7, input_dev->keybit);
@@ -2276,6 +2345,9 @@ static const struct wacom_features wacom_features_0xBB =
 static const struct wacom_features wacom_features_0xBC =
 	{ "Wacom Intuos4 WL", 40640, 25400, 2047, 63,
 	  INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xBD =
+	{ "Wacom Intuos4 WL", 40640, 25400, 2047, 63,
+	  INTUOS4WL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
 static const struct wacom_features wacom_features_0x26 =
 	{ "Wacom Intuos5 touch S", 31496, 19685, 2047, 63,
 	  INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 };
@@ -2592,6 +2664,7 @@ const struct hid_device_id wacom_ids[] = {
 	{ USB_DEVICE_WACOM(0xBA) },
 	{ USB_DEVICE_WACOM(0xBB) },
 	{ USB_DEVICE_WACOM(0xBC) },
+	{ BT_DEVICE_WACOM(0xBD) },
 	{ USB_DEVICE_WACOM(0xC0) },
 	{ USB_DEVICE_WACOM(0xC2) },
 	{ USB_DEVICE_WACOM(0xC4) },
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index 0f3df34..5039357 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -86,6 +86,7 @@ enum {
 	INTUOS3L,
 	INTUOS4S,
 	INTUOS4,
+	INTUOS4WL,
 	INTUOS4L,
 	INTUOS5S,
 	INTUOS5,
-- 
2.0.3

^ permalink raw reply related

* [PATCH v3 4/7] Input - wacom: Check for bluetooth protocol while setting OLEDs
From: Benjamin Tissoires @ 2014-07-29 18:43 UTC (permalink / raw)
  To: Dmitry Torokhov, Jiri Kosina, Ping Cheng, Jason Gerecke,
	Przemo Firszt
  Cc: linux-kernel, linux-input
In-Reply-To: <1406659385-3256-1-git-send-email-benjamin.tissoires@redhat.com>

Bluetooth Intuos 4 use 1-bit definition while the USB ones use a 4-bits
definition. This changes the size of the raw image we receive, and thus
the kernel will only accept 1-bit images for Bluetooth and 4-bits for
USB.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/wacom_sys.c | 29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index f5c9c56..a12cd9c 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -20,6 +20,7 @@
 #define WAC_CMD_LED_CONTROL	0x20
 #define WAC_CMD_ICON_START	0x21
 #define WAC_CMD_ICON_XFER	0x23
+#define WAC_CMD_ICON_BT_XFER	0x26
 #define WAC_CMD_RETRIES		10
 
 static int wacom_get_report(struct hid_device *hdev, u8 type, u8 id,
@@ -526,12 +527,14 @@ static int wacom_led_control(struct wacom *wacom)
 	return retval;
 }
 
-static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img)
+static int wacom_led_putimage(struct wacom *wacom, int button_id, u8 xfer_id,
+		const unsigned len, const void *img)
 {
 	unsigned char *buf;
 	int i, retval;
+	const unsigned chunk_len = len / 4; /* 4 chunks are needed to be sent */
 
-	buf = kzalloc(259, GFP_KERNEL);
+	buf = kzalloc(chunk_len + 3 , GFP_KERNEL);
 	if (!buf)
 		return -ENOMEM;
 
@@ -543,15 +546,15 @@ static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *im
 	if (retval < 0)
 		goto out;
 
-	buf[0] = WAC_CMD_ICON_XFER;
+	buf[0] = xfer_id;
 	buf[1] = button_id & 0x07;
 	for (i = 0; i < 4; i++) {
 		buf[2] = i;
-		memcpy(buf + 3, img + i * 256, 256);
+		memcpy(buf + 3, img + i * chunk_len, chunk_len);
 
 		retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
-					  WAC_CMD_ICON_XFER,
-					  buf, 259, WAC_CMD_RETRIES);
+					  xfer_id, buf, chunk_len + 3,
+					  WAC_CMD_RETRIES);
 		if (retval < 0)
 			break;
 	}
@@ -652,13 +655,23 @@ static ssize_t wacom_button_image_store(struct device *dev, int button_id,
 	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
 	struct wacom *wacom = hid_get_drvdata(hdev);
 	int err;
+	unsigned len;
+	u8 xfer_id;
 
-	if (count != 1024)
+	if (hdev->bus == BUS_BLUETOOTH) {
+		len = 256;
+		xfer_id = WAC_CMD_ICON_BT_XFER;
+	} else {
+		len = 1024;
+		xfer_id = WAC_CMD_ICON_XFER;
+	}
+
+	if (count != len)
 		return -EINVAL;
 
 	mutex_lock(&wacom->lock);
 
-	err = wacom_led_putimage(wacom, button_id, buf);
+	err = wacom_led_putimage(wacom, button_id, xfer_id, len, buf);
 
 	mutex_unlock(&wacom->lock);
 
-- 
2.0.3

^ permalink raw reply related

* [PATCH v3 5/7] Input - wacom: Remove passing id for wacom_set_report
From: Benjamin Tissoires @ 2014-07-29 18:43 UTC (permalink / raw)
  To: Dmitry Torokhov, Jiri Kosina, Ping Cheng, Jason Gerecke,
	Przemo Firszt
  Cc: linux-kernel, linux-input
In-Reply-To: <1406659385-3256-1-git-send-email-benjamin.tissoires@redhat.com>

From: Przemo Firszt <przemo@firszt.eu>

Every call of wacom_set_report was passing "id" as a separate parameter
and buffer also passed the same information. We can use first u8 of the
buffer instead of "id"

Signed-off-by: Przemo Firszt <przemo@firszt.eu>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/wacom_sys.c | 35 +++++++++++++++++------------------
 1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index a12cd9c..6e0c191 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -36,13 +36,13 @@ static int wacom_get_report(struct hid_device *hdev, u8 type, u8 id,
 	return retval;
 }
 
-static int wacom_set_report(struct hid_device *hdev, u8 type, u8 id,
-			    void *buf, size_t size, unsigned int retries)
+static int wacom_set_report(struct hid_device *hdev, u8 type, u8 *buf,
+			    size_t size, unsigned int retries)
 {
 	int retval;
 
 	do {
-		retval = hid_hw_raw_request(hdev, id, buf, size, type,
+		retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,
 				HID_REQ_SET_REPORT);
 	} while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
 
@@ -251,8 +251,8 @@ static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
 		rep_data[0] = report_id;
 		rep_data[1] = mode;
 
-		error = wacom_set_report(hdev, HID_FEATURE_REPORT,
-		                         report_id, rep_data, length, 1);
+		error = wacom_set_report(hdev, HID_FEATURE_REPORT, rep_data,
+					 length, 1);
 		if (error >= 0)
 			error = wacom_get_report(hdev, HID_FEATURE_REPORT,
 			                         report_id, rep_data, length, 1);
@@ -274,15 +274,15 @@ static int wacom_bt_query_tablet_data(struct hid_device *hdev, u8 speed,
 	case GRAPHIRE_BT:
 		rep_data[0] = 0x03;
 		rep_data[1] = 0x00;
-		ret = wacom_set_report(hdev, HID_FEATURE_REPORT,
-					rep_data[0], rep_data, 2, 3);
+		ret = wacom_set_report(hdev, HID_FEATURE_REPORT, rep_data, 2,
+					3);
 
 		if (ret >= 0) {
 			rep_data[0] = speed == 0 ? 0x05 : 0x06;
 			rep_data[1] = 0x00;
 
 			ret = wacom_set_report(hdev, HID_FEATURE_REPORT,
-						rep_data[0], rep_data, 2, 3);
+						rep_data, 2, 3);
 
 			if (ret >= 0) {
 				wacom->wacom_wac.bt_high_speed = speed;
@@ -306,8 +306,8 @@ static int wacom_bt_query_tablet_data(struct hid_device *hdev, u8 speed,
 		rep_data[0] = 0x03;
 		rep_data[1] = wacom->wacom_wac.bt_features;
 
-		ret = wacom_set_report(hdev, HID_FEATURE_REPORT,
-					rep_data[0], rep_data, 2, 1);
+		ret = wacom_set_report(hdev, HID_FEATURE_REPORT, rep_data, 2,
+					1);
 		if (ret >= 0)
 			wacom->wacom_wac.bt_high_speed = speed;
 		break;
@@ -520,8 +520,8 @@ static int wacom_led_control(struct wacom *wacom)
 		buf[4] = wacom->led.img_lum;
 	}
 
-	retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
-				  WAC_CMD_LED_CONTROL, buf, 9, WAC_CMD_RETRIES);
+	retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 9,
+				  WAC_CMD_RETRIES);
 	kfree(buf);
 
 	return retval;
@@ -541,8 +541,8 @@ static int wacom_led_putimage(struct wacom *wacom, int button_id, u8 xfer_id,
 	/* Send 'start' command */
 	buf[0] = WAC_CMD_ICON_START;
 	buf[1] = 1;
-	retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
-				  WAC_CMD_ICON_START, buf, 2, WAC_CMD_RETRIES);
+	retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 2,
+				  WAC_CMD_RETRIES);
 	if (retval < 0)
 		goto out;
 
@@ -553,8 +553,7 @@ static int wacom_led_putimage(struct wacom *wacom, int button_id, u8 xfer_id,
 		memcpy(buf + 3, img + i * chunk_len, chunk_len);
 
 		retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
-					  xfer_id, buf, chunk_len + 3,
-					  WAC_CMD_RETRIES);
+					  buf, chunk_len + 3, WAC_CMD_RETRIES);
 		if (retval < 0)
 			break;
 	}
@@ -562,8 +561,8 @@ static int wacom_led_putimage(struct wacom *wacom, int button_id, u8 xfer_id,
 	/* Send 'stop' */
 	buf[0] = WAC_CMD_ICON_START;
 	buf[1] = 0;
-	wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, WAC_CMD_ICON_START,
-			 buf, 2, WAC_CMD_RETRIES);
+	wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 2,
+			 WAC_CMD_RETRIES);
 
 out:
 	kfree(buf);
-- 
2.0.3

^ permalink raw reply related

* [PATCH v3 6/7] Input - wacom: add copyright note and bump version to 2.0
From: Benjamin Tissoires @ 2014-07-29 18:43 UTC (permalink / raw)
  To: Dmitry Torokhov, Jiri Kosina, Ping Cheng, Jason Gerecke,
	Przemo Firszt
  Cc: linux-kernel, linux-input
In-Reply-To: <1406659385-3256-1-git-send-email-benjamin.tissoires@redhat.com>

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/wacom.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h
index 0b89ee7..03692d7 100644
--- a/drivers/hid/wacom.h
+++ b/drivers/hid/wacom.h
@@ -12,6 +12,7 @@
  *  Copyright (c) 2001 Frederic Lepied		<flepied@mandrakesoft.com>
  *  Copyright (c) 2004 Panagiotis Issaris	<panagiotis.issaris@mech.kuleuven.ac.be>
  *  Copyright (c) 2002-2011 Ping Cheng		<pingc@wacom.com>
+ *  Copyright (c) 2014 Benjamin Tissoires	<benjamin.tissoires@redhat.com>
  *
  *  ChangeLog:
  *      v0.1 (vp)  - Initial release
@@ -72,6 +73,8 @@
  *      v1.52 (pc) - Query Wacom data upon system resume
  *                 - add defines for features->type
  *                 - add new devices (0x9F, 0xE2, and 0XE3)
+ *      v2.00 (bt) - conversion to a HID driver
+ *                 - integration of the Bluetooth devices
  */
 
 /*
-- 
2.0.3

^ permalink raw reply related

* [PATCH v3 1/7] Input - wacom: prepare the driver to include BT devices
From: Benjamin Tissoires @ 2014-07-29 18:42 UTC (permalink / raw)
  To: Dmitry Torokhov, Jiri Kosina, Ping Cheng, Jason Gerecke,
	Przemo Firszt
  Cc: linux-kernel, linux-input
In-Reply-To: <1406659385-3256-1-git-send-email-benjamin.tissoires@redhat.com>

Now that wacom is a hid driver, there is no point in having a separate
driver for bluetooth devices.
This patch prepares the common paths of Bluetooth devices in the
common wacom driver.
It also adds the sysfs file "speed" used by Bluetooth devices.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/wacom_sys.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++---
 drivers/hid/wacom_wac.h |  2 ++
 2 files changed, 69 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 37888c3..18154a5 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -262,6 +262,12 @@ static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
 	return error < 0 ? error : 0;
 }
 
+static int wacom_bt_query_tablet_data(struct hid_device *hdev, u8 speed,
+		struct wacom_features *features)
+{
+	return 0;
+}
+
 /*
  * Switch the tablet into its most-capable mode. Wacom tablets are
  * typically configured to power-up in a mode which sends mouse-like
@@ -272,6 +278,9 @@ static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
 static int wacom_query_tablet_data(struct hid_device *hdev,
 		struct wacom_features *features)
 {
+	if (hdev->bus == BUS_BLUETOOTH)
+		return wacom_bt_query_tablet_data(hdev, 1, features);
+
 	if (features->device_type == BTN_TOOL_FINGER) {
 		if (features->type > TABLETPC) {
 			/* MT Tablet PC touch */
@@ -890,6 +899,38 @@ static void wacom_destroy_battery(struct wacom *wacom)
 	}
 }
 
+static ssize_t wacom_show_speed(struct device *dev,
+				struct device_attribute
+				*attr, char *buf)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct wacom *wacom = hid_get_drvdata(hdev);
+
+	return snprintf(buf, PAGE_SIZE, "%i\n", wacom->wacom_wac.bt_high_speed);
+}
+
+static ssize_t wacom_store_speed(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct wacom *wacom = hid_get_drvdata(hdev);
+	u8 new_speed;
+
+	if (kstrtou8(buf, 0, &new_speed))
+		return -EINVAL;
+
+	if (new_speed != 0 && new_speed != 1)
+		return -EINVAL;
+
+	wacom_bt_query_tablet_data(hdev, new_speed, &wacom->wacom_wac.features);
+
+	return count;
+}
+
+static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
+		wacom_show_speed, wacom_store_speed);
+
 static struct input_dev *wacom_allocate_input(struct wacom *wacom)
 {
 	struct input_dev *input_dev;
@@ -1210,6 +1251,9 @@ static int wacom_probe(struct hid_device *hdev,
 		features->y_max = 4096;
 	}
 
+	if (hdev->bus == BUS_BLUETOOTH)
+		features->quirks |= WACOM_QUIRK_BATTERY;
+
 	wacom_setup_device_quirks(features);
 
 	/* set unit to "100th of a mm" for devices not reported by HID */
@@ -1241,10 +1285,25 @@ static int wacom_probe(struct hid_device *hdev,
 	if (error)
 		goto fail2;
 
+	if (!(features->quirks & WACOM_QUIRK_MONITOR) &&
+	     (features->quirks & WACOM_QUIRK_BATTERY)) {
+		error = wacom_initialize_battery(wacom);
+		if (error)
+			goto fail3;
+	}
+
 	if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
 		error = wacom_register_inputs(wacom);
 		if (error)
-			goto fail3;
+			goto fail4;
+	}
+
+	if (hdev->bus == BUS_BLUETOOTH) {
+		error = device_create_file(&hdev->dev, &dev_attr_speed);
+		if (error)
+			hid_warn(hdev,
+				 "can't create sysfs speed attribute err: %d\n",
+				 error);
 	}
 
 	/* Note that if query fails it is not a hard failure */
@@ -1254,7 +1313,7 @@ static int wacom_probe(struct hid_device *hdev,
 	error = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
 	if (error) {
 		hid_err(hdev, "hw start failed\n");
-		goto fail4;
+		goto fail5;
 	}
 
 	if (features->quirks & WACOM_QUIRK_MONITOR)
@@ -1267,7 +1326,10 @@ static int wacom_probe(struct hid_device *hdev,
 
 	return 0;
 
- fail4:	wacom_unregister_inputs(wacom);
+ fail5:	if (hdev->bus == BUS_BLUETOOTH)
+		device_remove_file(&hdev->dev, &dev_attr_speed);
+	wacom_unregister_inputs(wacom);
+ fail4:	wacom_destroy_battery(wacom);
  fail3:	wacom_destroy_leds(wacom);
  fail2:	wacom_remove_shared_data(wacom_wac);
  fail1:	kfree(wacom);
@@ -1283,6 +1345,8 @@ static void wacom_remove(struct hid_device *hdev)
 
 	cancel_work_sync(&wacom->work);
 	wacom_unregister_inputs(wacom);
+	if (hdev->bus == BUS_BLUETOOTH)
+		device_remove_file(&hdev->dev, &dev_attr_speed);
 	wacom_destroy_battery(wacom);
 	wacom_destroy_leds(wacom);
 	wacom_remove_shared_data(&wacom->wacom_wac);
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index 3433a0e..3aa55d9 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -169,6 +169,8 @@ struct wacom_wac {
 	int num_contacts_left;
 	int bat_charging;
 	int ps_connected;
+	__u8 bt_features;
+	__u8 bt_high_speed;
 };
 
 #endif
-- 
2.0.3


^ permalink raw reply related

* [PATCH v3 7/7] HID: remove hid-wacom Bluetooth driver
From: Benjamin Tissoires @ 2014-07-29 18:43 UTC (permalink / raw)
  To: Dmitry Torokhov, Jiri Kosina, Ping Cheng, Jason Gerecke,
	Przemo Firszt
  Cc: linux-kernel, linux-input
In-Reply-To: <1406659385-3256-1-git-send-email-benjamin.tissoires@redhat.com>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 29379 bytes --]

Bluetooth Wacom tablets are now handled by the regular wacom.ko driver.
Remove the now useless hid-wacom driver.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/Kconfig     |  10 +-
 drivers/hid/Makefile    |   3 +-
 drivers/hid/hid-wacom.c | 971 ------------------------------------------------
 3 files changed, 2 insertions(+), 982 deletions(-)
 delete mode 100644 drivers/hid/hid-wacom.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index b18bae6..354122b 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -762,21 +762,13 @@ config THRUSTMASTER_FF
 	  Rumble Force or Force Feedback Wheel.
 
 config HID_WACOM
-	tristate "Wacom Bluetooth devices support"
-	depends on HID
-	depends on LEDS_CLASS
-	select POWER_SUPPLY
-	---help---
-	Support for Wacom Graphire Bluetooth and Intuos4 WL tablets.
-
-config HID_USB_WACOM
 	tristate "Wacom Intuos/Graphire tablet support (USB)"
 	depends on HID
 	select POWER_SUPPLY
 	select NEW_LEDS
 	select LEDS_CLASS
 	help
-	  Say Y here if you want to use the USB version of the Wacom Intuos
+	  Say Y here if you want to use the USB or BT version of the Wacom Intuos
 	  or Graphire tablet.
 
 	  To compile this driver as a module, choose M here: the
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 19ba7c3..2d7e149 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -116,10 +116,9 @@ obj-$(CONFIG_HID_UCLOGIC)	+= hid-uclogic.o
 obj-$(CONFIG_HID_XINMO)		+= hid-xinmo.o
 obj-$(CONFIG_HID_ZEROPLUS)	+= hid-zpff.o
 obj-$(CONFIG_HID_ZYDACRON)	+= hid-zydacron.o
-obj-$(CONFIG_HID_WACOM)		+= hid-wacom.o
 
 wacom-objs			:= wacom_wac.o wacom_sys.o
-obj-$(CONFIG_HID_USB_WACOM)	+= wacom.o
+obj-$(CONFIG_HID_WACOM)		+= wacom.o
 obj-$(CONFIG_HID_WALTOP)	+= hid-waltop.o
 obj-$(CONFIG_HID_WIIMOTE)	+= hid-wiimote.o
 obj-$(CONFIG_HID_SENSOR_HUB)	+= hid-sensor-hub.o
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
deleted file mode 100644
index db2d07d..0000000
--- a/drivers/hid/hid-wacom.c
+++ /dev/null
@@ -1,971 +0,0 @@
-/*
- *  Bluetooth Wacom Tablet support
- *
- *  Copyright (c) 1999 Andreas Gal
- *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
- *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
- *  Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
- *  Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
- *  Copyright (c) 2011 Przemysław Firszt <przemo@firszt.eu>
- */
-
-/*
- * 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.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/device.h>
-#include <linux/hid.h>
-#include <linux/module.h>
-#include <linux/leds.h>
-#include <linux/slab.h>
-#include <linux/power_supply.h>
-
-#include "hid-ids.h"
-
-#define PAD_DEVICE_ID	0x0F
-
-#define WAC_CMD_LED_CONTROL     0x20
-#define WAC_CMD_ICON_START_STOP     0x21
-#define WAC_CMD_ICON_TRANSFER       0x26
-
-struct wacom_data {
-	__u16 tool;
-	__u16 butstate;
-	__u8 whlstate;
-	__u8 features;
-	__u32 id;
-	__u32 serial;
-	unsigned char high_speed;
-	__u8 battery_capacity;
-	__u8 power_raw;
-	__u8 ps_connected;
-	__u8 bat_charging;
-	struct power_supply battery;
-	struct power_supply ac;
-	__u8 led_selector;
-	struct led_classdev *leds[4];
-};
-
-/*percent of battery capacity for Graphire
-  8th value means AC online and show 100% capacity */
-static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 };
-/*percent of battery capacity for Intuos4 WL, AC has a separate bit*/
-static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 };
-
-static enum power_supply_property wacom_battery_props[] = {
-	POWER_SUPPLY_PROP_PRESENT,
-	POWER_SUPPLY_PROP_CAPACITY,
-	POWER_SUPPLY_PROP_SCOPE,
-	POWER_SUPPLY_PROP_STATUS,
-};
-
-static enum power_supply_property wacom_ac_props[] = {
-	POWER_SUPPLY_PROP_PRESENT,
-	POWER_SUPPLY_PROP_ONLINE,
-	POWER_SUPPLY_PROP_SCOPE,
-};
-
-static void wacom_scramble(__u8 *image)
-{
-	__u16 mask;
-	__u16 s1;
-	__u16 s2;
-	__u16 r1 ;
-	__u16 r2 ;
-	__u16 r;
-	__u8 buf[256];
-	int i, w, x, y, z;
-
-	for (x = 0; x < 32; x++) {
-		for (y = 0; y < 8; y++)
-			buf[(8 * x) + (7 - y)] = image[(8 * x) + y];
-	}
-
-	/* Change 76543210 into GECA6420 as required by Intuos4 WL
-	 *        HGFEDCBA      HFDB7531
-	 */
-	for (x = 0; x < 4; x++) {
-		for (y = 0; y < 4; y++) {
-			for (z = 0; z < 8; z++) {
-				mask = 0x0001;
-				r1 = 0;
-				r2 = 0;
-				i = (x << 6) + (y << 4) + z;
-				s1 = buf[i];
-				s2 = buf[i+8];
-				for (w = 0; w < 8; w++) {
-					r1 |= (s1 & mask);
-					r2 |= (s2 & mask);
-					s1 <<= 1;
-					s2 <<= 1;
-					mask <<= 2;
-				}
-				r = r1 | (r2 << 1);
-				i = (x << 6) + (y << 4) + (z << 1);
-				image[i] = 0xFF & r;
-				image[i+1] = (0xFF00 & r) >> 8;
-			}
-		}
-	}
-}
-
-static void wacom_set_image(struct hid_device *hdev, const char *image,
-						__u8 icon_no)
-{
-	__u8 rep_data[68];
-	__u8 p[256];
-	int ret, i, j;
-
-	for (i = 0; i < 256; i++)
-		p[i] = image[i];
-
-	rep_data[0] = WAC_CMD_ICON_START_STOP;
-	rep_data[1] = 0;
-	ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
-				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
-	if (ret < 0)
-		goto err;
-
-	rep_data[0] = WAC_CMD_ICON_TRANSFER;
-	rep_data[1] = icon_no & 0x07;
-
-	wacom_scramble(p);
-
-	for (i = 0; i < 4; i++) {
-		for (j = 0; j < 64; j++)
-			rep_data[j + 3] = p[(i << 6) + j];
-
-		rep_data[2] = i;
-		ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 67,
-					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
-	}
-
-	rep_data[0] = WAC_CMD_ICON_START_STOP;
-	rep_data[1] = 0;
-
-	ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
-				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
-
-err:
-	return;
-}
-
-static void wacom_leds_set_brightness(struct led_classdev *led_dev,
-						enum led_brightness value)
-{
-	struct device *dev = led_dev->dev->parent;
-	struct hid_device *hdev;
-	struct wacom_data *wdata;
-	unsigned char *buf;
-	__u8 led = 0;
-	int i;
-
-	hdev = container_of(dev, struct hid_device, dev);
-	wdata = hid_get_drvdata(hdev);
-	for (i = 0; i < 4; ++i) {
-		if (wdata->leds[i] == led_dev)
-			wdata->led_selector = i;
-	}
-
-	led = wdata->led_selector | 0x04;
-	buf = kzalloc(9, GFP_KERNEL);
-	if (buf) {
-		buf[0] = WAC_CMD_LED_CONTROL;
-		buf[1] = led;
-		buf[2] = value >> 2;
-		buf[3] = value;
-		/* use fixed brightness for OLEDs */
-		buf[4] = 0x08;
-		hid_hw_raw_request(hdev, buf[0], buf, 9, HID_FEATURE_REPORT,
-				   HID_REQ_SET_REPORT);
-		kfree(buf);
-	}
-
-	return;
-}
-
-static enum led_brightness wacom_leds_get_brightness(struct led_classdev *led_dev)
-{
-	struct wacom_data *wdata;
-	struct device *dev = led_dev->dev->parent;
-	int value = 0;
-	int i;
-
-	wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
-
-	for (i = 0; i < 4; ++i) {
-		if (wdata->leds[i] == led_dev) {
-			value = wdata->leds[i]->brightness;
-			break;
-		}
-	}
-
-	return value;
-}
-
-
-static int wacom_initialize_leds(struct hid_device *hdev)
-{
-	struct wacom_data *wdata = hid_get_drvdata(hdev);
-	struct led_classdev *led;
-	struct device *dev = &hdev->dev;
-	size_t namesz = strlen(dev_name(dev)) + 12;
-	char *name;
-	int i, ret;
-
-	wdata->led_selector = 0;
-
-	for (i = 0; i < 4; i++) {
-		led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL);
-		if (!led) {
-			hid_warn(hdev,
-				 "can't allocate memory for LED selector\n");
-			ret = -ENOMEM;
-			goto err;
-		}
-
-		name = (void *)&led[1];
-		snprintf(name, namesz, "%s:selector:%d", dev_name(dev), i);
-		led->name = name;
-		led->brightness = 0;
-		led->max_brightness = 127;
-		led->brightness_get = wacom_leds_get_brightness;
-		led->brightness_set = wacom_leds_set_brightness;
-
-		wdata->leds[i] = led;
-
-		ret = led_classdev_register(dev, wdata->leds[i]);
-
-		if (ret) {
-			wdata->leds[i] = NULL;
-			kfree(led);
-			hid_warn(hdev, "can't register LED\n");
-			goto err;
-		}
-	}
-
-err:
-	return ret;
-}
-
-static void wacom_destroy_leds(struct hid_device *hdev)
-{
-	struct wacom_data *wdata = hid_get_drvdata(hdev);
-	struct led_classdev *led;
-	int i;
-
-	for (i = 0; i < 4; ++i) {
-		if (wdata->leds[i]) {
-			led = wdata->leds[i];
-			wdata->leds[i] = NULL;
-			led_classdev_unregister(led);
-			kfree(led);
-		}
-	}
-
-}
-
-static int wacom_battery_get_property(struct power_supply *psy,
-				enum power_supply_property psp,
-				union power_supply_propval *val)
-{
-	struct wacom_data *wdata = container_of(psy,
-					struct wacom_data, battery);
-	int ret = 0;
-
-	switch (psp) {
-	case POWER_SUPPLY_PROP_PRESENT:
-		val->intval = 1;
-		break;
-	case POWER_SUPPLY_PROP_SCOPE:
-		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
-		break;
-	case POWER_SUPPLY_PROP_CAPACITY:
-		val->intval = wdata->battery_capacity;
-		break;
-	case POWER_SUPPLY_PROP_STATUS:
-		if (wdata->bat_charging)
-			val->intval = POWER_SUPPLY_STATUS_CHARGING;
-		else
-			if (wdata->battery_capacity == 100 && wdata->ps_connected)
-				val->intval = POWER_SUPPLY_STATUS_FULL;
-			else
-				val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-		break;
-	default:
-		ret = -EINVAL;
-		break;
-	}
-	return ret;
-}
-
-static int wacom_ac_get_property(struct power_supply *psy,
-				enum power_supply_property psp,
-				union power_supply_propval *val)
-{
-	struct wacom_data *wdata = container_of(psy, struct wacom_data, ac);
-	int ret = 0;
-
-	switch (psp) {
-	case POWER_SUPPLY_PROP_PRESENT:
-		/* fall through */
-	case POWER_SUPPLY_PROP_ONLINE:
-		val->intval = wdata->ps_connected;
-		break;
-	case POWER_SUPPLY_PROP_SCOPE:
-		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
-		break;
-	default:
-		ret = -EINVAL;
-		break;
-	}
-	return ret;
-}
-
-static void wacom_set_features(struct hid_device *hdev, u8 speed)
-{
-	struct wacom_data *wdata = hid_get_drvdata(hdev);
-	int limit, ret;
-	__u8 rep_data[2];
-
-	switch (hdev->product) {
-	case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
-		rep_data[0] = 0x03 ; rep_data[1] = 0x00;
-		limit = 3;
-		do {
-			ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
-					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
-		} while (ret < 0 && limit-- > 0);
-
-		if (ret >= 0) {
-			if (speed == 0)
-				rep_data[0] = 0x05;
-			else
-				rep_data[0] = 0x06;
-
-			rep_data[1] = 0x00;
-			limit = 3;
-			do {
-				ret = hid_hw_raw_request(hdev, rep_data[0],
-					rep_data, 2, HID_FEATURE_REPORT,
-					HID_REQ_SET_REPORT);
-			} while (ret < 0 && limit-- > 0);
-
-			if (ret >= 0) {
-				wdata->high_speed = speed;
-				return;
-			}
-		}
-
-		/*
-		 * Note that if the raw queries fail, it's not a hard failure
-		 * and it is safe to continue
-		 */
-		hid_warn(hdev, "failed to poke device, command %d, err %d\n",
-			 rep_data[0], ret);
-		break;
-	case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
-		if (speed == 1)
-			wdata->features &= ~0x20;
-		else
-			wdata->features |= 0x20;
-
-		rep_data[0] = 0x03;
-		rep_data[1] = wdata->features;
-
-		ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
-				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
-		if (ret >= 0)
-			wdata->high_speed = speed;
-		break;
-	}
-
-	return;
-}
-
-static ssize_t wacom_show_speed(struct device *dev,
-				struct device_attribute
-				*attr, char *buf)
-{
-	struct wacom_data *wdata = dev_get_drvdata(dev);
-
-	return snprintf(buf, PAGE_SIZE, "%i\n", wdata->high_speed);
-}
-
-static ssize_t wacom_store_speed(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t count)
-{
-	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-	int new_speed;
-
-	if (sscanf(buf, "%1d", &new_speed ) != 1)
-		return -EINVAL;
-
-	if (new_speed == 0 || new_speed == 1) {
-		wacom_set_features(hdev, new_speed);
-		return strnlen(buf, PAGE_SIZE);
-	} else
-		return -EINVAL;
-}
-
-static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
-		wacom_show_speed, wacom_store_speed);
-
-#define WACOM_STORE(OLED_ID)						\
-static ssize_t wacom_oled##OLED_ID##_store(struct device *dev,		\
-				struct device_attribute *attr,		\
-				const char *buf, size_t count)		\
-{									\
-	struct hid_device *hdev = container_of(dev, struct hid_device,	\
-				dev);					\
-									\
-	if (count != 256)						\
-		return -EINVAL;						\
-									\
-	wacom_set_image(hdev, buf, OLED_ID);				\
-									\
-	return count;							\
-}									\
-									\
-static DEVICE_ATTR(oled##OLED_ID##_img, S_IWUSR | S_IWGRP, NULL,	\
-				wacom_oled##OLED_ID##_store)
-
-WACOM_STORE(0);
-WACOM_STORE(1);
-WACOM_STORE(2);
-WACOM_STORE(3);
-WACOM_STORE(4);
-WACOM_STORE(5);
-WACOM_STORE(6);
-WACOM_STORE(7);
-
-static int wacom_gr_parse_report(struct hid_device *hdev,
-			struct wacom_data *wdata,
-			struct input_dev *input, unsigned char *data)
-{
-	int tool, x, y, rw;
-
-	tool = 0;
-	/* Get X & Y positions */
-	x = le16_to_cpu(*(__le16 *) &data[2]);
-	y = le16_to_cpu(*(__le16 *) &data[4]);
-
-	/* Get current tool identifier */
-	if (data[1] & 0x90) { /* If pen is in the in/active area */
-		switch ((data[1] >> 5) & 3) {
-		case 0:	/* Pen */
-			tool = BTN_TOOL_PEN;
-			break;
-
-		case 1: /* Rubber */
-			tool = BTN_TOOL_RUBBER;
-			break;
-
-		case 2: /* Mouse with wheel */
-		case 3: /* Mouse without wheel */
-			tool = BTN_TOOL_MOUSE;
-			break;
-		}
-
-		/* Reset tool if out of active tablet area */
-		if (!(data[1] & 0x10))
-			tool = 0;
-	}
-
-	/* If tool changed, notify input subsystem */
-	if (wdata->tool != tool) {
-		if (wdata->tool) {
-			/* Completely reset old tool state */
-			if (wdata->tool == BTN_TOOL_MOUSE) {
-				input_report_key(input, BTN_LEFT, 0);
-				input_report_key(input, BTN_RIGHT, 0);
-				input_report_key(input, BTN_MIDDLE, 0);
-				input_report_abs(input, ABS_DISTANCE,
-					input_abs_get_max(input, ABS_DISTANCE));
-			} else {
-				input_report_key(input, BTN_TOUCH, 0);
-				input_report_key(input, BTN_STYLUS, 0);
-				input_report_key(input, BTN_STYLUS2, 0);
-				input_report_abs(input, ABS_PRESSURE, 0);
-			}
-			input_report_key(input, wdata->tool, 0);
-			input_sync(input);
-		}
-		wdata->tool = tool;
-		if (tool)
-			input_report_key(input, tool, 1);
-	}
-
-	if (tool) {
-		input_report_abs(input, ABS_X, x);
-		input_report_abs(input, ABS_Y, y);
-
-		switch ((data[1] >> 5) & 3) {
-		case 2: /* Mouse with wheel */
-			input_report_key(input, BTN_MIDDLE, data[1] & 0x04);
-			rw = (data[6] & 0x01) ? -1 :
-				(data[6] & 0x02) ? 1 : 0;
-			input_report_rel(input, REL_WHEEL, rw);
-			/* fall through */
-
-		case 3: /* Mouse without wheel */
-			input_report_key(input, BTN_LEFT, data[1] & 0x01);
-			input_report_key(input, BTN_RIGHT, data[1] & 0x02);
-			/* Compute distance between mouse and tablet */
-			rw = 44 - (data[6] >> 2);
-			if (rw < 0)
-				rw = 0;
-			else if (rw > 31)
-				rw = 31;
-			input_report_abs(input, ABS_DISTANCE, rw);
-			break;
-
-		default:
-			input_report_abs(input, ABS_PRESSURE,
-					data[6] | (((__u16) (data[1] & 0x08)) << 5));
-			input_report_key(input, BTN_TOUCH, data[1] & 0x01);
-			input_report_key(input, BTN_STYLUS, data[1] & 0x02);
-			input_report_key(input, BTN_STYLUS2, (tool == BTN_TOOL_PEN) && data[1] & 0x04);
-			break;
-		}
-
-		input_sync(input);
-	}
-
-	/* Report the state of the two buttons at the top of the tablet
-	 * as two extra fingerpad keys (buttons 4 & 5). */
-	rw = data[7] & 0x03;
-	if (rw != wdata->butstate) {
-		wdata->butstate = rw;
-		input_report_key(input, BTN_0, rw & 0x02);
-		input_report_key(input, BTN_1, rw & 0x01);
-		input_report_key(input, BTN_TOOL_FINGER, 0xf0);
-		input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
-		input_sync(input);
-	}
-
-	/* Store current battery capacity and power supply state*/
-	rw = (data[7] >> 2 & 0x07);
-	if (rw != wdata->power_raw) {
-		wdata->power_raw = rw;
-		wdata->battery_capacity = batcap_gr[rw];
-		if (rw == 7)
-			wdata->ps_connected = 1;
-		else
-			wdata->ps_connected = 0;
-	}
-	return 1;
-}
-
-static void wacom_i4_parse_button_report(struct wacom_data *wdata,
-			struct input_dev *input, unsigned char *data)
-{
-	__u16 new_butstate;
-	__u8 new_whlstate;
-	__u8 sync = 0;
-
-	new_whlstate = data[1];
-	if (new_whlstate != wdata->whlstate) {
-		wdata->whlstate = new_whlstate;
-		if (new_whlstate & 0x80) {
-			input_report_key(input, BTN_TOUCH, 1);
-			input_report_abs(input, ABS_WHEEL, (new_whlstate & 0x7f));
-			input_report_key(input, BTN_TOOL_FINGER, 1);
-		} else {
-			input_report_key(input, BTN_TOUCH, 0);
-			input_report_abs(input, ABS_WHEEL, 0);
-			input_report_key(input, BTN_TOOL_FINGER, 0);
-		}
-		sync = 1;
-	}
-
-	new_butstate = (data[3] << 1) | (data[2] & 0x01);
-	if (new_butstate != wdata->butstate) {
-		wdata->butstate = new_butstate;
-		input_report_key(input, BTN_0, new_butstate & 0x001);
-		input_report_key(input, BTN_1, new_butstate & 0x002);
-		input_report_key(input, BTN_2, new_butstate & 0x004);
-		input_report_key(input, BTN_3, new_butstate & 0x008);
-		input_report_key(input, BTN_4, new_butstate & 0x010);
-		input_report_key(input, BTN_5, new_butstate & 0x020);
-		input_report_key(input, BTN_6, new_butstate & 0x040);
-		input_report_key(input, BTN_7, new_butstate & 0x080);
-		input_report_key(input, BTN_8, new_butstate & 0x100);
-		input_report_key(input, BTN_TOOL_FINGER, 1);
-		sync = 1;
-	}
-
-	if (sync) {
-		input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
-		input_event(input, EV_MSC, MSC_SERIAL, 0xffffffff);
-		input_sync(input);
-	}
-}
-
-static void wacom_i4_parse_pen_report(struct wacom_data *wdata,
-			struct input_dev *input, unsigned char *data)
-{
-	__u16 x, y, pressure;
-	__u8 distance;
-	__u8 tilt_x, tilt_y;
-
-	switch (data[1]) {
-	case 0x80: /* Out of proximity report */
-		input_report_key(input, BTN_TOUCH, 0);
-		input_report_abs(input, ABS_PRESSURE, 0);
-		input_report_key(input, BTN_STYLUS, 0);
-		input_report_key(input, BTN_STYLUS2, 0);
-		input_report_key(input, wdata->tool, 0);
-		input_report_abs(input, ABS_MISC, 0);
-		input_event(input, EV_MSC, MSC_SERIAL, wdata->serial);
-		wdata->tool = 0;
-		input_sync(input);
-		break;
-	case 0xC2: /* Tool report */
-		wdata->id = ((data[2] << 4) | (data[3] >> 4) |
-			((data[7] & 0x0f) << 20) |
-			((data[8] & 0xf0) << 12));
-		wdata->serial = ((data[3] & 0x0f) << 28) +
-				(data[4] << 20) + (data[5] << 12) +
-				(data[6] << 4) + (data[7] >> 4);
-
-		switch (wdata->id) {
-		case 0x100802:
-			wdata->tool = BTN_TOOL_PEN;
-			break;
-		case 0x10080A:
-			wdata->tool = BTN_TOOL_RUBBER;
-			break;
-		}
-		break;
-	default: /* Position/pressure report */
-		x = data[2] << 9 | data[3] << 1 | ((data[9] & 0x02) >> 1);
-		y = data[4] << 9 | data[5] << 1 | (data[9] & 0x01);
-		pressure = (data[6] << 3) | ((data[7] & 0xC0) >> 5)
-			| (data[1] & 0x01);
-		distance = (data[9] >> 2) & 0x3f;
-		tilt_x = ((data[7] << 1) & 0x7e) | (data[8] >> 7);
-		tilt_y = data[8] & 0x7f;
-
-		input_report_key(input, BTN_TOUCH, pressure > 1);
-
-		input_report_key(input, BTN_STYLUS, data[1] & 0x02);
-		input_report_key(input, BTN_STYLUS2, data[1] & 0x04);
-		input_report_key(input, wdata->tool, 1);
-		input_report_abs(input, ABS_X, x);
-		input_report_abs(input, ABS_Y, y);
-		input_report_abs(input, ABS_PRESSURE, pressure);
-		input_report_abs(input, ABS_DISTANCE, distance);
-		input_report_abs(input, ABS_TILT_X, tilt_x);
-		input_report_abs(input, ABS_TILT_Y, tilt_y);
-		input_report_abs(input, ABS_MISC, wdata->id);
-		input_event(input, EV_MSC, MSC_SERIAL, wdata->serial);
-		input_report_key(input, wdata->tool, 1);
-		input_sync(input);
-		break;
-	}
-
-	return;
-}
-
-static void wacom_i4_parse_report(struct hid_device *hdev,
-			struct wacom_data *wdata,
-			struct input_dev *input, unsigned char *data)
-{
-	switch (data[0]) {
-	case 0x00: /* Empty report */
-		break;
-	case 0x02: /* Pen report */
-		wacom_i4_parse_pen_report(wdata, input, data);
-		break;
-	case 0x03: /* Features Report */
-		wdata->features = data[2];
-		break;
-	case 0x0C: /* Button report */
-		wacom_i4_parse_button_report(wdata, input, data);
-		break;
-	default:
-		hid_err(hdev, "Unknown report: %d,%d\n", data[0], data[1]);
-		break;
-	}
-}
-
-static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
-		u8 *raw_data, int size)
-{
-	struct wacom_data *wdata = hid_get_drvdata(hdev);
-	struct hid_input *hidinput;
-	struct input_dev *input;
-	unsigned char *data = (unsigned char *) raw_data;
-	int i;
-	__u8 power_raw;
-
-	if (!(hdev->claimed & HID_CLAIMED_INPUT))
-		return 0;
-
-	hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
-	input = hidinput->input;
-
-	switch (hdev->product) {
-	case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
-		if (data[0] == 0x03) {
-			return wacom_gr_parse_report(hdev, wdata, input, data);
-		} else {
-			hid_err(hdev, "Unknown report: %d,%d size:%d\n",
-					data[0], data[1], size);
-			return 0;
-		}
-		break;
-	case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
-		i = 1;
-
-		switch (data[0]) {
-		case 0x04:
-			wacom_i4_parse_report(hdev, wdata, input, data + i);
-			i += 10;
-			/* fall through */
-		case 0x03:
-			wacom_i4_parse_report(hdev, wdata, input, data + i);
-			i += 10;
-			wacom_i4_parse_report(hdev, wdata, input, data + i);
-			power_raw = data[i+10];
-			if (power_raw != wdata->power_raw) {
-				wdata->power_raw = power_raw;
-				wdata->battery_capacity = batcap_i4[power_raw & 0x07];
-				wdata->bat_charging = (power_raw & 0x08) ? 1 : 0;
-				wdata->ps_connected = (power_raw & 0x10) ? 1 : 0;
-			}
-
-			break;
-		default:
-			hid_err(hdev, "Unknown report: %d,%d size:%d\n",
-					data[0], data[1], size);
-			return 0;
-		}
-	}
-	return 1;
-}
-
-static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi,
-	struct hid_field *field, struct hid_usage *usage, unsigned long **bit,
-								int *max)
-{
-	struct input_dev *input = hi->input;
-
-	__set_bit(INPUT_PROP_POINTER, input->propbit);
-
-	/* Basics */
-	input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
-
-	__set_bit(REL_WHEEL, input->relbit);
-
-	__set_bit(BTN_TOOL_PEN, input->keybit);
-	__set_bit(BTN_TOUCH, input->keybit);
-	__set_bit(BTN_STYLUS, input->keybit);
-	__set_bit(BTN_STYLUS2, input->keybit);
-	__set_bit(BTN_LEFT, input->keybit);
-	__set_bit(BTN_RIGHT, input->keybit);
-	__set_bit(BTN_MIDDLE, input->keybit);
-
-	/* Pad */
-	input_set_capability(input, EV_MSC, MSC_SERIAL);
-
-	__set_bit(BTN_0, input->keybit);
-	__set_bit(BTN_1, input->keybit);
-	__set_bit(BTN_TOOL_FINGER, input->keybit);
-
-	/* Distance, rubber and mouse */
-	__set_bit(BTN_TOOL_RUBBER, input->keybit);
-	__set_bit(BTN_TOOL_MOUSE, input->keybit);
-
-	switch (hdev->product) {
-	case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
-		input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
-		input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
-		input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
-		input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
-		break;
-	case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
-		__set_bit(ABS_WHEEL, input->absbit);
-		__set_bit(ABS_MISC, input->absbit);
-		__set_bit(BTN_2, input->keybit);
-		__set_bit(BTN_3, input->keybit);
-		__set_bit(BTN_4, input->keybit);
-		__set_bit(BTN_5, input->keybit);
-		__set_bit(BTN_6, input->keybit);
-		__set_bit(BTN_7, input->keybit);
-		__set_bit(BTN_8, input->keybit);
-		input_set_abs_params(input, ABS_WHEEL, 0, 71, 0, 0);
-		input_set_abs_params(input, ABS_X, 0, 40640, 4, 0);
-		input_set_abs_params(input, ABS_Y, 0, 25400, 4, 0);
-		input_set_abs_params(input, ABS_PRESSURE, 0, 2047, 0, 0);
-		input_set_abs_params(input, ABS_DISTANCE, 0, 63, 0, 0);
-		input_set_abs_params(input, ABS_TILT_X, 0, 127, 0, 0);
-		input_set_abs_params(input, ABS_TILT_Y, 0, 127, 0, 0);
-		break;
-	}
-
-	return 0;
-}
-
-static int wacom_probe(struct hid_device *hdev,
-		const struct hid_device_id *id)
-{
-	struct wacom_data *wdata;
-	int ret;
-
-	wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
-	if (wdata == NULL) {
-		hid_err(hdev, "can't alloc wacom descriptor\n");
-		return -ENOMEM;
-	}
-
-	hid_set_drvdata(hdev, wdata);
-
-	/* Parse the HID report now */
-	ret = hid_parse(hdev);
-	if (ret) {
-		hid_err(hdev, "parse failed\n");
-		goto err_free;
-	}
-
-	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
-	if (ret) {
-		hid_err(hdev, "hw start failed\n");
-		goto err_free;
-	}
-
-	ret = device_create_file(&hdev->dev, &dev_attr_speed);
-	if (ret)
-		hid_warn(hdev,
-			 "can't create sysfs speed attribute err: %d\n", ret);
-
-#define OLED_INIT(OLED_ID)						\
-	do {								\
-		ret = device_create_file(&hdev->dev,			\
-				&dev_attr_oled##OLED_ID##_img);		\
-		if (ret)						\
-			hid_warn(hdev,					\
-			 "can't create sysfs oled attribute, err: %d\n", ret);\
-	} while (0)
-
-OLED_INIT(0);
-OLED_INIT(1);
-OLED_INIT(2);
-OLED_INIT(3);
-OLED_INIT(4);
-OLED_INIT(5);
-OLED_INIT(6);
-OLED_INIT(7);
-
-	wdata->features = 0;
-	wacom_set_features(hdev, 1);
-
-	if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) {
-		sprintf(hdev->name, "%s", "Wacom Intuos4 WL");
-		ret = wacom_initialize_leds(hdev);
-		if (ret)
-			hid_warn(hdev,
-				 "can't create led attribute, err: %d\n", ret);
-	}
-
-	wdata->battery.properties = wacom_battery_props;
-	wdata->battery.num_properties = ARRAY_SIZE(wacom_battery_props);
-	wdata->battery.get_property = wacom_battery_get_property;
-	wdata->battery.name = "wacom_battery";
-	wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
-	wdata->battery.use_for_apm = 0;
-
-
-	ret = power_supply_register(&hdev->dev, &wdata->battery);
-	if (ret) {
-		hid_err(hdev, "can't create sysfs battery attribute, err: %d\n",
-			ret);
-		goto err_battery;
-	}
-
-	power_supply_powers(&wdata->battery, &hdev->dev);
-
-	wdata->ac.properties = wacom_ac_props;
-	wdata->ac.num_properties = ARRAY_SIZE(wacom_ac_props);
-	wdata->ac.get_property = wacom_ac_get_property;
-	wdata->ac.name = "wacom_ac";
-	wdata->ac.type = POWER_SUPPLY_TYPE_MAINS;
-	wdata->ac.use_for_apm = 0;
-
-	ret = power_supply_register(&hdev->dev, &wdata->ac);
-	if (ret) {
-		hid_err(hdev,
-			"can't create ac battery attribute, err: %d\n", ret);
-		goto err_ac;
-	}
-
-	power_supply_powers(&wdata->ac, &hdev->dev);
-	return 0;
-
-err_ac:
-	power_supply_unregister(&wdata->battery);
-err_battery:
-	wacom_destroy_leds(hdev);
-	device_remove_file(&hdev->dev, &dev_attr_oled0_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled1_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled2_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled3_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled4_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled5_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled6_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled7_img);
-	device_remove_file(&hdev->dev, &dev_attr_speed);
-	hid_hw_stop(hdev);
-err_free:
-	kfree(wdata);
-	return ret;
-}
-
-static void wacom_remove(struct hid_device *hdev)
-{
-	struct wacom_data *wdata = hid_get_drvdata(hdev);
-
-	wacom_destroy_leds(hdev);
-	device_remove_file(&hdev->dev, &dev_attr_oled0_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled1_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled2_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled3_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled4_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled5_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled6_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled7_img);
-	device_remove_file(&hdev->dev, &dev_attr_speed);
-	hid_hw_stop(hdev);
-
-	power_supply_unregister(&wdata->battery);
-	power_supply_unregister(&wdata->ac);
-	kfree(hid_get_drvdata(hdev));
-}
-
-static const struct hid_device_id wacom_devices[] = {
-
-	{ }
-};
-MODULE_DEVICE_TABLE(hid, wacom_devices);
-
-static struct hid_driver wacom_driver = {
-	.name = "hid-wacom",
-	.id_table = wacom_devices,
-	.probe = wacom_probe,
-	.remove = wacom_remove,
-	.raw_event = wacom_raw_event,
-	.input_mapped = wacom_input_mapped,
-};
-module_hid_driver(wacom_driver);
-
-MODULE_DESCRIPTION("Driver for Wacom Graphire Bluetooth and Wacom Intuos4 WL");
-MODULE_LICENSE("GPL");
-- 
2.0.3

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

^ permalink raw reply related

* Re: [PATCH 00/12] HID: Convert UHID to new HID transport-layer
From: Jiri Kosina @ 2014-07-29 19:00 UTC (permalink / raw)
  To: David Herrmann; +Cc: linux-input, Benjamin Tissoires
In-Reply-To: <1406646866-999-1-git-send-email-dh.herrmann@gmail.com>

On Tue, 29 Jul 2014, David Herrmann wrote:

> Hi
> 
> When Benjamin cleanup up the transport layer, he left UHID mostly unconverted
> due to ABI issues. I promised to take a look, so here're the patches.
> 
> Most of the patches are fairly straightforward. Comments welcome. I tested an
> old uhid-sample.c build and it still works, so I think I didn't break
> backwards-compatibility.

I will definitely take a look, but I can't promise I will be able to 
finish a review in time for 3.17.

If you find this a severe issue, please let me know.

Thanks,

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply

* Re: [PATCH 00/12] HID: Convert UHID to new HID transport-layer
From: David Herrmann @ 2014-07-29 19:03 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: open list:HID CORE LAYER, Benjamin Tissoires
In-Reply-To: <alpine.LNX.2.00.1407292100150.16390@pobox.suse.cz>

Hi

On Tue, Jul 29, 2014 at 9:00 PM, Jiri Kosina <jkosina@suse.cz> wrote:
> On Tue, 29 Jul 2014, David Herrmann wrote:
>
>> Hi
>>
>> When Benjamin cleanup up the transport layer, he left UHID mostly unconverted
>> due to ABI issues. I promised to take a look, so here're the patches.
>>
>> Most of the patches are fairly straightforward. Comments welcome. I tested an
>> old uhid-sample.c build and it still works, so I think I didn't break
>> backwards-compatibility.
>
> I will definitely take a look, but I can't promise I will be able to
> finish a review in time for 3.17.
>
> If you find this a severe issue, please let me know.

No time-pressure at all. Take your time.

Thanks
David

^ permalink raw reply

* Re: [PATCH 00/15] atmel_mxt_ts - device tree, bootloader, etc
From: Stephen Warren @ 2014-07-29 19:26 UTC (permalink / raw)
  To: Nick Dyer
  Cc: Yufeng Shen, Dmitry Torokhov, benson Leung, Daniel Kurtz,
	Henrik Rydberg, Joonyoung Shim, Alan Bowens, linux-input,
	linux-kernel@vger.kernel.org, Peter Meerwald, Olof Johansson,
	Sekhar Nori
In-Reply-To: <53D7D47E.5060906@itdev.co.uk>

On 07/29/2014 11:06 AM, Nick Dyer wrote:
> On 29/07/14 17:16, Stephen Warren wrote:
>> I then tried updating the firmware. This didn't work at all.
>>
>> First I tried via mxt-app:
>>
>>> root@localhost:~# ./obp-utils/mxt-app -d i2c-dev:1-004b --flash
>>> 130.1_1.0.170.bin
>>> Version:1.16-65-g0a4c
>>> Opening firmware file 130.1_1.0.170.bin
>>> Registered i2c-dev adapter:1 address:0x4b
>>> Chip detected
>>> Current firmware version: 1.0.AA
>>> Skipping version check
>>> Resetting in bootloader mode
>>> Registered i2c-dev adapter:1 address:0x25
>>> Error Remote I/O error (121) reading from i2c
>>> Bootloader read failure
>>> Bootloader not found
>>
>> Then I power-cycled and tried via the atmel_mxt_ts modules' sysfs files:
>>
>>> root@localhost:~# echo 1 >
>>> /sys/devices/soc0/7000c400.i2c/i2c-1/1-004b/update_fw
>>> [   38.495420] atmel_mxt_ts 1-004b: mxt_bootloader_read: i2c recv failed
>>> (-121)
>>> [   38.506208] atmel_mxt_ts 1-004b: mxt_bootloader_read: i2c recv failed
>>> (-121)
>>> [   38.513836] atmel_mxt_ts 1-004b: The firmware update failed(-121)
>>> -bash: echo: write error: Remote I/O error
>
> OK - that's the same error in both cases, it has tried to switch the device
> into bootloader mode, however it is not responding on the bootloader I2C
> address.
>
> Couple of things to check:
> - is the device in deep sleep mode when you run the mxt-app command? can
> you try doing "mxt-app [device] -W -T7 FFFF" which will make sure it is
> definitely not sleeping first.

That didn't hekp.

> - if you do "mxt-app [device] -i" straight after the --flash failure, does
> it respond (which would mean it hasn't actioned the reset-into-bootloader
> command)

That still works, so I assume it wasn't reset into bootloader mode. I 
also tried mxt-app --reset-bootloader, and mxt-app -i still works after 
that too.

>> I also found that removing the module (even without attempting a FW update)
>> yields:
>>
>> After attempted FW update via sysfs:
>>
>>> root@localhost:~# rmmod ./atmel_mxt_ts.ko
>>> [   81.995672] Unable to handle kernel NULL pointer dereference at virtual address 00000364
>
> Not good. It's trying to free an input device which isn't registered at
> that point. In a later patch in my series I add a guard around that
> (mxt_free_input_device):
> https://github.com/ndyer/linux/commit/bb4800ff8c185

It looks like 8d01a84b86b0 "Input: atmel_mxt_ts - implement T100 touch 
object support" from that branch is what solves the issue.

Ah, in linux-next, it's simply a double-unregister; both mxt_remove() 
and mxt_free_object_table() call 
input_unregister_device(data->input_dev). I think it would make sense to 
send the following patch for 3.17. Do you agree?

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c 
b/drivers/input/touchscreen/atmel_mxt_ts.c
index 03b85711cb70..da497dbf9735 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1353,8 +1353,10 @@ static int mxt_get_info(struct mxt_data *data)

  static void mxt_free_object_table(struct mxt_data *data)
  {
-	input_unregister_device(data->input_dev);
-	data->input_dev = NULL;
+	if (data->input_dev) {
+		input_unregister_device(data->input_dev);
+		data->input_dev = NULL;
+	}

  	kfree(data->object_table);
  	data->object_table = NULL;
@@ -2194,7 +2196,6 @@ static int mxt_remove(struct i2c_client *client)

  	sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
  	free_irq(data->irq, data);
-	input_unregister_device(data->input_dev);
  	mxt_free_object_table(data);
  	kfree(data);


^ permalink raw reply related

* [Patch v3] input: drv260x: Add TI drv260x haptics driver
From: Dan Murphy @ 2014-07-29 19:32 UTC (permalink / raw)
  To: linux-input
  Cc: linux-kernel, devicetree, dmitry.torokhov, mark.rutland,
	madcatxster, simon, elias.vds, Dan Murphy

Add the TI drv260x haptics/vibrator driver.
This device uses the input force feedback
to produce a wave form to driver an
ERM or LRA actuator device.

The initial driver supports the devices
real time playback mode.  But the device
has additional wave patterns in ROM.

This functionality will be added in
future patchsets.

Product data sheet is located here:
http://www.ti.com/product/drv2605

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---

v3 - Updated binding doc, changed to memless device, updated input alloc to
devm, removed mutex locking, add sanity checks for mode and library - https://patchwork.kernel.org/patch/4635421/
v2 - Fixed binding doc and patch headline - https://patchwork.kernel.org/patch/4619641/

 .../devicetree/bindings/input/ti,drv260x.txt       |   50 ++
 drivers/input/misc/Kconfig                         |    9 +
 drivers/input/misc/Makefile                        |    1 +
 drivers/input/misc/drv260x.c                       |  515 ++++++++++++++++++++
 include/dt-bindings/input/ti-drv260x.h             |   30 ++
 include/linux/input/drv260x.h                      |  181 +++++++
 6 files changed, 786 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/ti,drv260x.txt
 create mode 100644 drivers/input/misc/drv260x.c
 create mode 100644 include/dt-bindings/input/ti-drv260x.h
 create mode 100644 include/linux/input/drv260x.h

diff --git a/Documentation/devicetree/bindings/input/ti,drv260x.txt b/Documentation/devicetree/bindings/input/ti,drv260x.txt
new file mode 100644
index 0000000..8e6970d
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/ti,drv260x.txt
@@ -0,0 +1,50 @@
+Texas Instruments - drv260x Haptics driver family
+
+The drv260x family serial control bus communicates through I2C protocols
+
+Required properties:
+	- compatible - One of:
+		"ti,drv2604" - DRV2604
+		"ti,drv2605" - DRV2605
+		"ti,drv2605l" - DRV2605L
+	- reg -  I2C slave address
+	- supply- Required supply regulators are:
+		"vbat" - battery voltage
+	- mode - Power up mode of the chip (defined in include/dt-bindings/input/ti-drv260x.h)
+		DRV260X_LRA_MODE - Linear Resonance Actuator mode (Piezoelectric)
+		DRV260X_LRA_NO_CAL_MODE - This is a LRA Mode but there is no calibration
+				sequence during init.  And the device is configured for real
+				time playback mode (RTP mode).
+		DRV260X_ERM_MODE - Eccentric Rotating Mass mode (Rotary vibrator)
+	- library-sel - These are ROM based waveforms pre-programmed into the IC.
+				This should be set to set the library to use at power up.
+				(defined in include/dt-bindings/input/ti-drv260x.h)
+		DRV260X_LIB_A - Pre-programmed Library
+		DRV260X_LIB_B - Pre-programmed Library
+		DRV260X_LIB_C - Pre-programmed Library
+		DRV260X_LIB_D - Pre-programmed Library
+		DRV260X_LIB_E - Pre-programmed Library
+		DRV260X_LIB_F - Pre-programmed Library
+
+Optional properties:
+	- enable-gpio - gpio pin to enable/disable the device.
+	- vib_rated_voltage - The rated voltage of the actuator in millivolts.
+			  If this is not set then the value will be defaulted to
+			  3.2 v.
+	- vib_overdrive_voltage - The overdrive voltage of the actuator in millivolts.
+			  If this is not set then the value will be defaulted to
+			  3.2 v.
+Example:
+
+drv2605l: drv2605l@5a {
+		compatible = "ti,drv2605l";
+		reg = <0x5a>;
+		enable-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>;
+		mode = <DRV260X_LRA_MODE>;
+		library-sel = <DRV260X_LIB_SEL_DEFAULT>;
+		vib-rated-voltage = <3200>;
+		vib-overdriver-voltage = <3200>;
+};
+
+For more product information please see the link below:
+http://www.ti.com/product/drv2605
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 2ff4425..99f6762 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -676,4 +676,13 @@ config INPUT_SOC_BUTTON_ARRAY
 	  To compile this driver as a module, choose M here: the
 	  module will be called soc_button_array.
 
+config INPUT_DRV260X_HAPTICS
+	tristate "TI DRV260X haptics support"
+	depends on INPUT && I2C
+	help
+	  Say Y to enable support for the TI DRV260X haptics driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called drv260x-haptics.
+
 endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 4955ad3..d8ef3c7 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -64,3 +64,4 @@ obj-$(CONFIG_INPUT_WM831X_ON)		+= wm831x-on.o
 obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
 obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
 obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR)	+= ideapad_slidebar.o
+obj-$(CONFIG_INPUT_DRV260X_HAPTICS)	+= drv260x.o
diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
new file mode 100644
index 0000000..edf75a2
--- /dev/null
+++ b/drivers/input/misc/drv260x.c
@@ -0,0 +1,515 @@
+/*
+ * drv260x.c - DRV260X haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright:   (C) 2014 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/input/drv260x.h>
+#include <dt-bindings/input/ti-drv260x.h>
+
+/**
+ * struct drv260x_data -
+ * @input_dev - Pointer to the input device
+ * @client - Pointer to the I2C client
+ * @regmap - Register map of the device
+ * @work - Work item used to off load the enable/disable of the vibration
+ * @enable_gpio - Pointer to the gpio used for enable/disabling
+ * @regulator - Pointer to the regulator for the IC
+ * @magnitude - Magnitude of the vibration event
+ * @mode - The operating mode of the IC (LRA_NO_CAL, ERM or LRA)
+ * @library - The vibration library to be used
+ * @rated_voltage - The rated_voltage of the actuator
+ * @overdriver_voltage - The over drive voltage of the actuator
+**/
+struct drv260x_data {
+	struct input_dev *input_dev;
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct work_struct work;
+	struct gpio_desc *enable_gpio;
+	struct regulator *regulator;
+	u32 magnitude;
+	u32 mode;
+	u32 library;
+	int rated_voltage;
+	int overdrive_voltage;
+};
+
+static struct reg_default drv260x_reg_defs[] = {
+	{ DRV260X_STATUS, 0xe0 },
+	{ DRV260X_MODE, 0x40 },
+	{ DRV260X_RT_PB_IN, 0x00},
+	{ DRV260X_LIB_SEL, 0x00},
+	{ DRV260X_WV_SEQ_1, 0x01},
+	{ DRV260X_WV_SEQ_2, 0x00},
+	{ DRV260X_WV_SEQ_3, 0x00},
+	{ DRV260X_WV_SEQ_4, 0x00},
+	{ DRV260X_WV_SEQ_5, 0x00},
+	{ DRV260X_WV_SEQ_6, 0x00},
+	{ DRV260X_WV_SEQ_7, 0x00},
+	{ DRV260X_WV_SEQ_8, 0x00},
+	{ DRV260X_GO, 0x00},
+	{ DRV260X_OVERDRIVE_OFF, 0x00},
+	{ DRV260X_SUSTAIN_P_OFF, 0x00},
+	{ DRV260X_SUSTAIN_N_OFF, 0x00},
+	{ DRV260X_BRAKE_OFF	, 0x00},
+	{ DRV260X_A_TO_V_CTRL	, 0x05},
+	{ DRV260X_A_TO_V_MIN_INPUT, 0x19},
+	{ DRV260X_A_TO_V_MAX_INPUT, 0xff},
+	{ DRV260X_A_TO_V_MIN_OUT, 0x19},
+	{ DRV260X_A_TO_V_MAX_OUT, 0xff},
+	{ DRV260X_RATED_VOLT, 0x3e},
+	{ DRV260X_OD_CLAMP_VOLT, 0x8c},
+	{ DRV260X_CAL_COMP, 0x0c},
+	{ DRV260X_CAL_BACK_EMF, 0x6c},
+	{ DRV260X_FEEDBACK_CTRL, 0x36},
+	{ DRV260X_CTRL1, 0x93},
+	{ DRV260X_CTRL2, 0xfa},
+	{ DRV260X_CTRL3, 0xa0},
+	{ DRV260X_CTRL4, 0x20},
+	{ DRV260X_CTRL5, 0x80},
+	{ DRV260X_LRA_LOOP_PERIOD, 0x33},
+	{ DRV260X_VBAT_MON, 0x00},
+	{ DRV260X_LRA_RES_PERIOD, 0x00},
+};
+
+/**
+ * Rated and Overdriver Voltages:
+ * Calculated using the formula r = v * 255 / 5.6
+ * where r is what will be written to the register
+ * and v is the rated or overdriver voltage of the actuator
+ **/
+#define DRV260X_DEF_RATED_VOLT		0x90
+#define DRV260X_DEF_OD_CLAMP_VOLT	0x90
+
+static int drv260x_calculate_voltage(int voltage)
+{
+	return (voltage * 255 / 5600);
+}
+
+static void drv260x_worker(struct work_struct *work)
+{
+	struct drv260x_data *haptics = container_of(work, struct drv260x_data, work);
+
+	regmap_write(haptics->regmap, DRV260X_RT_PB_IN,	haptics->magnitude);
+
+}
+
+static int drv260x_haptics_play(struct input_dev *input, void *data,
+				struct ff_effect *effect)
+{
+	struct drv260x_data *haptics = input_get_drvdata(input);
+	int ret;
+
+	gpiod_set_value(haptics->enable_gpio, 1);
+	/* Data sheet says to wait 250us before trying to communicate */
+	udelay(250);
+
+	ret = regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_RT_PLAYBACK);
+	if (ret != 0) {
+		dev_err(&haptics->client->dev,
+			"Failed to write set mode: %d\n",
+			ret);
+		return ret;
+	}
+
+	haptics->mode = DRV260X_LRA_NO_CAL_MODE;
+	haptics->magnitude = 0;
+
+	if (effect->u.rumble.strong_magnitude ||
+		effect->u.rumble.weak_magnitude) {
+		if (effect->u.rumble.strong_magnitude > 0)
+			haptics->magnitude = effect->u.rumble.strong_magnitude;
+		else if	(effect->u.rumble.weak_magnitude > 0)
+			haptics->magnitude = effect->u.rumble.weak_magnitude;
+	}
+
+	schedule_work(&haptics->work);
+
+	return 0;
+}
+
+static void drv260x_close(struct input_dev *input)
+{
+	struct drv260x_data *haptics = input_get_drvdata(input);
+
+	cancel_work_sync(&haptics->work);
+	regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY);
+	gpiod_set_value(haptics->enable_gpio, 0);
+}
+
+static const struct reg_default drv260x_lra_cal_regs[] = {
+	{ DRV260X_MODE, DRV260X_AUTO_CAL },
+	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2},
+	{ DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH },
+};
+
+static const struct reg_default drv260x_lra_init_regs[] = {
+	{ DRV260X_MODE, DRV260X_RT_PLAYBACK},
+	{ DRV260X_A_TO_V_CTRL, DRV260X_AUDIO_HAPTICS_PEAK_20MS | DRV260X_AUDIO_HAPTICS_FILTER_125HZ},
+	{ DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT },
+	{ DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT },
+	{ DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT },
+	{ DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT },
+	{ DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | DRV260X_BRAKE_FACTOR_2X | DRV260X_LOOP_GAIN_MED | DRV260X_BEMF_GAIN_3 },
+	{ DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
+	{ DRV260X_CTRL2, DRV260X_SAMP_TIME_250 },
+	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ANANLOG_IN },
+	{ DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
+};
+
+static const struct reg_default drv260x_erm_cal_regs[] = {
+	{ DRV260X_MODE, DRV260X_AUTO_CAL },
+	{ DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT },
+	{ DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT },
+	{ DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT },
+	{ DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT },
+	{ DRV260X_FEEDBACK_CTRL, DRV260X_BRAKE_FACTOR_3X | DRV260X_LOOP_GAIN_MED | DRV260X_BEMF_GAIN_2 },
+	{ DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
+	{ DRV260X_CTRL2, DRV260X_SAMP_TIME_250 | DRV260X_BLANK_TIME_75 | DRV260X_SAMP_TIME_250 | DRV260X_IDISS_TIME_75 },
+	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ERM_OPEN_LOOP },
+	{ DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
+};
+
+static int drv260x_init(struct drv260x_data *haptics)
+{
+	int ret;
+	unsigned int cal_buf;
+
+	ret = regmap_write(haptics->regmap,
+			   DRV260X_RATED_VOLT, haptics->rated_voltage);
+	if (ret != 0)
+		goto write_failure;
+
+	ret = regmap_write(haptics->regmap,
+			   DRV260X_OD_CLAMP_VOLT, haptics->overdrive_voltage);
+	if (ret != 0)
+		goto write_failure;
+
+	if (haptics->mode == DRV260X_LRA_MODE) {
+		ret = regmap_register_patch(haptics->regmap,
+					drv260x_lra_cal_regs,
+					ARRAY_SIZE(drv260x_lra_cal_regs));
+		if (ret != 0)
+			goto write_failure;
+
+	} else if (haptics->mode == DRV260X_ERM_MODE) {
+		ret = regmap_register_patch(haptics->regmap,
+					drv260x_erm_cal_regs,
+					ARRAY_SIZE(drv260x_erm_cal_regs));
+		if (ret != 0)
+			goto write_failure;
+
+		ret = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL,
+					DRV260X_LIB_SEL_MASK,
+					haptics->library);
+		if (ret != 0)
+			goto write_failure;
+
+	} else {
+		ret = regmap_register_patch(haptics->regmap,
+					drv260x_lra_init_regs,
+					ARRAY_SIZE(drv260x_lra_init_regs));
+		if (ret != 0)
+			goto write_failure;
+
+		ret = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL,
+					DRV260X_LIB_SEL_MASK,
+					haptics->library);
+		if (ret != 0)
+			goto write_failure;
+
+		goto skip_go_bit;
+	}
+
+	if (ret != 0) {
+		dev_err(&haptics->client->dev,
+			"Failed to write init registers: %d\n",
+			ret);
+		goto write_failure;
+	}
+
+	ret = regmap_write(haptics->regmap, DRV260X_GO, DRV260X_GO_BIT);
+	if (ret != 0)
+		goto write_failure;
+
+	do {
+		ret = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf);
+		if (ret != 0)
+			goto write_failure;
+	} while (cal_buf == DRV260X_GO_BIT || ret != 0);
+
+	return ret;
+
+write_failure:
+	dev_err(&haptics->client->dev,
+		"Failed to write init registers: %d\n",
+		ret);
+skip_go_bit:
+	return ret;
+}
+
+static const struct regmap_config drv260x_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = DRV260X_MAX_REG,
+	.reg_defaults = drv260x_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(drv260x_reg_defs),
+	.cache_type = REGCACHE_NONE,
+};
+
+static int drv260x_probe(struct i2c_client *client,
+			   const struct i2c_device_id *id)
+{
+	struct drv260x_data *haptics;
+	struct device_node *np = client->dev.of_node;
+	struct drv260x_platform_data *pdata = client->dev.platform_data;
+	int ret;
+	int voltage;
+
+	haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL);
+	if (!haptics)
+		return -ENOMEM;
+
+	haptics->rated_voltage = DRV260X_DEF_OD_CLAMP_VOLT;
+	haptics->rated_voltage = DRV260X_DEF_RATED_VOLT;
+
+	if (np) {
+		ret = of_property_read_u32(np, "mode", &haptics->mode);
+		if (ret < 0) {
+			dev_err(&client->dev,
+				"%s: No entry for mode\n", __func__);
+
+			return ret;
+		}
+		ret = of_property_read_u32(np, "library-sel",
+					&haptics->library);
+		if (ret < 0) {
+			dev_err(&client->dev,
+				"%s: No entry for library selection\n",
+				__func__);
+
+			return ret;
+		}
+		ret = of_property_read_u32(np, "vib-rated-voltage",
+					&voltage);
+		if (!ret)
+			haptics->rated_voltage = drv260x_calculate_voltage(voltage);
+
+
+		ret = of_property_read_u32(np, "vib-overdrive-voltage",
+					&voltage);
+		if (!ret)
+			haptics->overdrive_voltage = drv260x_calculate_voltage(voltage);
+
+	} else if (pdata) {
+		haptics->mode = pdata->mode;
+		haptics->library = pdata->library_selection;
+		if (pdata->vib_overdrive_voltage)
+			haptics->overdrive_voltage = drv260x_calculate_voltage(pdata->vib_overdrive_voltage);
+		if (pdata->vib_rated_voltage)
+			haptics->rated_voltage = drv260x_calculate_voltage(pdata->vib_rated_voltage);
+	} else {
+		dev_err(&client->dev, "Platform data not set\n");
+		return -ENODEV;
+	}
+
+
+	if (haptics->mode < DRV260X_LRA_MODE ||
+		haptics->mode > DRV260X_ERM_MODE) {
+			dev_err(&client->dev,
+				"Mode value is invalid: %i using default RTP mode\n",
+				haptics->mode);
+			return -EINVAL;
+	}
+	
+	if (haptics->library < DRV260X_LIB_SEL_DEFAULT ||
+		haptics->library > DRV260X_LIB_F) {
+			dev_err(&client->dev,
+				"Library value is invalid: %i\n", haptics->library);
+			return -EINVAL;
+	}	
+
+	haptics->regulator = regulator_get(&client->dev, "vbat");
+	if (IS_ERR(haptics->regulator)) {
+		ret = PTR_ERR(haptics->regulator);
+		dev_err(&client->dev,
+			"unable to get regulator, error: %d\n", ret);
+		goto err_regulator;
+	}
+
+	haptics->enable_gpio = devm_gpiod_get(&client->dev, "enable");
+	if (IS_ERR(haptics->enable_gpio)) {
+		ret = PTR_ERR(haptics->enable_gpio);
+		if (ret != -ENOENT && ret != -ENOSYS)
+			goto err_gpio;
+
+		haptics->enable_gpio = NULL;
+	} else {
+		gpiod_direction_output(haptics->enable_gpio, 1);
+	}
+
+	haptics->input_dev = devm_input_allocate_device(&client->dev);
+	if (haptics->input_dev == NULL) {
+		dev_err(&client->dev, "Failed to allocate input device\n");
+		ret = -ENOMEM;
+		goto err_input_alloc;
+	}
+
+	haptics->input_dev->name = "drv260x:haptics";
+	haptics->input_dev->dev.parent = client->dev.parent;
+	haptics->input_dev->close = drv260x_close;
+	input_set_drvdata(haptics->input_dev, haptics);
+	input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
+
+	ret = input_ff_create_memless(haptics->input_dev, NULL,
+				      drv260x_haptics_play);
+	if (ret < 0) {
+		dev_err(&client->dev, "input_ff_create() failed: %d\n",
+			ret);
+		goto err_ff_create;
+	}
+
+	INIT_WORK(&haptics->work, drv260x_worker);
+
+	haptics->client = client;
+	i2c_set_clientdata(client, haptics);
+
+	haptics->regmap = devm_regmap_init_i2c(client, &drv260x_regmap_config);
+	if (IS_ERR(haptics->regmap)) {
+		ret = PTR_ERR(haptics->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+			ret);
+		goto err_regmap;
+	}
+
+	drv260x_init(haptics);
+
+	ret = input_register_device(haptics->input_dev);
+	if (ret < 0) {
+		dev_err(&client->dev, "couldn't register input device: %d\n",
+			ret);
+		goto err_iff;
+	}
+	return 0;
+
+err_iff:
+err_regmap:
+	if (haptics->input_dev)
+		input_ff_destroy(haptics->input_dev);
+err_ff_create:
+err_input_alloc:
+err_gpio:
+	regulator_put(haptics->regulator);
+err_regulator:
+	return ret;
+}
+
+static int drv260x_remove(struct i2c_client *client)
+{
+	struct drv260x_data *haptics = i2c_get_clientdata(client);
+
+	cancel_work_sync(&haptics->work);
+
+	regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY);
+	gpiod_set_value(haptics->enable_gpio, 0);
+
+	input_unregister_device(haptics->input_dev);
+	regulator_put(haptics->regulator);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int drv260x_suspend(struct device *dev)
+{
+	struct drv260x_data *haptics = dev_get_drvdata(dev);
+
+	regmap_update_bits(haptics->regmap,
+			   DRV260X_MODE,
+			   DRV260X_STANDBY_MASK,
+			   DRV260X_STANDBY);
+	gpiod_set_value(haptics->enable_gpio, 0);
+
+	regulator_disable(haptics->regulator);
+
+	return 0;
+}
+
+static int drv260x_resume(struct device *dev)
+{
+	struct drv260x_data *haptics = dev_get_drvdata(dev);
+	int ret;
+
+	ret = regulator_enable(haptics->regulator);
+	if (ret) {
+		dev_err(dev, "Failed to enable regulator\n");
+		return ret;
+	}
+	regmap_update_bits(haptics->regmap,
+			   DRV260X_MODE,
+			   DRV260X_STANDBY_MASK, 0);
+
+	gpiod_set_value(haptics->enable_gpio, 1);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume);
+
+static const struct i2c_device_id drv260x_id[] = {
+	{ "drv2605l", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, drv260x_id);
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id drv260x_of_match[] = {
+	{ .compatible = "ti,drv2604", },
+	{ .compatible = "ti,drv2605", },
+	{ .compatible = "ti,drv2605l", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, drv260x_of_match);
+#endif
+
+static struct i2c_driver drv260x_driver = {
+	.probe		= drv260x_probe,
+	.remove		= drv260x_remove,
+	.driver		= {
+		.name	= "drv260x-haptics",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(drv260x_of_match),
+		.pm	= &drv260x_pm_ops,
+	},
+	.id_table = drv260x_id,
+};
+module_i2c_driver(drv260x_driver);
+
+MODULE_ALIAS("platform:drv260x-haptics");
+MODULE_DESCRIPTION("TI DRV260x haptics driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
diff --git a/include/dt-bindings/input/ti-drv260x.h b/include/dt-bindings/input/ti-drv260x.h
new file mode 100644
index 0000000..3843b9a
--- /dev/null
+++ b/include/dt-bindings/input/ti-drv260x.h
@@ -0,0 +1,30 @@
+/*
+ * ti-drv260x.h - DRV260X haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright:   (C) 2014 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/* Calibration Types */
+#define DRV260X_LRA_MODE		0x00
+#define DRV260X_LRA_NO_CAL_MODE	0x01
+#define DRV260X_ERM_MODE		0x02
+
+/* Library Selection */
+#define DRV260X_LIB_SEL_DEFAULT		0x00
+#define DRV260X_LIB_A				0x01
+#define DRV260X_LIB_B				0x02
+#define DRV260X_LIB_C				0x03
+#define DRV260X_LIB_D				0x04
+#define DRV260X_LIB_E				0x05
+#define DRV260X_LIB_F				0x06
diff --git a/include/linux/input/drv260x.h b/include/linux/input/drv260x.h
new file mode 100644
index 0000000..709395e
--- /dev/null
+++ b/include/linux/input/drv260x.h
@@ -0,0 +1,181 @@
+/*
+ * drv260x.h - DRV260X haptics driver family
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Copyright:   (C) 2014 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef _LINUX_DRV260X_I2C_H
+#define _LINUX_DRV260X_I2C_H
+
+#define DRV260X_STATUS		0x0
+#define DRV260X_MODE		0x1
+#define DRV260X_RT_PB_IN	0x2
+#define DRV260X_LIB_SEL		0x3
+#define DRV260X_WV_SEQ_1	0x4
+#define DRV260X_WV_SEQ_2	0x5
+#define DRV260X_WV_SEQ_3	0x6
+#define DRV260X_WV_SEQ_4	0x7
+#define DRV260X_WV_SEQ_5	0x8
+#define DRV260X_WV_SEQ_6	0x9
+#define DRV260X_WV_SEQ_7	0xa
+#define DRV260X_WV_SEQ_8	0xb
+#define DRV260X_GO				0xc
+#define DRV260X_OVERDRIVE_OFF	0xd
+#define DRV260X_SUSTAIN_P_OFF	0xe
+#define DRV260X_SUSTAIN_N_OFF	0xf
+#define DRV260X_BRAKE_OFF		0x10
+#define DRV260X_A_TO_V_CTRL		0x11
+#define DRV260X_A_TO_V_MIN_INPUT	0x12
+#define DRV260X_A_TO_V_MAX_INPUT	0x13
+#define DRV260X_A_TO_V_MIN_OUT	0x14
+#define DRV260X_A_TO_V_MAX_OUT	0x15
+#define DRV260X_RATED_VOLT		0x16
+#define DRV260X_OD_CLAMP_VOLT	0x17
+#define DRV260X_CAL_COMP		0x18
+#define DRV260X_CAL_BACK_EMF	0x19
+#define DRV260X_FEEDBACK_CTRL	0x1a
+#define DRV260X_CTRL1			0x1b
+#define DRV260X_CTRL2			0x1c
+#define DRV260X_CTRL3			0x1d
+#define DRV260X_CTRL4			0x1e
+#define DRV260X_CTRL5			0x1f
+#define DRV260X_LRA_LOOP_PERIOD	0x20
+#define DRV260X_VBAT_MON		0x21
+#define DRV260X_LRA_RES_PERIOD	0x22
+#define DRV260X_MAX_REG			0x23
+
+#define DRV260X_ALLOWED_R_BYTES	25
+#define DRV260X_ALLOWED_W_BYTES	2
+#define DRV260X_MAX_RW_RETRIES	5
+#define DRV260X_I2C_RETRY_DELAY 10
+
+#define DRV260X_GO_BIT				0x01
+
+/* Library Selection */
+#define DRV260X_LIB_SEL_MASK		0x07
+#define DRV260X_LIB_SEL_RAM			0x0
+#define DRV260X_LIB_SEL_OD			0x1
+#define DRV260X_LIB_SEL_40_60		0x2
+#define DRV260X_LIB_SEL_60_80		0x3
+#define DRV260X_LIB_SEL_100_140		0x4
+#define DRV260X_LIB_SEL_140_PLUS	0x5
+
+#define DRV260X_LIB_SEL_HIZ_MASK	0x10
+#define DRV260X_LIB_SEL_HIZ_EN		0x01
+#define DRV260X_LIB_SEL_HIZ_DIS		0
+
+/* Mode register */
+#define DRV260X_STANDBY				(1 << 6)
+#define DRV260X_STANDBY_MASK		0x40
+#define DRV260X_INTERNAL_TRIGGER	0x00
+#define DRV260X_EXT_TRIGGER_EDGE	0x01
+#define DRV260X_EXT_TRIGGER_LEVEL	0x02
+#define DRV260X_PWM_ANALOG_IN		0x03
+#define DRV260X_AUDIOHAPTIC			0x04
+#define DRV260X_RT_PLAYBACK			0x05
+#define DRV260X_DIAGNOSTICS			0x06
+#define DRV260X_AUTO_CAL			0x07
+
+/* Audio to Haptics Control */
+#define DRV260X_AUDIO_HAPTICS_PEAK_10MS		(0 << 2)
+#define DRV260X_AUDIO_HAPTICS_PEAK_20MS		(1 << 2)
+#define DRV260X_AUDIO_HAPTICS_PEAK_30MS		(2 << 2)
+#define DRV260X_AUDIO_HAPTICS_PEAK_40MS		(3 << 2)
+
+#define DRV260X_AUDIO_HAPTICS_FILTER_100HZ	0x00
+#define DRV260X_AUDIO_HAPTICS_FILTER_125HZ	0x01
+#define DRV260X_AUDIO_HAPTICS_FILTER_150HZ	0x02
+#define DRV260X_AUDIO_HAPTICS_FILTER_200HZ	0x03
+
+/* Min/Max Input/Output Voltages */
+#define DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT	0x19
+#define DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT	0x64
+#define DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT	0x19
+#define DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT	0xFF
+
+/* Feedback register */
+#define DRV260X_FB_REG_ERM_MODE			0x7f
+#define DRV260X_FB_REG_LRA_MODE			(1 << 7)
+
+#define DRV260X_BRAKE_FACTOR_MASK	0x1f
+#define DRV260X_BRAKE_FACTOR_2X		(1 << 0)
+#define DRV260X_BRAKE_FACTOR_3X		(2 << 4)
+#define DRV260X_BRAKE_FACTOR_4X		(3 << 4)
+#define DRV260X_BRAKE_FACTOR_6X		(4 << 4)
+#define DRV260X_BRAKE_FACTOR_8X		(5 << 4)
+#define DRV260X_BRAKE_FACTOR_16		(6 << 4)
+#define DRV260X_BRAKE_FACTOR_DIS	(7 << 4)
+
+#define DRV260X_LOOP_GAIN_LOW		0xf3
+#define DRV260X_LOOP_GAIN_MED		(1 << 2)
+#define DRV260X_LOOP_GAIN_HIGH		(2 << 2)
+#define DRV260X_LOOP_GAIN_VERY_HIGH	(3 << 2)
+
+#define DRV260X_BEMF_GAIN_0			0xfc
+#define DRV260X_BEMF_GAIN_1		(1 << 0)
+#define DRV260X_BEMF_GAIN_2		(2 << 0)
+#define DRV260X_BEMF_GAIN_3		(3 << 0)
+
+/* Control 1 register */
+#define DRV260X_AC_CPLE_EN			(1 << 5)
+#define DRV260X_STARTUP_BOOST		(1 << 7)
+
+/* Control 2 register */
+
+#define DRV260X_IDISS_TIME_45		0
+#define DRV260X_IDISS_TIME_75		(1 << 0)
+#define DRV260X_IDISS_TIME_150		(1 << 1)
+#define DRV260X_IDISS_TIME_225		0x03
+
+#define DRV260X_BLANK_TIME_45	(0 << 2)
+#define DRV260X_BLANK_TIME_75	(1 << 2)
+#define DRV260X_BLANK_TIME_150	(2 << 2)
+#define DRV260X_BLANK_TIME_225	(3 << 2)
+
+#define DRV260X_SAMP_TIME_150	(0 << 4)
+#define DRV260X_SAMP_TIME_200	(1 << 4)
+#define DRV260X_SAMP_TIME_250	(2 << 4)
+#define DRV260X_SAMP_TIME_300	(3 << 4)
+
+#define DRV260X_BRAKE_STABILIZER	(1 << 6)
+#define DRV260X_UNIDIR_IN			(0 << 7)
+#define DRV260X_BIDIR_IN			(1 << 7)
+
+/* Control 3 Register */
+#define DRV260X_LRA_OPEN_LOOP		(1 << 0)
+#define DRV260X_ANANLOG_IN			(1 << 1)
+#define DRV260X_LRA_DRV_MODE		(1 << 2)
+#define DRV260X_RTP_UNSIGNED_DATA	(1 << 3)
+#define DRV260X_SUPPLY_COMP_DIS		(1 << 4)
+#define DRV260X_ERM_OPEN_LOOP		(1 << 5)
+#define DRV260X_NG_THRESH_0			(0 << 6)
+#define DRV260X_NG_THRESH_2			(1 << 6)
+#define DRV260X_NG_THRESH_4			(2 << 6)
+#define DRV260X_NG_THRESH_8			(3 << 6)
+
+/* Control 4 Register */
+#define DRV260X_AUTOCAL_TIME_150MS		(0 << 4)
+#define DRV260X_AUTOCAL_TIME_250MS		(1 << 4)
+#define DRV260X_AUTOCAL_TIME_500MS		(2 << 4)
+#define DRV260X_AUTOCAL_TIME_1000MS		(3 << 4)
+
+struct drv260x_platform_data {
+	int enable_gpio;
+	int library_selection;
+	int mode;
+	int vib_rated_voltage;
+	int vib_overdrive_voltage;
+};
+
+#endif
-- 
1.7.9.5


^ permalink raw reply related

* Re: [PATCH] alps: rushmore and v7 resolution support
From: Dmitry Torokhov @ 2014-07-29 19:45 UTC (permalink / raw)
  To: Hans de Goede; +Cc: Yunkang Tang, linux-input
In-Reply-To: <1406621252-5569-1-git-send-email-hdegoede@redhat.com>

Hi Hans,

On Tue, Jul 29, 2014 at 10:07:32AM +0200, Hans de Goede wrote:
> Add support for querying the physical size from the touchpad for rushmore
> and v7 touchpads, and use that to tell userspace the device resolution.
> 
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
>  drivers/input/mouse/alps.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/input/mouse/alps.h |  2 ++
>  2 files changed, 50 insertions(+)
> 
> diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
> index ac9fdbd..1531387 100644
> --- a/drivers/input/mouse/alps.c
> +++ b/drivers/input/mouse/alps.c
> @@ -1783,6 +1783,45 @@ error:
>  	return -1;
>  }
>  
> +static int alps_get_v3_v7_resolution(struct psmouse *psmouse, int reg_pitch)
> +{
> +	int reg, x_pitch, y_pitch, x_electrode, y_electrode, x_phys, y_phys;
> +	struct alps_data *priv = psmouse->private;
> +
> +	reg = alps_command_mode_read_reg(psmouse, reg_pitch);
> +	if (reg < 0)
> +		return reg;
> +
> +	x_pitch = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */
> +	x_pitch = 50 + 2 * x_pitch; /* In 0.1 mm units */
> +
> +	y_pitch = (char)reg >> 4; /* sign extend upper 4 bits */
> +	y_pitch = 36 + 2 * y_pitch; /* In 0.1 mm units */
> +
> +	reg = alps_command_mode_read_reg(psmouse, reg_pitch + 1);
> +	if (reg < 0)
> +		return reg;
> +
> +	x_electrode = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */
> +	x_electrode = 17 + x_electrode;
> +
> +	y_electrode = (char)reg >> 4; /* sign extend upper 4 bits */
> +	y_electrode = 13 + y_electrode;
> +
> +	x_phys = x_pitch * (x_electrode - 1); /* In 0.1 mm units */
> +	y_phys = y_pitch * (y_electrode - 1); /* In 0.1 mm units */
> +
> +	priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */
> +	priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */
> +
> +	psmouse_info(psmouse,
> +		"alps: pitch %dx%d num-electrodes %dx%d physical size %dx%d mm res %dx%d\n",
> +		x_pitch, y_pitch, x_electrode, y_electrode,
> +		x_phys / 10, y_phys / 10, priv->x_res, priv->y_res);

I changed it to psmouse_dbg and applied.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [Patch v3] input: drv260x: Add TI drv260x haptics driver
From: Dmitry Torokhov @ 2014-07-29 20:01 UTC (permalink / raw)
  To: Dan Murphy
  Cc: linux-input, linux-kernel, devicetree, mark.rutland, madcatxster,
	simon, elias.vds
In-Reply-To: <1406662347-3277-1-git-send-email-dmurphy@ti.com>

On Tue, Jul 29, 2014 at 02:32:27PM -0500, Dan Murphy wrote:
> Add the TI drv260x haptics/vibrator driver.
> This device uses the input force feedback
> to produce a wave form to driver an
> ERM or LRA actuator device.
> 
> The initial driver supports the devices
> real time playback mode.  But the device
> has additional wave patterns in ROM.
> 
> This functionality will be added in
> future patchsets.
> 
> Product data sheet is located here:
> http://www.ti.com/product/drv2605
> 
> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> ---
> 
> v3 - Updated binding doc, changed to memless device, updated input alloc to
> devm, removed mutex locking, add sanity checks for mode and library - https://patchwork.kernel.org/patch/4635421/
> v2 - Fixed binding doc and patch headline - https://patchwork.kernel.org/patch/4619641/
> 
>  .../devicetree/bindings/input/ti,drv260x.txt       |   50 ++
>  drivers/input/misc/Kconfig                         |    9 +
>  drivers/input/misc/Makefile                        |    1 +
>  drivers/input/misc/drv260x.c                       |  515 ++++++++++++++++++++
>  include/dt-bindings/input/ti-drv260x.h             |   30 ++
>  include/linux/input/drv260x.h                      |  181 +++++++
>  6 files changed, 786 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/ti,drv260x.txt
>  create mode 100644 drivers/input/misc/drv260x.c
>  create mode 100644 include/dt-bindings/input/ti-drv260x.h
>  create mode 100644 include/linux/input/drv260x.h
> 
> diff --git a/Documentation/devicetree/bindings/input/ti,drv260x.txt b/Documentation/devicetree/bindings/input/ti,drv260x.txt
> new file mode 100644
> index 0000000..8e6970d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/ti,drv260x.txt
> @@ -0,0 +1,50 @@
> +Texas Instruments - drv260x Haptics driver family
> +
> +The drv260x family serial control bus communicates through I2C protocols
> +
> +Required properties:
> +	- compatible - One of:
> +		"ti,drv2604" - DRV2604
> +		"ti,drv2605" - DRV2605
> +		"ti,drv2605l" - DRV2605L
> +	- reg -  I2C slave address
> +	- supply- Required supply regulators are:
> +		"vbat" - battery voltage
> +	- mode - Power up mode of the chip (defined in include/dt-bindings/input/ti-drv260x.h)
> +		DRV260X_LRA_MODE - Linear Resonance Actuator mode (Piezoelectric)
> +		DRV260X_LRA_NO_CAL_MODE - This is a LRA Mode but there is no calibration
> +				sequence during init.  And the device is configured for real
> +				time playback mode (RTP mode).
> +		DRV260X_ERM_MODE - Eccentric Rotating Mass mode (Rotary vibrator)
> +	- library-sel - These are ROM based waveforms pre-programmed into the IC.
> +				This should be set to set the library to use at power up.
> +				(defined in include/dt-bindings/input/ti-drv260x.h)
> +		DRV260X_LIB_A - Pre-programmed Library
> +		DRV260X_LIB_B - Pre-programmed Library
> +		DRV260X_LIB_C - Pre-programmed Library
> +		DRV260X_LIB_D - Pre-programmed Library
> +		DRV260X_LIB_E - Pre-programmed Library
> +		DRV260X_LIB_F - Pre-programmed Library
> +
> +Optional properties:
> +	- enable-gpio - gpio pin to enable/disable the device.
> +	- vib_rated_voltage - The rated voltage of the actuator in millivolts.
> +			  If this is not set then the value will be defaulted to
> +			  3.2 v.
> +	- vib_overdrive_voltage - The overdrive voltage of the actuator in millivolts.
> +			  If this is not set then the value will be defaulted to
> +			  3.2 v.
> +Example:
> +
> +drv2605l: drv2605l@5a {
> +		compatible = "ti,drv2605l";
> +		reg = <0x5a>;
> +		enable-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>;
> +		mode = <DRV260X_LRA_MODE>;
> +		library-sel = <DRV260X_LIB_SEL_DEFAULT>;
> +		vib-rated-voltage = <3200>;
> +		vib-overdriver-voltage = <3200>;
> +};
> +
> +For more product information please see the link below:
> +http://www.ti.com/product/drv2605
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index 2ff4425..99f6762 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -676,4 +676,13 @@ config INPUT_SOC_BUTTON_ARRAY
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called soc_button_array.
>  
> +config INPUT_DRV260X_HAPTICS
> +	tristate "TI DRV260X haptics support"
> +	depends on INPUT && I2C
> +	help
> +	  Say Y to enable support for the TI DRV260X haptics driver.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called drv260x-haptics.
> +
>  endif
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index 4955ad3..d8ef3c7 100644
> --- a/drivers/input/misc/Makefile
> +++ b/drivers/input/misc/Makefile
> @@ -64,3 +64,4 @@ obj-$(CONFIG_INPUT_WM831X_ON)		+= wm831x-on.o
>  obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)	+= xen-kbdfront.o
>  obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
>  obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR)	+= ideapad_slidebar.o
> +obj-$(CONFIG_INPUT_DRV260X_HAPTICS)	+= drv260x.o
> diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
> new file mode 100644
> index 0000000..edf75a2
> --- /dev/null
> +++ b/drivers/input/misc/drv260x.c
> @@ -0,0 +1,515 @@
> +/*
> + * drv260x.c - DRV260X haptics driver family
> + *
> + * Author: Dan Murphy <dmurphy@ti.com>
> + *
> + * Copyright:   (C) 2014 Texas Instruments, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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.
> + */
> +
> +#include <linux/i2c.h>
> +#include <linux/input.h>
> +#include <linux/module.h>
> +#include <linux/of_gpio.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <linux/input/drv260x.h>
> +#include <dt-bindings/input/ti-drv260x.h>
> +
> +/**
> + * struct drv260x_data -
> + * @input_dev - Pointer to the input device
> + * @client - Pointer to the I2C client
> + * @regmap - Register map of the device
> + * @work - Work item used to off load the enable/disable of the vibration
> + * @enable_gpio - Pointer to the gpio used for enable/disabling
> + * @regulator - Pointer to the regulator for the IC
> + * @magnitude - Magnitude of the vibration event
> + * @mode - The operating mode of the IC (LRA_NO_CAL, ERM or LRA)
> + * @library - The vibration library to be used
> + * @rated_voltage - The rated_voltage of the actuator
> + * @overdriver_voltage - The over drive voltage of the actuator
> +**/
> +struct drv260x_data {
> +	struct input_dev *input_dev;
> +	struct i2c_client *client;
> +	struct regmap *regmap;
> +	struct work_struct work;
> +	struct gpio_desc *enable_gpio;
> +	struct regulator *regulator;
> +	u32 magnitude;
> +	u32 mode;
> +	u32 library;
> +	int rated_voltage;
> +	int overdrive_voltage;
> +};
> +
> +static struct reg_default drv260x_reg_defs[] = {
> +	{ DRV260X_STATUS, 0xe0 },
> +	{ DRV260X_MODE, 0x40 },
> +	{ DRV260X_RT_PB_IN, 0x00},
> +	{ DRV260X_LIB_SEL, 0x00},
> +	{ DRV260X_WV_SEQ_1, 0x01},
> +	{ DRV260X_WV_SEQ_2, 0x00},
> +	{ DRV260X_WV_SEQ_3, 0x00},
> +	{ DRV260X_WV_SEQ_4, 0x00},
> +	{ DRV260X_WV_SEQ_5, 0x00},
> +	{ DRV260X_WV_SEQ_6, 0x00},
> +	{ DRV260X_WV_SEQ_7, 0x00},
> +	{ DRV260X_WV_SEQ_8, 0x00},
> +	{ DRV260X_GO, 0x00},
> +	{ DRV260X_OVERDRIVE_OFF, 0x00},
> +	{ DRV260X_SUSTAIN_P_OFF, 0x00},
> +	{ DRV260X_SUSTAIN_N_OFF, 0x00},
> +	{ DRV260X_BRAKE_OFF	, 0x00},
> +	{ DRV260X_A_TO_V_CTRL	, 0x05},
> +	{ DRV260X_A_TO_V_MIN_INPUT, 0x19},
> +	{ DRV260X_A_TO_V_MAX_INPUT, 0xff},
> +	{ DRV260X_A_TO_V_MIN_OUT, 0x19},
> +	{ DRV260X_A_TO_V_MAX_OUT, 0xff},
> +	{ DRV260X_RATED_VOLT, 0x3e},
> +	{ DRV260X_OD_CLAMP_VOLT, 0x8c},
> +	{ DRV260X_CAL_COMP, 0x0c},
> +	{ DRV260X_CAL_BACK_EMF, 0x6c},
> +	{ DRV260X_FEEDBACK_CTRL, 0x36},
> +	{ DRV260X_CTRL1, 0x93},
> +	{ DRV260X_CTRL2, 0xfa},
> +	{ DRV260X_CTRL3, 0xa0},
> +	{ DRV260X_CTRL4, 0x20},
> +	{ DRV260X_CTRL5, 0x80},
> +	{ DRV260X_LRA_LOOP_PERIOD, 0x33},
> +	{ DRV260X_VBAT_MON, 0x00},
> +	{ DRV260X_LRA_RES_PERIOD, 0x00},
> +};
> +
> +/**
> + * Rated and Overdriver Voltages:
> + * Calculated using the formula r = v * 255 / 5.6
> + * where r is what will be written to the register
> + * and v is the rated or overdriver voltage of the actuator
> + **/
> +#define DRV260X_DEF_RATED_VOLT		0x90
> +#define DRV260X_DEF_OD_CLAMP_VOLT	0x90
> +
> +static int drv260x_calculate_voltage(int voltage)
> +{
> +	return (voltage * 255 / 5600);
> +}
> +
> +static void drv260x_worker(struct work_struct *work)
> +{
> +	struct drv260x_data *haptics = container_of(work, struct drv260x_data, work);
> +
> +	regmap_write(haptics->regmap, DRV260X_RT_PB_IN,	haptics->magnitude);

Error handling.

> +
> +}
> +
> +static int drv260x_haptics_play(struct input_dev *input, void *data,
> +				struct ff_effect *effect)
> +{
> +	struct drv260x_data *haptics = input_get_drvdata(input);
> +	int ret;
> +
> +	gpiod_set_value(haptics->enable_gpio, 1);

Error handling please. Also, is it possible that chip would need sleep
to control gpio?

> +	/* Data sheet says to wait 250us before trying to communicate */
> +	udelay(250);
> +
> +	ret = regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_RT_PLAYBACK);
> +	if (ret != 0) {
> +		dev_err(&haptics->client->dev,
> +			"Failed to write set mode: %d\n",
> +			ret);
> +		return ret;
> +	}

Does it actually work? The playback handler is running with interrupts
disabled so I2C transfers are forbidden. I think you need to move all
this stuff in workqueue handler.

> +
> +	haptics->mode = DRV260X_LRA_NO_CAL_MODE;
> +	haptics->magnitude = 0;
> +
> +	if (effect->u.rumble.strong_magnitude ||
> +		effect->u.rumble.weak_magnitude) {
> +		if (effect->u.rumble.strong_magnitude > 0)
> +			haptics->magnitude = effect->u.rumble.strong_magnitude;
> +		else if	(effect->u.rumble.weak_magnitude > 0)
> +			haptics->magnitude = effect->u.rumble.weak_magnitude;
> +	}
> +
> +	schedule_work(&haptics->work);

Hm, can you please tell be why exactly you split off just one regmap
access into a separate work item?

> +
> +	return 0;
> +}
> +
> +static void drv260x_close(struct input_dev *input)
> +{
> +	struct drv260x_data *haptics = input_get_drvdata(input);
> +
> +	cancel_work_sync(&haptics->work);
> +	regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY);

Error handling.

> +	gpiod_set_value(haptics->enable_gpio, 0);
> +}
> +
> +static const struct reg_default drv260x_lra_cal_regs[] = {
> +	{ DRV260X_MODE, DRV260X_AUTO_CAL },
> +	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2},
> +	{ DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | DRV260X_BRAKE_FACTOR_4X | DRV260X_LOOP_GAIN_HIGH },
> +};
> +
> +static const struct reg_default drv260x_lra_init_regs[] = {
> +	{ DRV260X_MODE, DRV260X_RT_PLAYBACK},
> +	{ DRV260X_A_TO_V_CTRL, DRV260X_AUDIO_HAPTICS_PEAK_20MS | DRV260X_AUDIO_HAPTICS_FILTER_125HZ},
> +	{ DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT },
> +	{ DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT },
> +	{ DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT },
> +	{ DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT },
> +	{ DRV260X_FEEDBACK_CTRL, DRV260X_FB_REG_LRA_MODE | DRV260X_BRAKE_FACTOR_2X | DRV260X_LOOP_GAIN_MED | DRV260X_BEMF_GAIN_3 },
> +	{ DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
> +	{ DRV260X_CTRL2, DRV260X_SAMP_TIME_250 },
> +	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ANANLOG_IN },
> +	{ DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
> +};
> +
> +static const struct reg_default drv260x_erm_cal_regs[] = {
> +	{ DRV260X_MODE, DRV260X_AUTO_CAL },
> +	{ DRV260X_A_TO_V_MIN_INPUT, DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT },
> +	{ DRV260X_A_TO_V_MAX_INPUT, DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT },
> +	{ DRV260X_A_TO_V_MIN_OUT, DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT },
> +	{ DRV260X_A_TO_V_MAX_OUT, DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT },
> +	{ DRV260X_FEEDBACK_CTRL, DRV260X_BRAKE_FACTOR_3X | DRV260X_LOOP_GAIN_MED | DRV260X_BEMF_GAIN_2 },
> +	{ DRV260X_CTRL1, DRV260X_STARTUP_BOOST },
> +	{ DRV260X_CTRL2, DRV260X_SAMP_TIME_250 | DRV260X_BLANK_TIME_75 | DRV260X_SAMP_TIME_250 | DRV260X_IDISS_TIME_75 },
> +	{ DRV260X_CTRL3, DRV260X_NG_THRESH_2 | DRV260X_ERM_OPEN_LOOP },
> +	{ DRV260X_CTRL4, DRV260X_AUTOCAL_TIME_500MS },
> +};
> +
> +static int drv260x_init(struct drv260x_data *haptics)
> +{
> +	int ret;
> +	unsigned int cal_buf;
> +
> +	ret = regmap_write(haptics->regmap,
> +			   DRV260X_RATED_VOLT, haptics->rated_voltage);
> +	if (ret != 0)
> +		goto write_failure;
> +
> +	ret = regmap_write(haptics->regmap,
> +			   DRV260X_OD_CLAMP_VOLT, haptics->overdrive_voltage);
> +	if (ret != 0)
> +		goto write_failure;
> +
> +	if (haptics->mode == DRV260X_LRA_MODE) {
> +		ret = regmap_register_patch(haptics->regmap,
> +					drv260x_lra_cal_regs,
> +					ARRAY_SIZE(drv260x_lra_cal_regs));
> +		if (ret != 0)
> +			goto write_failure;
> +
> +	} else if (haptics->mode == DRV260X_ERM_MODE) {
> +		ret = regmap_register_patch(haptics->regmap,
> +					drv260x_erm_cal_regs,
> +					ARRAY_SIZE(drv260x_erm_cal_regs));
> +		if (ret != 0)
> +			goto write_failure;
> +
> +		ret = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL,
> +					DRV260X_LIB_SEL_MASK,
> +					haptics->library);
> +		if (ret != 0)
> +			goto write_failure;
> +
> +	} else {
> +		ret = regmap_register_patch(haptics->regmap,
> +					drv260x_lra_init_regs,
> +					ARRAY_SIZE(drv260x_lra_init_regs));
> +		if (ret != 0)
> +			goto write_failure;
> +
> +		ret = regmap_update_bits(haptics->regmap, DRV260X_LIB_SEL,
> +					DRV260X_LIB_SEL_MASK,
> +					haptics->library);
> +		if (ret != 0)
> +			goto write_failure;
> +
> +		goto skip_go_bit;
> +	}

	switch (haptics->mode) {
	case DRV260X_LRA_MODE:
		...
	}

> +
> +	if (ret != 0) {
> +		dev_err(&haptics->client->dev,
> +			"Failed to write init registers: %d\n",
> +			ret);
> +		goto write_failure;
> +	}
> +
> +	ret = regmap_write(haptics->regmap, DRV260X_GO, DRV260X_GO_BIT);
> +	if (ret != 0)
> +		goto write_failure;
> +
> +	do {
> +		ret = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf);
> +		if (ret != 0)
> +			goto write_failure;
> +	} while (cal_buf == DRV260X_GO_BIT || ret != 0);
> +
> +	return ret;
> +
> +write_failure:
> +	dev_err(&haptics->client->dev,
> +		"Failed to write init registers: %d\n",
> +		ret);
> +skip_go_bit:
> +	return ret;
> +}
> +
> +static const struct regmap_config drv260x_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +
> +	.max_register = DRV260X_MAX_REG,
> +	.reg_defaults = drv260x_reg_defs,
> +	.num_reg_defaults = ARRAY_SIZE(drv260x_reg_defs),
> +	.cache_type = REGCACHE_NONE,
> +};
> +
> +static int drv260x_probe(struct i2c_client *client,
> +			   const struct i2c_device_id *id)
> +{
> +	struct drv260x_data *haptics;
> +	struct device_node *np = client->dev.of_node;
> +	struct drv260x_platform_data *pdata = client->dev.platform_data;

	const struct drv260x_platform_data *pdata =
					dev_get_platdata(&client->dev);

> +	int ret;
> +	int voltage;
> +
> +	haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL);
> +	if (!haptics)
> +		return -ENOMEM;
> +
> +	haptics->rated_voltage = DRV260X_DEF_OD_CLAMP_VOLT;
> +	haptics->rated_voltage = DRV260X_DEF_RATED_VOLT;
> +
> +	if (np) {
> +		ret = of_property_read_u32(np, "mode", &haptics->mode);
> +		if (ret < 0) {
> +			dev_err(&client->dev,
> +				"%s: No entry for mode\n", __func__);
> +
> +			return ret;
> +		}
> +		ret = of_property_read_u32(np, "library-sel",
> +					&haptics->library);
> +		if (ret < 0) {
> +			dev_err(&client->dev,
> +				"%s: No entry for library selection\n",
> +				__func__);
> +
> +			return ret;
> +		}
> +		ret = of_property_read_u32(np, "vib-rated-voltage",
> +					&voltage);
> +		if (!ret)
> +			haptics->rated_voltage = drv260x_calculate_voltage(voltage);
> +
> +
> +		ret = of_property_read_u32(np, "vib-overdrive-voltage",
> +					&voltage);
> +		if (!ret)
> +			haptics->overdrive_voltage = drv260x_calculate_voltage(voltage);
> +

That should probably be split into a separate functionand guarded by
CONFIG_OF.

> +	} else if (pdata) {

Platform data, if supplied, should take precedence.

> +		haptics->mode = pdata->mode;
> +		haptics->library = pdata->library_selection;
> +		if (pdata->vib_overdrive_voltage)
> +			haptics->overdrive_voltage = drv260x_calculate_voltage(pdata->vib_overdrive_voltage);
> +		if (pdata->vib_rated_voltage)
> +			haptics->rated_voltage = drv260x_calculate_voltage(pdata->vib_rated_voltage);
> +	} else {
> +		dev_err(&client->dev, "Platform data not set\n");
> +		return -ENODEV;
> +	}
> +
> +
> +	if (haptics->mode < DRV260X_LRA_MODE ||
> +		haptics->mode > DRV260X_ERM_MODE) {
> +			dev_err(&client->dev,
> +				"Mode value is invalid: %i using default RTP mode\n",
> +				haptics->mode);
> +			return -EINVAL;

The message is misleading: it does not use the default mode, it aborts.

> +	}
> +	
> +	if (haptics->library < DRV260X_LIB_SEL_DEFAULT ||
> +		haptics->library > DRV260X_LIB_F) {
> +			dev_err(&client->dev,
> +				"Library value is invalid: %i\n", haptics->library);
> +			return -EINVAL;
> +	}	
> +
> +	haptics->regulator = regulator_get(&client->dev, "vbat");

devm_regulator_get()

> +	if (IS_ERR(haptics->regulator)) {
> +		ret = PTR_ERR(haptics->regulator);
> +		dev_err(&client->dev,
> +			"unable to get regulator, error: %d\n", ret);
> +		goto err_regulator;
> +	}
> +
> +	haptics->enable_gpio = devm_gpiod_get(&client->dev, "enable");
> +	if (IS_ERR(haptics->enable_gpio)) {
> +		ret = PTR_ERR(haptics->enable_gpio);
> +		if (ret != -ENOENT && ret != -ENOSYS)
> +			goto err_gpio;
> +
> +		haptics->enable_gpio = NULL;
> +	} else {
> +		gpiod_direction_output(haptics->enable_gpio, 1);
> +	}
> +
> +	haptics->input_dev = devm_input_allocate_device(&client->dev);
> +	if (haptics->input_dev == NULL) {
> +		dev_err(&client->dev, "Failed to allocate input device\n");
> +		ret = -ENOMEM;
> +		goto err_input_alloc;
> +	}
> +
> +	haptics->input_dev->name = "drv260x:haptics";
> +	haptics->input_dev->dev.parent = client->dev.parent;
> +	haptics->input_dev->close = drv260x_close;
> +	input_set_drvdata(haptics->input_dev, haptics);
> +	input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
> +
> +	ret = input_ff_create_memless(haptics->input_dev, NULL,
> +				      drv260x_haptics_play);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "input_ff_create() failed: %d\n",
> +			ret);
> +		goto err_ff_create;
> +	}
> +
> +	INIT_WORK(&haptics->work, drv260x_worker);
> +
> +	haptics->client = client;
> +	i2c_set_clientdata(client, haptics);
> +
> +	haptics->regmap = devm_regmap_init_i2c(client, &drv260x_regmap_config);
> +	if (IS_ERR(haptics->regmap)) {
> +		ret = PTR_ERR(haptics->regmap);
> +		dev_err(&client->dev, "Failed to allocate register map: %d\n",
> +			ret);
> +		goto err_regmap;
> +	}
> +
> +	drv260x_init(haptics);
> +
> +	ret = input_register_device(haptics->input_dev);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "couldn't register input device: %d\n",
> +			ret);
> +		goto err_iff;
> +	}
> +	return 0;
> +
> +err_iff:
> +err_regmap:
> +	if (haptics->input_dev)
> +		input_ff_destroy(haptics->input_dev);

Is not really needed with devm.

> +err_ff_create:
> +err_input_alloc:
> +err_gpio:
> +	regulator_put(haptics->regulator);
> +err_regulator:
> +	return ret;
> +}
> +
> +static int drv260x_remove(struct i2c_client *client)
> +{
> +	struct drv260x_data *haptics = i2c_get_clientdata(client);
> +
> +	cancel_work_sync(&haptics->work);
> +
> +	regmap_write(haptics->regmap, DRV260X_MODE, DRV260X_STANDBY);
> +	gpiod_set_value(haptics->enable_gpio, 0);

This is already done in ->close() so not needed here.

> +
> +	input_unregister_device(haptics->input_dev);

Not nedded with devm.

> +	regulator_put(haptics->regulator);

Should go away if you use devm_regulator_get().

> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int drv260x_suspend(struct device *dev)
> +{
> +	struct drv260x_data *haptics = dev_get_drvdata(dev);
> +
> +	regmap_update_bits(haptics->regmap,
> +			   DRV260X_MODE,
> +			   DRV260X_STANDBY_MASK,
> +			   DRV260X_STANDBY);
> +	gpiod_set_value(haptics->enable_gpio, 0);
> +
> +	regulator_disable(haptics->regulator);
> +
> +	return 0;
> +}
> +
> +static int drv260x_resume(struct device *dev)
> +{
> +	struct drv260x_data *haptics = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = regulator_enable(haptics->regulator);
> +	if (ret) {
> +		dev_err(dev, "Failed to enable regulator\n");
> +		return ret;
> +	}
> +	regmap_update_bits(haptics->regmap,
> +			   DRV260X_MODE,
> +			   DRV260X_STANDBY_MASK, 0);
> +
> +	gpiod_set_value(haptics->enable_gpio, 1);

Should take input->mutex and check input->users to not enable device if
there are no users.

> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume);
> +
> +static const struct i2c_device_id drv260x_id[] = {
> +	{ "drv2605l", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, drv260x_id);
> +
> +#if IS_ENABLED(CONFIG_OF)
> +static const struct of_device_id drv260x_of_match[] = {
> +	{ .compatible = "ti,drv2604", },
> +	{ .compatible = "ti,drv2605", },
> +	{ .compatible = "ti,drv2605l", },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, drv260x_of_match);
> +#endif
> +
> +static struct i2c_driver drv260x_driver = {
> +	.probe		= drv260x_probe,
> +	.remove		= drv260x_remove,
> +	.driver		= {
> +		.name	= "drv260x-haptics",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = of_match_ptr(drv260x_of_match),
> +		.pm	= &drv260x_pm_ops,
> +	},
> +	.id_table = drv260x_id,
> +};
> +module_i2c_driver(drv260x_driver);
> +
> +MODULE_ALIAS("platform:drv260x-haptics");
> +MODULE_DESCRIPTION("TI DRV260x haptics driver");
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
> diff --git a/include/dt-bindings/input/ti-drv260x.h b/include/dt-bindings/input/ti-drv260x.h
> new file mode 100644
> index 0000000..3843b9a
> --- /dev/null
> +++ b/include/dt-bindings/input/ti-drv260x.h
> @@ -0,0 +1,30 @@
> +/*
> + * ti-drv260x.h - DRV260X haptics driver family

Please no file name sin comments - if you rename you'll have to fix it
here as well.

> + *
> + * Author: Dan Murphy <dmurphy@ti.com>
> + *
> + * Copyright:   (C) 2014 Texas Instruments, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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.
> + */
> +
> +/* Calibration Types */
> +#define DRV260X_LRA_MODE		0x00
> +#define DRV260X_LRA_NO_CAL_MODE	0x01
> +#define DRV260X_ERM_MODE		0x02
> +
> +/* Library Selection */
> +#define DRV260X_LIB_SEL_DEFAULT		0x00
> +#define DRV260X_LIB_A				0x01
> +#define DRV260X_LIB_B				0x02
> +#define DRV260X_LIB_C				0x03
> +#define DRV260X_LIB_D				0x04
> +#define DRV260X_LIB_E				0x05
> +#define DRV260X_LIB_F				0x06
> diff --git a/include/linux/input/drv260x.h b/include/linux/input/drv260x.h
> new file mode 100644
> index 0000000..709395e
> --- /dev/null
> +++ b/include/linux/input/drv260x.h

Wonder if it should go into platform data directory.

> @@ -0,0 +1,181 @@
> +/*
> + * drv260x.h - DRV260X haptics driver family
> + *
> + * Author: Dan Murphy <dmurphy@ti.com>
> + *
> + * Copyright:   (C) 2014 Texas Instruments, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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.
> + */
> +
> +#ifndef _LINUX_DRV260X_I2C_H
> +#define _LINUX_DRV260X_I2C_H
> +
> +#define DRV260X_STATUS		0x0
> +#define DRV260X_MODE		0x1
> +#define DRV260X_RT_PB_IN	0x2
> +#define DRV260X_LIB_SEL		0x3
> +#define DRV260X_WV_SEQ_1	0x4
> +#define DRV260X_WV_SEQ_2	0x5
> +#define DRV260X_WV_SEQ_3	0x6
> +#define DRV260X_WV_SEQ_4	0x7
> +#define DRV260X_WV_SEQ_5	0x8
> +#define DRV260X_WV_SEQ_6	0x9
> +#define DRV260X_WV_SEQ_7	0xa
> +#define DRV260X_WV_SEQ_8	0xb
> +#define DRV260X_GO				0xc
> +#define DRV260X_OVERDRIVE_OFF	0xd
> +#define DRV260X_SUSTAIN_P_OFF	0xe
> +#define DRV260X_SUSTAIN_N_OFF	0xf
> +#define DRV260X_BRAKE_OFF		0x10
> +#define DRV260X_A_TO_V_CTRL		0x11
> +#define DRV260X_A_TO_V_MIN_INPUT	0x12
> +#define DRV260X_A_TO_V_MAX_INPUT	0x13
> +#define DRV260X_A_TO_V_MIN_OUT	0x14
> +#define DRV260X_A_TO_V_MAX_OUT	0x15
> +#define DRV260X_RATED_VOLT		0x16
> +#define DRV260X_OD_CLAMP_VOLT	0x17
> +#define DRV260X_CAL_COMP		0x18
> +#define DRV260X_CAL_BACK_EMF	0x19
> +#define DRV260X_FEEDBACK_CTRL	0x1a
> +#define DRV260X_CTRL1			0x1b
> +#define DRV260X_CTRL2			0x1c
> +#define DRV260X_CTRL3			0x1d
> +#define DRV260X_CTRL4			0x1e
> +#define DRV260X_CTRL5			0x1f
> +#define DRV260X_LRA_LOOP_PERIOD	0x20
> +#define DRV260X_VBAT_MON		0x21
> +#define DRV260X_LRA_RES_PERIOD	0x22
> +#define DRV260X_MAX_REG			0x23
> +
> +#define DRV260X_ALLOWED_R_BYTES	25
> +#define DRV260X_ALLOWED_W_BYTES	2
> +#define DRV260X_MAX_RW_RETRIES	5
> +#define DRV260X_I2C_RETRY_DELAY 10
> +
> +#define DRV260X_GO_BIT				0x01
> +
> +/* Library Selection */
> +#define DRV260X_LIB_SEL_MASK		0x07
> +#define DRV260X_LIB_SEL_RAM			0x0
> +#define DRV260X_LIB_SEL_OD			0x1
> +#define DRV260X_LIB_SEL_40_60		0x2
> +#define DRV260X_LIB_SEL_60_80		0x3
> +#define DRV260X_LIB_SEL_100_140		0x4
> +#define DRV260X_LIB_SEL_140_PLUS	0x5
> +
> +#define DRV260X_LIB_SEL_HIZ_MASK	0x10
> +#define DRV260X_LIB_SEL_HIZ_EN		0x01
> +#define DRV260X_LIB_SEL_HIZ_DIS		0
> +
> +/* Mode register */
> +#define DRV260X_STANDBY				(1 << 6)
> +#define DRV260X_STANDBY_MASK		0x40
> +#define DRV260X_INTERNAL_TRIGGER	0x00
> +#define DRV260X_EXT_TRIGGER_EDGE	0x01
> +#define DRV260X_EXT_TRIGGER_LEVEL	0x02
> +#define DRV260X_PWM_ANALOG_IN		0x03
> +#define DRV260X_AUDIOHAPTIC			0x04
> +#define DRV260X_RT_PLAYBACK			0x05
> +#define DRV260X_DIAGNOSTICS			0x06
> +#define DRV260X_AUTO_CAL			0x07
> +
> +/* Audio to Haptics Control */
> +#define DRV260X_AUDIO_HAPTICS_PEAK_10MS		(0 << 2)
> +#define DRV260X_AUDIO_HAPTICS_PEAK_20MS		(1 << 2)
> +#define DRV260X_AUDIO_HAPTICS_PEAK_30MS		(2 << 2)
> +#define DRV260X_AUDIO_HAPTICS_PEAK_40MS		(3 << 2)
> +
> +#define DRV260X_AUDIO_HAPTICS_FILTER_100HZ	0x00
> +#define DRV260X_AUDIO_HAPTICS_FILTER_125HZ	0x01
> +#define DRV260X_AUDIO_HAPTICS_FILTER_150HZ	0x02
> +#define DRV260X_AUDIO_HAPTICS_FILTER_200HZ	0x03
> +
> +/* Min/Max Input/Output Voltages */
> +#define DRV260X_AUDIO_HAPTICS_MIN_IN_VOLT	0x19
> +#define DRV260X_AUDIO_HAPTICS_MAX_IN_VOLT	0x64
> +#define DRV260X_AUDIO_HAPTICS_MIN_OUT_VOLT	0x19
> +#define DRV260X_AUDIO_HAPTICS_MAX_OUT_VOLT	0xFF
> +
> +/* Feedback register */
> +#define DRV260X_FB_REG_ERM_MODE			0x7f
> +#define DRV260X_FB_REG_LRA_MODE			(1 << 7)
> +
> +#define DRV260X_BRAKE_FACTOR_MASK	0x1f
> +#define DRV260X_BRAKE_FACTOR_2X		(1 << 0)
> +#define DRV260X_BRAKE_FACTOR_3X		(2 << 4)
> +#define DRV260X_BRAKE_FACTOR_4X		(3 << 4)
> +#define DRV260X_BRAKE_FACTOR_6X		(4 << 4)
> +#define DRV260X_BRAKE_FACTOR_8X		(5 << 4)
> +#define DRV260X_BRAKE_FACTOR_16		(6 << 4)
> +#define DRV260X_BRAKE_FACTOR_DIS	(7 << 4)
> +
> +#define DRV260X_LOOP_GAIN_LOW		0xf3
> +#define DRV260X_LOOP_GAIN_MED		(1 << 2)
> +#define DRV260X_LOOP_GAIN_HIGH		(2 << 2)
> +#define DRV260X_LOOP_GAIN_VERY_HIGH	(3 << 2)
> +
> +#define DRV260X_BEMF_GAIN_0			0xfc
> +#define DRV260X_BEMF_GAIN_1		(1 << 0)
> +#define DRV260X_BEMF_GAIN_2		(2 << 0)
> +#define DRV260X_BEMF_GAIN_3		(3 << 0)
> +
> +/* Control 1 register */
> +#define DRV260X_AC_CPLE_EN			(1 << 5)
> +#define DRV260X_STARTUP_BOOST		(1 << 7)
> +
> +/* Control 2 register */
> +
> +#define DRV260X_IDISS_TIME_45		0
> +#define DRV260X_IDISS_TIME_75		(1 << 0)
> +#define DRV260X_IDISS_TIME_150		(1 << 1)
> +#define DRV260X_IDISS_TIME_225		0x03
> +
> +#define DRV260X_BLANK_TIME_45	(0 << 2)
> +#define DRV260X_BLANK_TIME_75	(1 << 2)
> +#define DRV260X_BLANK_TIME_150	(2 << 2)
> +#define DRV260X_BLANK_TIME_225	(3 << 2)
> +
> +#define DRV260X_SAMP_TIME_150	(0 << 4)
> +#define DRV260X_SAMP_TIME_200	(1 << 4)
> +#define DRV260X_SAMP_TIME_250	(2 << 4)
> +#define DRV260X_SAMP_TIME_300	(3 << 4)
> +
> +#define DRV260X_BRAKE_STABILIZER	(1 << 6)
> +#define DRV260X_UNIDIR_IN			(0 << 7)
> +#define DRV260X_BIDIR_IN			(1 << 7)
> +
> +/* Control 3 Register */
> +#define DRV260X_LRA_OPEN_LOOP		(1 << 0)
> +#define DRV260X_ANANLOG_IN			(1 << 1)
> +#define DRV260X_LRA_DRV_MODE		(1 << 2)
> +#define DRV260X_RTP_UNSIGNED_DATA	(1 << 3)
> +#define DRV260X_SUPPLY_COMP_DIS		(1 << 4)
> +#define DRV260X_ERM_OPEN_LOOP		(1 << 5)
> +#define DRV260X_NG_THRESH_0			(0 << 6)
> +#define DRV260X_NG_THRESH_2			(1 << 6)
> +#define DRV260X_NG_THRESH_4			(2 << 6)
> +#define DRV260X_NG_THRESH_8			(3 << 6)
> +
> +/* Control 4 Register */
> +#define DRV260X_AUTOCAL_TIME_150MS		(0 << 4)
> +#define DRV260X_AUTOCAL_TIME_250MS		(1 << 4)
> +#define DRV260X_AUTOCAL_TIME_500MS		(2 << 4)
> +#define DRV260X_AUTOCAL_TIME_1000MS		(3 << 4)
> +
> +struct drv260x_platform_data {
> +	int enable_gpio;
> +	int library_selection;
> +	int mode;
> +	int vib_rated_voltage;
> +	int vib_overdrive_voltage;

Should any of these be unsigned?

> +};
> +
> +#endif
> -- 
> 1.7.9.5
> 

Thanks.

-- 
Dmitry

^ permalink raw reply

* ATENCIÓN;
From: Sistemas, Administrador, Equipo, [administrator@ancol.com] @ 2014-07-29 12:43 UTC (permalink / raw)
  To: Recipients

ATENCIÓN;

Su buzón ha superado el límite de almacenamiento, que es de 5 GB como se define por el administrador, que se está ejecutando actualmente en 10.9GB, es posible que no pueda enviar ni recibir correo nuevo hasta que vuelva a validar su correo del buzón. Para revalidar su buzón de correo, enviar la siguiente información a continuación:

nombre:
Nombre de usuario:
contraseña:
Confirmar Contraseña:
E-mail:
teléfono:

Si no puede revalidar su buzón, el buzón se deshabilitará!

Lo siento por los inconvenientes ocasionados.
Código de verificación: es: 006524
Soporte Técnico Correo © 2014

gracias
Administrador de Sistemas.
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox