diff options
Diffstat (limited to 'net/bluetooth')
-rw-r--r-- | net/bluetooth/hidp/core.c | 191 | ||||
-rw-r--r-- | net/bluetooth/hidp/hidp.h | 2 |
2 files changed, 111 insertions, 82 deletions
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 56a51f91591a..d8029cfcd452 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c | |||
@@ -623,9 +623,15 @@ static struct device *hidp_get_device(struct hidp_session *session) | |||
623 | static int hidp_setup_input(struct hidp_session *session, | 623 | static int hidp_setup_input(struct hidp_session *session, |
624 | struct hidp_connadd_req *req) | 624 | struct hidp_connadd_req *req) |
625 | { | 625 | { |
626 | struct input_dev *input = session->input; | 626 | struct input_dev *input; |
627 | int i; | 627 | int i; |
628 | 628 | ||
629 | input = input_allocate_device(); | ||
630 | if (!input) | ||
631 | return -ENOMEM; | ||
632 | |||
633 | session->input = input; | ||
634 | |||
629 | input_set_drvdata(input, session); | 635 | input_set_drvdata(input, session); |
630 | 636 | ||
631 | input->name = "Bluetooth HID Boot Protocol Device"; | 637 | input->name = "Bluetooth HID Boot Protocol Device"; |
@@ -698,55 +704,117 @@ static void hidp_setup_quirks(struct hid_device *hid) | |||
698 | hid->quirks = hidp_blacklist[n].quirks; | 704 | hid->quirks = hidp_blacklist[n].quirks; |
699 | } | 705 | } |
700 | 706 | ||
707 | static int hidp_parse(struct hid_device *hid) | ||
708 | { | ||
709 | struct hidp_session *session = hid->driver_data; | ||
710 | struct hidp_connadd_req *req = session->req; | ||
711 | unsigned char *buf; | ||
712 | int ret; | ||
713 | |||
714 | buf = kmalloc(req->rd_size, GFP_KERNEL); | ||
715 | if (!buf) | ||
716 | return -ENOMEM; | ||
717 | |||
718 | if (copy_from_user(buf, req->rd_data, req->rd_size)) { | ||
719 | kfree(buf); | ||
720 | return -EFAULT; | ||
721 | } | ||
722 | |||
723 | ret = hid_parse_report(session->hid, buf, req->rd_size); | ||
724 | |||
725 | kfree(buf); | ||
726 | |||
727 | if (ret) | ||
728 | return ret; | ||
729 | |||
730 | session->req = NULL; | ||
731 | |||
732 | hidp_setup_quirks(hid); | ||
733 | return 0; | ||
734 | } | ||
735 | |||
736 | static int hidp_start(struct hid_device *hid) | ||
737 | { | ||
738 | struct hidp_session *session = hid->driver_data; | ||
739 | struct hid_report *report; | ||
740 | |||
741 | list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT]. | ||
742 | report_list, list) | ||
743 | hidp_send_report(session, report); | ||
744 | |||
745 | list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT]. | ||
746 | report_list, list) | ||
747 | hidp_send_report(session, report); | ||
748 | |||
749 | if (hidinput_connect(hid) == 0) | ||
750 | hid->claimed |= HID_CLAIMED_INPUT; | ||
751 | |||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | static void hidp_stop(struct hid_device *hid) | ||
756 | { | ||
757 | struct hidp_session *session = hid->driver_data; | ||
758 | |||
759 | skb_queue_purge(&session->ctrl_transmit); | ||
760 | skb_queue_purge(&session->intr_transmit); | ||
761 | |||
762 | if (hid->claimed & HID_CLAIMED_INPUT) | ||
763 | hidinput_disconnect(hid); | ||
764 | hid->claimed = 0; | ||
765 | } | ||
766 | |||
767 | static struct hid_ll_driver hidp_hid_driver = { | ||
768 | .parse = hidp_parse, | ||
769 | .start = hidp_start, | ||
770 | .stop = hidp_stop, | ||
771 | .open = hidp_open, | ||
772 | .close = hidp_close, | ||
773 | .hidinput_input_event = hidp_hidinput_event, | ||
774 | }; | ||
775 | |||
701 | static int hidp_setup_hid(struct hidp_session *session, | 776 | static int hidp_setup_hid(struct hidp_session *session, |
702 | struct hidp_connadd_req *req) | 777 | struct hidp_connadd_req *req) |
703 | { | 778 | { |
704 | struct hid_device *hid = session->hid; | 779 | struct hid_device *hid; |
705 | struct hid_report *report; | ||
706 | bdaddr_t src, dst; | 780 | bdaddr_t src, dst; |
707 | int ret; | 781 | int ret; |
708 | 782 | ||
709 | baswap(&src, &bt_sk(session->ctrl_sock->sk)->src); | 783 | hid = hid_allocate_device(); |
710 | baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst); | 784 | if (IS_ERR(hid)) { |
785 | ret = PTR_ERR(session->hid); | ||
786 | goto err; | ||
787 | } | ||
711 | 788 | ||
789 | session->hid = hid; | ||
790 | session->req = req; | ||
712 | hid->driver_data = session; | 791 | hid->driver_data = session; |
713 | 792 | ||
714 | hid->country = req->country; | 793 | baswap(&src, &bt_sk(session->ctrl_sock->sk)->src); |
794 | baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst); | ||
715 | 795 | ||
716 | hid->bus = BUS_BLUETOOTH; | 796 | hid->bus = BUS_BLUETOOTH; |
717 | hid->vendor = req->vendor; | 797 | hid->vendor = req->vendor; |
718 | hid->product = req->product; | 798 | hid->product = req->product; |
719 | hid->version = req->version; | 799 | hid->version = req->version; |
800 | hid->country = req->country; | ||
720 | 801 | ||
721 | strncpy(hid->name, req->name, 128); | 802 | strncpy(hid->name, req->name, 128); |
722 | strncpy(hid->phys, batostr(&src), 64); | 803 | strncpy(hid->phys, batostr(&src), 64); |
723 | strncpy(hid->uniq, batostr(&dst), 64); | 804 | strncpy(hid->uniq, batostr(&dst), 64); |
724 | 805 | ||
725 | hid->dev.parent = hidp_get_device(session); | 806 | hid->dev.parent = hidp_get_device(session); |
726 | 807 | hid->ll_driver = &hidp_hid_driver; | |
727 | hid->hid_open = hidp_open; | ||
728 | hid->hid_close = hidp_close; | ||
729 | |||
730 | hid->hidinput_input_event = hidp_hidinput_event; | ||
731 | |||
732 | hidp_setup_quirks(hid); | ||
733 | |||
734 | list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) | ||
735 | hidp_send_report(session, report); | ||
736 | |||
737 | list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) | ||
738 | hidp_send_report(session, report); | ||
739 | |||
740 | if (hidinput_connect(hid) == 0) | ||
741 | hid->claimed |= HID_CLAIMED_INPUT; | ||
742 | 808 | ||
743 | ret = hid_add_device(hid); | 809 | ret = hid_add_device(hid); |
744 | if (ret) { | 810 | if (ret) |
745 | if (hid->claimed & HID_CLAIMED_INPUT) | 811 | goto err_hid; |
746 | hidinput_disconnect(hid); | ||
747 | skb_queue_purge(&session->intr_transmit); | ||
748 | } | ||
749 | 812 | ||
813 | return 0; | ||
814 | err_hid: | ||
815 | hid_destroy_device(hid); | ||
816 | session->hid = NULL; | ||
817 | err: | ||
750 | return ret; | 818 | return ret; |
751 | } | 819 | } |
752 | 820 | ||
@@ -767,46 +835,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, | |||
767 | 835 | ||
768 | BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); | 836 | BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); |
769 | 837 | ||
770 | if (req->rd_size > 0) { | ||
771 | unsigned char *buf = kmalloc(req->rd_size, GFP_KERNEL); | ||
772 | |||
773 | if (!buf) { | ||
774 | kfree(session); | ||
775 | return -ENOMEM; | ||
776 | } | ||
777 | |||
778 | if (copy_from_user(buf, req->rd_data, req->rd_size)) { | ||
779 | kfree(buf); | ||
780 | kfree(session); | ||
781 | return -EFAULT; | ||
782 | } | ||
783 | |||
784 | session->hid = hid_allocate_device(); | ||
785 | if (IS_ERR(session->hid)) { | ||
786 | kfree(buf); | ||
787 | kfree(session); | ||
788 | return PTR_ERR(session->hid); | ||
789 | } | ||
790 | |||
791 | err = hid_parse_report(session->hid, buf, req->rd_size); | ||
792 | |||
793 | kfree(buf); | ||
794 | |||
795 | if (err) { | ||
796 | hid_destroy_device(session->hid); | ||
797 | kfree(session); | ||
798 | return -EINVAL; | ||
799 | } | ||
800 | } | ||
801 | |||
802 | if (!session->hid) { | ||
803 | session->input = input_allocate_device(); | ||
804 | if (!session->input) { | ||
805 | kfree(session); | ||
806 | return -ENOMEM; | ||
807 | } | ||
808 | } | ||
809 | |||
810 | down_write(&hidp_session_sem); | 838 | down_write(&hidp_session_sem); |
811 | 839 | ||
812 | s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); | 840 | s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); |
@@ -834,16 +862,16 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, | |||
834 | session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); | 862 | session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); |
835 | session->idle_to = req->idle_to; | 863 | session->idle_to = req->idle_to; |
836 | 864 | ||
837 | if (session->input) { | 865 | if (req->rd_size > 0) { |
838 | err = hidp_setup_input(session, req); | ||
839 | if (err < 0) | ||
840 | goto failed; | ||
841 | } | ||
842 | |||
843 | if (session->hid) { | ||
844 | err = hidp_setup_hid(session, req); | 866 | err = hidp_setup_hid(session, req); |
845 | if (err) | 867 | if (err) |
846 | goto failed; | 868 | goto err_skb; |
869 | } | ||
870 | |||
871 | if (!session->hid) { | ||
872 | err = hidp_setup_input(session, req); | ||
873 | if (err < 0) | ||
874 | goto err_skb; | ||
847 | } | 875 | } |
848 | 876 | ||
849 | __hidp_link_session(session); | 877 | __hidp_link_session(session); |
@@ -871,16 +899,15 @@ unlink: | |||
871 | 899 | ||
872 | __hidp_unlink_session(session); | 900 | __hidp_unlink_session(session); |
873 | 901 | ||
874 | if (session->input) { | 902 | if (session->input) |
875 | input_unregister_device(session->input); | 903 | input_unregister_device(session->input); |
876 | session->input = NULL; /* don't try to free it here */ | ||
877 | } | ||
878 | |||
879 | failed: | ||
880 | up_write(&hidp_session_sem); | ||
881 | |||
882 | if (session->hid) | 904 | if (session->hid) |
883 | hid_destroy_device(session->hid); | 905 | hid_destroy_device(session->hid); |
906 | err_skb: | ||
907 | skb_queue_purge(&session->ctrl_transmit); | ||
908 | skb_queue_purge(&session->intr_transmit); | ||
909 | failed: | ||
910 | up_write(&hidp_session_sem); | ||
884 | 911 | ||
885 | input_free_device(session->input); | 912 | input_free_device(session->input); |
886 | kfree(session); | 913 | kfree(session); |
diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index 343fb0566b3e..e503c89057ad 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h | |||
@@ -151,6 +151,8 @@ struct hidp_session { | |||
151 | 151 | ||
152 | struct sk_buff_head ctrl_transmit; | 152 | struct sk_buff_head ctrl_transmit; |
153 | struct sk_buff_head intr_transmit; | 153 | struct sk_buff_head intr_transmit; |
154 | |||
155 | struct hidp_connadd_req *req; | ||
154 | }; | 156 | }; |
155 | 157 | ||
156 | static inline void hidp_schedule(struct hidp_session *session) | 158 | static inline void hidp_schedule(struct hidp_session *session) |