Skip to content

Commit

Permalink
Input: usbtouchscreen - add NEXIO (or iNexio) support
Browse files Browse the repository at this point in the history
Add support for NEXIO (or iNexio) USB touchscreens to usbtouchscreen
driver.  Tested with NEX170MRT 17" LCD monitor with integrated touchscreen
(with xserver-xorg-input-evtouch 0.8.8-1):

T:  Bus=02 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 54 Spd=12  MxCh= 0
D:  Ver= 1.10 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=1870 ProdID=0001 Rev= 1.00
S:  Manufacturer=iNexio
S:  Product=iNexio USB
C:* #Ifs= 2 Cfg#= 1 Atr=c0 MxPwr=500mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=00 Driver=(none)
E:  Ad=83(I) Atr=03(Int.) MxPS=   8 Ivl=255ms
I:* If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=(none)
E:  Ad=01(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms

No datasheet is available, this was written by capturing some data with
SniffUSB in Windows: http://www.rainbow-software.org/linux_files/nexio/

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
  • Loading branch information
Ondrej Zary authored and Dmitry Torokhov committed Feb 4, 2010
1 parent f4a5e35 commit 5197424
Show file tree
Hide file tree
Showing 2 changed files with 264 additions and 2 deletions.
5 changes: 5 additions & 0 deletions drivers/input/touchscreen/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,11 @@ config TOUCHSCREEN_USB_ETT_TC5UH
bool "ET&T TC5UH touchscreen controler support" if EMBEDDED
depends on TOUCHSCREEN_USB_COMPOSITE

config TOUCHSCREEN_USB_NEXIO
default y
bool "NEXIO/iNexio device support" if EMBEDDED
depends on TOUCHSCREEN_USB_COMPOSITE

config TOUCHSCREEN_TOUCHIT213
tristate "Sahara TouchIT-213 touchscreen"
select SERIO
Expand Down
261 changes: 259 additions & 2 deletions drivers/input/touchscreen/usbtouchscreen.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* - GoTop Super_Q2/GogoPen/PenPower tablets
* - JASTEC USB touch controller/DigiTech DTR-02U
* - Zytronic capacitive touchscreen
* - NEXIO/iNexio
*
* Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch>
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
Expand Down Expand Up @@ -95,6 +96,7 @@ struct usbtouch_device_info {

int (*read_data) (struct usbtouch_usb *usbtouch, unsigned char *pkt);
int (*init) (struct usbtouch_usb *usbtouch);
void (*exit) (struct usbtouch_usb *usbtouch);
};

/* a usbtouch device */
Expand All @@ -109,6 +111,7 @@ struct usbtouch_usb {
struct usbtouch_device_info *type;
char name[128];
char phys[64];
void *priv;

int x, y;
int touch, press;
Expand All @@ -133,6 +136,7 @@ enum {
DEVTYPE_E2I,
DEVTYPE_ZYTRONIC,
DEVTYPE_TC5UH,
DEVTYPE_NEXIO,
};

#define USB_DEVICE_HID_CLASS(vend, prod) \
Expand Down Expand Up @@ -222,6 +226,14 @@ static const struct usb_device_id usbtouch_devices[] = {
{USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC5UH},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO
/* data interface only */
{USB_DEVICE_AND_INTERFACE_INFO(0x10f0, 0x2002, 0x0a, 0x00, 0x00),
.driver_info = DEVTYPE_NEXIO},
{USB_DEVICE_AND_INTERFACE_INFO(0x1870, 0x0001, 0x0a, 0x00, 0x00),
.driver_info = DEVTYPE_NEXIO},
#endif

{}
};

Expand Down Expand Up @@ -691,6 +703,229 @@ static int zytronic_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
}
#endif

/*****************************************************************************
* NEXIO Part
*/
#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO

#define NEXIO_TIMEOUT 5000
#define NEXIO_BUFSIZE 1024
#define NEXIO_THRESHOLD 50

struct nexio_priv {
struct urb *ack;
unsigned char *ack_buf;
};

struct nexio_touch_packet {
u8 flags; /* 0xe1 = touch, 0xe1 = release */
__be16 data_len; /* total bytes of touch data */
__be16 x_len; /* bytes for X axis */
__be16 y_len; /* bytes for Y axis */
u8 data[];
} __attribute__ ((packed));

static unsigned char nexio_ack_pkt[2] = { 0xaa, 0x02 };
static unsigned char nexio_init_pkt[4] = { 0x82, 0x04, 0x0a, 0x0f };

static void nexio_ack_complete(struct urb *urb)
{
}

static int nexio_init(struct usbtouch_usb *usbtouch)
{
struct usb_device *dev = interface_to_usbdev(usbtouch->interface);
struct usb_host_interface *interface = usbtouch->interface->cur_altsetting;
struct nexio_priv *priv;
int ret = -ENOMEM;
int actual_len, i;
unsigned char *buf;
char *firmware_ver = NULL, *device_name = NULL;
int input_ep = 0, output_ep = 0;

/* find first input and output endpoint */
for (i = 0; i < interface->desc.bNumEndpoints; i++) {
if (!input_ep &&
usb_endpoint_dir_in(&interface->endpoint[i].desc))
input_ep = interface->endpoint[i].desc.bEndpointAddress;
if (!output_ep &&
usb_endpoint_dir_out(&interface->endpoint[i].desc))
output_ep = interface->endpoint[i].desc.bEndpointAddress;
}
if (!input_ep || !output_ep)
return -ENXIO;

buf = kmalloc(NEXIO_BUFSIZE, GFP_KERNEL);
if (!buf)
goto out_buf;

/* two empty reads */
for (i = 0; i < 2; i++) {
ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, input_ep),
buf, NEXIO_BUFSIZE, &actual_len,
NEXIO_TIMEOUT);
if (ret < 0)
goto out_buf;
}

