Bluetooth: Process num completed data blocks event
authorAndrei Emeltchenko <andrei.emeltchenko@intel.com>
Wed, 4 Jan 2012 10:41:58 +0000 (12:41 +0200)
committerJohan Hedberg <johan.hedberg@intel.com>
Mon, 13 Feb 2012 15:01:20 +0000 (17:01 +0200)
Adds support for Number Of Completed Data Blocks Event.

Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
include/net/bluetooth/hci.h
net/bluetooth/hci_event.c

index ce5133b224457647001993e9ba302f20af320d05..6a9d316fb9773e7015a23e38bfa6c5ebc2f10086 100644 (file)
@@ -1168,6 +1168,19 @@ struct hci_ev_le_meta {
        __u8     subevent;
 } __packed;
 
+#define HCI_EV_NUM_COMP_BLOCKS         0x48
+struct hci_comp_blocks_info {
+       __le16   handle;
+       __le16   pkts;
+       __le16   blocks;
+} __packed;
+
+struct hci_ev_num_comp_blocks {
+       __le16   num_blocks;
+       __u8     num_hndl;
+       struct hci_comp_blocks_info handles[0];
+} __packed;
+
 /* Low energy meta events */
 #define HCI_EV_LE_CONN_COMPLETE                0x01
 struct hci_ev_le_conn_complete {
index 089dff80ccb0e6ed392aea9198612485d6150c18..0466ed9c1b4743aaa8b62f2e19cdf1bc33417e0a 100644 (file)
@@ -2408,6 +2408,56 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s
        queue_work(hdev->workqueue, &hdev->tx_work);
 }
 
+static inline void hci_num_comp_blocks_evt(struct hci_dev *hdev,
+                                                       struct sk_buff *skb)
+{
+       struct hci_ev_num_comp_blocks *ev = (void *) skb->data;
+       int i;
+
+       if (hdev->flow_ctl_mode != HCI_FLOW_CTL_MODE_BLOCK_BASED) {
+               BT_ERR("Wrong event for mode %d", hdev->flow_ctl_mode);
+               return;
+       }
+
+       if (skb->len < sizeof(*ev) || skb->len < sizeof(*ev) +
+                       ev->num_hndl * sizeof(struct hci_comp_blocks_info)) {
+               BT_DBG("%s bad parameters", hdev->name);
+               return;
+       }
+
+       BT_DBG("%s num_blocks %d num_hndl %d", hdev->name, ev->num_blocks,
+                                                               ev->num_hndl);
+
+       for (i = 0; i < ev->num_hndl; i++) {
+               struct hci_comp_blocks_info *info = &ev->handles[i];
+               struct hci_conn *conn;
+               __u16  handle, block_count;
+
+               handle = __le16_to_cpu(info->handle);
+               block_count = __le16_to_cpu(info->blocks);
+
+               conn = hci_conn_hash_lookup_handle(hdev, handle);
+               if (!conn)
+                       continue;
+
+               conn->sent -= block_count;
+
+               switch (conn->type) {
+               case ACL_LINK:
+                       hdev->block_cnt += block_count;
+                       if (hdev->block_cnt > hdev->num_blocks)
+                               hdev->block_cnt = hdev->num_blocks;
+                       break;
+
+               default:
+                       BT_ERR("Unknown type %d conn %p", conn->type, conn);
+                       break;
+               }
+       }
+
+       queue_work(hdev->workqueue, &hdev->tx_work);
+}
+
 static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_mode_change *ev = (void *) skb->data;
@@ -3386,6 +3436,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                hci_remote_oob_data_request_evt(hdev, skb);
                break;
 
+       case HCI_EV_NUM_COMP_BLOCKS:
+               hci_num_comp_blocks_evt(hdev, skb);
+               break;
+
        default:
                BT_DBG("%s event 0x%x", hdev->name, event);
                break;