Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH] Bluetooth: Set HCI_QUIRK_RESET_ON_CLOSE for Socket SDIO cards
From: Marcel Holtmann @ 2013-12-23 14:52 UTC (permalink / raw)
  To: linux-bluetooth

The Socket Bluetooth SDIO cards are branded versions of Toshiba SD-BT2
and they do not support sending HCI_Reset as first command. To make
this card work the HCI_QUIRK_RESET_ON_CLOSE quirk needs to be set
before registering the controller.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
 drivers/bluetooth/btsdio.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c
index b61440aaee65..1f6815825e61 100644
--- a/drivers/bluetooth/btsdio.c
+++ b/drivers/bluetooth/btsdio.c
@@ -333,6 +333,9 @@ static int btsdio_probe(struct sdio_func *func,
 	hdev->flush    = btsdio_flush;
 	hdev->send     = btsdio_send_frame;
 
+	if (func->vendor == 0x0104 && func->device == 0x00c5)
+		set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+
 	err = hci_register_dev(hdev);
 	if (err < 0) {
 		hci_free_dev(hdev);
-- 
1.8.4.2


^ permalink raw reply related

* [PATCHv2] btdev: Return Command Status for Authentication Requested
From: Andrei Emeltchenko @ 2013-12-23 15:07 UTC (permalink / raw)
  To: linux-bluetooth

From: Andrei Emeltchenko <andrei.emeltchenko@intel.com>

Add support for "Authentication Requested" command
---
 emulator/btdev.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/emulator/btdev.c b/emulator/btdev.c
index d0dff74..b1e804b 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -268,6 +268,7 @@ static void set_bredr_commands(struct btdev *btdev)
 	btdev->commands[0]  |= 0x80;	/* Cancel Create Connection */
 	btdev->commands[1]  |= 0x01;	/* Accept Connection Request */
 	btdev->commands[1]  |= 0x02;	/* Reject Connection Request */
+	btdev->commands[1]  |= 0x80;	/* Authentication Requested */
 	btdev->commands[2]  |= 0x08;	/* Remote Name Request */
 	btdev->commands[2]  |= 0x10;	/* Cancel Remote Name Request */
 	btdev->commands[2]  |= 0x20;	/* Read Remote Supported Features */
@@ -1191,6 +1192,12 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
 		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
 		break;
 
+	case BT_HCI_CMD_AUTH_REQUESTED:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+		break;
+
 	case BT_HCI_CMD_REMOTE_NAME_REQUEST:
 		if (btdev->type == BTDEV_TYPE_LE)
 			goto unsupported;
-- 
1.8.3.2


^ permalink raw reply related

* Re: [PATCH BlueZ 1/2] android/AVDTP: Remove get_all parameter for get_capability callback
From: Szymon Janc @ 2013-12-23 15:31 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <1387807690-31617-1-git-send-email-luiz.dentz@gmail.com>

Hi Luiz,

> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> 
> This is not necessary anymore since all delay reporting is now added
> automatically whenever supported.
> ---
>  android/avdtp.c   | 4 ++--
>  android/avdtp.h   | 1 -
>  unit/test-avdtp.c | 8 ++++----
>  3 files changed, 6 insertions(+), 7 deletions(-)
> 
> diff --git a/android/avdtp.c b/android/avdtp.c
> index 02ee920..3cf390f 100644
> --- a/android/avdtp.c
> +++ b/android/avdtp.c
> @@ -1192,8 +1192,8 @@ static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
>  		goto failed;
>  	}
>  
> -	if (!sep->ind->get_capability(session, sep, get_all, &caps,
> -							&err, sep->user_data))
> +	if (!sep->ind->get_capability(session, sep, &caps, &err,
> +							sep->user_data))
>  		goto failed;
>  
>  	for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
> diff --git a/android/avdtp.h b/android/avdtp.h
> index d371653..e8d1907 100644
> --- a/android/avdtp.h
> +++ b/android/avdtp.h
> @@ -161,7 +161,6 @@ struct avdtp_sep_cfm {
>  struct avdtp_sep_ind {
>  	gboolean (*get_capability) (struct avdtp *session,
>  					struct avdtp_local_sep *sep,
> -					gboolean get_all,
>  					GSList **caps, uint8_t *err,
>  					void *user_data);
>  	gboolean (*set_configuration) (struct avdtp *session,
> diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
> index 458daa7..66c45f3 100644
> --- a/unit/test-avdtp.c
> +++ b/unit/test-avdtp.c
> @@ -269,8 +269,8 @@ static void execute_context(struct context *context)
>  
>  static gboolean sep_getcap_ind(struct avdtp *session,
>  					struct avdtp_local_sep *sep,
> -					gboolean get_all, GSList **caps,
> -					uint8_t *err, void *user_data)
> +					GSList **caps, uint8_t *err,
> +					void *user_data)
>  {
>  	struct avdtp_service_capability *media_transport, *media_codec;
>  	struct avdtp_media_codec_capability *codec_caps;
> @@ -550,8 +550,8 @@ static void test_server_0_sep(gconstpointer data)
>  
>  static gboolean sep_getcap_ind_frg(struct avdtp *session,
>  					struct avdtp_local_sep *sep,
> -					gboolean get_all, GSList **caps,
> -					uint8_t *err, void *user_data)
> +					GSList **caps, uint8_t *err,
> +					void *user_data)
>  {
>  	struct avdtp_service_capability *media_transport, *media_codec;
>  	struct avdtp_service_capability *content_protection;
> 

Both patches applied, thanks.

-- 
BR
Szymon Janc



^ permalink raw reply

* [RFC] btdev: Send Link Key Request on Authentication Requested
From: Andrei Emeltchenko @ 2013-12-23 15:33 UTC (permalink / raw)
  To: linux-bluetooth

From: Andrei Emeltchenko <andrei.emeltchenko@intel.com>

The Link Key Request event shall be generated when Simple Pairing Mode
is enabled.
---
 emulator/btdev.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/emulator/btdev.c b/emulator/btdev.c
index b1e804b..c0cfdef 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -2005,6 +2005,19 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode,
 		conn_complete(btdev, rcr->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
 		break;
 
+	case BT_HCI_CMD_AUTH_REQUESTED:
+		if (btdev->type == BTDEV_TYPE_LE)
+			return;
+
+		if (btdev->simple_pairing_mode) {
+			struct bt_hci_evt_link_key_request lkr;
+
+			memcpy(lkr.bdaddr, btdev->conn->bdaddr, 6);
+			send_event(btdev, BT_HCI_EVT_LINK_KEY_REQUEST, &lkr,
+								sizeof(lkr));
+		}
+		break;
+
 	case BT_HCI_CMD_REMOTE_NAME_REQUEST:
 		if (btdev->type == BTDEV_TYPE_LE)
 			return;
-- 
1.8.3.2


^ permalink raw reply related

* Re: [RFC] btdev: Send Link Key Request on Authentication Requested
From: Marcel Holtmann @ 2013-12-23 15:41 UTC (permalink / raw)
  To: Andrei Emeltchenko; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <1387812799-21728-1-git-send-email-Andrei.Emeltchenko.news@gmail.com>

Hi Andrei,

> From: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
> 
> The Link Key Request event shall be generated when Simple Pairing Mode
> is enabled.
> ---
> emulator/btdev.c | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
> 
> diff --git a/emulator/btdev.c b/emulator/btdev.c
> index b1e804b..c0cfdef 100644
> --- a/emulator/btdev.c
> +++ b/emulator/btdev.c
> @@ -2005,6 +2005,19 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode,
> 		conn_complete(btdev, rcr->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
> 		break;
> 
> +	case BT_HCI_CMD_AUTH_REQUESTED:
> +		if (btdev->type == BTDEV_TYPE_LE)
> +			return;

please make sure that you send correct command status event first. The Core specification is pretty detailed on what needs to be send when here.

> +
> +		if (btdev->simple_pairing_mode) {
> +			struct bt_hci_evt_link_key_request lkr;
> +
> +			memcpy(lkr.bdaddr, btdev->conn->bdaddr, 6);
> +			send_event(btdev, BT_HCI_EVT_LINK_KEY_REQUEST, &lkr,
> +								sizeof(lkr));
> +		}
> +		break;
> +
> 	case BT_HCI_CMD_REMOTE_NAME_REQUEST:
> 		if (btdev->type == BTDEV_TYPE_LE)
> 			return;

Regards

Marcel


^ permalink raw reply

* Re: [PATCH v2 1/6] android/tester: Seperate Socket HAL cbs from Bluetooth HAL cb
From: Szymon Janc @ 2013-12-23 15:59 UTC (permalink / raw)
  To: Grzegorz Kolodziejczyk; +Cc: linux-bluetooth
In-Reply-To: <1387808154-21661-1-git-send-email-grzegorz.kolodziejczyk@tieto.com>

Hi Grzegorz,

> This patch adds seperate callbacks structure for socket HAL test cases.
> Is's needed beceause Socket HAL cb have other purpose than Bluetooth HAL
> cb. Callbacks are now initialized outside test setup function and
> cb struct depends on HAL type.
> ---
>  android/android-tester.c | 63 ++++++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 56 insertions(+), 7 deletions(-)
> 
> diff --git a/android/android-tester.c b/android/android-tester.c
> index 0383115..f5ab14f 100644
> --- a/android/android-tester.c
> +++ b/android/android-tester.c
> @@ -872,7 +872,6 @@ static void setup(struct test_data *data)
>  {
>  	const hw_module_t *module;
>  	hw_device_t *device;
> -	bt_status_t status;
>  	int signal_fd[2];
>  	char buf[1024];
>  	pid_t pid;
> @@ -935,19 +934,21 @@ static void setup(struct test_data *data)
>  		return;
>  	}
>  
> -	status = data->if_bluetooth->init(&bt_callbacks);
> -	if (status != BT_STATUS_SUCCESS) {
> -		data->if_bluetooth = NULL;
> -		tester_setup_failed();
> -	}
>  }
>  
>  static void setup_base(const void *test_data)
>  {
>  	struct test_data *data = tester_get_data();
> +	bt_status_t status;
>  
>  	setup(data);
>  
> +	status = data->if_bluetooth->init(&bt_callbacks);
> +	if (status != BT_STATUS_SUCCESS) {
> +		data->if_bluetooth = NULL;
> +		tester_setup_failed();
> +	}
> +
>  	tester_setup_complete();
>  }
>  
> @@ -958,6 +959,11 @@ static void setup_enabled_adapter(const void *test_data)
>  
>  	setup(data);
>  
> +	status = data->if_bluetooth->init(&bt_callbacks);
> +	if (status != BT_STATUS_SUCCESS) {
> +		data->if_bluetooth = NULL;
> +		tester_setup_failed();
> +	}
>  	status = data->if_bluetooth->enable();
>  	if (status != BT_STATUS_SUCCESS)
>  		tester_setup_failed();
> @@ -1157,6 +1163,20 @@ static void test_setprop_service_record_invalid(const void *test_data)
>  
>  /* Test Socket HAL */
>  
> +static void adapter_socket_state_changed_cb(bt_state_t state)
> +{
> +	switch (state) {
> +	case BT_STATE_ON:
> +		setup_powered_emulated_remote();
> +		break;
> +	case BT_STATE_OFF:
> +		tester_setup_failed();
> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
>  const bt_bdaddr_t bdaddr_dummy = {
>  	.address = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55}
>  };
> @@ -1233,13 +1253,36 @@ static const struct socket_data btsock_inv_listen_listen = {
>  	.test_channel = true,
>  };
>  
> +static bt_callbacks_t bt_socket_callbacks = {
> +	.size = sizeof(bt_callbacks),
> +	.adapter_state_changed_cb = adapter_socket_state_changed_cb,
> +	.adapter_properties_cb = NULL,
> +	.remote_device_properties_cb = NULL,
> +	.device_found_cb = NULL,
> +	.discovery_state_changed_cb = NULL,
> +	.pin_request_cb = NULL,
> +	.ssp_request_cb = NULL,
> +	.bond_state_changed_cb = NULL,
> +	.acl_state_changed_cb = NULL,
> +	.thread_evt_cb = NULL,
> +	.dut_mode_recv_cb = NULL,
> +	.le_test_mode_cb = NULL
> +};
> +
>  static void setup_socket_interface(const void *test_data)
>  {
>  	struct test_data *data = tester_get_data();
> +	bt_status_t status;
>  	const void *sock;
>  
>  	setup(data);
>  
> +	status = data->if_bluetooth->init(&bt_socket_callbacks);
> +	if (status != BT_STATUS_SUCCESS) {
> +		data->if_bluetooth = NULL;
> +		tester_setup_failed();
> +	}
> +
>  	sock = data->if_bluetooth->get_profile_interface(BT_PROFILE_SOCKETS_ID);
>  	if (!sock) {
>  		tester_setup_failed();
> @@ -1254,11 +1297,17 @@ static void setup_socket_interface(const void *test_data)
>  static void setup_socket_interface_enabled(const void *test_data)
>  {
>  	struct test_data *data = tester_get_data();
> -	const void *sock;
>  	bt_status_t status;
> +	const void *sock;
>  
>  	setup(data);
>  
> +	status = data->if_bluetooth->init(&bt_socket_callbacks);
> +	if (status != BT_STATUS_SUCCESS) {
> +		data->if_bluetooth = NULL;
> +		tester_setup_failed();
> +	}
> +
>  	sock = data->if_bluetooth->get_profile_interface(BT_PROFILE_SOCKETS_ID);
>  	if (!sock) {
>  		tester_setup_failed();
> 

All patches applied, thanks.

-- 
BR
Szymon Janc

^ permalink raw reply

* Re: [PATCH] Bluetooth: Set HCI_QUIRK_RESET_ON_CLOSE for Socket SDIO cards
From: Johan Hedberg @ 2013-12-23 16:51 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-bluetooth
In-Reply-To: <1387810352-48383-1-git-send-email-marcel@holtmann.org>

Hi Marcel,

On Mon, Dec 23, 2013, Marcel Holtmann wrote:
> The Socket Bluetooth SDIO cards are branded versions of Toshiba SD-BT2
> and they do not support sending HCI_Reset as first command. To make
> this card work the HCI_QUIRK_RESET_ON_CLOSE quirk needs to be set
> before registering the controller.
> 
> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
> ---
>  drivers/bluetooth/btsdio.c | 3 +++
>  1 file changed, 3 insertions(+)

Applied to bluetooth-next. Thanks.

Johan

^ permalink raw reply

* Re: [PATCHv2] btdev: Return Command Status for Authentication Requested
From: Johan Hedberg @ 2013-12-23 17:06 UTC (permalink / raw)
  To: Andrei Emeltchenko; +Cc: linux-bluetooth
In-Reply-To: <1387811245-21272-1-git-send-email-Andrei.Emeltchenko.news@gmail.com>

Hi Andrei,

On Mon, Dec 23, 2013, Andrei Emeltchenko wrote:
> Add support for "Authentication Requested" command
> ---
>  emulator/btdev.c | 7 +++++++
>  1 file changed, 7 insertions(+)
> 
> diff --git a/emulator/btdev.c b/emulator/btdev.c
> index d0dff74..b1e804b 100644
> --- a/emulator/btdev.c
> +++ b/emulator/btdev.c
> @@ -268,6 +268,7 @@ static void set_bredr_commands(struct btdev *btdev)
>  	btdev->commands[0]  |= 0x80;	/* Cancel Create Connection */
>  	btdev->commands[1]  |= 0x01;	/* Accept Connection Request */
>  	btdev->commands[1]  |= 0x02;	/* Reject Connection Request */
> +	btdev->commands[1]  |= 0x80;	/* Authentication Requested */
>  	btdev->commands[2]  |= 0x08;	/* Remote Name Request */
>  	btdev->commands[2]  |= 0x10;	/* Cancel Remote Name Request */
>  	btdev->commands[2]  |= 0x20;	/* Read Remote Supported Features */
> @@ -1191,6 +1192,12 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
>  		cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
>  		break;
>  
> +	case BT_HCI_CMD_AUTH_REQUESTED:
> +		if (btdev->type == BTDEV_TYPE_LE)
> +			return;

I think this should be "goto unsupported;" instead of a direct return.

Johan

^ permalink raw reply

* Re: [REGRESSION] rfcomm (userland) broken by commit 29cd718b
From: Alexander Holler @ 2013-12-24 13:21 UTC (permalink / raw)
  To: Gianluca Anzolin, Peter Hurley
  Cc: marcel, Gustavo Padovan, linux-bluetooth, gregkh, jslaby,
	linux-kernel
In-Reply-To: <20131216211542.GA20419@sottospazio.it>

Hello,

Am 16.12.2013 22:15, schrieb Gianluca Anzolin:

> This last patch does release the port in that situation.
>
> Tested with:
> # rfcomm bind 1 <addr>
> # rfcomm release 1
>
> and with
> # rfcomm connect 1 <addr>

I've just tested the patch rfc3.patch as attached to the mail I'm 
replying to on top of 3.12.6, it works here too.

I've tested 3 use cases:

1. rfcomm connect then ctrl-c,
2. rfcomm connect, screen /dev/rfcommN, ctrl-c for rfcomm then quit screen
3. rfcomm connect, disappearing remote device (hard power down of the 
remote device)

Everything worked as expected.

Thanks again, I wish everyone a merry christmas,

Alexander Holler

^ permalink raw reply

* [PATCH v2] Bluetooth: Add hci_h4p driver
From: Pali Rohár @ 2013-12-27 11:02 UTC (permalink / raw)
  To: Marcel Holtmann,
	Ивайло Димитров
  Cc: Gustavo Padovan, Johan Hedberg, Pavel Machek, linux-kernel,
	linux-bluetooth, Ville Tervo, sre
In-Reply-To: <1379703710-5757-1-git-send-email-pali.rohar@gmail.com>

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

I'm sending updated version of hci_h4p bluetooth driver. It is needed for
Nokia N900 bluetooth hardware. This (v2) is older version of hci_h4p driver,
but I tested it with v3.13-rc3 kernel on Nokia N900 and working without any
problems. Previous (v1) version had some problems. So for future development
please use this (v2) version of hci_h4p driver.

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 11a6104..95155c3 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -242,4 +242,14 @@ config BT_WILINK
 
 	  Say Y here to compile support for Texas Instrument's WiLink7 driver
 	  into the kernel or say M to compile it as module.
+
+config BT_HCIH4P
+	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 (hci_h4p).
 endmenu
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 9fe8a87..77b01b6 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -31,4 +31,6 @@ hci_uart-$(CONFIG_BT_HCIUART_ATH3K)	+= hci_ath.o
 hci_uart-$(CONFIG_BT_HCIUART_3WIRE)	+= hci_h5.o
 hci_uart-objs				:= $(hci_uart-y)
 
+obj-y				+= hci_h4p/
+
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/bluetooth/hci_h4p/Makefile b/drivers/bluetooth/hci_h4p/Makefile
new file mode 100644
index 0000000..f20bd9a
--- /dev/null
+++ b/drivers/bluetooth/hci_h4p/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux Bluetooth HCI device drivers.
+#
+
+obj-$(CONFIG_BT_HCIH4P)		+= hci_h4p.o
+
+hci_h4p-objs := core.o fw.o uart.o fw-csr.o fw-bcm.o fw-ti1273.o
diff --git a/drivers/bluetooth/hci_h4p/core.c b/drivers/bluetooth/hci_h4p/core.c
new file mode 100644
index 0000000..e76e889
--- /dev/null
+++ b/drivers/bluetooth/hci_h4p/core.c
@@ -0,0 +1,1357 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005-2008 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/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 <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#include <linux/bluetooth/hci_h4p.h>
+
+#include "hci_h4p.h"
+
+static struct task_struct *h4p_thread;
+
+/* 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) {
+		NBT_DBG_POWER("Enabling %p\n", 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) {
+		NBT_DBG_POWER("Disabling %p\n", 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)
+{
+	if (unlikely(!h4p_thread))
+		return;
+
+	set_bit(H4P_SCHED_TRANSFER_MODE, &info->pm_flags);
+
+	if (unlikely(!test_bit(H4P_TRANSFER_MODE, &info->pm_flags)))
+		wake_up_process(h4p_thread);
+}
+
+static void hci_h4p_disable_tx(struct hci_h4p_info *info)
+{
+	NBT_DBG_POWER("\n");
+
+	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;
+	NBT_DBG_POWER("\n");
+
+	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;
+
+	NBT_DBG("Sending alive packet\n");
+
+	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);
+
+	NBT_DBG("Alive packet sent\n");
+
+	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;
+
+	NBT_DBG("Received alive packet\n");
+	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;
+
+	NBT_DBG("Sending negotiation..\n");
+
+	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;
+
+	NBT_DBG("Negotiation succesful\n");
+	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))) {
+		if (bt_cb(skb)->pkt_type == H4_NEG_PKT) {
+			hci_h4p_negotiation_packet(info, skb);
+			info->rx_state = WAIT_FOR_PKT_TYPE;
+			return;
+		}
+		if (bt_cb(skb)->pkt_type == 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)) {
+			NBT_DBG("fw_event\n");
+			hci_h4p_parse_fw_event(info, skb);
+			return;
+		}
+	}
+
+	hci_recv_frame(info->hdev, skb);
+	NBT_DBG("Frame sent to upper layer\n");
+}
+
+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) {
+			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, "Too long frame.\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 allways 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;
+
+	NBT_DBG("tasklet woke up\n");
+	NBT_DBG_TRANSFER("rx_tasklet woke up\ndata ");
+
+	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->hdev->stat.byte_rx++;
+		NBT_DBG_TRANSFER_NF("0x%.2x  ", byte);
+		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:
+	NBT_DBG_TRANSFER_NF("\n");
+	NBT_DBG("rx_ended\n");
+}
+
+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;
+
+	NBT_DBG("tasklet woke up\n");
+	NBT_DBG_TRANSFER("tx_tasklet woke up\n data ");
+
+	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 */
+		NBT_DBG("skb ready\n");
+		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;
+		} else
+			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)) {
+		NBT_DBG_TRANSFER_NF("0x%.2x ", skb->data[sent]);
+		hci_h4p_outb(info, UART_TX, skb->data[sent]);
+		sent++;
+	}
+
+	info->hdev->stat.byte_tx += sent;
+	NBT_DBG_TRANSFER_NF("\n");
+	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;
+
+	NBT_DBG("In interrupt handler iir 0x%.2x\n", 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;
+	}
+
+	NBT_DBG_POWER("gpio interrupt %d\n", 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;
+
+	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);
+		set_bit(H4P_ACTIVE_MODE, &info->pm_flags);
+		BT_DBG("Change pm constraints to: %s", set ?
+				"set" : "clear");
+		return;
+	}
+
+	if (!set && test_bit(H4P_ACTIVE_MODE, &info->pm_flags)) {
+		bt_plat_data->set_pm_limits(info->dev, set);
+		clear_bit(H4P_ACTIVE_MODE, &info->pm_flags);
+		BT_DBG("Change pm constraints to: %s",
+				set ? "set" : "clear");
+		return;
+	}
+
+	BT_DBG("pm constraints remains: %s",
+			set ? "set" : "clear");
+}
+
+static int h4p_run(void *data)
+{
+#define TIMEOUT_MIN msecs_to_jiffies(100)
+#define TIMEOUT_MAX msecs_to_jiffies(2000)
+	struct hci_h4p_info *info = data;
+	unsigned long last_jiffies = jiffies;
+	unsigned long timeout = TIMEOUT_MIN;
+	unsigned long elapsed;
+	BT_DBG("");
+	set_user_nice(current, -10);
+
+	while (!kthread_should_stop()) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (!test_bit(H4P_SCHED_TRANSFER_MODE, &info->pm_flags)) {
+			if (timeout != TIMEOUT_MIN) {
+				BT_DBG("Exit from active mode. Rest. constr.");
+				hci_h4p_set_pm_limits(info, false);
+			}
+
+			BT_DBG("No pending events. Sleeping.");
+			schedule();
+		}
+
+		set_bit(H4P_TRANSFER_MODE, &info->pm_flags);
+		clear_bit(H4P_SCHED_TRANSFER_MODE, &info->pm_flags);
+
+		elapsed = jiffies - last_jiffies;
+
+		BT_DBG("Wake up. %u msec expired since last BT activity.",
+				jiffies_to_msecs(elapsed));
+		BT_DBG("Timeout before calculation = %u",
+				jiffies_to_msecs(timeout));
+
+		/* Empiric analyzer  :-) */
+		if (elapsed < TIMEOUT_MIN) {
+			timeout <<= 1;
+			timeout = (timeout > TIMEOUT_MAX) ?
+				TIMEOUT_MAX : timeout;
+		} else {
+			timeout = (elapsed > timeout - TIMEOUT_MIN) ?
+				TIMEOUT_MIN : timeout - elapsed;
+		}
+
+		BT_DBG("Timeout after calculation = %u",
+				jiffies_to_msecs(timeout));
+
+		/* Sometimes we get couple of HCI command during (e)SCO
+		   connection. Turn ON transfer mode _ONLY_ if there is
+		   still BT activity after 100ms sleep */
+		if (timeout == TIMEOUT_MIN)
+			BT_DBG("Do not enable transfer mode yet");
+		else {
+			hci_h4p_set_pm_limits(info, true);
+			BT_DBG("Set active mode for %u msec.",
+					jiffies_to_msecs(timeout));
+		}
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(timeout);
+
+		last_jiffies = jiffies;
+		clear_bit(H4P_TRANSFER_MODE, &info->pm_flags);
+	}
+
+	hci_h4p_set_pm_limits(info, false);
+
+	return 0;
+}
+
+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;
+	}
+
+	reinit_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;
+	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);
+
+	NBT_DBG("hci up and running\n");
+	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;
+
+	/* Wake up h4p_thread which removes pm constraints */
+	wake_up_process(h4p_thread);
+
+	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;
+
+	if (!hdev) {
+		printk(KERN_WARNING "hci_h4p: Frame for unknown device\n");
+		return -ENODEV;
+	}
+
+	NBT_DBG("dev %p, skb %p\n", 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++)
+		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, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
+		       info->bd_addr[0], info->bd_addr[1], info->bd_addr[2],
+		       info->bd_addr[3], info->bd_addr[4], info->bd_addr[5]);
+}
+
+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");
+		return -ENODEV;
+	}
+
+	if (hci_register_dev(hdev) < 0) {
+		dev_err(info->dev, "hci_register failed %s.\n", hdev->name);
+		hci_h4p_sysfs_remove_files(info->dev);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+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 = kzalloc(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");
+		kfree(info);
+		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;
+
+	NBT_DBG("RESET gpio: %d\n", info->reset_gpio);
+	NBT_DBG("BTWU gpio: %d\n", info->bt_wakeup_gpio);
+	NBT_DBG("HOSTWU gpio: %d\n", info->host_wakeup_gpio);
+	NBT_DBG("sysclk: %d\n", info->bt_sysclk);
+
+	init_completion(&info->test_completion);
+	complete_all(&info->test_completion);
+
+	if (!info->reset_gpio_shared) {
+		err = gpio_request(info->reset_gpio, "bt_reset");
+		if (err < 0) {
+			dev_err(&pdev->dev, "Cannot get GPIO line %d\n",
+				info->reset_gpio);
+			goto cleanup_setup;
+		}
+	}
+
+	err = gpio_request(info->bt_wakeup_gpio, "bt_wakeup");
+	if (err < 0) {
+		dev_err(info->dev, "Cannot get GPIO line 0x%d",
+			info->bt_wakeup_gpio);
+		if (!info->reset_gpio_shared)
+			gpio_free(info->reset_gpio);
+		goto cleanup_setup;
+	}
+
+	err = gpio_request(info->host_wakeup_gpio, "host_wakeup");
+	if (err < 0) {
+		dev_err(info->dev, "Cannot get GPIO line %d",
+		       info->host_wakeup_gpio);
+		if (!info->reset_gpio_shared)
+			gpio_free(info->reset_gpio);
+		gpio_free(info->bt_wakeup_gpio);
+		goto cleanup_setup;
+	}
+
+	gpio_direction_output(info->reset_gpio, 0);
+	gpio_direction_output(info->bt_wakeup_gpio, 0);
+	gpio_direction_input(info->host_wakeup_gpio);
+
+	info->irq = bt_plat_data->uart_irq;
+	info->uart_base = ioremap(bt_plat_data->uart_base, SZ_2K);
+	info->uart_iclk = clk_get(NULL, bt_plat_data->uart_iclk);
+	info->uart_fclk = clk_get(NULL, bt_plat_data->uart_fclk);
+
+	err = request_irq(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);
+		goto cleanup;
+	}
+
+	err = request_irq(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));
+		free_irq(info->irq, info);
+		goto cleanup;
+	}
+
+	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));
+		free_irq(info->irq, info);
+		free_irq(gpio_to_irq(info->host_wakeup_gpio), info);
+		goto cleanup;
+	}
+
+	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)
+		goto cleanup_irq;
+	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");
+		goto cleanup_irq;
+	}
+
+	h4p_thread = kthread_run(h4p_run, info, "h4p_pm");
+	if (IS_ERR(h4p_thread)) {
+		err = PTR_ERR(h4p_thread);
+		goto cleanup_irq;
+	}
+
+	return 0;
+
+cleanup_irq:
+	free_irq(info->irq, (void *)info);
+	free_irq(gpio_to_irq(info->host_wakeup_gpio), info);
+cleanup:
+	gpio_set_value(info->reset_gpio, 0);
+	if (!info->reset_gpio_shared)
+		gpio_free(info->reset_gpio);
+	gpio_free(info->bt_wakeup_gpio);
+	gpio_free(info->host_wakeup_gpio);
+
+cleanup_setup:
+
+	kfree(info);
+	return err;
+
+}
+
+static int hci_h4p_remove(struct platform_device *pdev)
+{
+	struct hci_h4p_info *info;
+
+	info = platform_get_drvdata(pdev);
+
+	kthread_stop(h4p_thread);
+
+	hci_h4p_sysfs_remove_files(info->dev);
+	hci_h4p_hci_close(info->hdev);
+	free_irq(gpio_to_irq(info->host_wakeup_gpio), info);
+	hci_unregister_dev(info->hdev);
+	hci_free_dev(info->hdev);
+	if (!info->reset_gpio_shared)
+		gpio_free(info->reset_gpio);
+	gpio_free(info->bt_wakeup_gpio);
+	gpio_free(info->host_wakeup_gpio);
+	free_irq(info->irq, (void *) info);
+	kfree(info);
+
+	return 0;
+}
+
+static struct platform_driver hci_h4p_driver = {
+	.probe		= hci_h4p_probe,
+	.remove		= hci_h4p_remove,
+	.driver		= {
+		.name	= "hci_h4p",
+	},
+};
+
+static int __init hci_h4p_init(void)
+{
+	int err = 0;
+
+	/* Register the driver with LDM */
+	err = platform_driver_register(&hci_h4p_driver);
+	if (err < 0)
+		printk(KERN_WARNING "failed to register hci_h4p driver\n");
+
+	return err;
+}
+
+static void __exit hci_h4p_exit(void)
+{
+	platform_driver_unregister(&hci_h4p_driver);
+}
+
+module_init(hci_h4p_init);
+module_exit(hci_h4p_exit);
+
+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/bluetooth/hci_h4p/fw-bcm.c b/drivers/bluetooth/hci_h4p/fw-bcm.c
new file mode 100644
index 0000000..390d021
--- /dev/null
+++ b/drivers/bluetooth/hci_h4p/fw-bcm.c
@@ -0,0 +1,149 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005-2008 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/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) {
+		NBT_DBG_FW("Setting bluetooth address\n");
+		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;
+
+	NBT_DBG_FW("Sending firmware\n");
+
+	time = jiffies;
+
+	info->fw_q = fw_queue;
+	skb = skb_dequeue(fw_queue);
+	if (!skb)
+		return -ENODATA;
+
+	NBT_DBG_FW("Sending commands\n");
+
+	/*
+	 * 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;
+	}
+
+	NBT_DBG_FW("Firmware sent in %d msecs\n",
+		   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/bluetooth/hci_h4p/fw-csr.c b/drivers/bluetooth/hci_h4p/fw-csr.c
new file mode 100644
index 0000000..020fa52
--- /dev/null
+++ b/drivers/bluetooth/hci_h4p/fw-csr.c
@@ -0,0 +1,152 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005-2008 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/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;
+
+	NBT_DBG_FW("Sending firmware\n");
+	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++) {
+		NBT_DBG_FW("Sending firmware command %d\n", 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/bluetooth/hci_h4p/fw-ti1273.c b/drivers/bluetooth/hci_h4p/fw-ti1273.c
new file mode 100644
index 0000000..32e5fa0
--- /dev/null
+++ b/drivers/bluetooth/hci_h4p/fw-ti1273.c
@@ -0,0 +1,112 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2009 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/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;
+
+	NBT_DBG_FW("Sending firmware\n");
+
+	time = jiffies;
+
+	fw_q = fw_queue;
+	skb = skb_dequeue(fw_queue);
+	if (!skb)
+		return -ENODATA;
+
+	NBT_DBG_FW("Sending commands\n");
+	/* 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;
+	}
+
+	NBT_DBG_FW("Firmware sent in %d msecs\n",
+		   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/bluetooth/hci_h4p/fw.c b/drivers/bluetooth/hci_h4p/fw.c
new file mode 100644
index 0000000..b3d39f9
--- /dev/null
+++ b/drivers/bluetooth/hci_h4p/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;
+	NBT_DBG_FW("Opening firmware man_id 0x%.2x ver_id 0x%.2x\n",
+			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/bluetooth/hci_h4p/hci_h4p.h b/drivers/bluetooth/hci_h4p/hci_h4p.h
new file mode 100644
index 0000000..d1d313b
--- /dev/null
+++ b/drivers/bluetooth/hci_h4p/hci_h4p.h
@@ -0,0 +1,248 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005-2008 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 <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#ifndef __DRIVERS_BLUETOOTH_HCI_H4P_H
+#define __DRIVERS_BLUETOOTH_HCI_H4P_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 NBT_DBG(fmt, arg...) \
+		pr_debug("%s: " fmt "" , __func__ , ## arg)
+
+#define NBT_DBG_FW(fmt, arg...) \
+		pr_debug("%s: " fmt "" , __func__ , ## arg)
+
+#define NBT_DBG_POWER(fmt, arg...) \
+		pr_debug("%s: " fmt "" , __func__ , ## arg)
+
+#define NBT_DBG_TRANSFER(fmt, arg...) \
+		pr_debug("%s: " fmt "" , __func__ , ## arg)
+
+#define NBT_DBG_TRANSFER_NF(fmt, arg...) \
+		pr_debug(fmt "" , ## arg)
+
+#define NBT_DBG_DMA(fmt, arg...) \
+		pr_debug("%s: " fmt "" , __func__ , ## arg)
+
+#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/bluetooth/hci_h4p/uart.c b/drivers/bluetooth/hci_h4p/uart.c
new file mode 100644
index 0000000..7973c6c
--- /dev/null
+++ b/drivers/bluetooth/hci_h4p/uart.c
@@ -0,0 +1,202 @@
+/*
+ * 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/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;
+
+	NBT_DBG("Setting speed %lu\n", 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/bluetooth/hci_h4p.h b/include/linux/bluetooth/hci_h4p.h
new file mode 100644
index 0000000..daf83fc
--- /dev/null
+++ b/include/linux/bluetooth/hci_h4p.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * Contact: Roger Quadros <roger.quadros@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
+ *
+ */
+
+
+/**
+ * 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);
+};

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply related

* Re: [PATCH v2] Bluetooth: Add hci_h4p driver
From: Pali Rohár @ 2013-12-27 11:34 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Ивайло Димитров,
	Gustavo Padovan, Johan Hedberg, Pavel Machek, linux-kernel,
	linux-bluetooth, Ville Tervo, sre
In-Reply-To: <1727897.LBX8128hIo@izba>

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

On Friday 27 December 2013 12:02:47 Pali Rohár wrote:
> I'm sending updated version of hci_h4p bluetooth driver. It is needed for
> Nokia N900 bluetooth hardware. This (v2) is older version of hci_h4p driver,
> but I tested it with v3.13-rc3 kernel on Nokia N900 and working without any
> problems. Previous (v1) version had some problems. So for future
> development please use this (v2) version of hci_h4p driver.
> 

EDIT: I tested it with 3.12 (not 3.13)

-- 
Pali Rohár
pali.rohar@gmail.com


[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply

* Re: [PATCH] Bluetooth: hidp: make sure input buffers are big enough
From: David Herrmann @ 2013-12-27 12:35 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: open list:HID CORE LAYER, Jiri Kosina,
	linux-bluetooth@vger.kernel.org development, Gustavo F. Padovan
In-Reply-To: <3109C2D8-E01C-4728-A593-1978BB3D202D@holtmann.org>

Hi Marcel

On Fri, Dec 20, 2013 at 2:36 PM, Marcel Holtmann <marcel@holtmann.org> wrote:
> Hi David,
>
>> HID core expects the input buffers to be at least of size 4096
>> (HID_MAX_BUFFER_SIZE). Other sizes will result in buffer-overflows if an
>> input-report is smaller than advertised. We could, like i2c, compute the
>> biggest report-size instead of using HID_MAX_BUFFER_SIZE, but this will
>> blow up if report-descriptors are changed after ->start() has been called.
>> So lets be safe and just use the biggest buffer we have.
>>
>> Note that this adds an additional copy to the HIDP input path. If there is
>> a way to make sure the skb-buf is big enough, we should use that instead.
>>
>> The best way would be to make hid-core honor the @size argument, though,
>> that sounds easier than it is. So lets just fix the buffer-overflows for
>> now and afterwards look for a faster way for all transport drivers.
>>
>> Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
>> ---
>> Hi
>>
>> Any ideas how to improve this patch? I'd like to avoid the extra copy but I have
>> no clue how the skb stuff works exactly.
>
> the buffers are coming straight from L2CAP. They might come in fragments. We have to reassemble them and while there are large packets for sure, we rather not want to allocate 4096 for every reassembled packet to make HID happy.
>
> I am not super familiar with the underlying memory management of SKBs, but I assume they use slices of some sort internally. So uses large SKBs for 20 byte HID report will cause an issue with all other protocols running on top of L2CAP

I was more thinking of an skb call to increase the underlying buffer
of a single package. We could call this together with skb_linearize()
in HIDP to guarantee the skb-buf is big enough. Technically, this will
probably result in the same behavior as my own patch so probably not
the way to go.

>> I also haven't figured out a nice way to make HID-core honor the "size"
>> parameter. hid-input depends on getting the whole input-report.
>
> I think this needs clearly fixing.

And we have a volunteer, yippie! ;)

I think hid_input_report() in hid-core.c is the only place that relies
on this. Maybe it really is easier to fix it. But I am not even
slightly familiar with that code. So if no-one of the HID core
developers steps up to fix it, we should take the transport-driver
fixes instead. As nearly all transport-drivers are affected by this,
maybe we should even move this buffer into hid_device and provide
hid_input_truncated_report() which does this.

Anyhow, waiting for Jiri's comments on this.

>> Comments welcome!
>> David
>>
>> net/bluetooth/hidp/core.c | 16 ++++++++++++++--
>> net/bluetooth/hidp/hidp.h |  4 ++++
>> 2 files changed, 18 insertions(+), 2 deletions(-)
>>
>> diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
>> index 292e619..d9fb934 100644
>> --- a/net/bluetooth/hidp/core.c
>> +++ b/net/bluetooth/hidp/core.c
>> @@ -430,6 +430,16 @@ static void hidp_del_timer(struct hidp_session *session)
>>               del_timer(&session->timer);
>> }
>>
>> +static void hidp_process_report(struct hidp_session *session,
>> +                             int type, const u8 *data, int len, int intr)
>> +{
>> +     if (len > HID_MAX_BUFFER_SIZE)
>> +             len = HID_MAX_BUFFER_SIZE;
>> +
>> +     memcpy(session->input_buf, data, len);
>> +     hid_input_report(session->hid, type, session->input_buf, len, intr);
>> +}
>> +
>> static void hidp_process_handshake(struct hidp_session *session,
>>                                       unsigned char param)
>> {
>> @@ -502,7 +512,8 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
>>                       hidp_input_report(session, skb);
>>
>>               if (session->hid)
>> -                     hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0);
>> +                     hidp_process_report(session, HID_INPUT_REPORT,
>> +                                         skb->data, skb->len, 0);
>>               break;
>>
>>       case HIDP_DATA_RTYPE_OTHER:
>> @@ -584,7 +595,8 @@ static void hidp_recv_intr_frame(struct hidp_session *session,
>>                       hidp_input_report(session, skb);
>>
>>               if (session->hid) {
>> -                     hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1);
>> +                     hidp_process_report(session, HID_INPUT_REPORT,
>> +                                         skb->data, skb->len, 1);
>>                       BT_DBG("report len %d", skb->len);
>>               }
>>       } else {
>> diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h
>> index ab52414..8798492 100644
>> --- a/net/bluetooth/hidp/hidp.h
>> +++ b/net/bluetooth/hidp/hidp.h
>> @@ -24,6 +24,7 @@
>> #define __HIDP_H
>>
>> #include <linux/types.h>
>> +#include <linux/hid.h>
>> #include <linux/kref.h>
>> #include <net/bluetooth/bluetooth.h>
>> #include <net/bluetooth/l2cap.h>
>> @@ -179,6 +180,9 @@ struct hidp_session {
>>
>>       /* Used in hidp_output_raw_report() */
>>       int output_report_success; /* boolean */
>> +
>> +     /* temporary input buffer */
>> +     u8 input_buf[HID_MAX_BUFFER_SIZE];
>> };
>
> The report does not need any specific alignment?

This is already aligned to native size, isn't it? Anyhow, HID core
does not have any alignment-restrictions I am aware of. Jiri? I
thought the extract()/implement() stuff was fixed recently.

Thanks
David

^ permalink raw reply

* [PATCH] tools/bluetooth-player: Formatting commands
From: Sebastian Chlad @ 2013-12-27 14:10 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Sebastian Chlad

Formatting commands passed to bluetooth player.
Whitespace character trimmed.
---
 tools/bluetooth-player.c |    9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/tools/bluetooth-player.c b/tools/bluetooth-player.c
index 622d391..1dadc70 100644
--- a/tools/bluetooth-player.c
+++ b/tools/bluetooth-player.c
@@ -1065,9 +1065,10 @@ static char **cmd_completion(const char *text, int start, int end)
 
 static void rl_handler(char *input)
 {
+
 	int argc;
 	char **argv = NULL;
-	int i;
+	int i, len;
 
 	if (!input) {
 		rl_insert_text("quit");
@@ -1080,6 +1081,12 @@ static void rl_handler(char *input)
 	if (!strlen(input))
 		goto done;
 
+	if (input) {
+		len = strlen(input);
+		if (len > 0 && input[len - 1] == ' ')
+			input[len - 1] = '\0';
+	}
+
 	add_history(input);
 
 	argv = g_strsplit(input, " ", -1);
-- 
1.7.9.5


^ permalink raw reply related

* Re: [PATCH] tools/bluetooth-player: Formatting commands
From: Johan Hedberg @ 2013-12-27 15:22 UTC (permalink / raw)
  To: Sebastian Chlad; +Cc: linux-bluetooth, Sebastian Chlad
In-Reply-To: <1388153434-8459-1-git-send-email-sebastianx.chlad@intel.com>

Hi Sebastian,

On Fri, Dec 27, 2013, Sebastian Chlad wrote:
> Formatting commands passed to bluetooth player.
> Whitespace character trimmed.
> ---
>  tools/bluetooth-player.c |    9 ++++++++-
>  1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/bluetooth-player.c b/tools/bluetooth-player.c
> index 622d391..1dadc70 100644
> --- a/tools/bluetooth-player.c
> +++ b/tools/bluetooth-player.c
> @@ -1065,9 +1065,10 @@ static char **cmd_completion(const char *text, int start, int end)
>  
>  static void rl_handler(char *input)
>  {
> +

This line addition is unnecessary.

>  	int argc;
>  	char **argv = NULL;
> -	int i;
> +	int i, len;
>  
>  	if (!input) {
>  		rl_insert_text("quit");
> @@ -1080,6 +1081,12 @@ static void rl_handler(char *input)
>  	if (!strlen(input))
>  		goto done;
>  
> +	if (input) {
> +		len = strlen(input);
> +		if (len > 0 && input[len - 1] == ' ')
> +			input[len - 1] = '\0';
> +	}

Since bluetooth-player uses GLib I suppose it'd make more sense to use
the provided g_strstrip convenience function (or g_strchomp if you
really only want to strip trailing whitespace).

Johan

^ permalink raw reply

* Re: [REGRESSION] rfcomm (userland) broken by commit 29cd718b
From: Benson Chow @ 2013-12-27 23:01 UTC (permalink / raw)
  To: Gianluca Anzolin; +Cc: linux-bluetooth
In-Reply-To: <20131216211542.GA20419@sottospazio.it>

First off, thanks for the fix to stop rfcomm from taking down the machine. 
However, I have noted that blueman and networkmanager/modemmanager no 
longer recognize the /dev/rfcomm0 device as a valid dialup device.  This 
seems to be a kernel-userspace interface regression as I can boot into 
3.6.11 and it would work just fine.

When I saw this thread, I agree there appears to be some 
kernel-userspace changes that broke something, but the recent patch still 
did not seem to let modemmanger detect the bluetooth device as it did pre 
linux-3.12.

Blueman reports "connection failed: modem manager did not support 
the connection" implying there's still some userspace differences from the 
old behavior.

I do notice I can run a terminal emulator on /dev/rfcomm0 and able to run 
modem AT-commands which means that I can communicate with the phone 
through bluetooth, so that part is working.  Plus, tearing up that 
connection no longer results in a crash like before linux-3.8.

Any other information I could get from my system to help debug what's 
going on here?  Or perhaps modem-manager will need to be updated to 
work with the new behavior?

Thanks,
-Benson

On Mon, 16 Dec 2013, Gianluca Anzolin wrote:

> Date: Mon, 16 Dec 2013 22:15:42 +0100
> From: Gianluca Anzolin <gianluca@sottospazio.it>
> To: Peter Hurley <peter@hurleysoftware.com>
> Cc: Alexander Holler <holler@ahsoftware.de>, marcel@holtmann.org,
>     Gustavo Padovan <gustavo@padovan.org>, linux-bluetooth@vger.kernel.org,
>     gregkh@linuxfoundation.org, jslaby@suse.cz, linux-kernel@vger.kernel.org
> Subject: Re: [REGRESSION] rfcomm (userland) broken by commit 29cd718b
> 
> On Mon, Dec 16, 2013 at 09:58:58PM +0100, Gianluca Anzolin wrote:
>> On Mon, Dec 16, 2013 at 09:27:20PM +0100, Gianluca Anzolin wrote:
>>> On Mon, Dec 16, 2013 at 09:20:44PM +0100, Gianluca Anzolin wrote:
>>>> On Mon, Dec 16, 2013 at 02:34:12PM -0500, Peter Hurley wrote:
>>>>>
>>>>> This solution is acceptable to me, but I think the comment should briefly
>>>>> explain why this fix is necessary, and the changelog should explain why in detail.
>>>>>
>>>>> Perhaps with a fixme comment that rfcomm_tty_install() should just take over
>>>>> the port reference (instead of adding one) and rfcomm_tty_cleanup() should
>>>>> conditionally release on RFCOMM_RELEASE_ONHUP.
>>>>>
>>>>> Because then:
>>>>> 1) this fix would not be necessary.
>>>>> 2) the release in rfcomm_tty_hangup() would not be necessary
>>>>> 3) the second release in rfcomm_release_dev would not be necessary
>>>>> 4) the RFCOMM_TTY_RELEASED bit could be removed
>>>>>
>>>>>
>>>>> Regards,
>>>>> Peter Hurley
>>>>
>>>> Taking over the refcount in the install method would certainly look better. I'm
>>>> going to test it ASAP :D
>>>>
>>>> But why getting rid of the release in in rfcomm_tty_hangup()?
>>>> We could lose the bluetooth connection at any time and the dlc callback
>>>> would have to hangup the tty (and release the port if necessary).
>>>>
>>>> Also the RFCOMM_TTY_RELEASED bit should still be necessary if the port is
>>>> created without the RFCOMM_RELEASE_ONHUP flag.
>>>>
>>>> Besides any process could release the port behind us (with the command rfcomm
>>>> release rfcomm1 for example).
>>>>
>>>> Gianluca
>>>
>>> Nevermind I figured it out the reason...
>>
>> I'm testing the attached patch ATM, which does what you described. It works
>> very well.
>>
>> It doesn't remove the RFCOMM_TTY_RELEASE flag yet, another patch should remove
>> that bit.
>
> ok, replying to myself again (sorry for that). RFCOMM_TTY_RELEASE cannot go
> away. We have still to manage the case where the port is opened without
> RFCOMM_RELEASE_ONHUP.
>
> This last patch does release the port in that situation.
>
> Tested with:
> # rfcomm bind 1 <addr>
> # rfcomm release 1
>
> and with
> # rfcomm connect 1 <addr>
>
> Thanks,
> Gianluca
>

^ permalink raw reply

* Re: [PATCH v2] Bluetooth: Add hci_h4p driver
From: Marcel Holtmann @ 2013-12-28  1:21 UTC (permalink / raw)
  To: Pali Rohár
  Cc: Ивайло Димитров,
	Gustavo F. Padovan, Johan Hedberg, Pavel Machek, linux-kernel,
	linux-bluetooth@vger.kernel.org development, Ville Tervo,
	Sebastian Reichel
In-Reply-To: <1727897.LBX8128hIo@izba>

Hi Pali,

> I'm sending updated version of hci_h4p bluetooth driver. It is needed for
> Nokia N900 bluetooth hardware. This (v2) is older version of hci_h4p driver,
> but I tested it with v3.13-rc3 kernel on Nokia N900 and working without any
> problems. Previous (v1) version had some problems. So for future development
> please use this (v2) version of hci_h4p driver.

please create a proper commit message explaining this driver. Revision updates should go between the diffstat and the patch itself. Use git format-patch and you see what I mean.

> 
> diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
> index 11a6104..95155c3 100644
> --- a/drivers/bluetooth/Kconfig
> +++ b/drivers/bluetooth/Kconfig
> @@ -242,4 +242,14 @@ config BT_WILINK
> 
> 	  Say Y here to compile support for Texas Instrument's WiLink7 driver
> 	  into the kernel or say M to compile it as module.
> +
> +config BT_HCIH4P
> +	tristate "HCI driver with H4 Nokia extensions"
> +	depends on BT && ARCH_OMAP

Since then we moved away from doing hci_* prefix of drivers since that is misleading. See btusb.ko, btmrvl_sdio.ko etc.

So this might be better named BT_NOK_H4P or BT_NOKIA_H4P and the module named btnok_h4p.ko or btnokia_h4p.ko.

I still never understood what “p” was for.

Can we also make this just depend on some device tree information and not on a specific architecture. I know that this driver is pretty much OMAP specific, but if we want this upstream, we should at least try to make it more generic.

> +	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 (hci_h4p).
> endmenu
> diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
> index 9fe8a87..77b01b6 100644
> --- a/drivers/bluetooth/Makefile
> +++ b/drivers/bluetooth/Makefile
> @@ -31,4 +31,6 @@ hci_uart-$(CONFIG_BT_HCIUART_ATH3K)	+= hci_ath.o
> hci_uart-$(CONFIG_BT_HCIUART_3WIRE)	+= hci_h5.o
> hci_uart-objs				:= $(hci_uart-y)
> 
> +obj-y				+= hci_h4p/
> +

So far we have not done driver sub-directories since the drivers are all small and tiny. Even the ones that have multiple files like the Marvell one.

Please check if just prefixing this with nok_ or nokia_ would work.

> ccflags-y += -D__CHECK_ENDIAN__
> diff --git a/drivers/bluetooth/hci_h4p/Makefile b/drivers/bluetooth/hci_h4p/Makefile
> new file mode 100644
> index 0000000..f20bd9a
> --- /dev/null
> +++ b/drivers/bluetooth/hci_h4p/Makefile
> @@ -0,0 +1,7 @@
> +#
> +# Makefile for the Linux Bluetooth HCI device drivers.
> +#
> +
> +obj-$(CONFIG_BT_HCIH4P)		+= hci_h4p.o
> +
> +hci_h4p-objs := core.o fw.o uart.o fw-csr.o fw-bcm.o fw-ti1273.o
> diff --git a/drivers/bluetooth/hci_h4p/core.c b/drivers/bluetooth/hci_h4p/core.c
> new file mode 100644
> index 0000000..e76e889
> --- /dev/null
> +++ b/drivers/bluetooth/hci_h4p/core.c
> @@ -0,0 +1,1357 @@
> +/*
> + * This file is part of hci_h4p bluetooth driver
> + *
> + * Copyright (C) 2005-2008 Nokia Corporation.
> + *
> + * Contact: Ville Tervo <ville.tervo@nokia.com>

I think you can just remove the contact names since I think nobody of the original authors is still working at Nokia and I bet this emails addresses just do not work anymore.

> + *
> + * 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/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 <net/bluetooth/bluetooth.h>
> +#include <net/bluetooth/hci_core.h>
> +#include <net/bluetooth/hci.h>
> +
> +#include <linux/bluetooth/hci_h4p.h>
> +
> +#include "hci_h4p.h”
> +

Please do not introduce public includes for a driver. This should be all confined to the driver itself or if it platform data, it should go into the place for platform data.

> +static struct task_struct *h4p_thread;

Can’t this be done using a work queue. You are looking at a 3.14 kernel the earliest. We have way better primitives these days.

> +
> +/* 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) {
> +		NBT_DBG_POWER("Enabling %p\n", 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) {
> +		NBT_DBG_POWER("Disabling %p\n", 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)
> +{
> +	if (unlikely(!h4p_thread))
> +		return;
> +
> +	set_bit(H4P_SCHED_TRANSFER_MODE, &info->pm_flags);
> +
> +	if (unlikely(!test_bit(H4P_TRANSFER_MODE, &info->pm_flags)))
> +		wake_up_process(h4p_thread);
> +}
> +
> +static void hci_h4p_disable_tx(struct hci_h4p_info *info)
> +{
> +	NBT_DBG_POWER("\n");
> +
> +	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;
> +	NBT_DBG_POWER("\n");
> +
> +	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;
> +
> +	NBT_DBG("Sending alive packet\n");
> +
> +	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);
> +
> +	NBT_DBG("Alive packet sent\n");
> +
> +	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;
> +
> +	NBT_DBG("Received alive packet\n");
> +	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;
> +
> +	NBT_DBG("Sending negotiation..\n");
> +
> +	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)))

Please follow the net subsystem coding style.

> +		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;
> +
> +	NBT_DBG("Negotiation succesful\n");
> +	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))) {
> +		if (bt_cb(skb)->pkt_type == H4_NEG_PKT) {
> +			hci_h4p_negotiation_packet(info, skb);
> +			info->rx_state = WAIT_FOR_PKT_TYPE;
> +			return;
> +		}

Use "else if” here or a switch statement.

> +		if (bt_cb(skb)->pkt_type == 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)) {
> +			NBT_DBG("fw_event\n");
> +			hci_h4p_parse_fw_event(info, skb);
> +			return;
> +		}
> +	}
> +
> +	hci_recv_frame(info->hdev, skb);
> +	NBT_DBG("Frame sent to upper layer\n");
> +}
> +
> +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) {
> +			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, "Too long frame.\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 allways 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;
> +
> +	NBT_DBG("tasklet woke up\n");
> +	NBT_DBG_TRANSFER("rx_tasklet woke up\ndata ");
> +
> +	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->hdev->stat.byte_rx++;
> +		NBT_DBG_TRANSFER_NF("0x%.2x  ", byte);
> +		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:
> +	NBT_DBG_TRANSFER_NF("\n");
> +	NBT_DBG("rx_ended\n");
> +}
> +
> +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;
> +
> +	NBT_DBG("tasklet woke up\n");
> +	NBT_DBG_TRANSFER("tx_tasklet woke up\n data ");
> +
> +	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 */
> +		NBT_DBG("skb ready\n");
> +		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;
> +		} else
> +			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)) {
> +		NBT_DBG_TRANSFER_NF("0x%.2x ", skb->data[sent]);
> +		hci_h4p_outb(info, UART_TX, skb->data[sent]);
> +		sent++;
> +	}
> +
> +	info->hdev->stat.byte_tx += sent;
> +	NBT_DBG_TRANSFER_NF("\n");
> +	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;
> +
> +	NBT_DBG("In interrupt handler iir 0x%.2x\n", 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;
> +	}
> +
> +	NBT_DBG_POWER("gpio interrupt %d\n", 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;
> +
> +	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);
> +		set_bit(H4P_ACTIVE_MODE, &info->pm_flags);
> +		BT_DBG("Change pm constraints to: %s", set ?
> +				"set" : "clear");
> +		return;
> +	}
> +
> +	if (!set && test_bit(H4P_ACTIVE_MODE, &info->pm_flags)) {
> +		bt_plat_data->set_pm_limits(info->dev, set);
> +		clear_bit(H4P_ACTIVE_MODE, &info->pm_flags);
> +		BT_DBG("Change pm constraints to: %s",
> +				set ? "set" : "clear");
> +		return;
> +	}
> +
> +	BT_DBG("pm constraints remains: %s",
> +			set ? "set" : "clear");
> +}
> +
> +static int h4p_run(void *data)
> +{
> +#define TIMEOUT_MIN msecs_to_jiffies(100)
> +#define TIMEOUT_MAX msecs_to_jiffies(2000)
> +	struct hci_h4p_info *info = data;
> +	unsigned long last_jiffies = jiffies;
> +	unsigned long timeout = TIMEOUT_MIN;
> +	unsigned long elapsed;
> +	BT_DBG("");
> +	set_user_nice(current, -10);
> +
> +	while (!kthread_should_stop()) {
> +		set_current_state(TASK_INTERRUPTIBLE);
> +		if (!test_bit(H4P_SCHED_TRANSFER_MODE, &info->pm_flags)) {
> +			if (timeout != TIMEOUT_MIN) {
> +				BT_DBG("Exit from active mode. Rest. constr.");
> +				hci_h4p_set_pm_limits(info, false);
> +			}
> +
> +			BT_DBG("No pending events. Sleeping.");
> +			schedule();
> +		}
> +
> +		set_bit(H4P_TRANSFER_MODE, &info->pm_flags);
> +		clear_bit(H4P_SCHED_TRANSFER_MODE, &info->pm_flags);
> +
> +		elapsed = jiffies - last_jiffies;
> +
> +		BT_DBG("Wake up. %u msec expired since last BT activity.",
> +				jiffies_to_msecs(elapsed));
> +		BT_DBG("Timeout before calculation = %u",
> +				jiffies_to_msecs(timeout));
> +
> +		/* Empiric analyzer  :-) */
> +		if (elapsed < TIMEOUT_MIN) {
> +			timeout <<= 1;
> +			timeout = (timeout > TIMEOUT_MAX) ?
> +				TIMEOUT_MAX : timeout;
> +		} else {
> +			timeout = (elapsed > timeout - TIMEOUT_MIN) ?
> +				TIMEOUT_MIN : timeout - elapsed;
> +		}
> +
> +		BT_DBG("Timeout after calculation = %u",
> +				jiffies_to_msecs(timeout));
> +
> +		/* Sometimes we get couple of HCI command during (e)SCO
> +		   connection. Turn ON transfer mode _ONLY_ if there is
> +		   still BT activity after 100ms sleep */
> +		if (timeout == TIMEOUT_MIN)
> +			BT_DBG("Do not enable transfer mode yet");
> +		else {
> +			hci_h4p_set_pm_limits(info, true);
> +			BT_DBG("Set active mode for %u msec.",
> +					jiffies_to_msecs(timeout));
> +		}
> +
> +		set_current_state(TASK_INTERRUPTIBLE);
> +		schedule_timeout(timeout);
> +
> +		last_jiffies = jiffies;
> +		clear_bit(H4P_TRANSFER_MODE, &info->pm_flags);
> +	}
> +
> +	hci_h4p_set_pm_limits(info, false);
> +
> +	return 0;
> +}
> +
> +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;
> +	}
> +
> +	reinit_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;
> +	info = hci_get_drvdata(hdev);

