aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2010-12-07 17:21:06 -0500
committerGustavo F. Padovan <padovan@profusion.mobi>2010-12-07 20:03:38 -0500
commit0381101fd6a73c7d6b545044dc1472d019fc64e3 (patch)
tree8b7ec25d3e97e3bd7918135fa3fe3b29941e0db4 /net/bluetooth
parentc02178d22b3ef2d18c38c96151600ee1c7ed94f0 (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>
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/Makefile2
-rw-r--r--net/bluetooth/hci_sock.c39
-rw-r--r--net/bluetooth/mgmt.c99
3 files changed, 133 insertions, 7 deletions
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/
10obj-$(CONFIG_BT_CMTP) += cmtp/ 10obj-$(CONFIG_BT_CMTP) += cmtp/
11obj-$(CONFIG_BT_HIDP) += hidp/ 11obj-$(CONFIG_BT_HIDP) += hidp/
12 12
13bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o hci_sysfs.o lib.o 13bluetooth-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
52static int enable_mgmt;
53
52/* ----- HCI socket interface ----- */ 54/* ----- HCI socket interface ----- */
53 55
54static inline int hci_test_bit(int nr, void *addr) 56static 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
354static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) 356static 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
859module_param(enable_mgmt, bool, 0644);
860MODULE_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
32static 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
57int 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
96done:
97 kfree(buf);
98 return err;
99}