/* * Texas Instrument's Bluetooth Driver For Shared Transport. * * Bluetooth Driver acts as interface between HCI CORE and * TI Shared Transport Layer. * * Copyright (C) 2009 Texas Instruments * * 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 * */ #include #include #include "st.h" #include "bt_drv.h" /* Define this macro to get debug msg */ #undef DEBUG #ifdef DEBUG #define BT_DRV_DBG(fmt, arg...) printk(KERN_INFO "(btdrv):"fmt"\n" , ## arg) #define BTDRV_API_START() printk(KERN_INFO "(btdrv): %s Start\n", \ __func__) #define BTDRV_API_EXIT(errno) printk(KERN_INFO "(btdrv): %s Exit(%d)\n", \ __func__, errno) #else #define BT_DRV_DBG(fmt, arg...) #define BTDRV_API_START() #define BTDRV_API_EXIT(errno) #endif #define BT_DRV_ERR(fmt, arg...) printk(KERN_ERR "(btdrv):"fmt"\n" , ## arg) static int reset; static struct hci_st *hst; /* Increments HCI counters based on pocket ID (cmd,acl,sco) */ static inline void hci_st_tx_complete(struct hci_st *hst, int pkt_type) { struct hci_dev *hdev; BTDRV_API_START(); 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.cmd_tx++; break; } BTDRV_API_EXIT(0); } /* ------- Interfaces to Shared Transport ------ */ /* Called by ST layer to indicate protocol registration completion * status.hci_st_open() function will wait for signal from this * API when st_register() function returns ST_PENDING. */ static void hci_st_registration_completion_cb(char data) { BTDRV_API_START(); /* hci_st_open() function needs value of 'data' to know * the registration status(success/fail),So have a back * up of it. */ hst->streg_cbdata = data; /* Got a feedback from ST for BT driver registration * request.Wackup hci_st_open() function to continue * it's open operation. */ complete(&hst->wait_for_btdrv_reg_completion); BTDRV_API_EXIT(0); } /* Called by Shared Transport layer when receive data is * available */ static long hci_st_receive(struct sk_buff *skb) { int err; int len; BTDRV_API_START(); err = 0; len = 0; if (skb == NULL) { BT_DRV_ERR("Invalid SKB received from ST"); BTDRV_API_EXIT(-EFAULT); return -EFAULT; } if (!hst) { kfree_skb(skb); BT_DRV_ERR("Invalid hci_st memory,freeing SKB"); BTDRV_API_EXIT(-EFAULT); return -EFAULT; } if (!test_bit(BT_DRV_RUNNING, &hst->flags)) { kfree_skb(skb); BT_DRV_ERR("Device is not running,freeing SKB"); BTDRV_API_EXIT(-EINVAL); return -EINVAL; } len = skb->len; skb->dev = (struct net_device *)hst->hdev; /* Forward skb to HCI CORE layer */ err = hci_recv_frame(skb); if (err) { kfree_skb(skb); BT_DRV_ERR("Unable to push skb to HCI CORE(%d),freeing SKB", err); BTDRV_API_EXIT(err); return err; } hst->hdev->stat.byte_rx += len; BTDRV_API_EXIT(0); return 0; } /* ------- Interfaces to HCI layer ------ */ /* Called from HCI core to initialize the device */ static int hci_st_open(struct hci_dev *hdev) { static struct st_proto_s hci_st_proto; unsigned long timeleft; int err; BTDRV_API_START(); err = 0; BT_DRV_DBG("%s %p", hdev->name, hdev); /* Already registered with ST ? */ if (test_bit(BT_ST_REGISTERED, &hst->flags)) { BT_DRV_ERR("Registered with ST already,open called again?"); BTDRV_API_EXIT(0); return 0; } /* Populate BT driver info required by ST */ memset(&hci_st_proto, 0, sizeof(hci_st_proto)); /* BT driver ID */ hci_st_proto.type = ST_BT; /* Receive function which called from ST */ hci_st_proto.recv = hci_st_receive; /* Packet match function may used in future */ hci_st_proto.match_packet = NULL; /* Callback to be called when registration is pending */ hci_st_proto.reg_complete_cb = hci_st_registration_completion_cb; /* This is write function pointer of ST. BT driver will make use of this * for sending any packets to chip. ST will assign and give to us, so * make it as NULL */ hci_st_proto.write = NULL; /* Register with ST layer */ err = st_register(&hci_st_proto); if (err == ST_ERR_PENDING) { /* Prepare wait-for-completion handler data structures. * Needed to syncronize this and st_registration_completion_cb() * functions. */ init_completion(&hst->wait_for_btdrv_reg_completion); /* Reset ST registration callback status flag , this value * will be updated in hci_st_registration_completion_cb() * function whenever it called from ST driver. */ hst->streg_cbdata = -EINPROGRESS; /* ST is busy with other protocol registration(may be busy with * firmware download).So,Wait till the registration callback * (passed as a argument to st_register() function) getting * called from ST. */ BT_DRV_DBG(" %s waiting for reg completion signal from ST", __func__); timeleft = wait_for_completion_timeout (&hst->wait_for_btdrv_reg_completion, msecs_to_jiffies(BT_REGISTER_TIMEOUT)); if (!timeleft) { BT_DRV_ERR("Timeout(%ld sec),didn't get reg" "completion signal from ST", BT_REGISTER_TIMEOUT / 1000); BTDRV_API_EXIT(-ETIMEDOUT); return -ETIMEDOUT; } /* Is ST registration callback called with ERROR value? */ if (hst->streg_cbdata != 0) { BT_DRV_ERR("ST reg completion CB called with invalid" "status %d", hst->streg_cbdata); BTDRV_API_EXIT(-EAGAIN); return -EAGAIN; } err = 0; } else if (err == ST_ERR_FAILURE) { BT_DRV_ERR("st_register failed %d", err); BTDRV_API_EXIT(-EAGAIN); return -EAGAIN; } /* Do we have proper ST write function? */ if (hci_st_proto.write != NULL) { /* We need this pointer for sending any Bluetooth pkts */ hst->st_write = hci_st_proto.write; } else { BT_DRV_ERR("failed to get ST write func pointer"); /* Undo registration with ST */ err = st_unregister(ST_BT); if (err < 0) BT_DRV_ERR("st_unregister failed %d", err); hst->st_write = NULL; BTDRV_API_EXIT(-EAGAIN); return -EAGAIN; } /* Registration with ST layer is completed successfully, * now chip is ready to accept commands from HCI CORE. * Mark HCI Device flag as RUNNING */ set_bit(HCI_RUNNING, &hdev->flags); /* Registration with ST successful */ set_bit(BT_ST_REGISTERED, &hst->flags); BTDRV_API_EXIT(err); return err; } /* Close device */ static int hci_st_close(struct hci_dev *hdev) { int err; BTDRV_API_START(); err = 0; /* Unregister from ST layer */ if (test_and_clear_bit(BT_ST_REGISTERED, &hst->flags)) { err = st_unregister(ST_BT); if (err != ST_SUCCESS) { BT_DRV_ERR("st_unregister failed %d", err); BTDRV_API_EXIT(-EBUSY); return -EBUSY; } } hst->st_write = NULL; /* ST layer would have moved chip to inactive state. * So,clear HCI device RUNNING flag. */ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) { BTDRV_API_EXIT(0); return 0; } BTDRV_API_EXIT(err); return err; } /* Called from HCI CORE , Sends frames to Shared Transport */ static int hci_st_send_frame(struct sk_buff *skb) { struct hci_dev *hdev; struct hci_st *hst; long len; BTDRV_API_START(); if (skb == NULL) { BT_DRV_ERR("Invalid skb received from HCI CORE"); BTDRV_API_EXIT(-ENOMEM); return -ENOMEM; } hdev = (struct hci_dev *)skb->dev; if (!hdev) { BT_DRV_ERR("SKB received for invalid HCI Device (hdev=NULL)"); BTDRV_API_EXIT(-ENODEV); return -ENODEV; } if (!test_bit(HCI_RUNNING, &hdev->flags)) { BT_DRV_ERR("Device is not running"); BTDRV_API_EXIT(-EBUSY); return -EBUSY; } hst = (struct hci_st *)hdev->driver_data; /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); BT_DRV_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. */ if (!hst->st_write) { kfree_skb(skb); BT_DRV_ERR(" Can't write to ST, st_write null?"); BTDRV_API_EXIT(-EAGAIN); return -EAGAIN; } len = hst->st_write(skb); if (len < 0) { /* Something went wrong in st write , free skb memory */ kfree_skb(skb); BT_DRV_ERR(" ST write failed (%ld)", len); BTDRV_API_EXIT(-EAGAIN); return -EAGAIN; } /* ST accepted our skb. So, Go ahead and do rest */ hdev->stat.byte_tx += len; hci_st_tx_complete(hst, bt_cb(skb)->pkt_type); BTDRV_API_EXIT(0); return 0; } static void hci_st_destruct(struct hci_dev *hdev) { BTDRV_API_START(); if (!hdev) { BT_DRV_ERR("Destruct called with invalid HCI Device" "(hdev=NULL)"); BTDRV_API_EXIT(0); return; } BT_DRV_DBG("%s", hdev->name); /* free hci_st memory */ if (hdev->driver_data != NULL) kfree(hdev->driver_data); BTDRV_API_EXIT(0); return; } /* Creates new HCI device */ static int hci_st_register_dev(struct hci_st *hst) { struct hci_dev *hdev; BTDRV_API_START(); /* Initialize and register HCI device */ hdev = hci_alloc_dev(); if (!hdev) { BT_DRV_ERR("Can't allocate HCI device"); BTDRV_API_EXIT(-ENOMEM); return -ENOMEM; } BT_DRV_DBG(" HCI device allocated. hdev= %p", hdev); hst->hdev = hdev; hdev->bus = HCI_UART; hdev->driver_data = hst; hdev->open = hci_st_open; hdev->close = hci_st_close; hdev->flush = NULL; hdev->send = hci_st_send_frame; hdev->destruct = hci_st_destruct; hdev->owner = THIS_MODULE; if (reset) set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); if (hci_register_dev(hdev) < 0) { BT_DRV_ERR("Can't register HCI device"); hci_free_dev(hdev); BTDRV_API_EXIT(-ENODEV); return -ENODEV; } BT_DRV_DBG(" HCI device registered. hdev= %p", hdev); BTDRV_API_EXIT(0); return 0; } /* ------- Module Init interface ------ */ static int __init bt_drv_init(void) { int err; BTDRV_API_START(); err = 0; BT_DRV_DBG(" Bluetooth Driver Version %s", VERSION); /* Allocate local resource memory */ hst = kzalloc(sizeof(struct hci_st), GFP_KERNEL); if (!hst) { BT_DRV_ERR("Can't allocate control structure"); BTDRV_API_EXIT(-ENFILE); return -ENFILE; } /* Expose "hciX" device to user space */ err = hci_st_register_dev(hst); if (err) { /* Release local resource memory */ kfree(hst); BT_DRV_ERR("Unable to expose hci0 device(%d)", err); BTDRV_API_EXIT(err); return err; } set_bit(BT_DRV_RUNNING, &hst->flags); BTDRV_API_EXIT(err); return err; } /* ------- Module Exit interface ------ */ static void __exit bt_drv_exit(void) { BTDRV_API_START(); /* Deallocate local resource's memory */ if (hst) { struct hci_dev *hdev = hst->hdev; if (hdev == NULL) { BT_DRV_ERR("Invalid hdev memory"); kfree(hst); } else { hci_st_close(hdev); if (test_and_clear_bit(BT_DRV_RUNNING, &hst->flags)) { /* Remove HCI device (hciX) created * in module init. */ hci_unregister_dev(hdev); /* Free HCI device memory */ hci_free_dev(hdev); } } } BTDRV_API_EXIT(0); } module_init(bt_drv_init); module_exit(bt_drv_exit); /* ------ Module Info ------ */ module_param(reset, bool, 0644); MODULE_PARM_DESC(reset, "Send HCI reset command on initialization"); MODULE_AUTHOR("Raja Mani "); MODULE_DESCRIPTION("Bluetooth Driver for TI Shared Transport" VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL");