diff options
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r-- | drivers/gpio/gpiolib.c | 99 |
1 files changed, 86 insertions, 13 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f0fc3a0d37c8..93ed0e00c578 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/uaccess.h> | 21 | #include <linux/uaccess.h> |
22 | #include <linux/compat.h> | 22 | #include <linux/compat.h> |
23 | #include <linux/anon_inodes.h> | 23 | #include <linux/anon_inodes.h> |
24 | #include <linux/file.h> | ||
24 | #include <linux/kfifo.h> | 25 | #include <linux/kfifo.h> |
25 | #include <linux/poll.h> | 26 | #include <linux/poll.h> |
26 | #include <linux/timekeeping.h> | 27 | #include <linux/timekeeping.h> |
@@ -333,6 +334,13 @@ struct linehandle_state { | |||
333 | u32 numdescs; | 334 | u32 numdescs; |
334 | }; | 335 | }; |
335 | 336 | ||
337 | #define GPIOHANDLE_REQUEST_VALID_FLAGS \ | ||
338 | (GPIOHANDLE_REQUEST_INPUT | \ | ||
339 | GPIOHANDLE_REQUEST_OUTPUT | \ | ||
340 | GPIOHANDLE_REQUEST_ACTIVE_LOW | \ | ||
341 | GPIOHANDLE_REQUEST_OPEN_DRAIN | \ | ||
342 | GPIOHANDLE_REQUEST_OPEN_SOURCE) | ||
343 | |||
336 | static long linehandle_ioctl(struct file *filep, unsigned int cmd, | 344 | static long linehandle_ioctl(struct file *filep, unsigned int cmd, |
337 | unsigned long arg) | 345 | unsigned long arg) |
338 | { | 346 | { |
@@ -344,6 +352,8 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, | |||
344 | if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { | 352 | if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { |
345 | int val; | 353 | int val; |
346 | 354 | ||
355 | memset(&ghd, 0, sizeof(ghd)); | ||
356 | |||
347 | /* TODO: check if descriptors are really input */ | 357 | /* TODO: check if descriptors are really input */ |
348 | for (i = 0; i < lh->numdescs; i++) { | 358 | for (i = 0; i < lh->numdescs; i++) { |
349 | val = gpiod_get_value_cansleep(lh->descs[i]); | 359 | val = gpiod_get_value_cansleep(lh->descs[i]); |
@@ -414,6 +424,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) | |||
414 | { | 424 | { |
415 | struct gpiohandle_request handlereq; | 425 | struct gpiohandle_request handlereq; |
416 | struct linehandle_state *lh; | 426 | struct linehandle_state *lh; |
427 | struct file *file; | ||
417 | int fd, i, ret; | 428 | int fd, i, ret; |
418 | 429 | ||
419 | if (copy_from_user(&handlereq, ip, sizeof(handlereq))) | 430 | if (copy_from_user(&handlereq, ip, sizeof(handlereq))) |
@@ -444,6 +455,17 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) | |||
444 | u32 lflags = handlereq.flags; | 455 | u32 lflags = handlereq.flags; |
445 | struct gpio_desc *desc; | 456 | struct gpio_desc *desc; |
446 | 457 | ||
458 | if (offset >= gdev->ngpio) { | ||
459 | ret = -EINVAL; | ||
460 | goto out_free_descs; | ||
461 | } | ||
462 | |||
463 | /* Return an error if a unknown flag is set */ | ||
464 | if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) { | ||
465 | ret = -EINVAL; | ||
466 | goto out_free_descs; | ||
467 | } | ||
468 | |||
447 | desc = &gdev->descs[offset]; | 469 | desc = &gdev->descs[offset]; |
448 | ret = gpiod_request(desc, lh->label); | 470 | ret = gpiod_request(desc, lh->label); |
449 | if (ret) | 471 | if (ret) |
@@ -479,26 +501,41 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) | |||
479 | i--; | 501 | i--; |
480 | lh->numdescs = handlereq.lines; | 502 | lh->numdescs = handlereq.lines; |
481 | 503 | ||
482 | fd = anon_inode_getfd("gpio-linehandle", | 504 | fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); |
483 | &linehandle_fileops, | ||
484 | lh, | ||
485 | O_RDONLY | O_CLOEXEC); | ||
486 | if (fd < 0) { | 505 | if (fd < 0) { |
487 | ret = fd; | 506 | ret = fd; |
488 | goto out_free_descs; | 507 | goto out_free_descs; |
489 | } | 508 | } |
490 | 509 | ||
510 | file = anon_inode_getfile("gpio-linehandle", | ||
511 | &linehandle_fileops, | ||
512 | lh, | ||
513 | O_RDONLY | O_CLOEXEC); | ||
514 | if (IS_ERR(file)) { | ||
515 | ret = PTR_ERR(file); | ||
516 | goto out_put_unused_fd; | ||
517 | } | ||
518 | |||
491 | handlereq.fd = fd; | 519 | handlereq.fd = fd; |
492 | if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { | 520 | if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { |
493 | ret = -EFAULT; | 521 | /* |
494 | goto out_free_descs; | 522 | * fput() will trigger the release() callback, so do not go onto |
523 | * the regular error cleanup path here. | ||
524 | */ | ||
525 | fput(file); | ||
526 | put_unused_fd(fd); | ||
527 | return -EFAULT; | ||
495 | } | 528 | } |
496 | 529 | ||
530 | fd_install(fd, file); | ||
531 | |||
497 | dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", | 532 | dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", |
498 | lh->numdescs); | 533 | lh->numdescs); |
499 | 534 | ||
500 | return 0; | 535 | return 0; |
501 | 536 | ||
537 | out_put_unused_fd: | ||
538 | put_unused_fd(fd); | ||
502 | out_free_descs: | 539 | out_free_descs: |
503 | for (; i >= 0; i--) | 540 | for (; i >= 0; i--) |
504 | gpiod_free(lh->descs[i]); | 541 | gpiod_free(lh->descs[i]); |
@@ -536,6 +573,10 @@ struct lineevent_state { | |||
536 | struct mutex read_lock; | 573 | struct mutex read_lock; |
537 | }; | 574 | }; |
538 | 575 | ||
576 | #define GPIOEVENT_REQUEST_VALID_FLAGS \ | ||
577 | (GPIOEVENT_REQUEST_RISING_EDGE | \ | ||
578 | GPIOEVENT_REQUEST_FALLING_EDGE) | ||
579 | |||
539 | static unsigned int lineevent_poll(struct file *filep, | 580 | static unsigned int lineevent_poll(struct file *filep, |
540 | struct poll_table_struct *wait) | 581 | struct poll_table_struct *wait) |
541 | { | 582 | { |
@@ -623,6 +664,8 @@ static long lineevent_ioctl(struct file *filep, unsigned int cmd, | |||
623 | if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { | 664 | if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { |
624 | int val; | 665 | int val; |
625 | 666 | ||
667 | memset(&ghd, 0, sizeof(ghd)); | ||
668 | |||
626 | val = gpiod_get_value_cansleep(le->desc); | 669 | val = gpiod_get_value_cansleep(le->desc); |
627 | if (val < 0) | 670 | if (val < 0) |
628 | return val; | 671 | return val; |
@@ -695,6 +738,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) | |||
695 | struct gpioevent_request eventreq; | 738 | struct gpioevent_request eventreq; |
696 | struct lineevent_state *le; | 739 | struct lineevent_state *le; |
697 | struct gpio_desc *desc; | 740 | struct gpio_desc *desc; |
741 | struct file *file; | ||
698 | u32 offset; | 742 | u32 offset; |
699 | u32 lflags; | 743 | u32 lflags; |
700 | u32 eflags; | 744 | u32 eflags; |
@@ -726,6 +770,18 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) | |||
726 | lflags = eventreq.handleflags; | 770 | lflags = eventreq.handleflags; |
727 | eflags = eventreq.eventflags; | 771 | eflags = eventreq.eventflags; |
728 | 772 | ||
773 | if (offset >= gdev->ngpio) { | ||
774 | ret = -EINVAL; | ||
775 | goto out_free_label; | ||
776 | } | ||
777 | |||
778 | /* Return an error if a unknown flag is set */ | ||
779 | if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) || | ||
780 | (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) { | ||
781 | ret = -EINVAL; | ||
782 | goto out_free_label; | ||
783 | } | ||
784 | |||
729 | /* This is just wrong: we don't look for events on output lines */ | 785 | /* This is just wrong: we don't look for events on output lines */ |
730 | if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { | 786 | if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { |
731 | ret = -EINVAL; | 787 | ret = -EINVAL; |
@@ -777,23 +833,38 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) | |||
777 | if (ret) | 833 | if (ret) |
778 | goto out_free_desc; | 834 | goto out_free_desc; |
779 | 835 | ||
780 | fd = anon_inode_getfd("gpio-event", | 836 | fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); |
781 | &lineevent_fileops, | ||
782 | le, | ||
783 | O_RDONLY | O_CLOEXEC); | ||
784 | if (fd < 0) { | 837 | if (fd < 0) { |
785 | ret = fd; | 838 | ret = fd; |
786 | goto out_free_irq; | 839 | goto out_free_irq; |
787 | } | 840 | } |
788 | 841 | ||
842 | file = anon_inode_getfile("gpio-event", | ||
843 | &lineevent_fileops, | ||
844 | le, | ||
845 | O_RDONLY | O_CLOEXEC); | ||
846 | if (IS_ERR(file)) { | ||
847 | ret = PTR_ERR(file); | ||
848 | goto out_put_unused_fd; | ||
849 | } | ||
850 | |||
789 | eventreq.fd = fd; | 851 | eventreq.fd = fd; |
790 | if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { | 852 | if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { |
791 | ret = -EFAULT; | 853 | /* |
792 | goto out_free_irq; | 854 | * fput() will trigger the release() callback, so do not go onto |
855 | * the regular error cleanup path here. | ||
856 | */ | ||
857 | fput(file); | ||
858 | put_unused_fd(fd); | ||
859 | return -EFAULT; | ||
793 | } | 860 | } |
794 | 861 | ||
862 | fd_install(fd, file); | ||
863 | |||
795 | return 0; | 864 | return 0; |
796 | 865 | ||
866 | out_put_unused_fd: | ||
867 | put_unused_fd(fd); | ||
797 | out_free_irq: | 868 | out_free_irq: |
798 | free_irq(le->irq, le); | 869 | free_irq(le->irq, le); |
799 | out_free_desc: | 870 | out_free_desc: |
@@ -823,6 +894,8 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
823 | if (cmd == GPIO_GET_CHIPINFO_IOCTL) { | 894 | if (cmd == GPIO_GET_CHIPINFO_IOCTL) { |
824 | struct gpiochip_info chipinfo; | 895 | struct gpiochip_info chipinfo; |
825 | 896 | ||
897 | memset(&chipinfo, 0, sizeof(chipinfo)); | ||
898 | |||
826 | strncpy(chipinfo.name, dev_name(&gdev->dev), | 899 | strncpy(chipinfo.name, dev_name(&gdev->dev), |
827 | sizeof(chipinfo.name)); | 900 | sizeof(chipinfo.name)); |
828 | chipinfo.name[sizeof(chipinfo.name)-1] = '\0'; | 901 | chipinfo.name[sizeof(chipinfo.name)-1] = '\0'; |
@@ -839,7 +912,7 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
839 | 912 | ||
840 | if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) | 913 | if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) |
841 | return -EFAULT; | 914 | return -EFAULT; |
842 | if (lineinfo.line_offset > gdev->ngpio) | 915 | if (lineinfo.line_offset >= gdev->ngpio) |
843 | return -EINVAL; | 916 | return -EINVAL; |
844 | 917 | ||
845 | desc = &gdev->descs[lineinfo.line_offset]; | 918 | desc = &gdev->descs[lineinfo.line_offset]; |