This can be directly assigned at variable declaration.

> +
> +	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);
> +
> +	NBT_DBG("hci up and running\n");
> +	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;
> +
> +	/* Wake up h4p_thread which removes pm constraints */
> +	wake_up_process(h4p_thread);
> +
> +	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;
> +
> +	if (!hdev) {
> +		printk(KERN_WARNING "hci_h4p: Frame for unknown device\n");
> +		return -ENODEV;
> +	}
> +
> +	NBT_DBG("dev %p, skb %p\n", 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 */

What is a h4+ device?

> +	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++)
> +		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, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
> +		       info->bd_addr[0], info->bd_addr[1], info->bd_addr[2],
> +		       info->bd_addr[3], info->bd_addr[4], info->bd_addr[5]);

We have printf modifier to print BD_ADDRs.

> +}
> +
> +static DEVICE_ATTR(bdaddr, S_IRUGO | S_IWUSR, hci_h4p_show_bdaddr,
> +		   hci_h4p_store_bdaddr);

I do not like this whole configure address via sysfs file business. This is something that has been discussed on linux-wireless for WiFi as well. This needs a way better solution.

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

Why is this quirk needed? This should only be needed for pre-1.1 and some 1.1 devices. Not a 2.0 chip.

> +
> +	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");
> +		return -ENODEV;
> +	}
> +
> +	if (hci_register_dev(hdev) < 0) {
> +		dev_err(info->dev, "hci_register failed %s.\n", hdev->name);
> +		hci_h4p_sysfs_remove_files(info->dev);
> +		return -ENODEV;
> +	}
> +

Who is freeing hdev here in case of an error?

> +	return 0;
> +}
> +
> +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 = kzalloc(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");
> +		kfree(info);
> +		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;
> +
> +	NBT_DBG("RESET gpio: %d\n", info->reset_gpio);
> +	NBT_DBG("BTWU gpio: %d\n", info->bt_wakeup_gpio);
> +	NBT_DBG("HOSTWU gpio: %d\n", info->host_wakeup_gpio);
> +	NBT_DBG("sysclk: %d\n", info->bt_sysclk);
> +
> +	init_completion(&info->test_completion);
> +	complete_all(&info->test_completion);
> +
> +	if (!info->reset_gpio_shared) {
> +		err = gpio_request(info->reset_gpio, "bt_reset");
> +		if (err < 0) {
> +			dev_err(&pdev->dev, "Cannot get GPIO line %d\n",
> +				info->reset_gpio);
> +			goto cleanup_setup;
> +		}
> +	}
> +
> +	err = gpio_request(info->bt_wakeup_gpio, "bt_wakeup");
> +	if (err < 0) {
> +		dev_err(info->dev, "Cannot get GPIO line 0x%d",
> +			info->bt_wakeup_gpio);
> +		if (!info->reset_gpio_shared)
> +			gpio_free(info->reset_gpio);
> +		goto cleanup_setup;
> +	}
> +
> +	err = gpio_request(info->host_wakeup_gpio, "host_wakeup");
> +	if (err < 0) {
> +		dev_err(info->dev, "Cannot get GPIO line %d",
> +		       info->host_wakeup_gpio);
> +		if (!info->reset_gpio_shared)
> +			gpio_free(info->reset_gpio);
> +		gpio_free(info->bt_wakeup_gpio);
> +		goto cleanup_setup;
> +	}
> +
> +	gpio_direction_output(info->reset_gpio, 0);
> +	gpio_direction_output(info->bt_wakeup_gpio, 0);
> +	gpio_direction_input(info->host_wakeup_gpio);
> +
> +	info->irq = bt_plat_data->uart_irq;
> +	info->uart_base = ioremap(bt_plat_data->uart_base, SZ_2K);
> +	info->uart_iclk = clk_get(NULL, bt_plat_data->uart_iclk);
> +	info->uart_fclk = clk_get(NULL, bt_plat_data->uart_fclk);
> +
> +	err = request_irq(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);
> +		goto cleanup;
> +	}
> +
> +	err = request_irq(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));
> +		free_irq(info->irq, info);
> +		goto cleanup;
> +	}
> +
> +	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));
> +		free_irq(info->irq, info);
> +		free_irq(gpio_to_irq(info->host_wakeup_gpio), info);
> +		goto cleanup;
> +	}
> +
> +	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)
> +		goto cleanup_irq;
> +	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");
> +		goto cleanup_irq;
> +	}
> +
> +	h4p_thread = kthread_run(h4p_run, info, "h4p_pm");
> +	if (IS_ERR(h4p_thread)) {
> +		err = PTR_ERR(h4p_thread);
> +		goto cleanup_irq;
> +	}

