mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH 1/2] Input: add Uniwest EVI front panel driver
@ 2015-07-24 17:43 Joshua Clayton
  2015-07-24 17:43 ` [RFC PATCH 2/2] Input: evifpanel: show sliders through sysfs Joshua Clayton
  0 siblings, 1 reply; 2+ messages in thread
From: Joshua Clayton @ 2015-07-24 17:43 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Dan Murphy, Nishanth Menon, Chanwoo Choi, Jaewon Kim,
	linux-kernel, linux-input, Joshua Clayton

This is my first input driver, and my first attempt to istart the process of
upstreaming something, (other than a small patch). 
I apologize in advance :)

The Uniwest EVI front panel has 9 usable buttons
which correspond to keyboard keys, as well as
horizontal and verical slidersr,
(the sliders are like touch based scroll bars)
and 9 indicator LEDs

This initial version of the driver just supports the buttons.

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
 drivers/input/misc/Kconfig     |  10 ++
 drivers/input/misc/Makefile    |   1 +
 drivers/input/misc/evifpanel.c | 265 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 276 insertions(+)
 create mode 100644 drivers/input/misc/evifpanel.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 23297ab..87ce0e6 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -222,6 +222,16 @@ config INPUT_APANEL
 	 To compile this driver as a module, choose M here: the module will
 	 be called apanel.
 
+config INPUT_EVIFPANEL
+	tristate "Uniwest EVI Front panel"
+	select SERIO
+	help
+	  Say Y if you have a Uniwest EVI and want to enable the
+	  sliders and buttons on the front.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called evifpanel.
+
 config INPUT_GP2A
 	tristate "Sharp GP2AP002A00F I2C Proximity/Opto sensor driver"
 	depends on I2C
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 19c7603..5764467 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_INPUT_DA9055_ONKEY)	+= da9055_onkey.o
 obj-$(CONFIG_INPUT_DM355EVM)		+= dm355evm_keys.o
 obj-$(CONFIG_INPUT_DRV260X_HAPTICS)	+= drv260x.o
 obj-$(CONFIG_INPUT_DRV2667_HAPTICS)	+= drv2667.o
+obj-$(CONFIG_INPUT_EVIFPANEL)		+= evifpanel.o
 obj-$(CONFIG_INPUT_GP2A)		+= gp2ap002a00f.o
 obj-$(CONFIG_INPUT_GPIO_BEEPER)		+= gpio-beeper.o
 obj-$(CONFIG_INPUT_GPIO_TILT_POLLED)	+= gpio_tilt_polled.o
diff --git a/drivers/input/misc/evifpanel.c b/drivers/input/misc/evifpanel.c
new file mode 100644
index 0000000..aa28e6c
--- /dev/null
+++ b/drivers/input/misc/evifpanel.c
@@ -0,0 +1,265 @@
+/*
+ * Uniwest EVI Front Panel driver
+ *
+ * Copyright 2015 United Western Technologies
+ *
+ * Joshua Clayton <stillcompiling@gmail.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ */
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+
+#define DRIVER_DESC "Uniwest EVI Frontpanel input driver"
+MODULE_AUTHOR("Joshua Clayton <stillcompiling@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+struct evifpanel {
+	struct input_dev *dev;
+	struct serio *serio;
+	unsigned int bytes;
+	char name[64];
+	char phys[32];
+	unsigned char buf[8];
+};
+
+struct key_map {
+	u16 type;
+	u16 code;
+	s32 value;
+	unsigned int byte;
+	unsigned int offset;
+};
+
+static struct key_map btns[] = {
+	{ EV_KEY, KEY_F1, 1, 6, 0 },
+	{ EV_KEY, KEY_D, 1, 6, 1 },
+	{ EV_KEY, KEY_N, 1, 7, 0 },
+	{ EV_KEY, KEY_BACKSPACE, 1, 7, 1 },
+	{ EV_KEY, KEY_ENTER, 1, 7, 2 },
+	{ EV_KEY, KEY_ESC, 1, 7, 3 },
+	{ EV_KEY, KEY_F4, 1, 7, 4 },
+	{ EV_KEY, KEY_F3, 1, 7, 5 },
+	{ EV_KEY, KEY_F2, 1, 7, 6 },
+	{ },
+};
+
+static void fp_check_key(struct evifpanel *fp, struct key_map *key)
+{
+	s32 value = fp->buf[key->byte] & BIT(key->offset);
+
+	input_report_key(fp->dev, key->code, value);
+}
+
+/*
+ * Check buttons against array of key_map
+ */
+static void fp_check_btns(struct evifpanel *fp, struct key_map *key)
+{
+	while (key->type) {
+		switch (key->type) {
+		case EV_KEY:
+			fp_check_key(fp, key);
+			break;
+		default:
+			break; /* ignore unknown types */
+		}
+		key++;
+	}
+
+	input_sync(fp->dev);
+}
+
+/*
+ * Set the firmware version coming from the fp in an ascii file
+ */
+static void fp_set_fw_ver(struct evifpanel *fp)
+{
+	scnprintf(fp->serio->firmware_id, sizeof(fp->serio->firmware_id),
+			"evifpanel v%3.3u.%3.3u.%3.3u.%3.3u", fp->buf[4],
+			fp->buf[5], fp->buf[6], fp->buf[7]);
+
+	dev_info(&fp->serio->dev, "firmware found: %s\n",
+			fp->serio->firmware_id);
+}
+
+/*
+ * Request firmware version info from the device
+ */
+static void fp_request_fw_ver(struct evifpanel *fp)
+{
+	serio_write(fp->serio, '\xC0');
+	serio_write(fp->serio, '\x00');
+	serio_write(fp->serio, '\x00');
+	serio_write(fp->serio, '\x09');
+	serio_write(fp->serio, '\x00');
+	serio_write(fp->serio, '\x01');
+	serio_write(fp->serio, '\x00');
+	serio_write(fp->serio, '\x00');
+}
+
+/*
+ * Send zero or more input events based on the state of the fp
+ * Call this when you have a full packet.
+ */
+static int fp_parse_buf(struct evifpanel *fp)
+{
+	switch (fp->buf[1]) {
+	case '\x03':
+		fp_check_btns(fp, btns);
+		break;
+	case '\x09':
+		if (fp->buf[4] || fp->buf[5])
+			fp_set_fw_ver(fp);
+		break;
+	default:
+		dev_err(&fp->dev->dev, "Bad cmd id %X in sequence %llX\n",
+				fp->buf[1], *(u64 *)(fp->buf));
+
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void fp_add_byte(struct evifpanel *fp, unsigned char c)
+{
+	if (c != '\xC0' && !fp->bytes) {
+		dev_err(&fp->dev->dev, "drop %X. looking for check byte\n", c);
+		return;
+	}
+
+	if (c == '\xC0' && fp->bytes) {
+		/* msg check byte should not be found in the middle of a set */
+		dev_warn(&fp->dev->dev, "discarding %d bytes from %llX\n",
+			 fp->bytes, *(u64 *)(fp->buf));
+		fp->bytes = 0;
+	}
+
+	fp->buf[fp->bytes] = c;
+	++fp->bytes;
+}
+
+
+static irqreturn_t fp_interrupt(struct serio *serio, unsigned char data,
+		unsigned int flags)
+{
+	struct evifpanel *fp = serio_get_drvdata(serio);
+
+	fp_add_byte(fp, data);
+	if (fp->bytes == 8) {
+		fp_parse_buf(fp);
+		fp->bytes = 0;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void fp_set_device_attrs(struct evifpanel *fp)
+{
+	snprintf(fp->name, sizeof(fp->name),
+			"EVI Frontpanel keypad and sliders");
+	snprintf(fp->phys, sizeof(fp->phys),
+			"%s/input0", fp->serio->phys);
+
+	fp->dev->name = fp->name;
+	fp->dev->phys = fp->phys;
+	fp->dev->id.bustype = BUS_RS232;
+	fp->dev->dev.parent = &fp->serio->dev;
+
+	fp->dev->evbit[0] = BIT_MASK(EV_KEY);
+	__set_bit(KEY_D, fp->dev->keybit);
+	__set_bit(KEY_N, fp->dev->keybit);
+	__set_bit(KEY_BACKSPACE, fp->dev->keybit);
+	__set_bit(KEY_ENTER, fp->dev->keybit);
+	__set_bit(KEY_F1, fp->dev->keybit);
+	__set_bit(KEY_F2, fp->dev->keybit);
+	__set_bit(KEY_F3, fp->dev->keybit);
+	__set_bit(KEY_F4, fp->dev->keybit);
+	__set_bit(KEY_ESC, fp->dev->keybit);
+}
+
+static int fp_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct evifpanel *fp;
+	struct input_dev *input_dev;
+	int error = -ENOMEM;
+
+	fp = kzalloc(sizeof(struct evifpanel), GFP_KERNEL);
+
+	input_dev = input_allocate_device();
+	if (!input_dev || !fp) {
+		pr_err("evifpanel: failed to get memory\n");
+		goto fail1;
+	}
+
+	fp->dev = input_dev;
+	fp->serio = serio;
+	fp_set_device_attrs(fp);
+	serio_set_drvdata(serio, fp);
+
+	error = serio_open(serio, drv);
+	if (error) {
+		pr_err("evifpanel: failed to open serio\n");
+		goto fail2;
+	}
+	fp_request_fw_ver(fp);
+
+	error = input_register_device(input_dev);
+	if (error) {
+		pr_err("evifpanel: failed to register input device\n");
+		goto fail3;
+	}
+
+	return 0;
+
+fail3:
+	serio_close(serio);
+fail2:
+	serio_set_drvdata(serio, NULL);
+fail1:
+	input_free_device(input_dev);
+	kfree(fp);
+	pr_err("fp_connect: FAILED\n");
+
+	return error;
+}
+
+static void fp_disconnect(struct serio *serio)
+{
+	struct evifpanel *fp = serio_get_drvdata(serio);
+
+	input_unregister_device(fp->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(fp);
+};
+
+static struct serio_device_id fp_ids[] = {
+	{
+		.type  = SERIO_RS232,
+		.proto = SERIO_ANY,
+		.id    = SERIO_ANY,
+		.extra = SERIO_ANY,
+	},
+	{ 0 }
+};
+
+static struct serio_driver fp_drv = {
+	.driver = {
+		.name = "evifpanel",
+	},
+	.description = DRIVER_DESC,
+	.id_table    = fp_ids,
+	.connect     = fp_connect,
+	.interrupt   = fp_interrupt,
+	.disconnect  = fp_disconnect,
+};
+
+module_serio_driver(fp_drv);
-- 
2.1.4


^ permalink raw reply	[flat|nested] 2+ messages in thread

* [RFC PATCH 2/2] Input: evifpanel: show sliders through sysfs
  2015-07-24 17:43 [RFC PATCH 1/2] Input: add Uniwest EVI front panel driver Joshua Clayton
@ 2015-07-24 17:43 ` Joshua Clayton
  0 siblings, 0 replies; 2+ messages in thread
