diff options
author | Daniel Ritz <daniel.ritz-ml@swissonline.ch> | 2005-11-27 16:23:38 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-01-04 16:51:39 -0500 |
commit | 5d3202949c9ac6e135d98bde15a8f05ad3fa5849 (patch) | |
tree | 2807209840c56f37a003ebb9aa3a4e4171c3cfe9 /drivers/usb/input/touchkitusb.c | |
parent | 8a4613f01f5bb850cab34e3db572d97251d997b3 (diff) |
[PATCH] USB: input/touchkitusb: handle multiple packets
Some versions of the controller seem to put multiple report packet into a
single urb. also it can happen that a packet is split across multiple urbs.
unpatched you get a jumpy cursor on some screens.
the patch does:
- handle multiple packets per urb
- handle packets split across multiple urb
- check packet type
- cleanups
Signed-off-by: Daniel Ritz <daniel.ritz@gmx.ch>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/input/touchkitusb.c')
-rw-r--r-- | drivers/usb/input/touchkitusb.c | 148 |
1 files changed, 126 insertions, 22 deletions
diff --git a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c index 75e7c12e7189..3b3c7b4120a2 100644 --- a/drivers/usb/input/touchkitusb.c +++ b/drivers/usb/input/touchkitusb.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /****************************************************************************** | 1 | /****************************************************************************** |
2 | * touchkitusb.c -- Driver for eGalax TouchKit USB Touchscreens | 2 | * touchkitusb.c -- Driver for eGalax TouchKit USB Touchscreens |
3 | * | 3 | * |
4 | * Copyright (C) 2004 by Daniel Ritz | 4 | * Copyright (C) 2004-2005 by Daniel Ritz <daniel.ritz@gmx.ch> |
5 | * Copyright (C) by Todd E. Johnson (mtouchusb.c) | 5 | * Copyright (C) by Todd E. Johnson (mtouchusb.c) |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or | 7 | * This program is free software; you can redistribute it and/or |
@@ -41,15 +41,13 @@ | |||
41 | #define TOUCHKIT_MAX_YC 0x07ff | 41 | #define TOUCHKIT_MAX_YC 0x07ff |
42 | #define TOUCHKIT_YC_FUZZ 0x0 | 42 | #define TOUCHKIT_YC_FUZZ 0x0 |
43 | #define TOUCHKIT_YC_FLAT 0x0 | 43 | #define TOUCHKIT_YC_FLAT 0x0 |
44 | #define TOUCHKIT_REPORT_DATA_SIZE 8 | 44 | #define TOUCHKIT_REPORT_DATA_SIZE 16 |
45 | 45 | ||
46 | #define TOUCHKIT_DOWN 0x01 | 46 | #define TOUCHKIT_DOWN 0x01 |
47 | #define TOUCHKIT_POINT_TOUCH 0x81 | ||
48 | #define TOUCHKIT_POINT_NOTOUCH 0x80 | ||
49 | 47 | ||
50 | #define TOUCHKIT_GET_TOUCHED(dat) ((((dat)[0]) & TOUCHKIT_DOWN) ? 1 : 0) | 48 | #define TOUCHKIT_PKT_TYPE_MASK 0xFE |
51 | #define TOUCHKIT_GET_X(dat) (((dat)[3] << 7) | (dat)[4]) | 49 | #define TOUCHKIT_PKT_TYPE_REPT 0x80 |
52 | #define TOUCHKIT_GET_Y(dat) (((dat)[1] << 7) | (dat)[2]) | 50 | #define TOUCHKIT_PKT_TYPE_DIAG 0x0A |
53 | 51 | ||
54 | #define DRIVER_VERSION "v0.1" | 52 | #define DRIVER_VERSION "v0.1" |
55 | #define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>" | 53 | #define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>" |
@@ -62,6 +60,8 @@ MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped."); | |||
62 | struct touchkit_usb { | 60 | struct touchkit_usb { |
63 | unsigned char *data; | 61 | unsigned char *data; |
64 | dma_addr_t data_dma; | 62 | dma_addr_t data_dma; |
63 | char buffer[TOUCHKIT_REPORT_DATA_SIZE]; | ||
64 | int buf_len; | ||
65 | struct urb *irq; | 65 | struct urb *irq; |
66 | struct usb_device *udev; | 66 | struct usb_device *udev; |
67 | struct input_dev *input; | 67 | struct input_dev *input; |
@@ -77,11 +77,128 @@ static struct usb_device_id touchkit_devices[] = { | |||
77 | {} | 77 | {} |
78 | }; | 78 | }; |
79 | 79 | ||
80 | /* helpers to read the data */ | ||
81 | static inline int touchkit_get_touched(char *data) | ||
82 | { | ||
83 | return (data[0] & TOUCHKIT_DOWN) ? 1 : 0; | ||
84 | } | ||
85 | |||
86 | static inline int touchkit_get_x(char *data) | ||
87 | { | ||
88 | return ((data[3] & 0x0F) << 7) | (data[4] & 0x7F); | ||
89 | } | ||
90 | |||
91 | static inline int touchkit_get_y(char *data) | ||
92 | { | ||
93 | return ((data[1] & 0x0F) << 7) | (data[2] & 0x7F); | ||
94 | } | ||
95 | |||
96 | |||
97 | /* processes one input packet. */ | ||
98 | static void touchkit_process_pkt(struct touchkit_usb *touchkit, | ||
99 | struct pt_regs *regs, char *pkt) | ||
100 | { | ||
101 | int x, y; | ||
102 | |||
103 | /* only process report packets */ | ||
104 | if ((pkt[0] & TOUCHKIT_PKT_TYPE_MASK) != TOUCHKIT_PKT_TYPE_REPT) | ||
105 | return; | ||
106 | |||
107 | if (swap_xy) { | ||
108 | y = touchkit_get_x(pkt); | ||
109 | x = touchkit_get_y(pkt); | ||
110 | } else { | ||
111 | x = touchkit_get_x(pkt); | ||
112 | y = touchkit_get_y(pkt); | ||
113 | } | ||
114 | |||
115 | input_regs(touchkit->input, regs); | ||
116 | input_report_key(touchkit->input, BTN_TOUCH, touchkit_get_touched(pkt)); | ||
117 | input_report_abs(touchkit->input, ABS_X, x); | ||
118 | input_report_abs(touchkit->input, ABS_Y, y); | ||
119 | input_sync(touchkit->input); | ||
120 | } | ||
121 | |||
122 | |||
123 | static int touchkit_get_pkt_len(char *buf) | ||
124 | { | ||
125 | switch (buf[0] & TOUCHKIT_PKT_TYPE_MASK) { | ||
126 | case TOUCHKIT_PKT_TYPE_REPT: | ||
127 | return 5; | ||
128 | |||
129 | case TOUCHKIT_PKT_TYPE_DIAG: | ||
130 | return buf[1] + 2; | ||
131 | } | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static void touchkit_process(struct touchkit_usb *touchkit, int len, | ||
137 | struct pt_regs *regs) | ||
138 | { | ||
139 | char *buffer; | ||
140 | int pkt_len, buf_len, pos; | ||
141 | |||
142 | /* if the buffer contains data, append */ | ||
143 | if (unlikely(touchkit->buf_len)) { | ||
144 | int tmp; | ||
145 | |||
146 | /* if only 1 byte in buffer, add another one to get length */ | ||
147 | if (touchkit->buf_len == 1) | ||
148 | touchkit->buffer[1] = touchkit->data[0]; | ||
149 | |||
150 | pkt_len = touchkit_get_pkt_len(touchkit->buffer); | ||
151 | |||
152 | /* unknown packet: drop everything */ | ||
153 | if (!pkt_len) | ||
154 | return; | ||
155 | |||
156 | /* append, process */ | ||
157 | tmp = pkt_len - touchkit->buf_len; | ||
158 | memcpy(touchkit->buffer + touchkit->buf_len, touchkit->data, tmp); | ||
159 | touchkit_process_pkt(touchkit, regs, touchkit->buffer); | ||
160 | |||
161 | buffer = touchkit->data + tmp; | ||
162 | buf_len = len - tmp; | ||
163 | } else { | ||
164 | buffer = touchkit->data; | ||
165 | buf_len = len; | ||
166 | } | ||
167 | |||
168 | /* only one byte left in buffer */ | ||
169 | if (unlikely(buf_len == 1)) { | ||
170 | touchkit->buffer[0] = buffer[0]; | ||
171 | touchkit->buf_len = 1; | ||
172 | return; | ||
173 | } | ||
174 | |||
175 | /* loop over the buffer */ | ||
176 | pos = 0; | ||
177 | while (pos < buf_len) { | ||
178 | /* get packet len */ | ||
179 | pkt_len = touchkit_get_pkt_len(buffer + pos); | ||
180 | |||
181 | /* unknown packet: drop everything */ | ||
182 | if (unlikely(!pkt_len)) | ||
183 | return; | ||
184 | |||
185 | /* full packet: process */ | ||
186 | if (likely(pkt_len <= buf_len)) { | ||
187 | touchkit_process_pkt(touchkit, regs, buffer + pos); | ||
188 | } else { | ||
189 | /* incomplete packet: save in buffer */ | ||
190 | memcpy(touchkit->buffer, buffer + pos, buf_len - pos); | ||
191 | touchkit->buf_len = buf_len - pos; | ||
192 | } | ||
193 | pos += pkt_len; | ||
194 | } | ||
195 | } | ||
196 | |||
197 | |||
80 | static void touchkit_irq(struct urb *urb, struct pt_regs *regs) | 198 | static void touchkit_irq(struct urb *urb, struct pt_regs *regs) |
81 | { | 199 | { |
82 | struct touchkit_usb *touchkit = urb->context; | 200 | struct touchkit_usb *touchkit = urb->context; |
83 | int retval; | 201 | int retval; |
84 | int x, y; | ||
85 | 202 | ||
86 | switch (urb->status) { | 203 | switch (urb->status) { |
87 | case 0: | 204 | case 0: |
@@ -105,20 +222,7 @@ static void touchkit_irq(struct urb *urb, struct pt_regs *regs) | |||
105 | goto exit; | 222 | goto exit; |
106 | } | 223 | } |
107 | 224 | ||
108 | if (swap_xy) { | 225 | touchkit_process(touchkit, urb->actual_length, regs); |
109 | y = TOUCHKIT_GET_X(touchkit->data); | ||
110 | x = TOUCHKIT_GET_Y(touchkit->data); | ||
111 | } else { | ||
112 | x = TOUCHKIT_GET_X(touchkit->data); | ||
113 | y = TOUCHKIT_GET_Y(touchkit->data); | ||
114 | } | ||
115 | |||
116 | input_regs(touchkit->input, regs); | ||
117 | input_report_key(touchkit->input, BTN_TOUCH, | ||
118 | TOUCHKIT_GET_TOUCHED(touchkit->data)); | ||
119 | input_report_abs(touchkit->input, ABS_X, x); | ||
120 | input_report_abs(touchkit->input, ABS_Y, y); | ||
121 | input_sync(touchkit->input); | ||
122 | 226 | ||
123 | exit: | 227 | exit: |
124 | retval = usb_submit_urb(urb, GFP_ATOMIC); | 228 | retval = usb_submit_urb(urb, GFP_ATOMIC); |