diff options
Diffstat (limited to 'drivers/input/touchscreen/elo.c')
-rw-r--r-- | drivers/input/touchscreen/elo.c | 167 |
1 files changed, 133 insertions, 34 deletions
diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c index c86a2eb310fd..ab565335ee44 100644 --- a/drivers/input/touchscreen/elo.c +++ b/drivers/input/touchscreen/elo.c @@ -23,6 +23,7 @@ #include <linux/input.h> #include <linux/serio.h> #include <linux/init.h> +#include <linux/ctype.h> #define DRIVER_DESC "Elo serial touchscreen driver" @@ -34,7 +35,19 @@ MODULE_LICENSE("GPL"); * Definitions & global arrays. */ -#define ELO_MAX_LENGTH 10 +#define ELO_MAX_LENGTH 10 + +#define ELO10_PACKET_LEN 8 +#define ELO10_TOUCH 0x03 +#define ELO10_PRESSURE 0x80 + +#define ELO10_LEAD_BYTE 'U' + +#define ELO10_ID_CMD 'i' + +#define ELO10_TOUCH_PACKET 'T' +#define ELO10_ACK_PACKET 'A' +#define ELI10_ID_PACKET 'I' /* * Per-touchscreen data. @@ -43,51 +56,67 @@ MODULE_LICENSE("GPL"); struct elo { struct input_dev *dev; struct serio *serio; + struct mutex cmd_mutex; + struct completion cmd_done; int id; int idx; + unsigned char expected_packet; unsigned char csum; unsigned char data[ELO_MAX_LENGTH]; + unsigned char response[ELO10_PACKET_LEN]; char phys[32]; }; -static void elo_process_data_10(struct elo* elo, unsigned char data, struct pt_regs *regs) +static void elo_process_data_10(struct elo *elo, unsigned char data, struct pt_regs *regs) { struct input_dev *dev = elo->dev; - elo->csum += elo->data[elo->idx] = data; - + elo->data[elo->idx] = data; switch (elo->idx++) { - case 0: - if (data != 'U') { + elo->csum = 0xaa; + if (data != ELO10_LEAD_BYTE) { + pr_debug("elo: unsynchronized data: 0x%02x\n", data); elo->idx = 0; - elo->csum = 0; - } - break; - - case 1: - if (data != 'T') { - elo->idx = 0; - elo->csum = 0; } break; case 9: - if (elo->csum) { + elo->idx = 0; + if (data != elo->csum) { + pr_debug("elo: bad checksum: 0x%02x, expected 0x%02x\n", + data, elo->csum); + break; + } + if (elo->data[1] != elo->expected_packet) { + if (elo->data[1] != ELO10_TOUCH_PACKET) + pr_debug("elo: unexpected packet: 0x%02x\n", + elo->data[1]); + break; + } + if (likely(elo->data[1] == ELO10_TOUCH_PACKET)) { input_regs(dev, regs); input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]); input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]); - input_report_abs(dev, ABS_PRESSURE, (elo->data[8] << 8) | elo->data[7]); - input_report_key(dev, BTN_TOUCH, elo->data[8] || elo->data[7]); + if (elo->data[2] & ELO10_PRESSURE) + input_report_abs(dev, ABS_PRESSURE, + (elo->data[8] << 8) | elo->data[7]); + input_report_key(dev, BTN_TOUCH, elo->data[2] & ELO10_TOUCH); input_sync(dev); + } else if (elo->data[1] == ELO10_ACK_PACKET) { + if (elo->data[2] == '0') + elo->expected_packet = ELO10_TOUCH_PACKET; + complete(&elo->cmd_done); + } else { + memcpy(elo->response, &elo->data[1], ELO10_PACKET_LEN); + elo->expected_packet = ELO10_ACK_PACKET; } - elo->idx = 0; - elo->csum = 0; break; } + elo->csum += data; } -static void elo_process_data_6(struct elo* elo, unsigned char data, struct pt_regs *regs) +static void elo_process_data_6(struct elo *elo, unsigned char data, struct pt_regs *regs) { struct input_dev *dev = elo->dev; @@ -135,7 +164,7 @@ static void elo_process_data_6(struct elo* elo, unsigned char data, struct pt_re } } -static void elo_process_data_3(struct elo* elo, unsigned char data, struct pt_regs *regs) +static void elo_process_data_3(struct elo *elo, unsigned char data, struct pt_regs *regs) { struct input_dev *dev = elo->dev; @@ -161,7 +190,7 @@ static void elo_process_data_3(struct elo* elo, unsigned char data, struct pt_re static irqreturn_t elo_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs) { - struct elo* elo = serio_get_drvdata(serio); + struct elo *elo = serio_get_drvdata(serio); switch(elo->id) { case 0: @@ -181,17 +210,81 @@ static irqreturn_t elo_interrupt(struct serio *serio, return IRQ_HANDLED; } +static int elo_command_10(struct elo *elo, unsigned char *packet) +{ + int rc = -1; + int i; + unsigned char csum = 0xaa + ELO10_LEAD_BYTE; + + mutex_lock(&elo->cmd_mutex); + + serio_pause_rx(elo->serio); + elo->expected_packet = toupper(packet[0]); + init_completion(&elo->cmd_done); + serio_continue_rx(elo->serio); + + if (serio_write(elo->serio, ELO10_LEAD_BYTE)) + goto out; + + for (i = 0; i < ELO10_PACKET_LEN; i++) { + csum += packet[i]; + if (serio_write(elo->serio, packet[i])) + goto out; + } + + if (serio_write(elo->serio, csum)) + goto out; + + wait_for_completion_timeout(&elo->cmd_done, HZ); + + if (elo->expected_packet == ELO10_TOUCH_PACKET) { + /* We are back in reporting mode, the command was ACKed */ + memcpy(packet, elo->response, ELO10_PACKET_LEN); + rc = 0; + } + + out: + mutex_unlock(&elo->cmd_mutex); + return rc; +} + +static int elo_setup_10(struct elo *elo) +{ + static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" }; + struct input_dev *dev = elo->dev; + unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD }; + + if (elo_command_10(elo, packet)) + return -1; + + dev->id.version = (packet[5] << 8) | packet[4]; + + input_set_abs_params(dev, ABS_X, 96, 4000, 0, 0); + input_set_abs_params(dev, ABS_Y, 96, 4000, 0, 0); + if (packet[3] & ELO10_PRESSURE) + input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); + + printk(KERN_INFO "elo: %sTouch touchscreen, fw: %02x.%02x, " + "features: %x02x, controller: 0x%02x\n", + elo_types[(packet[1] -'0') & 0x03], + packet[5], packet[4], packet[3], packet[7]); + + return 0; +} + /* * elo_disconnect() is the opposite of elo_connect() */ static void elo_disconnect(struct serio *serio) { - struct elo* elo = serio_get_drvdata(serio); + struct elo *elo = serio_get_drvdata(serio); + input_get_device(elo->dev); input_unregister_device(elo->dev); serio_close(serio); serio_set_drvdata(serio, NULL); + input_put_device(elo->dev); kfree(elo); } @@ -211,12 +304,15 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) input_dev = input_allocate_device(); if (!elo || !input_dev) { err = -ENOMEM; - goto fail; + goto fail1; } elo->serio = serio; elo->id = serio->id.id; elo->dev = input_dev; + elo->expected_packet = ELO10_TOUCH_PACKET; + mutex_init(&elo->cmd_mutex); + init_completion(&elo->cmd_done); snprintf(elo->phys, sizeof(elo->phys), "%s/input0", serio->phys); input_dev->private = elo; @@ -231,12 +327,17 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + serio_set_drvdata(serio, elo); + err = serio_open(serio, drv); + if (err) + goto fail2; + switch (elo->id) { case 0: /* 10-byte protocol */ - input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0); - input_set_abs_params(input_dev, ABS_Y, 96, 4000, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); + if (elo_setup_10(elo)) + goto fail3; + break; case 1: /* 6-byte protocol */ @@ -253,17 +354,15 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv) break; } - serio_set_drvdata(serio, elo); - - err = serio_open(serio, drv); + err = input_register_device(elo->dev); if (err) - goto fail; + goto fail3; - input_register_device(elo->dev); return 0; - fail: serio_set_drvdata(serio, NULL); - input_free_device(input_dev); + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); kfree(elo); return err; } |