Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 943cc592 authored by Marcel Holtmann's avatar Marcel Holtmann Committed by Johan Hedberg
Browse files

Bluetooth: bpa10x: Use h4_recv_buf helper for frame reassembly



The manually coded frame reassembly is actually broken. The h4_recv_buf
helper from the UART driver is a perfect fit for frame reassembly for
this driver. So just export that function and use it here as well.

Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarJohan Hedberg <johan.hedberg@intel.com>
parent 881f7e86
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -184,6 +184,7 @@ config BT_HCIBCM203X
config BT_HCIBPA10X
	tristate "HCI BPA10x USB driver"
	depends on USB
	select BT_HCIUART_H4
	help
	  Bluetooth HCI BPA10x USB driver.
	  This driver provides support for the Digianswer BPA 100/105 Bluetooth
+27 −109
Original line number Diff line number Diff line
@@ -35,7 +35,9 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>

#define VERSION "0.10"
#include "hci_uart.h"

#define VERSION "0.11"

static const struct usb_device_id bpa10x_table[] = {
	/* Tektronix BPA 100/105 (Digianswer) */
@@ -56,112 +58,6 @@ struct bpa10x_data {
	struct sk_buff *rx_skb[2];
};

#define HCI_VENDOR_HDR_SIZE 5

struct hci_vendor_hdr {
	__u8    type;
	__le16  snum;
	__le16  dlen;
} __packed;

static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count)
{
	struct bpa10x_data *data = hci_get_drvdata(hdev);

	BT_DBG("%s queue %d buffer %p count %d", hdev->name,
							queue, buf, count);

	if (queue < 0 || queue > 1)
		return -EILSEQ;

	hdev->stat.byte_rx += count;

	while (count) {
		struct sk_buff *skb = data->rx_skb[queue];
		struct { __u8 type; int expect; } *scb;
		int type, len = 0;

		if (!skb) {
			/* Start of the frame */

			type = *((__u8 *) buf);
			count--; buf++;

			switch (type) {
			case HCI_EVENT_PKT:
				if (count >= HCI_EVENT_HDR_SIZE) {
					struct hci_event_hdr *h = buf;
					len = HCI_EVENT_HDR_SIZE + h->plen;
				} else
					return -EILSEQ;
				break;

			case HCI_ACLDATA_PKT:
				if (count >= HCI_ACL_HDR_SIZE) {
					struct hci_acl_hdr *h = buf;
					len = HCI_ACL_HDR_SIZE +
							__le16_to_cpu(h->dlen);
				} else
					return -EILSEQ;
				break;

			case HCI_SCODATA_PKT:
				if (count >= HCI_SCO_HDR_SIZE) {
					struct hci_sco_hdr *h = buf;
					len = HCI_SCO_HDR_SIZE + h->dlen;
				} else
					return -EILSEQ;
				break;

			case HCI_VENDOR_PKT:
				if (count >= HCI_VENDOR_HDR_SIZE) {
					struct hci_vendor_hdr *h = buf;
					len = HCI_VENDOR_HDR_SIZE +
							__le16_to_cpu(h->dlen);
				} else
					return -EILSEQ;
				break;
			}

			skb = bt_skb_alloc(len, GFP_ATOMIC);
			if (!skb) {
				BT_ERR("%s no memory for packet", hdev->name);
				return -ENOMEM;
			}

			data->rx_skb[queue] = skb;

			scb = (void *) skb->cb;
			scb->type = type;
			scb->expect = len;
		} else {
			/* Continuation */

			scb = (void *) skb->cb;
			len = scb->expect;
		}

		len = min(len, count);

		memcpy(skb_put(skb, len), buf, len);

		scb->expect -= len;

		if (scb->expect == 0) {
			/* Complete frame */

			data->rx_skb[queue] = NULL;

			bt_cb(skb)->pkt_type = scb->type;
			hci_recv_frame(hdev, skb);
		}

		count -= len; buf += len;
	}

	return 0;
}

static void bpa10x_tx_complete(struct urb *urb)
{
	struct sk_buff *skb = urb->context;
@@ -184,6 +80,22 @@ static void bpa10x_tx_complete(struct urb *urb)
	kfree_skb(skb);
}

#define HCI_VENDOR_HDR_SIZE 5

#define HCI_RECV_VENDOR \
	.type = HCI_VENDOR_PKT, \
	.hlen = HCI_VENDOR_HDR_SIZE, \
	.loff = 3, \
	.lsize = 2, \
	.maxlen = HCI_MAX_FRAME_SIZE

static const struct h4_recv_pkt bpa10x_recv_pkts[] = {
	{ H4_RECV_ACL,     .recv = hci_recv_frame },
	{ H4_RECV_SCO,     .recv = hci_recv_frame },
	{ H4_RECV_EVENT,   .recv = hci_recv_frame },
	{ HCI_RECV_VENDOR, .recv = hci_recv_diag  },
};

static void bpa10x_rx_complete(struct urb *urb)
{
	struct hci_dev *hdev = urb->context;
@@ -197,11 +109,17 @@ static void bpa10x_rx_complete(struct urb *urb)
		return;

	if (urb->status == 0) {
		if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe),
		bool idx = usb_pipebulk(urb->pipe);

		data->rx_skb[idx] = h4_recv_buf(hdev, data->rx_skb[idx],
						urb->transfer_buffer,
						urb->actual_length) < 0) {
						urb->actual_length,
						bpa10x_recv_pkts,
						ARRAY_SIZE(bpa10x_recv_pkts));
		if (IS_ERR(data->rx_skb[idx])) {
			BT_ERR("%s corrupted event packet", hdev->name);
			hdev->stat.err_rx++;
			data->rx_skb[idx] = NULL;
		}
	}

+1 −0
Original line number Diff line number Diff line
@@ -266,3 +266,4 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,

	return skb;
}
EXPORT_SYMBOL_GPL(h4_recv_buf);