* Compilation using clang
From: Donald Carr @ 2014-01-10 17:57 UTC (permalink / raw)
To: linux-bluetooth
Top of the morning,
qtconnectivity currently contains a kludge which is required due to
the use of the GCC typeof extension in bluetooth.h which is breaking
with Clang compilation against the cxx11 spec.
The kludge in question involves molesting the standard (eg -std=cxx11)
specified to the compiler (explicitly away from cxx11 toward g++0x)
and hence I am hoping to be able to murder its intrusive self. I am
not entirely clear on the nuances though:
This is the offending section:
#define bt_get_unaligned(ptr) \
({ \
struct __attribute__((packed)) { \
typeof(*(ptr)) __v; \
} *__p = (typeof(__p)) (ptr); \
__p->__v; \
})
#define bt_put_unaligned(val, ptr) \
do { \
struct __attribute__((packed)) { \
typeof(*(ptr)) __v; \
} *__p = (typeof(__p)) (ptr); \
__p->__v = (val); \
} while(0)
and changing typeof to __typeof__ has been verified to succeed in the
stated circumstance. I don't know whether this is the ideal solution,
or whether decltype or so other designator would be a closer/better
match.
Any feedback would be greatly appreciated, I am using this change
locally and I think it could be of broader use to people.
Yours sincerely,
Donald Carr
--
-------------------------------
°v° Donald Carr
/(_)\ Vaguely Professional Penguin lover
^ ^
Cave canem, te necet lingendo
^ permalink raw reply
* Re: [PATCH v4 01/15] Bluetooth: Add LMP feature definitions for Secure Connections support
From: Johan Hedberg @ 2014-01-10 19:50 UTC (permalink / raw)
To: Marcel Holtmann; +Cc: linux-bluetooth
In-Reply-To: <1389348450-23303-1-git-send-email-marcel@holtmann.org>
Hi Marcel,
On Fri, Jan 10, 2014, Marcel Holtmann wrote:
> The support for Secure Connections introduces two new controller
> features and one new host feature.
>
> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
> ---
> include/net/bluetooth/hci.h | 4 ++++
> include/net/bluetooth/hci_core.h | 3 +++
> 2 files changed, 7 insertions(+)
All patches in this set have been applied to bluetooth-next. Thanks.
Johan
^ permalink raw reply
* Re: Compilation using clang
From: Vinicius Costa Gomes @ 2014-01-10 20:06 UTC (permalink / raw)
To: Donald Carr; +Cc: linux-bluetooth
In-Reply-To: <CAOVeLGSGAf9LmsEcaAiECh+zL-7bg-gZJnue4TqzbBCNFfMj3w@mail.gmail.com>
Hi Donald,
On 09:57 Fri 10 Jan, Donald Carr wrote:
> Top of the morning,
>
> qtconnectivity currently contains a kludge which is required due to
> the use of the GCC typeof extension in bluetooth.h which is breaking
> with Clang compilation against the cxx11 spec.
Another kludge that I have seen is putting a
"#define typeof(x) __tupeof__(x)" before including bluetooth.h.
>
> The kludge in question involves molesting the standard (eg -std=cxx11)
> specified to the compiler (explicitly away from cxx11 toward g++0x)
> and hence I am hoping to be able to murder its intrusive self. I am
> not entirely clear on the nuances though:
>
> This is the offending section:
>
> #define bt_get_unaligned(ptr) \
> ({ \
> struct __attribute__((packed)) { \
> typeof(*(ptr)) __v; \
> } *__p = (typeof(__p)) (ptr); \
> __p->__v; \
> })
>
> #define bt_put_unaligned(val, ptr) \
> do { \
> struct __attribute__((packed)) { \
> typeof(*(ptr)) __v; \
> } *__p = (typeof(__p)) (ptr); \
> __p->__v = (val); \
> } while(0)
>
> and changing typeof to __typeof__ has been verified to succeed in the
> stated circumstance. I don't know whether this is the ideal solution,
> or whether decltype or so other designator would be a closer/better
> match.
I would vote for changing it to __typeof__.
>
> Any feedback would be greatly appreciated, I am using this change
> locally and I think it could be of broader use to people.
Please send a patch and let's see what others think.
>
> Yours sincerely,
> Donald Carr
>
>
> --
> -------------------------------
> °v° Donald Carr
> /(_)\ Vaguely Professional Penguin lover
> ^ ^
>
> Cave canem, te necet lingendo
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Cheers,
--
Vinicius
^ permalink raw reply
* Re: [PATCH v5] Bluetooth: Add hci_h4p driver
From: Pavel Machek @ 2014-01-11 0:19 UTC (permalink / raw)
To: Joe Perches
Cc: Marcel Holtmann, Pali Rohár,
Ивайло Димитров,
Gustavo F. Padovan, Johan Hedberg, linux-kernel,
linux-bluetooth@vger.kernel.org development, Ville Tervo,
Sebastian Reichel
In-Reply-To: <1389375184.2537.31.camel@joe-AO722>
Hi!
> > +static inline void hci_h4p_handle_byte(struct hci_h4p_info *info, u8 byte)
>
> pretty big function to be inline
Called from just one place, so that should be ok.
> > +static void hci_h4p_rx_tasklet(unsigned long data)
> > +{
> []
> > + while (hci_h4p_inb(info, UART_LSR) & UART_LSR_DR) {
> []
> > + pr_debug("0x%.2x ", byte);
>
> pr_debug is prefixed by a newline if necessary
> and then <7>, one for each use.
>
> This will produce a lot of dmesg output lines
> (1 for each byte) and isn't in my opinion
> necessary/useful.
Ok, I just killed it.
> > + 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);
>
> missing newline
Actually, it is the other way around. BT_DBG adds the newline. I'll
remove it from the rest.
> > + if (!hdev) {
> > + printk(KERN_WARNING "hci_h4p: Frame for unknown device\n");
> > + return -ENODEV;
> > + }
>
> Is this possible?
Probably not, removed.
> > + if (ret != 6)
> > + return -EINVAL;
> > +
> > + for (i = 0; i < 6; i++)
> > + info->bd_addr[i] = bdaddr[i] & 0xff;
>
> This could also return -EINVAL if bdaddr[i] > 0xff
Why not.
> > +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;
> > + }
> > + }
>
> This seems wrong as addresses can have valid 0 bytes.
> Perhaps use:
>
> if (!is_valid_ether_addr(info->bd_addr))
>
I am not sure bluetooth rules are same as ethernet. And notice that it
only errors out on 00:00:00:00:00:00 which seems like invalid address
to me.
I fixed the other ones.
Thanks,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply
* [PATCH v6] Bluetooth: Add hci_h4p driver
From: Pavel Machek @ 2014-01-11 0:28 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: <20140110145207.GB12048@amd.pavel.ucw.cz>
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.
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 11a6104..a53e8c7 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -241,5 +241,15 @@ config BT_WILINK
core driver to communicate with the BT core of the combo chip.
Say Y here to compile support for Texas Instrument's WiLink7 driver
- into the kernel or say M to compile it as module.
+ into the kernel or say M to compile it as module (btwilink).
+
+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).
endmenu
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 9fe8a87..6f25db2 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -31,4 +31,8 @@ hci_uart-$(CONFIG_BT_HCIUART_ATH3K) += hci_ath.o
hci_uart-$(CONFIG_BT_HCIUART_3WIRE) += hci_h5.o
hci_uart-objs := $(hci_uart-y)
+obj-$(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/bluetooth/hci_h4p.h b/drivers/bluetooth/hci_h4p.h
new file mode 100644
index 0000000..fd7a640
--- /dev/null
+++ b/drivers/bluetooth/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/bluetooth/nokia_core.c b/drivers/bluetooth/nokia_core.c
new file mode 100644
index 0000000..54ec594
--- /dev/null
+++ b/drivers/bluetooth/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/hci-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/bluetooth/nokia_fw-bcm.c b/drivers/bluetooth/nokia_fw-bcm.c
new file mode 100644
index 0000000..e8912bf
--- /dev/null
+++ b/drivers/bluetooth/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/bluetooth/nokia_fw-csr.c b/drivers/bluetooth/nokia_fw-csr.c
new file mode 100644
index 0000000..e39c4a3
--- /dev/null
+++ b/drivers/bluetooth/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/bluetooth/nokia_fw-ti1273.c b/drivers/bluetooth/nokia_fw-ti1273.c
new file mode 100644
index 0000000..f5500f7
--- /dev/null
+++ b/drivers/bluetooth/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/bluetooth/nokia_fw.c b/drivers/bluetooth/nokia_fw.c
new file mode 100644
index 0000000..cfea61c
--- /dev/null
+++ b/drivers/bluetooth/nokia_fw.c
@@ -0,0 +1,195 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation.
+ *
+ * Contact: Ville Tervo <ville.tervo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/firmware.h>
+#include <linux/clk.h>
+
+#include <net/bluetooth/bluetooth.h>
+
+#include "hci_h4p.h"
+
+static int fw_pos;
+
+/* Firmware handling */
+static int hci_h4p_open_firmware(struct hci_h4p_info *info,
+ const struct firmware **fw_entry)
+{
+ int err;
+
+ fw_pos = 0;
+ 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/bluetooth/nokia_uart.c b/drivers/bluetooth/nokia_uart.c
new file mode 100644
index 0000000..0fb57de
--- /dev/null
+++ b/drivers/bluetooth/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/hci-h4p.h b/include/linux/platform_data/hci-h4p.h
new file mode 100644
index 0000000..30d169d
--- /dev/null
+++ b/include/linux/platform_data/hci-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
* [PATCH BlueZ] shared: Fix crash if adapter is removed before mgmt event is received
From: Anderson Lizardo @ 2014-01-11 2:54 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
If "Index Removed" mgmt event is received after a mgmt command was sent
by userspace, but before its Command Status/Complete event is received,
bluetoothd will eventually call mgmt_cancel_index(), which will destroy
the queue of pending commands. By the time request_complete() is called,
the request callback is no more valid, because the destroy callback was
already called.
Therefore, the fix is to simply ignore the event.
Valgrind output:
==3676== Invalid read of size 4
==3676== at 0x80BCD07: request_complete (mgmt.c:239)
==3676== by 0x80BCF72: can_read_data (mgmt.c:350)
==3676== by 0x80BBE22: read_callback (io-glib.c:164)
==3676== by 0x40C019D: g_io_unix_dispatch (giounix.c:166)
==3676== by 0x407FD45: g_main_context_dispatch (gmain.c:2539)
==3676== by 0x40800E4: g_main_context_iterate.isra.21 (gmain.c:3146)
==3676== by 0x408052A: g_main_loop_run (gmain.c:3340)
==3676== by 0x41BE4D2: (below main) (libc-start.c:226)
==3676== Address 0x10 is not stack'd, malloc'd or (recently) free'd
---
src/shared/mgmt.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/shared/mgmt.c b/src/shared/mgmt.c
index a391ab5..a4ee224 100644
--- a/src/shared/mgmt.c
+++ b/src/shared/mgmt.c
@@ -235,6 +235,8 @@ static void request_complete(struct mgmt *mgmt, uint8_t status,
request = queue_remove_if(mgmt->pending_list,
match_request_opcode_index, &match);
+ if (!request)
+ goto done;
if (request->callback)
request->callback(status, length, param, request->user_data);
@@ -244,6 +246,7 @@ static void request_complete(struct mgmt *mgmt, uint8_t status,
if (mgmt->destroyed)
return;
+done:
wakeup_writer(mgmt);
}
--
1.8.3.2
^ permalink raw reply related
* Re: [PATCH BlueZ] shared: Fix crash if adapter is removed before mgmt event is received
From: Marcel Holtmann @ 2014-01-11 3:51 UTC (permalink / raw)
To: Anderson Lizardo; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <1389408885-25395-1-git-send-email-anderson.lizardo@openbossa.org>
Hi Anderson,
> If "Index Removed" mgmt event is received after a mgmt command was sent
> by userspace, but before its Command Status/Complete event is received,
> bluetoothd will eventually call mgmt_cancel_index(), which will destroy
> the queue of pending commands. By the time request_complete() is called,
> the request callback is no more valid, because the destroy callback was
> already called.
>
> Therefore, the fix is to simply ignore the event.
>
> Valgrind output:
>
> ==3676== Invalid read of size 4
> ==3676== at 0x80BCD07: request_complete (mgmt.c:239)
> ==3676== by 0x80BCF72: can_read_data (mgmt.c:350)
> ==3676== by 0x80BBE22: read_callback (io-glib.c:164)
> ==3676== by 0x40C019D: g_io_unix_dispatch (giounix.c:166)
> ==3676== by 0x407FD45: g_main_context_dispatch (gmain.c:2539)
> ==3676== by 0x40800E4: g_main_context_iterate.isra.21 (gmain.c:3146)
> ==3676== by 0x408052A: g_main_loop_run (gmain.c:3340)
> ==3676== by 0x41BE4D2: (below main) (libc-start.c:226)
> ==3676== Address 0x10 is not stack'd, malloc'd or (recently) free'd
> ---
> src/shared/mgmt.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/src/shared/mgmt.c b/src/shared/mgmt.c
> index a391ab5..a4ee224 100644
> --- a/src/shared/mgmt.c
> +++ b/src/shared/mgmt.c
> @@ -235,6 +235,8 @@ static void request_complete(struct mgmt *mgmt, uint8_t status,
>
> request = queue_remove_if(mgmt->pending_list,
> match_request_opcode_index, &match);
> + if (!request)
> + goto done;
>
> if (request->callback)
> request->callback(status, length, param, request->user_data);
> @@ -244,6 +246,7 @@ static void request_complete(struct mgmt *mgmt, uint8_t status,
you might want to put the done: label here.
> if (mgmt->destroyed)
> return;
>
> +done:
> wakeup_writer(mgmt);
> }
Actually, I fixed this without using a label.
Regards
Marcel
^ permalink raw reply
* [PATCH BlueZ 01/11] attrib: Modify gatt_cb_t signature
From: Anderson Lizardo @ 2014-01-11 4:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
Use standard C types instead of GLib ones (which are unnecessary here)
and move the "status" parameter to the first position, so it is
consistent with other callbacks.
---
attrib/gatt.c | 10 +++++-----
attrib/gatt.h | 2 +-
attrib/gatttool.c | 9 ++++-----
attrib/interactive.c | 9 ++++-----
profiles/cyclingspeed/cyclingspeed.c | 2 +-
profiles/deviceinfo/deviceinfo.c | 4 ++--
profiles/gatt/gas.c | 4 ++--
profiles/heartrate/heartrate.c | 2 +-
profiles/input/hog.c | 6 +++---
profiles/proximity/monitor.c | 12 ++++++------
profiles/scanparam/scan.c | 7 +++----
profiles/thermometer/thermometer.c | 4 ++--
src/device.c | 5 ++---
13 files changed, 36 insertions(+), 40 deletions(-)
diff --git a/attrib/gatt.c b/attrib/gatt.c
index bb2cae1..9ed588b 100644
--- a/attrib/gatt.c
+++ b/attrib/gatt.c
@@ -90,9 +90,9 @@ static void isd_unref(struct included_discovery *isd)
return;
if (isd->err)
- isd->cb(NULL, isd->err, isd->user_data);
+ isd->cb(isd->err, NULL, isd->user_data);
else
- isd->cb(isd->includes, isd->err, isd->user_data);
+ isd->cb(isd->err, isd->includes, isd->user_data);
g_slist_free_full(isd->includes, g_free);
g_attrib_unref(isd->attrib);
@@ -183,7 +183,7 @@ static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu,
return;
done:
- dp->cb(dp->primaries, err, dp->user_data);
+ dp->cb(err, dp->primaries, dp->user_data);
discover_primary_free(dp);
}
@@ -252,7 +252,7 @@ static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
}
done:
- dp->cb(dp->primaries, err, dp->user_data);
+ dp->cb(err, dp->primaries, dp->user_data);
discover_primary_free(dp);
}
@@ -517,7 +517,7 @@ static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
done:
err = (dc->characteristics ? 0 : err);
- dc->cb(dc->characteristics, err, dc->user_data);
+ dc->cb(err, dc->characteristics, dc->user_data);
discover_char_free(dc);
}
diff --git a/attrib/gatt.h b/attrib/gatt.h
index e5abd85..0f113e7 100644
--- a/attrib/gatt.h
+++ b/attrib/gatt.h
@@ -53,7 +53,7 @@
#define GATT_CLIENT_CHARAC_CFG_NOTIF_BIT 0x0001
#define GATT_CLIENT_CHARAC_CFG_IND_BIT 0x0002
-typedef void (*gatt_cb_t) (GSList *l, guint8 status, gpointer user_data);
+typedef void (*gatt_cb_t) (uint8_t status, GSList *l, void *user_data);
struct gatt_primary {
char uuid[MAX_LEN_UUID_STR + 1];
diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index f211dcd..ebc8123 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -137,7 +137,7 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
operation(attrib);
}
-static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
+static void primary_all_cb(uint8_t status, GSList *services, void *user_data)
{
GSList *l;
@@ -157,8 +157,7 @@ done:
g_main_loop_quit(event_loop);
}
-static void primary_by_uuid_cb(GSList *ranges, guint8 status,
- gpointer user_data)
+static void primary_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data)
{
GSList *l;
@@ -191,8 +190,8 @@ static gboolean primary(gpointer user_data)
return FALSE;
}
-static void char_discovered_cb(GSList *characteristics, guint8 status,
- gpointer user_data)
+static void char_discovered_cb(uint8_t status, GSList *characteristics,
+ void *user_data)
{
GSList *l;
diff --git a/attrib/interactive.c b/attrib/interactive.c
index 5bd27af..9826a4b 100644
--- a/attrib/interactive.c
+++ b/attrib/interactive.c
@@ -180,7 +180,7 @@ static void disconnect_io()
set_state(STATE_DISCONNECTED);
}
-static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
+static void primary_all_cb(uint8_t status, GSList *services, void *user_data)
{
GSList *l;
@@ -202,8 +202,7 @@ static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
}
}
-static void primary_by_uuid_cb(GSList *ranges, guint8 status,
- gpointer user_data)
+static void primary_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data)
{
GSList *l;
@@ -225,7 +224,7 @@ static void primary_by_uuid_cb(GSList *ranges, guint8 status,
}
}
-static void included_cb(GSList *includes, guint8 status, gpointer user_data)
+static void included_cb(uint8_t status, GSList *includes, void *user_data)
{
GSList *l;
@@ -249,7 +248,7 @@ static void included_cb(GSList *includes, guint8 status, gpointer user_data)
}
}
-static void char_cb(GSList *characteristics, guint8 status, gpointer user_data)
+static void char_cb(uint8_t status, GSList *characteristics, void *user_data)
{
GSList *l;
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
index 6ecc985..b6d881a 100644
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -759,7 +759,7 @@ done:
g_attrib_send(csc->attrib, 0, opdu, olen, NULL, NULL, NULL);
}
-static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
+static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
{
struct csc *csc = user_data;
uint16_t feature_val_handle = 0;
diff --git a/profiles/deviceinfo/deviceinfo.c b/profiles/deviceinfo/deviceinfo.c
index 8343bb5..bfecfc2 100644
--- a/profiles/deviceinfo/deviceinfo.c
+++ b/profiles/deviceinfo/deviceinfo.c
@@ -107,8 +107,8 @@ static void process_deviceinfo_char(struct characteristic *ch)
read_pnpid_cb, ch);
}
-static void configure_deviceinfo_cb(GSList *characteristics, guint8 status,
- gpointer user_data)
+static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics,
+ void *user_data)
{
struct deviceinfo *d = user_data;
GSList *l;
diff --git a/profiles/gatt/gas.c b/profiles/gatt/gas.c
index 5d6880f..de07464 100644
--- a/profiles/gatt/gas.c
+++ b/profiles/gatt/gas.c
@@ -271,8 +271,8 @@ done:
att_data_list_free(list);
}
-static void gatt_characteristic_cb(GSList *characteristics, guint8 status,
- gpointer user_data)
+static void gatt_characteristic_cb(uint8_t status, GSList *characteristics,
+ void *user_data)
{
struct gas *gas = user_data;
struct gatt_char *chr;
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
index d87f0ff..3be8f9e 100644
--- a/profiles/heartrate/heartrate.c
+++ b/profiles/heartrate/heartrate.c
@@ -464,7 +464,7 @@ static void discover_measurement_ccc(struct heartrate *hr,
gatt_discover_char_desc(hr->attrib, start, end, discover_ccc_cb, hr);
}
-static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
+static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
{
struct heartrate *hr = user_data;
diff --git a/profiles/input/hog.c b/profiles/input/hog.c
index 4647fef..e357aa0 100644
--- a/profiles/input/hog.c
+++ b/profiles/input/hog.c
@@ -283,8 +283,8 @@ static void discover_descriptor(GAttrib *attrib, uint16_t start, uint16_t end,
ddcb_data);
}
-static void external_service_char_cb(GSList *chars, guint8 status,
- gpointer user_data)
+static void external_service_char_cb(uint8_t status, GSList *chars,
+ void *user_data)
{
struct hog_device *hogdev = user_data;
struct gatt_primary *prim = hogdev->hog_primary;
@@ -463,7 +463,7 @@ static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
hogdev->id);
}
-static void char_discovered_cb(GSList *chars, guint8 status, gpointer user_data)
+static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)
{
struct hog_device *hogdev = user_data;
struct gatt_primary *prim = hogdev->hog_primary;
diff --git a/profiles/proximity/monitor.c b/profiles/proximity/monitor.c
index eaa5b0d..e78bfc3 100644
--- a/profiles/proximity/monitor.c
+++ b/profiles/proximity/monitor.c
@@ -190,8 +190,8 @@ static void linkloss_written(guint8 status, const guint8 *pdu, guint16 plen,
PROXIMITY_INTERFACE, "LinkLossAlertLevel");
}
-static void char_discovered_cb(GSList *characteristics, guint8 status,
- gpointer user_data)
+static void char_discovered_cb(uint8_t status, GSList *characteristics,
+ void *user_data)
{
struct monitor *monitor = user_data;
struct gatt_char *chr;
@@ -259,8 +259,8 @@ static void tx_power_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
DBG("Tx Power Level: %02x", (int8_t) value[0]);
}
-static void tx_power_handle_cb(GSList *characteristics, guint8 status,
- gpointer user_data)
+static void tx_power_handle_cb(uint8_t status, GSList *characteristics,
+ void *user_data)
{
struct monitor *monitor = user_data;
struct gatt_char *chr;
@@ -346,8 +346,8 @@ static void write_immediate_alert(struct monitor *monitor)
immediate_written, monitor);
}
-static void immediate_handle_cb(GSList *characteristics, guint8 status,
- gpointer user_data)
+static void immediate_handle_cb(uint8_t status, GSList *characteristics,
+ void *user_data)
{
struct monitor *monitor = user_data;
struct gatt_char *chr;
diff --git a/profiles/scanparam/scan.c b/profiles/scanparam/scan.c
index 0236af6..d3f3aa5 100644
--- a/profiles/scanparam/scan.c
+++ b/profiles/scanparam/scan.c
@@ -132,8 +132,8 @@ done:
att_data_list_free(list);
}
-static void refresh_discovered_cb(GSList *chars, guint8 status,
- gpointer user_data)
+static void refresh_discovered_cb(uint8_t status, GSList *chars,
+ void *user_data)
{
struct scan *scan = user_data;
struct gatt_char *chr;
@@ -165,8 +165,7 @@ static void refresh_discovered_cb(GSList *chars, guint8 status,
discover_descriptor_cb, user_data);
}
-static void iwin_discovered_cb(GSList *chars, guint8 status,
- gpointer user_data)
+static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data)
{
struct scan *scan = user_data;
struct gatt_char *chr;
diff --git a/profiles/thermometer/thermometer.c b/profiles/thermometer/thermometer.c
index bd8413d..85cca03 100644
--- a/profiles/thermometer/thermometer.c
+++ b/profiles/thermometer/thermometer.c
@@ -737,8 +737,8 @@ static void process_thermometer_char(struct thermometer *t,
}
}
-static void configure_thermometer_cb(GSList *characteristics, guint8 status,
- gpointer user_data)
+static void configure_thermometer_cb(uint8_t status, GSList *characteristics,
+ void *user_data)
{
struct thermometer *t = user_data;
GSList *l;
diff --git a/src/device.c b/src/device.c
index 6fec701..aa663f7 100644
--- a/src/device.c
+++ b/src/device.c
@@ -3151,8 +3151,7 @@ static int service_by_range_cmp(gconstpointer a, gconstpointer b)
return memcmp(&prim->range, range, sizeof(*range));
}
-static void find_included_cb(GSList *includes, uint8_t status,
- gpointer user_data)
+static void find_included_cb(uint8_t status, GSList *includes, void *user_data)
{
struct included_search *search = user_data;
struct btd_device *device = search->req->device;
@@ -3222,7 +3221,7 @@ static void find_included_services(struct browse_req *req, GSList *services)
find_included_cb, search);
}
-static void primary_cb(GSList *services, guint8 status, gpointer user_data)
+static void primary_cb(uint8_t status, GSList *services, void *user_data)
{
struct browse_req *req = user_data;
--
1.8.3.2
^ permalink raw reply related
* [PATCH BlueZ 02/11] attrib: Add missing g_error_free()
From: Anderson Lizardo @ 2014-01-11 4:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
In-Reply-To: <1389415647-25831-1-git-send-email-anderson.lizardo@openbossa.org>
Also print error message.
---
src/attrib-server.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/attrib-server.c b/src/attrib-server.c
index a7ee55d..fcc6601 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -944,10 +944,12 @@ static uint16_t mtu_exchange(struct gatt_channel *channel, uint16_t mtu,
io = g_attrib_get_channel(channel->attrib);
bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID);
-
- if (gerr)
- return enc_error_resp(ATT_OP_MTU_REQ, 0,
- ATT_ECODE_UNLIKELY, pdu, len);
+ if (gerr) {
+ error("bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ return enc_error_resp(ATT_OP_MTU_REQ, 0, ATT_ECODE_UNLIKELY,
+ pdu, len);
+ }
channel->mtu = MIN(mtu, imtu);
g_attrib_set_mtu(channel->attrib, channel->mtu);
--
1.8.3.2
^ permalink raw reply related
* [PATCH BlueZ 03/11] attrib: Use att_put_u16() instead of htobs() + memcpy()
From: Anderson Lizardo @ 2014-01-11 4:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
In-Reply-To: <1389415647-25831-1-git-send-email-anderson.lizardo@openbossa.org>
---
attrib/att.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/attrib/att.c b/attrib/att.c
index 753c753..d9ca709 100644
--- a/attrib/att.c
+++ b/attrib/att.c
@@ -723,15 +723,13 @@ uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status,
{
const uint16_t min_len = sizeof(pdu[0]) + sizeof(opcode) +
sizeof(handle) + sizeof(status);
- uint16_t u16;
if (len < min_len)
return 0;
- u16 = htobs(handle);
pdu[0] = ATT_OP_ERROR;
pdu[1] = opcode;
- memcpy(&pdu[2], &u16, sizeof(u16));
+ att_put_u16(handle, &pdu[2]);
pdu[4] = status;
return min_len;
--
1.8.3.2
^ permalink raw reply related
* [PATCH BlueZ 04/11] attrib: Remove unnecessary checks for PDU length on ATT encoding
From: Anderson Lizardo @ 2014-01-11 4:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
In-Reply-To: <1389415647-25831-1-git-send-email-anderson.lizardo@openbossa.org>
Both userspace and kernel enforce a minimum ATT MTU of 23 octets, which
is also used as minimum size for buffers passed to ATT encoding
functions. Therefore, it is unnecessary to perform these checks on ATT
requests that are guaranteed to fit into 23 octets.
Also document ATT parameter lengths where a constant is being used for
calculating the PDU length.
---
attrib/att.c | 131 +++++++++++++++++++++--------------------------------------
1 file changed, 46 insertions(+), 85 deletions(-)
diff --git a/attrib/att.c b/attrib/att.c
index d9ca709..a8f641a 100644
--- a/attrib/att.c
+++ b/attrib/att.c
@@ -123,29 +123,28 @@ struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len)
uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
uint8_t *pdu, size_t len)
{
- uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
- uint16_t length;
+ uint16_t uuid_len;
if (!uuid)
return 0;
if (uuid->type == BT_UUID16)
- length = 2;
+ uuid_len = 2;
else if (uuid->type == BT_UUID128)
- length = 16;
+ uuid_len = 16;
else
return 0;
- if (len < min_len + length)
- return 0;
-
+ /* Attribute Opcode (1 octet) */
pdu[0] = ATT_OP_READ_BY_GROUP_REQ;
+ /* Starting Handle (2 octets) */
att_put_u16(start, &pdu[1]);
+ /* Ending Handle (2 octets) */
att_put_u16(end, &pdu[3]);
-
+ /* Attribute Group Type (2 or 16 octet UUID) */
att_put_uuid(*uuid, &pdu[5]);
- return min_len + length;
+ return 5 + uuid_len;
}
uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start,
@@ -244,9 +243,6 @@ uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
if (uuid->type != BT_UUID16)
return 0;
- if (len < min_len)
- return 0;
-
if (vlen > len - min_len)
vlen = len - min_len;
@@ -309,7 +305,7 @@ uint16_t enc_find_by_type_resp(GSList *matches, uint8_t *pdu, size_t len)
GSList *l;
uint16_t offset;
- if (pdu == NULL || len < 5)
+ if (!pdu)
return 0;
pdu[0] = ATT_OP_FIND_BY_TYPE_RESP;
@@ -354,29 +350,28 @@ GSList *dec_find_by_type_resp(const uint8_t *pdu, size_t len)
uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
uint8_t *pdu, size_t len)
{
- uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
- uint16_t length;
+ uint16_t uuid_len;
if (!uuid)
return 0;
if (uuid->type == BT_UUID16)
- length = 2;
+ uuid_len = 2;
else if (uuid->type == BT_UUID128)
- length = 16;
+ uuid_len = 16;
else
return 0;
- if (len < min_len + length)
- return 0;
-
+ /* Attribute Opcode (1 octet) */
pdu[0] = ATT_OP_READ_BY_TYPE_REQ;
+ /* Starting Handle (2 octets) */
att_put_u16(start, &pdu[1]);
+ /* Ending Handle (2 octets) */
att_put_u16(end, &pdu[3]);
-
+ /* Attribute Type (2 or 16 octet UUID) */
att_put_uuid(*uuid, &pdu[5]);
- return min_len + length;
+ return 5 + uuid_len;
}
uint16_t dec_read_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start,
@@ -468,9 +463,6 @@ uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, size_t vlen,
if (pdu == NULL)
return 0;
- if (len < min_len)
- return 0;
-
if (vlen > len - min_len)
vlen = len - min_len;
@@ -517,9 +509,6 @@ uint16_t enc_write_req(uint16_t handle, const uint8_t *value, size_t vlen,
if (pdu == NULL)
return 0;
- if (len < min_len)
- return 0;
-
if (vlen > len - min_len)
vlen = len - min_len;
@@ -582,37 +571,31 @@ uint16_t dec_write_resp(const uint8_t *pdu, size_t len)
uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, size_t len)
{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
-
if (pdu == NULL)
return 0;
- if (len < min_len)
- return 0;
-
+ /* Attribute Opcode (1 octet) */
pdu[0] = ATT_OP_READ_REQ;
+ /* Attribute Handle (2 octets) */
att_put_u16(handle, &pdu[1]);
- return min_len;
+ return 3;
}
uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu,
size_t len)
{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) +
- sizeof(offset);
-
if (pdu == NULL)
return 0;
- if (len < min_len)
- return 0;
-
+ /* Attribute Opcode (1 octet) */
pdu[0] = ATT_OP_READ_BLOB_REQ;
+ /* Attribute Handle (2 octets) */
att_put_u16(handle, &pdu[1]);
+ /* Value Offset (2 octets) */
att_put_u16(offset, &pdu[3]);
- return min_len;
+ return 5;
}
uint16_t dec_read_req(const uint8_t *pdu, size_t len, uint16_t *handle)
@@ -721,36 +704,32 @@ ssize_t dec_read_resp(const uint8_t *pdu, size_t len, uint8_t *value,
uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status,
uint8_t *pdu, size_t len)
{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(opcode) +
- sizeof(handle) + sizeof(status);
-
- if (len < min_len)
- return 0;
-
+ /* Attribute Opcode (1 octet) */
pdu[0] = ATT_OP_ERROR;
+ /* Request Opcode In Error (1 octet) */
pdu[1] = opcode;
+ /* Attribute Handle In Error (2 octets) */
att_put_u16(handle, &pdu[2]);
+ /* Error Code (1 octet) */
pdu[4] = status;
- return min_len;
+ return 5;
}
uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu,
size_t len)
{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
-
if (pdu == NULL)
return 0;
- if (len < min_len)
- return 0;
-
+ /* Attribute Opcode (1 octet) */
pdu[0] = ATT_OP_FIND_INFO_REQ;
+ /* Starting Handle (2 octets) */
att_put_u16(start, &pdu[1]);
+ /* Ending Handle (2 octets) */
att_put_u16(end, &pdu[3]);
- return min_len;
+ return 5;
}
uint16_t dec_find_info_req(const uint8_t *pdu, size_t len, uint16_t *start,
@@ -907,33 +886,26 @@ uint16_t dec_indication(const uint8_t *pdu, size_t len, uint16_t *handle,
uint16_t enc_confirmation(uint8_t *pdu, size_t len)
{
- const uint16_t min_len = sizeof(pdu[0]);
-
if (pdu == NULL)
return 0;
- if (len < min_len)
- return 0;
-
+ /* Attribute Opcode (1 octet) */
pdu[0] = ATT_OP_HANDLE_CNF;
- return min_len;
+ return 1;
}
uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, size_t len)
{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu);
-
if (pdu == NULL)
return 0;
- if (len < min_len)
- return 0;
-
+ /* Attribute Opcode (1 octet) */
pdu[0] = ATT_OP_MTU_REQ;
+ /* Client Rx MTU (2 octets) */
att_put_u16(mtu, &pdu[1]);
- return min_len;
+ return 3;
}
uint16_t dec_mtu_req(const uint8_t *pdu, size_t len, uint16_t *mtu)
@@ -959,18 +931,15 @@ uint16_t dec_mtu_req(const uint8_t *pdu, size_t len, uint16_t *mtu)
uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, size_t len)
{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu);
-
if (pdu == NULL)
return 0;
- if (len < min_len)
- return 0;
-
+ /* Attribute Opcode (1 octet) */
pdu[0] = ATT_OP_MTU_RESP;
+ /* Server Rx MTU (2 octets) */
att_put_u16(mtu, &pdu[1]);
- return min_len;
+ return 3;
}
uint16_t dec_mtu_resp(const uint8_t *pdu, size_t len, uint16_t *mtu)
@@ -1004,9 +973,6 @@ uint16_t enc_prep_write_req(uint16_t handle, uint16_t offset,
if (pdu == NULL)
return 0;
- if (len < min_len)
- return 0;
-
if (vlen > len - min_len)
vlen = len - min_len;
@@ -1060,9 +1026,6 @@ uint16_t enc_prep_write_resp(uint16_t handle, uint16_t offset,
if (pdu == NULL)
return 0;
- if (len < min_len)
- return 0;
-
if (vlen > len - min_len)
vlen = len - min_len;
@@ -1107,21 +1070,18 @@ uint16_t dec_prep_write_resp(const uint8_t *pdu, size_t len, uint16_t *handle,
uint16_t enc_exec_write_req(uint8_t flags, uint8_t *pdu, size_t len)
{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(flags);
-
if (pdu == NULL)
return 0;
- if (len < min_len)
- return 0;
-
if (flags > 1)
return 0;
+ /* Attribute Opcode (1 octet) */
pdu[0] = ATT_OP_EXEC_WRITE_REQ;
+ /* Flags (1 octet) */
pdu[1] = flags;
- return min_len;
+ return 2;
}
uint16_t dec_exec_write_req(const uint8_t *pdu, size_t len, uint8_t *flags)
@@ -1150,9 +1110,10 @@ uint16_t enc_exec_write_resp(uint8_t *pdu)
if (pdu == NULL)
return 0;
+ /* Attribute Opcode (1 octet) */
pdu[0] = ATT_OP_EXEC_WRITE_RESP;
- return sizeof(pdu[0]);
+ return 1;
}
uint16_t dec_exec_write_resp(const uint8_t *pdu, size_t len)
--
1.8.3.2
^ permalink raw reply related
* [PATCH BlueZ 05/11] attrib: Fix PDU length check for Read by Group Type Request
From: Anderson Lizardo @ 2014-01-11 4:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
In-Reply-To: <1389415647-25831-1-git-send-email-anderson.lizardo@openbossa.org>
PDU length must be either 7 or 21 octets.
---
attrib/att.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/attrib/att.c b/attrib/att.c
index a8f641a..472c25c 100644
--- a/attrib/att.c
+++ b/attrib/att.c
@@ -161,7 +161,7 @@ uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start,
if (pdu[0] != ATT_OP_READ_BY_GROUP_REQ)
return 0;
- if (len < min_len + 2)
+ if (len != (min_len + 2) && len != (min_len + 16))
return 0;
*start = att_get_u16(&pdu[1]);
--
1.8.3.2
^ permalink raw reply related
* [PATCH BlueZ 06/11] attrib: Add extra PDU checks when decoding Read by Group Type Response
From: Anderson Lizardo @ 2014-01-11 4:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
In-Reply-To: <1389415647-25831-1-git-send-email-anderson.lizardo@openbossa.org>
These checks are needed to avoid invalid memory access on bogus PDUs.
---
attrib/att.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/attrib/att.c b/attrib/att.c
index 472c25c..777ef46 100644
--- a/attrib/att.c
+++ b/attrib/att.c
@@ -211,7 +211,25 @@ struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, size_t len)
if (pdu[0] != ATT_OP_READ_BY_GROUP_RESP)
return NULL;
+ /* PDU must contain at least:
+ * - Attribute Opcode (1 octet)
+ * - Length (1 octet)
+ * - Attribute Data List (at least one entry):
+ * - Attribute Handle (2 octets)
+ * - End Group Handle (2 octets)
+ * - Attribute Value (at least 1 octet) */
+ if (len < 7)
+ return NULL;
+
elen = pdu[1];
+ /* Minimum Attribute Data List size */
+ if (elen < 5)
+ return NULL;
+
+ /* Reject incomplete Attribute Data List */
+ if ((len - 2) % elen)
+ return NULL;
+
num = (len - 2) / elen;
list = att_data_list_alloc(num, elen);
if (list == NULL)
--
1.8.3.2
^ permalink raw reply related
* [PATCH BlueZ 07/11] attrib: Remove unnecessary NULL checks on dec_find_by_type_req()
From: Anderson Lizardo @ 2014-01-11 4:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
In-Reply-To: <1389415647-25831-1-git-send-email-anderson.lizardo@openbossa.org>
Just assume that the caller will pass non-NULL pointers as arguments
(which is true for the only current caller of this function).
---
attrib/att.c | 24 +++++++++---------------
1 file changed, 9 insertions(+), 15 deletions(-)
diff --git a/attrib/att.c b/attrib/att.c
index 777ef46..435a26d 100644
--- a/attrib/att.c
+++ b/attrib/att.c
@@ -291,30 +291,24 @@ uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start,
if (len < min_len)
return 0;
+ /* Attribute Opcode (1 octet) */
if (pdu[0] != ATT_OP_FIND_BY_TYPE_REQ)
return 0;
- /* First requested handle number */
- if (start)
- *start = att_get_u16(&pdu[1]);
-
- /* Last requested handle number */
- if (end)
- *end = att_get_u16(&pdu[3]);
-
- /* Always UUID16 */
- if (uuid)
- *uuid = att_get_uuid16(&pdu[5]);
+ /* First requested handle number (2 octets) */
+ *start = att_get_u16(&pdu[1]);
+ /* Last requested handle number (2 octets) */
+ *end = att_get_u16(&pdu[3]);
+ /* 16-bit UUID to find (2 octets) */
+ *uuid = att_get_uuid16(&pdu[5]);
valuelen = len - min_len;
+ *vlen = valuelen;
/* Attribute value to find */
- if (valuelen > 0 && value)
+ if (valuelen > 0)
memcpy(value, pdu + min_len, valuelen);
- if (vlen)
- *vlen = valuelen;
-
return len;
}
--
1.8.3.2
^ permalink raw reply related
* [PATCH BlueZ 08/11] attrib: Remove unnecessary local variables from dec_find_by_type_req()
From: Anderson Lizardo @ 2014-01-11 4:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
In-Reply-To: <1389415647-25831-1-git-send-email-anderson.lizardo@openbossa.org>
Use number instead of "min_len", which is easier to review (with help of
the documented parameter sizes).
valuelen is redundant as *vlen can be used directly.
---
attrib/att.c | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/attrib/att.c b/attrib/att.c
index 435a26d..d367918 100644
--- a/attrib/att.c
+++ b/attrib/att.c
@@ -281,14 +281,10 @@ uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start,
uint16_t *end, bt_uuid_t *uuid,
uint8_t *value, size_t *vlen)
{
- size_t valuelen;
- uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) +
- sizeof(*end) + sizeof(uint16_t);
-
if (pdu == NULL)
return 0;
- if (len < min_len)
+ if (len < 7)
return 0;
/* Attribute Opcode (1 octet) */
@@ -302,12 +298,10 @@ uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start,
/* 16-bit UUID to find (2 octets) */
*uuid = att_get_uuid16(&pdu[5]);
- valuelen = len - min_len;
- *vlen = valuelen;
-
/* Attribute value to find */
- if (valuelen > 0)
- memcpy(value, pdu + min_len, valuelen);
+ *vlen = len - 7;
+ if (*vlen > 0)
+ memcpy(value, pdu + 7, *vlen);
return len;
}
--
1.8.3.2
^ permalink raw reply related
* [PATCH BlueZ 09/11] attrib: Reject incomplete PDU in dec_find_by_type_resp()
From: Anderson Lizardo @ 2014-01-11 4:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
In-Reply-To: <1389415647-25831-1-git-send-email-anderson.lizardo@openbossa.org>
Otherwise, an incomplete PDU may be silently accepted (with any
remaining data discarded).
---
attrib/att.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/attrib/att.c b/attrib/att.c
index d367918..c279b2c 100644
--- a/attrib/att.c
+++ b/attrib/att.c
@@ -334,12 +334,21 @@ GSList *dec_find_by_type_resp(const uint8_t *pdu, size_t len)
GSList *matches;
off_t offset;
+ /* PDU should contain at least:
+ * - Attribute Opcode (1 octet)
+ * - Handles Information List (at least one entry):
+ * - Found Attribute Handle (2 octets)
+ * - Group End Handle (2 octets) */
if (pdu == NULL || len < 5)
return NULL;
if (pdu[0] != ATT_OP_FIND_BY_TYPE_RESP)
return NULL;
+ /* Reject incomplete Handles Information List */
+ if ((len - 1) % 4)
+ return NULL;
+
for (offset = 1, matches = NULL;
len >= (offset + sizeof(uint16_t) * 2);
offset += sizeof(uint16_t) * 2) {
--
1.8.3.2
^ permalink raw reply related
* [PATCH BlueZ 10/11] attrib: Fix PDU length check for Read by Type Request
From: Anderson Lizardo @ 2014-01-11 4:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
In-Reply-To: <1389415647-25831-1-git-send-email-anderson.lizardo@openbossa.org>
PDU length must be either 7 or 21 octets.
---
attrib/att.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/attrib/att.c b/attrib/att.c
index c279b2c..183390b 100644
--- a/attrib/att.c
+++ b/attrib/att.c
@@ -400,7 +400,7 @@ uint16_t dec_read_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start,
if (start == NULL || end == NULL || uuid == NULL)
return 0;
- if (len < min_len + 2)
+ if (len != (min_len + 2) && len != (min_len + 16))
return 0;
if (pdu[0] != ATT_OP_READ_BY_TYPE_REQ)
--
1.8.3.2
^ permalink raw reply related
* [PATCH BlueZ 11/11] attrib: Add extra PDU checks when decoding Read by Type Response
From: Anderson Lizardo @ 2014-01-11 4:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anderson Lizardo
In-Reply-To: <1389415647-25831-1-git-send-email-anderson.lizardo@openbossa.org>
These checks are needed to avoid invalid memory access on bogus PDUs.
---
attrib/att.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/attrib/att.c b/attrib/att.c
index 183390b..e28be25 100644
--- a/attrib/att.c
+++ b/attrib/att.c
@@ -454,7 +454,24 @@ struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, size_t len)
if (pdu[0] != ATT_OP_READ_BY_TYPE_RESP)
return NULL;
+ /* PDU must contain at least:
+ * - Attribute Opcode (1 octet)
+ * - Length (1 octet)
+ * - Attribute Data List (at least one entry):
+ * - Attribute Handle (2 octets)
+ * - Attribute Value (at least 1 octet) */
+ if (len < 5)
+ return NULL;
+
elen = pdu[1];
+ /* Minimum Attribute Data List size */
+ if (elen < 3)
+ return NULL;
+
+ /* Reject incomplete Attribute Data List */
+ if ((len - 2) % elen)
+ return NULL;
+
num = (len - 2) / elen;
list = att_data_list_alloc(num, elen);
if (list == NULL)
--
1.8.3.2
^ permalink raw reply related
* [PATCH BlueZ 1/6] audio/A2DP: Add implemention of audio Open command
From: Luiz Augusto von Dentz @ 2014-01-11 10:13 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
android/a2dp.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 158 insertions(+), 1 deletion(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index b59c53d..28b7406 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -52,9 +52,23 @@
static GIOChannel *server = NULL;
static GSList *devices = NULL;
+static GSList *endpoints = NULL;
static bdaddr_t adapter_addr;
static uint32_t record_id = 0;
+struct a2dp_preset {
+ void *data;
+ int8_t len;
+};
+
+struct a2dp_endpoint {
+ uint8_t id;
+ uint8_t codec;
+ struct avdtp_local_sep *sep;
+ struct a2dp_preset *caps;
+ GSList *presets;
+};
+
struct a2dp_device {
bdaddr_t dst;
uint8_t state;
@@ -70,6 +84,29 @@ static int device_cmp(gconstpointer s, gconstpointer user_data)
return bacmp(&dev->dst, dst);
}
+static void preset_free(void *data)
+{
+ struct a2dp_preset *preset = data;
+
+ g_free(preset->data);
+ g_free(preset);
+}
+
+static void unregister_endpoint(void *data)
+{
+ struct a2dp_endpoint *endpoint = data;
+
+ if (endpoint->sep)
+ avdtp_unregister_sep(endpoint->sep);
+
+ if (endpoint->caps)
+ preset_free(endpoint->caps);
+
+ g_slist_free_full(endpoint->presets, preset_free);
+
+ g_free(endpoint);
+}
+
static void a2dp_device_free(struct a2dp_device *dev)
{
if (dev->session)
@@ -354,10 +391,127 @@ static sdp_record_t *a2dp_record(void)
return record;
}
+static gboolean sep_getcap_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ GSList **caps, uint8_t *err,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_preset *cap = endpoint->presets->data;
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct avdtp_media_codec_capability *codec_caps;
+
+ *caps = NULL;
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ *caps = g_slist_append(*caps, media_transport);
+
+ codec_caps = g_malloc0(sizeof(*codec_caps) + sizeof(cap));
+ codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ codec_caps->media_codec_type = endpoint->codec;
+ memcpy(codec_caps->data, cap->data, cap->len);
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
+ sizeof(*codec_caps) + sizeof(cap));
+
+ *caps = g_slist_append(*caps, media_codec);
+ g_free(codec_caps);
+
+ return TRUE;
+}
+
+static struct avdtp_sep_ind sep_ind = {
+ .get_capability = sep_getcap_ind,
+};
+
+static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
+ GSList *presets)
+{
+ struct a2dp_endpoint *endpoint;
+
+ /* FIXME: Add proper check for uuid */
+
+ endpoint = g_new0(struct a2dp_endpoint, 1);
+ endpoint->id = g_slist_length(endpoints) + 1;
+ endpoint->codec = codec;
+ endpoint->sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE,
+ AVDTP_MEDIA_TYPE_AUDIO,
+ codec, FALSE, &sep_ind, NULL,
+ endpoint);
+ endpoint->caps = g_slist_nth_data(presets->data, 0);
+ endpoint->presets = g_slist_copy(g_slist_nth(presets, 1));
+
+ endpoints = g_slist_append(endpoints, endpoint);
+
+ return endpoint->id;
+}
+
+static GSList *parse_presets(const struct audio_preset *p, uint8_t count,
+ uint16_t len)
+{
+ GSList *l = NULL;
+ uint8_t i;
+
+ for (i = 0; count > i; i++) {
+ struct a2dp_preset *preset;
+
+ if (len < sizeof(struct audio_preset)) {
+ DBG("Invalid preset index %u", i);
+ break;
+ }
+
+ len -= sizeof(struct audio_preset);
+ if (len == 0 || len < p->len) {
+ DBG("Invalid preset size of %u for index %u", len, i);
+ break;
+ }
+
+ preset = g_new0(struct a2dp_preset, 1);
+ preset->len = p->len;
+ preset->data = g_memdup(p->data, preset->len);
+ l = g_slist_append(l, preset);
+
+ len -= preset->len;
+ p += sizeof(struct audio_preset) + preset->len;
+ }
+
+ return l;
+}
+
static void bt_audio_open(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct audio_cmd_open *cmd = buf;
+ struct audio_rsp_open rsp;
+ GSList *presets;
+
+ DBG("");
+ if (cmd->presets == 0) {
+ error("No audio presets found");
+ goto failed;
+ }
+
+ presets = parse_presets(cmd->preset, cmd->presets, len - sizeof(*cmd));
+ if (!presets) {
+ error("No audio presets found");
+ goto failed;
+ }
+
+ rsp.id = register_endpoint(cmd->uuid, cmd->codec, presets);
+ g_slist_free(presets);
+
+ if (rsp.id == 0) {
+ error("Unable to register endpoint");
+ goto failed;
+ }
+
+ audio_ipc_send_rsp_full(AUDIO_OP_OPEN, sizeof(rsp), &rsp, -1);
+
+ return;
+
+failed:
audio_ipc_send_rsp(AUDIO_OP_OPEN, AUDIO_STATUS_FAILED);
}
@@ -471,6 +625,9 @@ void bt_a2dp_unregister(void)
{
DBG("");
+ g_slist_free_full(endpoints, unregister_endpoint);
+ endpoints = NULL;
+
g_slist_foreach(devices, a2dp_device_disconnected, NULL);
devices = NULL;
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ 2/6] audio/A2DP: Add implemention of audio Close command
From: Luiz Augusto von Dentz @ 2014-01-11 10:13 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1389435216-29040-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
android/a2dp.c | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 28b7406..8649cf3 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -515,11 +515,37 @@ failed:
audio_ipc_send_rsp(AUDIO_OP_OPEN, AUDIO_STATUS_FAILED);
}
+static struct a2dp_endpoint *find_endpoint(uint8_t id)
+{
+ GSList *l;
+
+ for (l = endpoints; l; l = g_slist_next(l)) {
+ struct a2dp_endpoint *endpoint = l->data;
+
+ if (endpoint->id == id)
+ return endpoint;
+ }
+
+ return NULL;
+}
+
static void bt_audio_close(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct audio_cmd_close *cmd = buf;
+ struct a2dp_endpoint *endpoint;
+
+ DBG("");
+
+ endpoint = find_endpoint(cmd->id);
+ if (!endpoint) {
+ error("Unable to find endpoint %u", cmd->id);
+ audio_ipc_send_rsp(AUDIO_OP_CLOSE, HAL_STATUS_FAILED);
+ return;
+ }
+
+ unregister_endpoint(endpoint);
- audio_ipc_send_rsp(AUDIO_OP_CLOSE, HAL_STATUS_FAILED);
+ audio_ipc_send_rsp(AUDIO_OP_CLOSE, HAL_STATUS_SUCCESS);
}
static void bt_stream_open(const void *buf, uint16_t len)
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ 3/6] audio/A2DP: Add implemention of audio Open Stream command
From: Luiz Augusto von Dentz @ 2014-01-11 10:13 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1389435216-29040-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
android/a2dp.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 197 insertions(+), 2 deletions(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 8649cf3..479cb71 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -37,6 +37,7 @@
#include "lib/bluetooth.h"
#include "lib/sdp.h"
#include "lib/sdp_lib.h"
+#include "profiles/audio/a2dp-codecs.h"
#include "log.h"
#include "a2dp.h"
#include "hal-msg.h"
@@ -53,6 +54,7 @@
static GIOChannel *server = NULL;
static GSList *devices = NULL;
static GSList *endpoints = NULL;
+static GSList *setups = NULL;
static bdaddr_t adapter_addr;
static uint32_t record_id = 0;
@@ -67,6 +69,7 @@ struct a2dp_endpoint {
struct avdtp_local_sep *sep;
struct a2dp_preset *caps;
GSList *presets;
+ struct a2dp_config *config;
};
struct a2dp_device {
@@ -76,6 +79,13 @@ struct a2dp_device {
struct avdtp *session;
};
+struct a2dp_setup {
+ struct a2dp_device *dev;
+ struct a2dp_endpoint *endpoint;
+ struct a2dp_preset *preset;
+ struct avdtp_stream *stream;
+};
+
static int device_cmp(gconstpointer s, gconstpointer user_data)
{
const struct a2dp_device *dev = s;
@@ -422,8 +432,160 @@ static gboolean sep_getcap_ind(struct avdtp *session,
return TRUE;
}
+static int sbc_check_config(struct a2dp_endpoint *endpoint,
+ struct a2dp_preset *conf)
+{
+ a2dp_sbc_t *caps, *config;
+
+ if (conf->len != sizeof(a2dp_sbc_t)) {
+ error("SBC: Invalid configuration size (%u)", conf->len);
+ return -EINVAL;
+ }
+
+ caps = endpoint->caps->data;
+ config = conf->data;
+
+ if (!(caps->frequency & config->frequency)) {
+ error("SBC: Unsupported frequency (%u) by endpoint",
+ config->frequency);
+ return -EINVAL;
+ }
+
+ if (!(caps->channel_mode & config->channel_mode)) {
+ error("SBC: Unsupported channel mode (%u) by endpoint",
+ config->channel_mode);
+ return -EINVAL;
+ }
+
+ if (!(caps->block_length & config->block_length)) {
+ error("SBC: Unsupported block length (%u) by endpoint",
+ config->block_length);
+ return -EINVAL;
+ }
+
+ if (!(caps->allocation_method & config->allocation_method)) {
+ error("SBC: Unsupported allocation method (%u) by endpoint",
+ config->block_length);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int check_config(struct a2dp_endpoint *endpoint,
+ struct a2dp_preset *config)
+{
+ GSList *l;
+
+ for (l = endpoint->presets; l; l = g_slist_next(l)) {
+ struct a2dp_preset *preset = l->data;
+
+ if (preset->len != config->len)
+ continue;
+
+ if (memcmp(preset->data, config->data, preset->len) == 0)
+ return 0;
+ }
+
+ /* Codec specific */
+ switch (endpoint->codec) {
+ case A2DP_CODEC_SBC:
+ return sbc_check_config(endpoint, config);
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct a2dp_device *find_device_by_session(struct avdtp *session)
+{
+ GSList *l;
+
+ for (l = devices; l; l = g_slist_next(l)) {
+ struct a2dp_device *dev = l->data;
+
+ if (dev->session == session)
+ return dev;
+ }
+
+ return NULL;
+}
+
+static void setup_free(void *data)
+{
+ struct a2dp_setup *setup = data;
+
+ preset_free(setup->preset);
+ g_free(setup);
+}
+
+static void setup_add(struct a2dp_device *dev, struct a2dp_endpoint *endpoint,
+ struct a2dp_preset *preset, struct avdtp_stream *stream)
+{
+ struct a2dp_setup *setup;
+
+ setup = g_new0(struct a2dp_setup, 1);
+ setup->dev = dev;
+ setup->endpoint = endpoint;
+ setup->preset = preset;
+ setup->stream = stream;
+ setups = g_slist_append(setups, setup);
+}
+
+static gboolean sep_setconf_ind(struct avdtp *session,
+ struct avdtp_local_sep *sep,
+ struct avdtp_stream *stream,
+ GSList *caps,
+ avdtp_set_configuration_cb cb,
+ void *user_data)
+{
+ struct a2dp_endpoint *endpoint = user_data;
+ struct a2dp_device *dev;
+ struct a2dp_preset *preset = NULL;
+
+ DBG("");
+
+ dev = find_device_by_session(session);
+ if (!dev) {
+ error("Unable to find device for session %p", session);
+ return FALSE;
+ }
+
+ for (; caps != NULL; caps = g_slist_next(caps)) {
+ struct avdtp_service_capability *cap = caps->data;
+ struct avdtp_media_codec_capability *codec;
+
+ if (cap->category == AVDTP_DELAY_REPORTING)
+ return FALSE;
+
+ if (cap->category != AVDTP_MEDIA_CODEC)
+ continue;
+
+ codec = (struct avdtp_media_codec_capability *) cap->data;
+
+ if (codec->media_codec_type != endpoint->codec)
+ return FALSE;
+
+ preset = g_new0(struct a2dp_preset, 1);
+ preset->len = cap->length - sizeof(*codec);
+ preset->data = g_memdup(codec->data, preset->len);
+
+ if (check_config(endpoint, preset) < 0) {
+ preset_free(preset);
+ return FALSE;
+ }
+ }
+
+ if (!preset)
+ return FALSE;
+
+ setup_add(dev, endpoint, preset, stream);
+
+ return TRUE;
+}
+
static struct avdtp_sep_ind sep_ind = {
.get_capability = sep_getcap_ind,
+ .set_configuration = sep_setconf_ind,
};
static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
@@ -548,11 +710,41 @@ static void bt_audio_close(const void *buf, uint16_t len)
audio_ipc_send_rsp(AUDIO_OP_CLOSE, HAL_STATUS_SUCCESS);
}
+static struct a2dp_setup *find_setup(uint8_t id)
+{
+ GSList *l;
+
+ for (l = setups; l; l = g_slist_next(l)) {
+ struct a2dp_setup *setup = l->data;
+
+ if (setup->endpoint->id == id)
+ return setup;
+ }
+
+ return NULL;
+}
+
static void bt_stream_open(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct audio_cmd_open_stream *cmd = buf;
+ struct audio_rsp_open_stream *rsp;
+ struct a2dp_setup *setup;
+
+ DBG("");
- audio_ipc_send_rsp(AUDIO_OP_OPEN_STREAM, AUDIO_STATUS_FAILED);
+ setup = find_setup(cmd->id);
+ if (!setup) {
+ error("Unable to find stream for endpoint %u", cmd->id);
+ audio_ipc_send_rsp(AUDIO_OP_OPEN_STREAM, HAL_STATUS_FAILED);
+ return;
+ }
+
+ len = sizeof(*rsp) + setup->preset->len;
+ rsp = g_malloc0(sizeof(*rsp) + setup->preset->len);
+ rsp->preset->len = setup->preset->len;
+ memcpy(rsp->preset->data, setup->preset->data, setup->preset->len);
+
+ audio_ipc_send_rsp_full(AUDIO_OP_OPEN_STREAM, len, rsp, -1);
}
static void bt_stream_close(const void *buf, uint16_t len)
@@ -651,6 +843,9 @@ void bt_a2dp_unregister(void)
{
DBG("");
+ g_slist_free_full(setups, setup_free);
+ setups = NULL;
+
g_slist_free_full(endpoints, unregister_endpoint);
endpoints = NULL;
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ 4/6] audio/A2DP: Add implemention of audio Close Stream command
From: Luiz Augusto von Dentz @ 2014-01-11 10:13 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1389435216-29040-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
android/a2dp.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 479cb71..4e4f43f 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -749,8 +749,29 @@ static void bt_stream_open(const void *buf, uint16_t len)
static void bt_stream_close(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct audio_cmd_close_stream *cmd = buf;
+ struct a2dp_setup *setup;
+ int err;
+ DBG("");
+
+ setup = find_setup(cmd->id);
+ if (!setup) {
+ error("Unable to find stream for endpoint %u", cmd->id);
+ goto failed;
+ }
+
+ err = avdtp_close(setup->dev->session, setup->stream, FALSE);
+ if (err < 0) {
+ error("avdtp_close: %s", strerror(-err));
+ goto failed;
+ }
+
+ audio_ipc_send_rsp(AUDIO_OP_CLOSE_STREAM, AUDIO_STATUS_SUCCESS);
+
+ return;
+
+failed:
audio_ipc_send_rsp(AUDIO_OP_CLOSE_STREAM, AUDIO_STATUS_FAILED);
}
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ 5/6] audio/A2DP: Add implemention of audio Resume Stream command
From: Luiz Augusto von Dentz @ 2014-01-11 10:13 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1389435216-29040-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
android/a2dp.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 4e4f43f..2d886e9 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -777,8 +777,29 @@ failed:
static void bt_stream_resume(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct audio_cmd_resume_stream *cmd = buf;
+ struct a2dp_setup *setup;
+ int err;
+
+ DBG("");
+ setup = find_setup(cmd->id);
+ if (!setup) {
+ error("Unable to find stream for endpoint %u", cmd->id);
+ goto failed;
+ }
+
+ err = avdtp_start(setup->dev->session, setup->stream);
+ if (err < 0) {
+ error("avdtp_start: %s", strerror(-err));
+ goto failed;
+ }
+
+ audio_ipc_send_rsp(AUDIO_OP_RESUME_STREAM, AUDIO_STATUS_SUCCESS);
+
+ return;
+
+failed:
audio_ipc_send_rsp(AUDIO_OP_RESUME_STREAM, AUDIO_STATUS_FAILED);
}
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ 6/6] audio/A2DP: Add implemention of audio Suspend Stream command
From: Luiz Augusto von Dentz @ 2014-01-11 10:13 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1389435216-29040-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
android/a2dp.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 2d886e9..8d7d554 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -805,8 +805,29 @@ failed:
static void bt_stream_suspend(const void *buf, uint16_t len)
{
- DBG("Not Implemented");
+ const struct audio_cmd_suspend_stream *cmd = buf;
+ struct a2dp_setup *setup;
+ int err;
+
+ DBG("");
+
+ setup = find_setup(cmd->id);
+ if (!setup) {
+ error("Unable to find stream for endpoint %u", cmd->id);
+ goto failed;
+ }
+
+ err = avdtp_suspend(setup->dev->session, setup->stream);
+ if (err < 0) {
+ error("avdtp_suspend: %s", strerror(-err));
+ goto failed;
+ }
+ audio_ipc_send_rsp(AUDIO_OP_SUSPEND_STREAM, AUDIO_STATUS_SUCCESS);
+
+ return;
+
+failed:
audio_ipc_send_rsp(AUDIO_OP_SUSPEND_STREAM, AUDIO_STATUS_FAILED);
}
--
1.8.4.2
^ permalink raw reply related
* Re: [PATCH BlueZ 01/11] attrib: Modify gatt_cb_t signature
From: Johan Hedberg @ 2014-01-11 16:56 UTC (permalink / raw)
To: Anderson Lizardo; +Cc: linux-bluetooth
In-Reply-To: <1389415647-25831-1-git-send-email-anderson.lizardo@openbossa.org>
Hi Lizardo,
On Sat, Jan 11, 2014, Anderson Lizardo wrote:
> Use standard C types instead of GLib ones (which are unnecessary here)
> and move the "status" parameter to the first position, so it is
> consistent with other callbacks.
> ---
> attrib/gatt.c | 10 +++++-----
> attrib/gatt.h | 2 +-
> attrib/gatttool.c | 9 ++++-----
> attrib/interactive.c | 9 ++++-----
> profiles/cyclingspeed/cyclingspeed.c | 2 +-
> profiles/deviceinfo/deviceinfo.c | 4 ++--
> profiles/gatt/gas.c | 4 ++--
> profiles/heartrate/heartrate.c | 2 +-
> profiles/input/hog.c | 6 +++---
> profiles/proximity/monitor.c | 12 ++++++------
> profiles/scanparam/scan.c | 7 +++----
> profiles/thermometer/thermometer.c | 4 ++--
> src/device.c | 5 ++---
> 13 files changed, 36 insertions(+), 40 deletions(-)
All patches in this set have been applied. Thanks.
Johan
^ 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