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 | |
| 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>
| -rw-r--r-- | include/net/bluetooth/hci_core.h | 3 | ||||
| -rw-r--r-- | net/bluetooth/hci_core.c | 97 |
2 files changed, 98 insertions, 2 deletions
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c0fc39620f36..7c78744ec0fd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h | |||
| @@ -109,6 +109,7 @@ struct hci_dev { | |||
| 109 | struct sk_buff_head cmd_q; | 109 | struct sk_buff_head cmd_q; |
| 110 | 110 | ||
| 111 | struct sk_buff *sent_cmd; | 111 | struct sk_buff *sent_cmd; |
| 112 | struct sk_buff *reassembly[3]; | ||
| 112 | 113 | ||
| 113 | struct semaphore req_lock; | 114 | struct semaphore req_lock; |
| 114 | wait_queue_head_t req_wait_q; | 115 | wait_queue_head_t req_wait_q; |
| @@ -437,6 +438,8 @@ static inline int hci_recv_frame(struct sk_buff *skb) | |||
| 437 | return 0; | 438 | return 0; |
| 438 | } | 439 | } |
| 439 | 440 | ||
| 441 | int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count); | ||
| 442 | |||
| 440 | int hci_register_sysfs(struct hci_dev *hdev); | 443 | int hci_register_sysfs(struct hci_dev *hdev); |
| 441 | void hci_unregister_sysfs(struct hci_dev *hdev); | 444 | void hci_unregister_sysfs(struct hci_dev *hdev); |
| 442 | void hci_conn_add_sysfs(struct hci_conn *conn); | 445 | void hci_conn_add_sysfs(struct hci_conn *conn); |
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 | ||