/* send init command */
memcpy(buf, nexio_init_pkt, sizeof(nexio_init_pkt));
ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, output_ep),
buf, sizeof(nexio_init_pkt), &actual_len,
NEXIO_TIMEOUT);
if (ret < 0)
goto out_buf;

/* read replies */
for (i = 0; i < 3; i++) {
memset(buf, 0, NEXIO_BUFSIZE);
ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, input_ep),
buf, NEXIO_BUFSIZE, &actual_len,
NEXIO_TIMEOUT);
if (ret < 0 || actual_len < 1 || buf[1] != actual_len)
continue;
switch (buf[0]) {
case 0x83: /* firmware version */
if (!firmware_ver)
firmware_ver = kstrdup(&buf[2], GFP_KERNEL);
break;
case 0x84: /* device name */
if (!device_name)
device_name = kstrdup(&buf[2], GFP_KERNEL);
break;
}
}

printk(KERN_INFO "Nexio device: %s, firmware version: %s\n",
device_name, firmware_ver);

kfree(firmware_ver);
kfree(device_name);

/* prepare ACK URB */
ret = -ENOMEM;

usbtouch->priv = kmalloc(sizeof(struct nexio_priv), GFP_KERNEL);
if (!usbtouch->priv)
goto out_buf;

priv = usbtouch->priv;

priv->ack_buf = kmalloc(sizeof(nexio_ack_pkt), GFP_KERNEL);
if (!priv->ack_buf)
goto err_priv;

memcpy(priv->ack_buf, nexio_ack_pkt, sizeof(nexio_ack_pkt));

priv->ack = usb_alloc_urb(0, GFP_KERNEL);
if (!priv->ack) {
dbg("%s - usb_alloc_urb failed: usbtouch->ack", __func__);
goto err_ack_buf;
}

usb_fill_bulk_urb(priv->ack, dev, usb_sndbulkpipe(dev, output_ep),
priv->ack_buf, sizeof(nexio_ack_pkt),
nexio_ack_complete, usbtouch);
ret = 0;
goto out_buf;

err_ack_buf:
kfree(priv->ack_buf);
err_priv:
kfree(priv);
out_buf:
kfree(buf);
return ret;
}

static void nexio_exit(struct usbtouch_usb *usbtouch)
{
struct nexio_priv *priv = usbtouch->priv;

usb_kill_urb(priv->ack);
usb_free_urb(priv->ack);
kfree(priv->ack_buf);
kfree(priv);
}