I still do not get how this is going to work with a global thread structure. Even if you normally only have one device, do not do that. Use work queues and make them per device.

> +
> +	return 0;
> +
> +cleanup_irq:
> +	free_irq(info->irq, (void *)info);
> +	free_irq(gpio_to_irq(info->host_wakeup_gpio), info);
> +cleanup:
> +	gpio_set_value(info->reset_gpio, 0);
> +	if (!info->reset_gpio_shared)
> +		gpio_free(info->reset_gpio);
> +	gpio_free(info->bt_wakeup_gpio);
> +	gpio_free(info->host_wakeup_gpio);
> +
> +cleanup_setup:
> +
> +	kfree(info);
> +	return err;
> +

Avoid these extra empty lines at function end.

> +}
> +
> +static int hci_h4p_remove(struct platform_device *pdev)
> +{
> +	struct hci_h4p_info *info;
> +
> +	info = platform_get_drvdata(pdev);
> +
> +	kthread_stop(h4p_thread);
> +
> +	hci_h4p_sysfs_remove_files(info->dev);
> +	hci_h4p_hci_close(info->hdev);
> +	free_irq(gpio_to_irq(info->host_wakeup_gpio), info);
> +	hci_unregister_dev(info->hdev);
> +	hci_free_dev(info->hdev);
> +	if (!info->reset_gpio_shared)
> +		gpio_free(info->reset_gpio);
> +	gpio_free(info->bt_wakeup_gpio);
> +	gpio_free(info->host_wakeup_gpio);
> +	free_irq(info->irq, (void *) info);
> +	kfree(info);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver hci_h4p_driver = {
> +	.probe		= hci_h4p_probe,
> +	.remove		= hci_h4p_remove,
> +	.driver		= {
> +		.name	= "hci_h4p",
> +	},
> +};
> +
> +static int __init hci_h4p_init(void)
> +{
> +	int err = 0;
> +
> +	/* Register the driver with LDM */
> +	err = platform_driver_register(&hci_h4p_driver);
> +	if (err < 0)
> +		printk(KERN_WARNING "failed to register hci_h4p driver\n");
> +
> +	return err;
> +}
> +
> +static void __exit hci_h4p_exit(void)
> +{
> +	platform_driver_unregister(&hci_h4p_driver);
> +}
> +
> +module_init(hci_h4p_init);
> +module_exit(hci_h4p_exit);
> +
> +MODULE_ALIAS("platform:hci_h4p”);

You need to find a proper platform alias here.

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

Do we actually have all these firmware files still available. If not, then focus on the ones we have.

> diff --git a/drivers/bluetooth/hci_h4p/fw-bcm.c b/drivers/bluetooth/hci_h4p/fw-bcm.c
> new file mode 100644
> index 0000000..390d021
> --- /dev/null
> +++ b/drivers/bluetooth/hci_h4p/fw-bcm.c
> @@ -0,0 +1,149 @@
> +/*
> + * This file is part of hci_h4p bluetooth driver
> + *
> + * Copyright (C) 2005-2008 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/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);
> +	}


This behavior is extremely dangerous. I would rather have the device init or powering on the device fail instead of making up a number that might clash with a real Nokia device.

> +
> +	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) {
> +		NBT_DBG_FW("Setting bluetooth address\n");
> +		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;
> +
> +	NBT_DBG_FW("Sending firmware\n");
> +
> +	time = jiffies;
> +
> +	info->fw_q = fw_queue;
> +	skb = skb_dequeue(fw_queue);
> +	if (!skb)
> +		return -ENODATA;
> +
> +	NBT_DBG_FW("Sending commands\n");
> +
> +	/*
> +	 * 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;
> +	}
> +
> +	NBT_DBG_FW("Firmware sent in %d msecs\n",
> +		   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/bluetooth/hci_h4p/fw-csr.c b/drivers/bluetooth/hci_h4p/fw-csr.c
> new file mode 100644
> index 0000000..020fa52
> --- /dev/null
> +++ b/drivers/bluetooth/hci_h4p/fw-csr.c
> @@ -0,0 +1,152 @@
> +/*
> + * This file is part of hci_h4p bluetooth driver
> + *
> + * Copyright (C) 2005-2008 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/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;
> +
> +	NBT_DBG_FW("Sending firmware\n");
> +	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++) {
> +		NBT_DBG_FW("Sending firmware command %d\n", 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/bluetooth/hci_h4p/fw-ti1273.c b/drivers/bluetooth/hci_h4p/fw-ti1273.c
> new file mode 100644
> index 0000000..32e5fa0
> --- /dev/null
> +++ b/drivers/bluetooth/hci_h4p/fw-ti1273.c
> @@ -0,0 +1,112 @@
> +/*
> + * This file is part of hci_h4p bluetooth driver
> + *
> + * Copyright (C) 2009 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/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;
> +
> +	NBT_DBG_FW("Sending firmware\n");
> +
> +	time = jiffies;
> +
> +	fw_q = fw_queue;
> +	skb = skb_dequeue(fw_queue);
> +	if (!skb)
> +		return -ENODATA;
> +
> +	NBT_DBG_FW("Sending commands\n");
> +	/* 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;
> +	}
> +
> +	NBT_DBG_FW("Firmware sent in %d msecs\n",
> +		   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/bluetooth/hci_h4p/fw.c b/drivers/bluetooth/hci_h4p/fw.c
> new file mode 100644
> index 0000000..b3d39f9
> --- /dev/null
> +++ b/drivers/bluetooth/hci_h4p/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;
> +	NBT_DBG_FW("Opening firmware man_id 0x%.2x ver_id 0x%.2x\n",
> +			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/bluetooth/hci_h4p/hci_h4p.h b/drivers/bluetooth/hci_h4p/hci_h4p.h
> new file mode 100644
> index 0000000..d1d313b
> --- /dev/null
> +++ b/drivers/bluetooth/hci_h4p/hci_h4p.h
> @@ -0,0 +1,248 @@
> +/*
> + * This file is part of hci_h4p bluetooth driver
> + *
> + * Copyright (C) 2005-2008 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 <net/bluetooth/bluetooth.h>
> +#include <net/bluetooth/hci_core.h>
> +#include <net/bluetooth/hci.h>
> +
> +#ifndef __DRIVERS_BLUETOOTH_HCI_H4P_H
> +#define __DRIVERS_BLUETOOTH_HCI_H4P_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 NBT_DBG(fmt, arg...) \
> +		pr_debug("%s: " fmt "" , __func__ , ## arg)
> +
> +#define NBT_DBG_FW(fmt, arg...) \
> +		pr_debug("%s: " fmt "" , __func__ , ## arg)
> +
> +#define NBT_DBG_POWER(fmt, arg...) \
> +		pr_debug("%s: " fmt "" , __func__ , ## arg)
> +
> +#define NBT_DBG_TRANSFER(fmt, arg...) \
> +		pr_debug("%s: " fmt "" , __func__ , ## arg)
> +
> +#define NBT_DBG_TRANSFER_NF(fmt, arg...) \
> +		pr_debug(fmt "" , ## arg)
> +
> +#define NBT_DBG_DMA(fmt, arg...) \
> +		pr_debug("%s: " fmt "" , __func__ , ## arg)


I rather not introduce another ton of new debug helpers. Either use the BT_ ones or just spell this out if you need something that is not common.

> +
> +#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/bluetooth/hci_h4p/uart.c b/drivers/bluetooth/hci_h4p/uart.c
> new file mode 100644
> index 0000000..7973c6c
> --- /dev/null
> +++ b/drivers/bluetooth/hci_h4p/uart.c
> @@ -0,0 +1,202 @@
> +/*
> + * 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/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;
> +
> +	NBT_DBG("Setting speed %lu\n", 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;
> +}
> +
> +

No double empty lines please.

> +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/bluetooth/hci_h4p.h b/include/linux/bluetooth/hci_h4p.h
> new file mode 100644
> index 0000000..daf83fc
> --- /dev/null
> +++ b/include/linux/bluetooth/hci_h4p.h
> @@ -0,0 +1,40 @@
> +/*
> + * This file is part of hci_h4p bluetooth driver
> + *
> + * Copyright (C) 2010 Nokia Corporation.
> + *
> + * Contact: Roger Quadros <roger.quadros@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
> + *
> + */
> +
> +
> +/**
> + * 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);
> +};

I also wonder how much could actually be done nicely via the added hdev->setup() callback that is called once before brining up the device.

Regards

Marcel


^ permalink raw reply

* Re: [REGRESSION] rfcomm (userland) broken by commit 29cd718b
From: Gianluca Anzolin @ 2013-12-28  8:44 UTC (permalink / raw)
  To: Benson Chow; +Cc: linux-bluetooth
In-Reply-To: <alpine.LNX.2.00.1312271536480.28992@doujima.vanade.com>

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

On Fri, Dec 27, 2013 at 04:01:27PM -0700, Benson Chow wrote:
> First off, thanks for the fix to stop rfcomm from taking down the
> machine. However, I have noted that blueman and
> networkmanager/modemmanager no longer recognize the /dev/rfcomm0
> device as a valid dialup device.  This seems to be a
> kernel-userspace interface regression as I can boot into 3.6.11 and
> it would work just fine.
> 
> When I saw this thread, I agree there appears to be some
> kernel-userspace changes that broke something, but the recent patch
> still did not seem to let modemmanger detect the bluetooth device as
> it did pre linux-3.12.
> 
> Blueman reports "connection failed: modem manager did not support
> the connection" implying there's still some userspace differences
> from the old behavior.
> 
> I do notice I can run a terminal emulator on /dev/rfcomm0 and able
> to run modem AT-commands which means that I can communicate with the
> phone through bluetooth, so that part is working.  Plus, tearing up
> that connection no longer results in a crash like before linux-3.8.
> 
> Any other information I could get from my system to help debug
> what's going on here?  Or perhaps modem-manager will need to be
> updated to work with the new behavior?
> 
> Thanks,
> -Benson

Thank you for the report.

Could you try the attached patch on top of the last rfc3.patch and see if it
works?

Regards,
Gianluca

[-- Attachment #2: modman.patch --]
[-- Type: text/x-diff, Size: 1558 bytes --]

diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index 0357dcf..90c1872 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -124,9 +124,6 @@ static void rfcomm_dev_shutdown(struct tty_port *port)
 {
 	struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
 
-	if (dev->tty_dev->parent)
-		device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST);
-
 	/* close the dlc */
 	rfcomm_dlc_close(dev->dlc, 0);
 }
@@ -577,9 +574,6 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
 
 	dev->err = err;
 	if (dlc->state == BT_CONNECTED) {
-		device_move(dev->tty_dev, rfcomm_get_device(dev),
-			    DPM_ORDER_DEV_AFTER_PARENT);
-
 		wake_up_interruptible(&dev->port.open_wait);
 	} else if (dlc->state == BT_CLOSED)
 		tty_port_tty_hangup(&dev->port, false);
@@ -632,6 +626,9 @@ static void rfcomm_tty_cleanup(struct tty_struct *tty)
 {
 	struct rfcomm_dev *dev = tty->driver_data;
 
+	if (dev->tty_dev->parent)
+		device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST);
+
 	clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
 
 	rfcomm_dlc_lock(dev->dlc);
@@ -674,6 +671,9 @@ static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
 	if (err)
 		rfcomm_tty_cleanup(tty);
 
+	device_move(dev->tty_dev, rfcomm_get_device(dev),
+		    DPM_ORDER_DEV_AFTER_PARENT);
+
 	/* take over the tty_port reference if it was created with the
 	 * flag RFCOMM_RELEASE_ONHUP. This will force the release of the port
 	 * when the last process closes the tty. This behaviour is expected by

^ permalink raw reply related

* [PATCH] btmon: Fix build for Android
From: Andrei Emeltchenko @ 2013-12-28  9:22 UTC (permalink / raw)
  To: linux-bluetooth

From: Andrei Emeltchenko <andrei.emeltchenko@intel.com>

Android build was really broken with additions of functions hwdb_*

...
external/bluetooth/bluez/android/../monitor/packet.c:406: error:
undefined reference to 'hwdb_get_company'
external/bluetooth/bluez/android/../monitor/packet.c:2487: error:
undefined reference to 'hwdb_get_vendor_model'
collect2: error: ld returned 1 exit status
...
---
 android/Android.mk | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/android/Android.mk b/android/Android.mk
index 2cc0064..5981379 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -185,6 +185,8 @@ LOCAL_SRC_FILES := \
 	../monitor/crc.c \
 	../monitor/ll.h \
 	../monitor/ll.c \
+	../monitor/hwdb.h \
+	../monitor/hwdb.c \
 	../lib/hci.c \
 	../lib/bluetooth.c \
 
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH] Bluetooth: Add support for vectored writes to virtual HCI driver
From: Marcel Holtmann @ 2013-12-29  5:57 UTC (permalink / raw)
  To: linux-bluetooth

The Bluetooth virtual HCI driver is using a misc character device to
allow emulation of HCI devices from userspace. This change enables the
support for vectored writes. Previously this was failing with EINVAL
since no complete H:4 packet was written.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
 drivers/bluetooth/hci_vhci.c | 29 ++++++++++++++++++-----------
 1 file changed, 18 insertions(+), 11 deletions(-)

diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index 7b167385a1c4..1ef6990a5c7e 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -141,22 +141,28 @@ static int vhci_create_device(struct vhci_data *data, __u8 dev_type)
 }
 
 static inline ssize_t vhci_get_user(struct vhci_data *data,
-				    const char __user *buf, size_t count)
+				    const struct iovec *iov,
+				    unsigned long count)
 {
+	size_t len = iov_length(iov, count);
 	struct sk_buff *skb;
 	__u8 pkt_type, dev_type;
+	unsigned long i;
 	int ret;
 
-	if (count < 2 || count > HCI_MAX_FRAME_SIZE)
+	if (len < 2 || len > HCI_MAX_FRAME_SIZE)
 		return -EINVAL;
 
-	skb = bt_skb_alloc(count, GFP_KERNEL);
+	skb = bt_skb_alloc(len, GFP_KERNEL);
 	if (!skb)
 		return -ENOMEM;
 
-	if (copy_from_user(skb_put(skb, count), buf, count)) {
-		kfree_skb(skb);
-		return -EFAULT;
+	for (i = 0; i < count; i++) {
+		if (copy_from_user(skb_put(skb, iov[i].iov_len),
+				   iov[i].iov_base, iov[i].iov_len)) {
+			kfree_skb(skb);
+			return -EFAULT;
+		}
 	}
 
 	pkt_type = *((__u8 *) skb->data);
@@ -205,7 +211,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *data,
 		return -EINVAL;
 	}
 
-	return (ret < 0) ? ret : count;
+	return (ret < 0) ? ret : len;
 }
 
 static inline ssize_t vhci_put_user(struct vhci_data *data,
@@ -272,12 +278,13 @@ static ssize_t vhci_read(struct file *file,
 	return ret;
 }
 
-static ssize_t vhci_write(struct file *file,
-			  const char __user *buf, size_t count, loff_t *pos)
+static ssize_t vhci_write(struct kiocb *iocb, const struct iovec *iov,
+			  unsigned long count, loff_t pos)
 {
+	struct file *file = iocb->ki_filp;
 	struct vhci_data *data = file->private_data;
 
-	return vhci_get_user(data, buf, count);
+	return vhci_get_user(data, iov, count);
 }
 
 static unsigned int vhci_poll(struct file *file, poll_table *wait)
@@ -342,7 +349,7 @@ static int vhci_release(struct inode *inode, struct file *file)
 static const struct file_operations vhci_fops = {
 	.owner		= THIS_MODULE,
 	.read		= vhci_read,
-	.write		= vhci_write,
+	.aio_write	= vhci_write,
 	.poll		= vhci_poll,
 	.open		= vhci_open,
 	.release	= vhci_release,
-- 
1.8.4.2


^ permalink raw reply related

* [PATCH] Bluetooth: Use MD SET register for changing SDIO Type-B to Type-A
From: Marcel Holtmann @ 2013-12-29  6:10 UTC (permalink / raw)
  To: linux-bluetooth

The register for setting the SDIO card mode of a Type-B Bluetooth card
is called MD SET. The MD STAT register is used for reading the current
mode back.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
 drivers/bluetooth/btsdio.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c
index 1f6815825e61..83f6437dd91d 100644
--- a/drivers/bluetooth/btsdio.c
+++ b/drivers/bluetooth/btsdio.c
@@ -73,6 +73,7 @@ struct btsdio_data {
 #define REG_CL_INTRD 0x13	/* Interrupt Clear */
 #define REG_EN_INTRD 0x14	/* Interrupt Enable */
 #define REG_MD_STAT  0x20	/* Bluetooth Mode Status */
+#define REG_MD_SET   0x20	/* Bluetooth Mode Set */
 
 static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb)
 {
@@ -212,7 +213,7 @@ static int btsdio_open(struct hci_dev *hdev)
 	}
 
 	if (data->func->class == SDIO_CLASS_BT_B)
