Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH BlueZ 4/4] android/A2DP: Notify when audio state change to suspend
From: Luiz Augusto von Dentz @ 2014-01-17 13:33 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1389965587-20066-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

---
 android/a2dp.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/android/a2dp.c b/android/a2dp.c
index 12a8a69..8ec03c8 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -1007,6 +1007,8 @@ static gboolean sep_suspend_ind(struct avdtp *session,
 		return FALSE;
 	}
 
+	bt_audio_notify_state(setup, HAL_AUDIO_SUSPEND);
+
 	return TRUE;
 }
 
-- 
1.8.4.2


^ permalink raw reply related

* [PATCH BlueZ 3/4] android/A2DP: Notify when audio state change to stopped
From: Luiz Augusto von Dentz @ 2014-01-17 13:33 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1389965587-20066-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

---
 android/a2dp.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/android/a2dp.c b/android/a2dp.c
index 515cf17..12a8a69 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -1106,13 +1106,23 @@ static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 			void *user_data)
 {
 	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
 
 	DBG("");
 
-	if (!err)
+	if (err) {
+		setup_remove_by_id(endpoint->id);
 		return;
+	}
 
-	setup_remove_by_id(endpoint->id);
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for %u endpoint",
+								endpoint->id);
+		return;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
 }
 
 static void sep_close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
-- 
1.8.4.2


^ permalink raw reply related

* [PATCH BlueZ 2/4] android/A2DP: Notify when audio state change to started
From: Luiz Augusto von Dentz @ 2014-01-17 13:33 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1389965587-20066-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

---
 android/a2dp.c | 37 +++++++++++++++++++++++++++++++++++--
 1 file changed, 35 insertions(+), 2 deletions(-)

diff --git a/android/a2dp.c b/android/a2dp.c
index 2288912..515cf17 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -85,6 +85,7 @@ struct a2dp_setup {
 	struct a2dp_endpoint *endpoint;
 	struct a2dp_preset *preset;
 	struct avdtp_stream *stream;
+	uint8_t state;
 };
 
 static int device_cmp(gconstpointer s, gconstpointer user_data)
@@ -190,6 +191,26 @@ static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state)
 	a2dp_device_free(dev);
 }
 
+static void bt_audio_notify_state(struct a2dp_setup *setup, uint8_t state)
+{
+	struct hal_ev_a2dp_audio_state ev;
+	char address[18];
+
+	if (setup->state == state)
+		return;
+
+	setup->state = state;
+
+	ba2str(&setup->dev->dst, address);
+	DBG("device %s state %u", address, state);
+
+	bdaddr2android(&setup->dev->dst, ev.bdaddr);
+	ev.state = state;
+
+	ipc_send_notif(HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_AUDIO_STATE, sizeof(ev),
+									&ev);
+}
+
 static void disconnect_cb(void *user_data)
 {
 	struct a2dp_device *dev = user_data;
@@ -962,6 +983,8 @@ static gboolean sep_start_ind(struct avdtp *session,
 		return FALSE;
 	}
 
+	bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
+
 	return TRUE;
 }
 
@@ -1059,13 +1082,23 @@ static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 			void *user_data)
 {
 	struct a2dp_endpoint *endpoint = user_data;
+	struct a2dp_setup *setup;
 
 	DBG("");
 
-	if (!err)
+	if (err) {
+		setup_remove_by_id(endpoint->id);
 		return;
+	}
 
-	setup_remove_by_id(endpoint->id);
+	setup = find_setup(endpoint->id);
+	if (!setup) {
+		error("Unable to find stream setup for %u endpoint",
+								endpoint->id);
+		return;
+	}
+
+	bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
 }
 
 static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
-- 
1.8.4.2


^ permalink raw reply related

* [PATCH BlueZ 1/4] android/hal-msg: Add defines for each audio state
From: Luiz Augusto von Dentz @ 2014-01-17 13:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

---
 android/hal-msg.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/android/hal-msg.h b/android/hal-msg.h
index ceaa3b2..cfb5460 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -564,6 +564,10 @@ struct hal_ev_a2dp_conn_state {
 	uint8_t bdaddr[6];
 } __attribute__((packed));
 
