diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/bluetooth/hidp/core.c | 214 | ||||
-rw-r--r-- | net/bluetooth/hidp/hidp.h | 2 |
2 files changed, 133 insertions, 83 deletions
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 96434d774c84..acdeab3d9807 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c | |||
@@ -578,7 +578,7 @@ static int hidp_session(void *arg) | |||
578 | if (session->hid) { | 578 | if (session->hid) { |
579 | if (session->hid->claimed & HID_CLAIMED_INPUT) | 579 | if (session->hid->claimed & HID_CLAIMED_INPUT) |
580 | hidinput_disconnect(session->hid); | 580 | hidinput_disconnect(session->hid); |
581 | hid_free_device(session->hid); | 581 | hid_destroy_device(session->hid); |
582 | } | 582 | } |
583 | 583 | ||
584 | /* Wakeup user-space polling for socket errors */ | 584 | /* Wakeup user-space polling for socket errors */ |
@@ -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"; |
@@ -677,67 +683,114 @@ static void hidp_close(struct hid_device *hid) | |||
677 | { | 683 | { |
678 | } | 684 | } |
679 | 685 | ||
680 | static const struct { | 686 | static int hidp_parse(struct hid_device *hid) |
681 | __u16 idVendor; | 687 | { |
682 | __u16 idProduct; | 688 | struct hidp_session *session = hid->driver_data; |
683 | unsigned quirks; | 689 | struct hidp_connadd_req *req = session->req; |
684 | } hidp_blacklist[] = { | 690 | unsigned char *buf; |
685 | /* Apple wireless Mighty Mouse */ | 691 | int ret; |
686 | { 0x05ac, 0x030c, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, | ||
687 | 692 | ||
688 | { } /* Terminating entry */ | 693 | buf = kmalloc(req->rd_size, GFP_KERNEL); |
689 | }; | 694 | if (!buf) |
695 | return -ENOMEM; | ||
696 | |||
697 | if (copy_from_user(buf, req->rd_data, req->rd_size)) { | ||
698 | kfree(buf); | ||
699 | return -EFAULT; | ||
700 | } | ||
701 | |||
702 | ret = hid_parse_report(session->hid, buf, req->rd_size); | ||
703 | |||
704 | kfree(buf); | ||
705 | |||
706 | if (ret) | ||
707 | return ret; | ||
708 | |||
709 | session->req = NULL; | ||
710 | |||
711 | return 0; | ||
712 | } | ||
713 | |||
714 | static int hidp_start(struct hid_device *hid) | ||
715 | { | ||
716 | struct hidp_session *session = hid->driver_data; | ||
717 | struct hid_report *report; | ||
690 | 718 | ||
691 | static void hidp_setup_quirks(struct hid_device *hid) | 719 | list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT]. |
720 | report_list, list) | ||
721 | hidp_send_report(session, report); | ||
722 | |||
723 | list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT]. | ||
724 | report_list, list) | ||
725 | hidp_send_report(session, report); | ||
726 | |||
727 | return 0; | ||
728 | } | ||
729 | |||
730 | static void hidp_stop(struct hid_device *hid) | ||
692 | { | 731 | { |
693 | unsigned int n; | 732 | struct hidp_session *session = hid->driver_data; |
733 | |||
734 | skb_queue_purge(&session->ctrl_transmit); | ||
735 | skb_queue_purge(&session->intr_transmit); | ||
694 | 736 | ||
695 | for (n = 0; hidp_blacklist[n].idVendor; n++) | 737 | if (hid->claimed & HID_CLAIMED_INPUT) |
696 | if (hidp_blacklist[n].idVendor == le16_to_cpu(hid->vendor) && | 738 | hidinput_disconnect(hid); |
697 | hidp_blacklist[n].idProduct == le16_to_cpu(hid->product)) | 739 | hid->claimed = 0; |
698 | hid->quirks = hidp_blacklist[n].quirks; | ||
699 | } | 740 | } |
700 | 741 | ||
701 | static void hidp_setup_hid(struct hidp_session *session, | 742 | static struct hid_ll_driver hidp_hid_driver = { |
743 | .parse = hidp_parse, | ||
744 | .start = hidp_start, | ||
745 | .stop = hidp_stop, | ||
746 | .open = hidp_open, | ||
747 | .close = hidp_close, | ||
748 | .hidinput_input_event = hidp_hidinput_event, | ||
749 | }; | ||
750 | |||
751 | static int hidp_setup_hid(struct hidp_session *session, | ||
702 | struct hidp_connadd_req *req) | 752 | struct hidp_connadd_req *req) |
703 | { | 753 | { |
704 | struct hid_device *hid = session->hid; | 754 | struct hid_device *hid; |
705 | struct hid_report *report; | ||
706 | bdaddr_t src, dst; | 755 | bdaddr_t src, dst; |
756 | int ret; | ||
707 | 757 | ||
708 | baswap(&src, &bt_sk(session->ctrl_sock->sk)->src); | 758 | hid = hid_allocate_device(); |
709 | baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst); | 759 | if (IS_ERR(hid)) { |
760 | ret = PTR_ERR(session->hid); | ||
761 | goto err; | ||
762 | } | ||
710 | 763 | ||
764 | session->hid = hid; | ||
765 | session->req = req; | ||
711 | hid->driver_data = session; | 766 | hid->driver_data = session; |
712 | 767 | ||
713 | hid->country = req->country; | 768 | baswap(&src, &bt_sk(session->ctrl_sock->sk)->src); |
769 | baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst); | ||
714 | 770 | ||
715 | hid->bus = BUS_BLUETOOTH; | 771 | hid->bus = BUS_BLUETOOTH; |
716 | hid->vendor = req->vendor; | 772 | hid->vendor = req->vendor; |
717 | hid->product = req->product; | 773 | hid->product = req->product; |
718 | hid->version = req->version; | 774 | hid->version = req->version; |
775 | hid->country = req->country; | ||
719 | 776 | ||
720 | strncpy(hid->name, req->name, 128); | 777 | strncpy(hid->name, req->name, 128); |
721 | strncpy(hid->phys, batostr(&src), 64); | 778 | strncpy(hid->phys, batostr(&src), 64); |
722 | strncpy(hid->uniq, batostr(&dst), 64); | 779 | strncpy(hid->uniq, batostr(&dst), 64); |
723 | 780 | ||
724 | hid->dev = hidp_get_device(session); | 781 | hid->dev.parent = hidp_get_device(session); |
725 | 782 | hid->ll_driver = &hidp_hid_driver; | |
726 | hid->hid_open = hidp_open; | ||
727 | hid->hid_close = hidp_close; | ||
728 | |||
729 | hid->hidinput_input_event = hidp_hidinput_event; | ||
730 | 783 | ||
731 | hidp_setup_quirks(hid); | 784 | ret = hid_add_device(hid); |
785 | if (ret) | ||
786 | goto err_hid; | ||
732 | 787 | ||
733 | list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) | 788 | return 0; |
734 | hidp_send_report(session, report); | 789 | err_hid: |
735 | 790 | hid_destroy_device(hid); | |
736 | list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) | 791 | session->hid = NULL; |
737 | hidp_send_report(session, report); | 792 | err: |
738 | 793 | return ret; | |
739 | if (hidinput_connect(hid) == 0) | ||
740 | hid->claimed |= HID_CLAIMED_INPUT; | ||
741 | } | 794 | } |
742 | 795 | ||
743 | int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock) | 796 | int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock) |
@@ -757,38 +810,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, | |||
757 | 810 | ||
758 | BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); | 811 | BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); |
759 | 812 | ||
760 | if (req->rd_size > 0) { | ||
761 | unsigned char *buf = kmalloc(req->rd_size, GFP_KERNEL); | ||
762 | |||
763 | if (!buf) { | ||
764 | kfree(session); | ||
765 | return -ENOMEM; | ||
766 | } | ||
767 | |||
768 | if (copy_from_user(buf, req->rd_data, req->rd_size)) { | ||
769 | kfree(buf); | ||
770 | kfree(session); | ||
771 | return -EFAULT; | ||
772 | } | ||
773 | |||
774 | session->hid = hid_parse_report(buf, req->rd_size); | ||
775 | |||
776 | kfree(buf); | ||
777 | |||
778 | if (!session->hid) { | ||
779 | kfree(session); | ||
780 | return -EINVAL; | ||
781 | } | ||
782 | } | ||
783 | |||
784 | if (!session->hid) { | ||
785 | session->input = input_allocate_device(); | ||
786 | if (!session->input) { | ||
787 | kfree(session); | ||
788 | return -ENOMEM; | ||
789 | } | ||
790 | } | ||
791 | |||
792 | down_write(&hidp_session_sem); | 813 | down_write(&hidp_session_sem); |
793 | 814 | ||
794 | s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); | 815 | s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); |
@@ -816,15 +837,18 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, | |||
816 | session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); | 837 | session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); |
817 | session->idle_to = req->idle_to; | 838 | session->idle_to = req->idle_to; |
818 | 839 | ||
819 | if (session->input) { | 840 | if (req->rd_size > 0) { |
841 | err = hidp_setup_hid(session, req); | ||
842 | if (err && err != -ENODEV) | ||
843 | goto err_skb; | ||
844 | } | ||
845 | |||
846 | if (!session->hid) { | ||
820 | err = hidp_setup_input(session, req); | 847 | err = hidp_setup_input(session, req); |
821 | if (err < 0) | 848 | if (err < 0) |
822 | goto failed; | 849 | goto err_skb; |
823 | } | 850 | } |
824 | 851 | ||
825 | if (session->hid) | ||
826 | hidp_setup_hid(session, req); | ||
827 | |||
828 | __hidp_link_session(session); | 852 | __hidp_link_session(session); |
829 | 853 | ||
830 | hidp_set_timer(session); | 854 | hidp_set_timer(session); |
@@ -850,17 +874,16 @@ unlink: | |||
850 | 874 | ||
851 | __hidp_unlink_session(session); | 875 | __hidp_unlink_session(session); |
852 | 876 | ||
853 | if (session->input) { | 877 | if (session->input) |
854 | input_unregister_device(session->input); | 878 | input_unregister_device(session->input); |
855 | session->input = NULL; /* don't try to free it here */ | 879 | if (session->hid) |
856 | } | 880 | hid_destroy_device(session->hid); |
857 | 881 | err_skb: | |
882 | skb_queue_purge(&session->ctrl_transmit); | ||
883 | skb_queue_purge(&session->intr_transmit); | ||
858 | failed: | 884 | failed: |
859 | up_write(&hidp_session_sem); | 885 | up_write(&hidp_session_sem); |
860 | 886 | ||
861 | if (session->hid) | ||
862 | hid_free_device(session->hid); | ||
863 | |||
864 | input_free_device(session->input); | 887 | input_free_device(session->input); |
865 | kfree(session); | 888 | kfree(session); |
866 | return err; | 889 | return err; |
@@ -950,18 +973,43 @@ int hidp_get_conninfo(struct hidp_conninfo *ci) | |||
950 | return err; | 973 | return err; |
951 | } | 974 | } |
952 | 975 | ||
976 | static const struct hid_device_id hidp_table[] = { | ||
977 | { HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) }, | ||
978 | { } | ||
979 | }; | ||
980 | |||
981 | static struct hid_driver hidp_driver = { | ||
982 | .name = "generic-bluetooth", | ||
983 | .id_table = hidp_table, | ||
984 | }; | ||
985 | |||
953 | static int __init hidp_init(void) | 986 | static int __init hidp_init(void) |
954 | { | 987 | { |
988 | int ret; | ||
989 | |||
955 | l2cap_load(); | 990 | l2cap_load(); |
956 | 991 | ||
957 | BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION); | 992 | BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION); |
958 | 993 | ||
959 | return hidp_init_sockets(); | 994 | ret = hid_register_driver(&hidp_driver); |
995 | if (ret) | ||
996 | goto err; | ||
997 | |||
998 | ret = hidp_init_sockets(); | ||
999 | if (ret) | ||
1000 | goto err_drv; | ||
1001 | |||
1002 | return 0; | ||
1003 | err_drv: | ||
1004 | hid_unregister_driver(&hidp_driver); | ||
1005 | err: | ||
1006 | return ret; | ||
960 | } | 1007 | } |
961 | 1008 | ||
962 | static void __exit hidp_exit(void) | 1009 | static void __exit hidp_exit(void) |
963 | { | 1010 | { |
964 | hidp_cleanup_sockets(); | 1011 | hidp_cleanup_sockets(); |
1012 | hid_unregister_driver(&hidp_driver); | ||
965 | } | 1013 | } |
966 | 1014 | ||
967 | module_init(hidp_init); | 1015 | module_init(hidp_init); |
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) |