diff options
| -rw-r--r-- | include/net/bluetooth/mgmt.h | 10 | ||||
| -rw-r--r-- | net/bluetooth/mgmt.c | 72 |
2 files changed, 82 insertions, 0 deletions
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 1d822f2c0f1a..3d8d589fa559 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h | |||
| @@ -130,6 +130,16 @@ struct mgmt_rp_disconnect { | |||
| 130 | bdaddr_t bdaddr; | 130 | bdaddr_t bdaddr; |
| 131 | } __packed; | 131 | } __packed; |
| 132 | 132 | ||
| 133 | #define MGMT_OP_GET_CONNECTIONS 0x0010 | ||
| 134 | struct mgmt_cp_get_connections { | ||
| 135 | __le16 index; | ||
| 136 | } __packed; | ||
| 137 | struct mgmt_rp_get_connections { | ||
| 138 | __le16 index; | ||
| 139 | __le16 conn_count; | ||
| 140 | bdaddr_t conn[0]; | ||
| 141 | } __packed; | ||
| 142 | |||
| 133 | #define MGMT_EV_CMD_COMPLETE 0x0001 | 143 | #define MGMT_EV_CMD_COMPLETE 0x0001 |
| 134 | struct mgmt_ev_cmd_complete { | 144 | struct mgmt_ev_cmd_complete { |
| 135 | __le16 opcode; | 145 | __le16 opcode; |
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9fb989f4216e..8f4f47e9d5c9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
| @@ -941,6 +941,75 @@ failed: | |||
| 941 | return err; | 941 | return err; |
| 942 | } | 942 | } |
| 943 | 943 | ||
| 944 | static int get_connections(struct sock *sk, unsigned char *data, u16 len) | ||
| 945 | { | ||
| 946 | struct sk_buff *skb; | ||
| 947 | struct mgmt_hdr *hdr; | ||
| 948 | struct mgmt_cp_get_connections *cp; | ||
| 949 | struct mgmt_ev_cmd_complete *ev; | ||
| 950 | struct mgmt_rp_get_connections *rp; | ||
| 951 | struct hci_dev *hdev; | ||
| 952 | struct list_head *p; | ||
| 953 | size_t body_len; | ||
| 954 | u16 dev_id, count; | ||
| 955 | int i, err; | ||
| 956 | |||
| 957 | BT_DBG(""); | ||
| 958 | |||
| 959 | cp = (void *) data; | ||
| 960 | dev_id = get_unaligned_le16(&cp->index); | ||
| 961 | |||
| 962 | hdev = hci_dev_get(dev_id); | ||
| 963 | if (!hdev) | ||
| 964 | return cmd_status(sk, MGMT_OP_GET_CONNECTIONS, ENODEV); | ||
| 965 | |||
| 966 | hci_dev_lock_bh(hdev); | ||
| 967 | |||
| 968 | count = 0; | ||
| 969 | list_for_each(p, &hdev->conn_hash.list) { | ||
| 970 | count++; | ||
| 971 | } | ||
| 972 | |||
| 973 | body_len = sizeof(*ev) + sizeof(*rp) + (count * sizeof(bdaddr_t)); | ||
| 974 | skb = alloc_skb(sizeof(*hdr) + body_len, GFP_ATOMIC); | ||
| 975 | if (!skb) { | ||
| 976 | err = -ENOMEM; | ||
| 977 | goto unlock; | ||
| 978 | } | ||
| 979 | |||
| 980 | hdr = (void *) skb_put(skb, sizeof(*hdr)); | ||
| 981 | hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); | ||
| 982 | hdr->len = cpu_to_le16(body_len); | ||
| 983 | |||
| 984 | ev = (void *) skb_put(skb, sizeof(*ev)); | ||
| 985 | put_unaligned_le16(MGMT_OP_GET_CONNECTIONS, &ev->opcode); | ||
| 986 | |||
| 987 | rp = (void *) skb_put(skb, sizeof(*rp) + (count * sizeof(bdaddr_t))); | ||
| 988 | put_unaligned_le16(dev_id, &rp->index); | ||
| 989 | put_unaligned_le16(count, &rp->conn_count); | ||
| 990 | |||
| 991 | read_lock(&hci_dev_list_lock); | ||
| 992 | |||
| 993 | i = 0; | ||
| 994 | list_for_each(p, &hdev->conn_hash.list) { | ||
| 995 | struct hci_conn *c = list_entry(p, struct hci_conn, list); | ||
| 996 | |||
| 997 | bacpy(&rp->conn[i++], &c->dst); | ||
| 998 | } | ||
| 999 | |||
| 1000 | read_unlock(&hci_dev_list_lock); | ||
| 1001 | |||
| 1002 | if (sock_queue_rcv_skb(sk, skb) < 0) | ||
| 1003 | kfree_skb(skb); | ||
| 1004 | |||
| 1005 | err = 0; | ||
| 1006 | |||
| 1007 | unlock: | ||
| 1008 | hci_dev_unlock_bh(hdev); | ||
| 1009 | hci_dev_put(hdev); | ||
| 1010 | return err; | ||
| 1011 | } | ||
| 1012 | |||
| 944 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | 1013 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) |
| 945 | { | 1014 | { |
| 946 | unsigned char *buf; | 1015 | unsigned char *buf; |
| @@ -1014,6 +1083,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | |||
| 1014 | case MGMT_OP_DISCONNECT: | 1083 | case MGMT_OP_DISCONNECT: |
| 1015 | err = disconnect(sk, buf + sizeof(*hdr), len); | 1084 | err = disconnect(sk, buf + sizeof(*hdr), len); |
| 1016 | break; | 1085 | break; |
| 1086 | case MGMT_OP_GET_CONNECTIONS: | ||
| 1087 | err = get_connections(sk, buf + sizeof(*hdr), len); | ||
| 1088 | break; | ||
| 1017 | default: | 1089 | default: |
| 1018 | BT_DBG("Unknown op %u", opcode); | 1090 | BT_DBG("Unknown op %u", opcode); |
| 1019 | err = cmd_status(sk, opcode, 0x01); | 1091 | err = cmd_status(sk, opcode, 0x01); |
