Linux bluetooth development
 help / color / mirror / Atom feed
* Re: [PATCH v2] Bluetooth: Add hci_h4p driver
From: Pali Rohár @ 2013-12-30 14:04 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Pavel Machek, Marcel Holtmann,
	Ивайло Димитров,
	Gustavo F. Padovan, Johan Hedberg, linux-kernel,
	linux-bluetooth@vger.kernel.org development, Ville Tervo
In-Reply-To: <20131230131942.GA13816@earth.universe>

[-- Attachment #1: Type: Text/Plain, Size: 1029 bytes --]

On Monday 30 December 2013 14:19:44 Sebastian Reichel wrote:
> Hi,
> 
> On Mon, Dec 30, 2013 at 01:13:50PM +0100, Pavel Machek wrote:
> > [...]
> > 
> > Well, I can rename config option, but renaming the module
> > would break existing userland, no?
> 
> Why is the userland depending on the module name?
> 

grep told me that hci_h4p is used only in these (text) files:

* /etc/modprobe.d/maemo.conf
   alias platform:hci_h4p hci_h4p

So this is not needed if kernel driver contains correct 
MODULE_ALIAS macro.

* /etc/event.d/bluetooth-sysinfo
   echo ... > /sys/devices/platform/hci_h4p/bdaddr

This upstart script contains code for reading mac address from 
special sysinfod daemon and setting it to kernel driver. Once 
there will be uniform way for setting mac address that script can 
be changed.

So I think that renaming module will not break anything if kernel 
module will get (somehow) correct mac address (or if random one 
is acceptable).

-- 
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 v2] Bluetooth: Add hci_h4p driver
From: Pali Rohár @ 2013-12-30 13:54 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Marcel Holtmann,
	Ивайло Димитров,
	Gustavo F. Padovan, Johan Hedberg, linux-kernel,
	linux-bluetooth@vger.kernel.org development, Ville Tervo,
	Sebastian Reichel
In-Reply-To: <20131230121350.GB31236@amd.pavel.ucw.cz>

[-- Attachment #1: Type: Text/Plain, Size: 1015 bytes --]

On Monday 30 December 2013 13:13:50 Pavel Machek wrote:
> > > +
> > > +	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.
> 
> Perhaps people can donate bt addresses from their
> no-longer-functional bluetooth devices and we can select from
> such pool here? ;-).
> 
> Is there some experimental range we can allocate from?
> 

Wifi driver wl1251 (it is already in mainline) is doing same: 
https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/net/wireless/ti/wl1251/main.c#n1431

-- 
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: [RFC BlueZ 3/9] android: Add audio open command/response struct
From: Luiz Augusto von Dentz @ 2013-12-30 13:36 UTC (permalink / raw)
  To: Szymon Janc; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <1532451.hxPrzbXmr3@athlon>

Hi Szymon,

On Mon, Dec 30, 2013 at 3:27 PM, Szymon Janc <szymon.janc@gmail.com> wrote:
> Hi Luiz,
>
> On Monday 30 December 2013 14:34:09 Luiz Augusto von Dentz wrote:
>> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>>
>> This adds the definitions to audio open command and response.
>> ---
>>  android/a2dp.c            |  9 +++++++++
>>  android/audio-ipc-api.txt |  2 +-
>>  android/hal-msg.h         | 18 ++++++++++++++++++
>>  android/ipc.c             |  5 ++++-
>>  4 files changed, 32 insertions(+), 2 deletions(-)
>>
>> diff --git a/android/a2dp.c b/android/a2dp.c
>> index 63f1f58..5cb01f7 100644
>> --- a/android/a2dp.c
>> +++ b/android/a2dp.c
>> @@ -352,7 +352,16 @@ static sdp_record_t *a2dp_record(void)
>>       return record;
>>  }
>>
>> +static void bt_audio_open(const void *buf, uint16_t len)
>> +{
>> +     DBG("Not Implemented");
>> +
>> +     ipc_send_rsp(HAL_SERVICE_ID_AUDIO, AUDIO_OP_OPEN, HAL_STATUS_FAILED);
>> +}
>> +
>>  static const struct ipc_handler audio_handlers[] = {
>> +     /* AUDIO_OP_OPEN */
>> +     { bt_audio_open, true, sizeof(struct audio_cmd_open) },
>>  };
>>
>>  bool bt_a2dp_register(const bdaddr_t *addr)
>> diff --git a/android/audio-ipc-api.txt b/android/audio-ipc-api.txt
>> index 1c42800..37a1569 100644
>> --- a/android/audio-ipc-api.txt
>> +++ b/android/audio-ipc-api.txt
>> @@ -49,9 +49,9 @@ Identifier: "audio" (BT_AUDIO_ID)
>>
>>               Command parameters: Service UUID (16 octets)
>>                                   Codec ID (1 octet)
>> +                                 Number of codec presets (1 octet)
>>                                   Codec capabilities length (1 octet)
>>                                   Codec capabilities (variable)
>> -                                 Number of codec presets (1 octet)
>>                                   Codec preset # length (1 octet)
>>                                   Codec preset # configuration (variable)
>>                                   ...
>> diff --git a/android/hal-msg.h b/android/hal-msg.h
>> index 1afb1bc..4b52e5e 100644
>> --- a/android/hal-msg.h
>> +++ b/android/hal-msg.h
>> @@ -567,3 +567,21 @@ struct hal_ev_a2dp_audio_state {
>>       uint8_t state;
>>       uint8_t bdaddr[6];
>>  } __attribute__((packed));
>> +
>> +#define AUDIO_OP_OPEN                                0x01
>> +struct audio_cmd_open {
>> +     uint16_t uuid;
>> +     uint8_t codec;
>> +     uint8_t presets;
>> +     uint8_t len;
>> +     uint8_t data[0];
>
> Maybe this could be
> struct audio_preset[0];
> ? (if that would make code cleaner)

Indeed, will fix it.

Luiz Augusto von Dentz

^ permalink raw reply

* Re: [RFC BlueZ 1/9] android: Add initial code for audio IPC
From: Luiz Augusto von Dentz @ 2013-12-30 13:34 UTC (permalink / raw)
  To: Szymon Janc; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <1919373.99Va0DTt5V@athlon>

Hi Szymon,

On Mon, Dec 30, 2013 at 3:26 PM, Szymon Janc <szymon.janc@gmail.com> wrote:
> Hi Luiz,
>
> On Monday 30 December 2013 14:34:07 Luiz Augusto von Dentz wrote:
>> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>>
>> This add initial code for listen and accept connections on the abstract
>> socket defined for the audio IPC.
>> ---
>>  android/hal-msg.h |  1 +
>>  android/ipc.c     | 53
>> +++++++++++++++++++++++++++++++++++++++++++++++------ android/ipc.h     |
>> 3 +++
>>  3 files changed, 51 insertions(+), 6 deletions(-)
>>
>> diff --git a/android/hal-msg.h b/android/hal-msg.h
>> index c351501..b14eced 100644
>> --- a/android/hal-msg.h
>> +++ b/android/hal-msg.h
>> @@ -24,6 +24,7 @@
>>  #define BLUEZ_HAL_MTU 1024
>>
>>  static const char BLUEZ_HAL_SK_PATH[] = "\0bluez_hal_socket";
>> +static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
>>
>>  struct hal_hdr {
>>       uint8_t  service_id;
>> diff --git a/android/ipc.c b/android/ipc.c
>> index 9e8ccc3..6cdbf60 100644
>> --- a/android/ipc.c
>> +++ b/android/ipc.c
>> @@ -49,6 +49,7 @@ static struct service_handler services[HAL_SERVICE_ID_MAX
>> + 1];
>>
>>  static GIOChannel *cmd_io = NULL;
>>  static GIOChannel *notif_io = NULL;
>> +static GIOChannel *audio_io = NULL;
>>
>>  static void ipc_handle_msg(const void *buf, ssize_t len)
>>  {
>> @@ -145,7 +146,8 @@ static gboolean notif_watch_cb(GIOChannel *io,
>> GIOCondition cond, return FALSE;
>>  }
>>
>> -static GIOChannel *connect_hal(GIOFunc connect_cb)
>> +static GIOChannel *connect_hal(const char *path, size_t size,
>> +                                                     GIOFunc connect_cb)
>>  {
>>       struct sockaddr_un addr;
>>       GIOCondition cond;
>> @@ -167,11 +169,11 @@ static GIOChannel *connect_hal(GIOFunc connect_cb)
>>       memset(&addr, 0, sizeof(addr));
>>       addr.sun_family = AF_UNIX;
>>
>> -     memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
>> +     memcpy(addr.sun_path, path, size);
>>
>>       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
>> -             error("IPC: failed to connect HAL socket: %d (%s)", errno,
>> -                                                     strerror(errno));
>> +             error("IPC: failed to connect HAL socket %s: %d (%s)", &path[1],
>> +                                                     errno, strerror(errno));
>>               g_io_channel_unref(io);
>>               return NULL;
>>       }
>> @@ -218,7 +220,8 @@ static gboolean cmd_connect_cb(GIOChannel *io,
>> GIOCondition cond, return FALSE;
>>       }
>>
>> -     notif_io = connect_hal(notif_connect_cb);
>> +     notif_io = connect_hal(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH),
>> +                                                     notif_connect_cb);
>>       if (!notif_io)
>>               raise(SIGTERM);
>>
>> @@ -227,7 +230,8 @@ static gboolean cmd_connect_cb(GIOChannel *io,
>> GIOCondition cond,
>>
>>  void ipc_init(void)
>>  {
>> -     cmd_io = connect_hal(cmd_connect_cb);
>> +     cmd_io = connect_hal(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH),
>> +                                                     cmd_connect_cb);
>>       if (!cmd_io)
>>               raise(SIGTERM);
>>  }
>> @@ -338,3 +342,40 @@ void ipc_unregister(uint8_t service)
>>       services[service].handler = NULL;
>>       services[service].size = 0;
>>  }
>> +
>> +static gboolean audio_connect_cb(GIOChannel *io, GIOCondition cond,
>> +                                                     gpointer user_data)
>> +{
>> +     DBG("");
>> +
>> +     if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
>> +             error("Audio IPC: socket connect failed, terminating");
>
> This message is misleading since we should raise(SIGTERM) to terminate.
>
> But I wonder if we should really terminate on audio IPC failure...
> That is failing on IPC error is OK (since that is a bug in our code anyway),
> but on not being able to connect we could simply fail to register a2dp HAL.
> Same goes with socket being closed - in such case we could fail any request to
> a2dp HAL until connection is recovered.

Yep, the terminating part of the message is misleading it should not
really exit just fail to initialize a2dp.

>> +             audio_ipc_cleanup();
>> +             return FALSE;
>> +     }
>> +
>> +     cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
>> +
>> +     g_io_add_watch(audio_io, cond, cmd_watch_cb, NULL);
>> +
>> +     info("Audio IPC: successfully connected");
>> +
>> +     return FALSE;
>> +}
>> +
>> +void audio_ipc_init(void)
>> +{
>> +     audio_io = connect_hal(BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH),
>> +                                                     audio_connect_cb);
>> +     if (!cmd_io)
>
> audio_io here?

Yep, that is a bug.

-- 
Luiz Augusto von Dentz

^ permalink raw reply

* Re: [RFC BlueZ 3/9] android: Add audio open command/response struct
From: Szymon Janc @ 2013-12-30 13:27 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <1388406855-8809-3-git-send-email-luiz.dentz@gmail.com>

Hi Luiz,

On Monday 30 December 2013 14:34:09 Luiz Augusto von Dentz wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> 
> This adds the definitions to audio open command and response.
> ---
>  android/a2dp.c            |  9 +++++++++
>  android/audio-ipc-api.txt |  2 +-
>  android/hal-msg.h         | 18 ++++++++++++++++++
>  android/ipc.c             |  5 ++++-
>  4 files changed, 32 insertions(+), 2 deletions(-)
> 
> diff --git a/android/a2dp.c b/android/a2dp.c
> index 63f1f58..5cb01f7 100644
> --- a/android/a2dp.c
> +++ b/android/a2dp.c
> @@ -352,7 +352,16 @@ static sdp_record_t *a2dp_record(void)
>  	return record;
>  }
> 
> +static void bt_audio_open(const void *buf, uint16_t len)
> +{
> +	DBG("Not Implemented");
> +
> +	ipc_send_rsp(HAL_SERVICE_ID_AUDIO, AUDIO_OP_OPEN, HAL_STATUS_FAILED);
> +}
> +
>  static const struct ipc_handler audio_handlers[] = {
> +	/* AUDIO_OP_OPEN */
> +	{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
>  };
> 
>  bool bt_a2dp_register(const bdaddr_t *addr)
> diff --git a/android/audio-ipc-api.txt b/android/audio-ipc-api.txt
> index 1c42800..37a1569 100644
> --- a/android/audio-ipc-api.txt
> +++ b/android/audio-ipc-api.txt
> @@ -49,9 +49,9 @@ Identifier: "audio" (BT_AUDIO_ID)
> 
>  		Command parameters: Service UUID (16 octets)
>  				    Codec ID (1 octet)
> +				    Number of codec presets (1 octet)
>  				    Codec capabilities length (1 octet)
>  				    Codec capabilities (variable)
> -				    Number of codec presets (1 octet)
>  				    Codec preset # length (1 octet)
>  				    Codec preset # configuration (variable)
>  				    ...
> diff --git a/android/hal-msg.h b/android/hal-msg.h
> index 1afb1bc..4b52e5e 100644
> --- a/android/hal-msg.h
> +++ b/android/hal-msg.h
> @@ -567,3 +567,21 @@ struct hal_ev_a2dp_audio_state {
>  	uint8_t state;
>  	uint8_t bdaddr[6];
>  } __attribute__((packed));
> +
> +#define AUDIO_OP_OPEN				0x01
> +struct audio_cmd_open {
> +	uint16_t uuid;
> +	uint8_t codec;
> +	uint8_t presets;
> +	uint8_t len;
> +	uint8_t data[0];

Maybe this could be
struct audio_preset[0];
? (if that would make code cleaner)

> +} __attribute__((packed));
> +
> +struct audio_preset {
> +	uint8_t len;
> +	uint8_t data[0];
> +} __attribute__((packed));
> +
> +struct audio_rsp_open {
> +	uint8_t id;
> +} __attribute__((packed));
> diff --git a/android/ipc.c b/android/ipc.c
> index 6cdbf60..bb16553 100644
> --- a/android/ipc.c
> +++ b/android/ipc.c
> @@ -301,7 +301,10 @@ void ipc_send_rsp(uint8_t service_id, uint8_t opcode,
> uint8_t status) struct hal_status s;
>  	int sk;
> 
> -	sk = g_io_channel_unix_get_fd(cmd_io);
> +	if (service_id == HAL_SERVICE_ID_AUDIO)
> +		sk = g_io_channel_unix_get_fd(audio_io);
> +	else
> +		sk = g_io_channel_unix_get_fd(cmd_io);
> 
>  	if (status == HAL_STATUS_SUCCESS) {
>  		ipc_send(sk, service_id, opcode, 0, NULL, -1);

-- 
Szymon K. Janc
szymon.janc@gmail.com

^ permalink raw reply

* Re: [RFC BlueZ 1/9] android: Add initial code for audio IPC
From: Szymon Janc @ 2013-12-30 13:26 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <1388406855-8809-1-git-send-email-luiz.dentz@gmail.com>

Hi Luiz,

On Monday 30 December 2013 14:34:07 Luiz Augusto von Dentz wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> 
> This add initial code for listen and accept connections on the abstract
> socket defined for the audio IPC.
> ---
>  android/hal-msg.h |  1 +
>  android/ipc.c     | 53
> +++++++++++++++++++++++++++++++++++++++++++++++------ android/ipc.h     | 
> 3 +++
>  3 files changed, 51 insertions(+), 6 deletions(-)
> 
> diff --git a/android/hal-msg.h b/android/hal-msg.h
> index c351501..b14eced 100644
> --- a/android/hal-msg.h
> +++ b/android/hal-msg.h
> @@ -24,6 +24,7 @@
>  #define BLUEZ_HAL_MTU 1024
> 
>  static const char BLUEZ_HAL_SK_PATH[] = "\0bluez_hal_socket";
> +static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
> 
>  struct hal_hdr {
>  	uint8_t  service_id;
> diff --git a/android/ipc.c b/android/ipc.c
> index 9e8ccc3..6cdbf60 100644
> --- a/android/ipc.c
> +++ b/android/ipc.c
> @@ -49,6 +49,7 @@ static struct service_handler services[HAL_SERVICE_ID_MAX
> + 1];
> 
>  static GIOChannel *cmd_io = NULL;
>  static GIOChannel *notif_io = NULL;
> +static GIOChannel *audio_io = NULL;
> 
>  static void ipc_handle_msg(const void *buf, ssize_t len)
>  {
> @@ -145,7 +146,8 @@ static gboolean notif_watch_cb(GIOChannel *io,
> GIOCondition cond, return FALSE;
>  }
> 
> -static GIOChannel *connect_hal(GIOFunc connect_cb)
> +static GIOChannel *connect_hal(const char *path, size_t size,
> +							GIOFunc connect_cb)
>  {
>  	struct sockaddr_un addr;
>  	GIOCondition cond;
> @@ -167,11 +169,11 @@ static GIOChannel *connect_hal(GIOFunc connect_cb)
>  	memset(&addr, 0, sizeof(addr));
>  	addr.sun_family = AF_UNIX;
> 
> -	memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
> +	memcpy(addr.sun_path, path, size);
> 
>  	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
> -		error("IPC: failed to connect HAL socket: %d (%s)", errno,
> -							strerror(errno));
> +		error("IPC: failed to connect HAL socket %s: %d (%s)", &path[1],
> +							errno, strerror(errno));
>  		g_io_channel_unref(io);
>  		return NULL;
>  	}
> @@ -218,7 +220,8 @@ static gboolean cmd_connect_cb(GIOChannel *io,
> GIOCondition cond, return FALSE;
>  	}
> 
> -	notif_io = connect_hal(notif_connect_cb);
> +	notif_io = connect_hal(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH),
> +							notif_connect_cb);
>  	if (!notif_io)
>  		raise(SIGTERM);
> 
> @@ -227,7 +230,8 @@ static gboolean cmd_connect_cb(GIOChannel *io,
> GIOCondition cond,
> 
>  void ipc_init(void)
>  {
> -	cmd_io = connect_hal(cmd_connect_cb);
> +	cmd_io = connect_hal(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH),
> +							cmd_connect_cb);
>  	if (!cmd_io)
>  		raise(SIGTERM);
>  }
> @@ -338,3 +342,40 @@ void ipc_unregister(uint8_t service)
>  	services[service].handler = NULL;
>  	services[service].size = 0;
>  }
> +
> +static gboolean audio_connect_cb(GIOChannel *io, GIOCondition cond,
> +							gpointer user_data)
> +{
> +	DBG("");
> +
> +	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
> +		error("Audio IPC: socket connect failed, terminating");

This message is misleading since we should raise(SIGTERM) to terminate.

But I wonder if we should really terminate on audio IPC failure...
That is failing on IPC error is OK (since that is a bug in our code anyway), 
but on not being able to connect we could simply fail to register a2dp HAL.
Same goes with socket being closed - in such case we could fail any request to
a2dp HAL until connection is recovered.

> +		audio_ipc_cleanup();
> +		return FALSE;
> +	}
> +
> +	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
> +
> +	g_io_add_watch(audio_io, cond, cmd_watch_cb, NULL);
> +
> +	info("Audio IPC: successfully connected");
> +
> +	return FALSE;
> +}
> +
> +void audio_ipc_init(void)
> +{
> +	audio_io = connect_hal(BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH),
> +							audio_connect_cb);
> +	if (!cmd_io)

audio_io here?

> +		raise(SIGTERM);
> +}
> +
> +void audio_ipc_cleanup(void)
> +{
> +	if (audio_io) {
> +		g_io_channel_shutdown(audio_io, TRUE, NULL);
> +		g_io_channel_unref(audio_io);
> +		audio_io = NULL;
> +	}
> +}
> diff --git a/android/ipc.h b/android/ipc.h
> index 6cd102b..8e92811 100644
> --- a/android/ipc.h
> +++ b/android/ipc.h
> @@ -37,3 +37,6 @@ void ipc_send_notif(uint8_t service_id, uint8_t opcode, 
> uint16_t len, void ipc_register(uint8_t service, const struct ipc_handler
> *handlers, uint8_t size);
>  void ipc_unregister(uint8_t service);
> +
> +void audio_ipc_init(void);
> +void audio_ipc_cleanup(void);

-- 
Szymon K. Janc
szymon.janc@gmail.com

^ permalink raw reply

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

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

Hi,

On Mon, Dec 30, 2013 at 01:13:50PM +0100, Pavel Machek wrote:
> [...]
>
> Well, I can rename config option, but renaming the module would break
> existing userland, no?

Why is the userland depending on the module name?

> >  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.
> 
> Nokia N900 is certainly moving towards device tree, but we are not
> ready, yet...

Tony plans to remove OMAP3 boardcode (incl. omap3-rx51) in 3.14.

> > > [...]
> > >
> > 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.
> 
> (Could you insert newlines after 80 or so characters?)
> 
> Where would you like platform_data definition to go? That indeed is
> for platform data, and quick grep shows drivers normally do public
> header files for that.

Probably it can simply be removed, because it's not useful in 3.14?

> [...]

-- Sebastian

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [RFC 6/6] android/audio: Add listener thread on the Audio HAL socket
From: Luiz Augusto von Dentz @ 2013-12-30 13:07 UTC (permalink / raw)
  To: Lukasz Rymanowski; +Cc: linux-bluetooth@vger.kernel.org, Johan Hedberg
In-Reply-To: <CAMudyAv386wWW8YQKY9u_6SV6ZaSijwxm=qud0rk6sTtKsgRog@mail.gmail.com>

Hi Lukasz,

On Mon, Dec 30, 2013 at 1:40 PM, Lukasz Rymanowski
<lukasz.rymanowski@tieto.com> wrote:
> Hi Luiz,
>
> On 30 December 2013 12:31, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
>> Hi Lukasz,
>>
>> On Mon, Dec 30, 2013 at 12:17 PM, Lukasz Rymanowski
>> <lukasz.rymanowski@tieto.com> wrote:
>>> This patch add thread which is reponsible for listen on audio HAL
>>> socket, register a2dp endpoint(s) and maintain socket.
>>> When bluetooth daemon goes down, HAL audio plugin starts to listen on Audio HAL
>>> socket again.
>>>
>>> ---
>>>  android/Makefile.am |   2 +
>>>  android/hal-audio.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>  android/hal-audio.h |  18 +++++++
>>>  3 files changed, 165 insertions(+)
>>>  create mode 100644 android/hal-audio.h
>>>
>>> diff --git a/android/Makefile.am b/android/Makefile.am
>>> index eaf39bd..bd90c13 100644
>>> --- a/android/Makefile.am
>>> +++ b/android/Makefile.am
>>> @@ -112,6 +112,8 @@ android_libaudio_internal_la_SOURCES = android/hal-audio.c \
>>>
>>>  android_libaudio_internal_la_CFLAGS = -I$(srcdir)/android
>>>
>>> +android_libaudio_internal_la_LDFLAGS = -pthread
>>> +
>>>  endif
>>>
>>>  EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
>>> diff --git a/android/hal-audio.c b/android/hal-audio.c
>>> index 011a699..0e3bc70 100644
>>> --- a/android/hal-audio.c
>>> +++ b/android/hal-audio.c
>>> @@ -16,18 +16,30 @@
>>>   */
>>>
>>>  #include <errno.h>
>>> +#include <pthread.h>
>>> +#include <poll.h>
>>>  #include <stdio.h>
>>>  #include <stdlib.h>
>>>  #include <string.h>
>>> +#include <sys/socket.h>
>>> +#include <sys/un.h>
>>> +#include <unistd.h>
>>>
>>>  #include <hardware/audio.h>
>>>  #include <hardware/hardware.h>
>>>
>>> +#include "hal-audio.h"
>>>  #include "hal-log.h"
>>>
>>>  struct a2dp_audio_dev {
>>>         struct audio_hw_device dev;
>>>         struct a2dp_stream_out *stream_out;
>>> +
>>> +       pthread_t bt_watcher;
>>> +       pthread_mutex_t hal_sk_mutex;
>>> +       pthread_cond_t bt_watcher_cond;
>>> +
>>> +       int hal_sk;
>>>  };
>>>
>>>  struct a2dp_stream_out {
>>> @@ -384,15 +396,135 @@ static int audio_dump(const audio_hw_device_t *device, int fd)
>>>
>>>  static int audio_close(hw_device_t *device)
>>>  {
>>> +       struct audio_hw_device *dev = (struct audio_hw_device *)device;
>>> +       struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)dev;
>>> +

Hmm, Im afraid these are not the same pointers as you do  *device =
&a2dp_dev->dev.common; so this will probably cause invalid accesses.

>>>         DBG("");
>>> +
>>> +       if (a2dp_dev) {
>>> +               /* TODO: We could try to unregister Endpoint here */
>>> +               shutdown(a2dp_dev->hal_sk, 2);
>>> +
>>> +               pthread_mutex_lock(&a2dp_dev->hal_sk_mutex);
>>> +               pthread_cond_signal(&a2dp_dev->bt_watcher_cond);
>>> +               pthread_mutex_unlock(&a2dp_dev->hal_sk_mutex);
>>> +
>>> +               (void) pthread_join(a2dp_dev->bt_watcher, NULL);
>>> +               free(a2dp_dev);
>>> +       }
>>> +
>>>         free(device);
>>>         return 0;
>>>  }
>>>
>>> +static int wait_for_client(void)
>>> +{
>>> +       struct sockaddr_un addr;
>>> +       int err;
>>> +       int sk;
>>> +       int new_sk;
>>> +
>>> +       DBG("");
>>> +
>>> +       sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
>>> +       if (sk < 0) {
>>> +               err = errno;
>>> +               error("Failed to create socket: %d (%s)", err, strerror(err));
>>> +               return -1;
>>> +       }
>>> +
>>> +       memset(&addr, 0, sizeof(addr));
>>> +       addr.sun_family = AF_UNIX;
>>> +
>>> +       memcpy(addr.sun_path, BLUEZ_AUDIO_HAL_SK_PATH,
>>> +                                       sizeof(BLUEZ_AUDIO_HAL_SK_PATH));
>>> +
>>> +       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
>>> +               err = errno;
>>> +               error("Failed to bind socket: %d (%s)", err, strerror(err));
>>> +               goto error;
>>> +       }
>>> +
>>> +       if (listen(sk, 1) < 0) {
>>> +               err = errno;
>>> +               error("Failed to bind socket: %d (%s)", err, strerror(err));
>>> +               goto error;
>>> +       }
>>> +
>>> +       new_sk = accept(sk, NULL, NULL);
>>> +       if (new_sk < 0) {
>>> +               err = errno;
>>> +               error("Failed to accept socket: %d (%s)", err, strerror(err));
>>> +               goto error;
>>> +       }
>>> +
>>> +       close(sk);
>>> +       return new_sk;
>>> +
>>> +error:
>>> +       close(sk);
>>> +       return -1;
>>> +}
>>> +
>>> +static void *bluetoothd_watcher(void *data)
>>> +{
>>> +       int err;
>>> +       struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)data;
>>> +       struct pollfd pfd;
>>> +       struct timeval now;
>>> +       struct timespec timeout;
>>> +
>>> +       DBG("");
>>> +
>>> +       while (1) {
>>> +               a2dp_dev->hal_sk = wait_for_client();
>>> +               if (a2dp_dev->hal_sk < 0) {
>>> +                       error("Failed to create listening socket");
>>> +                       continue;
>>> +               }
>>> +
>>> +               DBG("Audio IPC: Connected");
>>> +
>>> +               /* TODO: Register ENDPOINT here */
>>> +
>>> +               memset(&pfd, 0, sizeof(pfd));
>>> +               pfd.fd = a2dp_dev->hal_sk;
>>> +               pfd.events = POLLHUP | POLLERR | POLLNVAL;
>>> +
>>> +               pthread_mutex_lock(&a2dp_dev->hal_sk_mutex);
>>> +
>>> +               /* Check if socket is still alive */
>>> +               err = poll(&pfd, 1, -1);
>>> +               if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) {
>>> +                       info("Audio HAL: Socket closed");
>>> +                       a2dp_dev->hal_sk = -1;
>>> +               }
>>> +
>>> +               /* Maybe audio system is closing and audio_close() has been called? */
>>> +               gettimeofday(&now, NULL);
>>> +               timeout.tv_sec = now.tv_sec;
>>> +               timeout.tv_nsec = now.tv_usec * 1000;
>>> +               timeout.tv_sec += 1;
>>> +
>>> +               err = pthread_cond_timedwait(&a2dp_dev->bt_watcher_cond,
>>> +                                       &a2dp_dev->hal_sk_mutex, &timeout);
>>> +
>>> +               pthread_mutex_unlock(&a2dp_dev->hal_sk_mutex);
>>> +               if (err != ETIMEDOUT)
>>> +                       /* Seems that device has been closed */
>>> +                       break;
>>> +       }
>>> +
>>> +       info("Closing bluetooth_watcher thread");
>>> +       pthread_exit(NULL);
>>> +       return NULL;
>>> +}
>>> +
>>>  static int audio_open(const hw_module_t *module, const char *name,
>>>                                                         hw_device_t **device)
>>>  {
>>>         struct a2dp_audio_dev *a2dp_dev;
>>> +       int err;
>>>
>>>         DBG("");
>>>
>>> @@ -427,6 +559,19 @@ static int audio_open(const hw_module_t *module, const char *name,
>>>
>>>         *device = &a2dp_dev->dev.common;
>>>
>>> +       a2dp_dev->hal_sk = -1;
>>> +
>>> +       pthread_mutex_init(&a2dp_dev->hal_sk_mutex, NULL);
>>> +       pthread_cond_init(&a2dp_dev->bt_watcher_cond, NULL);
>>> +
>>> +       err = pthread_create(&a2dp_dev->bt_watcher, NULL, bluetoothd_watcher, a2dp_dev);
>>> +       if (err < 0) {
>>> +               a2dp_dev->bt_watcher = 0;
>>> +               error("Failed to start bluetoothd watcher thread: %d (%s)", -err,
>>> +                                                               strerror(-err));
>>> +               return (-err);
>>> +       }
>>> +
>>>         return 0;
>>>  }
>>>
>>> diff --git a/android/hal-audio.h b/android/hal-audio.h
>>> new file mode 100644
>>> index 0000000..93e49f6
>>> --- /dev/null
>>> +++ b/android/hal-audio.h
>>> @@ -0,0 +1,18 @@
>>> +/*
>>> + *
>>> + * Copyright (C) 2013 Intel Corporation
>>> + *
>>> + * Licensed under the Apache License, Version 2.0 (the "License");
>>> + * you may not use this file except in compliance with the License.
>>> + * You may obtain a copy of the License at
>>> + *
>>> + *      http://www.apache.org/licenses/LICENSE-2.0
>>> + *
>>> + * Unless required by applicable law or agreed to in writing, software
>>> + * distributed under the License is distributed on an "AS IS" BASIS,
>>> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>>> + * See the License for the specific language governing permissions and
>>> + * limitations under the License.
>>> + *
>>> + */
>>> +static const char BLUEZ_AUDIO_HAL_SK_PATH[] = "\0bluez_audio_socket";
>>> --
>>> 1.8.4
>>
>> Ive started working on the daemon side and should be able to post some
>> patches today, Im not really sure we are going to need a thread for
>> command handling since all the commands will be sent by the plugin
>> side so they can be synchronous, the only problem would be if the
>> plugin cannot really block even for a short period.
>>
>
> This Thread is only going to be used for accept, endpoint register and
> then listening for socket close (so we can start listen for connection
> from new bluetoothd).
>
> Commands and response will be handled in main thread (ex.
> audio_open(), out_write()) and we can block there for a while - no
> problem with that

Well it seems the init/listen stage needs to be non-blocking so we
don't interrupt the audio init procedure which may block bluetooth HAL
to initialized, the endpoint then should be registered whenever
bluetoothd connects.


-- 
Luiz Augusto von Dentz

^ permalink raw reply

* Re: [RFC 5/6] android/audio: Add wrapper struct for audio structures
From: Luiz Augusto von Dentz @ 2013-12-30 12:43 UTC (permalink / raw)
  To: Lukasz Rymanowski; +Cc: linux-bluetooth@vger.kernel.org, Johan Hedberg
In-Reply-To: <CAMudyAsGXQ3cPjv_5R5cFrM=asYr8ZZH2YmLdOh95mEiz0oc8w@mail.gmail.com>

Hi Lukasz,

On Mon, Dec 30, 2013 at 2:37 PM, Lukasz Rymanowski
<lukasz.rymanowski@tieto.com> wrote:
> Hi Luiz,
>
> On 30 December 2013 13:04, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
>> Hi Lukasz,
>>
>> On Mon, Dec 30, 2013 at 1:33 PM, Lukasz Rymanowski
>> <lukasz.rymanowski@tieto.com> wrote:
>>> Hi Luiz,
>>>
>>> On 30 December 2013 12:27, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
>>>> Hi Lukasz,
>>>>
>>>> On Mon, Dec 30, 2013 at 12:17 PM, Lukasz Rymanowski
>>>> <lukasz.rymanowski@tieto.com> wrote:
>>>>> This patch add wrapping struct for audio_hw_dev and audio_stream_out.
>>>>> We will need this to keep some more addition info related to a2dp stream
>>>>> and also hal IPC later on
>>>>>
>>>>> ---
>>>>>  android/Makefile.am |  1 +
>>>>>  android/hal-audio.c | 97 +++++++++++++++++++++++++++++------------------------
>>>>>  2 files changed, 55 insertions(+), 43 deletions(-)
>>>>>
>>>>> diff --git a/android/Makefile.am b/android/Makefile.am
>>>>> index dec81ce..eaf39bd 100644
>>>>> --- a/android/Makefile.am
>>>>> +++ b/android/Makefile.am
>>>>> @@ -104,6 +104,7 @@ android_android_tester_LDFLAGS = -pthread
>>>>>  noinst_LTLIBRARIES += android/libaudio-internal.la
>>>>>
>>>>>  android_libaudio_internal_la_SOURCES = android/hal-audio.c \
>>>>> +                                       android/hal-audio.h \
>>>>>                                         android/hardware/audio.h \
>>>>>                                         android/hardware/audio_effect.h \
>>>>>                                         android/hardware/hardware.h \
>>>>> diff --git a/android/hal-audio.c b/android/hal-audio.c
>>>>> index 7f4a3f2..011a699 100644
>>>>> --- a/android/hal-audio.c
>>>>> +++ b/android/hal-audio.c
>>>>> @@ -25,6 +25,15 @@
>>>>>
>>>>>  #include "hal-log.h"
>>>>>
>>>>> +struct a2dp_audio_dev {
>>>>> +       struct audio_hw_device dev;
>>>>> +       struct a2dp_stream_out *stream_out;
>>>>> +};
>>>>> +
>>>>> +struct a2dp_stream_out {
>>>>> +       struct audio_stream_out stream;
>>>>> +};
>>>>> +
>>>>>  static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
>>>>>                                                                 size_t bytes)
>>>>>  {
>>>>> @@ -230,32 +239,34 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
>>>>>                                         struct audio_stream_out **stream_out)
>>>>>
>>>>>  {
>>>>> -       struct audio_stream_out *out;
>>>>> +       struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)dev;
>>>>> +       struct a2dp_stream_out *a2dp_out;
>>>>>
>>>>> -       out = calloc(1, sizeof(struct audio_stream_out));
>>>>> -       if (!out)
>>>>> +       a2dp_out = calloc(1, sizeof(struct a2dp_stream_out));
>>>>> +       if (!a2dp_out)
>>>>>                 return -ENOMEM;
>>>>>
>>>>>         DBG("");
>>>>>
>>>>> -       out->common.get_sample_rate = out_get_sample_rate;
>>>>> -       out->common.set_sample_rate = out_set_sample_rate;
>>>>> -       out->common.get_buffer_size = out_get_buffer_size;
>>>>> -       out->common.get_channels = out_get_channels;
>>>>> -       out->common.get_format = out_get_format;
>>>>> -       out->common.set_format = out_set_format;
>>>>> -       out->common.standby = out_standby;
>>>>> -       out->common.dump = out_dump;
>>>>> -       out->common.set_parameters = out_set_parameters;
>>>>> -       out->common.get_parameters = out_get_parameters;
>>>>> -       out->common.add_audio_effect = out_add_audio_effect;
>>>>> -       out->common.remove_audio_effect = out_remove_audio_effect;
>>>>> -       out->get_latency = out_get_latency;
>>>>> -       out->set_volume = out_set_volume;
>>>>> -       out->write = out_write;
>>>>> -       out->get_render_position = out_get_render_position;
>>>>> +       a2dp_out->stream.common.get_sample_rate = out_get_sample_rate;
>>>>> +       a2dp_out->stream.common.set_sample_rate = out_set_sample_rate;
>>>>> +       a2dp_out->stream.common.get_buffer_size = out_get_buffer_size;
>>>>> +       a2dp_out->stream.common.get_channels = out_get_channels;
>>>>> +       a2dp_out->stream.common.get_format = out_get_format;
>>>>> +       a2dp_out->stream.common.set_format = out_set_format;
>>>>> +       a2dp_out->stream.common.standby = out_standby;
>>>>> +       a2dp_out->stream.common.dump = out_dump;
>>>>> +       a2dp_out->stream.common.set_parameters = out_set_parameters;
>>>>> +       a2dp_out->stream.common.get_parameters = out_get_parameters;
>>>>> +       a2dp_out->stream.common.add_audio_effect = out_add_audio_effect;
>>>>> +       a2dp_out->stream.common.remove_audio_effect = out_remove_audio_effect;
>>>>> +       a2dp_out->stream.get_latency = out_get_latency;
>>>>> +       a2dp_out->stream.set_volume = out_set_volume;
>>>>> +       a2dp_out->stream.write = out_write;
>>>>> +       a2dp_out->stream.get_render_position = out_get_render_position;
>>>>>
>>>>> -       *stream_out = out;
>>>>> +       *stream_out = &a2dp_out->stream;
>>>>> +       a2dp_dev->stream_out = a2dp_out;
>>>>>
>>>>>         return 0;
>>>>>  }
>>>>> @@ -381,7 +392,7 @@ static int audio_close(hw_device_t *device)
>>>>>  static int audio_open(const hw_module_t *module, const char *name,
>>>>>                                                         hw_device_t **device)
>>>>>  {
>>>>> -       struct audio_hw_device *audio;
>>>>> +       struct a2dp_audio_dev *a2dp_dev;
>>>>>
>>>>>         DBG("");
>>>>>
>>>>> @@ -391,30 +402,30 @@ static int audio_open(const hw_module_t *module, const char *name,
>>>>>                 return -EINVAL;
>>>>>         }
>>>>>
>>>>> -       audio = calloc(1, sizeof(struct audio_hw_device));
>>>>> -       if (!audio)
>>>>> +       a2dp_dev = calloc(1, sizeof(struct a2dp_audio_dev));
>>>>> +       if (!a2dp_dev)
>>>>>                 return -ENOMEM;
>>>>>
>>>>> -       audio->common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
>>>>> -       audio->common.module = (struct hw_module_t *) module;
>>>>> -       audio->common.close = audio_close;
>>>>> -
>>>>> -       audio->init_check = audio_init_check;
>>>>> -       audio->set_voice_volume = audio_set_voice_volume;
>>>>> -       audio->set_master_volume = audio_set_master_volume
>>>>> -       audio->set_mode = audio_set_mode;
>>>>> -       audio->set_mic_mute = audio_set_mic_mute;
>>>>> -       audio->get_mic_mute = audio_get_mic_mute;
>>>>> -       audio->set_parameters = audio_set_parameters;
>>>>> -       audio->get_parameters = audio_get_parameters;
>>>>> -       audio->get_input_buffer_size = audio_get_input_buffer_size;
>>>>> -       audio->open_output_stream = audio_open_output_stream;
>>>>> -       audio->close_output_stream = audio_close_output_stream;
>>>>> -       audio->open_input_stream = audio_open_input_stream;
>>>>> -       audio->close_input_stream = audio_close_input_stream;
>>>>> -       audio->dump = audio_dump;
>>>>> -
>>>>> -       *device = &audio->common;
>>>>> +       a2dp_dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
>>>>> +       a2dp_dev->dev.common.module = (struct hw_module_t *) module;
>>>>> +       a2dp_dev->dev.common.close = audio_close;
>>>>> +
>>>>> +       a2dp_dev->dev.init_check = audio_init_check;
>>>>> +       a2dp_dev->dev.set_voice_volume = audio_set_voice_volume;
>>>>> +       a2dp_dev->dev.set_master_volume = audio_set_master_volume;
>>>>> +       a2dp_dev->dev.set_mode = audio_set_mode;
>>>>> +       a2dp_dev->dev.set_mic_mute = audio_set_mic_mute;
>>>>> +       a2dp_dev->dev.get_mic_mute = audio_get_mic_mute;
>>>>> +       a2dp_dev->dev.set_parameters = audio_set_parameters;
>>>>> +       a2dp_dev->dev.get_parameters = audio_get_parameters;
>>>>> +       a2dp_dev->dev.get_input_buffer_size = audio_get_input_buffer_size;
>>>>> +       a2dp_dev->dev.open_output_stream = audio_open_output_stream;
>>>>> +       a2dp_dev->dev.close_output_stream = audio_close_output_stream;
>>>>> +       a2dp_dev->dev.open_input_stream = audio_open_input_stream;
>>>>> +       a2dp_dev->dev.close_input_stream = audio_close_input_stream;
>>>>> +       a2dp_dev->dev.dump = audio_dump;
>>>>> +
>>>>> +       *device = &a2dp_dev->dev.common;
>>>>>
>>>>>         return 0;
>>>>>  }
>>>>> --
>>>>> 1.8.4
>>>>
>>>> How is this supposed to be accessed, are you planning on adding a
>>>> global variable?
>>>>
>>>>
>>> Have a look on audio_open_output_stream(). This is actually how they did so
>>> far and it is fine for me.
>>> I want to avoid globals here.
>>
>> I can see that you allocate a different struct for a2dp_dev and
>> a2dp_out, but then you return a member of the struct back to HAL but
>> after that you wont access these structs anymore and the HAL only uses
>> the members that you return so you probably gonna need to lookup for
>> these structs to access any auxiliary data that we might want to add,
>> also these structs never seems to be freed which I suppose can
>> generate memory leaks.
>>
>>
> You right I missed free of a2dp_out
> It will be done in  audio_close_output_stream(). Access to that will
> be similar as to a2dp_dev() in audio_close()

Well it doesn't seems you are changing audio_close, in fact the only
thing that audio_close frees is hw_device_t *device not struct
a2dp_audio_dev so you still have to deal with mapping access between
those struct and it doesn't seems the HAL have any concept of
user_data to attach so this normally indicates we need to somehow
store pointer to access latter.


-- 
Luiz Augusto von Dentz

^ permalink raw reply

* Re: [RFC 5/6] android/audio: Add wrapper struct for audio structures
From: Lukasz Rymanowski @ 2013-12-30 12:37 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth@vger.kernel.org, Johan Hedberg
In-Reply-To: <CABBYNZK5rRnRnrvDCx7w9zsrG4wZyf8GpGBhLq57fi_o_kNVxA@mail.gmail.com>

Hi Luiz,

On 30 December 2013 13:04, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
> Hi Lukasz,
>
> On Mon, Dec 30, 2013 at 1:33 PM, Lukasz Rymanowski
> <lukasz.rymanowski@tieto.com> wrote:
>> Hi Luiz,
>>
>> On 30 December 2013 12:27, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
>>> Hi Lukasz,
>>>
>>> On Mon, Dec 30, 2013 at 12:17 PM, Lukasz Rymanowski
>>> <lukasz.rymanowski@tieto.com> wrote:
>>>> This patch add wrapping struct for audio_hw_dev and audio_stream_out.
>>>> We will need this to keep some more addition info related to a2dp stream
>>>> and also hal IPC later on
>>>>
>>>> ---
>>>>  android/Makefile.am |  1 +
>>>>  android/hal-audio.c | 97 +++++++++++++++++++++++++++++------------------------
>>>>  2 files changed, 55 insertions(+), 43 deletions(-)
>>>>
>>>> diff --git a/android/Makefile.am b/android/Makefile.am
>>>> index dec81ce..eaf39bd 100644
>>>> --- a/android/Makefile.am
>>>> +++ b/android/Makefile.am
>>>> @@ -104,6 +104,7 @@ android_android_tester_LDFLAGS = -pthread
>>>>  noinst_LTLIBRARIES += android/libaudio-internal.la
>>>>
>>>>  android_libaudio_internal_la_SOURCES = android/hal-audio.c \
>>>> +                                       android/hal-audio.h \
>>>>                                         android/hardware/audio.h \
>>>>                                         android/hardware/audio_effect.h \
>>>>                                         android/hardware/hardware.h \
>>>> diff --git a/android/hal-audio.c b/android/hal-audio.c
>>>> index 7f4a3f2..011a699 100644
>>>> --- a/android/hal-audio.c
>>>> +++ b/android/hal-audio.c
>>>> @@ -25,6 +25,15 @@
>>>>
>>>>  #include "hal-log.h"
>>>>
>>>> +struct a2dp_audio_dev {
>>>> +       struct audio_hw_device dev;
>>>> +       struct a2dp_stream_out *stream_out;
>>>> +};
>>>> +
>>>> +struct a2dp_stream_out {
>>>> +       struct audio_stream_out stream;
>>>> +};
>>>> +
>>>>  static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
>>>>                                                                 size_t bytes)
>>>>  {
>>>> @@ -230,32 +239,34 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
>>>>                                         struct audio_stream_out **stream_out)
>>>>
>>>>  {
>>>> -       struct audio_stream_out *out;
>>>> +       struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)dev;
>>>> +       struct a2dp_stream_out *a2dp_out;
>>>>
>>>> -       out = calloc(1, sizeof(struct audio_stream_out));
>>>> -       if (!out)
>>>> +       a2dp_out = calloc(1, sizeof(struct a2dp_stream_out));
>>>> +       if (!a2dp_out)
>>>>                 return -ENOMEM;
>>>>
>>>>         DBG("");
>>>>
>>>> -       out->common.get_sample_rate = out_get_sample_rate;
>>>> -       out->common.set_sample_rate = out_set_sample_rate;
>>>> -       out->common.get_buffer_size = out_get_buffer_size;
>>>> -       out->common.get_channels = out_get_channels;
>>>> -       out->common.get_format = out_get_format;
>>>> -       out->common.set_format = out_set_format;
>>>> -       out->common.standby = out_standby;
>>>> -       out->common.dump = out_dump;
>>>> -       out->common.set_parameters = out_set_parameters;
>>>> -       out->common.get_parameters = out_get_parameters;
>>>> -       out->common.add_audio_effect = out_add_audio_effect;
>>>> -       out->common.remove_audio_effect = out_remove_audio_effect;
>>>> -       out->get_latency = out_get_latency;
>>>> -       out->set_volume = out_set_volume;
>>>> -       out->write = out_write;
>>>> -       out->get_render_position = out_get_render_position;
>>>> +       a2dp_out->stream.common.get_sample_rate = out_get_sample_rate;
>>>> +       a2dp_out->stream.common.set_sample_rate = out_set_sample_rate;
>>>> +       a2dp_out->stream.common.get_buffer_size = out_get_buffer_size;
>>>> +       a2dp_out->stream.common.get_channels = out_get_channels;
>>>> +       a2dp_out->stream.common.get_format = out_get_format;
>>>> +       a2dp_out->stream.common.set_format = out_set_format;
>>>> +       a2dp_out->stream.common.standby = out_standby;
>>>> +       a2dp_out->stream.common.dump = out_dump;
>>>> +       a2dp_out->stream.common.set_parameters = out_set_parameters;
>>>> +       a2dp_out->stream.common.get_parameters = out_get_parameters;
>>>> +       a2dp_out->stream.common.add_audio_effect = out_add_audio_effect;
>>>> +       a2dp_out->stream.common.remove_audio_effect = out_remove_audio_effect;
>>>> +       a2dp_out->stream.get_latency = out_get_latency;
>>>> +       a2dp_out->stream.set_volume = out_set_volume;
>>>> +       a2dp_out->stream.write = out_write;
>>>> +       a2dp_out->stream.get_render_position = out_get_render_position;
>>>>
>>>> -       *stream_out = out;
>>>> +       *stream_out = &a2dp_out->stream;
>>>> +       a2dp_dev->stream_out = a2dp_out;
>>>>
>>>>         return 0;
>>>>  }
>>>> @@ -381,7 +392,7 @@ static int audio_close(hw_device_t *device)
>>>>  static int audio_open(const hw_module_t *module, const char *name,
>>>>                                                         hw_device_t **device)
>>>>  {
>>>> -       struct audio_hw_device *audio;
>>>> +       struct a2dp_audio_dev *a2dp_dev;
>>>>
>>>>         DBG("");
>>>>
>>>> @@ -391,30 +402,30 @@ static int audio_open(const hw_module_t *module, const char *name,
>>>>                 return -EINVAL;
>>>>         }
>>>>
>>>> -       audio = calloc(1, sizeof(struct audio_hw_device));
>>>> -       if (!audio)
>>>> +       a2dp_dev = calloc(1, sizeof(struct a2dp_audio_dev));
>>>> +       if (!a2dp_dev)
>>>>                 return -ENOMEM;
>>>>
>>>> -       audio->common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
>>>> -       audio->common.module = (struct hw_module_t *) module;
>>>> -       audio->common.close = audio_close;
>>>> -
>>>> -       audio->init_check = audio_init_check;
>>>> -       audio->set_voice_volume = audio_set_voice_volume;
>>>> -       audio->set_master_volume = audio_set_master_volume;
>>>> -       audio->set_mode = audio_set_mode;
>>>> -       audio->set_mic_mute = audio_set_mic_mute;
>>>> -       audio->get_mic_mute = audio_get_mic_mute;
>>>> -       audio->set_parameters = audio_set_parameters;
>>>> -       audio->get_parameters = audio_get_parameters;
>>>> -       audio->get_input_buffer_size = audio_get_input_buffer_size;
>>>> -       audio->open_output_stream = audio_open_output_stream;
>>>> -       audio->close_output_stream = audio_close_output_stream;
>>>> -       audio->open_input_stream = audio_open_input_stream;
>>>> -       audio->close_input_stream = audio_close_input_stream;
>>>> -       audio->dump = audio_dump;
>>>> -
>>>> -       *device = &audio->common;
>>>> +       a2dp_dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
>>>> +       a2dp_dev->dev.common.module = (struct hw_module_t *) module;
>>>> +       a2dp_dev->dev.common.close = audio_close;
>>>> +
>>>> +       a2dp_dev->dev.init_check = audio_init_check;
>>>> +       a2dp_dev->dev.set_voice_volume = audio_set_voice_volume;
>>>> +       a2dp_dev->dev.set_master_volume = audio_set_master_volume;
>>>> +       a2dp_dev->dev.set_mode = audio_set_mode;
>>>> +       a2dp_dev->dev.set_mic_mute = audio_set_mic_mute;
>>>> +       a2dp_dev->dev.get_mic_mute = audio_get_mic_mute;
>>>> +       a2dp_dev->dev.set_parameters = audio_set_parameters;
>>>> +       a2dp_dev->dev.get_parameters = audio_get_parameters;
>>>> +       a2dp_dev->dev.get_input_buffer_size = audio_get_input_buffer_size;
>>>> +       a2dp_dev->dev.open_output_stream = audio_open_output_stream;
>>>> +       a2dp_dev->dev.close_output_stream = audio_close_output_stream;
>>>> +       a2dp_dev->dev.open_input_stream = audio_open_input_stream;
>>>> +       a2dp_dev->dev.close_input_stream = audio_close_input_stream;
>>>> +       a2dp_dev->dev.dump = audio_dump;
>>>> +
>>>> +       *device = &a2dp_dev->dev.common;
>>>>
>>>>         return 0;
>>>>  }
>>>> --
>>>> 1.8.4
>>>
>>> How is this supposed to be accessed, are you planning on adding a
>>> global variable?
>>>
>>>
>> Have a look on audio_open_output_stream(). This is actually how they did so
>> far and it is fine for me.
>> I want to avoid globals here.
>
> I can see that you allocate a different struct for a2dp_dev and
> a2dp_out, but then you return a member of the struct back to HAL but
> after that you wont access these structs anymore and the HAL only uses
> the members that you return so you probably gonna need to lookup for
> these structs to access any auxiliary data that we might want to add,
> also these structs never seems to be freed which I suppose can
> generate memory leaks.
>
>
You right I missed free of a2dp_out
It will be done in  audio_close_output_stream(). Access to that will
be similar as to a2dp_dev() in audio_close()

> --
> Luiz Augusto von Dentz

^ permalink raw reply

* [RFC BlueZ 9/9] android: Add hal_audio_ipc_init and hal_audio_ipc_cleanup
From: Luiz Augusto von Dentz @ 2013-12-30 12:34 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1388406855-8809-1-git-send-email-luiz.dentz@gmail.com>

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

These function initialize the audio IPC in the HAL side.
---
 android/hal-audio.c |  9 +++++++++
 android/hal-ipc.c   | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 android/hal-ipc.h   |  3 +++
 3 files changed, 68 insertions(+)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 7f4a3f2..2e7dcca 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -24,6 +24,7 @@
 #include <hardware/hardware.h>
 
 #include "hal-log.h"
+#include "hal-ipc.h"
 
 static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
 								size_t bytes)
@@ -374,6 +375,9 @@ static int audio_dump(const audio_hw_device_t *device, int fd)
 static int audio_close(hw_device_t *device)
 {
 	DBG("");
+
+	hal_audio_ipc_cleanup();
+
 	free(device);
 	return 0;
 }
@@ -391,6 +395,11 @@ static int audio_open(const hw_module_t *module, const char *name,
 		return -EINVAL;
 	}
 
+	if (!hal_audio_ipc_init()) {
+		error("Unable to initialize audio IPC");
+		return -ENOTCONN;
+	}
+
 	audio = calloc(1, sizeof(struct audio_hw_device));
 	if (!audio)
 		return -ENOMEM;
diff --git a/android/hal-ipc.c b/android/hal-ipc.c
index b19704a..3a1fcc7 100644
--- a/android/hal-ipc.c
+++ b/android/hal-ipc.c
@@ -38,6 +38,7 @@
 
 static int cmd_sk = -1;
 static int notif_sk = -1;
+static int audio_sk = -1;
 
 static pthread_mutex_t cmd_sk_mutex = PTHREAD_MUTEX_INITIALIZER;
 
@@ -449,3 +450,58 @@ int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
 
 	return BT_STATUS_SUCCESS;
 }
+
+bool hal_audio_ipc_init(void)
+{
+	struct sockaddr_un addr;
+	int sk;
+	int err;
+
+	sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
+	if (sk < 0) {
+		err = errno;
+		error("Failed to create socket: %d (%s)", err,
+							strerror(err));
+		return false;
+	}
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	memcpy(addr.sun_path, BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH));
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		err = errno;
+		error("Failed to bind socket: %d (%s)", err, strerror(err));
+		close(sk);
+		return false;
+	}
+
+	if (listen(sk, 2) < 0) {
+		err = errno;
+		error("Failed to listen on socket: %d (%s)", err,
+								strerror(err));
+		close(sk);
+		return false;
+	}
+
+	audio_sk = accept_connection(sk);
+	if (audio_sk < 0) {
+		close(sk);
+		return false;
+	}
+
+	info("audio connected");
+
+	close(sk);
+
+	return true;
+}
+
+void hal_audio_ipc_cleanup(void)
+{
+	close(audio_sk);
+	audio_sk = -1;
+
+	shutdown(audio_sk, SHUT_RD);
+}
diff --git a/android/hal-ipc.h b/android/hal-ipc.h
index 2fbf30f..bd1682d 100644
--- a/android/hal-ipc.h
+++ b/android/hal-ipc.h
@@ -30,3 +30,6 @@ int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
 void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers,
 								uint8_t size);
 void hal_ipc_unregister(uint8_t service);
+
+bool hal_audio_ipc_init(void);
+void hal_audio_ipc_cleanup(void);
-- 
1.8.4.2


^ permalink raw reply related

* [RFC BlueZ 8/9] android: Add stream suspend command/response struct
From: Luiz Augusto von Dentz @ 2013-12-30 12:34 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1388406855-8809-1-git-send-email-luiz.dentz@gmail.com>

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

This adds the definitions to stream suspend command and response.
---
 android/a2dp.c    | 10 ++++++++++
 android/hal-msg.h |  5 +++++
 2 files changed, 15 insertions(+)

diff --git a/android/a2dp.c b/android/a2dp.c
index 284d44d..9dbb470 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -390,6 +390,14 @@ static void bt_stream_resume(const void *buf, uint16_t len)
 							HAL_STATUS_FAILED);
 }
 
+static void bt_stream_suspend(const void *buf, uint16_t len)
+{
+	DBG("Not Implemented");
+
+	ipc_send_rsp(HAL_SERVICE_ID_AUDIO, AUDIO_OP_SUSPEND_STREAM,
+							HAL_STATUS_FAILED);
+}
+
 static const struct ipc_handler audio_handlers[] = {
 	/* AUDIO_OP_OPEN */
 	{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
@@ -401,6 +409,8 @@ static const struct ipc_handler audio_handlers[] = {
 	{ bt_stream_close, false, sizeof(struct audio_cmd_close_stream) },
 	/* AUDIO_OP_RESUME_STREAM */
 	{ bt_stream_resume, false, sizeof(struct audio_cmd_resume_stream) },
+	/* AUDIO_OP_SUSPEND_STREAM */
+	{ bt_stream_suspend, false, sizeof(struct audio_cmd_suspend_stream) },
 };
 
 bool bt_a2dp_register(const bdaddr_t *addr)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index fe433d1..ef40dac 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -610,3 +610,8 @@ struct audio_cmd_close_stream {
 struct audio_cmd_resume_stream {
 	uint8_t id;
 } __attribute__((packed));
+
+#define AUDIO_OP_SUSPEND_STREAM			0x06
+struct audio_cmd_suspend_stream {
+	uint8_t id;
+} __attribute__((packed));
-- 
1.8.4.2


^ permalink raw reply related

* [RFC BlueZ 7/9] android: Add stream resume command/response struct
From: Luiz Augusto von Dentz @ 2013-12-30 12:34 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1388406855-8809-1-git-send-email-luiz.dentz@gmail.com>

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

This adds the definitions to stream resume command and response.
---
 android/a2dp.c    | 10 ++++++++++
 android/hal-msg.h |  5 +++++
 2 files changed, 15 insertions(+)

diff --git a/android/a2dp.c b/android/a2dp.c
index 2dba482..284d44d 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -382,6 +382,14 @@ static void bt_stream_close(const void *buf, uint16_t len)
 							HAL_STATUS_FAILED);
 }
 
+static void bt_stream_resume(const void *buf, uint16_t len)
+{
+	DBG("Not Implemented");
+
+	ipc_send_rsp(HAL_SERVICE_ID_AUDIO, AUDIO_OP_RESUME_STREAM,
+							HAL_STATUS_FAILED);
+}
+
 static const struct ipc_handler audio_handlers[] = {
 	/* AUDIO_OP_OPEN */
 	{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
@@ -391,6 +399,8 @@ static const struct ipc_handler audio_handlers[] = {
 	{ bt_stream_open, false, sizeof(struct audio_cmd_open_stream) },
 	/* AUDIO_OP_CLOSE_STREAM */
 	{ bt_stream_close, false, sizeof(struct audio_cmd_close_stream) },
+	/* AUDIO_OP_RESUME_STREAM */
+	{ bt_stream_resume, false, sizeof(struct audio_cmd_resume_stream) },
 };
 
 bool bt_a2dp_register(const bdaddr_t *addr)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 2816f6f..fe433d1 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -605,3 +605,8 @@ struct audio_rsp_open_stream {
 struct audio_cmd_close_stream {
 	uint8_t id;
 } __attribute__((packed));
+
+#define AUDIO_OP_RESUME_STREAM			0x05
+struct audio_cmd_resume_stream {
+	uint8_t id;
+} __attribute__((packed));
-- 
1.8.4.2


^ permalink raw reply related

* [RFC BlueZ 6/9] android: Add stream close command/response struct
From: Luiz Augusto von Dentz @ 2013-12-30 12:34 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1388406855-8809-1-git-send-email-luiz.dentz@gmail.com>

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

This adds the definitions to stream close command and response.
---
 android/a2dp.c    | 10 ++++++++++
 android/hal-msg.h |  5 +++++
 2 files changed, 15 insertions(+)

diff --git a/android/a2dp.c b/android/a2dp.c
index 104b950..2dba482 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -374,6 +374,14 @@ static void bt_stream_open(const void *buf, uint16_t len)
 							HAL_STATUS_FAILED);
 }
 
+static void bt_stream_close(const void *buf, uint16_t len)
+{
+	DBG("Not Implemented");
+
+	ipc_send_rsp(HAL_SERVICE_ID_AUDIO, AUDIO_OP_CLOSE_STREAM,
+							HAL_STATUS_FAILED);
+}
+
 static const struct ipc_handler audio_handlers[] = {
 	/* AUDIO_OP_OPEN */
 	{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
@@ -381,6 +389,8 @@ static const struct ipc_handler audio_handlers[] = {
 	{ bt_audio_close, false, sizeof(struct audio_cmd_close) },
 	/* AUDIO_OP_OPEN_STREAM */
 	{ bt_stream_open, false, sizeof(struct audio_cmd_open_stream) },
+	/* AUDIO_OP_CLOSE_STREAM */
+	{ bt_stream_close, false, sizeof(struct audio_cmd_close_stream) },
 };
 
 bool bt_a2dp_register(const bdaddr_t *addr)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 62674fa..2816f6f 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -600,3 +600,8 @@ struct audio_rsp_open_stream {
 	uint8_t len;
 	uint8_t data[0];
 } __attribute__((packed));
+
+#define AUDIO_OP_CLOSE_STREAM			0x04
+struct audio_cmd_close_stream {
+	uint8_t id;
+} __attribute__((packed));
-- 
1.8.4.2


^ permalink raw reply related

* [RFC BlueZ 5/9] android: Add stream open command/response struct
From: Luiz Augusto von Dentz @ 2013-12-30 12:34 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1388406855-8809-1-git-send-email-luiz.dentz@gmail.com>

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

This adds the definitions to stream open command and response.
---
 android/a2dp.c    | 10 ++++++++++
 android/hal-msg.h | 10 ++++++++++
 2 files changed, 20 insertions(+)

diff --git a/android/a2dp.c b/android/a2dp.c
index 43590f0..104b950 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -366,11 +366,21 @@ static void bt_audio_close(const void *buf, uint16_t len)
 	ipc_send_rsp(HAL_SERVICE_ID_AUDIO, AUDIO_OP_CLOSE, HAL_STATUS_FAILED);
 }
 
+static void bt_stream_open(const void *buf, uint16_t len)
+{
+	DBG("Not Implemented");
+
+	ipc_send_rsp(HAL_SERVICE_ID_AUDIO, AUDIO_OP_OPEN_STREAM,
+							HAL_STATUS_FAILED);
+}
+
 static const struct ipc_handler audio_handlers[] = {
 	/* AUDIO_OP_OPEN */
 	{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
 	/* AUDIO_OP_CLOSE */
 	{ bt_audio_close, false, sizeof(struct audio_cmd_close) },
+	/* AUDIO_OP_OPEN_STREAM */
+	{ bt_stream_open, false, sizeof(struct audio_cmd_open_stream) },
 };
 
 bool bt_a2dp_register(const bdaddr_t *addr)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index f359952..62674fa 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -590,3 +590,13 @@ struct audio_rsp_open {
 struct audio_cmd_close {
 	uint8_t id;
 } __attribute__((packed));
+
+#define AUDIO_OP_OPEN_STREAM			0x03
+struct audio_cmd_open_stream {
+	uint8_t id;
+} __attribute__((packed));
+
+struct audio_rsp_open_stream {
+	uint8_t len;
+	uint8_t data[0];
+} __attribute__((packed));
-- 
1.8.4.2


^ permalink raw reply related

* [RFC BlueZ 4/9] android: Add audio close command/response struct
From: Luiz Augusto von Dentz @ 2013-12-30 12:34 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1388406855-8809-1-git-send-email-luiz.dentz@gmail.com>

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

This adds the definitions to audio close command and response.
---
 android/a2dp.c    | 9 +++++++++
 android/hal-msg.h | 5 +++++
 2 files changed, 14 insertions(+)

diff --git a/android/a2dp.c b/android/a2dp.c
index 5cb01f7..43590f0 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -359,9 +359,18 @@ static void bt_audio_open(const void *buf, uint16_t len)
 	ipc_send_rsp(HAL_SERVICE_ID_AUDIO, AUDIO_OP_OPEN, HAL_STATUS_FAILED);
 }
 
+static void bt_audio_close(const void *buf, uint16_t len)
+{
+	DBG("Not Implemented");
+
+	ipc_send_rsp(HAL_SERVICE_ID_AUDIO, AUDIO_OP_CLOSE, HAL_STATUS_FAILED);
+}
+
 static const struct ipc_handler audio_handlers[] = {
 	/* AUDIO_OP_OPEN */
 	{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
+	/* AUDIO_OP_CLOSE */
+	{ bt_audio_close, false, sizeof(struct audio_cmd_close) },
 };
 
 bool bt_a2dp_register(const bdaddr_t *addr)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 4b52e5e..f359952 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -585,3 +585,8 @@ struct audio_preset {
 struct audio_rsp_open {
 	uint8_t id;
 } __attribute__((packed));
+
+#define AUDIO_OP_CLOSE				0x02
+struct audio_cmd_close {
+	uint8_t id;
+} __attribute__((packed));
-- 
1.8.4.2


^ permalink raw reply related

* [RFC BlueZ 3/9] android: Add audio open command/response struct
From: Luiz Augusto von Dentz @ 2013-12-30 12:34 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1388406855-8809-1-git-send-email-luiz.dentz@gmail.com>

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

This adds the definitions to audio open command and response.
---
 android/a2dp.c            |  9 +++++++++
 android/audio-ipc-api.txt |  2 +-
 android/hal-msg.h         | 18 ++++++++++++++++++
 android/ipc.c             |  5 ++++-
 4 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/android/a2dp.c b/android/a2dp.c
index 63f1f58..5cb01f7 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -352,7 +352,16 @@ static sdp_record_t *a2dp_record(void)
 	return record;
 }
 
+static void bt_audio_open(const void *buf, uint16_t len)
+{
+	DBG("Not Implemented");
+
+	ipc_send_rsp(HAL_SERVICE_ID_AUDIO, AUDIO_OP_OPEN, HAL_STATUS_FAILED);
+}
+
 static const struct ipc_handler audio_handlers[] = {
+	/* AUDIO_OP_OPEN */
+	{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
 };
 
 bool bt_a2dp_register(const bdaddr_t *addr)
diff --git a/android/audio-ipc-api.txt b/android/audio-ipc-api.txt
index 1c42800..37a1569 100644
--- a/android/audio-ipc-api.txt
+++ b/android/audio-ipc-api.txt
@@ -49,9 +49,9 @@ Identifier: "audio" (BT_AUDIO_ID)
 
 		Command parameters: Service UUID (16 octets)
 				    Codec ID (1 octet)
+				    Number of codec presets (1 octet)
 				    Codec capabilities length (1 octet)
 				    Codec capabilities (variable)
-				    Number of codec presets (1 octet)
 				    Codec preset # length (1 octet)
 				    Codec preset # configuration (variable)
 				    ...
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 1afb1bc..4b52e5e 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -567,3 +567,21 @@ struct hal_ev_a2dp_audio_state {
 	uint8_t state;
 	uint8_t bdaddr[6];
 } __attribute__((packed));
+
+#define AUDIO_OP_OPEN				0x01
+struct audio_cmd_open {
+	uint16_t uuid;
+	uint8_t codec;
+	uint8_t presets;
+	uint8_t len;
+	uint8_t data[0];
+} __attribute__((packed));
+
+struct audio_preset {
+	uint8_t len;
+	uint8_t data[0];
+} __attribute__((packed));
+
+struct audio_rsp_open {
+	uint8_t id;
+} __attribute__((packed));
diff --git a/android/ipc.c b/android/ipc.c
index 6cdbf60..bb16553 100644
--- a/android/ipc.c
+++ b/android/ipc.c
@@ -301,7 +301,10 @@ void ipc_send_rsp(uint8_t service_id, uint8_t opcode, uint8_t status)
 	struct hal_status s;
 	int sk;
 
-	sk = g_io_channel_unix_get_fd(cmd_io);
+	if (service_id == HAL_SERVICE_ID_AUDIO)
+		sk = g_io_channel_unix_get_fd(audio_io);
+	else
+		sk = g_io_channel_unix_get_fd(cmd_io);
 
 	if (status == HAL_STATUS_SUCCESS) {
 		ipc_send(sk, service_id, opcode, 0, NULL, -1);
-- 
1.8.4.2


^ permalink raw reply related

* [RFC BlueZ 2/9] android/A2DP: Add initial code to handle audio IPC commands
From: Luiz Augusto von Dentz @ 2013-12-30 12:34 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1388406855-8809-1-git-send-email-luiz.dentz@gmail.com>

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

This adds initial code to handle audio IPC commands and defines a new
service id (10).
---
 android/a2dp.c    | 13 ++++++++++++-
 android/hal-msg.h |  3 ++-
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/android/a2dp.c b/android/a2dp.c
index 581d094..63f1f58 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -352,6 +352,9 @@ static sdp_record_t *a2dp_record(void)
 	return record;
 }
 
+static const struct ipc_handler audio_handlers[] = {
+};
+
 bool bt_a2dp_register(const bdaddr_t *addr)
 {
 	GError *err = NULL;
@@ -359,6 +362,8 @@ bool bt_a2dp_register(const bdaddr_t *addr)
 
 	DBG("");
 
+	audio_ipc_init();
+
 	bacpy(&adapter_addr, addr);
 
 	server = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
@@ -388,6 +393,9 @@ bool bt_a2dp_register(const bdaddr_t *addr)
 	ipc_register(HAL_SERVICE_ID_A2DP, cmd_handlers,
 						G_N_ELEMENTS(cmd_handlers));
 
+	ipc_register(HAL_SERVICE_ID_AUDIO, audio_handlers,
+						G_N_ELEMENTS(audio_handlers));
+
 	return true;
 
 fail:
@@ -411,8 +419,9 @@ void bt_a2dp_unregister(void)
 	g_slist_foreach(devices, a2dp_device_disconnected, NULL);
 	devices = NULL;
 
-
 	ipc_unregister(HAL_SERVICE_ID_A2DP);
+	ipc_unregister(HAL_SERVICE_ID_AUDIO);
+
 	bt_adapter_remove_record(record_id);
 	record_id = 0;
 
@@ -421,4 +430,6 @@ void bt_a2dp_unregister(void)
 		g_io_channel_unref(server);
 		server = NULL;
 	}
+
+	audio_ipc_cleanup();
 }
diff --git a/android/hal-msg.h b/android/hal-msg.h
index b14eced..1afb1bc 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -45,8 +45,9 @@ struct hal_hdr {
 #define HAL_SERVICE_ID_HEALTH		7
 #define HAL_SERVICE_ID_AVRCP		8
 #define HAL_SERVICE_ID_GATT		9
+#define HAL_SERVICE_ID_AUDIO		10
 
-#define HAL_SERVICE_ID_MAX HAL_SERVICE_ID_GATT
+#define HAL_SERVICE_ID_MAX HAL_SERVICE_ID_AUDIO
 
 /* Core Service */
 
-- 
1.8.4.2


^ permalink raw reply related

* [RFC BlueZ 1/9] android: Add initial code for audio IPC
From: Luiz Augusto von Dentz @ 2013-12-30 12:34 UTC (permalink / raw)
  To: linux-bluetooth

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

This add initial code for listen and accept connections on the abstract
socket defined for the audio IPC.
---
 android/hal-msg.h |  1 +
 android/ipc.c     | 53 +++++++++++++++++++++++++++++++++++++++++++++++------
 android/ipc.h     |  3 +++
 3 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/android/hal-msg.h b/android/hal-msg.h
index c351501..b14eced 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -24,6 +24,7 @@
 #define BLUEZ_HAL_MTU 1024
 
 static const char BLUEZ_HAL_SK_PATH[] = "\0bluez_hal_socket";
+static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
 
 struct hal_hdr {
 	uint8_t  service_id;
diff --git a/android/ipc.c b/android/ipc.c
index 9e8ccc3..6cdbf60 100644
--- a/android/ipc.c
+++ b/android/ipc.c
@@ -49,6 +49,7 @@ static struct service_handler services[HAL_SERVICE_ID_MAX + 1];
 
 static GIOChannel *cmd_io = NULL;
 static GIOChannel *notif_io = NULL;
+static GIOChannel *audio_io = NULL;
 
 static void ipc_handle_msg(const void *buf, ssize_t len)
 {
@@ -145,7 +146,8 @@ static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond,
 	return FALSE;
 }
 
-static GIOChannel *connect_hal(GIOFunc connect_cb)
+static GIOChannel *connect_hal(const char *path, size_t size,
+							GIOFunc connect_cb)
 {
 	struct sockaddr_un addr;
 	GIOCondition cond;
@@ -167,11 +169,11 @@ static GIOChannel *connect_hal(GIOFunc connect_cb)
 	memset(&addr, 0, sizeof(addr));
 	addr.sun_family = AF_UNIX;
 
-	memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
+	memcpy(addr.sun_path, path, size);
 
 	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		error("IPC: failed to connect HAL socket: %d (%s)", errno,
-							strerror(errno));
+		error("IPC: failed to connect HAL socket %s: %d (%s)", &path[1],
+							errno, strerror(errno));
 		g_io_channel_unref(io);
 		return NULL;
 	}
@@ -218,7 +220,8 @@ static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond,
 		return FALSE;
 	}
 
-	notif_io = connect_hal(notif_connect_cb);
+	notif_io = connect_hal(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH),
+							notif_connect_cb);
 	if (!notif_io)
 		raise(SIGTERM);
 
@@ -227,7 +230,8 @@ static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond,
 
 void ipc_init(void)
 {
-	cmd_io = connect_hal(cmd_connect_cb);
+	cmd_io = connect_hal(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH),
+							cmd_connect_cb);
 	if (!cmd_io)
 		raise(SIGTERM);
 }
@@ -338,3 +342,40 @@ void ipc_unregister(uint8_t service)
 	services[service].handler = NULL;
 	services[service].size = 0;
 }
+
+static gboolean audio_connect_cb(GIOChannel *io, GIOCondition cond,
+							gpointer user_data)
+{
+	DBG("");
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		error("Audio IPC: socket connect failed, terminating");
+		audio_ipc_cleanup();
+		return FALSE;
+	}
+
+	cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	g_io_add_watch(audio_io, cond, cmd_watch_cb, NULL);
+
+	info("Audio IPC: successfully connected");
+
+	return FALSE;
+}
+
+void audio_ipc_init(void)
+{
+	audio_io = connect_hal(BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH),
+							audio_connect_cb);
+	if (!cmd_io)
+		raise(SIGTERM);
+}
+
+void audio_ipc_cleanup(void)
+{
+	if (audio_io) {
+		g_io_channel_shutdown(audio_io, TRUE, NULL);
+		g_io_channel_unref(audio_io);
+		audio_io = NULL;
+	}
+}
diff --git a/android/ipc.h b/android/ipc.h
index 6cd102b..8e92811 100644
--- a/android/ipc.h
+++ b/android/ipc.h
@@ -37,3 +37,6 @@ void ipc_send_notif(uint8_t service_id, uint8_t opcode,  uint16_t len,
 void ipc_register(uint8_t service, const struct ipc_handler *handlers,
 								uint8_t size);
 void ipc_unregister(uint8_t service);
+
+void audio_ipc_init(void);
+void audio_ipc_cleanup(void);
-- 
1.8.4.2


^ permalink raw reply related

* Re: [RFC 0/6] Audio HAL plugin concept
From: Luiz Augusto von Dentz @ 2013-12-30 12:29 UTC (permalink / raw)
  To: Lukasz Rymanowski; +Cc: linux-bluetooth@vger.kernel.org, Johan Hedberg
In-Reply-To: <1388398647-25420-1-git-send-email-lukasz.rymanowski@tieto.com>

Hi Lukasz,

On Mon, Dec 30, 2013 at 12:17 PM, Lukasz Rymanowski
<lukasz.rymanowski@tieto.com> wrote:
> 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

Patches 1--4 are now upstream, thanks. For patches 5 and 6 lets
continue to discuss what is the best way to do the socket handling.


-- 
Luiz Augusto von Dentz

^ permalink raw reply

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

Hi!

> > > @@ -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.
> > 
> 
> I sent him an email with question. I guess we should move Ville to
> CREDITS?

You just won the bet :-).

<ville.tervo@nokia.com>: host mx1.nokia.com[147.243.142.136] said: 550
5.1.1
    <ville.tervo@nokia.com>... User unknown (in reply to RCPT TO
    command)

(I'd bet that way, too, but I thought it should be checked).

Anyway, time to catch some sunlight while it lasts...

									Pavel


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

^ permalink raw reply

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

Hi!

> > --- 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.

And here's patch for that. IMO it does not look too bad... Let me know
what you think,
								Pavel

commit 6a11376e228c2a10821872fe0fb7dd0f0a759b05
Author: Pavel <pavel@ucw.cz>
Date:   Mon Dec 30 13:21:03 2013 +0100

    Avoid using subdirectory for hci_h4p driver, as Marcel requested.
    
    Signed-off-by: Pavel Machek <pavel@ucw.cz>

diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index a5e6e19..c286dbe 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -31,4 +31,7 @@ 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/
+obj-$(CONFIG_BT_NOKIA_H4P)		+= hci_h4p.o
+hci_h4p-objs := nokia_core.o nokia_fw.o nokia_uart.o nokia_fw-csr.o \
+		nokia_fw-bcm.o nokia_fw-ti1273.o
+
diff --git a/drivers/bluetooth/hci_h4p.h b/drivers/bluetooth/hci_h4p.h
new file mode 100644
index 0000000..d1d313b
--- /dev/null
+++ b/drivers/bluetooth/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/Makefile b/drivers/bluetooth/hci_h4p/Makefile
deleted file mode 100644
index 6f2ef85..0000000
--- a/drivers/bluetooth/hci_h4p/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-#
-# Makefile for the Linux Bluetooth HCI device drivers.
-#
-
-obj-$(CONFIG_BT_NOKIA_H4P)		+= 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
deleted file mode 100644
index a91bd7b..0000000
--- a/drivers/bluetooth/hci_h4p/core.c
+++ /dev/null
@@ -1,1361 +0,0 @@
-/*
- * 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))) {
-		switch (bt_cb(skb)->pkt_type) {
-		case H4_NEG_PKT:
-			hci_h4p_negotiation_packet(info, skb);
-			info->rx_state = WAIT_FOR_PKT_TYPE;
-			return;
-		case H4_ALIVE_PKT:
-			hci_h4p_alive_packet(info, skb);
-			info->rx_state = WAIT_FOR_PKT_TYPE;
-			return;
-		}
-
-		if (!test_bit(HCI_UP, &info->hdev->flags)) {
-			NBT_DBG("fw_event\n");
-			hci_h4p_parse_fw_event(info, skb);
-			return;
-		}
-	}
-
-	hci_recv_frame(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->rx_skb->dev = (void *)info->hdev;
-		}
-		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;
-	}
-
-	INIT_COMPLETION(info->test_completion);
-	gpio_set_value(info->reset_gpio, 1);
-
-	if (!wait_for_completion_interruptible_timeout(&info->test_completion,
-						       msecs_to_jiffies(100))) {
-		dev_err(info->dev, "wakeup test timed out\n");
-		complete_all(&info->test_completion);
-		return -EPROTO;
-	}
-
-	err = hci_h4p_wait_for_cts(info, 1, 100);
-	if (err < 0) {
-		dev_err(info->dev, "No cts from bt chip\n");
-		return err;
-	}
-
-	hci_h4p_set_rts(info, 1);
-
-	return 0;
-}
-
-/* hci callback functions */
-static int hci_h4p_hci_flush(struct hci_dev *hdev)
-{
-	struct hci_h4p_info *info = hci_get_drvdata(hdev);
-	skb_queue_purge(&info->txq);
-
-	return 0;
-}
-
-static int hci_h4p_bt_wakeup_test(struct hci_h4p_info *info)
-{
-	/*
-	 * Test Sequence:
-	 * Host de-asserts the BT_WAKE_UP line.
-	 * Host polls the UART_CTS line, waiting for it to be de-asserted.
-	 * Host asserts the BT_WAKE_UP line.
-	 * Host polls the UART_CTS line, waiting for it to be asserted.
-	 * Host de-asserts the BT_WAKE_UP line (allow the Bluetooth device to
-	 * sleep).
-	 * Host polls the UART_CTS line, waiting for it to be de-asserted.
-	 */
-	int err;
-	int ret = -ECOMM;
-
-	if (!info)
-		return -EINVAL;
-
-	/* Disable wakeup interrupts */
-	disable_irq(gpio_to_irq(info->host_wakeup_gpio));
-
-	gpio_set_value(info->bt_wakeup_gpio, 0);
-	err = hci_h4p_wait_for_cts(info, 0, 100);
-	if (err) {
-		dev_warn(info->dev, "bt_wakeup_test: fail: "
-			 "CTS low timed out: %d\n", err);
-		goto out;
-	}
-
-	gpio_set_value(info->bt_wakeup_gpio, 1);
-	err = hci_h4p_wait_for_cts(info, 1, 100);
-	if (err) {
-		dev_warn(info->dev, "bt_wakeup_test: fail: "
-			 "CTS high timed out: %d\n", err);
-		goto out;
-	}
-
-	gpio_set_value(info->bt_wakeup_gpio, 0);
-	err = hci_h4p_wait_for_cts(info, 0, 100);
-	if (err) {
-		dev_warn(info->dev, "bt_wakeup_test: fail: "
-			 "CTS re-low timed out: %d\n", err);
-		goto out;
-	}
-
-	ret = 0;
-
-out:
-
-	/* Re-enable wakeup interrupts */
-	enable_irq(gpio_to_irq(info->host_wakeup_gpio));
-
-	return ret;
-}
-
-static int hci_h4p_hci_open(struct hci_dev *hdev)
-{
-	struct hci_h4p_info *info;
-	int err, retries = 0;
-	struct sk_buff_head fw_queue;
-	unsigned long flags;
-
-	info = hci_get_drvdata(hdev);
-
-	if (test_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
-
-	/* TI1271 has HW bug and boot up might fail. Retry up to three times */
-again:
-
-	info->rx_enabled = 1;
-	info->rx_state = WAIT_FOR_PKT_TYPE;
-	info->rx_count = 0;
-	info->garbage_bytes = 0;
-	info->rx_skb = NULL;
-	info->pm_enabled = 0;
-	init_completion(&info->fw_completion);
-	hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
-	hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
-	skb_queue_head_init(&fw_queue);
-
-	err = hci_h4p_reset(info);
-	if (err < 0)
-		goto err_clean;
-
-	hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_CTS | UART_EFR_RTS);
-	info->autorts = 1;
-
-	err = hci_h4p_send_negotiation(info);
-
-	err = hci_h4p_read_fw(info, &fw_queue);
-	if (err < 0) {
-		dev_err(info->dev, "Cannot read firmware\n");
-		goto err_clean;
-	}
-
-	err = hci_h4p_send_fw(info, &fw_queue);
-	if (err < 0) {
-		dev_err(info->dev, "Sending firmware failed.\n");
-		goto err_clean;
-	}
-
-	info->pm_enabled = 1;
-
-	err = hci_h4p_bt_wakeup_test(info);
-	if (err < 0) {
-		dev_err(info->dev, "BT wakeup test failed.\n");
-		goto err_clean;
-	}
-
-	spin_lock_irqsave(&info->lock, flags);
-	info->rx_enabled = gpio_get_value(info->host_wakeup_gpio);
-	hci_h4p_set_clk(info, &info->rx_clocks_en, info->rx_enabled);
-	spin_unlock_irqrestore(&info->lock, flags);
-
-	hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
-
-	kfree_skb(info->alive_cmd_skb);
-	info->alive_cmd_skb = NULL;
-	set_bit(HCI_RUNNING, &hdev->flags);
-
-	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 sk_buff *skb)
-{
-	struct hci_h4p_info *info;
-	struct hci_dev *hdev = (struct hci_dev *)skb->dev;
-	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 int hci_h4p_hci_ioctl(struct hci_dev *hdev, unsigned int cmd,
-			     unsigned long arg)
-{
-	return -ENOIOCTLCMD;
-}
-
-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, "%pMR\n", info->bd_addr);
-}
-
-static DEVICE_ATTR(bdaddr, S_IRUGO | S_IWUSR, hci_h4p_show_bdaddr,
-		   hci_h4p_store_bdaddr);
-
-static int hci_h4p_sysfs_create_files(struct device *dev)
-{
-	return device_create_file(dev, &dev_attr_bdaddr);
-}
-
-static void hci_h4p_sysfs_remove_files(struct device *dev)
-{
-	device_remove_file(dev, &dev_attr_bdaddr);
-}
-
-static int hci_h4p_register_hdev(struct hci_h4p_info *info)
-{
-	struct hci_dev *hdev;
-
-	/* Initialize and register HCI device */
-
-	hdev = hci_alloc_dev();
-	if (!hdev) {
-		dev_err(info->dev, "Can't allocate memory for device\n");
-		return -ENOMEM;
-	}
-	info->hdev = hdev;
-
-	hdev->bus = HCI_UART;
-	hci_set_drvdata(hdev, info);
-
-	hdev->open = hci_h4p_hci_open;
-	hdev->close = hci_h4p_hci_close;
-	hdev->flush = hci_h4p_hci_flush;
-	hdev->send = hci_h4p_hci_send_frame;
-	hdev->ioctl = hci_h4p_hci_ioctl;
-	set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
-
-	SET_HCIDEV_DEV(hdev, info->dev);
-
-	if (hci_h4p_sysfs_create_files(info->dev) < 0) {
-		dev_err(info->dev, "failed to create sysfs files\n");
-		goto free;
-	}
-
-	if (hci_register_dev(hdev) >= 0)
-		return 0;
-
-	dev_err(info->dev, "hci_register failed %s.\n", hdev->name);
-	hci_h4p_sysfs_remove_files(info->dev);
-free:
-	hci_free_dev(info->hdev);
-	return -ENODEV;
-}
-
-static int hci_h4p_probe(struct platform_device *pdev)
-{
-	struct hci_h4p_platform_data *bt_plat_data;
-	struct hci_h4p_info *info;
-	int err;
-
-	dev_info(&pdev->dev, "Registering HCI H4P device\n");
-	info = 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
deleted file mode 100644
index 390d021..0000000
--- a/drivers/bluetooth/hci_h4p/fw-bcm.c
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * 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
deleted file mode 100644
index af880d9..0000000
--- a/drivers/bluetooth/hci_h4p/fw-csr.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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(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
deleted file mode 100644
index 32e5fa0..0000000
--- a/drivers/bluetooth/hci_h4p/fw-ti1273.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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
deleted file mode 100644
index b3d39f9..0000000
--- a/drivers/bluetooth/hci_h4p/fw.c
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * 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
deleted file mode 100644
index d1d313b..0000000
--- a/drivers/bluetooth/hci_h4p/hci_h4p.h
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 8e0a93c..0000000
--- a/drivers/bluetooth/hci_h4p/uart.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * 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/drivers/bluetooth/nokia_core.c b/drivers/bluetooth/nokia_core.c
new file mode 100644
index 0000000..a91bd7b
--- /dev/null
+++ b/drivers/bluetooth/nokia_core.c
@@ -0,0 +1,1361 @@
+/*
+ * 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))) {
+		switch (bt_cb(skb)->pkt_type) {
+		case H4_NEG_PKT:
+			hci_h4p_negotiation_packet(info, skb);
+			info->rx_state = WAIT_FOR_PKT_TYPE;
+			return;
+		case H4_ALIVE_PKT:
+			hci_h4p_alive_packet(info, skb);
+			info->rx_state = WAIT_FOR_PKT_TYPE;
+			return;
+		}
+
+		if (!test_bit(HCI_UP, &info->hdev->flags)) {
+			NBT_DBG("fw_event\n");
+			hci_h4p_parse_fw_event(info, skb);
+			return;
+		}
+	}
+
+	hci_recv_frame(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->rx_skb->dev = (void *)info->hdev;
+		}
+		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;
+	}
+
+	INIT_COMPLETION(info->test_completion);
+	gpio_set_value(info->reset_gpio, 1);
+
+	if (!wait_for_completion_interruptible_timeout(&info->test_completion,
+						       msecs_to_jiffies(100))) {
+		dev_err(info->dev, "wakeup test timed out\n");
+		complete_all(&info->test_completion);
+		return -EPROTO;
+	}
+
+	err = hci_h4p_wait_for_cts(info, 1, 100);
+	if (err < 0) {
+		dev_err(info->dev, "No cts from bt chip\n");
+		return err;
+	}
+
+	hci_h4p_set_rts(info, 1);
+
+	return 0;
+}
+
+/* hci callback functions */
+static int hci_h4p_hci_flush(struct hci_dev *hdev)
+{
+	struct hci_h4p_info *info = hci_get_drvdata(hdev);
+	skb_queue_purge(&info->txq);
+
+	return 0;
+}
+
+static int hci_h4p_bt_wakeup_test(struct hci_h4p_info *info)
+{
+	/*
+	 * Test Sequence:
+	 * Host de-asserts the BT_WAKE_UP line.
+	 * Host polls the UART_CTS line, waiting for it to be de-asserted.
+	 * Host asserts the BT_WAKE_UP line.
+	 * Host polls the UART_CTS line, waiting for it to be asserted.
+	 * Host de-asserts the BT_WAKE_UP line (allow the Bluetooth device to
+	 * sleep).
+	 * Host polls the UART_CTS line, waiting for it to be de-asserted.
+	 */
+	int err;
+	int ret = -ECOMM;
+
+	if (!info)
+		return -EINVAL;
+
+	/* Disable wakeup interrupts */
+	disable_irq(gpio_to_irq(info->host_wakeup_gpio));
+
+	gpio_set_value(info->bt_wakeup_gpio, 0);
+	err = hci_h4p_wait_for_cts(info, 0, 100);
+	if (err) {
+		dev_warn(info->dev, "bt_wakeup_test: fail: "
+			 "CTS low timed out: %d\n", err);
+		goto out;
+	}
+
+	gpio_set_value(info->bt_wakeup_gpio, 1);
+	err = hci_h4p_wait_for_cts(info, 1, 100);
+	if (err) {
+		dev_warn(info->dev, "bt_wakeup_test: fail: "
+			 "CTS high timed out: %d\n", err);
+		goto out;
+	}
+
+	gpio_set_value(info->bt_wakeup_gpio, 0);
+	err = hci_h4p_wait_for_cts(info, 0, 100);
+	if (err) {
+		dev_warn(info->dev, "bt_wakeup_test: fail: "
+			 "CTS re-low timed out: %d\n", err);
+		goto out;
+	}
+
+	ret = 0;
+
+out:
+
+	/* Re-enable wakeup interrupts */
+	enable_irq(gpio_to_irq(info->host_wakeup_gpio));
+
+	return ret;
+}
+
+static int hci_h4p_hci_open(struct hci_dev *hdev)
+{
+	struct hci_h4p_info *info;
+	int err, retries = 0;
+	struct sk_buff_head fw_queue;
+	unsigned long flags;
+
+	info = hci_get_drvdata(hdev);
+
+	if (test_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	/* TI1271 has HW bug and boot up might fail. Retry up to three times */
+again:
+
+	info->rx_enabled = 1;
+	info->rx_state = WAIT_FOR_PKT_TYPE;
+	info->rx_count = 0;
+	info->garbage_bytes = 0;
+	info->rx_skb = NULL;
+	info->pm_enabled = 0;
+	init_completion(&info->fw_completion);
+	hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+	hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
+	skb_queue_head_init(&fw_queue);
+
+	err = hci_h4p_reset(info);
+	if (err < 0)
+		goto err_clean;
+
+	hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_CTS | UART_EFR_RTS);
+	info->autorts = 1;
+
+	err = hci_h4p_send_negotiation(info);
+
+	err = hci_h4p_read_fw(info, &fw_queue);
+	if (err < 0) {
+		dev_err(info->dev, "Cannot read firmware\n");
+		goto err_clean;
+	}
+
+	err = hci_h4p_send_fw(info, &fw_queue);
+	if (err < 0) {
+		dev_err(info->dev, "Sending firmware failed.\n");
+		goto err_clean;
+	}
+
+	info->pm_enabled = 1;
+
+	err = hci_h4p_bt_wakeup_test(info);
+	if (err < 0) {
+		dev_err(info->dev, "BT wakeup test failed.\n");
+		goto err_clean;
+	}
+
+	spin_lock_irqsave(&info->lock, flags);
+	info->rx_enabled = gpio_get_value(info->host_wakeup_gpio);
+	hci_h4p_set_clk(info, &info->rx_clocks_en, info->rx_enabled);
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+
+	kfree_skb(info->alive_cmd_skb);
+	info->alive_cmd_skb = NULL;
+	set_bit(HCI_RUNNING, &hdev->flags);
+
+	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 sk_buff *skb)
+{
+	struct hci_h4p_info *info;
+	struct hci_dev *hdev = (struct hci_dev *)skb->dev;
+	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 int hci_h4p_hci_ioctl(struct hci_dev *hdev, unsigned int cmd,
+			     unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+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, "%pMR\n", info->bd_addr);
+}
+
+static DEVICE_ATTR(bdaddr, S_IRUGO | S_IWUSR, hci_h4p_show_bdaddr,
+		   hci_h4p_store_bdaddr);
+
+static int hci_h4p_sysfs_create_files(struct device *dev)
+{
+	return device_create_file(dev, &dev_attr_bdaddr);
+}
+
+static void hci_h4p_sysfs_remove_files(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_bdaddr);
+}
+
+static int hci_h4p_register_hdev(struct hci_h4p_info *info)
+{
+	struct hci_dev *hdev;
+
+	/* Initialize and register HCI device */
+
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		dev_err(info->dev, "Can't allocate memory for device\n");
+		return -ENOMEM;
+	}
+	info->hdev = hdev;
+
+	hdev->bus = HCI_UART;
+	hci_set_drvdata(hdev, info);
+
+	hdev->open = hci_h4p_hci_open;
+	hdev->close = hci_h4p_hci_close;
+	hdev->flush = hci_h4p_hci_flush;
+	hdev->send = hci_h4p_hci_send_frame;
+	hdev->ioctl = hci_h4p_hci_ioctl;
+	set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+
+	SET_HCIDEV_DEV(hdev, info->dev);
+
+	if (hci_h4p_sysfs_create_files(info->dev) < 0) {
+		dev_err(info->dev, "failed to create sysfs files\n");
+		goto free;
+	}
+
+	if (hci_register_dev(hdev) >= 0)
+		return 0;
+
+	dev_err(info->dev, "hci_register failed %s.\n", hdev->name);
+	hci_h4p_sysfs_remove_files(info->dev);
+free:
+	hci_free_dev(info->hdev);
+	return -ENODEV;
+}
+
+static int hci_h4p_probe(struct platform_device *pdev)
+{
+	struct hci_h4p_platform_data *bt_plat_data;
+	struct hci_h4p_info *info;
+	int err;
+
+	dev_info(&pdev->dev, "Registering HCI H4P device\n");
+	info = 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/nokia_fw-bcm.c b/drivers/bluetooth/nokia_fw-bcm.c
new file mode 100644
index 0000000..390d021
--- /dev/null
+++ b/drivers/bluetooth/nokia_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/nokia_fw-csr.c b/drivers/bluetooth/nokia_fw-csr.c
new file mode 100644
index 0000000..af880d9
--- /dev/null
+++ b/drivers/bluetooth/nokia_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(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/nokia_fw-ti1273.c b/drivers/bluetooth/nokia_fw-ti1273.c
new file mode 100644
index 0000000..32e5fa0
--- /dev/null
+++ b/drivers/bluetooth/nokia_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/nokia_fw.c b/drivers/bluetooth/nokia_fw.c
new file mode 100644
index 0000000..b3d39f9
--- /dev/null
+++ b/drivers/bluetooth/nokia_fw.c
@@ -0,0 +1,195 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation.
+ *
+ * Contact: Ville Tervo <ville.tervo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/firmware.h>
+#include <linux/clk.h>
+
+#include <net/bluetooth/bluetooth.h>
+
+#include "hci_h4p.h"
+
+static int fw_pos;
+
+/* Firmware handling */
+static int hci_h4p_open_firmware(struct hci_h4p_info *info,
+				 const struct firmware **fw_entry)
+{
+	int err;
+
+	fw_pos = 0;
+	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/nokia_uart.c b/drivers/bluetooth/nokia_uart.c
new file mode 100644
index 0000000..8e0a93c
--- /dev/null
+++ b/drivers/bluetooth/nokia_uart.c
@@ -0,0 +1,201 @@
+/*
+ * 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));
+}



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

^ permalink raw reply related

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

Hi!

> > @@ -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.
> 

Well, I can rename config option, but renaming the module would break
existing userland, no?

>  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.

Nokia N900 is certainly moving towards device tree, but we are not
ready, yet...


> > @@ -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.
> 

I sent him an email with question. I guess we should move Ville to
CREDITS?

> > +#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.
> 

(Could you insert newlines after 80 or so characters?)

Where would you like platform_data definition to go? That indeed is
for platform data, and quick grep shows drivers normally do public
header files for that.

> > +
> > +	if (!wait_for_completion_interruptible_timeout(&info->init_completion,
> > +				msecs_to_jiffies(1000)))
> 
> Please follow the net subsystem coding style.

Could you elaborate? I see nothing too obviously wrong here.

> > +{
> > +	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.

Ok.

> > +/* 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.

Ok.

> > +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.

%pMR. Ok.

> > +	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?

Ok.

> > +	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.

Ok.

> > +
> > +	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.
>

Perhaps people can donate bt addresses from their no-longer-functional
bluetooth devices and we can select from such pool here? ;-).

Is there some experimental range we can allocate from?

> > +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.

Ok.

Pali, here's first round of cleanups...

Signed-off-by: Pavel Machek <pavel@ucw.cz>

								Pavel

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 95155c3..9d46f23 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -243,7 +243,7 @@ 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
+config BT_NOKIA_H4P
 	tristate "HCI driver with H4 Nokia extensions"
 	depends on BT && ARCH_OMAP
 	help
diff --git a/drivers/bluetooth/hci_h4p/Makefile b/drivers/bluetooth/hci_h4p/Makefile
index f20bd9a..6f2ef85 100644
--- a/drivers/bluetooth/hci_h4p/Makefile
+++ b/drivers/bluetooth/hci_h4p/Makefile
@@ -2,6 +2,6 @@
 # Makefile for the Linux Bluetooth HCI device drivers.
 #
 
-obj-$(CONFIG_BT_HCIH4P)		+= hci_h4p.o
+obj-$(CONFIG_BT_NOKIA_H4P)		+= 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
index 2f1f8d4..a91bd7b 100644
--- a/drivers/bluetooth/hci_h4p/core.c
+++ b/drivers/bluetooth/hci_h4p/core.c
@@ -436,12 +436,12 @@ 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) {
+		switch (bt_cb(skb)->pkt_type) {
+		case 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) {
+		case H4_ALIVE_PKT:
 			hci_h4p_alive_packet(info, skb);
 			info->rx_state = WAIT_FOR_PKT_TYPE;
 			return;
@@ -843,9 +843,7 @@ static int hci_h4p_reset(struct hci_h4p_info *info)
 /* hci callback functions */
 static int hci_h4p_hci_flush(struct hci_dev *hdev)
 {
-	struct hci_h4p_info *info;
-	info = hci_get_drvdata(hdev);
-
+	struct hci_h4p_info *info = hci_get_drvdata(hdev);
 	skb_queue_purge(&info->txq);
 
 	return 0;
@@ -853,7 +851,8 @@ static int hci_h4p_hci_flush(struct hci_dev *hdev)
 
 static int hci_h4p_bt_wakeup_test(struct hci_h4p_info *info)
 {
-	/* Test Sequence:
+	/*
+	 * 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.
@@ -1101,9 +1100,7 @@ static ssize_t hci_h4p_show_bdaddr(struct device *dev,
 {
 	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]);
+	return sprintf(buf, "%pMR\n", info->bd_addr);
 }
 
 static DEVICE_ATTR(bdaddr, S_IRUGO | S_IWUSR, hci_h4p_show_bdaddr,
@@ -1146,16 +1143,17 @@ static int hci_h4p_register_hdev(struct hci_h4p_info *info)
 
 	if (hci_h4p_sysfs_create_files(info->dev) < 0) {
 		dev_err(info->dev, "failed to create sysfs files\n");
-		return -ENODEV;
+		goto free;
 	}
 
-	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;
-	}
+	if (hci_register_dev(hdev) >= 0)
+		return 0;
 
-	return 0;
+	dev_err(info->dev, "hci_register failed %s.\n", hdev->name);
+	hci_h4p_sysfs_remove_files(info->dev);
+free:
+	hci_free_dev(info->hdev);
+	return -ENODEV;
 }
 
 static int hci_h4p_probe(struct platform_device *pdev)
@@ -1296,12 +1294,9 @@ cleanup:
 		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)
diff --git a/drivers/bluetooth/hci_h4p/uart.c b/drivers/bluetooth/hci_h4p/uart.c
index 7973c6c..8e0a93c 100644
--- a/drivers/bluetooth/hci_h4p/uart.c
+++ b/drivers/bluetooth/hci_h4p/uart.c
@@ -129,7 +129,7 @@ int hci_h4p_reset_uart(struct hci_h4p_info *info)
 {
 	int count = 0;
 
-	/* Reset the  UART */
+	/* 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) {
@@ -142,7 +142,6 @@ int hci_h4p_reset_uart(struct hci_h4p_info *info)
 	return 0;
 }
 
-
 void hci_h4p_store_regs(struct hci_h4p_info *info)
 {
 	u16 lcr = 0;


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

^ permalink raw reply related

* Re: [RFC 5/6] android/audio: Add wrapper struct for audio structures
From: Luiz Augusto von Dentz @ 2013-12-30 12:04 UTC (permalink / raw)
  To: Lukasz Rymanowski; +Cc: linux-bluetooth@vger.kernel.org, Johan Hedberg
In-Reply-To: <CAMudyAuUDd0DVC+42Xh72-MTr8at46Agbma1e-XPm2grWHb75w@mail.gmail.com>

Hi Lukasz,

On Mon, Dec 30, 2013 at 1:33 PM, Lukasz Rymanowski
<lukasz.rymanowski@tieto.com> wrote:
> Hi Luiz,
>
> On 30 December 2013 12:27, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
>> Hi Lukasz,
>>
>> On Mon, Dec 30, 2013 at 12:17 PM, Lukasz Rymanowski
>> <lukasz.rymanowski@tieto.com> wrote:
>>> This patch add wrapping struct for audio_hw_dev and audio_stream_out.
>>> We will need this to keep some more addition info related to a2dp stream
>>> and also hal IPC later on
>>>
>>> ---
>>>  android/Makefile.am |  1 +
>>>  android/hal-audio.c | 97 +++++++++++++++++++++++++++++------------------------
>>>  2 files changed, 55 insertions(+), 43 deletions(-)
>>>
>>> diff --git a/android/Makefile.am b/android/Makefile.am
>>> index dec81ce..eaf39bd 100644
>>> --- a/android/Makefile.am
>>> +++ b/android/Makefile.am
>>> @@ -104,6 +104,7 @@ android_android_tester_LDFLAGS = -pthread
>>>  noinst_LTLIBRARIES += android/libaudio-internal.la
>>>
>>>  android_libaudio_internal_la_SOURCES = android/hal-audio.c \
>>> +                                       android/hal-audio.h \
>>>                                         android/hardware/audio.h \
>>>                                         android/hardware/audio_effect.h \
>>>                                         android/hardware/hardware.h \
>>> diff --git a/android/hal-audio.c b/android/hal-audio.c
>>> index 7f4a3f2..011a699 100644
>>> --- a/android/hal-audio.c
>>> +++ b/android/hal-audio.c
>>> @@ -25,6 +25,15 @@
>>>
>>>  #include "hal-log.h"
>>>
>>> +struct a2dp_audio_dev {
>>> +       struct audio_hw_device dev;
>>> +       struct a2dp_stream_out *stream_out;
>>> +};
>>> +
>>> +struct a2dp_stream_out {
>>> +       struct audio_stream_out stream;
>>> +};
>>> +
>>>  static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
>>>                                                                 size_t bytes)
>>>  {
>>> @@ -230,32 +239,34 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
>>>                                         struct audio_stream_out **stream_out)
>>>
>>>  {
>>> -       struct audio_stream_out *out;
>>> +       struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)dev;
>>> +       struct a2dp_stream_out *a2dp_out;
>>>
>>> -       out = calloc(1, sizeof(struct audio_stream_out));
>>> -       if (!out)
>>> +       a2dp_out = calloc(1, sizeof(struct a2dp_stream_out));
>>> +       if (!a2dp_out)
>>>                 return -ENOMEM;
>>>
>>>         DBG("");
>>>
>>> -       out->common.get_sample_rate = out_get_sample_rate;
>>> -       out->common.set_sample_rate = out_set_sample_rate;
>>> -       out->common.get_buffer_size = out_get_buffer_size;
>>> -       out->common.get_channels = out_get_channels;
>>> -       out->common.get_format = out_get_format;
>>> -       out->common.set_format = out_set_format;
>>> -       out->common.standby = out_standby;
>>> -       out->common.dump = out_dump;
>>> -       out->common.set_parameters = out_set_parameters;
>>> -       out->common.get_parameters = out_get_parameters;
>>> -       out->common.add_audio_effect = out_add_audio_effect;
>>> -       out->common.remove_audio_effect = out_remove_audio_effect;
>>> -       out->get_latency = out_get_latency;
>>> -       out->set_volume = out_set_volume;
>>> -       out->write = out_write;
>>> -       out->get_render_position = out_get_render_position;
>>> +       a2dp_out->stream.common.get_sample_rate = out_get_sample_rate;
>>> +       a2dp_out->stream.common.set_sample_rate = out_set_sample_rate;
>>> +       a2dp_out->stream.common.get_buffer_size = out_get_buffer_size;
>>> +       a2dp_out->stream.common.get_channels = out_get_channels;
>>> +       a2dp_out->stream.common.get_format = out_get_format;
>>> +       a2dp_out->stream.common.set_format = out_set_format;
>>> +       a2dp_out->stream.common.standby = out_standby;
>>> +       a2dp_out->stream.common.dump = out_dump;
>>> +       a2dp_out->stream.common.set_parameters = out_set_parameters;
>>> +       a2dp_out->stream.common.get_parameters = out_get_parameters;
>>> +       a2dp_out->stream.common.add_audio_effect = out_add_audio_effect;
>>> +       a2dp_out->stream.common.remove_audio_effect = out_remove_audio_effect;
>>> +       a2dp_out->stream.get_latency = out_get_latency;
>>> +       a2dp_out->stream.set_volume = out_set_volume;
>>> +       a2dp_out->stream.write = out_write;
>>> +       a2dp_out->stream.get_render_position = out_get_render_position;
>>>
>>> -       *stream_out = out;
>>> +       *stream_out = &a2dp_out->stream;
>>> +       a2dp_dev->stream_out = a2dp_out;
>>>
>>>         return 0;
>>>  }
>>> @@ -381,7 +392,7 @@ static int audio_close(hw_device_t *device)
>>>  static int audio_open(const hw_module_t *module, const char *name,
>>>                                                         hw_device_t **device)
>>>  {
>>> -       struct audio_hw_device *audio;
>>> +       struct a2dp_audio_dev *a2dp_dev;
>>>
>>>         DBG("");
>>>
>>> @@ -391,30 +402,30 @@ static int audio_open(const hw_module_t *module, const char *name,
>>>                 return -EINVAL;
>>>         }
>>>
>>> -       audio = calloc(1, sizeof(struct audio_hw_device));
>>> -       if (!audio)
>>> +       a2dp_dev = calloc(1, sizeof(struct a2dp_audio_dev));
>>> +       if (!a2dp_dev)
>>>                 return -ENOMEM;
>>>
>>> -       audio->common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
>>> -       audio->common.module = (struct hw_module_t *) module;
>>> -       audio->common.close = audio_close;
>>> -
>>> -       audio->init_check = audio_init_check;
>>> -       audio->set_voice_volume = audio_set_voice_volume;
>>> -       audio->set_master_volume = audio_set_master_volume;
>>> -       audio->set_mode = audio_set_mode;
>>> -       audio->set_mic_mute = audio_set_mic_mute;
>>> -       audio->get_mic_mute = audio_get_mic_mute;
>>> -       audio->set_parameters = audio_set_parameters;
>>> -       audio->get_parameters = audio_get_parameters;
>>> -       audio->get_input_buffer_size = audio_get_input_buffer_size;
>>> -       audio->open_output_stream = audio_open_output_stream;
>>> -       audio->close_output_stream = audio_close_output_stream;
>>> -       audio->open_input_stream = audio_open_input_stream;
>>> -       audio->close_input_stream = audio_close_input_stream;
>>> -       audio->dump = audio_dump;
>>> -
>>> -       *device = &audio->common;
>>> +       a2dp_dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
>>> +       a2dp_dev->dev.common.module = (struct hw_module_t *) module;
>>> +       a2dp_dev->dev.common.close = audio_close;
>>> +
>>> +       a2dp_dev->dev.init_check = audio_init_check;
>>> +       a2dp_dev->dev.set_voice_volume = audio_set_voice_volume;
>>> +       a2dp_dev->dev.set_master_volume = audio_set_master_volume;
>>> +       a2dp_dev->dev.set_mode = audio_set_mode;
>>> +       a2dp_dev->dev.set_mic_mute = audio_set_mic_mute;
>>> +       a2dp_dev->dev.get_mic_mute = audio_get_mic_mute;
>>> +       a2dp_dev->dev.set_parameters = audio_set_parameters;
>>> +       a2dp_dev->dev.get_parameters = audio_get_parameters;
>>> +       a2dp_dev->dev.get_input_buffer_size = audio_get_input_buffer_size;
>>> +       a2dp_dev->dev.open_output_stream = audio_open_output_stream;
>>> +       a2dp_dev->dev.close_output_stream = audio_close_output_stream;
>>> +       a2dp_dev->dev.open_input_stream = audio_open_input_stream;
>>> +       a2dp_dev->dev.close_input_stream = audio_close_input_stream;
>>> +       a2dp_dev->dev.dump = audio_dump;
>>> +
>>> +       *device = &a2dp_dev->dev.common;
>>>
>>>         return 0;
>>>  }
>>> --
>>> 1.8.4
>>
>> How is this supposed to be accessed, are you planning on adding a
>> global variable?
>>
>>
> Have a look on audio_open_output_stream(). This is actually how they did so
> far and it is fine for me.
> I want to avoid globals here.

I can see that you allocate a different struct for a2dp_dev and
a2dp_out, but then you return a member of the struct back to HAL but
after that you wont access these structs anymore and the HAL only uses
the members that you return so you probably gonna need to lookup for
these structs to access any auxiliary data that we might want to add,
also these structs never seems to be freed which I suppose can
generate memory leaks.


-- 
Luiz Augusto von Dentz

^ permalink raw reply

* Re: [RFC 6/6] android/audio: Add listener thread on the Audio HAL socket
From: Lukasz Rymanowski @ 2013-12-30 11:40 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth@vger.kernel.org, Johan Hedberg
In-Reply-To: <CABBYNZKAqa=conbeaakt_hE1p_bqOsiGritjqvCf8JYiWAwQUA@mail.gmail.com>

Hi Luiz,

On 30 December 2013 12:31, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
> Hi Lukasz,
>
> On Mon, Dec 30, 2013 at 12:17 PM, Lukasz Rymanowski
> <lukasz.rymanowski@tieto.com> wrote:
>> This patch add thread which is reponsible for listen on audio HAL
>> socket, register a2dp endpoint(s) and maintain socket.
>> When bluetooth daemon goes down, HAL audio plugin starts to listen on Audio HAL
>> socket again.
>>
>> ---
>>  android/Makefile.am |   2 +
>>  android/hal-audio.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  android/hal-audio.h |  18 +++++++
>>  3 files changed, 165 insertions(+)
>>  create mode 100644 android/hal-audio.h
>>
>> diff --git a/android/Makefile.am b/android/Makefile.am
>> index eaf39bd..bd90c13 100644
>> --- a/android/Makefile.am
>> +++ b/android/Makefile.am
>> @@ -112,6 +112,8 @@ android_libaudio_internal_la_SOURCES = android/hal-audio.c \
>>
>>  android_libaudio_internal_la_CFLAGS = -I$(srcdir)/android
>>
>> +android_libaudio_internal_la_LDFLAGS = -pthread
>> +
>>  endif
>>
>>  EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
>> diff --git a/android/hal-audio.c b/android/hal-audio.c
>> index 011a699..0e3bc70 100644
>> --- a/android/hal-audio.c
>> +++ b/android/hal-audio.c
>> @@ -16,18 +16,30 @@
>>   */
>>
>>  #include <errno.h>
>> +#include <pthread.h>
>> +#include <poll.h>
>>  #include <stdio.h>
>>  #include <stdlib.h>
>>  #include <string.h>
>> +#include <sys/socket.h>
>> +#include <sys/un.h>
>> +#include <unistd.h>
>>
>>  #include <hardware/audio.h>
>>  #include <hardware/hardware.h>
>>
>> +#include "hal-audio.h"
>>  #include "hal-log.h"
>>
>>  struct a2dp_audio_dev {
>>         struct audio_hw_device dev;
>>         struct a2dp_stream_out *stream_out;
>> +
>> +       pthread_t bt_watcher;
>> +       pthread_mutex_t hal_sk_mutex;
>> +       pthread_cond_t bt_watcher_cond;
>> +
>> +       int hal_sk;
>>  };
>>
>>  struct a2dp_stream_out {
>> @@ -384,15 +396,135 @@ static int audio_dump(const audio_hw_device_t *device, int fd)
>>
>>  static int audio_close(hw_device_t *device)
>>  {
>> +       struct audio_hw_device *dev = (struct audio_hw_device *)device;
>> +       struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)dev;
>> +
>>         DBG("");
>> +
>> +       if (a2dp_dev) {
>> +               /* TODO: We could try to unregister Endpoint here */
>> +               shutdown(a2dp_dev->hal_sk, 2);
>> +
>> +               pthread_mutex_lock(&a2dp_dev->hal_sk_mutex);
>> +               pthread_cond_signal(&a2dp_dev->bt_watcher_cond);
>> +               pthread_mutex_unlock(&a2dp_dev->hal_sk_mutex);
>> +
>> +               (void) pthread_join(a2dp_dev->bt_watcher, NULL);
>> +               free(a2dp_dev);
>> +       }
>> +
>>         free(device);
>>         return 0;
>>  }
>>
>> +static int wait_for_client(void)
>> +{
>> +       struct sockaddr_un addr;
>> +       int err;
>> +       int sk;
>> +       int new_sk;
>> +
>> +       DBG("");
>> +
>> +       sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
>> +       if (sk < 0) {
>> +               err = errno;
>> +               error("Failed to create socket: %d (%s)", err, strerror(err));
>> +               return -1;
>> +       }
>> +
>> +       memset(&addr, 0, sizeof(addr));
>> +       addr.sun_family = AF_UNIX;
>> +
>> +       memcpy(addr.sun_path, BLUEZ_AUDIO_HAL_SK_PATH,
>> +                                       sizeof(BLUEZ_AUDIO_HAL_SK_PATH));
>> +
>> +       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
>> +               err = errno;
>> +               error("Failed to bind socket: %d (%s)", err, strerror(err));
>> +               goto error;
>> +       }
>> +
>> +       if (listen(sk, 1) < 0) {
>> +               err = errno;
>> +               error("Failed to bind socket: %d (%s)", err, strerror(err));
>> +               goto error;
>> +       }
>> +
>> +       new_sk = accept(sk, NULL, NULL);
>> +       if (new_sk < 0) {
>> +               err = errno;
>> +               error("Failed to accept socket: %d (%s)", err, strerror(err));
>> +               goto error;
>> +       }
>> +
>> +       close(sk);
>> +       return new_sk;
>> +
>> +error:
>> +       close(sk);
>> +       return -1;
>> +}
>> +
>> +static void *bluetoothd_watcher(void *data)
>> +{
>> +       int err;
>> +       struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)data;
>> +       struct pollfd pfd;
>> +       struct timeval now;
>> +       struct timespec timeout;
>> +
>> +       DBG("");
>> +
>> +       while (1) {
>> +               a2dp_dev->hal_sk = wait_for_client();
>> +               if (a2dp_dev->hal_sk < 0) {
>> +                       error("Failed to create listening socket");
>> +                       continue;
>> +               }
>> +
>> +               DBG("Audio IPC: Connected");
>> +
>> +               /* TODO: Register ENDPOINT here */
>> +
>> +               memset(&pfd, 0, sizeof(pfd));
>> +               pfd.fd = a2dp_dev->hal_sk;
>> +               pfd.events = POLLHUP | POLLERR | POLLNVAL;
>> +
>> +               pthread_mutex_lock(&a2dp_dev->hal_sk_mutex);
>> +
>> +               /* Check if socket is still alive */
>> +               err = poll(&pfd, 1, -1);
>> +               if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) {
>> +                       info("Audio HAL: Socket closed");
>> +                       a2dp_dev->hal_sk = -1;
>> +               }
>> +
>> +               /* Maybe audio system is closing and audio_close() has been called? */
>> +               gettimeofday(&now, NULL);
>> +               timeout.tv_sec = now.tv_sec;
>> +               timeout.tv_nsec = now.tv_usec * 1000;
>> +               timeout.tv_sec += 1;
>> +
>> +               err = pthread_cond_timedwait(&a2dp_dev->bt_watcher_cond,
>> +                                       &a2dp_dev->hal_sk_mutex, &timeout);
>> +
>> +               pthread_mutex_unlock(&a2dp_dev->hal_sk_mutex);
>> +               if (err != ETIMEDOUT)
>> +                       /* Seems that device has been closed */
>> +                       break;
>> +       }
>> +
>> +       info("Closing bluetooth_watcher thread");
>> +       pthread_exit(NULL);
>> +       return NULL;
>> +}
>> +
>>  static int audio_open(const hw_module_t *module, const char *name,
>>                                                         hw_device_t **device)
>>  {
>>         struct a2dp_audio_dev *a2dp_dev;
>> +       int err;
>>
>>         DBG("");
>>
>> @@ -427,6 +559,19 @@ static int audio_open(const hw_module_t *module, const char *name,
>>
>>         *device = &a2dp_dev->dev.common;
>>
>> +       a2dp_dev->hal_sk = -1;
>> +
>> +       pthread_mutex_init(&a2dp_dev->hal_sk_mutex, NULL);
>> +       pthread_cond_init(&a2dp_dev->bt_watcher_cond, NULL);
>> +
>> +       err = pthread_create(&a2dp_dev->bt_watcher, NULL, bluetoothd_watcher, a2dp_dev);
>> +       if (err < 0) {
>> +               a2dp_dev->bt_watcher = 0;
>> +               error("Failed to start bluetoothd watcher thread: %d (%s)", -err,
>> +                                                               strerror(-err));
>> +               return (-err);
>> +       }
>> +
>>         return 0;
>>  }
>>
>> diff --git a/android/hal-audio.h b/android/hal-audio.h
>> new file mode 100644
>> index 0000000..93e49f6
>> --- /dev/null
>> +++ b/android/hal-audio.h
>> @@ -0,0 +1,18 @@
>> +/*
>> + *
>> + * Copyright (C) 2013 Intel Corporation
>> + *
>> + * Licensed under the Apache License, Version 2.0 (the "License");
>> + * you may not use this file except in compliance with the License.
>> + * You may obtain a copy of the License at
>> + *
>> + *      http://www.apache.org/licenses/LICENSE-2.0
>> + *
>> + * Unless required by applicable law or agreed to in writing, software
>> + * distributed under the License is distributed on an "AS IS" BASIS,
>> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>> + * See the License for the specific language governing permissions and
>> + * limitations under the License.
>> + *
>> + */
>> +static const char BLUEZ_AUDIO_HAL_SK_PATH[] = "\0bluez_audio_socket";
>> --
>> 1.8.4
>
> Ive started working on the daemon side and should be able to post some
> patches today, Im not really sure we are going to need a thread for
> command handling since all the commands will be sent by the plugin
> side so they can be synchronous, the only problem would be if the
> plugin cannot really block even for a short period.
>

This Thread is only going to be used for accept, endpoint register and
then listening for socket close (so we can start listen for connection
from new bluetoothd).

Commands and response will be handled in main thread (ex.
audio_open(), out_write()) and we can block there for a while - no
problem with that

>
> --
> Luiz Augusto von Dentz

\Łukasz

^ permalink raw reply


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