diff options
author | Johan Hedberg <johan.hedberg@nokia.com> | 2010-12-07 17:21:06 -0500 |
---|---|---|
committer | Gustavo F. Padovan <padovan@profusion.mobi> | 2010-12-07 20:03:38 -0500 |
commit | 0381101fd6a73c7d6b545044dc1472d019fc64e3 (patch) | |
tree | 8b7ec25d3e97e3bd7918135fa3fe3b29941e0db4 | |
parent | c02178d22b3ef2d18c38c96151600ee1c7ed94f0 (diff) |
Bluetooth: Add initial Bluetooth Management interface callbacks
Add initial code for handling Bluetooth Management interface messages.
Signed-off-by: Johan Hedberg <johan.hedberg@nokia.com>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Acked-by: Andrei Emeltchenko <andrei.emeltchenko@nokia.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
-rw-r--r-- | include/net/bluetooth/hci_core.h | 3 | ||||
-rw-r--r-- | net/bluetooth/Makefile | 2 | ||||
-rw-r--r-- | net/bluetooth/hci_sock.c | 39 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 99 |
4 files changed, 136 insertions, 7 deletions
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3e3435945980..1992fac7e921 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h | |||
@@ -660,6 +660,9 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data); | |||
660 | /* ----- HCI Sockets ----- */ | 660 | /* ----- HCI Sockets ----- */ |
661 | void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); | 661 | void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); |
662 | 662 | ||
663 | /* Management interface */ | ||
664 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); | ||
665 | |||
663 | /* HCI info for socket */ | 666 | /* HCI info for socket */ |
664 | #define hci_pi(sk) ((struct hci_pinfo *) sk) | 667 | #define hci_pi(sk) ((struct hci_pinfo *) sk) |
665 | 668 | ||
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 7ca1f46a471a..250f954f0213 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile | |||
@@ -10,4 +10,4 @@ obj-$(CONFIG_BT_BNEP) += bnep/ | |||
10 | obj-$(CONFIG_BT_CMTP) += cmtp/ | 10 | obj-$(CONFIG_BT_CMTP) += cmtp/ |
11 | obj-$(CONFIG_BT_HIDP) += hidp/ | 11 | obj-$(CONFIG_BT_HIDP) += hidp/ |
12 | 12 | ||
13 | bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o hci_sysfs.o lib.o | 13 | bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o |
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index b3753bad2a55..207be7abda9f 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c | |||
@@ -49,6 +49,8 @@ | |||
49 | #include <net/bluetooth/bluetooth.h> | 49 | #include <net/bluetooth/bluetooth.h> |
50 | #include <net/bluetooth/hci_core.h> | 50 | #include <net/bluetooth/hci_core.h> |
51 | 51 | ||
52 | static int enable_mgmt; | ||
53 | |||
52 | /* ----- HCI socket interface ----- */ | 54 | /* ----- HCI socket interface ----- */ |
53 | 55 | ||
54 | static inline int hci_test_bit(int nr, void *addr) | 56 | static inline int hci_test_bit(int nr, void *addr) |
@@ -353,25 +355,35 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long a | |||
353 | 355 | ||
354 | static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) | 356 | static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) |
355 | { | 357 | { |
356 | struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr; | 358 | struct sockaddr_hci haddr; |
357 | struct sock *sk = sock->sk; | 359 | struct sock *sk = sock->sk; |
358 | struct hci_dev *hdev = NULL; | 360 | struct hci_dev *hdev = NULL; |
359 | int err = 0; | 361 | int len, err = 0; |
360 | 362 | ||
361 | BT_DBG("sock %p sk %p", sock, sk); | 363 | BT_DBG("sock %p sk %p", sock, sk); |
362 | 364 | ||
363 | if (!haddr || haddr->hci_family != AF_BLUETOOTH) | 365 | if (!addr) |
366 | return -EINVAL; | ||
367 | |||
368 | memset(&haddr, 0, sizeof(haddr)); | ||
369 | len = min_t(unsigned int, sizeof(haddr), addr_len); | ||
370 | memcpy(&haddr, addr, len); | ||
371 | |||
372 | if (haddr.hci_family != AF_BLUETOOTH) | ||
373 | return -EINVAL; | ||
374 | |||
375 | if (haddr.hci_channel != HCI_CHANNEL_RAW && !enable_mgmt) | ||
364 | return -EINVAL; | 376 | return -EINVAL; |
365 | 377 | ||
366 | lock_sock(sk); | 378 | lock_sock(sk); |
367 | 379 | ||
368 | if (hci_pi(sk)->hdev) { | 380 | if (sk->sk_state == BT_BOUND || hci_pi(sk)->hdev) { |
369 | err = -EALREADY; | 381 | err = -EALREADY; |
370 | goto done; | 382 | goto done; |
371 | } | 383 | } |
372 | 384 | ||
373 | if (haddr->hci_dev != HCI_DEV_NONE) { | 385 | if (haddr.hci_dev != HCI_DEV_NONE) { |
374 | hdev = hci_dev_get(haddr->hci_dev); | 386 | hdev = hci_dev_get(haddr.hci_dev); |
375 | if (!hdev) { | 387 | if (!hdev) { |
376 | err = -ENODEV; | 388 | err = -ENODEV; |
377 | goto done; | 389 | goto done; |
@@ -380,6 +392,7 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le | |||
380 | atomic_inc(&hdev->promisc); | 392 | atomic_inc(&hdev->promisc); |
381 | } | 393 | } |
382 | 394 | ||
395 | hci_pi(sk)->channel = haddr.hci_channel; | ||
383 | hci_pi(sk)->hdev = hdev; | 396 | hci_pi(sk)->hdev = hdev; |
384 | sk->sk_state = BT_BOUND; | 397 | sk->sk_state = BT_BOUND; |
385 | 398 | ||
@@ -502,6 +515,17 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, | |||
502 | 515 | ||
503 | lock_sock(sk); | 516 | lock_sock(sk); |
504 | 517 | ||
518 | switch (hci_pi(sk)->channel) { | ||
519 | case HCI_CHANNEL_RAW: | ||
520 | break; | ||
521 | case HCI_CHANNEL_CONTROL: | ||
522 | err = mgmt_control(sk, msg, len); | ||
523 | goto done; | ||
524 | default: | ||
525 | err = -EINVAL; | ||
526 | goto done; | ||
527 | } | ||
528 | |||
505 | hdev = hci_pi(sk)->hdev; | 529 | hdev = hci_pi(sk)->hdev; |
506 | if (!hdev) { | 530 | if (!hdev) { |
507 | err = -EBADFD; | 531 | err = -EBADFD; |
@@ -831,3 +855,6 @@ void __exit hci_sock_cleanup(void) | |||
831 | 855 | ||
832 | proto_unregister(&hci_sk_proto); | 856 | proto_unregister(&hci_sk_proto); |
833 | } | 857 | } |
858 | |||
859 | module_param(enable_mgmt, bool, 0644); | ||
860 | MODULE_PARM_DESC(enable_mgmt, "Enable Management interface"); | ||
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c new file mode 100644 index 000000000000..d15bf676c350 --- /dev/null +++ b/net/bluetooth/mgmt.c | |||
@@ -0,0 +1,99 @@ | |||
1 | /* | ||
2 | BlueZ - Bluetooth protocol stack for Linux | ||
3 | Copyright (C) 2010 Nokia Corporation | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License version 2 as | ||
7 | published by the Free Software Foundation; | ||
8 | |||
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
10 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. | ||
12 | IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY | ||
13 | CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES | ||
14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
17 | |||
18 | ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, | ||
19 | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS | ||
20 | SOFTWARE IS DISCLAIMED. | ||
21 | */ | ||
22 | |||
23 | /* Bluetooth HCI Management interface */ | ||
24 | |||
25 | #include <asm/uaccess.h> | ||
26 | #include <asm/unaligned.h> | ||
27 | |||
28 | #include <net/bluetooth/bluetooth.h> | ||
29 | #include <net/bluetooth/hci_core.h> | ||
30 | #include <net/bluetooth/mgmt.h> | ||
31 | |||
32 | static void cmd_status(struct sock *sk, u16 cmd, u8 status) | ||
33 | { | ||
34 | struct sk_buff *skb; | ||
35 | struct mgmt_hdr *hdr; | ||
36 | struct mgmt_ev_cmd_status *ev; | ||
37 | |||
38 | BT_DBG("sock %p", sk); | ||
39 | |||
40 | skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC); | ||
41 | if (!skb) | ||
42 | return; | ||
43 | |||
44 | hdr = (void *) skb_put(skb, sizeof(*hdr)); | ||
45 | |||
46 | hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); | ||
47 | hdr->len = cpu_to_le16(sizeof(*ev)); | ||
48 | |||
49 | ev = (void *) skb_put(skb, sizeof(*ev)); | ||
50 | ev->status = status; | ||
51 | put_unaligned_le16(cmd, &ev->opcode); | ||
52 | |||
53 | if (sock_queue_rcv_skb(sk, skb) < 0) | ||
54 | kfree_skb(skb); | ||
55 | } | ||
56 | |||
57 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | ||
58 | { | ||
59 | unsigned char *buf; | ||
60 | struct mgmt_hdr *hdr; | ||
61 | u16 opcode, len; | ||
62 | int err; | ||
63 | |||
64 | BT_DBG("got %zu bytes", msglen); | ||
65 | |||
66 | if (msglen < sizeof(*hdr)) | ||
67 | return -EINVAL; | ||
68 | |||
69 | buf = kmalloc(msglen, GFP_ATOMIC); | ||
70 | if (!buf) | ||
71 | return -ENOMEM; | ||
72 | |||
73 | if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) { | ||
74 | err = -EFAULT; | ||
75 | goto done; | ||
76 | } | ||
77 | |||
78 | hdr = (struct mgmt_hdr *) buf; | ||
79 | opcode = get_unaligned_le16(&hdr->opcode); | ||
80 | len = get_unaligned_le16(&hdr->len); | ||
81 | |||
82 | if (len != msglen - sizeof(*hdr)) { | ||
83 | err = -EINVAL; | ||
84 | goto done; | ||
85 | } | ||
86 | |||
87 | switch (opcode) { | ||
88 | default: | ||
89 | BT_DBG("Unknown op %u", opcode); | ||
90 | cmd_status(sk, opcode, 0x01); | ||
91 | break; | ||
92 | } | ||
93 | |||
94 | err = msglen; | ||
95 | |||
96 | done: | ||
97 | kfree(buf); | ||
98 | return err; | ||
99 | } | ||