+#define HAL_AUDIO_SUSPEND			0x00
+#define HAL_AUDIO_STOPPED			0x01
+#define HAL_AUDIO_STARTED			0x02
+
 #define HAL_EV_A2DP_AUDIO_STATE			0x82
 struct hal_ev_a2dp_audio_state {
 	uint8_t state;
-- 
1.8.4.2


^ permalink raw reply related

* [PATCH v7] staging/bluetooth: Add hci_h4p driver
From: Pavel Machek @ 2014-01-17 13:29 UTC (permalink / raw)
  To: Marcel Holtmann, gregkh
  Cc: Pali Rohár,
	Ивайло Димитров,
	Gustavo F. Padovan, Johan Hedberg, linux-kernel,
	linux-bluetooth@vger.kernel.org development, Ville Tervo,
	Sebastian Reichel
In-Reply-To: <FD05FD52-1502-4AB8-8650-FC785B700088@holtmann.org>


Add hci_h4p bluetooth driver to bluetooth-next. This device is used
for example on Nokia N900 cell phone.

Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
Signed-off-by: Pavel Machek <pavel@ucw.cz>
Thanks-to: Sebastian Reichel <sre@debian.org>
Thanks-to: Joe Perches <joe@perches.com>

---

Move it to staging, rename header file as Marcel requested. Driver
still needs too many changes to make direct merge easy, yet it is
useful for using Maemo on N900.

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 3bfdaa8..8d5af7d 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -150,4 +150,6 @@ source "drivers/staging/dgnc/Kconfig"
 
 source "drivers/staging/dgap/Kconfig"
 
+source "drivers/staging/nokia_h4p/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index b0d3303..cb2991e 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -67,3 +67,4 @@ obj-$(CONFIG_XILLYBUS)		+= xillybus/
 obj-$(CONFIG_DGNC)			+= dgnc/
 obj-$(CONFIG_DGAP)			+= dgap/
 obj-$(CONFIG_MTD_SPINAND_MT29F)	+= mt29f_spinand/
+obj-$(CONFIG_BT_NOKIA_H4P)	+= nokia_h4p/
diff --git a/drivers/staging/nokia_h4p/Kconfig b/drivers/staging/nokia_h4p/Kconfig
new file mode 100644
index 0000000..4336c0a
--- /dev/null
+++ b/drivers/staging/nokia_h4p/Kconfig
@@ -0,0 +1,9 @@
+config BT_NOKIA_H4P
+	tristate "HCI driver with H4 Nokia extensions"
+	depends on BT && ARCH_OMAP
+	help
+	  Bluetooth HCI driver with H4 extensions.  This driver provides
+	  support for H4+ Bluetooth chip with vendor-specific H4 extensions.
+
+	  Say Y here to compile support for h4 extended devices into the kernel
+	  or say M to compile it as module (btnokia_h4p).
diff --git a/drivers/staging/nokia_h4p/Makefile b/drivers/staging/nokia_h4p/Makefile
new file mode 100644
index 0000000..9625db4
--- /dev/null
+++ b/drivers/staging/nokia_h4p/Makefile
@@ -0,0 +1,6 @@
+
+obj-$(CONFIG_BT_NOKIA_H4P)		+= btnokia_h4p.o
+btnokia_h4p-objs := nokia_core.o nokia_fw.o nokia_uart.o nokia_fw-csr.o \
+		nokia_fw-bcm.o nokia_fw-ti1273.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/staging/nokia_h4p/TODO b/drivers/staging/nokia_h4p/TODO
new file mode 100644
index 0000000..2194146
--- /dev/null
+++ b/drivers/staging/nokia_h4p/TODO
@@ -0,0 +1,140 @@
+Few attempts to submission have been made, last review comments were received in
+
+Date: Wed, 15 Jan 2014 19:01:51 -0800
+From: Marcel Holtmann <marcel@holtmann.org>
+Subject: Re: [PATCH v6] Bluetooth: Add hci_h4p driver
+
+Some code refactoring is still needed.
+
+TODO:
+
+> +++ b/drivers/bluetooth/hci_h4p.h
+
+can we please get the naming straight. File names do not start with
+hci_ anymore. We moved away from it since that term is too generic.
+
+> +#define FW_NAME_TI1271_LE    "ti1273_le.bin"
+> +#define FW_NAME_TI1271               "ti1273.bin"
+> +#define FW_NAME_BCM2048              "bcmfw.bin"
+> +#define FW_NAME_CSR          "bc4fw.bin"
+
+We do these have to be global in a header file. This should be
+confined to the specific firmware part.
+
+> +struct hci_h4p_info {
+
+Can we please get rid of the hci_ prefix for everything. Copying from
+drivers that are over 10 years old is not a good idea. Please look at
+recent ones.
+
+> +     struct timer_list lazy_release;
+
+Timer? Not delayed work?
+
+> +void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val);
+> +u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset);
+> +void hci_h4p_set_rts(struct hci_h4p_info *info, int active);
+> +int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active, int timeout_ms);
+> +void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
+> +void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
+> +void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed);
+> +int hci_h4p_reset_uart(struct hci_h4p_info *info);
+> +void hci_h4p_init_uart(struct hci_h4p_info *info);
+> +void hci_h4p_enable_tx(struct hci_h4p_info *info);
+> +void hci_h4p_store_regs(struct hci_h4p_info *info);
+> +void hci_h4p_restore_regs(struct hci_h4p_info *info);
+> +void hci_h4p_smart_idle(struct hci_h4p_info *info, bool enable);
+
+These are a lot of public functions. Are they all really needed or can
+the code be done smart.
+
+> +static ssize_t hci_h4p_store_bdaddr(struct device *dev,
+> +                                 struct device_attribute *attr,
+> +                                 const char *buf, size_t count)
+> +{
+> +     struct hci_h4p_info *info = dev_get_drvdata(dev);
+
+Since none of these devices can function without having a valid
+address, the way this should work is that we should not register the
+HCI device when probing the platform device.
+    
+The HCI device should be registered once a valid address has been
+written into the sysfs file. I do not want to play the tricks with
+bringing up the device without a valid address.
+
+> +     hdev->close = hci_h4p_hci_close;
+> +     hdev->flush = hci_h4p_hci_flush;
+> +     hdev->send = hci_h4p_hci_send_frame;
+    
+It needs to use hdev->setup to load the firmware. I assume the
+firmware only needs to be loaded once. That is exactly what
+hdev->setup does. It gets executed once.
+    
+> +     set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+
+Is this quirk really needed? Normally only Bluetooth 1.1 and early
+devices qualify for it.
+
+> +static int hci_h4p_bcm_set_bdaddr(struct hci_h4p_info *info, struct sk_buff *skb)
+> +{
+> +     int i;
+> +     static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
+> +     int not_valid;
+
+Has this actually been confirmed that we can just randomly set an
+address out of the Nokia range. I do not think so. This is a pretty
+bad idea.
+
+I have no interest in merging a driver with such a hack.
+
+> +     not_valid = 1;
+> +     for (i = 0; i < 6; i++) {
+> +             if (info->bd_addr[i] != 0x00) {
+> +                     not_valid = 0;
+> +                     break;
+> +             }   
+> +     }
+
+Anybody every heard of memcmp or bacmp and BDADDR_ANY?
+
+> +             if (not_valid) {
+> +                     dev_info(info->dev, "Valid bluetooth address not found,"
+> +                                     " setting some random\n");
+> +                     /* When address is not valid, use some random */
+> +                     memcpy(info->bd_addr, nokia_oui, 3);
+> +                     get_random_bytes(info->bd_addr + 3, 3);
+> +             }
+
+
+And why does every single chip firmware does this differently. Seriously, this is a mess.
+
+> +void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
+> +{
+> +     switch (info->man_id) {
+> +     case H4P_ID_CSR:
+> +             hci_h4p_bc4_parse_fw_event(info, skb);
+> +             break;
+...
+> +}
+
+We have proper HCI sync command handling in recent kernels. I really
+do not know why this is hand coded these days. Check how the Intel
+firmware loading inside btusb.c does it.
+
+> +inline u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset)
+> +{ 
+> +     return __raw_readb(info->uart_base + (offset << 2));
+> +}
+
+Inline in a *.c file for a non-static function. Makes no sense to me.
+
+> +/**
+> + * struct hci_h4p_platform data - hci_h4p Platform data structure
+> + */
+> +struct hci_h4p_platform_data {
+
+please have a proper name here. For example
+btnokia_h4p_platform_data.
+
+Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
+Pavel Machek <pavel@ucw.cz>
diff --git a/drivers/staging/nokia_h4p/hci_h4p.h b/drivers/staging/nokia_h4p/hci_h4p.h
new file mode 100644
index 0000000..fd7a640
--- /dev/null
+++ b/drivers/staging/nokia_h4p/hci_h4p.h
@@ -0,0 +1,228 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * Copyright (C) 2005-2008 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __DRIVERS_BLUETOOTH_HCI_H4P_H
+#define __DRIVERS_BLUETOOTH_HCI_H4P_H
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#define FW_NAME_TI1271_PRELE	"ti1273_prele.bin"
+#define FW_NAME_TI1271_LE	"ti1273_le.bin"
+#define FW_NAME_TI1271		"ti1273.bin"
+#define FW_NAME_BCM2048		"bcmfw.bin"
+#define FW_NAME_CSR		"bc4fw.bin"
+
+#define UART_SYSC_OMAP_RESET	0x03
+#define UART_SYSS_RESETDONE	0x01
+#define UART_OMAP_SCR_EMPTY_THR	0x08
+#define UART_OMAP_SCR_WAKEUP	0x10
+#define UART_OMAP_SSR_WAKEUP	0x02
+#define UART_OMAP_SSR_TXFULL	0x01
+
+#define UART_OMAP_SYSC_IDLEMODE		0x03
+#define UART_OMAP_SYSC_IDLEMASK		(3 << UART_OMAP_SYSC_IDLEMODE)
+
+#define UART_OMAP_SYSC_FORCE_IDLE	(0 << UART_OMAP_SYSC_IDLEMODE)
+#define UART_OMAP_SYSC_NO_IDLE		(1 << UART_OMAP_SYSC_IDLEMODE)
+#define UART_OMAP_SYSC_SMART_IDLE	(2 << UART_OMAP_SYSC_IDLEMODE)
+
+#define H4P_TRANSFER_MODE		1
+#define H4P_SCHED_TRANSFER_MODE		2
+#define H4P_ACTIVE_MODE			3
+
+struct hci_h4p_info {
+	struct timer_list lazy_release;
+	struct hci_dev *hdev;
+	spinlock_t lock;
+
+	void __iomem *uart_base;
+	unsigned long uart_phys_base;
+	int irq;
+	struct device *dev;
+	u8 chip_type;
+	u8 bt_wakeup_gpio;
+	u8 host_wakeup_gpio;
+	u8 reset_gpio;
+	u8 reset_gpio_shared;
+	u8 bt_sysclk;
+	u8 man_id;
+	u8 ver_id;
+
+	struct sk_buff_head fw_queue;
+	struct sk_buff *alive_cmd_skb;
+	struct completion init_completion;
+	struct completion fw_completion;
+	struct completion test_completion;
+	int fw_error;
+	int init_error;
+
+	struct sk_buff_head txq;
+
+	struct sk_buff *rx_skb;
+	long rx_count;
+	unsigned long rx_state;
+	unsigned long garbage_bytes;
+
+	u8 bd_addr[6];
+	struct sk_buff_head *fw_q;
+
+	int pm_enabled;
+	int tx_enabled;
+	int autorts;
+	int rx_enabled;
+	unsigned long pm_flags;
+
+	int tx_clocks_en;
+	int rx_clocks_en;
+	spinlock_t clocks_lock;
+	struct clk *uart_iclk;
+	struct clk *uart_fclk;
+	atomic_t clk_users;
+	u16 dll;
+	u16 dlh;
+	u16 ier;
+	u16 mdr1;
+	u16 efr;
+};
+
+struct hci_h4p_radio_hdr {
+	__u8 evt;
+	__u8 dlen;
+} __attribute__ ((packed));
+
+struct hci_h4p_neg_hdr {
+	__u8 dlen;
+} __attribute__ ((packed));
+#define H4P_NEG_HDR_SIZE 1
+
+#define H4P_NEG_REQ	0x00
+#define H4P_NEG_ACK	0x20
+#define H4P_NEG_NAK	0x40
+
+#define H4P_PROTO_PKT	0x44
+#define H4P_PROTO_BYTE	0x4c
+
+#define H4P_ID_CSR	0x02
+#define H4P_ID_BCM2048	0x04
+#define H4P_ID_TI1271	0x31
+
+struct hci_h4p_neg_cmd {
+	__u8	ack;
+	__u16	baud;
+	__u16	unused1;
+	__u8	proto;
+	__u16	sys_clk;
+	__u16	unused2;
+} __attribute__ ((packed));
+
+struct hci_h4p_neg_evt {
+	__u8	ack;
+	__u16	baud;
+	__u16	unused1;
+	__u8	proto;
+	__u16	sys_clk;
+	__u16	unused2;
+	__u8	man_id;
+	__u8	ver_id;
+} __attribute__ ((packed));
+
+#define H4P_ALIVE_REQ	0x55
+#define H4P_ALIVE_RESP	0xcc
+
+struct hci_h4p_alive_hdr {
+	__u8	dlen;
+} __attribute__ ((packed));
+#define H4P_ALIVE_HDR_SIZE 1
+
+struct hci_h4p_alive_pkt {
+	__u8	mid;
+	__u8	unused;
+} __attribute__ ((packed));
+
+#define MAX_BAUD_RATE		921600
+#define BC4_MAX_BAUD_RATE	3692300
+#define UART_CLOCK		48000000
+#define BT_INIT_DIVIDER		320
+#define BT_BAUDRATE_DIVIDER	384000000
+#define BT_SYSCLK_DIV		1000
+#define INIT_SPEED		120000
+
+#define H4_TYPE_SIZE		1
+#define H4_RADIO_HDR_SIZE	2
+
+/* H4+ packet types */
+#define H4_CMD_PKT		0x01
+#define H4_ACL_PKT		0x02
+#define H4_SCO_PKT		0x03
+#define H4_EVT_PKT		0x04
+#define H4_NEG_PKT		0x06
+#define H4_ALIVE_PKT		0x07
+#define H4_RADIO_PKT		0x08
+
+/* TX states */
+#define WAIT_FOR_PKT_TYPE	1
+#define WAIT_FOR_HEADER		2
+#define WAIT_FOR_DATA		3
+
+struct hci_fw_event {
+	struct hci_event_hdr hev;
+	struct hci_ev_cmd_complete cmd;
+	u8 status;
+} __attribute__ ((packed));
+
+int hci_h4p_send_alive_packet(struct hci_h4p_info *info);
+
+void hci_h4p_bcm_parse_fw_event(struct hci_h4p_info *info,
+				struct sk_buff *skb);
+int hci_h4p_bcm_send_fw(struct hci_h4p_info *info,
+			struct sk_buff_head *fw_queue);
+
+void hci_h4p_bc4_parse_fw_event(struct hci_h4p_info *info,
+				struct sk_buff *skb);
+int hci_h4p_bc4_send_fw(struct hci_h4p_info *info,
+			struct sk_buff_head *fw_queue);
+
+void hci_h4p_ti1273_parse_fw_event(struct hci_h4p_info *info,
+				    struct sk_buff *skb);
+int hci_h4p_ti1273_send_fw(struct hci_h4p_info *info,
+			    struct sk_buff_head *fw_queue);
+
+int hci_h4p_read_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue);
+int hci_h4p_send_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue);
+void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb);
+
+void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val);
+u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset);
+void hci_h4p_set_rts(struct hci_h4p_info *info, int active);
+int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active, int timeout_ms);
+void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
+void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
+void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed);
+int hci_h4p_reset_uart(struct hci_h4p_info *info);
+void hci_h4p_init_uart(struct hci_h4p_info *info);
+void hci_h4p_enable_tx(struct hci_h4p_info *info);
+void hci_h4p_store_regs(struct hci_h4p_info *info);
+void hci_h4p_restore_regs(struct hci_h4p_info *info);
+void hci_h4p_smart_idle(struct hci_h4p_info *info, bool enable);
+
+#endif /* __DRIVERS_BLUETOOTH_HCI_H4P_H */
diff --git a/drivers/staging/nokia_h4p/nokia_core.c b/drivers/staging/nokia_h4p/nokia_core.c
new file mode 100644
index 0000000..5da84b0
--- /dev/null
+++ b/drivers/staging/nokia_h4p/nokia_core.c
@@ -0,0 +1,1205 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * Copyright (C) 2005-2008 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Thanks to all the Nokia people that helped with this driver,
+ * including Ville Tervo and Roger Quadros.
+ *
+ * Power saving functionality was removed from this driver to make
+ * merging easier.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/serial_reg.h>
+#include <linux/skbuff.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/timer.h>
+#include <linux/kthread.h>
+#include <linux/io.h>
+#include <linux/completion.h>
+#include <linux/sizes.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#include <linux/platform_data/bt-nokia-h4p.h>
+
+#include "hci_h4p.h"
+
+/* This should be used in function that cannot release clocks */
+static void hci_h4p_set_clk(struct hci_h4p_info *info, int *clock, int enable)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->clocks_lock, flags);
+	if (enable && !*clock) {
+		BT_DBG("Enabling %p", clock);
+		clk_prepare_enable(info->uart_fclk);
+		clk_prepare_enable(info->uart_iclk);
+		if (atomic_read(&info->clk_users) == 0)
+			hci_h4p_restore_regs(info);
+		atomic_inc(&info->clk_users);
+	}
+
+	if (!enable && *clock) {
+		BT_DBG("Disabling %p", clock);
+		if (atomic_dec_and_test(&info->clk_users))
+			hci_h4p_store_regs(info);
+		clk_disable_unprepare(info->uart_fclk);
+		clk_disable_unprepare(info->uart_iclk);
+	}
+
+	*clock = enable;
+	spin_unlock_irqrestore(&info->clocks_lock, flags);
+}
+
+static void hci_h4p_lazy_clock_release(unsigned long data)
+{
+	struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->lock, flags);
+	if (!info->tx_enabled)
+		hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+	spin_unlock_irqrestore(&info->lock, flags);
+}
+
+/* Power management functions */
+void hci_h4p_smart_idle(struct hci_h4p_info *info, bool enable)
+{
+	u8 v;
+
+	v = hci_h4p_inb(info, UART_OMAP_SYSC);
+	v &= ~(UART_OMAP_SYSC_IDLEMASK);
+
+	if (enable)
+		v |= UART_OMAP_SYSC_SMART_IDLE;
+	else
+		v |= UART_OMAP_SYSC_NO_IDLE;
+
+	hci_h4p_outb(info, UART_OMAP_SYSC, v);
+}
+
+static inline void h4p_schedule_pm(struct hci_h4p_info *info)
+{
+}
+
+static void hci_h4p_disable_tx(struct hci_h4p_info *info)
+{
+	if (!info->pm_enabled)
+		return;
+
+	/* Re-enable smart-idle */
+	hci_h4p_smart_idle(info, 1);
+
+	gpio_set_value(info->bt_wakeup_gpio, 0);
+	mod_timer(&info->lazy_release, jiffies + msecs_to_jiffies(100));
+	info->tx_enabled = 0;
+}
+
+void hci_h4p_enable_tx(struct hci_h4p_info *info)
+{
+	unsigned long flags;
+
+	if (!info->pm_enabled)
+		return;
+
+	h4p_schedule_pm(info);
+
+	spin_lock_irqsave(&info->lock, flags);
+	del_timer(&info->lazy_release);
+	hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+	info->tx_enabled = 1;
+	gpio_set_value(info->bt_wakeup_gpio, 1);
+	hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+		     UART_IER_THRI);
+	/*
+	 * Disable smart-idle as UART TX interrupts
+	 * are not wake-up capable
+	 */
+	hci_h4p_smart_idle(info, 0);
+
+	spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static void hci_h4p_disable_rx(struct hci_h4p_info *info)
+{
+	if (!info->pm_enabled)
+		return;
+
+	info->rx_enabled = 0;
+
+	if (hci_h4p_inb(info, UART_LSR) & UART_LSR_DR)
+		return;
+
+	if (!(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT))
+		return;
+
+	__hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
+	info->autorts = 0;
+	hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+}
+
+static void hci_h4p_enable_rx(struct hci_h4p_info *info)
+{
+	if (!info->pm_enabled)
+		return;
+
+	h4p_schedule_pm(info);
+
+	hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
+	info->rx_enabled = 1;
+
+	if (!(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT))
+		return;
+
+	__hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
+	info->autorts = 1;
+}
+
+/* Negotiation functions */
+int hci_h4p_send_alive_packet(struct hci_h4p_info *info)
+{
+	struct hci_h4p_alive_hdr *hdr;
+	struct hci_h4p_alive_pkt *pkt;
+	struct sk_buff *skb;
+	unsigned long flags;
+	int len;
+
+	BT_DBG("Sending alive packet");
+
+	len = H4_TYPE_SIZE + sizeof(*hdr) + sizeof(*pkt);
+	skb = bt_skb_alloc(len, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	memset(skb->data, 0x00, len);
+	*skb_put(skb, 1) = H4_ALIVE_PKT;
+	hdr = (struct hci_h4p_alive_hdr *)skb_put(skb, sizeof(*hdr));
+	hdr->dlen = sizeof(*pkt);
+	pkt = (struct hci_h4p_alive_pkt *)skb_put(skb, sizeof(*pkt));
+	pkt->mid = H4P_ALIVE_REQ;
+
+	skb_queue_tail(&info->txq, skb);
+	spin_lock_irqsave(&info->lock, flags);
+	hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+		     UART_IER_THRI);
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	BT_DBG("Alive packet sent");
+
+	return 0;
+}
+
+static void hci_h4p_alive_packet(struct hci_h4p_info *info,
+				 struct sk_buff *skb)
+{
+	struct hci_h4p_alive_hdr *hdr;
+	struct hci_h4p_alive_pkt *pkt;
+
+	BT_DBG("Received alive packet");
+	hdr = (struct hci_h4p_alive_hdr *)skb->data;
+	if (hdr->dlen != sizeof(*pkt)) {
+		dev_err(info->dev, "Corrupted alive message\n");
+		info->init_error = -EIO;
+		goto finish_alive;
+	}
+
+	pkt = (struct hci_h4p_alive_pkt *)skb_pull(skb, sizeof(*hdr));
+	if (pkt->mid != H4P_ALIVE_RESP) {
+		dev_err(info->dev, "Could not negotiate hci_h4p settings\n");
+		info->init_error = -EINVAL;
+	}
+
+finish_alive:
+	complete(&info->init_completion);
+	kfree_skb(skb);
+}
+
+static int hci_h4p_send_negotiation(struct hci_h4p_info *info)
+{
+	struct hci_h4p_neg_cmd *neg_cmd;
+	struct hci_h4p_neg_hdr *neg_hdr;
+	struct sk_buff *skb;
+	unsigned long flags;
+	int err, len;
+	u16 sysclk;
+
+	BT_DBG("Sending negotiation..");
+
+	switch (info->bt_sysclk) {
+	case 1:
+		sysclk = 12000;
+		break;
+	case 2:
+		sysclk = 38400;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	len = sizeof(*neg_cmd) + sizeof(*neg_hdr) + H4_TYPE_SIZE;
+	skb = bt_skb_alloc(len, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	memset(skb->data, 0x00, len);
+	*skb_put(skb, 1) = H4_NEG_PKT;
+	neg_hdr = (struct hci_h4p_neg_hdr *)skb_put(skb, sizeof(*neg_hdr));
+	neg_cmd = (struct hci_h4p_neg_cmd *)skb_put(skb, sizeof(*neg_cmd));
+
+	neg_hdr->dlen = sizeof(*neg_cmd);
+	neg_cmd->ack = H4P_NEG_REQ;
+	neg_cmd->baud = cpu_to_le16(BT_BAUDRATE_DIVIDER/MAX_BAUD_RATE);
+	neg_cmd->proto = H4P_PROTO_BYTE;
+	neg_cmd->sys_clk = cpu_to_le16(sysclk);
+
+	hci_h4p_change_speed(info, INIT_SPEED);
+
+	hci_h4p_set_rts(info, 1);
+	info->init_error = 0;
+	init_completion(&info->init_completion);
+	skb_queue_tail(&info->txq, skb);
+	spin_lock_irqsave(&info->lock, flags);
+	hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+		     UART_IER_THRI);
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	if (!wait_for_completion_interruptible_timeout(&info->init_completion,
+				msecs_to_jiffies(1000)))
+		return -ETIMEDOUT;
+
+	if (info->init_error < 0)
+		return info->init_error;
+
+	/* Change to operational settings */
+	hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
+	hci_h4p_set_rts(info, 0);
+	hci_h4p_change_speed(info, MAX_BAUD_RATE);
+
+	err = hci_h4p_wait_for_cts(info, 1, 100);
+	if (err < 0)
+		return err;
+
+	hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
+	init_completion(&info->init_completion);
+	err = hci_h4p_send_alive_packet(info);
+
+	if (err < 0)
+		return err;
+
+	if (!wait_for_completion_interruptible_timeout(&info->init_completion,
+				msecs_to_jiffies(1000)))
+		return -ETIMEDOUT;
+
+	if (info->init_error < 0)
+		return info->init_error;
+
+	BT_DBG("Negotiation successful");
+	return 0;
+}
+
+static void hci_h4p_negotiation_packet(struct hci_h4p_info *info,
+				       struct sk_buff *skb)
+{
+	struct hci_h4p_neg_hdr *hdr;
+	struct hci_h4p_neg_evt *evt;
+
+	hdr = (struct hci_h4p_neg_hdr *)skb->data;
+	if (hdr->dlen != sizeof(*evt)) {
+		info->init_error = -EIO;
+		goto finish_neg;
+	}
+
+	evt = (struct hci_h4p_neg_evt *)skb_pull(skb, sizeof(*hdr));
+
+	if (evt->ack != H4P_NEG_ACK) {
+		dev_err(info->dev, "Could not negotiate hci_h4p settings\n");
+		info->init_error = -EINVAL;
+	}
+
+	info->man_id = evt->man_id;
+	info->ver_id = evt->ver_id;
+
+finish_neg:
+
+	complete(&info->init_completion);
+	kfree_skb(skb);
+}
+
+/* H4 packet handling functions */
+static int hci_h4p_get_hdr_len(struct hci_h4p_info *info, u8 pkt_type)
+{
+	long retval;
+
+	switch (pkt_type) {
+	case H4_EVT_PKT:
+		retval = HCI_EVENT_HDR_SIZE;
+		break;
+	case H4_ACL_PKT:
+		retval = HCI_ACL_HDR_SIZE;
+		break;
+	case H4_SCO_PKT:
+		retval = HCI_SCO_HDR_SIZE;
+		break;
+	case H4_NEG_PKT:
+		retval = H4P_NEG_HDR_SIZE;
+		break;
+	case H4_ALIVE_PKT:
+		retval = H4P_ALIVE_HDR_SIZE;
+		break;
+	case H4_RADIO_PKT:
+		retval = H4_RADIO_HDR_SIZE;
+		break;
+	default:
+		dev_err(info->dev, "Unknown H4 packet type 0x%.2x\n", pkt_type);
+		retval = -1;
+		break;
+	}
+
+	return retval;
+}
+
+static unsigned int hci_h4p_get_data_len(struct hci_h4p_info *info,
+					 struct sk_buff *skb)
+{
+	long retval = -1;
+	struct hci_acl_hdr *acl_hdr;
+	struct hci_sco_hdr *sco_hdr;
+	struct hci_event_hdr *evt_hdr;
+	struct hci_h4p_neg_hdr *neg_hdr;
+	struct hci_h4p_alive_hdr *alive_hdr;
+	struct hci_h4p_radio_hdr *radio_hdr;
+
+	switch (bt_cb(skb)->pkt_type) {
+	case H4_EVT_PKT:
+		evt_hdr = (struct hci_event_hdr *)skb->data;
+		retval = evt_hdr->plen;
+		break;
+	case H4_ACL_PKT:
+		acl_hdr = (struct hci_acl_hdr *)skb->data;
+		retval = le16_to_cpu(acl_hdr->dlen);
+		break;
+	case H4_SCO_PKT:
+		sco_hdr = (struct hci_sco_hdr *)skb->data;
+		retval = sco_hdr->dlen;
+		break;
+	case H4_RADIO_PKT:
+		radio_hdr = (struct hci_h4p_radio_hdr *)skb->data;
+		retval = radio_hdr->dlen;
+		break;
+	case H4_NEG_PKT:
+		neg_hdr = (struct hci_h4p_neg_hdr *)skb->data;
+		retval = neg_hdr->dlen;
+		break;
+	case H4_ALIVE_PKT:
+		alive_hdr = (struct hci_h4p_alive_hdr *)skb->data;
+		retval = alive_hdr->dlen;
+		break;
+	}
+
+	return retval;
+}
+
+static inline void hci_h4p_recv_frame(struct hci_h4p_info *info,
+				      struct sk_buff *skb)
+{
+	if (unlikely(!test_bit(HCI_RUNNING, &info->hdev->flags))) {
+		switch (bt_cb(skb)->pkt_type) {
+		case H4_NEG_PKT:
+			hci_h4p_negotiation_packet(info, skb);
+			info->rx_state = WAIT_FOR_PKT_TYPE;
+			return;
+		case H4_ALIVE_PKT:
+			hci_h4p_alive_packet(info, skb);
+			info->rx_state = WAIT_FOR_PKT_TYPE;
+			return;
+		}
+
+		if (!test_bit(HCI_UP, &info->hdev->flags)) {
+			BT_DBG("fw_event");
+			hci_h4p_parse_fw_event(info, skb);
+			return;
+		}
+	}
+
+	hci_recv_frame(info->hdev, skb);
+	BT_DBG("Frame sent to upper layer");
+}
+
+static inline void hci_h4p_handle_byte(struct hci_h4p_info *info, u8 byte)
+{
+	switch (info->rx_state) {
+	case WAIT_FOR_PKT_TYPE:
+		bt_cb(info->rx_skb)->pkt_type = byte;
+		info->rx_count = hci_h4p_get_hdr_len(info, byte);
+		if (info->rx_count < 0) {
+			info->hdev->stat.err_rx++;
+			kfree_skb(info->rx_skb);
+			info->rx_skb = NULL;
+		} else {
+			info->rx_state = WAIT_FOR_HEADER;
+		}
+		break;
+	case WAIT_FOR_HEADER:
+		info->rx_count--;
+		*skb_put(info->rx_skb, 1) = byte;
+		if (info->rx_count != 0)
+			break;
+		info->rx_count = hci_h4p_get_data_len(info, info->rx_skb);
+		if (info->rx_count > skb_tailroom(info->rx_skb)) {
+			dev_err(info->dev, "frame too long\n");
+			info->garbage_bytes = info->rx_count
+				- skb_tailroom(info->rx_skb);
+			kfree_skb(info->rx_skb);
+			info->rx_skb = NULL;
+			break;
+		}
+		info->rx_state = WAIT_FOR_DATA;
+		break;
+	case WAIT_FOR_DATA:
+		info->rx_count--;
+		*skb_put(info->rx_skb, 1) = byte;
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+
+	if (info->rx_count == 0) {
+		/* H4+ devices should always send word aligned packets */
+		if (!(info->rx_skb->len % 2))
+			info->garbage_bytes++;
+		hci_h4p_recv_frame(info, info->rx_skb);
+		info->rx_skb = NULL;
+	}
+}
+
+static void hci_h4p_rx_tasklet(unsigned long data)
+{
+	u8 byte;
+	struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+
+	BT_DBG("tasklet woke up");
+	BT_DBG("rx_tasklet woke up");
+
+	while (hci_h4p_inb(info, UART_LSR) & UART_LSR_DR) {
+		byte = hci_h4p_inb(info, UART_RX);
+		if (info->garbage_bytes) {
+			info->garbage_bytes--;
+			continue;
+		}
+		if (info->rx_skb == NULL) {
+			info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE,
+						    GFP_ATOMIC | GFP_DMA);
+			if (!info->rx_skb) {
+				dev_err(info->dev,
+					"No memory for new packet\n");
+				goto finish_rx;
+			}
+			info->rx_state = WAIT_FOR_PKT_TYPE;
+			info->rx_skb->dev = (void *)info->hdev;
+		}
+		info->hdev->stat.byte_rx++;
+		hci_h4p_handle_byte(info, byte);
+	}
+
+	if (!info->rx_enabled) {
+		if (hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT &&
+						  info->autorts) {
+			__hci_h4p_set_auto_ctsrts(info, 0 , UART_EFR_RTS);
+			info->autorts = 0;
+		}
+		/* Flush posted write to avoid spurious interrupts */
+		hci_h4p_inb(info, UART_OMAP_SCR);
+		hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+	}
+
+finish_rx:
+	BT_DBG("rx_ended");
+}
+
+static void hci_h4p_tx_tasklet(unsigned long data)
+{
+	unsigned int sent = 0;
+	struct sk_buff *skb;
+	struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+
+	BT_DBG("tasklet woke up");
+	BT_DBG("tx_tasklet woke up");
+
+	if (info->autorts != info->rx_enabled) {
+		if (hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT) {
+			if (info->autorts && !info->rx_enabled) {
+				__hci_h4p_set_auto_ctsrts(info, 0,
+							  UART_EFR_RTS);
+				info->autorts = 0;
+			}
+			if (!info->autorts && info->rx_enabled) {
+				__hci_h4p_set_auto_ctsrts(info, 1,
+							  UART_EFR_RTS);
+				info->autorts = 1;
+			}
+		} else {
+			hci_h4p_outb(info, UART_OMAP_SCR,
+				     hci_h4p_inb(info, UART_OMAP_SCR) |
+				     UART_OMAP_SCR_EMPTY_THR);
+			goto finish_tx;
+		}
+	}
+
+	skb = skb_dequeue(&info->txq);
+	if (!skb) {
+		/* No data in buffer */
+		BT_DBG("skb ready");
+		if (hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT) {
+			hci_h4p_outb(info, UART_IER,
+				     hci_h4p_inb(info, UART_IER) &
+				     ~UART_IER_THRI);
+			hci_h4p_inb(info, UART_OMAP_SCR);
+			hci_h4p_disable_tx(info);
+			return;
+		}
+		hci_h4p_outb(info, UART_OMAP_SCR,
+			     hci_h4p_inb(info, UART_OMAP_SCR) |
+			     UART_OMAP_SCR_EMPTY_THR);
+		goto finish_tx;
+	}
+
+	/* Copy data to tx fifo */
+	while (!(hci_h4p_inb(info, UART_OMAP_SSR) & UART_OMAP_SSR_TXFULL) &&
+	       (sent < skb->len)) {
+		hci_h4p_outb(info, UART_TX, skb->data[sent]);
+		sent++;
+	}
+
+	info->hdev->stat.byte_tx += sent;
+	if (skb->len == sent) {
+		kfree_skb(skb);
+	} else {
+		skb_pull(skb, sent);
+		skb_queue_head(&info->txq, skb);
+	}
+
+	hci_h4p_outb(info, UART_OMAP_SCR, hci_h4p_inb(info, UART_OMAP_SCR) &
+						     ~UART_OMAP_SCR_EMPTY_THR);
+	hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+						 UART_IER_THRI);
+
+finish_tx:
+	/* Flush posted write to avoid spurious interrupts */
+	hci_h4p_inb(info, UART_OMAP_SCR);
+
+}
+
+static irqreturn_t hci_h4p_interrupt(int irq, void *data)
+{
+	struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+	u8 iir, msr;
+	int ret;
+
+	ret = IRQ_NONE;
+
+	iir = hci_h4p_inb(info, UART_IIR);
+	if (iir & UART_IIR_NO_INT)
+		return IRQ_HANDLED;
+
+	BT_DBG("In interrupt handler iir 0x%.2x", iir);
+
+	iir &= UART_IIR_ID;
+
+	if (iir == UART_IIR_MSI) {
+		msr = hci_h4p_inb(info, UART_MSR);
+		ret = IRQ_HANDLED;
+	}
+	if (iir == UART_IIR_RLSI) {
+		hci_h4p_inb(info, UART_RX);
+		hci_h4p_inb(info, UART_LSR);
+		ret = IRQ_HANDLED;
+	}
+
+	if (iir == UART_IIR_RDI) {
+		hci_h4p_rx_tasklet((unsigned long)data);
+		ret = IRQ_HANDLED;
+	}
+
+	if (iir == UART_IIR_THRI) {
+		hci_h4p_tx_tasklet((unsigned long)data);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+static irqreturn_t hci_h4p_wakeup_interrupt(int irq, void *dev_inst)
+{
+	struct hci_h4p_info *info = dev_inst;
+	int should_wakeup;
+	struct hci_dev *hdev;
+
+	if (!info->hdev)
+		return IRQ_HANDLED;
+
+	should_wakeup = gpio_get_value(info->host_wakeup_gpio);
+	hdev = info->hdev;
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+		if (should_wakeup == 1)
+			complete_all(&info->test_completion);
+
+		return IRQ_HANDLED;
+	}
+
+	BT_DBG("gpio interrupt %d", should_wakeup);
+
+	/* Check if wee have missed some interrupts */
+	if (info->rx_enabled == should_wakeup)
+		return IRQ_HANDLED;
+
+	if (should_wakeup)
+		hci_h4p_enable_rx(info);
+	else
+		hci_h4p_disable_rx(info);
+
+	return IRQ_HANDLED;
+}
+
+static inline void hci_h4p_set_pm_limits(struct hci_h4p_info *info, bool set)
+{
+	struct hci_h4p_platform_data *bt_plat_data = info->dev->platform_data;
+	const char *sset = set ? "set" : "clear";
+
+	if (unlikely(!bt_plat_data || !bt_plat_data->set_pm_limits))
+		return;
+
+	if (set != !!test_bit(H4P_ACTIVE_MODE, &info->pm_flags)) {
+		bt_plat_data->set_pm_limits(info->dev, set);
+		if (set)
+			set_bit(H4P_ACTIVE_MODE, &info->pm_flags);
+		else
+			clear_bit(H4P_ACTIVE_MODE, &info->pm_flags);
+		BT_DBG("Change pm constraints to: %s", sset);
+		return;
+	}
+
+	BT_DBG("pm constraints remains: %s", sset);
+}
+
+static int hci_h4p_reset(struct hci_h4p_info *info)
+{
+	int err;
+
+	err = hci_h4p_reset_uart(info);
+	if (err < 0) {
+		dev_err(info->dev, "Uart reset failed\n");
+		return err;
+	}
+	hci_h4p_init_uart(info);
+	hci_h4p_set_rts(info, 0);
+
+	gpio_set_value(info->reset_gpio, 0);
+	gpio_set_value(info->bt_wakeup_gpio, 1);
+	msleep(10);
+
+	if (gpio_get_value(info->host_wakeup_gpio) == 1) {
+		dev_err(info->dev, "host_wakeup_gpio not low\n");
+		return -EPROTO;
+	}
+
+	init_completion(&info->test_completion);
+	gpio_set_value(info->reset_gpio, 1);
+
+	if (!wait_for_completion_interruptible_timeout(&info->test_completion,
+						       msecs_to_jiffies(100))) {
+		dev_err(info->dev, "wakeup test timed out\n");
+		complete_all(&info->test_completion);
+		return -EPROTO;
+	}
+
+	err = hci_h4p_wait_for_cts(info, 1, 100);
+	if (err < 0) {
+		dev_err(info->dev, "No cts from bt chip\n");
+		return err;
+	}
+
+	hci_h4p_set_rts(info, 1);
+
+	return 0;
+}
+
+/* hci callback functions */
+static int hci_h4p_hci_flush(struct hci_dev *hdev)
+{
+	struct hci_h4p_info *info = hci_get_drvdata(hdev);
+	skb_queue_purge(&info->txq);
+
+	return 0;
+}
+
+static int hci_h4p_bt_wakeup_test(struct hci_h4p_info *info)
+{
+	/*
+	 * Test Sequence:
+	 * Host de-asserts the BT_WAKE_UP line.
+	 * Host polls the UART_CTS line, waiting for it to be de-asserted.
+	 * Host asserts the BT_WAKE_UP line.
+	 * Host polls the UART_CTS line, waiting for it to be asserted.
+	 * Host de-asserts the BT_WAKE_UP line (allow the Bluetooth device to
+	 * sleep).
+	 * Host polls the UART_CTS line, waiting for it to be de-asserted.
+	 */
+	int err;
+	int ret = -ECOMM;
+
+	if (!info)
+		return -EINVAL;
+
+	/* Disable wakeup interrupts */
+	disable_irq(gpio_to_irq(info->host_wakeup_gpio));
+
+	gpio_set_value(info->bt_wakeup_gpio, 0);
+	err = hci_h4p_wait_for_cts(info, 0, 100);
+	if (err) {
+		dev_warn(info->dev, "bt_wakeup_test: fail: "
+			 "CTS low timed out: %d\n", err);
+		goto out;
+	}
+
+	gpio_set_value(info->bt_wakeup_gpio, 1);
+	err = hci_h4p_wait_for_cts(info, 1, 100);
+	if (err) {
+		dev_warn(info->dev, "bt_wakeup_test: fail: "
+			 "CTS high timed out: %d\n", err);
+		goto out;
+	}
+
+	gpio_set_value(info->bt_wakeup_gpio, 0);
+	err = hci_h4p_wait_for_cts(info, 0, 100);
+	if (err) {
+		dev_warn(info->dev, "bt_wakeup_test: fail: "
+			 "CTS re-low timed out: %d\n", err);
+		goto out;
+	}
+
+	ret = 0;
+
+out:
+
+	/* Re-enable wakeup interrupts */
+	enable_irq(gpio_to_irq(info->host_wakeup_gpio));
+
+	return ret;
+}
+
+static int hci_h4p_hci_open(struct hci_dev *hdev)
+{
+	struct hci_h4p_info *info;
+	int err, retries = 0;
+	struct sk_buff_head fw_queue;
+	unsigned long flags;
+
+	info = hci_get_drvdata(hdev);
+
+	if (test_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	/* TI1271 has HW bug and boot up might fail. Retry up to three times */
+again:
+
+	info->rx_enabled = 1;
+	info->rx_state = WAIT_FOR_PKT_TYPE;
+	info->rx_count = 0;
+	info->garbage_bytes = 0;
+	info->rx_skb = NULL;
+	info->pm_enabled = 0;
+	init_completion(&info->fw_completion);
+	hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+	hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
+	skb_queue_head_init(&fw_queue);
+
+	err = hci_h4p_reset(info);
+	if (err < 0)
+		goto err_clean;
+
+	hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_CTS | UART_EFR_RTS);
+	info->autorts = 1;
+
+	err = hci_h4p_send_negotiation(info);
+
+	err = hci_h4p_read_fw(info, &fw_queue);
+	if (err < 0) {
+		dev_err(info->dev, "Cannot read firmware\n");
+		goto err_clean;
+	}
+
+	err = hci_h4p_send_fw(info, &fw_queue);
+	if (err < 0) {
+		dev_err(info->dev, "Sending firmware failed.\n");
+		goto err_clean;
+	}
+
+	info->pm_enabled = 1;
+
+	err = hci_h4p_bt_wakeup_test(info);
+	if (err < 0) {
+		dev_err(info->dev, "BT wakeup test failed.\n");
+		goto err_clean;
+	}
+
+	spin_lock_irqsave(&info->lock, flags);
+	info->rx_enabled = gpio_get_value(info->host_wakeup_gpio);
+	hci_h4p_set_clk(info, &info->rx_clocks_en, info->rx_enabled);
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+
+	kfree_skb(info->alive_cmd_skb);
+	info->alive_cmd_skb = NULL;
+	set_bit(HCI_RUNNING, &hdev->flags);
+
+	BT_DBG("hci up and running");
+	return 0;
+
+err_clean:
+	hci_h4p_hci_flush(hdev);
+	hci_h4p_reset_uart(info);
+	del_timer_sync(&info->lazy_release);
+	hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+	hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+	gpio_set_value(info->reset_gpio, 0);
+	gpio_set_value(info->bt_wakeup_gpio, 0);
+	skb_queue_purge(&fw_queue);
+	kfree_skb(info->alive_cmd_skb);
+	info->alive_cmd_skb = NULL;
+	kfree_skb(info->rx_skb);
+	info->rx_skb = NULL;
+
+	if (retries++ < 3) {
+		dev_err(info->dev, "FW loading try %d fail. Retry.\n", retries);
+		goto again;
+	}
+
+	return err;
+}
+
+static int hci_h4p_hci_close(struct hci_dev *hdev)
+{
+	struct hci_h4p_info *info = hci_get_drvdata(hdev);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	hci_h4p_hci_flush(hdev);
+	hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+	hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
+	hci_h4p_reset_uart(info);
+	del_timer_sync(&info->lazy_release);
+	hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+	hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+	gpio_set_value(info->reset_gpio, 0);
+	gpio_set_value(info->bt_wakeup_gpio, 0);
+	kfree_skb(info->rx_skb);
+
+	return 0;
+}
+
+static int hci_h4p_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_h4p_info *info;
+	int err = 0;
+
+	BT_DBG("dev %p, skb %p", hdev, skb);
+
+	info = hci_get_drvdata(hdev);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+		dev_warn(info->dev, "Frame for non-running device\n");
+		return -EIO;
+	}
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	}
+
+	/* Push frame type to skb */
+	*skb_push(skb, 1) = (bt_cb(skb)->pkt_type);
+	/* We should allways send word aligned data to h4+ devices */
+	if (skb->len % 2) {
+		err = skb_pad(skb, 1);
+		if (!err)
+			*skb_put(skb, 1) = 0x00;
+	}
+	if (err)
+		return err;
+
+	skb_queue_tail(&info->txq, skb);
+	hci_h4p_enable_tx(info);
+
+	return 0;
+}
+
+static ssize_t hci_h4p_store_bdaddr(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct hci_h4p_info *info = dev_get_drvdata(dev);
+	unsigned int bdaddr[6];
+	int ret, i;
+
+	ret = sscanf(buf, "%2x:%2x:%2x:%2x:%2x:%2x\n",
+			&bdaddr[0], &bdaddr[1], &bdaddr[2],
+			&bdaddr[3], &bdaddr[4], &bdaddr[5]);
+
+	if (ret != 6)
+		return -EINVAL;
+
+	for (i = 0; i < 6; i++) {
+		if (bdaddr[i] > 0xff)
+			return -EINVAL;
+		info->bd_addr[i] = bdaddr[i] & 0xff;
+	}
+
+	return count;
+}
+
+static ssize_t hci_h4p_show_bdaddr(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct hci_h4p_info *info = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%pMR\n", info->bd_addr);
+}
+
+static DEVICE_ATTR(bdaddr, S_IRUGO | S_IWUSR, hci_h4p_show_bdaddr,
+		   hci_h4p_store_bdaddr);
+
+static int hci_h4p_sysfs_create_files(struct device *dev)
+{
+	return device_create_file(dev, &dev_attr_bdaddr);
+}
+
+static void hci_h4p_sysfs_remove_files(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_bdaddr);
+}
+
+static int hci_h4p_register_hdev(struct hci_h4p_info *info)
+{
+	struct hci_dev *hdev;
+
+	/* Initialize and register HCI device */
+
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		dev_err(info->dev, "Can't allocate memory for device\n");
+		return -ENOMEM;
+	}
+	info->hdev = hdev;
+
+	hdev->bus = HCI_UART;
+	hci_set_drvdata(hdev, info);
+
+	hdev->open = hci_h4p_hci_open;
+	hdev->close = hci_h4p_hci_close;
+	hdev->flush = hci_h4p_hci_flush;
+	hdev->send = hci_h4p_hci_send_frame;
+	set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+
+	SET_HCIDEV_DEV(hdev, info->dev);
+
+	if (hci_h4p_sysfs_create_files(info->dev) < 0) {
+		dev_err(info->dev, "failed to create sysfs files\n");
+		goto free;
+	}
+
+	if (hci_register_dev(hdev) >= 0)
+		return 0;
+
+	dev_err(info->dev, "hci_register failed %s.\n", hdev->name);
+	hci_h4p_sysfs_remove_files(info->dev);
+free:
+	hci_free_dev(info->hdev);
+	return -ENODEV;
+}
+
+static int hci_h4p_probe(struct platform_device *pdev)
+{
+	struct hci_h4p_platform_data *bt_plat_data;
+	struct hci_h4p_info *info;
+	int err;
+
+	dev_info(&pdev->dev, "Registering HCI H4P device\n");
+	info = devm_kzalloc(&pdev->dev, sizeof(struct hci_h4p_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->dev = &pdev->dev;
+	info->tx_enabled = 1;
+	info->rx_enabled = 1;
+	spin_lock_init(&info->lock);
+	spin_lock_init(&info->clocks_lock);
+	skb_queue_head_init(&info->txq);
+
+	if (pdev->dev.platform_data == NULL) {
+		dev_err(&pdev->dev, "Could not get Bluetooth config data\n");
+		return -ENODATA;
+	}
+
+	bt_plat_data = pdev->dev.platform_data;
+	info->chip_type = bt_plat_data->chip_type;
+	info->bt_wakeup_gpio = bt_plat_data->bt_wakeup_gpio;
+	info->host_wakeup_gpio = bt_plat_data->host_wakeup_gpio;
+	info->reset_gpio = bt_plat_data->reset_gpio;
+	info->reset_gpio_shared = bt_plat_data->reset_gpio_shared;
+	info->bt_sysclk = bt_plat_data->bt_sysclk;
+
+	BT_DBG("RESET gpio: %d", info->reset_gpio);
+	BT_DBG("BTWU gpio: %d", info->bt_wakeup_gpio);
+	BT_DBG("HOSTWU gpio: %d", info->host_wakeup_gpio);
+	BT_DBG("sysclk: %d", info->bt_sysclk);
+
+	init_completion(&info->test_completion);
+	complete_all(&info->test_completion);
+
+	if (!info->reset_gpio_shared) {
+		err = devm_gpio_request_one(&pdev->dev, info->reset_gpio,
+					    GPIOF_OUT_INIT_LOW, "bt_reset");
+		if (err < 0) {
+			dev_err(&pdev->dev, "Cannot get GPIO line %d\n",
+				info->reset_gpio);
+			return err;
+		}
+	}
+
+	err = devm_gpio_request_one(&pdev->dev, info->bt_wakeup_gpio,
+				    GPIOF_OUT_INIT_LOW, "bt_wakeup");
+
+	if (err < 0) {
+		dev_err(info->dev, "Cannot get GPIO line 0x%d",
+			info->bt_wakeup_gpio);
+		return err;
+	}
+
+	err = devm_gpio_request_one(&pdev->dev, info->host_wakeup_gpio,
+				    GPIOF_DIR_IN, "host_wakeup");
+	if (err < 0) {
+		dev_err(info->dev, "Cannot get GPIO line %d",
+		       info->host_wakeup_gpio);
+		return err;
+	}
+
+	info->irq = bt_plat_data->uart_irq;
+	info->uart_base = devm_ioremap(&pdev->dev, bt_plat_data->uart_base, SZ_2K);
+	info->uart_iclk = devm_clk_get(&pdev->dev, bt_plat_data->uart_iclk);
+	info->uart_fclk = devm_clk_get(&pdev->dev, bt_plat_data->uart_fclk);
+
+	err = devm_request_irq(&pdev->dev, info->irq, hci_h4p_interrupt, IRQF_DISABLED,
+			       "hci_h4p", info);
+	if (err < 0) {
+		dev_err(info->dev, "hci_h4p: unable to get IRQ %d\n", info->irq);
+		return err;
+	}
+
+	err = devm_request_irq(&pdev->dev, gpio_to_irq(info->host_wakeup_gpio),
+			  hci_h4p_wakeup_interrupt,  IRQF_TRIGGER_FALLING |
+			  IRQF_TRIGGER_RISING | IRQF_DISABLED,
+			  "hci_h4p_wkup", info);
+	if (err < 0) {
+		dev_err(info->dev, "hci_h4p: unable to get wakeup IRQ %d\n",
+			  gpio_to_irq(info->host_wakeup_gpio));
+		return err;
+	}
+
+	err = irq_set_irq_wake(gpio_to_irq(info->host_wakeup_gpio), 1);
+	if (err < 0) {
+		dev_err(info->dev, "hci_h4p: unable to set wakeup for IRQ %d\n",
+				gpio_to_irq(info->host_wakeup_gpio));
+		return err;
+	}
+
+	init_timer_deferrable(&info->lazy_release);
+	info->lazy_release.function = hci_h4p_lazy_clock_release;
+	info->lazy_release.data = (unsigned long)info;
+	hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+	err = hci_h4p_reset_uart(info);
+	if (err < 0)
+		return err;
+	gpio_set_value(info->reset_gpio, 0);
+	hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+
+	platform_set_drvdata(pdev, info);
+
+	if (hci_h4p_register_hdev(info) < 0) {
+		dev_err(info->dev, "failed to register hci_h4p hci device\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hci_h4p_remove(struct platform_device *pdev)
+{
+	struct hci_h4p_info *info;
+
+	info = platform_get_drvdata(pdev);
+
+	hci_h4p_sysfs_remove_files(info->dev);
+	hci_h4p_hci_close(info->hdev);
+	hci_unregister_dev(info->hdev);
+	hci_free_dev(info->hdev);
+
+	return 0;
+}
+
+static struct platform_driver hci_h4p_driver = {
+	.probe		= hci_h4p_probe,
+	.remove		= hci_h4p_remove,
+	.driver		= {
+		.name	= "hci_h4p",
+	},
+};
+
+module_platform_driver(hci_h4p_driver);
+
+MODULE_ALIAS("platform:hci_h4p");
+MODULE_DESCRIPTION("Bluetooth h4 driver with nokia extensions");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ville Tervo");
+MODULE_FIRMWARE(FW_NAME_TI1271_PRELE);
+MODULE_FIRMWARE(FW_NAME_TI1271_LE);
+MODULE_FIRMWARE(FW_NAME_TI1271);
+MODULE_FIRMWARE(FW_NAME_BCM2048);
+MODULE_FIRMWARE(FW_NAME_CSR);
diff --git a/drivers/staging/nokia_h4p/nokia_fw-bcm.c b/drivers/staging/nokia_h4p/nokia_fw-bcm.c
new file mode 100644
index 0000000..e8912bf
--- /dev/null
+++ b/drivers/staging/nokia_h4p/nokia_fw-bcm.c
@@ -0,0 +1,147 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * Copyright (C) 2005-2008 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/serial_reg.h>
+
+#include "hci_h4p.h"
+
+static int hci_h4p_bcm_set_bdaddr(struct hci_h4p_info *info, struct sk_buff *skb)
+{
+	int i;
+	static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
+	int not_valid;
+
+	not_valid = 1;
+	for (i = 0; i < 6; i++) {
+		if (info->bd_addr[i] != 0x00) {
+			not_valid = 0;
+			break;
+		}
+	}
+
+	if (not_valid) {
+		dev_info(info->dev, "Valid bluetooth address not found, setting some random\n");
+		/* When address is not valid, use some random but Nokia MAC */
+		memcpy(info->bd_addr, nokia_oui, 3);
+		get_random_bytes(info->bd_addr + 3, 3);
+	}
+
+	for (i = 0; i < 6; i++)
+		skb->data[9 - i] = info->bd_addr[i];
+
+	return 0;
+}
+
+void hci_h4p_bcm_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
+{
+	struct sk_buff *fw_skb;
+	int err;
+	unsigned long flags;
+
+	if (skb->data[5] != 0x00) {
+		dev_err(info->dev, "Firmware sending command failed 0x%.2x\n",
+			skb->data[5]);
+		info->fw_error = -EPROTO;
+	}
+
+	kfree_skb(skb);
+
+	fw_skb = skb_dequeue(info->fw_q);
+	if (fw_skb == NULL || info->fw_error) {
+		complete(&info->fw_completion);
+		return;
+	}
+
+	if (fw_skb->data[1] == 0x01 && fw_skb->data[2] == 0xfc && fw_skb->len >= 10) {
+		BT_DBG("Setting bluetooth address");
+		err = hci_h4p_bcm_set_bdaddr(info, fw_skb);
+		if (err < 0) {
+			kfree_skb(fw_skb);
+			info->fw_error = err;
+			complete(&info->fw_completion);
+			return;
+		}
+	}
+
+	skb_queue_tail(&info->txq, fw_skb);
+	spin_lock_irqsave(&info->lock, flags);
+	hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+			UART_IER_THRI);
+	spin_unlock_irqrestore(&info->lock, flags);
+}
+
+
+int hci_h4p_bcm_send_fw(struct hci_h4p_info *info,
+			struct sk_buff_head *fw_queue)
+{
+	struct sk_buff *skb;
+	unsigned long flags, time;
+
+	info->fw_error = 0;
+
+	BT_DBG("Sending firmware");
+
+	time = jiffies;
+
+	info->fw_q = fw_queue;
+	skb = skb_dequeue(fw_queue);
+	if (!skb)
+		return -ENODATA;
+
+	BT_DBG("Sending commands");
+
+	/*
+	 * Disable smart-idle as UART TX interrupts
+	 * are not wake-up capable
+	 */
+	hci_h4p_smart_idle(info, 0);
+
+	/* Check if this is bd_address packet */
+	init_completion(&info->fw_completion);
+	skb_queue_tail(&info->txq, skb);
+	spin_lock_irqsave(&info->lock, flags);
+	hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+			UART_IER_THRI);
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	if (!wait_for_completion_timeout(&info->fw_completion,
+				msecs_to_jiffies(2000))) {
+		dev_err(info->dev, "No reply to fw command\n");
+		return -ETIMEDOUT;
+	}
+
+	if (info->fw_error) {
+		dev_err(info->dev, "FW error\n");
+		return -EPROTO;
+	}
+
+	BT_DBG("Firmware sent in %d msecs",
+		   jiffies_to_msecs(jiffies-time));
+
+	hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
+	hci_h4p_set_rts(info, 0);
+	hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
+	hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
+
+	return 0;
+}
diff --git a/drivers/staging/nokia_h4p/nokia_fw-csr.c b/drivers/staging/nokia_h4p/nokia_fw-csr.c
new file mode 100644
index 0000000..e39c4a3
--- /dev/null
+++ b/drivers/staging/nokia_h4p/nokia_fw-csr.c
@@ -0,0 +1,150 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * Copyright (C) 2005-2008 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/serial_reg.h>
+
+#include "hci_h4p.h"
+
+void hci_h4p_bc4_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
+{
+	/* Check if this is fw packet */
+	if (skb->data[0] != 0xff) {
+		hci_recv_frame(info->hdev, skb);
+		return;
+	}
+
+	if (skb->data[11] || skb->data[12]) {
+		dev_err(info->dev, "Firmware sending command failed\n");
+		info->fw_error = -EPROTO;
+	}
+
+	kfree_skb(skb);
+	complete(&info->fw_completion);
+}
+
+int hci_h4p_bc4_send_fw(struct hci_h4p_info *info,
+			struct sk_buff_head *fw_queue)
+{
+	static const u8 nokia_oui[3] = {0x00, 0x19, 0x4F};
+	struct sk_buff *skb;
+	unsigned int offset;
+	int retries, count, i, not_valid;
+	unsigned long flags;
+
+	info->fw_error = 0;
+
+	BT_DBG("Sending firmware");
+	skb = skb_dequeue(fw_queue);
+
+	if (!skb)
+		return -ENOMSG;
+
+	/* Check if this is bd_address packet */
+	if (skb->data[15] == 0x01 && skb->data[16] == 0x00) {
+		offset = 21;
+		skb->data[offset + 1] = 0x00;
+		skb->data[offset + 5] = 0x00;
+
+		not_valid = 1;
+		for (i = 0; i < 6; i++) {
+			if (info->bd_addr[i] != 0x00) {
+				not_valid = 0;
+				break;
+			}
+		}
+
+		if (not_valid) {
+			dev_info(info->dev, "Valid bluetooth address not found,"
+					" setting some random\n");
+			/* When address is not valid, use some random */
+			memcpy(info->bd_addr, nokia_oui, 3);
+			get_random_bytes(info->bd_addr + 3, 3);
+		}
+
+		skb->data[offset + 7] = info->bd_addr[0];
+		skb->data[offset + 6] = info->bd_addr[1];
+		skb->data[offset + 4] = info->bd_addr[2];
+		skb->data[offset + 0] = info->bd_addr[3];
+		skb->data[offset + 3] = info->bd_addr[4];
+		skb->data[offset + 2] = info->bd_addr[5];
+	}
+
+	for (count = 1; ; count++) {
+		BT_DBG("Sending firmware command %d", count);
+		init_completion(&info->fw_completion);
+		skb_queue_tail(&info->txq, skb);
+		spin_lock_irqsave(&info->lock, flags);
+		hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+							 UART_IER_THRI);
+		spin_unlock_irqrestore(&info->lock, flags);
+
+		skb = skb_dequeue(fw_queue);
+		if (!skb)
+			break;
+
+		if (!wait_for_completion_timeout(&info->fw_completion,
+						 msecs_to_jiffies(1000))) {
+			dev_err(info->dev, "No reply to fw command\n");
+			return -ETIMEDOUT;
+		}
+
+		if (info->fw_error) {
+			dev_err(info->dev, "FW error\n");
+			return -EPROTO;
+		}
+	};
+
+	/* Wait for chip warm reset */
+	retries = 100;
+	while ((!skb_queue_empty(&info->txq) ||
+	       !(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT)) &&
+	       retries--) {
+		msleep(10);
+	}
+	if (!retries) {
+		dev_err(info->dev, "Transmitter not empty\n");
+		return -ETIMEDOUT;
+	}
+
+	hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
+
+	if (hci_h4p_wait_for_cts(info, 1, 100)) {
+		dev_err(info->dev, "cts didn't deassert after final speed\n");
+		return -ETIMEDOUT;
+	}
+
+	retries = 100;
+	do {
+		init_completion(&info->init_completion);
+		hci_h4p_send_alive_packet(info);
+		retries--;
+	} while (!wait_for_completion_timeout(&info->init_completion, 100) &&
+		 retries > 0);
+
+	if (!retries) {
+		dev_err(info->dev, "No alive reply after speed change\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
diff --git a/drivers/staging/nokia_h4p/nokia_fw-ti1273.c b/drivers/staging/nokia_h4p/nokia_fw-ti1273.c
new file mode 100644
index 0000000..f5500f7
--- /dev/null
+++ b/drivers/staging/nokia_h4p/nokia_fw-ti1273.c
@@ -0,0 +1,110 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * Copyright (C) 2009 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/serial_reg.h>
+
+#include "hci_h4p.h"
+
+static struct sk_buff_head *fw_q;
+
+void hci_h4p_ti1273_parse_fw_event(struct hci_h4p_info *info,
+			struct sk_buff *skb)
+{
+	struct sk_buff *fw_skb;
+	unsigned long flags;
+
+	if (skb->data[5] != 0x00) {
+		dev_err(info->dev, "Firmware sending command failed 0x%.2x\n",
+			skb->data[5]);
+		info->fw_error = -EPROTO;
+	}
+
+	kfree_skb(skb);
+
+	fw_skb = skb_dequeue(fw_q);
+	if (fw_skb == NULL || info->fw_error) {
+		complete(&info->fw_completion);
+		return;
+	}
+
+	skb_queue_tail(&info->txq, fw_skb);
+	spin_lock_irqsave(&info->lock, flags);
+	hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+			UART_IER_THRI);
+	spin_unlock_irqrestore(&info->lock, flags);
+}
+
+
+int hci_h4p_ti1273_send_fw(struct hci_h4p_info *info,
+			struct sk_buff_head *fw_queue)
+{
+	struct sk_buff *skb;
+	unsigned long flags, time;
+
+	info->fw_error = 0;
+
+	BT_DBG("Sending firmware");
+
+	time = jiffies;
+
+	fw_q = fw_queue;
+	skb = skb_dequeue(fw_queue);
+	if (!skb)
+		return -ENODATA;
+
+	BT_DBG("Sending commands");
+	/* Check if this is bd_address packet */
+	init_completion(&info->fw_completion);
+	hci_h4p_smart_idle(info, 0);
+	skb_queue_tail(&info->txq, skb);
+	spin_lock_irqsave(&info->lock, flags);
+	hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+			UART_IER_THRI);
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	if (!wait_for_completion_timeout(&info->fw_completion,
+				msecs_to_jiffies(2000))) {
+		dev_err(info->dev, "No reply to fw command\n");
+		return -ETIMEDOUT;
+	}
+
+	if (info->fw_error) {
+		dev_err(info->dev, "FW error\n");
+		return -EPROTO;
+	}
+
+	BT_DBG("Firmware sent in %d msecs",
+		   jiffies_to_msecs(jiffies-time));
+
+	hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
+	hci_h4p_set_rts(info, 0);
+	hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
+	if (hci_h4p_wait_for_cts(info, 1, 100)) {
+		dev_err(info->dev,
+			"cts didn't go down after final speed change\n");
+		return -ETIMEDOUT;
+	}
+	hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
+
+	return 0;
+}
diff --git a/drivers/staging/nokia_h4p/nokia_fw.c b/drivers/staging/nokia_h4p/nokia_fw.c
new file mode 100644
index 0000000..cfea61c
--- /dev/null
+++ b/drivers/staging/nokia_h4p/nokia_fw.c
@@ -0,0 +1,195 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation.
+ *
+ * Contact: Ville Tervo <ville.tervo@nokia.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/firmware.h>
+#include <linux/clk.h>
+
+#include <net/bluetooth/bluetooth.h>
+
+#include "hci_h4p.h"
+
+static int fw_pos;
+
+/* Firmware handling */
+static int hci_h4p_open_firmware(struct hci_h4p_info *info,
+				 const struct firmware **fw_entry)
+{
+	int err;
+
+	fw_pos = 0;
+	BT_DBG("Opening firmware man_id 0x%.2x ver_id 0x%.2x",
+			info->man_id, info->ver_id);
+	switch (info->man_id) {
+	case H4P_ID_TI1271:
+		switch (info->ver_id) {
+		case 0xe1:
+			err = request_firmware(fw_entry, FW_NAME_TI1271_PRELE,
+						info->dev);
+			break;
+		case 0xd1:
+		case 0xf1:
+			err = request_firmware(fw_entry, FW_NAME_TI1271_LE,
+						info->dev);
+			break;
+		default:
+			err = request_firmware(fw_entry, FW_NAME_TI1271,
+						info->dev);
+		}
+		break;
+	case H4P_ID_CSR:
+		err = request_firmware(fw_entry, FW_NAME_CSR, info->dev);
+		break;
+	case H4P_ID_BCM2048:
+		err = request_firmware(fw_entry, FW_NAME_BCM2048, info->dev);
+		break;
+	default:
+		dev_err(info->dev, "Invalid chip type\n");
+		*fw_entry = NULL;
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
+static void hci_h4p_close_firmware(const struct firmware *fw_entry)
+{
+	release_firmware(fw_entry);
+}
+
+/* Read fw. Return length of the command. If no more commands in
+ * fw 0 is returned. In error case return value is negative.
+ */
+static int hci_h4p_read_fw_cmd(struct hci_h4p_info *info, struct sk_buff **skb,
+			       const struct firmware *fw_entry, gfp_t how)
+{
+	unsigned int cmd_len;
+
+	if (fw_pos >= fw_entry->size)
+		return 0;
+
+	if (fw_pos + 2 > fw_entry->size) {
+		dev_err(info->dev, "Corrupted firmware image 1\n");
+		return -EMSGSIZE;
+	}
+
+	cmd_len = fw_entry->data[fw_pos++];
+	cmd_len += fw_entry->data[fw_pos++] << 8;
+	if (cmd_len == 0)
+		return 0;
+
+	if (fw_pos + cmd_len > fw_entry->size) {
+		dev_err(info->dev, "Corrupted firmware image 2\n");
+		return -EMSGSIZE;
+	}
+
+	*skb = bt_skb_alloc(cmd_len, how);
+	if (!*skb) {
+		dev_err(info->dev, "Cannot reserve memory for buffer\n");
+		return -ENOMEM;
+	}
+	memcpy(skb_put(*skb, cmd_len), &fw_entry->data[fw_pos], cmd_len);
+
+	fw_pos += cmd_len;
+
+	return (*skb)->len;
+}
+
+int hci_h4p_read_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue)
+{
+	const struct firmware *fw_entry = NULL;
+	struct sk_buff *skb = NULL;
+	int err;
+
+	err = hci_h4p_open_firmware(info, &fw_entry);
+	if (err < 0 || !fw_entry)
+		goto err_clean;
+
+	while ((err = hci_h4p_read_fw_cmd(info, &skb, fw_entry, GFP_KERNEL))) {
+		if (err < 0 || !skb)
+			goto err_clean;
+
+		skb_queue_tail(fw_queue, skb);
+	}
+
+	/* Chip detection code does neg and alive stuff
+	 * discard two first skbs */
+	skb = skb_dequeue(fw_queue);
+	if (!skb) {
+		err = -EMSGSIZE;
+		goto err_clean;
+	}
+	kfree_skb(skb);
+	skb = skb_dequeue(fw_queue);
+	if (!skb) {
+		err = -EMSGSIZE;
+		goto err_clean;
+	}
+	kfree_skb(skb);
+
+err_clean:
+	hci_h4p_close_firmware(fw_entry);
+	return err;
+}
+
+int hci_h4p_send_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue)
+{
+	int err;
+
+	switch (info->man_id) {
+	case H4P_ID_CSR:
+		err = hci_h4p_bc4_send_fw(info, fw_queue);
+		break;
+	case H4P_ID_TI1271:
+		err = hci_h4p_ti1273_send_fw(info, fw_queue);
+		break;
+	case H4P_ID_BCM2048:
+		err = hci_h4p_bcm_send_fw(info, fw_queue);
+		break;
+	default:
+		dev_err(info->dev, "Don't know how to send firmware\n");
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
+void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
+{
+	switch (info->man_id) {
+	case H4P_ID_CSR:
+		hci_h4p_bc4_parse_fw_event(info, skb);
+		break;
+	case H4P_ID_TI1271:
+		hci_h4p_ti1273_parse_fw_event(info, skb);
+		break;
+	case H4P_ID_BCM2048:
+		hci_h4p_bcm_parse_fw_event(info, skb);
+		break;
+	default:
+		dev_err(info->dev, "Don't know how to parse fw event\n");
+		info->fw_error = -EINVAL;
+	}
+
+	return;
+}
diff --git a/drivers/staging/nokia_h4p/nokia_uart.c b/drivers/staging/nokia_h4p/nokia_uart.c
new file mode 100644
index 0000000..0fb57de
--- /dev/null
+++ b/drivers/staging/nokia_h4p/nokia_uart.c
@@ -0,0 +1,199 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * Copyright (C) 2005, 2006 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/serial_reg.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <linux/io.h>
+
+#include "hci_h4p.h"
+
+inline void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val)
+{
+	__raw_writeb(val, info->uart_base + (offset << 2));
+}
+
+inline u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset)
+{
+	return __raw_readb(info->uart_base + (offset << 2));
+}
+
+void hci_h4p_set_rts(struct hci_h4p_info *info, int active)
+{
+	u8 b;
+
+	b = hci_h4p_inb(info, UART_MCR);
+	if (active)
+		b |= UART_MCR_RTS;
+	else
+		b &= ~UART_MCR_RTS;
+	hci_h4p_outb(info, UART_MCR, b);
+}
+
+int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active,
+			 int timeout_ms)
+{
+	unsigned long timeout;
+	int state;
+
+	timeout = jiffies + msecs_to_jiffies(timeout_ms);
+	for (;;) {
+		state = hci_h4p_inb(info, UART_MSR) & UART_MSR_CTS;
+		if (active) {
+			if (state)
+				return 0;
+		} else {
+			if (!state)
+				return 0;
+		}
+		if (time_after(jiffies, timeout))
+			return -ETIMEDOUT;
+		msleep(1);
+	}
+}
+
+void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which)
+{
+	u8 lcr, b;
+
+	lcr = hci_h4p_inb(info, UART_LCR);
+	hci_h4p_outb(info, UART_LCR, 0xbf);
+	b = hci_h4p_inb(info, UART_EFR);
+	if (on)
+		b |= which;
+	else
+		b &= ~which;
+	hci_h4p_outb(info, UART_EFR, b);
+	hci_h4p_outb(info, UART_LCR, lcr);
+}
+
+void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&info->lock, flags);
+	__hci_h4p_set_auto_ctsrts(info, on, which);
+	spin_unlock_irqrestore(&info->lock, flags);
+}
+
+void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed)
+{
+	unsigned int divisor;
+	u8 lcr, mdr1;
+
+	BT_DBG("Setting speed %lu", speed);
+
+	if (speed >= 460800) {
+		divisor = UART_CLOCK / 13 / speed;
+		mdr1 = 3;
+	} else {
+		divisor = UART_CLOCK / 16 / speed;
+		mdr1 = 0;
+	}
+
+	/* Make sure UART mode is disabled */
+	hci_h4p_outb(info, UART_OMAP_MDR1, 7);
+
+	lcr = hci_h4p_inb(info, UART_LCR);
+	hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);     /* Set DLAB */
+	hci_h4p_outb(info, UART_DLL, divisor & 0xff);    /* Set speed */
+	hci_h4p_outb(info, UART_DLM, divisor >> 8);
+	hci_h4p_outb(info, UART_LCR, lcr);
+
+	/* Make sure UART mode is enabled */
+	hci_h4p_outb(info, UART_OMAP_MDR1, mdr1);
+}
+
+int hci_h4p_reset_uart(struct hci_h4p_info *info)
+{
+	int count = 0;
+
+	/* Reset the UART */
+	hci_h4p_outb(info, UART_OMAP_SYSC, UART_SYSC_OMAP_RESET);
+	while (!(hci_h4p_inb(info, UART_OMAP_SYSS) & UART_SYSS_RESETDONE)) {
+		if (count++ > 100) {
+			dev_err(info->dev, "hci_h4p: UART reset timeout\n");
+			return -ENODEV;
+		}
+		udelay(1);
+	}
+
+	return 0;
+}
+
+void hci_h4p_store_regs(struct hci_h4p_info *info)
+{
+	u16 lcr = 0;
+
+	lcr = hci_h4p_inb(info, UART_LCR);
+	hci_h4p_outb(info, UART_LCR, 0xBF);
+	info->dll = hci_h4p_inb(info, UART_DLL);
+	info->dlh = hci_h4p_inb(info, UART_DLM);
+	info->efr = hci_h4p_inb(info, UART_EFR);
+	hci_h4p_outb(info, UART_LCR, lcr);
+	info->mdr1 = hci_h4p_inb(info, UART_OMAP_MDR1);
+	info->ier = hci_h4p_inb(info, UART_IER);
+}
+
+void hci_h4p_restore_regs(struct hci_h4p_info *info)
+{
+	u16 lcr = 0;
+
+	hci_h4p_init_uart(info);
+
+	hci_h4p_outb(info, UART_OMAP_MDR1, 7);
+	lcr = hci_h4p_inb(info, UART_LCR);
+	hci_h4p_outb(info, UART_LCR, 0xBF);
+	hci_h4p_outb(info, UART_DLL, info->dll);    /* Set speed */
+	hci_h4p_outb(info, UART_DLM, info->dlh);
+	hci_h4p_outb(info, UART_EFR, info->efr);
+	hci_h4p_outb(info, UART_LCR, lcr);
+	hci_h4p_outb(info, UART_OMAP_MDR1, info->mdr1);
+	hci_h4p_outb(info, UART_IER, info->ier);
+}
+
+void hci_h4p_init_uart(struct hci_h4p_info *info)
+{
+	u8 mcr, efr;
+
+	/* Enable and setup FIFO */
+	hci_h4p_outb(info, UART_OMAP_MDR1, 0x00);
+
+	hci_h4p_outb(info, UART_LCR, 0xbf);
+	efr = hci_h4p_inb(info, UART_EFR);
+	hci_h4p_outb(info, UART_EFR, UART_EFR_ECB);
+	hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);
+	mcr = hci_h4p_inb(info, UART_MCR);
+	hci_h4p_outb(info, UART_MCR, UART_MCR_TCRTLR);
+	hci_h4p_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO |
+			UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT |
+			(3 << 6) | (0 << 4));
+	hci_h4p_outb(info, UART_LCR, 0xbf);
+	hci_h4p_outb(info, UART_TI752_TLR, 0xed);
+	hci_h4p_outb(info, UART_TI752_TCR, 0xef);
+	hci_h4p_outb(info, UART_EFR, efr);
+	hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);
+	hci_h4p_outb(info, UART_MCR, 0x00);
+	hci_h4p_outb(info, UART_LCR, UART_LCR_WLEN8);
+	hci_h4p_outb(info, UART_IER, UART_IER_RDI);
+	hci_h4p_outb(info, UART_OMAP_SYSC, (1 << 0) | (1 << 2) | (2 << 3));
+}
diff --git a/include/linux/platform_data/bt-nokia-h4p.h b/include/linux/platform_data/bt-nokia-h4p.h
new file mode 100644
index 0000000..30d169d
--- /dev/null
+++ b/include/linux/platform_data/bt-nokia-h4p.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+
+/**
+ * struct hci_h4p_platform data - hci_h4p Platform data structure
+ */
+struct hci_h4p_platform_data {
+	int chip_type;
+	int bt_sysclk;
+	unsigned int bt_wakeup_gpio;
+	unsigned int host_wakeup_gpio;
+	unsigned int reset_gpio;
+	int reset_gpio_shared;
+	unsigned int uart_irq;
+	phys_addr_t uart_base;
+	const char *uart_iclk;
+	const char *uart_fclk;
+	void (*set_pm_limits)(struct device *dev, bool set);
+};

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply related

* Re: [PATCH v6] Bluetooth: Add hci_h4p driver
From: Pavel Machek @ 2014-01-17 12:14 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Pali Rohár,
	Ивайло Димитров,
	Gustavo F. Padovan, Johan Hedberg, linux-kernel,
	linux-bluetooth@vger.kernel.org development, Ville Tervo,
	Sebastian Reichel
In-Reply-To: <FD05FD52-1502-4AB8-8650-FC785B700088@holtmann.org>

Hi!

> > Add hci_h4p bluetooth driver to bluetooth-next. This device is used
> > for example on Nokia N900 cell phone.
> > 
> > Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
> > Signed-off-by: Pavel Machek <pavel@ucw.cz>
> > Thanks-to: Sebastian Reichel <sre@debian.org>
> > Thanks-to: Joe Perches <joe@perches.com>
> > 
> > ---
> > 
> > Changes from v5: Comment fixes and some refactoring suggested by 
> > Joe Perches. Please apply.

> > --- /dev/null
> > +++ b/drivers/bluetooth/hci_h4p.h
> 
> can we please get the naming straight. File names do not start with hci_ anymore. We moved away from it since that term is too generic.
> 

Ok, this is easy.

> > +	struct timer_list lazy_release;
> 
> Timer? Not delayed work?

