diff options
Diffstat (limited to 'drivers/usb/gadget/f_acm.c')
-rw-r--r-- | drivers/usb/gadget/f_acm.c | 137 |
1 files changed, 73 insertions, 64 deletions
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 1ae180baa597..4b7e33e5d9c6 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c | |||
@@ -715,13 +715,31 @@ fail: | |||
715 | return status; | 715 | return status; |
716 | } | 716 | } |
717 | 717 | ||
718 | static struct f_acm *acm_alloc_basic_func(void) | 718 | static void acm_unbind(struct usb_configuration *c, struct usb_function *f) |
719 | { | ||
720 | struct f_acm *acm = func_to_acm(f); | ||
721 | |||
722 | acm_string_defs[0].id = 0; | ||
723 | usb_free_all_descriptors(f); | ||
724 | if (acm->notify_req) | ||
725 | gs_free_req(acm->notify, acm->notify_req); | ||
726 | } | ||
727 | |||
728 | static void acm_free_func(struct usb_function *f) | ||
729 | { | ||
730 | struct f_acm *acm = func_to_acm(f); | ||
731 | |||
732 | kfree(acm); | ||
733 | } | ||
734 | |||
735 | static struct usb_function *acm_alloc_func(struct usb_function_instance *fi) | ||
719 | { | 736 | { |
720 | struct f_acm *acm; | 737 | struct f_serial_opts *opts; |
738 | struct f_acm *acm; | ||
721 | 739 | ||
722 | acm = kzalloc(sizeof(*acm), GFP_KERNEL); | 740 | acm = kzalloc(sizeof(*acm), GFP_KERNEL); |
723 | if (!acm) | 741 | if (!acm) |
724 | return NULL; | 742 | return ERR_PTR(-ENOMEM); |
725 | 743 | ||
726 | spin_lock_init(&acm->lock); | 744 | spin_lock_init(&acm->lock); |
727 | 745 | ||
@@ -730,109 +748,100 @@ static struct f_acm *acm_alloc_basic_func(void) | |||
730 | acm->port.send_break = acm_send_break; | 748 | acm->port.send_break = acm_send_break; |
731 | 749 | ||
732 | acm->port.func.name = "acm"; | 750 | acm->port.func.name = "acm"; |
751 | acm->port.func.strings = acm_strings; | ||
733 | /* descriptors are per-instance copies */ | 752 | /* descriptors are per-instance copies */ |
734 | acm->port.func.bind = acm_bind; | 753 | acm->port.func.bind = acm_bind; |
735 | acm->port.func.set_alt = acm_set_alt; | 754 | acm->port.func.set_alt = acm_set_alt; |
736 | acm->port.func.setup = acm_setup; | 755 | acm->port.func.setup = acm_setup; |
737 | acm->port.func.disable = acm_disable; | 756 | acm->port.func.disable = acm_disable; |
738 | 757 | ||
739 | return acm; | 758 | opts = container_of(fi, struct f_serial_opts, func_inst); |
759 | acm->port_num = opts->port_num; | ||
760 | acm->port.func.unbind = acm_unbind; | ||
761 | acm->port.func.free_func = acm_free_func; | ||
762 | |||
763 | return &acm->port.func; | ||
740 | } | 764 | } |
741 | 765 | ||
742 | #ifdef USB_FACM_INCLUDED | 766 | static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item) |
743 | static void | ||
744 | acm_old_unbind(struct usb_configuration *c, struct usb_function *f) | ||
745 | { | 767 | { |
746 | struct f_acm *acm = func_to_acm(f); | 768 | return container_of(to_config_group(item), struct f_serial_opts, |
747 | 769 | func_inst.group); | |
748 | usb_free_all_descriptors(f); | ||
749 | if (acm->notify_req) | ||
750 | gs_free_req(acm->notify, acm->notify_req); | ||
751 | kfree(acm); | ||
752 | } | 770 | } |
753 | 771 | ||
754 | /** | 772 | CONFIGFS_ATTR_STRUCT(f_serial_opts); |
755 | * acm_bind_config - add a CDC ACM function to a configuration | 773 | static ssize_t f_acm_attr_show(struct config_item *item, |
756 | * @c: the configuration to support the CDC ACM instance | 774 | struct configfs_attribute *attr, |
757 | * @port_num: /dev/ttyGS* port this interface will use | 775 | char *page) |
758 | * Context: single threaded during gadget setup | ||
759 | * | ||
760 | * Returns zero on success, else negative errno. | ||
761 | * | ||
762 | */ | ||
763 | int acm_bind_config(struct usb_configuration *c, u8 port_num) | ||
764 | { | 776 | { |
765 | struct f_acm *acm; | 777 | struct f_serial_opts *opts = to_f_serial_opts(item); |
766 | int status; | 778 | struct f_serial_opts_attribute *f_serial_opts_attr = |
767 | 779 | container_of(attr, struct f_serial_opts_attribute, attr); | |
768 | /* allocate and initialize one new instance */ | 780 | ssize_t ret = 0; |
769 | acm = acm_alloc_basic_func(); | 781 | |
770 | if (!acm) | 782 | if (f_serial_opts_attr->show) |
771 | return -ENOMEM; | 783 | ret = f_serial_opts_attr->show(opts, page); |
772 | 784 | return ret; | |
773 | acm->port_num = port_num; | ||
774 | acm->port.func.unbind = acm_old_unbind; | ||
775 | |||
776 | status = usb_add_function(c, &acm->port.func); | ||
777 | if (status) | ||
778 | kfree(acm); | ||
779 | return status; | ||
780 | } | 785 | } |
781 | 786 | ||
782 | #else | 787 | static void acm_attr_release(struct config_item *item) |
783 | |||
784 | static void acm_unbind(struct usb_configuration *c, struct usb_function *f) | ||
785 | { | 788 | { |
786 | struct f_acm *acm = func_to_acm(f); | 789 | struct f_serial_opts *opts = to_f_serial_opts(item); |
787 | 790 | ||
788 | acm_string_defs[0].id = 0; | 791 | usb_put_function_instance(&opts->func_inst); |
789 | usb_free_all_descriptors(f); | ||
790 | if (acm->notify_req) | ||
791 | gs_free_req(acm->notify, acm->notify_req); | ||
792 | } | 792 | } |
793 | 793 | ||
794 | static void acm_free_func(struct usb_function *f) | 794 | static struct configfs_item_operations acm_item_ops = { |
795 | { | 795 | .release = acm_attr_release, |
796 | struct f_acm *acm = func_to_acm(f); | 796 | .show_attribute = f_acm_attr_show, |
797 | }; | ||
797 | 798 | ||
798 | kfree(acm); | 799 | static ssize_t f_acm_port_num_show(struct f_serial_opts *opts, char *page) |
800 | { | ||
801 | return sprintf(page, "%u\n", opts->port_num); | ||
799 | } | 802 | } |
800 | 803 | ||
801 | static struct usb_function *acm_alloc_func(struct usb_function_instance *fi) | 804 | static struct f_serial_opts_attribute f_acm_port_num = |
802 | { | 805 | __CONFIGFS_ATTR_RO(port_num, f_acm_port_num_show); |
803 | struct f_serial_opts *opts; | ||
804 | struct f_acm *acm; | ||
805 | 806 | ||
806 | acm = acm_alloc_basic_func(); | ||
807 | if (!acm) | ||
808 | return ERR_PTR(-ENOMEM); | ||
809 | 807 | ||
810 | opts = container_of(fi, struct f_serial_opts, func_inst); | 808 | static struct configfs_attribute *acm_attrs[] = { |
811 | acm->port_num = opts->port_num; | 809 | &f_acm_port_num.attr, |
812 | acm->port.func.unbind = acm_unbind; | 810 | NULL, |
813 | acm->port.func.free_func = acm_free_func; | 811 | }; |
814 | 812 | ||
815 | return &acm->port.func; | 813 | static struct config_item_type acm_func_type = { |
816 | } | 814 | .ct_item_ops = &acm_item_ops, |
815 | .ct_attrs = acm_attrs, | ||
816 | .ct_owner = THIS_MODULE, | ||
817 | }; | ||
817 | 818 | ||
818 | static void acm_free_instance(struct usb_function_instance *fi) | 819 | static void acm_free_instance(struct usb_function_instance *fi) |
819 | { | 820 | { |
820 | struct f_serial_opts *opts; | 821 | struct f_serial_opts *opts; |
821 | 822 | ||
822 | opts = container_of(fi, struct f_serial_opts, func_inst); | 823 | opts = container_of(fi, struct f_serial_opts, func_inst); |
824 | gserial_free_line(opts->port_num); | ||
823 | kfree(opts); | 825 | kfree(opts); |
824 | } | 826 | } |
825 | 827 | ||
826 | static struct usb_function_instance *acm_alloc_instance(void) | 828 | static struct usb_function_instance *acm_alloc_instance(void) |
827 | { | 829 | { |
828 | struct f_serial_opts *opts; | 830 | struct f_serial_opts *opts; |
831 | int ret; | ||
829 | 832 | ||
830 | opts = kzalloc(sizeof(*opts), GFP_KERNEL); | 833 | opts = kzalloc(sizeof(*opts), GFP_KERNEL); |
831 | if (!opts) | 834 | if (!opts) |
832 | return ERR_PTR(-ENOMEM); | 835 | return ERR_PTR(-ENOMEM); |
833 | opts->func_inst.free_func_inst = acm_free_instance; | 836 | opts->func_inst.free_func_inst = acm_free_instance; |
837 | ret = gserial_alloc_line(&opts->port_num); | ||
838 | if (ret) { | ||
839 | kfree(opts); | ||
840 | return ERR_PTR(ret); | ||
841 | } | ||
842 | config_group_init_type_name(&opts->func_inst.group, "", | ||
843 | &acm_func_type); | ||
834 | return &opts->func_inst; | 844 | return &opts->func_inst; |
835 | } | 845 | } |
836 | DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func); | 846 | DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func); |
837 | MODULE_LICENSE("GPL"); | 847 | MODULE_LICENSE("GPL"); |
838 | #endif | ||