Merge commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126' into msm-3.4
AU_LINUX_ANDROID_ICS.04.00.04.00.126 from msm-3.0.
First parent is from google/android-3.4.
* commit 'AU_LINUX_ANDROID_ICS.04.00.04.00.126': (8712 commits)
PRNG: Device tree entry for qrng device.
vidc:1080p: Set video core timeout value for Thumbnail mode
msm: sps: improve the debugging support in SPS driver
board-8064 msm: Overlap secure and non secure video firmware heaps.
msm: clock: Add handoff ops for 7x30 and copper XO clocks
msm_fb: display: Wait for external vsync before DTV IOMMU unmap
msm: Fix ciruclar dependency in debug UART settings
msm: gdsc: Add GDSC regulator driver for msm-copper
defconfig: Enable Mobicore Driver.
mobicore: Add mobicore driver.
mobicore: rename variable to lower case.
mobicore: rename folder.
mobicore: add makefiles
mobicore: initial import of kernel driver
ASoC: msm: Add SLIMBUS_2_RX CPU DAI
board-8064-gpio: Update FUNC for EPM SPI CS
msm_fb: display: Remove chicken bit config during video playback
mmc: msm_sdcc: enable the sanitize capability
msm-fb: display: lm2 writeback support on mpq platfroms
msm_fb: display: Disable LVDS phy & pll during panel off
...
Signed-off-by: Steve Muckle <smuckle@codeaurora.org>
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 5914623..61c2ca1 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -1,6 +1,6 @@
/*
BlueZ - Bluetooth protocol stack for Linux
- Copyright (C) 2000-2001 Qualcomm Incorporated
+ Copyright (c) 2000-2001, 2011, Code Aurora Forum. All rights reserved.
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
@@ -42,14 +42,14 @@
#include <linux/ioctl.h>
#include <net/sock.h>
+#include <asm/system.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/hci_mon.h>
-static atomic_t monitor_promisc = ATOMIC_INIT(0);
+static bool enable_mgmt = 1;
/* ----- HCI socket interface ----- */
@@ -85,20 +85,22 @@
};
/* Send frame to RAW socket */
-void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
+void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb,
+ struct sock *skip_sk)
{
struct sock *sk;
struct hlist_node *node;
- struct sk_buff *skb_copy = NULL;
BT_DBG("hdev %p len %d", hdev, skb->len);
read_lock(&hci_sk_list.lock);
-
sk_for_each(sk, node, &hci_sk_list.head) {
struct hci_filter *flt;
struct sk_buff *nskb;
+ if (sk == skip_sk)
+ continue;
+
if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev)
continue;
@@ -106,9 +108,12 @@
if (skb->sk == sk)
continue;
- if (hci_pi(sk)->channel != HCI_CHANNEL_RAW)
+ if (bt_cb(skb)->channel != hci_pi(sk)->channel)
continue;
+ if (bt_cb(skb)->channel == HCI_CHANNEL_CONTROL)
+ goto clone;
+
/* Apply filter */
flt = &hci_pi(sk)->filter;
@@ -132,301 +137,19 @@
continue;
}
- if (!skb_copy) {
- /* Create a private copy with headroom */
- skb_copy = __pskb_copy(skb, 1, GFP_ATOMIC);
- if (!skb_copy)
- continue;
-
- /* Put type byte before the data */
- memcpy(skb_push(skb_copy, 1), &bt_cb(skb)->pkt_type, 1);
- }
-
- nskb = skb_clone(skb_copy, GFP_ATOMIC);
- if (!nskb)
- continue;
-
- if (sock_queue_rcv_skb(sk, nskb))
- kfree_skb(nskb);
- }
-
- read_unlock(&hci_sk_list.lock);
-
- kfree_skb(skb_copy);
-}
-
-/* Send frame to control socket */
-void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk)
-{
- struct sock *sk;
- struct hlist_node *node;
-
- BT_DBG("len %d", skb->len);
-
- read_lock(&hci_sk_list.lock);
-
- sk_for_each(sk, node, &hci_sk_list.head) {
- struct sk_buff *nskb;
-
- /* Skip the original socket */
- if (sk == skip_sk)
- continue;
-
- if (sk->sk_state != BT_BOUND)
- continue;
-
- if (hci_pi(sk)->channel != HCI_CHANNEL_CONTROL)
- continue;
-
+clone:
nskb = skb_clone(skb, GFP_ATOMIC);
if (!nskb)
continue;
- if (sock_queue_rcv_skb(sk, nskb))
- kfree_skb(nskb);
- }
-
- read_unlock(&hci_sk_list.lock);
-}
-
-/* Send frame to monitor socket */
-void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
-{
- struct sock *sk;
- struct hlist_node *node;
- struct sk_buff *skb_copy = NULL;
- __le16 opcode;
-
- if (!atomic_read(&monitor_promisc))
- return;
-
- BT_DBG("hdev %p len %d", hdev, skb->len);
-
- switch (bt_cb(skb)->pkt_type) {
- case HCI_COMMAND_PKT:
- opcode = __constant_cpu_to_le16(HCI_MON_COMMAND_PKT);
- break;
- case HCI_EVENT_PKT:
- opcode = __constant_cpu_to_le16(HCI_MON_EVENT_PKT);
- break;
- case HCI_ACLDATA_PKT:
- if (bt_cb(skb)->incoming)
- opcode = __constant_cpu_to_le16(HCI_MON_ACL_RX_PKT);
- else
- opcode = __constant_cpu_to_le16(HCI_MON_ACL_TX_PKT);
- break;
- case HCI_SCODATA_PKT:
- if (bt_cb(skb)->incoming)
- opcode = __constant_cpu_to_le16(HCI_MON_SCO_RX_PKT);
- else
- opcode = __constant_cpu_to_le16(HCI_MON_SCO_TX_PKT);
- break;
- default:
- return;
- }
-
- read_lock(&hci_sk_list.lock);
-
- sk_for_each(sk, node, &hci_sk_list.head) {
- struct sk_buff *nskb;
-
- if (sk->sk_state != BT_BOUND)
- continue;
-
- if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR)
- continue;
-
- if (!skb_copy) {
- struct hci_mon_hdr *hdr;
-
- /* Create a private copy with headroom */
- skb_copy = __pskb_copy(skb, HCI_MON_HDR_SIZE, GFP_ATOMIC);
- if (!skb_copy)
- continue;
-
- /* Put header before the data */
- hdr = (void *) skb_push(skb_copy, HCI_MON_HDR_SIZE);
- hdr->opcode = opcode;
- hdr->index = cpu_to_le16(hdev->id);
- hdr->len = cpu_to_le16(skb->len);
- }
-
- nskb = skb_clone(skb_copy, GFP_ATOMIC);
- if (!nskb)
- continue;
+ /* Put type byte before the data */
+ if (bt_cb(skb)->channel == HCI_CHANNEL_RAW)
+ memcpy(skb_push(nskb, 1), &bt_cb(nskb)->pkt_type, 1);
if (sock_queue_rcv_skb(sk, nskb))
kfree_skb(nskb);
}
-
read_unlock(&hci_sk_list.lock);
-
- kfree_skb(skb_copy);
-}
-
-static void send_monitor_event(struct sk_buff *skb)
-{
- struct sock *sk;
- struct hlist_node *node;
-
- BT_DBG("len %d", skb->len);
-
- read_lock(&hci_sk_list.lock);
-
- sk_for_each(sk, node, &hci_sk_list.head) {
- struct sk_buff *nskb;
-
- if (sk->sk_state != BT_BOUND)
- continue;
-
- if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR)
- continue;
-
- nskb = skb_clone(skb, GFP_ATOMIC);
- if (!nskb)
- continue;
-
- if (sock_queue_rcv_skb(sk, nskb))
- kfree_skb(nskb);
- }
-
- read_unlock(&hci_sk_list.lock);
-}
-
-static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
-{
- struct hci_mon_hdr *hdr;
- struct hci_mon_new_index *ni;
- struct sk_buff *skb;
- __le16 opcode;
-
- switch (event) {
- case HCI_DEV_REG:
- skb = bt_skb_alloc(HCI_MON_NEW_INDEX_SIZE, GFP_ATOMIC);
- if (!skb)
- return NULL;
-
- ni = (void *) skb_put(skb, HCI_MON_NEW_INDEX_SIZE);
- ni->type = hdev->dev_type;
- ni->bus = hdev->bus;
- bacpy(&ni->bdaddr, &hdev->bdaddr);
- memcpy(ni->name, hdev->name, 8);
-
- opcode = __constant_cpu_to_le16(HCI_MON_NEW_INDEX);
- break;
-
- case HCI_DEV_UNREG:
- skb = bt_skb_alloc(0, GFP_ATOMIC);
- if (!skb)
- return NULL;
-
- opcode = __constant_cpu_to_le16(HCI_MON_DEL_INDEX);
- break;
-
- default:
- return NULL;
- }
-
- __net_timestamp(skb);
-
- hdr = (void *) skb_push(skb, HCI_MON_HDR_SIZE);
- hdr->opcode = opcode;
- hdr->index = cpu_to_le16(hdev->id);
- hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
-
- return skb;
-}
-
-static void send_monitor_replay(struct sock *sk)
-{
- struct hci_dev *hdev;
-
- read_lock(&hci_dev_list_lock);
-
- list_for_each_entry(hdev, &hci_dev_list, list) {
- struct sk_buff *skb;
-
- skb = create_monitor_event(hdev, HCI_DEV_REG);
- if (!skb)
- continue;
-
- if (sock_queue_rcv_skb(sk, skb))
- kfree_skb(skb);
- }
-
- read_unlock(&hci_dev_list_lock);
-}
-
-/* Generate internal stack event */
-static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
-{
- struct hci_event_hdr *hdr;
- struct hci_ev_stack_internal *ev;
- struct sk_buff *skb;
-
- skb = bt_skb_alloc(HCI_EVENT_HDR_SIZE + sizeof(*ev) + dlen, GFP_ATOMIC);
- if (!skb)
- return;
-
- hdr = (void *) skb_put(skb, HCI_EVENT_HDR_SIZE);
- hdr->evt = HCI_EV_STACK_INTERNAL;
- hdr->plen = sizeof(*ev) + dlen;
-
- ev = (void *) skb_put(skb, sizeof(*ev) + dlen);
- ev->type = type;
- memcpy(ev->data, data, dlen);
-
- bt_cb(skb)->incoming = 1;
- __net_timestamp(skb);
-
- bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
- skb->dev = (void *) hdev;
- hci_send_to_sock(hdev, skb);
- kfree_skb(skb);
-}
-
-void hci_sock_dev_event(struct hci_dev *hdev, int event)
-{
- struct hci_ev_si_device ev;
-
- BT_DBG("hdev %s event %d", hdev->name, event);
-
- /* Send event to monitor */
- if (atomic_read(&monitor_promisc)) {
- struct sk_buff *skb;
-
- skb = create_monitor_event(hdev, event);
- if (skb) {
- send_monitor_event(skb);
- kfree_skb(skb);
- }
- }
-
- /* Send event to sockets */
- ev.event = event;
- ev.dev_id = hdev->id;
- hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev);
-
- if (event == HCI_DEV_UNREG) {
- struct sock *sk;
- struct hlist_node *node;
-
- /* Detach sockets from device */
- read_lock(&hci_sk_list.lock);
- sk_for_each(sk, node, &hci_sk_list.head) {
- bh_lock_sock_nested(sk);
- if (hci_pi(sk)->hdev == hdev) {
- hci_pi(sk)->hdev = NULL;
- sk->sk_err = EPIPE;
- sk->sk_state = BT_OPEN;
- sk->sk_state_change(sk);
-
- hci_dev_put(hdev);
- }
- bh_unlock_sock(sk);
- }
- read_unlock(&hci_sk_list.lock);
- }
}
static int hci_sock_release(struct socket *sock)
@@ -441,9 +164,6 @@
hdev = hci_pi(sk)->hdev;
- if (hci_pi(sk)->channel == HCI_CHANNEL_MONITOR)
- atomic_dec(&monitor_promisc);
-
bt_sock_unlink(&hci_sk_list, sk);
if (hdev) {
@@ -460,38 +180,82 @@
return 0;
}
-static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
+struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
- bdaddr_t bdaddr;
- int err;
+ struct list_head *p;
- if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
- return -EFAULT;
+ list_for_each(p, &hdev->blacklist) {
+ struct bdaddr_list *b;
- hci_dev_lock(hdev);
+ b = list_entry(p, struct bdaddr_list, list);
- err = hci_blacklist_add(hdev, &bdaddr, 0);
+ if (bacmp(bdaddr, &b->bdaddr) == 0)
+ return b;
+ }
- hci_dev_unlock(hdev);
-
- return err;
+ return NULL;
}
-static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
+static int hci_blacklist_add(struct hci_dev *hdev, void __user *arg)
{
bdaddr_t bdaddr;
- int err;
+ struct bdaddr_list *entry;
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
return -EFAULT;
- hci_dev_lock(hdev);
+ if (bacmp(&bdaddr, BDADDR_ANY) == 0)
+ return -EBADF;
- err = hci_blacklist_del(hdev, &bdaddr, 0);
+ if (hci_blacklist_lookup(hdev, &bdaddr))
+ return -EEXIST;
- hci_dev_unlock(hdev);
+ entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
- return err;
+ bacpy(&entry->bdaddr, &bdaddr);
+
+ list_add(&entry->list, &hdev->blacklist);
+
+ return 0;
+}
+
+int hci_blacklist_clear(struct hci_dev *hdev)
+{
+ struct list_head *p, *n;
+
+ list_for_each_safe(p, n, &hdev->blacklist) {
+ struct bdaddr_list *b;
+
+ b = list_entry(p, struct bdaddr_list, list);
+
+ list_del(p);
+ kfree(b);
+ }
+
+ return 0;
+}
+
+static int hci_blacklist_del(struct hci_dev *hdev, void __user *arg)
+{
+ bdaddr_t bdaddr;
+ struct bdaddr_list *entry;
+
+ if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
+ return -EFAULT;
+
+ if (bacmp(&bdaddr, BDADDR_ANY) == 0)
+ return hci_blacklist_clear(hdev);
+
+ entry = hci_blacklist_lookup(hdev, &bdaddr);
+ if (!entry)
+ return -ENOENT;
+
+ list_del(&entry->list);
+ kfree(entry);
+
+ return 0;
}
/* Ioctls that require bound socket */
@@ -526,12 +290,15 @@
case HCIBLOCKADDR:
if (!capable(CAP_NET_ADMIN))
return -EACCES;
- return hci_sock_blacklist_add(hdev, (void __user *) arg);
+ return hci_blacklist_add(hdev, (void __user *) arg);
case HCIUNBLOCKADDR:
if (!capable(CAP_NET_ADMIN))
return -EACCES;
- return hci_sock_blacklist_del(hdev, (void __user *) arg);
+ return hci_blacklist_del(hdev, (void __user *) arg);
+
+ case HCISETAUTHINFO:
+ return hci_set_auth_info(hdev, (void __user *) arg);
default:
if (hdev->ioctl)
@@ -561,7 +328,12 @@
case HCIDEVUP:
if (!capable(CAP_NET_ADMIN))
return -EACCES;
- return hci_dev_open(arg);
+
+ err = hci_dev_open(arg);
+ if (!err || err == -EALREADY)
+ return 0;
+ else
+ return err;
case HCIDEVDOWN:
if (!capable(CAP_NET_ADMIN))
@@ -620,69 +392,31 @@
if (haddr.hci_family != AF_BLUETOOTH)
return -EINVAL;
+ if (haddr.hci_channel > HCI_CHANNEL_CONTROL)
+ return -EINVAL;
+
+ if (haddr.hci_channel == HCI_CHANNEL_CONTROL && !enable_mgmt)
+ return -EINVAL;
+
lock_sock(sk);
- if (sk->sk_state == BT_BOUND) {
+ if (sk->sk_state == BT_BOUND || hci_pi(sk)->hdev) {
err = -EALREADY;
goto done;
}
- switch (haddr.hci_channel) {
- case HCI_CHANNEL_RAW:
- if (hci_pi(sk)->hdev) {
- err = -EALREADY;
+ if (haddr.hci_dev != HCI_DEV_NONE) {
+ hdev = hci_dev_get(haddr.hci_dev);
+ if (!hdev) {
+ err = -ENODEV;
goto done;
}
- if (haddr.hci_dev != HCI_DEV_NONE) {
- hdev = hci_dev_get(haddr.hci_dev);
- if (!hdev) {
- err = -ENODEV;
- goto done;
- }
-
- atomic_inc(&hdev->promisc);
- }
-
- hci_pi(sk)->hdev = hdev;
- break;
-
- case HCI_CHANNEL_CONTROL:
- if (haddr.hci_dev != HCI_DEV_NONE) {
- err = -EINVAL;
- goto done;
- }
-
- if (!capable(CAP_NET_ADMIN)) {
- err = -EPERM;
- goto done;
- }
-
- break;
-
- case HCI_CHANNEL_MONITOR:
- if (haddr.hci_dev != HCI_DEV_NONE) {
- err = -EINVAL;
- goto done;
- }
-
- if (!capable(CAP_NET_RAW)) {
- err = -EPERM;
- goto done;
- }
-
- send_monitor_replay(sk);
-
- atomic_inc(&monitor_promisc);
- break;
-
- default:
- err = -EINVAL;
- goto done;
+ atomic_inc(&hdev->promisc);
}
-
hci_pi(sk)->channel = haddr.hci_channel;
+ hci_pi(sk)->hdev = hdev;
sk->sk_state = BT_BOUND;
done:
@@ -733,8 +467,7 @@
data = &tv;
len = sizeof(tv);
#ifdef CONFIG_COMPAT
- if (!COMPAT_USE_64BIT_TIME &&
- (msg->msg_flags & MSG_CMSG_COMPAT)) {
+ if (msg->msg_flags & MSG_CMSG_COMPAT) {
ctv.tv_sec = tv.tv_sec;
ctv.tv_usec = tv.tv_usec;
data = &ctv;
@@ -777,15 +510,7 @@
skb_reset_transport_header(skb);
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
- switch (hci_pi(sk)->channel) {
- case HCI_CHANNEL_RAW:
- hci_sock_cmsg(sk, msg, skb);
- break;
- case HCI_CHANNEL_CONTROL:
- case HCI_CHANNEL_MONITOR:
- sock_recv_timestamp(msg, sk, skb);
- break;
- }
+ hci_sock_cmsg(sk, msg, skb);
skb_free_datagram(sk, skb);
@@ -798,6 +523,7 @@
struct sock *sk = sock->sk;
struct hci_dev *hdev;
struct sk_buff *skb;
+ int reserve = 0;
int err;
BT_DBG("sock %p sk %p", sock, sk);
@@ -819,9 +545,6 @@
case HCI_CHANNEL_CONTROL:
err = mgmt_control(sk, msg, len);
goto done;
- case HCI_CHANNEL_MONITOR:
- err = -EOPNOTSUPP;
- goto done;
default:
err = -EINVAL;
goto done;
@@ -838,10 +561,18 @@
goto done;
}
- skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err);
+ /* Allocate extra headroom for Qualcomm PAL */
+ if (hdev->dev_type == HCI_AMP && hdev->manufacturer == 0x001d)
+ reserve = BT_SKB_RESERVE_80211;
+
+ skb = bt_skb_send_alloc(sk, len + reserve,
+ msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
goto done;
+ if (reserve)
+ skb_reserve(skb, reserve);
+
if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
err = -EFAULT;
goto drop;
@@ -865,10 +596,10 @@
if (test_bit(HCI_RAW, &hdev->flags) || (ogf == 0x3f)) {
skb_queue_tail(&hdev->raw_q, skb);
- queue_work(hdev->workqueue, &hdev->tx_work);
+ tasklet_schedule(&hdev->tx_task);
} else {
skb_queue_tail(&hdev->cmd_q, skb);
- queue_work(hdev->workqueue, &hdev->cmd_work);
+ tasklet_schedule(&hdev->cmd_task);
}
} else {
if (!capable(CAP_NET_RAW)) {
@@ -877,7 +608,7 @@
}
skb_queue_tail(&hdev->raw_q, skb);
- queue_work(hdev->workqueue, &hdev->tx_work);
+ tasklet_schedule(&hdev->tx_task);
}
err = len;
@@ -901,11 +632,6 @@
lock_sock(sk);
- if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
- err = -EINVAL;
- goto done;
- }
-
switch (optname) {
case HCI_DATA_DIR:
if (get_user(opt, (int __user *)optval)) {
@@ -968,7 +694,6 @@
break;
}
-done:
release_sock(sk);
return err;
}
@@ -977,20 +702,11 @@
{
struct hci_ufilter uf;
struct sock *sk = sock->sk;
- int len, opt, err = 0;
-
- BT_DBG("sk %p, opt %d", sk, optname);
+ int len, opt;
if (get_user(len, optlen))
return -EFAULT;
- lock_sock(sk);
-
- if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) {
- err = -EINVAL;
- goto done;
- }
-
switch (optname) {
case HCI_DATA_DIR:
if (hci_pi(sk)->cmsg_mask & HCI_CMSG_DIR)
@@ -999,7 +715,7 @@
opt = 0;
if (put_user(opt, optval))
- err = -EFAULT;
+ return -EFAULT;
break;
case HCI_TIME_STAMP:
@@ -1009,7 +725,7 @@
opt = 0;
if (put_user(opt, optval))
- err = -EFAULT;
+ return -EFAULT;
break;
case HCI_FILTER:
@@ -1024,17 +740,15 @@
len = min_t(unsigned int, len, sizeof(uf));
if (copy_to_user(optval, &uf, len))
- err = -EFAULT;
+ return -EFAULT;
break;
default:
- err = -ENOPROTOOPT;
+ return -ENOPROTOOPT;
break;
}
-done:
- release_sock(sk);
- return err;
+ return 0;
}
static const struct proto_ops hci_sock_ops = {
@@ -1092,12 +806,54 @@
return 0;
}
+static int hci_sock_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ struct hci_dev *hdev = (struct hci_dev *) ptr;
+ struct hci_ev_si_device ev;
+
+ BT_DBG("hdev %s event %ld", hdev->name, event);
+
+ /* Send event to sockets */
+ ev.event = event;
+ ev.dev_id = hdev->id;
+ hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev);
+
+ if (event == HCI_DEV_UNREG) {
+ struct sock *sk;
+ struct hlist_node *node;
+
+ /* Detach sockets from device */
+ read_lock(&hci_sk_list.lock);
+ sk_for_each(sk, node, &hci_sk_list.head) {
+ local_bh_disable();
+ bh_lock_sock_nested(sk);
+ if (hci_pi(sk)->hdev == hdev) {
+ hci_pi(sk)->hdev = NULL;
+ sk->sk_err = EPIPE;
+ sk->sk_state = BT_OPEN;
+ sk->sk_state_change(sk);
+
+ hci_dev_put(hdev);
+ }
+ bh_unlock_sock(sk);
+ local_bh_enable();
+ }
+ read_unlock(&hci_sk_list.lock);
+ }
+
+ return NOTIFY_DONE;
+}
+
static const struct net_proto_family hci_sock_family_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.create = hci_sock_create,
};
+static struct notifier_block hci_sock_nblock = {
+ .notifier_call = hci_sock_dev_event
+};
+
int __init hci_sock_init(void)
{
int err;
@@ -1110,6 +866,8 @@
if (err < 0)
goto error;
+ hci_register_notifier(&hci_sock_nblock);
+
BT_INFO("HCI socket layer initialized");
return 0;
@@ -1125,5 +883,10 @@
if (bt_sock_unregister(BTPROTO_HCI) < 0)
BT_ERR("HCI socket unregistration failed");
+ hci_unregister_notifier(&hci_sock_nblock);
+
proto_unregister(&hci_sk_proto);
}
+
+module_param(enable_mgmt, bool, 0644);
+MODULE_PARM_DESC(enable_mgmt, "Enable Management interface");