Bluetooth: Introduce hci_dev_test_and_set_flag helper macro
[firefly-linux-kernel-4.4.55.git] / net / bluetooth / hci_sock.c
index 3f8f69239e4116b1f89a247161426c6c3e86485e..b614543b4fe310f11d1ba2f176508bc71f47c191 100644 (file)
@@ -31,6 +31,9 @@
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/hci_mon.h>
 
+static LIST_HEAD(mgmt_chan_list);
+static DEFINE_MUTEX(mgmt_chan_list_lock);
+
 static atomic_t monitor_promisc = ATOMIC_INIT(0);
 
 /* ----- HCI socket interface ----- */
@@ -401,6 +404,56 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event)
        }
 }
 
+static struct hci_mgmt_chan *__hci_mgmt_chan_find(unsigned short channel)
+{
+       struct hci_mgmt_chan *c;
+
+       list_for_each_entry(c, &mgmt_chan_list, list) {
+               if (c->channel == channel)
+                       return c;
+       }
+
+       return NULL;
+}
+
+static struct hci_mgmt_chan *hci_mgmt_chan_find(unsigned short channel)
+{
+       struct hci_mgmt_chan *c;
+
+       mutex_lock(&mgmt_chan_list_lock);
+       c = __hci_mgmt_chan_find(channel);
+       mutex_unlock(&mgmt_chan_list_lock);
+
+       return c;
+}
+
+int hci_mgmt_chan_register(struct hci_mgmt_chan *c)
+{
+       if (c->channel < HCI_CHANNEL_CONTROL)
+               return -EINVAL;
+
+       mutex_lock(&mgmt_chan_list_lock);
+       if (__hci_mgmt_chan_find(c->channel)) {
+               mutex_unlock(&mgmt_chan_list_lock);
+               return -EALREADY;
+       }
+
+       list_add_tail(&c->list, &mgmt_chan_list);
+
+       mutex_unlock(&mgmt_chan_list_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(hci_mgmt_chan_register);
+
+void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c)
+{
+       mutex_lock(&mgmt_chan_list_lock);
+       list_del(&c->list);
+       mutex_unlock(&mgmt_chan_list_lock);
+}
+EXPORT_SYMBOL(hci_mgmt_chan_unregister);
+
 static int hci_sock_release(struct socket *sock)
 {
        struct sock *sk = sock->sk;
@@ -421,7 +474,7 @@ static int hci_sock_release(struct socket *sock)
        if (hdev) {
                if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
                        mgmt_index_added(hdev);
-                       clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
+                       hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
                        hci_dev_close(hdev->id);
                }
 
@@ -481,10 +534,10 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
        if (!hdev)
                return -EBADFD;
 
-       if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
+       if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
                return -EBUSY;
 
-       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+       if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED))
                return -EOPNOTSUPP;
 
        if (hdev->dev_type != HCI_BREDR)
@@ -660,14 +713,14 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
 
                if (test_bit(HCI_UP, &hdev->flags) ||
                    test_bit(HCI_INIT, &hdev->flags) ||
-                   test_bit(HCI_SETUP, &hdev->dev_flags) ||
-                   test_bit(HCI_CONFIG, &hdev->dev_flags)) {
+                   hci_dev_test_flag(hdev, HCI_SETUP) ||
+                   hci_dev_test_flag(hdev, HCI_CONFIG)) {
                        err = -EBUSY;
                        hci_dev_put(hdev);
                        goto done;
                }
 
-               if (test_and_set_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+               if (hci_dev_test_and_set_flag(hdev, HCI_USER_CHANNEL)) {
                        err = -EUSERS;
                        hci_dev_put(hdev);
                        goto done;
@@ -677,7 +730,7 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
 
                err = hci_dev_open(hdev->id);
                if (err) {
-                       clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
+                       hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
                        mgmt_index_added(hdev);
                        hci_dev_put(hdev);
                        goto done;
@@ -688,38 +741,39 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
                hci_pi(sk)->hdev = hdev;
                break;
 
-       case HCI_CHANNEL_CONTROL:
+       case HCI_CHANNEL_MONITOR:
                if (haddr.hci_dev != HCI_DEV_NONE) {
                        err = -EINVAL;
                        goto done;
                }
 
-               if (!capable(CAP_NET_ADMIN)) {
+               if (!capable(CAP_NET_RAW)) {
                        err = -EPERM;
                        goto done;
                }
 
+               send_monitor_replay(sk);
+
+               atomic_inc(&monitor_promisc);
                break;
 
-       case HCI_CHANNEL_MONITOR:
+       default:
+               if (!hci_mgmt_chan_find(haddr.hci_channel)) {
+                       err = -EINVAL;
+                       goto done;
+               }
+
                if (haddr.hci_dev != HCI_DEV_NONE) {
                        err = -EINVAL;
                        goto done;
                }
 
-               if (!capable(CAP_NET_RAW)) {
+               if (!capable(CAP_NET_ADMIN)) {
                        err = -EPERM;
                        goto done;
                }
 
-               send_monitor_replay(sk);
-
-               atomic_inc(&monitor_promisc);
                break;
-
-       default:
-               err = -EINVAL;
-               goto done;
        }
 
 
@@ -799,8 +853,8 @@ static void hci_sock_cmsg(struct sock *sk, struct msghdr *msg,
        }
 }
 
-static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
-                           struct msghdr *msg, size_t len, int flags)
+static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+                           int flags)
 {
        int noblock = flags & MSG_DONTWAIT;
        struct sock *sk = sock->sk;
@@ -833,10 +887,13 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
                hci_sock_cmsg(sk, msg, skb);
                break;
        case HCI_CHANNEL_USER:
-       case HCI_CHANNEL_CONTROL:
        case HCI_CHANNEL_MONITOR:
                sock_recv_timestamp(msg, sk, skb);
                break;
+       default:
+               if (hci_mgmt_chan_find(hci_pi(sk)->channel))
+                       sock_recv_timestamp(msg, sk, skb);
+               break;
        }
 
        skb_free_datagram(sk, skb);
@@ -844,10 +901,11 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
        return err ? : copied;
 }
 
-static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
-                           struct msghdr *msg, size_t len)
+static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
+                           size_t len)
 {
        struct sock *sk = sock->sk;
+       struct hci_mgmt_chan *chan;
        struct hci_dev *hdev;
        struct sk_buff *skb;
        int err;
@@ -869,14 +927,18 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
        case HCI_CHANNEL_RAW:
        case HCI_CHANNEL_USER:
                break;
-       case HCI_CHANNEL_CONTROL:
-               err = mgmt_control(sk, msg, len);
-               goto done;
        case HCI_CHANNEL_MONITOR:
                err = -EOPNOTSUPP;
                goto done;
        default:
-               err = -EINVAL;
+               mutex_lock(&mgmt_chan_list_lock);
+               chan = __hci_mgmt_chan_find(hci_pi(sk)->channel);
+               if (chan)
+                       err = mgmt_control(chan, sk, msg, len);
+               else
+                       err = -EINVAL;
+
+               mutex_unlock(&mgmt_chan_list_lock);
                goto done;
        }
 
@@ -938,7 +1000,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
                        /* Stand-alone HCI commands must be flagged as
                         * single-command requests.
                         */
-                       bt_cb(skb)->req.start = true;
+                       bt_cb(skb)->req_start = 1;
 
                        skb_queue_tail(&hdev->cmd_q, skb);
                        queue_work(hdev->workqueue, &hdev->cmd_work);