in bfusb.c [245:331]
static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned char *buf, int len)
{
BT_DBG("bfusb %p hdr 0x%02x data %p len %d", data, hdr, buf, len);
if (hdr & 0x10) {
bt_dev_err(data->hdev, "error in block");
kfree_skb(data->reassembly);
data->reassembly = NULL;
return -EIO;
}
if (hdr & 0x04) {
struct sk_buff *skb;
unsigned char pkt_type;
int pkt_len = 0;
if (data->reassembly) {
bt_dev_err(data->hdev, "unexpected start block");
kfree_skb(data->reassembly);
data->reassembly = NULL;
}
if (len < 1) {
bt_dev_err(data->hdev, "no packet type found");
return -EPROTO;
}
pkt_type = *buf++; len--;
switch (pkt_type) {
case HCI_EVENT_PKT:
if (len >= HCI_EVENT_HDR_SIZE) {
struct hci_event_hdr *hdr = (struct hci_event_hdr *) buf;
pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
} else {
bt_dev_err(data->hdev, "event block is too short");
return -EILSEQ;
}
break;
case HCI_ACLDATA_PKT:
if (len >= HCI_ACL_HDR_SIZE) {
struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) buf;
pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
} else {
bt_dev_err(data->hdev, "data block is too short");
return -EILSEQ;
}
break;
case HCI_SCODATA_PKT:
if (len >= HCI_SCO_HDR_SIZE) {
struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) buf;
pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
} else {
bt_dev_err(data->hdev, "audio block is too short");
return -EILSEQ;
}
break;
}
skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
if (!skb) {
bt_dev_err(data->hdev, "no memory for the packet");
return -ENOMEM;
}
hci_skb_pkt_type(skb) = pkt_type;
data->reassembly = skb;
} else {
if (!data->reassembly) {
bt_dev_err(data->hdev, "unexpected continuation block");
return -EIO;
}
}
if (len > 0)
skb_put_data(data->reassembly, buf, len);
if (hdr & 0x08) {
hci_recv_frame(data->hdev, data->reassembly);
data->reassembly = NULL;
}
return 0;
}