On Wednesday, July 15, 2015 11:22:06 AM you wrote:
> Hi list,
> I'm starting work on an input driver.
> the device is connected to an RS232 uart in my freescale imx6q arm system,
> which has 2 serial ports, one of which is a console
>
> It is not anything like a ps2/at device.
> How can I get it to take over the uart device? A devicetree entry?
>
> I do not understand serio probing, and don't understand if I should use serio
> for a non ps2 compatible device.
>
> Any pointers would be helpful.
>
> The input device documentation is great if I can just figure out how to
> connect it to my hardware.
>
>
I went ahead and converted (parts of) the existing userspace driver.
Clearly I am missing something. My driver is registered with serio, but serio is not associated with any devices.
I turned on the SERIO_SERPORT...
/sys/bus/serio/devices/ is empty.
I would like it to bind my driver to /dev/ttymxc1 (uart 2 on this system), or its underlying uart.
--
~Joshua Clayton
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/slab.h>
#include <asm/io.h>
#define DRIVER_DESC "Uniwest EVI Frontpanel input driver"
struct fpanel_state {
int16_t h_value;
int16_t v_value;
unsigned char in[8];
unsigned char led[2];
};
struct evifpanel {
struct input_dev *dev;
struct serio *serio;
unsigned char version[4];
struct fpanel_state cur;
struct fpanel_state old;
unsigned int bytes;
char name[64];
char phys[32];
};
struct panel_event {
uint32_t first;
union {
uint32_t raw_val;
struct {
int16_t second;
int16_t third;
};
};
};
struct key_map {
char *name;
unsigned int code;
unsigned int byte;
unsigned int offset;
};
static struct key_map btns[] = {
{ "F1", KEY_F1, 6, 0 },
{ "Display", KEY_D, 6, 1 },
{ "Null", KEY_N, 7, 0 },
{ "Clear", KEY_BACKSPACE, 7, 1 },
{ "Enter", KEY_ENTER, 7, 2 },
{ "Cancel", KEY_ESC, 7, 3 },
{ "F4", KEY_F4, 7, 4 },
{ "F3", KEY_F3, 7, 5 },
{ "F2", KEY_F2, 7, 6 },
{ },
};
static struct attribute *attrs[] = {
NULL,
};
static struct bin_attribute *bin_attrs[] = {
NULL,
};
struct attribute_group fpanel_attrs = {
.attrs = attrs,
.bin_attrs = bin_attrs,
};
/**
* @brief display horizontal slider positions for testing hardware
* For each pin, display a '.' for off, or a '-' for on
*/
static void fp_print_h_sliders(struct evifpanel *fp, unsigned char *in)
{
char h_output[13];
/* horizontal slider */
h_output[0] = '-' + !(in[3] & '\x10');
h_output[1] = '-' + !(in[3] & '\x01');
h_output[2] = '-' + !(in[4] & '\x40');
h_output[3] = '-' + !(in[4] & '\x20');
h_output[4] = '-' + !(in[4] & '\x10');
h_output[5] = '-' + !(in[4] & '\x08');
h_output[6] = '-' + !(in[4] & '\x04');
h_output[7] = '-' + !(in[4] & '\x02');
h_output[8] = '-' + !(in[4] & '\x01');
h_output[9] = '-' + !(in[5] & '\x40');
h_output[10] = '-' + !(in[5] & '\x20');
h_output[11] = '-' + !(in[3] & '\x08');
h_output[12] = '\0';
dev_dbg(&fp->dev->dev, "horizontal slider array: %s\n", h_output);
}
/**
* @brief display horizontal slider positions for testing hardware
* For each pin, display a '.' for off, or a '-' for on
*/
static void fp_print_v_sliders(struct evifpanel *fp, unsigned char *in)
{
char v_output[13];
v_output[0] = '-' + !(in[3] & '\x02');
v_output[1] = '-' + !(in[5] & '\x10');
v_output[2] = '-' + !(in[5] & '\x08');
v_output[3] = '-' + !(in[5] & '\x04');
v_output[4] = '-' + !(in[5] & '\x02');
v_output[5] = '-' + !(in[5] & '\x01');
v_output[6] = '-' + !(in[6] & '\x40');
v_output[7] = '-' + !(in[6] & '\x20');
v_output[8] = '-' + !(in[6] & '\x10');
v_output[9] = '-' + !(in[6] & '\x08');
v_output[10] = '-' + !(in[6] & '\x04');
v_output[11] = '-' + !(in[3] & '\x04');
v_output[12] = '\0';
dev_dbg(&fp->dev->dev, "vertical slider array: %s\n", v_output);
}
/**
* @brief calculate horizontal and vertical sliders and send the event
*/
static void calc_slidervals(struct evifpanel *fp)
{
uint16_t temp;
int lo_pins, hi_pins;
/* horizontal slider */
temp = 0;
lo_pins = 0;
hi_pins = 0;
if (fp->cur.in[3] & '\x10') {
lo_pins++;
temp += 0;
}
if (fp->cur.in[3] & '\x01') {
lo_pins++;
temp += 1;
}
if (fp->cur.in[4] & '\x40') {
lo_pins++;
temp += 2;
}
if (fp->cur.in[4] & '\x20') {
lo_pins++;
temp += 3;
}
if (fp->cur.in[4] & '\x10') {
lo_pins++;
temp += 4;
}
if (fp->cur.in[4] & '\x08') {
lo_pins++;
temp += 5;
}
if (fp->cur.in[4] & '\x04') {
hi_pins++;
temp += 6;
}
if (fp->cur.in[4] & '\x02') {
hi_pins++;
temp += 7;
}
if (fp->cur.in[4] & '\x01') {
hi_pins++;
temp += 8;
}
if (fp->cur.in[5] & '\x40') {
hi_pins++;
temp += 9;
}
if (fp->cur.in[5] & '\x20') {
hi_pins++;
temp += 10;
}
if (fp->cur.in[3] & '\x08') {
hi_pins++;
temp += 11;
}
fp->old.h_value = fp->cur.h_value;
if (lo_pins && !hi_pins) {
fp->cur.h_value = (temp * 0xffff / (11 * lo_pins)) - 0x8000;
} else if (hi_pins && !lo_pins) {
fp->cur.h_value = (temp * 0xffff / (11 * hi_pins)) - 0x8000;
} else {
fp->cur.h_value = 0;
}
if (fp->old.h_value != fp->cur.h_value) {
struct panel_event evt;
fp_print_h_sliders(fp, fp->cur.in);
evt.first = 1;
evt.second = 0;
evt.third = fp->cur.h_value;
dev_dbg(&fp->dev->dev, "sending %#.8x %#.8x horizontal value %d\n",
evt.first, evt.raw_val, fp->cur.h_value);
/* if (event_fd >= 0) { */
/* bytes = write(event_fd, &evt, sizeof(struct panel_event)); */
/* if (bytes != sizeof(struct panel_event)) { */
/* dev_err(&fp->dev->dev, "Failed to send slider event %#.8x %#.8x: %s\n", */
/* evt.first, evt.raw_val, strerror(errno)); */
/* } */
/* } */
}
/*vertical slider */
temp = 0;
lo_pins = 0;
hi_pins = 0;
if (fp->cur.in[3] & '\x02') {
lo_pins++;
temp += 11;
}
if (fp->cur.in[5] & '\x10') {
lo_pins++;
temp += 10;
}
if (fp->cur.in[5] & '\x08') {
lo_pins++;
temp += 9;
}
if (fp->cur.in[5] & '\x04') {
lo_pins++;
temp += 8;
}
if (fp->cur.in[5] & '\x02') {
lo_pins++;
temp += 7;
}
if (fp->cur.in[5] & '\x01') {
lo_pins++;
temp += 6;
}
if (fp->cur.in[6] & '\x40') {
hi_pins++;
temp += 5;
}
if (fp->cur.in[6] & '\x20') {
hi_pins++;
temp += 4;
}
if (fp->cur.in[6] & '\x10') {
hi_pins++;
temp += 3;
}
if (fp->cur.in[6] & '\x08') {
hi_pins++;
temp += 2;
}
if (fp->cur.in[6] & '\x04') {
hi_pins++;
temp += 1;
}
if (fp->cur.in[3] & '\x04') {
hi_pins++;
temp += 0;
}
fp->old.v_value = fp->cur.v_value;
if (lo_pins && !hi_pins) {
fp->cur.v_value = (temp * 0xffff / (11 * lo_pins)) - 0x8000;
} else if (hi_pins && !lo_pins) {
fp->cur.v_value = (temp * 0xffff / (11 * hi_pins)) - 0x8000;
} else {
fp->cur.v_value = 0;
}
if (fp->old.v_value != fp->cur.v_value) {
struct panel_event evt;
fp_print_v_sliders(fp, fp->cur.in);
evt.first = 2;
evt.second = 0;
evt.third = fp->cur.v_value;
dev_dbg(&fp->dev->dev, "sending %#.8x %#.8x vertical value %d\n",
evt.first, evt.raw_val, fp->cur.v_value);
/* if (event_fd >= 0) { */
/* bytes = write(event_fd, &evt, sizeof(struct panel_event)); */
/* if (bytes != sizeof(struct panel_event)) { */
/* dev_err(&fp->dev->dev, "Failed to send slider event %#.8x %#.8x: %s\n", */
/* evt.first, evt.raw_val, strerror(errno)); */
/* } */
/* } */
}
}
/**
* @brief check buttons against a range of bits in a byte
*/
static void fp_check_btns(struct evifpanel *fp, struct key_map *key)
{
while (key->name) {
unsigned int mask = 1 << key->offset;
unsigned int changed = (fp->old.in[key->byte] ^
fp->cur.in[key->byte]) & mask;
unsigned int value = fp->cur.in[key->byte] & mask;
if (changed) {
input_report_key(fp->dev, key->code, value);
dev_dbg(&fp->dev->dev, "%s %d\n", key->name, !!(value));
}
key++;
}
}
/**
* @brief check the led status bytes coming from the fp
*/
static void fp_check_leds(struct evifpanel *fp)
{
memcpy(fp->old.led, fp->cur.led, 2);
memcpy(fp->cur.led, fp->cur.in + 6, 2);
dev_dbg(&fp->dev->dev, "Received led status 0x%2.2X%2.2X\n",
fp->cur.in[6], fp->cur.in[7]);
}
/**
* @brief save the version bytes coming from the fp in an ascii file
*/
static void fp_store_version(struct evifpanel *fp)
{
memcpy(fp->version, fp->cur.in + 4, 4);
dev_info(&fp->dev->dev, "CPLD Version: %3.3u.%3.3u.%3.3u.%3.3u\n",
fp->version[0], fp->version[1],
fp->version[2], fp->version[3]);
}
/**
* @brief request version info from the fp
*/
static void fp_request_version(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');
}
/**
* read a command from the input_fd pipe and send it to the fp
*
static int input_read(int read_fd, struct evifpanel *fp) {
char buf[4] = {'\0', '\0', '\0', '\0'};
ssize_t bytes;
bytes = read(read_fd, buf, 3);
if (bytes != 3) {
return errno;
}
if (!strncmp("LED", buf, 3)) {
char send_buf[8] = { '\xC0', '\x00', '\x00', '\x09', '\x00', '\x02',
fp->cur.led[0], fp->cur.led[1]};
bytes = read(read_fd, buf, 3);
char enable = buf[2] - '0';
char change_bit;
switch(buf[0]) {
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
change_bit = 1 << (buf[0] - '1');
if (enable) {
send_buf[7] |= change_bit;
} else {
send_buf[7] &= ~change_bit;
}
break;
case '8':
case '9':
change_bit = 1 << (buf[0] - '8');
if (enable) {
send_buf[6] |= change_bit;
} else {
send_buf[6] &= ~change_bit;
}
break;
default:
buf[3] = '\0';
dev_err(&fp->dev->dev, "Bad input received: LED%s\n", buf);
return -1;
}
// bytes = write(fps->ser->fd, send_buf, 8);
// if (bytes != 8) {
// dev_err(&fp->dev->dev, "Failed initial write to fp: %s\n", strerror(errno));
// return errno;
// }
}
return 0;
}
*/
/**
* send zero or more events on event_fd based on the state of the fp
*/
static int fp_process_output(struct evifpanel *fp)
{
dev_dbg(&fp->dev->dev, "Read %llX\n", *(u64 *)(fp->cur.in));
switch (fp->cur.in[1]) {
case '\x03':
fp_check_btns(fp, btns);
calc_slidervals(fp);
memcpy(fp->old.in, fp->cur.in, 8);
break;
case '\x09':
if (fp->cur.in[4] || fp->cur.in[5]) {
fp_store_version(fp);
} else {
fp_check_leds(fp);
}
break;
default:
dev_err(&fp->dev->dev, "Bad cmd id %X in sequence %llX\n",
fp->cur.in[1], *(u64 *)(fp->cur.in));
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->cur.in));
fp->bytes = 0;
}
fp->cur.in[fp->bytes] = c;
++fp->bytes;
if (fp->bytes == 8) {
fp_process_output(fp);
fp->bytes = 0;
}
}
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);
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);
/* fp->dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); */
}
static int fp_connect(struct serio *serio, struct serio_driver *drv)
{
struct evifpanel *fp;
struct input_dev *input_dev;
int error = -ENOMEM;
pr_info("fp_connect start\n");
fp = kzalloc(sizeof(*fp), 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;
serio_set_drvdata(serio, fp);
error = serio_open(serio, drv);
if (error) {
pr_err("evifpanel: failed to open serio\n");
goto fail2;
}
fp_request_version(fp);
error = sysfs_create_group(&serio->dev.kobj, &fpanel_attrs);
if (error) {
pr_err("evifpanel: failed to create sysfs group\n");
goto fail3;
}
fp_set_device_attrs(fp);
error = input_register_device(fp->dev);
if (error) {
pr_err("evifpanel: failed to register input device\n");
goto fail4;
}
return 0;
fail4:
sysfs_remove_group(&serio->dev.kobj, &fpanel_attrs);
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 const struct of_device_id my_of_ids[] = {
{ .compatible = "uniwest,evi-fpanel" },
{},
};
static struct serio_driver fp_drv = {
.driver = {
.name = "evifpanel",
.of_match_table = my_of_ids,
},
.description = DRIVER_DESC,
.id_table = fp_ids,
.connect = fp_connect,
.interrupt = fp_interrupt,
.disconnect = fp_disconnect,
};
module_serio_driver(fp_drv);
MODULE_AUTHOR("Joshua Clayton <joshua.clayton@xxxxxxxxxxx>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");