aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2010-12-16 03:17:38 -0500
committerGustavo F. Padovan <padovan@profusion.mobi>2011-02-07 22:40:04 -0500
commiteec8d2bcc841ae44edcde9660ff21144a2016053 (patch)
tree32756650c2932b07b4c4fcd7184eadaed766e3b3 /net
parent5add6af8fcbce269cac2457584c0ebfda055474a (diff)
Bluetooth: Add support for set_powered management command
This patch adds a set_powered command to the management interface through which the powered state of local adapters can be controlled. Signed-off-by: Johan Hedberg <johan.hedberg@nokia.com> Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/hci_core.c4
-rw-r--r--net/bluetooth/hci_event.c2
-rw-r--r--net/bluetooth/hci_sock.c6
-rw-r--r--net/bluetooth/mgmt.c200
4 files changed, 203 insertions, 9 deletions
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index c5a78e797bc2..dfc4ef90deca 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1377,7 +1377,7 @@ static int hci_send_frame(struct sk_buff *skb)
1377 /* Time stamp */ 1377 /* Time stamp */
1378 __net_timestamp(skb); 1378 __net_timestamp(skb);
1379 1379
1380 hci_send_to_sock(hdev, skb); 1380 hci_send_to_sock(hdev, skb, NULL);
1381 } 1381 }
1382 1382
1383 /* Get rid of skb owner, prior to sending to the driver. */ 1383 /* Get rid of skb owner, prior to sending to the driver. */
@@ -1767,7 +1767,7 @@ static void hci_rx_task(unsigned long arg)
1767 while ((skb = skb_dequeue(&hdev->rx_q))) { 1767 while ((skb = skb_dequeue(&hdev->rx_q))) {
1768 if (atomic_read(&hdev->promisc)) { 1768 if (atomic_read(&hdev->promisc)) {
1769 /* Send copy to the sockets */ 1769 /* Send copy to the sockets */
1770 hci_send_to_sock(hdev, skb); 1770 hci_send_to_sock(hdev, skb, NULL);
1771 } 1771 }
1772 1772
1773 if (test_bit(HCI_RAW, &hdev->flags)) { 1773 if (test_bit(HCI_RAW, &hdev->flags)) {
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index a290854fdaa6..d42fb35309b5 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2083,6 +2083,6 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
2083 2083
2084 bt_cb(skb)->pkt_type = HCI_EVENT_PKT; 2084 bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
2085 skb->dev = (void *) hdev; 2085 skb->dev = (void *) hdev;
2086 hci_send_to_sock(hdev, skb); 2086 hci_send_to_sock(hdev, skb, NULL);
2087 kfree_skb(skb); 2087 kfree_skb(skb);
2088} 2088}
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 29827c77f6ce..d50e96136608 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -85,7 +85,8 @@ static struct bt_sock_list hci_sk_list = {
85}; 85};
86 86
87/* Send frame to RAW socket */ 87/* Send frame to RAW socket */
88void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) 88void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb,
89 struct sock *skip_sk)
89{ 90{
90 struct sock *sk; 91 struct sock *sk;
91 struct hlist_node *node; 92 struct hlist_node *node;
@@ -97,6 +98,9 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
97 struct hci_filter *flt; 98 struct hci_filter *flt;
98 struct sk_buff *nskb; 99 struct sk_buff *nskb;
99 100
101 if (sk == skip_sk)
102 continue;
103
100 if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev) 104 if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev)
101 continue; 105 continue;
102 106
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index f746e19ebec0..b65b6ca08463 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -32,6 +32,16 @@
32#define MGMT_VERSION 0 32#define MGMT_VERSION 0
33#define MGMT_REVISION 1 33#define MGMT_REVISION 1
34 34
35struct pending_cmd {
36 struct list_head list;
37 __u16 opcode;
38 int index;
39 void *cmd;
40 struct sock *sk;
41};
42
43LIST_HEAD(cmd_list);
44
35static int cmd_status(struct sock *sk, u16 cmd, u8 status) 45static int cmd_status(struct sock *sk, u16 cmd, u8 status)
36{ 46{
37 struct sk_buff *skb; 47 struct sk_buff *skb;
@@ -220,6 +230,129 @@ static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
220 return 0; 230 return 0;
221} 231}
222 232
233static void mgmt_pending_free(struct pending_cmd *cmd)
234{
235 sock_put(cmd->sk);
236 kfree(cmd->cmd);
237 kfree(cmd);
238}
239
240static int mgmt_pending_add(struct sock *sk, u16 opcode, int index,
241 void *data, u16 len)
242{
243 struct pending_cmd *cmd;
244
245 cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
246 if (!cmd)
247 return -ENOMEM;
248
249 cmd->opcode = opcode;
250 cmd->index = index;
251
252 cmd->cmd = kmalloc(len, GFP_ATOMIC);
253 if (!cmd->cmd) {
254 kfree(cmd);
255 return -ENOMEM;
256 }
257
258 memcpy(cmd->cmd, data, len);
259
260 cmd->sk = sk;
261 sock_hold(sk);
262
263 list_add(&cmd->list, &cmd_list);
264
265 return 0;
266}
267
268static void mgmt_pending_foreach(u16 opcode, int index,
269 void (*cb)(struct pending_cmd *cmd, void *data),
270 void *data)
271{
272 struct list_head *p, *n;
273
274 list_for_each_safe(p, n, &cmd_list) {
275 struct pending_cmd *cmd;
276
277 cmd = list_entry(p, struct pending_cmd, list);
278
279 if (cmd->opcode != opcode)
280 continue;
281
282 if (index >= 0 && cmd->index != index)
283 continue;
284
285 cb(cmd, data);
286 }
287}
288
289static struct pending_cmd *mgmt_pending_find(u16 opcode, int index)
290{
291 struct list_head *p;
292
293 list_for_each(p, &cmd_list) {
294 struct pending_cmd *cmd;
295
296 cmd = list_entry(p, struct pending_cmd, list);
297
298 if (cmd->opcode != opcode)
299 continue;
300
301 if (index >= 0 && cmd->index != index)
302 continue;
303
304 return cmd;
305 }
306
307 return NULL;
308}
309
310static int set_powered(struct sock *sk, unsigned char *data, u16 len)
311{
312 struct mgmt_cp_set_powered *cp;
313 struct hci_dev *hdev;
314 u16 dev_id;
315 int ret, up;
316
317 cp = (void *) data;
318 dev_id = get_unaligned_le16(&cp->index);
319
320 BT_DBG("request for hci%u", dev_id);
321
322 hdev = hci_dev_get(dev_id);
323 if (!hdev)
324 return cmd_status(sk, MGMT_OP_SET_POWERED, ENODEV);
325
326 hci_dev_lock_bh(hdev);
327
328 up = test_bit(HCI_UP, &hdev->flags);
329 if ((cp->powered && up) || (!cp->powered && !up)) {
330 ret = cmd_status(sk, MGMT_OP_SET_POWERED, EALREADY);
331 goto failed;
332 }
333
334 if (mgmt_pending_find(MGMT_OP_SET_POWERED, dev_id)) {
335 ret = cmd_status(sk, MGMT_OP_SET_POWERED, EBUSY);
336 goto failed;
337 }
338
339 ret = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, dev_id, data, len);
340 if (ret < 0)
341 goto failed;
342
343 if (cp->powered)
344 queue_work(hdev->workqueue, &hdev->power_on);
345 else
346 queue_work(hdev->workqueue, &hdev->power_off);
347
348 ret = 0;
349
350failed:
351 hci_dev_unlock_bh(hdev);
352 hci_dev_put(hdev);
353 return ret;
354}
355
223int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) 356int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
224{ 357{
225 unsigned char *buf; 358 unsigned char *buf;
@@ -260,6 +393,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
260 case MGMT_OP_READ_INFO: 393 case MGMT_OP_READ_INFO:
261 err = read_controller_info(sk, buf + sizeof(*hdr), len); 394 err = read_controller_info(sk, buf + sizeof(*hdr), len);
262 break; 395 break;
396 case MGMT_OP_SET_POWERED:
397 err = set_powered(sk, buf + sizeof(*hdr), len);
398 break;
263 default: 399 default:
264 BT_DBG("Unknown op %u", opcode); 400 BT_DBG("Unknown op %u", opcode);
265 err = cmd_status(sk, opcode, 0x01); 401 err = cmd_status(sk, opcode, 0x01);
@@ -276,7 +412,7 @@ done:
276 return err; 412 return err;
277} 413}
278 414
279static int mgmt_event(u16 event, void *data, u16 data_len) 415static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk)
280{ 416{
281 struct sk_buff *skb; 417 struct sk_buff *skb;
282 struct mgmt_hdr *hdr; 418 struct mgmt_hdr *hdr;
@@ -293,7 +429,7 @@ static int mgmt_event(u16 event, void *data, u16 data_len)
293 429
294 memcpy(skb_put(skb, data_len), data, data_len); 430 memcpy(skb_put(skb, data_len), data, data_len);
295 431
296 hci_send_to_sock(NULL, skb); 432 hci_send_to_sock(NULL, skb, skip_sk);
297 kfree_skb(skb); 433 kfree_skb(skb);
298 434
299 return 0; 435 return 0;
@@ -305,7 +441,7 @@ int mgmt_index_added(u16 index)
305 441
306 put_unaligned_le16(index, &ev.index); 442 put_unaligned_le16(index, &ev.index);
307 443
308 return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev)); 444 return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev), NULL);
309} 445}
310 446
311int mgmt_index_removed(u16 index) 447int mgmt_index_removed(u16 index)
@@ -314,15 +450,69 @@ int mgmt_index_removed(u16 index)
314 450
315 put_unaligned_le16(index, &ev.index); 451 put_unaligned_le16(index, &ev.index);
316 452
317 return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev)); 453 return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev), NULL);
454}
455
456struct powered_lookup {
457 u8 powered;
458 struct sock *sk;
459};
460
461static void power_rsp(struct pending_cmd *cmd, void *data)
462{
463 struct mgmt_hdr *hdr;
464 struct mgmt_ev_cmd_complete *ev;
465 struct mgmt_rp_set_powered *rp;
466 struct mgmt_cp_set_powered *cp = cmd->cmd;
467 struct sk_buff *skb;
468 struct powered_lookup *match = data;
469
470 if (cp->powered != match->powered)
471 return;
472
473 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
474 if (!skb)
475 return;
476
477 hdr = (void *) skb_put(skb, sizeof(*hdr));
478 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
479 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
480
481 ev = (void *) skb_put(skb, sizeof(*ev));
482 put_unaligned_le16(cmd->opcode, &ev->opcode);
483
484 rp = (void *) skb_put(skb, sizeof(*rp));
485 put_unaligned_le16(cmd->index, &rp->index);
486 rp->powered = cp->powered;
487
488 if (sock_queue_rcv_skb(cmd->sk, skb) < 0)
489 kfree_skb(skb);
490
491 list_del(&cmd->list);
492
493 if (match->sk == NULL) {
494 match->sk = cmd->sk;
495 sock_hold(match->sk);
496 }
497
498 mgmt_pending_free(cmd);
318} 499}
319 500
320int mgmt_powered(u16 index, u8 powered) 501int mgmt_powered(u16 index, u8 powered)
321{ 502{
322 struct mgmt_ev_powered ev; 503 struct mgmt_ev_powered ev;
504 struct powered_lookup match = { powered, NULL };
505 int ret;
323 506
324 put_unaligned_le16(index, &ev.index); 507 put_unaligned_le16(index, &ev.index);
325 ev.powered = powered; 508 ev.powered = powered;
326 509
327 return mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev)); 510 mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, power_rsp, &match);
511
512 ret = mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev), match.sk);
513
514 if (match.sk)
515 sock_put(match.sk);
516
517 return ret;
328} 518}