diff options
| author | Suraj Sumangala <suraj@atheros.com> | 2010-07-14 03:32:17 -0400 |
|---|---|---|
| committer | Marcel Holtmann <marcel@holtmann.org> | 2010-07-21 13:39:12 -0400 |
| commit | 33e882a5f2301a23a85ef2994e30fd9f48d39d9b (patch) | |
| tree | 4048a954d11698581e123b25389a9f8f72125f90 /net/bluetooth | |
| parent | cd4c53919ed50b0d532f106aeb76e79077bece98 (diff) | |
Bluetooth: Implement hci_reassembly helper to reassemble RX packets
Implements feature to reassemble received HCI frames from any input stream
Signed-off-by: Suraj Sumangala <suraj@atheros.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth')
| -rw-r--r-- | net/bluetooth/hci_core.c | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0ded790bfb67..477c4a60a079 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c | |||
| @@ -1033,6 +1033,115 @@ int hci_recv_frame(struct sk_buff *skb) | |||
| 1033 | } | 1033 | } |
| 1034 | EXPORT_SYMBOL(hci_recv_frame); | 1034 | EXPORT_SYMBOL(hci_recv_frame); |
| 1035 | 1035 | ||
| 1036 | static int hci_reassembly(struct hci_dev *hdev, int type, void *data, | ||
| 1037 | int count, __u8 index, gfp_t gfp_mask) | ||
| 1038 | { | ||
| 1039 | int len = 0; | ||
| 1040 | int hlen = 0; | ||
| 1041 | int remain = count; | ||
| 1042 | struct sk_buff *skb; | ||
| 1043 | struct bt_skb_cb *scb; | ||
| 1044 | |||
| 1045 | if ((type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) || | ||
| 1046 | index >= NUM_REASSEMBLY) | ||
| 1047 | return -EILSEQ; | ||
| 1048 | |||
| 1049 | skb = hdev->reassembly[index]; | ||
| 1050 | |||
| 1051 | if (!skb) { | ||
| 1052 | switch (type) { | ||
| 1053 | case HCI_ACLDATA_PKT: | ||
| 1054 | len = HCI_MAX_FRAME_SIZE; | ||
| 1055 | hlen = HCI_ACL_HDR_SIZE; | ||
| 1056 | break; | ||
| 1057 | case HCI_EVENT_PKT: | ||
| 1058 | len = HCI_MAX_EVENT_SIZE; | ||
| 1059 | hlen = HCI_EVENT_HDR_SIZE; | ||
| 1060 | break; | ||
| 1061 | case HCI_SCODATA_PKT: | ||
| 1062 | len = HCI_MAX_SCO_SIZE; | ||
| 1063 | hlen = HCI_SCO_HDR_SIZE; | ||
| 1064 | break; | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | skb = bt_skb_alloc(len, gfp_mask); | ||
| 1068 | if (!skb) | ||
| 1069 | return -ENOMEM; | ||
| 1070 | |||
| 1071 | scb = (void *) skb->cb; | ||
| 1072 | scb->expect = hlen; | ||
| 1073 | scb->pkt_type = type; | ||
| 1074 | |||
| 1075 | skb->dev = (void *) hdev; | ||
| 1076 | hdev->reassembly[index] = skb; | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | while (count) { | ||
| 1080 | scb = (void *) skb->cb; | ||
| 1081 | len = min(scb->expect, (__u16)count); | ||
| 1082 | |||
| 1083 | memcpy(skb_put(skb, len), data, len); | ||
| 1084 | |||
| 1085 | count -= len; | ||
| 1086 | data += len; | ||
| 1087 | scb->expect -= len; | ||
| 1088 | remain = count; | ||
| 1089 | |||
| 1090 | switch (type) { | ||
| 1091 | case HCI_EVENT_PKT: | ||
| 1092 | if (skb->len == HCI_EVENT_HDR_SIZE) { | ||
| 1093 | struct hci_event_hdr *h = hci_event_hdr(skb); | ||
| 1094 | scb->expect = h->plen; | ||
| 1095 | |||
| 1096 | if (skb_tailroom(skb) < scb->expect) { | ||
| 1097 | kfree_skb(skb); | ||
| 1098 | hdev->reassembly[index] = NULL; | ||
| 1099 | return -ENOMEM; | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | break; | ||
| 1103 | |||
| 1104 | case HCI_ACLDATA_PKT: | ||
| 1105 | if (skb->len == HCI_ACL_HDR_SIZE) { | ||
| 1106 | struct hci_acl_hdr *h = hci_acl_hdr(skb); | ||
| 1107 | scb->expect = __le16_to_cpu(h->dlen); | ||
| 1108 | |||
| 1109 | if (skb_tailroom(skb) < scb->expect) { | ||
| 1110 | kfree_skb(skb); | ||
| 1111 | hdev->reassembly[index] = NULL; | ||
| 1112 | return -ENOMEM; | ||
| 1113 | } | ||
| 1114 | } | ||
| 1115 | break; | ||
| 1116 | |||
| 1117 | case HCI_SCODATA_PKT: | ||
| 1118 | if (skb->len == HCI_SCO_HDR_SIZE) { | ||
| 1119 | struct hci_sco_hdr *h = hci_sco_hdr(skb); | ||
| 1120 | scb->expect = h->dlen; | ||
| 1121 | |||
| 1122 | if (skb_tailroom(skb) < scb->expect) { | ||
| 1123 | kfree_skb(skb); | ||
| 1124 | hdev->reassembly[index] = NULL; | ||
| 1125 | return -ENOMEM; | ||
| 1126 | } | ||
| 1127 | } | ||
| 1128 | break; | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | if (scb->expect == 0) { | ||
| 1132 | /* Complete frame */ | ||
| 1133 | |||
| 1134 | bt_cb(skb)->pkt_type = type; | ||
| 1135 | hci_recv_frame(skb); | ||
| 1136 | |||
| 1137 | hdev->reassembly[index] = NULL; | ||
| 1138 | return remain; | ||
| 1139 | } | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | return remain; | ||
| 1143 | } | ||
| 1144 | |||
| 1036 | /* Receive packet type fragment */ | 1145 | /* Receive packet type fragment */ |
| 1037 | #define __reassembly(hdev, type) ((hdev)->reassembly[(type) - 1]) | 1146 | #define __reassembly(hdev, type) ((hdev)->reassembly[(type) - 1]) |
| 1038 | 1147 | ||
