Commit 6ed58ec5 authored by Ville Tervo's avatar Ville Tervo Committed by Gustavo F. Padovan

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
...@@ -123,15 +123,19 @@ struct hci_dev { ...@@ -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); ...@@ -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 {
......
...@@ -326,6 +326,11 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -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) {
......
...@@ -263,6 +263,14 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) ...@@ -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) ...@@ -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) ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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);
......
...@@ -776,6 +776,25 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -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 ...@@ -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 ...@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment