Bluetooth: Add mgmt HCI channel registration API
This patch adds an API for registering HCI channels with mgmt-like semantics. For now the only user will be HCI_CHANNEL_CONTROL, but e.g. 6lowpan is intended to use this as well in the future. Signed-off-by: Johan Hedberg <johan.hedberg@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
Родитель
93690c227a
Коммит
801c1e8da5
|
@ -1273,6 +1273,23 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb);
|
||||||
|
|
||||||
void hci_sock_dev_event(struct hci_dev *hdev, int event);
|
void hci_sock_dev_event(struct hci_dev *hdev, int event);
|
||||||
|
|
||||||
|
struct hci_mgmt_handler {
|
||||||
|
int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
|
||||||
|
u16 data_len);
|
||||||
|
bool var_len;
|
||||||
|
size_t data_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hci_mgmt_chan {
|
||||||
|
struct list_head list;
|
||||||
|
unsigned short channel;
|
||||||
|
size_t handler_count;
|
||||||
|
const struct hci_mgmt_handler *handlers;
|
||||||
|
};
|
||||||
|
|
||||||
|
int hci_mgmt_chan_register(struct hci_mgmt_chan *c);
|
||||||
|
void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c);
|
||||||
|
|
||||||
/* Management interface */
|
/* Management interface */
|
||||||
#define DISCOV_TYPE_BREDR (BIT(BDADDR_BREDR))
|
#define DISCOV_TYPE_BREDR (BIT(BDADDR_BREDR))
|
||||||
#define DISCOV_TYPE_LE (BIT(BDADDR_LE_PUBLIC) | \
|
#define DISCOV_TYPE_LE (BIT(BDADDR_LE_PUBLIC) | \
|
||||||
|
|
|
@ -31,6 +31,9 @@
|
||||||
#include <net/bluetooth/hci_core.h>
|
#include <net/bluetooth/hci_core.h>
|
||||||
#include <net/bluetooth/hci_mon.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);
|
static atomic_t monitor_promisc = ATOMIC_INIT(0);
|
||||||
|
|
||||||
/* ----- HCI socket interface ----- */
|
/* ----- 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)
|
static int hci_sock_release(struct socket *sock)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
|
@ -718,8 +771,22 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err = -EINVAL;
|
if (!hci_mgmt_chan_find(haddr.hci_channel)) {
|
||||||
goto done;
|
err = -EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haddr.hci_dev != HCI_DEV_NONE) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!capable(CAP_NET_ADMIN)) {
|
||||||
|
err = -EPERM;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -837,6 +904,10 @@ static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
|
||||||
case HCI_CHANNEL_MONITOR:
|
case HCI_CHANNEL_MONITOR:
|
||||||
sock_recv_timestamp(msg, sk, skb);
|
sock_recv_timestamp(msg, sk, skb);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
if (hci_mgmt_chan_find(hci_pi(sk)->channel))
|
||||||
|
sock_recv_timestamp(msg, sk, skb);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
skb_free_datagram(sk, skb);
|
skb_free_datagram(sk, skb);
|
||||||
|
@ -848,6 +919,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||||
size_t len)
|
size_t len)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
|
struct hci_mgmt_chan *chan;
|
||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
int err;
|
int err;
|
||||||
|
@ -876,7 +948,14 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||||
err = -EOPNOTSUPP;
|
err = -EOPNOTSUPP;
|
||||||
goto done;
|
goto done;
|
||||||
default:
|
default:
|
||||||
err = -EINVAL;
|
mutex_lock(&mgmt_chan_list_lock);
|
||||||
|
chan = __hci_mgmt_chan_find(hci_pi(sk)->channel);
|
||||||
|
if (chan)
|
||||||
|
err = -ENOSYS; /* FIXME: call handler */
|
||||||
|
else
|
||||||
|
err = -EINVAL;
|
||||||
|
|
||||||
|
mutex_unlock(&mgmt_chan_list_lock);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче