diff options
| -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 3e343594598..1992fac7e92 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);  /* ----- HCI Sockets ----- */  void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); +/* Management interface */ +int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); +  /* HCI info for socket */  #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 7ca1f46a471..250f954f021 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -10,4 +10,4 @@ obj-$(CONFIG_BT_BNEP)	+= bnep/  obj-$(CONFIG_BT_CMTP)	+= cmtp/  obj-$(CONFIG_BT_HIDP)	+= hidp/ -bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o hci_sysfs.o lib.o +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 b3753bad2a5..207be7abda9 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -49,6 +49,8 @@  #include <net/bluetooth/bluetooth.h>  #include <net/bluetooth/hci_core.h> +static int enable_mgmt; +  /* ----- HCI socket interface ----- */  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  static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)  { -	struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr; +	struct sockaddr_hci haddr;  	struct sock *sk = sock->sk;  	struct hci_dev *hdev = NULL; -	int err = 0; +	int len, err = 0;  	BT_DBG("sock %p sk %p", sock, sk); -	if (!haddr || haddr->hci_family != AF_BLUETOOTH) +	if (!addr) +		return -EINVAL; + +	memset(&haddr, 0, sizeof(haddr)); +	len = min_t(unsigned int, sizeof(haddr), addr_len); +	memcpy(&haddr, addr, len); + +	if (haddr.hci_family != AF_BLUETOOTH) +		return -EINVAL; + +	if (haddr.hci_channel != HCI_CHANNEL_RAW && !enable_mgmt)  		return -EINVAL;  	lock_sock(sk); -	if (hci_pi(sk)->hdev) { +	if (sk->sk_state == BT_BOUND || hci_pi(sk)->hdev) {  		err = -EALREADY;  		goto done;  	} -	if (haddr->hci_dev != HCI_DEV_NONE) { -		hdev = hci_dev_get(haddr->hci_dev); +	if (haddr.hci_dev != HCI_DEV_NONE) { +		hdev = hci_dev_get(haddr.hci_dev);  		if (!hdev) {  			err = -ENODEV;  			goto done; @@ -380,6 +392,7 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le  		atomic_inc(&hdev->promisc);  	} +	hci_pi(sk)->channel = haddr.hci_channel;  	hci_pi(sk)->hdev = hdev;  	sk->sk_state = BT_BOUND; @@ -502,6 +515,17 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,  	lock_sock(sk); +	switch (hci_pi(sk)->channel) { +	case HCI_CHANNEL_RAW: +		break; +	case HCI_CHANNEL_CONTROL: +		err = mgmt_control(sk, msg, len); +		goto done; +	default: +		err = -EINVAL; +		goto done; +	} +  	hdev = hci_pi(sk)->hdev;  	if (!hdev) {  		err = -EBADFD; @@ -831,3 +855,6 @@ void __exit hci_sock_cleanup(void)  	proto_unregister(&hci_sk_proto);  } + +module_param(enable_mgmt, bool, 0644); +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 00000000000..d15bf676c35 --- /dev/null +++ b/net/bluetooth/mgmt.c @@ -0,0 +1,99 @@ +/* +   BlueZ - Bluetooth protocol stack for Linux +   Copyright (C) 2010  Nokia Corporation + +   This program is free software; you can redistribute it and/or modify +   it under the terms of the GNU General Public License version 2 as +   published by the Free Software Foundation; + +   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. +   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY +   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES +   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, +   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS +   SOFTWARE IS DISCLAIMED. +*/ + +/* Bluetooth HCI Management interface */ + +#include <asm/uaccess.h> +#include <asm/unaligned.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> +#include <net/bluetooth/mgmt.h> + +static void cmd_status(struct sock *sk, u16 cmd, u8 status) +{ +	struct sk_buff *skb; +	struct mgmt_hdr *hdr; +	struct mgmt_ev_cmd_status *ev; + +	BT_DBG("sock %p", sk); + +	skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC); +	if (!skb) +		return; + +	hdr = (void *) skb_put(skb, sizeof(*hdr)); + +	hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); +	hdr->len = cpu_to_le16(sizeof(*ev)); + +	ev = (void *) skb_put(skb, sizeof(*ev)); +	ev->status = status; +	put_unaligned_le16(cmd, &ev->opcode); + +	if (sock_queue_rcv_skb(sk, skb) < 0) +		kfree_skb(skb); +} + +int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) +{ +	unsigned char *buf; +	struct mgmt_hdr *hdr; +	u16 opcode, len; +	int err; + +	BT_DBG("got %zu bytes", msglen); + +	if (msglen < sizeof(*hdr)) +		return -EINVAL; + +	buf = kmalloc(msglen, GFP_ATOMIC); +	if (!buf) +		return -ENOMEM; + +	if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) { +		err = -EFAULT; +		goto done; +	} + +	hdr = (struct mgmt_hdr *) buf; +	opcode = get_unaligned_le16(&hdr->opcode); +	len = get_unaligned_le16(&hdr->len); + +	if (len != msglen - sizeof(*hdr)) { +		err = -EINVAL; +		goto done; +	} + +	switch (opcode) { +	default: +		BT_DBG("Unknown op %u", opcode); +		cmd_status(sk, opcode, 0x01); +		break; +	} + +	err = msglen; + +done: +	kfree(buf); +	return err; +}  |