aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorSuraj Sumangala <suraj@atheros.com>2010-07-14 03:32:17 -0400
committerMarcel Holtmann <marcel@holtmann.org>2010-07-21 13:39:12 -0400
commit33e882a5f2301a23a85ef2994e30fd9f48d39d9b (patch)
tree4048a954d11698581e123b25389a9f8f72125f90 /net
parentcd4c53919ed50b0d532f106aeb76e79077bece98 (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')
-rw-r--r--net/bluetooth/hci_core.c109
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}
1034EXPORT_SYMBOL(hci_recv_frame); 1034EXPORT_SYMBOL(hci_recv_frame);
1035 1035
1036static 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