|  | /* SPDX-License-Identifier: GPL-2.0-or-later */ | 
|  | /* | 
|  | * | 
|  | *  Generic Bluetooth HCI UART driver | 
|  | * | 
|  | *  Copyright (C) 2015-2018  Intel Corporation | 
|  | */ | 
|  |  | 
|  | #include <asm/unaligned.h> | 
|  |  | 
|  | struct h4_recv_pkt { | 
|  | u8  type;	/* Packet type */ | 
|  | u8  hlen;	/* Header length */ | 
|  | u8  loff;	/* Data length offset in header */ | 
|  | u8  lsize;	/* Data length field size */ | 
|  | u16 maxlen;	/* Max overall packet length */ | 
|  | int (*recv)(struct hci_dev *hdev, struct sk_buff *skb); | 
|  | }; | 
|  |  | 
|  | #define H4_RECV_ACL \ | 
|  | .type = HCI_ACLDATA_PKT, \ | 
|  | .hlen = HCI_ACL_HDR_SIZE, \ | 
|  | .loff = 2, \ | 
|  | .lsize = 2, \ | 
|  | .maxlen = HCI_MAX_FRAME_SIZE \ | 
|  |  | 
|  | #define H4_RECV_SCO \ | 
|  | .type = HCI_SCODATA_PKT, \ | 
|  | .hlen = HCI_SCO_HDR_SIZE, \ | 
|  | .loff = 2, \ | 
|  | .lsize = 1, \ | 
|  | .maxlen = HCI_MAX_SCO_SIZE | 
|  |  | 
|  | #define H4_RECV_EVENT \ | 
|  | .type = HCI_EVENT_PKT, \ | 
|  | .hlen = HCI_EVENT_HDR_SIZE, \ | 
|  | .loff = 1, \ | 
|  | .lsize = 1, \ | 
|  | .maxlen = HCI_MAX_EVENT_SIZE | 
|  |  | 
|  | static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev, | 
|  | struct sk_buff *skb, | 
|  | const unsigned char *buffer, | 
|  | int count, | 
|  | const struct h4_recv_pkt *pkts, | 
|  | int pkts_count) | 
|  | { | 
|  | /* Check for error from previous call */ | 
|  | if (IS_ERR(skb)) | 
|  | skb = NULL; | 
|  |  | 
|  | while (count) { | 
|  | int i, len; | 
|  |  | 
|  | if (!skb) { | 
|  | for (i = 0; i < pkts_count; i++) { | 
|  | if (buffer[0] != (&pkts[i])->type) | 
|  | continue; | 
|  |  | 
|  | skb = bt_skb_alloc((&pkts[i])->maxlen, | 
|  | GFP_ATOMIC); | 
|  | if (!skb) | 
|  | return ERR_PTR(-ENOMEM); | 
|  |  | 
|  | hci_skb_pkt_type(skb) = (&pkts[i])->type; | 
|  | hci_skb_expect(skb) = (&pkts[i])->hlen; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* Check for invalid packet type */ | 
|  | if (!skb) | 
|  | return ERR_PTR(-EILSEQ); | 
|  |  | 
|  | count -= 1; | 
|  | buffer += 1; | 
|  | } | 
|  |  | 
|  | len = min_t(uint, hci_skb_expect(skb) - skb->len, count); | 
|  | skb_put_data(skb, buffer, len); | 
|  |  | 
|  | count -= len; | 
|  | buffer += len; | 
|  |  | 
|  | /* Check for partial packet */ | 
|  | if (skb->len < hci_skb_expect(skb)) | 
|  | continue; | 
|  |  | 
|  | for (i = 0; i < pkts_count; i++) { | 
|  | if (hci_skb_pkt_type(skb) == (&pkts[i])->type) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (i >= pkts_count) { | 
|  | kfree_skb(skb); | 
|  | return ERR_PTR(-EILSEQ); | 
|  | } | 
|  |  | 
|  | if (skb->len == (&pkts[i])->hlen) { | 
|  | u16 dlen; | 
|  |  | 
|  | switch ((&pkts[i])->lsize) { | 
|  | case 0: | 
|  | /* No variable data length */ | 
|  | dlen = 0; | 
|  | break; | 
|  | case 1: | 
|  | /* Single octet variable length */ | 
|  | dlen = skb->data[(&pkts[i])->loff]; | 
|  | hci_skb_expect(skb) += dlen; | 
|  |  | 
|  | if (skb_tailroom(skb) < dlen) { | 
|  | kfree_skb(skb); | 
|  | return ERR_PTR(-EMSGSIZE); | 
|  | } | 
|  | break; | 
|  | case 2: | 
|  | /* Double octet variable length */ | 
|  | dlen = get_unaligned_le16(skb->data + | 
|  | (&pkts[i])->loff); | 
|  | hci_skb_expect(skb) += dlen; | 
|  |  | 
|  | if (skb_tailroom(skb) < dlen) { | 
|  | kfree_skb(skb); | 
|  | return ERR_PTR(-EMSGSIZE); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | /* Unsupported variable length */ | 
|  | kfree_skb(skb); | 
|  | return ERR_PTR(-EILSEQ); | 
|  | } | 
|  |  | 
|  | if (!dlen) { | 
|  | /* No more data, complete frame */ | 
|  | (&pkts[i])->recv(hdev, skb); | 
|  | skb = NULL; | 
|  | } | 
|  | } else { | 
|  | /* Complete frame */ | 
|  | (&pkts[i])->recv(hdev, skb); | 
|  | skb = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | return skb; | 
|  | } |