summaryrefslogtreecommitdiffstats
path: root/net/bluetooth
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2011-01-21 13:56:35 +0200
committerGustavo F. Padovan <padovan@profusion.mobi>2011-02-08 01:40:07 -0200
commit2784eb41b1fbb3ff80f4921fe9dbb4c4acb6dc24 (patch)
treee13ea997be45f3d1abf5f3b4733a9da075a0410f /net/bluetooth
parent17d5c04cb597418a177c3ca18dfde679636dd51c (diff)
downloadop-kernel-dev-2784eb41b1fbb3ff80f4921fe9dbb4c4acb6dc24.zip
op-kernel-dev-2784eb41b1fbb3ff80f4921fe9dbb4c4acb6dc24.tar.gz
Bluetooth: Add get_connections managment interface command
This patch adds a get_connections command to the management interface. With this command userspace can get the current list of connected devices. Typically this command would only be used once when enumerating existing adapters. After that the connected and disconnected events are used to track connections. Signed-off-by: Johan Hedberg <johan.hedberg@nokia.com> Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/mgmt.c72
1 files changed, 72 insertions, 0 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 9fb989f..8f4f47e9d 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -941,6 +941,75 @@ failed:
return err;
}
+static int get_connections(struct sock *sk, unsigned char *data, u16 len)
+{
+ struct sk_buff *skb;
+ struct mgmt_hdr *hdr;
+ struct mgmt_cp_get_connections *cp;
+ struct mgmt_ev_cmd_complete *ev;
+ struct mgmt_rp_get_connections *rp;
+ struct hci_dev *hdev;
+ struct list_head *p;
+ size_t body_len;
+ u16 dev_id, count;
+ int i, err;
+
+ BT_DBG("");
+
+ cp = (void *) data;
+ dev_id = get_unaligned_le16(&cp->index);
+
+ hdev = hci_dev_get(dev_id);
+ if (!hdev)
+ return cmd_status(sk, MGMT_OP_GET_CONNECTIONS, ENODEV);
+
+ hci_dev_lock_bh(hdev);
+
+ count = 0;
+ list_for_each(p, &hdev->conn_hash.list) {
+ count++;
+ }
+
+ body_len = sizeof(*ev) + sizeof(*rp) + (count * sizeof(bdaddr_t));
+ skb = alloc_skb(sizeof(*hdr) + body_len, GFP_ATOMIC);
+ if (!skb) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ hdr = (void *) skb_put(skb, sizeof(*hdr));
+ hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
+ hdr->len = cpu_to_le16(body_len);
+
+ ev = (void *) skb_put(skb, sizeof(*ev));
+ put_unaligned_le16(MGMT_OP_GET_CONNECTIONS, &ev->opcode);
+
+ rp = (void *) skb_put(skb, sizeof(*rp) + (count * sizeof(bdaddr_t)));
+ put_unaligned_le16(dev_id, &rp->index);
+ put_unaligned_le16(count, &rp->conn_count);
+
+ read_lock(&hci_dev_list_lock);
+
+ i = 0;
+ list_for_each(p, &hdev->conn_hash.list) {
+ struct hci_conn *c = list_entry(p, struct hci_conn, list);
+
+ bacpy(&rp->conn[i++], &c->dst);
+ }
+
+ read_unlock(&hci_dev_list_lock);
+
+ if (sock_queue_rcv_skb(sk, skb) < 0)
+ kfree_skb(skb);
+
+ err = 0;
+
+unlock:
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+ return err;
+}
+
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
{
unsigned char *buf;
@@ -1014,6 +1083,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
case MGMT_OP_DISCONNECT:
err = disconnect(sk, buf + sizeof(*hdr), len);
break;
+ case MGMT_OP_GET_CONNECTIONS:
+ err = get_connections(sk, buf + sizeof(*hdr), len);
+ break;
default:
BT_DBG("Unknown op %u", opcode);
err = cmd_status(sk, opcode, 0x01);
OpenPOWER on IntegriCloud