* [PATCH v3] Bluetooth: Add hci_h4p driver
From: Pavel Machek @ 2014-01-02 16:18 UTC (permalink / raw)
To: Pali Rohár
Cc: Marcel Holtmann,
Ивайло Димитров,
Gustavo Padovan, Johan Hedberg, linux-kernel, linux-bluetooth,
Ville Tervo, sre
In-Reply-To: <1727897.LBX8128hIo@izba>
Add hci_h4p bluetooth driver to 3.12. 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>
---
Changes from v2: Cleanups as suggested by Marcel. Platform data are
still used to communicate with board code; N900 is not quite ready
for device tree just yet. Power management thread was removed.
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 4afae20..a5ed271 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -30,3 +30,8 @@ hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o
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
+
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..6a693dc
--- /dev/null
+++ b/drivers/bluetooth/nokia_core.c
@@ -0,0 +1,1339 @@
+/*
+ * 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 <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#include <linux/bluetooth/hci_h4p.h>
+
+#include "hci_h4p.h"
+
+/* 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\n", clock);
+ clk_prepare_enable(info->uart_fclk);
+ clk_prepare_enable(info->uart_iclk);
+ if (atomic_read(&info->clk_users) == 0)
+ hci_h4p_restore_regs(info);
+ atomic_inc(&info->clk_users);
+ }
+
+ if (!enable && *clock) {
+ BT_DBG("Disabling %p\n", clock);
+ if (atomic_dec_and_test(&info->clk_users))
+ hci_h4p_store_regs(info);
+ clk_disable_unprepare(info->uart_fclk);
+ clk_disable_unprepare(info->uart_iclk);
+ }
+
+ *clock = enable;
+ spin_unlock_irqrestore(&info->clocks_lock, flags);
+}
+
+static void hci_h4p_lazy_clock_release(unsigned long data)
+{
+ struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->lock, flags);
+ if (!info->tx_enabled)
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+/* Power management functions */
+void hci_h4p_smart_idle(struct hci_h4p_info *info, bool enable)
+{
+ u8 v;
+
+ v = hci_h4p_inb(info, UART_OMAP_SYSC);
+ v &= ~(UART_OMAP_SYSC_IDLEMASK);
+
+ if (enable)
+ v |= UART_OMAP_SYSC_SMART_IDLE;
+ else
+ v |= UART_OMAP_SYSC_NO_IDLE;
+
+ hci_h4p_outb(info, UART_OMAP_SYSC, v);
+}
+
+static inline void h4p_schedule_pm(struct hci_h4p_info *info)
+{
+}
+
+static void hci_h4p_disable_tx(struct hci_h4p_info *info)
+{
+ BT_DBG("\n");
+
+ if (!info->pm_enabled)
+ return;
+
+ /* Re-enable smart-idle */
+ hci_h4p_smart_idle(info, 1);
+
+ gpio_set_value(info->bt_wakeup_gpio, 0);
+ mod_timer(&info->lazy_release, jiffies + msecs_to_jiffies(100));
+ info->tx_enabled = 0;
+}
+
+void hci_h4p_enable_tx(struct hci_h4p_info *info)
+{
+ unsigned long flags;
+ BT_DBG("\n");
+
+ if (!info->pm_enabled)
+ return;
+
+ h4p_schedule_pm(info);
+
+ spin_lock_irqsave(&info->lock, flags);
+ del_timer(&info->lazy_release);
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+ info->tx_enabled = 1;
+ gpio_set_value(info->bt_wakeup_gpio, 1);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ /*
+ * Disable smart-idle as UART TX interrupts
+ * are not wake-up capable
+ */
+ hci_h4p_smart_idle(info, 0);
+
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static void hci_h4p_disable_rx(struct hci_h4p_info *info)
+{
+ if (!info->pm_enabled)
+ return;
+
+ info->rx_enabled = 0;
+
+ if (hci_h4p_inb(info, UART_LSR) & UART_LSR_DR)
+ return;
+
+ if (!(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT))
+ return;
+
+ __hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
+ info->autorts = 0;
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+}
+
+static void hci_h4p_enable_rx(struct hci_h4p_info *info)
+{
+ if (!info->pm_enabled)
+ return;
+
+ h4p_schedule_pm(info);
+
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
+ info->rx_enabled = 1;
+
+ if (!(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT))
+ return;
+
+ __hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
+ info->autorts = 1;
+}
+
+/* Negotiation functions */
+int hci_h4p_send_alive_packet(struct hci_h4p_info *info)
+{
+ struct hci_h4p_alive_hdr *hdr;
+ struct hci_h4p_alive_pkt *pkt;
+ struct sk_buff *skb;
+ unsigned long flags;
+ int len;
+
+ BT_DBG("Sending alive packet\n");
+
+ len = H4_TYPE_SIZE + sizeof(*hdr) + sizeof(*pkt);
+ skb = bt_skb_alloc(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ memset(skb->data, 0x00, len);
+ *skb_put(skb, 1) = H4_ALIVE_PKT;
+ hdr = (struct hci_h4p_alive_hdr *)skb_put(skb, sizeof(*hdr));
+ hdr->dlen = sizeof(*pkt);
+ pkt = (struct hci_h4p_alive_pkt *)skb_put(skb, sizeof(*pkt));
+ pkt->mid = H4P_ALIVE_REQ;
+
+ skb_queue_tail(&info->txq, skb);
+ spin_lock_irqsave(&info->lock, flags);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ BT_DBG("Alive packet sent\n");
+
+ return 0;
+}
+
+static void hci_h4p_alive_packet(struct hci_h4p_info *info,
+ struct sk_buff *skb)
+{
+ struct hci_h4p_alive_hdr *hdr;
+ struct hci_h4p_alive_pkt *pkt;
+
+ BT_DBG("Received alive packet\n");
+ hdr = (struct hci_h4p_alive_hdr *)skb->data;
+ if (hdr->dlen != sizeof(*pkt)) {
+ dev_err(info->dev, "Corrupted alive message\n");
+ info->init_error = -EIO;
+ goto finish_alive;
+ }
+
+ pkt = (struct hci_h4p_alive_pkt *)skb_pull(skb, sizeof(*hdr));
+ if (pkt->mid != H4P_ALIVE_RESP) {
+ dev_err(info->dev, "Could not negotiate hci_h4p settings\n");
+ info->init_error = -EINVAL;
+ }
+
+finish_alive:
+ complete(&info->init_completion);
+ kfree_skb(skb);
+}
+
+static int hci_h4p_send_negotiation(struct hci_h4p_info *info)
+{
+ struct hci_h4p_neg_cmd *neg_cmd;
+ struct hci_h4p_neg_hdr *neg_hdr;
+ struct sk_buff *skb;
+ unsigned long flags;
+ int err, len;
+ u16 sysclk;
+
+ BT_DBG("Sending negotiation..\n");
+
+ switch (info->bt_sysclk) {
+ case 1:
+ sysclk = 12000;
+ break;
+ case 2:
+ sysclk = 38400;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ len = sizeof(*neg_cmd) + sizeof(*neg_hdr) + H4_TYPE_SIZE;
+ skb = bt_skb_alloc(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ memset(skb->data, 0x00, len);
+ *skb_put(skb, 1) = H4_NEG_PKT;
+ neg_hdr = (struct hci_h4p_neg_hdr *)skb_put(skb, sizeof(*neg_hdr));
+ neg_cmd = (struct hci_h4p_neg_cmd *)skb_put(skb, sizeof(*neg_cmd));
+
+ neg_hdr->dlen = sizeof(*neg_cmd);
+ neg_cmd->ack = H4P_NEG_REQ;
+ neg_cmd->baud = cpu_to_le16(BT_BAUDRATE_DIVIDER/MAX_BAUD_RATE);
+ neg_cmd->proto = H4P_PROTO_BYTE;
+ neg_cmd->sys_clk = cpu_to_le16(sysclk);
+
+ hci_h4p_change_speed(info, INIT_SPEED);
+
+ hci_h4p_set_rts(info, 1);
+ info->init_error = 0;
+ init_completion(&info->init_completion);
+ skb_queue_tail(&info->txq, skb);
+ spin_lock_irqsave(&info->lock, flags);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ if (!wait_for_completion_interruptible_timeout(&info->init_completion,
+ msecs_to_jiffies(1000)))
+ return -ETIMEDOUT;
+
+ if (info->init_error < 0)
+ return info->init_error;
+
+ /* Change to operational settings */
+ hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
+ hci_h4p_set_rts(info, 0);
+ hci_h4p_change_speed(info, MAX_BAUD_RATE);
+
+ err = hci_h4p_wait_for_cts(info, 1, 100);
+ if (err < 0)
+ return err;
+
+ hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
+ init_completion(&info->init_completion);
+ err = hci_h4p_send_alive_packet(info);
+
+ if (err < 0)
+ return err;
+
+ if (!wait_for_completion_interruptible_timeout(&info->init_completion,
+ msecs_to_jiffies(1000)))
+ return -ETIMEDOUT;
+
+ if (info->init_error < 0)
+ return info->init_error;
+
+ BT_DBG("Negotiation succesful\n");
+ return 0;
+}
+
+static void hci_h4p_negotiation_packet(struct hci_h4p_info *info,
+ struct sk_buff *skb)
+{
+ struct hci_h4p_neg_hdr *hdr;
+ struct hci_h4p_neg_evt *evt;
+
+ hdr = (struct hci_h4p_neg_hdr *)skb->data;
+ if (hdr->dlen != sizeof(*evt)) {
+ info->init_error = -EIO;
+ goto finish_neg;
+ }
+
+ evt = (struct hci_h4p_neg_evt *)skb_pull(skb, sizeof(*hdr));
+
+ if (evt->ack != H4P_NEG_ACK) {
+ dev_err(info->dev, "Could not negotiate hci_h4p settings\n");
+ info->init_error = -EINVAL;
+ }
+
+ info->man_id = evt->man_id;
+ info->ver_id = evt->ver_id;
+
+finish_neg:
+
+ complete(&info->init_completion);
+ kfree_skb(skb);
+}
+
+/* H4 packet handling functions */
+static int hci_h4p_get_hdr_len(struct hci_h4p_info *info, u8 pkt_type)
+{
+ long retval;
+
+ switch (pkt_type) {
+ case H4_EVT_PKT:
+ retval = HCI_EVENT_HDR_SIZE;
+ break;
+ case H4_ACL_PKT:
+ retval = HCI_ACL_HDR_SIZE;
+ break;
+ case H4_SCO_PKT:
+ retval = HCI_SCO_HDR_SIZE;
+ break;
+ case H4_NEG_PKT:
+ retval = H4P_NEG_HDR_SIZE;
+ break;
+ case H4_ALIVE_PKT:
+ retval = H4P_ALIVE_HDR_SIZE;
+ break;
+ case H4_RADIO_PKT:
+ retval = H4_RADIO_HDR_SIZE;
+ break;
+ default:
+ dev_err(info->dev, "Unknown H4 packet type 0x%.2x\n", pkt_type);
+ retval = -1;
+ break;
+ }
+
+ return retval;
+}
+
+static unsigned int hci_h4p_get_data_len(struct hci_h4p_info *info,
+ struct sk_buff *skb)
+{
+ long retval = -1;
+ struct hci_acl_hdr *acl_hdr;
+ struct hci_sco_hdr *sco_hdr;
+ struct hci_event_hdr *evt_hdr;
+ struct hci_h4p_neg_hdr *neg_hdr;
+ struct hci_h4p_alive_hdr *alive_hdr;
+ struct hci_h4p_radio_hdr *radio_hdr;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case H4_EVT_PKT:
+ evt_hdr = (struct hci_event_hdr *)skb->data;
+ retval = evt_hdr->plen;
+ break;
+ case H4_ACL_PKT:
+ acl_hdr = (struct hci_acl_hdr *)skb->data;
+ retval = le16_to_cpu(acl_hdr->dlen);
+ break;
+ case H4_SCO_PKT:
+ sco_hdr = (struct hci_sco_hdr *)skb->data;
+ retval = sco_hdr->dlen;
+ break;
+ case H4_RADIO_PKT:
+ radio_hdr = (struct hci_h4p_radio_hdr *)skb->data;
+ retval = radio_hdr->dlen;
+ break;
+ case H4_NEG_PKT:
+ neg_hdr = (struct hci_h4p_neg_hdr *)skb->data;
+ retval = neg_hdr->dlen;
+ break;
+ case H4_ALIVE_PKT:
+ alive_hdr = (struct hci_h4p_alive_hdr *)skb->data;
+ retval = alive_hdr->dlen;
+ break;
+ }
+
+ return retval;
+}
+
+static inline void hci_h4p_recv_frame(struct hci_h4p_info *info,
+ struct sk_buff *skb)
+{
+ if (unlikely(!test_bit(HCI_RUNNING, &info->hdev->flags))) {
+ switch (bt_cb(skb)->pkt_type) {
+ case H4_NEG_PKT:
+ hci_h4p_negotiation_packet(info, skb);
+ info->rx_state = WAIT_FOR_PKT_TYPE;
+ return;
+ case H4_ALIVE_PKT:
+ hci_h4p_alive_packet(info, skb);
+ info->rx_state = WAIT_FOR_PKT_TYPE;
+ return;
+ }
+
+ if (!test_bit(HCI_UP, &info->hdev->flags)) {
+ BT_DBG("fw_event\n");
+ hci_h4p_parse_fw_event(info, skb);
+ return;
+ }
+ }
+
+ hci_recv_frame(skb);
+ BT_DBG("Frame sent to upper layer\n");
+}
+
+static inline void hci_h4p_handle_byte(struct hci_h4p_info *info, u8 byte)
+{
+ switch (info->rx_state) {
+ case WAIT_FOR_PKT_TYPE:
+ bt_cb(info->rx_skb)->pkt_type = byte;
+ info->rx_count = hci_h4p_get_hdr_len(info, byte);
+ if (info->rx_count < 0) {
+ info->hdev->stat.err_rx++;
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ } else {
+ info->rx_state = WAIT_FOR_HEADER;
+ }
+ break;
+ case WAIT_FOR_HEADER:
+ info->rx_count--;
+ *skb_put(info->rx_skb, 1) = byte;
+ if (info->rx_count == 0) {
+ info->rx_count = hci_h4p_get_data_len(info,
+ info->rx_skb);
+ if (info->rx_count > skb_tailroom(info->rx_skb)) {
+ dev_err(info->dev, "Too long frame.\n");
+ info->garbage_bytes = info->rx_count -
+ skb_tailroom(info->rx_skb);
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+ }
+ info->rx_state = WAIT_FOR_DATA;
+
+ }
+ break;
+ case WAIT_FOR_DATA:
+ info->rx_count--;
+ *skb_put(info->rx_skb, 1) = byte;
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ if (info->rx_count == 0) {
+ /* H4+ devices should allways send word aligned
+ * packets */
+ if (!(info->rx_skb->len % 2))
+ info->garbage_bytes++;
+ hci_h4p_recv_frame(info, info->rx_skb);
+ info->rx_skb = NULL;
+ }
+}
+
+static void hci_h4p_rx_tasklet(unsigned long data)
+{
+ u8 byte;
+ struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+
+ BT_DBG("tasklet woke up\n");
+ BT_DBG("rx_tasklet woke up\ndata ");
+
+ while (hci_h4p_inb(info, UART_LSR) & UART_LSR_DR) {
+ byte = hci_h4p_inb(info, UART_RX);
+ if (info->garbage_bytes) {
+ info->garbage_bytes--;
+ continue;
+ }
+ if (info->rx_skb == NULL) {
+ info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE,
+ GFP_ATOMIC | GFP_DMA);
+ if (!info->rx_skb) {
+ dev_err(info->dev,
+ "No memory for new packet\n");
+ goto finish_rx;
+ }
+ info->rx_state = WAIT_FOR_PKT_TYPE;
+ info->rx_skb->dev = (void *)info->hdev;
+ }
+ info->hdev->stat.byte_rx++;
+ pr_debug("0x%.2x ", byte);
+ hci_h4p_handle_byte(info, byte);
+ }
+
+ if (!info->rx_enabled) {
+ if (hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT &&
+ info->autorts) {
+ __hci_h4p_set_auto_ctsrts(info, 0 , UART_EFR_RTS);
+ info->autorts = 0;
+ }
+ /* Flush posted write to avoid spurious interrupts */
+ hci_h4p_inb(info, UART_OMAP_SCR);
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+ }
+
+finish_rx:
+ pr_debug("\n");
+ BT_DBG("rx_ended\n");
+}
+
+static void hci_h4p_tx_tasklet(unsigned long data)
+{
+ unsigned int sent = 0;
+ struct sk_buff *skb;
+ struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+
+ BT_DBG("tasklet woke up\n");
+ BT_DBG("tx_tasklet woke up\n data ");
+
+ if (info->autorts != info->rx_enabled) {
+ if (hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT) {
+ if (info->autorts && !info->rx_enabled) {
+ __hci_h4p_set_auto_ctsrts(info, 0,
+ UART_EFR_RTS);
+ info->autorts = 0;
+ }
+ if (!info->autorts && info->rx_enabled) {
+ __hci_h4p_set_auto_ctsrts(info, 1,
+ UART_EFR_RTS);
+ info->autorts = 1;
+ }
+ } else {
+ hci_h4p_outb(info, UART_OMAP_SCR,
+ hci_h4p_inb(info, UART_OMAP_SCR) |
+ UART_OMAP_SCR_EMPTY_THR);
+ goto finish_tx;
+ }
+ }
+
+ skb = skb_dequeue(&info->txq);
+ if (!skb) {
+ /* No data in buffer */
+ BT_DBG("skb ready\n");
+ if (hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT) {
+ hci_h4p_outb(info, UART_IER,
+ hci_h4p_inb(info, UART_IER) &
+ ~UART_IER_THRI);
+ hci_h4p_inb(info, UART_OMAP_SCR);
+ hci_h4p_disable_tx(info);
+ return;
+ } else
+ hci_h4p_outb(info, UART_OMAP_SCR,
+ hci_h4p_inb(info, UART_OMAP_SCR) |
+ UART_OMAP_SCR_EMPTY_THR);
+ goto finish_tx;
+ }
+
+ /* Copy data to tx fifo */
+ while (!(hci_h4p_inb(info, UART_OMAP_SSR) & UART_OMAP_SSR_TXFULL) &&
+ (sent < skb->len)) {
+ pr_debug("0x%.2x ", skb->data[sent]);
+ hci_h4p_outb(info, UART_TX, skb->data[sent]);
+ sent++;
+ }
+
+ info->hdev->stat.byte_tx += sent;
+ pr_debug("\n");
+ if (skb->len == sent) {
+ kfree_skb(skb);
+ } else {
+ skb_pull(skb, sent);
+ skb_queue_head(&info->txq, skb);
+ }
+
+ hci_h4p_outb(info, UART_OMAP_SCR, hci_h4p_inb(info, UART_OMAP_SCR) &
+ ~UART_OMAP_SCR_EMPTY_THR);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+
+finish_tx:
+ /* Flush posted write to avoid spurious interrupts */
+ hci_h4p_inb(info, UART_OMAP_SCR);
+
+}
+
+static irqreturn_t hci_h4p_interrupt(int irq, void *data)
+{
+ struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+ u8 iir, msr;
+ int ret;
+
+ ret = IRQ_NONE;
+
+ iir = hci_h4p_inb(info, UART_IIR);
+ if (iir & UART_IIR_NO_INT)
+ return IRQ_HANDLED;
+
+ BT_DBG("In interrupt handler iir 0x%.2x\n", iir);
+
+ iir &= UART_IIR_ID;
+
+ if (iir == UART_IIR_MSI) {
+ msr = hci_h4p_inb(info, UART_MSR);
+ ret = IRQ_HANDLED;
+ }
+ if (iir == UART_IIR_RLSI) {
+ hci_h4p_inb(info, UART_RX);
+ hci_h4p_inb(info, UART_LSR);
+ ret = IRQ_HANDLED;
+ }
+
+ if (iir == UART_IIR_RDI) {
+ hci_h4p_rx_tasklet((unsigned long)data);
+ ret = IRQ_HANDLED;
+ }
+
+ if (iir == UART_IIR_THRI) {
+ hci_h4p_tx_tasklet((unsigned long)data);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static irqreturn_t hci_h4p_wakeup_interrupt(int irq, void *dev_inst)
+{
+ struct hci_h4p_info *info = dev_inst;
+ int should_wakeup;
+ struct hci_dev *hdev;
+
+ if (!info->hdev)
+ return IRQ_HANDLED;
+
+ should_wakeup = gpio_get_value(info->host_wakeup_gpio);
+ hdev = info->hdev;
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+ if (should_wakeup == 1)
+ complete_all(&info->test_completion);
+
+ return IRQ_HANDLED;
+ }
+
+ BT_DBG("gpio interrupt %d\n", should_wakeup);
+
+ /* Check if wee have missed some interrupts */
+ if (info->rx_enabled == should_wakeup)
+ return IRQ_HANDLED;
+
+ if (should_wakeup)
+ hci_h4p_enable_rx(info);
+ else
+ hci_h4p_disable_rx(info);
+
+ return IRQ_HANDLED;
+}
+
+static inline void hci_h4p_set_pm_limits(struct hci_h4p_info *info, bool set)
+{
+ struct hci_h4p_platform_data *bt_plat_data = info->dev->platform_data;
+ 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 h4p_run(void *data)
+{
+#define TIMEOUT_MIN msecs_to_jiffies(100)
+#define TIMEOUT_MAX msecs_to_jiffies(2000)
+ struct hci_h4p_info *info = data;
+ unsigned long last_jiffies = jiffies;
+ unsigned long timeout = TIMEOUT_MIN;
+ unsigned long elapsed;
+ BT_DBG("");
+ set_user_nice(current, -10);
+
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!test_bit(H4P_SCHED_TRANSFER_MODE, &info->pm_flags)) {
+ if (timeout != TIMEOUT_MIN) {
+ BT_DBG("Exit from active mode. Rest. constr.");
+ hci_h4p_set_pm_limits(info, false);
+ }
+
+ BT_DBG("No pending events. Sleeping.");
+ schedule();
+ }
+
+ set_bit(H4P_TRANSFER_MODE, &info->pm_flags);
+ clear_bit(H4P_SCHED_TRANSFER_MODE, &info->pm_flags);
+
+ elapsed = jiffies - last_jiffies;
+
+ BT_DBG("Wake up. %u msec expired since last BT activity.",
+ jiffies_to_msecs(elapsed));
+ BT_DBG("Timeout before calculation = %u",
+ jiffies_to_msecs(timeout));
+
+ if (elapsed < TIMEOUT_MIN) {
+ timeout <<= 1;
+ } else {
+ timeout -= elapsed;
+ }
+
+ if (timeout > TIMEOUT_MAX)
+ timeout = TIMEOUT_MAX;
+ if (timeout < TIMEOUT_MIN)
+ timeout = TIMEOUT_MIN;
+
+ BT_DBG("Timeout after calculation = %u",
+ jiffies_to_msecs(timeout));
+
+ /* Sometimes we get couple of HCI command during (e)SCO
+ connection. Turn ON transfer mode _ONLY_ if there is
+ still BT activity after 100ms sleep */
+ if (timeout == TIMEOUT_MIN)
+ BT_DBG("Do not enable transfer mode yet");
+ else {
+ hci_h4p_set_pm_limits(info, true);
+ BT_DBG("Set active mode for %u msec.",
+ jiffies_to_msecs(timeout));
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(timeout);
+
+ last_jiffies = jiffies;
+ clear_bit(H4P_TRANSFER_MODE, &info->pm_flags);
+ }
+
+ hci_h4p_set_pm_limits(info, false);
+
+ return 0;
+}
+
+static int hci_h4p_reset(struct hci_h4p_info *info)
+{
+ int err;
+
+ err = hci_h4p_reset_uart(info);
+ if (err < 0) {
+ dev_err(info->dev, "Uart reset failed\n");
+ return err;
+ }
+ hci_h4p_init_uart(info);
+ hci_h4p_set_rts(info, 0);
+
+ gpio_set_value(info->reset_gpio, 0);
+ gpio_set_value(info->bt_wakeup_gpio, 1);
+ msleep(10);
+
+ if (gpio_get_value(info->host_wakeup_gpio) == 1) {
+ dev_err(info->dev, "host_wakeup_gpio not low\n");
+ return -EPROTO;
+ }
+
+ INIT_COMPLETION(info->test_completion);
+ gpio_set_value(info->reset_gpio, 1);
+
+ if (!wait_for_completion_interruptible_timeout(&info->test_completion,
+ msecs_to_jiffies(100))) {
+ dev_err(info->dev, "wakeup test timed out\n");
+ complete_all(&info->test_completion);
+ return -EPROTO;
+ }
+
+ err = hci_h4p_wait_for_cts(info, 1, 100);
+ if (err < 0) {
+ dev_err(info->dev, "No cts from bt chip\n");
+ return err;
+ }
+
+ hci_h4p_set_rts(info, 1);
+
+ return 0;
+}
+
+/* hci callback functions */
+static int hci_h4p_hci_flush(struct hci_dev *hdev)
+{
+ struct hci_h4p_info *info = hci_get_drvdata(hdev);
+ skb_queue_purge(&info->txq);
+
+ return 0;
+}
+
+static int hci_h4p_bt_wakeup_test(struct hci_h4p_info *info)
+{
+ /*
+ * Test Sequence:
+ * Host de-asserts the BT_WAKE_UP line.
+ * Host polls the UART_CTS line, waiting for it to be de-asserted.
+ * Host asserts the BT_WAKE_UP line.
+ * Host polls the UART_CTS line, waiting for it to be asserted.
+ * Host de-asserts the BT_WAKE_UP line (allow the Bluetooth device to
+ * sleep).
+ * Host polls the UART_CTS line, waiting for it to be de-asserted.
+ */
+ int err;
+ int ret = -ECOMM;
+
+ if (!info)
+ return -EINVAL;
+
+ /* Disable wakeup interrupts */
+ disable_irq(gpio_to_irq(info->host_wakeup_gpio));
+
+ gpio_set_value(info->bt_wakeup_gpio, 0);
+ err = hci_h4p_wait_for_cts(info, 0, 100);
+ if (err) {
+ dev_warn(info->dev, "bt_wakeup_test: fail: "
+ "CTS low timed out: %d\n", err);
+ goto out;
+ }
+
+ gpio_set_value(info->bt_wakeup_gpio, 1);
+ err = hci_h4p_wait_for_cts(info, 1, 100);
+ if (err) {
+ dev_warn(info->dev, "bt_wakeup_test: fail: "
+ "CTS high timed out: %d\n", err);
+ goto out;
+ }
+
+ gpio_set_value(info->bt_wakeup_gpio, 0);
+ err = hci_h4p_wait_for_cts(info, 0, 100);
+ if (err) {
+ dev_warn(info->dev, "bt_wakeup_test: fail: "
+ "CTS re-low timed out: %d\n", err);
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+
+ /* Re-enable wakeup interrupts */
+ enable_irq(gpio_to_irq(info->host_wakeup_gpio));
+
+ return ret;
+}
+
+static int hci_h4p_hci_open(struct hci_dev *hdev)
+{
+ struct hci_h4p_info *info;
+ int err, retries = 0;
+ struct sk_buff_head fw_queue;
+ unsigned long flags;
+
+ info = hci_get_drvdata(hdev);
+
+ if (test_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ /* TI1271 has HW bug and boot up might fail. Retry up to three times */
+again:
+
+ info->rx_enabled = 1;
+ info->rx_state = WAIT_FOR_PKT_TYPE;
+ info->rx_count = 0;
+ info->garbage_bytes = 0;
+ info->rx_skb = NULL;
+ info->pm_enabled = 0;
+ init_completion(&info->fw_completion);
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
+ skb_queue_head_init(&fw_queue);
+
+ err = hci_h4p_reset(info);
+ if (err < 0)
+ goto err_clean;
+
+ hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_CTS | UART_EFR_RTS);
+ info->autorts = 1;
+
+ err = hci_h4p_send_negotiation(info);
+
+ err = hci_h4p_read_fw(info, &fw_queue);
+ if (err < 0) {
+ dev_err(info->dev, "Cannot read firmware\n");
+ goto err_clean;
+ }
+
+ err = hci_h4p_send_fw(info, &fw_queue);
+ if (err < 0) {
+ dev_err(info->dev, "Sending firmware failed.\n");
+ goto err_clean;
+ }
+
+ info->pm_enabled = 1;
+
+ err = hci_h4p_bt_wakeup_test(info);
+ if (err < 0) {
+ dev_err(info->dev, "BT wakeup test failed.\n");
+ goto err_clean;
+ }
+
+ spin_lock_irqsave(&info->lock, flags);
+ info->rx_enabled = gpio_get_value(info->host_wakeup_gpio);
+ hci_h4p_set_clk(info, &info->rx_clocks_en, info->rx_enabled);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+
+ kfree_skb(info->alive_cmd_skb);
+ info->alive_cmd_skb = NULL;
+ set_bit(HCI_RUNNING, &hdev->flags);
+
+ BT_DBG("hci up and running\n");
+ return 0;
+
+err_clean:
+ hci_h4p_hci_flush(hdev);
+ hci_h4p_reset_uart(info);
+ del_timer_sync(&info->lazy_release);
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+ gpio_set_value(info->reset_gpio, 0);
+ gpio_set_value(info->bt_wakeup_gpio, 0);
+ skb_queue_purge(&fw_queue);
+ kfree_skb(info->alive_cmd_skb);
+ info->alive_cmd_skb = NULL;
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+
+ if (retries++ < 3) {
+ dev_err(info->dev, "FW loading try %d fail. Retry.\n", retries);
+ goto again;
+ }
+
+ return err;
+}
+
+static int hci_h4p_hci_close(struct hci_dev *hdev)
+{
+ struct hci_h4p_info *info = hci_get_drvdata(hdev);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ hci_h4p_hci_flush(hdev);
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
+ hci_h4p_reset_uart(info);
+ del_timer_sync(&info->lazy_release);
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+ hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+ gpio_set_value(info->reset_gpio, 0);
+ gpio_set_value(info->bt_wakeup_gpio, 0);
+ kfree_skb(info->rx_skb);
+
+ return 0;
+}
+
+static int hci_h4p_hci_send_frame(struct sk_buff *skb)
+{
+ struct hci_h4p_info *info;
+ struct hci_dev *hdev = (struct hci_dev *)skb->dev;
+ int err = 0;
+
+ if (!hdev) {
+ printk(KERN_WARNING "hci_h4p: Frame for unknown device\n");
+ return -ENODEV;
+ }
+
+ BT_DBG("dev %p, skb %p\n", hdev, skb);
+
+ info = hci_get_drvdata(hdev);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+ dev_warn(info->dev, "Frame for non-running device\n");
+ return -EIO;
+ }
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ }
+
+ /* Push frame type to skb */
+ *skb_push(skb, 1) = (bt_cb(skb)->pkt_type);
+ /* We should allways send word aligned data to h4+ devices */
+ if (skb->len % 2) {
+ err = skb_pad(skb, 1);
+ if (!err)
+ *skb_put(skb, 1) = 0x00;
+ }
+ if (err)
+ return err;
+
+ skb_queue_tail(&info->txq, skb);
+ hci_h4p_enable_tx(info);
+
+ return 0;
+}
+
+static int hci_h4p_hci_ioctl(struct hci_dev *hdev, unsigned int cmd,
+ unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+static ssize_t hci_h4p_store_bdaddr(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hci_h4p_info *info = dev_get_drvdata(dev);
+ unsigned int bdaddr[6];
+ int ret, i;
+
+ ret = sscanf(buf, "%2x:%2x:%2x:%2x:%2x:%2x\n",
+ &bdaddr[0], &bdaddr[1], &bdaddr[2],
+ &bdaddr[3], &bdaddr[4], &bdaddr[5]);
+
+ if (ret != 6)
+ return -EINVAL;
+
+ for (i = 0; i < 6; i++)
+ info->bd_addr[i] = bdaddr[i] & 0xff;
+
+ return count;
+}
+
+static ssize_t hci_h4p_show_bdaddr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hci_h4p_info *info = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%pMR\n", info->bd_addr);
+}
+
+static DEVICE_ATTR(bdaddr, S_IRUGO | S_IWUSR, hci_h4p_show_bdaddr,
+ hci_h4p_store_bdaddr);
+
+static int hci_h4p_sysfs_create_files(struct device *dev)
+{
+ return device_create_file(dev, &dev_attr_bdaddr);
+}
+
+static void hci_h4p_sysfs_remove_files(struct device *dev)
+{
+ device_remove_file(dev, &dev_attr_bdaddr);
+}
+
+static int hci_h4p_register_hdev(struct hci_h4p_info *info)
+{
+ struct hci_dev *hdev;
+
+ /* Initialize and register HCI device */
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ dev_err(info->dev, "Can't allocate memory for device\n");
+ return -ENOMEM;
+ }
+ info->hdev = hdev;
+
+ hdev->bus = HCI_UART;
+ hci_set_drvdata(hdev, info);
+
+ hdev->open = hci_h4p_hci_open;
+ hdev->close = hci_h4p_hci_close;
+ hdev->flush = hci_h4p_hci_flush;
+ hdev->send = hci_h4p_hci_send_frame;
+ hdev->ioctl = hci_h4p_hci_ioctl;
+ set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+
+ SET_HCIDEV_DEV(hdev, info->dev);
+
+ if (hci_h4p_sysfs_create_files(info->dev) < 0) {
+ dev_err(info->dev, "failed to create sysfs files\n");
+ goto free;
+ }
+
+ if (hci_register_dev(hdev) >= 0)
+ return 0;
+
+ dev_err(info->dev, "hci_register failed %s.\n", hdev->name);
+ hci_h4p_sysfs_remove_files(info->dev);
+free:
+ hci_free_dev(info->hdev);
+ return -ENODEV;
+}
+
+static int hci_h4p_probe(struct platform_device *pdev)
+{
+ struct hci_h4p_platform_data *bt_plat_data;
+ struct hci_h4p_info *info;
+ int err;
+
+ dev_info(&pdev->dev, "Registering HCI H4P device\n");
+ info = kzalloc(sizeof(struct hci_h4p_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = &pdev->dev;
+ info->tx_enabled = 1;
+ info->rx_enabled = 1;
+ spin_lock_init(&info->lock);
+ spin_lock_init(&info->clocks_lock);
+ skb_queue_head_init(&info->txq);
+
+ if (pdev->dev.platform_data == NULL) {
+ dev_err(&pdev->dev, "Could not get Bluetooth config data\n");
+ kfree(info);
+ return -ENODATA;
+ }
+
+ bt_plat_data = pdev->dev.platform_data;
+ info->chip_type = bt_plat_data->chip_type;
+ info->bt_wakeup_gpio = bt_plat_data->bt_wakeup_gpio;
+ info->host_wakeup_gpio = bt_plat_data->host_wakeup_gpio;
+ info->reset_gpio = bt_plat_data->reset_gpio;
+ info->reset_gpio_shared = bt_plat_data->reset_gpio_shared;
+ info->bt_sysclk = bt_plat_data->bt_sysclk;
+
+ BT_DBG("RESET gpio: %d\n", info->reset_gpio);
+ BT_DBG("BTWU gpio: %d\n", info->bt_wakeup_gpio);
+ BT_DBG("HOSTWU gpio: %d\n", info->host_wakeup_gpio);
+ BT_DBG("sysclk: %d\n", info->bt_sysclk);
+
+ init_completion(&info->test_completion);
+ complete_all(&info->test_completion);
+
+ if (!info->reset_gpio_shared) {
+ err = gpio_request(info->reset_gpio, "bt_reset");
+ if (err < 0) {
+ dev_err(&pdev->dev, "Cannot get GPIO line %d\n",
+ info->reset_gpio);
+ goto cleanup_setup;
+ }
+ }
+
+ err = gpio_request(info->bt_wakeup_gpio, "bt_wakeup");
+ if (err < 0) {
+ dev_err(info->dev, "Cannot get GPIO line 0x%d",
+ info->bt_wakeup_gpio);
+ if (!info->reset_gpio_shared)
+ gpio_free(info->reset_gpio);
+ goto cleanup_setup;
+ }
+
+ err = gpio_request(info->host_wakeup_gpio, "host_wakeup");
+ if (err < 0) {
+ dev_err(info->dev, "Cannot get GPIO line %d",
+ info->host_wakeup_gpio);
+ if (!info->reset_gpio_shared)
+ gpio_free(info->reset_gpio);
+ gpio_free(info->bt_wakeup_gpio);
+ goto cleanup_setup;
+ }
+
+ gpio_direction_output(info->reset_gpio, 0);
+ gpio_direction_output(info->bt_wakeup_gpio, 0);
+ gpio_direction_input(info->host_wakeup_gpio);
+
+ info->irq = bt_plat_data->uart_irq;
+ info->uart_base = ioremap(bt_plat_data->uart_base, SZ_2K);
+ info->uart_iclk = clk_get(NULL, bt_plat_data->uart_iclk);
+ info->uart_fclk = clk_get(NULL, bt_plat_data->uart_fclk);
+
+ err = request_irq(info->irq, hci_h4p_interrupt, IRQF_DISABLED, "hci_h4p",
+ info);
+ if (err < 0) {
+ dev_err(info->dev, "hci_h4p: unable to get IRQ %d\n", info->irq);
+ goto cleanup;
+ }
+
+ err = request_irq(gpio_to_irq(info->host_wakeup_gpio),
+ hci_h4p_wakeup_interrupt, IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING | IRQF_DISABLED,
+ "hci_h4p_wkup", info);
+ if (err < 0) {
+ dev_err(info->dev, "hci_h4p: unable to get wakeup IRQ %d\n",
+ gpio_to_irq(info->host_wakeup_gpio));
+ free_irq(info->irq, info);
+ goto cleanup;
+ }
+
+ err = irq_set_irq_wake(gpio_to_irq(info->host_wakeup_gpio), 1);
+ if (err < 0) {
+ dev_err(info->dev, "hci_h4p: unable to set wakeup for IRQ %d\n",
+ gpio_to_irq(info->host_wakeup_gpio));
+ free_irq(info->irq, info);
+ free_irq(gpio_to_irq(info->host_wakeup_gpio), info);
+ goto cleanup;
+ }
+
+ init_timer_deferrable(&info->lazy_release);
+ info->lazy_release.function = hci_h4p_lazy_clock_release;
+ info->lazy_release.data = (unsigned long)info;
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+ err = hci_h4p_reset_uart(info);
+ if (err < 0)
+ goto cleanup_irq;
+ gpio_set_value(info->reset_gpio, 0);
+ hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+
+ platform_set_drvdata(pdev, info);
+
+ if (hci_h4p_register_hdev(info) < 0) {
+ dev_err(info->dev, "failed to register hci_h4p hci device\n");
+ goto cleanup_irq;
+ }
+
+ return 0;
+
+cleanup_irq:
+ free_irq(info->irq, (void *)info);
+ free_irq(gpio_to_irq(info->host_wakeup_gpio), info);
+cleanup:
+ gpio_set_value(info->reset_gpio, 0);
+ if (!info->reset_gpio_shared)
+ gpio_free(info->reset_gpio);
+ gpio_free(info->bt_wakeup_gpio);
+ gpio_free(info->host_wakeup_gpio);
+cleanup_setup:
+ kfree(info);
+ return err;
+}
+
+static int hci_h4p_remove(struct platform_device *pdev)
+{
+ struct hci_h4p_info *info;
+
+ info = platform_get_drvdata(pdev);
+
+ hci_h4p_sysfs_remove_files(info->dev);
+ hci_h4p_hci_close(info->hdev);
+ free_irq(gpio_to_irq(info->host_wakeup_gpio), info);
+ hci_unregister_dev(info->hdev);
+ hci_free_dev(info->hdev);
+ if (!info->reset_gpio_shared)
+ gpio_free(info->reset_gpio);
+ gpio_free(info->bt_wakeup_gpio);
+ gpio_free(info->host_wakeup_gpio);
+ free_irq(info->irq, (void *) info);
+ kfree(info);
+
+ return 0;
+}
+
+static struct platform_driver hci_h4p_driver = {
+ .probe = hci_h4p_probe,
+ .remove = hci_h4p_remove,
+ .driver = {
+ .name = "hci_h4p",
+ },
+};
+
+static int __init hci_h4p_init(void)
+{
+ int err = 0;
+
+ /* Register the driver with LDM */
+ err = platform_driver_register(&hci_h4p_driver);
+ if (err < 0)
+ printk(KERN_WARNING "failed to register hci_h4p driver\n");
+
+ return err;
+}
+
+static void __exit hci_h4p_exit(void)
+{
+ platform_driver_unregister(&hci_h4p_driver);
+}
+
+module_init(hci_h4p_init);
+module_exit(hci_h4p_exit);
+
+MODULE_ALIAS("platform:hci_h4p");
+MODULE_DESCRIPTION("Bluetooth h4 driver with nokia extensions");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ville Tervo");
+MODULE_FIRMWARE(FW_NAME_TI1271_PRELE);
+MODULE_FIRMWARE(FW_NAME_TI1271_LE);
+MODULE_FIRMWARE(FW_NAME_TI1271);
+MODULE_FIRMWARE(FW_NAME_BCM2048);
+MODULE_FIRMWARE(FW_NAME_CSR);
diff --git a/drivers/bluetooth/nokia_fw-bcm.c b/drivers/bluetooth/nokia_fw-bcm.c
new file mode 100644
index 0000000..0cf8535
--- /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\n");
+ err = hci_h4p_bcm_set_bdaddr(info, fw_skb);
+ if (err < 0) {
+ kfree_skb(fw_skb);
+ info->fw_error = err;
+ complete(&info->fw_completion);
+ return;
+ }
+ }
+
+ skb_queue_tail(&info->txq, fw_skb);
+ spin_lock_irqsave(&info->lock, flags);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+}
+
+
+int hci_h4p_bcm_send_fw(struct hci_h4p_info *info,
+ struct sk_buff_head *fw_queue)
+{
+ struct sk_buff *skb;
+ unsigned long flags, time;
+
+ info->fw_error = 0;
+
+ BT_DBG("Sending firmware\n");
+
+ time = jiffies;
+
+ info->fw_q = fw_queue;
+ skb = skb_dequeue(fw_queue);
+ if (!skb)
+ return -ENODATA;
+
+ BT_DBG("Sending commands\n");
+
+ /*
+ * Disable smart-idle as UART TX interrupts
+ * are not wake-up capable
+ */
+ hci_h4p_smart_idle(info, 0);
+
+ /* Check if this is bd_address packet */
+ init_completion(&info->fw_completion);
+ skb_queue_tail(&info->txq, skb);
+ spin_lock_irqsave(&info->lock, flags);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ if (!wait_for_completion_timeout(&info->fw_completion,
+ msecs_to_jiffies(2000))) {
+ dev_err(info->dev, "No reply to fw command\n");
+ return -ETIMEDOUT;
+ }
+
+ if (info->fw_error) {
+ dev_err(info->dev, "FW error\n");
+ return -EPROTO;
+ }
+
+ BT_DBG("Firmware sent in %d msecs\n",
+ jiffies_to_msecs(jiffies-time));
+
+ hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
+ hci_h4p_set_rts(info, 0);
+ hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
+ hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
+
+ return 0;
+}
diff --git a/drivers/bluetooth/nokia_fw-csr.c b/drivers/bluetooth/nokia_fw-csr.c
new file mode 100644
index 0000000..886b721
--- /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(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\n");
+ skb = skb_dequeue(fw_queue);
+
+ if (!skb)
+ return -ENOMSG;
+
+ /* Check if this is bd_address packet */
+ if (skb->data[15] == 0x01 && skb->data[16] == 0x00) {
+ offset = 21;
+ skb->data[offset + 1] = 0x00;
+ skb->data[offset + 5] = 0x00;
+
+ not_valid = 1;
+ for (i = 0; i < 6; i++) {
+ if (info->bd_addr[i] != 0x00) {
+ not_valid = 0;
+ break;
+ }
+ }
+
+ if (not_valid) {
+ dev_info(info->dev, "Valid bluetooth address not found,"
+ " setting some random\n");
+ /* When address is not valid, use some random */
+ memcpy(info->bd_addr, nokia_oui, 3);
+ get_random_bytes(info->bd_addr + 3, 3);
+ }
+
+ skb->data[offset + 7] = info->bd_addr[0];
+ skb->data[offset + 6] = info->bd_addr[1];
+ skb->data[offset + 4] = info->bd_addr[2];
+ skb->data[offset + 0] = info->bd_addr[3];
+ skb->data[offset + 3] = info->bd_addr[4];
+ skb->data[offset + 2] = info->bd_addr[5];
+ }
+
+ for (count = 1; ; count++) {
+ BT_DBG("Sending firmware command %d\n", count);
+ init_completion(&info->fw_completion);
+ skb_queue_tail(&info->txq, skb);
+ spin_lock_irqsave(&info->lock, flags);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ skb = skb_dequeue(fw_queue);
+ if (!skb)
+ break;
+
+ if (!wait_for_completion_timeout(&info->fw_completion,
+ msecs_to_jiffies(1000))) {
+ dev_err(info->dev, "No reply to fw command\n");
+ return -ETIMEDOUT;
+ }
+
+ if (info->fw_error) {
+ dev_err(info->dev, "FW error\n");
+ return -EPROTO;
+ }
+ };
+
+ /* Wait for chip warm reset */
+ retries = 100;
+ while ((!skb_queue_empty(&info->txq) ||
+ !(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT)) &&
+ retries--) {
+ msleep(10);
+ }
+ if (!retries) {
+ dev_err(info->dev, "Transmitter not empty\n");
+ return -ETIMEDOUT;
+ }
+
+ hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
+
+ if (hci_h4p_wait_for_cts(info, 1, 100)) {
+ dev_err(info->dev, "cts didn't deassert after final speed\n");
+ return -ETIMEDOUT;
+ }
+
+ retries = 100;
+ do {
+ init_completion(&info->init_completion);
+ hci_h4p_send_alive_packet(info);
+ retries--;
+ } while (!wait_for_completion_timeout(&info->init_completion, 100) &&
+ retries > 0);
+
+ if (!retries) {
+ dev_err(info->dev, "No alive reply after speed change\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
diff --git a/drivers/bluetooth/nokia_fw-ti1273.c b/drivers/bluetooth/nokia_fw-ti1273.c
new file mode 100644
index 0000000..fd82494
--- /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\n");
+
+ time = jiffies;
+
+ fw_q = fw_queue;
+ skb = skb_dequeue(fw_queue);
+ if (!skb)
+ return -ENODATA;
+
+ BT_DBG("Sending commands\n");
+ /* Check if this is bd_address packet */
+ init_completion(&info->fw_completion);
+ hci_h4p_smart_idle(info, 0);
+ skb_queue_tail(&info->txq, skb);
+ spin_lock_irqsave(&info->lock, flags);
+ hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
+ UART_IER_THRI);
+ spin_unlock_irqrestore(&info->lock, flags);
+
+ if (!wait_for_completion_timeout(&info->fw_completion,
+ msecs_to_jiffies(2000))) {
+ dev_err(info->dev, "No reply to fw command\n");
+ return -ETIMEDOUT;
+ }
+
+ if (info->fw_error) {
+ dev_err(info->dev, "FW error\n");
+ return -EPROTO;
+ }
+
+ BT_DBG("Firmware sent in %d msecs\n",
+ jiffies_to_msecs(jiffies-time));
+
+ hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
+ hci_h4p_set_rts(info, 0);
+ hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
+ if (hci_h4p_wait_for_cts(info, 1, 100)) {
+ dev_err(info->dev,
+ "cts didn't go down after final speed change\n");
+ return -ETIMEDOUT;
+ }
+ hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
+
+ return 0;
+}
diff --git a/drivers/bluetooth/nokia_fw.c b/drivers/bluetooth/nokia_fw.c
new file mode 100644
index 0000000..f69efd8
--- /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\n",
+ info->man_id, info->ver_id);
+ switch (info->man_id) {
+ case H4P_ID_TI1271:
+ switch (info->ver_id) {
+ case 0xe1:
+ err = request_firmware(fw_entry, FW_NAME_TI1271_PRELE,
+ info->dev);
+ break;
+ case 0xd1:
+ case 0xf1:
+ err = request_firmware(fw_entry, FW_NAME_TI1271_LE,
+ info->dev);
+ break;
+ default:
+ err = request_firmware(fw_entry, FW_NAME_TI1271,
+ info->dev);
+ }
+ break;
+ case H4P_ID_CSR:
+ err = request_firmware(fw_entry, FW_NAME_CSR, info->dev);
+ break;
+ case H4P_ID_BCM2048:
+ err = request_firmware(fw_entry, FW_NAME_BCM2048, info->dev);
+ break;
+ default:
+ dev_err(info->dev, "Invalid chip type\n");
+ *fw_entry = NULL;
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+static void hci_h4p_close_firmware(const struct firmware *fw_entry)
+{
+ release_firmware(fw_entry);
+}
+
+/* Read fw. Return length of the command. If no more commands in
+ * fw 0 is returned. In error case return value is negative.
+ */
+static int hci_h4p_read_fw_cmd(struct hci_h4p_info *info, struct sk_buff **skb,
+ const struct firmware *fw_entry, gfp_t how)
+{
+ unsigned int cmd_len;
+
+ if (fw_pos >= fw_entry->size)
+ return 0;
+
+ if (fw_pos + 2 > fw_entry->size) {
+ dev_err(info->dev, "Corrupted firmware image 1\n");
+ return -EMSGSIZE;
+ }
+
+ cmd_len = fw_entry->data[fw_pos++];
+ cmd_len += fw_entry->data[fw_pos++] << 8;
+ if (cmd_len == 0)
+ return 0;
+
+ if (fw_pos + cmd_len > fw_entry->size) {
+ dev_err(info->dev, "Corrupted firmware image 2\n");
+ return -EMSGSIZE;
+ }
+
+ *skb = bt_skb_alloc(cmd_len, how);
+ if (!*skb) {
+ dev_err(info->dev, "Cannot reserve memory for buffer\n");
+ return -ENOMEM;
+ }
+ memcpy(skb_put(*skb, cmd_len), &fw_entry->data[fw_pos], cmd_len);
+
+ fw_pos += cmd_len;
+
+ return (*skb)->len;
+}
+
+int hci_h4p_read_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue)
+{
+ const struct firmware *fw_entry = NULL;
+ struct sk_buff *skb = NULL;
+ int err;
+
+ err = hci_h4p_open_firmware(info, &fw_entry);
+ if (err < 0 || !fw_entry)
+ goto err_clean;
+
+ while ((err = hci_h4p_read_fw_cmd(info, &skb, fw_entry, GFP_KERNEL))) {
+ if (err < 0 || !skb)
+ goto err_clean;
+
+ skb_queue_tail(fw_queue, skb);
+ }
+
+ /* Chip detection code does neg and alive stuff
+ * discard two first skbs */
+ skb = skb_dequeue(fw_queue);
+ if (!skb) {
+ err = -EMSGSIZE;
+ goto err_clean;
+ }
+ kfree_skb(skb);
+ skb = skb_dequeue(fw_queue);
+ if (!skb) {
+ err = -EMSGSIZE;
+ goto err_clean;
+ }
+ kfree_skb(skb);
+
+err_clean:
+ hci_h4p_close_firmware(fw_entry);
+ return err;
+}
+
+int hci_h4p_send_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue)
+{
+ int err;
+
+ switch (info->man_id) {
+ case H4P_ID_CSR:
+ err = hci_h4p_bc4_send_fw(info, fw_queue);
+ break;
+ case H4P_ID_TI1271:
+ err = hci_h4p_ti1273_send_fw(info, fw_queue);
+ break;
+ case H4P_ID_BCM2048:
+ err = hci_h4p_bcm_send_fw(info, fw_queue);
+ break;
+ default:
+ dev_err(info->dev, "Don't know how to send firmware\n");
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
+{
+ switch (info->man_id) {
+ case H4P_ID_CSR:
+ hci_h4p_bc4_parse_fw_event(info, skb);
+ break;
+ case H4P_ID_TI1271:
+ hci_h4p_ti1273_parse_fw_event(info, skb);
+ break;
+ case H4P_ID_BCM2048:
+ hci_h4p_bcm_parse_fw_event(info, skb);
+ break;
+ default:
+ dev_err(info->dev, "Don't know how to parse fw event\n");
+ info->fw_error = -EINVAL;
+ }
+
+ return;
+}
diff --git a/drivers/bluetooth/nokia_uart.c b/drivers/bluetooth/nokia_uart.c
new file mode 100644
index 0000000..c19b8d2
--- /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\n", speed);
+
+ if (speed >= 460800) {
+ divisor = UART_CLOCK / 13 / speed;
+ mdr1 = 3;
+ } else {
+ divisor = UART_CLOCK / 16 / speed;
+ mdr1 = 0;
+ }
+
+ /* Make sure UART mode is disabled */
+ hci_h4p_outb(info, UART_OMAP_MDR1, 7);
+
+ lcr = hci_h4p_inb(info, UART_LCR);
+ hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB); /* Set DLAB */
+ hci_h4p_outb(info, UART_DLL, divisor & 0xff); /* Set speed */
+ hci_h4p_outb(info, UART_DLM, divisor >> 8);
+ hci_h4p_outb(info, UART_LCR, lcr);
+
+ /* Make sure UART mode is enabled */
+ hci_h4p_outb(info, UART_OMAP_MDR1, mdr1);
+}
+
+int hci_h4p_reset_uart(struct hci_h4p_info *info)
+{
+ int count = 0;
+
+ /* Reset the UART */
+ hci_h4p_outb(info, UART_OMAP_SYSC, UART_SYSC_OMAP_RESET);
+ while (!(hci_h4p_inb(info, UART_OMAP_SYSS) & UART_SYSS_RESETDONE)) {
+ if (count++ > 100) {
+ dev_err(info->dev, "hci_h4p: UART reset timeout\n");
+ return -ENODEV;
+ }
+ udelay(1);
+ }
+
+ return 0;
+}
+
+void hci_h4p_store_regs(struct hci_h4p_info *info)
+{
+ u16 lcr = 0;
+
+ lcr = hci_h4p_inb(info, UART_LCR);
+ hci_h4p_outb(info, UART_LCR, 0xBF);
+ info->dll = hci_h4p_inb(info, UART_DLL);
+ info->dlh = hci_h4p_inb(info, UART_DLM);
+ info->efr = hci_h4p_inb(info, UART_EFR);
+ hci_h4p_outb(info, UART_LCR, lcr);
+ info->mdr1 = hci_h4p_inb(info, UART_OMAP_MDR1);
+ info->ier = hci_h4p_inb(info, UART_IER);
+}
+
+void hci_h4p_restore_regs(struct hci_h4p_info *info)
+{
+ u16 lcr = 0;
+
+ hci_h4p_init_uart(info);
+
+ hci_h4p_outb(info, UART_OMAP_MDR1, 7);
+ lcr = hci_h4p_inb(info, UART_LCR);
+ hci_h4p_outb(info, UART_LCR, 0xBF);
+ hci_h4p_outb(info, UART_DLL, info->dll); /* Set speed */
+ hci_h4p_outb(info, UART_DLM, info->dlh);
+ hci_h4p_outb(info, UART_EFR, info->efr);
+ hci_h4p_outb(info, UART_LCR, lcr);
+ hci_h4p_outb(info, UART_OMAP_MDR1, info->mdr1);
+ hci_h4p_outb(info, UART_IER, info->ier);
+}
+
+void hci_h4p_init_uart(struct hci_h4p_info *info)
+{
+ u8 mcr, efr;
+
+ /* Enable and setup FIFO */
+ hci_h4p_outb(info, UART_OMAP_MDR1, 0x00);
+
+ hci_h4p_outb(info, UART_LCR, 0xbf);
+ efr = hci_h4p_inb(info, UART_EFR);
+ hci_h4p_outb(info, UART_EFR, UART_EFR_ECB);
+ hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);
+ mcr = hci_h4p_inb(info, UART_MCR);
+ hci_h4p_outb(info, UART_MCR, UART_MCR_TCRTLR);
+ hci_h4p_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT |
+ (3 << 6) | (0 << 4));
+ hci_h4p_outb(info, UART_LCR, 0xbf);
+ hci_h4p_outb(info, UART_TI752_TLR, 0xed);
+ hci_h4p_outb(info, UART_TI752_TCR, 0xef);
+ hci_h4p_outb(info, UART_EFR, efr);
+ hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);
+ hci_h4p_outb(info, UART_MCR, 0x00);
+ hci_h4p_outb(info, UART_LCR, UART_LCR_WLEN8);
+ hci_h4p_outb(info, UART_IER, UART_IER_RDI);
+ hci_h4p_outb(info, UART_OMAP_SYSC, (1 << 0) | (1 << 2) | (2 << 3));
+}
diff --git a/include/linux/bluetooth/hci_h4p.h b/include/linux/bluetooth/hci_h4p.h
new file mode 100644
index 0000000..30d169d
--- /dev/null
+++ b/include/linux/bluetooth/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
* Re: [PATCH v3 1/2] sdp: Add MAP_PROFILE_ID
From: Luiz Augusto von Dentz @ 2014-01-02 14:18 UTC (permalink / raw)
To: Lukasz Rymanowski
Cc: linux-bluetooth@vger.kernel.org, Johan Hedberg, Szymon Janc
In-Reply-To: <1388667286-28993-1-git-send-email-lukasz.rymanowski@tieto.com>
Hi Lukasz,
On Thu, Jan 2, 2014 at 2:54 PM, Lukasz Rymanowski
<lukasz.rymanowski@tieto.com> wrote:
> ---
> lib/sdp.h | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/lib/sdp.h b/lib/sdp.h
> index f2f2484..c6dfc27 100644
> --- a/lib/sdp.h
> +++ b/lib/sdp.h
> @@ -207,6 +207,7 @@ extern "C" {
> #define PBAP_PCE_PROFILE_ID PBAP_PCE_SVCLASS_ID
> #define PBAP_PSE_PROFILE_ID PBAP_PSE_SVCLASS_ID
> #define PBAP_PROFILE_ID PBAP_SVCLASS_ID
> +#define MAP_PROFILE_ID MAP_SVCLASS_ID
> #define PNP_INFO_PROFILE_ID PNP_INFO_SVCLASS_ID
> #define GENERIC_NETWORKING_PROFILE_ID GENERIC_NETWORKING_SVCLASS_ID
> #define GENERIC_FILETRANS_PROFILE_ID GENERIC_FILETRANS_SVCLASS_ID
> --
> 1.8.4
>
Applied, thanks.
--
Luiz Augusto von Dentz
^ permalink raw reply
* [PATCH v3 2/2] android/map: Add support for MAP/MAS
From: Lukasz Rymanowski @ 2014-01-02 12:54 UTC (permalink / raw)
To: linux-bluetooth; +Cc: johan.hedberg, szymon.janc, luiz.dentz, Lukasz Rymanowski
In-Reply-To: <1388667286-28993-1-git-send-email-lukasz.rymanowski@tieto.com>
Theres no HAL API exposed to application to register different MAS
instances. Android 4.4 does support only MAS SMS. All Instance ID,
supported msg type and rfcomm channel are hardcoded in the stack.
Unfortunatelly we need to do the same way and wait for new HAL API
which is expected.
---
android/socket.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 80 insertions(+), 2 deletions(-)
diff --git a/android/socket.c b/android/socket.c
index 15c1bb8..11d64f8 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -52,6 +52,14 @@
#define SVC_HINT_OBEX 0x10
+/* Hardcoded MAP stuff needed for MAS SMS Instance.*/
+#define DEFAULT_MAS_INSTANCE 0x00
+
+#define MAP_MSG_TYPE_SMS_GSM 0x02
+#define MAP_MSG_TYPE_SMS_CDMA 0x04
+#define DEFAULT_MAS_MSG_TYPE (MAP_MSG_TYPE_SMS_GSM | MAP_MSG_TYPE_SMS_CDMA)
+
+
static bdaddr_t adapter_addr;
/* Simple list of RFCOMM server sockets */
@@ -311,6 +319,76 @@ static sdp_record_t *create_pbap_record(uint8_t chan, const char *svc_name)
return record;
}
+static sdp_record_t *create_mas_record(uint8_t chan, const char *svc_name)
+{
+ const char *service_name = "MAP MAS SMS";
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, mse_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_data_t *channel;
+ uint8_t minst = DEFAULT_MAS_INSTANCE;
+ uint8_t mtype = DEFAULT_MAS_MSG_TYPE;
+ sdp_record_t *record;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ record->handle = sdp_next_handle();
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&mse_uuid, MAP_MSE_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &mse_uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, MAP_PROFILE_ID);
+ profile[0].version = 0x0101;
+ pfseq = sdp_list_append(NULL, profile);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_attr_add_new(record, SDP_ATTR_MAS_INSTANCE_ID, SDP_UINT8, &minst);
+ sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_MESSAGE_TYPES, SDP_UINT8,
+ &mtype);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &chan);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto[2] = sdp_list_append(NULL, &obex_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ if (svc_name)
+ service_name = svc_name;
+
+ sdp_set_info_attr(record, service_name, NULL, NULL);
+
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(proto[2], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(pfseq, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(svclass_id, NULL);
+
+ return record;
+}
+
static sdp_record_t *create_spp_record(uint8_t chan, const char *svc_name)
{
const char *service_name = "Serial Port";
@@ -411,9 +489,9 @@ static const struct profile_info {
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
},
.channel = MAS_DEFAULT_CHANNEL,
- .svc_hint = 0,
+ .svc_hint = SVC_HINT_OBEX,
.sec_level = BT_IO_SEC_MEDIUM,
- .create_record = NULL
+ .create_record = create_mas_record
}, {
.uuid = {
0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
--
1.8.4
^ permalink raw reply related
* [PATCH v3 1/2] sdp: Add MAP_PROFILE_ID
From: Lukasz Rymanowski @ 2014-01-02 12:54 UTC (permalink / raw)
To: linux-bluetooth; +Cc: johan.hedberg, szymon.janc, luiz.dentz, Lukasz Rymanowski
---
lib/sdp.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/sdp.h b/lib/sdp.h
index f2f2484..c6dfc27 100644
--- a/lib/sdp.h
+++ b/lib/sdp.h
@@ -207,6 +207,7 @@ extern "C" {
#define PBAP_PCE_PROFILE_ID PBAP_PCE_SVCLASS_ID
#define PBAP_PSE_PROFILE_ID PBAP_PSE_SVCLASS_ID
#define PBAP_PROFILE_ID PBAP_SVCLASS_ID
+#define MAP_PROFILE_ID MAP_SVCLASS_ID
#define PNP_INFO_PROFILE_ID PNP_INFO_SVCLASS_ID
#define GENERIC_NETWORKING_PROFILE_ID GENERIC_NETWORKING_SVCLASS_ID
#define GENERIC_FILETRANS_PROFILE_ID GENERIC_FILETRANS_SVCLASS_ID
--
1.8.4
^ permalink raw reply related
* [PATCH v2 2/2] android/map: Add support for MAP/MAS
From: Lukasz Rymanowski @ 2014-01-02 12:51 UTC (permalink / raw)
To: linux-bluetooth; +Cc: johan.hedberg, szymon.janc, luiz.dentz, Lukasz Rymanowski
In-Reply-To: <1388667100-28806-1-git-send-email-lukasz.rymanowski@tieto.com>
Theres no HAL API exposed to application to register different MAS
instances. Android 4.4 does support only MAS SMS. All Instance ID,
supported msg type and rfcomm channel are hardcoded in the stack.
Unfortunatelly we need to do the same way and wait for new HAL API
which is expected.
Change-Id: Ie151e34db8eed1e07c73aeb400bdfd2e5bd79f0d
---
android/socket.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 80 insertions(+), 2 deletions(-)
diff --git a/android/socket.c b/android/socket.c
index 15c1bb8..11d64f8 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -52,6 +52,14 @@
#define SVC_HINT_OBEX 0x10
+/* Hardcoded MAP stuff needed for MAS SMS Instance.*/
+#define DEFAULT_MAS_INSTANCE 0x00
+
+#define MAP_MSG_TYPE_SMS_GSM 0x02
+#define MAP_MSG_TYPE_SMS_CDMA 0x04
+#define DEFAULT_MAS_MSG_TYPE (MAP_MSG_TYPE_SMS_GSM | MAP_MSG_TYPE_SMS_CDMA)
+
+
static bdaddr_t adapter_addr;
/* Simple list of RFCOMM server sockets */
@@ -311,6 +319,76 @@ static sdp_record_t *create_pbap_record(uint8_t chan, const char *svc_name)
return record;
}
+static sdp_record_t *create_mas_record(uint8_t chan, const char *svc_name)
+{
+ const char *service_name = "MAP MAS SMS";
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, mse_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_data_t *channel;
+ uint8_t minst = DEFAULT_MAS_INSTANCE;
+ uint8_t mtype = DEFAULT_MAS_MSG_TYPE;
+ sdp_record_t *record;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ record->handle = sdp_next_handle();
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&mse_uuid, MAP_MSE_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &mse_uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, MAP_PROFILE_ID);
+ profile[0].version = 0x0101;
+ pfseq = sdp_list_append(NULL, profile);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_attr_add_new(record, SDP_ATTR_MAS_INSTANCE_ID, SDP_UINT8, &minst);
+ sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_MESSAGE_TYPES, SDP_UINT8,
+ &mtype);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &chan);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto[2] = sdp_list_append(NULL, &obex_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ if (svc_name)
+ service_name = svc_name;
+
+ sdp_set_info_attr(record, service_name, NULL, NULL);
+
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(proto[2], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(pfseq, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(svclass_id, NULL);
+
+ return record;
+}
+
static sdp_record_t *create_spp_record(uint8_t chan, const char *svc_name)
{
const char *service_name = "Serial Port";
@@ -411,9 +489,9 @@ static const struct profile_info {
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
},
.channel = MAS_DEFAULT_CHANNEL,
- .svc_hint = 0,
+ .svc_hint = SVC_HINT_OBEX,
.sec_level = BT_IO_SEC_MEDIUM,
- .create_record = NULL
+ .create_record = create_mas_record
}, {
.uuid = {
0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
--
1.8.4
^ permalink raw reply related
* [PATCH v2 1/2] sdp: Add MAP_PROFILE_ID
From: Lukasz Rymanowski @ 2014-01-02 12:51 UTC (permalink / raw)
To: linux-bluetooth; +Cc: johan.hedberg, szymon.janc, luiz.dentz, Lukasz Rymanowski
Change-Id: I3e9d4c591584e08babf979f8dc99827e12104644
---
lib/sdp.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/sdp.h b/lib/sdp.h
index f2f2484..c6dfc27 100644
--- a/lib/sdp.h
+++ b/lib/sdp.h
@@ -207,6 +207,7 @@ extern "C" {
#define PBAP_PCE_PROFILE_ID PBAP_PCE_SVCLASS_ID
#define PBAP_PSE_PROFILE_ID PBAP_PSE_SVCLASS_ID
#define PBAP_PROFILE_ID PBAP_SVCLASS_ID
+#define MAP_PROFILE_ID MAP_SVCLASS_ID
#define PNP_INFO_PROFILE_ID PNP_INFO_SVCLASS_ID
#define GENERIC_NETWORKING_PROFILE_ID GENERIC_NETWORKING_SVCLASS_ID
#define GENERIC_FILETRANS_PROFILE_ID GENERIC_FILETRANS_SVCLASS_ID
--
1.8.4
^ permalink raw reply related
* [PATCH BlueZ v2 10/10] android/A2DP: Add stream suspend command/response struct
From: Luiz Augusto von Dentz @ 2014-01-02 11:58 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1388663914-25003-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds the definitions to stream suspend command and response.
---
android/a2dp.c | 10 ++++++++++
android/hal-msg.h | 5 +++++
2 files changed, 15 insertions(+)
diff --git a/android/a2dp.c b/android/a2dp.c
index d3c02d6..dc2890c 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -392,6 +392,14 @@ static void bt_stream_resume(const void *buf, uint16_t len)
HAL_STATUS_FAILED);
}
+static void bt_stream_suspend(const void *buf, uint16_t len)
+{
+ DBG("Not Implemented");
+
+ audio_ipc_send_rsp(AUDIO_SERVICE_ID_CORE, AUDIO_OP_SUSPEND_STREAM,
+ HAL_STATUS_FAILED);
+}
+
static const struct ipc_handler audio_handlers[] = {
/* AUDIO_OP_OPEN */
{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
@@ -403,6 +411,8 @@ static const struct ipc_handler audio_handlers[] = {
{ bt_stream_close, false, sizeof(struct audio_cmd_close_stream) },
/* AUDIO_OP_RESUME_STREAM */
{ bt_stream_resume, false, sizeof(struct audio_cmd_resume_stream) },
+ /* AUDIO_OP_SUSPEND_STREAM */
+ { bt_stream_suspend, false, sizeof(struct audio_cmd_suspend_stream) },
};
bool bt_a2dp_register(const bdaddr_t *addr)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 259b687..6b2ec88 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -613,3 +613,8 @@ struct audio_cmd_close_stream {
struct audio_cmd_resume_stream {
uint8_t id;
} __attribute__((packed));
+
+#define AUDIO_OP_SUSPEND_STREAM 0x06
+struct audio_cmd_suspend_stream {
+ uint8_t id;
+} __attribute__((packed));
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ v2 09/10] android/A2DP: Add stream resume command/response struct
From: Luiz Augusto von Dentz @ 2014-01-02 11:58 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1388663914-25003-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds the definitions to stream resume command and response.
---
android/a2dp.c | 10 ++++++++++
android/hal-msg.h | 5 +++++
2 files changed, 15 insertions(+)
diff --git a/android/a2dp.c b/android/a2dp.c
index eac427d..d3c02d6 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -384,6 +384,14 @@ static void bt_stream_close(const void *buf, uint16_t len)
HAL_STATUS_FAILED);
}
+static void bt_stream_resume(const void *buf, uint16_t len)
+{
+ DBG("Not Implemented");
+
+ audio_ipc_send_rsp(AUDIO_SERVICE_ID_CORE, AUDIO_OP_RESUME_STREAM,
+ HAL_STATUS_FAILED);
+}
+
static const struct ipc_handler audio_handlers[] = {
/* AUDIO_OP_OPEN */
{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
@@ -393,6 +401,8 @@ static const struct ipc_handler audio_handlers[] = {
{ bt_stream_open, false, sizeof(struct audio_cmd_open_stream) },
/* AUDIO_OP_CLOSE_STREAM */
{ bt_stream_close, false, sizeof(struct audio_cmd_close_stream) },
+ /* AUDIO_OP_RESUME_STREAM */
+ { bt_stream_resume, false, sizeof(struct audio_cmd_resume_stream) },
};
bool bt_a2dp_register(const bdaddr_t *addr)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index a708157..259b687 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -608,3 +608,8 @@ struct audio_rsp_open_stream {
struct audio_cmd_close_stream {
uint8_t id;
} __attribute__((packed));
+
+#define AUDIO_OP_RESUME_STREAM 0x05
+struct audio_cmd_resume_stream {
+ uint8_t id;
+} __attribute__((packed));
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ v2 08/10] android/A2DP: Add stream close command/response struct
From: Luiz Augusto von Dentz @ 2014-01-02 11:58 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1388663914-25003-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds the definitions to stream close command and response.
---
android/a2dp.c | 10 ++++++++++
android/hal-msg.h | 5 +++++
2 files changed, 15 insertions(+)
diff --git a/android/a2dp.c b/android/a2dp.c
index 7709b15..eac427d 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -376,6 +376,14 @@ static void bt_stream_open(const void *buf, uint16_t len)
HAL_STATUS_FAILED);
}
+static void bt_stream_close(const void *buf, uint16_t len)
+{
+ DBG("Not Implemented");
+
+ audio_ipc_send_rsp(AUDIO_SERVICE_ID_CORE, AUDIO_OP_CLOSE_STREAM,
+ HAL_STATUS_FAILED);
+}
+
static const struct ipc_handler audio_handlers[] = {
/* AUDIO_OP_OPEN */
{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
@@ -383,6 +391,8 @@ static const struct ipc_handler audio_handlers[] = {
{ bt_audio_close, false, sizeof(struct audio_cmd_close) },
/* AUDIO_OP_OPEN_STREAM */
{ bt_stream_open, false, sizeof(struct audio_cmd_open_stream) },
+ /* AUDIO_OP_CLOSE_STREAM */
+ { bt_stream_close, false, sizeof(struct audio_cmd_close_stream) },
};
bool bt_a2dp_register(const bdaddr_t *addr)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index ff62de0..a708157 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -603,3 +603,8 @@ struct audio_rsp_open_stream {
uint8_t len;
uint8_t data[0];
} __attribute__((packed));
+
+#define AUDIO_OP_CLOSE_STREAM 0x04
+struct audio_cmd_close_stream {
+ uint8_t id;
+} __attribute__((packed));
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ v2 07/10] android/A2DP: Add stream open command/response struct
From: Luiz Augusto von Dentz @ 2014-01-02 11:58 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1388663914-25003-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds the definitions to stream open command and response.
---
android/a2dp.c | 10 ++++++++++
android/hal-msg.h | 10 ++++++++++
2 files changed, 20 insertions(+)
diff --git a/android/a2dp.c b/android/a2dp.c
index e3d58ca..7709b15 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -368,11 +368,21 @@ static void bt_audio_close(const void *buf, uint16_t len)
HAL_STATUS_FAILED);
}
+static void bt_stream_open(const void *buf, uint16_t len)
+{
+ DBG("Not Implemented");
+
+ audio_ipc_send_rsp(AUDIO_SERVICE_ID_CORE, AUDIO_OP_OPEN_STREAM,
+ HAL_STATUS_FAILED);
+}
+
static const struct ipc_handler audio_handlers[] = {
/* AUDIO_OP_OPEN */
{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
/* AUDIO_OP_CLOSE */
{ bt_audio_close, false, sizeof(struct audio_cmd_close) },
+ /* AUDIO_OP_OPEN_STREAM */
+ { bt_stream_open, false, sizeof(struct audio_cmd_open_stream) },
};
bool bt_a2dp_register(const bdaddr_t *addr)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index d1135df..ff62de0 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -593,3 +593,13 @@ struct audio_rsp_open {
struct audio_cmd_close {
uint8_t id;
} __attribute__((packed));
+
+#define AUDIO_OP_OPEN_STREAM 0x03
+struct audio_cmd_open_stream {
+ uint8_t id;
+} __attribute__((packed));
+
+struct audio_rsp_open_stream {
+ uint8_t len;
+ uint8_t data[0];
+} __attribute__((packed));
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ v2 06/10] android/A2DP: Add audio close command/response struct
From: Luiz Augusto von Dentz @ 2014-01-02 11:58 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1388663914-25003-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds the definitions to audio close command and response.
---
android/a2dp.c | 10 ++++++++++
android/hal-msg.h | 5 +++++
2 files changed, 15 insertions(+)
diff --git a/android/a2dp.c b/android/a2dp.c
index 325c282..e3d58ca 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -360,9 +360,19 @@ static void bt_audio_open(const void *buf, uint16_t len)
HAL_STATUS_FAILED);
}
+static void bt_audio_close(const void *buf, uint16_t len)
+{
+ DBG("Not Implemented");
+
+ audio_ipc_send_rsp(AUDIO_SERVICE_ID_CORE, AUDIO_OP_CLOSE,
+ HAL_STATUS_FAILED);
+}
+
static const struct ipc_handler audio_handlers[] = {
/* AUDIO_OP_OPEN */
{ bt_audio_open, true, sizeof(struct audio_cmd_open) },
+ /* AUDIO_OP_CLOSE */
+ { bt_audio_close, false, sizeof(struct audio_cmd_close) },
};
bool bt_a2dp_register(const bdaddr_t *addr)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 1036988..d1135df 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -588,3 +588,8 @@ struct audio_cmd_open {
struct audio_rsp_open {
uint8_t id;
} __attribute__((packed));
+
+#define AUDIO_OP_CLOSE 0x02
+struct audio_cmd_close {
+ uint8_t id;
+} __attribute__((packed));
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ v2 05/10] android/A2DP: Add audio open command/response struct
From: Luiz Augusto von Dentz @ 2014-01-02 11:58 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1388663914-25003-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds the definitions to audio open command and response.
---
android/a2dp.c | 10 ++++++++++
android/audio-ipc-api.txt | 2 +-
android/hal-msg.h | 18 ++++++++++++++++++
3 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 7550644..325c282 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -352,7 +352,17 @@ static sdp_record_t *a2dp_record(void)
return record;
}
+static void bt_audio_open(const void *buf, uint16_t len)
+{
+ DBG("Not Implemented");
+
+ audio_ipc_send_rsp(AUDIO_SERVICE_ID_CORE, AUDIO_OP_OPEN,
+ HAL_STATUS_FAILED);
+}
+
static const struct ipc_handler audio_handlers[] = {
+ /* AUDIO_OP_OPEN */
+ { bt_audio_open, true, sizeof(struct audio_cmd_open) },
};
bool bt_a2dp_register(const bdaddr_t *addr)
diff --git a/android/audio-ipc-api.txt b/android/audio-ipc-api.txt
index 1c42800..37a1569 100644
--- a/android/audio-ipc-api.txt
+++ b/android/audio-ipc-api.txt
@@ -49,9 +49,9 @@ Identifier: "audio" (BT_AUDIO_ID)
Command parameters: Service UUID (16 octets)
Codec ID (1 octet)
+ Number of codec presets (1 octet)
Codec capabilities length (1 octet)
Codec capabilities (variable)
- Number of codec presets (1 octet)
Codec preset # length (1 octet)
Codec preset # configuration (variable)
...
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 267f9b2..1036988 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -570,3 +570,21 @@ struct hal_ev_a2dp_audio_state {
uint8_t state;
uint8_t bdaddr[6];
} __attribute__((packed));
+
+#define AUDIO_OP_OPEN 0x01
+struct audio_preset {
+ uint8_t len;
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct audio_cmd_open {
+ uint16_t uuid;
+ uint8_t codec;
+ uint8_t presets;
+ uint8_t len;
+ struct audio_preset preset[0];
+} __attribute__((packed));
+
+struct audio_rsp_open {
+ uint8_t id;
+} __attribute__((packed));
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ v2 04/10] android/A2DP: Add initial code to handle audio IPC commands
From: Luiz Augusto von Dentz @ 2014-01-02 11:58 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1388663914-25003-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds initial code to handle audio IPC commands.
---
android/a2dp.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/android/a2dp.c b/android/a2dp.c
index 581d094..7550644 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -352,6 +352,9 @@ static sdp_record_t *a2dp_record(void)
return record;
}
+static const struct ipc_handler audio_handlers[] = {
+};
+
bool bt_a2dp_register(const bdaddr_t *addr)
{
GError *err = NULL;
@@ -359,6 +362,8 @@ bool bt_a2dp_register(const bdaddr_t *addr)
DBG("");
+ audio_ipc_init();
+
bacpy(&adapter_addr, addr);
server = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
@@ -388,6 +393,9 @@ bool bt_a2dp_register(const bdaddr_t *addr)
ipc_register(HAL_SERVICE_ID_A2DP, cmd_handlers,
G_N_ELEMENTS(cmd_handlers));
+ audio_ipc_register(AUDIO_SERVICE_ID_CORE, audio_handlers,
+ G_N_ELEMENTS(audio_handlers));
+
return true;
fail:
@@ -411,8 +419,9 @@ void bt_a2dp_unregister(void)
g_slist_foreach(devices, a2dp_device_disconnected, NULL);
devices = NULL;
-
ipc_unregister(HAL_SERVICE_ID_A2DP);
+ audio_ipc_unregister(AUDIO_SERVICE_ID_CORE);
+
bt_adapter_remove_record(record_id);
record_id = 0;
@@ -421,4 +430,6 @@ void bt_a2dp_unregister(void)
g_io_channel_unref(server);
server = NULL;
}
+
+ audio_ipc_cleanup();
}
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ v2 03/10] android/ipc: Add audio_ipc_send_rsp and audio_ipc_send_rsp_full
From: Luiz Augusto von Dentz @ 2014-01-02 11:58 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1388663914-25003-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
These functions can be used to respond to commands recieved over audio
IPC.
---
android/ipc.c | 24 ++++++++++++++++++++++++
android/ipc.h | 4 ++++
2 files changed, 28 insertions(+)
diff --git a/android/ipc.c b/android/ipc.c
index f18e1eb..a116a20 100644
--- a/android/ipc.c
+++ b/android/ipc.c
@@ -414,6 +414,30 @@ void audio_ipc_cleanup(void)
}
}
+void audio_ipc_send_rsp(uint8_t service_id, uint8_t opcode, uint8_t status)
+{
+ struct hal_status s;
+ int sk;
+
+ sk = g_io_channel_unix_get_fd(audio_io);
+
+ if (status == HAL_STATUS_SUCCESS) {
+ ipc_send(sk, service_id, opcode, 0, NULL, -1);
+ return;
+ }
+
+ s.code = status;
+
+ ipc_send(sk, service_id, HAL_OP_STATUS, sizeof(s), &s, -1);
+}
+
+void audio_ipc_send_rsp_full(uint8_t service_id, uint8_t opcode, uint16_t len,
+ void *param, int fd)
+{
+ ipc_send(g_io_channel_unix_get_fd(audio_io), service_id, opcode, len,
+ param, fd);
+}
+
void audio_ipc_register(uint8_t service, const struct ipc_handler *handlers,
uint8_t size)
{
diff --git a/android/ipc.h b/android/ipc.h
index 44d5a5d..f224367 100644
--- a/android/ipc.h
+++ b/android/ipc.h
@@ -41,6 +41,10 @@ void ipc_unregister(uint8_t service);
void audio_ipc_init(void);
void audio_ipc_cleanup(void);
+void audio_ipc_send_rsp(uint8_t service_id, uint8_t opcode, uint8_t status);
+void audio_ipc_send_rsp_full(uint8_t service_id, uint8_t opcode, uint16_t len,
+ void *param, int fd);
+
void audio_ipc_register(uint8_t service, const struct ipc_handler *handlers,
uint8_t size);
void audio_ipc_unregister(uint8_t service);
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ v2 02/10] android/ipc: Add message handling for audio IPC
From: Luiz Augusto von Dentz @ 2014-01-02 11:58 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1388663914-25003-1-git-send-email-luiz.dentz@gmail.com>
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This adds audio_ipc_register and audio_ipc_unregister and adapt
ipc_handle_msg to be able to handle audio services messages.
---
android/hal-msg.h | 4 +++
android/ipc.c | 78 +++++++++++++++++++++++++++++++++++--------------------
android/ipc.h | 4 +++
3 files changed, 58 insertions(+), 28 deletions(-)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index b14eced..267f9b2 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -48,6 +48,10 @@ struct hal_hdr {
#define HAL_SERVICE_ID_MAX HAL_SERVICE_ID_GATT
+#define AUDIO_SERVICE_ID_CORE 0
+
+#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_ID_CORE
+
/* Core Service */
#define HAL_STATUS_SUCCESS 0x00
diff --git a/android/ipc.c b/android/ipc.c
index 4c5a77e..f18e1eb 100644
--- a/android/ipc.c
+++ b/android/ipc.c
@@ -46,51 +46,46 @@ struct service_handler {
};
static struct service_handler services[HAL_SERVICE_ID_MAX + 1];
+static struct service_handler audio_services[AUDIO_SERVICE_ID_MAX + 1];
static GIOChannel *cmd_io = NULL;
static GIOChannel *notif_io = NULL;
static GIOChannel *audio_io = NULL;
-static void ipc_handle_msg(const void *buf, ssize_t len)
+static int ipc_handle_msg(struct service_handler *handlers, size_t max_index,
+ const void *buf, ssize_t len)
{
const struct hal_hdr *msg = buf;
const struct ipc_handler *handler;
if (len < (ssize_t) sizeof(*msg)) {
- error("IPC: message too small (%zd bytes), terminating", len);
- raise(SIGTERM);
- return;
+ DBG("message too small (%zd bytes)", len);
+ return -EBADMSG;
}
if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
- error("IPC: message malformed (%zd bytes), terminating", len);
- raise(SIGTERM);
- return;
+ DBG("message malformed (%zd bytes)", len);
+ return -EBADMSG;
}
/* if service is valid */
- if (msg->service_id > HAL_SERVICE_ID_MAX) {
- error("IPC: unknown service (0x%x), terminating",
- msg->service_id);
- raise(SIGTERM);
- return;
+ if (msg->service_id > max_index) {
+ DBG("unknown service (0x%x)", msg->service_id);
+ return -EOPNOTSUPP;
}
/* if service is registered */
- if (!services[msg->service_id].handler) {
- error("IPC: unregistered service (0x%x), terminating",
- msg->service_id);
- raise(SIGTERM);
- return;
+ if (!handlers[msg->service_id].handler) {
+ DBG("service not registered (0x%x)", msg->service_id);
+ return -EOPNOTSUPP;
}
/* if opcode is valid */
if (msg->opcode == HAL_OP_STATUS ||
- msg->opcode > services[msg->service_id].size) {
- error("IPC: invalid opcode 0x%x for service 0x%x, terminating",
- msg->opcode, msg->service_id);
- raise(SIGTERM);
- return;
+ msg->opcode > handlers[msg->service_id].size) {
+ DBG("invalid opcode 0x%x for service 0x%x", msg->opcode,
+ msg->service_id);
+ return -EOPNOTSUPP;
}
/* opcode is table offset + 1 */
@@ -99,13 +94,14 @@ static void ipc_handle_msg(const void *buf, ssize_t len)
/* if payload size is valid */
if ((handler->var_len && handler->data_len > msg->len) ||
(!handler->var_len && handler->data_len != msg->len)) {
- error("IPC: size invalid opcode 0x%x service 0x%x, terminating",
+ DBG("invalid size for opcode 0x%x service 0x%x",
msg->service_id, msg->opcode);
- raise(SIGTERM);
- return;
+ return -EMSGSIZE;
}
handler->handler(msg->payload, msg->len);
+
+ return 0;
}
static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond,
@@ -113,7 +109,7 @@ static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond,
{
char buf[BLUEZ_HAL_MTU];
ssize_t ret;
- int fd;
+ int fd, err;
if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
info("IPC: command socket closed, terminating");
@@ -129,7 +125,13 @@ static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond,
goto fail;
}
- ipc_handle_msg(buf, ret);
+ err = ipc_handle_msg(services, HAL_SERVICE_ID_MAX, buf, ret);
+ if (err < 0) {
+ error("IPC: failed to handle message, terminating (%s)",
+ strerror(-err));
+ goto fail;
+ }
+
return TRUE;
fail:
@@ -348,7 +350,7 @@ static gboolean audio_watch_cb(GIOChannel *io, GIOCondition cond,
{
char buf[BLUEZ_HAL_MTU];
ssize_t ret;
- int fd;
+ int fd, err;
if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
info("Audio IPC: command socket closed");
@@ -363,6 +365,13 @@ static gboolean audio_watch_cb(GIOChannel *io, GIOCondition cond,
goto fail;
}
+ err = ipc_handle_msg(audio_services, AUDIO_SERVICE_ID_MAX, buf, ret);
+ if (err < 0) {
+ error("Audio IPC: failed to handle message (%s)",
+ strerror(-err));
+ goto fail;
+ }
+
return TRUE;
fail:
@@ -404,3 +413,16 @@ void audio_ipc_cleanup(void)
audio_io = NULL;
}
}
+
+void audio_ipc_register(uint8_t service, const struct ipc_handler *handlers,
+ uint8_t size)
+{
+ audio_services[service].handler = handlers;
+ audio_services[service].size = size;
+}
+
+void audio_ipc_unregister(uint8_t service)
+{
+ audio_services[service].handler = NULL;
+ audio_services[service].size = 0;
+}
diff --git a/android/ipc.h b/android/ipc.h
index 8e92811..44d5a5d 100644
--- a/android/ipc.h
+++ b/android/ipc.h
@@ -40,3 +40,7 @@ void ipc_unregister(uint8_t service);
void audio_ipc_init(void);
void audio_ipc_cleanup(void);
+
+void audio_ipc_register(uint8_t service, const struct ipc_handler *handlers,
+ uint8_t size);
+void audio_ipc_unregister(uint8_t service);
--
1.8.4.2
^ permalink raw reply related
* [PATCH BlueZ v2 01/10] android/ipc: Add initial code for audio IPC
From: Luiz Augusto von Dentz @ 2014-01-02 11:58 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This add initial code for listen and accept connections on the abstract
socket defined for the audio IPC.
---
v2: Split audio IPC services for HAL services and fix invalid messages or
disconnections causing the daemon to exit. The audio HAL is independent of
bluetooth and should only affect A2DP service.
android/hal-msg.h | 1 +
android/ipc.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
android/ipc.h | 3 +++
3 files changed, 76 insertions(+), 6 deletions(-)
diff --git a/android/hal-msg.h b/android/hal-msg.h
index c351501..b14eced 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -24,6 +24,7 @@
#define BLUEZ_HAL_MTU 1024
static const char BLUEZ_HAL_SK_PATH[] = "\0bluez_hal_socket";
+static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
struct hal_hdr {
uint8_t service_id;
diff --git a/android/ipc.c b/android/ipc.c
index 9e8ccc3..4c5a77e 100644
--- a/android/ipc.c
+++ b/android/ipc.c
@@ -49,6 +49,7 @@ static struct service_handler services[HAL_SERVICE_ID_MAX + 1];
static GIOChannel *cmd_io = NULL;
static GIOChannel *notif_io = NULL;
+static GIOChannel *audio_io = NULL;
static void ipc_handle_msg(const void *buf, ssize_t len)
{
@@ -145,7 +146,8 @@ static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond,
return FALSE;
}
-static GIOChannel *connect_hal(GIOFunc connect_cb)
+static GIOChannel *ipc_connect(const char *path, size_t size,
+ GIOFunc connect_cb)
{
struct sockaddr_un addr;
GIOCondition cond;
@@ -167,11 +169,11 @@ static GIOChannel *connect_hal(GIOFunc connect_cb)
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
- memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
+ memcpy(addr.sun_path, path, size);
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- error("IPC: failed to connect HAL socket: %d (%s)", errno,
- strerror(errno));
+ error("IPC: failed to connect HAL socket %s: %d (%s)", &path[1],
+ errno, strerror(errno));
g_io_channel_unref(io);
return NULL;
}
@@ -218,7 +220,8 @@ static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond,
return FALSE;
}
- notif_io = connect_hal(notif_connect_cb);
+ notif_io = ipc_connect(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH),
+ notif_connect_cb);
if (!notif_io)
raise(SIGTERM);
@@ -227,7 +230,8 @@ static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond,
void ipc_init(void)
{
- cmd_io = connect_hal(cmd_connect_cb);
+ cmd_io = ipc_connect(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH),
+ cmd_connect_cb);
if (!cmd_io)
raise(SIGTERM);
}
@@ -338,3 +342,65 @@ void ipc_unregister(uint8_t service)
services[service].handler = NULL;
services[service].size = 0;
}
+
+static gboolean audio_watch_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ char buf[BLUEZ_HAL_MTU];
+ ssize_t ret;
+ int fd;
+
+ if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ info("Audio IPC: command socket closed");
+ goto fail;
+ }
+
+ fd = g_io_channel_unix_get_fd(io);
+
+ ret = read(fd, buf, sizeof(buf));
+ if (ret < 0) {
+ error("Audio IPC: command read failed (%s)", strerror(errno));
+ goto fail;
+ }
+
+ return TRUE;
+
+fail:
+ audio_ipc_cleanup();
+ return FALSE;
+}
+
+static gboolean audio_connect_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ DBG("");
+
+ if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ error("Audio IPC: socket connect failed");
+ audio_ipc_cleanup();
+ return FALSE;
+ }
+
+ cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+ g_io_add_watch(audio_io, cond, audio_watch_cb, NULL);
+
+ info("Audio IPC: successfully connected");
+
+ return FALSE;
+}
+
+void audio_ipc_init(void)
+{
+ audio_io = ipc_connect(BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH),
+ audio_connect_cb);
+}
+
+void audio_ipc_cleanup(void)
+{
+ if (audio_io) {
+ g_io_channel_shutdown(audio_io, TRUE, NULL);
+ g_io_channel_unref(audio_io);
+ audio_io = NULL;
+ }
+}
diff --git a/android/ipc.h b/android/ipc.h
index 6cd102b..8e92811 100644
--- a/android/ipc.h
+++ b/android/ipc.h
@@ -37,3 +37,6 @@ void ipc_send_notif(uint8_t service_id, uint8_t opcode, uint16_t len,
void ipc_register(uint8_t service, const struct ipc_handler *handlers,
uint8_t size);
void ipc_unregister(uint8_t service);
+
+void audio_ipc_init(void);
+void audio_ipc_cleanup(void);
--
1.8.4.2
^ permalink raw reply related
* [PATCH 2/4] android/map: Add support for MAP/MAS
From: Lukasz Rymanowski @ 2014-01-02 11:25 UTC (permalink / raw)
To: linux-bluetooth; +Cc: johan.hedberg, szymon.janc, luiz.dentz, Lukasz Rymanowski
In-Reply-To: <1388661934-25340-1-git-send-email-lukasz.rymanowski@tieto.com>
Theres no HAL API exposed to application which would allow to register
different MAS instances. Android 4.4 does support only MAS SMS.
All Instance ID, supported msg type and rfcomm channel are hardcoded
in the stack. Unfortunatelly we need to do the same way and wait for
new HAL API which is expected.
---
android/socket.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 80 insertions(+), 2 deletions(-)
diff --git a/android/socket.c b/android/socket.c
index 15c1bb8..11d64f8 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -52,6 +52,14 @@
#define SVC_HINT_OBEX 0x10
+/* Hardcoded MAP stuff needed for MAS SMS Instance.*/
+#define DEFAULT_MAS_INSTANCE 0x00
+
+#define MAP_MSG_TYPE_SMS_GSM 0x02
+#define MAP_MSG_TYPE_SMS_CDMA 0x04
+#define DEFAULT_MAS_MSG_TYPE (MAP_MSG_TYPE_SMS_GSM | MAP_MSG_TYPE_SMS_CDMA)
+
+
static bdaddr_t adapter_addr;
/* Simple list of RFCOMM server sockets */
@@ -311,6 +319,76 @@ static sdp_record_t *create_pbap_record(uint8_t chan, const char *svc_name)
return record;
}
+static sdp_record_t *create_mas_record(uint8_t chan, const char *svc_name)
+{
+ const char *service_name = "MAP MAS SMS";
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, mse_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[3];
+ sdp_data_t *channel;
+ uint8_t minst = DEFAULT_MAS_INSTANCE;
+ uint8_t mtype = DEFAULT_MAS_MSG_TYPE;
+ sdp_record_t *record;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ record->handle = sdp_next_handle();
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&mse_uuid, MAP_MSE_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &mse_uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, MAP_PROFILE_ID);
+ profile[0].version = 0x0101;
+ pfseq = sdp_list_append(NULL, profile);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_attr_add_new(record, SDP_ATTR_MAS_INSTANCE_ID, SDP_UINT8, &minst);
+ sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_MESSAGE_TYPES, SDP_UINT8,
+ &mtype);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+ proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+ channel = sdp_data_alloc(SDP_UINT8, &chan);
+ proto[1] = sdp_list_append(proto[1], channel);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+ proto[2] = sdp_list_append(NULL, &obex_uuid);
+ apseq = sdp_list_append(apseq, proto[2]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ if (svc_name)
+ service_name = svc_name;
+
+ sdp_set_info_attr(record, service_name, NULL, NULL);
+
+ sdp_data_free(channel);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(proto[2], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(pfseq, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(svclass_id, NULL);
+
+ return record;
+}
+
static sdp_record_t *create_spp_record(uint8_t chan, const char *svc_name)
{
const char *service_name = "Serial Port";
@@ -411,9 +489,9 @@ static const struct profile_info {
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
},
.channel = MAS_DEFAULT_CHANNEL,
- .svc_hint = 0,
+ .svc_hint = SVC_HINT_OBEX,
.sec_level = BT_IO_SEC_MEDIUM,
- .create_record = NULL
+ .create_record = create_mas_record
}, {
.uuid = {
0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
--
1.8.4
^ permalink raw reply related
* [PATCH 1/4] sdp: Add MAP_PROFILE_ID
From: Lukasz Rymanowski @ 2014-01-02 11:25 UTC (permalink / raw)
To: linux-bluetooth; +Cc: johan.hedberg, szymon.janc, luiz.dentz, Lukasz Rymanowski
---
lib/sdp.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/sdp.h b/lib/sdp.h
index f2f2484..c6dfc27 100644
--- a/lib/sdp.h
+++ b/lib/sdp.h
@@ -207,6 +207,7 @@ extern "C" {
#define PBAP_PCE_PROFILE_ID PBAP_PCE_SVCLASS_ID
#define PBAP_PSE_PROFILE_ID PBAP_PSE_SVCLASS_ID
#define PBAP_PROFILE_ID PBAP_SVCLASS_ID
+#define MAP_PROFILE_ID MAP_SVCLASS_ID
#define PNP_INFO_PROFILE_ID PNP_INFO_SVCLASS_ID
#define GENERIC_NETWORKING_PROFILE_ID GENERIC_NETWORKING_SVCLASS_ID
#define GENERIC_FILETRANS_PROFILE_ID GENERIC_FILETRANS_SVCLASS_ID
--
1.8.4
^ permalink raw reply related
* Re: [PATCH v2] Bluetooth: Add hci_h4p driver
From: Pavel Machek @ 2014-01-01 20:09 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: <E7C15C38-8E0E-4D49-9E93-5E3DAF77E252@holtmann.org>
Hi!
> >>> +static struct task_struct *h4p_thread;
> >>
> >> Can’t this be done using a work queue. You are looking at a 3.14
> >> kernel the earliest. We have way better primitives these days.
> >
> > I tried to convert it to work queue, but was not too
> > succesfull. Workqueue is not really good match for what this is trying
> > to do... Nokia code relies on sleeping, than timing those sleeps for
> > signaling. I'm still trying to wrap my head around it.
> >
> > Ok, I guess I could convert it to one big workqueue task, and leave
> > the logic alone. Was that what you wanted?
>
> the Bluetooth subsystem moved away from tasklets and uses workqueues for everything. So this should be just fine for this driver as well. I do not know about what timings are required, but they should only matter during initial device setup. The HCI traffic is actually driven by the Bluetooth core.
>
I actually tried to convert it to workqueue... result is below but I
was not successful.
h4p driver actually has one long-running, mostly sleeping, thread and
communicates with it by sending it wakeups; the thread toggles some
power management options based on activity. (No, I don't like that
design). That is not something that can be easily converted to
workqueue, AFAICT.
I don't think other Bluetooth drivers do anything similar.
Pavel
diff --git a/drivers/bluetooth/hci_h4p.h b/drivers/bluetooth/hci_h4p.h
index a2174ea..5dcbaa1 100644
--- a/drivers/bluetooth/hci_h4p.h
+++ b/drivers/bluetooth/hci_h4p.h
@@ -19,12 +19,13 @@
*
*/
+#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>
-
-#ifndef __DRIVERS_BLUETOOTH_HCI_H4P_H
-#define __DRIVERS_BLUETOOTH_HCI_H4P_H
+#include <linux/workqueue.h>
#define FW_NAME_TI1271_PRELE "ti1273_prele.bin"
#define FW_NAME_TI1271_LE "ti1273_le.bin"
@@ -103,6 +104,13 @@ struct hci_h4p_info {
u16 ier;
u16 mdr1;
u16 efr;
+
+#if 1
+ struct work_struct work;
+ struct workqueue_struct work_queue; /* FIXME: init me */
+ unsigned long last_transfer_jiffies;
+ unsigned long transfer_timeout;
+#endif
};
struct hci_h4p_radio_hdr {
diff --git a/drivers/bluetooth/nokia_core.c b/drivers/bluetooth/nokia_core.c
index 07761a3..6e37866 100644
--- a/drivers/bluetooth/nokia_core.c
+++ b/drivers/bluetooth/nokia_core.c
@@ -35,7 +35,6 @@
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/timer.h>
-#include <linux/kthread.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -45,8 +44,6 @@
#include "hci_h4p.h"
-static struct task_struct *h4p_thread;
-
/* This should be used in function that cannot release clocks */
static void hci_h4p_set_clk(struct hci_h4p_info *info, int *clock, int enable)
{
@@ -103,13 +100,11 @@ void hci_h4p_smart_idle(struct hci_h4p_info *info, bool enable)
static inline void h4p_schedule_pm(struct hci_h4p_info *info)
{
- if (unlikely(!h4p_thread))
- return;
-
set_bit(H4P_SCHED_TRANSFER_MODE, &info->pm_flags);
if (unlikely(!test_bit(H4P_TRANSFER_MODE, &info->pm_flags)))
- wake_up_process(h4p_thread);
+ /* FIXME */
+ /* wake_up_process(h4p_thread) */ ;
}
static void hci_h4p_disable_tx(struct hci_h4p_info *info)
@@ -723,18 +718,18 @@ static inline void hci_h4p_set_pm_limits(struct hci_h4p_info *info, bool set)
BT_DBG("pm constraints remains: %s", sset);
}
-static int h4p_run(void *data)
+static int h4p_run(struct work_struct *work)
{
#define TIMEOUT_MIN msecs_to_jiffies(100)
#define TIMEOUT_MAX msecs_to_jiffies(2000)
- struct hci_h4p_info *info = data;
+ struct hci_h4p_info *info = container_of(work, struct hci_h4p_info, work);
unsigned long last_jiffies = jiffies;
unsigned long timeout = TIMEOUT_MIN;
unsigned long elapsed;
BT_DBG("");
set_user_nice(current, -10);
- while (!kthread_should_stop()) {
+ while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (!test_bit(H4P_SCHED_TRANSFER_MODE, &info->pm_flags)) {
if (timeout != TIMEOUT_MIN) {
@@ -998,7 +993,8 @@ static int hci_h4p_hci_close(struct hci_dev *hdev)
return 0;
/* Wake up h4p_thread which removes pm constraints */
- wake_up_process(h4p_thread);
+ /* FIXME */
+// wake_up_process(h4p_thread);
hci_h4p_hci_flush(hdev);
hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
@@ -1272,12 +1268,11 @@ static int hci_h4p_probe(struct platform_device *pdev)
goto cleanup_irq;
}
- h4p_thread = kthread_run(h4p_run, info, "h4p_pm");
- if (IS_ERR(h4p_thread)) {
- err = PTR_ERR(h4p_thread);
- goto cleanup_irq;
- }
+ INIT_WORK(&info->work, h4p_run);
+// schedule_work(&info->work);
+// info->work_queue = init_work
+ queue_work(info->work_queue, &info->work);
return 0;
cleanup_irq:
@@ -1300,7 +1295,7 @@ static int hci_h4p_remove(struct platform_device *pdev)
info = platform_get_drvdata(pdev);
- kthread_stop(h4p_thread);
+ cancel_work_sync(&info->work);
hci_h4p_sysfs_remove_files(info->dev);
hci_h4p_hci_close(info->hdev);
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply related
* Re: [PATCH 1/5] android/tester: Add SCAN_MODE get prop success test case
From: Johan Hedberg @ 2014-01-01 16:32 UTC (permalink / raw)
To: Grzegorz Kolodziejczyk; +Cc: linux-bluetooth
In-Reply-To: <1388501191-5377-1-git-send-email-grzegorz.kolodziejczyk@tieto.com>
Hi Grzegorz,
On Tue, Dec 31, 2013, Grzegorz Kolodziejczyk wrote:
> This adds SCAN_MODE get property success test case.
> ---
> android/android-tester.c | 29 +++++++++++++++++++++++++++++
> 1 file changed, 29 insertions(+)
All patches in this set have been applied. Thanks.
However, I'm still expecting to see follow-up patches that add missing
checks for HCI-level behavior to the tests.
Johan
^ permalink raw reply
* Re: [PATCH 1/7] android: Add HCI snooping tool
From: Johan Hedberg @ 2014-01-01 16:30 UTC (permalink / raw)
To: Szymon Janc; +Cc: linux-bluetooth
In-Reply-To: <1388443421-6295-1-git-send-email-szymon.janc@gmail.com>
Hi Szymon,
On Mon, Dec 30, 2013, Szymon Janc wrote:
> This tool is intended to be run as Android service. It supports
> writing HCI snoop data in old btsnoop format only. By default traffic
> is stored in /sdcard/btsnoop_hci.log file (can be overridded with
> option - mainly for testing on Linux host). Only index 0 is sniffed.
> ---
> .gitignore | 1 +
> android/Android.mk | 23 +++++
> android/Makefile.am | 6 ++
> android/bluetoothd-snoop.c | 219 +++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 249 insertions(+)
> create mode 100644 android/bluetoothd-snoop.c
All patches in this set have been applied. Thanks.
Johan
^ permalink raw reply
* Re: RFCOMM connection failing
From: Patrick Valsecchi @ 2014-01-01 8:34 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <52AF228D.1000608@thus.ch>
Hi,
Can somebody tell me if I should send this email on some other mailing list?
Thanks.
On 16. 12. 13 16:55, Patrick Valsecchi wrote:
> Hi,
>
> I'm trying to connect my PC:
> Linux ... 3.11.0-14-generic #21-Ubuntu SMP ... x86_64 x86_64
> x86_64 GNU/Linux
> 0a5c:2198 Broadcom Corp. Bluetooth 3.0 Device
>
> To connect with my bluetooth dive computer:
> Shearwater Petrel
>
> It fails with a "Transport endpoint is not connected (107)" most of
> the time or go further but seem to have corrupted RFCOMM payload.
>
> If I pass the USB device to a W7 VM (virtualbox) and try from them,
> the communication works like charm.
>
> So I went ahead and sniffed the USB communication in both cases using
> wireshark. The two dumps () can be found here (UsbDumpFrom*.pcapng,
> can be open using wireshark):
> https://cloud.thus.ch/public.php?service=files&t=de2eabf30c82efa08cf546ff5045e585
>
>
> Basically the Linux one is just stopping at frame 203 where it sends a
> RFCOMM SABM, gets the answer and reports an error to the user. The
> windows dump show the same RFCOMM SABM command in frame 191 and gets
> the same answer but continues and everything works.
>
> On IRC aholler told me you guys would prefer to get a btmon dump with
> bluez v5. So I went ahead, installed bluez 5.12 and generated a dump
> (yes, the comm still fails the same way) that you can find in the same
> location as the dumps.
>
> I'm stuck there. Can somebody help me go further?
>
> Thanks
> --
> 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
>
^ permalink raw reply
* Re: [PATCH v2] Bluetooth: Add hci_h4p driver
From: Marcel Holtmann @ 2013-12-31 23:23 UTC (permalink / raw)
To: Pavel Machek
Cc: Pali Rohár,
Ивайло Димитров,
Gustavo F. Padovan, Johan Hedberg, linux-kernel,
linux-bluetooth@vger.kernel.org development, Ville Tervo,
Sebastian Reichel
In-Reply-To: <20131231221202.GB25336@amd.pavel.ucw.cz>
Hi Pavel,
>>> +static struct task_struct *h4p_thread;
>>
>> Can’t this be done using a work queue. You are looking at a 3.14
>> kernel the earliest. We have way better primitives these days.
>
> I tried to convert it to work queue, but was not too
> succesfull. Workqueue is not really good match for what this is trying
> to do... Nokia code relies on sleeping, than timing those sleeps for
> signaling. I'm still trying to wrap my head around it.
>
> Ok, I guess I could convert it to one big workqueue task, and leave
> the logic alone. Was that what you wanted?
the Bluetooth subsystem moved away from tasklets and uses workqueues for everything. So this should be just fine for this driver as well. I do not know about what timings are required, but they should only matter during initial device setup. The HCI traffic is actually driven by the Bluetooth core.
Regards
Marcel
^ permalink raw reply
* Re: [PATCH v2] Bluetooth: Add hci_h4p driver
From: Pavel Machek @ 2013-12-31 22:12 UTC (permalink / raw)
To: Marcel Holtmann
Cc: Pali Rohár,
Ивайло Димитров,
Gustavo F. Padovan, Johan Hedberg, linux-kernel,
linux-bluetooth@vger.kernel.org development, Ville Tervo,
Sebastian Reichel
In-Reply-To: <C8D1F470-7964-4EAC-82E0-D53CF54DE086@holtmann.org>
Hi!
> > +static struct task_struct *h4p_thread;
>
> Can’t this be done using a work queue. You are looking at a 3.14
> kernel the earliest. We have way better primitives these days.
I tried to convert it to work queue, but was not too
succesfull. Workqueue is not really good match for what this is trying
to do... Nokia code relies on sleeping, than timing those sleeps for
signaling. I'm still trying to wrap my head around it.
Ok, I guess I could convert it to one big workqueue task, and leave
the logic alone. Was that what you wanted?
In the meantime, while trying to untangle it:
commit b02dc19d9269f22baa705d16d1019b86acf15758
Author: Pavel <pavel@ucw.cz>
Date: Tue Dec 31 23:01:23 2013 +0100
De-obfuscate power management code. Someone please carefully review
this.
Signed-off-by: Pavel Machek <pavel@ucw.cz>
diff --git a/drivers/bluetooth/nokia_core.c b/drivers/bluetooth/nokia_core.c
index 85dd106..07761a3 100644
--- a/drivers/bluetooth/nokia_core.c
+++ b/drivers/bluetooth/nokia_core.c
@@ -705,28 +705,22 @@ static irqreturn_t hci_h4p_wakeup_interrupt(int irq, void *dev_inst)
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;
+ 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)) {
+ if (set != !!test_bit(H4P_ACTIVE_MODE, &info->pm_flags)) {
bt_plat_data->set_pm_limits(info->dev, set);
- set_bit(H4P_ACTIVE_MODE, &info->pm_flags);
- BT_DBG("Change pm constraints to: %s", set ?
- "set" : "clear");
+ 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;
}
- if (!set && test_bit(H4P_ACTIVE_MODE, &info->pm_flags)) {
- bt_plat_data->set_pm_limits(info->dev, set);
- clear_bit(H4P_ACTIVE_MODE, &info->pm_flags);
- BT_DBG("Change pm constraints to: %s",
- set ? "set" : "clear");
- return;
- }
-
- BT_DBG("pm constraints remains: %s",
- set ? "set" : "clear");
+ BT_DBG("pm constraints remains: %s", sset);
}
static int h4p_run(void *data)
@@ -762,16 +756,17 @@ static int h4p_run(void *data)
BT_DBG("Timeout before calculation = %u",
jiffies_to_msecs(timeout));
- /* Empiric analyzer :-) */
if (elapsed < TIMEOUT_MIN) {
timeout <<= 1;
- timeout = (timeout > TIMEOUT_MAX) ?
- TIMEOUT_MAX : timeout;
} else {
- timeout = (elapsed > timeout - TIMEOUT_MIN) ?
- TIMEOUT_MIN : timeout - elapsed;
+ timeout -= elapsed;
}
+ if (timeout > TIMEOUT_MAX)
+ timeout = TIMEOUT_MAX;
+ if (timeout < TIMEOUT_MIN)
+ timeout = TIMEOUT_MIN;
+
BT_DBG("Timeout after calculation = %u",
jiffies_to_msecs(timeout));
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply related
* [PATCH 5/5] android/tester: Add SCAN_MODE=NONE set prop done test case
From: Grzegorz Kolodziejczyk @ 2013-12-31 14:46 UTC (permalink / raw)
To: linux-bluetooth
In-Reply-To: <1388501191-5377-1-git-send-email-grzegorz.kolodziejczyk@tieto.com>
This adds SCAN_MODE property set to NONE - done test case.
SCAN_MODE at startup is set to NONE. Bluez return status - done when
requested status is the same as was.
---
android/android-tester.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/android/android-tester.c b/android/android-tester.c
index 5467aff..04fa811 100644
--- a/android/android-tester.c
+++ b/android/android-tester.c
@@ -900,6 +900,17 @@ static const struct generic_data bluetooth_getprop_bondeddev_success_test = {
.expected_property.len = 0,
};
+static bt_scan_mode_t setprop_scanmode_none = BT_SCAN_MODE_NONE;
+
+static const struct generic_data bluetooth_setprop_scanmode_none_done_test = {
+ .expected_hal_cb.adapter_properties_cb = getprop_success_cb,
+ .expected_cb_count = 1,
+ .expected_adapter_status = BT_STATUS_DONE,
+ .expected_property.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+ .expected_property.val = &setprop_scanmode_none,
+ .expected_property.len = sizeof(setprop_scanmode_none),
+};
+
static const struct generic_data bluetooth_discovery_start_success_test = {
.expected_hal_cb.discovery_state_changed_cb =
discovery_start_success_cb,
@@ -1353,6 +1364,19 @@ static void test_getprop_bondeddev_success(const void *test_data)
check_expected_status(adapter_status);
}
+static void test_setprop_scanmode_none_done(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ const struct generic_data *test = data->test_data;
+ const bt_property_t *prop = &test->expected_property;
+ bt_status_t adapter_status;
+
+ init_test_conditions(data);
+
+ adapter_status = data->if_bluetooth->set_adapter_property(prop);
+ check_expected_status(adapter_status);
+}
+
static void test_discovery_start_success(const void *test_data)
{
struct test_data *data = tester_get_data();
@@ -1957,6 +1981,11 @@ int main(int argc, char *argv[])
setup_enabled_adapter,
test_getprop_bondeddev_success, teardown);
+ test_bredrle("Bluetooth Set SCAN_MODE NONE - Done",
+ &bluetooth_setprop_scanmode_none_done_test,
+ setup_enabled_adapter,
+ test_setprop_scanmode_none_done, teardown);
+
test_bredrle("Bluetooth BREDR Discovery Start - Success",
&bluetooth_discovery_start_success_test,
setup_enabled_adapter,
--
1.8.5.2
^ permalink raw reply related
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