| /* | 
 |  * | 
 |  *  Generic Bluetooth HCI UART driver | 
 |  * | 
 |  *  Copyright (C) 2015-2018  Intel Corporation | 
 |  * | 
 |  * | 
 |  *  This program is free software; you can redistribute it and/or modify | 
 |  *  it under the terms of the GNU General Public License as published by | 
 |  *  the Free Software Foundation; either version 2 of the License, or | 
 |  *  (at your option) any later version. | 
 |  * | 
 |  *  This program is distributed in the hope that it will be useful, | 
 |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |  *  GNU General Public License for more details. | 
 |  * | 
 |  *  You should have received a copy of the GNU General Public License | 
 |  *  along with this program; if not, write to the Free Software | 
 |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
 |  * | 
 |  */ | 
 |  | 
 | #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 (!count) | 
 | 			break; | 
 |  | 
 | 		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; | 
 | } |