static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt)
{
int x, y, begin_x, begin_y, end_x, end_y, w, h, ret;
struct nexio_touch_packet *packet = (void *) pkt;
struct nexio_priv *priv = usbtouch->priv;

/* got touch data? */
if ((pkt[0] & 0xe0) != 0xe0)
return 0;

/* send ACK */
ret = usb_submit_urb(priv->ack, GFP_ATOMIC);

if (!usbtouch->type->max_xc) {
usbtouch->type->max_xc = 2 * be16_to_cpu(packet->x_len);
input_set_abs_params(usbtouch->input, ABS_X, 0,
2 * be16_to_cpu(packet->x_len), 0, 0);
usbtouch->type->max_yc = 2 * be16_to_cpu(packet->y_len);
input_set_abs_params(usbtouch->input, ABS_Y, 0,
2 * be16_to_cpu(packet->y_len), 0, 0);
}
/*
* The device reports state of IR sensors on X and Y axes.
* Each byte represents "darkness" percentage (0-100) of one element.
* 17" touchscreen reports only 64 x 52 bytes so the resolution is low.
* This also means that there's a limited multi-touch capability but
* it's disabled (and untested) here as there's no X driver for that.
*/
begin_x = end_x = begin_y = end_y = -1;
for (x = 0; x < be16_to_cpu(packet->x_len); x++) {
if (begin_x == -1 && packet->data[x] > NEXIO_THRESHOLD) {
begin_x = x;
continue;
}
if (end_x == -1 && begin_x != -1 && packet->data[x] < NEXIO_THRESHOLD) {
end_x = x - 1;
for (y = be16_to_cpu(packet->x_len);
y < be16_to_cpu(packet->data_len); y++) {
if (begin_y == -1 && packet->data[y] > NEXIO_THRESHOLD) {
begin_y = y - be16_to_cpu(packet->x_len);
continue;
}
if (end_y == -1 &&
begin_y != -1 && packet->data[y] < NEXIO_THRESHOLD) {
end_y = y - 1 - be16_to_cpu(packet->x_len);
w = end_x - begin_x;
h = end_y - begin_y;
#if 0
/* multi-touch */
input_report_abs(usbtouch->input,
ABS_MT_TOUCH_MAJOR, max(w,h));
input_report_abs(usbtouch->input,
ABS_MT_TOUCH_MINOR, min(x,h));
input_report_abs(usbtouch->input,
ABS_MT_POSITION_X, 2*begin_x+w);
input_report_abs(usbtouch->input,
ABS_MT_POSITION_Y, 2*begin_y+h);
input_report_abs(usbtouch->input,
ABS_MT_ORIENTATION, w > h);
input_mt_sync(usbtouch->input);
#endif
/* single touch */
usbtouch->x = 2 * begin_x + w;
usbtouch->y = 2 * begin_y + h;
usbtouch->touch = packet->flags & 0x01;
begin_y = end_y = -1;
return 1;
}
}
begin_x = end_x = -1;
}

}
return 0;
}
#endif


/*****************************************************************************
* the different device descriptors
*/
Expand Down Expand Up @@ -875,6 +1110,16 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.read_data = tc5uh_read_data,
},
#endif

#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO
[DEVTYPE_NEXIO] = {
.rept_size = 128,
.irq_always = true,
.read_data = nexio_read_data,
.init = nexio_init,
.exit = nexio_exit,
},
#endif
};


Expand Down Expand Up @@ -1000,6 +1245,7 @@ static void usbtouch_irq(struct urb *urb)
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
case -EPIPE:
/* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d",
__func__, urb->status);
Expand Down Expand Up @@ -1146,10 +1392,16 @@ static int usbtouch_probe(struct usb_interface *intf,
input_set_abs_params(input_dev, ABS_PRESSURE, type->min_press,
type->max_press, 0, 0);

usb_fill_int_urb(usbtouch->irq, udev,
if (usb_endpoint_type(endpoint) == USB_ENDPOINT_XFER_INT)
usb_fill_int_urb(usbtouch->irq, udev,
usb_rcvintpipe(udev, endpoint->bEndpointAddress),
usbtouch->data, type->rept_size,
usbtouch_irq, usbtouch, endpoint->bInterval);
else
usb_fill_bulk_urb(usbtouch->irq, udev,
usb_rcvbulkpipe(udev, endpoint->bEndpointAddress),
usbtouch->data, type->rept_size,
usbtouch_irq, usbtouch);

usbtouch->irq->dev = udev;
usbtouch->irq->transfer_dma = usbtouch->data_dma;
Expand All @@ -1167,7 +1419,7 @@ static int usbtouch_probe(struct usb_interface *intf,
err = input_register_device(usbtouch->input);
if (err) {
dbg("%s - input_register_device failed, err: %d", __func__, err);
goto out_free_buffers;
goto out_do_exit;
}

usb_set_intfdata(intf, usbtouch);
Expand All @@ -1177,6 +1429,9 @@ static int usbtouch_probe(struct usb_interface *intf,

return 0;

out_do_exit:
if (type->exit)
type->exit(usbtouch);
out_free_buffers:
usbtouch_free_buffers(udev, usbtouch);
out_free:
Expand All @@ -1199,6 +1454,8 @@ static void usbtouch_disconnect(struct usb_interface *intf)
/* this will stop IO via close */
input_unregister_device(usbtouch->input);
usb_free_urb(usbtouch->irq);
if (usbtouch->type->exit)
usbtouch->type->exit(usbtouch);
usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch);
kfree(usbtouch);
}
Expand Down

0 comments on commit 5197424

Please sign in to comment.