diff options
Diffstat (limited to 'drivers/input/tsdev.c')
-rw-r--r-- | drivers/input/tsdev.c | 175 |
1 files changed, 107 insertions, 68 deletions
diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c index 05dfc10bc079..5e5b5c91d75b 100644 --- a/drivers/input/tsdev.c +++ b/drivers/input/tsdev.c | |||
@@ -111,13 +111,13 @@ struct tsdev { | |||
111 | int minor; | 111 | int minor; |
112 | char name[8]; | 112 | char name[8]; |
113 | wait_queue_head_t wait; | 113 | wait_queue_head_t wait; |
114 | struct list_head list; | 114 | struct list_head client_list; |
115 | struct input_handle handle; | 115 | struct input_handle handle; |
116 | int x, y, pressure; | 116 | int x, y, pressure; |
117 | struct ts_calibration cal; | 117 | struct ts_calibration cal; |
118 | }; | 118 | }; |
119 | 119 | ||
120 | struct tsdev_list { | 120 | struct tsdev_client { |
121 | struct fasync_struct *fasync; | 121 | struct fasync_struct *fasync; |
122 | struct list_head node; | 122 | struct list_head node; |
123 | struct tsdev *tsdev; | 123 | struct tsdev *tsdev; |
@@ -139,38 +139,49 @@ static struct tsdev *tsdev_table[TSDEV_MINORS/2]; | |||
139 | 139 | ||
140 | static int tsdev_fasync(int fd, struct file *file, int on) | 140 | static int tsdev_fasync(int fd, struct file *file, int on) |
141 | { | 141 | { |
142 | struct tsdev_list *list = file->private_data; | 142 | struct tsdev_client *client = file->private_data; |
143 | int retval; | 143 | int retval; |
144 | 144 | ||
145 | retval = fasync_helper(fd, file, on, &list->fasync); | 145 | retval = fasync_helper(fd, file, on, &client->fasync); |
146 | return retval < 0 ? retval : 0; | 146 | return retval < 0 ? retval : 0; |
147 | } | 147 | } |
148 | 148 | ||
149 | static int tsdev_open(struct inode *inode, struct file *file) | 149 | static int tsdev_open(struct inode *inode, struct file *file) |
150 | { | 150 | { |
151 | int i = iminor(inode) - TSDEV_MINOR_BASE; | 151 | int i = iminor(inode) - TSDEV_MINOR_BASE; |
152 | struct tsdev_list *list; | 152 | struct tsdev_client *client; |
153 | struct tsdev *tsdev; | ||
154 | int error; | ||
153 | 155 | ||
154 | printk(KERN_WARNING "tsdev (compaq touchscreen emulation) is scheduled " | 156 | printk(KERN_WARNING "tsdev (compaq touchscreen emulation) is scheduled " |
155 | "for removal.\nSee Documentation/feature-removal-schedule.txt " | 157 | "for removal.\nSee Documentation/feature-removal-schedule.txt " |
156 | "for details.\n"); | 158 | "for details.\n"); |
157 | 159 | ||
158 | if (i >= TSDEV_MINORS || !tsdev_table[i & TSDEV_MINOR_MASK]) | 160 | if (i >= TSDEV_MINORS) |
161 | return -ENODEV; | ||
162 | |||
163 | tsdev = tsdev_table[i & TSDEV_MINOR_MASK]; | ||
164 | if (!tsdev || !tsdev->exist) | ||
159 | return -ENODEV; | 165 | return -ENODEV; |
160 | 166 | ||
161 | if (!(list = kzalloc(sizeof(struct tsdev_list), GFP_KERNEL))) | 167 | client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL); |
168 | if (!client) | ||
162 | return -ENOMEM; | 169 | return -ENOMEM; |
163 | 170 | ||
164 | list->raw = (i >= TSDEV_MINORS/2) ? 1 : 0; | 171 | client->tsdev = tsdev; |
172 | client->raw = (i >= TSDEV_MINORS / 2) ? 1 : 0; | ||
173 | list_add_tail(&client->node, &tsdev->client_list); | ||
165 | 174 | ||
166 | i &= TSDEV_MINOR_MASK; | 175 | if (!tsdev->open++ && tsdev->exist) { |
167 | list->tsdev = tsdev_table[i]; | 176 | error = input_open_device(&tsdev->handle); |
168 | list_add_tail(&list->node, &tsdev_table[i]->list); | 177 | if (error) { |
169 | file->private_data = list; | 178 | list_del(&client->node); |
179 | kfree(client); | ||
180 | return error; | ||
181 | } | ||
182 | } | ||
170 | 183 | ||
171 | if (!list->tsdev->open++) | 184 | file->private_data = client; |
172 | if (list->tsdev->exist) | ||
173 | input_open_device(&list->tsdev->handle); | ||
174 | return 0; | 185 | return 0; |
175 | } | 186 | } |
176 | 187 | ||
@@ -182,45 +193,48 @@ static void tsdev_free(struct tsdev *tsdev) | |||
182 | 193 | ||
183 | static int tsdev_release(struct inode *inode, struct file *file) | 194 | static int tsdev_release(struct inode *inode, struct file *file) |
184 | { | 195 | { |
185 | struct tsdev_list *list = file->private_data; | 196 | struct tsdev_client *client = file->private_data; |
197 | struct tsdev *tsdev = client->tsdev; | ||
186 | 198 | ||
187 | tsdev_fasync(-1, file, 0); | 199 | tsdev_fasync(-1, file, 0); |
188 | list_del(&list->node); | ||
189 | 200 | ||
190 | if (!--list->tsdev->open) { | 201 | list_del(&client->node); |
191 | if (list->tsdev->exist) | 202 | kfree(client); |
192 | input_close_device(&list->tsdev->handle); | 203 | |
204 | if (!--tsdev->open) { | ||
205 | if (tsdev->exist) | ||
206 | input_close_device(&tsdev->handle); | ||
193 | else | 207 | else |
194 | tsdev_free(list->tsdev); | 208 | tsdev_free(tsdev); |
195 | } | 209 | } |
196 | kfree(list); | 210 | |
197 | return 0; | 211 | return 0; |
198 | } | 212 | } |
199 | 213 | ||
200 | static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, | 214 | static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, |
201 | loff_t * ppos) | 215 | loff_t *ppos) |
202 | { | 216 | { |
203 | struct tsdev_list *list = file->private_data; | 217 | struct tsdev_client *client = file->private_data; |
218 | struct tsdev *tsdev = client->tsdev; | ||
204 | int retval = 0; | 219 | int retval = 0; |
205 | 220 | ||
206 | if (list->head == list->tail && list->tsdev->exist && (file->f_flags & O_NONBLOCK)) | 221 | if (client->head == client->tail && tsdev->exist && (file->f_flags & O_NONBLOCK)) |
207 | return -EAGAIN; | 222 | return -EAGAIN; |
208 | 223 | ||
209 | retval = wait_event_interruptible(list->tsdev->wait, | 224 | retval = wait_event_interruptible(tsdev->wait, |
210 | list->head != list->tail || !list->tsdev->exist); | 225 | client->head != client->tail || !tsdev->exist); |
211 | |||
212 | if (retval) | 226 | if (retval) |
213 | return retval; | 227 | return retval; |
214 | 228 | ||
215 | if (!list->tsdev->exist) | 229 | if (!tsdev->exist) |
216 | return -ENODEV; | 230 | return -ENODEV; |
217 | 231 | ||
218 | while (list->head != list->tail && | 232 | while (client->head != client->tail && |
219 | retval + sizeof (struct ts_event) <= count) { | 233 | retval + sizeof (struct ts_event) <= count) { |
220 | if (copy_to_user (buffer + retval, list->event + list->tail, | 234 | if (copy_to_user (buffer + retval, client->event + client->tail, |
221 | sizeof (struct ts_event))) | 235 | sizeof (struct ts_event))) |
222 | return -EFAULT; | 236 | return -EFAULT; |
223 | list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1); | 237 | client->tail = (client->tail + 1) & (TSDEV_BUFFER_SIZE - 1); |
224 | retval += sizeof (struct ts_event); | 238 | retval += sizeof (struct ts_event); |
225 | } | 239 | } |
226 | 240 | ||
@@ -228,32 +242,33 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, | |||
228 | } | 242 | } |
229 | 243 | ||
230 | /* No kernel lock - fine */ | 244 | /* No kernel lock - fine */ |
231 | static unsigned int tsdev_poll(struct file *file, poll_table * wait) | 245 | static unsigned int tsdev_poll(struct file *file, poll_table *wait) |
232 | { | 246 | { |
233 | struct tsdev_list *list = file->private_data; | 247 | struct tsdev_client *client = file->private_data; |
248 | struct tsdev *tsdev = client->tsdev; | ||
234 | 249 | ||
235 | poll_wait(file, &list->tsdev->wait, wait); | 250 | poll_wait(file, &tsdev->wait, wait); |
236 | return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) | | 251 | return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) | |
237 | (list->tsdev->exist ? 0 : (POLLHUP | POLLERR)); | 252 | (tsdev->exist ? 0 : (POLLHUP | POLLERR)); |
238 | } | 253 | } |
239 | 254 | ||
240 | static int tsdev_ioctl(struct inode *inode, struct file *file, | 255 | static int tsdev_ioctl(struct inode *inode, struct file *file, |
241 | unsigned int cmd, unsigned long arg) | 256 | unsigned int cmd, unsigned long arg) |
242 | { | 257 | { |
243 | struct tsdev_list *list = file->private_data; | 258 | struct tsdev_client *client = file->private_data; |
244 | struct tsdev *tsdev = list->tsdev; | 259 | struct tsdev *tsdev = client->tsdev; |
245 | int retval = 0; | 260 | int retval = 0; |
246 | 261 | ||
247 | switch (cmd) { | 262 | switch (cmd) { |
248 | case TS_GET_CAL: | 263 | case TS_GET_CAL: |
249 | if (copy_to_user ((void __user *)arg, &tsdev->cal, | 264 | if (copy_to_user((void __user *)arg, &tsdev->cal, |
250 | sizeof (struct ts_calibration))) | 265 | sizeof (struct ts_calibration))) |
251 | retval = -EFAULT; | 266 | retval = -EFAULT; |
252 | break; | 267 | break; |
253 | 268 | ||
254 | case TS_SET_CAL: | 269 | case TS_SET_CAL: |
255 | if (copy_from_user (&tsdev->cal, (void __user *)arg, | 270 | if (copy_from_user(&tsdev->cal, (void __user *)arg, |
256 | sizeof (struct ts_calibration))) | 271 | sizeof (struct ts_calibration))) |
257 | retval = -EFAULT; | 272 | retval = -EFAULT; |
258 | break; | 273 | break; |
259 | 274 | ||
@@ -279,7 +294,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, | |||
279 | unsigned int code, int value) | 294 | unsigned int code, int value) |
280 | { | 295 | { |
281 | struct tsdev *tsdev = handle->private; | 296 | struct tsdev *tsdev = handle->private; |
282 | struct tsdev_list *list; | 297 | struct tsdev_client *client; |
283 | struct timeval time; | 298 | struct timeval time; |
284 | 299 | ||
285 | switch (type) { | 300 | switch (type) { |
@@ -343,18 +358,18 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, | |||
343 | if (type != EV_SYN || code != SYN_REPORT) | 358 | if (type != EV_SYN || code != SYN_REPORT) |
344 | return; | 359 | return; |
345 | 360 | ||
346 | list_for_each_entry(list, &tsdev->list, node) { | 361 | list_for_each_entry(client, &tsdev->client_list, node) { |
347 | int x, y, tmp; | 362 | int x, y, tmp; |
348 | 363 | ||
349 | do_gettimeofday(&time); | 364 | do_gettimeofday(&time); |
350 | list->event[list->head].millisecs = time.tv_usec / 100; | 365 | client->event[client->head].millisecs = time.tv_usec / 100; |
351 | list->event[list->head].pressure = tsdev->pressure; | 366 | client->event[client->head].pressure = tsdev->pressure; |
352 | 367 | ||
353 | x = tsdev->x; | 368 | x = tsdev->x; |
354 | y = tsdev->y; | 369 | y = tsdev->y; |
355 | 370 | ||
356 | /* Calibration */ | 371 | /* Calibration */ |
357 | if (!list->raw) { | 372 | if (!client->raw) { |
358 | x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans; | 373 | x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans; |
359 | y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans; | 374 | y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans; |
360 | if (tsdev->cal.xyswap) { | 375 | if (tsdev->cal.xyswap) { |
@@ -362,33 +377,35 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, | |||
362 | } | 377 | } |
363 | } | 378 | } |
364 | 379 | ||
365 | list->event[list->head].x = x; | 380 | client->event[client->head].x = x; |
366 | list->event[list->head].y = y; | 381 | client->event[client->head].y = y; |
367 | list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1); | 382 | client->head = (client->head + 1) & (TSDEV_BUFFER_SIZE - 1); |
368 | kill_fasync(&list->fasync, SIGIO, POLL_IN); | 383 | kill_fasync(&client->fasync, SIGIO, POLL_IN); |
369 | } | 384 | } |
370 | wake_up_interruptible(&tsdev->wait); | 385 | wake_up_interruptible(&tsdev->wait); |
371 | } | 386 | } |
372 | 387 | ||
373 | static struct input_handle *tsdev_connect(struct input_handler *handler, | 388 | static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, |
374 | struct input_dev *dev, | 389 | const struct input_device_id *id) |
375 | const struct input_device_id *id) | ||
376 | { | 390 | { |
377 | struct tsdev *tsdev; | 391 | struct tsdev *tsdev; |
378 | struct class_device *cdev; | 392 | struct class_device *cdev; |
393 | dev_t devt; | ||
379 | int minor, delta; | 394 | int minor, delta; |
395 | int error; | ||
380 | 396 | ||
381 | for (minor = 0; minor < TSDEV_MINORS / 2 && tsdev_table[minor]; minor++); | 397 | for (minor = 0; minor < TSDEV_MINORS / 2 && tsdev_table[minor]; minor++); |
382 | if (minor >= TSDEV_MINORS / 2) { | 398 | if (minor >= TSDEV_MINORS / 2) { |
383 | printk(KERN_ERR | 399 | printk(KERN_ERR |
384 | "tsdev: You have way too many touchscreens\n"); | 400 | "tsdev: You have way too many touchscreens\n"); |
385 | return NULL; | 401 | return -ENFILE; |
386 | } | 402 | } |
387 | 403 | ||
388 | if (!(tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL))) | 404 | tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL); |
389 | return NULL; | 405 | if (!tsdev) |
406 | return -ENOMEM; | ||
390 | 407 | ||
391 | INIT_LIST_HEAD(&tsdev->list); | 408 | INIT_LIST_HEAD(&tsdev->client_list); |
392 | init_waitqueue_head(&tsdev->wait); | 409 | init_waitqueue_head(&tsdev->wait); |
393 | 410 | ||
394 | sprintf(tsdev->name, "ts%d", minor); | 411 | sprintf(tsdev->name, "ts%d", minor); |
@@ -415,21 +432,43 @@ static struct input_handle *tsdev_connect(struct input_handler *handler, | |||
415 | 432 | ||
416 | tsdev_table[minor] = tsdev; | 433 | tsdev_table[minor] = tsdev; |
417 | 434 | ||
418 | cdev = class_device_create(&input_class, &dev->cdev, | 435 | devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor), |
419 | MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor), | 436 | |
420 | dev->cdev.dev, tsdev->name); | 437 | cdev = class_device_create(&input_class, &dev->cdev, devt, |
438 | dev->cdev.dev, tsdev->name); | ||
439 | if (IS_ERR(cdev)) { | ||
440 | error = PTR_ERR(cdev); | ||
441 | goto err_free_tsdev; | ||
442 | } | ||
421 | 443 | ||
422 | /* temporary symlink to keep userspace happy */ | 444 | /* temporary symlink to keep userspace happy */ |
423 | sysfs_create_link(&input_class.subsys.kobj, &cdev->kobj, | 445 | error = sysfs_create_link(&input_class.subsys.kobj, |
424 | tsdev->name); | 446 | &cdev->kobj, tsdev->name); |
447 | if (error) | ||
448 | goto err_cdev_destroy; | ||
449 | |||
450 | error = input_register_handle(&tsdev->handle); | ||
451 | if (error) | ||
452 | goto err_remove_link; | ||
453 | |||
454 | return 0; | ||
425 | 455 | ||
426 | return &tsdev->handle; | 456 | err_remove_link: |
457 | sysfs_remove_link(&input_class.subsys.kobj, tsdev->name); | ||
458 | err_cdev_destroy: | ||
459 | class_device_destroy(&input_class, devt); | ||
460 | err_free_tsdev: | ||
461 | tsdev_table[minor] = NULL; | ||
462 | kfree(tsdev); | ||
463 | return error; | ||
427 | } | 464 | } |
428 | 465 | ||
429 | static void tsdev_disconnect(struct input_handle *handle) | 466 | static void tsdev_disconnect(struct input_handle *handle) |
430 | { | 467 | { |
431 | struct tsdev *tsdev = handle->private; | 468 | struct tsdev *tsdev = handle->private; |
432 | struct tsdev_list *list; | 469 | struct tsdev_client *client; |
470 | |||
471 | input_unregister_handle(handle); | ||
433 | 472 | ||
434 | sysfs_remove_link(&input_class.subsys.kobj, tsdev->name); | 473 | sysfs_remove_link(&input_class.subsys.kobj, tsdev->name); |
435 | class_device_destroy(&input_class, | 474 | class_device_destroy(&input_class, |
@@ -439,8 +478,8 @@ static void tsdev_disconnect(struct input_handle *handle) | |||
439 | if (tsdev->open) { | 478 | if (tsdev->open) { |
440 | input_close_device(handle); | 479 | input_close_device(handle); |
441 | wake_up_interruptible(&tsdev->wait); | 480 | wake_up_interruptible(&tsdev->wait); |
442 | list_for_each_entry(list, &tsdev->list, node) | 481 | list_for_each_entry(client, &tsdev->client_list, node) |
443 | kill_fasync(&list->fasync, SIGIO, POLL_HUP); | 482 | kill_fasync(&client->fasync, SIGIO, POLL_HUP); |
444 | } else | 483 | } else |
445 | tsdev_free(tsdev); | 484 | tsdev_free(tsdev); |
446 | } | 485 | } |