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

Commit 6ed58ec5 authored by Ville Tervo's avatar Ville Tervo Committed by Gustavo Padovan
Browse files

Bluetooth: Use LE buffers for LE traffic



Bluetooth chips may have separate buffers for LE traffic.
This patch add support to use LE buffers provided by the chip.

Signed-off-by: default avatarVille Tervo <ville.tervo@nokia.com>
Acked-by: default avatarMarcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarGustavo F. Padovan <padovan@profusion.mobi>
parent fcd89c09
Loading
Loading
Loading
Loading
+5 −0
Original line number Original line Diff line number Diff line
@@ -123,15 +123,19 @@ struct hci_dev {
	atomic_t	cmd_cnt;
	atomic_t	cmd_cnt;
	unsigned int	acl_cnt;
	unsigned int	acl_cnt;
	unsigned int	sco_cnt;
	unsigned int	sco_cnt;
	unsigned int	le_cnt;


	unsigned int	acl_mtu;
	unsigned int	acl_mtu;
	unsigned int	sco_mtu;
	unsigned int	sco_mtu;
	unsigned int	le_mtu;
	unsigned int	acl_pkts;
	unsigned int	acl_pkts;
	unsigned int	sco_pkts;
	unsigned int	sco_pkts;
	unsigned int	le_pkts;


	unsigned long	cmd_last_tx;
	unsigned long	cmd_last_tx;
	unsigned long	acl_last_tx;
	unsigned long	acl_last_tx;
	unsigned long	sco_last_tx;
	unsigned long	sco_last_tx;
	unsigned long	le_last_tx;


	struct workqueue_struct	*workqueue;
	struct workqueue_struct	*workqueue;


@@ -521,6 +525,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
#define lmp_esco_capable(dev)      ((dev)->features[3] & LMP_ESCO)
#define lmp_esco_capable(dev)      ((dev)->features[3] & LMP_ESCO)
#define lmp_ssp_capable(dev)       ((dev)->features[6] & LMP_SIMPLE_PAIR)
#define lmp_ssp_capable(dev)       ((dev)->features[6] & LMP_SIMPLE_PAIR)
#define lmp_no_flush_capable(dev)  ((dev)->features[6] & LMP_NO_FLUSH)
#define lmp_no_flush_capable(dev)  ((dev)->features[6] & LMP_NO_FLUSH)
#define lmp_le_capable(dev)        ((dev)->features[4] & LMP_LE)


/* ----- HCI protocols ----- */
/* ----- HCI protocols ----- */
struct hci_proto {
struct hci_proto {
+5 −0
Original line number Original line Diff line number Diff line
@@ -326,6 +326,11 @@ int hci_conn_del(struct hci_conn *conn)


		/* Unacked frames */
		/* Unacked frames */
		hdev->acl_cnt += conn->sent;
		hdev->acl_cnt += conn->sent;
	} else if (conn->type == LE_LINK) {
		if (hdev->le_pkts)
			hdev->le_cnt += conn->sent;
		else
			hdev->acl_cnt += conn->sent;
	} else {
	} else {
		struct hci_conn *acl = conn->link;
		struct hci_conn *acl = conn->link;
		if (acl) {
		if (acl) {
+70 −4
Original line number Original line Diff line number Diff line
@@ -263,6 +263,14 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
	hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp);
	hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp);
}
}


static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt)
{
	BT_DBG("%s", hdev->name);

	/* Read LE buffer size */
	hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
}

static void hci_scan_req(struct hci_dev *hdev, unsigned long opt)
static void hci_scan_req(struct hci_dev *hdev, unsigned long opt)
{
{
	__u8 scan = opt;
	__u8 scan = opt;
@@ -529,6 +537,10 @@ int hci_dev_open(__u16 dev)
		ret = __hci_request(hdev, hci_init_req, 0,
		ret = __hci_request(hdev, hci_init_req, 0,
					msecs_to_jiffies(HCI_INIT_TIMEOUT));
					msecs_to_jiffies(HCI_INIT_TIMEOUT));


		if (lmp_le_capable(hdev))
			ret = __hci_request(hdev, hci_le_init_req, 0,
					msecs_to_jiffies(HCI_INIT_TIMEOUT));

		clear_bit(HCI_INIT, &hdev->flags);
		clear_bit(HCI_INIT, &hdev->flags);
	}
	}


@@ -671,7 +683,7 @@ int hci_dev_reset(__u16 dev)
		hdev->flush(hdev);
		hdev->flush(hdev);


	atomic_set(&hdev->cmd_cnt, 1);
	atomic_set(&hdev->cmd_cnt, 1);
	hdev->acl_cnt = 0; hdev->sco_cnt = 0;
	hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0;


	if (!test_bit(HCI_RAW, &hdev->flags))
	if (!test_bit(HCI_RAW, &hdev->flags))
		ret = __hci_request(hdev, hci_reset_req, 0,
		ret = __hci_request(hdev, hci_reset_req, 0,
@@ -1672,8 +1684,25 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
	}
	}


	if (conn) {
	if (conn) {
		int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
		int cnt, q;
		int q = cnt / num;

		switch (conn->type) {
		case ACL_LINK:
			cnt = hdev->acl_cnt;
			break;
		case SCO_LINK:
		case ESCO_LINK:
			cnt = hdev->sco_cnt;
			break;
		case LE_LINK:
			cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
			break;
		default:
			cnt = 0;
			BT_ERR("Unknown link type");
		}

		q = cnt / num;
		*quote = q ? q : 1;
		*quote = q ? q : 1;
	} else
	} else
		*quote = 0;
		*quote = 0;
@@ -1772,6 +1801,40 @@ static inline void hci_sched_esco(struct hci_dev *hdev)
	}
	}
}
}


static inline void hci_sched_le(struct hci_dev *hdev)
{
	struct hci_conn *conn;
	struct sk_buff *skb;
	int quote, cnt;

	BT_DBG("%s", hdev->name);

	if (!test_bit(HCI_RAW, &hdev->flags)) {
		/* LE tx timeout must be longer than maximum
		 * link supervision timeout (40.9 seconds) */
		if (!hdev->le_cnt &&
				time_after(jiffies, hdev->le_last_tx + HZ * 45))
			hci_acl_tx_to(hdev);
	}

	cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt;
	while (cnt && (conn = hci_low_sent(hdev, LE_LINK, &quote))) {
		while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
			BT_DBG("skb %p len %d", skb, skb->len);

			hci_send_frame(skb);
			hdev->le_last_tx = jiffies;

			cnt--;
			conn->sent++;
		}
	}
	if (hdev->le_pkts)
		hdev->le_cnt = cnt;
	else
		hdev->acl_cnt = cnt;
}

static void hci_tx_task(unsigned long arg)
static void hci_tx_task(unsigned long arg)
{
{
	struct hci_dev *hdev = (struct hci_dev *) arg;
	struct hci_dev *hdev = (struct hci_dev *) arg;
@@ -1779,7 +1842,8 @@ static void hci_tx_task(unsigned long arg)


	read_lock(&hci_task_lock);
	read_lock(&hci_task_lock);


	BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
	BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
		hdev->sco_cnt, hdev->le_cnt);


	/* Schedule queues and send stuff to HCI driver */
	/* Schedule queues and send stuff to HCI driver */


@@ -1789,6 +1853,8 @@ static void hci_tx_task(unsigned long arg)


	hci_sched_esco(hdev);
	hci_sched_esco(hdev);


	hci_sched_le(hdev);

	/* Send next queued raw (unknown type) packet */
	/* Send next queued raw (unknown type) packet */
	while ((skb = skb_dequeue(&hdev->raw_q)))
	while ((skb = skb_dequeue(&hdev->raw_q)))
		hci_send_frame(skb);
		hci_send_frame(skb);
+33 −0
Original line number Original line Diff line number Diff line
@@ -776,6 +776,25 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
		mgmt_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr,
		mgmt_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr,
								rp->status);
								rp->status);
}
}
static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
				       struct sk_buff *skb)
{
	struct hci_rp_le_read_buffer_size *rp = (void *) skb->data;

	BT_DBG("%s status 0x%x", hdev->name, rp->status);

	if (rp->status)
		return;

	hdev->le_mtu = __le16_to_cpu(rp->le_mtu);
	hdev->le_pkts = rp->le_max_pkt;

	hdev->le_cnt = hdev->le_pkts;

	BT_DBG("%s le mtu %d:%d", hdev->name, hdev->le_mtu, hdev->le_pkts);

	hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status);
}


static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
{
{
@@ -1704,6 +1723,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
		hci_cc_pin_code_neg_reply(hdev, skb);
		hci_cc_pin_code_neg_reply(hdev, skb);
		break;
		break;


	case HCI_OP_LE_READ_BUFFER_SIZE:
		hci_cc_le_read_buffer_size(hdev, skb);
		break;

	default:
	default:
		BT_DBG("%s opcode 0x%x", hdev->name, opcode);
		BT_DBG("%s opcode 0x%x", hdev->name, opcode);
		break;
		break;
@@ -1849,6 +1872,16 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s
				hdev->acl_cnt += count;
				hdev->acl_cnt += count;
				if (hdev->acl_cnt > hdev->acl_pkts)
				if (hdev->acl_cnt > hdev->acl_pkts)
					hdev->acl_cnt = hdev->acl_pkts;
					hdev->acl_cnt = hdev->acl_pkts;
			} else if (conn->type == LE_LINK) {
				if (hdev->le_pkts) {
					hdev->le_cnt += count;
					if (hdev->le_cnt > hdev->le_pkts)
						hdev->le_cnt = hdev->le_pkts;
				} else {
					hdev->acl_cnt += count;
					if (hdev->acl_cnt > hdev->acl_pkts)
						hdev->acl_cnt = hdev->acl_pkts;
				}
			} else {
			} else {
				hdev->sco_cnt += count;
				hdev->sco_cnt += count;
				if (hdev->sco_cnt > hdev->sco_pkts)
				if (hdev->sco_cnt > hdev->sco_pkts)