summaryrefslogtreecommitdiffstats
path: root/drivers/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r--drivers/bluetooth/Kconfig10
-rw-r--r--drivers/bluetooth/Makefile1
-rw-r--r--drivers/bluetooth/ath3k.c287
-rw-r--r--drivers/bluetooth/btusb.c13
-rw-r--r--drivers/bluetooth/btwilink.c395
-rw-r--r--drivers/bluetooth/hci_ath.c26
-rw-r--r--drivers/bluetooth/hci_ldisc.c1
7 files changed, 708 insertions, 25 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 02deef4..8e0de9a 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -219,4 +219,14 @@ config BT_ATH3K
Say Y here to compile support for "Atheros firmware download driver"
into the kernel or say M to compile it as module (ath3k).
+config BT_WILINK
+ tristate "Texas Instruments WiLink7 driver"
+ depends on TI_ST
+ help
+ This enables the Bluetooth driver for Texas Instrument's BT/FM/GPS
+ combo devices. This makes use of shared transport line discipline
+ 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.
endmenu
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 71bdf13..f4460f4 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
obj-$(CONFIG_BT_ATH3K) += ath3k.o
obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
+obj-$(CONFIG_BT_WILINK) += btwilink.o
btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 6dcd55a..5577ed6 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -31,6 +31,30 @@
#define VERSION "1.0"
+#define ATH3K_DNLOAD 0x01
+#define ATH3K_GETSTATE 0x05
+#define ATH3K_SET_NORMAL_MODE 0x07
+#define ATH3K_GETVERSION 0x09
+#define USB_REG_SWITCH_VID_PID 0x0a
+
+#define ATH3K_MODE_MASK 0x3F
+#define ATH3K_NORMAL_MODE 0x0E
+
+#define ATH3K_PATCH_UPDATE 0x80
+#define ATH3K_SYSCFG_UPDATE 0x40
+
+#define ATH3K_XTAL_FREQ_26M 0x00
+#define ATH3K_XTAL_FREQ_40M 0x01
+#define ATH3K_XTAL_FREQ_19P2 0x02
+#define ATH3K_NAME_LEN 0xFF
+
+struct ath3k_version {
+ unsigned int rom_version;
+ unsigned int build_version;
+ unsigned int ram_version;
+ unsigned char ref_clock;
+ unsigned char reserved[0x07];
+};
static struct usb_device_id ath3k_table[] = {
/* Atheros AR3011 */
@@ -42,15 +66,31 @@ static struct usb_device_id ath3k_table[] = {
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03F0, 0x311D) },
+ /* Atheros AR3012 with sflash firmware*/
+ { USB_DEVICE(0x0CF3, 0x3004) },
+
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE02C) },
+
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, ath3k_table);
+#define BTUSB_ATH3012 0x80
+/* This table is to load patch and sysconfig files
+ * for AR3012 */
+static struct usb_device_id ath3k_blist_tbl[] = {
+
+ /* Atheros AR3012 with sflash firmware*/
+ { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
+
+ { } /* Terminating entry */
+};
+
#define USB_REQ_DFU_DNLOAD 1
#define BULK_SIZE 4096
+#define FW_HDR_SIZE 20
static int ath3k_load_firmware(struct usb_device *udev,
const struct firmware *firmware)
@@ -106,28 +146,265 @@ error:
return err;
}
+static int ath3k_get_state(struct usb_device *udev, unsigned char *state)
+{
+ int pipe = 0;
+
+ pipe = usb_rcvctrlpipe(udev, 0);
+ return usb_control_msg(udev, pipe, ATH3K_GETSTATE,
+ USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
+ state, 0x01, USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_get_version(struct usb_device *udev,
+ struct ath3k_version *version)
+{
+ int pipe = 0;
+
+ pipe = usb_rcvctrlpipe(udev, 0);
+ return usb_control_msg(udev, pipe, ATH3K_GETVERSION,
+ USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, version,
+ sizeof(struct ath3k_version),
+ USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_load_fwfile(struct usb_device *udev,
+ const struct firmware *firmware)
+{
+ u8 *send_buf;
+ int err, pipe, len, size, count, sent = 0;
+ int ret;
+
+ count = firmware->size;
+
+ send_buf = kmalloc(BULK_SIZE, GFP_ATOMIC);
+ if (!send_buf) {
+ BT_ERR("Can't allocate memory chunk for firmware");
+ return -ENOMEM;
+ }
+
+ size = min_t(uint, count, FW_HDR_SIZE);
+ memcpy(send_buf, firmware->data, size);
+
+ pipe = usb_sndctrlpipe(udev, 0);
+ ret = usb_control_msg(udev, pipe, ATH3K_DNLOAD,
+ USB_TYPE_VENDOR, 0, 0, send_buf,
+ size, USB_CTRL_SET_TIMEOUT);
+ if (ret < 0) {
+ BT_ERR("Can't change to loading configuration err");
+ kfree(send_buf);
+ return ret;
+ }
+
+ sent += size;
+ count -= size;
+
+ while (count) {
+ size = min_t(uint, count, BULK_SIZE);
+ pipe = usb_sndbulkpipe(udev, 0x02);
+
+ memcpy(send_buf, firmware->data + sent, size);
+
+ err = usb_bulk_msg(udev, pipe, send_buf, size,
+ &len, 3000);
+ if (err || (len != size)) {
+ BT_ERR("Error in firmware loading err = %d,"
+ "len = %d, size = %d", err, len, size);
+ kfree(send_buf);
+ return err;
+ }
+ sent += size;
+ count -= size;
+ }
+
+ kfree(send_buf);
+ return 0;
+}
+
+static int ath3k_switch_pid(struct usb_device *udev)
+{
+ int pipe = 0;
+
+ pipe = usb_sndctrlpipe(udev, 0);
+ return usb_control_msg(udev, pipe, USB_REG_SWITCH_VID_PID,
+ USB_TYPE_VENDOR, 0, 0,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_set_normal_mode(struct usb_device *udev)
+{
+ unsigned char fw_state;
+ int pipe = 0, ret;
+
+ ret = ath3k_get_state(udev, &fw_state);
+ if (ret < 0) {
+ BT_ERR("Can't get state to change to normal mode err");
+ return ret;
+ }
+
+ if ((fw_state & ATH3K_MODE_MASK) == ATH3K_NORMAL_MODE) {
+ BT_DBG("firmware was already in normal mode");
+ return 0;
+ }
+
+ pipe = usb_sndctrlpipe(udev, 0);
+ return usb_control_msg(udev, pipe, ATH3K_SET_NORMAL_MODE,
+ USB_TYPE_VENDOR, 0, 0,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+static int ath3k_load_patch(struct usb_device *udev)
+{
+ unsigned char fw_state;
+ char filename[ATH3K_NAME_LEN] = {0};
+ const struct firmware *firmware;
+ struct ath3k_version fw_version, pt_version;
+ int ret;
+
+ ret = ath3k_get_state(udev, &fw_state);
+ if (ret < 0) {
+ BT_ERR("Can't get state to change to load ram patch err");
+ return ret;
+ }
+
+ if (fw_state & ATH3K_PATCH_UPDATE) {
+ BT_DBG("Patch was already downloaded");
+ return 0;
+ }
+
+ ret = ath3k_get_version(udev, &fw_version);
+ if (ret < 0) {
+ BT_ERR("Can't get version to change to load ram patch err");
+ return ret;
+ }
+
+ snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu",
+ fw_version.rom_version);
+
+ ret = request_firmware(&firmware, filename, &udev->dev);
+ if (ret < 0) {
+ BT_ERR("Patch file not found %s", filename);
+ return ret;
+ }
+
+ pt_version.rom_version = *(int *)(firmware->data + firmware->size - 8);
+ pt_version.build_version = *(int *)
+ (firmware->data + firmware->size - 4);
+
+ if ((pt_version.rom_version != fw_version.rom_version) ||
+ (pt_version.build_version <= fw_version.build_version)) {
+ BT_ERR("Patch file version did not match with firmware");
+ release_firmware(firmware);
+ return -EINVAL;
+ }
+
+ ret = ath3k_load_fwfile(udev, firmware);
+ release_firmware(firmware);
+
+ return ret;
+}
+
+static int ath3k_load_syscfg(struct usb_device *udev)
+{
+ unsigned char fw_state;
+ char filename[ATH3K_NAME_LEN] = {0};
+ const struct firmware *firmware;
+ struct ath3k_version fw_version;
+ int clk_value, ret;
+
+ ret = ath3k_get_state(udev, &fw_state);
+ if (ret < 0) {
+ BT_ERR("Can't get state to change to load configration err");
+ return -EBUSY;
+ }
+
+ ret = ath3k_get_version(udev, &fw_version);
+ if (ret < 0) {
+ BT_ERR("Can't get version to change to load ram patch err");
+ return ret;
+ }
+
+ switch (fw_version.ref_clock) {
+
+ case ATH3K_XTAL_FREQ_26M:
+ clk_value = 26;
+ break;
+ case ATH3K_XTAL_FREQ_40M:
+ clk_value = 40;
+ break;
+ case ATH3K_XTAL_FREQ_19P2:
+ clk_value = 19;
+ break;
+ default:
+ clk_value = 0;
+ break;
+ }
+
+ snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s",
+ fw_version.rom_version, clk_value, ".dfu");
+
+ ret = request_firmware(&firmware, filename, &udev->dev);
+ if (ret < 0) {
+ BT_ERR("Configuration file not found %s", filename);
+ return ret;
+ }
+
+ ret = ath3k_load_fwfile(udev, firmware);
+ release_firmware(firmware);
+
+ return ret;
+}
+
static int ath3k_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
const struct firmware *firmware;
struct usb_device *udev = interface_to_usbdev(intf);
+ int ret;
BT_DBG("intf %p id %p", intf, id);
if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
return -ENODEV;
- if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) {
- return -EIO;
+ /* match device ID in ath3k blacklist table */
+ if (!id->driver_info) {
+ const struct usb_device_id *match;
+ match = usb_match_id(intf, ath3k_blist_tbl);
+ if (match)
+ id = match;
}
- if (ath3k_load_firmware(udev, firmware)) {
- release_firmware(firmware);
+ /* load patch and sysconfig files for AR3012 */
+ if (id->driver_info & BTUSB_ATH3012) {
+ ret = ath3k_load_patch(udev);
+ if (ret < 0) {
+ BT_ERR("Loading patch file failed");
+ return ret;
+ }
+ ret = ath3k_load_syscfg(udev);
+ if (ret < 0) {
+ BT_ERR("Loading sysconfig file failed");
+ return ret;
+ }
+ ret = ath3k_set_normal_mode(udev);
+ if (ret < 0) {
+ BT_ERR("Set normal mode failed");
+ return ret;
+ }
+ ath3k_switch_pid(udev);
+ return 0;
+ }
+
+ if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) {
+ BT_ERR("Error loading firmware");
return -EIO;
}
+
+ ret = ath3k_load_firmware(udev, firmware);
release_firmware(firmware);
- return 0;
+ return ret;
}
static void ath3k_disconnect(struct usb_interface *intf)
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 700a384..7e0ebd4 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -105,6 +105,9 @@ static struct usb_device_id blacklist_table[] = {
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
+ /* Atheros 3012 with sflash firmware */
+ { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_IGNORE },
+
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
@@ -714,15 +717,11 @@ static int btusb_send_frame(struct sk_buff *skb)
pipe = usb_sndisocpipe(data->udev,
data->isoc_tx_ep->bEndpointAddress);
- urb->dev = data->udev;
- urb->pipe = pipe;
- urb->context = skb;
- urb->complete = btusb_isoc_tx_complete;
- urb->interval = data->isoc_tx_ep->bInterval;
+ usb_fill_int_urb(urb, data->udev, pipe,
+ skb->data, skb->len, btusb_isoc_tx_complete,
+ skb, data->isoc_tx_ep->bInterval);
urb->transfer_flags = URB_ISO_ASAP;
- urb->transfer_buffer = skb->data;
- urb->transfer_buffer_length = skb->len;
__fill_isoc_descriptor(urb, skb->len,
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c
new file mode 100644
index 0000000..65d27af
--- /dev/null
+++ b/drivers/bluetooth/btwilink.c
@@ -0,0 +1,395 @@
+/*
+ * Texas Instrument's Bluetooth Driver For Shared Transport.
+ *
+ * Bluetooth Driver acts as interface between HCI core and
+ * TI Shared Transport Layer.
+ *
+ * Copyright (C) 2009-2010 Texas Instruments
+ * Author: Raja Mani <raja_mani@ti.com>
+ * Pavan Savoy <pavan_savoy@ti.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#define DEBUG
+#include <linux/platform_device.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#include <linux/ti_wilink_st.h>
+
+/* Bluetooth Driver Version */
+#define VERSION "1.0"
+#define MAX_BT_CHNL_IDS 3
+
+/* Number of seconds to wait for registration completion
+ * when ST returns PENDING status.
+ */
+#define BT_REGISTER_TIMEOUT 6000 /* 6 sec */
+
+/**
+ * struct ti_st - driver operation structure
+ * @hdev: hci device pointer which binds to bt driver
+ * @reg_status: ST registration callback status
+ * @st_write: write function provided by the ST driver
+ * to be used by the driver during send_frame.
+ * @wait_reg_completion - completion sync between ti_st_open
+ * and st_reg_completion_cb.
+ */
+struct ti_st {
+ struct hci_dev *hdev;
+ char reg_status;
+ long (*st_write) (struct sk_buff *);
+ struct completion wait_reg_completion;
+};
+
+/* Increments HCI counters based on pocket ID (cmd,acl,sco) */
+static inline void ti_st_tx_complete(struct ti_st *hst, int pkt_type)
+{
+ struct hci_dev *hdev = hst->hdev;
+
+ /* Update HCI stat counters */
+ switch (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;
+ }
+}
+
+/* ------- Interfaces to Shared Transport ------ */
+
+/* Called by ST layer to indicate protocol registration completion
+ * status.ti_st_open() function will wait for signal from this
+ * API when st_register() function returns ST_PENDING.
+ */
+static void st_reg_completion_cb(void *priv_data, char data)
+{
+ struct ti_st *lhst = priv_data;
+
+ /* Save registration status for use in ti_st_open() */
+ lhst->reg_status = data;
+ /* complete the wait in ti_st_open() */
+ complete(&lhst->wait_reg_completion);
+}
+
+/* Called by Shared Transport layer when receive data is
+ * available */
+static long st_receive(void *priv_data, struct sk_buff *skb)
+{
+ struct ti_st *lhst = priv_data;
+ int err;
+
+ if (!skb)
+ return -EFAULT;
+
+ if (!lhst) {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+
+ skb->dev = (void *) lhst->hdev;
+
+ /* Forward skb to HCI core layer */
+ err = hci_recv_frame(skb);
+ if (err < 0) {
+ BT_ERR("Unable to push skb to HCI core(%d)", err);
+ return err;
+ }
+
+ lhst->hdev->stat.byte_rx += skb->len;
+
+ return 0;
+}
+
+/* ------- Interfaces to HCI layer ------ */
+/* protocol structure registered with shared transport */
+static struct st_proto_s ti_st_proto[MAX_BT_CHNL_IDS] = {
+ {
+ .chnl_id = HCI_ACLDATA_PKT, /* ACL */
+ .hdr_len = sizeof(struct hci_acl_hdr),
+ .offset_len_in_hdr = offsetof(struct hci_acl_hdr, dlen),
+ .len_size = 2, /* sizeof(dlen) in struct hci_acl_hdr */
+ .reserve = 8,
+ },
+ {
+ .chnl_id = HCI_SCODATA_PKT, /* SCO */
+ .hdr_len = sizeof(struct hci_sco_hdr),
+ .offset_len_in_hdr = offsetof(struct hci_sco_hdr, dlen),
+ .len_size = 1, /* sizeof(dlen) in struct hci_sco_hdr */
+ .reserve = 8,
+ },
+ {
+ .chnl_id = HCI_EVENT_PKT, /* HCI Events */
+ .hdr_len = sizeof(struct hci_event_hdr),
+ .offset_len_in_hdr = offsetof(struct hci_event_hdr, plen),
+ .len_size = 1, /* sizeof(plen) in struct hci_event_hdr */
+ .reserve = 8,
+ },
+};
+
+/* Called from HCI core to initialize the device */
+static int ti_st_open(struct hci_dev *hdev)
+{
+ unsigned long timeleft;
+ struct ti_st *hst;
+ int err, i;
+
+ BT_DBG("%s %p", hdev->name, hdev);
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ /* provide contexts for callbacks from ST */
+ hst = hdev->driver_data;
+
+ for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
+ ti_st_proto[i].priv_data = hst;
+ ti_st_proto[i].max_frame_size = HCI_MAX_FRAME_SIZE;
+ ti_st_proto[i].recv = st_receive;
+ ti_st_proto[i].reg_complete_cb = st_reg_completion_cb;
+
+ /* Prepare wait-for-completion handler */
+ init_completion(&hst->wait_reg_completion);
+ /* Reset ST registration callback status flag,
+ * this value will be updated in
+ * st_reg_completion_cb()
+ * function whenever it called from ST driver.
+ */
+ hst->reg_status = -EINPROGRESS;
+
+ err = st_register(&ti_st_proto[i]);
+ if (!err)
+ goto done;
+
+ if (err != -EINPROGRESS) {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ BT_ERR("st_register failed %d", err);
+ return err;
+ }
+
+ /* ST is busy with either protocol
+ * registration or firmware download.
+ */
+ BT_DBG("waiting for registration "
+ "completion signal from ST");
+ timeleft = wait_for_completion_timeout
+ (&hst->wait_reg_completion,
+ msecs_to_jiffies(BT_REGISTER_TIMEOUT));
+ if (!timeleft) {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ BT_ERR("Timeout(%d sec),didn't get reg "
+ "completion signal from ST",
+ BT_REGISTER_TIMEOUT / 1000);
+ return -ETIMEDOUT;
+ }
+
+ /* Is ST registration callback
+ * called with ERROR status? */
+ if (hst->reg_status != 0) {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ BT_ERR("ST registration completed with invalid "
+ "status %d", hst->reg_status);
+ return -EAGAIN;
+ }
+
+done:
+ hst->st_write = ti_st_proto[i].write;
+ if (!hst->st_write) {
+ BT_ERR("undefined ST write function");
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
+ /* Undo registration with ST */
+ err = st_unregister(&ti_st_proto[i]);
+ if (err)
+ BT_ERR("st_unregister() failed with "
+ "error %d", err);
+ hst->st_write = NULL;
+ }
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+/* Close device */
+static int ti_st_close(struct hci_dev *hdev)
+{
+ int err, i;
+ struct ti_st *hst = hdev->driver_data;
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
+ err = st_unregister(&ti_st_proto[i]);
+ if (err)
+ BT_ERR("st_unregister(%d) failed with error %d",
+ ti_st_proto[i].chnl_id, err);
+ }
+
+ hst->st_write = NULL;
+
+ return err;
+}
+
+static int ti_st_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev;
+ struct ti_st *hst;
+ long len;
+
+ hdev = (struct hci_dev *)skb->dev;
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ hst = hdev->driver_data;
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+ BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type,
+ skb->len);
+
+ /* Insert skb to shared transport layer's transmit queue.
+ * Freeing skb memory is taken care in shared transport layer,
+ * so don't free skb memory here.
+ */
+ len = hst->st_write(skb);
+ if (len < 0) {
+ kfree_skb(skb);
+ BT_ERR("ST write failed (%ld)", len);
+ /* Try Again, would only fail if UART has gone bad */
+ return -EAGAIN;
+ }
+
+ /* ST accepted our skb. So, Go ahead and do rest */
+ hdev->stat.byte_tx += len;
+ ti_st_tx_complete(hst, bt_cb(skb)->pkt_type);
+
+ return 0;
+}
+
+static void ti_st_destruct(struct hci_dev *hdev)
+{
+ BT_DBG("%s", hdev->name);
+ /* do nothing here, since platform remove
+ * would free the hdev->driver_data
+ */
+}
+
+static int bt_ti_probe(struct platform_device *pdev)
+{
+ static struct ti_st *hst;
+ struct hci_dev *hdev;
+ int err;
+
+ hst = kzalloc(sizeof(struct ti_st), GFP_KERNEL);
+ if (!hst)
+ return -ENOMEM;
+
+ /* Expose "hciX" device to user space */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ kfree(hst);
+ return -ENOMEM;
+ }
+
+ BT_DBG("hdev %p", hdev);
+
+ hst->hdev = hdev;
+ hdev->bus = HCI_UART;
+ hdev->driver_data = hst;
+ hdev->open = ti_st_open;
+ hdev->close = ti_st_close;
+ hdev->flush = NULL;
+ hdev->send = ti_st_send_frame;
+ hdev->destruct = ti_st_destruct;
+ hdev->owner = THIS_MODULE;
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ BT_ERR("Can't register HCI device error %d", err);
+ kfree(hst);
+ hci_free_dev(hdev);
+ return err;
+ }
+
+ BT_DBG("HCI device registered (hdev %p)", hdev);
+
+ dev_set_drvdata(&pdev->dev, hst);
+ return err;
+}
+
+static int bt_ti_remove(struct platform_device *pdev)
+{
+ struct hci_dev *hdev;
+ struct ti_st *hst = dev_get_drvdata(&pdev->dev);
+
+ if (!hst)
+ return -EFAULT;
+
+ BT_DBG("%s", hst->hdev->name);
+
+ hdev = hst->hdev;
+ ti_st_close(hdev);
+ hci_unregister_dev(hdev);
+
+ hci_free_dev(hdev);
+ kfree(hst);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+ return 0;
+}
+
+static struct platform_driver btwilink_driver = {
+ .probe = bt_ti_probe,
+ .remove = bt_ti_remove,
+ .driver = {
+ .name = "btwilink",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* ------- Module Init/Exit interfaces ------ */
+static int __init btwilink_init(void)
+{
+ BT_INFO("Bluetooth Driver for TI WiLink - Version %s", VERSION);
+
+ return platform_driver_register(&btwilink_driver);
+}
+
+static void __exit btwilink_exit(void)
+{
+ platform_driver_unregister(&btwilink_driver);
+}
+
+module_init(btwilink_init);
+module_exit(btwilink_exit);
+
+/* ------ Module Info ------ */
+
+MODULE_AUTHOR("Raja Mani <raja_mani@ti.com>");
+MODULE_DESCRIPTION("Bluetooth Driver for TI Shared Transport" VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index 6a160c1..bd34406 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -51,32 +51,32 @@ struct ath_struct {
static int ath_wakeup_ar3k(struct tty_struct *tty)
{
- struct termios settings;
- int status = tty->driver->ops->tiocmget(tty, NULL);
+ struct ktermios ktermios;
+ int status = tty->driver->ops->tiocmget(tty);
if (status & TIOCM_CTS)
return status;
/* Disable Automatic RTSCTS */
- n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings);
- settings.c_cflag &= ~CRTSCTS;
- n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings);
+ memcpy(&ktermios, tty->termios, sizeof(ktermios));
+ ktermios.c_cflag &= ~CRTSCTS;
+ tty_set_termios(tty, &ktermios);
/* Clear RTS first */
- status = tty->driver->ops->tiocmget(tty, NULL);
- tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS);
+ status = tty->driver->ops->tiocmget(tty);
+ tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS);
mdelay(20);
/* Set RTS, wake up board */
- status = tty->driver->ops->tiocmget(tty, NULL);
- tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00);
+ status = tty->driver->ops->tiocmget(tty);
+ tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
mdelay(20);
- status = tty->driver->ops->tiocmget(tty, NULL);
+ status = tty->driver->ops->tiocmget(tty);
- n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings);
- settings.c_cflag |= CRTSCTS;
- n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings);
+ /* Disable Automatic RTSCTS */
+ ktermios.c_cflag |= CRTSCTS;
+ status = tty_set_termios(tty, &ktermios);
return status;
}
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 3c6cabc..48ad2a7 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -398,6 +398,7 @@ static int hci_uart_register_dev(struct hci_uart *hu)
hdev->flush = hci_uart_flush;
hdev->send = hci_uart_send_frame;
hdev->destruct = hci_uart_destruct;
+ hdev->parent = hu->tty->dev;
hdev->owner = THIS_MODULE;
OpenPOWER on IntegriCloud