But these changes sound like bigger piece of refactoring, and more is
suggested below. Which is good, but will be tricky to develop/test on
bluetooth-next, as running Maemo is really needed for testing.

So... this will take longer than expected, and I believe it makes
sense to push it to staging, first. That will bring linux-n900 tree
closer to mainline, and preserve more of history.

Regards,
								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply

* [PATCH 3/3] unit: prevent use of glibc's error(3)
From: Natanael Copa @ 2014-01-17 12:08 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Natanael Copa
In-Reply-To: <1389960506-10273-1-git-send-email-ncopa@alpinelinux.org>

When building the test-sdp we don't want src/sdpd-request.c end up
using the incompatible GNU libc's error(3).

This also fixes building on musl libc which misses the error(3) GNU
extension.
---
 unit/test-sdp.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/unit/test-sdp.c b/unit/test-sdp.c
index 6d699e2..eeed0cb 100644
--- a/unit/test-sdp.c
+++ b/unit/test-sdp.c
@@ -134,6 +134,12 @@ void btd_debug(const char *format, ...)
 {
 }
 
+void error(const char *format, ...);
+
+void error(const char *format, ...)
+{
+}
+
 static void context_quit(struct context *context)
 {
 	g_main_loop_quit(context->main_loop);
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH 2/3] various header include fixes for building with musl libc
From: Natanael Copa @ 2014-01-17 12:08 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Natanael Copa
In-Reply-To: <1389960506-10273-1-git-send-email-ncopa@alpinelinux.org>

we need:
 sys/stat.h for mode_t
 limits.h for PATH_MAX
---
 src/textfile.h  | 2 ++
 tools/csr_usb.c | 1 +
 tools/hid2hci.c | 1 +
 3 files changed, 4 insertions(+)

diff --git a/src/textfile.h b/src/textfile.h
index b779bd2..e26da5d 100644
--- a/src/textfile.h
+++ b/src/textfile.h
@@ -24,6 +24,8 @@
 #ifndef __TEXTFILE_H
 #define __TEXTFILE_H
 
+#include <sys/stat.h>
+
 int create_file(const char *filename, const mode_t mode);
 int create_name(char *buf, size_t size, const char *path,
 				const char *address, const char *name);
diff --git a/tools/csr_usb.c b/tools/csr_usb.c
index a483bc1..5fb6bdc 100644
--- a/tools/csr_usb.c
+++ b/tools/csr_usb.c
@@ -33,6 +33,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <dirent.h>
+#include <limits.h>
 #include <sys/ioctl.h>
 
 #include "csr.h"
diff --git a/tools/hid2hci.c b/tools/hid2hci.c
index 95b4abf..2dbfca7 100644
--- a/tools/hid2hci.c
+++ b/tools/hid2hci.c
@@ -35,6 +35,7 @@
 #include <string.h>
 #include <dirent.h>
 #include <getopt.h>
+#include <limits.h>
 #include <sys/ioctl.h>
 #include <linux/types.h>
 #include <linux/hiddev.h>
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH 1/3] bnep: avoid use of caddr_t
From: Natanael Copa @ 2014-01-17 12:08 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Natanael Copa

caddr_t is legacy BSD and should be avoided.

This fixes building against musl libc.
---
 profiles/network/bnep.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/profiles/network/bnep.c b/profiles/network/bnep.c
index 2a74016..4f9b801 100644
--- a/profiles/network/bnep.c
+++ b/profiles/network/bnep.c
@@ -202,7 +202,7 @@ static int bnep_if_up(const char *devname)
 	ifr.ifr_flags |= IFF_UP;
 	ifr.ifr_flags |= IFF_MULTICAST;
 
-	err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+	err = ioctl(sk, SIOCSIFFLAGS, (void *) &ifr);
 
 	close(sk);
 
@@ -227,7 +227,7 @@ static int bnep_if_down(const char *devname)
 	ifr.ifr_flags &= ~IFF_UP;
 
 	/* Bring down the interface */
-	err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+	err = ioctl(sk, SIOCSIFFLAGS, (void *) &ifr);
 
 	close(sk);
 
-- 
1.8.5.3


^ permalink raw reply related

* Re: Dualshock4 - 'HIDP: Handshake: Unsupported request' after 'unplug virtual cable'.
From: David Herrmann @ 2014-01-17 10:16 UTC (permalink / raw)
  To: Simon Wood; +Cc: linux-bluetooth@vger.kernel.org, Frank Praznik
In-Reply-To: <8ccf0ad88f16335719c367ed69c90a3e.squirrel@mungewell.org>

Hi

On Fri, Jan 17, 2014 at 6:09 AM,  <simon@mungewell.org> wrote:
> Hi all,
> Frank and I have been working on a kernel driver for the Dualshock 4 and
> we're having problems with the BT connection (USB is OK).
>
> When powered on the DS4 will initiate a connection to the PC once it knows
> it's Bdaddr (via pairing), but will power off shortly there after. This
> seems to be related to incorrect (??) data sent from Bluez, or if no hid
> connection is made within a short window.
>
> On the two systems I have tried:
> 1) Debian with bluez 4.99, I can 'hidd --connect xxx' to hold the
> connection. I get joystick data but can't send data to DS4 to drive
> ff/leds. (I am told that 'hidd' is deprated)
>
> 2) Fedora LiveCD with bluez 5.13. Connection is immediately terminated by
> the controller. HCI log attached.
>
> It appears that the controller is insisting on a encrypted link ('noenc'
> and 'noauth' make no difference) and does not like the 'unplug virtual
> cable' which is sent (presumably as the link is being brought down to
> re-start encrypted).

Could you provide a bluetoothd log, too? I suspect it contains a
message along the line:
  "Refusing input device connect: %s (%d)"

"connect_ds4_fedora_4.txt" contains all the important information.
I'll just summarize what it does:
 1. The remote device initiates a base-band connection to your host
 2. Your host fetches all SDP records from the device (L2CAP PSM: 1)
 3. The SDP connection is closed
 4. Link-key negotiation is successful (=> you already were connected
to the device before)
 5. encryption-mode is changed successfully (not sure whether it's
turned ON or OFF, but I guess "encrypt 0x01" means ON)
 6. device initiates L2CAP on PSM 0x17 (HIDP control channel)
 7. Host sends "HIDP: Control: Virtual cable unplug" on the HIDP control channel
 8. host closes L2CAP PSM 0x17
 9. device initiates L2CAP on PSM 0x19 (HIDP intr channel)
10. host refuses connection on PSM 0x19
11. device closes baseband connection

So the problem is, your host refuses the HIDP connection from the
device. This is intentional, as we don't want *any* remote Bluetooth
device to be allowed to provide HID services. This could be used for
very subtle attacks. The code responsible for that is
./profiles/input/server.c in bluez.

Now, there are two ways to go on:
 - try to initiate the HIDP connection from the host instead of the
device. Simply use "bluetoothctl" and then "connect <bdaddr>"
 - add the remote device to the list of known devices so incoming
connections are not blocked. You can do this by triggering the
->device_probe() callback of the input device. I'm not sure how this
is done, but I think a simple "connect <bdaddr>" should trigger it on
*all* available services.

I hope that helps. If not, we can try to add the device manually.
Cheers
David

> In the BT HID 1.1 spec (page 48) it says
> --
> If the HIDVirtualCable SDP attribute is set to TRUE, then a Virtual Cable
> is considered to be established after both the HID Control and HID
> Interrupt L2CAP channels have been opened.
> --
>
> However the Fedora system does not have a copy of the SDP records for the
> controller, it never pulled them and I have never managed to make a
> connection long enough to read them.
>
> My theory is that the controller is rejecting the 'Unplug Virtual Cable'
> command as it shouldn't have been established, but I'm not really sure.
>
>
>
> I managed use my Debian system to read SDP records but that barfs on
> what's sent.... files also attached.
>
> Does anyone have any suggestions on what I should try next?
> Thanks, Simon
>
>
>
>

^ permalink raw reply

* [PATCH v3 2/2] Bluetooth: Queue incoming ACL data until BT_CONNECTED state is reached
From: johan.hedberg @ 2014-01-17  9:33 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1389951206-22294-1-git-send-email-johan.hedberg@gmail.com>

From: Johan Hedberg <johan.hedberg@intel.com>

This patch adds a queue for incoming L2CAP data that's received before
l2cap_connect_cfm is called and processes the data once
l2cap_connect_cfm is called. This way we ensure that we have e.g. all
remote features before processing L2CAP signaling data (which is very
important for making the correct security decisions).

The processing of the pending rx data needs to be done through
queue_work since unlike l2cap_recv_acldata, l2cap_connect_cfm is called
with the hci_dev lock held which could cause potential deadlocks.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
 include/net/bluetooth/l2cap.h |  3 +++
 net/bluetooth/l2cap_core.c    | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index c695083eee2b..85cf40acc47e 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -624,6 +624,9 @@ struct l2cap_conn {
 	__u32			rx_len;
 	__u8			tx_ident;
 
+	struct sk_buff_head	pending_rx;
+	struct work_struct	pending_rx_work;
+
 	__u8			disc_reason;
 
 	struct delayed_work	security_timer;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 317a5737daf6..fa59a32b0497 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1550,6 +1550,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
 	}
 
 	mutex_unlock(&conn->chan_lock);
+
+	queue_work(hcon->hdev->workqueue, &conn->pending_rx_work);
 }
 
 /* Notify sockets that we cannot guaranty reliability anymore */
@@ -1675,6 +1677,9 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
 
 	kfree_skb(conn->rx_skb);
 
+	skb_queue_purge(&conn->pending_rx);
+	flush_work(&conn->pending_rx_work);
+
 	l2cap_unregister_all_users(conn);
 
 	mutex_lock(&conn->chan_lock);
@@ -6880,9 +6885,16 @@ drop:
 static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
 {
 	struct l2cap_hdr *lh = (void *) skb->data;
+	struct hci_conn *hcon = conn->hcon;
 	u16 cid, len;
 	__le16 psm;
 
+	if (hcon->state != BT_CONNECTED) {
+		BT_DBG("queueing pending rx skb");
+		skb_queue_tail(&conn->pending_rx, skb);
+		return;
+	}
+
 	skb_pull(skb, L2CAP_HDR_SIZE);
 	cid = __le16_to_cpu(lh->cid);
 	len = __le16_to_cpu(lh->len);
@@ -6928,6 +6940,24 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
 	}
 }
 
+static void process_pending_rx(struct work_struct *work)
+{
+	struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
+					       pending_rx_work);
+
+	BT_DBG("");
+
+	while (1) {
+		struct sk_buff *skb;
+
+		skb = skb_dequeue(&conn->pending_rx);
+		if (!skb)
+			break;
+
+		l2cap_recv_frame(conn, skb);
+	}
+}
+
 static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
 {
 	struct l2cap_conn *conn = hcon->l2cap_data;
@@ -6983,6 +7013,9 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
 	else
 		INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
 
+	skb_queue_head_init(&conn->pending_rx);
+	INIT_WORK(&conn->pending_rx_work, process_pending_rx);
+
 	conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
 
 	return conn;
-- 
1.8.4.2


^ permalink raw reply related

* [PATCH v3 1/2] Bluetooth: Reorder L2CAP functions to avoid forward declarations
From: johan.hedberg @ 2014-01-17  9:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Johan Hedberg <johan.hedberg@intel.com>

This patch moves the l2cap_conn_add, is_valid_psm and l2cap_chan_connect
functions further down in l2cap_core.c. The patch doesn't contain
anything else except the relocation of these functions. By moving the
functions further down the patch enables a subsequent patch that adds a
pending RX queue to be implemented without a forward declaration of a
function.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
 net/bluetooth/l2cap_core.c | 415 ++++++++++++++++++++++-----------------------
 1 file changed, 207 insertions(+), 208 deletions(-)

diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 3f0dd552cb2b..317a5737daf6 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1722,66 +1722,6 @@ static void security_timeout(struct work_struct *work)
 	}
 }
 
-static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
-{
-	struct l2cap_conn *conn = hcon->l2cap_data;
-	struct hci_chan *hchan;
-
-	if (conn)
-		return conn;
-
-	hchan = hci_chan_create(hcon);
-	if (!hchan)
-		return NULL;
-
-	conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL);
-	if (!conn) {
-		hci_chan_del(hchan);
-		return NULL;
-	}
-
-	kref_init(&conn->ref);
-	hcon->l2cap_data = conn;
-	conn->hcon = hcon;
-	hci_conn_get(conn->hcon);
-	conn->hchan = hchan;
-
-	BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
-
-	switch (hcon->type) {
-	case LE_LINK:
-		if (hcon->hdev->le_mtu) {
-			conn->mtu = hcon->hdev->le_mtu;
-			break;
-		}
-		/* fall through */
-	default:
-		conn->mtu = hcon->hdev->acl_mtu;
-		break;
-	}
-
-	conn->feat_mask = 0;
-
-	if (hcon->type == ACL_LINK)
-		conn->hs_enabled = test_bit(HCI_HS_ENABLED,
-					    &hcon->hdev->dev_flags);
-
-	spin_lock_init(&conn->lock);
-	mutex_init(&conn->chan_lock);
-
-	INIT_LIST_HEAD(&conn->chan_l);
-	INIT_LIST_HEAD(&conn->users);
-
-	if (hcon->type == LE_LINK)
-		INIT_DELAYED_WORK(&conn->security_timer, security_timeout);
-	else
-		INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
-
-	conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
-
-	return conn;
-}
-
 static void l2cap_conn_free(struct kref *ref)
 {
 	struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref);
@@ -1852,154 +1792,6 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
 	return c1;
 }
 
-static bool is_valid_psm(u16 psm, u8 dst_type)
-{
-	if (!psm)
-		return false;
-
-	if (bdaddr_type_is_le(dst_type))
-		return (psm <= 0x00ff);
-
-	/* PSM must be odd and lsb of upper byte must be 0 */
-	return ((psm & 0x0101) == 0x0001);
-}
-
-int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
-		       bdaddr_t *dst, u8 dst_type)
-{
-	struct l2cap_conn *conn;
-	struct hci_conn *hcon;
-	struct hci_dev *hdev;
-	__u8 auth_type;
-	int err;
-
-	BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
-	       dst_type, __le16_to_cpu(psm));
-
-	hdev = hci_get_route(dst, &chan->src);
-	if (!hdev)
-		return -EHOSTUNREACH;
-
-	hci_dev_lock(hdev);
-
-	l2cap_chan_lock(chan);
-
-	if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
-	    chan->chan_type != L2CAP_CHAN_RAW) {
-		err = -EINVAL;
-		goto done;
-	}
-
-	if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(psm || cid)) {
-		err = -EINVAL;
-		goto done;
-	}
-
-	switch (chan->mode) {
-	case L2CAP_MODE_BASIC:
-		break;
-	case L2CAP_MODE_LE_FLOWCTL:
-		l2cap_le_flowctl_init(chan);
-		break;
-	case L2CAP_MODE_ERTM:
-	case L2CAP_MODE_STREAMING:
-		if (!disable_ertm)
-			break;
-		/* fall through */
-	default:
-		err = -ENOTSUPP;
-		goto done;
-	}
-
-	switch (chan->state) {
-	case BT_CONNECT:
-	case BT_CONNECT2:
-	case BT_CONFIG:
-		/* Already connecting */
-		err = 0;
-		goto done;
-
-	case BT_CONNECTED:
-		/* Already connected */
-		err = -EISCONN;
-		goto done;
-
-	case BT_OPEN:
-	case BT_BOUND:
-		/* Can connect */
-		break;
-
-	default:
-		err = -EBADFD;
-		goto done;
-	}
-
-	/* Set destination address and psm */
-	bacpy(&chan->dst, dst);
-	chan->dst_type = dst_type;
-
-	chan->psm = psm;
-	chan->dcid = cid;
-
-	auth_type = l2cap_get_auth_type(chan);
-
-	if (bdaddr_type_is_le(dst_type))
-		hcon = hci_connect(hdev, LE_LINK, dst, dst_type,
-				   chan->sec_level, auth_type);
-	else
-		hcon = hci_connect(hdev, ACL_LINK, dst, dst_type,
-				   chan->sec_level, auth_type);
-
-	if (IS_ERR(hcon)) {
-		err = PTR_ERR(hcon);
-		goto done;
-	}
-
-	conn = l2cap_conn_add(hcon);
-	if (!conn) {
-		hci_conn_drop(hcon);
-		err = -ENOMEM;
-		goto done;
-	}
-
-	if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
-		hci_conn_drop(hcon);
-		err = -EBUSY;
-		goto done;
-	}
-
-	/* Update source addr of the socket */
-	bacpy(&chan->src, &hcon->src);
-	chan->src_type = bdaddr_type(hcon, hcon->src_type);
-
-	l2cap_chan_unlock(chan);
-	l2cap_chan_add(conn, chan);
-	l2cap_chan_lock(chan);
-
-	/* l2cap_chan_add takes its own ref so we can drop this one */
-	hci_conn_drop(hcon);
-
-	l2cap_state_change(chan, BT_CONNECT);
-	__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
-
-	if (hcon->state == BT_CONNECTED) {
-		if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
-			__clear_chan_timer(chan);
-			if (l2cap_chan_check_security(chan))
-				l2cap_state_change(chan, BT_CONNECTED);
-		} else
-			l2cap_do_start(chan);
-	}
-
-	err = 0;
-
-done:
-	l2cap_chan_unlock(chan);
-	hci_dev_unlock(hdev);
-	hci_dev_put(hdev);
-	return err;
-}
-
 static void l2cap_monitor_timeout(struct work_struct *work)
 {
 	struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
@@ -7136,6 +6928,213 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
 	}
 }
 
+static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
+{
+	struct l2cap_conn *conn = hcon->l2cap_data;
+	struct hci_chan *hchan;
+
+	if (conn)
+		return conn;
+
+	hchan = hci_chan_create(hcon);
+	if (!hchan)
+		return NULL;
+
+	conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL);
+	if (!conn) {
+		hci_chan_del(hchan);
+		return NULL;
+	}
+
+	kref_init(&conn->ref);
+	hcon->l2cap_data = conn;
+	conn->hcon = hcon;
+	hci_conn_get(conn->hcon);
+	conn->hchan = hchan;
+
+	BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
+
+	switch (hcon->type) {
+	case LE_LINK:
+		if (hcon->hdev->le_mtu) {
+			conn->mtu = hcon->hdev->le_mtu;
+			break;
+		}
+		/* fall through */
+	default:
+		conn->mtu = hcon->hdev->acl_mtu;
+		break;
+	}
+
+	conn->feat_mask = 0;
+
+	if (hcon->type == ACL_LINK)
+		conn->hs_enabled = test_bit(HCI_HS_ENABLED,
+					    &hcon->hdev->dev_flags);
+
+	spin_lock_init(&conn->lock);
+	mutex_init(&conn->chan_lock);
+
+	INIT_LIST_HEAD(&conn->chan_l);
+	INIT_LIST_HEAD(&conn->users);
+
+	if (hcon->type == LE_LINK)
+		INIT_DELAYED_WORK(&conn->security_timer, security_timeout);
+	else
+		INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
+
+	conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
+
+	return conn;
+}
+
+static bool is_valid_psm(u16 psm, u8 dst_type) {
+	if (!psm)
+		return false;
+
+	if (bdaddr_type_is_le(dst_type))
+		return (psm <= 0x00ff);
+
+	/* PSM must be odd and lsb of upper byte must be 0 */
+	return ((psm & 0x0101) == 0x0001);
+}
+
+int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
+		       bdaddr_t *dst, u8 dst_type)
+{
+	struct l2cap_conn *conn;
+	struct hci_conn *hcon;
+	struct hci_dev *hdev;
+	__u8 auth_type;
+	int err;
+
+	BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
+	       dst_type, __le16_to_cpu(psm));
+
+	hdev = hci_get_route(dst, &chan->src);
+	if (!hdev)
+		return -EHOSTUNREACH;
+
+	hci_dev_lock(hdev);
+
+	l2cap_chan_lock(chan);
+
+	if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
+	    chan->chan_type != L2CAP_CHAN_RAW) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(psm || cid)) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	switch (chan->mode) {
+	case L2CAP_MODE_BASIC:
+		break;
+	case L2CAP_MODE_LE_FLOWCTL:
+		l2cap_le_flowctl_init(chan);
+		break;
+	case L2CAP_MODE_ERTM:
+	case L2CAP_MODE_STREAMING:
+		if (!disable_ertm)
+			break;
+		/* fall through */
+	default:
+		err = -ENOTSUPP;
+		goto done;
+	}
+
+	switch (chan->state) {
+	case BT_CONNECT:
+	case BT_CONNECT2:
+	case BT_CONFIG:
+		/* Already connecting */
+		err = 0;
+		goto done;
+
+	case BT_CONNECTED:
+		/* Already connected */
+		err = -EISCONN;
+		goto done;
+
+	case BT_OPEN:
+	case BT_BOUND:
+		/* Can connect */
+		break;
+
+	default:
+		err = -EBADFD;
+		goto done;
+	}
+
+	/* Set destination address and psm */
+	bacpy(&chan->dst, dst);
+	chan->dst_type = dst_type;
+
+	chan->psm = psm;
+	chan->dcid = cid;
+
+	auth_type = l2cap_get_auth_type(chan);
+
+	if (bdaddr_type_is_le(dst_type))
+		hcon = hci_connect(hdev, LE_LINK, dst, dst_type,
+				   chan->sec_level, auth_type);
+	else
+		hcon = hci_connect(hdev, ACL_LINK, dst, dst_type,
+				   chan->sec_level, auth_type);
+
+	if (IS_ERR(hcon)) {
+		err = PTR_ERR(hcon);
+		goto done;
+	}
+
+	conn = l2cap_conn_add(hcon);
+	if (!conn) {
+		hci_conn_drop(hcon);
+		err = -ENOMEM;
+		goto done;
+	}
+
+	if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
+		hci_conn_drop(hcon);
+		err = -EBUSY;
+		goto done;
+	}
+
+	/* Update source addr of the socket */
+	bacpy(&chan->src, &hcon->src);
+	chan->src_type = bdaddr_type(hcon, hcon->src_type);
+
+	l2cap_chan_unlock(chan);
+	l2cap_chan_add(conn, chan);
+	l2cap_chan_lock(chan);
+
+	/* l2cap_chan_add takes its own ref so we can drop this one */
+	hci_conn_drop(hcon);
+
+	l2cap_state_change(chan, BT_CONNECT);
+	__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
+
+	if (hcon->state == BT_CONNECTED) {
+		if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+			__clear_chan_timer(chan);
+			if (l2cap_chan_check_security(chan))
+				l2cap_state_change(chan, BT_CONNECTED);
+		} else
+			l2cap_do_start(chan);
+	}
+
+	err = 0;
+
+done:
+	l2cap_chan_unlock(chan);
+	hci_dev_unlock(hdev);
+	hci_dev_put(hdev);
+	return err;
+}
+
 /* ---- L2CAP interface with lower layer (HCI) ---- */
 
 int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
-- 
1.8.4.2


^ permalink raw reply related

* Re: [RFC 1/4] android: Add sample init.bluetooth.rc file
From: Szymon Janc @ 2014-01-17  9:06 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <19D4671A-DE2F-49A3-9FEE-F17B5B1CA32D@holtmann.org>

Hi Marcel,

On Friday 17 of January 2014 00:53:54 Marcel Holtmann wrote:
> Hi Szymon,
> 
> >>> This file is intended to be included from device init.rc.
> >>> ---
> >>> android/Android.mk        | 16 +++++++++++++++-
> >>> android/Makefile.am       |  1 +
> >>> android/init.bluetooth.rc | 27 +++++++++++++++++++++++++++
> >>> 3 files changed, 43 insertions(+), 1 deletion(-)
> >>> create mode 100644 android/init.bluetooth.rc
> >>> 
> >>> diff --git a/android/Android.mk b/android/Android.mk
> >>> index ae52ab4..f13e703 100644
> >>> --- a/android/Android.mk
> >>> +++ b/android/Android.mk
> >>> @@ -109,7 +109,7 @@ LOCAL_MODULE := bluetooth.default
> >>> LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
> >>> LOCAL_MODULE_TAGS := optional
> >>> LOCAL_MODULE_CLASS := SHARED_LIBRARIES
> >>> -LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop
> >>> +LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
> >>> 
> >>> include $(BUILD_SHARED_LIBRARY)
> >>> 
> >>> @@ -263,3 +263,17 @@ LOCAL_MODULE_TAGS := optional
> >>> LOCAL_MODULE := bluetoothd-snoop
> >>> 
> >>> include $(BUILD_EXECUTABLE)
> >>> +
> >>> +#
> >>> +# init.bleutooth.rc
> >>> +#
> >>> +
> >>> +include $(CLEAR_VARS)
> >>> +
> >>> +LOCAL_MODULE := init.bluetooth.rc
> >>> +LOCAL_MODULE_CLASS := ETC
> >>> +LOCAL_SRC_FILES := $(LOCAL_MODULE)
> >>> +LOCAL_MODULE_TAGS := optional
> >>> +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
> >>> +
> >>> +include $(BUILD_PREBUILT)
> >>> diff --git a/android/Makefile.am b/android/Makefile.am
> >>> index 7806f79..b205019 100644
> >>> --- a/android/Makefile.am
> >>> +++ b/android/Makefile.am
> >>> @@ -134,6 +134,7 @@ android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
> >>> endif
> >>> 
> >>> EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
> >>> +		android/init.bluetooth.rc \
> >>> 		android/pics-gap.txt android/pics-hid.txt \
> >>> 		android/pics-pan.txt android/pics-did.txt \
> >>> 		android/pics-opp.txt android/pics-pbap.txt \
> >>> diff --git a/android/init.bluetooth.rc b/android/init.bluetooth.rc
> >>> new file mode 100644
> >>> index 0000000..e2be9e8
> >>> --- /dev/null
> >>> +++ b/android/init.bluetooth.rc
> >>> @@ -0,0 +1,27 @@
> >>> +chown bluetooth bluetooth /data/misc/bluetooth
> >>> +
> >>> +chown bluetooth bluetooth /dev/uhid
> >>> +
> >>> +on property:bluetooth.start=bluetoothd
> >>> +    start bluetoothd
> >>> +
> >>> +on property:bluetooth.stop=bluetoothd
> >>> +    stop bluetoothd
> >>> +
> >>> +on property:bluetooth.start=bluetoothd-snoop
> >>> +    start bluetoothd-snoop
> >>> +
> >>> +on property:bluetooth.stop=bluetoothd-snoop
> >>> +    stop bluetoothd-snoop
> >> 
> >> I think this is actually racy. The ctl.start and ctl.stop sound like they have special handling inside Android init. If you accidentally set bluetooth.start , then bad things happen.
> > 
> > on property:foo=bar is also triggered if foo is already set to bar so I don't
> > see what could happen wrong here.
> > 
> >> 
> >> So I would propose doing bluetooth.daemon=on/off and bluetooth.snoop=on/off
> >> 
> >> We just have to make sure that when bluetoothd or bluetoothd-snoop die unexpectedly that these properties get set back to off.
> > 
> > I'm not sure if we are able to set properties back to proper state if service
> > exited in such case (that would trigger loop in init).
> 
> there is a service-exited-<name> type of action. I think we should look into using that one somehow.

I'll exercise that and send V2.

> 
> > My idea was to use bluetooth.start/stop to start/stop (actually stop is needed
> > only for bluetoothd-snoop service) service by bluetooth user (sort of "sudo"
> > wrapper) but don't care about values in those props and if one want to use
> > props to track service lifetime then just use init.svc.bluetoothd{-snoop}
> > property.
> 
> Fair enough.
> 
> However then I would prefer if we do bluetooth.{start,stop}=daemon and bluetooth.{start,stop}=snoop to make this really tight into how our solution works.

OK.

> 
> >>> +service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
> >>> +    class main
> >>> +    group bluetooth net_admin
> >>> +    disabled
> >>> +    oneshot
> >>> +
> >>> +service bluetoothd-snoop /system/bin/bluetoothd-snoop
> >>> +    class main
> >>> +    group bluetooth net_admin
> >>> +    disabled
> >>> +    oneshot
> >> 
> >> Does net_admin include CAP_NET_RAW actually or are we using some sort of patched module for this?
> > 
> > I'm not sure, this was probably just copied from <4.2 android. Since we
> > start as root and drop caps I think this can be removed.
> 
> The snoop process just needs CAP_NET_RAW and write access to the file location. I would prefer to really make sure it runs with as less right as possible.

OK.

-- 
Best regards, 
Szymon Janc

^ permalink raw reply

* Re: [RFC 1/4] android: Add sample init.bluetooth.rc file
From: Szymon Janc @ 2014-01-17  9:02 UTC (permalink / raw)
  To: Luiz Augusto von Dentz
  Cc: Marcel Holtmann, linux-bluetooth@vger.kernel.org development
In-Reply-To: <CABBYNZ+Pop6PooUd17gn29PrKfASiRm+QT6xGy0fen5ipg0-0A@mail.gmail.com>

Hi Luiz,

On Friday 17 of January 2014 10:56:36 Luiz Augusto von Dentz wrote:
> Hi Szymon,
> 
> On Fri, Jan 17, 2014 at 10:34 AM, Szymon Janc <szymon.janc@tieto.com> wrote:
> > Hi Marcel,
> >
> > On Tuesday 14 of January 2014 13:20:32 Marcel Holtmann wrote:
> >> Hi Szymon,
> >>
> >> > This file is intended to be included from device init.rc.
> >> > ---
> >> > android/Android.mk        | 16 +++++++++++++++-
> >> > android/Makefile.am       |  1 +
> >> > android/init.bluetooth.rc | 27 +++++++++++++++++++++++++++
> >> > 3 files changed, 43 insertions(+), 1 deletion(-)
> >> > create mode 100644 android/init.bluetooth.rc
> >> >
> >> > diff --git a/android/Android.mk b/android/Android.mk
> >> > index ae52ab4..f13e703 100644
> >> > --- a/android/Android.mk
> >> > +++ b/android/Android.mk
> >> > @@ -109,7 +109,7 @@ LOCAL_MODULE := bluetooth.default
> >> > LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
> >> > LOCAL_MODULE_TAGS := optional
> >> > LOCAL_MODULE_CLASS := SHARED_LIBRARIES
> >> > -LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop
> >> > +LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
> >> >
> >> > include $(BUILD_SHARED_LIBRARY)
> >> >
> >> > @@ -263,3 +263,17 @@ LOCAL_MODULE_TAGS := optional
> >> > LOCAL_MODULE := bluetoothd-snoop
> >> >
> >> > include $(BUILD_EXECUTABLE)
> >> > +
> >> > +#
> >> > +# init.bleutooth.rc
> >> > +#
> >> > +
> >> > +include $(CLEAR_VARS)
> >> > +
> >> > +LOCAL_MODULE := init.bluetooth.rc
> >> > +LOCAL_MODULE_CLASS := ETC
> >> > +LOCAL_SRC_FILES := $(LOCAL_MODULE)
> >> > +LOCAL_MODULE_TAGS := optional
> >> > +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
> >> > +
> >> > +include $(BUILD_PREBUILT)
> >> > diff --git a/android/Makefile.am b/android/Makefile.am
> >> > index 7806f79..b205019 100644
> >> > --- a/android/Makefile.am
> >> > +++ b/android/Makefile.am
> >> > @@ -134,6 +134,7 @@ android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
> >> > endif
> >> >
> >> > EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
> >> > +           android/init.bluetooth.rc \
> >> >             android/pics-gap.txt android/pics-hid.txt \
> >> >             android/pics-pan.txt android/pics-did.txt \
> >> >             android/pics-opp.txt android/pics-pbap.txt \
> >> > diff --git a/android/init.bluetooth.rc b/android/init.bluetooth.rc
> >> > new file mode 100644
> >> > index 0000000..e2be9e8
> >> > --- /dev/null
> >> > +++ b/android/init.bluetooth.rc
> >> > @@ -0,0 +1,27 @@
> >> > +chown bluetooth bluetooth /data/misc/bluetooth
> >> > +
> >> > +chown bluetooth bluetooth /dev/uhid
> >> > +
> >> > +on property:bluetooth.start=bluetoothd
> >> > +    start bluetoothd
> >> > +
> >> > +on property:bluetooth.stop=bluetoothd
> >> > +    stop bluetoothd
> >> > +
> >> > +on property:bluetooth.start=bluetoothd-snoop
> >> > +    start bluetoothd-snoop
> >> > +
> >> > +on property:bluetooth.stop=bluetoothd-snoop
> >> > +    stop bluetoothd-snoop
> >>
> >> I think this is actually racy. The ctl.start and ctl.stop sound like they have special handling inside Android init. If you accidentally set bluetooth.start , then bad things happen.
> >
> > on property:foo=bar is also triggered if foo is already set to bar so I don't
> > see what could happen wrong here.
> >
> >>
> >> So I would propose doing bluetooth.daemon=on/off and bluetooth.snoop=on/off
> >>
> >> We just have to make sure that when bluetoothd or bluetoothd-snoop die unexpectedly that these properties get set back to off.
> >
> > I'm not sure if we are able to set properties back to proper state if service
> > exited in such case (that would trigger loop in init).
> >
> > My idea was to use bluetooth.start/stop to start/stop (actually stop is needed
> > only for bluetoothd-snoop service) service by bluetooth user (sort of "sudo"
> > wrapper) but don't care about values in those props and if one want to use
> > props to track service lifetime then just use init.svc.bluetoothd{-snoop}
> > property.
> >
> >>
> >> > +
> >> > +service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
> >> > +    class main
> >> > +    group bluetooth net_admin
> >> > +    disabled
> >> > +    oneshot
> >> > +
> >> > +service bluetoothd-snoop /system/bin/bluetoothd-snoop
> >> > +    class main
> >> > +    group bluetooth net_admin
> >> > +    disabled
> >> > +    oneshot
> >>
> >> Does net_admin include CAP_NET_RAW actually or are we using some sort of patched module for this?
> >
> > I'm not sure, this was probably just copied from <4.2 android. Since we
> > start as root and drop caps I think this can be removed.
> 
> Iirc CAP_NET_RAW is not required by mgmt nor any other part of
> bluetoothd for Android make use of raw sockets so it was dropped at
> some point.

As per comment in main.c we need this for bnep ioctl.

-- 
Best regards, 
Szymon Janc

^ permalink raw reply

* Re: [RFC 1/4] android: Add sample init.bluetooth.rc file
From: Luiz Augusto von Dentz @ 2014-01-17  8:56 UTC (permalink / raw)
  To: Szymon Janc; +Cc: Marcel Holtmann, linux-bluetooth@vger.kernel.org development
In-Reply-To: <2728377.B2HgAZfSS5@uw000953>

Hi Szymon,

On Fri, Jan 17, 2014 at 10:34 AM, Szymon Janc <szymon.janc@tieto.com> wrote:
> Hi Marcel,
>
> On Tuesday 14 of January 2014 13:20:32 Marcel Holtmann wrote:
>> Hi Szymon,
>>
>> > This file is intended to be included from device init.rc.
>> > ---
>> > android/Android.mk        | 16 +++++++++++++++-
>> > android/Makefile.am       |  1 +
>> > android/init.bluetooth.rc | 27 +++++++++++++++++++++++++++
>> > 3 files changed, 43 insertions(+), 1 deletion(-)
>> > create mode 100644 android/init.bluetooth.rc
>> >
>> > diff --git a/android/Android.mk b/android/Android.mk
>> > index ae52ab4..f13e703 100644
>> > --- a/android/Android.mk
>> > +++ b/android/Android.mk
>> > @@ -109,7 +109,7 @@ LOCAL_MODULE := bluetooth.default
>> > LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
>> > LOCAL_MODULE_TAGS := optional
>> > LOCAL_MODULE_CLASS := SHARED_LIBRARIES
>> > -LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop
>> > +LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
>> >
>> > include $(BUILD_SHARED_LIBRARY)
>> >
>> > @@ -263,3 +263,17 @@ LOCAL_MODULE_TAGS := optional
>> > LOCAL_MODULE := bluetoothd-snoop
>> >
>> > include $(BUILD_EXECUTABLE)
>> > +
>> > +#
>> > +# init.bleutooth.rc
>> > +#
>> > +
>> > +include $(CLEAR_VARS)
>> > +
>> > +LOCAL_MODULE := init.bluetooth.rc
>> > +LOCAL_MODULE_CLASS := ETC
>> > +LOCAL_SRC_FILES := $(LOCAL_MODULE)
>> > +LOCAL_MODULE_TAGS := optional
>> > +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
>> > +
>> > +include $(BUILD_PREBUILT)
>> > diff --git a/android/Makefile.am b/android/Makefile.am
>> > index 7806f79..b205019 100644
>> > --- a/android/Makefile.am
>> > +++ b/android/Makefile.am
>> > @@ -134,6 +134,7 @@ android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
>> > endif
>> >
>> > EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
>> > +           android/init.bluetooth.rc \
>> >             android/pics-gap.txt android/pics-hid.txt \
>> >             android/pics-pan.txt android/pics-did.txt \
>> >             android/pics-opp.txt android/pics-pbap.txt \
>> > diff --git a/android/init.bluetooth.rc b/android/init.bluetooth.rc
>> > new file mode 100644
>> > index 0000000..e2be9e8
>> > --- /dev/null
>> > +++ b/android/init.bluetooth.rc
>> > @@ -0,0 +1,27 @@
>> > +chown bluetooth bluetooth /data/misc/bluetooth
>> > +
>> > +chown bluetooth bluetooth /dev/uhid
>> > +
>> > +on property:bluetooth.start=bluetoothd
>> > +    start bluetoothd
>> > +
>> > +on property:bluetooth.stop=bluetoothd
>> > +    stop bluetoothd
>> > +
>> > +on property:bluetooth.start=bluetoothd-snoop
>> > +    start bluetoothd-snoop
>> > +
>> > +on property:bluetooth.stop=bluetoothd-snoop
>> > +    stop bluetoothd-snoop
>>
>> I think this is actually racy. The ctl.start and ctl.stop sound like they have special handling inside Android init. If you accidentally set bluetooth.start , then bad things happen.
>
> on property:foo=bar is also triggered if foo is already set to bar so I don't
> see what could happen wrong here.
>
>>
>> So I would propose doing bluetooth.daemon=on/off and bluetooth.snoop=on/off
>>
>> We just have to make sure that when bluetoothd or bluetoothd-snoop die unexpectedly that these properties get set back to off.
>
> I'm not sure if we are able to set properties back to proper state if service
> exited in such case (that would trigger loop in init).
>
> My idea was to use bluetooth.start/stop to start/stop (actually stop is needed
> only for bluetoothd-snoop service) service by bluetooth user (sort of "sudo"
> wrapper) but don't care about values in those props and if one want to use
> props to track service lifetime then just use init.svc.bluetoothd{-snoop}
> property.
>
>>
>> > +
>> > +service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
>> > +    class main
>> > +    group bluetooth net_admin
>> > +    disabled
>> > +    oneshot
>> > +
>> > +service bluetoothd-snoop /system/bin/bluetoothd-snoop
>> > +    class main
>> > +    group bluetooth net_admin
>> > +    disabled
>> > +    oneshot
>>
>> Does net_admin include CAP_NET_RAW actually or are we using some sort of patched module for this?
>
> I'm not sure, this was probably just copied from <4.2 android. Since we
> start as root and drop caps I think this can be removed.

Iirc CAP_NET_RAW is not required by mgmt nor any other part of
bluetoothd for Android make use of raw sockets so it was dropped at
some point.


-- 
Luiz Augusto von Dentz

^ permalink raw reply

* Re: [RFC 1/4] android: Add sample init.bluetooth.rc file
From: Marcel Holtmann @ 2014-01-17  8:53 UTC (permalink / raw)
  To: Szymon Janc; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <2728377.B2HgAZfSS5@uw000953>

Hi Szymon,

>>> This file is intended to be included from device init.rc.
>>> ---
>>> android/Android.mk        | 16 +++++++++++++++-
>>> android/Makefile.am       |  1 +
>>> android/init.bluetooth.rc | 27 +++++++++++++++++++++++++++
>>> 3 files changed, 43 insertions(+), 1 deletion(-)
>>> create mode 100644 android/init.bluetooth.rc
>>> 
>>> diff --git a/android/Android.mk b/android/Android.mk
>>> index ae52ab4..f13e703 100644
>>> --- a/android/Android.mk
>>> +++ b/android/Android.mk
>>> @@ -109,7 +109,7 @@ LOCAL_MODULE := bluetooth.default
>>> LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
>>> LOCAL_MODULE_TAGS := optional
>>> LOCAL_MODULE_CLASS := SHARED_LIBRARIES
>>> -LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop
>>> +LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
>>> 
>>> include $(BUILD_SHARED_LIBRARY)
>>> 
>>> @@ -263,3 +263,17 @@ LOCAL_MODULE_TAGS := optional
>>> LOCAL_MODULE := bluetoothd-snoop
>>> 
>>> include $(BUILD_EXECUTABLE)
>>> +
>>> +#
>>> +# init.bleutooth.rc
>>> +#
>>> +
>>> +include $(CLEAR_VARS)
>>> +
>>> +LOCAL_MODULE := init.bluetooth.rc
>>> +LOCAL_MODULE_CLASS := ETC
>>> +LOCAL_SRC_FILES := $(LOCAL_MODULE)
>>> +LOCAL_MODULE_TAGS := optional
>>> +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
>>> +
>>> +include $(BUILD_PREBUILT)
>>> diff --git a/android/Makefile.am b/android/Makefile.am
>>> index 7806f79..b205019 100644
>>> --- a/android/Makefile.am
>>> +++ b/android/Makefile.am
>>> @@ -134,6 +134,7 @@ android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
>>> endif
>>> 
>>> EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
>>> +		android/init.bluetooth.rc \
>>> 		android/pics-gap.txt android/pics-hid.txt \
>>> 		android/pics-pan.txt android/pics-did.txt \
>>> 		android/pics-opp.txt android/pics-pbap.txt \
>>> diff --git a/android/init.bluetooth.rc b/android/init.bluetooth.rc
>>> new file mode 100644
>>> index 0000000..e2be9e8
>>> --- /dev/null
>>> +++ b/android/init.bluetooth.rc
>>> @@ -0,0 +1,27 @@
>>> +chown bluetooth bluetooth /data/misc/bluetooth
>>> +
>>> +chown bluetooth bluetooth /dev/uhid
>>> +
>>> +on property:bluetooth.start=bluetoothd
>>> +    start bluetoothd
>>> +
>>> +on property:bluetooth.stop=bluetoothd
>>> +    stop bluetoothd
>>> +
>>> +on property:bluetooth.start=bluetoothd-snoop
>>> +    start bluetoothd-snoop
>>> +
>>> +on property:bluetooth.stop=bluetoothd-snoop
>>> +    stop bluetoothd-snoop
>> 
>> I think this is actually racy. The ctl.start and ctl.stop sound like they have special handling inside Android init. If you accidentally set bluetooth.start , then bad things happen.
> 
> on property:foo=bar is also triggered if foo is already set to bar so I don't
> see what could happen wrong here.
> 
>> 
>> So I would propose doing bluetooth.daemon=on/off and bluetooth.snoop=on/off
>> 
>> We just have to make sure that when bluetoothd or bluetoothd-snoop die unexpectedly that these properties get set back to off.
> 
> I'm not sure if we are able to set properties back to proper state if service
> exited in such case (that would trigger loop in init).

there is a service-exited-<name> type of action. I think we should look into using that one somehow.

> My idea was to use bluetooth.start/stop to start/stop (actually stop is needed
> only for bluetoothd-snoop service) service by bluetooth user (sort of "sudo"
> wrapper) but don't care about values in those props and if one want to use
> props to track service lifetime then just use init.svc.bluetoothd{-snoop}
> property.

Fair enough.

However then I would prefer if we do bluetooth.{start,stop}=daemon and bluetooth.{start,stop}=snoop to make this really tight into how our solution works.

>>> +service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
>>> +    class main
>>> +    group bluetooth net_admin
>>> +    disabled
>>> +    oneshot
>>> +
>>> +service bluetoothd-snoop /system/bin/bluetoothd-snoop
>>> +    class main
>>> +    group bluetooth net_admin
>>> +    disabled
>>> +    oneshot
>> 
>> Does net_admin include CAP_NET_RAW actually or are we using some sort of patched module for this?
> 
> I'm not sure, this was probably just copied from <4.2 android. Since we
> start as root and drop caps I think this can be removed.

The snoop process just needs CAP_NET_RAW and write access to the file location. I would prefer to really make sure it runs with as less right as possible.

Regards

Marcel


^ permalink raw reply

* Re: [RFC 1/4] android: Add sample init.bluetooth.rc file
From: Szymon Janc @ 2014-01-17  8:34 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <7410E472-B6D3-4F2C-B83F-3882BA60DD31@holtmann.org>

Hi Marcel,

On Tuesday 14 of January 2014 13:20:32 Marcel Holtmann wrote:
> Hi Szymon,
> 
> > This file is intended to be included from device init.rc.
> > ---
> > android/Android.mk        | 16 +++++++++++++++-
> > android/Makefile.am       |  1 +
> > android/init.bluetooth.rc | 27 +++++++++++++++++++++++++++
> > 3 files changed, 43 insertions(+), 1 deletion(-)
> > create mode 100644 android/init.bluetooth.rc
> > 
> > diff --git a/android/Android.mk b/android/Android.mk
> > index ae52ab4..f13e703 100644
> > --- a/android/Android.mk
> > +++ b/android/Android.mk
> > @@ -109,7 +109,7 @@ LOCAL_MODULE := bluetooth.default
> > LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
> > LOCAL_MODULE_TAGS := optional
> > LOCAL_MODULE_CLASS := SHARED_LIBRARIES
> > -LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop
> > +LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
> > 
> > include $(BUILD_SHARED_LIBRARY)
> > 
> > @@ -263,3 +263,17 @@ LOCAL_MODULE_TAGS := optional
> > LOCAL_MODULE := bluetoothd-snoop
> > 
> > include $(BUILD_EXECUTABLE)
> > +
> > +#
> > +# init.bleutooth.rc
> > +#
> > +
> > +include $(CLEAR_VARS)
> > +
> > +LOCAL_MODULE := init.bluetooth.rc
> > +LOCAL_MODULE_CLASS := ETC
> > +LOCAL_SRC_FILES := $(LOCAL_MODULE)
> > +LOCAL_MODULE_TAGS := optional
> > +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
> > +
> > +include $(BUILD_PREBUILT)
> > diff --git a/android/Makefile.am b/android/Makefile.am
> > index 7806f79..b205019 100644
> > --- a/android/Makefile.am
> > +++ b/android/Makefile.am
> > @@ -134,6 +134,7 @@ android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
> > endif
> > 
> > EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
> > +		android/init.bluetooth.rc \
> > 		android/pics-gap.txt android/pics-hid.txt \
> > 		android/pics-pan.txt android/pics-did.txt \
> > 		android/pics-opp.txt android/pics-pbap.txt \
> > diff --git a/android/init.bluetooth.rc b/android/init.bluetooth.rc
> > new file mode 100644
> > index 0000000..e2be9e8
> > --- /dev/null
> > +++ b/android/init.bluetooth.rc
> > @@ -0,0 +1,27 @@
> > +chown bluetooth bluetooth /data/misc/bluetooth
> > +
> > +chown bluetooth bluetooth /dev/uhid
> > +
> > +on property:bluetooth.start=bluetoothd
> > +    start bluetoothd
> > +
> > +on property:bluetooth.stop=bluetoothd
> > +    stop bluetoothd
> > +
> > +on property:bluetooth.start=bluetoothd-snoop
> > +    start bluetoothd-snoop
> > +
> > +on property:bluetooth.stop=bluetoothd-snoop
> > +    stop bluetoothd-snoop
> 
> I think this is actually racy. The ctl.start and ctl.stop sound like they have special handling inside Android init. If you accidentally set bluetooth.start , then bad things happen.

on property:foo=bar is also triggered if foo is already set to bar so I don't
see what could happen wrong here.

> 
> So I would propose doing bluetooth.daemon=on/off and bluetooth.snoop=on/off
> 
> We just have to make sure that when bluetoothd or bluetoothd-snoop die unexpectedly that these properties get set back to off.

I'm not sure if we are able to set properties back to proper state if service
exited in such case (that would trigger loop in init).

My idea was to use bluetooth.start/stop to start/stop (actually stop is needed
only for bluetoothd-snoop service) service by bluetooth user (sort of "sudo"
wrapper) but don't care about values in those props and if one want to use
props to track service lifetime then just use init.svc.bluetoothd{-snoop}
property.

> 
> > +
> > +service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
> > +    class main
> > +    group bluetooth net_admin
> > +    disabled
> > +    oneshot
> > +
> > +service bluetoothd-snoop /system/bin/bluetoothd-snoop
> > +    class main
> > +    group bluetooth net_admin
> > +    disabled
> > +    oneshot
> 
> Does net_admin include CAP_NET_RAW actually or are we using some sort of patched module for this?

I'm not sure, this was probably just copied from <4.2 android. Since we
start as root and drop caps I think this can be removed.
 

-- 
Best regards, 
Szymon Janc

^ permalink raw reply

* Dualshock4 - 'HIDP: Handshake: Unsupported request' after 'unplug virtual cable'.
From: simon @ 2014-01-17  5:09 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Frank Praznik

[-- Attachment #1: Type: text/plain, Size: 1706 bytes --]

Hi all,
Frank and I have been working on a kernel driver for the Dualshock 4 and
we're having problems with the BT connection (USB is OK).

When powered on the DS4 will initiate a connection to the PC once it knows
it's Bdaddr (via pairing), but will power off shortly there after. This
seems to be related to incorrect (??) data sent from Bluez, or if no hid
connection is made within a short window.

On the two systems I have tried:
1) Debian with bluez 4.99, I can 'hidd --connect xxx' to hold the
connection. I get joystick data but can't send data to DS4 to drive
ff/leds. (I am told that 'hidd' is deprated)

2) Fedora LiveCD with bluez 5.13. Connection is immediately terminated by
the controller. HCI log attached.

It appears that the controller is insisting on a encrypted link ('noenc'
and 'noauth' make no difference) and does not like the 'unplug virtual
cable' which is sent (presumably as the link is being brought down to
re-start encrypted).

In the BT HID 1.1 spec (page 48) it says
--
If the HIDVirtualCable SDP attribute is set to TRUE, then a Virtual Cable
is considered to be established after both the HID Control and HID
Interrupt L2CAP channels have been opened.
--

However the Fedora system does not have a copy of the SDP records for the
controller, it never pulled them and I have never managed to make a
connection long enough to read them.

My theory is that the controller is rejecting the 'Unplug Virtual Cable'
command as it shouldn't have been established, but I'm not really sure.



I managed use my Debian system to read SDP records but that barfs on
what's sent.... files also attached.

Does anyone have any suggestions on what I should try next?
Thanks, Simon





[-- Attachment #2: hcidump_records.txt.gz --]
[-- Type: application/gzip, Size: 16136 bytes --]

[-- Attachment #3: records.txt --]
[-- Type: text/plain, Size: 673 bytes --]

Service Name: Wireless Controller
Service Description: Game Controller
Service Provider: Sony Computer Entertainment
Service RecHandle: 0x10001
Service Class ID List:
  "Human Interface Device" (0x1124)
Protocol Descriptor List:
  "L2CAP" (0x0100)
    PSM: 17
  "HIDP" (0x0011)
Language Base Attr List:
  code_ISO639: 0x656e
  encoding:    0x6a
  base_offset: 0x100
Profile Descriptor List:
  "Human Interface Device" (0x1124)
    Version: 0x0100

Service RecHandle: 0x10002
Service Class ID List:
  "PnP Information" (0x1200)
Protocol Descriptor List:
  "L2CAP" (0x0100)
    PSM: 1
  "SDP" (0x0001)
Profile Descriptor List:
  "PnP Information" (0x1200)
    Version: 0x0103

[-- Attachment #4: records_raw.txt --]
[-- Type: text/plain, Size: 2851 bytes --]

Sequence
	Attribute 0x0000 - ServiceRecordHandle
		UINT32 0x00010001
	Attribute 0x0001 - ServiceClassIDList
		Sequence
			UUID16 0x1124 - HumanInterfaceDeviceService (HID)
	Attribute 0x0004 - ProtocolDescriptorList
		Sequence
			Sequence
				UUID16 0x0100 - L2CAP
				UINT16 0x0011
			Sequence
				UUID16 0x0011 - HIDP
	Attribute 0x0006 - LanguageBaseAttributeIDList
		Sequence
			UINT16 0x656e
			UINT16 0x006a
			UINT16 0x0100
	Attribute 0x0009 - BluetoothProfileDescriptorList
		Sequence
			Sequence
				UUID16 0x1124 - HumanInterfaceDeviceService (HID)
				UINT16 0x0100
	Attribute 0x000d - AdditionalProtocolDescriptorLists
		Sequence
			Sequence
				Sequence
					UUID16 0x0100 - L2CAP
					UINT16 0x0013
				Sequence
					UUID16 0x0011 - HIDP
	Attribute 0x0100
		String Wireless Controller\0
	Attribute 0x0101
		String Game Controller\0
	Attribute 0x0102
		String Sony Computer Entertainment\0
	Attribute 0x0200
		UINT16 0x0100
	Attribute 0x0201
		UINT16 0x0111
	Attribute 0x0202
		UINT8 0x08
	Attribute 0x0203
		UINT8 0x00
	Attribute 0x0204
		Bool False
	Attribute 0x0205
		Bool True
	Attribute 0x0206
		Sequence
			Sequence
				UINT8 0x22
				Data 05 01 09 05 a1 01 85 01 09 30 09 31 09 32 09 35 15 00 26 ff 00 75 08 95 04 81 02 09 39 15 00 25 07 75 04 95 01 81 42 05 09 19 01 29 0e 15 00 25 01 75 01 95 0e 81 02 75 06 95 01 81 01 05 01 09 33 09 34 15 00 26 ff 00 75 08 95 02 81 02 06 04 ff 85 02 09 24 95 24 b1 02 85 a3 09 25 95 30 b1 02 85 05 09 26 95 28 b1 02 85 06 09 27 95 34 b1 02 85 07 09 28 95 30 b1 02 85 08 09 29 95 2f b1 02 06 03 ff 85 03 09 21 95 26 b1 02 85 04 09 22 95 2e b1 02 85 f0 09 47 95 3f b1 02 85 f1 09 48 95 3f b1 02 85 f2 09 49 95 0f b1 02 06 00 ff 85 11 09 20 15 00 26 ff 00 75 08 95 4d 81 02 09 21 91 02 85 12 09 22 95 8d 81 02 09 23 91 02 85 13 09 24 95 cd 81 02 09 25 91 02 85 14 09 26 96 0d 01 81 02 09 27 91 02 85 15 09 28 96 4d 01 81 02 09 29 91 02 85 16 09 2a 96 8d 01 81 02 09 2b 91 02 85 17 09 2c 96 cd 01 81 02 09 2d 91 02 85 18 09 2e 96 0d 02 81 02 09 2f 91 02 85 19 09 30 96 22 02 81 02 09 31 91 02 06 80 ff 85 82 09 22 95 3f b1 02 85 83 09 23 b1 02 85 84 09 24 b1 02 85 90 09 30 b1 02 85 91 09 31 b1 02 85 92 09 32 b1 02 85 93 09 33 b1 02 85 a0 09 40 b1 02 85 a4 09 44 b1 02 c0 00
Sequence
	Attribute 0x0000 - ServiceRecordHandle
		UINT32 0x00010002
	Attribute 0x0001 - ServiceClassIDList
		Sequence
			UUID16 0x1200 - PnPInformation
	Attribute 0x0004 - ProtocolDescriptorList
		Sequence
			Sequence
				UUID16 0x0100 - L2CAP
				UINT16 0x0001
			Sequence
				UUID16 0x0001 - SDP
	Attribute 0x0009 - BluetoothProfileDescriptorList
		Sequence
			Sequence
				UUID16 0x1200 - PnPInformation
				UINT16 0x0103
	Attribute 0x0200
		UINT16 0x0103
	Attribute 0x0201
		UINT16 0x054c
	Attribute 0x0202
		UINT16 0x05c4
	Attribute 0x0203
		UINT16 0x0100
	Attribute 0x0204
		Bool True
	Attribute 0x0205
		UINT16 0x0002

[-- Attachment #5: connect_ds4_fedora_4.txt --]
[-- Type: text/plain, Size: 10027 bytes --]

HCI sniffer - Bluetooth packet analyzer ver 5.12
device: hci0 snap_len: 1500 filter: 0xffffffff
> HCI Event: Connect Request (0x04) plen 10
    bdaddr 1C:66:6D:07:C3:E0 class 0x002508 type ACL
< HCI Command: Accept Connection Request (0x01|0x0009) plen 7
    bdaddr 1C:66:6D:07:C3:E0 role 0x00
    Role: Master
> HCI Event: Command Status (0x0f) plen 4
    Accept Connection Request (0x01|0x0009) status 0x00 ncmd 1
> HCI Event: Role Change (0x12) plen 8
    status 0x00 bdaddr 1C:66:6D:07:C3:E0 role 0x00
    Role: Master
> HCI Event: Connect Complete (0x03) plen 11
    status 0x00 handle 41 bdaddr 1C:66:6D:07:C3:E0 type ACL encrypt 0x00
< HCI Command: Read Remote Supported Features (0x01|0x001b) plen 2
    handle 41
> HCI Event: Page Scan Repetition Mode Change (0x20) plen 7
    bdaddr 1C:66:6D:07:C3:E0 mode 1
> HCI Event: Command Status (0x0f) plen 4
    Read Remote Supported Features (0x01|0x001b) status 0x00 ncmd 0
> HCI Event: Max Slots Change (0x1b) plen 3
    handle 41 slots 5
> HCI Event: Command Status (0x0f) plen 4
    Unknown (0x00|0x0000) status 0x00 ncmd 1
< HCI Command: Change Connection Packet Type (0x01|0x000f) plen 4
    handle 41 ptype 0xcc18
    Packet type: DM1 DM3 DM5 DH1 DH3 DH5 
> HCI Event: Command Status (0x0f) plen 4
    Change Connection Packet Type (0x01|0x000f) status 0x00 ncmd 1
> HCI Event: Connection Packet Type Changed (0x1d) plen 5
    status 0x00 handle 41 ptype 0xcc18
    Packet type: DM1 DM3 DM5 DH1 DH3 DH5 
> HCI Event: Read Remote Supported Features (0x0b) plen 11
    status 0x00 handle 41
    Features: 0xff 0xfe 0x0d 0xfe 0x98 0x7f 0x79 0x87
< HCI Command: Remote Name Request (0x01|0x0019) plen 10
    bdaddr 1C:66:6D:07:C3:E0 mode 2 clkoffset 0x0000
> HCI Event: Command Status (0x0f) plen 4
    Remote Name Request (0x01|0x0019) status 0x00 ncmd 1
> ACL data: handle 41 flags 0x02 dlen 12
    L2CAP(s): Connect req: psm 1 scid 0x0040
< ACL data: handle 41 flags 0x02 dlen 16
    L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 1 status 0
      Connection pending - No futher information available
< ACL data: handle 41 flags 0x02 dlen 10
    L2CAP(s): Info req: type 2
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 16
    L2CAP(s): Info rsp: type 2 result 0
      Extended feature mask 0x0000
< ACL data: handle 41 flags 0x02 dlen 16
    L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0
      Connection successful
< ACL data: handle 41 flags 0x02 dlen 12
    L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 0
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 16
    L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 4
      MTU 672 
< ACL data: handle 41 flags 0x02 dlen 18
    L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 4
      MTU 672 
> ACL data: handle 41 flags 0x02 dlen 18
    L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 4
      MTU 672 
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 24
    L2CAP(d): cid 0x0040 len 20 [psm 1]
        SDP SSA Req: tid 0x1 len 0xf
          pat uuid-16 0x0100 (L2CAP)
          max 2048
          aid(s) 0x0000 - 0xffff
          cont 00
< ACL data: handle 41 flags 0x02 dlen 192
< ACL data: handle 41 flags 0x01 dlen 192
< ACL data: handle 41 flags 0x01 dlen 93
    L2CAP(d): cid 0x0040 len 473 [psm 1]
        SDP SSA Rsp: tid 0x1 len 0x1d4
          count 465
          record #0
              aid 0x0000 (SrvRecHndl)
                 uint 0x10001
              aid 0x0001 (SrvClassIDList)
                 < uuid-16 0x1800 >
              aid 0x0004 (ProtocolDescList)
                 < < uuid-16 0x0100 (L2CAP) uint 0x1f > <
                 uuid-16 0x0007 uint 0x1 uint 0x8 > >
              aid 0x0005 (BrwGrpList)
                 < uuid-16 0x1002 (PubBrwsGrp) >
              aid 0x000a (DocURL)
                 url "http://www.bluez.org/"
              aid 0x000b (ClientExeURL)
                 url "http://www.bluez.org/"
              aid 0x000c (IconURL)
                 url "http://www.bluez.org/"
              aid 0x0100 (SrvName)
                 str "Generic Access Profile"
              aid 0x0102 (ProviderName)
                 str "BlueZ"
          record #1
              aid 0x0000 (SrvRecHndl)
                 uint 0x10002
              aid 0x0001 (SrvClassIDList)
                 < uuid-16 0x1801 >
              aid 0x0004 (ProtocolDescList)
                 < < uuid-16 0x0100 (L2CAP) uint 0x1f > <
                 uuid-16 0x0007 uint 0x10 uint 0x10 > >
              aid 0x0005 (BrwGrpList)
                 < uuid-16 0x1002 (PubBrwsGrp) >
              aid 0x0100 (SrvName)
                 str "Generic Attribute Profile"
              aid 0x0102 (ProviderName)
                 str "BlueZ"
          record #2
              aid 0x0000 (SrvRecHndl)
                 uint 0x10003
              aid 0x0001 (SrvClassIDList)
                 < uuid-16 0x110e (AVRemote) uuid-16 0x110f (AVRemCt) >
              aid 0x0004 (ProtocolDescList)
                 < < uuid-16 0x0100 (L2CAP) uint 0x17 > <
                 uuid-16 0x0017 (AVCTP) uint 0x103 > >
              aid 0x0005 (BrwGrpList)
                 < uuid-16 0x1002 (PubBrwsGrp) >
              aid 0x0009 (BTProfileDescList)
                 < < uuid-16 0x110e (AVRemote) uint 0x105 > >
              aid 0x000d (AdditionalProtocolDescLists)
                 < < < uuid-16 0x0100 (L2CAP) uint 0x1b > < uuid-16 0x0017 (AVCTP) uint 0x103 > > >
              aid 0x0100 (SrvName)
                 str "AVRCP CT"
              aid 0x0311 (SuppFeatures)
                 uint 0x4f
          record #3
              aid 0x0000 (SrvRecHndl)
                 uint 0x10004
              aid 0x0001 (SrvClassIDList)
                 < uuid-16 0x110c (AVRemTarget) >
              aid 0x0004 (ProtocolDescList)
                 < < uuid-16 0x0100 (L2CAP) uint 0x17 > <
                 uuid-16 0x0017 (AVCTP) uint 0x103 > >
              aid 0x0005 (BrwGrpList)
                 < uuid-16 0x1002 (PubBrwsGrp) >
              aid 0x0009 (BTProfileDescList)
                 < < uuid-16 0x110e (AVRemote) uint 0x104 > >
              aid 0x000d (AdditionalProtocolDescLists)
                 < < < uuid-16 0x0100 (L2CAP) uint 0x1b > < uuid-16 0x0017 (AVCTP) uint 0x103 > > >
              aid 0x0100 (SrvName)
                 str "AVRCP TG"
              aid 0x0311 (SuppFeatures)
                 uint 0x5f
          cont 00
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 2
> ACL data: handle 41 flags 0x02 dlen 12
    L2CAP(s): Disconn req: dcid 0x0040 scid 0x0040
< ACL data: handle 41 flags 0x02 dlen 12
    L2CAP(s): Disconn rsp: dcid 0x0040 scid 0x0040
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> HCI Event: Remote Name Req Complete (0x07) plen 255
    status 0x00 bdaddr 1C:66:6D:07:C3:E0 name 'Wireless Controller'
> HCI Event: Link Key Request (0x17) plen 6
    bdaddr 1C:66:6D:07:C3:E0
< HCI Command: Link Key Request Reply (0x01|0x000b) plen 22
    bdaddr 1C:66:6D:07:C3:E0 key DCD82F85FA8F56D21A7AFBF7B227F7E9
> HCI Event: Command Complete (0x0e) plen 10
    Link Key Request Reply (0x01|0x000b) ncmd 1
    status 0x00 bdaddr 1C:66:6D:07:C3:E0
> HCI Event: Encrypt Change (0x08) plen 4	<=============================
    status 0x00 handle 41 encrypt 0x01
> ACL data: handle 41 flags 0x02 dlen 12
    L2CAP(s): Connect req: psm 17 scid 0x0041
< ACL data: handle 41 flags 0x02 dlen 16
    L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0041 result 0 status 0
      Connection successful
< ACL data: handle 41 flags 0x02 dlen 12
    L2CAP(s): Config req: dcid 0x0041 flags 0x00 clen 0
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 16
    L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 4
      MTU 672 
< ACL data: handle 41 flags 0x02 dlen 18
    L2CAP(s): Config rsp: scid 0x0041 flags 0x00 result 0 clen 4
      MTU 672 
> ACL data: handle 41 flags 0x02 dlen 18
    L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 4
      MTU 672 
< ACL data: handle 41 flags 0x02 dlen 5
    L2CAP(d): cid 0x0041 len 1 [psm 17]
      HIDP: Control: Virtual cable unplug		<======================
< ACL data: handle 41 flags 0x02 dlen 12
    L2CAP(s): Disconn req: dcid 0x0041 scid 0x0040
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 12
    L2CAP(s): Connect req: psm 19 scid 0x0042
< ACL data: handle 41 flags 0x02 dlen 16
    L2CAP(s): Connect rsp: dcid 0x0041 scid 0x0042 result 1 status 2
      Connection pending - Authorization pending	<======================
< ACL data: handle 41 flags 0x02 dlen 16
    L2CAP(s): Connect rsp: dcid 0x0041 scid 0x0042 result 3 status 0
      Connection refused - security block		<=====================
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 5
    L2CAP(d): cid 0x0040 len 1 [psm 17]			<====================
      HIDP: Handshake: Unsupported request
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 12
    L2CAP(s): Disconn rsp: dcid 0x0041 scid 0x0040
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
    handle 41 packets 1
> HCI Event: Disconn Complete (0x05) plen 4
    status 0x00 handle 41 reason 0x13
    Reason: Remote User Terminated Connection		<====================

[-- Attachment #6: info --]
[-- Type: application/octet-stream, Size: 157 bytes --]

[General]
Name=
Class=0x002508
SupportedTechnologies=BR/EDR
Trusted=false
Blocked=false

[LinkKey]
Key=0xDCD82F85FA8F56D21A7AFBF7B227F7E9
Type=0
PINLength=4

^ permalink raw reply

* Re: Questions on BlueZ 5.13: device creation API + persistent hid/input pipeline
From: Marcel Holtmann @ 2014-01-16 23:42 UTC (permalink / raw)
  To: Petri Gynther; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <CAGXr9JHi5UuDd01POdua1UZ2kY+n=0wGcxaWx4Ta=55fHvZxyw@mail.gmail.com>

Hi Petri,

>>> 2) There is a difference in HID vs HoG hid/input pipeline persistence.
>>> When the Bluetooth classic HID remote disconnects, the corresponding
>>> hid and input kernel devices are destroyed. And when it reconnects,
>>> hid and input devices are created again. This adds a lot of
>>> unnecessary delay at reconnect. And, the first keypress is always lost
>>> on reconnect.
>>> 
>>> In contrast, for the Bluetooth LE HoG remote, the hid/input pipeline
>>> is persistent. When HoG remote disconnects, hid and input devices
>>> remain in the system and are reused when the HoG remote reconnects.
>>> 
>>> Is there a way to make Bluetooth classic HID behave the same as HoG,
>>> i.e. retain the hid and input kernel devices on disconnect and reuse
>>> them on reconnect?
>> 
>> HID and HoG work a little bit different. HID uses a kernel transport driver while HoG uses /dev/uhid to do the work. Porting HID over to use /dev/uhid is certainly a possibility. We have considered that, but nobody has done that work yet.
>> 
>> Please remember that HID (with HIDP) is finding its right place in /sys device tree. While all HoG using /dev/uhid are considered virtual devices. There is a semi proposal that we agreed on New Orleans during the PlumbersConf, but I have no idea if David actually implemented it.
>> 
>> Another option is to make the HIDP transport smarter and persistent by integrating it into BlueZ 5’s kernel mgmt support. That however means you will need a much more recent Bluetooth subsystem. So 2.6.37 is not getting you anywhere near that even if we change HIDP support.
> 
> I'm using BlueZ kernel modules (bluetooth, hidp) from Linux
> backports-3.13-rc2-1, so that is pretty recent code. Works fine on top
> of 2.6.37. However, my HID and input drivers come from 2.6.37, as they
> are not available in backports-3.13-rc2-1.
> 
> I understand the difference between HID and HoG. Both hidp and uhid
> are hid_ll_drivers. HID input reports stay entirely in kernel, whereas
> HoG is handled in bluetoothd and input reports are passed back to
> kernel via /dev/uhid.
> 
> Assuming the latest HIDP code, what kind of effort would it be to make
> the hid/input pipeline persistent when HID remote disconnects and
> reconnects?

for the HIDP code itself there is actually a lot of work. It does not have the model of persistent connections. It is designed to disconnect and reconnect. Since normally when you disconnect your device it is off. Otherwise you just sniff the connection. It is also a bit legacy reason from where L2CAP only had a concept of sockets and not l2cap_chan that only recently got introduced.

Why you are loosing your first input, that sounds like a bug we should look into. That should actually not happen.

To be honest, the easiest for you might be to run HID over /dev/uhid as well. So HoG and HID are both using /dev/uhid. A port of the input plugin to use /dev/uhid for HID should be straight forward. All the building blocks should be already there.

Regards

Marcel


^ permalink raw reply

* [PATCH 11/11] android/tester: Add HIDhost SendData test
From: Ravi kumar Veeramally @ 2014-01-16 23:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1389914751-18545-1-git-send-email-ravikumar.veeramally@linux.intel.com>

---
 android/android-tester.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/android/android-tester.c b/android/android-tester.c
index c79ea16..2ef44ef 100644
--- a/android/android-tester.c
+++ b/android/android-tester.c
@@ -3208,6 +3208,8 @@ static void setup_hidhost_interface(const void *test_data)
 #define HID_SET_OUTPUT_REPORT		0x52
 #define HID_SET_FEATURE_REPORT		0x53
 
+#define HID_SEND_DATA			0xa2
+
 static void hid_prepare_reply_protocol_mode(const void *data, uint16_t len)
 {
 	struct test_data *t_data = tester_get_data();
@@ -3242,6 +3244,12 @@ static void hid_prepare_reply_report(const void *data, uint16_t len)
 static void hid_intr_cid_hook_cb(const void *data, uint16_t len,
 							void *user_data)
 {
+	uint8_t header = ((uint8_t *) data)[0];
+
+	switch (header) {
+	case HID_SEND_DATA:
+		tester_test_passed();
+	}
 }
 
 static void hid_intr_connect_cb(uint16_t handle, uint16_t cid, void *user_data)
@@ -3277,6 +3285,7 @@ static void hid_ctrl_cid_hook_cb(const void *data, uint16_t len,
 	case HID_SET_INPUT_REPORT:
 	case HID_SET_OUTPUT_REPORT:
 	case HID_SET_FEATURE_REPORT:
+	case HID_SEND_DATA:
 		tester_test_passed();
 	}
 }
@@ -3550,6 +3559,21 @@ static void test_hidhost_set_report(const void *test_data)
 		tester_test_failed();
 }
 
+static void test_hidhost_send_data(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+	char *buf = "fe0201";
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->send_data(&bdaddr, buf);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_test_failed();
+}
+
 #define test_bredrle(name, data, test_setup, test, test_teardown) \
 	do { \
 		struct test_data *user; \
@@ -3923,5 +3947,9 @@ int main(int argc, char *argv[])
 				NULL, setup_hidhost_connect,
 				test_hidhost_set_report, teardown);
 
+	test_bredrle("HIDHost SendData Success",
+				NULL, setup_hidhost_connect,
+				test_hidhost_send_data, teardown);
+
 	return tester_run();
 }
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 10/11] android/tester: Add HIDhost SetReport test
From: Ravi kumar Veeramally @ 2014-01-16 23:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1389914751-18545-1-git-send-email-ravikumar.veeramally@linux.intel.com>

---
 android/android-tester.c | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/android/android-tester.c b/android/android-tester.c
index f8d539d..c79ea16 100644
--- a/android/android-tester.c
+++ b/android/android-tester.c
@@ -3204,6 +3204,10 @@ static void setup_hidhost_interface(const void *test_data)
 #define HID_GET_OUTPUT_REPORT		0x4a
 #define HID_GET_FEATURE_REPORT		0x4b
 
+#define HID_SET_INPUT_REPORT		0x51
+#define HID_SET_OUTPUT_REPORT		0x52
+#define HID_SET_FEATURE_REPORT		0x53
+
 static void hid_prepare_reply_protocol_mode(const void *data, uint16_t len)
 {
 	struct test_data *t_data = tester_get_data();
@@ -3268,6 +3272,12 @@ static void hid_ctrl_cid_hook_cb(const void *data, uint16_t len,
 	case HID_GET_FEATURE_REPORT:
 		hid_prepare_reply_report(data, len);
 		break;
+	/* HID device doesnot reply for this commads, so reaching pdu's
+	 * to hid device means assuming test passed */
+	case HID_SET_INPUT_REPORT:
+	case HID_SET_OUTPUT_REPORT:
+	case HID_SET_FEATURE_REPORT:
+		tester_test_passed();
 	}
 }
 
@@ -3525,6 +3535,21 @@ static void test_hidhost_get_report(const void *test_data)
 		tester_test_failed();
 }
 
