diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 10a3eec..414313a 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -767,6 +767,9 @@ struct hci_dev_stats { __u32 byte_tx; }; +/* Fields down there are mostly the same as hci_dev, + as this structure is meant to communicate info + to userspace */ struct hci_dev_info { __u16 dev_id; char name[8]; @@ -782,9 +785,13 @@ struct hci_dev_info { __u32 link_policy; __u32 link_mode; + /* Maximum transmition unit for ACL packets */ __u16 acl_mtu; + /* Number of ACL packets the baseband is able to buffer */ __u16 acl_pkts; + /* Maximum transmition unit for SCO packets */ __u16 sco_mtu; + /* Number of SCO packets the baseband is able to buffer */ __u16 sco_pkts; struct hci_dev_stats stat; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c0fc396..5bad7c0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -25,6 +25,8 @@ #ifndef __HCI_CORE_H #define __HCI_CORE_H +#include + #include /* HCI upper protocols */ @@ -88,12 +90,18 @@ struct hci_dev { unsigned long quirks; atomic_t cmd_cnt; + /* Number of available controller buffers for ACL packets */ unsigned int acl_cnt; - unsigned int sco_cnt; + /* Number of available controller buffers for SCO packets */ + atomic_t sco_cnt; + /* Maximum transmition unit for ACL packets */ unsigned int acl_mtu; + /* Maximum transmition unit for SCO packets */ unsigned int sco_mtu; + /* Maximum number of ACL packets the controller is able to buffer */ unsigned int acl_pkts; + /* Maximum number of SCO packets the controller is able to buffer */ unsigned int sco_pkts; unsigned long cmd_last_tx; @@ -145,12 +153,12 @@ struct hci_conn { struct list_head list; atomic_t refcnt; - spinlock_t lock; bdaddr_t dst; __u16 handle; __u16 state; __u8 mode; + /* type : ACL or SCO */ __u8 type; __u8 out; __u8 attempt; @@ -162,10 +170,17 @@ struct hci_conn { __u8 power_save; unsigned long pend; - unsigned int sent; - - struct sk_buff_head data_q; - + /* sent represents the number of packets this connections + has "on the wire" : .... oh f.... there are no wire + with bluetooth. By on the wire, i mean packets that have been sent + to the HCI device, and that are still in its buffers */ + atomic_t sent; + + struct sk_buff_head out_q; + + /* tx timer : used only for SCO */ + struct hrtimer tx_timer; + /* Disconnect timer */ struct timer_list disc_timer; struct timer_list idle_timer; @@ -175,6 +190,7 @@ struct hci_conn { struct hci_dev *hdev; void *l2cap_data; + /* private use for sco */ void *sco_data; void *priv; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 6cd5711..979464d 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -184,7 +184,9 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) conn->power_save = 1; - skb_queue_head_init(&conn->data_q); + skb_queue_head_init(&conn->out_q); + + hrtimer_init(&conn->tx_timer, CLOCK_MONOTONIC, HRTIMER_NORESTART); init_timer(&conn->disc_timer); conn->disc_timer.function = hci_conn_timeout; @@ -195,6 +197,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) conn->idle_timer.data = (unsigned long) conn; atomic_set(&conn->refcnt, 0); + atomic_set(&conn->sent, 0); hci_dev_hold(hdev); @@ -221,6 +224,8 @@ int hci_conn_del(struct hci_conn *conn) del_timer(&conn->disc_timer); + hrtimer_cancel(&conn->tx_timer); + if (conn->type == SCO_LINK) { struct hci_conn *acl = conn->link; if (acl) { @@ -233,7 +238,7 @@ int hci_conn_del(struct hci_conn *conn) sco->link = NULL; /* Unacked frames */ - hdev->acl_cnt += conn->sent; + hdev->acl_cnt += atomic_read(&conn->sent); } tasklet_disable(&hdev->tx_task); @@ -246,7 +251,7 @@ int hci_conn_del(struct hci_conn *conn) tasklet_enable(&hdev->tx_task); - skb_queue_purge(&conn->data_q); + skb_queue_purge(&conn->out_q); hci_dev_put(hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 338ae97..a2627af 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -619,8 +619,9 @@ int hci_dev_reset(__u16 dev) if (hdev->flush) hdev->flush(hdev); - atomic_set(&hdev->cmd_cnt, 1); - hdev->acl_cnt = 0; hdev->sco_cnt = 0; + atomic_set(&hdev->cmd_cnt, 1); + atomic_set(&hdev->sco_cnt, 0); + hdev->acl_cnt = 0; if (!test_bit(HCI_RAW, &hdev->flags)) ret = __hci_request(hdev, hci_reset_req, 0, @@ -1012,7 +1013,7 @@ static int hci_send_frame(struct sk_buff *skb) hci_send_to_sock(hdev, skb); } - /* Get rid of skb owner, prior to sending to the driver. */ + /* Get rid of skb owner, prior to sending to the driver. */ skb_orphan(skb); return hdev->send(skb); @@ -1096,7 +1097,7 @@ int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) /* Non fragmented */ BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len); - skb_queue_tail(&conn->data_q, skb); + skb_queue_tail(&conn->out_q, skb); } else { /* Fragmented */ BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); @@ -1104,9 +1105,9 @@ int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) skb_shinfo(skb)->frag_list = NULL; /* Queue all fragments atomically */ - spin_lock_bh(&conn->data_q.lock); + spin_lock_bh(&conn->out_q.lock); - __skb_queue_tail(&conn->data_q, skb); + __skb_queue_tail(&conn->out_q, skb); do { skb = list; list = list->next; @@ -1116,10 +1117,10 @@ int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); - __skb_queue_tail(&conn->data_q, skb); + __skb_queue_tail(&conn->out_q, skb); } while (list); - spin_unlock_bh(&conn->data_q.lock); + spin_unlock_bh(&conn->out_q.lock); } hci_sched_tx(hdev); @@ -1132,14 +1133,25 @@ int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb) { struct hci_dev *hdev = conn->hdev; struct hci_sco_hdr hdr; - - BT_DBG("%s len %d", hdev->name, skb->len); + ktime_t now = conn->tx_timer.base->get_time(); +#ifdef CONFIG_BT_HCI_CORE_DEBUG + ktime_t timer_exp = conn->tx_timer.expires; + BT_DBG("conn %p skb %p, timer %5lu.%06lu", conn, skb, + (unsigned long) timer_exp.tv64, + do_div(timer_exp.tv64, NSEC_PER_SEC) / 1000); +#endif if (skb->len > hdev->sco_mtu) { kfree_skb(skb); return -EINVAL; } + /* Criteria for underrun condition : more than 100 ms late */ + if(conn->tx_timer.expires.tv64 + NSEC_PER_SEC / 10 <= now.tv64) { + /* We are under underrun condition, just we do a clean start */ + conn->tx_timer.expires = now; + } + hdr.handle = __cpu_to_le16(conn->handle); hdr.dlen = skb->len; @@ -1148,7 +1160,7 @@ int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb) skb->dev = (void *) hdev; bt_cb(skb)->pkt_type = HCI_SCODATA_PKT; - skb_queue_tail(&conn->data_q, skb); + skb_queue_tail(&conn->out_q, skb); hci_sched_tx(hdev); return 0; } @@ -1156,12 +1168,12 @@ EXPORT_SYMBOL(hci_send_sco); /* ---- HCI TX task (outgoing data) ---- */ -/* HCI Connection scheduler */ -static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote) +/* HCI ACL Connection scheduler */ +static inline struct hci_conn *hci_low_sent_acl(struct hci_dev *hdev, int *quote) { struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *conn = NULL; - int num = 0, min = ~0; + unsigned int num = 0, min = ~0; struct list_head *p; /* We don't have to lock device here. Connections are always @@ -1170,20 +1182,22 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int struct hci_conn *c; c = list_entry(p, struct hci_conn, list); - if (c->type != type || c->state != BT_CONNECTED - || skb_queue_empty(&c->data_q)) + BT_DBG("c->type %d c->state %d len(c->out_q) %d min %d c->sent %d", + c->type, c->state, skb_queue_len(&c->out_q), min, atomic_read(&c->sent)); + + if (c->type != ACL_LINK || c->state != BT_CONNECTED + || skb_queue_empty(&c->out_q)) continue; num++; - if (c->sent < min) { - min = c->sent; + if (atomic_read(&c->sent) < min) { + min = atomic_read(&c->sent); conn = c; } } if (conn) { - int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt); - int q = cnt / num; + int q = hdev->acl_cnt / num; *quote = q ? q : 1; } else *quote = 0; @@ -1203,7 +1217,7 @@ static inline void hci_acl_tx_to(struct hci_dev *hdev) /* Kill stalled connections */ list_for_each(p, &h->list) { c = list_entry(p, struct hci_conn, list); - if (c->type == ACL_LINK && c->sent) { + if (c->type == ACL_LINK && atomic_read(&c->sent)) { BT_ERR("%s killing stalled ACL connection %s", hdev->name, batostr(&c->dst)); hci_acl_disconn(c, 0x13); @@ -1226,8 +1240,8 @@ static inline void hci_sched_acl(struct hci_dev *hdev) hci_acl_tx_to(hdev); } - while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) { - while (quote-- && (skb = skb_dequeue(&conn->data_q))) { + while (hdev->acl_cnt && (conn = hci_low_sent_acl(hdev, "e))) { + while (quote-- && (skb = skb_dequeue(&conn->out_q))) { BT_DBG("skb %p len %d", skb, skb->len); hci_conn_enter_active_mode(conn); @@ -1236,28 +1250,79 @@ static inline void hci_sched_acl(struct hci_dev *hdev) hdev->acl_last_tx = jiffies; hdev->acl_cnt--; - conn->sent++; + atomic_inc(&conn->sent); } } } -/* Schedule SCO */ -static inline void hci_sched_sco(struct hci_dev *hdev) +/* HCI SCO tx timer */ + +static int hci_sco_tx_timer(struct hrtimer *timer) { - struct hci_conn *conn; - struct sk_buff *skb; - int quote; + struct hci_conn *conn = container_of(timer, struct hci_conn, tx_timer); +#ifdef CONFIG_BT_HCI_CORE_DEBUG + ktime_t now = timer->base->get_time(); - BT_DBG("%s", hdev->name); + BT_DBG("%s, conn %p, time %5lu.%06lu", conn->hdev->name, conn, + (unsigned long) now.tv64, + do_div(now.tv64, NSEC_PER_SEC) / 1000); +#endif - while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) { - while (quote-- && (skb = skb_dequeue(&conn->data_q))) { - BT_DBG("skb %p len %d", skb, skb->len); - hci_send_frame(skb); + if(atomic_read(&conn->sent) > 0) { + atomic_dec(&conn->sent); + atomic_inc(&conn->hdev->sco_cnt); + hci_sched_tx(conn->hdev); + } + return HRTIMER_NORESTART; +} + +/* HCI SCO Connection scheduler */ - conn->sent++; - if (conn->sent == ~0) - conn->sent = 0; +static inline void hci_sched_sco(struct hci_dev *hdev) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct sk_buff *skb; + struct list_head *p; + struct hci_conn *c; + + BT_DBG("%s", hdev->name); + + /* We don't have to lock device here. Connections are always + * added and removed with TX task disabled. */ + list_for_each(p, &h->list) { + c = list_entry(p, struct hci_conn, list); + + /* SCO scheduling algorithm makes sure there is never more than + 1 outstanding packet for each connection */ + if (c->type == SCO_LINK && atomic_read(&c->sent) < 1 && c->state == BT_CONNECTED) + { + if(atomic_read(&hdev->sco_cnt) > 0) { + if((skb = skb_dequeue(&c->out_q)) != NULL) { + ktime_t now, pkt_time; + + hci_send_frame(skb); + + atomic_inc(&c->sent); + atomic_dec(&hdev->sco_cnt); + + c->tx_timer.function = hci_sco_tx_timer; + + pkt_time = + ktime_set(0, NSEC_PER_SEC / 16000 * (skb->len - HCI_SCO_HDR_SIZE)); + now = c->tx_timer.base->get_time(); + + c->tx_timer.expires.tv64 += pkt_time.tv64; + if(c->tx_timer.expires.tv64 > now.tv64) { + hrtimer_restart(&c->tx_timer); + } + else { + /* Timer is to expire in the past - this can happen if timer base + precision is less than pkt_time. In this case we force timer + expiration by calling its expires function */ + c->tx_timer.function(&c->tx_timer); + } + } + } } } } @@ -1269,14 +1334,14 @@ 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", hdev->name, hdev->acl_cnt, atomic_read(&hdev->sco_cnt)); /* Schedule queues and send stuff to HCI driver */ - hci_sched_acl(hdev); - hci_sched_sco(hdev); + hci_sched_acl(hdev); + /* Send next queued raw (unknown type) packet */ while ((skb = skb_dequeue(&hdev->raw_q))) hci_send_frame(skb); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index bb94e6d..815c79d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -320,7 +320,7 @@ static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *s lv = (struct hci_rp_read_loc_version *) skb->data; if (lv->status) { - BT_DBG("%s READ_LOCAL_VERSION failed %d", hdev->name, lf->status); + BT_DBG("%s READ_LOCAL_VERSION failed %d", hdev->name, lv->status); break; } @@ -382,7 +382,7 @@ static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *s } hdev->acl_cnt = hdev->acl_pkts; - hdev->sco_cnt = hdev->sco_pkts; + atomic_set(&hdev->sco_cnt, hdev->sco_pkts); BT_DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name, hdev->acl_mtu, hdev->sco_mtu, hdev->acl_pkts, hdev->sco_pkts); @@ -880,15 +880,15 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s conn = hci_conn_hash_lookup_handle(hdev, handle); if (conn) { - conn->sent -= count; + atomic_sub(count, &conn->sent); - if (conn->type == SCO_LINK) { - if ((hdev->sco_cnt += count) > hdev->sco_pkts) - hdev->sco_cnt = hdev->sco_pkts; - } else { + if (conn->type == ACL_LINK) { if ((hdev->acl_cnt += count) > hdev->acl_pkts) hdev->acl_cnt = hdev->acl_pkts; } + /* Note : we do not use the "number of completed packets" event + to increment hdev->sco_cnt, as this feature is only optionnally support + by bluetooth controllers. So there is no if branch for SCO_LINK packets */ } } hci_sched_tx(hdev);