Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 *
  3 *  Generic Bluetooth HCI UART driver
  4 *
  5 *  Copyright (C) 2015-2018  Intel Corporation
  6 *
  7 *
  8 *  This program is free software; you can redistribute it and/or modify
  9 *  it under the terms of the GNU General Public License as published by
 10 *  the Free Software Foundation; either version 2 of the License, or
 11 *  (at your option) any later version.
 12 *
 13 *  This program is distributed in the hope that it will be useful,
 14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 *  GNU General Public License for more details.
 17 *
 18 *  You should have received a copy of the GNU General Public License
 19 *  along with this program; if not, write to the Free Software
 20 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 21 *
 22 */
 23
 24#include <asm/unaligned.h>
 25
 26struct h4_recv_pkt {
 27	u8  type;	/* Packet type */
 28	u8  hlen;	/* Header length */
 29	u8  loff;	/* Data length offset in header */
 30	u8  lsize;	/* Data length field size */
 31	u16 maxlen;	/* Max overall packet length */
 32	int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
 33};
 34
 35#define H4_RECV_ACL \
 36	.type = HCI_ACLDATA_PKT, \
 37	.hlen = HCI_ACL_HDR_SIZE, \
 38	.loff = 2, \
 39	.lsize = 2, \
 40	.maxlen = HCI_MAX_FRAME_SIZE \
 41
 42#define H4_RECV_SCO \
 43	.type = HCI_SCODATA_PKT, \
 44	.hlen = HCI_SCO_HDR_SIZE, \
 45	.loff = 2, \
 46	.lsize = 1, \
 47	.maxlen = HCI_MAX_SCO_SIZE
 48
 49#define H4_RECV_EVENT \
 50	.type = HCI_EVENT_PKT, \
 51	.hlen = HCI_EVENT_HDR_SIZE, \
 52	.loff = 1, \
 53	.lsize = 1, \
 54	.maxlen = HCI_MAX_EVENT_SIZE
 55
 56static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev,
 57					  struct sk_buff *skb,
 58					  const unsigned char *buffer,
 59					  int count,
 60					  const struct h4_recv_pkt *pkts,
 61					  int pkts_count)
 62{
 63	while (count) {
 64		int i, len;
 65
 66		if (!count)
 67			break;
 68
 69		if (!skb) {
 70			for (i = 0; i < pkts_count; i++) {
 71				if (buffer[0] != (&pkts[i])->type)
 72					continue;
 73
 74				skb = bt_skb_alloc((&pkts[i])->maxlen,
 75						   GFP_ATOMIC);
 76				if (!skb)
 77					return ERR_PTR(-ENOMEM);
 78
 79				hci_skb_pkt_type(skb) = (&pkts[i])->type;
 80				hci_skb_expect(skb) = (&pkts[i])->hlen;
 81				break;
 82			}
 83
 84			/* Check for invalid packet type */
 85			if (!skb)
 86				return ERR_PTR(-EILSEQ);
 87
 88			count -= 1;
 89			buffer += 1;
 90		}
 91
 92		len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
 93		skb_put_data(skb, buffer, len);
 94
 95		count -= len;
 96		buffer += len;
 97
 98		/* Check for partial packet */
 99		if (skb->len < hci_skb_expect(skb))
100			continue;
101
102		for (i = 0; i < pkts_count; i++) {
103			if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
104				break;
105		}
106
107		if (i >= pkts_count) {
108			kfree_skb(skb);
109			return ERR_PTR(-EILSEQ);
110		}
111
112		if (skb->len == (&pkts[i])->hlen) {
113			u16 dlen;
114
115			switch ((&pkts[i])->lsize) {
116			case 0:
117				/* No variable data length */
118				dlen = 0;
119				break;
120			case 1:
121				/* Single octet variable length */
122				dlen = skb->data[(&pkts[i])->loff];
123				hci_skb_expect(skb) += dlen;
124
125				if (skb_tailroom(skb) < dlen) {
126					kfree_skb(skb);
127					return ERR_PTR(-EMSGSIZE);
128				}
129				break;
130			case 2:
131				/* Double octet variable length */
132				dlen = get_unaligned_le16(skb->data +
133							  (&pkts[i])->loff);
134				hci_skb_expect(skb) += dlen;
135
136				if (skb_tailroom(skb) < dlen) {
137					kfree_skb(skb);
138					return ERR_PTR(-EMSGSIZE);
139				}
140				break;
141			default:
142				/* Unsupported variable length */
143				kfree_skb(skb);
144				return ERR_PTR(-EILSEQ);
145			}
146
147			if (!dlen) {
148				/* No more data, complete frame */
149				(&pkts[i])->recv(hdev, skb);
150				skb = NULL;
151			}
152		} else {
153			/* Complete frame */
154			(&pkts[i])->recv(hdev, skb);
155			skb = NULL;
156		}
157	}
158
159	return skb;
160}