From: Ricardo Ribalda Delgado <ricardo.riba...@uam.es> New char device emulating an Elo serial touchpad.
v2: -Emulate id packages (linux recognizes the hw) -Limit output to 96-4000 (thanks to Dmitry Zhurikhin) v3: -Output buffer (thanks to Juan Quintela) -Code Style --- Makefile.objs | 2 +- hw/elo.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/elo.h | 2 + qemu-char.c | 3 + qemu-options.hx | 5 +- 5 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 hw/elo.c create mode 100644 hw/elo.h diff --git a/Makefile.objs b/Makefile.objs index b73e2cb..07c2e68 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -75,7 +75,7 @@ common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o u common-obj-y += bt-hci-csr.o common-obj-y += buffered_file.o migration.o migration-tcp.o qemu-sockets.o common-obj-y += qemu-char.o savevm.o #aio.o -common-obj-y += msmouse.o ps2.o +common-obj-y += msmouse.o ps2.o elo.o common-obj-y += qdev.o qdev-properties.o common-obj-y += qemu-config.o block-migration.o diff --git a/hw/elo.c b/hw/elo.c new file mode 100644 index 0000000..d5b4124 --- /dev/null +++ b/hw/elo.c @@ -0,0 +1,189 @@ +/* + * QEMU ELO Touchpad via serial port + * + * Copyright (c) 2010 Ricardo Ribalda: QTechnology http://qtec.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include "qemu-common.h" +#include "qemu-char.h" +#include "console.h" +#include "elo.h" + +#define ELO_DOWN 0x1 +#define ELO_MOVE 0x2 +#define ELO_UP 0x4 +#define ELO_PACKET_SIZE 10 + +typedef struct { + CharDriverState chr; + uint8_t out_buf[ELO_PACKET_SIZE]; + uint8_t send_out; +} EloDriverState; + +static void elo_event(void *opaque, + int ax, int ay, int az, int buttons_state) +{ + EloDriverState *elo=(EloDriverState *)opaque; + CharDriverState *chr = &elo->chr; + unsigned char bytes[ELO_PACKET_SIZE]; + static int is_down=0; + int old_ax=0,old_ay=0; + int i; + + /*A touchpad cannot capture flight events*/ + if ((!is_down)&&(!buttons_state)) + return; + + ax=(ax*(4000-96))/0x7fff; + ax+=96; + ay=(ay*(4000-96))/0x7fff; + ay+=96; + + /*Move event*/ + if (is_down&&buttons_state){ + bytes[2]=ELO_MOVE; + is_down++; + if ((old_ay==ay)&&(old_ax==ax)) + return; + /*SDL move is much more precise than the real elo*/ + if (qemu_chr_can_read(chr)<ELO_PACKET_SIZE) + return; + old_ay=ay; + old_ax=ax; + is_down++; + } + + /*Click*/ + if ((!is_down)&&buttons_state){ + bytes[2]=ELO_DOWN; + is_down=1; + old_ay=ay; + old_ax=ax; + } + /*Release*/ + if (is_down&&(!buttons_state)){ + bytes[2]=ELO_UP; + is_down=0; + } + + bytes[0]='U'; + bytes[1]='T'; + bytes[3]=ax&0xff; + bytes[4]=(ax>>8)&0xff; + bytes[5]=ay&0xff; + bytes[6]=(ay>>8)&0xff; + bytes[7]=0x0;/*No presure capabilities*/ + bytes[8]=0x0; + bytes[9]=0xaa; + for(i=0;i<9;i++) + bytes[9]+=bytes[i]; + + qemu_chr_read(chr, bytes, ELO_PACKET_SIZE); + return; +} + +static int elo_chr_write(struct CharDriverState *s, const uint8_t *buf, int len) +{ + EloDriverState *elo=(EloDriverState *)s->opaque; + unsigned char bytes[ELO_PACKET_SIZE]; + static int in_cmd=0,i; + + if (buf[0]==0x55) + in_cmd=0; + + /*Only response to ID*/ + in_cmd+=len; + if (in_cmd<ELO_PACKET_SIZE) + return len; + + /* Only respond cmd ID This should be enough at least for linux*/ + /*Full ref at http://tge.cegep-baie-comeau.qc.ca/fichestech/References/Elo%20Touch%20Screen/smartset/pages/chapter_6.htm#command_descriptions*/ + /*ID*/ + in_cmd=0; + bytes[0]='U'; + bytes[1]='I'; + bytes[2]='0';/*Accu*/ + bytes[3]='0';/*Serial*/ + bytes[4]=0;/*No features*/ + bytes[5]=1; + bytes[6]=2; + bytes[7]=1;/*1 response*/ + bytes[8]=0xe; + bytes[9]=0xaa; + for(i=0;i<9;i++) + bytes[9]+=bytes[i]; + qemu_chr_read(s, bytes, ELO_PACKET_SIZE); + + /*ACK no err*/ + in_cmd=0; + bytes[0]='U'; + bytes[1]='A'; + bytes[2]='0'; + bytes[3]='0'; + bytes[4]='0'; + bytes[5]=0; + bytes[6]=0; + bytes[7]=0; + bytes[8]=0; + bytes[9]=0xaa; + for(i=0;i<9;i++) + bytes[9]+=bytes[i]; + if (qemu_chr_can_read(s)<ELO_PACKET_SIZE){ + elo->send_out=1; + memcpy(elo->out_buf,bytes,ELO_PACKET_SIZE); + in_cmd=0; + return len; + } + qemu_chr_read(s, bytes, ELO_PACKET_SIZE); + in_cmd=0; + return len; +} + +static void elo_chr_accept_input(struct CharDriverState *chr){ + EloDriverState *elo=(EloDriverState *)chr->opaque; + + if (!elo->send_out) + return; + if (qemu_chr_can_read(chr)<ELO_PACKET_SIZE) + return; + qemu_chr_read(chr, elo->out_buf, ELO_PACKET_SIZE); + return; +} + +static void elo_chr_close(struct CharDriverState *chr) +{ + qemu_free(chr->opaque); +} + +CharDriverState *qemu_chr_open_elo(QemuOpts *opts) +{ + EloDriverState *elo; + elo = qemu_mallocz(sizeof(EloDriverState)); + elo->chr.chr_write = elo_chr_write; + elo->chr.chr_close = elo_chr_close; + elo->chr.chr_accept_input= elo_chr_accept_input; + elo->chr.opaque=elo; + elo->send_out=0; + + qemu_add_mouse_event_handler(elo_event, elo, 1, "QEMU Elo Touchpad"); + + return &elo->chr; +} diff --git a/hw/elo.h b/hw/elo.h new file mode 100644 index 0000000..4b4e09a --- /dev/null +++ b/hw/elo.h @@ -0,0 +1,2 @@ +/* elo.c */ +CharDriverState *qemu_chr_open_elo(QemuOpts *opts); diff --git a/qemu-char.c b/qemu-char.c index 40cfefa..2f95767 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -32,6 +32,7 @@ #include "hw/usb.h" #include "hw/baum.h" #include "hw/msmouse.h" +#include "hw/elo.h" #include "qemu-objects.h" #include <unistd.h> @@ -2278,6 +2279,7 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) if (strcmp(filename, "null") == 0 || strcmp(filename, "pty") == 0 || strcmp(filename, "msmouse") == 0 || + strcmp(filename, "elo") == 0 || strcmp(filename, "braille") == 0 || strcmp(filename, "stdio") == 0) { qemu_opt_set(opts, "backend", filename); @@ -2391,6 +2393,7 @@ static const struct { { .name = "socket", .open = qemu_chr_open_socket }, { .name = "udp", .open = qemu_chr_open_udp }, { .name = "msmouse", .open = qemu_chr_open_msmouse }, + { .name = "elo", .open = qemu_chr_open_elo }, { .name = "vc", .open = text_console_init }, #ifdef _WIN32 { .name = "file", .open = qemu_chr_open_win_file_out }, diff --git a/qemu-options.hx b/qemu-options.hx index 7c33736..7ee62cb 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1183,7 +1183,7 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev socket,id=id,path=path[,server][,nowait][,telnet] (unix)\n" "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n" " [,localport=localport][,ipv4][,ipv6]\n" - "-chardev msmouse,id=id\n" + "-chardev elo,id=id\n" "-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]\n" "-chardev file,id=id,path=path\n" "-chardev pipe,id=id,path=path\n" @@ -1650,6 +1650,9 @@ or fake device. @item msmouse Three button serial mouse. Configure the guest to use Microsoft protocol. + +...@item elo +Elo Touchpad 10bytes emulator. @end table ETEXI -- 1.7.0.3