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");