* [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