|
@@ -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);
|
|
|
}
|
|
|
|
|
|
+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)
|
|
|
{
|
|
|
__u8 scan = opt;
|
|
@@ -529,6 +537,10 @@ int hci_dev_open(__u16 dev)
|
|
|
ret = __hci_request(hdev, hci_init_req, 0,
|
|
|
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);
|
|
|
}
|
|
|
|
|
@@ -671,7 +683,7 @@ int hci_dev_reset(__u16 dev)
|
|
|
hdev->flush(hdev);
|
|
|
|
|
|
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))
|
|
|
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) {
|
|
|
- int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
|
|
|
- int q = cnt / num;
|
|
|
+ int cnt, q;
|
|
|
+
|
|
|
+ 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;
|
|
|
} else
|
|
|
*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, "e))) {
|
|
|
+ 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)
|
|
|
{
|
|
|
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);
|
|
|
|
|
|
- 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 */
|
|
|
|
|
@@ -1789,6 +1853,8 @@ static void hci_tx_task(unsigned long arg)
|
|
|
|
|
|
hci_sched_esco(hdev);
|
|
|
|
|
|
+ hci_sched_le(hdev);
|
|
|
+
|
|
|
/* Send next queued raw (unknown type) packet */
|
|
|
while ((skb = skb_dequeue(&hdev->raw_q)))
|
|
|
hci_send_frame(skb);
|