diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2007-08-30 00:22:39 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2007-08-30 00:22:39 -0400 |
commit | b9d2d110b10f7b4788d0fdd328cf57e34b767817 (patch) | |
tree | 0be026e664e983f3c77c954a0b26cfe60b8d22e2 | |
parent | b126207ccdfe492fbc339c18d4898b1b5353fc6b (diff) |
Input: tsdev - implement proper locking
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r-- | drivers/input/tsdev.c | 388 |
1 files changed, 276 insertions, 112 deletions
diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c index d2f882e98e5e..c189f1dd569f 100644 --- a/drivers/input/tsdev.c +++ b/drivers/input/tsdev.c | |||
@@ -112,6 +112,8 @@ struct tsdev { | |||
112 | struct input_handle handle; | 112 | struct input_handle handle; |
113 | wait_queue_head_t wait; | 113 | wait_queue_head_t wait; |
114 | struct list_head client_list; | 114 | struct list_head client_list; |
115 | spinlock_t client_lock; /* protects client_list */ | ||
116 | struct mutex mutex; | ||
115 | struct device dev; | 117 | struct device dev; |
116 | 118 | ||
117 | int x, y, pressure; | 119 | int x, y, pressure; |
@@ -122,8 +124,9 @@ struct tsdev_client { | |||
122 | struct fasync_struct *fasync; | 124 | struct fasync_struct *fasync; |
123 | struct list_head node; | 125 | struct list_head node; |
124 | struct tsdev *tsdev; | 126 | struct tsdev *tsdev; |
127 | struct ts_event buffer[TSDEV_BUFFER_SIZE]; | ||
125 | int head, tail; | 128 | int head, tail; |
126 | struct ts_event event[TSDEV_BUFFER_SIZE]; | 129 | spinlock_t buffer_lock; /* protects access to buffer, head and tail */ |
127 | int raw; | 130 | int raw; |
128 | }; | 131 | }; |
129 | 132 | ||
@@ -137,6 +140,7 @@ struct tsdev_client { | |||
137 | #define TS_SET_CAL _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration) | 140 | #define TS_SET_CAL _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration) |
138 | 141 | ||
139 | static struct tsdev *tsdev_table[TSDEV_MINORS/2]; | 142 | static struct tsdev *tsdev_table[TSDEV_MINORS/2]; |
143 | static DEFINE_MUTEX(tsdev_table_mutex); | ||
140 | 144 | ||
141 | static int tsdev_fasync(int fd, struct file *file, int on) | 145 | static int tsdev_fasync(int fd, struct file *file, int on) |
142 | { | 146 | { |
@@ -144,9 +148,91 @@ static int tsdev_fasync(int fd, struct file *file, int on) | |||
144 | int retval; | 148 | int retval; |
145 | 149 | ||
146 | retval = fasync_helper(fd, file, on, &client->fasync); | 150 | retval = fasync_helper(fd, file, on, &client->fasync); |
151 | |||
147 | return retval < 0 ? retval : 0; | 152 | return retval < 0 ? retval : 0; |
148 | } | 153 | } |
149 | 154 | ||
155 | static void tsdev_free(struct device *dev) | ||
156 | { | ||
157 | struct tsdev *tsdev = container_of(dev, struct tsdev, dev); | ||
158 | |||
159 | kfree(tsdev); | ||
160 | } | ||
161 | |||
162 | static void tsdev_attach_client(struct tsdev *tsdev, struct tsdev_client *client) | ||
163 | { | ||
164 | spin_lock(&tsdev->client_lock); | ||
165 | list_add_tail_rcu(&client->node, &tsdev->client_list); | ||
166 | spin_unlock(&tsdev->client_lock); | ||
167 | synchronize_sched(); | ||
168 | } | ||
169 | |||
170 | static void tsdev_detach_client(struct tsdev *tsdev, struct tsdev_client *client) | ||
171 | { | ||
172 | spin_lock(&tsdev->client_lock); | ||
173 | list_del_rcu(&client->node); | ||
174 | spin_unlock(&tsdev->client_lock); | ||
175 | synchronize_sched(); | ||
176 | } | ||
177 | |||
178 | static int tsdev_open_device(struct tsdev *tsdev) | ||
179 | { | ||
180 | int retval; | ||
181 | |||
182 | retval = mutex_lock_interruptible(&tsdev->mutex); | ||
183 | if (retval) | ||
184 | return retval; | ||
185 | |||
186 | if (!tsdev->exist) | ||
187 | retval = -ENODEV; | ||
188 | else if (!tsdev->open++) | ||
189 | retval = input_open_device(&tsdev->handle); | ||
190 | |||
191 | mutex_unlock(&tsdev->mutex); | ||
192 | return retval; | ||
193 | } | ||
194 | |||
195 | static void tsdev_close_device(struct tsdev *tsdev) | ||
196 | { | ||
197 | mutex_lock(&tsdev->mutex); | ||
198 | |||
199 | if (tsdev->exist && !--tsdev->open) | ||
200 | input_close_device(&tsdev->handle); | ||
201 | |||
202 | mutex_unlock(&tsdev->mutex); | ||
203 | } | ||
204 | |||
205 | /* | ||
206 | * Wake up users waiting for IO so they can disconnect from | ||
207 | * dead device. | ||
208 | */ | ||
209 | static void tsdev_hangup(struct tsdev *tsdev) | ||
210 | { | ||
211 | struct tsdev_client *client; | ||
212 | |||
213 | spin_lock(&tsdev->client_lock); | ||
214 | list_for_each_entry(client, &tsdev->client_list, node) | ||
215 | kill_fasync(&client->fasync, SIGIO, POLL_HUP); | ||
216 | spin_unlock(&tsdev->client_lock); | ||
217 | |||
218 | wake_up_interruptible(&tsdev->wait); | ||
219 | } | ||
220 | |||
221 | static int tsdev_release(struct inode *inode, struct file *file) | ||
222 | { | ||
223 | struct tsdev_client *client = file->private_data; | ||
224 | struct tsdev *tsdev = client->tsdev; | ||
225 | |||
226 | tsdev_fasync(-1, file, 0); | ||
227 | tsdev_detach_client(tsdev, client); | ||
228 | kfree(client); | ||
229 | |||
230 | tsdev_close_device(tsdev); | ||
231 | put_device(&tsdev->dev); | ||
232 | |||
233 | return 0; | ||
234 | } | ||
235 | |||
150 | static int tsdev_open(struct inode *inode, struct file *file) | 236 | static int tsdev_open(struct inode *inode, struct file *file) |
151 | { | 237 | { |
152 | int i = iminor(inode) - TSDEV_MINOR_BASE; | 238 | int i = iminor(inode) - TSDEV_MINOR_BASE; |
@@ -161,11 +247,16 @@ static int tsdev_open(struct inode *inode, struct file *file) | |||
161 | if (i >= TSDEV_MINORS) | 247 | if (i >= TSDEV_MINORS) |
162 | return -ENODEV; | 248 | return -ENODEV; |
163 | 249 | ||
250 | error = mutex_lock_interruptible(&tsdev_table_mutex); | ||
251 | if (error) | ||
252 | return error; | ||
164 | tsdev = tsdev_table[i & TSDEV_MINOR_MASK]; | 253 | tsdev = tsdev_table[i & TSDEV_MINOR_MASK]; |
165 | if (!tsdev || !tsdev->exist) | 254 | if (tsdev) |
166 | return -ENODEV; | 255 | get_device(&tsdev->dev); |
256 | mutex_unlock(&tsdev_table_mutex); | ||
167 | 257 | ||
168 | get_device(&tsdev->dev); | 258 | if (!tsdev) |
259 | return -ENODEV; | ||
169 | 260 | ||
170 | client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL); | 261 | client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL); |
171 | if (!client) { | 262 | if (!client) { |
@@ -173,51 +264,42 @@ static int tsdev_open(struct inode *inode, struct file *file) | |||
173 | goto err_put_tsdev; | 264 | goto err_put_tsdev; |
174 | } | 265 | } |
175 | 266 | ||
267 | spin_lock_init(&client->buffer_lock); | ||
176 | client->tsdev = tsdev; | 268 | client->tsdev = tsdev; |
177 | client->raw = (i >= TSDEV_MINORS / 2) ? 1 : 0; | 269 | client->raw = i >= TSDEV_MINORS / 2; |
178 | list_add_tail(&client->node, &tsdev->client_list); | 270 | tsdev_attach_client(tsdev, client); |
179 | 271 | ||
180 | if (!tsdev->open++ && tsdev->exist) { | 272 | error = tsdev_open_device(tsdev); |
181 | error = input_open_device(&tsdev->handle); | 273 | if (error) |
182 | if (error) | 274 | goto err_free_client; |
183 | goto err_free_client; | ||
184 | } | ||
185 | 275 | ||
186 | file->private_data = client; | 276 | file->private_data = client; |
187 | return 0; | 277 | return 0; |
188 | 278 | ||
189 | err_free_client: | 279 | err_free_client: |
190 | list_del(&client->node); | 280 | tsdev_detach_client(tsdev, client); |
191 | kfree(client); | 281 | kfree(client); |
192 | err_put_tsdev: | 282 | err_put_tsdev: |
193 | put_device(&tsdev->dev); | 283 | put_device(&tsdev->dev); |
194 | return error; | 284 | return error; |
195 | } | 285 | } |
196 | 286 | ||
197 | static void tsdev_free(struct device *dev) | 287 | static int tsdev_fetch_next_event(struct tsdev_client *client, |
198 | { | 288 | struct ts_event *event) |
199 | struct tsdev *tsdev = container_of(dev, struct tsdev, dev); | ||
200 | |||
201 | tsdev_table[tsdev->minor] = NULL; | ||
202 | kfree(tsdev); | ||
203 | } | ||
204 | |||
205 | static int tsdev_release(struct inode *inode, struct file *file) | ||
206 | { | 289 | { |
207 | struct tsdev_client *client = file->private_data; | 290 | int have_event; |
208 | struct tsdev *tsdev = client->tsdev; | ||
209 | 291 | ||
210 | tsdev_fasync(-1, file, 0); | 292 | spin_lock_irq(&client->buffer_lock); |
211 | |||
212 | list_del(&client->node); | ||
213 | kfree(client); | ||
214 | 293 | ||
215 | if (!--tsdev->open && tsdev->exist) | 294 | have_event = client->head != client->tail; |
216 | input_close_device(&tsdev->handle); | 295 | if (have_event) { |
296 | *event = client->buffer[client->tail++]; | ||
297 | client->tail &= TSDEV_BUFFER_SIZE - 1; | ||
298 | } | ||
217 | 299 | ||
218 | put_device(&tsdev->dev); | 300 | spin_unlock_irq(&client->buffer_lock); |
219 | 301 | ||
220 | return 0; | 302 | return have_event; |
221 | } | 303 | } |
222 | 304 | ||
223 | static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, | 305 | static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, |
@@ -225,9 +307,11 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, | |||
225 | { | 307 | { |
226 | struct tsdev_client *client = file->private_data; | 308 | struct tsdev_client *client = file->private_data; |
227 | struct tsdev *tsdev = client->tsdev; | 309 | struct tsdev *tsdev = client->tsdev; |
228 | int retval = 0; | 310 | struct ts_event event; |
311 | int retval; | ||
229 | 312 | ||
230 | if (client->head == client->tail && tsdev->exist && (file->f_flags & O_NONBLOCK)) | 313 | if (client->head == client->tail && tsdev->exist && |
314 | (file->f_flags & O_NONBLOCK)) | ||
231 | return -EAGAIN; | 315 | return -EAGAIN; |
232 | 316 | ||
233 | retval = wait_event_interruptible(tsdev->wait, | 317 | retval = wait_event_interruptible(tsdev->wait, |
@@ -238,13 +322,14 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, | |||
238 | if (!tsdev->exist) | 322 | if (!tsdev->exist) |
239 | return -ENODEV; | 323 | return -ENODEV; |
240 | 324 | ||
241 | while (client->head != client->tail && | 325 | while (retval + sizeof(struct ts_event) <= count && |
242 | retval + sizeof (struct ts_event) <= count) { | 326 | tsdev_fetch_next_event(client, &event)) { |
243 | if (copy_to_user (buffer + retval, client->event + client->tail, | 327 | |
244 | sizeof (struct ts_event))) | 328 | if (copy_to_user(buffer + retval, &event, |
329 | sizeof(struct ts_event))) | ||
245 | return -EFAULT; | 330 | return -EFAULT; |
246 | client->tail = (client->tail + 1) & (TSDEV_BUFFER_SIZE - 1); | 331 | |
247 | retval += sizeof (struct ts_event); | 332 | retval += sizeof(struct ts_event); |
248 | } | 333 | } |
249 | 334 | ||
250 | return retval; | 335 | return retval; |
@@ -261,14 +346,23 @@ static unsigned int tsdev_poll(struct file *file, poll_table *wait) | |||
261 | (tsdev->exist ? 0 : (POLLHUP | POLLERR)); | 346 | (tsdev->exist ? 0 : (POLLHUP | POLLERR)); |
262 | } | 347 | } |
263 | 348 | ||
264 | static int tsdev_ioctl(struct inode *inode, struct file *file, | 349 | static long tsdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
265 | unsigned int cmd, unsigned long arg) | ||
266 | { | 350 | { |
267 | struct tsdev_client *client = file->private_data; | 351 | struct tsdev_client *client = file->private_data; |
268 | struct tsdev *tsdev = client->tsdev; | 352 | struct tsdev *tsdev = client->tsdev; |
269 | int retval = 0; | 353 | int retval = 0; |
270 | 354 | ||
355 | retval = mutex_lock_interruptible(&tsdev->mutex); | ||
356 | if (retval) | ||
357 | return retval; | ||
358 | |||
359 | if (!tsdev->exist) { | ||
360 | retval = -ENODEV; | ||
361 | goto out; | ||
362 | } | ||
363 | |||
271 | switch (cmd) { | 364 | switch (cmd) { |
365 | |||
272 | case TS_GET_CAL: | 366 | case TS_GET_CAL: |
273 | if (copy_to_user((void __user *)arg, &tsdev->cal, | 367 | if (copy_to_user((void __user *)arg, &tsdev->cal, |
274 | sizeof (struct ts_calibration))) | 368 | sizeof (struct ts_calibration))) |
@@ -277,7 +371,7 @@ static int tsdev_ioctl(struct inode *inode, struct file *file, | |||
277 | 371 | ||
278 | case TS_SET_CAL: | 372 | case TS_SET_CAL: |
279 | if (copy_from_user(&tsdev->cal, (void __user *)arg, | 373 | if (copy_from_user(&tsdev->cal, (void __user *)arg, |
280 | sizeof (struct ts_calibration))) | 374 | sizeof(struct ts_calibration))) |
281 | retval = -EFAULT; | 375 | retval = -EFAULT; |
282 | break; | 376 | break; |
283 | 377 | ||
@@ -286,29 +380,79 @@ static int tsdev_ioctl(struct inode *inode, struct file *file, | |||
286 | break; | 380 | break; |
287 | } | 381 | } |
288 | 382 | ||
383 | out: | ||
384 | mutex_unlock(&tsdev->mutex); | ||
289 | return retval; | 385 | return retval; |
290 | } | 386 | } |
291 | 387 | ||
292 | static const struct file_operations tsdev_fops = { | 388 | static const struct file_operations tsdev_fops = { |
293 | .owner = THIS_MODULE, | 389 | .owner = THIS_MODULE, |
294 | .open = tsdev_open, | 390 | .open = tsdev_open, |
295 | .release = tsdev_release, | 391 | .release = tsdev_release, |
296 | .read = tsdev_read, | 392 | .read = tsdev_read, |
297 | .poll = tsdev_poll, | 393 | .poll = tsdev_poll, |
298 | .fasync = tsdev_fasync, | 394 | .fasync = tsdev_fasync, |
299 | .ioctl = tsdev_ioctl, | 395 | .unlocked_ioctl = tsdev_ioctl, |
300 | }; | 396 | }; |
301 | 397 | ||
398 | static void tsdev_pass_event(struct tsdev *tsdev, struct tsdev_client *client, | ||
399 | int x, int y, int pressure, int millisecs) | ||
400 | { | ||
401 | struct ts_event *event; | ||
402 | int tmp; | ||
403 | |||
404 | /* Interrupts are already disabled, just acquire the lock */ | ||
405 | spin_lock(&client->buffer_lock); | ||
406 | |||
407 | event = &client->buffer[client->head++]; | ||
408 | client->head &= TSDEV_BUFFER_SIZE - 1; | ||
409 | |||
410 | /* Calibration */ | ||
411 | if (!client->raw) { | ||
412 | x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans; | ||
413 | y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans; | ||
414 | if (tsdev->cal.xyswap) { | ||
415 | tmp = x; x = y; y = tmp; | ||
416 | } | ||
417 | } | ||
418 | |||
419 | event->millisecs = millisecs; | ||
420 | event->x = x; | ||
421 | event->y = y; | ||
422 | event->pressure = pressure; | ||
423 | |||
424 | spin_unlock(&client->buffer_lock); | ||
425 | |||
426 | kill_fasync(&client->fasync, SIGIO, POLL_IN); | ||
427 | } | ||
428 | |||
429 | static void tsdev_distribute_event(struct tsdev *tsdev) | ||
430 | { | ||
431 | struct tsdev_client *client; | ||
432 | struct timeval time; | ||
433 | int millisecs; | ||
434 | |||
435 | do_gettimeofday(&time); | ||
436 | millisecs = time.tv_usec / 1000; | ||
437 | |||
438 | list_for_each_entry_rcu(client, &tsdev->client_list, node) | ||
439 | tsdev_pass_event(tsdev, client, | ||
440 | tsdev->x, tsdev->y, | ||
441 | tsdev->pressure, millisecs); | ||
442 | } | ||
443 | |||
302 | static void tsdev_event(struct input_handle *handle, unsigned int type, | 444 | static void tsdev_event(struct input_handle *handle, unsigned int type, |
303 | unsigned int code, int value) | 445 | unsigned int code, int value) |
304 | { | 446 | { |
305 | struct tsdev *tsdev = handle->private; | 447 | struct tsdev *tsdev = handle->private; |
306 | struct tsdev_client *client; | 448 | struct input_dev *dev = handle->dev; |
307 | struct timeval time; | 449 | int wake_up_readers = 0; |
308 | 450 | ||
309 | switch (type) { | 451 | switch (type) { |
452 | |||
310 | case EV_ABS: | 453 | case EV_ABS: |
311 | switch (code) { | 454 | switch (code) { |
455 | |||
312 | case ABS_X: | 456 | case ABS_X: |
313 | tsdev->x = value; | 457 | tsdev->x = value; |
314 | break; | 458 | break; |
@@ -318,9 +462,9 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, | |||
318 | break; | 462 | break; |
319 | 463 | ||
320 | case ABS_PRESSURE: | 464 | case ABS_PRESSURE: |
321 | if (value > handle->dev->absmax[ABS_PRESSURE]) | 465 | if (value > dev->absmax[ABS_PRESSURE]) |
322 | value = handle->dev->absmax[ABS_PRESSURE]; | 466 | value = dev->absmax[ABS_PRESSURE]; |
323 | value -= handle->dev->absmin[ABS_PRESSURE]; | 467 | value -= dev->absmin[ABS_PRESSURE]; |
324 | if (value < 0) | 468 | if (value < 0) |
325 | value = 0; | 469 | value = 0; |
326 | tsdev->pressure = value; | 470 | tsdev->pressure = value; |
@@ -330,6 +474,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, | |||
330 | 474 | ||
331 | case EV_REL: | 475 | case EV_REL: |
332 | switch (code) { | 476 | switch (code) { |
477 | |||
333 | case REL_X: | 478 | case REL_X: |
334 | tsdev->x += value; | 479 | tsdev->x += value; |
335 | if (tsdev->x < 0) | 480 | if (tsdev->x < 0) |
@@ -351,6 +496,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, | |||
351 | case EV_KEY: | 496 | case EV_KEY: |
352 | if (code == BTN_TOUCH || code == BTN_MOUSE) { | 497 | if (code == BTN_TOUCH || code == BTN_MOUSE) { |
353 | switch (value) { | 498 | switch (value) { |
499 | |||
354 | case 0: | 500 | case 0: |
355 | tsdev->pressure = 0; | 501 | tsdev->pressure = 0; |
356 | break; | 502 | break; |
@@ -362,49 +508,71 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, | |||
362 | } | 508 | } |
363 | } | 509 | } |
364 | break; | 510 | break; |
511 | |||
512 | case EV_SYN: | ||
513 | if (code == SYN_REPORT) { | ||
514 | tsdev_distribute_event(tsdev); | ||
515 | wake_up_readers = 1; | ||
516 | } | ||
517 | break; | ||
365 | } | 518 | } |
366 | 519 | ||
367 | if (type != EV_SYN || code != SYN_REPORT) | 520 | if (wake_up_readers) |
368 | return; | 521 | wake_up_interruptible(&tsdev->wait); |
522 | } | ||
523 | |||
524 | static int tsdev_install_chrdev(struct tsdev *tsdev) | ||
525 | { | ||
526 | tsdev_table[tsdev->minor] = tsdev; | ||
527 | return 0; | ||
528 | } | ||
369 | 529 | ||
370 | list_for_each_entry(client, &tsdev->client_list, node) { | 530 | static void tsdev_remove_chrdev(struct tsdev *tsdev) |
371 | int x, y, tmp; | 531 | { |
532 | mutex_lock(&tsdev_table_mutex); | ||
533 | tsdev_table[tsdev->minor] = NULL; | ||
534 | mutex_unlock(&tsdev_table_mutex); | ||
535 | } | ||
372 | 536 | ||
373 | do_gettimeofday(&time); | 537 | /* |
374 | client->event[client->head].millisecs = time.tv_usec / 1000; | 538 | * Mark device non-existant. This disables writes, ioctls and |
375 | client->event[client->head].pressure = tsdev->pressure; | 539 | * prevents new users from opening the device. Already posted |
540 | * blocking reads will stay, however new ones will fail. | ||
541 | */ | ||
542 | static void tsdev_mark_dead(struct tsdev *tsdev) | ||
543 | { | ||
544 | mutex_lock(&tsdev->mutex); | ||
545 | tsdev->exist = 0; | ||
546 | mutex_unlock(&tsdev->mutex); | ||
547 | } | ||
376 | 548 | ||
377 | x = tsdev->x; | 549 | static void tsdev_cleanup(struct tsdev *tsdev) |
378 | y = tsdev->y; | 550 | { |
551 | struct input_handle *handle = &tsdev->handle; | ||
379 | 552 | ||
380 | /* Calibration */ | 553 | tsdev_mark_dead(tsdev); |
381 | if (!client->raw) { | 554 | tsdev_hangup(tsdev); |
382 | x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans; | 555 | tsdev_remove_chrdev(tsdev); |
383 | y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans; | ||
384 | if (tsdev->cal.xyswap) { | ||
385 | tmp = x; x = y; y = tmp; | ||
386 | } | ||
387 | } | ||
388 | 556 | ||
389 | client->event[client->head].x = x; | 557 | /* tsdev is marked dead so noone else accesses tsdev->open */ |
390 | client->event[client->head].y = y; | 558 | if (tsdev->open) |
391 | client->head = (client->head + 1) & (TSDEV_BUFFER_SIZE - 1); | 559 | input_close_device(handle); |
392 | kill_fasync(&client->fasync, SIGIO, POLL_IN); | ||
393 | } | ||
394 | wake_up_interruptible(&tsdev->wait); | ||
395 | } | 560 | } |
396 | 561 | ||
397 | static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, | 562 | static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, |
398 | const struct input_device_id *id) | 563 | const struct input_device_id *id) |
399 | { | 564 | { |
400 | struct tsdev *tsdev; | 565 | struct tsdev *tsdev; |
401 | int minor, delta; | 566 | int delta; |
567 | int minor; | ||
402 | int error; | 568 | int error; |
403 | 569 | ||
404 | for (minor = 0; minor < TSDEV_MINORS / 2 && tsdev_table[minor]; minor++); | 570 | for (minor = 0; minor < TSDEV_MINORS / 2; minor++) |
405 | if (minor >= TSDEV_MINORS / 2) { | 571 | if (!tsdev_table[minor]) |
406 | printk(KERN_ERR | 572 | break; |
407 | "tsdev: You have way too many touchscreens\n"); | 573 | |
574 | if (minor == TSDEV_MINORS) { | ||
575 | printk(KERN_ERR "tsdev: no more free tsdev devices\n"); | ||
408 | return -ENFILE; | 576 | return -ENFILE; |
409 | } | 577 | } |
410 | 578 | ||
@@ -413,15 +581,18 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, | |||
413 | return -ENOMEM; | 581 | return -ENOMEM; |
414 | 582 | ||
415 | INIT_LIST_HEAD(&tsdev->client_list); | 583 | INIT_LIST_HEAD(&tsdev->client_list); |
584 | spin_lock_init(&tsdev->client_lock); | ||
585 | mutex_init(&tsdev->mutex); | ||
416 | init_waitqueue_head(&tsdev->wait); | 586 | init_waitqueue_head(&tsdev->wait); |
417 | 587 | ||
588 | snprintf(tsdev->name, sizeof(tsdev->name), "ts%d", minor); | ||
418 | tsdev->exist = 1; | 589 | tsdev->exist = 1; |
419 | tsdev->minor = minor; | 590 | tsdev->minor = minor; |
591 | |||
420 | tsdev->handle.dev = dev; | 592 | tsdev->handle.dev = dev; |
421 | tsdev->handle.name = tsdev->name; | 593 | tsdev->handle.name = tsdev->name; |
422 | tsdev->handle.handler = handler; | 594 | tsdev->handle.handler = handler; |
423 | tsdev->handle.private = tsdev; | 595 | tsdev->handle.private = tsdev; |
424 | snprintf(tsdev->name, sizeof(tsdev->name), "ts%d", minor); | ||
425 | 596 | ||
426 | /* Precompute the rough calibration matrix */ | 597 | /* Precompute the rough calibration matrix */ |
427 | delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1; | 598 | delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1; |
@@ -436,28 +607,31 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, | |||
436 | tsdev->cal.yscale = (yres << 8) / delta; | 607 | tsdev->cal.yscale = (yres << 8) / delta; |
437 | tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8); | 608 | tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8); |
438 | 609 | ||
439 | snprintf(tsdev->dev.bus_id, sizeof(tsdev->dev.bus_id), | 610 | strlcpy(tsdev->dev.bus_id, tsdev->name, sizeof(tsdev->dev.bus_id)); |
440 | "ts%d", minor); | 611 | tsdev->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor); |
441 | tsdev->dev.class = &input_class; | 612 | tsdev->dev.class = &input_class; |
442 | tsdev->dev.parent = &dev->dev; | 613 | tsdev->dev.parent = &dev->dev; |
443 | tsdev->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor); | ||
444 | tsdev->dev.release = tsdev_free; | 614 | tsdev->dev.release = tsdev_free; |
445 | device_initialize(&tsdev->dev); | 615 | device_initialize(&tsdev->dev); |
446 | 616 | ||
447 | tsdev_table[minor] = tsdev; | 617 | error = input_register_handle(&tsdev->handle); |
448 | |||
449 | error = device_add(&tsdev->dev); | ||
450 | if (error) | 618 | if (error) |
451 | goto err_free_tsdev; | 619 | goto err_free_tsdev; |
452 | 620 | ||
453 | error = input_register_handle(&tsdev->handle); | 621 | error = tsdev_install_chrdev(tsdev); |
454 | if (error) | 622 | if (error) |
455 | goto err_delete_tsdev; | 623 | goto err_unregister_handle; |
624 | |||
625 | error = device_add(&tsdev->dev); | ||
626 | if (error) | ||
627 | goto err_cleanup_tsdev; | ||
456 | 628 | ||
457 | return 0; | 629 | return 0; |
458 | 630 | ||
459 | err_delete_tsdev: | 631 | err_cleanup_tsdev: |
460 | device_del(&tsdev->dev); | 632 | tsdev_cleanup(tsdev); |
633 | err_unregister_handle: | ||
634 | input_unregister_handle(&tsdev->handle); | ||
461 | err_free_tsdev: | 635 | err_free_tsdev: |
462 | put_device(&tsdev->dev); | 636 | put_device(&tsdev->dev); |
463 | return error; | 637 | return error; |
@@ -466,20 +640,10 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, | |||
466 | static void tsdev_disconnect(struct input_handle *handle) | 640 | static void tsdev_disconnect(struct input_handle *handle) |
467 | { | 641 | { |
468 | struct tsdev *tsdev = handle->private; | 642 | struct tsdev *tsdev = handle->private; |
469 | struct tsdev_client *client; | ||
470 | 643 | ||
471 | input_unregister_handle(handle); | ||
472 | device_del(&tsdev->dev); | 644 | device_del(&tsdev->dev); |
473 | 645 | tsdev_cleanup(tsdev); | |
474 | tsdev->exist = 0; | 646 | input_unregister_handle(handle); |
475 | |||
476 | if (tsdev->open) { | ||
477 | input_close_device(handle); | ||
478 | list_for_each_entry(client, &tsdev->client_list, node) | ||
479 | kill_fasync(&client->fasync, SIGIO, POLL_HUP); | ||
480 | wake_up_interruptible(&tsdev->wait); | ||
481 | } | ||
482 | |||
483 | put_device(&tsdev->dev); | 647 | put_device(&tsdev->dev); |
484 | } | 648 | } |
485 | 649 | ||
@@ -510,13 +674,13 @@ static const struct input_device_id tsdev_ids[] = { | |||
510 | MODULE_DEVICE_TABLE(input, tsdev_ids); | 674 | MODULE_DEVICE_TABLE(input, tsdev_ids); |
511 | 675 | ||
512 | static struct input_handler tsdev_handler = { | 676 | static struct input_handler tsdev_handler = { |
513 | .event = tsdev_event, | 677 | .event = tsdev_event, |
514 | .connect = tsdev_connect, | 678 | .connect = tsdev_connect, |
515 | .disconnect = tsdev_disconnect, | 679 | .disconnect = tsdev_disconnect, |
516 | .fops = &tsdev_fops, | 680 | .fops = &tsdev_fops, |
517 | .minor = TSDEV_MINOR_BASE, | 681 | .minor = TSDEV_MINOR_BASE, |
518 | .name = "tsdev", | 682 | .name = "tsdev", |
519 | .id_table = tsdev_ids, | 683 | .id_table = tsdev_ids, |
520 | }; | 684 | }; |
521 | 685 | ||
522 | static int __init tsdev_init(void) | 686 | static int __init tsdev_init(void) |