diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2007-07-11 00:42:04 -0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2007-07-11 00:42:04 -0400 |
commit | ef222013fc8c1a2fcba5c7ab169be8ffcb778ec4 (patch) | |
tree | 706fb330afd783755eee511bd9182e507c88c5e6 /net | |
parent | 7dcca30a32aadb0520417521b0c44f42d09fe05c (diff) |
[Bluetooth] Add hci_recv_fragment() helper function
Most drivers must handle fragmented HCI data packets and events. This
patch adds a generic function for their reassembly to the Bluetooth
core layer and thus allows to shrink the complexity of the drivers.
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/bluetooth/hci_core.c | 97 |
1 files changed, 95 insertions, 2 deletions
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index aa4b56a8c3ea..9c71cffbc6b0 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c | |||
@@ -826,7 +826,7 @@ EXPORT_SYMBOL(hci_free_dev); | |||
826 | int hci_register_dev(struct hci_dev *hdev) | 826 | int hci_register_dev(struct hci_dev *hdev) |
827 | { | 827 | { |
828 | struct list_head *head = &hci_dev_list, *p; | 828 | struct list_head *head = &hci_dev_list, *p; |
829 | int id = 0; | 829 | int i, id = 0; |
830 | 830 | ||
831 | BT_DBG("%p name %s type %d owner %p", hdev, hdev->name, hdev->type, hdev->owner); | 831 | BT_DBG("%p name %s type %d owner %p", hdev, hdev->name, hdev->type, hdev->owner); |
832 | 832 | ||
@@ -865,6 +865,9 @@ int hci_register_dev(struct hci_dev *hdev) | |||
865 | skb_queue_head_init(&hdev->cmd_q); | 865 | skb_queue_head_init(&hdev->cmd_q); |
866 | skb_queue_head_init(&hdev->raw_q); | 866 | skb_queue_head_init(&hdev->raw_q); |
867 | 867 | ||
868 | for (i = 0; i < 3; i++) | ||
869 | hdev->reassembly[i] = NULL; | ||
870 | |||
868 | init_waitqueue_head(&hdev->req_wait_q); | 871 | init_waitqueue_head(&hdev->req_wait_q); |
869 | init_MUTEX(&hdev->req_lock); | 872 | init_MUTEX(&hdev->req_lock); |
870 | 873 | ||
@@ -889,6 +892,8 @@ EXPORT_SYMBOL(hci_register_dev); | |||
889 | /* Unregister HCI device */ | 892 | /* Unregister HCI device */ |
890 | int hci_unregister_dev(struct hci_dev *hdev) | 893 | int hci_unregister_dev(struct hci_dev *hdev) |
891 | { | 894 | { |
895 | int i; | ||
896 | |||
892 | BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type); | 897 | BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type); |
893 | 898 | ||
894 | hci_unregister_sysfs(hdev); | 899 | hci_unregister_sysfs(hdev); |
@@ -899,9 +904,13 @@ int hci_unregister_dev(struct hci_dev *hdev) | |||
899 | 904 | ||
900 | hci_dev_do_close(hdev); | 905 | hci_dev_do_close(hdev); |
901 | 906 | ||
907 | for (i = 0; i < 3; i++) | ||
908 | kfree_skb(hdev->reassembly[i]); | ||
909 | |||
902 | hci_notify(hdev, HCI_DEV_UNREG); | 910 | hci_notify(hdev, HCI_DEV_UNREG); |
903 | 911 | ||
904 | __hci_dev_put(hdev); | 912 | __hci_dev_put(hdev); |
913 | |||
905 | return 0; | 914 | return 0; |
906 | } | 915 | } |
907 | EXPORT_SYMBOL(hci_unregister_dev); | 916 | EXPORT_SYMBOL(hci_unregister_dev); |
@@ -922,6 +931,90 @@ int hci_resume_dev(struct hci_dev *hdev) | |||
922 | } | 931 | } |
923 | EXPORT_SYMBOL(hci_resume_dev); | 932 | EXPORT_SYMBOL(hci_resume_dev); |
924 | 933 | ||
934 | /* Receive packet type fragment */ | ||
935 | #define __reassembly(hdev, type) ((hdev)->reassembly[(type) - 2]) | ||
936 | |||
937 | int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count) | ||
938 | { | ||
939 | if (type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) | ||
940 | return -EILSEQ; | ||
941 | |||
942 | while (count) { | ||
943 | struct sk_buff *skb = __reassembly(hdev, type); | ||
944 | struct { int expect; } *scb; | ||
945 | int len = 0; | ||
946 | |||
947 | if (!skb) { | ||
948 | /* Start of the frame */ | ||
949 | |||
950 | switch (type) { | ||
951 | case HCI_EVENT_PKT: | ||
952 | if (count >= HCI_EVENT_HDR_SIZE) { | ||
953 | struct hci_event_hdr *h = data; | ||
954 | len = HCI_EVENT_HDR_SIZE + h->plen; | ||
955 | } else | ||
956 | return -EILSEQ; | ||
957 | break; | ||
958 | |||
959 | case HCI_ACLDATA_PKT: | ||
960 | if (count >= HCI_ACL_HDR_SIZE) { | ||
961 | struct hci_acl_hdr *h = data; | ||
962 | len = HCI_ACL_HDR_SIZE + __le16_to_cpu(h->dlen); | ||
963 | } else | ||
964 | return -EILSEQ; | ||
965 | break; | ||
966 | |||
967 | case HCI_SCODATA_PKT: | ||
968 | if (count >= HCI_SCO_HDR_SIZE) { | ||
969 | struct hci_sco_hdr *h = data; | ||
970 | len = HCI_SCO_HDR_SIZE + h->dlen; | ||
971 | } else | ||
972 | return -EILSEQ; | ||
973 | break; | ||
974 | } | ||
975 | |||
976 | skb = bt_skb_alloc(len, GFP_ATOMIC); | ||
977 | if (!skb) { | ||
978 | BT_ERR("%s no memory for packet", hdev->name); | ||
979 | return -ENOMEM; | ||
980 | } | ||
981 | |||
982 | skb->dev = (void *) hdev; | ||
983 | bt_cb(skb)->pkt_type = type; | ||
984 | |||
985 | __reassembly(hdev, type) = skb; | ||
986 | |||
987 | scb = (void *) skb->cb; | ||
988 | scb->expect = len; | ||
989 | } else { | ||
990 | /* Continuation */ | ||
991 | |||
992 | scb = (void *) skb->cb; | ||
993 | len = scb->expect; | ||
994 | } | ||
995 | |||
996 | len = min(len, count); | ||
997 | |||
998 | memcpy(skb_put(skb, len), data, len); | ||
999 | |||
1000 | scb->expect -= len; | ||
1001 | |||
1002 | if (scb->expect == 0) { | ||
1003 | /* Complete frame */ | ||
1004 | |||
1005 | __reassembly(hdev, type) = NULL; | ||
1006 | |||
1007 | bt_cb(skb)->pkt_type = type; | ||
1008 | hci_recv_frame(skb); | ||
1009 | } | ||
1010 | |||
1011 | count -= len; data += len; | ||
1012 | } | ||
1013 | |||
1014 | return 0; | ||
1015 | } | ||
1016 | EXPORT_SYMBOL(hci_recv_fragment); | ||
1017 | |||
925 | /* ---- Interface to upper protocols ---- */ | 1018 | /* ---- Interface to upper protocols ---- */ |
926 | 1019 | ||
927 | /* Register/Unregister protocols. | 1020 | /* Register/Unregister protocols. |
@@ -1029,7 +1122,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *p | |||
1029 | 1122 | ||
1030 | skb = bt_skb_alloc(len, GFP_ATOMIC); | 1123 | skb = bt_skb_alloc(len, GFP_ATOMIC); |
1031 | if (!skb) { | 1124 | if (!skb) { |
1032 | BT_ERR("%s Can't allocate memory for HCI command", hdev->name); | 1125 | BT_ERR("%s no memory for command", hdev->name); |
1033 | return -ENOMEM; | 1126 | return -ENOMEM; |
1034 | } | 1127 | } |
1035 | 1128 | ||