aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/bluetooth/mgmt.h19
-rw-r--r--net/bluetooth/mgmt.c90
2 files changed, 101 insertions, 8 deletions
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index c2b4c83ab175..70985aacc14b 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -39,6 +39,25 @@ struct mgmt_rp_read_index_list {
39 __le16 index[0]; 39 __le16 index[0];
40} __packed; 40} __packed;
41 41
42#define MGMT_OP_READ_INFO 0x0004
43struct mgmt_cp_read_info {
44 __le16 index;
45} __packed;
46struct mgmt_rp_read_info {
47 __le16 index;
48 __u8 type;
49 __u8 powered;
50 __u8 discoverable;
51 __u8 pairable;
52 __u8 sec_mode;
53 bdaddr_t bdaddr;
54 __u8 dev_class[3];
55 __u8 features[8];
56 __u16 manufacturer;
57 __u8 hci_ver;
58 __u16 hci_rev;
59} __packed;
60
42#define MGMT_EV_CMD_COMPLETE 0x0001 61#define MGMT_EV_CMD_COMPLETE 0x0001
43struct mgmt_ev_cmd_complete { 62struct mgmt_ev_cmd_complete {
44 __le16 opcode; 63 __le16 opcode;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 7a8e321875c9..d6c5a32de0b6 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -32,6 +32,33 @@
32#define MGMT_VERSION 0 32#define MGMT_VERSION 0
33#define MGMT_REVISION 1 33#define MGMT_REVISION 1
34 34
35static int cmd_status(struct sock *sk, u16 cmd, u8 status)
36{
37 struct sk_buff *skb;
38 struct mgmt_hdr *hdr;
39 struct mgmt_ev_cmd_status *ev;
40
41 BT_DBG("sock %p", sk);
42
43 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC);
44 if (!skb)
45 return -ENOMEM;
46
47 hdr = (void *) skb_put(skb, sizeof(*hdr));
48
49 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
50 hdr->len = cpu_to_le16(sizeof(*ev));
51
52 ev = (void *) skb_put(skb, sizeof(*ev));
53 ev->status = status;
54 put_unaligned_le16(cmd, &ev->opcode);
55
56 if (sock_queue_rcv_skb(sk, skb) < 0)
57 kfree_skb(skb);
58
59 return 0;
60}
61
35static int read_version(struct sock *sk) 62static int read_version(struct sock *sk)
36{ 63{
37 struct sk_buff *skb; 64 struct sk_buff *skb;
@@ -112,26 +139,70 @@ static int read_index_list(struct sock *sk)
112 return 0; 139 return 0;
113} 140}
114 141
115static int cmd_status(struct sock *sk, u16 cmd, u8 status) 142static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
116{ 143{
117 struct sk_buff *skb; 144 struct sk_buff *skb;
118 struct mgmt_hdr *hdr; 145 struct mgmt_hdr *hdr;
119 struct mgmt_ev_cmd_status *ev; 146 struct mgmt_ev_cmd_complete *ev;
147 struct mgmt_rp_read_info *rp;
148 struct mgmt_cp_read_info *cp;
149 struct hci_dev *hdev;
150 u16 dev_id;
120 151
121 BT_DBG("sock %p", sk); 152 BT_DBG("sock %p", sk);
122 153
123 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC); 154 if (len != 2)
155 return cmd_status(sk, MGMT_OP_READ_INFO, EINVAL);
156
157 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
124 if (!skb) 158 if (!skb)
125 return -ENOMEM; 159 return -ENOMEM;
126 160
127 hdr = (void *) skb_put(skb, sizeof(*hdr)); 161 hdr = (void *) skb_put(skb, sizeof(*hdr));
128 162 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
129 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); 163 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
130 hdr->len = cpu_to_le16(sizeof(*ev));
131 164
132 ev = (void *) skb_put(skb, sizeof(*ev)); 165 ev = (void *) skb_put(skb, sizeof(*ev));
133 ev->status = status; 166 put_unaligned_le16(MGMT_OP_READ_INFO, &ev->opcode);
134 put_unaligned_le16(cmd, &ev->opcode); 167
168 rp = (void *) skb_put(skb, sizeof(*rp));
169
170 cp = (void *) data;
171 dev_id = get_unaligned_le16(&cp->index);
172
173 BT_DBG("request for hci%u", dev_id);
174
175 hdev = hci_dev_get(dev_id);
176 if (!hdev) {
177 kfree_skb(skb);
178 return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV);
179 }
180
181 hci_dev_lock_bh(hdev);
182
183 put_unaligned_le16(hdev->id, &rp->index);
184 rp->type = hdev->dev_type;
185
186 rp->powered = test_bit(HCI_UP, &hdev->flags);
187 rp->discoverable = test_bit(HCI_ISCAN, &hdev->flags);
188 rp->pairable = test_bit(HCI_PSCAN, &hdev->flags);
189
190 if (test_bit(HCI_AUTH, &hdev->flags))
191 rp->sec_mode = 3;
192 else if (hdev->ssp_mode > 0)
193 rp->sec_mode = 4;
194 else
195 rp->sec_mode = 2;
196
197 bacpy(&rp->bdaddr, &hdev->bdaddr);
198 memcpy(rp->features, hdev->features, 8);
199 memcpy(rp->dev_class, hdev->dev_class, 3);
200 put_unaligned_le16(hdev->manufacturer, &rp->manufacturer);
201 rp->hci_ver = hdev->hci_ver;
202 put_unaligned_le16(hdev->hci_rev, &rp->hci_rev);
203
204 hci_dev_unlock_bh(hdev);
205 hci_dev_put(hdev);
135 206
136 if (sock_queue_rcv_skb(sk, skb) < 0) 207 if (sock_queue_rcv_skb(sk, skb) < 0)
137 kfree_skb(skb); 208 kfree_skb(skb);
@@ -176,6 +247,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
176 case MGMT_OP_READ_INDEX_LIST: 247 case MGMT_OP_READ_INDEX_LIST:
177 err = read_index_list(sk); 248 err = read_index_list(sk);
178 break; 249 break;
250 case MGMT_OP_READ_INFO:
251 err = read_controller_info(sk, buf + sizeof(*hdr), len);
252 break;
179 default: 253 default:
180 BT_DBG("Unknown op %u", opcode); 254 BT_DBG("Unknown op %u", opcode);
181 err = cmd_status(sk, opcode, 0x01); 255 err = cmd_status(sk, opcode, 0x01);