From: Joshua Clayton @ 2015-07-24 17:43 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Dan Murphy, Nishanth Menon, Chanwoo Choi, Jaewon Kim,
	linux-kernel, linux-input, Joshua Clayton

sysfs file export the sliders in short int range

slider sysfs files will be superceded by input events
when we figure out what that should look like.

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
 drivers/input/misc/evifpanel.c | 112 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)

diff --git a/drivers/input/misc/evifpanel.c b/drivers/input/misc/evifpanel.c
index aa28e6c..0bda86f 100644
--- a/drivers/input/misc/evifpanel.c
+++ b/drivers/input/misc/evifpanel.c
@@ -14,12 +14,18 @@
 #include <linux/init.h>
 #include <linux/serio.h>
 #include <linux/slab.h>
+#include <linux/sysfs.h>
 
 #define DRIVER_DESC "Uniwest EVI Frontpanel input driver"
 MODULE_AUTHOR("Joshua Clayton <stillcompiling@gmail.com>");
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
+struct fp_slider {
+	s32 sum;
+	s32 count;
+};
+
 struct evifpanel {
 	struct input_dev *dev;
 	struct serio *serio;
@@ -27,6 +33,8 @@ struct evifpanel {
 	char name[64];
 	char phys[32];
 	unsigned char buf[8];
+	s32 h_slider;
+	s32 v_slider;
 };
 
 struct key_map {
@@ -38,6 +46,30 @@ struct key_map {
 };
 
 static struct key_map btns[] = {
+	{ EV_ABS, ABS_X, 0, 3, 4 },
+	{ EV_ABS, ABS_X, 1, 3, 0 },
+	{ EV_ABS, ABS_X, 2, 4, 6 },
+	{ EV_ABS, ABS_X, 3, 4, 5 },
+	{ EV_ABS, ABS_X, 4, 4, 4 },
+	{ EV_ABS, ABS_X, 5, 4, 3 },
+	{ EV_ABS, ABS_X, 6, 4, 2 },
+	{ EV_ABS, ABS_X, 7, 4, 1 },
+	{ EV_ABS, ABS_X, 8, 4, 0 },
+	{ EV_ABS, ABS_X, 9, 5, 6 },
+	{ EV_ABS, ABS_X, 10, 5, 5 },
+	{ EV_ABS, ABS_X, 11, 3, 3 },
+	{ EV_ABS, ABS_Y, 0, 3, 2 },
+	{ EV_ABS, ABS_Y, 1, 6, 2 },
+	{ EV_ABS, ABS_Y, 2, 6, 3 },
+	{ EV_ABS, ABS_Y, 3, 6, 4 },
+	{ EV_ABS, ABS_Y, 4, 6, 5 },
+	{ EV_ABS, ABS_Y, 5, 6, 6 },
+	{ EV_ABS, ABS_Y, 6, 5, 0 },
+	{ EV_ABS, ABS_Y, 7, 5, 1 },
+	{ EV_ABS, ABS_Y, 8, 5, 2 },
+	{ EV_ABS, ABS_Y, 9, 5, 3 },
+	{ EV_ABS, ABS_Y, 10, 5, 4 },
+	{ EV_ABS, ABS_Y, 11, 3, 1 },
 	{ EV_KEY, KEY_F1, 1, 6, 0 },
 	{ EV_KEY, KEY_D, 1, 6, 1 },
 	{ EV_KEY, KEY_N, 1, 7, 0 },
@@ -57,22 +89,63 @@ static void fp_check_key(struct evifpanel *fp, struct key_map *key)
 	input_report_key(fp->dev, key->code, value);
 }
 
+static void fp_slider_accumulate(struct evifpanel *fp,
+		struct fp_slider *slider, struct key_map *key)
+{
+	s32 value = !!(fp->buf[key->byte] & BIT(key->offset));
+
+	slider->sum += value * key->value;
+	slider->count += value;
+}
+
+static s32 fp_slider_value(struct evifpanel *fp, struct fp_slider *slider)
+{
+	s32 value;
+
+	if (slider->count)
+		value = (slider->sum * 0xffff / (11 * slider->count)) - 0x8000;
+	else
+		value = 0;
+
+	if (value == -1)
+		value = 0;
+
+	return value;
+}
+
 /*
  * Check buttons against array of key_map
  */
 static void fp_check_btns(struct evifpanel *fp, struct key_map *key)
 {
+	struct fp_slider h_axis, v_axis;
+
+	h_axis.sum = 0;
+	h_axis.count = 0;
+	v_axis.sum = 0;
+	v_axis.count = 0;
+
 	while (key->type) {
 		switch (key->type) {
 		case EV_KEY:
 			fp_check_key(fp, key);
 			break;
+		case EV_ABS:
+			if (key->code == ABS_X)
+				fp_slider_accumulate(fp, &h_axis, key);
+			else
+				fp_slider_accumulate(fp, &v_axis, key);
+
+			break;
 		default:
 			break; /* ignore unknown types */
 		}
 		key++;
 	}
 
+	fp->h_slider = fp_slider_value(fp, &h_axis);
+	fp->v_slider = fp_slider_value(fp, &v_axis);
+
 	input_sync(fp->dev);
 }
 
@@ -161,6 +234,37 @@ static irqreturn_t fp_interrupt(struct serio *serio, unsigned char data,
 	return IRQ_HANDLED;
 }
 
+static ssize_t v_slider_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	struct evifpanel *fp = serio_get_drvdata(serio);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", fp->v_slider);
+}
+
+static ssize_t h_slider_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	struct evifpanel *fp = serio_get_drvdata(serio);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", fp->h_slider);
+}
+
+static DEVICE_ATTR_RO(v_slider);
+static DEVICE_ATTR_RO(h_slider);
+
+static struct attribute *attrs[] = {
+	&dev_attr_v_slider.attr,
+	&dev_attr_h_slider.attr,
+	NULL,
+};
+
+struct attribute_group fp_attrs = {
+	.attrs = attrs,
+};
+
 static void fp_set_device_attrs(struct evifpanel *fp)
 {
 	snprintf(fp->name, sizeof(fp->name),
@@ -210,6 +314,11 @@ static int fp_connect(struct serio *serio, struct serio_driver *drv)
 		goto fail2;
 	}
 	fp_request_fw_ver(fp);
+	error = sysfs_create_group(&serio->dev.kobj, &fp_attrs);
+	if (error) {
+		dev_err(&serio->dev, "failed to add sysfs group\n");
+		goto sysfs_fail;
+	}
 
 	error = input_register_device(input_dev);
 	if (error) {
@@ -221,6 +330,8 @@ static int fp_connect(struct serio *serio, struct serio_driver *drv)
 
 fail3:
 	serio_close(serio);
+sysfs_fail:
+	sysfs_remove_group(&serio->dev.kobj, &fp_attrs);
 fail2:
 	serio_set_drvdata(serio, NULL);
 fail1:
@@ -236,6 +347,7 @@ static void fp_disconnect(struct serio *serio)
 	struct evifpanel *fp = serio_get_drvdata(serio);
 
 	input_unregister_device(fp->dev);
+	sysfs_remove_group(&serio->dev.kobj, &fp_attrs);
 	serio_close(serio);
 	serio_set_drvdata(serio, NULL);
 	kfree(fp);
-- 
2.1.4


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2015-07-24 17:43 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-07-24 17:43 [RFC PATCH 1/2] Input: add Uniwest EVI front panel driver Joshua Clayton
2015-07-24 17:43 ` [RFC PATCH 2/2] Input: evifpanel: show sliders through sysfs Joshua Clayton

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

Powered by JetHome