-		sdio_writeb(data->func, 0x00, REG_MD_STAT, NULL);
+		sdio_writeb(data->func, 0x00, REG_MD_SET, NULL);
 
 	sdio_writeb(data->func, 0x01, REG_EN_INTRD, NULL);
 
-- 
1.8.4.2


^ permalink raw reply related

* Re: [PATCH] btmon: Fix build for Android
From: Johan Hedberg @ 2013-12-29 19:23 UTC (permalink / raw)
  To: Andrei Emeltchenko; +Cc: linux-bluetooth
In-Reply-To: <1388222555-22995-1-git-send-email-Andrei.Emeltchenko.news@gmail.com>

Hi Andrei,

On Sat, Dec 28, 2013, Andrei Emeltchenko wrote:
> Android build was really broken with additions of functions hwdb_*
> 
> ...
> external/bluetooth/bluez/android/../monitor/packet.c:406: error:
> undefined reference to 'hwdb_get_company'
> external/bluetooth/bluez/android/../monitor/packet.c:2487: error:
> undefined reference to 'hwdb_get_vendor_model'
> collect2: error: ld returned 1 exit status
> ...
> ---
>  android/Android.mk | 2 ++
>  1 file changed, 2 insertions(+)

Applied. Thanks.

It's unfortunate this didn't make it to the release since the tree has
been broken this way since Monday.

Johan

^ permalink raw reply

* Re: [PATCH] Bluetooth: Add support for vectored writes to virtual HCI driver
From: Johan Hedberg @ 2013-12-29 19:36 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-bluetooth
In-Reply-To: <1388296634-46026-1-git-send-email-marcel@holtmann.org>

Hi Marcel,

On Sat, Dec 28, 2013, Marcel Holtmann wrote:
> The Bluetooth virtual HCI driver is using a misc character device to
> allow emulation of HCI devices from userspace. This change enables the
> support for vectored writes. Previously this was failing with EINVAL
> since no complete H:4 packet was written.
> 
> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
> ---
>  drivers/bluetooth/hci_vhci.c | 29 ++++++++++++++++++-----------
>  1 file changed, 18 insertions(+), 11 deletions(-)

Applied to bluetooth-next. Thanks.

Johan

^ permalink raw reply

* Re: [PATCH] Bluetooth: Use MD SET register for changing SDIO Type-B to Type-A
From: Johan Hedberg @ 2013-12-29 19:37 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-bluetooth
In-Reply-To: <1388297402-48660-1-git-send-email-marcel@holtmann.org>

Hi Marcel,

On Sat, Dec 28, 2013, Marcel Holtmann wrote:
> The register for setting the SDIO card mode of a Type-B Bluetooth card
> is called MD SET. The MD STAT register is used for reading the current
> mode back.
> 
> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
> ---
>  drivers/bluetooth/btsdio.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)

Applied to bluetooth-next. Thanks.

Johan

^ permalink raw reply

* [RFC 0/6] Audio HAL plugin concept
From: Lukasz Rymanowski @ 2013-12-30 10:17 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, johan.hedberg, Lukasz Rymanowski

Here is the set od some startup patches for audio hal plugin plus some trivial
fixes which are not RFC actually.

As we want audio plugin to register endpoint we need some special way to
handle it having in mind that audio device is open on the system startup
and closed on system down. We need to handle bluetooth daemon start/stop 
in between.

Need your comment on that. For me it does not look bad but we still have
time to consider having some fixed or configurable way (eg. some config file)
to register endpoints in bluetooth daemon instead.

Lukasz Rymanowski (6):
  android/ipc: Remove not needed include
  android/audio: Fix Makefile.am for libaudio
  android: Minor fix to Android Bluetooth Audio protocol API doc
  android: Keep stream_out example in the API doc
  android/audio: Add wrapper stuct for audio structures
  android/audio: Add listener thread on the Audio HAL socket

 android/Makefile.am       |   5 +-
 android/audio-ipc-api.txt |  18 ++--
 android/hal-audio.c       | 242 ++++++++++++++++++++++++++++++++++++++--------
 android/hal-audio.h       |  18 ++++
 android/ipc.c             |   1 -
 5 files changed, 231 insertions(+), 53 deletions(-)
 create mode 100644 android/hal-audio.h

-- 
1.8.4


^ permalink raw reply

* [RFC 1/6] android/ipc: Remove not needed include
From: Lukasz Rymanowski @ 2013-12-30 10:17 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, johan.hedberg, Lukasz Rymanowski
In-Reply-To: <1388398647-25420-1-git-send-email-lukasz.rymanowski@tieto.com>

---
 android/ipc.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/android/ipc.c b/android/ipc.c
index 6f940cd..9e8ccc3 100644
--- a/android/ipc.c
+++ b/android/ipc.c
@@ -32,7 +32,6 @@
 #include <signal.h>
 #include <stdbool.h>
 #include <sys/socket.h>
-#include <sys/socket.h>
 #include <sys/un.h>
 #include <unistd.h>
 #include <glib.h>
-- 
1.8.4


^ 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