+static void test_hidhost_set_report(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+	char *buf = "010101";
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->set_report(&bdaddr, BTHH_INPUT_REPORT, buf);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_test_failed();
+}
+
 #define test_bredrle(name, data, test_setup, test, test_teardown) \
 	do { \
 		struct test_data *user; \
@@ -3894,5 +3919,9 @@ int main(int argc, char *argv[])
 			&hidhost_test_get_report, setup_hidhost_connect,
 				test_hidhost_get_report, teardown);
 
+	test_bredrle("HIDHost SetReport Success",
+				NULL, setup_hidhost_connect,
+				test_hidhost_set_report, teardown);
+
 	return tester_run();
 }
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 09/11] android/tester: Add HIDhost GetReport test
From: Ravi kumar Veeramally @ 2014-01-16 23:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1389914751-18545-1-git-send-email-ravikumar.veeramally@linux.intel.com>

---
 android/android-tester.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/android/android-tester.c b/android/android-tester.c
index da1fc28..f8d539d 100644
--- a/android/android-tester.c
+++ b/android/android-tester.c
@@ -3200,6 +3200,10 @@ static void setup_hidhost_interface(const void *test_data)
 #define HID_SET_REPORT_PROTOCOL		0x70
 #define HID_SET_BOOT_PROTOCOL		0x71
 
+#define HID_GET_INPUT_REPORT		0x49
+#define HID_GET_OUTPUT_REPORT		0x4a
+#define HID_GET_FEATURE_REPORT		0x4b
+
 static void hid_prepare_reply_protocol_mode(const void *data, uint16_t len)
 {
 	struct test_data *t_data = tester_get_data();
@@ -3215,6 +3219,22 @@ static void hid_prepare_reply_protocol_mode(const void *data, uint16_t len)
 						(void *)pdu, pdu_len);
 }
 
+static void hid_prepare_reply_report(const void *data, uint16_t len)
+{
+	struct test_data *t_data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+	uint8_t pdu[3] = { 0, 0, 0 };
+	uint16_t pdu_len = 0;
+
+	pdu_len = 3;
+	pdu[0] = 0xa2;
+	pdu[1] = 0x01;
+	pdu[2] = 0x00;
+
+	bthost_send_cid(bthost, t_data->ctrl_handle, t_data->ctrl_cid,
+						(void *)pdu, pdu_len);
+}
+
 static void hid_intr_cid_hook_cb(const void *data, uint16_t len,
 							void *user_data)
 {
@@ -3243,6 +3263,11 @@ static void hid_ctrl_cid_hook_cb(const void *data, uint16_t len,
 	case HID_SET_BOOT_PROTOCOL:
 		hid_prepare_reply_protocol_mode(data, len);
 		break;
+	case HID_GET_INPUT_REPORT:
+	case HID_GET_OUTPUT_REPORT:
+	case HID_GET_FEATURE_REPORT:
+		hid_prepare_reply_report(data, len);
+		break;
 	}
 }
 
@@ -3465,6 +3490,41 @@ static void test_hidhost_set_protocol(const void *test_data)
 		tester_test_failed();
 }
 
+static void hid_get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t status,
+						uint8_t *report, int size)
+{
+	struct test_data *data = tester_get_data();
+	const struct hidhost_generic_data *test = data->test_data;
+
+	if (data->cb_count == test->expected_cb_count &&
+					status == test->expected_status &&
+					size == test->expected_report_size)
+		tester_test_passed();
+	else
+		tester_test_failed();
+}
+
+static const struct hidhost_generic_data hidhost_test_get_report = {
+	.expected_hal_cb.get_report_cb = hid_get_report_cb,
+	.expected_cb_count = 1,
+	.expected_status = BTHH_OK,
+	.expected_report_size = 2,
+};
+
+static void test_hidhost_get_report(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->get_report(&bdaddr, BTHH_INPUT_REPORT, 1, 20);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_test_failed();
+}
+
 #define test_bredrle(name, data, test_setup, test, test_teardown) \
 	do { \
 		struct test_data *user; \
@@ -3830,5 +3890,9 @@ int main(int argc, char *argv[])
 			&hidhost_test_get_protocol, setup_hidhost_connect,
 				test_hidhost_set_protocol, teardown);
 
+	test_bredrle("HIDHost GetReport Success",
+			&hidhost_test_get_report, setup_hidhost_connect,
+				test_hidhost_get_report, teardown);
+
 	return tester_run();
 }
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 08/11] android/tester: Add HIDhost SetProtocol test
From: Ravi kumar Veeramally @ 2014-01-16 23:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1389914751-18545-1-git-send-email-ravikumar.veeramally@linux.intel.com>

---
 android/android-tester.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/android/android-tester.c b/android/android-tester.c
index 56be850..da1fc28 100644
--- a/android/android-tester.c
+++ b/android/android-tester.c
@@ -3197,6 +3197,8 @@ static void setup_hidhost_interface(const void *test_data)
 
 #define HID_GET_REPORT_PROTOCOL		0x60
 #define HID_GET_BOOT_PROTOCOL		0x61
+#define HID_SET_REPORT_PROTOCOL		0x70
+#define HID_SET_BOOT_PROTOCOL		0x71
 
 static void hid_prepare_reply_protocol_mode(const void *data, uint16_t len)
 {
@@ -3237,6 +3239,8 @@ static void hid_ctrl_cid_hook_cb(const void *data, uint16_t len,
 	switch (header) {
 	case HID_GET_REPORT_PROTOCOL:
 	case HID_GET_BOOT_PROTOCOL:
+	case HID_SET_REPORT_PROTOCOL:
+	case HID_SET_BOOT_PROTOCOL:
 		hid_prepare_reply_protocol_mode(data, len);
 		break;
 	}
@@ -3447,6 +3451,20 @@ static void test_hidhost_get_protocol(const void *test_data)
 		tester_test_failed();
 }
 
+static void test_hidhost_set_protocol(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->set_protocol(&bdaddr, BTHH_REPORT_MODE);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_test_failed();
+}
+
 #define test_bredrle(name, data, test_setup, test, test_teardown) \
 	do { \
 		struct test_data *user; \
@@ -3808,5 +3826,9 @@ int main(int argc, char *argv[])
 			&hidhost_test_get_protocol, setup_hidhost_connect,
 				test_hidhost_get_protocol, teardown);
 
+	test_bredrle("HIDHost SetProtocol Success",
+			&hidhost_test_get_protocol, setup_hidhost_connect,
+				test_hidhost_set_protocol, teardown);
+
 	return tester_run();
 }
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 07/11] android/tester: Add HIDhost GetProtocol test
From: Ravi kumar Veeramally @ 2014-01-16 23:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1389914751-18545-1-git-send-email-ravikumar.veeramally@linux.intel.com>

---
 android/android-tester.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 99 insertions(+), 2 deletions(-)

diff --git a/android/android-tester.c b/android/android-tester.c
index 2adbba1..56be850 100644
--- a/android/android-tester.c
+++ b/android/android-tester.c
@@ -3195,6 +3195,64 @@ static void setup_hidhost_interface(const void *test_data)
 	tester_setup_complete();
 }
 
+#define HID_GET_REPORT_PROTOCOL		0x60
+#define HID_GET_BOOT_PROTOCOL		0x61
+
+static void hid_prepare_reply_protocol_mode(const void *data, uint16_t len)
+{
+	struct test_data *t_data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+	uint8_t pdu[2] = { 0, 0 };
+	uint16_t pdu_len = 0;
+
+	pdu_len = 2;
+	pdu[0] = 0xa0;
+	pdu[1] = 0x00;
+
+	bthost_send_cid(bthost, t_data->ctrl_handle, t_data->ctrl_cid,
+						(void *)pdu, pdu_len);
+}
+
+static void hid_intr_cid_hook_cb(const void *data, uint16_t len,
+							void *user_data)
+{
+}
+
+static void hid_intr_connect_cb(uint16_t handle, uint16_t cid, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	data->intr_handle = handle;
+	data->intr_cid = cid;
+
+	bthost_add_cid_hook(bthost, handle, cid, hid_intr_cid_hook_cb, NULL);
+}
+
+static void hid_ctrl_cid_hook_cb(const void *data, uint16_t len,
+							void *user_data)
+{
+	uint8_t header = ((uint8_t *) data)[0];
+
+	switch (header) {
+	case HID_GET_REPORT_PROTOCOL:
+	case HID_GET_BOOT_PROTOCOL:
+		hid_prepare_reply_protocol_mode(data, len);
+		break;
+	}
+}
+
+static void hid_ctrl_connect_cb(uint16_t handle, uint16_t cid, void *user_data)
+{
+	struct test_data *data = tester_get_data();
+	struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+	data->ctrl_handle = handle;
+	data->ctrl_cid = cid;
+
+	bthost_add_cid_hook(bthost, handle, cid, hid_ctrl_cid_hook_cb, NULL);
+}
+
 static void hid_sdp_cid_hook_cb(const void *data, uint16_t len, void *user_data)
 {
 	struct test_data *t_data = tester_get_data();
@@ -3308,9 +3366,9 @@ static void setup_hidhost_connect(const void *test_data)
 	/* Emulate SDP (PSM = 1) */
 	bthost_add_l2cap_server(bthost, 1, hid_sdp_search_cb, NULL);
 	/* Emulate Control Channel (PSM = 17) */
-	bthost_add_l2cap_server(bthost, 17, NULL, NULL);
+	bthost_add_l2cap_server(bthost, 17, hid_ctrl_connect_cb, NULL);
 	/* Emulate Interrupt Channel (PSM = 19) */
-	bthost_add_l2cap_server(bthost, 19, NULL, NULL);
+	bthost_add_l2cap_server(bthost, 19, hid_intr_connect_cb, NULL);
 
 	bthost_set_cmd_complete_cb(bthost, emu_powered_complete, data);
 	bthost_write_scan_enable(bthost, 0x03);
@@ -3354,6 +3412,41 @@ static void test_hidhost_virtual_unplug(const void *test_data)
 		tester_test_failed();
 }
 
+static void hid_protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t status,
+						bthh_protocol_mode_t mode)
+{
+	struct test_data *data = tester_get_data();
+	const struct hidhost_generic_data *test = data->test_data;
+
+	if (data->cb_count == test->expected_cb_count &&
+					status == test->expected_status &&
+					mode == test->expected_protocol_mode)
+		tester_test_passed();
+	else
+		tester_test_failed();
+}
+
+static const struct hidhost_generic_data hidhost_test_get_protocol = {
+	.expected_hal_cb.protocol_mode_cb = hid_protocol_mode_cb,
+	.expected_cb_count = 1,
+	.expected_protocol_mode = BTHH_BOOT_MODE,
+	.expected_status = BTHH_OK,
+};
+
+static void test_hidhost_get_protocol(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->get_protocol(&bdaddr, BTHH_REPORT_MODE);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_test_failed();
+}
+
 #define test_bredrle(name, data, test_setup, test, test_teardown) \
 	do { \
 		struct test_data *user; \
@@ -3711,5 +3804,9 @@ int main(int argc, char *argv[])
 				&hidhost_test_disconnect, setup_hidhost_connect,
 				test_hidhost_virtual_unplug, teardown);
 
+	test_bredrle("HIDHost GetProtocol Success",
+			&hidhost_test_get_protocol, setup_hidhost_connect,
+				test_hidhost_get_protocol, teardown);
+
 	return tester_run();
 }
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 06/11] android/tester: Add HIDhost VirtualUnplug test
From: Ravi kumar Veeramally @ 2014-01-16 23:25 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1389914751-18545-1-git-send-email-ravikumar.veeramally@linux.intel.com>

---
 android/android-tester.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/android/android-tester.c b/android/android-tester.c
index 8edabb1..2adbba1 100644
--- a/android/android-tester.c
+++ b/android/android-tester.c
@@ -3340,6 +3340,20 @@ static void test_hidhost_disconnect(const void *test_data)
 		tester_test_failed();
 }
 
+static void test_hidhost_virtual_unplug(const void *test_data)
+{
+	struct test_data *data = tester_get_data();
+	const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+	bt_bdaddr_t bdaddr;
+	bt_status_t bt_status;
+
+	data->cb_count = 0;
+	bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+	bt_status = data->if_hid->virtual_unplug(&bdaddr);
+	if (bt_status != BT_STATUS_SUCCESS)
+		tester_test_failed();
+}
+
 #define test_bredrle(name, data, test_setup, test, test_teardown) \
 	do { \
 		struct test_data *user; \
@@ -3693,5 +3707,9 @@ int main(int argc, char *argv[])
 				&hidhost_test_disconnect, setup_hidhost_connect,
 				test_hidhost_disconnect, teardown);
 
+	test_bredrle("HIDHost VirtualUnplug Success",
+				&hidhost_test_disconnect, setup_hidhost_connect,
+				test_hidhost_virtual_unplug, teardown);
+
 	return tester_run();
 }
-- 
1.8.3.2


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox