* [RFC v2 4/6] android: Update README with init.rc updates
From: Szymon Janc @ 2014-01-17 15:30 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1389972646-3761-1-git-send-email-szymon.janc@tieto.com>
---
android/README | 32 +++++++-------------------------
1 file changed, 7 insertions(+), 25 deletions(-)
diff --git a/android/README b/android/README
index 717ffa2..24ed703 100644
--- a/android/README
+++ b/android/README
@@ -36,31 +36,13 @@ Runtime requirements
====================
BlueZ HAL library requires 'bluetoothd' and 'bluetoothd-snoop' services to be
-available on Android system. This can be done by defining following services in
-init.rc file of targeted board:
-
-service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
- class main
- group bluetooth net_admin
- disabled
- oneshot
-
-service bluetoothd-snoop /system/bin/bluetoothd-snoop
- class main
- group bluetooth net_admin
- disabled
- oneshot
-
-It is required that bluetooth user could start and stop bluetoothd and
-bluetoothd-snoop services by setting 'ctl.start' or 'ctl.stop' property. This
-can be achieved by whitelisting bluetooth user and bluetoothd and
-bluetoothd-snoop services in init source code.
-
-Required Android init system modifications can be found at
-https://code.google.com/p/aosp-bluez.platform-system-core/
-
-Some configuration changes like setting permissions, starting hciattach
-services etc. are device specific. For convenience examples are provided at:
+available on Android system. Some permissions settings are also required.
+
+This can be done by importing init.bluetooth.rc file in init.rc file of targeted
+board:
+import init.bluetooth.rc
+
+For convenience examples are provided at:
https://code.google.com/p/aosp-bluez.device-lge-mako/ (Nexus 4)
https://code.google.com/p/aosp-bluez.device-asus-flo/ (Nexus 7 2013)
--
1.8.3.2
^ permalink raw reply related
* [RFC v2 3/6] android/system-emulator: Update property used for start/stop services
From: Szymon Janc @ 2014-01-17 15:30 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1389972646-3761-1-git-send-email-szymon.janc@tieto.com>
---
android/system-emulator.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/android/system-emulator.c b/android/system-emulator.c
index f1c6622..cfeab8b 100644
--- a/android/system-emulator.c
+++ b/android/system-emulator.c
@@ -139,17 +139,17 @@ static void system_socket_callback(int fd, uint32_t events, void *user_data)
printf("Received %s\n", buf);
- if (!strcmp(buf, "ctl.start=bluetoothd")) {
+ if (!strcmp(buf, "bluetooth.start=daemon")) {
if (daemon_pid > 0)
return;
ctl_start();
- } else if (!strcmp(buf, "ctl.start=bluetoothd-snoop")) {
+ } else if (!strcmp(buf, "bluetooth.start=snoop")) {
if (snoop_pid > 0)
return;
snoop_start();
- } else if (!strcmp(buf, "ctl.stop=bluetoothd-snoop")) {
+ } else if (!strcmp(buf, "bluetooth.stop=snoop")) {
if (snoop_pid > 0)
snoop_stop();
}
--
1.8.3.2
^ permalink raw reply related
* [RFC v2 2/6] android/hal: Update property used for start/stop services
From: Szymon Janc @ 2014-01-17 15:30 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1389972646-3761-1-git-send-email-szymon.janc@tieto.com>
---
android/hal-bluetooth.c | 13 +++++--------
android/hal-ipc.c | 5 ++---
2 files changed, 7 insertions(+), 11 deletions(-)
diff --git a/android/hal-bluetooth.c b/android/hal-bluetooth.c
index be45836..4f0e7b7 100644
--- a/android/hal-bluetooth.c
+++ b/android/hal-bluetooth.c
@@ -28,8 +28,6 @@
#include "hal-ipc.h"
#include "hal-utils.h"
-#define SNOOP_SERVICE_NAME "bluetoothd-snoop"
-
static const bt_callbacks_t *bt_hal_cbacks = NULL;
#define enum_prop_to_hal(prop, hal_prop, type) do { \
@@ -820,15 +818,14 @@ static int le_test_mode(uint16_t opcode, uint8_t *buf, uint8_t len)
static int config_hci_snoop_log(uint8_t enable)
{
+ const char *property;
+
DBG("enable %u", enable);
- if (enable && property_set("ctl.start", SNOOP_SERVICE_NAME) < 0) {
- error("Failed to start service %s", SNOOP_SERVICE_NAME);
- return BT_STATUS_FAIL;
- }
+ property = enable ? "bluetooth.start" : "bluetooth.stop";
- if (!enable && property_set("ctl.stop", SNOOP_SERVICE_NAME) < 0) {
- error("Failed to stop service %s", SNOOP_SERVICE_NAME);
+ if (property_set(property, "snoop") < 0) {
+ error("Failed to set %s=snoop", property);
return BT_STATUS_FAIL;
}
diff --git a/android/hal-ipc.c b/android/hal-ipc.c
index 97f1bcd..99ba38e 100644
--- a/android/hal-ipc.c
+++ b/android/hal-ipc.c
@@ -34,7 +34,6 @@
#include "hal-ipc.h"
#define CONNECT_TIMEOUT (5 * 1000)
-#define SERVICE_NAME "bluetoothd"
static int cmd_sk = -1;
static int notif_sk = -1;
@@ -259,8 +258,8 @@ bool hal_ipc_init(void)
}
/* Start Android Bluetooth daemon service */
- if (property_set("ctl.start", SERVICE_NAME) < 0) {
- error("Failed to start service %s", SERVICE_NAME);
+ if (property_set("bluetooth.start", "daemon") < 0) {
+ error("Failed to set bluetooth.start=daemon");
close(sk);
return false;
}
--
1.8.3.2
^ permalink raw reply related
* [RFC v2 1/6] android: Add sample init.bluetooth.rc file
From: Szymon Janc @ 2014-01-17 15:30 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Szymon Janc
This file is intended to be included from device init.rc.
---
android/Android.mk | 16 +++++++++++++++-
android/Makefile.am | 1 +
android/init.bluetooth.rc | 37 +++++++++++++++++++++++++++++++++++++
3 files changed, 53 insertions(+), 1 deletion(-)
create mode 100644 android/init.bluetooth.rc
diff --git a/android/Android.mk b/android/Android.mk
index 7e97ec8..afa3a51 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -109,7 +109,7 @@ LOCAL_MODULE := bluetooth.default
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop
+LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
include $(BUILD_SHARED_LIBRARY)
@@ -282,3 +282,17 @@ LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := bluetoothd-snoop
include $(BUILD_EXECUTABLE)
+
+#
+# init.bluetooth.rc
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init.bluetooth.rc
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+include $(BUILD_PREBUILT)
diff --git a/android/Makefile.am b/android/Makefile.am
index 8d2714d..5aa3995 100644
--- a/android/Makefile.am
+++ b/android/Makefile.am
@@ -136,6 +136,7 @@ android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
endif
EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
+ android/init.bluetooth.rc \
android/pics-gap.txt android/pics-hid.txt \
android/pics-pan.txt android/pics-did.txt \
android/pics-opp.txt android/pics-pbap.txt \
diff --git a/android/init.bluetooth.rc b/android/init.bluetooth.rc
new file mode 100644
index 0000000..9f9aa84
--- /dev/null
+++ b/android/init.bluetooth.rc
@@ -0,0 +1,37 @@
+# required permissions
+on boot
+ chown bluetooth bluetooth /data/misc/bluetooth
+ chown bluetooth bluetooth /dev/uhid
+
+# services
+on property:bluetooth.start=daemon
+ setprop bluetooth.start none
+ start bluetoothd
+
+on property:bluetooth.stop=daemon
+ setprop bluetooth.stop none
+ stop bluetoothd
+
+on property:bluetooth.start=snoop
+ setprop bluetooth.start none
+ start bluetoothd-snoop
+
+on property:bluetooth.stop=snoop
+ setprop bluetooth.stop none
+ stop bluetoothd-snoop
+
+service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
+ class main
+ # init does not yet support setting capabilities so run as root,
+ # bluetoothd drop uid to bluetooth with the right linux capabilities
+ group bluetooth
+ disabled
+ oneshot
+
+service bluetoothd-snoop /system/bin/logwrapper /system/bin/bluetoothd-snoop
+ class main
+ # init does not yet support setting capabilities so run as root,
+ # bluetoothd-snoop drops unneeded linux capabilities
+ group nobody
+ disabled
+ oneshot
--
1.8.3.2
^ permalink raw reply related
* Re: [PATCH 3/3] unit: prevent use of glibc's error(3)
From: Natanael Copa @ 2014-01-17 15:07 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <CABBYNZ+LYk1c0YUU0==JyKxJ_SbX6nNbcpwRCd==HUV1CKx+Ow@mail.gmail.com>
On Fri, 17 Jan 2014 16:37:15 +0200
Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
> Hi Natanael,
>
> On Fri, Jan 17, 2014 at 2:08 PM, Natanael Copa <natanael.copa@gmail.com> wrote:
> > When building the test-sdp we don't want src/sdpd-request.c end up
> > using the incompatible GNU libc's error(3).
> >
> > This also fixes building on musl libc which misses the error(3) GNU
> > extension.
> > ---
> > unit/test-sdp.c | 6 ++++++
> > 1 file changed, 6 insertions(+)
> >
> > diff --git a/unit/test-sdp.c b/unit/test-sdp.c
> > index 6d699e2..eeed0cb 100644
> > --- a/unit/test-sdp.c
> > +++ b/unit/test-sdp.c
> > @@ -134,6 +134,12 @@ void btd_debug(const char *format, ...)
> > {
> > }
> >
> > +void error(const char *format, ...);
> > +
> > +void error(const char *format, ...)
> > +{
> > +}
> > +
> > static void context_quit(struct context *context)
> > {
> > g_main_loop_quit(context->main_loop);
> > --
> > 1.8.5.3
>
> We could perhaps do the same thing we did in test-avdtp.c, build with
> log support and add the following check:
>
> if (g_test_verbose())
> __btd_log_init("*", 0);
>
>
sounds good to me.
-nc
^ permalink raw reply
* Re: [PATCH 1/3] bnep: avoid use of caddr_t
From: Luiz Augusto von Dentz @ 2014-01-17 14:47 UTC (permalink / raw)
To: Natanael Copa; +Cc: linux-bluetooth@vger.kernel.org, Natanael Copa
In-Reply-To: <1389960506-10273-1-git-send-email-ncopa@alpinelinux.org>
Hi Natanael,
On Fri, Jan 17, 2014 at 2:08 PM, Natanael Copa <natanael.copa@gmail.com> wrote:
> caddr_t is legacy BSD and should be avoided.
>
> This fixes building against musl libc.
The patches looks good, but could please also add to the description
the actual errors you got.
^ permalink raw reply
* Re: [PATCH 3/3] unit: prevent use of glibc's error(3)
From: Luiz Augusto von Dentz @ 2014-01-17 14:37 UTC (permalink / raw)
To: Natanael Copa; +Cc: linux-bluetooth@vger.kernel.org, Natanael Copa
In-Reply-To: <1389960506-10273-3-git-send-email-ncopa@alpinelinux.org>
Hi Natanael,
On Fri, Jan 17, 2014 at 2:08 PM, Natanael Copa <natanael.copa@gmail.com> wrote:
> When building the test-sdp we don't want src/sdpd-request.c end up
> using the incompatible GNU libc's error(3).
>
> This also fixes building on musl libc which misses the error(3) GNU
> extension.
> ---
> unit/test-sdp.c | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/unit/test-sdp.c b/unit/test-sdp.c
> index 6d699e2..eeed0cb 100644
> --- a/unit/test-sdp.c
> +++ b/unit/test-sdp.c
> @@ -134,6 +134,12 @@ void btd_debug(const char *format, ...)
> {
> }
>
> +void error(const char *format, ...);
> +
> +void error(const char *format, ...)
> +{
> +}
> +
> static void context_quit(struct context *context)
> {
> g_main_loop_quit(context->main_loop);
> --
> 1.8.5.3
We could perhaps do the same thing we did in test-avdtp.c, build with
log support and add the following check:
if (g_test_verbose())
__btd_log_init("*", 0);
--
Luiz Augusto von Dentz
^ permalink raw reply
* [PATCH BlueZ 4/4] android/A2DP: Notify when audio state change to suspend
From: Luiz Augusto von Dentz @ 2014-01-17 13:33 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1389965587-20066-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
android/a2dp.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/android/a2dp.c b/android/a2dp.c
index 12a8a69..8ec03c8 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -1007,6 +1007,8 @@ static gboolean sep_suspend_ind(struct avdtp *session,
return FALSE;
}
+ bt_audio_notify_state(setup, HAL_AUDIO_SUSPEND);
+
return TRUE;
}
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ 3/4] android/A2DP: Notify when audio state change to stopped
From: Luiz Augusto von Dentz @ 2014-01-17 13:33 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1389965587-20066-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
android/a2dp.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 515cf17..12a8a69 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -1106,13 +1106,23 @@ static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
void *user_data)
{
struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_setup *setup;
DBG("");
- if (!err)
+ if (err) {
+ setup_remove_by_id(endpoint->id);
return;
+ }
- setup_remove_by_id(endpoint->id);
+ setup = find_setup(endpoint->id);
+ if (!setup) {
+ error("Unable to find stream setup for %u endpoint",
+ endpoint->id);
+ return;
+ }
+
+ bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
}
static void sep_close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ 2/4] android/A2DP: Notify when audio state change to started
From: Luiz Augusto von Dentz @ 2014-01-17 13:33 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1389965587-20066-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
android/a2dp.c | 37 +++++++++++++++++++++++++++++++++++--
1 file changed, 35 insertions(+), 2 deletions(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 2288912..515cf17 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -85,6 +85,7 @@ struct a2dp_setup {
struct a2dp_endpoint *endpoint;
struct a2dp_preset *preset;
struct avdtp_stream *stream;
+ uint8_t state;
};
static int device_cmp(gconstpointer s, gconstpointer user_data)
@@ -190,6 +191,26 @@ static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state)
a2dp_device_free(dev);
}
+static void bt_audio_notify_state(struct a2dp_setup *setup, uint8_t state)
+{
+ struct hal_ev_a2dp_audio_state ev;
+ char address[18];
+
+ if (setup->state == state)
+ return;
+
+ setup->state = state;
+
+ ba2str(&setup->dev->dst, address);
+ DBG("device %s state %u", address, state);
+
+ bdaddr2android(&setup->dev->dst, ev.bdaddr);
+ ev.state = state;
+
+ ipc_send_notif(HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_AUDIO_STATE, sizeof(ev),
+ &ev);
+}
+
static void disconnect_cb(void *user_data)
{
struct a2dp_device *dev = user_data;
@@ -962,6 +983,8 @@ static gboolean sep_start_ind(struct avdtp *session,
return FALSE;
}
+ bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
+
return TRUE;
}
@@ -1059,13 +1082,23 @@ static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
void *user_data)
{
struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_setup *setup;
DBG("");
- if (!err)
+ if (err) {
+ setup_remove_by_id(endpoint->id);
return;
+ }
- setup_remove_by_id(endpoint->id);
+ setup = find_setup(endpoint->id);
+ if (!setup) {
+ error("Unable to find stream setup for %u endpoint",
+ endpoint->id);
+ return;
+ }
+
+ bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
}
static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ 1/4] android/hal-msg: Add defines for each audio state
From: Luiz Augusto von Dentz @ 2014-01-17 13:33 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
android/hal-msg.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index ceaa3b2..cfb5460 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -564,6 +564,10 @@ struct hal_ev_a2dp_conn_state {
uint8_t bdaddr[6];
} __attribute__((packed));
+#define HAL_AUDIO_SUSPEND 0x00
+#define HAL_AUDIO_STOPPED 0x01
+#define HAL_AUDIO_STARTED 0x02
+
#define HAL_EV_A2DP_AUDIO_STATE 0x82
struct hal_ev_a2dp_audio_state {
uint8_t state;
--
1.8.4.2
^ permalink raw reply related
* [PATCH v7] staging/bluetooth: Add hci_h4p driver
From: Pavel Machek @ 2014-01-17 13:29 UTC (permalink / raw)
To: Marcel Holtmann, gregkh
Cc: Pali Rohár,
Ивайло Димитров,
Gustavo F. Padovan, Johan Hedberg, linux-kernel,
linux-bluetooth@vger.kernel.org development, Ville Tervo,
Sebastian Reichel
In-Reply-To: <FD05FD52-1502-4AB8-8650-FC785B700088@holtmann.org>
Add hci_h4p bluetooth driver to bluetooth-next. This device is used
for example on Nokia N900 cell phone.
Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
Signed-off-by: Pavel Machek <pavel@ucw.cz>
Thanks-to: Sebastian Reichel <sre@debian.org>
Thanks-to: Joe Perches <joe@perches.com>
---
Move it to staging, rename header file as Marcel requested. Driver
still needs too many changes to make direct merge easy, yet it is
useful for using Maemo on N900.
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 3bfdaa8..8d5af7d 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -150,4 +150,6 @@ source "drivers/staging/dgnc/Kconfig"
source "drivers/staging/dgap/Kconfig"
+source "drivers/staging/nokia_h4p/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index b0d3303..cb2991e 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -67,3 +67,4 @@ obj-$(CONFIG_XILLYBUS) += xillybus/
obj-$(CONFIG_DGNC) += dgnc/
obj-$(CONFIG_DGAP) += dgap/
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
+obj-$(CONFIG_BT_NOKIA_H4P) += nokia_h4p/
diff --git a/drivers/staging/nokia_h4p/Kconfig b/drivers/staging/nokia_h4p/Kconfig
new file mode 100644
index 0000000..4336c0a
--- /dev/null
+++ b/drivers/staging/nokia_h4p/Kconfig
@@ -0,0 +1,9 @@
+config BT_NOKIA_H4P
+ tristate "HCI driver with H4 Nokia extensions"
+ depends on BT && ARCH_OMAP
+ help
+ Bluetooth HCI driver with H4 extensions. This driver provides
+ support for H4+ Bluetooth chip with vendor-specific H4 extensions.
+
+ Say Y here to compile support for h4 extended devices into the kernel
+ or say M to compile it as module (btnokia_h4p).
diff --git a/drivers/staging/nokia_h4p/Makefile b/drivers/staging/nokia_h4p/Makefile
new file mode 100644
index 0000000..9625db4
--- /dev/null
+++ b/drivers/staging/nokia_h4p/Makefile
@@ -0,0 +1,6 @@
+
+obj-$(CONFIG_BT_NOKIA_H4P) += btnokia_h4p.o
+btnokia_h4p-objs := nokia_core.o nokia_fw.o nokia_uart.o nokia_fw-csr.o \
+ nokia_fw-bcm.o nokia_fw-ti1273.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/staging/nokia_h4p/TODO b/drivers/staging/nokia_h4p/TODO
new file mode 100644
index 0000000..2194146
--- /dev/null
+++ b/drivers/staging/nokia_h4p/TODO
@@ -0,0 +1,140 @@
+Few attempts to submission have been made, last review comments were received in
+
+Date: Wed, 15 Jan 2014 19:01:51 -0800
+From: Marcel Holtmann <marcel@holtmann.org>
+Subject: Re: [PATCH v6] Bluetooth: Add hci_h4p driver
+
+Some code refactoring is still needed.
+
+TODO:
+
+> +++ b/drivers/bluetooth/hci_h4p.h
+
+can we please get the naming straight. File names do not start with
+hci_ anymore. We moved away from it since that term is too generic.
+
+> +#define FW_NAME_TI1271_LE "ti1273_le.bin"
+> +#define FW_NAME_TI1271 "ti1273.bin"
+> +#define FW_NAME_BCM2048 "bcmfw.bin"
+> +#define FW_NAME_CSR "bc4fw.bin"
+
+We do these have to be global in a header file. This should be
+confined to the specific firmware part.
+
+> +struct hci_h4p_info {
+
+Can we please get rid of the hci_ prefix for everything. Copying from
+drivers that are over 10 years old is not a good idea. Please look at
+recent ones.
+
+> + struct timer_list lazy_release;
+
+Timer? Not delayed work?
+
+> +void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val);
+> +u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset);
+> +void hci_h4p_set_rts(struct hci_h4p_info *info, int active);
+> +int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active, int timeout_ms);
+> +void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
+> +void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
+> +void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed);
+> +int hci_h4p_reset_uart(struct hci_h4p_info *info);
+> +void hci_h4p_init_uart(struct hci_h4p_info *info);
+> +void hci_h4p_enable_tx(struct hci_h4p_info *info);
+> +void hci_h4p_store_regs(struct hci_h4p_info *info);
+> +void hci_h4p_restore_regs(struct hci_h4p_info *info);
+> +void hci_h4p_smart_idle(struct hci_h4p_info *info, bool enable);
+
+These are a lot of public functions. Are they all really needed or can
+the code be done smart.
+
+> +static ssize_t hci_h4p_store_bdaddr(struct device *dev,
+> + struct device_attribute *attr,
+> + const char *buf, size_t count)
+> +{
+> + struct hci_h4p_info *info = dev_get_drvdata(dev);
+
+Since none of these devices can function without having a valid
+address, the way this should work is that we should not register the
+HCI device when probing the platform device.
+
+The HCI device should be registered once a valid address has been
+written into the sysfs file. I do not want to play the tricks with
+bringing up the device without a valid address.
+
+> + hdev->close = hci_h4p_hci_close;
+> + hdev->flush = hci_h4p_hci_flush;
+> + hdev->send = hci_h4p_hci_send_frame;
+
+It needs to use hdev->setup to load the firmware. I assume the
+firmware only needs to be loaded once. That is exactly what
+hdev->setup does. It gets executed once.
+
+> + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+
+Is this quirk really needed? Normally only Bluetooth 1.1 and early
+devices qualify for it.
+
+> +static int hci_h4p_bcm_set_bdaddr(struct hci_h4p_info *info, struct sk_buff *skb)
+> +{
+> + int i;
+> + static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
+> + int not_valid;
+
+Has this actually been confirmed that we can just randomly set an
+address out of the Nokia range. I do not think so. This is a pretty
+bad idea.
+
+I have no interest in merging a driver with such a hack.
+
+> + not_valid = 1;
+> + for (i = 0; i < 6; i++) {
+> + if (info->bd_addr[i] != 0x00) {
+> + not_valid = 0;
+> + break;
+> + }
+> + }
+
+Anybody every heard of memcmp or bacmp and BDADDR_ANY?
+
+> + if (not_valid) {
+> + dev_info(info->dev, "Valid bluetooth address not found,"
+> + " setting some random\n");
+> + /* When address is not valid, use some random */
+> + memcpy(info->bd_addr, nokia_oui, 3);
+> + get_random_bytes(info->bd_addr + 3, 3);
+> + }
+
+
+And why does every single chip firmware does this differently. Seriously, this is a mess.
+
+> +void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
+> +{
+> + switch (info->man_id) {
+> + case H4P_ID_CSR:
+> + hci_h4p_bc4_parse_fw_event(info, skb);
+> + break;
+...
+> +}
+
+We have proper HCI sync command handling in recent kernels. I really
+do not know why this is hand coded these days. Check how the Intel
+firmware loading inside btusb.c does it.
+
+> +inline u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset)
+> +{
+> + return __raw_readb(info->uart_base + (offset << 2));
+> +}
+
+Inline in a *.c file for a non-static function. Makes no sense to me.
+
+> +/**
+> + * struct hci_h4p_platform data - hci_h4p Platform data structure
+> + */
+> +struct hci_h4p_platform_data {
+
+please have a proper name here. For example
+btnokia_h4p_platform_data.
+
+Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
+Pavel Machek <pavel@ucw.cz>
diff --git a/drivers/staging/nokia_h4p/hci_h4p.h b/drivers/staging/nokia_h4p/hci_h4p.h
new file mode 100644
index 0000000..fd7a640
--- /dev/null
+++ b/drivers/staging/nokia_h4p/hci_h4p.h
@@ -0,0 +1,228 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * Copyright (C) 2005-2008 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __DRIVERS_BLUETOOTH_HCI_H4P_H
+#define __DRIVERS_BLUETOOTH_HCI_H4P_H
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#define FW_NAME_TI1271_PRELE "ti1273_prele.bin"
+#define FW_NAME_TI1271_LE "ti1273_le.bin"
+#define FW_NAME_TI1271 "ti1273.bin"
+#define FW_NAME_BCM2048 "bcmfw.bin"
+#define FW_NAME_CSR "bc4fw.bin"
+
+#define UART_SYSC_OMAP_RESET 0x03
+#define UART_SYSS_RESETDONE 0x01
+#define UART_OMAP_SCR_EMPTY_THR 0x08
+#define UART_OMAP_SCR_WAKEUP 0x10
+#define UART_OMAP_SSR_WAKEUP 0x02
+#define UART_OMAP_SSR_TXFULL 0x01
+
+#define UART_OMAP_SYSC_IDLEMODE 0x03
+#define UART_OMAP_SYSC_IDLEMASK (3 << UART_OMAP_SYSC_IDLEMODE)
+
+#define UART_OMAP_SYSC_FORCE_IDLE (0 << UART_OMAP_SYSC_IDLEMODE)
+#define UART_OMAP_SYSC_NO_IDLE (1 << UART_OMAP_SYSC_IDLEMODE)
+#define UART_OMAP_SYSC_SMART_IDLE (2 << UART_OMAP_SYSC_IDLEMODE)
+
+#define H4P_TRANSFER_MODE 1
+#define H4P_SCHED_TRANSFER_MODE 2
+#define H4P_ACTIVE_MODE 3
+
+struct hci_h4p_info {
+ struct timer_list lazy_release;
+ struct hci_dev *hdev;
+ spinlock_t lock;
+
+ void __iomem *uart_base;
+ unsigned long uart_phys_base;
+ int irq;
+ struct device *dev;
+ u8 chip_type;
+ u8 bt_wakeup_gpio;
+ u8 host_wakeup_gpio;
+ u8 reset_gpio;
+ u8 reset_gpio_shared;
+ u8 bt_sysclk;
+ u8 man_id;
+ u8 ver_id;
+
+ struct sk_buff_head fw_queue;
+ struct sk_buff *alive_cmd_skb;
+ struct completion init_completion;
+ struct completion fw_completion;
+ struct completion test_completion;
+ int fw_error;
+ int init_error;
+
+ struct sk_buff_head txq;
+
+ struct sk_buff *rx_skb;
+ long rx_count;
+ unsigned long rx_state;
+ unsigned long garbage_bytes;
+
+ u8 bd_addr[6];
+ struct sk_buff_head *fw_q;
+
+ int pm_enabled;
+ int tx_enabled;
+ int autorts;
+ int rx_enabled;
+ unsigned long pm_flags;
+
+ int tx_clocks_en;
+ int rx_clocks_en;
+ spinlock_t clocks_lock;
+ struct clk *uart_iclk;
+ struct clk *uart_fclk;
+ atomic_t clk_users;
+ u16 dll;
+ u16 dlh;
+ u16 ier;
+ u16 mdr1;
+ u16 efr;
+};
+
+struct hci_h4p_radio_hdr {
+ __u8 evt;
+ __u8 dlen;
+} __attribute__ ((packed));
+
+struct hci_h4p_neg_hdr {
+ __u8 dlen;
+} __attribute__ ((packed));
+#define H4P_NEG_HDR_SIZE 1
+
+#define H4P_NEG_REQ 0x00
+#define H4P_NEG_ACK 0x20
+#define H4P_NEG_NAK 0x40
+
+#define H4P_PROTO_PKT 0x44
+#define H4P_PROTO_BYTE 0x4c
+
+#define H4P_ID_CSR 0x02
+#define H4P_ID_BCM2048 0x04
+#define H4P_ID_TI1271 0x31
+
+struct hci_h4p_neg_cmd {
+ __u8 ack;
+ __u16 baud;
+ __u16 unused1;
+ __u8 proto;
+ __u16 sys_clk;
+ __u16 unused2;
+} __attribute__ ((packed));
+
+struct hci_h4p_neg_evt {
+ __u8 ack;
+ __u16 baud;
+ __u16 unused1;
+ __u8 proto;
+ __u16 sys_clk;
+ __u16 unused2;
+ __u8 man_id;
+ __u8 ver_id;
+} __attribute__ ((packed));
+
+#define H4P_ALIVE_REQ 0x55
+#define H4P_ALIVE_RESP 0xcc
+
+struct hci_h4p_alive_hdr {
+ __u8 dlen;
+} __attribute__ ((packed));
+#define H4P_ALIVE_HDR_SIZE 1
+
+struct hci_h4p_alive_pkt {
+ __u8 mid;
+ __u8 unused;
+} __attribute__ ((packed));
+
+#define MAX_BAUD_RATE 921600
+#define BC4_MAX_BAUD_RATE 3692300
+#define UART_CLOCK 48000000
+#define BT_INIT_DIVIDER 320
+#define BT_BAUDRATE_DIVIDER 384000000
+#define BT_SYSCLK_DIV 1000
+#define INIT_SPEED 120000
+
+#define H4_TYPE_SIZE 1
+#define H4_RADIO_HDR_SIZE 2
+
+/* H4+ packet types */
+#define H4_CMD_PKT 0x01
+#define H4_ACL_PKT 0x02
+#define H4_SCO_PKT 0x03
+#define H4_EVT_PKT 0x04
+#define H4_NEG_PKT 0x06
+#define H4_ALIVE_PKT 0x07
+#define H4_RADIO_PKT 0x08
+
+/* TX states */
+#define WAIT_FOR_PKT_TYPE 1
+#define WAIT_FOR_HEADER 2
+#define WAIT_FOR_DATA 3
+
+struct hci_fw_event {
+ struct hci_event_hdr hev;
+ struct hci_ev_cmd_complete cmd;
+ u8 status;
+} __attribute__ ((packed));
+
+int hci_h4p_send_alive_packet(struct hci_h4p_info *info);
+
+void hci_h4p_bcm_parse_fw_event(struct hci_h4p_info *info,
+ struct sk_buff *skb);
+int hci_h4p_bcm_send_fw(struct hci_h4p_info *info,
+ struct sk_buff_head *fw_queue);
+
+void hci_h4p_bc4_parse_fw_event(struct hci_h4p_info *info,
+ struct sk_buff *skb);
+int hci_h4p_bc4_send_fw(struct hci_h4p_info *info,
+ struct sk_buff_head *fw_queue);
+
+void hci_h4p_ti1273_parse_fw_event(struct hci_h4p_info *info,
+ struct sk_buff *skb);
+int hci_h4p_ti1273_send_fw(struct hci_h4p_info *info,
+ struct sk_buff_head *fw_queue);
+
+int hci_h4p_read_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue);
+int hci_h4p_send_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue);
+void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb);
+
+void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val);
+u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset);
+void hci_h4p_set_rts(struct hci_h4p_info *info, int active);
+int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active, int timeout_ms);
+void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
+void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
+void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed);
+int hci_h4p_reset_uart(struct hci_h4p_info *info);
+void hci_h4p_init_uart(struct hci_h4p_info *info);
+void hci_h4p_enable_tx(struct hci_h4p_info *info);
+void hci_h4p_store_regs(struct hci_h4p_info *info);
+void hci_h4p_restore_regs(struct hci_h4p_info *info);
+void hci_h4p_smart_idle(struct hci_h4p_info *info, bool enable);
+
+#endif /* __DRIVERS_BLUETOOTH_HCI_H4P_H */
diff --git a/drivers/staging/nokia_h4p/nokia_core.c b/drivers/staging/nokia_h4p/nokia_core.c
new file mode 100644
index 0000000..5da84b0
--- /dev/null
+++ b/drivers/staging/nokia_h4p/nokia_core.c
@@ -0,0 +1,1205 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * Copyright (C) 2005-2008 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * Thanks to all the Nokia people that helped with this driver,
+ * including Ville Tervo and Roger Quadros.
+ *
+ * Power saving functionality was removed from this driver to make
+ * merging easier.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/serial_reg.h>
+#include <linux/skbuff.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/timer.h>
+#include <linux/kthread.h>
+#include <linux/io.h>
+#include <linux/completion.h>
+#include <linux/sizes.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#include <linux/platform_data/bt-nokia-h4p.h>
+
+#include "hci_h4p.h"
+
+/* This should be used in function that cannot release clocks */
+static void hci_h4p_set_clk(struct hci_h4p_info *info, int *clock, int enable)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->clocks_lock, flags);
+ if (enable && !*clock) {
+ BT_DBG("Enabling %p", clock);
+ clk_prepare_enable(info->uart_fclk);
+ clk_prepare_enable(info->uart_iclk);
+ if (atomic_read(&info->clk_users) == 0)
+ hci_h4p_restore_regs(info);
+ atomic_inc(&info->clk_users);
+ }
+
+ if (!enable && *clock) {
+ BT_DBG("Disabling %p", clock);
+ if (atomic_dec_and_test(&info->clk_users))
+ hci_h4p_store_regs(info);
+ clk_disable_unprepare(info->uart_fclk);
+ clk_disable_unprepare(info->uart_iclk);
+ }
+
+ *clock = enable;
+ spin_unlock_irqrestore(&info->clocks_lock, flags);
+}
+
+static void hci_h4p_lazy_clock_release(unsigned long data)
+{
+ struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->lock, flags);
+ if (!info->tx_enabled)
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+/* Power management functions */
+void hci_h4p_smart_idle(struct hci_h4p_info *info, bool enable)
+{
+ u8 v;
+
+ v = hci_h4p_inb(info, UART_OMAP_SYSC);
+ v &= ~(UART_OMAP_SYSC_IDLEMASK);
+
+ if (enable)
+ v |= UART_OMAP_SYSC_SMART_IDLE;
+ else
+ v |= UART_OMAP_SYSC_NO_IDLE;
+
+ hci_h4p_outb(info, UART_OMAP_SYSC, v);
+}
+
+static inline void h4p_schedule_pm(struct hci_h4p_info *info)
+{
+}
+
+static void hci_h4p_disable_tx(struct hci_h4p_info *info)
+{
+ if (!info->pm_enabled)
+ return;
+
+ /* Re-enable smart-idle */
+ hci_h4p_smart_idle(info, 1);
+
+ gpio_set_value(info->bt_wakeup_gpio, 0);
+ mod_timer(&info->lazy_release, jiffies + msecs_to_jiffies(100));
+ info->tx_enabled = 0;
+}
+
+void hci_h4p_enable_tx(struct hci_h4p_info *info)
+{
+ unsigned long flags;
+
+ if (!info->pm_enabled)
+ return;
+
+ h4p_schedule_pm(info);
+
+ spin_lock_irqsave(&info->lock, flags);
+ del_timer(&info->lazy_release);
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+ info->tx_enabled = 1;
+ gpio_set_value(info->bt_wakeup_gpio, 1);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ /*
+ * Disable smart-idle as UART TX interrupts
+ * are not wake-up capable
+ */
+ hci_h4p_smart_idle(info, 0);
+
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static void hci_h4p_disable_rx(struct hci_h4p_info *info)
+{
+ if (!info->pm_enabled)
+ return;
+
+ info->rx_enabled = 0;
+
+ if (hci_h4p_inb(info, UART_LSR) & UART_LSR_DR)
+ return;
+
+ if (!(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT))
+ return;
+
+ __hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
+ info->autorts = 0;
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+}
+
+static void hci_h4p_enable_rx(struct hci_h4p_info *info)
+{
+ if (!info->pm_enabled)
+ return;
+
+ h4p_schedule_pm(info);
+
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
+ info->rx_enabled = 1;
+
+ if (!(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT))
+ return;
+
+ __hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
+ info->autorts = 1;
+}
+
+/* Negotiation functions */
+int hci_h4p_send_alive_packet(struct hci_h4p_info *info)
+{
+ struct hci_h4p_alive_hdr *hdr;
+ struct hci_h4p_alive_pkt *pkt;
+ struct sk_buff *skb;
+ unsigned long flags;
+ int len;
+
+ BT_DBG("Sending alive packet");
+
+ len = H4_TYPE_SIZE + sizeof(*hdr) + sizeof(*pkt);
+ skb = bt_skb_alloc(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ memset(skb->data, 0x00, len);
+ *skb_put(skb, 1) = H4_ALIVE_PKT;
+ hdr = (struct hci_h4p_alive_hdr *)skb_put(skb, sizeof(*hdr));
+ hdr->dlen = sizeof(*pkt);
+ pkt = (struct hci_h4p_alive_pkt *)skb_put(skb, sizeof(*pkt));
+ pkt->mid = H4P_ALIVE_REQ;
+
+ skb_queue_tail(&info->txq, skb);
+ spin_lock_irqsave(&info->lock, flags);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ BT_DBG("Alive packet sent");
+
+ return 0;
+}
+
+static void hci_h4p_alive_packet(struct hci_h4p_info *info,
+ struct sk_buff *skb)
+{
+ struct hci_h4p_alive_hdr *hdr;
+ struct hci_h4p_alive_pkt *pkt;
+
+ BT_DBG("Received alive packet");
+ hdr = (struct hci_h4p_alive_hdr *)skb->data;
+ if (hdr->dlen != sizeof(*pkt)) {
+ dev_err(info->dev, "Corrupted alive message\n");
+ info->init_error = -EIO;
+ goto finish_alive;
+ }
+
+ pkt = (struct hci_h4p_alive_pkt *)skb_pull(skb, sizeof(*hdr));
+ if (pkt->mid != H4P_ALIVE_RESP) {
+ dev_err(info->dev, "Could not negotiate hci_h4p settings\n");
+ info->init_error = -EINVAL;
+ }
+
+finish_alive:
+ complete(&info->init_completion);
+ kfree_skb(skb);
+}
+
+static int hci_h4p_send_negotiation(struct hci_h4p_info *info)
+{
+ struct hci_h4p_neg_cmd *neg_cmd;
+ struct hci_h4p_neg_hdr *neg_hdr;
+ struct sk_buff *skb;
+ unsigned long flags;
+ int err, len;
+ u16 sysclk;
+
+ BT_DBG("Sending negotiation..");
+
+ switch (info->bt_sysclk) {
+ case 1:
+ sysclk = 12000;
+ break;
+ case 2:
+ sysclk = 38400;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ len = sizeof(*neg_cmd) + sizeof(*neg_hdr) + H4_TYPE_SIZE;
+ skb = bt_skb_alloc(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ memset(skb->data, 0x00, len);
+ *skb_put(skb, 1) = H4_NEG_PKT;
+ neg_hdr = (struct hci_h4p_neg_hdr *)skb_put(skb, sizeof(*neg_hdr));
+ neg_cmd = (struct hci_h4p_neg_cmd *)skb_put(skb, sizeof(*neg_cmd));
+
+ neg_hdr->dlen = sizeof(*neg_cmd);
+ neg_cmd->ack = H4P_NEG_REQ;
+ neg_cmd->baud = cpu_to_le16(BT_BAUDRATE_DIVIDER/MAX_BAUD_RATE);
+ neg_cmd->proto = H4P_PROTO_BYTE;
+ neg_cmd->sys_clk = cpu_to_le16(sysclk);
+
+ hci_h4p_change_speed(info, INIT_SPEED);
+
+ hci_h4p_set_rts(info, 1);
+ info->init_error = 0;
+ init_completion(&info->init_completion);
+ skb_queue_tail(&info->txq, skb);
+ spin_lock_irqsave(&info->lock, flags);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ if (!wait_for_completion_interruptible_timeout(&info->init_completion,
+ msecs_to_jiffies(1000)))
+ return -ETIMEDOUT;
+
+ if (info->init_error < 0)
+ return info->init_error;
+
+ /* Change to operational settings */
+ hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
+ hci_h4p_set_rts(info, 0);
+ hci_h4p_change_speed(info, MAX_BAUD_RATE);
+
+ err = hci_h4p_wait_for_cts(info, 1, 100);
+ if (err < 0)
+ return err;
+
+ hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
+ init_completion(&info->init_completion);
+ err = hci_h4p_send_alive_packet(info);
+
+ if (err < 0)
+ return err;
+
+ if (!wait_for_completion_interruptible_timeout(&info->init_completion,
+ msecs_to_jiffies(1000)))
+ return -ETIMEDOUT;
+
+ if (info->init_error < 0)
+ return info->init_error;
+
+ BT_DBG("Negotiation successful");
+ return 0;
+}
+
+static void hci_h4p_negotiation_packet(struct hci_h4p_info *info,
+ struct sk_buff *skb)
+{
+ struct hci_h4p_neg_hdr *hdr;
+ struct hci_h4p_neg_evt *evt;
+
+ hdr = (struct hci_h4p_neg_hdr *)skb->data;
+ if (hdr->dlen != sizeof(*evt)) {
+ info->init_error = -EIO;
+ goto finish_neg;
+ }
+
+ evt = (struct hci_h4p_neg_evt *)skb_pull(skb, sizeof(*hdr));
+
+ if (evt->ack != H4P_NEG_ACK) {
+ dev_err(info->dev, "Could not negotiate hci_h4p settings\n");
+ info->init_error = -EINVAL;
+ }
+
+ info->man_id = evt->man_id;
+ info->ver_id = evt->ver_id;
+
+finish_neg:
+
+ complete(&info->init_completion);
+ kfree_skb(skb);
+}
+
+/* H4 packet handling functions */
+static int hci_h4p_get_hdr_len(struct hci_h4p_info *info, u8 pkt_type)
+{
+ long retval;
+
+ switch (pkt_type) {
+ case H4_EVT_PKT:
+ retval = HCI_EVENT_HDR_SIZE;
+ break;
+ case H4_ACL_PKT:
+ retval = HCI_ACL_HDR_SIZE;
+ break;
+ case H4_SCO_PKT:
+ retval = HCI_SCO_HDR_SIZE;
+ break;
+ case H4_NEG_PKT:
+ retval = H4P_NEG_HDR_SIZE;
+ break;
+ case H4_ALIVE_PKT:
+ retval = H4P_ALIVE_HDR_SIZE;
+ break;
+ case H4_RADIO_PKT:
+ retval = H4_RADIO_HDR_SIZE;
+ break;
+ default:
+ dev_err(info->dev, "Unknown H4 packet type 0x%.2x\n", pkt_type);
+ retval = -1;
+ break;
+ }
+
+ return retval;
+}
+
+static unsigned int hci_h4p_get_data_len(struct hci_h4p_info *info,
+ struct sk_buff *skb)
+{
+ long retval = -1;
+ struct hci_acl_hdr *acl_hdr;
+ struct hci_sco_hdr *sco_hdr;
+ struct hci_event_hdr *evt_hdr;
+ struct hci_h4p_neg_hdr *neg_hdr;
+ struct hci_h4p_alive_hdr *alive_hdr;
+ struct hci_h4p_radio_hdr *radio_hdr;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case H4_EVT_PKT:
+ evt_hdr = (struct hci_event_hdr *)skb->data;
+ retval = evt_hdr->plen;
+ break;
+ case H4_ACL_PKT:
+ acl_hdr = (struct hci_acl_hdr *)skb->data;
+ retval = le16_to_cpu(acl_hdr->dlen);
+ break;
+ case H4_SCO_PKT:
+ sco_hdr = (struct hci_sco_hdr *)skb->data;
+ retval = sco_hdr->dlen;
+ break;
+ case H4_RADIO_PKT:
+ radio_hdr = (struct hci_h4p_radio_hdr *)skb->data;
+ retval = radio_hdr->dlen;
+ break;
+ case H4_NEG_PKT:
+ neg_hdr = (struct hci_h4p_neg_hdr *)skb->data;
+ retval = neg_hdr->dlen;
+ break;
+ case H4_ALIVE_PKT:
+ alive_hdr = (struct hci_h4p_alive_hdr *)skb->data;
+ retval = alive_hdr->dlen;
+ break;
+ }
+
+ return retval;
+}
+
+static inline void hci_h4p_recv_frame(struct hci_h4p_info *info,
+ struct sk_buff *skb)
+{
+ if (unlikely(!test_bit(HCI_RUNNING, &info->hdev->flags))) {
+ switch (bt_cb(skb)->pkt_type) {
+ case H4_NEG_PKT:
+ hci_h4p_negotiation_packet(info, skb);
+ info->rx_state = WAIT_FOR_PKT_TYPE;
+ return;
+ case H4_ALIVE_PKT:
+ hci_h4p_alive_packet(info, skb);
+ info->rx_state = WAIT_FOR_PKT_TYPE;
+ return;
+ }
+
+ if (!test_bit(HCI_UP, &info->hdev->flags)) {
+ BT_DBG("fw_event");
+ hci_h4p_parse_fw_event(info, skb);
+ return;
+ }
+ }
+
+ hci_recv_frame(info->hdev, skb);
+ BT_DBG("Frame sent to upper layer");
+}
+
+static inline void hci_h4p_handle_byte(struct hci_h4p_info *info, u8 byte)
+{
+ switch (info->rx_state) {
+ case WAIT_FOR_PKT_TYPE:
+ bt_cb(info->rx_skb)->pkt_type = byte;
+ info->rx_count = hci_h4p_get_hdr_len(info, byte);
+ if (info->rx_count < 0) {
+ info->hdev->stat.err_rx++;
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ } else {
+ info->rx_state = WAIT_FOR_HEADER;
+ }
+ break;
+ case WAIT_FOR_HEADER:
+ info->rx_count--;
+ *skb_put(info->rx_skb, 1) = byte;
+ if (info->rx_count != 0)
+ break;
+ info->rx_count = hci_h4p_get_data_len(info, info->rx_skb);
+ if (info->rx_count > skb_tailroom(info->rx_skb)) {
+ dev_err(info->dev, "frame too long\n");
+ info->garbage_bytes = info->rx_count
+ - skb_tailroom(info->rx_skb);
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+ }
+ info->rx_state = WAIT_FOR_DATA;
+ break;
+ case WAIT_FOR_DATA:
+ info->rx_count--;
+ *skb_put(info->rx_skb, 1) = byte;
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ if (info->rx_count == 0) {
+ /* H4+ devices should always send word aligned packets */
+ if (!(info->rx_skb->len % 2))
+ info->garbage_bytes++;
+ hci_h4p_recv_frame(info, info->rx_skb);
+ info->rx_skb = NULL;
+ }
+}
+
+static void hci_h4p_rx_tasklet(unsigned long data)
+{
+ u8 byte;
+ struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+
+ BT_DBG("tasklet woke up");
+ BT_DBG("rx_tasklet woke up");
+
+ while (hci_h4p_inb(info, UART_LSR) & UART_LSR_DR) {
+ byte = hci_h4p_inb(info, UART_RX);
+ if (info->garbage_bytes) {
+ info->garbage_bytes--;
+ continue;
+ }
+ if (info->rx_skb == NULL) {
+ info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE,
+ GFP_ATOMIC | GFP_DMA);
+ if (!info->rx_skb) {
+ dev_err(info->dev,
+ "No memory for new packet\n");
+ goto finish_rx;
+ }
+ info->rx_state = WAIT_FOR_PKT_TYPE;
+ info->rx_skb->dev = (void *)info->hdev;
+ }
+ info->hdev->stat.byte_rx++;
+ hci_h4p_handle_byte(info, byte);
+ }
+
+ if (!info->rx_enabled) {
+ if (hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT &&
+ info->autorts) {
+ __hci_h4p_set_auto_ctsrts(info, 0 , UART_EFR_RTS);
+ info->autorts = 0;
+ }
+ /* Flush posted write to avoid spurious interrupts */
+ hci_h4p_inb(info, UART_OMAP_SCR);
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+ }
+
+finish_rx:
+ BT_DBG("rx_ended");
+}
+
+static void hci_h4p_tx_tasklet(unsigned long data)
+{
+ unsigned int sent = 0;
+ struct sk_buff *skb;
+ struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+
+ BT_DBG("tasklet woke up");
+ BT_DBG("tx_tasklet woke up");
+
+ if (info->autorts != info->rx_enabled) {
+ if (hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT) {
+ if (info->autorts && !info->rx_enabled) {
+ __hci_h4p_set_auto_ctsrts(info, 0,
+ UART_EFR_RTS);
+ info->autorts = 0;
+ }
+ if (!info->autorts && info->rx_enabled) {
+ __hci_h4p_set_auto_ctsrts(info, 1,
+ UART_EFR_RTS);
+ info->autorts = 1;
+ }
+ } else {
+ hci_h4p_outb(info, UART_OMAP_SCR,
+ hci_h4p_inb(info, UART_OMAP_SCR) |
+ UART_OMAP_SCR_EMPTY_THR);
+ goto finish_tx;
+ }
+ }
+
+ skb = skb_dequeue(&info->txq);
+ if (!skb) {
+ /* No data in buffer */
+ BT_DBG("skb ready");
+ if (hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT) {
+ hci_h4p_outb(info, UART_IER,
+ hci_h4p_inb(info, UART_IER) &
+ ~UART_IER_THRI);
+ hci_h4p_inb(info, UART_OMAP_SCR);
+ hci_h4p_disable_tx(info);
+ return;
+ }
+ hci_h4p_outb(info, UART_OMAP_SCR,
+ hci_h4p_inb(info, UART_OMAP_SCR) |
+ UART_OMAP_SCR_EMPTY_THR);
+ goto finish_tx;
+ }
+
+ /* Copy data to tx fifo */
+ while (!(hci_h4p_inb(info, UART_OMAP_SSR) & UART_OMAP_SSR_TXFULL) &&
+ (sent < skb->len)) {
+ hci_h4p_outb(info, UART_TX, skb->data[sent]);
+ sent++;
+ }
+
+ info->hdev->stat.byte_tx += sent;
+ if (skb->len == sent) {
+ kfree_skb(skb);
+ } else {
+ skb_pull(skb, sent);
+ skb_queue_head(&info->txq, skb);
+ }
+
+ hci_h4p_outb(info, UART_OMAP_SCR, hci_h4p_inb(info, UART_OMAP_SCR) &
+ ~UART_OMAP_SCR_EMPTY_THR);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+
+finish_tx:
+ /* Flush posted write to avoid spurious interrupts */
+ hci_h4p_inb(info, UART_OMAP_SCR);
+
+}
+
+static irqreturn_t hci_h4p_interrupt(int irq, void *data)
+{
+ struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+ u8 iir, msr;
+ int ret;
+
+ ret = IRQ_NONE;
+
+ iir = hci_h4p_inb(info, UART_IIR);
+ if (iir & UART_IIR_NO_INT)
+ return IRQ_HANDLED;
+
+ BT_DBG("In interrupt handler iir 0x%.2x", iir);
+
+ iir &= UART_IIR_ID;
+
+ if (iir == UART_IIR_MSI) {
+ msr = hci_h4p_inb(info, UART_MSR);
+ ret = IRQ_HANDLED;
+ }
+ if (iir == UART_IIR_RLSI) {
+ hci_h4p_inb(info, UART_RX);
+ hci_h4p_inb(info, UART_LSR);
+ ret = IRQ_HANDLED;
+ }
+
+ if (iir == UART_IIR_RDI) {
+ hci_h4p_rx_tasklet((unsigned long)data);
+ ret = IRQ_HANDLED;
+ }
+
+ if (iir == UART_IIR_THRI) {
+ hci_h4p_tx_tasklet((unsigned long)data);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static irqreturn_t hci_h4p_wakeup_interrupt(int irq, void *dev_inst)
+{
+ struct hci_h4p_info *info = dev_inst;
+ int should_wakeup;
+ struct hci_dev *hdev;
+
+ if (!info->hdev)
+ return IRQ_HANDLED;
+
+ should_wakeup = gpio_get_value(info->host_wakeup_gpio);
+ hdev = info->hdev;
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+ if (should_wakeup == 1)
+ complete_all(&info->test_completion);
+
+ return IRQ_HANDLED;
+ }
+
+ BT_DBG("gpio interrupt %d", should_wakeup);
+
+ /* Check if wee have missed some interrupts */
+ if (info->rx_enabled == should_wakeup)
+ return IRQ_HANDLED;
+
+ if (should_wakeup)
+ hci_h4p_enable_rx(info);
+ else
+ hci_h4p_disable_rx(info);
+
+ return IRQ_HANDLED;
+}
+
+static inline void hci_h4p_set_pm_limits(struct hci_h4p_info *info, bool set)
+{
+ struct hci_h4p_platform_data *bt_plat_data = info->dev->platform_data;
+ const char *sset = set ? "set" : "clear";
+
+ if (unlikely(!bt_plat_data || !bt_plat_data->set_pm_limits))
+ return;
+
+ if (set != !!test_bit(H4P_ACTIVE_MODE, &info->pm_flags)) {
+ bt_plat_data->set_pm_limits(info->dev, set);
+ if (set)
+ set_bit(H4P_ACTIVE_MODE, &info->pm_flags);
+ else
+ clear_bit(H4P_ACTIVE_MODE, &info->pm_flags);
+ BT_DBG("Change pm constraints to: %s", sset);
+ return;
+ }
+
+ BT_DBG("pm constraints remains: %s", sset);
+}
+
+static int hci_h4p_reset(struct hci_h4p_info *info)
+{
+ int err;
+
+ err = hci_h4p_reset_uart(info);
+ if (err < 0) {
+ dev_err(info->dev, "Uart reset failed\n");
+ return err;
+ }
+ hci_h4p_init_uart(info);
+ hci_h4p_set_rts(info, 0);
+
+ gpio_set_value(info->reset_gpio, 0);
+ gpio_set_value(info->bt_wakeup_gpio, 1);
+ msleep(10);
+
+ if (gpio_get_value(info->host_wakeup_gpio) == 1) {
+ dev_err(info->dev, "host_wakeup_gpio not low\n");
+ return -EPROTO;
+ }
+
+ init_completion(&info->test_completion);
+ gpio_set_value(info->reset_gpio, 1);
+
+ if (!wait_for_completion_interruptible_timeout(&info->test_completion,
+ msecs_to_jiffies(100))) {
+ dev_err(info->dev, "wakeup test timed out\n");
+ complete_all(&info->test_completion);
+ return -EPROTO;
+ }
+
+ err = hci_h4p_wait_for_cts(info, 1, 100);
+ if (err < 0) {
+ dev_err(info->dev, "No cts from bt chip\n");
+ return err;
+ }
+
+ hci_h4p_set_rts(info, 1);
+
+ return 0;
+}
+
+/* hci callback functions */
+static int hci_h4p_hci_flush(struct hci_dev *hdev)
+{
+ struct hci_h4p_info *info = hci_get_drvdata(hdev);
+ skb_queue_purge(&info->txq);
+
+ return 0;
+}
+
+static int hci_h4p_bt_wakeup_test(struct hci_h4p_info *info)
+{
+ /*
+ * Test Sequence:
+ * Host de-asserts the BT_WAKE_UP line.
+ * Host polls the UART_CTS line, waiting for it to be de-asserted.
+ * Host asserts the BT_WAKE_UP line.
+ * Host polls the UART_CTS line, waiting for it to be asserted.
+ * Host de-asserts the BT_WAKE_UP line (allow the Bluetooth device to
+ * sleep).
+ * Host polls the UART_CTS line, waiting for it to be de-asserted.
+ */
+ int err;
+ int ret = -ECOMM;
+
+ if (!info)
+ return -EINVAL;
+
+ /* Disable wakeup interrupts */
+ disable_irq(gpio_to_irq(info->host_wakeup_gpio));
+
+ gpio_set_value(info->bt_wakeup_gpio, 0);
+ err = hci_h4p_wait_for_cts(info, 0, 100);
+ if (err) {
+ dev_warn(info->dev, "bt_wakeup_test: fail: "
+ "CTS low timed out: %d\n", err);
+ goto out;
+ }
+
+ gpio_set_value(info->bt_wakeup_gpio, 1);
+ err = hci_h4p_wait_for_cts(info, 1, 100);
+ if (err) {
+ dev_warn(info->dev, "bt_wakeup_test: fail: "
+ "CTS high timed out: %d\n", err);
+ goto out;
+ }
+
+ gpio_set_value(info->bt_wakeup_gpio, 0);
+ err = hci_h4p_wait_for_cts(info, 0, 100);
+ if (err) {
+ dev_warn(info->dev, "bt_wakeup_test: fail: "
+ "CTS re-low timed out: %d\n", err);
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+
+ /* Re-enable wakeup interrupts */
+ enable_irq(gpio_to_irq(info->host_wakeup_gpio));
+
+ return ret;
+}
+
+static int hci_h4p_hci_open(struct hci_dev *hdev)
+{
+ struct hci_h4p_info *info;
+ int err, retries = 0;
+ struct sk_buff_head fw_queue;
+ unsigned long flags;
+
+ info = hci_get_drvdata(hdev);
+
+ if (test_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ /* TI1271 has HW bug and boot up might fail. Retry up to three times */
+again:
+
+ info->rx_enabled = 1;
+ info->rx_state = WAIT_FOR_PKT_TYPE;
+ info->rx_count = 0;
+ info->garbage_bytes = 0;
+ info->rx_skb = NULL;
+ info->pm_enabled = 0;
+ init_completion(&info->fw_completion);
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
+ skb_queue_head_init(&fw_queue);
+
+ err = hci_h4p_reset(info);
+ if (err < 0)
+ goto err_clean;
+
+ hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_CTS | UART_EFR_RTS);
+ info->autorts = 1;
+
+ err = hci_h4p_send_negotiation(info);
+
+ err = hci_h4p_read_fw(info, &fw_queue);
+ if (err < 0) {
+ dev_err(info->dev, "Cannot read firmware\n");
+ goto err_clean;
+ }
+
+ err = hci_h4p_send_fw(info, &fw_queue);
+ if (err < 0) {
+ dev_err(info->dev, "Sending firmware failed.\n");
+ goto err_clean;
+ }
+
+ info->pm_enabled = 1;
+
+ err = hci_h4p_bt_wakeup_test(info);
+ if (err < 0) {
+ dev_err(info->dev, "BT wakeup test failed.\n");
+ goto err_clean;
+ }
+
+ spin_lock_irqsave(&info->lock, flags);
+ info->rx_enabled = gpio_get_value(info->host_wakeup_gpio);
+ hci_h4p_set_clk(info, &info->rx_clocks_en, info->rx_enabled);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+
+ kfree_skb(info->alive_cmd_skb);
+ info->alive_cmd_skb = NULL;
+ set_bit(HCI_RUNNING, &hdev->flags);
+
+ BT_DBG("hci up and running");
+ return 0;
+
+err_clean:
+ hci_h4p_hci_flush(hdev);
+ hci_h4p_reset_uart(info);
+ del_timer_sync(&info->lazy_release);
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+ gpio_set_value(info->reset_gpio, 0);
+ gpio_set_value(info->bt_wakeup_gpio, 0);
+ skb_queue_purge(&fw_queue);
+ kfree_skb(info->alive_cmd_skb);
+ info->alive_cmd_skb = NULL;
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+
+ if (retries++ < 3) {
+ dev_err(info->dev, "FW loading try %d fail. Retry.\n", retries);
+ goto again;
+ }
+
+ return err;
+}
+
+static int hci_h4p_hci_close(struct hci_dev *hdev)
+{
+ struct hci_h4p_info *info = hci_get_drvdata(hdev);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ hci_h4p_hci_flush(hdev);
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
+ hci_h4p_reset_uart(info);
+ del_timer_sync(&info->lazy_release);
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+ gpio_set_value(info->reset_gpio, 0);
+ gpio_set_value(info->bt_wakeup_gpio, 0);
+ kfree_skb(info->rx_skb);
+
+ return 0;
+}
+
+static int hci_h4p_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_h4p_info *info;
+ int err = 0;
+
+ BT_DBG("dev %p, skb %p", hdev, skb);
+
+ info = hci_get_drvdata(hdev);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+ dev_warn(info->dev, "Frame for non-running device\n");
+ return -EIO;
+ }
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ }
+
+ /* Push frame type to skb */
+ *skb_push(skb, 1) = (bt_cb(skb)->pkt_type);
+ /* We should allways send word aligned data to h4+ devices */
+ if (skb->len % 2) {
+ err = skb_pad(skb, 1);
+ if (!err)
+ *skb_put(skb, 1) = 0x00;
+ }
+ if (err)
+ return err;
+
+ skb_queue_tail(&info->txq, skb);
+ hci_h4p_enable_tx(info);
+
+ return 0;
+}
+
+static ssize_t hci_h4p_store_bdaddr(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hci_h4p_info *info = dev_get_drvdata(dev);
+ unsigned int bdaddr[6];
+ int ret, i;
+
+ ret = sscanf(buf, "%2x:%2x:%2x:%2x:%2x:%2x\n",
+ &bdaddr[0], &bdaddr[1], &bdaddr[2],
+ &bdaddr[3], &bdaddr[4], &bdaddr[5]);
+
+ if (ret != 6)
+ return -EINVAL;
+
+ for (i = 0; i < 6; i++) {
+ if (bdaddr[i] > 0xff)
+ return -EINVAL;
+ info->bd_addr[i] = bdaddr[i] & 0xff;
+ }
+
+ return count;
+}
+
+static ssize_t hci_h4p_show_bdaddr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hci_h4p_info *info = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%pMR\n", info->bd_addr);
+}
+
+static DEVICE_ATTR(bdaddr, S_IRUGO | S_IWUSR, hci_h4p_show_bdaddr,
+ hci_h4p_store_bdaddr);
+
+static int hci_h4p_sysfs_create_files(struct device *dev)
+{
+ return device_create_file(dev, &dev_attr_bdaddr);
+}
+
+static void hci_h4p_sysfs_remove_files(struct device *dev)
+{
+ device_remove_file(dev, &dev_attr_bdaddr);
+}
+
+static int hci_h4p_register_hdev(struct hci_h4p_info *info)
+{
+ struct hci_dev *hdev;
+
+ /* Initialize and register HCI device */
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ dev_err(info->dev, "Can't allocate memory for device\n");
+ return -ENOMEM;
+ }
+ info->hdev = hdev;
+
+ hdev->bus = HCI_UART;
+ hci_set_drvdata(hdev, info);
+
+ hdev->open = hci_h4p_hci_open;
+ hdev->close = hci_h4p_hci_close;
+ hdev->flush = hci_h4p_hci_flush;
+ hdev->send = hci_h4p_hci_send_frame;
+ set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+
+ SET_HCIDEV_DEV(hdev, info->dev);
+
+ if (hci_h4p_sysfs_create_files(info->dev) < 0) {
+ dev_err(info->dev, "failed to create sysfs files\n");
+ goto free;
+ }
+
+ if (hci_register_dev(hdev) >= 0)
+ return 0;
+
+ dev_err(info->dev, "hci_register failed %s.\n", hdev->name);
+ hci_h4p_sysfs_remove_files(info->dev);
+free:
+ hci_free_dev(info->hdev);
+ return -ENODEV;
+}
+
+static int hci_h4p_probe(struct platform_device *pdev)
+{
+ struct hci_h4p_platform_data *bt_plat_data;
+ struct hci_h4p_info *info;
+ int err;
+
+ dev_info(&pdev->dev, "Registering HCI H4P device\n");
+ info = devm_kzalloc(&pdev->dev, sizeof(struct hci_h4p_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = &pdev->dev;
+ info->tx_enabled = 1;
+ info->rx_enabled = 1;
+ spin_lock_init(&info->lock);
+ spin_lock_init(&info->clocks_lock);
+ skb_queue_head_init(&info->txq);
+
+ if (pdev->dev.platform_data == NULL) {
+ dev_err(&pdev->dev, "Could not get Bluetooth config data\n");
+ return -ENODATA;
+ }
+
+ bt_plat_data = pdev->dev.platform_data;
+ info->chip_type = bt_plat_data->chip_type;
+ info->bt_wakeup_gpio = bt_plat_data->bt_wakeup_gpio;
+ info->host_wakeup_gpio = bt_plat_data->host_wakeup_gpio;
+ info->reset_gpio = bt_plat_data->reset_gpio;
+ info->reset_gpio_shared = bt_plat_data->reset_gpio_shared;
+ info->bt_sysclk = bt_plat_data->bt_sysclk;
+
+ BT_DBG("RESET gpio: %d", info->reset_gpio);
+ BT_DBG("BTWU gpio: %d", info->bt_wakeup_gpio);
+ BT_DBG("HOSTWU gpio: %d", info->host_wakeup_gpio);
+ BT_DBG("sysclk: %d", info->bt_sysclk);
+
+ init_completion(&info->test_completion);
+ complete_all(&info->test_completion);
+
+ if (!info->reset_gpio_shared) {
+ err = devm_gpio_request_one(&pdev->dev, info->reset_gpio,
+ GPIOF_OUT_INIT_LOW, "bt_reset");
+ if (err < 0) {
+ dev_err(&pdev->dev, "Cannot get GPIO line %d\n",
+ info->reset_gpio);
+ return err;
+ }
+ }
+
+ err = devm_gpio_request_one(&pdev->dev, info->bt_wakeup_gpio,
+ GPIOF_OUT_INIT_LOW, "bt_wakeup");
+
+ if (err < 0) {
+ dev_err(info->dev, "Cannot get GPIO line 0x%d",
+ info->bt_wakeup_gpio);
+ return err;
+ }
+
+ err = devm_gpio_request_one(&pdev->dev, info->host_wakeup_gpio,
+ GPIOF_DIR_IN, "host_wakeup");
+ if (err < 0) {
+ dev_err(info->dev, "Cannot get GPIO line %d",
+ info->host_wakeup_gpio);
+ return err;
+ }
+
+ info->irq = bt_plat_data->uart_irq;
+ info->uart_base = devm_ioremap(&pdev->dev, bt_plat_data->uart_base, SZ_2K);
+ info->uart_iclk = devm_clk_get(&pdev->dev, bt_plat_data->uart_iclk);
+ info->uart_fclk = devm_clk_get(&pdev->dev, bt_plat_data->uart_fclk);
+
+ err = devm_request_irq(&pdev->dev, info->irq, hci_h4p_interrupt, IRQF_DISABLED,
+ "hci_h4p", info);
+ if (err < 0) {
+ dev_err(info->dev, "hci_h4p: unable to get IRQ %d\n", info->irq);
+ return err;
+ }
+
+ err = devm_request_irq(&pdev->dev, gpio_to_irq(info->host_wakeup_gpio),
+ hci_h4p_wakeup_interrupt, IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING | IRQF_DISABLED,
+ "hci_h4p_wkup", info);
+ if (err < 0) {
+ dev_err(info->dev, "hci_h4p: unable to get wakeup IRQ %d\n",
+ gpio_to_irq(info->host_wakeup_gpio));
+ return err;
+ }
+
+ err = irq_set_irq_wake(gpio_to_irq(info->host_wakeup_gpio), 1);
+ if (err < 0) {
+ dev_err(info->dev, "hci_h4p: unable to set wakeup for IRQ %d\n",
+ gpio_to_irq(info->host_wakeup_gpio));
+ return err;
+ }
+
+ init_timer_deferrable(&info->lazy_release);
+ info->lazy_release.function = hci_h4p_lazy_clock_release;
+ info->lazy_release.data = (unsigned long)info;
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+ err = hci_h4p_reset_uart(info);
+ if (err < 0)
+ return err;
+ gpio_set_value(info->reset_gpio, 0);
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+
+ platform_set_drvdata(pdev, info);
+
+ if (hci_h4p_register_hdev(info) < 0) {
+ dev_err(info->dev, "failed to register hci_h4p hci device\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hci_h4p_remove(struct platform_device *pdev)
+{
+ struct hci_h4p_info *info;
+
+ info = platform_get_drvdata(pdev);
+
+ hci_h4p_sysfs_remove_files(info->dev);
+ hci_h4p_hci_close(info->hdev);
+ hci_unregister_dev(info->hdev);
+ hci_free_dev(info->hdev);
+
+ return 0;
+}
+
+static struct platform_driver hci_h4p_driver = {
+ .probe = hci_h4p_probe,
+ .remove = hci_h4p_remove,
+ .driver = {
+ .name = "hci_h4p",
+ },
+};
+
+module_platform_driver(hci_h4p_driver);
+
+MODULE_ALIAS("platform:hci_h4p");
+MODULE_DESCRIPTION("Bluetooth h4 driver with nokia extensions");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ville Tervo");
+MODULE_FIRMWARE(FW_NAME_TI1271_PRELE);
+MODULE_FIRMWARE(FW_NAME_TI1271_LE);
+MODULE_FIRMWARE(FW_NAME_TI1271);
+MODULE_FIRMWARE(FW_NAME_BCM2048);
+MODULE_FIRMWARE(FW_NAME_CSR);
diff --git a/drivers/staging/nokia_h4p/nokia_fw-bcm.c b/drivers/staging/nokia_h4p/nokia_fw-bcm.c
new file mode 100644
index 0000000..e8912bf
--- /dev/null
+++ b/drivers/staging/nokia_h4p/nokia_fw-bcm.c
@@ -0,0 +1,147 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * Copyright (C) 2005-2008 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/serial_reg.h>
+
+#include "hci_h4p.h"
+
+static int hci_h4p_bcm_set_bdaddr(struct hci_h4p_info *info, struct sk_buff *skb)
+{
+ int i;
+ static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
+ int not_valid;
+
+ not_valid = 1;
+ for (i = 0; i < 6; i++) {
+ if (info->bd_addr[i] != 0x00) {
+ not_valid = 0;
+ break;
+ }
+ }
+
+ if (not_valid) {
+ dev_info(info->dev, "Valid bluetooth address not found, setting some random\n");
+ /* When address is not valid, use some random but Nokia MAC */
+ memcpy(info->bd_addr, nokia_oui, 3);
+ get_random_bytes(info->bd_addr + 3, 3);
+ }
+
+ for (i = 0; i < 6; i++)
+ skb->data[9 - i] = info->bd_addr[i];
+
+ return 0;
+}
+
+void hci_h4p_bcm_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
+{
+ struct sk_buff *fw_skb;
+ int err;
+ unsigned long flags;
+
+ if (skb->data[5] != 0x00) {
+ dev_err(info->dev, "Firmware sending command failed 0x%.2x\n",
+ skb->data[5]);
+ info->fw_error = -EPROTO;
+ }
+
+ kfree_skb(skb);
+
+ fw_skb = skb_dequeue(info->fw_q);
+ if (fw_skb == NULL || info->fw_error) {
+ complete(&info->fw_completion);
+ return;
+ }
+
+ if (fw_skb->data[1] == 0x01 && fw_skb->data[2] == 0xfc && fw_skb->len >= 10) {
+ BT_DBG("Setting bluetooth address");
+ err = hci_h4p_bcm_set_bdaddr(info, fw_skb);
+ if (err < 0) {
+ kfree_skb(fw_skb);
+ info->fw_error = err;
+ complete(&info->fw_completion);
+ return;
+ }
+ }
+
+ skb_queue_tail(&info->txq, fw_skb);
+ spin_lock_irqsave(&info->lock, flags);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+
+int hci_h4p_bcm_send_fw(struct hci_h4p_info *info,
+ struct sk_buff_head *fw_queue)
+{
+ struct sk_buff *skb;
+ unsigned long flags, time;
+
+ info->fw_error = 0;
+
+ BT_DBG("Sending firmware");
+
+ time = jiffies;
+
+ info->fw_q = fw_queue;
+ skb = skb_dequeue(fw_queue);
+ if (!skb)
+ return -ENODATA;
+
+ BT_DBG("Sending commands");
+
+ /*
+ * Disable smart-idle as UART TX interrupts
+ * are not wake-up capable
+ */
+ hci_h4p_smart_idle(info, 0);
+
+ /* Check if this is bd_address packet */
+ init_completion(&info->fw_completion);
+ skb_queue_tail(&info->txq, skb);
+ spin_lock_irqsave(&info->lock, flags);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ if (!wait_for_completion_timeout(&info->fw_completion,
+ msecs_to_jiffies(2000))) {
+ dev_err(info->dev, "No reply to fw command\n");
+ return -ETIMEDOUT;
+ }
+
+ if (info->fw_error) {
+ dev_err(info->dev, "FW error\n");
+ return -EPROTO;
+ }
+
+ BT_DBG("Firmware sent in %d msecs",
+ jiffies_to_msecs(jiffies-time));
+
+ hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
+ hci_h4p_set_rts(info, 0);
+ hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
+ hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
+
+ return 0;
+}
diff --git a/drivers/staging/nokia_h4p/nokia_fw-csr.c b/drivers/staging/nokia_h4p/nokia_fw-csr.c
new file mode 100644
index 0000000..e39c4a3
--- /dev/null
+++ b/drivers/staging/nokia_h4p/nokia_fw-csr.c
@@ -0,0 +1,150 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * Copyright (C) 2005-2008 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/serial_reg.h>
+
+#include "hci_h4p.h"
+
+void hci_h4p_bc4_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
+{
+ /* Check if this is fw packet */
+ if (skb->data[0] != 0xff) {
+ hci_recv_frame(info->hdev, skb);
+ return;
+ }
+
+ if (skb->data[11] || skb->data[12]) {
+ dev_err(info->dev, "Firmware sending command failed\n");
+ info->fw_error = -EPROTO;
+ }
+
+ kfree_skb(skb);
+ complete(&info->fw_completion);
+}
+
+int hci_h4p_bc4_send_fw(struct hci_h4p_info *info,
+ struct sk_buff_head *fw_queue)
+{
+ static const u8 nokia_oui[3] = {0x00, 0x19, 0x4F};
+ struct sk_buff *skb;
+ unsigned int offset;
+ int retries, count, i, not_valid;
+ unsigned long flags;
+
+ info->fw_error = 0;
+
+ BT_DBG("Sending firmware");
+ skb = skb_dequeue(fw_queue);
+
+ if (!skb)
+ return -ENOMSG;
+
+ /* Check if this is bd_address packet */
+ if (skb->data[15] == 0x01 && skb->data[16] == 0x00) {
+ offset = 21;
+ skb->data[offset + 1] = 0x00;
+ skb->data[offset + 5] = 0x00;
+
+ not_valid = 1;
+ for (i = 0; i < 6; i++) {
+ if (info->bd_addr[i] != 0x00) {
+ not_valid = 0;
+ break;
+ }
+ }
+
+ if (not_valid) {
+ dev_info(info->dev, "Valid bluetooth address not found,"
+ " setting some random\n");
+ /* When address is not valid, use some random */
+ memcpy(info->bd_addr, nokia_oui, 3);
+ get_random_bytes(info->bd_addr + 3, 3);
+ }
+
+ skb->data[offset + 7] = info->bd_addr[0];
+ skb->data[offset + 6] = info->bd_addr[1];
+ skb->data[offset + 4] = info->bd_addr[2];
+ skb->data[offset + 0] = info->bd_addr[3];
+ skb->data[offset + 3] = info->bd_addr[4];
+ skb->data[offset + 2] = info->bd_addr[5];
+ }
+
+ for (count = 1; ; count++) {
+ BT_DBG("Sending firmware command %d", count);
+ init_completion(&info->fw_completion);
+ skb_queue_tail(&info->txq, skb);
+ spin_lock_irqsave(&info->lock, flags);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ skb = skb_dequeue(fw_queue);
+ if (!skb)
+ break;
+
+ if (!wait_for_completion_timeout(&info->fw_completion,
+ msecs_to_jiffies(1000))) {
+ dev_err(info->dev, "No reply to fw command\n");
+ return -ETIMEDOUT;
+ }
+
+ if (info->fw_error) {
+ dev_err(info->dev, "FW error\n");
+ return -EPROTO;
+ }
+ };
+
+ /* Wait for chip warm reset */
+ retries = 100;
+ while ((!skb_queue_empty(&info->txq) ||
+ !(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT)) &&
+ retries--) {
+ msleep(10);
+ }
+ if (!retries) {
+ dev_err(info->dev, "Transmitter not empty\n");
+ return -ETIMEDOUT;
+ }
+
+ hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
+
+ if (hci_h4p_wait_for_cts(info, 1, 100)) {
+ dev_err(info->dev, "cts didn't deassert after final speed\n");
+ return -ETIMEDOUT;
+ }
+
+ retries = 100;
+ do {
+ init_completion(&info->init_completion);
+ hci_h4p_send_alive_packet(info);
+ retries--;
+ } while (!wait_for_completion_timeout(&info->init_completion, 100) &&
+ retries > 0);
+
+ if (!retries) {
+ dev_err(info->dev, "No alive reply after speed change\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/nokia_h4p/nokia_fw-ti1273.c b/drivers/staging/nokia_h4p/nokia_fw-ti1273.c
new file mode 100644
index 0000000..f5500f7
--- /dev/null
+++ b/drivers/staging/nokia_h4p/nokia_fw-ti1273.c
@@ -0,0 +1,110 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/serial_reg.h>
+
+#include "hci_h4p.h"
+
+static struct sk_buff_head *fw_q;
+
+void hci_h4p_ti1273_parse_fw_event(struct hci_h4p_info *info,
+ struct sk_buff *skb)
+{
+ struct sk_buff *fw_skb;
+ unsigned long flags;
+
+ if (skb->data[5] != 0x00) {
+ dev_err(info->dev, "Firmware sending command failed 0x%.2x\n",
+ skb->data[5]);
+ info->fw_error = -EPROTO;
+ }
+
+ kfree_skb(skb);
+
+ fw_skb = skb_dequeue(fw_q);
+ if (fw_skb == NULL || info->fw_error) {
+ complete(&info->fw_completion);
+ return;
+ }
+
+ skb_queue_tail(&info->txq, fw_skb);
+ spin_lock_irqsave(&info->lock, flags);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+
+int hci_h4p_ti1273_send_fw(struct hci_h4p_info *info,
+ struct sk_buff_head *fw_queue)
+{
+ struct sk_buff *skb;
+ unsigned long flags, time;
+
+ info->fw_error = 0;
+
+ BT_DBG("Sending firmware");
+
+ time = jiffies;
+
+ fw_q = fw_queue;
+ skb = skb_dequeue(fw_queue);
+ if (!skb)
+ return -ENODATA;
+
+ BT_DBG("Sending commands");
+ /* Check if this is bd_address packet */
+ init_completion(&info->fw_completion);
+ hci_h4p_smart_idle(info, 0);
+ skb_queue_tail(&info->txq, skb);
+ spin_lock_irqsave(&info->lock, flags);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ if (!wait_for_completion_timeout(&info->fw_completion,
+ msecs_to_jiffies(2000))) {
+ dev_err(info->dev, "No reply to fw command\n");
+ return -ETIMEDOUT;
+ }
+
+ if (info->fw_error) {
+ dev_err(info->dev, "FW error\n");
+ return -EPROTO;
+ }
+
+ BT_DBG("Firmware sent in %d msecs",
+ jiffies_to_msecs(jiffies-time));
+
+ hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
+ hci_h4p_set_rts(info, 0);
+ hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
+ if (hci_h4p_wait_for_cts(info, 1, 100)) {
+ dev_err(info->dev,
+ "cts didn't go down after final speed change\n");
+ return -ETIMEDOUT;
+ }
+ hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
+
+ return 0;
+}
diff --git a/drivers/staging/nokia_h4p/nokia_fw.c b/drivers/staging/nokia_h4p/nokia_fw.c
new file mode 100644
index 0000000..cfea61c
--- /dev/null
+++ b/drivers/staging/nokia_h4p/nokia_fw.c
@@ -0,0 +1,195 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation.
+ *
+ * Contact: Ville Tervo <ville.tervo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/firmware.h>
+#include <linux/clk.h>
+
+#include <net/bluetooth/bluetooth.h>
+
+#include "hci_h4p.h"
+
+static int fw_pos;
+
+/* Firmware handling */
+static int hci_h4p_open_firmware(struct hci_h4p_info *info,
+ const struct firmware **fw_entry)
+{
+ int err;
+
+ fw_pos = 0;
+ BT_DBG("Opening firmware man_id 0x%.2x ver_id 0x%.2x",
+ info->man_id, info->ver_id);
+ switch (info->man_id) {
+ case H4P_ID_TI1271:
+ switch (info->ver_id) {
+ case 0xe1:
+ err = request_firmware(fw_entry, FW_NAME_TI1271_PRELE,
+ info->dev);
+ break;
+ case 0xd1:
+ case 0xf1:
+ err = request_firmware(fw_entry, FW_NAME_TI1271_LE,
+ info->dev);
+ break;
+ default:
+ err = request_firmware(fw_entry, FW_NAME_TI1271,
+ info->dev);
+ }
+ break;
+ case H4P_ID_CSR:
+ err = request_firmware(fw_entry, FW_NAME_CSR, info->dev);
+ break;
+ case H4P_ID_BCM2048:
+ err = request_firmware(fw_entry, FW_NAME_BCM2048, info->dev);
+ break;
+ default:
+ dev_err(info->dev, "Invalid chip type\n");
+ *fw_entry = NULL;
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+static void hci_h4p_close_firmware(const struct firmware *fw_entry)
+{
+ release_firmware(fw_entry);
+}
+
+/* Read fw. Return length of the command. If no more commands in
+ * fw 0 is returned. In error case return value is negative.
+ */
+static int hci_h4p_read_fw_cmd(struct hci_h4p_info *info, struct sk_buff **skb,
+ const struct firmware *fw_entry, gfp_t how)
+{
+ unsigned int cmd_len;
+
+ if (fw_pos >= fw_entry->size)
+ return 0;
+
+ if (fw_pos + 2 > fw_entry->size) {
+ dev_err(info->dev, "Corrupted firmware image 1\n");
+ return -EMSGSIZE;
+ }
+
+ cmd_len = fw_entry->data[fw_pos++];
+ cmd_len += fw_entry->data[fw_pos++] << 8;
+ if (cmd_len == 0)
+ return 0;
+
+ if (fw_pos + cmd_len > fw_entry->size) {
+ dev_err(info->dev, "Corrupted firmware image 2\n");
+ return -EMSGSIZE;
+ }
+
+ *skb = bt_skb_alloc(cmd_len, how);
+ if (!*skb) {
+ dev_err(info->dev, "Cannot reserve memory for buffer\n");
+ return -ENOMEM;
+ }
+ memcpy(skb_put(*skb, cmd_len), &fw_entry->data[fw_pos], cmd_len);
+
+ fw_pos += cmd_len;
+
+ return (*skb)->len;
+}
+
+int hci_h4p_read_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue)
+{
+ const struct firmware *fw_entry = NULL;
+ struct sk_buff *skb = NULL;
+ int err;
+
+ err = hci_h4p_open_firmware(info, &fw_entry);
+ if (err < 0 || !fw_entry)
+ goto err_clean;
+
+ while ((err = hci_h4p_read_fw_cmd(info, &skb, fw_entry, GFP_KERNEL))) {
+ if (err < 0 || !skb)
+ goto err_clean;
+
+ skb_queue_tail(fw_queue, skb);
+ }
+
+ /* Chip detection code does neg and alive stuff
+ * discard two first skbs */
+ skb = skb_dequeue(fw_queue);
+ if (!skb) {
+ err = -EMSGSIZE;
+ goto err_clean;
+ }
+ kfree_skb(skb);
+ skb = skb_dequeue(fw_queue);
+ if (!skb) {
+ err = -EMSGSIZE;
+ goto err_clean;
+ }
+ kfree_skb(skb);
+
+err_clean:
+ hci_h4p_close_firmware(fw_entry);
+ return err;
+}
+
+int hci_h4p_send_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue)
+{
+ int err;
+
+ switch (info->man_id) {
+ case H4P_ID_CSR:
+ err = hci_h4p_bc4_send_fw(info, fw_queue);
+ break;
+ case H4P_ID_TI1271:
+ err = hci_h4p_ti1273_send_fw(info, fw_queue);
+ break;
+ case H4P_ID_BCM2048:
+ err = hci_h4p_bcm_send_fw(info, fw_queue);
+ break;
+ default:
+ dev_err(info->dev, "Don't know how to send firmware\n");
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
+{
+ switch (info->man_id) {
+ case H4P_ID_CSR:
+ hci_h4p_bc4_parse_fw_event(info, skb);
+ break;
+ case H4P_ID_TI1271:
+ hci_h4p_ti1273_parse_fw_event(info, skb);
+ break;
+ case H4P_ID_BCM2048:
+ hci_h4p_bcm_parse_fw_event(info, skb);
+ break;
+ default:
+ dev_err(info->dev, "Don't know how to parse fw event\n");
+ info->fw_error = -EINVAL;
+ }
+
+ return;
+}
diff --git a/drivers/staging/nokia_h4p/nokia_uart.c b/drivers/staging/nokia_h4p/nokia_uart.c
new file mode 100644
index 0000000..0fb57de
--- /dev/null
+++ b/drivers/staging/nokia_h4p/nokia_uart.c
@@ -0,0 +1,199 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/serial_reg.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <linux/io.h>
+
+#include "hci_h4p.h"
+
+inline void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val)
+{
+ __raw_writeb(val, info->uart_base + (offset << 2));
+}
+
+inline u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset)
+{
+ return __raw_readb(info->uart_base + (offset << 2));
+}
+
+void hci_h4p_set_rts(struct hci_h4p_info *info, int active)
+{
+ u8 b;
+
+ b = hci_h4p_inb(info, UART_MCR);
+ if (active)
+ b |= UART_MCR_RTS;
+ else
+ b &= ~UART_MCR_RTS;
+ hci_h4p_outb(info, UART_MCR, b);
+}
+
+int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active,
+ int timeout_ms)
+{
+ unsigned long timeout;
+ int state;
+
+ timeout = jiffies + msecs_to_jiffies(timeout_ms);
+ for (;;) {
+ state = hci_h4p_inb(info, UART_MSR) & UART_MSR_CTS;
+ if (active) {
+ if (state)
+ return 0;
+ } else {
+ if (!state)
+ return 0;
+ }
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ msleep(1);
+ }
+}
+
+void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which)
+{
+ u8 lcr, b;
+
+ lcr = hci_h4p_inb(info, UART_LCR);
+ hci_h4p_outb(info, UART_LCR, 0xbf);
+ b = hci_h4p_inb(info, UART_EFR);
+ if (on)
+ b |= which;
+ else
+ b &= ~which;
+ hci_h4p_outb(info, UART_EFR, b);
+ hci_h4p_outb(info, UART_LCR, lcr);
+}
+
+void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->lock, flags);
+ __hci_h4p_set_auto_ctsrts(info, on, which);
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed)
+{
+ unsigned int divisor;
+ u8 lcr, mdr1;
+
+ BT_DBG("Setting speed %lu", speed);
+
+ if (speed >= 460800) {
+ divisor = UART_CLOCK / 13 / speed;
+ mdr1 = 3;
+ } else {
+ divisor = UART_CLOCK / 16 / speed;
+ mdr1 = 0;
+ }
+
+ /* Make sure UART mode is disabled */
+ hci_h4p_outb(info, UART_OMAP_MDR1, 7);
+
+ lcr = hci_h4p_inb(info, UART_LCR);
+ hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB); /* Set DLAB */
+ hci_h4p_outb(info, UART_DLL, divisor & 0xff); /* Set speed */
+ hci_h4p_outb(info, UART_DLM, divisor >> 8);
+ hci_h4p_outb(info, UART_LCR, lcr);
+
+ /* Make sure UART mode is enabled */
+ hci_h4p_outb(info, UART_OMAP_MDR1, mdr1);
+}
+
+int hci_h4p_reset_uart(struct hci_h4p_info *info)
+{
+ int count = 0;
+
+ /* Reset the UART */
+ hci_h4p_outb(info, UART_OMAP_SYSC, UART_SYSC_OMAP_RESET);
+ while (!(hci_h4p_inb(info, UART_OMAP_SYSS) & UART_SYSS_RESETDONE)) {
+ if (count++ > 100) {
+ dev_err(info->dev, "hci_h4p: UART reset timeout\n");
+ return -ENODEV;
+ }
+ udelay(1);
+ }
+
+ return 0;
+}
+
+void hci_h4p_store_regs(struct hci_h4p_info *info)
+{
+ u16 lcr = 0;
+
+ lcr = hci_h4p_inb(info, UART_LCR);
+ hci_h4p_outb(info, UART_LCR, 0xBF);
+ info->dll = hci_h4p_inb(info, UART_DLL);
+ info->dlh = hci_h4p_inb(info, UART_DLM);
+ info->efr = hci_h4p_inb(info, UART_EFR);
+ hci_h4p_outb(info, UART_LCR, lcr);
+ info->mdr1 = hci_h4p_inb(info, UART_OMAP_MDR1);
+ info->ier = hci_h4p_inb(info, UART_IER);
+}
+
+void hci_h4p_restore_regs(struct hci_h4p_info *info)
+{
+ u16 lcr = 0;
+
+ hci_h4p_init_uart(info);
+
+ hci_h4p_outb(info, UART_OMAP_MDR1, 7);
+ lcr = hci_h4p_inb(info, UART_LCR);
+ hci_h4p_outb(info, UART_LCR, 0xBF);
+ hci_h4p_outb(info, UART_DLL, info->dll); /* Set speed */
+ hci_h4p_outb(info, UART_DLM, info->dlh);
+ hci_h4p_outb(info, UART_EFR, info->efr);
+ hci_h4p_outb(info, UART_LCR, lcr);
+ hci_h4p_outb(info, UART_OMAP_MDR1, info->mdr1);
+ hci_h4p_outb(info, UART_IER, info->ier);
+}
+
+void hci_h4p_init_uart(struct hci_h4p_info *info)
+{
+ u8 mcr, efr;
+
+ /* Enable and setup FIFO */
+ hci_h4p_outb(info, UART_OMAP_MDR1, 0x00);
+
+ hci_h4p_outb(info, UART_LCR, 0xbf);
+ efr = hci_h4p_inb(info, UART_EFR);
+ hci_h4p_outb(info, UART_EFR, UART_EFR_ECB);
+ hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);
+ mcr = hci_h4p_inb(info, UART_MCR);
+ hci_h4p_outb(info, UART_MCR, UART_MCR_TCRTLR);
+ hci_h4p_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT |
+ (3 << 6) | (0 << 4));
+ hci_h4p_outb(info, UART_LCR, 0xbf);
+ hci_h4p_outb(info, UART_TI752_TLR, 0xed);
+ hci_h4p_outb(info, UART_TI752_TCR, 0xef);
+ hci_h4p_outb(info, UART_EFR, efr);
+ hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);
+ hci_h4p_outb(info, UART_MCR, 0x00);
+ hci_h4p_outb(info, UART_LCR, UART_LCR_WLEN8);
+ hci_h4p_outb(info, UART_IER, UART_IER_RDI);
+ hci_h4p_outb(info, UART_OMAP_SYSC, (1 << 0) | (1 << 2) | (2 << 3));
+}
diff --git a/include/linux/platform_data/bt-nokia-h4p.h b/include/linux/platform_data/bt-nokia-h4p.h
new file mode 100644
index 0000000..30d169d
--- /dev/null
+++ b/include/linux/platform_data/bt-nokia-h4p.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of Nokia H4P bluetooth driver
+ *
+ * Copyright (C) 2010 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+
+/**
+ * struct hci_h4p_platform data - hci_h4p Platform data structure
+ */
+struct hci_h4p_platform_data {
+ int chip_type;
+ int bt_sysclk;
+ unsigned int bt_wakeup_gpio;
+ unsigned int host_wakeup_gpio;
+ unsigned int reset_gpio;
+ int reset_gpio_shared;
+ unsigned int uart_irq;
+ phys_addr_t uart_base;
+ const char *uart_iclk;
+ const char *uart_fclk;
+ void (*set_pm_limits)(struct device *dev, bool set);
+};
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply related
* Re: [PATCH v6] Bluetooth: Add hci_h4p driver
From: Pavel Machek @ 2014-01-17 12:14 UTC (permalink / raw)
To: Marcel Holtmann
Cc: Pali Rohár,
Ивайло Димитров,
Gustavo F. Padovan, Johan Hedberg, linux-kernel,
linux-bluetooth@vger.kernel.org development, Ville Tervo,
Sebastian Reichel
In-Reply-To: <FD05FD52-1502-4AB8-8650-FC785B700088@holtmann.org>
Hi!
> > Add hci_h4p bluetooth driver to bluetooth-next. This device is used
> > for example on Nokia N900 cell phone.
> >
> > Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
> > Signed-off-by: Pavel Machek <pavel@ucw.cz>
> > Thanks-to: Sebastian Reichel <sre@debian.org>
> > Thanks-to: Joe Perches <joe@perches.com>
> >
> > ---
> >
> > Changes from v5: Comment fixes and some refactoring suggested by
> > Joe Perches. Please apply.
> > --- /dev/null
> > +++ b/drivers/bluetooth/hci_h4p.h
>
> can we please get the naming straight. File names do not start with hci_ anymore. We moved away from it since that term is too generic.
>
Ok, this is easy.
> > + struct timer_list lazy_release;
>
> Timer? Not delayed work?
But these changes sound like bigger piece of refactoring, and more is
suggested below. Which is good, but will be tricky to develop/test on
bluetooth-next, as running Maemo is really needed for testing.
So... this will take longer than expected, and I believe it makes
sense to push it to staging, first. That will bring linux-n900 tree
closer to mainline, and preserve more of history.
Regards,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply
* [PATCH 3/3] unit: prevent use of glibc's error(3)
From: Natanael Copa @ 2014-01-17 12:08 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Natanael Copa
In-Reply-To: <1389960506-10273-1-git-send-email-ncopa@alpinelinux.org>
When building the test-sdp we don't want src/sdpd-request.c end up
using the incompatible GNU libc's error(3).
This also fixes building on musl libc which misses the error(3) GNU
extension.
---
unit/test-sdp.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/unit/test-sdp.c b/unit/test-sdp.c
index 6d699e2..eeed0cb 100644
--- a/unit/test-sdp.c
+++ b/unit/test-sdp.c
@@ -134,6 +134,12 @@ void btd_debug(const char *format, ...)
{
}
+void error(const char *format, ...);
+
+void error(const char *format, ...)
+{
+}
+
static void context_quit(struct context *context)
{
g_main_loop_quit(context->main_loop);
--
1.8.5.3
^ permalink raw reply related
* [PATCH 2/3] various header include fixes for building with musl libc
From: Natanael Copa @ 2014-01-17 12:08 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Natanael Copa
In-Reply-To: <1389960506-10273-1-git-send-email-ncopa@alpinelinux.org>
we need:
sys/stat.h for mode_t
limits.h for PATH_MAX
---
src/textfile.h | 2 ++
tools/csr_usb.c | 1 +
tools/hid2hci.c | 1 +
3 files changed, 4 insertions(+)
diff --git a/src/textfile.h b/src/textfile.h
index b779bd2..e26da5d 100644
--- a/src/textfile.h
+++ b/src/textfile.h
@@ -24,6 +24,8 @@
#ifndef __TEXTFILE_H
#define __TEXTFILE_H
+#include <sys/stat.h>
+
int create_file(const char *filename, const mode_t mode);
int create_name(char *buf, size_t size, const char *path,
const char *address, const char *name);
diff --git a/tools/csr_usb.c b/tools/csr_usb.c
index a483bc1..5fb6bdc 100644
--- a/tools/csr_usb.c
+++ b/tools/csr_usb.c
@@ -33,6 +33,7 @@
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
+#include <limits.h>
#include <sys/ioctl.h>
#include "csr.h"
diff --git a/tools/hid2hci.c b/tools/hid2hci.c
index 95b4abf..2dbfca7 100644
--- a/tools/hid2hci.c
+++ b/tools/hid2hci.c
@@ -35,6 +35,7 @@
#include <string.h>
#include <dirent.h>
#include <getopt.h>
+#include <limits.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/hiddev.h>
--
1.8.5.3
^ permalink raw reply related
* [PATCH 1/3] bnep: avoid use of caddr_t
From: Natanael Copa @ 2014-01-17 12:08 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Natanael Copa
caddr_t is legacy BSD and should be avoided.
This fixes building against musl libc.
---
profiles/network/bnep.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/profiles/network/bnep.c b/profiles/network/bnep.c
index 2a74016..4f9b801 100644
--- a/profiles/network/bnep.c
+++ b/profiles/network/bnep.c
@@ -202,7 +202,7 @@ static int bnep_if_up(const char *devname)
ifr.ifr_flags |= IFF_UP;
ifr.ifr_flags |= IFF_MULTICAST;
- err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+ err = ioctl(sk, SIOCSIFFLAGS, (void *) &ifr);
close(sk);
@@ -227,7 +227,7 @@ static int bnep_if_down(const char *devname)
ifr.ifr_flags &= ~IFF_UP;
/* Bring down the interface */
- err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+ err = ioctl(sk, SIOCSIFFLAGS, (void *) &ifr);
close(sk);
--
1.8.5.3
^ permalink raw reply related
* Re: Dualshock4 - 'HIDP: Handshake: Unsupported request' after 'unplug virtual cable'.
From: David Herrmann @ 2014-01-17 10:16 UTC (permalink / raw)
To: Simon Wood; +Cc: linux-bluetooth@vger.kernel.org, Frank Praznik
In-Reply-To: <8ccf0ad88f16335719c367ed69c90a3e.squirrel@mungewell.org>
Hi
On Fri, Jan 17, 2014 at 6:09 AM, <simon@mungewell.org> wrote:
> Hi all,
> Frank and I have been working on a kernel driver for the Dualshock 4 and
> we're having problems with the BT connection (USB is OK).
>
> When powered on the DS4 will initiate a connection to the PC once it knows
> it's Bdaddr (via pairing), but will power off shortly there after. This
> seems to be related to incorrect (??) data sent from Bluez, or if no hid
> connection is made within a short window.
>
> On the two systems I have tried:
> 1) Debian with bluez 4.99, I can 'hidd --connect xxx' to hold the
> connection. I get joystick data but can't send data to DS4 to drive
> ff/leds. (I am told that 'hidd' is deprated)
>
> 2) Fedora LiveCD with bluez 5.13. Connection is immediately terminated by
> the controller. HCI log attached.
>
> It appears that the controller is insisting on a encrypted link ('noenc'
> and 'noauth' make no difference) and does not like the 'unplug virtual
> cable' which is sent (presumably as the link is being brought down to
> re-start encrypted).
Could you provide a bluetoothd log, too? I suspect it contains a
message along the line:
"Refusing input device connect: %s (%d)"
"connect_ds4_fedora_4.txt" contains all the important information.
I'll just summarize what it does:
1. The remote device initiates a base-band connection to your host
2. Your host fetches all SDP records from the device (L2CAP PSM: 1)
3. The SDP connection is closed
4. Link-key negotiation is successful (=> you already were connected
to the device before)
5. encryption-mode is changed successfully (not sure whether it's
turned ON or OFF, but I guess "encrypt 0x01" means ON)
6. device initiates L2CAP on PSM 0x17 (HIDP control channel)
7. Host sends "HIDP: Control: Virtual cable unplug" on the HIDP control channel
8. host closes L2CAP PSM 0x17
9. device initiates L2CAP on PSM 0x19 (HIDP intr channel)
10. host refuses connection on PSM 0x19
11. device closes baseband connection
So the problem is, your host refuses the HIDP connection from the
device. This is intentional, as we don't want *any* remote Bluetooth
device to be allowed to provide HID services. This could be used for
very subtle attacks. The code responsible for that is
./profiles/input/server.c in bluez.
Now, there are two ways to go on:
- try to initiate the HIDP connection from the host instead of the
device. Simply use "bluetoothctl" and then "connect <bdaddr>"
- add the remote device to the list of known devices so incoming
connections are not blocked. You can do this by triggering the
->device_probe() callback of the input device. I'm not sure how this
is done, but I think a simple "connect <bdaddr>" should trigger it on
*all* available services.
I hope that helps. If not, we can try to add the device manually.
Cheers
David
> In the BT HID 1.1 spec (page 48) it says
> --
> If the HIDVirtualCable SDP attribute is set to TRUE, then a Virtual Cable
> is considered to be established after both the HID Control and HID
> Interrupt L2CAP channels have been opened.
> --
>
> However the Fedora system does not have a copy of the SDP records for the
> controller, it never pulled them and I have never managed to make a
> connection long enough to read them.
>
> My theory is that the controller is rejecting the 'Unplug Virtual Cable'
> command as it shouldn't have been established, but I'm not really sure.
>
>
>
> I managed use my Debian system to read SDP records but that barfs on
> what's sent.... files also attached.
>
> Does anyone have any suggestions on what I should try next?
> Thanks, Simon
>
>
>
>
^ permalink raw reply
* [PATCH v3 2/2] Bluetooth: Queue incoming ACL data until BT_CONNECTED state is reached
From: johan.hedberg @ 2014-01-17 9:33 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1389951206-22294-1-git-send-email-johan.hedberg@gmail.com>
From: Johan Hedberg <johan.hedberg@intel.com>
This patch adds a queue for incoming L2CAP data that's received before
l2cap_connect_cfm is called and processes the data once
l2cap_connect_cfm is called. This way we ensure that we have e.g. all
remote features before processing L2CAP signaling data (which is very
important for making the correct security decisions).
The processing of the pending rx data needs to be done through
queue_work since unlike l2cap_recv_acldata, l2cap_connect_cfm is called
with the hci_dev lock held which could cause potential deadlocks.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
include/net/bluetooth/l2cap.h | 3 +++
net/bluetooth/l2cap_core.c | 33 +++++++++++++++++++++++++++++++++
2 files changed, 36 insertions(+)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index c695083eee2b..85cf40acc47e 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -624,6 +624,9 @@ struct l2cap_conn {
__u32 rx_len;
__u8 tx_ident;
+ struct sk_buff_head pending_rx;
+ struct work_struct pending_rx_work;
+
__u8 disc_reason;
struct delayed_work security_timer;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 317a5737daf6..fa59a32b0497 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1550,6 +1550,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
}
mutex_unlock(&conn->chan_lock);
+
+ queue_work(hcon->hdev->workqueue, &conn->pending_rx_work);
}
/* Notify sockets that we cannot guaranty reliability anymore */
@@ -1675,6 +1677,9 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
kfree_skb(conn->rx_skb);
+ skb_queue_purge(&conn->pending_rx);
+ flush_work(&conn->pending_rx_work);
+
l2cap_unregister_all_users(conn);
mutex_lock(&conn->chan_lock);
@@ -6880,9 +6885,16 @@ drop:
static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct l2cap_hdr *lh = (void *) skb->data;
+ struct hci_conn *hcon = conn->hcon;
u16 cid, len;
__le16 psm;
+ if (hcon->state != BT_CONNECTED) {
+ BT_DBG("queueing pending rx skb");
+ skb_queue_tail(&conn->pending_rx, skb);
+ return;
+ }
+
skb_pull(skb, L2CAP_HDR_SIZE);
cid = __le16_to_cpu(lh->cid);
len = __le16_to_cpu(lh->len);
@@ -6928,6 +6940,24 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
}
}
+static void process_pending_rx(struct work_struct *work)
+{
+ struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
+ pending_rx_work);
+
+ BT_DBG("");
+
+ while (1) {
+ struct sk_buff *skb;
+
+ skb = skb_dequeue(&conn->pending_rx);
+ if (!skb)
+ break;
+
+ l2cap_recv_frame(conn, skb);
+ }
+}
+
static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
{
struct l2cap_conn *conn = hcon->l2cap_data;
@@ -6983,6 +7013,9 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
else
INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
+ skb_queue_head_init(&conn->pending_rx);
+ INIT_WORK(&conn->pending_rx_work, process_pending_rx);
+
conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
return conn;
--
1.8.4.2
^ permalink raw reply related
* [PATCH v3 1/2] Bluetooth: Reorder L2CAP functions to avoid forward declarations
From: johan.hedberg @ 2014-01-17 9:33 UTC (permalink / raw)
To: linux-bluetooth
From: Johan Hedberg <johan.hedberg@intel.com>
This patch moves the l2cap_conn_add, is_valid_psm and l2cap_chan_connect
functions further down in l2cap_core.c. The patch doesn't contain
anything else except the relocation of these functions. By moving the
functions further down the patch enables a subsequent patch that adds a
pending RX queue to be implemented without a forward declaration of a
function.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
---
net/bluetooth/l2cap_core.c | 415 ++++++++++++++++++++++-----------------------
1 file changed, 207 insertions(+), 208 deletions(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 3f0dd552cb2b..317a5737daf6 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1722,66 +1722,6 @@ static void security_timeout(struct work_struct *work)
}
}
-static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
-{
- struct l2cap_conn *conn = hcon->l2cap_data;
- struct hci_chan *hchan;
-
- if (conn)
- return conn;
-
- hchan = hci_chan_create(hcon);
- if (!hchan)
- return NULL;
-
- conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL);
- if (!conn) {
- hci_chan_del(hchan);
- return NULL;
- }
-
- kref_init(&conn->ref);
- hcon->l2cap_data = conn;
- conn->hcon = hcon;
- hci_conn_get(conn->hcon);
- conn->hchan = hchan;
-
- BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
-
- switch (hcon->type) {
- case LE_LINK:
- if (hcon->hdev->le_mtu) {
- conn->mtu = hcon->hdev->le_mtu;
- break;
- }
- /* fall through */
- default:
- conn->mtu = hcon->hdev->acl_mtu;
- break;
- }
-
- conn->feat_mask = 0;
-
- if (hcon->type == ACL_LINK)
- conn->hs_enabled = test_bit(HCI_HS_ENABLED,
- &hcon->hdev->dev_flags);
-
- spin_lock_init(&conn->lock);
- mutex_init(&conn->chan_lock);
-
- INIT_LIST_HEAD(&conn->chan_l);
- INIT_LIST_HEAD(&conn->users);
-
- if (hcon->type == LE_LINK)
- INIT_DELAYED_WORK(&conn->security_timer, security_timeout);
- else
- INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
-
- conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
-
- return conn;
-}
-
static void l2cap_conn_free(struct kref *ref)
{
struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref);
@@ -1852,154 +1792,6 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
return c1;
}
-static bool is_valid_psm(u16 psm, u8 dst_type)
-{
- if (!psm)
- return false;
-
- if (bdaddr_type_is_le(dst_type))
- return (psm <= 0x00ff);
-
- /* PSM must be odd and lsb of upper byte must be 0 */
- return ((psm & 0x0101) == 0x0001);
-}
-
-int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
- bdaddr_t *dst, u8 dst_type)
-{
- struct l2cap_conn *conn;
- struct hci_conn *hcon;
- struct hci_dev *hdev;
- __u8 auth_type;
- int err;
-
- BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
- dst_type, __le16_to_cpu(psm));
-
- hdev = hci_get_route(dst, &chan->src);
- if (!hdev)
- return -EHOSTUNREACH;
-
- hci_dev_lock(hdev);
-
- l2cap_chan_lock(chan);
-
- if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
- chan->chan_type != L2CAP_CHAN_RAW) {
- err = -EINVAL;
- goto done;
- }
-
- if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(psm || cid)) {
- err = -EINVAL;
- goto done;
- }
-
- switch (chan->mode) {
- case L2CAP_MODE_BASIC:
- break;
- case L2CAP_MODE_LE_FLOWCTL:
- l2cap_le_flowctl_init(chan);
- break;
- case L2CAP_MODE_ERTM:
- case L2CAP_MODE_STREAMING:
- if (!disable_ertm)
- break;
- /* fall through */
- default:
- err = -ENOTSUPP;
- goto done;
- }
-
- switch (chan->state) {
- case BT_CONNECT:
- case BT_CONNECT2:
- case BT_CONFIG:
- /* Already connecting */
- err = 0;
- goto done;
-
- case BT_CONNECTED:
- /* Already connected */
- err = -EISCONN;
- goto done;
-
- case BT_OPEN:
- case BT_BOUND:
- /* Can connect */
- break;
-
- default:
- err = -EBADFD;
- goto done;
- }
-
- /* Set destination address and psm */
- bacpy(&chan->dst, dst);
- chan->dst_type = dst_type;
-
- chan->psm = psm;
- chan->dcid = cid;
-
- auth_type = l2cap_get_auth_type(chan);
-
- if (bdaddr_type_is_le(dst_type))
- hcon = hci_connect(hdev, LE_LINK, dst, dst_type,
- chan->sec_level, auth_type);
- else
- hcon = hci_connect(hdev, ACL_LINK, dst, dst_type,
- chan->sec_level, auth_type);
-
- if (IS_ERR(hcon)) {
- err = PTR_ERR(hcon);
- goto done;
- }
-
- conn = l2cap_conn_add(hcon);
- if (!conn) {
- hci_conn_drop(hcon);
- err = -ENOMEM;
- goto done;
- }
-
- if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
- hci_conn_drop(hcon);
- err = -EBUSY;
- goto done;
- }
-
- /* Update source addr of the socket */
- bacpy(&chan->src, &hcon->src);
- chan->src_type = bdaddr_type(hcon, hcon->src_type);
-
- l2cap_chan_unlock(chan);
- l2cap_chan_add(conn, chan);
- l2cap_chan_lock(chan);
-
- /* l2cap_chan_add takes its own ref so we can drop this one */
- hci_conn_drop(hcon);
-
- l2cap_state_change(chan, BT_CONNECT);
- __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
-
- if (hcon->state == BT_CONNECTED) {
- if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
- __clear_chan_timer(chan);
- if (l2cap_chan_check_security(chan))
- l2cap_state_change(chan, BT_CONNECTED);
- } else
- l2cap_do_start(chan);
- }
-
- err = 0;
-
-done:
- l2cap_chan_unlock(chan);
- hci_dev_unlock(hdev);
- hci_dev_put(hdev);
- return err;
-}
-
static void l2cap_monitor_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
@@ -7136,6 +6928,213 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
}
}
+static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
+{
+ struct l2cap_conn *conn = hcon->l2cap_data;
+ struct hci_chan *hchan;
+
+ if (conn)
+ return conn;
+
+ hchan = hci_chan_create(hcon);
+ if (!hchan)
+ return NULL;
+
+ conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL);
+ if (!conn) {
+ hci_chan_del(hchan);
+ return NULL;
+ }
+
+ kref_init(&conn->ref);
+ hcon->l2cap_data = conn;
+ conn->hcon = hcon;
+ hci_conn_get(conn->hcon);
+ conn->hchan = hchan;
+
+ BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
+
+ switch (hcon->type) {
+ case LE_LINK:
+ if (hcon->hdev->le_mtu) {
+ conn->mtu = hcon->hdev->le_mtu;
+ break;
+ }
+ /* fall through */
+ default:
+ conn->mtu = hcon->hdev->acl_mtu;
+ break;
+ }
+
+ conn->feat_mask = 0;
+
+ if (hcon->type == ACL_LINK)
+ conn->hs_enabled = test_bit(HCI_HS_ENABLED,
+ &hcon->hdev->dev_flags);
+
+ spin_lock_init(&conn->lock);
+ mutex_init(&conn->chan_lock);
+
+ INIT_LIST_HEAD(&conn->chan_l);
+ INIT_LIST_HEAD(&conn->users);
+
+ if (hcon->type == LE_LINK)
+ INIT_DELAYED_WORK(&conn->security_timer, security_timeout);
+ else
+ INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
+
+ conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
+
+ return conn;
+}
+
+static bool is_valid_psm(u16 psm, u8 dst_type) {
+ if (!psm)
+ return false;
+
+ if (bdaddr_type_is_le(dst_type))
+ return (psm <= 0x00ff);
+
+ /* PSM must be odd and lsb of upper byte must be 0 */
+ return ((psm & 0x0101) == 0x0001);
+}
+
+int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
+ bdaddr_t *dst, u8 dst_type)
+{
+ struct l2cap_conn *conn;
+ struct hci_conn *hcon;
+ struct hci_dev *hdev;
+ __u8 auth_type;
+ int err;
+
+ BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
+ dst_type, __le16_to_cpu(psm));
+
+ hdev = hci_get_route(dst, &chan->src);
+ if (!hdev)
+ return -EHOSTUNREACH;
+
+ hci_dev_lock(hdev);
+
+ l2cap_chan_lock(chan);
+
+ if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
+ chan->chan_type != L2CAP_CHAN_RAW) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(psm || cid)) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ switch (chan->mode) {
+ case L2CAP_MODE_BASIC:
+ break;
+ case L2CAP_MODE_LE_FLOWCTL:
+ l2cap_le_flowctl_init(chan);
+ break;
+ case L2CAP_MODE_ERTM:
+ case L2CAP_MODE_STREAMING:
+ if (!disable_ertm)
+ break;
+ /* fall through */
+ default:
+ err = -ENOTSUPP;
+ goto done;
+ }
+
+ switch (chan->state) {
+ case BT_CONNECT:
+ case BT_CONNECT2:
+ case BT_CONFIG:
+ /* Already connecting */
+ err = 0;
+ goto done;
+
+ case BT_CONNECTED:
+ /* Already connected */
+ err = -EISCONN;
+ goto done;
+
+ case BT_OPEN:
+ case BT_BOUND:
+ /* Can connect */
+ break;
+
+ default:
+ err = -EBADFD;
+ goto done;
+ }
+
+ /* Set destination address and psm */
+ bacpy(&chan->dst, dst);
+ chan->dst_type = dst_type;
+
+ chan->psm = psm;
+ chan->dcid = cid;
+
+ auth_type = l2cap_get_auth_type(chan);
+
+ if (bdaddr_type_is_le(dst_type))
+ hcon = hci_connect(hdev, LE_LINK, dst, dst_type,
+ chan->sec_level, auth_type);
+ else
+ hcon = hci_connect(hdev, ACL_LINK, dst, dst_type,
+ chan->sec_level, auth_type);
+
+ if (IS_ERR(hcon)) {
+ err = PTR_ERR(hcon);
+ goto done;
+ }
+
+ conn = l2cap_conn_add(hcon);
+ if (!conn) {
+ hci_conn_drop(hcon);
+ err = -ENOMEM;
+ goto done;
+ }
+
+ if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
+ hci_conn_drop(hcon);
+ err = -EBUSY;
+ goto done;
+ }
+
+ /* Update source addr of the socket */
+ bacpy(&chan->src, &hcon->src);
+ chan->src_type = bdaddr_type(hcon, hcon->src_type);
+
+ l2cap_chan_unlock(chan);
+ l2cap_chan_add(conn, chan);
+ l2cap_chan_lock(chan);
+
+ /* l2cap_chan_add takes its own ref so we can drop this one */
+ hci_conn_drop(hcon);
+
+ l2cap_state_change(chan, BT_CONNECT);
+ __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
+
+ if (hcon->state == BT_CONNECTED) {
+ if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+ __clear_chan_timer(chan);
+ if (l2cap_chan_check_security(chan))
+ l2cap_state_change(chan, BT_CONNECTED);
+ } else
+ l2cap_do_start(chan);
+ }
+
+ err = 0;
+
+done:
+ l2cap_chan_unlock(chan);
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+ return err;
+}
+
/* ---- L2CAP interface with lower layer (HCI) ---- */
int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
--
1.8.4.2
^ permalink raw reply related
* Re: [RFC 1/4] android: Add sample init.bluetooth.rc file
From: Szymon Janc @ 2014-01-17 9:06 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <19D4671A-DE2F-49A3-9FEE-F17B5B1CA32D@holtmann.org>
Hi Marcel,
On Friday 17 of January 2014 00:53:54 Marcel Holtmann wrote:
> Hi Szymon,
>
> >>> This file is intended to be included from device init.rc.
> >>> ---
> >>> android/Android.mk | 16 +++++++++++++++-
> >>> android/Makefile.am | 1 +
> >>> android/init.bluetooth.rc | 27 +++++++++++++++++++++++++++
> >>> 3 files changed, 43 insertions(+), 1 deletion(-)
> >>> create mode 100644 android/init.bluetooth.rc
> >>>
> >>> diff --git a/android/Android.mk b/android/Android.mk
> >>> index ae52ab4..f13e703 100644
> >>> --- a/android/Android.mk
> >>> +++ b/android/Android.mk
> >>> @@ -109,7 +109,7 @@ LOCAL_MODULE := bluetooth.default
> >>> LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
> >>> LOCAL_MODULE_TAGS := optional
> >>> LOCAL_MODULE_CLASS := SHARED_LIBRARIES
> >>> -LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop
> >>> +LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
> >>>
> >>> include $(BUILD_SHARED_LIBRARY)
> >>>
> >>> @@ -263,3 +263,17 @@ LOCAL_MODULE_TAGS := optional
> >>> LOCAL_MODULE := bluetoothd-snoop
> >>>
> >>> include $(BUILD_EXECUTABLE)
> >>> +
> >>> +#
> >>> +# init.bleutooth.rc
> >>> +#
> >>> +
> >>> +include $(CLEAR_VARS)
> >>> +
> >>> +LOCAL_MODULE := init.bluetooth.rc
> >>> +LOCAL_MODULE_CLASS := ETC
> >>> +LOCAL_SRC_FILES := $(LOCAL_MODULE)
> >>> +LOCAL_MODULE_TAGS := optional
> >>> +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
> >>> +
> >>> +include $(BUILD_PREBUILT)
> >>> diff --git a/android/Makefile.am b/android/Makefile.am
> >>> index 7806f79..b205019 100644
> >>> --- a/android/Makefile.am
> >>> +++ b/android/Makefile.am
> >>> @@ -134,6 +134,7 @@ android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
> >>> endif
> >>>
> >>> EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
> >>> + android/init.bluetooth.rc \
> >>> android/pics-gap.txt android/pics-hid.txt \
> >>> android/pics-pan.txt android/pics-did.txt \
> >>> android/pics-opp.txt android/pics-pbap.txt \
> >>> diff --git a/android/init.bluetooth.rc b/android/init.bluetooth.rc
> >>> new file mode 100644
> >>> index 0000000..e2be9e8
> >>> --- /dev/null
> >>> +++ b/android/init.bluetooth.rc
> >>> @@ -0,0 +1,27 @@
> >>> +chown bluetooth bluetooth /data/misc/bluetooth
> >>> +
> >>> +chown bluetooth bluetooth /dev/uhid
> >>> +
> >>> +on property:bluetooth.start=bluetoothd
> >>> + start bluetoothd
> >>> +
> >>> +on property:bluetooth.stop=bluetoothd
> >>> + stop bluetoothd
> >>> +
> >>> +on property:bluetooth.start=bluetoothd-snoop
> >>> + start bluetoothd-snoop
> >>> +
> >>> +on property:bluetooth.stop=bluetoothd-snoop
> >>> + stop bluetoothd-snoop
> >>
> >> I think this is actually racy. The ctl.start and ctl.stop sound like they have special handling inside Android init. If you accidentally set bluetooth.start , then bad things happen.
> >
> > on property:foo=bar is also triggered if foo is already set to bar so I don't
> > see what could happen wrong here.
> >
> >>
> >> So I would propose doing bluetooth.daemon=on/off and bluetooth.snoop=on/off
> >>
> >> We just have to make sure that when bluetoothd or bluetoothd-snoop die unexpectedly that these properties get set back to off.
> >
> > I'm not sure if we are able to set properties back to proper state if service
> > exited in such case (that would trigger loop in init).
>
> there is a service-exited-<name> type of action. I think we should look into using that one somehow.
I'll exercise that and send V2.
>
> > My idea was to use bluetooth.start/stop to start/stop (actually stop is needed
> > only for bluetoothd-snoop service) service by bluetooth user (sort of "sudo"
> > wrapper) but don't care about values in those props and if one want to use
> > props to track service lifetime then just use init.svc.bluetoothd{-snoop}
> > property.
>
> Fair enough.
>
> However then I would prefer if we do bluetooth.{start,stop}=daemon and bluetooth.{start,stop}=snoop to make this really tight into how our solution works.
OK.
>
> >>> +service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
> >>> + class main
> >>> + group bluetooth net_admin
> >>> + disabled
> >>> + oneshot
> >>> +
> >>> +service bluetoothd-snoop /system/bin/bluetoothd-snoop
> >>> + class main
> >>> + group bluetooth net_admin
> >>> + disabled
> >>> + oneshot
> >>
> >> Does net_admin include CAP_NET_RAW actually or are we using some sort of patched module for this?
> >
> > I'm not sure, this was probably just copied from <4.2 android. Since we
> > start as root and drop caps I think this can be removed.
>
> The snoop process just needs CAP_NET_RAW and write access to the file location. I would prefer to really make sure it runs with as less right as possible.
OK.
--
Best regards,
Szymon Janc
^ permalink raw reply
* Re: [RFC 1/4] android: Add sample init.bluetooth.rc file
From: Szymon Janc @ 2014-01-17 9:02 UTC (permalink / raw)
To: Luiz Augusto von Dentz
Cc: Marcel Holtmann, linux-bluetooth@vger.kernel.org development
In-Reply-To: <CABBYNZ+Pop6PooUd17gn29PrKfASiRm+QT6xGy0fen5ipg0-0A@mail.gmail.com>
Hi Luiz,
On Friday 17 of January 2014 10:56:36 Luiz Augusto von Dentz wrote:
> Hi Szymon,
>
> On Fri, Jan 17, 2014 at 10:34 AM, Szymon Janc <szymon.janc@tieto.com> wrote:
> > Hi Marcel,
> >
> > On Tuesday 14 of January 2014 13:20:32 Marcel Holtmann wrote:
> >> Hi Szymon,
> >>
> >> > This file is intended to be included from device init.rc.
> >> > ---
> >> > android/Android.mk | 16 +++++++++++++++-
> >> > android/Makefile.am | 1 +
> >> > android/init.bluetooth.rc | 27 +++++++++++++++++++++++++++
> >> > 3 files changed, 43 insertions(+), 1 deletion(-)
> >> > create mode 100644 android/init.bluetooth.rc
> >> >
> >> > diff --git a/android/Android.mk b/android/Android.mk
> >> > index ae52ab4..f13e703 100644
> >> > --- a/android/Android.mk
> >> > +++ b/android/Android.mk
> >> > @@ -109,7 +109,7 @@ LOCAL_MODULE := bluetooth.default
> >> > LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
> >> > LOCAL_MODULE_TAGS := optional
> >> > LOCAL_MODULE_CLASS := SHARED_LIBRARIES
> >> > -LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop
> >> > +LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
> >> >
> >> > include $(BUILD_SHARED_LIBRARY)
> >> >
> >> > @@ -263,3 +263,17 @@ LOCAL_MODULE_TAGS := optional
> >> > LOCAL_MODULE := bluetoothd-snoop
> >> >
> >> > include $(BUILD_EXECUTABLE)
> >> > +
> >> > +#
> >> > +# init.bleutooth.rc
> >> > +#
> >> > +
> >> > +include $(CLEAR_VARS)
> >> > +
> >> > +LOCAL_MODULE := init.bluetooth.rc
> >> > +LOCAL_MODULE_CLASS := ETC
> >> > +LOCAL_SRC_FILES := $(LOCAL_MODULE)
> >> > +LOCAL_MODULE_TAGS := optional
> >> > +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
> >> > +
> >> > +include $(BUILD_PREBUILT)
> >> > diff --git a/android/Makefile.am b/android/Makefile.am
> >> > index 7806f79..b205019 100644
> >> > --- a/android/Makefile.am
> >> > +++ b/android/Makefile.am
> >> > @@ -134,6 +134,7 @@ android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
> >> > endif
> >> >
> >> > EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
> >> > + android/init.bluetooth.rc \
> >> > android/pics-gap.txt android/pics-hid.txt \
> >> > android/pics-pan.txt android/pics-did.txt \
> >> > android/pics-opp.txt android/pics-pbap.txt \
> >> > diff --git a/android/init.bluetooth.rc b/android/init.bluetooth.rc
> >> > new file mode 100644
> >> > index 0000000..e2be9e8
> >> > --- /dev/null
> >> > +++ b/android/init.bluetooth.rc
> >> > @@ -0,0 +1,27 @@
> >> > +chown bluetooth bluetooth /data/misc/bluetooth
> >> > +
> >> > +chown bluetooth bluetooth /dev/uhid
> >> > +
> >> > +on property:bluetooth.start=bluetoothd
> >> > + start bluetoothd
> >> > +
> >> > +on property:bluetooth.stop=bluetoothd
> >> > + stop bluetoothd
> >> > +
> >> > +on property:bluetooth.start=bluetoothd-snoop
> >> > + start bluetoothd-snoop
> >> > +
> >> > +on property:bluetooth.stop=bluetoothd-snoop
> >> > + stop bluetoothd-snoop
> >>
> >> I think this is actually racy. The ctl.start and ctl.stop sound like they have special handling inside Android init. If you accidentally set bluetooth.start , then bad things happen.
> >
> > on property:foo=bar is also triggered if foo is already set to bar so I don't
> > see what could happen wrong here.
> >
> >>
> >> So I would propose doing bluetooth.daemon=on/off and bluetooth.snoop=on/off
> >>
> >> We just have to make sure that when bluetoothd or bluetoothd-snoop die unexpectedly that these properties get set back to off.
> >
> > I'm not sure if we are able to set properties back to proper state if service
> > exited in such case (that would trigger loop in init).
> >
> > My idea was to use bluetooth.start/stop to start/stop (actually stop is needed
> > only for bluetoothd-snoop service) service by bluetooth user (sort of "sudo"
> > wrapper) but don't care about values in those props and if one want to use
> > props to track service lifetime then just use init.svc.bluetoothd{-snoop}
> > property.
> >
> >>
> >> > +
> >> > +service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
> >> > + class main
> >> > + group bluetooth net_admin
> >> > + disabled
> >> > + oneshot
> >> > +
> >> > +service bluetoothd-snoop /system/bin/bluetoothd-snoop
> >> > + class main
> >> > + group bluetooth net_admin
> >> > + disabled
> >> > + oneshot
> >>
> >> Does net_admin include CAP_NET_RAW actually or are we using some sort of patched module for this?
> >
> > I'm not sure, this was probably just copied from <4.2 android. Since we
> > start as root and drop caps I think this can be removed.
>
> Iirc CAP_NET_RAW is not required by mgmt nor any other part of
> bluetoothd for Android make use of raw sockets so it was dropped at
> some point.
As per comment in main.c we need this for bnep ioctl.
--
Best regards,
Szymon Janc
^ permalink raw reply
* Re: [RFC 1/4] android: Add sample init.bluetooth.rc file
From: Luiz Augusto von Dentz @ 2014-01-17 8:56 UTC (permalink / raw)
To: Szymon Janc; +Cc: Marcel Holtmann, linux-bluetooth@vger.kernel.org development
In-Reply-To: <2728377.B2HgAZfSS5@uw000953>
Hi Szymon,
On Fri, Jan 17, 2014 at 10:34 AM, Szymon Janc <szymon.janc@tieto.com> wrote:
> Hi Marcel,
>
> On Tuesday 14 of January 2014 13:20:32 Marcel Holtmann wrote:
>> Hi Szymon,
>>
>> > This file is intended to be included from device init.rc.
>> > ---
>> > android/Android.mk | 16 +++++++++++++++-
>> > android/Makefile.am | 1 +
>> > android/init.bluetooth.rc | 27 +++++++++++++++++++++++++++
>> > 3 files changed, 43 insertions(+), 1 deletion(-)
>> > create mode 100644 android/init.bluetooth.rc
>> >
>> > diff --git a/android/Android.mk b/android/Android.mk
>> > index ae52ab4..f13e703 100644
>> > --- a/android/Android.mk
>> > +++ b/android/Android.mk
>> > @@ -109,7 +109,7 @@ LOCAL_MODULE := bluetooth.default
>> > LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
>> > LOCAL_MODULE_TAGS := optional
>> > LOCAL_MODULE_CLASS := SHARED_LIBRARIES
>> > -LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop
>> > +LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
>> >
>> > include $(BUILD_SHARED_LIBRARY)
>> >
>> > @@ -263,3 +263,17 @@ LOCAL_MODULE_TAGS := optional
>> > LOCAL_MODULE := bluetoothd-snoop
>> >
>> > include $(BUILD_EXECUTABLE)
>> > +
>> > +#
>> > +# init.bleutooth.rc
>> > +#
>> > +
>> > +include $(CLEAR_VARS)
>> > +
>> > +LOCAL_MODULE := init.bluetooth.rc
>> > +LOCAL_MODULE_CLASS := ETC
>> > +LOCAL_SRC_FILES := $(LOCAL_MODULE)
>> > +LOCAL_MODULE_TAGS := optional
>> > +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
>> > +
>> > +include $(BUILD_PREBUILT)
>> > diff --git a/android/Makefile.am b/android/Makefile.am
>> > index 7806f79..b205019 100644
>> > --- a/android/Makefile.am
>> > +++ b/android/Makefile.am
>> > @@ -134,6 +134,7 @@ android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
>> > endif
>> >
>> > EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
>> > + android/init.bluetooth.rc \
>> > android/pics-gap.txt android/pics-hid.txt \
>> > android/pics-pan.txt android/pics-did.txt \
>> > android/pics-opp.txt android/pics-pbap.txt \
>> > diff --git a/android/init.bluetooth.rc b/android/init.bluetooth.rc
>> > new file mode 100644
>> > index 0000000..e2be9e8
>> > --- /dev/null
>> > +++ b/android/init.bluetooth.rc
>> > @@ -0,0 +1,27 @@
>> > +chown bluetooth bluetooth /data/misc/bluetooth
>> > +
>> > +chown bluetooth bluetooth /dev/uhid
>> > +
>> > +on property:bluetooth.start=bluetoothd
>> > + start bluetoothd
>> > +
>> > +on property:bluetooth.stop=bluetoothd
>> > + stop bluetoothd
>> > +
>> > +on property:bluetooth.start=bluetoothd-snoop
>> > + start bluetoothd-snoop
>> > +
>> > +on property:bluetooth.stop=bluetoothd-snoop
>> > + stop bluetoothd-snoop
>>
>> I think this is actually racy. The ctl.start and ctl.stop sound like they have special handling inside Android init. If you accidentally set bluetooth.start , then bad things happen.
>
> on property:foo=bar is also triggered if foo is already set to bar so I don't
> see what could happen wrong here.
>
>>
>> So I would propose doing bluetooth.daemon=on/off and bluetooth.snoop=on/off
>>
>> We just have to make sure that when bluetoothd or bluetoothd-snoop die unexpectedly that these properties get set back to off.
>
> I'm not sure if we are able to set properties back to proper state if service
> exited in such case (that would trigger loop in init).
>
> My idea was to use bluetooth.start/stop to start/stop (actually stop is needed
> only for bluetoothd-snoop service) service by bluetooth user (sort of "sudo"
> wrapper) but don't care about values in those props and if one want to use
> props to track service lifetime then just use init.svc.bluetoothd{-snoop}
> property.
>
>>
>> > +
>> > +service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
>> > + class main
>> > + group bluetooth net_admin
>> > + disabled
>> > + oneshot
>> > +
>> > +service bluetoothd-snoop /system/bin/bluetoothd-snoop
>> > + class main
>> > + group bluetooth net_admin
>> > + disabled
>> > + oneshot
>>
>> Does net_admin include CAP_NET_RAW actually or are we using some sort of patched module for this?
>
> I'm not sure, this was probably just copied from <4.2 android. Since we
> start as root and drop caps I think this can be removed.
Iirc CAP_NET_RAW is not required by mgmt nor any other part of
bluetoothd for Android make use of raw sockets so it was dropped at
some point.
--
Luiz Augusto von Dentz
^ permalink raw reply
* Re: [RFC 1/4] android: Add sample init.bluetooth.rc file
From: Marcel Holtmann @ 2014-01-17 8:53 UTC (permalink / raw)
To: Szymon Janc; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <2728377.B2HgAZfSS5@uw000953>
Hi Szymon,
>>> This file is intended to be included from device init.rc.
>>> ---
>>> android/Android.mk | 16 +++++++++++++++-
>>> android/Makefile.am | 1 +
>>> android/init.bluetooth.rc | 27 +++++++++++++++++++++++++++
>>> 3 files changed, 43 insertions(+), 1 deletion(-)
>>> create mode 100644 android/init.bluetooth.rc
>>>
>>> diff --git a/android/Android.mk b/android/Android.mk
>>> index ae52ab4..f13e703 100644
>>> --- a/android/Android.mk
>>> +++ b/android/Android.mk
>>> @@ -109,7 +109,7 @@ LOCAL_MODULE := bluetooth.default
>>> LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
>>> LOCAL_MODULE_TAGS := optional
>>> LOCAL_MODULE_CLASS := SHARED_LIBRARIES
>>> -LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop
>>> +LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
>>>
>>> include $(BUILD_SHARED_LIBRARY)
>>>
>>> @@ -263,3 +263,17 @@ LOCAL_MODULE_TAGS := optional
>>> LOCAL_MODULE := bluetoothd-snoop
>>>
>>> include $(BUILD_EXECUTABLE)
>>> +
>>> +#
>>> +# init.bleutooth.rc
>>> +#
>>> +
>>> +include $(CLEAR_VARS)
>>> +
>>> +LOCAL_MODULE := init.bluetooth.rc
>>> +LOCAL_MODULE_CLASS := ETC
>>> +LOCAL_SRC_FILES := $(LOCAL_MODULE)
>>> +LOCAL_MODULE_TAGS := optional
>>> +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
>>> +
>>> +include $(BUILD_PREBUILT)
>>> diff --git a/android/Makefile.am b/android/Makefile.am
>>> index 7806f79..b205019 100644
>>> --- a/android/Makefile.am
>>> +++ b/android/Makefile.am
>>> @@ -134,6 +134,7 @@ android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
>>> endif
>>>
>>> EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
>>> + android/init.bluetooth.rc \
>>> android/pics-gap.txt android/pics-hid.txt \
>>> android/pics-pan.txt android/pics-did.txt \
>>> android/pics-opp.txt android/pics-pbap.txt \
>>> diff --git a/android/init.bluetooth.rc b/android/init.bluetooth.rc
>>> new file mode 100644
>>> index 0000000..e2be9e8
>>> --- /dev/null
>>> +++ b/android/init.bluetooth.rc
>>> @@ -0,0 +1,27 @@
>>> +chown bluetooth bluetooth /data/misc/bluetooth
>>> +
>>> +chown bluetooth bluetooth /dev/uhid
>>> +
>>> +on property:bluetooth.start=bluetoothd
>>> + start bluetoothd
>>> +
>>> +on property:bluetooth.stop=bluetoothd
>>> + stop bluetoothd
>>> +
>>> +on property:bluetooth.start=bluetoothd-snoop
>>> + start bluetoothd-snoop
>>> +
>>> +on property:bluetooth.stop=bluetoothd-snoop
>>> + stop bluetoothd-snoop
>>
>> I think this is actually racy. The ctl.start and ctl.stop sound like they have special handling inside Android init. If you accidentally set bluetooth.start , then bad things happen.
>
> on property:foo=bar is also triggered if foo is already set to bar so I don't
> see what could happen wrong here.
>
>>
>> So I would propose doing bluetooth.daemon=on/off and bluetooth.snoop=on/off
>>
>> We just have to make sure that when bluetoothd or bluetoothd-snoop die unexpectedly that these properties get set back to off.
>
> I'm not sure if we are able to set properties back to proper state if service
> exited in such case (that would trigger loop in init).
there is a service-exited-<name> type of action. I think we should look into using that one somehow.
> My idea was to use bluetooth.start/stop to start/stop (actually stop is needed
> only for bluetoothd-snoop service) service by bluetooth user (sort of "sudo"
> wrapper) but don't care about values in those props and if one want to use
> props to track service lifetime then just use init.svc.bluetoothd{-snoop}
> property.
Fair enough.
However then I would prefer if we do bluetooth.{start,stop}=daemon and bluetooth.{start,stop}=snoop to make this really tight into how our solution works.
>>> +service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
>>> + class main
>>> + group bluetooth net_admin
>>> + disabled
>>> + oneshot
>>> +
>>> +service bluetoothd-snoop /system/bin/bluetoothd-snoop
>>> + class main
>>> + group bluetooth net_admin
>>> + disabled
>>> + oneshot
>>
>> Does net_admin include CAP_NET_RAW actually or are we using some sort of patched module for this?
>
> I'm not sure, this was probably just copied from <4.2 android. Since we
> start as root and drop caps I think this can be removed.
The snoop process just needs CAP_NET_RAW and write access to the file location. I would prefer to really make sure it runs with as less right as possible.
Regards
Marcel
^ permalink raw reply
* Re: [RFC 1/4] android: Add sample init.bluetooth.rc file
From: Szymon Janc @ 2014-01-17 8:34 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <7410E472-B6D3-4F2C-B83F-3882BA60DD31@holtmann.org>
Hi Marcel,
On Tuesday 14 of January 2014 13:20:32 Marcel Holtmann wrote:
> Hi Szymon,
>
> > This file is intended to be included from device init.rc.
> > ---
> > android/Android.mk | 16 +++++++++++++++-
> > android/Makefile.am | 1 +
> > android/init.bluetooth.rc | 27 +++++++++++++++++++++++++++
> > 3 files changed, 43 insertions(+), 1 deletion(-)
> > create mode 100644 android/init.bluetooth.rc
> >
> > diff --git a/android/Android.mk b/android/Android.mk
> > index ae52ab4..f13e703 100644
> > --- a/android/Android.mk
> > +++ b/android/Android.mk
> > @@ -109,7 +109,7 @@ LOCAL_MODULE := bluetooth.default
> > LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
> > LOCAL_MODULE_TAGS := optional
> > LOCAL_MODULE_CLASS := SHARED_LIBRARIES
> > -LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop
> > +LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
> >
> > include $(BUILD_SHARED_LIBRARY)
> >
> > @@ -263,3 +263,17 @@ LOCAL_MODULE_TAGS := optional
> > LOCAL_MODULE := bluetoothd-snoop
> >
> > include $(BUILD_EXECUTABLE)
> > +
> > +#
> > +# init.bleutooth.rc
> > +#
> > +
> > +include $(CLEAR_VARS)
> > +
> > +LOCAL_MODULE := init.bluetooth.rc
> > +LOCAL_MODULE_CLASS := ETC
> > +LOCAL_SRC_FILES := $(LOCAL_MODULE)
> > +LOCAL_MODULE_TAGS := optional
> > +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
> > +
> > +include $(BUILD_PREBUILT)
> > diff --git a/android/Makefile.am b/android/Makefile.am
> > index 7806f79..b205019 100644
> > --- a/android/Makefile.am
> > +++ b/android/Makefile.am
> > @@ -134,6 +134,7 @@ android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
> > endif
> >
> > EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
> > + android/init.bluetooth.rc \
> > android/pics-gap.txt android/pics-hid.txt \
> > android/pics-pan.txt android/pics-did.txt \
> > android/pics-opp.txt android/pics-pbap.txt \
> > diff --git a/android/init.bluetooth.rc b/android/init.bluetooth.rc
> > new file mode 100644
> > index 0000000..e2be9e8
> > --- /dev/null
> > +++ b/android/init.bluetooth.rc
> > @@ -0,0 +1,27 @@
> > +chown bluetooth bluetooth /data/misc/bluetooth
> > +
> > +chown bluetooth bluetooth /dev/uhid
> > +
> > +on property:bluetooth.start=bluetoothd
> > + start bluetoothd
> > +
> > +on property:bluetooth.stop=bluetoothd
> > + stop bluetoothd
> > +
> > +on property:bluetooth.start=bluetoothd-snoop
> > + start bluetoothd-snoop
> > +
> > +on property:bluetooth.stop=bluetoothd-snoop
> > + stop bluetoothd-snoop
>
> I think this is actually racy. The ctl.start and ctl.stop sound like they have special handling inside Android init. If you accidentally set bluetooth.start , then bad things happen.
on property:foo=bar is also triggered if foo is already set to bar so I don't
see what could happen wrong here.
>
> So I would propose doing bluetooth.daemon=on/off and bluetooth.snoop=on/off
>
> We just have to make sure that when bluetoothd or bluetoothd-snoop die unexpectedly that these properties get set back to off.
I'm not sure if we are able to set properties back to proper state if service
exited in such case (that would trigger loop in init).
My idea was to use bluetooth.start/stop to start/stop (actually stop is needed
only for bluetoothd-snoop service) service by bluetooth user (sort of "sudo"
wrapper) but don't care about values in those props and if one want to use
props to track service lifetime then just use init.svc.bluetoothd{-snoop}
property.
>
> > +
> > +service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
> > + class main
> > + group bluetooth net_admin
> > + disabled
> > + oneshot
> > +
> > +service bluetoothd-snoop /system/bin/bluetoothd-snoop
> > + class main
> > + group bluetooth net_admin
> > + disabled
> > + oneshot
>
> Does net_admin include CAP_NET_RAW actually or are we using some sort of patched module for this?
I'm not sure, this was probably just copied from <4.2 android. Since we
start as root and drop caps I think this can be removed.
--
Best regards,
Szymon Janc
^ permalink raw reply
* Dualshock4 - 'HIDP: Handshake: Unsupported request' after 'unplug virtual cable'.
From: simon @ 2014-01-17 5:09 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Frank Praznik
[-- Attachment #1: Type: text/plain, Size: 1706 bytes --]
Hi all,
Frank and I have been working on a kernel driver for the Dualshock 4 and
we're having problems with the BT connection (USB is OK).
When powered on the DS4 will initiate a connection to the PC once it knows
it's Bdaddr (via pairing), but will power off shortly there after. This
seems to be related to incorrect (??) data sent from Bluez, or if no hid
connection is made within a short window.
On the two systems I have tried:
1) Debian with bluez 4.99, I can 'hidd --connect xxx' to hold the
connection. I get joystick data but can't send data to DS4 to drive
ff/leds. (I am told that 'hidd' is deprated)
2) Fedora LiveCD with bluez 5.13. Connection is immediately terminated by
the controller. HCI log attached.
It appears that the controller is insisting on a encrypted link ('noenc'
and 'noauth' make no difference) and does not like the 'unplug virtual
cable' which is sent (presumably as the link is being brought down to
re-start encrypted).
In the BT HID 1.1 spec (page 48) it says
--
If the HIDVirtualCable SDP attribute is set to TRUE, then a Virtual Cable
is considered to be established after both the HID Control and HID
Interrupt L2CAP channels have been opened.
--
However the Fedora system does not have a copy of the SDP records for the
controller, it never pulled them and I have never managed to make a
connection long enough to read them.
My theory is that the controller is rejecting the 'Unplug Virtual Cable'
command as it shouldn't have been established, but I'm not really sure.
I managed use my Debian system to read SDP records but that barfs on
what's sent.... files also attached.
Does anyone have any suggestions on what I should try next?
Thanks, Simon
[-- Attachment #2: hcidump_records.txt.gz --]
[-- Type: application/gzip, Size: 16136 bytes --]
[-- Attachment #3: records.txt --]
[-- Type: text/plain, Size: 673 bytes --]
Service Name: Wireless Controller
Service Description: Game Controller
Service Provider: Sony Computer Entertainment
Service RecHandle: 0x10001
Service Class ID List:
"Human Interface Device" (0x1124)
Protocol Descriptor List:
"L2CAP" (0x0100)
PSM: 17
"HIDP" (0x0011)
Language Base Attr List:
code_ISO639: 0x656e
encoding: 0x6a
base_offset: 0x100
Profile Descriptor List:
"Human Interface Device" (0x1124)
Version: 0x0100
Service RecHandle: 0x10002
Service Class ID List:
"PnP Information" (0x1200)
Protocol Descriptor List:
"L2CAP" (0x0100)
PSM: 1
"SDP" (0x0001)
Profile Descriptor List:
"PnP Information" (0x1200)
Version: 0x0103
[-- Attachment #4: records_raw.txt --]
[-- Type: text/plain, Size: 2851 bytes --]
Sequence
Attribute 0x0000 - ServiceRecordHandle
UINT32 0x00010001
Attribute 0x0001 - ServiceClassIDList
Sequence
UUID16 0x1124 - HumanInterfaceDeviceService (HID)
Attribute 0x0004 - ProtocolDescriptorList
Sequence
Sequence
UUID16 0x0100 - L2CAP
UINT16 0x0011
Sequence
UUID16 0x0011 - HIDP
Attribute 0x0006 - LanguageBaseAttributeIDList
Sequence
UINT16 0x656e
UINT16 0x006a
UINT16 0x0100
Attribute 0x0009 - BluetoothProfileDescriptorList
Sequence
Sequence
UUID16 0x1124 - HumanInterfaceDeviceService (HID)
UINT16 0x0100
Attribute 0x000d - AdditionalProtocolDescriptorLists
Sequence
Sequence
Sequence
UUID16 0x0100 - L2CAP
UINT16 0x0013
Sequence
UUID16 0x0011 - HIDP
Attribute 0x0100
String Wireless Controller\0
Attribute 0x0101
String Game Controller\0
Attribute 0x0102
String Sony Computer Entertainment\0
Attribute 0x0200
UINT16 0x0100
Attribute 0x0201
UINT16 0x0111
Attribute 0x0202
UINT8 0x08
Attribute 0x0203
UINT8 0x00
Attribute 0x0204
Bool False
Attribute 0x0205
Bool True
Attribute 0x0206
Sequence
Sequence
UINT8 0x22
Data 05 01 09 05 a1 01 85 01 09 30 09 31 09 32 09 35 15 00 26 ff 00 75 08 95 04 81 02 09 39 15 00 25 07 75 04 95 01 81 42 05 09 19 01 29 0e 15 00 25 01 75 01 95 0e 81 02 75 06 95 01 81 01 05 01 09 33 09 34 15 00 26 ff 00 75 08 95 02 81 02 06 04 ff 85 02 09 24 95 24 b1 02 85 a3 09 25 95 30 b1 02 85 05 09 26 95 28 b1 02 85 06 09 27 95 34 b1 02 85 07 09 28 95 30 b1 02 85 08 09 29 95 2f b1 02 06 03 ff 85 03 09 21 95 26 b1 02 85 04 09 22 95 2e b1 02 85 f0 09 47 95 3f b1 02 85 f1 09 48 95 3f b1 02 85 f2 09 49 95 0f b1 02 06 00 ff 85 11 09 20 15 00 26 ff 00 75 08 95 4d 81 02 09 21 91 02 85 12 09 22 95 8d 81 02 09 23 91 02 85 13 09 24 95 cd 81 02 09 25 91 02 85 14 09 26 96 0d 01 81 02 09 27 91 02 85 15 09 28 96 4d 01 81 02 09 29 91 02 85 16 09 2a 96 8d 01 81 02 09 2b 91 02 85 17 09 2c 96 cd 01 81 02 09 2d 91 02 85 18 09 2e 96 0d 02 81 02 09 2f 91 02 85 19 09 30 96 22 02 81 02 09 31 91 02 06 80 ff 85 82 09 22 95 3f b1 02 85 83 09 23 b1 02 85 84 09 24 b1 02 85 90 09 30 b1 02 85 91 09 31 b1 02 85 92 09 32 b1 02 85 93 09 33 b1 02 85 a0 09 40 b1 02 85 a4 09 44 b1 02 c0 00
Sequence
Attribute 0x0000 - ServiceRecordHandle
UINT32 0x00010002
Attribute 0x0001 - ServiceClassIDList
Sequence
UUID16 0x1200 - PnPInformation
Attribute 0x0004 - ProtocolDescriptorList
Sequence
Sequence
UUID16 0x0100 - L2CAP
UINT16 0x0001
Sequence
UUID16 0x0001 - SDP
Attribute 0x0009 - BluetoothProfileDescriptorList
Sequence
Sequence
UUID16 0x1200 - PnPInformation
UINT16 0x0103
Attribute 0x0200
UINT16 0x0103
Attribute 0x0201
UINT16 0x054c
Attribute 0x0202
UINT16 0x05c4
Attribute 0x0203
UINT16 0x0100
Attribute 0x0204
Bool True
Attribute 0x0205
UINT16 0x0002
[-- Attachment #5: connect_ds4_fedora_4.txt --]
[-- Type: text/plain, Size: 10027 bytes --]
HCI sniffer - Bluetooth packet analyzer ver 5.12
device: hci0 snap_len: 1500 filter: 0xffffffff
> HCI Event: Connect Request (0x04) plen 10
bdaddr 1C:66:6D:07:C3:E0 class 0x002508 type ACL
< HCI Command: Accept Connection Request (0x01|0x0009) plen 7
bdaddr 1C:66:6D:07:C3:E0 role 0x00
Role: Master
> HCI Event: Command Status (0x0f) plen 4
Accept Connection Request (0x01|0x0009) status 0x00 ncmd 1
> HCI Event: Role Change (0x12) plen 8
status 0x00 bdaddr 1C:66:6D:07:C3:E0 role 0x00
Role: Master
> HCI Event: Connect Complete (0x03) plen 11
status 0x00 handle 41 bdaddr 1C:66:6D:07:C3:E0 type ACL encrypt 0x00
< HCI Command: Read Remote Supported Features (0x01|0x001b) plen 2
handle 41
> HCI Event: Page Scan Repetition Mode Change (0x20) plen 7
bdaddr 1C:66:6D:07:C3:E0 mode 1
> HCI Event: Command Status (0x0f) plen 4
Read Remote Supported Features (0x01|0x001b) status 0x00 ncmd 0
> HCI Event: Max Slots Change (0x1b) plen 3
handle 41 slots 5
> HCI Event: Command Status (0x0f) plen 4
Unknown (0x00|0x0000) status 0x00 ncmd 1
< HCI Command: Change Connection Packet Type (0x01|0x000f) plen 4
handle 41 ptype 0xcc18
Packet type: DM1 DM3 DM5 DH1 DH3 DH5
> HCI Event: Command Status (0x0f) plen 4
Change Connection Packet Type (0x01|0x000f) status 0x00 ncmd 1
> HCI Event: Connection Packet Type Changed (0x1d) plen 5
status 0x00 handle 41 ptype 0xcc18
Packet type: DM1 DM3 DM5 DH1 DH3 DH5
> HCI Event: Read Remote Supported Features (0x0b) plen 11
status 0x00 handle 41
Features: 0xff 0xfe 0x0d 0xfe 0x98 0x7f 0x79 0x87
< HCI Command: Remote Name Request (0x01|0x0019) plen 10
bdaddr 1C:66:6D:07:C3:E0 mode 2 clkoffset 0x0000
> HCI Event: Command Status (0x0f) plen 4
Remote Name Request (0x01|0x0019) status 0x00 ncmd 1
> ACL data: handle 41 flags 0x02 dlen 12
L2CAP(s): Connect req: psm 1 scid 0x0040
< ACL data: handle 41 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 1 status 0
Connection pending - No futher information available
< ACL data: handle 41 flags 0x02 dlen 10
L2CAP(s): Info req: type 2
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 16
L2CAP(s): Info rsp: type 2 result 0
Extended feature mask 0x0000
< ACL data: handle 41 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0
Connection successful
< ACL data: handle 41 flags 0x02 dlen 12
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 0
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 16
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 4
MTU 672
< ACL data: handle 41 flags 0x02 dlen 18
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 4
MTU 672
> ACL data: handle 41 flags 0x02 dlen 18
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 4
MTU 672
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 24
L2CAP(d): cid 0x0040 len 20 [psm 1]
SDP SSA Req: tid 0x1 len 0xf
pat uuid-16 0x0100 (L2CAP)
max 2048
aid(s) 0x0000 - 0xffff
cont 00
< ACL data: handle 41 flags 0x02 dlen 192
< ACL data: handle 41 flags 0x01 dlen 192
< ACL data: handle 41 flags 0x01 dlen 93
L2CAP(d): cid 0x0040 len 473 [psm 1]
SDP SSA Rsp: tid 0x1 len 0x1d4
count 465
record #0
aid 0x0000 (SrvRecHndl)
uint 0x10001
aid 0x0001 (SrvClassIDList)
< uuid-16 0x1800 >
aid 0x0004 (ProtocolDescList)
< < uuid-16 0x0100 (L2CAP) uint 0x1f > <
uuid-16 0x0007 uint 0x1 uint 0x8 > >
aid 0x0005 (BrwGrpList)
< uuid-16 0x1002 (PubBrwsGrp) >
aid 0x000a (DocURL)
url "http://www.bluez.org/"
aid 0x000b (ClientExeURL)
url "http://www.bluez.org/"
aid 0x000c (IconURL)
url "http://www.bluez.org/"
aid 0x0100 (SrvName)
str "Generic Access Profile"
aid 0x0102 (ProviderName)
str "BlueZ"
record #1
aid 0x0000 (SrvRecHndl)
uint 0x10002
aid 0x0001 (SrvClassIDList)
< uuid-16 0x1801 >
aid 0x0004 (ProtocolDescList)
< < uuid-16 0x0100 (L2CAP) uint 0x1f > <
uuid-16 0x0007 uint 0x10 uint 0x10 > >
aid 0x0005 (BrwGrpList)
< uuid-16 0x1002 (PubBrwsGrp) >
aid 0x0100 (SrvName)
str "Generic Attribute Profile"
aid 0x0102 (ProviderName)
str "BlueZ"
record #2
aid 0x0000 (SrvRecHndl)
uint 0x10003
aid 0x0001 (SrvClassIDList)
< uuid-16 0x110e (AVRemote) uuid-16 0x110f (AVRemCt) >
aid 0x0004 (ProtocolDescList)
< < uuid-16 0x0100 (L2CAP) uint 0x17 > <
uuid-16 0x0017 (AVCTP) uint 0x103 > >
aid 0x0005 (BrwGrpList)
< uuid-16 0x1002 (PubBrwsGrp) >
aid 0x0009 (BTProfileDescList)
< < uuid-16 0x110e (AVRemote) uint 0x105 > >
aid 0x000d (AdditionalProtocolDescLists)
< < < uuid-16 0x0100 (L2CAP) uint 0x1b > < uuid-16 0x0017 (AVCTP) uint 0x103 > > >
aid 0x0100 (SrvName)
str "AVRCP CT"
aid 0x0311 (SuppFeatures)
uint 0x4f
record #3
aid 0x0000 (SrvRecHndl)
uint 0x10004
aid 0x0001 (SrvClassIDList)
< uuid-16 0x110c (AVRemTarget) >
aid 0x0004 (ProtocolDescList)
< < uuid-16 0x0100 (L2CAP) uint 0x17 > <
uuid-16 0x0017 (AVCTP) uint 0x103 > >
aid 0x0005 (BrwGrpList)
< uuid-16 0x1002 (PubBrwsGrp) >
aid 0x0009 (BTProfileDescList)
< < uuid-16 0x110e (AVRemote) uint 0x104 > >
aid 0x000d (AdditionalProtocolDescLists)
< < < uuid-16 0x0100 (L2CAP) uint 0x1b > < uuid-16 0x0017 (AVCTP) uint 0x103 > > >
aid 0x0100 (SrvName)
str "AVRCP TG"
aid 0x0311 (SuppFeatures)
uint 0x5f
cont 00
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 2
> ACL data: handle 41 flags 0x02 dlen 12
L2CAP(s): Disconn req: dcid 0x0040 scid 0x0040
< ACL data: handle 41 flags 0x02 dlen 12
L2CAP(s): Disconn rsp: dcid 0x0040 scid 0x0040
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> HCI Event: Remote Name Req Complete (0x07) plen 255
status 0x00 bdaddr 1C:66:6D:07:C3:E0 name 'Wireless Controller'
> HCI Event: Link Key Request (0x17) plen 6
bdaddr 1C:66:6D:07:C3:E0
< HCI Command: Link Key Request Reply (0x01|0x000b) plen 22
bdaddr 1C:66:6D:07:C3:E0 key DCD82F85FA8F56D21A7AFBF7B227F7E9
> HCI Event: Command Complete (0x0e) plen 10
Link Key Request Reply (0x01|0x000b) ncmd 1
status 0x00 bdaddr 1C:66:6D:07:C3:E0
> HCI Event: Encrypt Change (0x08) plen 4 <=============================
status 0x00 handle 41 encrypt 0x01
> ACL data: handle 41 flags 0x02 dlen 12
L2CAP(s): Connect req: psm 17 scid 0x0041
< ACL data: handle 41 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0041 result 0 status 0
Connection successful
< ACL data: handle 41 flags 0x02 dlen 12
L2CAP(s): Config req: dcid 0x0041 flags 0x00 clen 0
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 16
L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 4
MTU 672
< ACL data: handle 41 flags 0x02 dlen 18
L2CAP(s): Config rsp: scid 0x0041 flags 0x00 result 0 clen 4
MTU 672
> ACL data: handle 41 flags 0x02 dlen 18
L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 4
MTU 672
< ACL data: handle 41 flags 0x02 dlen 5
L2CAP(d): cid 0x0041 len 1 [psm 17]
HIDP: Control: Virtual cable unplug <======================
< ACL data: handle 41 flags 0x02 dlen 12
L2CAP(s): Disconn req: dcid 0x0041 scid 0x0040
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 12
L2CAP(s): Connect req: psm 19 scid 0x0042
< ACL data: handle 41 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0041 scid 0x0042 result 1 status 2
Connection pending - Authorization pending <======================
< ACL data: handle 41 flags 0x02 dlen 16
L2CAP(s): Connect rsp: dcid 0x0041 scid 0x0042 result 3 status 0
Connection refused - security block <=====================
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 5
L2CAP(d): cid 0x0040 len 1 [psm 17] <====================
HIDP: Handshake: Unsupported request
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> ACL data: handle 41 flags 0x02 dlen 12
L2CAP(s): Disconn rsp: dcid 0x0041 scid 0x0040
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> HCI Event: Number of Completed Packets (0x13) plen 5
handle 41 packets 1
> HCI Event: Disconn Complete (0x05) plen 4
status 0x00 handle 41 reason 0x13
Reason: Remote User Terminated Connection <====================
[-- Attachment #6: info --]
[-- Type: application/octet-stream, Size: 157 bytes --]
[General]
Name=
Class=0x002508
SupportedTechnologies=BR/EDR
Trusted=false
Blocked=false
[LinkKey]
Key=0xDCD82F85FA8F56D21A7AFBF7B227F7E9
Type=0
PINLength=4
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox