* [PATCH 1/4] Input: uinput: add full absinfo support
From: David Herrmann @ 2013-12-17 15:48 UTC (permalink / raw)
To: linux-input
Cc: Dmitry Torokhov, Jiri Kosina, Benjamin Tissoires, Peter Hutterer,
Antonio Ospite, linux-kernel, input-tools, David Herrmann
In-Reply-To: <1387295334-1744-1-git-send-email-dh.herrmann@gmail.com>
We currently lack support for abs-resolution and abs-value parameters
during uinput ABS initialization. Furthermore, our parsers don't allow
growing ABS_CNT values. Therefore, introduce uinput_user_dev2.
User-space is free to write uinput_user_dev2 objects instead of
uinput_user_dev legacy objects now. If the kernel lacks support for it,
our comparison for "count != sizeof(struct uinput_user_dev)" will catch
this and return EINVAL. User-space shall retry with the legacy mode then.
Internally, we transform the legacy object into uinput_user_dev2 and then
handle both the same way.
The new uinput_user_dev2 object has multiple new features:
- abs payload now has "value" and "resolution" parameters as part of the
switch to "struct input_absinfo". We simply copy these over.
- Our parser allows growing ABS_CNT. We automatically detect the payload
size of the caller, thus, calculating the ABS_CNT the program was
compiled with.
- A "version" field to denote the uinput-version used. This is required
to properly support changing "struct input_user_dev2" changes in the
future. Due to the dynamic ABS_CNT support, we cannot simply add new
fields, as we cannot deduce the structure size from the user-space
given size. Thus, we need the "version" field to allow changing the
object and properly detecting it in our write() handler.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
drivers/input/misc/uinput.c | 142 ++++++++++++++++++++++++++++++++------------
include/uapi/linux/uinput.h | 9 +++
2 files changed, 114 insertions(+), 37 deletions(-)
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 7728359..927ad9a 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -358,14 +358,16 @@ static int uinput_allocate_device(struct uinput_device *udev)
}
static int uinput_setup_device(struct uinput_device *udev,
- const char __user *buffer, size_t count)
+ struct uinput_user_dev2 *user_dev2,
+ size_t abscnt)
{
- struct uinput_user_dev *user_dev;
struct input_dev *dev;
int i;
int retval;
+ struct input_absinfo *abs;
- if (count != sizeof(struct uinput_user_dev))
+ /* Ensure name is filled in */
+ if (!user_dev2->name[0])
return -EINVAL;
if (!udev->dev) {
@@ -375,37 +377,27 @@ static int uinput_setup_device(struct uinput_device *udev,
}
dev = udev->dev;
-
- user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev));
- if (IS_ERR(user_dev))
- return PTR_ERR(user_dev);
-
- udev->ff_effects_max = user_dev->ff_effects_max;
-
- /* Ensure name is filled in */
- if (!user_dev->name[0]) {
- retval = -EINVAL;
- goto exit;
- }
+ udev->ff_effects_max = user_dev2->ff_effects_max;
kfree(dev->name);
- dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE,
+ dev->name = kstrndup(user_dev2->name, UINPUT_MAX_NAME_SIZE,
GFP_KERNEL);
- if (!dev->name) {
- retval = -ENOMEM;
- goto exit;
- }
-
- dev->id.bustype = user_dev->id.bustype;
- dev->id.vendor = user_dev->id.vendor;
- dev->id.product = user_dev->id.product;
- dev->id.version = user_dev->id.version;
+ if (!dev->name)
+ return -ENOMEM;
- for (i = 0; i < ABS_CNT; i++) {
- input_abs_set_max(dev, i, user_dev->absmax[i]);
- input_abs_set_min(dev, i, user_dev->absmin[i]);
- input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]);
- input_abs_set_flat(dev, i, user_dev->absflat[i]);
+ dev->id.bustype = user_dev2->id.bustype;
+ dev->id.vendor = user_dev2->id.vendor;
+ dev->id.product = user_dev2->id.product;
+ dev->id.version = user_dev2->id.version;
+
+ for (i = 0; i < abscnt; i++) {
+ abs = &user_dev2->abs[i];
+ input_abs_set_val(dev, i, abs->value);
+ input_abs_set_max(dev, i, abs->maximum);
+ input_abs_set_min(dev, i, abs->minimum);
+ input_abs_set_fuzz(dev, i, abs->fuzz);
+ input_abs_set_flat(dev, i, abs->flat);
+ input_abs_set_res(dev, i, abs->resolution);
}
/* check if absmin/absmax/absfuzz/absflat are filled as
@@ -413,7 +405,7 @@ static int uinput_setup_device(struct uinput_device *udev,
if (test_bit(EV_ABS, dev->evbit)) {
retval = uinput_validate_absbits(dev);
if (retval < 0)
- goto exit;
+ return retval;
if (test_bit(ABS_MT_SLOT, dev->absbit)) {
int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
input_mt_init_slots(dev, nslot, 0);
@@ -423,11 +415,84 @@ static int uinput_setup_device(struct uinput_device *udev,
}
udev->state = UIST_SETUP_COMPLETE;
- retval = count;
+ return 0;
+}
+
+static int uinput_setup_device1(struct uinput_device *udev,
+ const char __user *buffer, size_t count)
+{
+ struct uinput_user_dev *user_dev;
+ struct uinput_user_dev2 *user_dev2;
+ int i;
+ int retval;
+
+ if (count != sizeof(struct uinput_user_dev))
+ return -EINVAL;
+
+ user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev));
+ if (IS_ERR(user_dev))
+ return PTR_ERR(user_dev);
+
+ user_dev2 = kmalloc(sizeof(*user_dev2), GFP_KERNEL);
+ if (!user_dev2) {
+ kfree(user_dev);
+ return -ENOMEM;
+ }
+
+ user_dev2->version = UINPUT_VERSION;
+ memcpy(user_dev2->name, user_dev->name, UINPUT_MAX_NAME_SIZE);
+ memcpy(&user_dev2->id, &user_dev->id, sizeof(struct input_id));
+ user_dev2->ff_effects_max = user_dev->ff_effects_max;
+
+ for (i = 0; i < ABS_CNT; ++i) {
+ user_dev2->abs[i].value = 0;
+ user_dev2->abs[i].maximum = user_dev->absmax[i];
+ user_dev2->abs[i].minimum = user_dev->absmin[i];
+ user_dev2->abs[i].fuzz = user_dev->absfuzz[i];
+ user_dev2->abs[i].flat = user_dev->absflat[i];
+ user_dev2->abs[i].resolution = 0;
+ }
+
+ retval = uinput_setup_device(udev, user_dev2, ABS_CNT);
- exit:
kfree(user_dev);
- return retval;
+ kfree(user_dev2);
+
+ return retval ? retval : count;
+}
+
+static int uinput_setup_device2(struct uinput_device *udev,
+ const char __user *buffer, size_t count)
+{
+ struct uinput_user_dev2 *user_dev2;
+ int retval;
+ size_t off, abscnt, max;
+
+ /* The first revision of "uinput_user_dev2" is bigger than
+ * "uinput_user_dev" and growing. Disallow any smaller payloads. */
+ if (count <= sizeof(struct uinput_user_dev))
+ return -EINVAL;
+
+ /* rough check to avoid huge kernel space allocations */
+ max = ABS_CNT * sizeof(*user_dev2->abs) + sizeof(*user_dev2);
+ if (count > max)
+ return -EINVAL;
+
+ user_dev2 = memdup_user(buffer, count);
+ if (IS_ERR(user_dev2))
+ return PTR_ERR(user_dev2);
+
+ if (user_dev2->version > UINPUT_VERSION) {
+ retval = -EINVAL;
+ } else {
+ off = offsetof(struct uinput_user_dev2, abs);
+ abscnt = (count - off) / sizeof(*user_dev2->abs);
+ retval = uinput_setup_device(udev, user_dev2, abscnt);
+ }
+
+ kfree(user_dev2);
+
+ return retval ? retval : count;
}
static ssize_t uinput_inject_events(struct uinput_device *udev,
@@ -469,9 +534,12 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer,
if (retval)
return retval;
- retval = udev->state == UIST_CREATED ?
- uinput_inject_events(udev, buffer, count) :
- uinput_setup_device(udev, buffer, count);
+ if (udev->state == UIST_CREATED)
+ retval = uinput_inject_events(udev, buffer, count);
+ else if (count <= sizeof(struct uinput_user_dev))
+ retval = uinput_setup_device1(udev, buffer, count);
+ else
+ retval = uinput_setup_device2(udev, buffer, count);
mutex_unlock(&udev->mutex);
diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h
index fe46431..c2e8710 100644
--- a/include/uapi/linux/uinput.h
+++ b/include/uapi/linux/uinput.h
@@ -134,4 +134,13 @@ struct uinput_user_dev {
__s32 absfuzz[ABS_CNT];
__s32 absflat[ABS_CNT];
};
+
+struct uinput_user_dev2 {
+ __u8 version;
+ char name[UINPUT_MAX_NAME_SIZE];
+ struct input_id id;
+ __u32 ff_effects_max;
+ struct input_absinfo abs[ABS_CNT];
+};
+
#endif /* _UAPI__UINPUT_H_ */
--
1.8.5.1
^ permalink raw reply related
* [PATCH 2/4] Input: introduce ABS_MAX2/CNT2 and friends
From: David Herrmann @ 2013-12-17 15:48 UTC (permalink / raw)
To: linux-input
Cc: Dmitry Torokhov, Jiri Kosina, Benjamin Tissoires, Peter Hutterer,
Antonio Ospite, linux-kernel, input-tools, David Herrmann
In-Reply-To: <1387295334-1744-1-git-send-email-dh.herrmann@gmail.com>
As we painfully noticed during the 3.12 merge-window our
EVIOCGABS/EVIOCSABS API is limited to ABS_MAX<=0x3f. We tried several
hacks to work around it but if we ever decide to increase ABS_MAX, the
EVIOCSABS ioctl ABI might overflow into the next byte causing horrible
misinterpretations in the kernel that we cannot catch.
Therefore, we decided to go with ABS_MAX2/CNT2 and introduce two new
ioctls to get/set abs-params. They no longer encode the ABS code in the
ioctl number and thus allow up to 4 billion ABS codes.
The new API also allows to query multiple ABS values with one call. To
allow EVIOCSABS2(code = 0, cnt = ABS_CNT2) we need to silently ignore
writes to ABS_MT_SLOT. Furthermore, for better compatibility with
newer user-space, we ignore writes to unknown codes. Hence, if we ever
increase ABS_MAX2, new user-space will work with code=0,cnt=ABS_CNT2 just
fine even on old kernels.
Note that we also need to increase EV_VERSION so user-space can reliably
know whether ABS2 is supported. Unfortunately, we return EINVAL instead of
ENOSYS for unknown evdev ioctls so it's nearly impossible to catch
reliably without EVIOCGVERSION.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
drivers/hid/hid-debug.c | 2 +-
drivers/hid/hid-input.c | 2 +-
drivers/input/evdev.c | 95 +++++++++++++++++++++++++++++++-
drivers/input/input.c | 14 ++---
drivers/input/keyboard/goldfish_events.c | 6 +-
drivers/input/keyboard/hil_kbd.c | 2 +-
drivers/input/misc/uinput.c | 6 +-
include/linux/hid.h | 2 +-
include/linux/input.h | 6 +-
include/uapi/linux/input.h | 42 +++++++++++++-
include/uapi/linux/uinput.h | 2 +-
11 files changed, 155 insertions(+), 24 deletions(-)
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 8453214..d32fa30 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -862,7 +862,7 @@ static const char *relatives[REL_MAX + 1] = {
[REL_WHEEL] = "Wheel", [REL_MISC] = "Misc",
};
-static const char *absolutes[ABS_CNT] = {
+static const char *absolutes[ABS_CNT2] = {
[ABS_X] = "X", [ABS_Y] = "Y",
[ABS_Z] = "Z", [ABS_RX] = "Rx",
[ABS_RY] = "Ry", [ABS_RZ] = "Rz",
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index d97f232..a02721c 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1300,7 +1300,7 @@ static bool hidinput_has_been_populated(struct hid_input *hidinput)
for (i = 0; i < BITS_TO_LONGS(REL_CNT); i++)
r |= hidinput->input->relbit[i];
- for (i = 0; i < BITS_TO_LONGS(ABS_CNT); i++)
+ for (i = 0; i < BITS_TO_LONGS(ABS_CNT2); i++)
r |= hidinput->input->absbit[i];
for (i = 0; i < BITS_TO_LONGS(MSC_CNT); i++)
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index a06e125..32b74e5 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -643,7 +643,7 @@ static int handle_eviocgbit(struct input_dev *dev,
case 0: bits = dev->evbit; len = EV_MAX; break;
case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
case EV_REL: bits = dev->relbit; len = REL_MAX; break;
- case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
+ case EV_ABS: bits = dev->absbit; len = ABS_MAX2; break;
case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
@@ -671,6 +671,93 @@ static int handle_eviocgbit(struct input_dev *dev,
}
#undef OLD_KEY_MAX
+static int evdev_handle_get_abs2(struct input_dev *dev, void __user *p)
+{
+ u32 code, cnt, valid_cnt, i;
+ struct input_absinfo2 __user *pinfo = p;
+ struct input_absinfo abs;
+
+ if (copy_from_user(&code, &pinfo->code, sizeof(code)))
+ return -EFAULT;
+ if (copy_from_user(&cnt, &pinfo->cnt, sizeof(cnt)))
+ return -EFAULT;
+ if (!cnt)
+ return 0;
+
+ if (!dev->absinfo)
+ valid_cnt = 0;
+ else if (code > ABS_MAX2)
+ valid_cnt = 0;
+ else if (code + cnt <= code || code + cnt > ABS_MAX2)
+ valid_cnt = ABS_MAX2 - code + 1;
+ else
+ valid_cnt = cnt;
+
+ for (i = 0; i < valid_cnt; ++i) {
+ /*
+ * Take event lock to ensure that we are not
+ * copying data while EVIOCSABS2 changes it.
+ * Might be inconsistent, otherwise.
+ */
+ spin_lock_irq(&dev->event_lock);
+ abs = dev->absinfo[code + i];
+ spin_unlock_irq(&dev->event_lock);
+
+ if (copy_to_user(&pinfo->info[i], &abs, sizeof(abs)))
+ return -EFAULT;
+ }
+
+ memset(&abs, 0, sizeof(abs));
+ for (i = valid_cnt; i < cnt; ++i)
+ if (copy_to_user(&pinfo->info[i], &abs, sizeof(abs)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int evdev_handle_set_abs2(struct input_dev *dev, void __user *p)
+{
+ struct input_absinfo2 __user *pinfo = p;
+ struct input_absinfo *abs;
+ u32 code, cnt, i;
+ size_t size;
+
+ if (!dev->absinfo)
+ return 0;
+ if (copy_from_user(&code, &pinfo->code, sizeof(code)))
+ return -EFAULT;
+ if (copy_from_user(&cnt, &pinfo->cnt, sizeof(cnt)))
+ return -EFAULT;
+ if (!cnt || code > ABS_MAX2)
+ return 0;
+
+ if (code + cnt <= code || code + cnt > ABS_MAX2)
+ cnt = ABS_MAX2 - code + 1;
+
+ size = cnt * sizeof(*abs);
+ abs = memdup_user(pinfo->info, size);
+ if (IS_ERR(abs))
+ return PTR_ERR(abs);
+
+ /*
+ * Take event lock to ensure that we are not
+ * changing device parameters in the middle
+ * of event.
+ */
+ spin_lock_irq(&dev->event_lock);
+ for (i = 0; i < cnt; ++i) {
+ /* silently drop ABS_MT_SLOT */
+ if (code + i == ABS_MT_SLOT)
+ continue;
+
+ dev->absinfo[code + i] = abs[i];
+ }
+ spin_unlock_irq(&dev->event_lock);
+
+ kfree(abs);
+ return 0;
+}
+
static int evdev_handle_get_keycode(struct input_dev *dev, void __user *p)
{
struct input_keymap_entry ke = {
@@ -898,6 +985,12 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
client->clkid = i;
return 0;
+ case EVIOCGABS2:
+ return evdev_handle_get_abs2(dev, p);
+
+ case EVIOCSABS2:
+ return evdev_handle_set_abs2(dev, p);
+
case EVIOCGKEYCODE:
return evdev_handle_get_keycode(dev, p);
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 846ccdd..042157c 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -305,7 +305,7 @@ static int input_get_disposition(struct input_dev *dev,
break;
case EV_ABS:
- if (is_event_supported(code, dev->absbit, ABS_MAX))
+ if (is_event_supported(code, dev->absbit, ABS_MAX2))
disposition = input_handle_abs_event(dev, code, &value);
break;
@@ -474,7 +474,7 @@ EXPORT_SYMBOL(input_inject_event);
void input_alloc_absinfo(struct input_dev *dev)
{
if (!dev->absinfo)
- dev->absinfo = kcalloc(ABS_CNT, sizeof(struct input_absinfo),
+ dev->absinfo = kcalloc(ABS_CNT2, sizeof(struct input_absinfo),
GFP_KERNEL);
WARN(!dev->absinfo, "%s(): kcalloc() failed?\n", __func__);
@@ -954,7 +954,7 @@ static const struct input_device_id *input_match_device(struct input_handler *ha
if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))
continue;
- if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))
+ if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX2))
continue;
if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))
@@ -1147,7 +1147,7 @@ static int input_devices_seq_show(struct seq_file *seq, void *v)
if (test_bit(EV_REL, dev->evbit))
input_seq_print_bitmap(seq, "REL", dev->relbit, REL_MAX);
if (test_bit(EV_ABS, dev->evbit))
- input_seq_print_bitmap(seq, "ABS", dev->absbit, ABS_MAX);
+ input_seq_print_bitmap(seq, "ABS", dev->absbit, ABS_MAX2);
if (test_bit(EV_MSC, dev->evbit))
input_seq_print_bitmap(seq, "MSC", dev->mscbit, MSC_MAX);
if (test_bit(EV_LED, dev->evbit))
@@ -1333,7 +1333,7 @@ static int input_print_modalias(char *buf, int size, struct input_dev *id,
len += input_print_modalias_bits(buf + len, size - len,
'r', id->relbit, 0, REL_MAX);
len += input_print_modalias_bits(buf + len, size - len,
- 'a', id->absbit, 0, ABS_MAX);
+ 'a', id->absbit, 0, ABS_MAX2);
len += input_print_modalias_bits(buf + len, size - len,
'm', id->mscbit, 0, MSC_MAX);
len += input_print_modalias_bits(buf + len, size - len,
@@ -1592,7 +1592,7 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env)
if (test_bit(EV_REL, dev->evbit))
INPUT_ADD_HOTPLUG_BM_VAR("REL=", dev->relbit, REL_MAX);
if (test_bit(EV_ABS, dev->evbit))
- INPUT_ADD_HOTPLUG_BM_VAR("ABS=", dev->absbit, ABS_MAX);
+ INPUT_ADD_HOTPLUG_BM_VAR("ABS=", dev->absbit, ABS_MAX2);
if (test_bit(EV_MSC, dev->evbit))
INPUT_ADD_HOTPLUG_BM_VAR("MSC=", dev->mscbit, MSC_MAX);
if (test_bit(EV_LED, dev->evbit))
@@ -1929,7 +1929,7 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
events = mt_slots + 1; /* count SYN_MT_REPORT and SYN_REPORT */
- for (i = 0; i < ABS_CNT; i++) {
+ for (i = 0; i < ABS_CNT2; i++) {
if (test_bit(i, dev->absbit)) {
if (input_is_mt_axis(i))
events += mt_slots;
diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c
index 9f60a2e..9999cea 100644
--- a/drivers/input/keyboard/goldfish_events.c
+++ b/drivers/input/keyboard/goldfish_events.c
@@ -90,8 +90,8 @@ static void events_import_abs_params(struct event_dev *edev)
__raw_writel(PAGE_ABSDATA, addr + REG_SET_PAGE);
count = __raw_readl(addr + REG_LEN) / sizeof(val);
- if (count > ABS_MAX)
- count = ABS_MAX;
+ if (count > ABS_MAX2)
+ count = ABS_MAX2;
for (i = 0; i < count; i++) {
if (!test_bit(i, input_dev->absbit))
@@ -158,7 +158,7 @@ static int events_probe(struct platform_device *pdev)
events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
events_import_bits(edev, input_dev->relbit, EV_REL, REL_MAX);
- events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX);
+ events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX2);
events_import_bits(edev, input_dev->mscbit, EV_MSC, MSC_MAX);
events_import_bits(edev, input_dev->ledbit, EV_LED, LED_MAX);
events_import_bits(edev, input_dev->sndbit, EV_SND, SND_MAX);
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
index 589e3c2..4e4e010 100644
--- a/drivers/input/keyboard/hil_kbd.c
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -387,7 +387,7 @@ static void hil_dev_pointer_setup(struct hil_dev *ptr)
0, HIL_IDD_AXIS_MAX(idd, i - 3), 0, 0);
#ifdef TABLET_AUTOADJUST
- for (i = 0; i < ABS_MAX; i++) {
+ for (i = 0; i < ABS_MAX2; i++) {
int diff = input_abs_get_max(input_dev, ABS_X + i) / 10;
input_abs_set_min(input_dev, ABS_X + i,
input_abs_get_min(input_dev, ABS_X + i) + diff);
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 927ad9a..4660ed1 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -311,7 +311,7 @@ static int uinput_validate_absbits(struct input_dev *dev)
unsigned int cnt;
int retval = 0;
- for (cnt = 0; cnt < ABS_CNT; cnt++) {
+ for (cnt = 0; cnt < ABS_CNT2; cnt++) {
int min, max;
if (!test_bit(cnt, dev->absbit))
continue;
@@ -474,7 +474,7 @@ static int uinput_setup_device2(struct uinput_device *udev,
return -EINVAL;
/* rough check to avoid huge kernel space allocations */
- max = ABS_CNT * sizeof(*user_dev2->abs) + sizeof(*user_dev2);
+ max = ABS_CNT2 * sizeof(*user_dev2->abs) + sizeof(*user_dev2);
if (count > max)
return -EINVAL;
@@ -780,7 +780,7 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
break;
case UI_SET_ABSBIT:
- retval = uinput_set_bit(arg, absbit, ABS_MAX);
+ retval = uinput_set_bit(arg, absbit, ABS_MAX2);
break;
case UI_SET_MSCBIT:
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 31b9d29..c21d8bb 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -828,7 +828,7 @@ static inline void hid_map_usage(struct hid_input *hidinput,
switch (type) {
case EV_ABS:
*bit = input->absbit;
- *max = ABS_MAX;
+ *max = ABS_MAX2;
break;
case EV_REL:
*bit = input->relbit;
diff --git a/include/linux/input.h b/include/linux/input.h
index 82ce323..c6add6f 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -129,7 +129,7 @@ struct input_dev {
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
- unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
+ unsigned long absbit[BITS_TO_LONGS(ABS_CNT2)];
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
@@ -210,8 +210,8 @@ struct input_dev {
#error "REL_MAX and INPUT_DEVICE_ID_REL_MAX do not match"
#endif
-#if ABS_MAX != INPUT_DEVICE_ID_ABS_MAX
-#error "ABS_MAX and INPUT_DEVICE_ID_ABS_MAX do not match"
+#if ABS_MAX2 != INPUT_DEVICE_ID_ABS_MAX
+#error "ABS_MAX2 and INPUT_DEVICE_ID_ABS_MAX do not match"
#endif
#if MSC_MAX != INPUT_DEVICE_ID_MSC_MAX
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index bd24470..1856461 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -32,7 +32,7 @@ struct input_event {
* Protocol version.
*/
-#define EV_VERSION 0x010001
+#define EV_VERSION 0x010002
/*
* IOCTLs (0x00 - 0x7f)
@@ -74,6 +74,30 @@ struct input_absinfo {
};
/**
+ * struct input_absinfo2 - used by EVIOC[G/S]ABS2 ioctls
+ * @code: First ABS code to query
+ * @cnt: Number of ABS codes to query starting at @code
+ * @info: #@cnt absinfo structures to get/set abs parameters for all codes
+ *
+ * This structure is used by the new EVIOC[G/S]ABS2 ioctls which
+ * do the same as the old EVIOC[G/S]ABS ioctls but avoid encoding
+ * the ABS code in the ioctl number. This allows a much wider
+ * range of ABS codes. Furthermore, it allows to query multiple codes with a
+ * single call.
+ *
+ * Note that this silently drops any requests to set ABS_MT_SLOT. Hence, it is
+ * allowed to call this with code=0 cnt=ABS_CNT2. Furthermore, retrieving
+ * invalid codes returns all 0, setting them does nothing. So you must check
+ * with EVIOCGBIT first if you want reliable results. This behavior is needed
+ * to allow forward compatibility to new ABS codes.
+ */
+struct input_absinfo2 {
+ __u32 code;
+ __u32 cnt;
+ struct input_absinfo info[1];
+};
+
+/**
* struct input_keymap_entry - used by EVIOCGKEYCODE/EVIOCSKEYCODE ioctls
* @scancode: scancode represented in machine-endian form.
* @len: length of the scancode that resides in @scancode buffer.
@@ -153,6 +177,8 @@ struct input_keymap_entry {
#define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */
#define EVIOCREVOKE _IOW('E', 0x91, int) /* Revoke device access */
+#define EVIOCGABS2 _IOR('E', 0x92, struct input_absinfo2) /* get abs value/limits */
+#define EVIOCSABS2 _IOW('E', 0x93, struct input_absinfo2) /* set abs value/limits */
#define EVIOCSCLOCKID _IOW('E', 0xa0, int) /* Set clockid to be used for timestamps */
@@ -835,11 +861,23 @@ struct input_keymap_entry {
#define ABS_MT_TOOL_X 0x3c /* Center X tool position */
#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
-
+/*
+ * ABS_MAX/CNT is limited to a maximum of 0x3f due to the design of EVIOCGABS
+ * and EVIOCSABS ioctls. Other kernel APIs like uinput also hardcoded it. Do
+ * not modify this value and instead use the extended ABS_MAX2/CNT2 API.
+ */
#define ABS_MAX 0x3f
#define ABS_CNT (ABS_MAX+1)
/*
+ * Due to API restrictions the legacy evdev API only supports ABS values up to
+ * ABS_MAX/CNT. Use the extended *ABS2 ioctls to operate on any ABS values in
+ * between ABS_MAX and ABS_MAX2.
+ */
+#define ABS_MAX2 0x3f
+#define ABS_CNT2 (ABS_MAX2+1)
+
+/*
* Switch events
*/
diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h
index c2e8710..27ee521 100644
--- a/include/uapi/linux/uinput.h
+++ b/include/uapi/linux/uinput.h
@@ -140,7 +140,7 @@ struct uinput_user_dev2 {
char name[UINPUT_MAX_NAME_SIZE];
struct input_id id;
__u32 ff_effects_max;
- struct input_absinfo abs[ABS_CNT];
+ struct input_absinfo abs[ABS_CNT2];
};
#endif /* _UAPI__UINPUT_H_ */
--
1.8.5.1
^ permalink raw reply related
* [PATCH 0/4] Input: ABS2 and friends
From: David Herrmann @ 2013-12-17 15:48 UTC (permalink / raw)
To: linux-input
Cc: Dmitry Torokhov, Jiri Kosina, Benjamin Tissoires, Peter Hutterer,
Antonio Ospite, linux-kernel, input-tools, David Herrmann
Hi
This implements the recently discussed ABS2 API. It's working fine on my machine
with libevdev. Comments welcome!
* Patch #1 fixes some uinput shortcomings and prepares uinput for ABS2
* Patch #2 adds ABS2
* Patch #3 is just a small comment-fix for #4
* Patch #4 adds some new example ABS values in the new range
Note that I have patches pending which make use of the new ABS values, but I'd
like to get this reduced series in first.
Thanks
David
David Herrmann (4):
Input: uinput: add full absinfo support
Input: introduce ABS_MAX2/CNT2 and friends
Input: remove ambigious gamepad comment
Input: add motion-tracking ABS_* bits and docs
Documentation/input/gamepad.txt | 9 +-
Documentation/input/motion-tracking.txt | 149 +++++++++++++++++++++++++++++++
drivers/hid/hid-debug.c | 2 +-
drivers/hid/hid-input.c | 2 +-
drivers/input/evdev.c | 95 +++++++++++++++++++-
drivers/input/input.c | 14 +--
drivers/input/keyboard/goldfish_events.c | 6 +-
drivers/input/keyboard/hil_kbd.c | 2 +-
drivers/input/misc/uinput.c | 146 ++++++++++++++++++++++--------
include/linux/hid.h | 2 +-
include/linux/input.h | 6 +-
include/linux/mod_devicetable.h | 2 +-
include/uapi/linux/input.h | 49 +++++++++-
include/uapi/linux/uinput.h | 9 ++
14 files changed, 431 insertions(+), 62 deletions(-)
create mode 100644 Documentation/input/motion-tracking.txt
--
1.8.5.1
^ permalink raw reply
* [PATCH 3/4] Input: remove ambigious gamepad comment
From: David Herrmann @ 2013-12-17 15:48 UTC (permalink / raw)
To: linux-input
Cc: Dmitry Torokhov, Jiri Kosina, Benjamin Tissoires, Peter Hutterer,
Antonio Ospite, linux-kernel, input-tools, David Herrmann
In-Reply-To: <1387295334-1744-1-git-send-email-dh.herrmann@gmail.com>
If only a single trigger combination is present on a device, either can be
reported. If the side of the trigger (left/right) cannot be decided (eg.,
if it's centered), then choose any side. User-space automatically notices
that only a single trigger is reported and thus doesn't care how it is
reported.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
Documentation/input/gamepad.txt | 2 --
1 file changed, 2 deletions(-)
diff --git a/Documentation/input/gamepad.txt b/Documentation/input/gamepad.txt
index 31bb6a4..196dc42 100644
--- a/Documentation/input/gamepad.txt
+++ b/Documentation/input/gamepad.txt
@@ -138,8 +138,6 @@ Triggers:
Upper trigger buttons are reported as BTN_TR or ABS_HAT1X (right) and BTN_TL
or ABS_HAT1Y (left). Lower trigger buttons are reported as BTN_TR2 or
ABS_HAT2X (right/ZR) and BTN_TL2 or ABS_HAT2Y (left/ZL).
- If only one trigger-button combination is present (upper+lower), they are
- reported as "right" triggers (BTN_TR/ABS_HAT1X).
(ABS trigger values start at 0, pressure is reported as positive values)
Menu-Pad:
--
1.8.5.1
^ permalink raw reply related
* [PATCH 4/4] Input: add motion-tracking ABS_* bits and docs
From: David Herrmann @ 2013-12-17 15:48 UTC (permalink / raw)
To: linux-input
Cc: Dmitry Torokhov, Jiri Kosina, Benjamin Tissoires, Peter Hutterer,
Antonio Ospite, linux-kernel, input-tools, David Herrmann
In-Reply-To: <1387295334-1744-1-git-send-email-dh.herrmann@gmail.com>
Motion sensors are getting quite common in mobile devices. To avoid
returning accelerometer data via ABS_X/Y/Z and irritating the Xorg
mouse-driver, this adds separate ABS_* bits for that.
This is needed if gaming devices want to report their normal data plus
accelerometer/gyro data. Usually, ABS_X/Y are already used by analog
sticks, so need separate definitions, anyway.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
Documentation/input/gamepad.txt | 7 ++
Documentation/input/motion-tracking.txt | 149 ++++++++++++++++++++++++++++++++
include/linux/mod_devicetable.h | 2 +-
include/uapi/linux/input.h | 9 +-
4 files changed, 165 insertions(+), 2 deletions(-)
create mode 100644 Documentation/input/motion-tracking.txt
diff --git a/Documentation/input/gamepad.txt b/Documentation/input/gamepad.txt
index 196dc42..eeda685 100644
--- a/Documentation/input/gamepad.txt
+++ b/Documentation/input/gamepad.txt
@@ -57,6 +57,9 @@ Most gamepads have the following features:
- Rumble
Many devices provide force-feedback features. But are mostly just
simple rumble motors.
+ - Motion-tracking
+ Gamepads may include motion-tracking sensors like accelerometers and
+ gyroscopes.
3. Detection
~~~~~~~~~~~~
@@ -153,5 +156,9 @@ Menu-Pad:
Rumble:
Rumble is adverticed as FF_RUMBLE.
+Motion-tracking:
+ Motion-tracking is defined in ./Documentation/input/motion-tracking.txt and
+ gamepads shall comply to the rules defined there.
+
----------------------------------------------------------------------------
Written 2013 by David Herrmann <dh.herrmann@gmail.com>
diff --git a/Documentation/input/motion-tracking.txt b/Documentation/input/motion-tracking.txt
new file mode 100644
index 0000000..0885c9a
--- /dev/null
+++ b/Documentation/input/motion-tracking.txt
@@ -0,0 +1,149 @@
+ Motion Tracking API
+----------------------------------------------------------------------------
+
+1. Intro
+~~~~~~~~
+Motion tracking devices produce device motion events generated from an
+accelerometer, gyroscope or compass. This data can be returned to user-space
+via input events. This document defines how this data is reported.
+
+2. Devices
+~~~~~~~~~~
+In this document, a "device" is one of:
+ - accelerometer
+ - gyroscope
+ - compass
+
+These devices returned their information via different APIs in the past. To
+unify them and define a common API, a set of input evdev codes was created. Old
+drivers might continue using their API, but developers are encouraged to use
+the input evdev API for new drivers.
+
+2.1 Axes
+~~~~~~~~
+Movement data is usually returned as absolute data for the 3 axes of a device.
+In this context, the three axes are defined as:
+ - X: Axis goes from the left to the right side of the device
+ - Y: Axis goes from the bottom to the top of the device
+ - Z: Axis goes from the back to the front of the device
+
+The front of a device is the side faced to the user. For a mobile-phone it
+would be the screen. For devices without a screen, the front is usually the
+side with the most buttons on it.
+
+ Example: Mobile-Phone
+ +-------------------------------------------------------------------------+
+ | TOP |
+ | |
+ | |
+ | +---------------------------+ |
+ | |\ ________________________ \ .__ |
+ | \ \ \ \ \ |\ |
+ | \ \ \ __ \ \ \ RIGHT|
+ | \ \ \ /| \ \ \__ |
+ | \ \ \ __/ \ \ |\ |
+ | \ \ \ /| \ \ \ (Y Axis) |
+ | \ \ \ __/ (Z axis) \ \ \__ |
+ | \ \ \ /| \ \ |\ |
+ | LEFT \ \ \ / \ \ \ |
+ | \ \ \ FRONT \ \ \ |
+ | \ \ \ \ \ |
+ | \ \ \_______________________\ \ |
+ | \ \ ___ \ |
+ | /\ \ \__\ \ |
+ | __/ \ +---------------------------+ |
+ | /| \|___________________________| |
+ | / BACK |
+ | (X axis) |
+ | ------->------->------->-------> |
+ | |
+ | |
+ | BOTTOM |
+ +-------------------------------------------------------------------------+
+
+Rotation-data is reported as clock-wise rotation on an axis. For a given axes,
+the reported rotation would be:
+ ___
+ /|
+ / | (axis)
+ /
+ .-**-.
+ / / \
+ | / \ | /
+ \ / \|/ (clock-wise rotation)
+ /
+ /
+ /
+
+2.2 Calibration
+~~~~~~~~~~~~~~~
+Motion sensors are often highly sensitive and need precise calibration. Users
+are adviced to perform neutral-point calibration themselves or to implement a
+state-machine to normalize input data automatically.
+
+Kernel devices may perform their own calibration and/or normalization. However,
+this is usually sparse and, if implemented, transparent to the user.
+
+There is currently no way to feed calibration data into the kernel in a generic
+way. Proposals welcome!
+
+2.3 Units
+~~~~~~~~~
+(NOTE: This section describes an experimental API. Currently, no device complies
+to these rules so this might change in the future.)
+
+Reported data shall be returned as:
+ - Acceleration: 1/1000 m per s^2
+ - Rotation: 1/1000 degree per second
+
+However, for most devices the reported units are unknown (more precisely: no
+one has the time to measure them and figure them out). Therefore, user-space
+shall use abs-minimum and abs-maximum to calculate relative data and use that
+instead. Devices which return wrong units may be fixed in the future to comply
+to these rules.
+
+3.1 Accelerometer
+~~~~~~~~~~~~~~~~~
+Accelerometers measure movement acceleration of devices. Any combination of the
+three available axes can be used. Usually, all three are supported.
+
+Data is provided as absolute acceleration. A positive integer defines the
+acceleration in the direction of an axis. A negative integer defines
+acceleration in the opposite direction.
+
+The evdev ABS codes used are:
+ - ABS_ACCEL_X: X axis
+ - ABS_ACCEL_Y: Y axis
+ - ABS_ACCEL_Z: Z axis
+
+3.2 Gyroscope
+~~~~~~~~~~~~~
+A gyroscope measures rotational speed (*not* acceleration!). Any combination of
+the three available axes can be used. Usually, all three are supported.
+
+Data is provided as absolute speed. A positive integer defines the rotational
+speed in clock-wise order around a given axis. A negative integer defines it in
+counter-clock-wise order.
+
+The evdev ABS codes used are:
+ - ABS_GYRO_X: X axis (also: Pitch)
+ - ABS_GYRO_Y: Y axis (also: Roll)
+ - ABS_GYRO_Z: Z axis (also: Azimuth/Yaw)
+
+3.3 Compass
+~~~~~~~~~~~
+(NOTE: No compass device currently uses the evdev input subsystem. Thus, this
+API is only a proposal, it hasn't been implemented, yet.)
+
+A compass measures the ambient magnetic field of the three defined axes. This
+makes the data self-contained and independent of the current device position.
+Any combination of the three axes can be used. Usually all three are supported,
+otherwise, it's not really useful as a compass.
+
+Proposed evdev ABS codes are:
+ - ABS_COMPASS_X: X axis
+ - ABS_COMPASS_Y: Y axis
+ - ABS_COMPASS_Z: Z axis
+
+----------------------------------------------------------------------------
+ Written 2013 by David Herrmann <dh.herrmann@gmail.com>
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 45e9214..329aa30 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -277,7 +277,7 @@ struct pcmcia_device_id {
#define INPUT_DEVICE_ID_KEY_MIN_INTERESTING 0x71
#define INPUT_DEVICE_ID_KEY_MAX 0x2ff
#define INPUT_DEVICE_ID_REL_MAX 0x0f
-#define INPUT_DEVICE_ID_ABS_MAX 0x3f
+#define INPUT_DEVICE_ID_ABS_MAX 0x4f
#define INPUT_DEVICE_ID_MSC_MAX 0x07
#define INPUT_DEVICE_ID_LED_MAX 0x0f
#define INPUT_DEVICE_ID_SND_MAX 0x07
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 1856461..e4c3596 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -869,12 +869,19 @@ struct input_keymap_entry {
#define ABS_MAX 0x3f
#define ABS_CNT (ABS_MAX+1)
+#define ABS_ACCEL_X 0x40 /* Accelerometer X axis */
+#define ABS_ACCEL_Y 0x41 /* Accelerometer Y axis */
+#define ABS_ACCEL_Z 0x42 /* Accelerometer Z axis */
+#define ABS_GYRO_X 0x43 /* Gyroscope X axis */
+#define ABS_GYRO_Y 0x44 /* Gyroscope Y axis */
+#define ABS_GYRO_Z 0x45 /* Gyroscope Z axis */
+
/*
* Due to API restrictions the legacy evdev API only supports ABS values up to
* ABS_MAX/CNT. Use the extended *ABS2 ioctls to operate on any ABS values in
* between ABS_MAX and ABS_MAX2.
*/
-#define ABS_MAX2 0x3f
+#define ABS_MAX2 0x4f
#define ABS_CNT2 (ABS_MAX2+1)
/*
--
1.8.5.1
^ permalink raw reply related
* Re: [PATCH 0/4] Input: ABS2 and friends
From: David Herrmann @ 2013-12-17 16:34 UTC (permalink / raw)
To: open list:HID CORE LAYER
Cc: Dmitry Torokhov, Jiri Kosina, Benjamin Tissoires, Peter Hutterer,
Antonio Ospite, linux-kernel, Input Tools, David Herrmann
In-Reply-To: <1387295334-1744-1-git-send-email-dh.herrmann@gmail.com>
Hi
On Tue, Dec 17, 2013 at 4:48 PM, David Herrmann <dh.herrmann@gmail.com> wrote:
> Hi
>
> This implements the recently discussed ABS2 API. It's working fine on my machine
> with libevdev. Comments welcome!
>
> * Patch #1 fixes some uinput shortcomings and prepares uinput for ABS2
> * Patch #2 adds ABS2
> * Patch #3 is just a small comment-fix for #4
> * Patch #4 adds some new example ABS values in the new range
>
> Note that I have patches pending which make use of the new ABS values, but I'd
> like to get this reduced series in first.
If someone is interested in the libevdev patches, see here:
http://cgit.freedesktop.org/~dvdhrm/libevdev/log/?h=abs2
I tested the libevdev test-suite in all combinations linux+libevdev,
linux-abs2 + libevdev, linux + libevdev-abs2, linux-abs2 +
libevdev-abs2 and all worked fine.
Thanks
David
^ permalink raw reply
* Re: Disable i8042 check on Apple machines
From: Dmitry Torokhov @ 2013-12-17 16:35 UTC (permalink / raw)
To: Josh Boyer; +Cc: Bastien Nocera, linux-input
In-Reply-To: <CA+5PVA565FmR+f5eOZDSUv_pb-pbgExg5L6zHXf9aJyNUoRVdg@mail.gmail.com>
On Tue, Dec 17, 2013 at 10:25:36AM -0500, Josh Boyer wrote:
> Hi Bastien and Dmitry,
>
> I was going through some of the patches we're carrying in Fedora and
> came across this one that Bastien submitted a long time ago:
>
> http://lkml.indiana.edu/hypermail/linux/kernel/1005.0/00938.html
>
> It works and we're still carrying it, but Dmitry said it needed to be
> done differently in i8042-x86ia64io.h. Would that just mean adding a
> DMI for Apple machines to the i8042_dmi_nopnp_table struct? Or
> exactly what should be done here?
That is one possible option that I had in mind originally. Another would
be to modify arch/x86/kernel/x86_init.c::default_x86_detect() and add
the check there.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH -next] Input: pmic8xxx-pwrkey - fix to pass correct device identity to free_irq()
From: Dmitry Torokhov @ 2013-12-17 16:48 UTC (permalink / raw)
To: Stephen Boyd; +Cc: Wei Yongjun, lars, sachin.kamat, yongjun_wei, linux-input
In-Reply-To: <20131217012358.GA31766@codeaurora.org>
On Mon, Dec 16, 2013 at 05:23:58PM -0800, Stephen Boyd wrote:
> On 12/17, Wei Yongjun wrote:
> > From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
> >
> > free_irq() in the error handling case is missing when change
> > pass input device directly to interrupt.
> >
> > Fixes: b27f8fee4965('Input: pmic8xxx-pwrkey - pass input device directly to interrupt')
> > Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
>
> This driver was supposed to be converted to devm before
> that patch. I forgot to send it and then Dmitry seems to have
> fixed up the patch to make it apply. We should just use the devm
> conversion patch to avoid needing to apply this one.
I am still going to apply it, but if you could take a look at my devm
conversion patch for the driver that would be great.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH] input synaptics-rmi4: Bug fixes to ATTN GPIO handling.
From: Dmitry Torokhov @ 2013-12-17 16:57 UTC (permalink / raw)
To: Christopher Heiny
Cc: Linux Input, Andrew Duggan, Vincent Huang, Vivian Ly,
Daniel Rosenberg, Jean Delvare, Joerie de Gram, Linus Walleij,
Benjamin Tissoires
In-Reply-To: <1387251906-23530-1-git-send-email-cheiny@synaptics.com>
Hi Chris,
On Mon, Dec 16, 2013 at 07:45:06PM -0800, Christopher Heiny wrote:
> This patch fixes two bugs in handling of the RMI4 attention line GPIO.
>
> 1) in enable_sensor(), make sure the attn_gpio is defined before attempting to
> get its value.
>
> 2) in rmi_driver_probe(), declare the name of the attn_gpio, then
> request the attn_gpio before attempting to export it. As an added bonus,
> the code relating to the export is tidied up.
>
> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>
> ---
>
> This patch implements changes to the synaptics-rmi4 branch of
> Dmitry's input tree. The base for the patch is commit
> e0c5aec5e6144ae8391d164e2dc659f8ef2b2ba7.
You do not have to mention base commit (and update it all the time),
that's way too much work. If you are the one posting patches I should
be able to figure out how to apply them.
>
> drivers/input/rmi4/rmi_driver.c | 37 ++++++++++++++++++++++---------------
> 1 file changed, 22 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
> index a30c7d3..030e8d5 100644
> --- a/drivers/input/rmi4/rmi_driver.c
> +++ b/drivers/input/rmi4/rmi_driver.c
> @@ -169,7 +169,7 @@ static int enable_sensor(struct rmi_device *rmi_dev)
>
> data->enabled = true;
>
> - if (!pdata->level_triggered &&
> + if (pdata->attn_gpio && !pdata->level_triggered &&
> gpio_get_value(pdata->attn_gpio) == pdata->attn_polarity)
> retval = process_interrupt_requests(rmi_dev);
>
> @@ -807,6 +807,9 @@ static int rmi_driver_remove(struct device *dev)
> return 0;
> }
>
> +
> +static const char *GPIO_LABEL = "attn";
> +
This wastes 4 or 8 bytes I believe. If you want to do that then you
should say:
static const char GPIO_LABEL[] = "attn";
> static int rmi_driver_probe(struct device *dev)
> {
> struct rmi_driver *rmi_driver;
> @@ -959,20 +962,24 @@ static int rmi_driver_probe(struct device *dev)
> }
>
> if (IS_ENABLED(CONFIG_RMI4_DEV) && pdata->attn_gpio) {
> - retval = gpio_export(pdata->attn_gpio, false);
> - if (retval) {
> - dev_warn(dev, "WARNING: Failed to export ATTN gpio!\n");
> - retval = 0;
> - } else {
> - retval = gpio_export_link(dev,
> - "attn", pdata->attn_gpio);
> - if (retval) {
> - dev_warn(dev,
> - "WARNING: Failed to symlink ATTN gpio!\n");
> - retval = 0;
> - } else {
> - dev_info(dev, "Exported ATTN GPIO %d.",
> - pdata->attn_gpio);
> + retval = gpio_request(pdata->attn_gpio, GPIO_LABEL);
> + if (retval)
> + dev_warn(dev, "WARNING: Failed to request ATTN gpio %d, code: %d.\n",
> + pdata->attn_gpio, retval);
> + else {
The rule is: if one branch needs {} then they both should use them:
if (condition) {
statement;
} else {
statement;
...
statement;
}
> + retval = gpio_export(pdata->attn_gpio, false);
> + if (retval)
> + dev_warn(dev, "WARNING: Failed to export ATTN %d, code: %d.\n",
> + pdata->attn_gpio, retval);
> + else {
> + retval = gpio_export_link(dev, "attn",
Why are we using constant when we request gpio but not here?
> + pdata->attn_gpio);
> + if (retval)
> + dev_warn(dev,
> + "WARNING: Failed to symlink ATTN gpio!\n");
> + else
> + dev_info(dev, "Exported ATTN GPIO %d.",
> + pdata->attn_gpio);
> }
> }
> }
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH] input synaptics-rmi4: Bug fixes to ATTN GPIO handling.
From: Christopher Heiny @ 2013-12-17 19:44 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Andrew Duggan, Vincent Huang, Vivian Ly,
Daniel Rosenberg, Jean Delvare, Joerie de Gram, Linus Walleij,
Benjamin Tissoires
In-Reply-To: <20131217165752.GB17790@core.coreip.homeip.net>
On 12/17/2013 08:57 AM, Dmitry Torokhov wrote:
> Hi Chris,
>
> On Mon, Dec 16, 2013 at 07:45:06PM -0800, Christopher Heiny wrote:
>> This patch fixes two bugs in handling of the RMI4 attention line GPIO.
>>
>> 1) in enable_sensor(), make sure the attn_gpio is defined before attempting to
>> get its value.
>>
>> 2) in rmi_driver_probe(), declare the name of the attn_gpio, then
>> request the attn_gpio before attempting to export it. As an added bonus,
>> the code relating to the export is tidied up.
>>
>> Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
>> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
>> Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>>
>> ---
>>
>> This patch implements changes to the synaptics-rmi4 branch of
>> Dmitry's input tree. The base for the patch is commit
>> e0c5aec5e6144ae8391d164e2dc659f8ef2b2ba7.
>
> You do not have to mention base commit (and update it all the time),
> that's way too much work. If you are the one posting patches I should
> be able to figure out how to apply them.
>
>>
>> drivers/input/rmi4/rmi_driver.c | 37 ++++++++++++++++++++++---------------
>> 1 file changed, 22 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
>> index a30c7d3..030e8d5 100644
>> --- a/drivers/input/rmi4/rmi_driver.c
>> +++ b/drivers/input/rmi4/rmi_driver.c
>> @@ -169,7 +169,7 @@ static int enable_sensor(struct rmi_device *rmi_dev)
>>
>> data->enabled = true;
>>
>> - if (!pdata->level_triggered &&
>> + if (pdata->attn_gpio && !pdata->level_triggered &&
>> gpio_get_value(pdata->attn_gpio) == pdata->attn_polarity)
>> retval = process_interrupt_requests(rmi_dev);
>>
>> @@ -807,6 +807,9 @@ static int rmi_driver_remove(struct device *dev)
>> return 0;
>> }
>>
>> +
>> +static const char *GPIO_LABEL = "attn";
>> +
>
> This wastes 4 or 8 bytes I believe. If you want to do that then you
> should say:
>
> static const char GPIO_LABEL[] = "attn";
Hmmm. Learned something new today!
>
>
>> static int rmi_driver_probe(struct device *dev)
>> {
>> struct rmi_driver *rmi_driver;
>> @@ -959,20 +962,24 @@ static int rmi_driver_probe(struct device *dev)
>> }
>>
>> if (IS_ENABLED(CONFIG_RMI4_DEV) && pdata->attn_gpio) {
>> - retval = gpio_export(pdata->attn_gpio, false);
>> - if (retval) {
>> - dev_warn(dev, "WARNING: Failed to export ATTN gpio!\n");
>> - retval = 0;
>> - } else {
>> - retval = gpio_export_link(dev,
>> - "attn", pdata->attn_gpio);
>> - if (retval) {
>> - dev_warn(dev,
>> - "WARNING: Failed to symlink ATTN gpio!\n");
>> - retval = 0;
>> - } else {
>> - dev_info(dev, "Exported ATTN GPIO %d.",
>> - pdata->attn_gpio);
>> + retval = gpio_request(pdata->attn_gpio, GPIO_LABEL);
>> + if (retval)
>> + dev_warn(dev, "WARNING: Failed to request ATTN gpio %d, code: %d.\n",
>> + pdata->attn_gpio, retval);
>> + else {
>
> The rule is: if one branch needs {} then they both should use them:
>
> if (condition) {
> statement;
> } else {
> statement;
> ...
> statement;
> }
OK.
>
>> + retval = gpio_export(pdata->attn_gpio, false);
>> + if (retval)
>> + dev_warn(dev, "WARNING: Failed to export ATTN %d, code: %d.\n",
>> + pdata->attn_gpio, retval);
>> + else {
>> + retval = gpio_export_link(dev, "attn",
>
> Why are we using constant when we request gpio but not here?
It's a leftover that wasn't caught. We'll use the constant.
>
>> + pdata->attn_gpio);
>> + if (retval)
>> + dev_warn(dev,
>> + "WARNING: Failed to symlink ATTN gpio!\n");
>> + else
>> + dev_info(dev, "Exported ATTN GPIO %d.",
>> + pdata->attn_gpio);
>> }
>> }
>> }
>
> Thanks.
>
^ permalink raw reply
* [PATCH V2] input synaptics-rmi4: Bug fixes to ATTN GPIO handling.
From: Christopher Heiny @ 2013-12-17 20:16 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Jean Delvare, Joerie de Gram,
Linus Walleij, Benjamin Tissoires
This patch fixes two bugs in handling of the RMI4 attention line GPIO.
1) in enable_sensor(), make sure the attn_gpio is defined before attempting to
get its value.
2) in rmi_driver_probe(), declare the name of the attn_gpio, then
request the attn_gpio before attempting to export it.
Also introduces a GPIO_LABEL constant for identifying the attention GPIO.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
drivers/input/rmi4/rmi_driver.c | 28 +++++++++++++++++-----------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index a30c7d3..33fb8f8 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -169,7 +169,7 @@ static int enable_sensor(struct rmi_device *rmi_dev)
data->enabled = true;
- if (!pdata->level_triggered &&
+ if (pdata->attn_gpio && !pdata->level_triggered &&
gpio_get_value(pdata->attn_gpio) == pdata->attn_polarity)
retval = process_interrupt_requests(rmi_dev);
@@ -807,6 +807,8 @@ static int rmi_driver_remove(struct device *dev)
return 0;
}
+static const char GPIO_LABEL[] = "attn";
+
static int rmi_driver_probe(struct device *dev)
{
struct rmi_driver *rmi_driver;
@@ -959,20 +961,24 @@ static int rmi_driver_probe(struct device *dev)
}
if (IS_ENABLED(CONFIG_RMI4_DEV) && pdata->attn_gpio) {
- retval = gpio_export(pdata->attn_gpio, false);
+ retval = gpio_request(pdata->attn_gpio, GPIO_LABEL);
if (retval) {
- dev_warn(dev, "WARNING: Failed to export ATTN gpio!\n");
- retval = 0;
+ dev_warn(dev, "WARNING: Failed to request ATTN gpio %d, code: %d.\n",
+ pdata->attn_gpio, retval);
} else {
- retval = gpio_export_link(dev,
- "attn", pdata->attn_gpio);
+ retval = gpio_export(pdata->attn_gpio, false);
if (retval) {
- dev_warn(dev,
- "WARNING: Failed to symlink ATTN gpio!\n");
- retval = 0;
+ dev_warn(dev, "WARNING: Failed to export ATTN gpio %d, code: %d.\n",
+ pdata->attn_gpio, retval);
} else {
- dev_info(dev, "Exported ATTN GPIO %d.",
- pdata->attn_gpio);
+ retval = gpio_export_link(dev, GPIO_LABEL,
+ pdata->attn_gpio);
+ if (retval)
+ dev_warn(dev,
+ "WARNING: Failed to symlink ATTN gpio!\n");
+ else
+ dev_info(dev, "Exported ATTN GPIO %d.",
+ pdata->attn_gpio);
}
}
}
^ permalink raw reply related
* Re: [Xen-devel] [PATCH v3 1/2] xen/pvhvm: If xen_platform_pci=0 is set don't blow up (v3).
From: Konrad Rzeszutek Wilk @ 2013-12-17 21:23 UTC (permalink / raw)
To: Fabio Fantoni
Cc: axboe, stefano.stabellini, ian.campbell, xen-devel, linux-kernel,
boris.ostrovsky, david.vrabel, leosilva, ashley, peterhuewe, mail,
tpmdd, dmitry.torokhov, bhelgaas, plagnioj, tomi.valkeinen,
tpmdd-devel, linux-input, netdev, linux-pci, linux-fbdev
In-Reply-To: <20131217145150.GC4683@phenom.dumpdata.com>
On Tue, Dec 17, 2013 at 09:51:50AM -0500, Konrad Rzeszutek Wilk wrote:
> On Tue, Dec 17, 2013 at 10:54:47AM +0100, Fabio Fantoni wrote:
> > Il 16/12/2013 16:04, Konrad Rzeszutek Wilk ha scritto:
> > >The user has the option of disabling the platform driver:
> > >00:02.0 Unassigned class [ff80]: XenSource, Inc. Xen Platform Device (rev 01)
> > >
> > >which is used to unplug the emulated drivers (IDE, Realtek 8169, etc)
> > >and allow the PV drivers to take over. If the user wishes
> > >to disable that they can set:
> > >
> > > xen_platform_pci=0
> > > (in the guest config file)
> > >
> > >or
> > > xen_emul_unplug=never
> > > (on the Linux command line)
> > >
> > >except it does not work properly. The PV drivers still try to
> > >load and since the Xen platform driver is not run - and it
> > >has not initialized the grant tables, most of the PV drivers
> > >stumble upon:
> > >
> > >input: Xen Virtual Keyboard as /devices/virtual/input/input5
> > >input: Xen Virtual Pointer as /devices/virtual/input/input6M
> > >------------[ cut here ]------------
> > >kernel BUG at /home/konrad/ssd/konrad/linux/drivers/xen/grant-table.c:1206!
> > >invalid opcode: 0000 [#1] SMP
> > >Modules linked in: xen_kbdfront(+) xenfs xen_privcmd
> > >CPU: 6 PID: 1389 Comm: modprobe Not tainted 3.13.0-rc1upstream-00021-ga6c892b-dirty #1
> > >Hardware name: Xen HVM domU, BIOS 4.4-unstable 11/26/2013
> > >RIP: 0010:[<ffffffff813ddc40>] [<ffffffff813ddc40>] get_free_entries+0x2e0/0x300
> > >Call Trace:
> > > [<ffffffff8150d9a3>] ? evdev_connect+0x1e3/0x240
> > > [<ffffffff813ddd0e>] gnttab_grant_foreign_access+0x2e/0x70
> > > [<ffffffffa0010081>] xenkbd_connect_backend+0x41/0x290 [xen_kbdfront]
> > > [<ffffffffa0010a12>] xenkbd_probe+0x2f2/0x324 [xen_kbdfront]
> > > [<ffffffff813e5757>] xenbus_dev_probe+0x77/0x130
> > > [<ffffffff813e7217>] xenbus_frontend_dev_probe+0x47/0x50
> > > [<ffffffff8145e9a9>] driver_probe_device+0x89/0x230
> > > [<ffffffff8145ebeb>] __driver_attach+0x9b/0xa0
> > > [<ffffffff8145eb50>] ? driver_probe_device+0x230/0x230
> > > [<ffffffff8145eb50>] ? driver_probe_device+0x230/0x230
> > > [<ffffffff8145cf1c>] bus_for_each_dev+0x8c/0xb0
> > > [<ffffffff8145e7d9>] driver_attach+0x19/0x20
> > > [<ffffffff8145e260>] bus_add_driver+0x1a0/0x220
> > > [<ffffffff8145f1ff>] driver_register+0x5f/0xf0
> > > [<ffffffff813e55c5>] xenbus_register_driver_common+0x15/0x20
> > > [<ffffffff813e76b3>] xenbus_register_frontend+0x23/0x40
> > > [<ffffffffa0015000>] ? 0xffffffffa0014fff
> > > [<ffffffffa001502b>] xenkbd_init+0x2b/0x1000 [xen_kbdfront]
> > > [<ffffffff81002049>] do_one_initcall+0x49/0x170
> > >
> > >.. snip..
> > >
> > >which is hardly nice. This patch fixes this by having each
> > >PV driver check for:
> > > - if running in PV, then it is fine to execute (as that is their
> > > native environment).
> > > - if running in HVM, check if user wanted 'xen_emul_unplug=never',
> > > in which case bail out and don't load any PV drivers.
> > > - if running in HVM, and if PCI device 5853:0001 (xen_platform_pci)
> > > does not exist, then bail out and not load PV drivers.
> > > - (v2) if running in HVM, and if the user wanted 'xen_emul_unplug=disks',
> > > then bail out for all PV devices _except_ the block one.
> > > Ditto for the network one ('nics').
> > > - (v2) if running in HVM, and if the user wanted 'xen_emul_unplug=unnecessary'
> > > then load block PV driver, and also setup the legacy IDE paths.
> > > In (v3) make it actually load PV drivers.
> > >
> > >Reported-by: Sander Eikelenboom <linux@eikelenboom.it
> > >Reported-by: Anthony PERARD <anthony.perard@citrix.com>
> > >Reported-by: Fabio Fantoni <fabio.fantoni@m2r.biz>
> > >Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
> > >[v2: Add extra logic to handle the myrid ways 'xen_emul_unplug'
> > >can be used per Ian and Stefano suggestion]
> > >[v3: Make the unnecessary case work properly]
> > >Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
> >
> > I tested this patch with all possible cases that I know, no crash or
> > calltrace found.
> >
> > I don't understand the utility of 'xen_emul_unplug=unnecessary' but
> > probably it is working correctly, it shows both pv and not pv for
> > blocks and nics.
>
> Great!
> >
> > About 'xen_emul_unplug=disks' and 'xen_emul_unplug=nics' probably
> > there is something wrong.
> > With 'xen_emul_unplug=nics' it shows pv nic (this should be correct)
> > and about disks it shows the same disk twice, as pv and not pv (xvda
> > and sda) with different device number.
>
> Good.
> > With 'xen_emul_unplug=disks' it shows pv block (this should be
> > correct) and it seems to show the pv nic too (this should be wrong).
>
> Correct. You should see only the PV disk and all other PV devices
> should not function. Let me dig in this and see whether there is
> another bug. Thank you!
That is because 'disks' is incorrect. It should have been 'ide-disks'
[ 0.000000] unrecognised option 'disks' in parameter 'xen_emul_unplug'
With the 'ide-disks' it should work. I will update the description to
mention 'ide-disks' instead of 'disks'. Thank you for finding this!
^ permalink raw reply
* Re: [PATCH 0/4] Input: ABS2 and friends
From: simon @ 2013-12-17 21:28 UTC (permalink / raw)
Cc: linux-input, Dmitry Torokhov, Jiri Kosina, Benjamin Tissoires,
Peter Hutterer, Antonio Ospite, linux-kernel, input-tools,
David Herrmann
In-Reply-To: <1387295334-1744-1-git-send-email-dh.herrmann@gmail.com>
> Hi
>
> This implements the recently discussed ABS2 API. It's working fine on my
> machine with libevdev. Comments welcome!
Just looking at the documentation file, I have a couple of suggestions.
1). Make a note on the direction of gravity wrt the example image. Ie what
which axis/value will be positive with the device laid flat/stationary on
a desk. Might just prevent some confusion later.
2). Calibration, Accels and Gyros are noisy in different ways. Gyros tend
to be wrong in the long term, giving some consistant bias which should be
calibrated out. Accels are noisy in the short term, but generally right
over a long period.
Am I correct that the event framework will provide time-stamping of data,
thus making computation of relative movements possible.
Simon
^ permalink raw reply
* [PATCH] input synaptics-rmi4: Transport layer renaming.
From: Christopher Heiny @ 2013-12-17 21:31 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Linux Input, Christopher Heiny, Andrew Duggan, Vincent Huang,
Vivian Ly, Daniel Rosenberg, Jean Delvare, Joerie de Gram,
Linus Walleij, Benjamin Tissoires
The current RMI4 driver uses the term "physical" for two different logical
entities: the communications transport layer (I2C, SPI, and so on), and the
actual RMI4 device that is being communicated with. Such usage makes the
code harder to understand due to confusion as to just which elements are
being referred to.
This patch renames the transport layer elements in order to eliminate this
confusion. Much of the renaming was accomplished by the following Bash script
#!/bin/bash
#
# Update RMI4 driver transport layer naming.
#
files="rmi_bus.c rmi_bus.h rmi_driver.c rmi_driver.h rmi_f01.c rmi_i2c.c"
cd drivers/input/rmi4
for f in $files ; do
echo $f
sed -i.bak s/rmi_phys_device/rmi_transport_dev/g $f
sed -i.bak s/rmi_phys_info/rmi_transport_info/g $f
sed -i.bak "s/rmi_transport_dev \*phys/rmi_transport_dev \*xport/g" $f
sed -i.bak "s/rmi_transport_dev \*rmi_phys/rmi_transport_dev \*xport/g" $f
sed -i.bak "s/phys\([^i]\)/xport\1/g" $f
sed -i.bak "s/->phys/->xport/g" $f
sed -i.bak "s/register_physical_device/register_transport_device/g" $f
sed -i.bak "s/physical_device_count/transport_device_count/g" $f
done
although some changes proved easier to simply do by hand, particularly in the
comments. Changes are confined strictly to the renaming, to keep the patch
relatively simple.
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
drivers/input/rmi4/rmi_bus.c | 38 ++++++++---------
drivers/input/rmi4/rmi_bus.h | 54 ++++++++++++-------------
drivers/input/rmi4/rmi_driver.c | 32 +++++++--------
drivers/input/rmi4/rmi_driver.h | 2 +-
drivers/input/rmi4/rmi_i2c.c | 90 ++++++++++++++++++++---------------------
5 files changed, 108 insertions(+), 108 deletions(-)
diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
index 173f6be..96a76e7 100644
--- a/drivers/input/rmi4/rmi_bus.c
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -76,38 +76,38 @@ static void rmi_physical_teardown_debugfs(struct rmi_device *rmi_dev)
#endif
/**
- * rmi_register_physical_device - register a physical device connection on the RMI
- * bus. Physical drivers provide communication from the devices on the bus to
- * the RMI4 sensor on a bus such as SPI, I2C, and so on.
+ * rmi_register_transport_device - register a transport device connection
+ * on the RMI bus. Transport drivers provide communication from the devices
+ * on a bus (such as SPI, I2C, and so on) to the RMI4 sensor.
*
- * @phys: the physical device to register
+ * @xport: the transport device to register
*/
-int rmi_register_physical_device(struct rmi_phys_device *phys)
+int rmi_register_transport_device(struct rmi_transport_dev *xport)
{
- static atomic_t physical_device_count = ATOMIC_INIT(0);
- struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+ static atomic_t transport_device_count = ATOMIC_INIT(0);
+ struct rmi_device_platform_data *pdata = xport->dev->platform_data;
struct rmi_device *rmi_dev;
int error;
if (!pdata) {
- dev_err(phys->dev, "no platform data!\n");
+ dev_err(xport->dev, "no platform data!\n");
return -EINVAL;
}
- rmi_dev = devm_kzalloc(phys->dev,
+ rmi_dev = devm_kzalloc(xport->dev,
sizeof(struct rmi_device), GFP_KERNEL);
if (!rmi_dev)
return -ENOMEM;
- rmi_dev->phys = phys;
- rmi_dev->number = atomic_inc_return(&physical_device_count) - 1;
+ rmi_dev->xport = xport;
+ rmi_dev->number = atomic_inc_return(&transport_device_count) - 1;
dev_set_name(&rmi_dev->dev, "sensor%02d", rmi_dev->number);
rmi_dev->dev.bus = &rmi_bus_type;
rmi_dev->dev.type = &rmi_device_type;
- phys->rmi_dev = rmi_dev;
+ xport->rmi_dev = rmi_dev;
rmi_physical_setup_debugfs(rmi_dev);
@@ -115,26 +115,26 @@ int rmi_register_physical_device(struct rmi_phys_device *phys)
if (error)
return error;
- dev_dbg(phys->dev, "%s: Registered %s as %s.\n", __func__,
+ dev_dbg(xport->dev, "%s: Registered %s as %s.\n", __func__,
pdata->sensor_name, dev_name(&rmi_dev->dev));
return 0;
}
-EXPORT_SYMBOL_GPL(rmi_register_physical_device);
+EXPORT_SYMBOL_GPL(rmi_register_transport_device);
/**
- * rmi_unregister_physical_device - unregister a physical device connection
- * @phys: the physical driver to unregister
+ * rmi_unregister_transport_device - unregister a transport device connection
+ * @xport: the transport driver to unregister
*
*/
-void rmi_unregister_physical_device(struct rmi_phys_device *phys)
+void rmi_unregister_transport_device(struct rmi_transport_dev *xport)
{
- struct rmi_device *rmi_dev = phys->rmi_dev;
+ struct rmi_device *rmi_dev = xport->rmi_dev;
rmi_physical_teardown_debugfs(rmi_dev);
device_unregister(&rmi_dev->dev);
}
-EXPORT_SYMBOL(rmi_unregister_physical_device);
+EXPORT_SYMBOL(rmi_unregister_transport_device);
/* Function specific stuff */
diff --git a/drivers/input/rmi4/rmi_bus.h b/drivers/input/rmi4/rmi_bus.h
index e2a3dc6..65dd934 100644
--- a/drivers/input/rmi4/rmi_bus.h
+++ b/drivers/input/rmi4/rmi_bus.h
@@ -38,7 +38,7 @@ struct rmi_device;
* interrupt handling.
* @data: Private data pointer
*
- * @node: entry in physical device list of functions
+ * @node: entry in device's list of functions
* @debugfs_root: used during debugging
*/
struct rmi_function {
@@ -135,8 +135,8 @@ struct rmi_driver {
#define to_rmi_driver(d) \
container_of(d, struct rmi_driver, driver);
-/** struct rmi_phys_info - diagnostic information about the RMI physical
- * device, used in the phys debugfs file.
+/** struct rmi_transport_info - diagnostic information about the RMI transport
+ * device, used in the xport_info debugfs file.
*
* @proto String indicating the protocol being used.
* @tx_count Number of transmit operations.
@@ -147,7 +147,7 @@ struct rmi_driver {
* @rx_errs Number of errors encountered during receive operations.
* @att_count Number of times ATTN assertions have been handled.
*/
-struct rmi_phys_info {
+struct rmi_transport_info {
char *proto;
long tx_count;
long tx_bytes;
@@ -158,7 +158,7 @@ struct rmi_phys_info {
};
/**
- * struct rmi_phys_device - represent an RMI physical device
+ * struct rmi_transport_dev - represent an RMI transport device
*
* @dev: Pointer to the communication device, e.g. i2c or spi
* @rmi_dev: Pointer to the RMI device
@@ -170,28 +170,28 @@ struct rmi_phys_info {
* handling
* @data: Private data pointer
*
- * The RMI physical device implements the glue between different communication
+ * The RMI transport device implements the glue between different communication
* buses such as I2C and SPI.
*
*/
-struct rmi_phys_device {
+struct rmi_transport_dev {
struct device *dev;
struct rmi_device *rmi_dev;
- int (*write_block)(struct rmi_phys_device *phys, u16 addr,
+ int (*write_block)(struct rmi_transport_dev *xport, u16 addr,
const void *buf, const int len);
- int (*read_block)(struct rmi_phys_device *phys, u16 addr,
+ int (*read_block)(struct rmi_transport_dev *xport, u16 addr,
void *buf, const int len);
- int (*enable_device) (struct rmi_phys_device *phys);
- void (*disable_device) (struct rmi_phys_device *phys);
+ int (*enable_device) (struct rmi_transport_dev *xport);
+ void (*disable_device) (struct rmi_transport_dev *xport);
irqreturn_t (*irq_thread)(int irq, void *p);
irqreturn_t (*hard_irq)(int irq, void *p);
void *data;
- struct rmi_phys_info info;
+ struct rmi_transport_info info;
};
/**
@@ -200,7 +200,7 @@ struct rmi_phys_device {
* @dev: The device created for the RMI bus
* @number: Unique number for the device on the bus.
* @driver: Pointer to associated driver
- * @phys: Pointer to the physical interface
+ * @xport: Pointer to the transport interface
* @debugfs_root: base for this particular sensor device.
*
*/
@@ -209,7 +209,7 @@ struct rmi_device {
int number;
struct rmi_driver *driver;
- struct rmi_phys_device *phys;
+ struct rmi_transport_dev *xport;
#ifdef CONFIG_RMI4_DEBUG
struct dentry *debugfs_root;
@@ -217,7 +217,7 @@ struct rmi_device {
};
#define to_rmi_device(d) container_of(d, struct rmi_device, dev)
-#define to_rmi_platform_data(d) ((d)->phys->dev->platform_data)
+#define to_rmi_platform_data(d) ((d)->xport->dev->platform_data)
bool rmi_is_physical_device(struct device *dev);
@@ -227,12 +227,12 @@ bool rmi_is_physical_device(struct device *dev);
* @addr: The address to read from
* @buf: The read buffer
*
- * Reads a byte of data using the underlaying physical protocol in to buf. It
+ * Reads a byte of data using the underlaying transport protocol in to buf. It
* returns zero or a negative error code.
*/
static inline int rmi_read(struct rmi_device *d, u16 addr, void *buf)
{
- return d->phys->read_block(d->phys, addr, buf, 1);
+ return d->xport->read_block(d->xport, addr, buf, 1);
}
/**
@@ -242,13 +242,13 @@ static inline int rmi_read(struct rmi_device *d, u16 addr, void *buf)
* @buf: The read buffer
* @len: Length of the read buffer
*
- * Reads a block of byte data using the underlaying physical protocol in to buf.
- * It returns the amount of bytes read or a negative error code.
+ * Reads a block of byte data using the underlaying transport protocol in
+ * to buf. It returns the amount of bytes read or a negative error code.
*/
static inline int rmi_read_block(struct rmi_device *d, u16 addr, void *buf,
const int len)
{
- return d->phys->read_block(d->phys, addr, buf, len);
+ return d->xport->read_block(d->xport, addr, buf, len);
}
/**
@@ -257,12 +257,12 @@ static inline int rmi_read_block(struct rmi_device *d, u16 addr, void *buf,
* @addr: The address to write to
* @data: The data to write
*
- * Writes a byte from buf using the underlaying physical protocol. It
+ * Writes a byte from buf using the underlaying transport protocol. It
* returns zero or a negative error code.
*/
static inline int rmi_write(struct rmi_device *d, u16 addr, const u8 data)
{
- return d->phys->write_block(d->phys, addr, &data, 1);
+ return d->xport->write_block(d->xport, addr, &data, 1);
}
/**
@@ -272,17 +272,17 @@ static inline int rmi_write(struct rmi_device *d, u16 addr, const u8 data)
* @buf: The write buffer
* @len: Length of the write buffer
*
- * Writes a block of byte data from buf using the underlaying physical protocol.
- * It returns the amount of bytes written or a negative error code.
+ * Writes a block of byte data from buf using the underlaying transport
+ * protocol. It returns the amount of bytes written or a negative error code.
*/
static inline int rmi_write_block(struct rmi_device *d, u16 addr,
const void *buf, const int len)
{
- return d->phys->write_block(d->phys, addr, buf, len);
+ return d->xport->write_block(d->xport, addr, buf, len);
}
-int rmi_register_physical_device(struct rmi_phys_device *phys);
-void rmi_unregister_physical_device(struct rmi_phys_device *phys);
+int rmi_register_transport_device(struct rmi_transport_dev *xport);
+void rmi_unregister_transport_device(struct rmi_transport_dev *xport);
int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data));
/**
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index a30c7d3..2ae9af9 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -45,16 +45,16 @@
static irqreturn_t rmi_irq_thread(int irq, void *p)
{
- struct rmi_phys_device *phys = p;
- struct rmi_device *rmi_dev = phys->rmi_dev;
+ struct rmi_transport_dev *xport = p;
+ struct rmi_device *rmi_dev = xport->rmi_dev;
struct rmi_driver *driver = rmi_dev->driver;
- struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+ struct rmi_device_platform_data *pdata = xport->dev->platform_data;
struct rmi_driver_data *data;
data = dev_get_drvdata(&rmi_dev->dev);
if (IRQ_DEBUG(data))
- dev_dbg(phys->dev, "ATTN gpio, value: %d.\n",
+ dev_dbg(xport->dev, "ATTN gpio, value: %d.\n",
gpio_get_value(pdata->attn_gpio));
if (gpio_get_value(pdata->attn_gpio) == pdata->attn_polarity) {
@@ -124,12 +124,12 @@ static void disable_sensor(struct rmi_device *rmi_dev)
if (!data->irq)
disable_polling(rmi_dev);
- if (rmi_dev->phys->disable_device)
- rmi_dev->phys->disable_device(rmi_dev->phys);
+ if (rmi_dev->xport->disable_device)
+ rmi_dev->xport->disable_device(rmi_dev->xport);
if (data->irq) {
disable_irq(data->irq);
- free_irq(data->irq, rmi_dev->phys);
+ free_irq(data->irq, rmi_dev->xport);
}
data->enabled = false;
@@ -138,27 +138,27 @@ static void disable_sensor(struct rmi_device *rmi_dev)
static int enable_sensor(struct rmi_device *rmi_dev)
{
struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
- struct rmi_phys_device *rmi_phys;
+ struct rmi_transport_dev *xport;
int retval = 0;
struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
if (data->enabled)
return 0;
- if (rmi_dev->phys->enable_device) {
- retval = rmi_dev->phys->enable_device(rmi_dev->phys);
+ if (rmi_dev->xport->enable_device) {
+ retval = rmi_dev->xport->enable_device(rmi_dev->xport);
if (retval)
return retval;
}
- rmi_phys = rmi_dev->phys;
+ xport = rmi_dev->xport;
if (data->irq) {
retval = request_threaded_irq(data->irq,
- rmi_phys->hard_irq ? rmi_phys->hard_irq : NULL,
- rmi_phys->irq_thread ?
- rmi_phys->irq_thread : rmi_irq_thread,
+ xport->hard_irq ? xport->hard_irq : NULL,
+ xport->irq_thread ?
+ xport->irq_thread : rmi_irq_thread,
data->irq_flags,
- dev_name(&rmi_dev->dev), rmi_phys);
+ dev_name(&rmi_dev->dev), xport);
if (retval)
return retval;
} else {
@@ -819,7 +819,7 @@ static int rmi_driver_probe(struct device *dev)
dev_dbg(dev, "%s: Starting probe.\n", __func__);
if (!rmi_is_physical_device(dev)) {
- dev_dbg(dev, "Not a sensor device.\n");
+ dev_dbg(dev, "Not a physical device.\n");
return -ENODEV;
}
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 5e3c4d4..0d57700 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -74,7 +74,7 @@ struct rmi_driver_data {
#ifdef CONFIG_RMI4_DEBUG
struct dentry *debugfs_delay;
- struct dentry *debugfs_phys;
+ struct dentry *debugfs_xport;
struct dentry *debugfs_reg_ctl;
struct dentry *debugfs_reg;
struct dentry *debugfs_irq;
diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c
index 62351c4..b33074c 100644
--- a/drivers/input/rmi4/rmi_i2c.c
+++ b/drivers/input/rmi4/rmi_i2c.c
@@ -27,7 +27,7 @@
*
* @page_mutex: Locks current page to avoid changing pages in unexpected ways.
* @page: Keeps track of the current virtual page
- * @phys: Pointer to the physical interface
+ * @xport: Pointer to the transport interface
*
* @tx_buf: Buffer used for transmitting data to the sensor over i2c.
* @tx_buf_size: Size of the buffer
@@ -40,7 +40,7 @@
struct rmi_i2c_data {
struct mutex page_mutex;
int page;
- struct rmi_phys_device *phys;
+ struct rmi_transport_dev *xport;
u8 *tx_buf;
int tx_buf_size;
@@ -97,14 +97,14 @@ static inline void teardown_debugfs(struct rmi_i2c_data *data)
#define RMI_PAGE_SELECT_REGISTER 0xff
#define RMI_I2C_PAGE(addr) (((addr) >> 8) & 0xff)
-static char *phys_proto_name = "i2c";
+static char *xport_proto_name = "i2c";
/*
* rmi_set_page - Set RMI page
- * @phys: The pointer to the rmi_phys_device struct
+ * @xport: The pointer to the rmi_transport_dev struct
* @page: The new page address.
*
- * RMI devices have 16-bit addressing, but some of the physical
+ * RMI devices have 16-bit addressing, but some of the transport
* implementations (like SMBus) only have 8-bit addressing. So RMI implements
* a page address at 0xff of every page so we can reliable page addresses
* every 256 registers.
@@ -113,21 +113,21 @@ static char *phys_proto_name = "i2c";
*
* Returns zero on success, non-zero on failure.
*/
-static int rmi_set_page(struct rmi_phys_device *phys, u8 page)
+static int rmi_set_page(struct rmi_transport_dev *xport, u8 page)
{
- struct i2c_client *client = to_i2c_client(phys->dev);
- struct rmi_i2c_data *data = phys->data;
+ struct i2c_client *client = to_i2c_client(xport->dev);
+ struct rmi_i2c_data *data = xport->data;
u8 txbuf[2] = {RMI_PAGE_SELECT_REGISTER, page};
int retval;
if (COMMS_DEBUG(data))
dev_dbg(&client->dev, "writes 3 bytes: %02x %02x\n",
txbuf[0], txbuf[1]);
- phys->info.tx_count++;
- phys->info.tx_bytes += sizeof(txbuf);
+ xport->info.tx_count++;
+ xport->info.tx_bytes += sizeof(txbuf);
retval = i2c_master_send(client, txbuf, sizeof(txbuf));
if (retval != sizeof(txbuf)) {
- phys->info.tx_errs++;
+ xport->info.tx_errs++;
dev_err(&client->dev,
"%s: set page failed: %d.", __func__, retval);
return (retval < 0) ? retval : -EIO;
@@ -164,11 +164,11 @@ static int copy_to_debug_buf(struct device *dev, struct rmi_i2c_data *data,
return 0;
}
-static int rmi_i2c_write_block(struct rmi_phys_device *phys, u16 addr,
+static int rmi_i2c_write_block(struct rmi_transport_dev *xport, u16 addr,
const void *buf, const int len)
{
- struct i2c_client *client = to_i2c_client(phys->dev);
- struct rmi_i2c_data *data = phys->data;
+ struct i2c_client *client = to_i2c_client(xport->dev);
+ struct rmi_i2c_data *data = xport->data;
int retval;
int tx_size = len + 1;
@@ -190,7 +190,7 @@ static int rmi_i2c_write_block(struct rmi_phys_device *phys, u16 addr,
memcpy(data->tx_buf + 1, buf, len);
if (RMI_I2C_PAGE(addr) != data->page) {
- retval = rmi_set_page(phys, RMI_I2C_PAGE(addr));
+ retval = rmi_set_page(xport, RMI_I2C_PAGE(addr));
if (retval < 0)
goto exit;
}
@@ -202,11 +202,11 @@ static int rmi_i2c_write_block(struct rmi_phys_device *phys, u16 addr,
len, addr, data->debug_buf);
}
- phys->info.tx_count++;
- phys->info.tx_bytes += tx_size;
+ xport->info.tx_count++;
+ xport->info.tx_bytes += tx_size;
retval = i2c_master_send(client, data->tx_buf, tx_size);
if (retval < 0)
- phys->info.tx_errs++;
+ xport->info.tx_errs++;
else
retval--; /* don't count the address byte */
@@ -216,18 +216,18 @@ exit:
}
-static int rmi_i2c_read_block(struct rmi_phys_device *phys, u16 addr,
+static int rmi_i2c_read_block(struct rmi_transport_dev *xport, u16 addr,
void *buf, const int len)
{
- struct i2c_client *client = to_i2c_client(phys->dev);
- struct rmi_i2c_data *data = phys->data;
+ struct i2c_client *client = to_i2c_client(xport->dev);
+ struct rmi_i2c_data *data = xport->data;
u8 txbuf[1] = {addr & 0xff};
int retval;
mutex_lock(&data->page_mutex);
if (RMI_I2C_PAGE(addr) != data->page) {
- retval = rmi_set_page(phys, RMI_I2C_PAGE(addr));
+ retval = rmi_set_page(xport, RMI_I2C_PAGE(addr));
if (retval < 0)
goto exit;
}
@@ -235,21 +235,21 @@ static int rmi_i2c_read_block(struct rmi_phys_device *phys, u16 addr,
if (COMMS_DEBUG(data))
dev_dbg(&client->dev, "writes 1 bytes: %02x\n", txbuf[0]);
- phys->info.tx_count++;
- phys->info.tx_bytes += sizeof(txbuf);
+ xport->info.tx_count++;
+ xport->info.tx_bytes += sizeof(txbuf);
retval = i2c_master_send(client, txbuf, sizeof(txbuf));
if (retval != sizeof(txbuf)) {
- phys->info.tx_errs++;
+ xport->info.tx_errs++;
retval = (retval < 0) ? retval : -EIO;
goto exit;
}
retval = i2c_master_recv(client, (u8 *) buf, len);
- phys->info.rx_count++;
- phys->info.rx_bytes += len;
+ xport->info.rx_count++;
+ xport->info.rx_bytes += len;
if (retval < 0)
- phys->info.rx_errs++;
+ xport->info.rx_errs++;
else if (COMMS_DEBUG(data)) {
int rc = copy_to_debug_buf(&client->dev, data, (u8 *) buf, len);
if (!rc)
@@ -265,7 +265,7 @@ exit:
static int rmi_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct rmi_phys_device *rmi_phys;
+ struct rmi_transport_dev *xport;
struct rmi_i2c_data *data;
struct rmi_device_platform_data *pdata = client->dev.platform_data;
int retval;
@@ -296,10 +296,10 @@ static int rmi_i2c_probe(struct i2c_client *client,
return retval;
}
- rmi_phys = devm_kzalloc(&client->dev, sizeof(struct rmi_phys_device),
+ xport = devm_kzalloc(&client->dev, sizeof(struct rmi_transport_dev),
GFP_KERNEL);
- if (!rmi_phys)
+ if (!xport)
return -ENOMEM;
data = devm_kzalloc(&client->dev, sizeof(struct rmi_i2c_data),
@@ -307,35 +307,35 @@ static int rmi_i2c_probe(struct i2c_client *client,
if (!data)
return -ENOMEM;
- data->phys = rmi_phys;
+ data->xport = xport;
- rmi_phys->data = data;
- rmi_phys->dev = &client->dev;
+ xport->data = data;
+ xport->dev = &client->dev;
- rmi_phys->write_block = rmi_i2c_write_block;
- rmi_phys->read_block = rmi_i2c_read_block;
- rmi_phys->info.proto = phys_proto_name;
+ xport->write_block = rmi_i2c_write_block;
+ xport->read_block = rmi_i2c_read_block;
+ xport->info.proto = xport_proto_name;
mutex_init(&data->page_mutex);
/* Setting the page to zero will (a) make sure the PSR is in a
* known state, and (b) make sure we can talk to the device.
*/
- retval = rmi_set_page(rmi_phys, 0);
+ retval = rmi_set_page(xport, 0);
if (retval) {
dev_err(&client->dev, "Failed to set page select to 0.\n");
return retval;
}
- retval = rmi_register_physical_device(rmi_phys);
+ retval = rmi_register_transport_device(xport);
if (retval) {
- dev_err(&client->dev, "Failed to register physical driver at 0x%.2X.\n",
+ dev_err(&client->dev, "Failed to register transport driver at 0x%.2X.\n",
client->addr);
goto err_gpio;
}
- i2c_set_clientdata(client, rmi_phys);
+ i2c_set_clientdata(client, xport);
- retval = setup_debugfs(rmi_phys->rmi_dev, data);
+ retval = setup_debugfs(xport->rmi_dev, data);
if (retval < 0)
dev_warn(&client->dev, "Failed to setup debugfs. Code: %d.\n",
retval);
@@ -352,12 +352,12 @@ err_gpio:
static int rmi_i2c_remove(struct i2c_client *client)
{
- struct rmi_phys_device *phys = i2c_get_clientdata(client);
+ struct rmi_transport_dev *xport = i2c_get_clientdata(client);
struct rmi_device_platform_data *pd = client->dev.platform_data;
- teardown_debugfs(phys->data);
+ teardown_debugfs(xport->data);
- rmi_unregister_physical_device(phys);
+ rmi_unregister_transport_device(xport);
if (pd->gpio_config)
pd->gpio_config(&pd->gpio_data, false);
^ permalink raw reply related
* [PATCH v2] Input: add i2c/smbus driver for elan touchpad
From: Duson Lin @ 2013-12-18 3:46 UTC (permalink / raw)
To: linux-kernel, linux-input, dmitry.torokhov
Cc: bleung, agnescheng, phoenix, Duson Lin
This driver adds support for elan i2c/smbus touchpad found on some laptops PC
Signed-off-by: Duson Lin <dusonlin@emc.com.tw>
---
drivers/input/mouse/Kconfig | 10 +
drivers/input/mouse/Makefile | 1 +
drivers/input/mouse/elan_i2c.c | 890 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 901 insertions(+)
create mode 100644 drivers/input/mouse/elan_i2c.c
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index effa9c5..095eccc 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -215,6 +215,16 @@ config MOUSE_CYAPA
To compile this driver as a module, choose M here: the module will be
called cyapa.
+config MOUSE_ELAN_I2C
+ tristate "ELAN I2C Touchpad support"
+ depends on I2C
+ help
+ This driver adds support for Elan I2C Trackpads.
+ Say Y here if you have a ELAN I2C Touchpad.
+
+ To compile this driver as a module, choose M here: the module will be
+ called elan_i2c.
+
config MOUSE_INPORT
tristate "InPort/MS/ATIXL busmouse"
depends on ISA
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index c25efdb..24a12a6 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
obj-$(CONFIG_MOUSE_CYAPA) += cyapa.o
+obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
obj-$(CONFIG_MOUSE_INPORT) += inport.o
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
diff --git a/drivers/input/mouse/elan_i2c.c b/drivers/input/mouse/elan_i2c.c
new file mode 100644
index 0000000..f7bfa22
--- /dev/null
+++ b/drivers/input/mouse/elan_i2c.c
@@ -0,0 +1,890 @@
+/*
+ * Elan I2C/SMBus Touchpad driver
+ *
+ * Copyright (c) 2013 ELAN Microelectronics Corp.
+ *
+ * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
+ * Version: 1.4.6
+ *
+ * Based on cyapa driver:
+ * copyright (c) 2011-2012 Cypress Semiconductor, Inc.
+ * copyright (c) 2011-2012 Google, 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.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_NAME "elan_i2c"
+#define ELAN_DRIVER_VERSION "1.4.6"
+#define ETP_PRESSURE_OFFSET 25
+#define ETP_MAX_PRESSURE 255
+#define ETP_FWIDTH_REDUCE 90
+#define ETP_FINGER_WIDTH 15
+
+#define ELAN_ADAPTER_FUNC_NONE 0
+#define ELAN_ADAPTER_FUNC_I2C 1
+#define ELAN_ADAPTER_FUNC_SMBUS 2
+#define ELAN_ADAPTER_FUNC_BOTH 3
+
+/* Length of Elan touchpad information */
+#define ETP_INF_LENGTH 2
+#define ETP_MAX_FINGERS 5
+#define ETP_FINGER_DATA_LEN 5
+#define ETP_REPORT_ID 0x5D
+#define ETP_MAX_REPORT_LEN 34
+#define ETP_ENABLE_ABS 0x0001
+#define ETP_ENABLE_CALIBRATE 0x0002
+#define ETP_DISABLE_CALIBRATE 0x0000
+
+/* Elan smbus command */
+#define ETP_SMBUS_IAP_CMD 0x00
+#define ETP_SMBUS_ENABLE_TP 0x20
+#define ETP_SMBUS_SLEEP_CMD 0x21
+#define ETP_SMBUS_IAP_PASSWORD_WRITE 0x29
+#define ETP_SMBUS_IAP_PASSWORD_READ 0x80
+#define ETP_SMBUS_WRITE_FW_BLOCK 0x2A
+#define ETP_SMBUS_IAP_RESET_CMD 0x2B
+#define ETP_SMBUS_RANGE_CMD 0xA0
+#define ETP_SMBUS_FW_VERSION_CMD 0xA1
+#define ETP_SMBUS_XY_TRACENUM_CMD 0xA2
+#define ETP_SMBUS_SM_VERSION_CMD 0xA3
+#define ETP_SMBUS_UNIQUEID_CMD 0xA3
+#define ETP_SMBUS_RESOLUTION_CMD 0xA4
+#define ETP_SMBUS_HELLOPACKET_CMD 0xA7
+#define ETP_SMBUS_PACKET_QUERY 0xA8
+#define ETP_SMBUS_IAP_VERSION_CMD 0xAC
+#define ETP_SMBUS_IAP_CTRL_CMD 0xAD
+#define ETP_SMBUS_IAP_CHECKSUM_CMD 0xAE
+#define ETP_SMBUS_FW_CHECKSUM_CMD 0xAF
+#define ETP_SMBUS_MAX_BASELINE_CMD 0xC3
+#define ETP_SMBUS_MIN_BASELINE_CMD 0xC4
+#define ETP_SMBUS_CALIBRATE_QUERY 0xC5
+#define ETP_SMBUS_REPORT_LEN 32
+#define ETP_SMBUS_FINGER_DATA_OFFSET 2
+#define ETP_SMBUS_HELLOPACKET_LEN 5
+#define ETP_SMBUS_IAP_PASSWORD 0x1234
+#define ETP_SMBUS_IAP_MODE_ON (1<<6)
+
+/* Elan i2c command */
+#define ETP_I2C_RESET 0x0100
+#define ETP_I2C_WAKE_UP 0x0800
+#define ETP_I2C_SLEEP 0x0801
+#define ETP_I2C_DESC_CMD 0x0001
+#define ETP_I2C_REPORT_DESC_CMD 0x0002
+#define ETP_I2C_STAND_CMD 0x0005
+#define ETP_I2C_UNIQUEID_CMD 0x0101
+#define ETP_I2C_FW_VERSION_CMD 0x0102
+#define ETP_I2C_SM_VERSION_CMD 0x0103
+#define ETP_I2C_XY_TRACENUM_CMD 0x0105
+#define ETP_I2C_MAX_X_AXIS_CMD 0x0106
+#define ETP_I2C_MAX_Y_AXIS_CMD 0x0107
+#define ETP_I2C_RESOLUTION_CMD 0x0108
+#define ETP_I2C_IAP_VERSION_CMD 0x0110
+#define ETP_I2C_SET_CMD 0x0300
+#define ETP_I2C_MAX_BASELINE_CMD 0x0306
+#define ETP_I2C_MIN_BASELINE_CMD 0x0307
+#define ETP_I2C_FW_CHECKSUM_CMD 0x030F
+#define ETP_I2C_IAP_CTRL_CMD 0x0310
+#define ETP_I2C_IAP_CMD 0x0311
+#define ETP_I2C_IAP_RESET_CMD 0x0314
+#define ETP_I2C_IAP_CHECKSUM_CMD 0x0315
+#define ETP_I2C_CALIBRATE_CMD 0x0316
+#define ETP_I2C_REPORT_LEN 34
+#define ETP_I2C_FINGER_DATA_OFFSET 4
+#define ETP_I2C_REPORT_ID_OFFSET 2
+#define ETP_I2C_DESC_LENGTH 30
+#define ETP_I2C_REPORT_DESC_LENGTH 158
+#define ETP_I2C_IAP_PASSWORD 0x1EA5
+#define ETP_I2C_IAP_RESET 0xF0F0
+#define ETP_I2C_MAIN_MODE_ON (1<<9)
+#define ETP_I2C_IAP_REG_L 0x01
+#define ETP_I2C_IAP_REG_H 0x06
+
+/* The main device structure */
+struct elan_tp_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ unsigned int max_x;
+ unsigned int max_y;
+ unsigned int width_x;
+ unsigned int width_y;
+ unsigned int irq;
+ u16 unique_id;
+ u16 fw_version;
+ u16 sm_version;
+ u16 iap_version;
+ bool smbus;
+};
+
+/*
+ *******************************************************************
+ * Elan smbus interface
+ *******************************************************************
+ */
+static int elan_smbus_initialize(struct i2c_client *client)
+{
+ u8 check[ETP_SMBUS_HELLOPACKET_LEN] = {0x55, 0x55, 0x55, 0x55, 0x55};
+ u8 values[ETP_SMBUS_HELLOPACKET_LEN] = {0, 0, 0, 0, 0};
+ int ret;
+
+ /* Get hello packet */
+ ret = i2c_smbus_read_block_data(client,
+ ETP_SMBUS_HELLOPACKET_CMD, values);
+ if (ret != ETP_SMBUS_HELLOPACKET_LEN) {
+ dev_err(&client->dev, "hello packet length fail\n");
+ return -1;
+ }
+
+ /* compare hello packet */
+ if (memcmp(values, check, ETP_SMBUS_HELLOPACKET_LEN)) {
+ dev_err(&client->dev, "hello packet fail [%x %x %x %x %x]\n",
+ values[0], values[1], values[2], values[3], values[4]);
+ return -1;
+ }
+
+ /* enable tp */
+ ret = i2c_smbus_write_byte(client, ETP_SMBUS_ENABLE_TP);
+ return ret;
+}
+
+static int elan_smbus_enable_absolute_mode(struct i2c_client *client)
+{
+ u8 cmd[4] = {0x00, 0x07, 0x00, ETP_ENABLE_ABS};
+
+ return i2c_smbus_write_block_data(client, ETP_SMBUS_IAP_CMD, 4, cmd);
+}
+
+/*
+ ******************************************************************
+ * Elan i2c interface
+ ******************************************************************
+ */
+static int elan_i2c_read_block(struct i2c_client *client,
+ u16 reg, u8 *val, u16 len)
+{
+ struct i2c_msg msgs[2];
+ u8 buf[2];
+ int ret;
+
+ buf[0] = reg & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+
+ msgs[0].addr = client->addr;
+ msgs[0].flags = client->flags & I2C_M_TEN;
+ msgs[0].len = 2;
+ msgs[0].buf = buf;
+
+ msgs[1].addr = client->addr;
+ msgs[1].flags = client->flags & I2C_M_TEN;
+ msgs[1].flags |= I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = val;
+
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ return ret != 2 ? -EIO : 0;
+}
+
+static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 *val)
+{
+ int retval;
+
+ retval = elan_i2c_read_block(client, reg, val, ETP_INF_LENGTH);
+ if (retval < 0) {
+ dev_err(&client->dev, "reading cmd (0x%04x) fail.\n", reg);
+ return retval;
+ }
+ return 0;
+}
+
+static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd)
+{
+ struct i2c_msg msg;
+ u8 buf[4];
+ int ret;
+
+ buf[0] = reg & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+ buf[2] = cmd & 0xff;
+ buf[3] = (cmd >> 8) & 0xff;
+
+ msg.addr = client->addr;
+ msg.flags = client->flags & I2C_M_TEN;
+ msg.len = 4;
+ msg.buf = buf;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ return ret != 1 ? -EIO : 0;
+}
+
+static int elan_i2c_reset(struct i2c_client *client)
+{
+ return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
+ ETP_I2C_RESET);
+}
+
+static int elan_i2c_wake_up(struct i2c_client *client)
+{
+ return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
+ ETP_I2C_WAKE_UP);
+}
+
+static int elan_i2c_sleep(struct i2c_client *client)
+{
+ return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
+ ETP_I2C_SLEEP);
+}
+
+static int elan_i2c_enable_absolute_mode(struct i2c_client *client)
+{
+ return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
+ ETP_ENABLE_ABS);
+}
+
+static int elan_i2c_get_desc(struct i2c_client *client, u8 *val)
+{
+ return elan_i2c_read_block(client, ETP_I2C_DESC_CMD, val,
+ ETP_I2C_DESC_LENGTH);
+}
+
+static int elan_i2c_get_report_desc(struct i2c_client *client, u8 *val)
+{
+ return elan_i2c_read_block(client, ETP_I2C_REPORT_DESC_CMD,
+ val, ETP_I2C_REPORT_DESC_LENGTH);
+}
+
+static int elan_i2c_initialize(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ int rc;
+ u8 val[256];
+
+ rc = elan_i2c_reset(client);
+ if (rc < 0) {
+ dev_err(dev, "device reset failed.\n");
+ return -1;
+ }
+
+ /* wait for get reset return flag */
+ msleep(100);
+ /* get reset return flag 0000 */
+ rc = i2c_master_recv(client, val, ETP_INF_LENGTH);
+ if (rc < 0) {
+ dev_err(dev, "get device reset return value failed.\n");
+ return -1;
+ }
+
+ rc = elan_i2c_get_desc(client, val);
+ if (rc < 0) {
+ dev_err(dev, "cannot get device descriptor.\n");
+ return -1;
+ }
+
+ rc = elan_i2c_get_report_desc(client, val);
+ if (rc < 0) {
+ dev_err(dev, "fetching report descriptor failed.\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ ******************************************************************
+ * General functions
+ ******************************************************************
+ */
+/*
+ * (value from firmware) * 10 + 790 = dpi
+ * we also have to convert dpi to dots/mm (*10/254 to avoid floating point)
+ */
+static unsigned int elan_convert_res(char val)
+{
+ int res;
+ if (val & 0x80) {
+ val = ~val + 1;
+ res = (790 - val * 10) * 10 / 254;
+ } else
+ res = (val * 10 + 790) * 10 / 254;
+ return res;
+}
+
+static int elan_get_iap_version(struct elan_tp_data *data)
+{
+ int ret;
+ u8 val[3];
+ if (data->smbus) {
+ i2c_smbus_read_block_data(data->client,
+ ETP_SMBUS_IAP_VERSION_CMD, val);
+ ret = val[2];
+ } else {
+ elan_i2c_read_cmd(data->client,
+ ETP_I2C_IAP_VERSION_CMD, val);
+ ret = val[0];
+ }
+ return ret;
+}
+
+static int elan_get_x_max(struct elan_tp_data *data)
+{
+ int ret;
+ u8 val[3];
+ if (data->smbus) {
+ i2c_smbus_read_block_data(data->client,
+ ETP_SMBUS_RANGE_CMD, val);
+ ret = (0x0f & val[0]) << 8 | val[1];
+ } else {
+ elan_i2c_read_cmd(data->client,
+ ETP_I2C_MAX_X_AXIS_CMD, val);
+ ret = (0x0f & val[1]) << 8 | val[0];
+ }
+ return ret;
+}
+
+static int elan_get_y_max(struct elan_tp_data *data)
+{
+ int ret;
+ u8 val[3];
+ if (data->smbus) {
+ i2c_smbus_read_block_data(data->client,
+ ETP_SMBUS_RANGE_CMD, val);
+ ret = (0xf0 & val[0]) << 4 | val[2];
+ } else {
+ elan_i2c_read_cmd(data->client,
+ ETP_I2C_MAX_Y_AXIS_CMD, val);
+ ret = (0x0f & val[1]) << 8 | val[0];
+ }
+ return ret;
+}
+
+static int elan_get_x_tracenum(struct elan_tp_data *data)
+{
+ int ret;
+ u8 val[3];
+ if (data->smbus) {
+ i2c_smbus_read_block_data(data->client,
+ ETP_SMBUS_XY_TRACENUM_CMD, val);
+ ret = (val[1] - 1);
+ } else {
+ elan_i2c_read_cmd(data->client,
+ ETP_I2C_XY_TRACENUM_CMD, val);
+ ret = (val[0] - 1);
+ }
+ return ret;
+}
+
+static int elan_get_y_tracenum(struct elan_tp_data *data)
+{
+ int ret;
+ u8 val[3];
+ if (data->smbus) {
+ i2c_smbus_read_block_data(data->client,
+ ETP_SMBUS_XY_TRACENUM_CMD, val);
+ ret = (val[2] - 1);
+ } else {
+ ret = elan_i2c_read_cmd(data->client,
+ ETP_I2C_XY_TRACENUM_CMD, val);
+ ret = (val[1] - 1);
+ }
+ return ret;
+}
+
+static int elan_get_fw_version(struct elan_tp_data *data)
+{
+ int ret;
+ u8 val[3];
+ if (data->smbus) {
+ i2c_smbus_read_block_data(data->client,
+ ETP_SMBUS_FW_VERSION_CMD, val);
+ ret = val[2];
+ } else {
+ elan_i2c_read_cmd(data->client,
+ ETP_I2C_FW_VERSION_CMD, val);
+ ret = val[0];
+ }
+ return ret;
+}
+
+static int elan_get_sm_version(struct elan_tp_data *data)
+{
+ int ret;
+ u8 val[3];
+ if (data->smbus)
+ i2c_smbus_read_block_data(data->client,
+ ETP_SMBUS_SM_VERSION_CMD, val);
+ else
+ elan_i2c_read_block(data->client,
+ ETP_I2C_SM_VERSION_CMD, val, 1);
+ ret = val[0];
+ return ret;
+}
+
+static int elan_get_unique_id(struct elan_tp_data *data)
+{
+ int ret;
+ u8 val[3];
+ if (data->smbus) {
+ i2c_smbus_read_block_data(data->client,
+ ETP_SMBUS_UNIQUEID_CMD, val);
+ ret = val[1];
+ } else {
+ elan_i2c_read_cmd(data->client,
+ ETP_I2C_UNIQUEID_CMD, val);
+ ret = val[0];
+ }
+ return ret;
+}
+
+static int elan_get_x_resolution(struct elan_tp_data *data)
+{
+ int ret;
+ u8 val[3];
+ if (data->smbus) {
+ i2c_smbus_read_block_data(data->client,
+ ETP_SMBUS_RESOLUTION_CMD, val);
+ ret = elan_convert_res(val[1] & 0x0F);
+ } else {
+ elan_i2c_read_cmd(data->client,
+ ETP_I2C_RESOLUTION_CMD, val);
+ ret = elan_convert_res(val[0]);
+ }
+ return ret;
+}
+
+static int elan_get_y_resolution(struct elan_tp_data *data)
+{
+ int ret;
+ u8 val[3];
+ if (data->smbus) {
+ i2c_smbus_read_block_data(data->client,
+ ETP_SMBUS_RESOLUTION_CMD, val);
+ ret = elan_convert_res((val[1] & 0xF0) >> 4);
+ } else {
+ elan_i2c_read_cmd(data->client,
+ ETP_I2C_RESOLUTION_CMD, val);
+ ret = elan_convert_res(val[1]);
+ }
+ return ret;
+}
+
+static int elan_initialize(struct elan_tp_data *data)
+{
+ int ret;
+ if (data->smbus) {
+ ret = elan_smbus_initialize(data->client);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "device initialize failed.\n");
+ goto err_initialize;
+ }
+
+ ret = elan_smbus_enable_absolute_mode(data->client);
+ if (ret < 0)
+ dev_err(&data->client->dev,
+ "cannot switch to absolute mode.\n");
+ } else {
+ ret = elan_i2c_initialize(data->client);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "device initialize failed.\n");
+ goto err_initialize;
+ }
+
+ ret = elan_i2c_enable_absolute_mode(data->client);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "cannot switch to absolute mode.\n");
+ goto err_initialize;
+ }
+
+ ret = elan_i2c_wake_up(data->client);
+ if (ret < 0)
+ dev_err(&data->client->dev,
+ "device wake up failed.\n");
+ }
+err_initialize:
+ return ret;
+}
+
+
+/*
+ ******************************************************************
+ * Elan isr functions
+ ******************************************************************
+ */
+static int elan_check_packet(struct elan_tp_data *data, u8 *packet)
+{
+ u8 rid;
+
+ if (data->smbus)
+ rid = packet[0];
+ else
+ rid = packet[ETP_I2C_REPORT_ID_OFFSET];
+
+ /* check report id */
+ if (rid != ETP_REPORT_ID) {
+ dev_err(&data->client->dev, "report id [%x] fail.\n", rid);
+ return -1;
+ }
+ return 0;
+}
+
+static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
+{
+ struct input_dev *input = data->input;
+ u8 *finger_data;
+ bool finger_on;
+ int pos_x, pos_y;
+ int pressure, mk_x, mk_y;
+ int i, area_x, area_y, major, minor, new_pressure;
+ int finger_count = 0;
+ int btn_click;
+ u8 tp_info;
+
+ if (data->smbus) {
+ finger_data = &packet[ETP_SMBUS_FINGER_DATA_OFFSET];
+ tp_info = packet[1];
+ } else {
+ finger_data = &packet[ETP_I2C_FINGER_DATA_OFFSET];
+ tp_info = packet[3];
+ }
+
+ btn_click = (tp_info & 0x01);
+ for (i = 0; i < ETP_MAX_FINGERS; i++) {
+ finger_on = (tp_info >> (3 + i)) & 0x01;
+
+ /* analyze touched finger raw data*/
+ if (finger_on) {
+ pos_x = ((finger_data[0] & 0xf0) << 4) |
+ finger_data[1];
+ pos_y = ((finger_data[0] & 0x0f) << 8) |
+ finger_data[2];
+ pos_y = data->max_y - pos_y;
+ mk_x = (finger_data[3] & 0x0f);
+ mk_y = (finger_data[3] >> 4);
+ pressure = finger_data[4];
+
+ /*
+ * to avoid fat finger be as palm, so reduce the
+ * width x and y per trace
+ */
+ area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE);
+ area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE);
+
+ major = max(area_x, area_y);
+ minor = min(area_x, area_y);
+
+ new_pressure = pressure + ETP_PRESSURE_OFFSET;
+ if (new_pressure > ETP_MAX_PRESSURE)
+ new_pressure = ETP_MAX_PRESSURE;
+
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER,
+ true);
+ input_report_abs(input, ABS_MT_POSITION_X, pos_x);
+ input_report_abs(input, ABS_MT_POSITION_Y, pos_y);
+ input_report_abs(input, ABS_MT_PRESSURE, new_pressure);
+ input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
+ input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
+ finger_data += ETP_FINGER_DATA_LEN;
+ finger_count++;
+ } else {
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input,
+ MT_TOOL_FINGER, false);
+ }
+ }
+
+ input_report_key(input, BTN_LEFT, (btn_click == 1));
+ input_mt_report_pointer_emulation(input, true);
+ input_sync(input);
+}
+
+static irqreturn_t elan_isr(int irq, void *dev_id)
+{
+ struct elan_tp_data *data = dev_id;
+ u8 raw[ETP_MAX_REPORT_LEN];
+ int retval;
+ int report_len;
+
+ if (data->smbus) {
+ report_len = ETP_SMBUS_REPORT_LEN;
+ retval = i2c_smbus_read_block_data(data->client,
+ ETP_SMBUS_PACKET_QUERY,
+ raw);
+ } else {
+ report_len = ETP_I2C_REPORT_LEN;
+ retval = i2c_master_recv(data->client, raw, report_len);
+ }
+
+ if (retval != report_len) {
+ dev_err(&data->client->dev, "wrong packet len(%d)", retval);
+ goto elan_isr_end;
+ }
+
+ if (elan_check_packet(data, raw) < 0) {
+ dev_err(&data->client->dev, "wrong packet format.");
+ goto elan_isr_end;
+ }
+ elan_report_absolute(data, raw);
+
+elan_isr_end:
+ return IRQ_HANDLED;
+}
+
+/*
+ ******************************************************************
+ * Elan initial functions
+ ******************************************************************
+ */
+static int elan_input_dev_create(struct elan_tp_data *data)
+{
+ struct i2c_client *client = data->client;
+ struct input_dev *input;
+ unsigned int x_res, y_res;
+ int ret, max_width, min_width;
+
+ data->input = input = input_allocate_device();
+ if (!input)
+ return -ENOMEM;
+ input->name = "Elan Touchpad";
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &data->client->dev;
+
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+ __set_bit(BTN_LEFT, input->keybit);
+
+ data->unique_id = elan_get_unique_id(data);
+ data->fw_version = elan_get_fw_version(data);
+ data->sm_version = elan_get_sm_version(data);
+ data->iap_version = elan_get_iap_version(data);
+ data->max_x = elan_get_x_max(data);
+ data->max_y = elan_get_y_max(data);
+ data->width_x = data->max_x / elan_get_x_tracenum(data);
+ data->width_y = data->max_y / elan_get_y_tracenum(data);
+ x_res = elan_get_x_resolution(data);
+ y_res = elan_get_y_resolution(data);
+ max_width = max(data->width_x, data->width_y);
+ min_width = min(data->width_x, data->width_y);
+
+ dev_dbg(&client->dev,
+ "Elan Touchpad Information:\n"
+ " Module unique ID: 0x%04x\n"
+ " Firmware Version: 0x%04x\n"
+ " Sample Version: 0x%04x\n"
+ " IAP Version: 0x%04x\n"
+ " Max ABS X,Y: %d,%d\n"
+ " Width X,Y: %d,%d\n"
+ " Resolution X,Y: %d,%d (dots/mm)\n",
+ data->unique_id,
+ data->fw_version,
+ data->sm_version,
+ data->iap_version,
+ data->max_x, data->max_y,
+ data->width_x, data->width_y,
+ x_res, y_res);
+
+ input_set_abs_params(input, ABS_X, 0, data->max_x, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, data->max_y, 0, 0);
+ input_abs_set_res(input, ABS_X, x_res);
+ input_abs_set_res(input, ABS_Y, y_res);
+ input_set_abs_params(input, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0);
+ input_set_abs_params(input, ABS_TOOL_WIDTH, 0, ETP_FINGER_WIDTH, 0, 0);
+
+ /* handle pointer emulation and unused slots in core */
+ ret = input_mt_init_slots(input, ETP_MAX_FINGERS,
+ INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
+ if (ret) {
+ dev_err(&client->dev, "allocate MT slots failed, %d\n", ret);
+ goto err_free_device;
+ }
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, data->max_x, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, data->max_y, 0, 0);
+ input_abs_set_res(input, ABS_MT_POSITION_X, x_res);
+ input_abs_set_res(input, ABS_MT_POSITION_Y, y_res);
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0,
+ ETP_MAX_PRESSURE, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
+ ETP_FINGER_WIDTH * max_width, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0,
+ ETP_FINGER_WIDTH * min_width, 0, 0);
+
+ /* Register the device in input subsystem */
+ ret = input_register_device(input);
+ if (ret) {
+ dev_err(&client->dev, "input_dev register failed, %d\n", ret);
+ goto err_free_device;
+ }
+
+ return 0;
+
+err_free_device:
+ input_free_device(input);
+ return ret;
+}
+
+static u8 elan_check_adapter_functionality(struct i2c_client *client)
+{
+ u8 ret = ELAN_ADAPTER_FUNC_NONE;
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ ret |= ELAN_ADAPTER_FUNC_I2C;
+ if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK))
+ ret |= ELAN_ADAPTER_FUNC_SMBUS;
+ return ret;
+}
+
+static int elan_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ struct elan_tp_data *data;
+ int ret;
+ u8 adapter_func;
+ union i2c_smbus_data dummy;
+ struct device *dev = &client->dev;
+
+ adapter_func = elan_check_adapter_functionality(client);
+ if (adapter_func == ELAN_ADAPTER_FUNC_NONE) {
+ dev_err(dev, "not a supported I2C/SMBus adapter\n");
+ return -EIO;
+ }
+
+ /* Make sure there is something at this address */
+ if (i2c_smbus_xfer(client->adapter, client->addr, 0,
+ I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0)
+ return -ENODEV;
+
+ data = kzalloc(sizeof(struct elan_tp_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* check protocol type */
+ if (adapter_func == ELAN_ADAPTER_FUNC_SMBUS)
+ data->smbus = true;
+ else
+ data->smbus = false;
+ data->client = client;
+ data->irq = client->irq;
+
+ ret = request_threaded_irq(client->irq, NULL, elan_isr,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ client->name, data);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot register irq=%d\n",
+ client->irq);
+ goto err_irq;
+ }
+
+ /* initial elan touch pad */
+ ret = elan_initialize(data);
+ if (ret < 0)
+ goto err_init;
+
+ /* create input device */
+ ret = elan_input_dev_create(data);
+ if (ret < 0)
+ goto err_input_dev;
+
+ device_init_wakeup(&client->dev, 1);
+ i2c_set_clientdata(client, data);
+ return 0;
+
+err_input_dev:
+err_init:
+ free_irq(data->irq, data);
+err_irq:
+ kfree(data);
+ dev_err(&client->dev, "Elan Trackpad probe fail!\n");
+ return ret;
+}
+
+static int elan_remove(struct i2c_client *client)
+{
+ struct elan_tp_data *data = i2c_get_clientdata(client);
+ free_irq(data->irq, data);
+ input_unregister_device(data->input);
+ kfree(data);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int elan_suspend(struct device *dev)
+{
+ int ret = 0;
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+
+ disable_irq(data->irq);
+ if (data->smbus)
+ ret = i2c_smbus_write_byte(data->client,
+ ETP_SMBUS_SLEEP_CMD);
+ else
+ ret = elan_i2c_sleep(data->client);
+
+ if (ret < 0)
+ dev_err(dev, "suspend mode failed, %d\n", ret);
+
+ return ret;
+}
+
+static int elan_resume(struct device *dev)
+{
+ int ret = 0;
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+
+ ret = elan_initialize(data);
+ if (ret < 0)
+ dev_err(dev, "resume active power failed, %d\n", ret);
+
+ enable_irq(data->irq);
+ return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);
+
+static const struct i2c_device_id elan_id[] = {
+ { DRIVER_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, elan_id);
+
+static struct i2c_driver elan_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &elan_pm_ops,
+ },
+ .probe = elan_probe,
+ .remove = elan_remove,
+ .id_table = elan_id,
+};
+
+module_i2c_driver(elan_driver);
+
+MODULE_AUTHOR("Duson Lin <dusonlin@emc.com.tw>");
+MODULE_DESCRIPTION("Elan I2C/SMBus Touchpad driver");
+MODULE_LICENSE("GPL");
--
1.7.10.4
^ permalink raw reply related
* [RFC Patch v1 10/13] ACPI, i2c-hid: replace open-coded _DSM specific code with helper functions
From: Jiang Liu @ 2013-12-18 6:58 UTC (permalink / raw)
To: Rafael J . Wysocki, Bjorn Helgaas, Lv Zheng, Len Brown,
Leonidas Da Silva Barbosa, Ashley Lai, Peter Huewe, Rajiv Andrade,
Marcel Selhorst, Sirrix AG, Daniel Vetter, David Airlie,
Jiri Kosina, Benjamin Tissoires, Mika Westerberg,
Greg Kroah-Hartman, linux-input, linux-kernel
Cc: Jiang Liu, Tony Luck
In-Reply-To: <1387349901-3391-1-git-send-email-jiang.liu@linux.intel.com>
Use helper functions to simplify _DSM related code in i2c-hid driver.
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
---
drivers/hid/i2c-hid/i2c-hid.c | 26 ++++++--------------------
1 file changed, 6 insertions(+), 20 deletions(-)
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index 5f7e55f..d22668f 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -850,37 +850,23 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,
0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45,
0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE,
};
- union acpi_object params[4];
- struct acpi_object_list input;
+ union acpi_object *obj;
struct acpi_device *adev;
- unsigned long long value;
acpi_handle handle;
handle = ACPI_HANDLE(&client->dev);
if (!handle || acpi_bus_get_device(handle, &adev))
return -ENODEV;
- input.count = ARRAY_SIZE(params);
- input.pointer = params;
-
- params[0].type = ACPI_TYPE_BUFFER;
- params[0].buffer.length = sizeof(i2c_hid_guid);
- params[0].buffer.pointer = i2c_hid_guid;
- params[1].type = ACPI_TYPE_INTEGER;
- params[1].integer.value = 1;
- params[2].type = ACPI_TYPE_INTEGER;
- params[2].integer.value = 1; /* HID function */
- params[3].type = ACPI_TYPE_PACKAGE;
- params[3].package.count = 0;
- params[3].package.elements = NULL;
-
- if (ACPI_FAILURE(acpi_evaluate_integer(handle, "_DSM", &input,
- &value))) {
+ obj = acpi_evaluate_dsm_typed(handle, i2c_hid_guid, 1, 1, NULL,
+ ACPI_TYPE_INTEGER);
+ if (!obj) {
dev_err(&client->dev, "device _DSM execution failed\n");
return -ENODEV;
}
- pdata->hid_descriptor_address = value;
+ pdata->hid_descriptor_address = obj->integer.value;
+ ACPI_FREE(obj);
return 0;
}
--
1.7.10.4
^ permalink raw reply related
* Re: [PATCH 0/4] Input: ABS2 and friends
From: David Herrmann @ 2013-12-18 8:12 UTC (permalink / raw)
To: Simon Wood
Cc: open list:HID CORE LAYER, Dmitry Torokhov, Jiri Kosina,
Benjamin Tissoires, Peter Hutterer, Antonio Ospite, linux-kernel,
Input Tools
In-Reply-To: <190d226e64e177a3cb631b8fc6f4787c.squirrel@mungewell.org>
Hi
On Tue, Dec 17, 2013 at 10:28 PM, <simon@mungewell.org> wrote:
>> Hi
>>
>> This implements the recently discussed ABS2 API. It's working fine on my
>> machine with libevdev. Comments welcome!
>
> Just looking at the documentation file, I have a couple of suggestions.
>
> 1). Make a note on the direction of gravity wrt the example image. Ie what
> which axis/value will be positive with the device laid flat/stationary on
> a desk. Might just prevent some confusion later.
Yepp, can add that to the Accelerometer section.
> 2). Calibration, Accels and Gyros are noisy in different ways. Gyros tend
> to be wrong in the long term, giving some consistant bias which should be
> calibrated out. Accels are noisy in the short term, but generally right
> over a long period.
I'm not entirely sure where to put calibration/normalization. Letting
user-space deal with that should simplify things a lot. We could even
add it to libevdev. Comments welcome..
> Am I correct that the event framework will provide time-stamping of data,
> thus making computation of relative movements possible.
Yes, part of the evdev API.
Thanks
David
^ permalink raw reply
* [PATCH 2/9] Input: pixcir_i2c_ts: Add register definitions
From: Roger Quadros @ 2013-12-18 9:21 UTC (permalink / raw)
To: dmitry.torokhov
Cc: rydberg, jcbian, linux-input, linux-kernel, devicetree,
Roger Quadros
In-Reply-To: <1387358480-8313-1-git-send-email-rogerq@ti.com>
Add power and interrupt register definitions.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
---
include/linux/input/pixcir_ts.h | 42 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/include/linux/input/pixcir_ts.h b/include/linux/input/pixcir_ts.h
index b34ff7e..f17c192 100644
--- a/include/linux/input/pixcir_ts.h
+++ b/include/linux/input/pixcir_ts.h
@@ -1,6 +1,48 @@
#ifndef _PIXCIR_I2C_TS_H
#define _PIXCIR_I2C_TS_H
+/*
+ * Register map
+ */
+#define PIXCIR_REG_POWER_MODE 51
+#define PIXCIR_REG_INT_MODE 52
+
+/*
+ * Power modes:
+ * active: max scan speed
+ * idle: lower scan speed with automatic transition to active on touch
+ * halt: datasheet says sleep but this is more like halt as the chip
+ * clocks are cut and it can only be brought out of this mode
+ * using the RESET pin.
+ */
+enum pixcir_power_mode {
+ PIXCIR_POWER_ACTIVE,
+ PIXCIR_POWER_IDLE,
+ PIXCIR_POWER_HALT,
+};
+
+#define PIXCIR_POWER_MODE_MASK 0x03
+#define PIXCIR_POWER_ALLOW_IDLE (1UL << 2)
+
+/*
+ * Interrupt modes:
+ * periodical: interrupt is asserted periodicaly
+ * diff coordinates: interrupt is asserted when coordinates change
+ * level on touch: interrupt level asserted during touch
+ * pulse on touch: interrupt pulse asserted druing touch
+ *
+ */
+enum pixcir_int_mode {
+ PIXCIR_INT_PERIODICAL,
+ PIXCIR_INT_DIFF_COORD,
+ PIXCIR_INT_LEVEL_TOUCH,
+ PIXCIR_INT_PULSE_TOUCH,
+};
+
+#define PIXCIR_INT_MODE_MASK 0x03
+#define PIXCIR_INT_ENABLE (1UL << 3)
+#define PIXCIR_INT_POL_HIGH (1UL << 2)
+
struct pixcir_ts_platform_data {
int (*attb_read_val)(void);
unsigned int x_size; /* X axis resolution */
--
1.8.3.2
^ permalink raw reply related
* [PATCH 3/9] Input: pixcir_i2c_ts: Initialize interrupt mode and power mode
From: Roger Quadros @ 2013-12-18 9:21 UTC (permalink / raw)
To: dmitry.torokhov
Cc: rydberg, jcbian, linux-input, linux-kernel, devicetree,
Roger Quadros
In-Reply-To: <1387358480-8313-1-git-send-email-rogerq@ti.com>
Introduce helper functions to configure power and interrupt
registers. Default to IDLE mode on probe as device supports
auto wakeup to ACVIE mode on detecting finger touch.
Configure interrupt mode and polarity on start up.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
---
drivers/input/touchscreen/pixcir_i2c_ts.c | 168 ++++++++++++++++++++++++++++++
1 file changed, 168 insertions(+)
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index 3a447c9..ce8abcd 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -104,6 +104,160 @@ static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts,
+ enum pixcir_power_mode mode)
+{
+ struct device *dev = &ts->client->dev;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE);
+ if (ret < 0) {
+ dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+ __func__, PIXCIR_REG_POWER_MODE, ret);
+ return ret;
+ }
+
+ ret &= ~PIXCIR_POWER_MODE_MASK;
+ ret |= mode;
+
+ /* Always AUTO_IDLE */
+ ret |= PIXCIR_POWER_ALLOW_IDLE;
+
+ ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_POWER_MODE, ret);
+ if (ret < 0) {
+ dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+ __func__, PIXCIR_REG_POWER_MODE, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Set the interrupt mode for the device i.e. ATTB line behaviour
+ *
+ * @polarity : 1 for active high, 0 for active low.
+ */
+static int pixcir_set_int_mode(struct pixcir_i2c_ts_data *ts,
+ enum pixcir_int_mode mode,
+ bool polarity)
+{
+ struct device *dev = &ts->client->dev;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE);
+ if (ret < 0) {
+ dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+ __func__, PIXCIR_REG_INT_MODE, ret);
+ return ret;
+ }
+
+ ret &= ~PIXCIR_INT_MODE_MASK;
+ ret |= mode;
+
+ if (polarity)
+ ret |= PIXCIR_INT_POL_HIGH;
+ else
+ ret &= ~PIXCIR_INT_POL_HIGH;
+
+ ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret);
+ if (ret < 0) {
+ dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+ __func__, PIXCIR_REG_INT_MODE, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Enable/disable interrupt generation
+ */
+static int pixcir_int_enable(struct pixcir_i2c_ts_data *ts, bool enable)
+{
+ struct device *dev = &ts->client->dev;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE);
+ if (ret < 0) {
+ dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+ __func__, PIXCIR_REG_INT_MODE, ret);
+ return ret;
+ }
+
+ if (enable)
+ ret |= PIXCIR_INT_ENABLE;
+ else
+ ret &= ~PIXCIR_INT_ENABLE;
+
+ ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret);
+ if (ret < 0) {
+ dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+ __func__, PIXCIR_REG_INT_MODE, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pixcir_start(struct pixcir_i2c_ts_data *ts)
+{
+ struct device *dev = &ts->client->dev;
+ int ret;
+
+ /* LEVEL_TOUCH interrupt with active low polarity */
+ ret = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0);
+ if (ret) {
+ dev_err(dev, "Failed to set interrupt mode\n");
+ return ret;
+ }
+
+ enable_irq(ts->client->irq);
+
+ /* enable interrupt generation */
+ ret = pixcir_int_enable(ts, 1);
+ if (ret) {
+ dev_err(dev, "Failed to enable interrupt generation\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pixcir_stop(struct pixcir_i2c_ts_data *ts)
+{
+ struct device *dev = &ts->client->dev;
+ int ret;
+
+ /* disable interrupt generation */
+ ret = pixcir_int_enable(ts, 0);
+ if (ret) {
+ dev_err(dev, "Failed to disable interrupt generation\n");
+ return ret;
+ }
+
+ disable_irq(ts->client->irq);
+
+ return 0;
+}
+
+static int pixcir_input_open(struct input_dev *dev)
+{
+ struct pixcir_i2c_ts_data *ts = input_get_drvdata(dev);
+
+ return pixcir_start(ts);
+}
+
+static void pixcir_input_close(struct input_dev *dev)
+{
+ struct pixcir_i2c_ts_data *ts = input_get_drvdata(dev);
+
+ pixcir_stop(ts);
+
+ return;
+}
+
+
#ifdef CONFIG_PM_SLEEP
static int pixcir_i2c_ts_suspend(struct device *dev)
{
@@ -207,6 +361,8 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
input->name = client->name;
input->id.bustype = BUS_I2C;
input->dev.parent = &client->dev;
+ input->open = pixcir_input_open;
+ input->close = pixcir_input_close;
__set_bit(EV_KEY, input->evbit);
__set_bit(EV_ABS, input->evbit);
@@ -230,6 +386,18 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
goto err_free_mem;
}
+ /* Always be in IDLE mode to save power, device supports auto wake */
+ error = pixcir_set_power_mode(tsdata, PIXCIR_POWER_IDLE);
+ if (error) {
+ dev_err(dev, "Failed to set IDLE mode\n");
+ return error;
+ }
+
+ /* Stop device till opened */
+ error = pixcir_stop(tsdata);
+ if (error)
+ goto err_free_irq;
+
error = input_register_device(input);
if (error)
goto err_free_irq;
--
1.8.3.2
^ permalink raw reply related
* [PATCH 4/9] Input: pixcir_i2c_ts: Use devres managed resource allocations
From: Roger Quadros @ 2013-12-18 9:21 UTC (permalink / raw)
To: dmitry.torokhov
Cc: rydberg, jcbian, linux-input, linux-kernel, devicetree,
Roger Quadros
In-Reply-To: <1387358480-8313-1-git-send-email-rogerq@ti.com>
Use devm_() and friends for allocating memory, input device
and IRQ.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
---
drivers/input/touchscreen/pixcir_i2c_ts.c | 35 ++++++++++++-------------------
1 file changed, 13 insertions(+), 22 deletions(-)
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index ce8abcd..3370fd9 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -346,12 +346,14 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
return -EINVAL;
}
- tsdata = kzalloc(sizeof(*tsdata), GFP_KERNEL);
- input = input_allocate_device();
- if (!tsdata || !input) {
- dev_err(&client->dev, "Failed to allocate driver data!\n");
- error = -ENOMEM;
- goto err_free_mem;
+ tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL);
+ if (!tsdata)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(dev);
+ if (!input) {
+ dev_err(&client->dev, "Failed to allocate input device\n");
+ return -ENOMEM;
}
tsdata->client = client;
@@ -378,12 +380,12 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
input_set_drvdata(input, tsdata);
- error = request_threaded_irq(client->irq, NULL, pixcir_ts_isr,
+ error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
client->name, tsdata);
if (error) {
- dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
- goto err_free_mem;
+ dev_err(dev, "failed to request irq %d\n", client->irq);
+ return error;
}
/* Always be in IDLE mode to save power, device supports auto wake */
@@ -396,23 +398,16 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
/* Stop device till opened */
error = pixcir_stop(tsdata);
if (error)
- goto err_free_irq;
+ return error;
error = input_register_device(input);
if (error)
- goto err_free_irq;
+ return error;
i2c_set_clientdata(client, tsdata);
device_init_wakeup(&client->dev, 1);
return 0;
-
-err_free_irq:
- free_irq(client->irq, tsdata);
-err_free_mem:
- input_free_device(input);
- kfree(tsdata);
- return error;
}
static int pixcir_i2c_ts_remove(struct i2c_client *client)
@@ -423,10 +418,6 @@ static int pixcir_i2c_ts_remove(struct i2c_client *client)
tsdata->exiting = true;
mb();
- free_irq(client->irq, tsdata);
-
- input_unregister_device(tsdata->input);
- kfree(tsdata);
return 0;
}
--
1.8.3.2
^ permalink raw reply related
* [PATCH 5/9] Input: pixcir_i2c_ts: Get rid of pdata->attb_read_val()
From: Roger Quadros @ 2013-12-18 9:21 UTC (permalink / raw)
To: dmitry.torokhov
Cc: rydberg, jcbian, linux-input, linux-kernel, devicetree,
Roger Quadros
In-Reply-To: <1387358480-8313-1-git-send-email-rogerq@ti.com>
Get rid of the attb_read_val() platform hook. Instead,
read the ATTB gpio directly from the driver.
Fail if valid ATTB gpio is not provided by patform data.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
---
drivers/input/touchscreen/pixcir_i2c_ts.c | 19 +++++++++++++++++--
include/linux/input/pixcir_ts.h | 1 -
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index 3370fd9..a783d94 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -91,11 +91,12 @@ static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
{
struct pixcir_i2c_ts_data *tsdata = dev_id;
+ const struct pixcir_ts_platform_data *pdata = tsdata->chip;
while (!tsdata->exiting) {
pixcir_ts_poscheck(tsdata);
- if (tsdata->chip->attb_read_val())
+ if (gpio_get_value(pdata->gpio_attb))
break;
msleep(20);
@@ -301,8 +302,10 @@ static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev)
return ERR_PTR(-ENOMEM);
pdata->gpio_attb = of_get_named_gpio(np, "attb-gpio", 0);
- if (!gpio_is_valid(pdata->gpio_attb))
+ if (!gpio_is_valid(pdata->gpio_attb)) {
dev_err(dev, "Failed to get ATTB GPIO\n");
+ return ERR_PTR(-EINVAL);
+ }
if (of_property_read_u32(np, "x-size", &pdata->x_size)) {
dev_err(dev, "Failed to get x-size property\n");
@@ -344,6 +347,11 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
} else if (!pdata) {
dev_err(&client->dev, "platform data not defined\n");
return -EINVAL;
+ } else {
+ if (!gpio_is_valid(pdata->gpio_attb)) {
+ dev_err(dev, "Invalid gpio_attb in pdata\n");
+ return -EINVAL;
+ }
}
tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL);
@@ -380,6 +388,13 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
input_set_drvdata(input, tsdata);
+ error = devm_gpio_request_one(dev, pdata->gpio_attb,
+ GPIOF_DIR_IN, "pixcir_i2c_attb");
+ if (error) {
+ dev_err(dev, "Failed to request ATTB gpio\n");
+ return error;
+ }
+
error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
client->name, tsdata);
diff --git a/include/linux/input/pixcir_ts.h b/include/linux/input/pixcir_ts.h
index f17c192..88ffdb50 100644
--- a/include/linux/input/pixcir_ts.h
+++ b/include/linux/input/pixcir_ts.h
@@ -44,7 +44,6 @@ enum pixcir_int_mode {
#define PIXCIR_INT_POL_HIGH (1UL << 2)
struct pixcir_ts_platform_data {
- int (*attb_read_val)(void);
unsigned int x_size; /* X axis resolution */
unsigned int y_size; /* Y axis resolution */
int gpio_attb; /* GPIO connected to ATTB line */
--
1.8.3.2
^ permalink raw reply related
* [PATCH 6/9] Input: pixcir_i2c_ts: Add chip specific data structure
From: Roger Quadros @ 2013-12-18 9:21 UTC (permalink / raw)
To: dmitry.torokhov
Cc: rydberg, jcbian, linux-input, linux-kernel, devicetree,
Roger Quadros
In-Reply-To: <1387358480-8313-1-git-send-email-rogerq@ti.com>
This is the data that differentiates different pixcir
chips.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
---
drivers/input/touchscreen/pixcir_i2c_ts.c | 8 +++++---
include/linux/input/pixcir_ts.h | 11 +++++++++++
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index a783d94..ff68246 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -32,7 +32,7 @@
struct pixcir_i2c_ts_data {
struct i2c_client *client;
struct input_dev *input;
- const struct pixcir_ts_platform_data *chip;
+ const struct pixcir_ts_platform_data *pdata;
bool exiting;
};
@@ -91,7 +91,7 @@ static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
{
struct pixcir_i2c_ts_data *tsdata = dev_id;
- const struct pixcir_ts_platform_data *pdata = tsdata->chip;
+ const struct pixcir_ts_platform_data *pdata = tsdata->pdata;
while (!tsdata->exiting) {
pixcir_ts_poscheck(tsdata);
@@ -301,6 +301,8 @@ static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev)
if (!pdata)
return ERR_PTR(-ENOMEM);
+ pdata->chip = *(const struct pixcir_i2c_chip_data *)match->data;
+
pdata->gpio_attb = of_get_named_gpio(np, "attb-gpio", 0);
if (!gpio_is_valid(pdata->gpio_attb)) {
dev_err(dev, "Failed to get ATTB GPIO\n");
@@ -366,7 +368,7 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
tsdata->client = client;
tsdata->input = input;
- tsdata->chip = pdata;
+ tsdata->pdata = pdata;
input->name = client->name;
input->id.bustype = BUS_I2C;
diff --git a/include/linux/input/pixcir_ts.h b/include/linux/input/pixcir_ts.h
index 88ffdb50..b9a2f6f 100644
--- a/include/linux/input/pixcir_ts.h
+++ b/include/linux/input/pixcir_ts.h
@@ -43,10 +43,21 @@ enum pixcir_int_mode {
#define PIXCIR_INT_ENABLE (1UL << 3)
#define PIXCIR_INT_POL_HIGH (1UL << 2)
+/**
+ * struct pixcir_irc_chip_data - chip related data
+ * @num_report_ids: Max number of finger ids reported simultaneously.
+ * if 0 it means chip doesn't support finger id reporting
+ * and driver will resort to Type A Multi-Touch reporting.
+ */
+struct pixcir_i2c_chip_data {
+ u8 num_report_ids;
+};
+
struct pixcir_ts_platform_data {
unsigned int x_size; /* X axis resolution */
unsigned int y_size; /* Y axis resolution */
int gpio_attb; /* GPIO connected to ATTB line */
+ struct pixcir_i2c_chip_data chip;
};
#endif
--
1.8.3.2
^ permalink raw reply related
* [PATCH 7/9] Input: pixcir_i2c_ts: Implement Type B Multi Touch reporting
From: Roger Quadros @ 2013-12-18 9:21 UTC (permalink / raw)
To: dmitry.torokhov
Cc: rydberg, jcbian, linux-input, linux-kernel, devicetree,
Roger Quadros
In-Reply-To: <1387358480-8313-1-git-send-email-rogerq@ti.com>
Some pixcir controllers e.g. tangoC family report finger IDs with
the co-ordinates and are more suitable for Type-B MT protocol.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
---
drivers/input/touchscreen/pixcir_i2c_ts.c | 202 +++++++++++++++++++++++-------
1 file changed, 155 insertions(+), 47 deletions(-)
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index ff68246..9e14415 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -23,84 +23,173 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/input/pixcir_ts.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
+#define MAX_FINGERS 5 /* Maximum supported by the driver */
+
struct pixcir_i2c_ts_data {
struct i2c_client *client;
struct input_dev *input;
const struct pixcir_ts_platform_data *pdata;
bool exiting;
+ u8 max_fingers; /* Maximum supported by the chip */
};
-static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
+static void pixcir_ts_typea_report(struct pixcir_i2c_ts_data *tsdata)
{
- struct pixcir_i2c_ts_data *tsdata = data;
+ const struct pixcir_ts_platform_data *pdata = tsdata->pdata;
u8 rdbuf[10], wrbuf[1] = { 0 };
u8 touch;
int ret;
- ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf));
- if (ret != sizeof(wrbuf)) {
- dev_err(&tsdata->client->dev,
- "%s: i2c_master_send failed(), ret=%d\n",
- __func__, ret);
- return;
- }
+ while (!tsdata->exiting) {
- ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf));
- if (ret != sizeof(rdbuf)) {
- dev_err(&tsdata->client->dev,
- "%s: i2c_master_recv failed(), ret=%d\n",
- __func__, ret);
- return;
- }
+ ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf));
+ if (ret != sizeof(wrbuf)) {
+ dev_err(&tsdata->client->dev,
+ "%s: i2c_master_send failed(), ret=%d\n",
+ __func__, ret);
+ return;
+ }
+
+ ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf));
+ if (ret != sizeof(rdbuf)) {
+ dev_err(&tsdata->client->dev,
+ "%s: i2c_master_recv failed(), ret=%d\n",
+ __func__, ret);
+ return;
+ }
- touch = rdbuf[0];
- if (touch) {
- u16 posx1 = (rdbuf[3] << 8) | rdbuf[2];
- u16 posy1 = (rdbuf[5] << 8) | rdbuf[4];
- u16 posx2 = (rdbuf[7] << 8) | rdbuf[6];
- u16 posy2 = (rdbuf[9] << 8) | rdbuf[8];
-
- input_report_key(tsdata->input, BTN_TOUCH, 1);
- input_report_abs(tsdata->input, ABS_X, posx1);
- input_report_abs(tsdata->input, ABS_Y, posy1);
-
- input_report_abs(tsdata->input, ABS_MT_POSITION_X, posx1);
- input_report_abs(tsdata->input, ABS_MT_POSITION_Y, posy1);
- input_mt_sync(tsdata->input);
-
- if (touch == 2) {
- input_report_abs(tsdata->input,
- ABS_MT_POSITION_X, posx2);
- input_report_abs(tsdata->input,
- ABS_MT_POSITION_Y, posy2);
+ touch = rdbuf[0];
+ if (touch) {
+ u16 posx1 = (rdbuf[3] << 8) | rdbuf[2];
+ u16 posy1 = (rdbuf[5] << 8) | rdbuf[4];
+ u16 posx2 = (rdbuf[7] << 8) | rdbuf[6];
+ u16 posy2 = (rdbuf[9] << 8) | rdbuf[8];
+
+ input_report_key(tsdata->input, BTN_TOUCH, 1);
+ input_report_abs(tsdata->input, ABS_X, posx1);
+ input_report_abs(tsdata->input, ABS_Y, posy1);
+
+ input_report_abs(tsdata->input, ABS_MT_POSITION_X,
+ posx1);
+ input_report_abs(tsdata->input, ABS_MT_POSITION_Y,
+ posy1);
input_mt_sync(tsdata->input);
+
+ if (touch == 2) {
+ input_report_abs(tsdata->input,
+ ABS_MT_POSITION_X, posx2);
+ input_report_abs(tsdata->input,
+ ABS_MT_POSITION_Y, posy2);
+ input_mt_sync(tsdata->input);
+ }
+ } else {
+ input_report_key(tsdata->input, BTN_TOUCH, 0);
}
- } else {
- input_report_key(tsdata->input, BTN_TOUCH, 0);
- }
- input_sync(tsdata->input);
+ input_sync(tsdata->input);
+
+ if (gpio_get_value(pdata->gpio_attb))
+ break;
+
+ msleep(20);
+ }
}
-static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
+static void pixcir_ts_typeb_report(struct pixcir_i2c_ts_data *ts)
{
- struct pixcir_i2c_ts_data *tsdata = dev_id;
- const struct pixcir_ts_platform_data *pdata = tsdata->pdata;
+ const struct pixcir_ts_platform_data *pdata = ts->pdata;
+ struct device *dev = &ts->client->dev;
+ u8 rdbuf[32], wrbuf[1] = { 0 };
+ u8 *bufptr;
+ u8 num_fingers;
+ u8 unreliable;
+ int ret, i;
+
+ while (!ts->exiting) {
+
+ ret = i2c_master_send(ts->client, wrbuf, sizeof(wrbuf));
+ if (ret != sizeof(wrbuf)) {
+ dev_err(dev, "%s: i2c_master_send failed(), ret=%d\n",
+ __func__, ret);
+ return;
+ }
- while (!tsdata->exiting) {
- pixcir_ts_poscheck(tsdata);
+ ret = i2c_master_recv(ts->client, rdbuf, sizeof(rdbuf));
+ if (ret != sizeof(rdbuf)) {
+ dev_err(dev, "%s: i2c_master_recv failed(), ret=%d\n",
+ __func__, ret);
+ return;
+ }
+
+ unreliable = rdbuf[0] & 0xe0;
+
+ if (unreliable)
+ goto next; /* ignore unreliable data */
+
+ num_fingers = rdbuf[0] & 0x7;
+ bufptr = &rdbuf[2];
+ if (num_fingers > ts->max_fingers) {
+ num_fingers = ts->max_fingers;
+ dev_dbg(dev, "limiting num_fingers to %d\n",
+ num_fingers);
+ }
+
+ for (i = 0; i < num_fingers; i++) {
+ u8 id;
+ unsigned int x, y;
+ int slot;
+
+ id = bufptr[4];
+ slot = input_mt_get_slot_by_key(ts->input, id);
+ if (slot < 0) {
+ dev_dbg(dev, "no free slot for id 0x%x\n", id);
+ continue;
+ }
+
+
+ x = bufptr[1] << 8 | bufptr[0];
+ y = bufptr[3] << 8 | bufptr[2];
+
+ input_mt_slot(ts->input, slot);
+ input_mt_report_slot_state(ts->input,
+ MT_TOOL_FINGER, true);
+
+ input_event(ts->input, EV_ABS, ABS_MT_POSITION_X, x);
+ input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y, y);
+
+ bufptr = &bufptr[5];
+ dev_dbg(dev, "%d: id 0x%x slot %d, x %d, y %d\n",
+ i, id, slot, x, y);
+ }
+
+ /* One frame is complete so sync it */
+ input_mt_sync_frame(ts->input);
+ input_sync(ts->input);
+
+next:
if (gpio_get_value(pdata->gpio_attb))
break;
- msleep(20);
+ usleep_range(2000, 5000);
}
+}
+
+static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
+{
+ struct pixcir_i2c_ts_data *tsdata = dev_id;
+
+ if (tsdata->input->mt)
+ pixcir_ts_typeb_report(tsdata);
+ else
+ pixcir_ts_typea_report(tsdata);
return IRQ_HANDLED;
}
@@ -376,9 +465,9 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
input->open = pixcir_input_open;
input->close = pixcir_input_close;
- __set_bit(EV_KEY, input->evbit);
__set_bit(EV_ABS, input->evbit);
__set_bit(BTN_TOUCH, input->keybit);
+
input_set_abs_params(input, ABS_X,
0, pdata->x_size - 1, 0, 0);
input_set_abs_params(input, ABS_Y,
@@ -388,6 +477,25 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
input_set_abs_params(input, ABS_MT_POSITION_Y,
0, pdata->y_size - 1, 0, 0);
+ /* Type-B Multi-Touch support */
+ if (pdata->chip.num_report_ids) {
+ const struct pixcir_i2c_chip_data *chip = &pdata->chip;
+
+ tsdata->max_fingers = chip->num_report_ids;
+ if (tsdata->max_fingers > MAX_FINGERS) {
+ dev_info(dev, "Limiting maximum fingers to %d\n",
+ MAX_FINGERS);
+ tsdata->max_fingers = MAX_FINGERS;
+ }
+
+ error = input_mt_init_slots(input, tsdata->max_fingers,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+ if (error) {
+ dev_err(dev, "Error initializing Multi-Touch slots\n");
+ return error;
+ }
+ }
+
input_set_drvdata(input, tsdata);
error = devm_gpio_request_one(dev, pdata->gpio_attb,
--
1.8.3.2
^ permalink raw reply related
* [PATCH 8/9] Input: pixcir_i2c_ts: Add support for TangoC family
From: Roger Quadros @ 2013-12-18 9:21 UTC (permalink / raw)
To: dmitry.torokhov
Cc: rydberg, jcbian, linux-input, linux-kernel, devicetree,
Roger Quadros
In-Reply-To: <1387358480-8313-1-git-send-email-rogerq@ti.com>
Add support for Pixcir TangoC controller.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
---
.../devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt | 2 +-
drivers/input/touchscreen/pixcir_i2c_ts.c | 6 ++++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt b/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt
index c0b0b270..0ab9505 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt
@@ -1,7 +1,7 @@
* Pixcir I2C touchscreen controllers
Required properties:
-- compatible: must be "pixcir,pixcir_ts"
+- compatible: must be "pixcir,pixcir_ts" or "pixcir,pixcir_tangoc"
- reg: I2C address of the chip
- interrupts: interrupt to which the chip is connected
- attb-gpio: GPIO connected to the ATTB line of the chip
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index 9e14415..3575eab 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -549,13 +549,19 @@ static int pixcir_i2c_ts_remove(struct i2c_client *client)
static const struct i2c_device_id pixcir_i2c_ts_id[] = {
{ "pixcir_ts", 0 },
+ { "pixcir_tangoc", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id);
#if defined(CONFIG_OF)
+static const struct pixcir_i2c_chip_data tangoc_data = {
+ .num_report_ids = 5,
+};
+
static const struct of_device_id pixcir_of_match[] = {
{ .compatible = "pixcir,pixcir_ts", },
+ { .compatible = "pixcir,pixcir_tangoc", .data = &tangoc_data, },
{ }
};
MODULE_DEVICE_TABLE(of, pixcir_of_match);
--
1.8.3.2
^ permalink raw reply related
* [PATCH 9/9] Input: pixcir_i2c_ts: Implement wakeup from suspend
From: Roger Quadros @ 2013-12-18 9:21 UTC (permalink / raw)
To: dmitry.torokhov
Cc: rydberg, jcbian, linux-input, linux-kernel, devicetree,
Roger Quadros
In-Reply-To: <1387358480-8313-1-git-send-email-rogerq@ti.com>
Improve the suspend and resume handlers to allow the device
to wakeup the system from suspend.
Signed-off-by: Roger Quadros <rogerq@ti.com>
Acked-by: Mugunthan V N <mugunthanvnm@ti.com>
---
drivers/input/touchscreen/pixcir_i2c_ts.c | 89 ++++++++++++++++++++++---------
1 file changed, 63 insertions(+), 26 deletions(-)
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index 3575eab..a7725b9 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -347,32 +347,6 @@ static void pixcir_input_close(struct input_dev *dev)
return;
}
-
-#ifdef CONFIG_PM_SLEEP
-static int pixcir_i2c_ts_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
-
- if (device_may_wakeup(&client->dev))
- enable_irq_wake(client->irq);
-
- return 0;
-}
-
-static int pixcir_i2c_ts_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
-
- if (device_may_wakeup(&client->dev))
- disable_irq_wake(client->irq);
-
- return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops,
- pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume);
-
#if defined(CONFIG_OF)
static const struct of_device_id pixcir_of_match[];
@@ -547,6 +521,69 @@ static int pixcir_i2c_ts_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int pixcir_i2c_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+ int ret = 0;
+
+ mutex_lock(&input->mutex);
+
+ if (device_may_wakeup(&client->dev)) {
+ /* need to start device if not open, to be wakeup source */
+ if (!input->users) {
+ ret = pixcir_start(ts);
+ if (ret)
+ goto unlock;
+ }
+
+ enable_irq_wake(client->irq);
+
+ } else if (input->users) {
+ ret = pixcir_stop(ts);
+ }
+
+unlock:
+ mutex_unlock(&input->mutex);
+
+ return ret;
+}
+
+static int pixcir_i2c_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pixcir_i2c_ts_data *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+ int ret = 0;
+
+ mutex_lock(&input->mutex);
+
+ if (device_may_wakeup(&client->dev)) {
+ disable_irq_wake(client->irq);
+
+ /* need to stop device if it was not open on suspend */
+ if (!input->users) {
+ ret = pixcir_stop(ts);
+ if (ret)
+ goto unlock;
+ }
+
+ } else if (input->users) {
+ ret = pixcir_start(ts);
+ }
+
+unlock:
+ mutex_unlock(&input->mutex);
+
+ return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops,
+ pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume);
+
static const struct i2c_device_id pixcir_i2c_ts_id[] = {
{ "pixcir_ts", 0 },
{ "pixcir_tangoc", 0},
--
1.8.3.2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox