lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Fri, 24 Sep 2010 15:46:44 +0200
From:	Par-Gunnar Hjalmdahl <par-gunnar.p.hjalmdahl@...ricsson.com>
To:	linux-bluetooth@...r.kernel.org, linux-kernel@...r.kernel.org,
	linus.walleij@...ricsson.com, Pavan Savoy <pavan_savoy@...y.com>
Subject: [PATCH 1/6] This patch adds support for the ST-Ericsson CG2900

This patch adds support for the ST-Ericsson CG2900
 connectivity controller.
 This patch contains the framework for registering users, chip
 handlers, and transports as well as the needed files towards
 the mach-ux500 board.

Signed-off-by: Par-Gunnar Hjalmdahl <par-gunnar.p.hjalmdahl@...ricsson.com>
---
 arch/arm/mach-ux500/Makefile                      |    5 +
 arch/arm/mach-ux500/cg2900_devices.c              |  353 ++++
 arch/arm/mach-ux500/include/mach/cg2900_devices.h |  120 ++
 drivers/mfd/Kconfig                               |    8 +
 drivers/mfd/Makefile                              |    1 +
 drivers/mfd/cg2900/Makefile                       |    7 +
 drivers/mfd/cg2900/cg2900_char_devices.c          |  709 +++++++
 drivers/mfd/cg2900/cg2900_char_devices.h          |   36 +
 drivers/mfd/cg2900/cg2900_core.c                  | 2287 +++++++++++++++++++++
 drivers/mfd/cg2900/cg2900_core.h                  |  303 +++
 drivers/mfd/cg2900/cg2900_debug.h                 |   77 +
 drivers/mfd/cg2900/hci_defines.h                  |  102 +
 include/linux/mfd/cg2900.h                        |  205 ++
 13 files changed, 4213 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-ux500/cg2900_devices.c
 create mode 100644 arch/arm/mach-ux500/include/mach/cg2900_devices.h
 create mode 100644 drivers/mfd/cg2900/Makefile
 create mode 100644 drivers/mfd/cg2900/cg2900_char_devices.c
 create mode 100644 drivers/mfd/cg2900/cg2900_char_devices.h
 create mode 100644 drivers/mfd/cg2900/cg2900_core.c
 create mode 100644 drivers/mfd/cg2900/cg2900_core.h
 create mode 100644 drivers/mfd/cg2900/cg2900_debug.h
 create mode 100644 drivers/mfd/cg2900/hci_defines.h
 create mode 100644 include/linux/mfd/cg2900.h

diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile
index 46dbaf6..afafdd6 100644
--- a/arch/arm/mach-ux500/Makefile
+++ b/arch/arm/mach-ux500/Makefile
@@ -11,3 +11,8 @@ obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o
 obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o
 obj-$(CONFIG_LOCAL_TIMERS)	+= localtimer.o
 obj-$(CONFIG_REGULATOR_AB8500)	+= board-mop500-regulators.o
+ifeq ($(CONFIG_MFD_CG2900), m)
+obj-y					+= cg2900_devices.o
+else
+obj-$(CONFIG_MFD_CG2900)		+= cg2900_devices.o
+endif
diff --git a/arch/arm/mach-ux500/cg2900_devices.c
b/arch/arm/mach-ux500/cg2900_devices.c
new file mode 100644
index 0000000..0aa6a07
--- /dev/null
+++ b/arch/arm/mach-ux500/cg2900_devices.c
@@ -0,0 +1,353 @@
+/*
+ * arch/arm/mach-ux500/cg2900_devices.c
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@...ricsson.com) for
ST-Ericsson.
+ * Henrik Possung (henrik.possung@...ricsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@...ricsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@...ricsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@...ricsson.com) for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ *
+ * Board specific device support for the Linux Bluetooth HCI H:4 Driver
+ * for ST-Ericsson connectivity controller.
+ */
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <asm-generic/errno-base.h>
+#include <asm/byteorder.h>
+#include <mach/cg2900_devices.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <plat/pincfg.h>
+#include "pins-db8500.h"
+
+#ifndef PIN_INPUT_PULLUP
+#define PIN_INPUT_PULLUP		(PIN_DIR_INPUT | PIN_PULL_UP)
+#endif
+
+#ifndef GPIO_LOW
+#define GPIO_LOW			0
+#endif
+
+#ifndef GPIO_HIGH
+#define GPIO_HIGH			1
+#endif
+
+#ifndef GPIO_TO_IRQ
+#define GPIO_TO_IRQ			NOMADIK_GPIO_TO_IRQ
+#endif
+
+/** BT_ENABLE_GPIO - GPIO to enable/disable the BT module.
+ */
+#define BT_ENABLE_GPIO			170
+
+/** GBF_ENA_RESET_GPIO - GPIO to enable/disable the controller.
+ */
+#define GBF_ENA_RESET_GPIO		171
+
+/** BT_CTS_GPIO - CTS GPIO.
+*/
+#define BT_CTS_GPIO			0
+
+/** GBF_ENA_RESET_NAME - Name of GPIO for enabling/disabling.
+ */
+#define GBF_ENA_RESET_NAME		"gbf_ena_reset"
+
+/** GBF_ENA_RESET_NAME - Name of GPIO for enabling/disabling.
+ */
+#define BT_ENABLE_NAME			"bt_enable"
+/** CG2900_DEVICE_NAME - Name for this module.
+*/
+#define CG2900_DEVICE_NAME		"cg2900_driver"
+
+/** UART_LINES_NUM - Number of uart lines we want to configure.
+*/
+#define UART_LINES_NUM			4
+
+/* Bluetooth Opcode Group Field */
+#define BT_OGF_VS			0x3F
+
+/* Bluetooth Opcode Command Field */
+#define BT_OCF_VS_POWER_SWITCH_OFF	0x0140
+#define BT_OCF_VS_BT_ENABLE		0x0310
+
+#define MAKE_CMD(__ogf, __ocf)		((u16)(((u16)__ogf << 10) | __ocf))
+
+#define BT_VS_POWER_SWITCH_OFF		MAKE_CMD(BT_OGF_VS, \
+						 BT_OCF_VS_POWER_SWITCH_OFF)
+#define BT_VS_BT_ENABLE			MAKE_CMD(BT_OGF_VS, BT_OCF_VS_BT_ENABLE)
+
+#define VS_BT_DISABLE			0x00
+#define VS_BT_ENABLE			0x01
+
+#define H4_HEADER_LENGTH		0x01
+#define BT_HEADER_LENGTH		0x03
+
+#define STLC2690_HCI_REV		0x0600
+#define CG2900_HCI_REV			0x0101
+#define CG2900_SPECIAL_HCI_REV		0x0700
+
+struct vs_power_sw_off_cmd {
+	__le16	op_code;
+	u8	len;
+	u8	gpio_0_7_pull_up;
+	u8	gpio_8_15_pull_up;
+	u8	gpio_16_20_pull_up;
+	u8	gpio_0_7_pull_down;
+	u8	gpio_8_15_pull_down;
+	u8	gpio_16_20_pull_down;
+} __attribute__((packed));
+
+struct vs_bt_enable_cmd {
+	__le16	op_code;
+	u8	len;
+	u8	enable;
+} __attribute__((packed));
+
+static u8	cg2900_hci_version;
+static u8	cg2900_lmp_version;
+static u8	cg2900_lmp_subversion;
+static u16	cg2900_hci_revision;
+static u16	cg2900_manufacturer;
+
+/* IRQ callback. */
+static struct cg2900_devices_cb *cg2900_dev_callback;
+
+/* Pin configuration for UART functions. */
+static pin_cfg_t uart0_enabled[] = {
+	GPIO0_U0_CTSn   | PIN_INPUT_PULLUP,
+	GPIO1_U0_RTSn   | PIN_OUTPUT_HIGH,
+	GPIO2_U0_RXD    | PIN_INPUT_PULLUP,
+	GPIO3_U0_TXD    | PIN_OUTPUT_HIGH,
+};
+
+/* Pin configuration for sleep mode. */
+static pin_cfg_t uart0_disabled[] = {
+	GPIO0_GPIO   | PIN_INPUT_PULLUP, /* CTS pull up. */
+	GPIO1_GPIO   | PIN_OUTPUT_HIGH,  /* RTS high - flow off. */
+	GPIO2_GPIO   | PIN_INPUT_PULLUP, /* RX pull down. */
+	GPIO3_GPIO   | PIN_OUTPUT_LOW,   /* TX low - break on. */
+};
+
+void cg2900_devices_enable_chip(void)
+{
+	gpio_set_value(GBF_ENA_RESET_GPIO, GPIO_HIGH);
+}
+EXPORT_SYMBOL(cg2900_devices_enable_chip);
+
+void cg2900_devices_disable_chip(void)
+{
+	gpio_set_value(GBF_ENA_RESET_GPIO, GPIO_LOW);
+	cg2900_dev_callback = NULL;
+}
+EXPORT_SYMBOL(cg2900_devices_disable_chip);
+
+void cg2900_devices_set_hci_revision(u8 hci_version,
+				     u16 hci_revision,
+				     u8 lmp_version,
+				     u8 lmp_subversion,
+				     u16 manufacturer)
+{
+	cg2900_hci_version	= hci_version;
+	cg2900_hci_revision	= hci_revision;
+	cg2900_lmp_version	= lmp_version;
+	cg2900_lmp_subversion	= lmp_subversion;
+	cg2900_manufacturer	= manufacturer;
+}
+EXPORT_SYMBOL(cg2900_devices_set_hci_revision);
+
+struct sk_buff *cg2900_devices_get_power_switch_off_cmd(u16 *op_code)
+{
+	struct sk_buff *skb;
+	struct vs_power_sw_off_cmd *cmd;
+
+	/* If connected chip does not support the command return NULL */
+	if (CG2900_HCI_REV != cg2900_hci_revision &&
+	    CG2900_SPECIAL_HCI_REV != cg2900_hci_revision)
+		return NULL;
+
+	skb = alloc_skb(sizeof(*cmd) + H4_HEADER_LENGTH, GFP_KERNEL);
+	if (!skb) {
+		pr_err("Could not allocate skb");
+		return NULL;
+	}
+
+	skb_reserve(skb, H4_HEADER_LENGTH);
+	cmd = (struct vs_power_sw_off_cmd *)skb_put(skb, sizeof(*cmd));
+	cmd->op_code = cpu_to_le16(BT_VS_POWER_SWITCH_OFF);
+	cmd->len = sizeof(*cmd) - BT_HEADER_LENGTH;
+	/*
+	 * Enter system specific GPIO settings here:
+	 * Section data[3-5] is GPIO pull-up selection
+	 * Section data[6-8] is GPIO pull-down selection
+	 * Each section is a bitfield where
+	 * - byte 0 bit 0 is GPIO 0
+	 * - byte 0 bit 1 is GPIO 1
+	 * - up to
+	 * - byte 2 bit 4 which is GPIO 20
+	 * where each bit means:
+	 * - 0: No pull-up / no pull-down
+	 * - 1: Pull-up / pull-down
+	 * All GPIOs are set as input.
+	 */
+	cmd->gpio_0_7_pull_up = 0x00;
+	cmd->gpio_8_15_pull_up = 0x00;
+	cmd->gpio_16_20_pull_up = 0x00;
+	cmd->gpio_0_7_pull_down = 0x00;
+	cmd->gpio_8_15_pull_down = 0x00;
+	cmd->gpio_16_20_pull_down = 0x00;
+
+	if (op_code)
+		*op_code = BT_VS_POWER_SWITCH_OFF;
+
+	return skb;
+}
+EXPORT_SYMBOL(cg2900_devices_get_power_switch_off_cmd);
+
+struct sk_buff *cg2900_devices_get_bt_enable_cmd(u16 *op_code, bool bt_enable)
+{
+	struct sk_buff *skb;
+	struct vs_bt_enable_cmd *cmd;
+
+	/* If connected chip does not support the command return NULL */
+	if (CG2900_HCI_REV != cg2900_hci_revision &&
+	    CG2900_SPECIAL_HCI_REV != cg2900_hci_revision)
+		return NULL;
+
+	/* CG2900 used */
+	skb = alloc_skb(sizeof(*cmd) + H4_HEADER_LENGTH, GFP_KERNEL);
+	if (!skb) {
+		pr_err("Could not allocate skb");
+		return NULL;
+	}
+
+	skb_reserve(skb, H4_HEADER_LENGTH);
+	cmd = (struct vs_bt_enable_cmd *)skb_put(skb, sizeof(*cmd));
+	cmd->op_code = cpu_to_le16(BT_VS_BT_ENABLE);
+	cmd->len = sizeof(*cmd) - BT_HEADER_LENGTH;
+	if (bt_enable)
+		cmd->enable = VS_BT_ENABLE;
+	else
+		cmd->enable = VS_BT_DISABLE;
+
+	if (op_code)
+		*op_code = BT_VS_BT_ENABLE;
+
+	return skb;
+}
+EXPORT_SYMBOL(cg2900_devices_get_bt_enable_cmd);
+
+static irqreturn_t cg2900_devices_interrupt(int irq, void *dev_id)
+{
+	disable_irq_nosync(irq);
+	if (cg2900_dev_callback && cg2900_dev_callback->interrupt_cb)
+		cg2900_dev_callback->interrupt_cb();
+
+	return IRQ_HANDLED;
+}
+
+int cg2900_devices_set_cts_irq(void)
+{
+	int err;
+
+	/*
+	 * Without this delay we get interrupt on CTS immediately
+	 * due to some turbulences on this line.
+	 */
+	mdelay(4);
+
+	/* Disable UART functions. */
+	err = nmk_config_pins(uart0_disabled, UART_LINES_NUM);
+
+	if (err)
+		goto error;
+
+	/* Set IRQ on CTS. */
+	err = request_irq(GPIO_TO_IRQ(BT_CTS_GPIO),
+					cg2900_devices_interrupt,
+					IRQF_TRIGGER_FALLING,
+					CG2900_DEVICE_NAME,
+					NULL);
+	if (err)
+		goto error;
+
+	return 0;
+
+error:
+	(void)nmk_config_pins(uart0_enabled, UART_LINES_NUM);
+	pr_err("Can not set intterupt.");
+	return err;
+}
+EXPORT_SYMBOL(cg2900_devices_set_cts_irq);
+
+void cg2900_devices_unset_cts_irq(void)
+{
+	int err;
+
+	/* Restore UART settings. */
+	free_irq(GPIO_TO_IRQ(BT_CTS_GPIO), NULL);
+	err = nmk_config_pins(uart0_enabled, UART_LINES_NUM);
+	if (err)
+		pr_err("Unable to enable UART");
+}
+EXPORT_SYMBOL(cg2900_devices_unset_cts_irq);
+
+void cg2900_devices_reg_cb(struct cg2900_devices_cb *cb)
+{
+	if (!cg2900_dev_callback)
+		cg2900_dev_callback = cb;
+	else
+		pr_err("Callback already registered");
+}
+EXPORT_SYMBOL(cg2900_devices_reg_cb);
+int cg2900_devices_init(void)
+{
+	int err = 0;
+
+	err = gpio_request(GBF_ENA_RESET_GPIO, GBF_ENA_RESET_NAME);
+	if (err < 0) {
+		pr_err("gpio_request failed with err: %d", err);
+		goto finished;
+	}
+
+	err = gpio_direction_output(GBF_ENA_RESET_GPIO, GPIO_HIGH);
+	if (err < 0) {
+		pr_err("gpio_direction_output failed with err: %d", err);
+		goto error_handling;
+	}
+
+	err = gpio_request(BT_ENABLE_GPIO, BT_ENABLE_NAME);
+	if (err < 0) {
+		pr_err("gpio_request failed with err: %d", err);
+		goto finished;
+	}
+
+	err = gpio_direction_output(BT_ENABLE_GPIO, GPIO_HIGH);
+	if (err < 0) {
+		pr_err("gpio_direction_output failed with err: %d", err);
+		goto error_handling;
+	}
+
+	goto finished;
+
+error_handling:
+	gpio_free(GBF_ENA_RESET_GPIO);
+
+finished:
+	cg2900_devices_disable_chip();
+	return err;
+}
+EXPORT_SYMBOL(cg2900_devices_init);
+
+void cg2900_devices_exit(void)
+{
+	cg2900_devices_disable_chip();
+	gpio_free(GBF_ENA_RESET_GPIO);
+}
+EXPORT_SYMBOL(cg2900_devices_exit);
diff --git a/arch/arm/mach-ux500/include/mach/cg2900_devices.h
b/arch/arm/mach-ux500/include/mach/cg2900_devices.h
new file mode 100644
index 0000000..2db25b3
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/cg2900_devices.h
@@ -0,0 +1,120 @@
+/*
+ * arch/arm/mach-ux500/include/mach/cg2900_devices.h
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@...ricsson.com) for
ST-Ericsson.
+ * Henrik Possung (henrik.possung@...ricsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@...ricsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@...ricsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@...ricsson.com) for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ *
+ * Board specific device support for the Linux Bluetooth HCI H4 Driver
+ * for ST-Ericsson connectivity controller.
+ */
+
+#ifndef _CG2900_DEVICES_H_
+#define _CG2900_DEVICES_H_
+
+#include <linux/types.h>
+#include <linux/skbuff.h>
+
+/**
+ * struct cg2900_devices_cb - Callback structure for cg2900_devices user.
+ * @interrupt_cb:  Callback function called when interrupt on CTS occurred.
+ *
+ * Defines the callback functions provided from the caller.
+ */
+struct cg2900_devices_cb {
+	void (*interrupt_cb)(void);
+};
+
+/**
+ * cg2900_devices_enable_chip() - Enable the controller.
+ */
+extern void cg2900_devices_enable_chip(void);
+
+/**
+ * cg2900_devices_disable_chip() - Disable the controller.
+ */
+extern void cg2900_devices_disable_chip(void);
+
+/**
+ * cg2900_devices_set_hci_revision() - Stores HCI revision info for
the connected connectivity controller.
+ * @hci_version:	HCI version from the controller.
+ * @hci_revision:	HCI revision from the controller.
+ * @lmp_version:	LMP version from the controller.
+ * @lmp_subversion:	LMP subversion from the controller.
+ * @manufacturer:	Manufacturer ID from the controller.
+ *
+ * See Bluetooth specification and white paper for used controller for details
+ * about parameters.
+ */
+extern void cg2900_devices_set_hci_revision(u8 hci_version,
+					    u16 hci_revision,
+					    u8 lmp_version,
+					    u8 lmp_subversion,
+					    u16 manufacturer);
+
+/**
+ * cg2900_devices_get_power_switch_off_cmd() - Get HCI power switch
off command to use based on connected connectivity controller.
+ * @op_code:	HCI opcode in generated packet. NULL if not needed.
+ *
+ * This command does not add the H4 channel header in front of the message.
+ *
+ * Returns:
+ *   NULL if no command shall be sent,
+ *   sk_buffer with command otherwise.
+ */
+extern struct sk_buff *cg2900_devices_get_power_switch_off_cmd(u16 *op_code);
+
+/**
+ * cg2900_devices_get_bt_enable_cmd() - Get HCI BT enable command to
use based on connected connectivity controller.
+ * @op_code:	HCI opcode in generated packet. NULL if not needed.
+ * @bt_enable:	true if Bluetooth IP shall be enabled, false otherwise.
+ *
+ * This command does not add the H4 channel header in front of the message.
+ *
+ * Returns:
+ *   NULL if no command shall be sent,
+ *   sk_buffer with command otherwise.
+ */
+extern struct sk_buff *cg2900_devices_get_bt_enable_cmd(u16 *op_code,
+							bool bt_enable);
+
+/**
+ * cg2900_devices_init() - Initialize the board config.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   Error codes from gpio_request and gpio_direction_output.
+ */
+extern int cg2900_devices_init(void);
+
+/**
+ * cg2900_devices_exit() - Exit function for the board config.
+ */
+extern void cg2900_devices_exit(void);
+
+/**
+ * cg2900_devices_unset_cts_irq() - Disable interrupt on CTS.
+ */
+extern void cg2900_devices_unset_cts_irq(void);
+
+/**
+ * cg2900_devices_set_cts_irq() - Enable interrupt on CTS.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   Error codes from request_irq and nmk_config_pins.
+ */
+extern int cg2900_devices_set_cts_irq(void);
+
+/**
+ * cg2900_devices_reg_cb() - Register callbacks from upper layer.
+ *@cb:	Callback structure from upper layer.
+ *
+ */
+extern void cg2900_devices_reg_cb(struct cg2900_devices_cb *cb);
+#endif /* _CG2900_DEVICES_H_ */
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index fbfe14a..c0a6652 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -576,6 +576,14 @@ config MFD_TPS6586X
 	  This driver can also be built as a module.  If so, the module
 	  will be called tps6586x.

+config MFD_CG2900
+	tristate "Support ST-Ericsson CG2900 main structure"
+	depends on NET
+	help
+	  Support for ST-Ericsson CG2900 Connectivity Combo controller main structure.
+	  Supports multiple functionalities muxed over a Bluetooth HCI H:4 interface.
+	  CG2900 support Bluetooth, FM radio, and GPS.
+
 endif # MFD_SUPPORT

 menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index d5968cd..e3d8206 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -78,3 +78,4 @@ obj-$(CONFIG_MFD_RDC321X)	+= rdc321x-southbridge.o
 obj-$(CONFIG_MFD_JANZ_CMODIO)	+= janz-cmodio.o
 obj-$(CONFIG_MFD_JZ4740_ADC)	+= jz4740-adc.o
 obj-$(CONFIG_MFD_TPS6586X)	+= tps6586x.o
+obj-y				+= cg2900/
diff --git a/drivers/mfd/cg2900/Makefile b/drivers/mfd/cg2900/Makefile
new file mode 100644
index 0000000..76af761
--- /dev/null
+++ b/drivers/mfd/cg2900/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for ST-Ericsson CG2900 connectivity combo controller
+#
+
+obj-$(CONFIG_MFD_CG2900)            += cg2900.o
+cg2900-objs                         := cg2900_core.o cg2900_char_devices.o
+export-objs                         := cg2900_core.o
diff --git a/drivers/mfd/cg2900/cg2900_char_devices.c
b/drivers/mfd/cg2900/cg2900_char_devices.c
new file mode 100644
index 0000000..709689b
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_char_devices.c
@@ -0,0 +1,709 @@
+/*
+ * drivers/mfd/cg2900/cg2900_char_devices.c
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@...ricsson.com) for
ST-Ericsson.
+ * Henrik Possung (henrik.possung@...ricsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@...ricsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@...ricsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@...ricsson.com) for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson connectivity controller.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/poll.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/list.h>
+
+#include <linux/mfd/cg2900.h>
+#include <mach/cg2900_devices.h>
+#include "cg2900_core.h"
+#include "cg2900_debug.h"
+
+/* Ioctls */
+#define CG2900_CHAR_DEV_IOCTL_RESET		_IOW('U', 210, int)
+#define CG2900_CHAR_DEV_IOCTL_CHECK4RESET	_IOR('U', 212, int)
+#define CG2900_CHAR_DEV_IOCTL_GET_REVISION	_IOR('U', 213, int)
+#define CG2900_CHAR_DEV_IOCTL_GET_SUB_VER	_IOR('U', 214, int)
+
+#define CG2900_CHAR_DEV_IOCTL_EVENT_RESET	1
+#define CG2900_CHAR_DEV_IOCTL_EVENT_CLOSED	2
+
+/* Internal type definitions */
+
+/**
+ * enum char_reset_state - Reset state.
+ * @CG2900_CHAR_IDLE:	Idle state.
+ * @CG2900_CHAR_RESET:	Reset state.
+ */
+enum char_reset_state {
+	CG2900_CHAR_IDLE,
+	CG2900_CHAR_RESET
+};
+
+/**
+ * struct char_dev_user - Stores device information.
+ * @dev:		Registered CG2900 Core device.
+ * @miscdev:	Registered device struct.
+ * @name:		Name of device.
+ * @rx_queue:		Data queue.
+ * @rx_wait_queue:	Wait queue.
+ * @reset_wait_queue:	Reset Wait queue.
+ * @reset_state:	Reset state.
+ * @read_mutex:		Read mutex.
+ * @write_mutex:	Write mutex.
+ * @list:		List header for inserting into device list.
+ */
+struct char_dev_user {
+	struct cg2900_device	*dev;
+	struct miscdevice	*miscdev;
+	char			*name;
+	struct sk_buff_head	rx_queue;
+	wait_queue_head_t	rx_wait_queue;
+	wait_queue_head_t	reset_wait_queue;
+	enum char_reset_state	reset_state;
+	struct mutex		read_mutex;
+	struct mutex		write_mutex;
+	struct list_head	list;
+};
+
+/**
+ * struct char_info - Stores all current users.
+ * @open_mutex:	Open mutex (used for both open and release).
+ * @dev_users:	List of char dev users.
+ */
+struct char_info {
+	struct mutex		open_mutex;
+	struct list_head	dev_users;
+};
+
+/* Internal variable declarations */
+
+/*
+ * char_info - Main information object for char devices.
+ */
+static struct char_info *char_info;
+
+/* ST-Ericsson CG2900 driver callbacks */
+
+/**
+ * char_dev_read_cb() - Handle data received from controller.
+ * @dev:	Device receiving data.
+ * @skb:	Buffer with data coming from controller.
+ *
+ * The char_dev_read_cb() function handles data received from
STE-CG2900 driver.
+ */
+static void char_dev_read_cb(struct cg2900_device *dev, struct sk_buff *skb)
+{
+	struct char_dev_user *char_dev = (struct char_dev_user *)dev->user_data;
+
+	CG2900_INFO("CharDev: char_dev_read_cb");
+
+	if (!char_dev) {
+		CG2900_ERR("No char dev! Exiting");
+		kfree_skb(skb);
+		return;
+	}
+
+	skb_queue_tail(&char_dev->rx_queue, skb);
+
+	wake_up_interruptible(&char_dev->rx_wait_queue);
+}
+
+/**
+ * char_dev_reset_cb() - Handle reset from controller.
+ * @dev:	Device resetting.
+ *
+ * The char_dev_reset_cb() function handles reset from the CG2900 driver.
+ */
+static void char_dev_reset_cb(struct cg2900_device *dev)
+{
+	struct char_dev_user *char_dev = (struct char_dev_user *)dev->user_data;
+
+	CG2900_INFO("CharDev: char_dev_reset_cb");
+
+	if (!char_dev) {
+		CG2900_ERR("char_dev == NULL");
+		return;
+	}
+
+	char_dev->reset_state = CG2900_CHAR_RESET;
+	/*
+	 * The device will be freed by CG2900 Core when this function is
+	 * finished.
+	 */
+	char_dev->dev = NULL;
+
+	wake_up_interruptible(&char_dev->rx_wait_queue);
+	wake_up_interruptible(&char_dev->reset_wait_queue);
+}
+
+/*
+ * struct char_cb - Callback structure for CG2900 user.
+ * @read_cb:	Callback function called when data is received from the
+ *		CG2900 driver.
+ * @reset_cb:	Callback function called when the controller has been reset.
+ */
+static struct cg2900_callbacks char_cb = {
+	.read_cb = char_dev_read_cb,
+	.reset_cb = char_dev_reset_cb
+};
+
+/* File operation functions */
+
+/**
+ * char_dev_open() - Open char device.
+ * @inode:	Device driver information.
+ * @filp:	Pointer to the file struct.
+ *
+ * The char_dev_open() function opens the char device.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EACCES if device was already registered to driver or if registration
+ *   failed.
+ */
+static int char_dev_open(struct inode *inode, struct file *filp)
+{
+	int err = 0;
+	int minor;
+	struct char_dev_user *dev = NULL;
+	struct char_dev_user *tmp;
+	struct list_head *cursor;
+
+	mutex_lock(&char_info->open_mutex);
+
+	minor = iminor(inode);
+
+	/* Find the device for this file */
+	list_for_each(cursor, &char_info->dev_users) {
+		tmp = list_entry(cursor, struct char_dev_user, list);
+		if (tmp->miscdev->minor == minor) {
+			dev = tmp;
+			break;
+		}
+	}
+	if (!dev) {
+		CG2900_ERR("Could not identify device in inode");
+		err = -EINVAL;
+		goto error_handling;
+	}
+
+	filp->private_data = dev;
+
+	CG2900_INFO("CharDev: char_dev_open %s", dev->name);
+
+	if (dev->dev) {
+		CG2900_ERR("Device already registered to CG2900 Driver");
+		err = -EACCES;
+		goto error_handling;
+	}
+	/* First initiate wait queues for this device. */
+	init_waitqueue_head(&dev->rx_wait_queue);
+	init_waitqueue_head(&dev->reset_wait_queue);
+
+	dev->reset_state = CG2900_CHAR_IDLE;
+
+	/* Register to CG2900 Driver */
+	dev->dev = cg2900_register_user(dev->name, &char_cb);
+	if (dev->dev)
+		dev->dev->user_data = dev;
+	else {
+		CG2900_ERR("Couldn't register to CG2900 for H:4 channel %s",
+			   dev->name);
+		err = -EACCES;
+	}
+
+error_handling:
+	mutex_unlock(&char_info->open_mutex);
+	return err;
+}
+
+/**
+ * char_dev_release() - Release char device.
+ * @inode:	Device driver information.
+ * @filp:	Pointer to the file struct.
+ *
+ * The char_dev_release() function release the char device.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EBADF if NULL pointer was supplied in private data.
+ */
+static int char_dev_release(struct inode *inode, struct file *filp)
+{
+	int err = 0;
+	struct char_dev_user *dev = (struct char_dev_user *)filp->private_data;
+
+	CG2900_INFO("CharDev: char_dev_release");
+
+	if (!dev) {
+		CG2900_ERR("Calling with NULL pointer");
+		return -EBADF;
+	}
+
+	mutex_lock(&char_info->open_mutex);
+	mutex_lock(&dev->read_mutex);
+	mutex_lock(&dev->write_mutex);
+
+	if (dev->reset_state == CG2900_CHAR_IDLE)
+		cg2900_deregister_user(dev->dev);
+
+	dev->dev = NULL;
+	filp->private_data = NULL;
+	wake_up_interruptible(&dev->rx_wait_queue);
+	wake_up_interruptible(&dev->reset_wait_queue);
+
+	mutex_unlock(&dev->write_mutex);
+	mutex_unlock(&dev->read_mutex);
+	mutex_unlock(&char_info->open_mutex);
+
+	return err;
+}
+
+/**
+ * char_dev_read() - Queue and copy buffer to user.
+ * @filp:	Pointer to the file struct.
+ * @buf:	Received buffer.
+ * @count:	Size of buffer.
+ * @f_pos:	Position in buffer.
+ *
+ * The char_dev_read() function queues and copy the received buffer to
+ * the user space char device. If no data is available this function
will block.
+ *
+ * Returns:
+ *   Bytes successfully read (could be 0).
+ *   -EBADF if NULL pointer was supplied in private data.
+ *   -EFAULT if copy_to_user fails.
+ *   Error codes from wait_event_interruptible.
+ */
+static ssize_t char_dev_read(struct file *filp, char __user *buf, size_t count,
+			     loff_t *f_pos)
+{
+	struct char_dev_user *dev = (struct char_dev_user *)filp->private_data;
+	struct sk_buff *skb;
+	int bytes_to_copy;
+	int err = 0;
+
+	CG2900_INFO("CharDev: char_dev_read");
+
+	if (!dev) {
+		CG2900_ERR("Calling with NULL pointer");
+		return -EBADF;
+	}
+	mutex_lock(&dev->read_mutex);
+
+	if (skb_queue_empty(&dev->rx_queue)) {
+		err = wait_event_interruptible(dev->rx_wait_queue,
+				(!(skb_queue_empty(&dev->rx_queue))) ||
+				(CG2900_CHAR_RESET == dev->reset_state) ||
+				(dev->dev == NULL));
+		if (err) {
+			CG2900_ERR("Failed to wait for event");
+			goto error_handling;
+		}
+	}
+
+	if (!dev->dev) {
+		CG2900_DBG("dev is empty - return with negative bytes");
+		err = -EBADF;
+		goto error_handling;
+	}
+
+	skb = skb_dequeue(&dev->rx_queue);
+	if (!skb) {
+		CG2900_DBG("skb queue is empty - return with zero bytes");
+		bytes_to_copy = 0;
+		goto finished;
+	}
+
+	bytes_to_copy = min(count, skb->len);
+
+	err = copy_to_user(buf, skb->data, bytes_to_copy);
+	if (err) {
+		skb_queue_head(&dev->rx_queue, skb);
+		err = -EFAULT;
+		goto error_handling;
+	}
+
+	skb_pull(skb, bytes_to_copy);
+
+	if (skb->len > 0)
+		skb_queue_head(&dev->rx_queue, skb);
+	else
+		kfree_skb(skb);
+
+	goto finished;
+
+error_handling:
+	mutex_unlock(&dev->read_mutex);
+	return (ssize_t)err;
+finished:
+	mutex_unlock(&dev->read_mutex);
+	return bytes_to_copy;
+}
+
+/**
+ * char_dev_write() - Copy buffer from user and write to CG2900 driver.
+ * @filp:	Pointer to the file struct.
+ * @buf:	Write buffer.
+ * @count:	Size of the buffer write.
+ * @f_pos:	Position of buffer.
+ *
+ * Returns:
+ *   Bytes successfully written (could be 0).
+ *   -EBADF if NULL pointer was supplied in private data.
+ *   -EFAULT if copy_from_user fails.
+ */
+static ssize_t char_dev_write(struct file *filp, const char __user *buf,
+			      size_t count, loff_t *f_pos)
+{
+	struct sk_buff *skb;
+	struct char_dev_user *dev = (struct char_dev_user *)filp->private_data;
+	int err = 0;
+
+	CG2900_INFO("CharDev: char_dev_write");
+
+	if (!dev) {
+		CG2900_ERR("Calling with NULL pointer");
+		return -EBADF;
+	}
+	mutex_lock(&dev->write_mutex);
+
+	skb = cg2900_alloc_skb(count, GFP_ATOMIC);
+	if (!skb) {
+		CG2900_ERR("Couldn't allocate sk_buff with length %d", count);
+		goto error_handling;
+	}
+
+	if (copy_from_user(skb_put(skb, count), buf, count)) {
+		kfree_skb(skb);
+		err = -EFAULT;
+		goto error_handling;
+	}
+
+	err = cg2900_write(dev->dev, skb);
+	if (err) {
+		CG2900_ERR("cg2900_write failed (%d)", err);
+		kfree_skb(skb);
+		goto error_handling;
+	}
+
+	mutex_unlock(&dev->write_mutex);
+	return count;
+
+error_handling:
+	mutex_unlock(&dev->write_mutex);
+	return err;
+}
+
+/**
+ * char_dev_unlocked_ioctl() - Handle IOCTL call to the interface.
+ * @filp:	Pointer to the file struct.
+ * @cmd:	IOCTL command.
+ * @arg:	IOCTL argument.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EBADF if NULL pointer was supplied in private data.
+ *   -EINVAL if supplied cmd is not supported.
+ *   For cmd CG2900_CHAR_DEV_IOCTL_CHECK4RESET 0x01 is returned if device is
+ *   reset and 0x02 is returned if device is closed.
+ */
+static long char_dev_unlocked_ioctl(struct file *filp, unsigned int cmd,
+				    unsigned long arg)
+{
+	struct char_dev_user *dev = (struct char_dev_user *)filp->private_data;
+	struct cg2900_rev_data rev_data;
+	int err = 0;
+
+	CG2900_INFO("CharDev: char_dev_unlocked_ioctl cmd %d for %s", cmd,
+		    dev->name);
+	CG2900_DBG("DIR: %d, TYPE: %d, NR: %d, SIZE: %d",
+		     _IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd),
+		     _IOC_SIZE(cmd));
+
+	switch (cmd) {
+	case CG2900_CHAR_DEV_IOCTL_RESET:
+		if (!dev) {
+			err = -EBADF;
+			goto error_handling;
+		}
+		CG2900_INFO("ioctl reset command for device %s", dev->name);
+		err = cg2900_reset(dev->dev);
+		break;
+
+	case CG2900_CHAR_DEV_IOCTL_CHECK4RESET:
+		if (!dev) {
+			CG2900_INFO("ioctl check for reset command for device");
+			/* Return positive value if closed */
+			err = CG2900_CHAR_DEV_IOCTL_EVENT_CLOSED;
+		} else if (dev->reset_state == CG2900_CHAR_RESET) {
+			CG2900_INFO("ioctl check for reset command for device "
+				    "%s", dev->name);
+			/* Return positive value if reset */
+			err = CG2900_CHAR_DEV_IOCTL_EVENT_RESET;
+		}
+		break;
+
+	case CG2900_CHAR_DEV_IOCTL_GET_REVISION:
+		CG2900_INFO("ioctl check for local revision info");
+		if (cg2900_get_local_revision(&rev_data)) {
+			CG2900_DBG("Read revision data revision %d "
+				   "sub_version %d",
+				   rev_data.revision, rev_data.sub_version);
+			err = rev_data.revision;
+		} else {
+			CG2900_DBG("No revision data available");
+			err = -EIO;
+		}
+		break;
+
+	case CG2900_CHAR_DEV_IOCTL_GET_SUB_VER:
+		CG2900_INFO("ioctl check for local sub-version info");
+		if (cg2900_get_local_revision(&rev_data)) {
+			CG2900_DBG("Read revision data revision %d "
+				   "sub_version %d",
+				   rev_data.revision, rev_data.sub_version);
+			err = rev_data.sub_version;
+		} else {
+			CG2900_DBG("No revision data available");
+			err = -EIO;
+		}
+		break;
+
+	default:
+		CG2900_ERR("Unknown ioctl command %08X", cmd);
+		err = -EINVAL;
+		break;
+	};
+
+error_handling:
+	return err;
+}
+
+/**
+ * char_dev_poll() - Handle POLL call to the interface.
+ * @filp:	Pointer to the file struct.
+ * @wait:	Poll table supplied to caller.
+ *
+ * Returns:
+ *   Mask of current set POLL values
+ */
+static unsigned int char_dev_poll(struct file *filp, poll_table *wait)
+{
+	struct char_dev_user *dev = (struct char_dev_user *)filp->private_data;
+	unsigned int mask = 0;
+
+	if (!dev) {
+		CG2900_DBG("Device not open");
+		return POLLERR | POLLRDHUP;
+	}
+
+	poll_wait(filp, &dev->reset_wait_queue, wait);
+	poll_wait(filp, &dev->rx_wait_queue, wait);
+
+	if (!dev->dev)
+		mask |= POLLERR | POLLRDHUP;
+	else
+		mask |= POLLOUT; /* We can TX unless there is an error */
+
+	if (!(skb_queue_empty(&dev->rx_queue)))
+		mask |= POLLIN | POLLRDNORM;
+
+	if (CG2900_CHAR_RESET == dev->reset_state)
+		mask |= POLLPRI;
+
+	return mask;
+}
+
+/*
+ * struct char_dev_fops - Char devices file operations.
+ * @read:		Function that reads from the char device.
+ * @write:		Function that writes to the char device.
+ * @unlocked_ioctl:	Function that performs IO operations with
+ *			the char device.
+ * @poll:		Function that checks if there are possible operations
+ *			with the char device.
+ * @open:		Function that opens the char device.
+ * @release:		Function that release the char device.
+ */
+static const struct file_operations char_dev_fops = {
+	.read		= char_dev_read,
+	.write		= char_dev_write,
+	.unlocked_ioctl	= char_dev_unlocked_ioctl,
+	.poll		= char_dev_poll,
+	.open		= char_dev_open,
+	.release	= char_dev_release
+};
+
+/**
+ * setup_dev() - Set up the char device structure for device.
+ * @parent:	Parent device pointer.
+ * @name:	Name of registered device.
+ *
+ * The setup_dev() function sets up the char_dev structure for this device.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EINVAL if NULL pointer has been supplied.
+ *   Error codes from cdev_add and device_create.
+ */
+static int setup_dev(struct device *parent, char *name)
+{
+	int err = 0;
+	struct char_dev_user *dev_usr;
+
+	CG2900_INFO("CharDev: setup_dev");
+
+	dev_usr = kzalloc(sizeof(*dev_usr), GFP_KERNEL);
+	if (!dev_usr) {
+		CG2900_ERR("Couldn't allocate dev_usr");
+		return -ENOMEM;
+	}
+
+	/* Store device name */
+	dev_usr->name = name;
+
+	dev_usr->miscdev = kzalloc(sizeof(*(dev_usr->miscdev)),
+				       GFP_KERNEL);
+	if (!dev_usr->miscdev) {
+		CG2900_ERR("Couldn't allocate char_dev");
+		err = -ENOMEM;
+		goto err_free_usr;
+	}
+
+	/* Prepare miscdevice struct before registering the device */
+	dev_usr->miscdev->minor = MISC_DYNAMIC_MINOR;
+	dev_usr->miscdev->name = name;
+	dev_usr->miscdev->fops = &char_dev_fops;
+	dev_usr->miscdev->parent = parent;
+
+	err = misc_register(dev_usr->miscdev);
+	if (err) {
+		CG2900_ERR("Error %d registering misc dev!", err);
+		goto err_free_dev;
+	}
+
+	CG2900_INFO("Added char device %s with major 0x%X and minor 0x%X",
+		    name, MAJOR(dev_usr->miscdev->this_device->devt),
+		    MINOR(dev_usr->miscdev->this_device->devt));
+
+	mutex_init(&dev_usr->read_mutex);
+	mutex_init(&dev_usr->write_mutex);
+
+	skb_queue_head_init(&dev_usr->rx_queue);
+
+	list_add_tail(&dev_usr->list, &char_info->dev_users);
+	return 0;
+
+err_free_dev:
+	kfree(dev_usr->miscdev);
+	dev_usr->miscdev = NULL;
+err_free_usr:
+	kfree(dev_usr);
+	return err;
+}
+
+/**
+ * remove_dev() - Remove char device structure for device.
+ * @dev_usr:	Char device user.
+ *
+ * The remove_dev() function releases the char_dev structure for this device.
+ */
+static void remove_dev(struct char_dev_user *dev_usr)
+{
+	CG2900_INFO("CharDev: remove_dev");
+
+	if (!dev_usr)
+		return;
+
+	skb_queue_purge(&dev_usr->rx_queue);
+
+	mutex_destroy(&dev_usr->read_mutex);
+	mutex_destroy(&dev_usr->write_mutex);
+
+	/* Remove device node in file system. */
+	misc_deregister(dev_usr->miscdev);
+	kfree(dev_usr->miscdev);
+	dev_usr->miscdev = NULL;
+
+	kfree(dev_usr);
+}
+
+/* External functions */
+
+void cg2900_char_devices_init(struct miscdevice *dev)
+{
+	CG2900_INFO("cg2900_char_devices_init");
+
+	if (!dev) {
+		CG2900_ERR("NULL supplied for dev");
+		return;
+	}
+
+	if (char_info) {
+		CG2900_ERR("Char devices already initiated");
+		return;
+	}
+
+	/* Initialize private data. */
+	char_info = kzalloc(sizeof(*char_info), GFP_ATOMIC);
+	if (!char_info) {
+		CG2900_ERR("Could not alloc char_info struct.");
+		return;
+	}
+
+	mutex_init(&char_info->open_mutex);
+	INIT_LIST_HEAD(&char_info->dev_users);
+
+	setup_dev(dev->this_device, CG2900_BT_CMD);
+	setup_dev(dev->this_device, CG2900_BT_ACL);
+	setup_dev(dev->this_device, CG2900_BT_EVT);
+	setup_dev(dev->this_device, CG2900_FM_RADIO);
+	setup_dev(dev->this_device, CG2900_GNSS);
+	setup_dev(dev->this_device, CG2900_DEBUG);
+	setup_dev(dev->this_device, CG2900_STE_TOOLS);
+	setup_dev(dev->this_device, CG2900_HCI_LOGGER);
+	setup_dev(dev->this_device, CG2900_US_CTRL);
+	setup_dev(dev->this_device, CG2900_BT_AUDIO);
+	setup_dev(dev->this_device, CG2900_FM_RADIO_AUDIO);
+	setup_dev(dev->this_device, CG2900_CORE);
+}
+
+void cg2900_char_devices_exit(void)
+{
+	struct list_head *cursor, *next;
+	struct char_dev_user *tmp;
+
+	CG2900_INFO("cg2900_char_devices_exit");
+
+	if (!char_info)
+		return;
+
+	list_for_each_safe(cursor, next, &char_info->dev_users) {
+		tmp = list_entry(cursor, struct char_dev_user, list);
+		list_del(cursor);
+		remove_dev(tmp);
+	}
+
+	mutex_destroy(&char_info->open_mutex);
+
+	kfree(char_info);
+	char_info = NULL;
+}
+
+MODULE_AUTHOR("Henrik Possung ST-Ericsson");
+MODULE_AUTHOR("Par-Gunnar Hjalmdahl ST-Ericsson");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ST-Ericsson CG2900 Char Devices Driver");
diff --git a/drivers/mfd/cg2900/cg2900_char_devices.h
b/drivers/mfd/cg2900/cg2900_char_devices.h
new file mode 100644
index 0000000..65d2b7f
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_char_devices.h
@@ -0,0 +1,36 @@
+/*
+ * drivers/mfd/cg2900/cg2900_char_devices.h
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@...ricsson.com) for
ST-Ericsson.
+ * Henrik Possung (henrik.possung@...ricsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@...ricsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@...ricsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@...ricsson.com) for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson connectivity controller.
+ */
+
+#ifndef _CG2900_CHAR_DEVICES_H_
+#define _CG2900_CHAR_DEVICES_H_
+
+#include <linux/miscdevice.h>
+
+/**
+ * cg2900_char_devices_init() - Initialize char device module.
+ * @parent:	Parent device for the driver.
+ *
+ * Returns:
+ *   0 if success.
+ *   Negative value upon error.
+ */
+extern int cg2900_char_devices_init(struct miscdevice *parent);
+
+/**
+ * cg2900_char_devices_exit() - Release the char device module.
+ */
+extern void cg2900_char_devices_exit(void);
+
+#endif /* _CG2900_CHAR_DEVICES_H_ */
diff --git a/drivers/mfd/cg2900/cg2900_core.c b/drivers/mfd/cg2900/cg2900_core.c
new file mode 100644
index 0000000..8c26202
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_core.c
@@ -0,0 +1,2287 @@
+/*
+ * drivers/mfd/cg2900/cg2900_core.c
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@...ricsson.com) for
ST-Ericsson.
+ * Henrik Possung (henrik.possung@...ricsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@...ricsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@...ricsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@...ricsson.com) for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson CG2900 GPS/BT/FM controller.
+ */
+
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/skbuff.h>
+#include <linux/gfp.h>
+#include <linux/stat.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <asm/byteorder.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci.h>
+
+#include <linux/mfd/cg2900.h>
+#include <mach/cg2900_devices.h>
+#include "cg2900_core.h"
+#include "cg2900_char_devices.h"
+#include "cg2900_debug.h"
+#include "hci_defines.h"
+
+/* Device names */
+#define CG2900_CDEV_NAME		"cg2900_core_test"
+#define CG2900_CLASS_NAME		"cg2900_class"
+#define CG2900_DEVICE_NAME		"cg2900_driver"
+#define CORE_WQ_NAME			"cg2900_core_wq"
+
+#define SET_MAIN_STATE(__core_new_state) \
+	CG2900_SET_STATE("main_state", core_info->main_state, \
+			 __core_new_state)
+#define SET_BOOT_STATE(__core_new_state) \
+	CG2900_SET_STATE("boot_state", core_info->boot_state, __core_new_state)
+#define SET_TRANSPORT_STATE(__core_new_state) \
+	CG2900_SET_STATE("transport_state", core_info->transport_state, \
+			 __core_new_state)
+
+#define LOGGER_DIRECTION_TX		0
+#define LOGGER_DIRECTION_RX		1
+
+/* Number of bytes to reserve at start of sk_buffer when receiving packet */
+#define RX_SKB_RESERVE			8
+
+/*
+ * Timeout values
+ */
+#define CHIP_STARTUP_TIMEOUT		(15000)	/* ms */
+#define CHIP_SHUTDOWN_TIMEOUT		(15000)	/* ms */
+#define LINE_TOGGLE_DETECT_TIMEOUT	(50)	/* ms */
+#define CHIP_READY_TIMEOUT		(100)	/* ms */
+#define REVISION_READOUT_TIMEOUT	(500)	/* ms */
+
+/*
+ * We can have up to 32 char devs with current bit mask and we also have
+ * the parent device here in the transport so that is 33 devices in total.
+ */
+#define MAX_NBR_OF_DEVS			33
+
+/* Default H4 channels which may change depending on connected controller */
+#define HCI_FM_RADIO_H4_CHANNEL		0x08
+#define HCI_GNSS_H4_CHANNEL		0x09
+
+/*
+ *	Internal type definitions
+ */
+
+/**
+ * enum main_state - Main-state for CG2900 Core.
+ * @CORE_INITIALIZING:	CG2900 Core initializing.
+ * @CORE_IDLE:		No user registered to CG2900 Core.
+ * @CORE_BOOTING:	CG2900 Core booting after first user is registered.
+ * @CORE_CLOSING:	CG2900 Core closing after last user has deregistered.
+ * @CORE_RESETING:	CG2900 Core reset requested.
+ * @CORE_ACTIVE:	CG2900 Core up and running with at least one user.
+ */
+enum main_state {
+	CORE_INITIALIZING,
+	CORE_IDLE,
+	CORE_BOOTING,
+	CORE_CLOSING,
+	CORE_RESETING,
+	CORE_ACTIVE
+};
+
+/**
+ * enum boot_state - BOOT-state for CG2900 Core.
+ * @BOOT_NOT_STARTED:				Boot has not yet started.
+ * @BOOT_READ_LOCAL_VERSION_INFORMATION:	ReadLocalVersionInformation
+ *						command has been sent.
+ * @BOOT_READY:					CG2900 Core boot is ready.
+ * @BOOT_FAILED:				CG2900 Core boot failed.
+ */
+enum boot_state {
+	BOOT_NOT_STARTED,
+	BOOT_READ_LOCAL_VERSION_INFORMATION,
+	BOOT_READY,
+	BOOT_FAILED
+};
+
+/**
+ * enum transport_state - State for the CG2900 transport.
+ * @TRANS_INITIALIZING:	Transport initializing.
+ * @TRANS_OPENED:	Transport is opened (data can be sent).
+ * @TRANS_CLOSED:	Transport is closed (data cannot be sent).
+ */
+enum transport_state {
+	TRANS_INITIALIZING,
+	TRANS_OPENED,
+	TRANS_CLOSED
+};
+
+/**
+ * struct cg2900_users - Stores all current users of CG2900 Core.
+ * @bt_cmd:		BT command channel user.
+ * @bt_acl:		BT ACL channel user.
+ * @bt_evt:		BT event channel user.
+ * @fm_radio:		FM radio channel user.
+ * @gnss GNSS:		GNSS channel user.
+ * @debug Debug:	Internal debug channel user.
+ * @ste_tools:		ST-E tools channel user.
+ * @hci_logger:		HCI logger channel user.
+ * @us_ctrl:		User space control channel user.
+ * @bt_audio:		BT audio command channel user.
+ * @fm_radio_audio:	FM audio command channel user.
+ * @core:		Core command channel user.
+ * @nbr_of_users:	Number of users currently registered (not including
+ *			the HCI logger).
+ */
+struct cg2900_users {
+	struct cg2900_device	*bt_cmd;
+	struct cg2900_device	*bt_acl;
+	struct cg2900_device	*bt_evt;
+	struct cg2900_device	*fm_radio;
+	struct cg2900_device	*gnss;
+	struct cg2900_device	*debug;
+	struct cg2900_device	*ste_tools;
+	struct cg2900_device	*hci_logger;
+	struct cg2900_device	*us_ctrl;
+	struct cg2900_device	*bt_audio;
+	struct cg2900_device	*fm_radio_audio;
+	struct cg2900_device	*core;
+	unsigned int		nbr_of_users;
+};
+
+/**
+  * struct local_chip_info - Stores local controller info.
+  * @version_set:		true if version data is valid.
+  * @hci_version:		HCI version of local controller.
+  * @hci_revision:		HCI revision of local controller.
+  * @lmp_pal_version:		LMP/PAL version of local controller.
+  * @manufacturer:		Manufacturer of local controller.
+  * @lmp_pal_subversion:	LMP/PAL sub-version of local controller.
+  *
+  * According to Bluetooth HCI Read Local Version Information command.
+  */
+struct local_chip_info {
+	bool	version_set;
+	u8	hci_version;
+	u16	hci_revision;
+	u8	lmp_pal_version;
+	u16	manufacturer;
+	u16	lmp_pal_subversion;
+};
+
+/**
+ * struct chip_handler_item - Structure to store chip handler cb.
+ * @list:	list_head struct.
+ * @cb:		Chip handler callback struct.
+ */
+struct chip_handler_item {
+	struct list_head		list;
+	struct cg2900_id_callbacks	cb;
+};
+
+/**
+ * struct cg2900_work_struct - Work structure for CG2900 Core module.
+ * @work:	Work structure.
+ * @data:	Pointer to private data.
+ *
+ * This structure is used to pack work for work queue.
+ */
+struct cg2900_work_struct{
+	struct work_struct	work;
+	void			*data;
+};
+
+/**
+ * struct test_char_dev_info - Stores device information.
+ * @test_miscdev:	Registered Misc Device.
+ * @rx_queue:		RX data queue.
+ */
+struct test_char_dev_info {
+	struct miscdevice	test_miscdev;
+	struct sk_buff_head	rx_queue;
+};
+
+/**
+ * struct trans_info - Stores transport information.
+ * @dev:	Transport device.
+ * @cb:		Transport cb.
+ */
+struct trans_info {
+	struct cg2900_trans_dev		dev;
+	struct cg2900_trans_callbacks	cb;
+};
+
+/**
+ * struct core_info - Main info structure for CG2900 Core.
+ * @users:		Stores all users of CG2900 Core.
+ * @local_chip_info:	Stores information of local controller.
+ * @main_state:		Current Main-state of CG2900 Core.
+ * @boot_state:		Current BOOT-state of CG2900 Core.
+ * @transport_state:	Current TRANSPORT-state of CG2900 Core.
+ * @wq:			CG2900 Core workqueue.
+ * @hci_logger_config:	Stores HCI logger configuration.
+ * @dev:		Device structure for STE Connectivity driver.
+ * @chip_dev:		Device structure for chip driver.
+ * @h4_channels:	HCI H:4 channel used by this device.
+ * @test_char_dev:	Stores information of test char dev.
+ * @trans_info:		Stores information about current transport.
+ */
+struct core_info {
+	struct cg2900_users		users;
+	struct local_chip_info		local_chip_info;
+	enum main_state			main_state;
+	enum boot_state			boot_state;
+	enum transport_state		transport_state;
+	struct workqueue_struct		*wq;
+	struct cg2900_hci_logger_config	hci_logger_config;
+	struct miscdevice		*dev;
+	struct cg2900_chip_dev		chip_dev;
+	struct cg2900_h4_channels	h4_channels;
+	struct test_char_dev_info	*test_char_dev;
+	struct trans_info		*trans_info;
+};
+
+/*
+ *	Internal variable declarations
+ */
+
+/*
+ * core_info - Main information object for CG2900 Core.
+ */
+static struct core_info *core_info;
+
+/* Module parameters */
+int cg2900_debug_level = CG2900_DEFAULT_DEBUG_LEVEL;
+EXPORT_SYMBOL(cg2900_debug_level);
+
+u8 bd_address[] = {0x00, 0xBE, 0xAD, 0xDE, 0x80, 0x00};
+EXPORT_SYMBOL(bd_address);
+int bd_addr_count = BT_BDADDR_SIZE;
+
+/* Setting default values to ST-E CG2900 */
+int default_manufacturer = 0x30;
+EXPORT_SYMBOL(default_manufacturer);
+int default_hci_revision = 0x0700;
+EXPORT_SYMBOL(default_hci_revision);
+int default_sub_version = 0x0011;
+EXPORT_SYMBOL(default_sub_version);
+
+static int sleep_timeout_ms;
+
+/*
+ * chip_handlers - List of the register handlers for different chips.
+ */
+LIST_HEAD(chip_handlers);
+
+/*
+ * main_wait_queue - Main Wait Queue in CG2900 Core.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(main_wait_queue);
+
+/*
+ * main_wait_queue - Char device Wait Queue in CG2900 Core.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(char_wait_queue);
+
+/*
+ *	Internal functions
+ */
+
+/**
+ * free_user_dev - Frees user device and also sets it to NULL to inform caller.
+ * @dev:	Pointer to user device.
+ */
+static void free_user_dev(struct cg2900_device **dev)
+{
+	if (*dev) {
+		kfree((*dev)->cb);
+		kfree(*dev);
+		*dev = NULL;
+	}
+}
+
+/**
+ * handle_reset_of_user - Calls the reset callback and frees the device.
+ * @dev:	Pointer to CG2900 device.
+ */
+static void handle_reset_of_user(struct cg2900_device **dev)
+{
+	if (*dev) {
+		if ((*dev)->cb->reset_cb)
+			(*dev)->cb->reset_cb((*dev));
+		free_user_dev(dev);
+	}
+}
+
+/**
+ * transmit_skb_to_chip() - Transmit buffer to the transport.
+ * @skb:	Data packet.
+ * @use_logger:	True if HCI logger shall be used, false otherwise.
+ *
+ * The transmit_skb_to_chip() function transmit buffer to the transport.
+ * If enabled, copy the transmitted data to the HCI logger as well.
+ */
+static void transmit_skb_to_chip(struct sk_buff *skb, bool use_logger)
+{
+	int err;
+	struct sk_buff *skb_log;
+	struct trans_info *trans_info = core_info->trans_info;
+	struct cg2900_device *logger;
+
+	CG2900_DBG_DATA("transmit_skb_to_chip %d bytes. First byte 0x%02X",
+			skb->len, *(skb->data));
+
+	if (TRANS_CLOSED == core_info->transport_state) {
+		CG2900_ERR("Trying to write on a closed channel");
+		kfree_skb(skb);
+		return;
+	}
+
+	/*
+	 * If HCI logging is enabled for this channel, copy the data to
+	 * the HCI logging output.
+	 */
+	logger = core_info->users.hci_logger;
+	if (!use_logger || !logger)
+		goto transmit;
+
+	/*
+	 * Alloc a new sk_buff and copy the data into it. Then send it to
+	 * the HCI logger.
+	 */
+	skb_log = alloc_skb(skb->len + 1, GFP_ATOMIC);
+	if (!skb_log) {
+		CG2900_ERR("Couldn't allocate skb_log");
+		goto transmit;
+	}
+
+	memcpy(skb_put(skb_log, skb->len), skb->data, skb->len);
+	skb_log->data[0] = (u8) LOGGER_DIRECTION_TX;
+
+	if (logger->cb->read_cb)
+		logger->cb->read_cb(logger, skb_log);
+
+transmit:
+	CG2900_DBG_DATA_CONTENT("Length: %d Data: %02X %02X %02X %02X %02X "
+				"%02X %02X %02X %02X %02X %02X %02X",
+				skb->len,
+				skb->data[0], skb->data[1], skb->data[2],
+				skb->data[3], skb->data[4], skb->data[5],
+				skb->data[6], skb->data[7], skb->data[8],
+				skb->data[9], skb->data[10], skb->data[11]);
+
+	if (trans_info && trans_info->cb.write) {
+		err = trans_info->cb.write(&trans_info->dev, skb);
+		if (err)
+			CG2900_ERR("Transport write failed (%d)", err);
+	} else {
+		CG2900_ERR("No way to write to chip");
+		err = -EPERM;
+	}
+
+	if (err)
+		kfree_skb(skb);
+}
+
+/**
+ * create_and_send_bt_cmd() - Copy and send sk_buffer.
+ * @data:	Data to send.
+ * @length:	Length in bytes of data.
+ *
+ * The create_and_send_bt_cmd() function allocates sk_buffer, copy supplied
+ * data to it, and send the sk_buffer to the transport.
+ */
+static void create_and_send_bt_cmd(void *data, int length)
+{
+	struct sk_buff *skb;
+
+	skb = cg2900_alloc_skb(length, GFP_ATOMIC);
+	if (!skb) {
+		CG2900_ERR("Couldn't allocate sk_buff with length %d",
+			     length);
+		return;
+	}
+
+	memcpy(skb_put(skb, length), data, length);
+	skb_push(skb, CG2900_SKB_RESERVE);
+	skb->data[0] = HCI_BT_CMD_H4_CHANNEL;
+
+	transmit_skb_to_chip(skb, core_info->hci_logger_config.bt_cmd_enable);
+}
+
+/**
+ * chip_not_detected() - Called when it is not possible to detect the chip.
+ *
+ * This function sets chip information to default values if it is not possible
+ * to read out information from the chip. This is common when running module
+ * tests.
+ */
+static void chip_not_detected(void)
+{
+	struct list_head *cursor;
+	struct chip_handler_item *tmp;
+
+	CG2900_ERR("Could not read out revision from the chip. This is "
+		   "typical when running stubbed CG2900.\n"
+		   "Switching to default value:\n"
+		   "\tman 0x%04X\n"
+		   "\trev 0x%04X\n"
+		   "\tsub 0x%04X",
+		   default_manufacturer,
+		   default_hci_revision,
+		   default_sub_version);
+
+	core_info->chip_dev.chip.manufacturer = default_manufacturer;
+	core_info->chip_dev.chip.hci_revision = default_hci_revision;
+	core_info->chip_dev.chip.hci_sub_version = default_sub_version;
+
+	memset(&(core_info->chip_dev.cb), 0, sizeof(core_info->chip_dev.cb));
+
+	/* Find the handler for our default chip */
+	list_for_each(cursor, &chip_handlers) {
+		tmp = list_entry(cursor, struct chip_handler_item, list);
+		if (tmp->cb.check_chip_support(&(core_info->chip_dev))) {
+			CG2900_INFO("Chip handler found");
+			SET_BOOT_STATE(BOOT_READY);
+			break;
+		}
+	}
+}
+
+/**
+ * enable_hci_logger() - Enable HCI logger for each device.
+ * @skb:	Received sk buffer.
+ *
+ * The enable_hci_logger() change HCI logger configuration for all registered
+ * devices.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EACCES if bad structure was supplied.
+ */
+static int enable_hci_logger(struct sk_buff *skb)
+{
+	struct cg2900_users *users;
+	struct cg2900_hci_logger_config	*config;
+
+	if (skb->len != sizeof(*config)) {
+		CG2900_ERR("Trying to configure HCI logger with bad structure");
+		return -EACCES;
+	}
+
+	users = &(core_info->users);
+	config = &(core_info->hci_logger_config);
+
+	/* First store the logger config */
+	memcpy(config, skb->data, sizeof(*config));
+
+	/* Then go through all devices and set the right settings */
+	if (users->bt_cmd)
+		users->bt_cmd->logger_enabled = config->bt_cmd_enable;
+	if (users->bt_audio)
+		users->bt_audio->logger_enabled = config->bt_audio_enable;
+	if (users->bt_acl)
+		users->bt_acl->logger_enabled = config->bt_acl_enable;
+	if (users->bt_evt)
+		users->bt_evt->logger_enabled = config->bt_evt_enable;
+	if (users->fm_radio)
+		users->fm_radio->logger_enabled = config->fm_radio_enable;
+	if (users->fm_radio_audio)
+		users->fm_radio_audio->logger_enabled =
+				config->fm_radio_audio_enable;
+	if (users->gnss)
+		users->gnss->logger_enabled = config->gnss_enable;
+
+	kfree_skb(skb);
+	return 0;
+}
+
+/**
+ * find_bt_audio_user() - Check if data packet is an audio related packet.
+ * @h4_channel:	H4 channel.
+ * @dev:	Stored CG2900 device.
+ * @skb:	skb with received packet.
+ * Returns:
+ *   0 - if no error occurred.
+ *   -ENXIO - if cg2900_device not found.
+ */
+static int find_bt_audio_user(int h4_channel, struct cg2900_device **dev,
+				  const struct sk_buff * const skb)
+{
+	if (core_info->chip_dev.cb.is_bt_audio_user &&
+	    core_info->chip_dev.cb.is_bt_audio_user(h4_channel, skb)) {
+		*dev = core_info->users.bt_audio;
+		if (!(*dev)) {
+			CG2900_ERR("H:4 channel not registered in core_info: "
+				     "0x%X", h4_channel);
+			return -ENXIO;
+		}
+	}
+	return 0;
+}
+
+/**
+ * find_fm_audio_user() - Check if data packet is an audio related packet.
+ * @h4_channel:	H4 channel.
+ * @dev:	Stored CG2900 device.
+ * @skb:	skb with received packet.
+ * Returns:
+ *   0 if no error occurred.
+ *   -ENXIO if cg2900_device not found.
+ */
+static int find_fm_audio_user(int h4_channel, struct cg2900_device **dev,
+				  const struct sk_buff * const skb)
+{
+	if (core_info->chip_dev.cb.is_fm_audio_user &&
+	    core_info->chip_dev.cb.is_fm_audio_user(h4_channel, skb)) {
+		*dev = core_info->users.fm_radio_audio;
+		if (!(*dev)) {
+			CG2900_ERR("H:4 channel not registered in core_info: "
+				     "0x%X", h4_channel);
+			return -ENXIO;
+		}
+	}
+	return 0;
+}
+
+/**
+ * find_h4_user() - Get H4 user based on supplied H4 channel.
+ * @h4_channel:	H4 channel.
+ * @dev:	Stored CG2900 device.
+ * @skb:	(optional) skb with received packet. Set to NULL if NA.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EINVAL if bad channel is supplied or no user was found.
+ *   -ENXIO if channel is audio channel but not registered with CG2900.
+ */
+static int find_h4_user(int h4_channel, struct cg2900_device **dev,
+			const struct sk_buff * const skb)
+{
+	int err = 0;
+	struct cg2900_users *users = &(core_info->users);
+	struct cg2900_h4_channels *chan = &(core_info->h4_channels);
+
+	if (h4_channel == chan->bt_cmd_channel) {
+		*dev = users->bt_cmd;
+	} else if (h4_channel == chan->bt_acl_channel) {
+		*dev = users->bt_acl;
+	} else if (h4_channel == chan->bt_evt_channel) {
+		*dev = users->bt_evt;
+		/* Check if it's event generated by previously sent audio user
+		 * command. If so then that event should be dispatched to audio
+		 * user*/
+		err = find_bt_audio_user(h4_channel, dev, skb);
+	} else if (h4_channel == chan->gnss_channel) {
+		*dev = users->gnss;
+	} else if (h4_channel == chan->fm_radio_channel) {
+		*dev = users->fm_radio;
+		/* Check if it's an event generated by previously sent audio
+		 * user command. If so then that event should be dispatched to
+		 * audio user */
+		err = find_fm_audio_user(h4_channel, dev, skb);
+	} else if (h4_channel == chan->debug_channel) {
+		*dev = users->debug;
+	} else if (h4_channel == chan->ste_tools_channel) {
+		*dev = users->ste_tools;
+	} else if (h4_channel == chan->hci_logger_channel) {
+		*dev = users->hci_logger;
+	} else if (h4_channel == chan->us_ctrl_channel) {
+		*dev = users->us_ctrl;
+	} else if (h4_channel == chan->core_channel) {
+		*dev = users->core;
+	} else {
+		*dev = NULL;
+		CG2900_ERR("Bad H:4 channel supplied: 0x%X", h4_channel);
+		return -EINVAL;
+	}
+
+	return err;
+}
+
+/**
+ * add_h4_user() - Add H4 user to user storage based on supplied H4 channel.
+ * @dev:	Stored CG2900 device.
+ * @name:	Device name to identify different devices that are using
+ *		the same H4 channel.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EINVAL if NULL pointer or bad channel is supplied.
+ *   -EBUSY if there already is a user for supplied channel.
+ */
+static int add_h4_user(struct cg2900_device *dev, const char * const name)
+{
+	int err = 0;
+	struct cg2900_users *users = &(core_info->users);
+	struct cg2900_hci_logger_config	*config =
+			&(core_info->hci_logger_config);
+	struct cg2900_h4_channels *chan = &(core_info->h4_channels);
+
+	if (!dev) {
+		CG2900_ERR("NULL device supplied");
+		return -EINVAL;
+	}
+
+	if (dev->h4_channel == chan->bt_cmd_channel) {
+		if (!users->bt_cmd &&
+		    0 == strncmp(name, CG2900_BT_CMD, CG2900_MAX_NAME_SIZE)) {
+			users->bt_cmd = dev;
+			users->bt_cmd->logger_enabled = config->bt_cmd_enable;
+			(users->nbr_of_users)++;
+		} else if (!users->bt_audio &&
+			   0 == strncmp(name, CG2900_BT_AUDIO,
+					CG2900_MAX_NAME_SIZE)) {
+			users->bt_audio = dev;
+			users->bt_audio->logger_enabled =
+					config->bt_audio_enable;
+			(users->nbr_of_users)++;
+		} else {
+			err = -EBUSY;
+			CG2900_ERR("name %s bt_cmd 0x%X  bt_audio 0x%X",
+				     name, (int)users->bt_cmd,
+				     (int)users->bt_audio);
+		}
+	} else if (dev->h4_channel == chan->bt_acl_channel) {
+		if (!users->bt_acl) {
+			users->bt_acl = dev;
+			users->bt_acl->logger_enabled = config->bt_acl_enable;
+			(users->nbr_of_users)++;
+		} else {
+			err = -EBUSY;
+		}
+	} else if (dev->h4_channel == chan->bt_evt_channel) {
+		if (!users->bt_evt) {
+			users->bt_evt = dev;
+			users->bt_evt->logger_enabled = config->bt_evt_enable;
+			(users->nbr_of_users)++;
+		} else {
+			err = -EBUSY;
+		}
+	} else if (dev->h4_channel == chan->gnss_channel) {
+		if (!users->gnss) {
+			users->gnss = dev;
+			users->gnss->logger_enabled = config->gnss_enable;
+			(users->nbr_of_users)++;
+		} else {
+			err = -EBUSY;
+		}
+	} else if (dev->h4_channel == chan->fm_radio_channel) {
+		if (!users->fm_radio &&
+		    0 == strncmp(name, CG2900_FM_RADIO,
+				 CG2900_MAX_NAME_SIZE)) {
+			users->fm_radio = dev;
+			users->fm_radio->logger_enabled =
+					config->fm_radio_enable;
+			(users->nbr_of_users)++;
+		} else if (!users->fm_radio_audio &&
+			   0 == strncmp(name, CG2900_FM_RADIO_AUDIO,
+					CG2900_MAX_NAME_SIZE)) {
+			users->fm_radio_audio = dev;
+			users->fm_radio_audio->logger_enabled =
+					config->fm_radio_audio_enable;
+			(users->nbr_of_users)++;
+		} else {
+			err = -EBUSY;
+		}
+	} else if (dev->h4_channel == chan->debug_channel) {
+		if (!users->debug)
+			users->debug = dev;
+		else
+			err = -EBUSY;
+	} else if (dev->h4_channel == chan->ste_tools_channel) {
+		if (!users->ste_tools)
+			users->ste_tools = dev;
+		else
+			err = -EBUSY;
+	} else if (dev->h4_channel == chan->hci_logger_channel) {
+		if (!users->hci_logger)
+			users->hci_logger = dev;
+		else
+			err = -EBUSY;
+	} else if (dev->h4_channel == chan->us_ctrl_channel) {
+		if (!users->us_ctrl)
+			users->us_ctrl = dev;
+		else
+			err = -EBUSY;
+	} else if (dev->h4_channel == chan->core_channel) {
+		if (!users->core) {
+			(users->nbr_of_users)++;
+			users->core = dev;
+		} else {
+			err = -EBUSY;
+		}
+	} else {
+		err = -EINVAL;
+		CG2900_ERR("Bad H:4 channel supplied: 0x%X", dev->h4_channel);
+	}
+
+	if (err)
+		CG2900_ERR("H:4 channel 0x%X, not registered (%d)",
+			   dev->h4_channel, err);
+
+	return err;
+}
+
+/**
+ * remove_h4_user() - Remove H4 user from user storage.
+ * @dev:	Stored CG2900 device.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EINVAL if NULL pointer is supplied, bad channel is supplied, or if there
+ *   is no user for supplied channel.
+ */
+static int remove_h4_user(struct cg2900_device **dev)
+{
+	int err = 0;
+	struct cg2900_users *users = &(core_info->users);
+	struct cg2900_h4_channels *chan = &(core_info->h4_channels);
+	struct cg2900_chip_callbacks *cb = &(core_info->chip_dev.cb);
+
+	if (!dev || !(*dev)) {
+		CG2900_ERR("NULL device supplied");
+		return -EINVAL;
+	}
+
+	if ((*dev)->h4_channel == chan->bt_cmd_channel) {
+		CG2900_DBG("bt_cmd 0x%X bt_audio 0x%X dev 0x%X",
+			     (int)users->bt_cmd,
+			     (int)users->bt_audio, (int)*dev);
+
+		if (*dev == users->bt_cmd) {
+			users->bt_cmd = NULL;
+			(users->nbr_of_users)--;
+		} else if (*dev == users->bt_audio) {
+			users->bt_audio = NULL;
+			(users->nbr_of_users)--;
+		} else
+			err = -EINVAL;
+
+		CG2900_DBG("bt_cmd 0x%X bt_audio 0x%X dev 0x%X",
+			     (int)users->bt_cmd,
+			     (int)users->bt_audio, (int)*dev);
+
+		/*
+		 * If both BT Command channel users are de-registered we
+		 * inform the chip handler.
+		 */
+		if (!users->bt_cmd && !users->bt_audio &&
+		    cb->last_bt_user_removed)
+			cb->last_bt_user_removed(&(core_info->chip_dev));
+	} else if ((*dev)->h4_channel == chan->bt_acl_channel) {
+		if (*dev == users->bt_acl) {
+			users->bt_acl = NULL;
+			(users->nbr_of_users)--;
+		} else
+			err = -EINVAL;
+	} else if ((*dev)->h4_channel == chan->bt_evt_channel) {
+		if (*dev == users->bt_evt) {
+			users->bt_evt = NULL;
+			(users->nbr_of_users)--;
+		} else
+			err = -EINVAL;
+	} else if ((*dev)->h4_channel == chan->gnss_channel) {
+		if (*dev == users->gnss) {
+			users->gnss = NULL;
+			(users->nbr_of_users)--;
+		} else
+			err = -EINVAL;
+
+		/*
+		 * If the GNSS channel user is de-registered we inform
+		 * the chip handler.
+		 */
+		if (users->gnss == NULL && cb->last_gnss_user_removed)
+			cb->last_gnss_user_removed(&(core_info->chip_dev));
+	} else if ((*dev)->h4_channel == chan->fm_radio_channel) {
+		if (*dev == users->fm_radio) {
+			users->fm_radio = NULL;
+			(users->nbr_of_users)--;
+		} else if (*dev == users->fm_radio_audio) {
+			users->fm_radio_audio = NULL;
+			(users->nbr_of_users)--;
+		} else
+			err = -EINVAL;
+
+		/*
+		 * If both FM Radio channel users are de-registered we inform
+		 * the chip handler.
+		 */
+		if (!users->fm_radio && !users->fm_radio_audio &&
+		    cb->last_fm_user_removed)
+			cb->last_fm_user_removed(&(core_info->chip_dev));
+	} else if ((*dev)->h4_channel == chan->debug_channel) {
+		if (*dev == users->debug)
+			users->debug = NULL;
+		else
+			err = -EINVAL;
+	} else if ((*dev)->h4_channel == chan->ste_tools_channel) {
+		if (*dev == users->ste_tools)
+			users->ste_tools = NULL;
+		else
+			err = -EINVAL;
+	} else if ((*dev)->h4_channel == chan->hci_logger_channel) {
+		if (*dev == users->hci_logger)
+			users->hci_logger = NULL;
+		else
+			err = -EINVAL;
+	} else if ((*dev)->h4_channel == chan->us_ctrl_channel) {
+		if (*dev == users->us_ctrl)
+			users->us_ctrl = NULL;
+		else
+			err = -EINVAL;
+	} else if ((*dev)->h4_channel == chan->core_channel) {
+		if (*dev == users->core) {
+			users->core = NULL;
+			(users->nbr_of_users)--;
+		} else
+			err = -EINVAL;
+	} else {
+		CG2900_ERR("Bad H:4 channel supplied: 0x%X",
+			     (*dev)->h4_channel);
+		return -EINVAL;
+	}
+
+	if (err)
+		CG2900_ERR("Trying to remove device that was not registered");
+
+	/*
+	 * Free the device even if there is an error with the device.
+	 * Also set to NULL to inform caller about the free.
+	 */
+	free_user_dev(dev);
+
+	return err;
+}
+
+/**
+ * chip_startup() - Start the connectivity controller and download
patches and settings.
+ */
+static void chip_startup(void)
+{
+	struct hci_command_hdr cmd;
+
+	CG2900_INFO("chip_startup");
+
+	SET_MAIN_STATE(CORE_BOOTING);
+	SET_BOOT_STATE(BOOT_NOT_STARTED);
+
+	/*
+	 * Transmit HCI reset command to ensure the chip is using
+	 * the correct transport
+	 */
+	cmd.opcode = cpu_to_le16(HCI_OP_RESET);
+	cmd.plen = 0; /* No parameters for HCI reset */
+	create_and_send_bt_cmd(&cmd, sizeof(cmd));
+}
+
+/**
+ * chip_shutdown() - Reset and power the chip off.
+ */
+static void chip_shutdown(void)
+{
+	int err = 0;
+	struct trans_info *trans_info = core_info->trans_info;
+	struct cg2900_chip_callbacks *cb = &(core_info->chip_dev.cb);
+
+	CG2900_INFO("chip_shutdown");
+
+	/* First do a quick power switch of the chip to assure a good state */
+	if (trans_info && trans_info->cb.set_chip_power)
+		trans_info->cb.set_chip_power(false);
+
+	/*
+	 * Wait 50ms before continuing to be sure that the chip detects
+	 * chip power off.
+	 */
+	schedule_timeout_interruptible(
+			msecs_to_jiffies(LINE_TOGGLE_DETECT_TIMEOUT));
+
+	if (trans_info && trans_info->cb.set_chip_power)
+		trans_info->cb.set_chip_power(true);
+
+	/* Wait 100ms before continuing to be sure that the chip is ready */
+	schedule_timeout_interruptible(msecs_to_jiffies(CHIP_READY_TIMEOUT));
+
+	/*
+	 * Let the chip handler finish the reset if any callback is registered.
+	 * Otherwise we are finished.
+	 */
+	if (!cb->chip_shutdown) {
+		CG2900_DBG("No registered handler. Finishing shutdown.");
+		cg2900_chip_shutdown_finished(err);
+		return;
+	}
+
+	err = cb->chip_shutdown(&(core_info->chip_dev));
+	if (err) {
+		CG2900_ERR("chip_shutdown failed (%d). Finishing shutdown.",
+			   err);
+		cg2900_chip_shutdown_finished(err);
+	}
+}
+
+/**
+ * handle_reset_cmd_complete_evt() - Handle a received HCI Command
Complete event for a Reset command.
+ * @data:	Pointer to received HCI data packet.
+ *
+ * Returns:
+ *   True,  if packet was handled internally,
+ *   False, otherwise.
+ */
+static bool handle_reset_cmd_complete_evt(u8 *data)
+{
+	bool pkt_handled = false;
+	u8 status = data[0];
+	struct hci_command_hdr cmd;
+
+	CG2900_INFO("Received Reset complete event with status 0x%X", status);
+
+	if ((core_info->main_state == CORE_BOOTING ||
+	     core_info->main_state == CORE_INITIALIZING) &&
+	    core_info->boot_state == BOOT_NOT_STARTED) {
+		/* Transmit HCI Read Local Version Information command */
+		SET_BOOT_STATE(BOOT_READ_LOCAL_VERSION_INFORMATION);
+		cmd.opcode = cpu_to_le16(HCI_OP_READ_LOCAL_VERSION);
+		cmd.plen = 0; /* No parameters for HCI reset */
+		create_and_send_bt_cmd(&cmd, sizeof(cmd));
+
+		pkt_handled = true;
+	}
+
+	return pkt_handled;
+}
+
+/**
+ * handle_read_local_version_info_cmd_complete_evt() - Handle a
received HCI Command Complete event for a ReadLocalVersionInformation
command.
+ * @data:	Pointer to received HCI data packet.
+ *
+ * Returns:
+ *   True,  if packet was handled internally,
+ *   False, otherwise.
+ */
+static bool handle_read_local_version_info_cmd_complete_evt(u8 *data)
+{
+	bool chip_handled = false;
+	struct list_head *cursor;
+	struct chip_handler_item *tmp;
+	struct local_chip_info *chip;
+	struct cg2900_chip_info *chip_info;
+	struct cg2900_chip_callbacks *cb;
+	int err;
+	struct hci_rp_read_local_version *evt;
+
+	/* Check we're in the right state */
+	if ((core_info->main_state != CORE_BOOTING &&
+	     core_info->main_state != CORE_INITIALIZING) ||
+	     core_info->boot_state != BOOT_READ_LOCAL_VERSION_INFORMATION)
+		return false;
+
+	/* We got an answer for our HCI command. Extract data */
+	evt = (struct hci_rp_read_local_version *)data;
+
+	/* We will handle the packet */
+	if (HCI_BT_ERROR_NO_ERROR != evt->status) {
+		CG2900_ERR("Received Read Local Version Information with "
+			     "status 0x%X", evt->status);
+		SET_BOOT_STATE(BOOT_FAILED);
+		cg2900_reset(NULL);
+		return true;
+	}
+
+	/* The command worked. Store the data */
+	chip = &(core_info->local_chip_info);
+	chip->version_set = true;
+	chip->hci_version = evt->hci_ver;
+	chip->hci_revision = le16_to_cpu(evt->hci_rev);
+	chip->lmp_pal_version = evt->lmp_ver;
+	chip->manufacturer = le16_to_cpu(evt->manufacturer);
+	chip->lmp_pal_subversion = le16_to_cpu(evt->lmp_subver);
+	CG2900_DBG("Received Read Local Version Information with:\n"
+		   "\thci_version:  0x%X\n"
+		   "\thci_revision: 0x%X\n"
+		   "\tlmp_pal_version: 0x%X\n"
+		   "\tmanufacturer: 0x%X\n"
+		   "\tlmp_pal_subversion: 0x%X",
+		   chip->hci_version, chip->hci_revision,
+		   chip->lmp_pal_version, chip->manufacturer,
+		   chip->lmp_pal_subversion);
+
+	cg2900_devices_set_hci_revision(chip->hci_version,
+					chip->hci_revision,
+					chip->lmp_pal_version,
+					chip->lmp_pal_subversion,
+					chip->manufacturer);
+
+	/* Received good confirmation. Find handler for the chip. */
+	chip_info = &(core_info->chip_dev.chip);
+	chip_info->hci_revision = chip->hci_revision;
+	chip_info->hci_sub_version = chip->lmp_pal_subversion;
+	chip_info->manufacturer = chip->manufacturer;
+
+	memset(&(core_info->chip_dev.cb), 0, sizeof(core_info->chip_dev.cb));
+
+	list_for_each(cursor, &chip_handlers) {
+		tmp = list_entry(cursor, struct chip_handler_item, list);
+		chip_handled = tmp->cb.check_chip_support(
+				&(core_info->chip_dev));
+		if (chip_handled) {
+			CG2900_INFO("Chip handler found");
+			break;
+		}
+	}
+
+	if (core_info->main_state == CORE_INITIALIZING) {
+		/*
+		 * We are now finished with the start-up during HwRegistered
+		 * operation.
+		 */
+		SET_BOOT_STATE(BOOT_READY);
+		wake_up_interruptible(&main_wait_queue);
+	} else if (!chip_handled) {
+		CG2900_INFO("No chip handler found. Start-up complete");
+		SET_BOOT_STATE(BOOT_READY);
+		cg2900_chip_startup_finished(0);
+	} else {
+		cb = &(core_info->chip_dev.cb);
+		if (!cb->chip_startup)
+			cg2900_chip_startup_finished(0);
+		else {
+			err = cb->chip_startup(&(core_info->chip_dev));
+			if (err)
+				cg2900_chip_startup_finished(err);
+		}
+	}
+
+	return true;
+}
+
+/**
+ * handle_rx_data_bt_evt() - Check if data should be handled in CG2900 Core.
+ * @skb:	Data packet
+ *
+ * The handle_rx_data_bt_evt() function checks if received data should be
+ * handled in CG2900 Core. If so handle it correctly.
+ * Received data is always HCI BT Event.
+ *
+ * Returns:
+ *   True,  if packet was handled internally,
+ *   False, otherwise.
+ */
+static bool handle_rx_data_bt_evt(struct sk_buff *skb)
+{
+	bool pkt_handled = false;
+	u8 *data = &(skb->data[CG2900_SKB_RESERVE]);
+	struct hci_event_hdr *evt;
+	struct hci_ev_cmd_complete *cmd_complete;
+	u16 op_code;
+
+	evt = (struct hci_event_hdr *)data;
+
+	/* First check the event code */
+	if (HCI_EV_CMD_COMPLETE != evt->evt)
+		return false;
+
+	data += sizeof(*evt);
+	cmd_complete = (struct hci_ev_cmd_complete *)data;
+
+	op_code = le16_to_cpu(cmd_complete->opcode);
+
+	CG2900_DBG_DATA("Received Command Complete: op_code = 0x%04X", op_code);
+	data += sizeof(*cmd_complete); /* Move to first byte after OCF */
+
+	if (op_code == HCI_OP_RESET)
+		pkt_handled = handle_reset_cmd_complete_evt(data);
+	else if (op_code == HCI_OP_READ_LOCAL_VERSION)
+		pkt_handled =
+			handle_read_local_version_info_cmd_complete_evt(data);
+
+	if (pkt_handled)
+		kfree_skb(skb);
+
+	return pkt_handled;
+}
+
+/**
+ * test_char_dev_tx_received() - Handle data received from CG2900 Core.
+ * @dev:	Current transport device information.
+ * @skb:	Buffer with data coming form device.
+ */
+static int test_char_dev_tx_received(struct cg2900_trans_dev *dev,
+				     struct sk_buff *skb)
+{
+	skb_queue_tail(&core_info->test_char_dev->rx_queue, skb);
+	wake_up_interruptible(&char_wait_queue);
+	return 0;
+}
+
+/**
+ * test_char_dev_open() - User space char device has been opened.
+ * @inode:	Device driver information.
+ * @filp:	Pointer to the file struct.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EACCES if transport already exists.
+ *   -ENOMEM if allocation fails.
+ *   Errors from create_work_item.
+ */
+static int test_char_dev_open(struct inode *inode, struct file *filp)
+{
+	struct cg2900_trans_callbacks cb = {
+		.write = test_char_dev_tx_received,
+		.open = NULL,
+		.close = NULL,
+		.set_chip_power = NULL
+	};
+
+	CG2900_INFO("test_char_dev_open");
+	return cg2900_register_trans_driver(&cb, NULL);
+}
+
+/**
+ * test_char_dev_release() - User space char device has been closed.
+ * @inode:	Device driver information.
+ * @filp:	Pointer to the file struct.
+ *
+ * Returns:
+ *   0 if there is no error.
+ */
+static int test_char_dev_release(struct inode *inode, struct file *filp)
+{
+	/* Clean the message queue */
+	skb_queue_purge(&core_info->test_char_dev->rx_queue);
+	return cg2900_deregister_trans_driver();
+}
+
+/**
+ * test_char_dev_read() - Queue and copy buffer to user space char device.
+ * @filp:	Pointer to the file struct.
+ * @buf:	Received buffer.
+ * @count:	Count of received data in bytes.
+ * @f_pos:	Position in buffer.
+ *
+ * Returns:
+ *   >= 0 is number of bytes read.
+ *   -EFAULT if copy_to_user fails.
+ */
+static ssize_t test_char_dev_read(struct file *filp, char __user *buf,
+				  size_t count, loff_t *f_pos)
+{
+	struct sk_buff *skb;
+	int bytes_to_copy;
+	int err;
+	struct sk_buff_head *rx_queue = &core_info->test_char_dev->rx_queue;
+
+	CG2900_INFO("test_char_dev_read");
+
+	if (skb_queue_empty(rx_queue))
+		wait_event_interruptible(char_wait_queue,
+					 !(skb_queue_empty(rx_queue)));
+
+	skb = skb_dequeue(rx_queue);
+	if (!skb) {
+		CG2900_INFO("skb queue is empty - return with zero bytes");
+		bytes_to_copy = 0;
+		goto finished;
+	}
+
+	bytes_to_copy = min(count, skb->len);
+	err = copy_to_user(buf, skb->data, bytes_to_copy);
+	if (err) {
+		skb_queue_head(rx_queue, skb);
+		return -EFAULT;
+	}
+
+	skb_pull(skb, bytes_to_copy);
+
+	if (skb->len > 0)
+		skb_queue_head(rx_queue, skb);
+	else
+		kfree_skb(skb);
+
+finished:
+	return bytes_to_copy;
+}
+
+/**
+ * test_char_dev_write() - Copy buffer from user and write to CG2900 Core.
+ * @filp:	Pointer to the file struct.
+ * @buf:	Read buffer.
+ * @count:	Size of the buffer write.
+ * @f_pos:	Position in buffer.
+ *
+ * Returns:
+ *   >= 0 is number of bytes written.
+ *   -EFAULT if copy_from_user fails.
+ */
+static ssize_t test_char_dev_write(struct file *filp, const char __user *buf,
+				   size_t count, loff_t *f_pos)
+{
+	struct sk_buff *skb;
+
+	CG2900_INFO("test_char_dev_write count %d", count);
+
+	/* Allocate the SKB and reserve space for the header */
+	skb = alloc_skb(count + RX_SKB_RESERVE, GFP_ATOMIC);
+	if (!skb) {
+		CG2900_ERR("Failed to alloc skb");
+		return -ENOMEM;
+	}
+	skb_reserve(skb, RX_SKB_RESERVE);
+
+	if (copy_from_user(skb_put(skb, count), buf, count)) {
+		kfree_skb(skb);
+		return -EFAULT;
+	}
+	cg2900_data_from_chip(skb);
+
+	return count;
+}
+
+/**
+ * test_char_dev_poll() - Handle POLL call to the interface.
+ * @filp:	Pointer to the file struct.
+ * @wait:	Poll table supplied to caller.
+ *
+ * Returns:
+ *   Mask of current set POLL values (0 or (POLLIN | POLLRDNORM))
+ */
+static unsigned int test_char_dev_poll(struct file *filp, poll_table *wait)
+{
+	unsigned int mask = 0;
+
+	poll_wait(filp, &char_wait_queue, wait);
+
+	if (!(skb_queue_empty(&core_info->test_char_dev->rx_queue)))
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+/*
+ * struct test_char_dev_fops - Test char devices file operations.
+ * @read:	Function that reads from the char device.
+ * @write:	Function that writes to the char device.
+ * @poll:	Function that handles poll call to the fd.
+ */
+static const struct file_operations test_char_dev_fops = {
+	.open = test_char_dev_open,
+	.release = test_char_dev_release,
+	.read = test_char_dev_read,
+	.write = test_char_dev_write,
+	.poll = test_char_dev_poll
+};
+
+/**
+ * test_char_dev_create() - Create a char device for testing.
+ *
+ * Creates a separate char device that will interact directly with userspace
+ * test application.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -ENOMEM if allocation fails.
+ *   -EBUSY if device has already been allocated.
+ *   Error codes from misc_register.
+ */
+static int test_char_dev_create(void)
+{
+	int err;
+
+	if (core_info->test_char_dev) {
+		CG2900_ERR("Trying to allocate test_char_dev twice");
+		return -EBUSY;
+	}
+
+	core_info->test_char_dev = kzalloc(sizeof(*(core_info->test_char_dev)),
+					   GFP_KERNEL);
+	if (!core_info->test_char_dev) {
+		CG2900_ERR("Couldn't allocate test_char_dev");
+		return -ENOMEM;
+	}
+
+	/* Initialize the RX queue */
+	skb_queue_head_init(&core_info->test_char_dev->rx_queue);
+
+	/* Prepare miscdevice struct before registering the device */
+	core_info->test_char_dev->test_miscdev.minor = MISC_DYNAMIC_MINOR;
+	core_info->test_char_dev->test_miscdev.name = CG2900_CDEV_NAME;
+	core_info->test_char_dev->test_miscdev.fops = &test_char_dev_fops;
+	core_info->test_char_dev->test_miscdev.parent =
+			core_info->dev->this_device;
+
+	err = misc_register(&core_info->test_char_dev->test_miscdev);
+	if (err) {
+		CG2900_ERR("Error %d registering misc dev!", err);
+		kfree(core_info->test_char_dev);
+		core_info->test_char_dev = NULL;
+		return err;
+	}
+
+	return 0;
+}
+
+/**
+ * test_char_dev_destroy() - Clean up after test_char_dev_create().
+ */
+static void test_char_dev_destroy(void)
+{
+	int err;
+
+	if (!core_info->test_char_dev)
+		return;
+
+	err = misc_deregister(&core_info->test_char_dev->test_miscdev);
+	if (err)
+		CG2900_ERR("Error %d deregistering misc dev!", err);
+
+	/* Clean the message queue */
+	skb_queue_purge(&core_info->test_char_dev->rx_queue);
+
+	kfree(core_info->test_char_dev);
+	core_info->test_char_dev = NULL;
+}
+
+/**
+ * open_transport() - Open the CG2900 transport for data transfers.
+ *
+ * Returns:
+ *   0 if there is no error,
+ *   -EACCES if write to transport failed,
+ *   -EIO if transport has not been selected or chip did not answer
to commands.
+ */
+static int open_transport(void)
+{
+	int err = 0;
+	struct trans_info *trans_info = core_info->trans_info;
+
+	CG2900_INFO("open_transport");
+
+	if (trans_info && trans_info->cb.open) {
+		err = trans_info->cb.open(&trans_info->dev);
+		if (err)
+			CG2900_ERR("Transport open failed (%d)", err);
+	}
+
+	if (!err)
+		SET_TRANSPORT_STATE(TRANS_OPENED);
+
+	return err;
+}
+
+/**
+ * close_transport() - Close the CG2900 transport for data transfers.
+ */
+static void close_transport(void)
+{
+	struct trans_info *trans_info = core_info->trans_info;
+
+	CG2900_INFO("close_transport");
+
+	/* Check so transport has not already been removed */
+	if (TRANS_OPENED == core_info->transport_state)
+		SET_TRANSPORT_STATE(TRANS_CLOSED);
+
+	if (trans_info && trans_info->cb.close) {
+		int err = trans_info->cb.close(&trans_info->dev);
+		if (err)
+			CG2900_ERR("Transport close failed (%d)", err);
+	}
+}
+
+/**
+ * create_work_item() - Create work item and add it to the work queue.
+ * @wq:		work queue struct where the work will be added.
+ * @work_func:	Work function.
+ * @data:	Private data for the work.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EBUSY if not possible to queue work.
+ *   -ENOMEM if allocation fails.
+ */
+static int create_work_item(struct workqueue_struct *wq, work_func_t work_func,
+			    void *data)
+{
+	struct cg2900_work_struct *new_work;
+	int err;
+
+	new_work = kmalloc(sizeof(*new_work), GFP_ATOMIC);
+	if (!new_work) {
+		CG2900_ERR("Failed to alloc memory for cg2900_work_struct!");
+		return -ENOMEM;
+	}
+
+	new_work->data = data;
+	INIT_WORK(&new_work->work, work_func);
+
+	err = queue_work(wq, &new_work->work);
+	if (!err) {
+		CG2900_ERR("Failed to queue work_struct because it's already "
+			   "in the queue!");
+		kfree(new_work);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+/**
+ * work_hw_registered() - Called when the interface to HW has been established.
+ * @work:	Reference to work data.
+ *
+ * Since there now is a transport identify the connected chip and decide which
+ * chip handler to use.
+ */
+static void work_hw_registered(struct work_struct *work)
+{
+	struct cg2900_work_struct *current_work = NULL;
+	bool run_shutdown = true;
+	struct cg2900_chip_callbacks *cb;
+	struct cg2900_h4_channels *chan;
+	struct trans_info *trans_info = core_info->trans_info;
+	struct hci_command_hdr cmd;
+
+	CG2900_INFO("work_hw_registered");
+
+	if (!work) {
+		CG2900_ERR("work == NULL");
+		return;
+	}
+
+	current_work = container_of(work, struct cg2900_work_struct, work);
+
+	SET_MAIN_STATE(CORE_INITIALIZING);
+	SET_BOOT_STATE(BOOT_NOT_STARTED);
+
+	/*
+	 * This might look strange, but we need to read out
+	 * the revision info in order to be able to shutdown the chip properly.
+	 */
+	if (trans_info && trans_info->cb.set_chip_power)
+		trans_info->cb.set_chip_power(true);
+
+	/* Wait 100ms before continuing to be sure that the chip is ready */
+	schedule_timeout_interruptible(msecs_to_jiffies(CHIP_READY_TIMEOUT));
+
+	/*
+	 * Transmit HCI reset command to ensure the chip is using
+	 * the correct transport
+	 */
+	cmd.opcode = cpu_to_le16(HCI_OP_RESET);
+	cmd.plen = 0; /* No parameters for HCI reset */
+	create_and_send_bt_cmd(&cmd, sizeof(cmd));
+
+	/* Wait up to 500 milliseconds for revision to be read out */
+	CG2900_DBG("Wait up to 500 milliseconds for revision to be read.");
+	wait_event_interruptible_timeout(main_wait_queue,
+		(BOOT_READY == core_info->boot_state),
+		msecs_to_jiffies(REVISION_READOUT_TIMEOUT));
+
+	/*
+	 * If we are in BOOT_READY we have a good revision.
+	 * Otherwise handle this as an error and switch to default handler.
+	 */
+	if (BOOT_READY != core_info->boot_state) {
+		chip_not_detected();
+		run_shutdown = false;
+	}
+
+	/* Read out the channels for connected chip */
+	cb = &(core_info->chip_dev.cb);
+	chan = &(core_info->h4_channels);
+	if (cb->get_h4_channel) {
+		/* Get the H4 channel ID for all channels */
+		cb->get_h4_channel(CG2900_BT_CMD, &(chan->bt_cmd_channel));
+		cb->get_h4_channel(CG2900_BT_ACL, &(chan->bt_acl_channel));
+		cb->get_h4_channel(CG2900_BT_EVT, &(chan->bt_evt_channel));
+		cb->get_h4_channel(CG2900_GNSS, &(chan->gnss_channel));
+		cb->get_h4_channel(CG2900_FM_RADIO, &(chan->fm_radio_channel));
+		cb->get_h4_channel(CG2900_DEBUG, &(chan->debug_channel));
+		cb->get_h4_channel(CG2900_STE_TOOLS,
+				   &(chan->ste_tools_channel));
+		cb->get_h4_channel(CG2900_HCI_LOGGER,
+				   &(chan->hci_logger_channel));
+		cb->get_h4_channel(CG2900_US_CTRL, &(chan->us_ctrl_channel));
+		cb->get_h4_channel(CG2900_CORE, &(chan->core_channel));
+	}
+
+	/*
+	 * Now it is time to shutdown the controller to reduce
+	 * power consumption until any users register
+	 */
+	if (run_shutdown)
+		chip_shutdown();
+	else
+		cg2900_chip_shutdown_finished(0);
+
+	kfree(current_work);
+}
+
+/*
+ *	CG2900 API functions
+ */
+
+struct cg2900_device *cg2900_register_user(char  *name,
+					   struct cg2900_callbacks *cb)
+{
+	struct cg2900_device *current_dev;
+	int err;
+	struct trans_info *trans_info = core_info->trans_info;
+
+	CG2900_INFO("cg2900_register_user %s", name);
+
+	BUG_ON(!core_info);
+
+	/* Wait for state CORE_IDLE or CORE_ACTIVE. */
+	err = wait_event_interruptible_timeout(main_wait_queue,
+			(CORE_IDLE == core_info->main_state ||
+			 CORE_ACTIVE == core_info->main_state),
+			msecs_to_jiffies(LINE_TOGGLE_DETECT_TIMEOUT));
+
+	if (err <= 0) {
+		if (CORE_INITIALIZING == core_info->main_state)
+			CG2900_ERR("Transport not opened");
+		else
+			CG2900_ERR("cg2900_register_user currently busy (0x%X)."
+				   " Try again.", core_info->main_state);
+		return NULL;
+	}
+
+	/* Allocate device */
+	current_dev = kzalloc(sizeof(*current_dev), GFP_ATOMIC);
+	if (!current_dev) {
+		CG2900_ERR("Couldn't allocate current dev");
+		goto error_handling;
+	}
+
+	if (!core_info->chip_dev.cb.get_h4_channel) {
+		CG2900_ERR("No channel handler registered");
+		goto error_handling;
+	}
+	err = core_info->chip_dev.cb.get_h4_channel(name,
+						    &(current_dev->h4_channel));
+	if (err) {
+		CG2900_ERR("Couldn't find H4 channel for %s", name);
+		goto error_handling;
+	}
+	current_dev->dev = core_info->dev->this_device;
+	current_dev->cb = kmalloc(sizeof(*(current_dev->cb)),
+					 GFP_ATOMIC);
+	if (!current_dev->cb) {
+		CG2900_ERR("Couldn't allocate cb ");
+		goto error_handling;
+	}
+	memcpy((char *)current_dev->cb, (char *)cb,
+	       sizeof(*(current_dev->cb)));
+
+	/* Retrieve pointer to the correct CG2900 Core user structure */
+	err = add_h4_user(current_dev, name);
+
+	if (!err) {
+		CG2900_DBG("H:4 channel 0x%X registered",
+			   current_dev->h4_channel);
+	} else {
+		CG2900_ERR("H:4 channel 0x%X already registered "
+			   "or other error (%d)",
+			   current_dev->h4_channel, err);
+		goto error_handling;
+	}
+
+	if (CORE_ACTIVE != core_info->main_state &&
+	    core_info->users.nbr_of_users == 1) {
+		/* Open transport and start-up the chip */
+		if (trans_info && trans_info->cb.set_chip_power)
+			trans_info->cb.set_chip_power(true);
+
+		/* Wait 100ms to be sure that the chip is ready */
+		schedule_timeout_interruptible(
+				msecs_to_jiffies(CHIP_READY_TIMEOUT));
+
+		err = open_transport();
+		if (err) {
+			/*
+			 * Remove the user. If there is no error it will be
+			 * freed as well.
+			 */
+			remove_h4_user(&current_dev);
+			goto finished;
+		}
+
+		chip_startup();
+
+		/* Wait up to 15 seconds for chip to start */
+		CG2900_DBG("Wait up to 15 seconds for chip to start..");
+		wait_event_interruptible_timeout(main_wait_queue,
+			(CORE_ACTIVE == core_info->main_state ||
+			 CORE_IDLE   == core_info->main_state),
+			msecs_to_jiffies(CHIP_STARTUP_TIMEOUT));
+		if (CORE_ACTIVE != core_info->main_state) {
+			CG2900_ERR("ST-Ericsson CG2900 driver failed to "
+				     "start");
+
+			/* Close the transport and power off the chip */
+			close_transport();
+
+			/*
+			 * Remove the user. If there is no error it will be
+			 * freed as well.
+			 */
+			remove_h4_user(&current_dev);
+
+			/* Chip shut-down finished, set correct state. */
+			SET_MAIN_STATE(CORE_IDLE);
+		}
+	}
+	goto finished;
+
+error_handling:
+	free_user_dev(&current_dev);
+finished:
+	return current_dev;
+}
+EXPORT_SYMBOL(cg2900_register_user);
+
+void cg2900_deregister_user(struct cg2900_device *dev)
+{
+	int h4_channel;
+	int err = 0;
+
+	CG2900_INFO("cg2900_deregister_user");
+
+	BUG_ON(!core_info);
+
+	if (!dev) {
+		CG2900_ERR("Calling with NULL pointer");
+		return;
+	}
+
+	h4_channel = dev->h4_channel;
+
+	/* Remove the user. If there is no error it will be freed as well */
+	err = remove_h4_user(&dev);
+	if (err) {
+		CG2900_ERR("Trying to deregister non-registered "
+			   "H:4 channel 0x%X or other error %d",
+			   h4_channel, err);
+		return;
+	}
+
+	CG2900_DBG("H:4 channel 0x%X deregistered", h4_channel);
+
+	if (0 != core_info->users.nbr_of_users)
+		/* This was not the last user, we're done. */
+		return;
+
+	if (CORE_IDLE == core_info->main_state)
+		/* Chip has already been shut down. */
+		return;
+
+	SET_MAIN_STATE(CORE_CLOSING);
+	chip_shutdown();
+
+	/* Wait up to 15 seconds for chip to shut-down */
+	CG2900_DBG("Wait up to 15 seconds for chip to shut-down..");
+	wait_event_interruptible_timeout(main_wait_queue,
+				(CORE_IDLE == core_info->main_state),
+				msecs_to_jiffies(CHIP_SHUTDOWN_TIMEOUT));
+
+	/* Force shutdown if we timed out */
+	if (CORE_IDLE != core_info->main_state) {
+		CG2900_ERR("ST-Ericsson CG2900 Core Driver was shut-down with "
+			   "problems.");
+
+		/* Close the transport and power off the chip */
+		close_transport();
+
+		/* Chip shut-down finished, set correct state. */
+		SET_MAIN_STATE(CORE_IDLE);
+	}
+}
+EXPORT_SYMBOL(cg2900_deregister_user);
+
+int cg2900_reset(struct cg2900_device *dev)
+{
+	CG2900_INFO("cg2900_reset");
+
+	BUG_ON(!core_info);
+
+	SET_MAIN_STATE(CORE_RESETING);
+
+	/* Shutdown the chip */
+	chip_shutdown();
+
+	/*
+	 * Inform all registered users about the reset and free the user devices
+	 * Don't send reset for debug and logging channels
+	 */
+	handle_reset_of_user(&(core_info->users.bt_cmd));
+	handle_reset_of_user(&(core_info->users.bt_audio));
+	handle_reset_of_user(&(core_info->users.bt_acl));
+	handle_reset_of_user(&(core_info->users.bt_evt));
+	handle_reset_of_user(&(core_info->users.fm_radio));
+	handle_reset_of_user(&(core_info->users.fm_radio_audio));
+	handle_reset_of_user(&(core_info->users.gnss));
+	handle_reset_of_user(&(core_info->users.core));
+
+	core_info->users.nbr_of_users = 0;
+
+	/* Reset finished. We are now idle until first user is registered */
+	SET_MAIN_STATE(CORE_IDLE);
+
+	/*
+	 * Send wake-up since this might have been called from a failed boot.
+	 * No harm done if it is a CG2900 Core user who called.
+	 */
+	wake_up_interruptible(&main_wait_queue);
+
+	return 0;
+}
+EXPORT_SYMBOL(cg2900_reset);
+
+struct sk_buff *cg2900_alloc_skb(unsigned int size, gfp_t priority)
+{
+	struct sk_buff *skb;
+
+	CG2900_INFO("cg2900_alloc_skb");
+	CG2900_DBG("size %d bytes", size);
+
+	/* Allocate the SKB and reserve space for the header */
+	skb = alloc_skb(size + CG2900_SKB_RESERVE, priority);
+	if (skb)
+		skb_reserve(skb, CG2900_SKB_RESERVE);
+
+	return skb;
+}
+EXPORT_SYMBOL(cg2900_alloc_skb);
+
+int cg2900_write(struct cg2900_device *dev, struct sk_buff *skb)
+{
+	int err = 0;
+	u8 *h4_header;
+	struct cg2900_chip_callbacks *cb;
+
+	CG2900_DBG_DATA("cg2900_write");
+
+	BUG_ON(!core_info);
+
+	if (!dev) {
+		CG2900_ERR("cg2900_write with no device");
+		return -EINVAL;
+	}
+
+	if (!skb) {
+		CG2900_ERR("cg2900_write with no sk_buffer");
+		return -EINVAL;
+	}
+
+	CG2900_DBG_DATA("Length %d bytes", skb->len);
+
+	if (core_info->h4_channels.hci_logger_channel == dev->h4_channel) {
+		/*
+		 * Treat the HCI logger write differently.
+		 * A write can only mean a change of configuration.
+		 */
+		err = enable_hci_logger(skb);
+	} else if (core_info->h4_channels.core_channel == dev->h4_channel) {
+		CG2900_ERR("Not possible to write data on core channel, "
+			   "it only supports enable / disable chip");
+		err = -EPERM;
+	} else if (CORE_ACTIVE == core_info->main_state) {
+		/*
+		 * Move the data pointer to the H:4 header position and
+		 * store the H4 header.
+		 */
+		h4_header = skb_push(skb, CG2900_SKB_RESERVE);
+		*h4_header = (u8)dev->h4_channel;
+
+		/*
+		 * Check if the chip handler wants to handle this packet.
+		 * If not, send it to the transport.
+		 */
+		cb = &(core_info->chip_dev.cb);
+		if (!cb->data_to_chip ||
+		    !(cb->data_to_chip(&(core_info->chip_dev), dev, skb)))
+			transmit_skb_to_chip(skb, dev->logger_enabled);
+	} else {
+		CG2900_ERR("Trying to transmit data when CG2900 Core is not "
+			     "active");
+		err = -EACCES;
+	}
+
+	return err;
+}
+EXPORT_SYMBOL(cg2900_write);
+
+bool cg2900_get_local_revision(struct cg2900_rev_data *rev_data)
+{
+	BUG_ON(!core_info);
+
+	if (!rev_data) {
+		CG2900_ERR("Calling with rev_data NULL");
+		return false;
+	}
+
+	if (!core_info->local_chip_info.version_set)
+		return false;
+
+	rev_data->revision = core_info->local_chip_info.hci_revision;
+	rev_data->sub_version = core_info->local_chip_info.lmp_pal_subversion;
+
+	return true;
+}
+EXPORT_SYMBOL(cg2900_get_local_revision);
+
+int cg2900_register_chip_driver(struct cg2900_id_callbacks *cb)
+{
+	struct chip_handler_item *item;
+
+	CG2900_INFO("cg2900_register_chip_driver");
+
+	if (!cb) {
+		CG2900_ERR("NULL supplied as cb");
+		return -EINVAL;
+	}
+
+	item = kzalloc(sizeof(*item), GFP_ATOMIC);
+	if (!item) {
+		CG2900_ERR("Failed to alloc memory!");
+		return -ENOMEM;
+	}
+
+	memcpy(&(item->cb), cb, sizeof(cb));
+	list_add_tail(&item->list, &chip_handlers);
+	return 0;
+}
+EXPORT_SYMBOL(cg2900_register_chip_driver);
+
+int cg2900_register_trans_driver(struct cg2900_trans_callbacks *cb, void *data)
+{
+	int err;
+
+	BUG_ON(!core_info);
+
+	CG2900_INFO("cg2900_register_trans_driver");
+
+	if (core_info->trans_info) {
+		CG2900_ERR("trans_info already exists");
+		return -EACCES;
+	}
+
+	core_info->trans_info = kzalloc(sizeof(*(core_info->trans_info)),
+					GFP_KERNEL);
+	if (!core_info->trans_info) {
+		CG2900_ERR("Could not allocate trans_info");
+		return -ENOMEM;
+	}
+
+	memcpy(&(core_info->trans_info->cb), cb, sizeof(*cb));
+	core_info->trans_info->dev.dev = core_info->dev->this_device;
+	core_info->trans_info->dev.user_data = data;
+
+	err = create_work_item(core_info->wq, work_hw_registered, NULL);
+	if (err) {
+		CG2900_ERR("Could not create work item (%d) "
+			     "work_hw_registered", err);
+	}
+
+	return err;
+}
+EXPORT_SYMBOL(cg2900_register_trans_driver);
+
+int cg2900_deregister_trans_driver(void)
+{
+	BUG_ON(!core_info);
+
+	CG2900_INFO("cg2900_deregister_trans_driver");
+
+	SET_MAIN_STATE(CORE_INITIALIZING);
+	SET_TRANSPORT_STATE(TRANS_INITIALIZING);
+
+	if (!core_info->trans_info)
+		return -EACCES;
+
+	kfree(core_info->trans_info);
+	core_info->trans_info = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL(cg2900_deregister_trans_driver);
+
+int cg2900_chip_startup_finished(int err)
+{
+	CG2900_INFO("cg2900_chip_startup_finished (%d)", err);
+
+	if (err)
+		/* Shutdown the chip */
+		chip_shutdown();
+	else
+		SET_MAIN_STATE(CORE_ACTIVE);
+
+	wake_up_interruptible(&main_wait_queue);
+
+	return 0;
+}
+EXPORT_SYMBOL(cg2900_chip_startup_finished);
+
+int cg2900_chip_shutdown_finished(int err)
+{
+	CG2900_INFO("cg2900_chip_shutdown_finished (%d)", err);
+
+	/* Close the transport, which will power off the chip */
+	close_transport();
+
+	/* Chip shut-down finished, set correct state and wake up the chip. */
+	SET_MAIN_STATE(CORE_IDLE);
+	wake_up_interruptible(&main_wait_queue);
+
+	return 0;
+}
+EXPORT_SYMBOL(cg2900_chip_shutdown_finished);
+
+int cg2900_send_to_chip(struct sk_buff *skb, bool use_logger)
+{
+	transmit_skb_to_chip(skb, use_logger);
+	return 0;
+}
+EXPORT_SYMBOL(cg2900_send_to_chip);
+
+struct cg2900_device *cg2900_get_bt_cmd_dev(void)
+{
+	if (core_info)
+		return core_info->users.bt_cmd;
+	else
+		return NULL;
+}
+EXPORT_SYMBOL(cg2900_get_bt_cmd_dev);
+
+struct cg2900_device *cg2900_get_fm_radio_dev(void)
+{
+	if (core_info)
+		return core_info->users.fm_radio;
+	else
+		return NULL;
+}
+EXPORT_SYMBOL(cg2900_get_fm_radio_dev);
+
+struct cg2900_device *cg2900_get_bt_audio_dev(void)
+{
+	if (core_info)
+		return core_info->users.bt_audio;
+	else
+		return NULL;
+}
+EXPORT_SYMBOL(cg2900_get_bt_audio_dev);
+
+struct cg2900_device *cg2900_get_fm_audio_dev(void)
+{
+	if (core_info)
+		return core_info->users.fm_radio_audio;
+	else
+		return NULL;
+}
+EXPORT_SYMBOL(cg2900_get_fm_audio_dev);
+
+struct cg2900_hci_logger_config *cg2900_get_hci_logger_config(void)
+{
+	if (core_info)
+		return &(core_info->hci_logger_config);
+	else
+		return NULL;
+}
+EXPORT_SYMBOL(cg2900_get_hci_logger_config);
+
+unsigned long cg2900_get_sleep_timeout(void)
+{
+	if (CORE_ACTIVE != core_info->main_state || !sleep_timeout_ms)
+		return 0;
+
+	return msecs_to_jiffies(sleep_timeout_ms);
+}
+EXPORT_SYMBOL(cg2900_get_sleep_timeout);
+
+void cg2900_data_from_chip(struct sk_buff *skb)
+{
+	struct cg2900_device *dev = NULL;
+	u8 h4_channel;
+	int err = 0;
+	struct cg2900_chip_callbacks *cb;
+	struct sk_buff *skb_log;
+	struct cg2900_device *logger;
+
+	CG2900_INFO("cg2900_data_from_chip");
+
+	if (!skb) {
+		CG2900_ERR("No data supplied");
+		return;
+	}
+
+	h4_channel = *(skb->data);
+
+	/*
+	 * First check if this is the response for something
+	 * we have sent internally.
+	 */
+	if ((core_info->main_state == CORE_BOOTING ||
+	     core_info->main_state == CORE_INITIALIZING) &&
+	    (HCI_BT_EVT_H4_CHANNEL == h4_channel) &&
+	    handle_rx_data_bt_evt(skb)) {
+		CG2900_DBG("Received packet handled internally");
+		return;
+	}
+
+	/* Find out where to route the data */
+	err = find_h4_user(h4_channel, &dev, skb);
+
+	/* Check if the chip handler wants to deal with the packet. */
+	cb = &(core_info->chip_dev.cb);
+	if (!err && cb->data_from_chip &&
+	    cb->data_from_chip(&(core_info->chip_dev), dev, skb))
+		return;
+
+	if (err || !dev) {
+		CG2900_ERR("H:4 channel: 0x%X,  does not match device",
+			     h4_channel);
+		kfree_skb(skb);
+		return;
+	}
+
+	/*
+	 * If HCI logging is enabled for this channel, copy the data to
+	 * the HCI logging output.
+	 */
+	logger = core_info->users.hci_logger;
+	if (!logger || !dev->logger_enabled)
+		goto transmit;
+
+	/*
+	 * Alloc a new sk_buffer and copy the data into it.
+	 * Then send it to the HCI logger.
+	 */
+	skb_log = alloc_skb(skb->len + 1, GFP_ATOMIC);
+	if (!skb_log) {
+		CG2900_ERR("Couldn't allocate skb_log");
+		goto transmit;
+	}
+
+	memcpy(skb_put(skb_log, skb->len), skb->data, skb->len);
+	skb_log->data[0] = (u8) LOGGER_DIRECTION_RX;
+
+	if (logger->cb->read_cb)
+		logger->cb->read_cb(logger, skb_log);
+
+transmit:
+	/* Remove the H4 header */
+	(void)skb_pull(skb, CG2900_SKB_RESERVE);
+
+	/* Call the Read callback */
+	if (dev->cb->read_cb)
+		dev->cb->read_cb(dev, skb);
+}
+EXPORT_SYMBOL(cg2900_data_from_chip);
+
+/*
+ * Module INIT and EXIT functions
+ */
+
+/**
+ * cg2900_init() - Initialize module.
+ *
+ * The cg2900_init() function initialize the transport and CG2900 Core, then
+ * register to the transport framework.
+ *
+ * Returns:
+ *   0 if success.
+ *   -ENOMEM for failed alloc or structure creation.
+ *   Error codes generated by cg2900_devices_init, alloc_chrdev_region,
+ *   class_create, device_create, core_init, tty_register_ldisc,
+ *   create_work_item, cg2900_char_devices_init.
+ */
+static int __init cg2900_init(void)
+{
+	int err;
+
+	CG2900_INFO("cg2900_init");
+
+	err = cg2900_devices_init();
+	if (err) {
+		CG2900_ERR("Couldn't initialize cg2900_devices");
+		return err;
+	}
+
+	core_info = kzalloc(sizeof(*core_info), GFP_KERNEL);
+	if (!core_info) {
+		CG2900_ERR("Couldn't allocate core_info");
+		return -ENOMEM;
+	}
+
+	/* Set the internal states */
+	core_info->main_state = CORE_INITIALIZING;
+	core_info->boot_state = BOOT_NOT_STARTED;
+	core_info->transport_state = TRANS_INITIALIZING;
+
+	/* Get the H4 channel ID for all channels */
+	core_info->h4_channels.bt_cmd_channel = HCI_BT_CMD_H4_CHANNEL;
+	core_info->h4_channels.bt_acl_channel = HCI_BT_ACL_H4_CHANNEL;
+	core_info->h4_channels.bt_evt_channel = HCI_BT_EVT_H4_CHANNEL;
+	core_info->h4_channels.gnss_channel = HCI_FM_RADIO_H4_CHANNEL;
+	core_info->h4_channels.fm_radio_channel = HCI_GNSS_H4_CHANNEL;
+
+	core_info->wq = create_singlethread_workqueue(CORE_WQ_NAME);
+	if (!core_info->wq) {
+		CG2900_ERR("Could not create workqueue");
+		err = -ENOMEM;
+		goto error_handling;
+	}
+
+	core_info->dev = kzalloc(sizeof(*(core_info->dev)), GFP_KERNEL);
+	if (!core_info->dev) {
+		CG2900_ERR("Couldn't allocate main device");
+		err = -ENOMEM;
+		goto error_handling_destroy_wq;
+	}
+
+	/* Prepare miscdevice struct before registering the device */
+	core_info->dev->minor = MISC_DYNAMIC_MINOR;
+	core_info->dev->name = CG2900_DEVICE_NAME;
+
+	err = misc_register(core_info->dev);
+	if (err) {
+		CG2900_ERR("Error %d registering main device!", err);
+		goto error_handling_dev_register;
+	}
+
+	core_info->chip_dev.dev = core_info->dev->this_device;
+
+	/* Create and add test char device. */
+	err = test_char_dev_create();
+	if (err)
+		goto error_handling_deregister;
+
+	/* Initialize the character devices */
+	err = cg2900_char_devices_init(core_info->dev);
+	if (err) {
+		CG2900_ERR("cg2900_char_devices_init failed %d", err);
+		goto error_handling_test_destroy;
+	}
+
+	return 0;
+
+error_handling_test_destroy:
+	test_char_dev_destroy();
+error_handling_deregister:
+	misc_deregister(core_info->dev);
+error_handling_dev_register:
+	kfree(core_info->dev);
+	core_info->dev = NULL;
+error_handling_destroy_wq:
+	destroy_workqueue(core_info->wq);
+error_handling:
+	kfree(core_info);
+	core_info = NULL;
+	return err;
+}
+
+/**
+ * cg2900_exit() - Remove module.
+ */
+static void __exit cg2900_exit(void)
+{
+	CG2900_INFO("cg2900_exit");
+
+	if (!core_info) {
+		CG2900_ERR("CG2900 Core not initiated");
+		return;
+	}
+
+	/* Remove initialized character devices */
+	cg2900_char_devices_exit();
+
+	test_char_dev_destroy();
+
+	/* Free the user devices */
+	free_user_dev(&(core_info->users.bt_cmd));
+	free_user_dev(&(core_info->users.bt_acl));
+	free_user_dev(&(core_info->users.bt_evt));
+	free_user_dev(&(core_info->users.fm_radio));
+	free_user_dev(&(core_info->users.gnss));
+	free_user_dev(&(core_info->users.debug));
+	free_user_dev(&(core_info->users.ste_tools));
+	free_user_dev(&(core_info->users.hci_logger));
+	free_user_dev(&(core_info->users.us_ctrl));
+	free_user_dev(&(core_info->users.core));
+
+	misc_deregister(core_info->dev);
+	kfree(core_info->dev);
+	core_info->dev = NULL;
+
+	destroy_workqueue(core_info->wq);
+
+	kfree(core_info);
+	core_info = NULL;
+
+	cg2900_devices_exit();
+}
+
+module_init(cg2900_init);
+module_exit(cg2900_exit);
+
+module_param(sleep_timeout_ms, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(sleep_timeout_ms,
+		 "Sleep timeout for data transmissions:\n"
+		 "\t0 = disable <default>\n"
+		 "\t>0 = sleep timeout in milliseconds");
+
+module_param(cg2900_debug_level, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(cg2900_debug_level,
+		 "Debug level. Default 1. Possible values:\n"
+		 "\t0  = No debug\n"
+		 "\t1  = Error prints\n"
+		 "\t10 = General info, e.g. function entries\n"
+		 "\t20 = Debug info, e.g. steps in a functionality\n"
+		 "\t25 = Data info, i.e. prints when data is transferred\n"
+		 "\t30 = Data content, i.e. contents of the transferred data");
+
+module_param_array(bd_address, byte, &bd_addr_count,
+		   S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(bd_address,
+		 "Bluetooth Device address. "
+		 "Default 0x00 0x80 0xDE 0xAD 0xBE 0xEF. "
+		 "Enter as comma separated value.");
+
+module_param(default_hci_revision, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(default_hci_revision,
+		 "Default HCI revision according to Bluetooth Assigned "
+		 "Numbers.");
+
+module_param(default_manufacturer, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(default_manufacturer,
+		 "Default Manufacturer according to Bluetooth Assigned "
+		 "Numbers.");
+
+module_param(default_sub_version, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(default_sub_version,
+		 "Default HCI sub-version according to Bluetooth Assigned "
+		 "Numbers.");
+
+MODULE_AUTHOR("Par-Gunnar Hjalmdahl ST-Ericsson");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Linux Bluetooth HCI H:4 CG2900 Connectivity
Device Driver");
diff --git a/drivers/mfd/cg2900/cg2900_core.h b/drivers/mfd/cg2900/cg2900_core.h
new file mode 100644
index 0000000..dd07305
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_core.h
@@ -0,0 +1,303 @@
+/*
+ * drivers/mfd/cg2900/cg2900_core.h
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@...ricsson.com) for
ST-Ericsson.
+ * Henrik Possung (henrik.possung@...ricsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@...ricsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@...ricsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@...ricsson.com) for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson CG2900 GPS/BT/FM controller.
+ */
+
+#ifndef _CG2900_CORE_H_
+#define _CG2900_CORE_H_
+
+#include <linux/skbuff.h>
+#include <linux/device.h>
+
+/* Reserve 1 byte for the HCI H:4 header */
+#define CG2900_SKB_RESERVE			1
+
+#define BT_BDADDR_SIZE				6
+
+struct cg2900_h4_channels {
+	int	bt_cmd_channel;
+	int	bt_acl_channel;
+	int	bt_evt_channel;
+	int	gnss_channel;
+	int	fm_radio_channel;
+	int	debug_channel;
+	int	ste_tools_channel;
+	int	hci_logger_channel;
+	int	us_ctrl_channel;
+	int	core_channel;
+};
+
+/**
+  * struct cg2900_hci_logger_config - Configures the HCI logger.
+  * @bt_cmd_enable: Enable BT command logging.
+  * @bt_acl_enable: Enable BT ACL logging.
+  * @bt_evt_enable: Enable BT event logging.
+  * @gnss_enable: Enable GNSS logging.
+  * @fm_radio_enable: Enable FM radio logging.
+  * @bt_audio_enable: Enable BT audio command logging.
+  * @fm_radio_audio_enable: Enable FM radio audio command logging.
+  *
+  * Set using cg2900_write on CHANNEL_HCI_LOGGER H4 channel.
+  */
+struct cg2900_hci_logger_config {
+	bool bt_cmd_enable;
+	bool bt_acl_enable;
+	bool bt_evt_enable;
+	bool gnss_enable;
+	bool fm_radio_enable;
+	bool bt_audio_enable;
+	bool fm_radio_audio_enable;
+};
+
+/**
+ * struct cg2900_chip_info - Chip info structure.
+ * @manufacturer:	Chip manufacturer.
+ * @hci_revision:	Chip revision, i.e. which chip is this.
+ * @hci_sub_version:	Chip sub-version, i.e. which tape-out is this.
+ *
+ * Note that these values match the Bluetooth Assigned Numbers,
+ * see http://www.bluetooth.org/
+ */
+struct cg2900_chip_info {
+	int manufacturer;
+	int hci_revision;
+	int hci_sub_version;
+};
+
+struct cg2900_chip_dev;
+
+/**
+ * struct cg2900_chip_callbacks - Callback functions registered by
chip handler.
+ * @chip_startup:		Called when chip is started up.
+ * @chip_shutdown:		Called when chip is shut down.
+ * @data_to_chip:		Called when data shall be transmitted to chip.
+ *				Return true when CG2900 Core shall not send it
+ *				to chip.
+ * @data_from_chip:		Called when data shall be transmitted to user.
+ *				Return true when packet is taken care of by
+ *				Return chip return handler.
+ * @get_h4_channel:		Connects channel name with H:4 channel number.
+ * @is_bt_audio_user:		Return true if current packet is for
+ *				the BT audio user.
+ * @is_fm_audio_user:		Return true if current packet is for
+ *				the FM audio user.
+ * @last_bt_user_removed:	Last BT channel user has been removed.
+ * @last_fm_user_removed:	Last FM channel user has been removed.
+ * @last_gnss_user_removed:	Last GNSS channel user has been removed.
+ *
+ * Note that some callbacks may be NULL. They must always be NULL
checked before
+ * calling.
+ */
+struct cg2900_chip_callbacks {
+	int (*chip_startup)(struct cg2900_chip_dev *dev);
+	int (*chip_shutdown)(struct cg2900_chip_dev *dev);
+	bool (*data_to_chip)(struct cg2900_chip_dev *dev,
+			     struct cg2900_device *cg2900_dev,
+			     struct sk_buff *skb);
+	bool (*data_from_chip)(struct cg2900_chip_dev *dev,
+			       struct cg2900_device *cg2900_dev,
+			       struct sk_buff *skb);
+	int (*get_h4_channel)(char *name, int *h4_channel);
+	bool (*is_bt_audio_user)(int h4_channel,
+				 const struct sk_buff * const skb);
+	bool (*is_fm_audio_user)(int h4_channel,
+				 const struct sk_buff * const skb);
+	void (*last_bt_user_removed)(struct cg2900_chip_dev *dev);
+	void (*last_fm_user_removed)(struct cg2900_chip_dev *dev);
+	void (*last_gnss_user_removed)(struct cg2900_chip_dev *dev);
+};
+
+/**
+ * struct cg2900_chip_dev - Chip handler info structure.
+ * @dev:	Parent device from CG2900 Core.
+ * @chip:	Chip info such as manufacturer.
+ * @cb:		Callback structure for the chip handler.
+ * @user_data:	Arbitrary data set by chip handler.
+ */
+struct cg2900_chip_dev {
+	struct device			*dev;
+	struct cg2900_chip_info		chip;
+	struct cg2900_chip_callbacks	cb;
+	void				*user_data;
+};
+
+/**
+ * struct cg2900_id_callbacks - Chip handler identification callbacks.
+ * @check_chip_support:	Called when chip is connected. If chip is supported by
+ *			driver, return true and fill in @callbacks in @dev.
+ *
+ * Note that the callback may be NULL. It must always be NULL checked before
+ * calling.
+ */
+struct cg2900_id_callbacks {
+	bool (*check_chip_support)(struct cg2900_chip_dev *dev);
+};
+
+/**
+ * struct cg2900_trans_dev - CG2900 transport info structure.
+ * @dev:	Parent device from CG2900 Core.
+ * @user_data:	Arbitrary data set by chip handler.
+ */
+struct cg2900_trans_dev {
+	struct device	*dev;
+	void		*user_data;
+};
+
+/**
+ * struct cg2900_trans_callbacks - Callback functions registered by transport.
+ * @open:		CG2900 Core needs a transport.
+ * @close:		CG2900 Core does not need a transport.
+ * @write:		CG2900 Core transmits to the chip.
+ * @set_chip_power:	CG2900 Core enables or disables the chip.
+ *
+ * Note that some callbacks may be NULL. They must always be NULL
checked before
+ * calling.
+ */
+struct cg2900_trans_callbacks {
+	int (*open)(struct cg2900_trans_dev *dev);
+	int (*close)(struct cg2900_trans_dev *dev);
+	int (*write)(struct cg2900_trans_dev *dev, struct sk_buff *skb);
+	void (*set_chip_power)(bool chip_on);
+};
+
+/**
+ * cg2900_register_chip_driver() - Register a chip handler.
+ * @cb:	Callbacks to call when chip is connected.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EINVAL if NULL is supplied as @cb.
+ *   -ENOMEM if allocation fails or work queue can't be created.
+ */
+extern int cg2900_register_chip_driver(struct cg2900_id_callbacks *cb);
+
+/**
+ * cg2900_register_trans_driver() - Register a transport driver.
+ * @cb:		Callbacks to call when chip is connected.
+ * @data:	Arbitrary data used by the transport driver.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EINVAL if NULL is supplied as @cb.
+ *   -ENOMEM if allocation fails or work queue can't be created.
+ */
+extern int cg2900_register_trans_driver(struct cg2900_trans_callbacks *cb,
+					void *data);
+
+/**
+ * cg2900_deregister_trans_driver() - Deregister a transport driver.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EINVAL if NULL is supplied as @cb.
+ *   -ENOMEM if allocation fails or work queue can't be created.
+ */
+extern int cg2900_deregister_trans_driver(void);
+
+/**
+ * cg2900_chip_startup_finished() - Called from chip handler when
start-up is finished.
+ * @err:	Result of the start-up.
+ *
+ * Returns:
+ *   0 if there is no error.
+ */
+extern int cg2900_chip_startup_finished(int err);
+
+/**
+ * cg2900_chip_shutdown_finished() - Called from chip handler when
shutdown is finished.
+ * @err:	Result of the shutdown.
+ *
+ * Returns:
+ *   0 if there is no error.
+ */
+extern int cg2900_chip_shutdown_finished(int err);
+
+/**
+ * cg2900_send_to_chip() - Send data to chip.
+ * @skb:	Packet to transmit.
+ * @use_logger:	true if hci_logger should copy data content.
+ *
+ * Returns:
+ *   0 if there is no error.
+ */
+extern int cg2900_send_to_chip(struct sk_buff *skb, bool use_logger);
+
+/**
+ * cg2900_get_bt_cmd_dev() - Return user of the BT command H:4 channel.
+ *
+ * Returns:
+ *   User of the BT command H:4 channel.
+ *   NULL if no user is registered.
+ */
+extern struct cg2900_device *cg2900_get_bt_cmd_dev(void);
+
+/**
+ * cg2900_get_fm_radio_dev() - Return user of the FM radio H:4 channel.
+ *
+ * Returns:
+ *   User of the FM radio H:4 channel.
+ *   NULL if no user is registered.
+ */
+extern struct cg2900_device *cg2900_get_fm_radio_dev(void);
+
+/**
+ * cg2900_get_bt_audio_dev() - Return user of the BT audio H:4 channel.
+ *
+ * Returns:
+ *   User of the BT audio H:4 channel.
+ *   NULL if no user is registered.
+ */
+extern struct cg2900_device *cg2900_get_bt_audio_dev(void);
+
+/**
+ * cg2900_get_fm_audio_dev() - Return user of the FM audio H:4 channel.
+ *
+ * Returns:
+ *   User of the FM audio H:4 channel.
+ *   NULL if no user is registered.
+ */
+extern struct cg2900_device *cg2900_get_fm_audio_dev(void);
+
+/**
+ * cg2900_get_hci_logger_config() - Return HCI Logger configuration.
+ *
+ * Returns:
+ *   HCI logger configuration.
+ *   NULL if CG2900 Core has not yet been started.
+ */
+extern struct cg2900_hci_logger_config *cg2900_get_hci_logger_config(void);
+
+/**
+ * cg2900_get_sleep_timeout() - Return sleep timeout in jiffies.
+ *
+ * Returns:
+ *   Sleep timeout in jiffies. 0 means that sleep timeout shall not be used.
+ */
+extern unsigned long cg2900_get_sleep_timeout(void);
+
+/**
+ * cg2900_data_from_chip() - Data received from connectivity controller.
+ * @skb: Data packet
+ *
+ * The cg2900_data_from_chip() function checks which channel
+ * the data was received on and send to the right user.
+ */
+extern void cg2900_data_from_chip(struct sk_buff *skb);
+
+/* module_param declared in cg2900_core.c */
+extern u8 bd_address[BT_BDADDR_SIZE];
+extern int default_manufacturer;
+extern int default_hci_revision;
+extern int default_sub_version;
+
+#endif /* _CG2900_CORE_H_ */
diff --git a/drivers/mfd/cg2900/cg2900_debug.h
b/drivers/mfd/cg2900/cg2900_debug.h
new file mode 100644
index 0000000..a3aeeaf
--- /dev/null
+++ b/drivers/mfd/cg2900/cg2900_debug.h
@@ -0,0 +1,77 @@
+/*
+ * drivers/mfd/cg2900/cg2900_debug.h
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@...ricsson.com) for
ST-Ericsson.
+ * Henrik Possung (henrik.possung@...ricsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@...ricsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@...ricsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@...ricsson.com) for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ *
+ * Debug functionality for the Linux Bluetooth HCI H:4 Driver for ST-Ericsson
+ * CG2900 connectivity controller.
+ */
+
+#ifndef _CG2900_DEBUG_H_
+#define _CG2900_DEBUG_H_
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#define CG2900_DEFAULT_DEBUG_LEVEL 1
+
+/* module_param declared in cg2900_core.c */
+extern int cg2900_debug_level;
+
+#if defined(NDEBUG) || CG2900_DEFAULT_DEBUG_LEVEL == 0
+	#define CG2900_DBG_DATA_CONTENT(fmt, arg...)
+	#define CG2900_DBG_DATA(fmt, arg...)
+	#define CG2900_DBG(fmt, arg...)
+	#define CG2900_INFO(fmt, arg...)
+	#define CG2900_ERR(fmt, arg...)
+#else
+	#define CG2900_DBG_DATA_CONTENT(fmt, arg...)			\
+	do {								\
+		if (cg2900_debug_level >= 30)				\
+			printk(KERN_DEBUG "CG2900 %s: " fmt "\n" , __func__ , \
+			       ## arg); \
+	} while (0)
+
+	#define CG2900_DBG_DATA(fmt, arg...)				\
+	do {								\
+		if (cg2900_debug_level >= 25)				\
+			printk(KERN_DEBUG "CG2900 %s: " fmt "\n" , __func__ , \
+			       ## arg); \
+	} while (0)
+
+	#define CG2900_DBG(fmt, arg...)				\
+	do {								\
+		if (cg2900_debug_level >= 20)				\
+			printk(KERN_DEBUG "CG2900 %s: " fmt "\n" , __func__ , \
+			       ## arg); \
+	} while (0)
+
+	#define CG2900_INFO(fmt, arg...)				\
+	do {								\
+		if (cg2900_debug_level >= 10)				\
+			printk(KERN_INFO "CG2900: " fmt "\n" , ## arg); \
+	} while (0)
+
+	#define CG2900_ERR(fmt, arg...)				\
+	do {								\
+		if (cg2900_debug_level >= 1)				\
+			printk(KERN_ERR  "CG2900 %s: " fmt "\n" , __func__ , \
+			       ## arg); \
+	} while (0)
+
+#endif /* NDEBUG */
+
+#define CG2900_SET_STATE(__name, __var, __new_state)	\
+do {									\
+	CG2900_DBG("New %s: 0x%X", __name, (uint32_t)__new_state);	\
+	__var = __new_state;				\
+} while (0)
+
+#endif /* _CG2900_DEBUG_H_ */
diff --git a/drivers/mfd/cg2900/hci_defines.h b/drivers/mfd/cg2900/hci_defines.h
new file mode 100644
index 0000000..e7f7c30
--- /dev/null
+++ b/drivers/mfd/cg2900/hci_defines.h
@@ -0,0 +1,102 @@
+/*
+ * drivers/mfd/cg2900/hci_defines.h
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@...ricsson.com) for
ST-Ericsson.
+ * Henrik Possung (henrik.possung@...ricsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@...ricsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@...ricsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@...ricsson.com) for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI defines for ST-Ericsson CG2900 connectivity controller.
+ */
+
+#ifndef _BLUETOOTH_DEFINES_H_
+#define _BLUETOOTH_DEFINES_H_
+
+#include <linux/types.h>
+
+/* H:4 offset in an HCI packet */
+#define HCI_H4_POS				0
+#define HCI_H4_SIZE				1
+
+/* Standardized Bluetooth H:4 channels */
+#define HCI_BT_CMD_H4_CHANNEL			0x01
+#define HCI_BT_ACL_H4_CHANNEL			0x02
+#define HCI_BT_SCO_H4_CHANNEL			0x03
+#define HCI_BT_EVT_H4_CHANNEL			0x04
+
+/* Bluetooth Opcode Group Field (OGF) */
+#define HCI_BT_OGF_LINK_CTRL			0x01
+#define HCI_BT_OGF_LINK_POLICY			0x02
+#define HCI_BT_OGF_CTRL_BB			0x03
+#define HCI_BT_OGF_LINK_INFO			0x04
+#define HCI_BT_OGF_LINK_STATUS			0x05
+#define HCI_BT_OGF_LINK_TESTING			0x06
+#define HCI_BT_OGF_VS				0x3F
+
+/* Bluetooth Opcode Command Field (OCF) */
+#define HCI_BT_OCF_READ_LOCAL_VERSION_INFO	0x0001
+#define HCI_BT_OCF_RESET			0x0003
+
+/* Bluetooth HCI command OpCodes in LSB/MSB fashion */
+#define HCI_BT_RESET_CMD_LSB			0x03
+#define HCI_BT_RESET_CMD_MSB			0x0C
+#define HCI_BT_READ_LOCAL_VERSION_CMD_LSB	0x01
+#define HCI_BT_READ_LOCAL_VERSION_CMD_MSB	0x10
+
+/* Bluetooth Event OpCodes */
+#define HCI_BT_EVT_CMD_COMPLETE			0x0E
+#define HCI_BT_EVT_CMD_STATUS			0x0F
+
+/* Bluetooth Command offsets */
+#define HCI_BT_CMD_ID_POS			1
+#define HCI_BT_CMD_PARAM_LEN_POS		3
+#define HCI_BT_CMD_PARAM_POS			4
+#define HCI_BT_CMD_HDR_SIZE			4
+
+/* Bluetooth Event offsets for CG2900 users, i.e. not including H:4 channel */
+#define HCI_BT_EVT_ID_POS			0
+#define HCI_BT_EVT_LEN_POS			1
+#define HCI_BT_EVT_CMD_COMPL_ID_POS		3
+#define HCI_BT_EVT_CMD_STATUS_ID_POS		4
+#define HCI_BT_EVT_CMD_COMPL_STATUS_POS		5
+#define HCI_BT_EVT_CMD_STATUS_STATUS_POS	2
+#define HCI_BT_EVT_CMD_COMPL_NR_OF_PKTS_POS	2
+#define HCI_BT_EVT_CMD_STATUS_NR_OF_PKTS_POS	3
+
+#define HCI_BT_EVT_CMD_COMPL_ID_POS_LSB		HCI_BT_EVT_CMD_COMPL_ID_POS
+#define HCI_BT_EVT_CMD_COMPL_ID_POS_MSB
(HCI_BT_EVT_CMD_COMPL_ID_POS + 1)
+#define HCI_BT_EVT_CMD_STATUS_ID_POS_LSB	HCI_BT_EVT_CMD_STATUS_ID_POS
+#define HCI_BT_EVT_CMD_STATUS_ID_POS_MSB
(HCI_BT_EVT_CMD_STATUS_ID_POS + 1)
+
+/* Bluetooth error codes */
+#define HCI_BT_ERROR_NO_ERROR			0x00
+#define HCI_BT_ERROR_CMD_DISALLOWED		0x0C
+
+/* Bluetooth lengths */
+#define HCI_BT_SEND_FILE_MAX_CHUNK_SIZE		254
+
+#define HCI_BT_RESET_LEN			3
+#define HCI_BT_RESET_PARAM_LEN			0
+#define HCI_BT_CMD_COMPLETE_NO_PARAM_LEN	4
+
+/* Utility macros */
+#define GET_U16_FROM_L_ENDIAN(__u8_lsb, __u8_msb)	((u16)(__u8_lsb | \
+							 (__u8_msb << 8)))
+#define HCI_BT_MAKE_FIRST_BYTE_IN_CMD(__ocf)		((u8)(__ocf & 0x00FF))
+#define HCI_BT_MAKE_SECOND_BYTE_IN_CMD(__ogf, __ocf)	((u8)(__ogf << 2) | \
+		((__ocf >> 8) & 0x0003))
+#define HCI_BT_ID_TO_OCF(__1st_byte, __2nd_byte)	((u16)(__1st_byte | \
+		((__2nd_byte & 0x03) << 8)))
+#define HCI_BT_ID_TO_OGF(__1st_byte)		((u8)(__1st_byte >> 2))
+#define HCI_BT_GET_ID				GET_U16_FROM_L_ENDIAN
+#define HCI_BT_EVENT_GET_ID			GET_U16_FROM_L_ENDIAN
+
+/* Macros to set multibyte words to correct HCI endian (always Little
Endian) */
+#define HCI_SET_U16_DATA_LSB(__u16_data)	((u8)(__u16_data & 0x00FF))
+#define HCI_SET_U16_DATA_MSB(__u16_data)	((u8)(__u16_data >> 8))
+
+#endif /* _BLUETOOTH_DEFINES_H_ */
diff --git a/include/linux/mfd/cg2900.h b/include/linux/mfd/cg2900.h
new file mode 100644
index 0000000..9c2afc5
--- /dev/null
+++ b/include/linux/mfd/cg2900.h
@@ -0,0 +1,205 @@
+/*
+ * include/linux/mfd/cg2900.h
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@...ricsson.com) for
ST-Ericsson.
+ * Henrik Possung (henrik.possung@...ricsson.com) for ST-Ericsson.
+ * Josef Kindberg (josef.kindberg@...ricsson.com) for ST-Ericsson.
+ * Dariusz Szymszak (dariusz.xd.szymczak@...ricsson.com) for ST-Ericsson.
+ * Kjell Andersson (kjell.k.andersson@...ricsson.com) for ST-Ericsson.
+ * License terms:  GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth HCI H:4 Driver for ST-Ericsson CG2900 connectivity
+ * controller.
+ */
+
+#ifndef _CG2900_H_
+#define _CG2900_H_
+
+#include <linux/skbuff.h>
+
+#define CG2900_MAX_NAME_SIZE 30
+
+/*
+ * Channel names to use when registering to CG2900 driver
+ */
+
+/** CG2900_BT_CMD - Bluetooth HCI H4 channel for Bluetooth commands.
+ */
+#define CG2900_BT_CMD		"cg2900_bt_cmd"
+
+/** CG2900_BT_ACL - Bluetooth HCI H4 channel for Bluetooth ACL data.
+ */
+#define CG2900_BT_ACL		"cg2900_bt_acl"
+
+/** CG2900_BT_EVT - Bluetooth HCI H4 channel for Bluetooth events.
+ */
+#define CG2900_BT_EVT		"cg2900_bt_evt"
+
+/** CG2900_FM_RADIO - Bluetooth HCI H4 channel for FM radio.
+ */
+#define CG2900_FM_RADIO		"cg2900_fm_radio"
+
+/** CG2900_GNSS - Bluetooth HCI H4 channel for GNSS.
+ */
+#define CG2900_GNSS		"cg2900_gnss"
+
+/** CG2900_DEBUG - Bluetooth HCI H4 channel for internal debug data.
+ */
+#define CG2900_DEBUG		"cg2900_debug"
+
+/** CG2900_STE_TOOLS - Bluetooth HCI H4 channel for development tools data.
+ */
+#define CG2900_STE_TOOLS	"cg2900_ste_tools"
+
+/** CG2900_HCI_LOGGER - BT channel for logging all transmitted H4 packets.
+ * Data read is copy of all data transferred on the other channels.
+ * Only write allowed is configuration of the HCI Logger.
+ */
+#define CG2900_HCI_LOGGER	"cg2900_hci_logger"
+
+/** CG2900_US_CTRL - Channel for user space init and control of CG2900.
+ */
+#define CG2900_US_CTRL		"cg2900_us_ctrl"
+
+/** CG2900_BT_AUDIO - HCI Channel for BT audio configuration commands.
+ * Maps to Bluetooth command and event channels.
+ */
+#define CG2900_BT_AUDIO		"cg2900_bt_audio"
+
+/** CG2900_FM_RADIO_AUDIO - HCI channel for FM audio configuration commands.
+ * Maps to FM Radio channel.
+ */
+#define CG2900_FM_RADIO_AUDIO	"cg2900_fm_audio"
+
+/** CG2900_CORE- Channel for keeping ST-Ericsson CG2900 enabled.
+ * Opening this channel forces the chip to stay powered.
+ * No data can be written to or read from this channel.
+ */
+#define CG2900_CORE		"cg2900_core"
+
+struct cg2900_callbacks;
+
+/**
+ * struct cg2900_device - Device structure for CG2900 user.
+ * @h4_channel:		HCI H:4 channel used by this device.
+ * @cb:			Callback functions registered by this device.
+ * @logger_enabled:	true if HCI logger is enabled for this channel,
+ *			false otherwise.
+ * @user_data:		Arbitrary data used by caller.
+ * @dev:		Parent device this driver is connected to.
+ *
+ * Defines data needed to access an HCI channel.
+ */
+struct cg2900_device {
+	int				h4_channel;
+	struct cg2900_callbacks		*cb;
+	bool				logger_enabled;
+	void				*user_data;
+	struct device			*dev;
+};
+
+/**
+ * struct cg2900_callbacks - Callback structure for CG2900 user.
+ * @read_cb:	Callback function called when data is received from
+ *		the connectivity controller.
+ * @reset_cb:	Callback function called when the connectivity controller has
+ *		been reset.
+ *
+ * Defines the callback functions provided from the caller.
+ */
+struct cg2900_callbacks {
+	void (*read_cb) (struct cg2900_device *dev, struct sk_buff *skb);
+	void (*reset_cb) (struct cg2900_device *dev);
+};
+
+/**
+ * struct cg2900_rev_data - Contains revision data for the local controller.
+ * @revision:		Revision of the controller, e.g. to indicate that it is
+ *			a CG2900 controller.
+ * @sub_version:	Subversion of the controller, e.g. to indicate a certain
+ *			tape-out of the controller.
+ *
+ * The values to match retrieved values to each controller may be
retrieved from
+ * the manufacturer.
+ */
+struct cg2900_rev_data {
+	int revision;
+	int sub_version;
+};
+
+/**
+ * cg2900_register_user() - Register CG2900 user.
+ * @name:	Name of HCI H:4 channel to register to.
+ * @cb:		Callback structure to use for the H:4 channel.
+ *
+ * Returns:
+ *   Pointer to CG2900 device structure if successful.
+ *   NULL upon failure.
+ */
+extern struct cg2900_device *cg2900_register_user(char *name,
+						  struct cg2900_callbacks *cb);
+
+/**
+ * cg2900_deregister_user() - Remove registration of CG2900 user.
+ * @dev:	CG2900 device.
+ */
+extern void cg2900_deregister_user(struct cg2900_device *dev);
+
+/**
+ * cg2900_reset() - Reset the CG2900 controller.
+ * @dev:	CG2900 device.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EACCES if driver has not been initialized.
+ */
+extern int cg2900_reset(struct cg2900_device *dev);
+
+/**
+ * cg2900_alloc_skb() - Alloc an sk_buff structure for CG2900 handling.
+ * @size:	Size in number of octets.
+ * @priority:	Allocation priority, e.g. GFP_KERNEL.
+ *
+ * Returns:
+ *   Pointer to sk_buff buffer structure if successful.
+ *   NULL upon allocation failure.
+ */
+extern struct sk_buff *cg2900_alloc_skb(unsigned int size, gfp_t priority);
+
+/**
+ * cg2900_write() - Send data to the connectivity controller.
+ * @dev: CG2900 device.
+ * @skb: Data packet.
+ *
+ * The cg2900_write() function sends data to the connectivity controller.
+ * If the return value is 0 the skb will be freed by the driver,
+ * otherwise it won't be freed.
+ *
+ * Returns:
+ *   0 if there is no error.
+ *   -EACCES if driver has not been initialized or trying to write while driver
+ *   is not active.
+ *   -EINVAL if NULL pointer was supplied.
+ *   -EPERM if operation is not permitted, e.g. trying to write to a channel
+ *   that doesn't handle write operations.
+ *   Error codes returned from core_enable_hci_logger.
+ */
+extern int cg2900_write(struct cg2900_device *dev, struct sk_buff *skb);
+
+/**
+ * cg2900_get_local_revision() - Read revision of the connected controller.
+ * @rev_data:	Revision data structure to fill. Must be allocated by caller.
+ *
+ * The cg2900_get_local_revision() function returns the revision data of the
+ * local controller if available. If data is not available, e.g. because the
+ * controller has not yet been started this function will return false.
+ *
+ * Returns:
+ *   true if revision data is available.
+ *   false if no revision data is available.
+ */
+extern bool cg2900_get_local_revision(struct cg2900_rev_data *rev_data);
+
+#endif /* _CG2900_H_ */
-- 
1.6.3.3
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