diff options
author | Dmitry Torokhov <dtor_core@ameritech.net> | 2005-11-20 00:51:22 -0500 |
---|---|---|
committer | Dmitry Torokhov <dtor_core@ameritech.net> | 2005-11-20 00:51:22 -0500 |
commit | 29506415a0ff0152cc2928f8fcac724fbbf98651 (patch) | |
tree | e1da48667c6b0499f2ea882b1d762758241da1e6 /drivers/input | |
parent | e753b650e10af8a040b1081e72088b826bdef72f (diff) |
Input: uinput - convert to dynalloc allocation
Also introduce proper locking when creating/deleting device.
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/misc/uinput.c | 310 |
1 files changed, 163 insertions, 147 deletions
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 948c1cc01bc9..713260322137 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c | |||
@@ -152,67 +152,62 @@ static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) | |||
152 | return retval; | 152 | return retval; |
153 | } | 153 | } |
154 | 154 | ||
155 | static int uinput_create_device(struct uinput_device *udev) | 155 | static void uinput_destroy_device(struct uinput_device *udev) |
156 | { | 156 | { |
157 | if (!udev->dev->name) { | 157 | const char *name, *phys; |
158 | printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); | 158 | |
159 | return -EINVAL; | 159 | if (udev->dev) { |
160 | name = udev->dev->name; | ||
161 | phys = udev->dev->phys; | ||
162 | if (udev->state == UIST_CREATED) | ||
163 | input_unregister_device(udev->dev); | ||
164 | else | ||
165 | input_free_device(udev->dev); | ||
166 | kfree(name); | ||
167 | kfree(phys); | ||
168 | udev->dev = NULL; | ||
160 | } | 169 | } |
161 | 170 | ||
162 | udev->dev->event = uinput_dev_event; | 171 | udev->state = UIST_NEW_DEVICE; |
163 | udev->dev->upload_effect = uinput_dev_upload_effect; | ||
164 | udev->dev->erase_effect = uinput_dev_erase_effect; | ||
165 | udev->dev->private = udev; | ||
166 | |||
167 | init_waitqueue_head(&udev->waitq); | ||
168 | |||
169 | input_register_device(udev->dev); | ||
170 | |||
171 | set_bit(UIST_CREATED, &udev->state); | ||
172 | |||
173 | return 0; | ||
174 | } | 172 | } |
175 | 173 | ||
176 | static int uinput_destroy_device(struct uinput_device *udev) | 174 | static int uinput_create_device(struct uinput_device *udev) |
177 | { | 175 | { |
178 | if (!test_bit(UIST_CREATED, &udev->state)) { | 176 | int error; |
179 | printk(KERN_WARNING "%s: create the device first\n", UINPUT_NAME); | 177 | |
178 | if (udev->state != UIST_SETUP_COMPLETE) { | ||
179 | printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); | ||
180 | return -EINVAL; | 180 | return -EINVAL; |
181 | } | 181 | } |
182 | 182 | ||
183 | input_unregister_device(udev->dev); | 183 | error = input_register_device(udev->dev); |
184 | if (error) { | ||
185 | uinput_destroy_device(udev); | ||
186 | return error; | ||
187 | } | ||
184 | 188 | ||
185 | clear_bit(UIST_CREATED, &udev->state); | 189 | udev->state = UIST_CREATED; |
186 | 190 | ||
187 | return 0; | 191 | return 0; |
188 | } | 192 | } |
189 | 193 | ||
190 | static int uinput_open(struct inode *inode, struct file *file) | 194 | static int uinput_open(struct inode *inode, struct file *file) |
191 | { | 195 | { |
192 | struct uinput_device *newdev; | 196 | struct uinput_device *newdev; |
193 | struct input_dev *newinput; | ||
194 | 197 | ||
195 | newdev = kmalloc(sizeof(struct uinput_device), GFP_KERNEL); | 198 | newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL); |
196 | if (!newdev) | 199 | if (!newdev) |
197 | goto error; | 200 | return -ENOMEM; |
198 | memset(newdev, 0, sizeof(struct uinput_device)); | 201 | |
202 | init_MUTEX(&newdev->sem); | ||
199 | spin_lock_init(&newdev->requests_lock); | 203 | spin_lock_init(&newdev->requests_lock); |
200 | init_waitqueue_head(&newdev->requests_waitq); | 204 | init_waitqueue_head(&newdev->requests_waitq); |
201 | 205 | init_waitqueue_head(&newdev->waitq); | |
202 | newinput = kmalloc(sizeof(struct input_dev), GFP_KERNEL); | 206 | newdev->state = UIST_NEW_DEVICE; |
203 | if (!newinput) | ||
204 | goto cleanup; | ||
205 | memset(newinput, 0, sizeof(struct input_dev)); | ||
206 | |||
207 | newdev->dev = newinput; | ||
208 | 207 | ||
209 | file->private_data = newdev; | 208 | file->private_data = newdev; |
210 | 209 | ||
211 | return 0; | 210 | return 0; |
212 | cleanup: | ||
213 | kfree(newdev); | ||
214 | error: | ||
215 | return -ENOMEM; | ||
216 | } | 211 | } |
217 | 212 | ||
218 | static int uinput_validate_absbits(struct input_dev *dev) | 213 | static int uinput_validate_absbits(struct input_dev *dev) |
@@ -246,34 +241,55 @@ static int uinput_validate_absbits(struct input_dev *dev) | |||
246 | return retval; | 241 | return retval; |
247 | } | 242 | } |
248 | 243 | ||
249 | static int uinput_alloc_device(struct file *file, const char __user *buffer, size_t count) | 244 | static int uinput_allocate_device(struct uinput_device *udev) |
245 | { | ||
246 | udev->dev = input_allocate_device(); | ||
247 | if (!udev->dev) | ||
248 | return -ENOMEM; | ||
249 | |||
250 | udev->dev->event = uinput_dev_event; | ||
251 | udev->dev->upload_effect = uinput_dev_upload_effect; | ||
252 | udev->dev->erase_effect = uinput_dev_erase_effect; | ||
253 | udev->dev->private = udev; | ||
254 | |||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | static int uinput_setup_device(struct uinput_device *udev, const char __user *buffer, size_t count) | ||
250 | { | 259 | { |
251 | struct uinput_user_dev *user_dev; | 260 | struct uinput_user_dev *user_dev; |
252 | struct input_dev *dev; | 261 | struct input_dev *dev; |
253 | struct uinput_device *udev; | ||
254 | char *name; | 262 | char *name; |
255 | int size; | 263 | int size; |
256 | int retval; | 264 | int retval; |
257 | 265 | ||
258 | retval = count; | 266 | if (count != sizeof(struct uinput_user_dev)) |
267 | return -EINVAL; | ||
268 | |||
269 | if (!udev->dev) { | ||
270 | retval = uinput_allocate_device(udev); | ||
271 | if (retval) | ||
272 | return retval; | ||
273 | } | ||
259 | 274 | ||
260 | udev = file->private_data; | ||
261 | dev = udev->dev; | 275 | dev = udev->dev; |
262 | 276 | ||
263 | user_dev = kmalloc(sizeof(struct uinput_user_dev), GFP_KERNEL); | 277 | user_dev = kmalloc(sizeof(struct uinput_user_dev), GFP_KERNEL); |
264 | if (!user_dev) { | 278 | if (!user_dev) |
265 | retval = -ENOMEM; | 279 | return -ENOMEM; |
266 | goto exit; | ||
267 | } | ||
268 | 280 | ||
269 | if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) { | 281 | if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) { |
270 | retval = -EFAULT; | 282 | retval = -EFAULT; |
271 | goto exit; | 283 | goto exit; |
272 | } | 284 | } |
273 | 285 | ||
274 | kfree(dev->name); | ||
275 | |||
276 | size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1; | 286 | size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1; |
287 | if (!size) { | ||
288 | retval = -EINVAL; | ||
289 | goto exit; | ||
290 | } | ||
291 | |||
292 | kfree(dev->name); | ||
277 | dev->name = name = kmalloc(size, GFP_KERNEL); | 293 | dev->name = name = kmalloc(size, GFP_KERNEL); |
278 | if (!name) { | 294 | if (!name) { |
279 | retval = -ENOMEM; | 295 | retval = -ENOMEM; |
@@ -296,32 +312,50 @@ static int uinput_alloc_device(struct file *file, const char __user *buffer, siz | |||
296 | /* check if absmin/absmax/absfuzz/absflat are filled as | 312 | /* check if absmin/absmax/absfuzz/absflat are filled as |
297 | * told in Documentation/input/input-programming.txt */ | 313 | * told in Documentation/input/input-programming.txt */ |
298 | if (test_bit(EV_ABS, dev->evbit)) { | 314 | if (test_bit(EV_ABS, dev->evbit)) { |
299 | int err = uinput_validate_absbits(dev); | 315 | retval = uinput_validate_absbits(dev); |
300 | if (err < 0) { | 316 | if (retval < 0) |
301 | retval = err; | 317 | goto exit; |
302 | kfree(dev->name); | ||
303 | } | ||
304 | } | 318 | } |
305 | 319 | ||
306 | exit: | 320 | udev->state = UIST_SETUP_COMPLETE; |
321 | retval = count; | ||
322 | |||
323 | exit: | ||
307 | kfree(user_dev); | 324 | kfree(user_dev); |
308 | return retval; | 325 | return retval; |
309 | } | 326 | } |
310 | 327 | ||
328 | static inline ssize_t uinput_inject_event(struct uinput_device *udev, const char __user *buffer, size_t count) | ||
329 | { | ||
330 | struct input_event ev; | ||
331 | |||
332 | if (count != sizeof(struct input_event)) | ||
333 | return -EINVAL; | ||
334 | |||
335 | if (copy_from_user(&ev, buffer, sizeof(struct input_event))) | ||
336 | return -EFAULT; | ||
337 | |||
338 | input_event(udev->dev, ev.type, ev.code, ev.value); | ||
339 | |||
340 | return sizeof(struct input_event); | ||
341 | } | ||
342 | |||
311 | static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) | 343 | static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) |
312 | { | 344 | { |
313 | struct uinput_device *udev = file->private_data; | 345 | struct uinput_device *udev = file->private_data; |
346 | int retval; | ||
314 | 347 | ||
315 | if (test_bit(UIST_CREATED, &udev->state)) { | 348 | retval = down_interruptible(&udev->sem); |
316 | struct input_event ev; | 349 | if (retval) |
350 | return retval; | ||
351 | |||
352 | retval = udev->state == UIST_CREATED ? | ||
353 | uinput_inject_event(udev, buffer, count) : | ||
354 | uinput_setup_device(udev, buffer, count); | ||
317 | 355 | ||
318 | if (copy_from_user(&ev, buffer, sizeof(struct input_event))) | 356 | up(&udev->sem); |
319 | return -EFAULT; | ||
320 | input_event(udev->dev, ev.type, ev.code, ev.value); | ||
321 | } else | ||
322 | count = uinput_alloc_device(file, buffer, count); | ||
323 | 357 | ||
324 | return count; | 358 | return retval; |
325 | } | 359 | } |
326 | 360 | ||
327 | static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) | 361 | static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) |
@@ -329,28 +363,38 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, | |||
329 | struct uinput_device *udev = file->private_data; | 363 | struct uinput_device *udev = file->private_data; |
330 | int retval = 0; | 364 | int retval = 0; |
331 | 365 | ||
332 | if (!test_bit(UIST_CREATED, &udev->state)) | 366 | if (udev->state != UIST_CREATED) |
333 | return -ENODEV; | 367 | return -ENODEV; |
334 | 368 | ||
335 | if (udev->head == udev->tail && (file->f_flags & O_NONBLOCK)) | 369 | if (udev->head == udev->tail && (file->f_flags & O_NONBLOCK)) |
336 | return -EAGAIN; | 370 | return -EAGAIN; |
337 | 371 | ||
338 | retval = wait_event_interruptible(udev->waitq, | 372 | retval = wait_event_interruptible(udev->waitq, |
339 | udev->head != udev->tail || !test_bit(UIST_CREATED, &udev->state)); | 373 | udev->head != udev->tail || udev->state != UIST_CREATED); |
340 | if (retval) | 374 | if (retval) |
341 | return retval; | 375 | return retval; |
342 | 376 | ||
343 | if (!test_bit(UIST_CREATED, &udev->state)) | 377 | retval = down_interruptible(&udev->sem); |
344 | return -ENODEV; | 378 | if (retval) |
379 | return retval; | ||
380 | |||
381 | if (udev->state != UIST_CREATED) { | ||
382 | retval = -ENODEV; | ||
383 | goto out; | ||
384 | } | ||
345 | 385 | ||
346 | while ((udev->head != udev->tail) && | 386 | while (udev->head != udev->tail && retval + sizeof(struct input_event) <= count) { |
347 | (retval + sizeof(struct input_event) <= count)) { | 387 | if (copy_to_user(buffer + retval, &udev->buff[udev->tail], sizeof(struct input_event))) { |
348 | if (copy_to_user(buffer + retval, &udev->buff[udev->tail], sizeof(struct input_event))) | 388 | retval = -EFAULT; |
349 | return -EFAULT; | 389 | goto out; |
390 | } | ||
350 | udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; | 391 | udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; |
351 | retval += sizeof(struct input_event); | 392 | retval += sizeof(struct input_event); |
352 | } | 393 | } |
353 | 394 | ||
395 | out: | ||
396 | up(&udev->sem); | ||
397 | |||
354 | return retval; | 398 | return retval; |
355 | } | 399 | } |
356 | 400 | ||
@@ -366,28 +410,30 @@ static unsigned int uinput_poll(struct file *file, poll_table *wait) | |||
366 | return 0; | 410 | return 0; |
367 | } | 411 | } |
368 | 412 | ||
369 | static int uinput_burn_device(struct uinput_device *udev) | 413 | static int uinput_release(struct inode *inode, struct file *file) |
370 | { | 414 | { |
371 | if (test_bit(UIST_CREATED, &udev->state)) | 415 | struct uinput_device *udev = file->private_data; |
372 | uinput_destroy_device(udev); | ||
373 | 416 | ||
374 | kfree(udev->dev->name); | 417 | uinput_destroy_device(udev); |
375 | kfree(udev->dev->phys); | ||
376 | kfree(udev->dev); | ||
377 | kfree(udev); | 418 | kfree(udev); |
378 | 419 | ||
379 | return 0; | 420 | return 0; |
380 | } | 421 | } |
381 | 422 | ||
382 | static int uinput_close(struct inode *inode, struct file *file) | 423 | #define uinput_set_bit(_arg, _bit, _max) \ |
424 | ({ \ | ||
425 | int __ret = 0; \ | ||
426 | if (udev->state == UIST_CREATED) \ | ||
427 | __ret = -EINVAL; \ | ||
428 | else if ((_arg) > (_max)) \ | ||
429 | __ret = -EINVAL; \ | ||
430 | else set_bit((_arg), udev->dev->_bit); \ | ||
431 | __ret; \ | ||
432 | }) | ||
433 | |||
434 | static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
383 | { | 435 | { |
384 | uinput_burn_device(file->private_data); | 436 | int retval; |
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) | ||
389 | { | ||
390 | int retval = 0; | ||
391 | struct uinput_device *udev; | 437 | struct uinput_device *udev; |
392 | void __user *p = (void __user *)arg; | 438 | void __user *p = (void __user *)arg; |
393 | struct uinput_ff_upload ff_up; | 439 | struct uinput_ff_upload ff_up; |
@@ -398,19 +444,14 @@ static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd | |||
398 | 444 | ||
399 | udev = file->private_data; | 445 | udev = file->private_data; |
400 | 446 | ||
401 | /* device attributes can not be changed after the device is created */ | 447 | retval = down_interruptible(&udev->sem); |
402 | switch (cmd) { | 448 | if (retval) |
403 | case UI_SET_EVBIT: | 449 | return retval; |
404 | case UI_SET_KEYBIT: | 450 | |
405 | case UI_SET_RELBIT: | 451 | if (!udev->dev) { |
406 | case UI_SET_ABSBIT: | 452 | retval = uinput_allocate_device(udev); |
407 | case UI_SET_MSCBIT: | 453 | if (retval) |
408 | case UI_SET_LEDBIT: | 454 | goto out; |
409 | case UI_SET_SNDBIT: | ||
410 | case UI_SET_FFBIT: | ||
411 | case UI_SET_PHYS: | ||
412 | if (test_bit(UIST_CREATED, &udev->state)) | ||
413 | return -EINVAL; | ||
414 | } | 455 | } |
415 | 456 | ||
416 | switch (cmd) { | 457 | switch (cmd) { |
@@ -419,74 +460,46 @@ static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd | |||
419 | break; | 460 | break; |
420 | 461 | ||
421 | case UI_DEV_DESTROY: | 462 | case UI_DEV_DESTROY: |
422 | retval = uinput_destroy_device(udev); | 463 | uinput_destroy_device(udev); |
423 | break; | 464 | break; |
424 | 465 | ||
425 | case UI_SET_EVBIT: | 466 | case UI_SET_EVBIT: |
426 | if (arg > EV_MAX) { | 467 | retval = uinput_set_bit(arg, evbit, EV_MAX); |
427 | retval = -EINVAL; | ||
428 | break; | ||
429 | } | ||
430 | set_bit(arg, udev->dev->evbit); | ||
431 | break; | 468 | break; |
432 | 469 | ||
433 | case UI_SET_KEYBIT: | 470 | case UI_SET_KEYBIT: |
434 | if (arg > KEY_MAX) { | 471 | retval = uinput_set_bit(arg, keybit, KEY_MAX); |
435 | retval = -EINVAL; | ||
436 | break; | ||
437 | } | ||
438 | set_bit(arg, udev->dev->keybit); | ||
439 | break; | 472 | break; |
440 | 473 | ||
441 | case UI_SET_RELBIT: | 474 | case UI_SET_RELBIT: |
442 | if (arg > REL_MAX) { | 475 | retval = uinput_set_bit(arg, relbit, REL_MAX); |
443 | retval = -EINVAL; | ||
444 | break; | ||
445 | } | ||
446 | set_bit(arg, udev->dev->relbit); | ||
447 | break; | 476 | break; |
448 | 477 | ||
449 | case UI_SET_ABSBIT: | 478 | case UI_SET_ABSBIT: |
450 | if (arg > ABS_MAX) { | 479 | retval = uinput_set_bit(arg, absbit, ABS_MAX); |
451 | retval = -EINVAL; | ||
452 | break; | ||
453 | } | ||
454 | set_bit(arg, udev->dev->absbit); | ||
455 | break; | 480 | break; |
456 | 481 | ||
457 | case UI_SET_MSCBIT: | 482 | case UI_SET_MSCBIT: |
458 | if (arg > MSC_MAX) { | 483 | retval = uinput_set_bit(arg, mscbit, MSC_MAX); |
459 | retval = -EINVAL; | ||
460 | break; | ||
461 | } | ||
462 | set_bit(arg, udev->dev->mscbit); | ||
463 | break; | 484 | break; |
464 | 485 | ||
465 | case UI_SET_LEDBIT: | 486 | case UI_SET_LEDBIT: |
466 | if (arg > LED_MAX) { | 487 | retval = uinput_set_bit(arg, ledbit, LED_MAX); |
467 | retval = -EINVAL; | ||
468 | break; | ||
469 | } | ||
470 | set_bit(arg, udev->dev->ledbit); | ||
471 | break; | 488 | break; |
472 | 489 | ||
473 | case UI_SET_SNDBIT: | 490 | case UI_SET_SNDBIT: |
474 | if (arg > SND_MAX) { | 491 | retval = uinput_set_bit(arg, sndbit, SND_MAX); |
475 | retval = -EINVAL; | ||
476 | break; | ||
477 | } | ||
478 | set_bit(arg, udev->dev->sndbit); | ||
479 | break; | 492 | break; |
480 | 493 | ||
481 | case UI_SET_FFBIT: | 494 | case UI_SET_FFBIT: |
482 | if (arg > FF_MAX) { | 495 | retval = uinput_set_bit(arg, ffbit, FF_MAX); |
483 | retval = -EINVAL; | ||
484 | break; | ||
485 | } | ||
486 | set_bit(arg, udev->dev->ffbit); | ||
487 | break; | 496 | break; |
488 | 497 | ||
489 | case UI_SET_PHYS: | 498 | case UI_SET_PHYS: |
499 | if (udev->state == UIST_CREATED) { | ||
500 | retval = -EINVAL; | ||
501 | goto out; | ||
502 | } | ||
490 | length = strnlen_user(p, 1024); | 503 | length = strnlen_user(p, 1024); |
491 | if (length <= 0) { | 504 | if (length <= 0) { |
492 | retval = -EFAULT; | 505 | retval = -EFAULT; |
@@ -575,23 +588,26 @@ static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd | |||
575 | default: | 588 | default: |
576 | retval = -EINVAL; | 589 | retval = -EINVAL; |
577 | } | 590 | } |
591 | |||
592 | out: | ||
593 | up(&udev->sem); | ||
578 | return retval; | 594 | return retval; |
579 | } | 595 | } |
580 | 596 | ||
581 | static struct file_operations uinput_fops = { | 597 | static struct file_operations uinput_fops = { |
582 | .owner = THIS_MODULE, | 598 | .owner = THIS_MODULE, |
583 | .open = uinput_open, | 599 | .open = uinput_open, |
584 | .release = uinput_close, | 600 | .release = uinput_release, |
585 | .read = uinput_read, | 601 | .read = uinput_read, |
586 | .write = uinput_write, | 602 | .write = uinput_write, |
587 | .poll = uinput_poll, | 603 | .poll = uinput_poll, |
588 | .ioctl = uinput_ioctl, | 604 | .unlocked_ioctl = uinput_ioctl, |
589 | }; | 605 | }; |
590 | 606 | ||
591 | static struct miscdevice uinput_misc = { | 607 | static struct miscdevice uinput_misc = { |
592 | .fops = &uinput_fops, | 608 | .fops = &uinput_fops, |
593 | .minor = UINPUT_MINOR, | 609 | .minor = UINPUT_MINOR, |
594 | .name = UINPUT_NAME, | 610 | .name = UINPUT_NAME, |
595 | }; | 611 | }; |
596 | 612 | ||
597 | static int __init uinput_init(void) | 613 | static int __init uinput_init(void) |