SOFTWARE IS DISCLAIMED.
*/
-/*
- * Bluetooth HCI Core.
- *
- * $Id: hci_core.c,v 1.6 2002/04/17 17:37:16 maxk Exp $
- */
+/* Bluetooth HCI core. */
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
-#include <linux/major.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
#ifndef CONFIG_BT_HCI_CORE_DEBUG
#undef BT_DBG
-#define BT_DBG( A... )
+#define BT_DBG(D...)
#endif
static void hci_cmd_task(unsigned long arg);
static void hci_tx_task(unsigned long arg);
static void hci_notify(struct hci_dev *hdev, int event);
-rwlock_t hci_task_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(hci_task_lock);
/* HCI device list */
LIST_HEAD(hci_dev_list);
-rwlock_t hci_dev_list_lock = RW_LOCK_UNLOCKED;
+DEFINE_RWLOCK(hci_dev_list_lock);
+
+/* HCI callback list */
+LIST_HEAD(hci_cb_list);
+DEFINE_RWLOCK(hci_cb_list_lock);
/* HCI protocols */
#define HCI_MAX_PROTO 2
struct hci_proto *hci_proto[HCI_MAX_PROTO];
/* HCI notifiers list */
-static struct notifier_block *hci_notifier;
+static ATOMIC_NOTIFIER_HEAD(hci_notifier);
/* ---- HCI notifications ---- */
int hci_register_notifier(struct notifier_block *nb)
{
- return notifier_chain_register(&hci_notifier, nb);
+ return atomic_notifier_chain_register(&hci_notifier, nb);
}
int hci_unregister_notifier(struct notifier_block *nb)
{
- return notifier_chain_unregister(&hci_notifier, nb);
+ return atomic_notifier_chain_unregister(&hci_notifier, nb);
}
-void hci_notify(struct hci_dev *hdev, int event)
+static void hci_notify(struct hci_dev *hdev, int event)
{
- notifier_call_chain(&hci_notifier, event, hdev);
+ atomic_notifier_call_chain(&hci_notifier, event, hdev);
}
/* ---- HCI requests ---- */
}
}
-void hci_req_cancel(struct hci_dev *hdev, int err)
+static void hci_req_cancel(struct hci_dev *hdev, int err)
{
BT_DBG("%s err 0x%2.2x", hdev->name, err);
static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
{
- __u16 param;
+ struct sk_buff *skb;
+ __le16 param;
BT_DBG("%s %ld", hdev->name, opt);
+ /* Driver initialization */
+
+ /* Special commands */
+ while ((skb = skb_dequeue(&hdev->driver_init))) {
+ bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+ skb->dev = (void *) hdev;
+ skb_queue_tail(&hdev->cmd_q, skb);
+ hci_sched_cmd(hdev);
+ }
+ skb_queue_purge(&hdev->driver_init);
+
/* Mandatory initialization */
/* Reset */
/* Read Local Supported Features */
hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES, 0, NULL);
+ /* Read Local Version */
+ hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION, 0, NULL);
+
/* Read Buffer Size (ACL mtu, max pkt, etc.) */
hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE, 0, NULL);
}
/* ---- Inquiry support ---- */
-void inquiry_cache_flush(struct hci_dev *hdev)
+static void inquiry_cache_flush(struct hci_dev *hdev)
{
struct inquiry_cache *cache = &hdev->inq_cache;
struct inquiry_entry *next = cache->list, *e;
}
}
-struct inquiry_entry *inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
+struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct inquiry_cache *cache = &hdev->inq_cache;
struct inquiry_entry *e;
BT_DBG("cache %p, %s", cache, batostr(bdaddr));
for (e = cache->list; e; e = e->next)
- if (!bacmp(&e->info.bdaddr, bdaddr))
+ if (!bacmp(&e->data.bdaddr, bdaddr))
break;
return e;
}
-void inquiry_cache_update(struct hci_dev *hdev, struct inquiry_info *info)
+void hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data)
{
struct inquiry_cache *cache = &hdev->inq_cache;
struct inquiry_entry *e;
- BT_DBG("cache %p, %s", cache, batostr(&info->bdaddr));
+ BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr));
- if (!(e = inquiry_cache_lookup(hdev, &info->bdaddr))) {
+ if (!(e = hci_inquiry_cache_lookup(hdev, &data->bdaddr))) {
/* Entry not in the cache. Add new one. */
- if (!(e = kmalloc(sizeof(struct inquiry_entry), GFP_ATOMIC)))
+ if (!(e = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC)))
return;
- memset(e, 0, sizeof(struct inquiry_entry));
e->next = cache->list;
cache->list = e;
}
- memcpy(&e->info, info, sizeof(*info));
+ memcpy(&e->data, data, sizeof(*data));
e->timestamp = jiffies;
cache->timestamp = jiffies;
}
-int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
+static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
{
struct inquiry_cache *cache = &hdev->inq_cache;
struct inquiry_info *info = (struct inquiry_info *) buf;
struct inquiry_entry *e;
int copied = 0;
- for (e = cache->list; e && copied < num; e = e->next, copied++)
- memcpy(info++, &e->info, sizeof(*info));
+ for (e = cache->list; e && copied < num; e = e->next, copied++) {
+ struct inquiry_data *data = &e->data;
+ bacpy(&info->bdaddr, &data->bdaddr);
+ info->pscan_rep_mode = data->pscan_rep_mode;
+ info->pscan_period_mode = data->pscan_period_mode;
+ info->pscan_mode = data->pscan_mode;
+ memcpy(info->dev_class, data->dev_class, 3);
+ info->clock_offset = data->clock_offset;
+ info++;
+ }
BT_DBG("cache %p, copied %d", cache, copied);
return copied;
hci_send_cmd(hdev, OGF_LINK_CTL, OCF_INQUIRY, sizeof(cp), &cp);
}
-int hci_inquiry(unsigned long arg)
+int hci_inquiry(void __user *arg)
{
+ __u8 __user *ptr = arg;
struct hci_inquiry_req ir;
struct hci_dev *hdev;
int err = 0, do_inquiry = 0, max_rsp;
long timeo;
- __u8 *buf, *ptr;
+ __u8 *buf;
- ptr = (void *) arg;
if (copy_from_user(&ir, ptr, sizeof(ir)))
return -EFAULT;
}
hci_dev_unlock_bh(hdev);
- timeo = ir.length * 2 * HZ;
+ timeo = ir.length * msecs_to_jiffies(2000);
if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
goto done;
if (!copy_to_user(ptr, &ir, sizeof(ir))) {
ptr += sizeof(ir);
- if (copy_to_user(ptr, buf, sizeof(struct inquiry_info) *
+ if (copy_to_user(ptr, buf, sizeof(struct inquiry_info) *
ir.num_rsp))
err = -EFAULT;
} else
goto done;
}
+ if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
+ set_bit(HCI_RAW, &hdev->flags);
+
if (hdev->open(hdev)) {
ret = -EIO;
goto done;
set_bit(HCI_INIT, &hdev->flags);
//__hci_request(hdev, hci_reset_req, 0, HZ);
- ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
-
+ ret = __hci_request(hdev, hci_init_req, 0,
+ msecs_to_jiffies(HCI_INIT_TIMEOUT));
+
clear_bit(HCI_INIT, &hdev->flags);
}
inquiry_cache_flush(hdev);
hci_conn_hash_flush(hdev);
hci_dev_unlock_bh(hdev);
-
+
hci_notify(hdev, HCI_DEV_DOWN);
if (hdev->flush)
/* Reset device */
skb_queue_purge(&hdev->cmd_q);
atomic_set(&hdev->cmd_cnt, 1);
- set_bit(HCI_INIT, &hdev->flags);
- __hci_request(hdev, hci_reset_req, 0, HZ/4);
- clear_bit(HCI_INIT, &hdev->flags);
+ if (!test_bit(HCI_RAW, &hdev->flags)) {
+ set_bit(HCI_INIT, &hdev->flags);
+ __hci_request(hdev, hci_reset_req, 0,
+ msecs_to_jiffies(250));
+ clear_bit(HCI_INIT, &hdev->flags);
+ }
/* Kill cmd task */
tasklet_kill(&hdev->cmd_task);
{
struct hci_dev *hdev;
int err;
-
+
if (!(hdev = hci_dev_get(dev)))
return -ENODEV;
err = hci_dev_do_close(hdev);
atomic_set(&hdev->cmd_cnt, 1);
hdev->acl_cnt = 0; hdev->sco_cnt = 0;
- ret = __hci_request(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT);
+ if (!test_bit(HCI_RAW, &hdev->flags))
+ ret = __hci_request(hdev, hci_reset_req, 0,
+ msecs_to_jiffies(HCI_INIT_TIMEOUT));
done:
tasklet_enable(&hdev->tx_task);
return ret;
}
-int hci_dev_cmd(unsigned int cmd, unsigned long arg)
+int hci_dev_cmd(unsigned int cmd, void __user *arg)
{
struct hci_dev *hdev;
struct hci_dev_req dr;
int err = 0;
- if (copy_from_user(&dr, (void *) arg, sizeof(dr)))
+ if (copy_from_user(&dr, arg, sizeof(dr)))
return -EFAULT;
if (!(hdev = hci_dev_get(dr.dev_id)))
switch (cmd) {
case HCISETAUTH:
- err = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+ err = hci_request(hdev, hci_auth_req, dr.dev_opt,
+ msecs_to_jiffies(HCI_INIT_TIMEOUT));
break;
case HCISETENCRYPT:
if (!test_bit(HCI_AUTH, &hdev->flags)) {
/* Auth must be enabled first */
- err = hci_request(hdev, hci_auth_req,
- dr.dev_opt, HCI_INIT_TIMEOUT);
+ err = hci_request(hdev, hci_auth_req, dr.dev_opt,
+ msecs_to_jiffies(HCI_INIT_TIMEOUT));
if (err)
break;
}
-
- err = hci_request(hdev, hci_encrypt_req,
- dr.dev_opt, HCI_INIT_TIMEOUT);
+
+ err = hci_request(hdev, hci_encrypt_req, dr.dev_opt,
+ msecs_to_jiffies(HCI_INIT_TIMEOUT));
break;
-
+
case HCISETSCAN:
- err = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+ err = hci_request(hdev, hci_scan_req, dr.dev_opt,
+ msecs_to_jiffies(HCI_INIT_TIMEOUT));
break;
-
+
case HCISETPTYPE:
hdev->pkt_type = (__u16) dr.dev_opt;
break;
-
+
case HCISETLINKPOL:
hdev->link_policy = (__u16) dr.dev_opt;
break;
default:
err = -EINVAL;
break;
- }
+ }
hci_dev_put(hdev);
return err;
}
-int hci_get_dev_list(unsigned long arg)
+int hci_get_dev_list(void __user *arg)
{
struct hci_dev_list_req *dl;
struct hci_dev_req *dr;
int n = 0, size, err;
__u16 dev_num;
- if (get_user(dev_num, (__u16 *) arg))
+ if (get_user(dev_num, (__u16 __user *) arg))
return -EFAULT;
if (!dev_num || dev_num > (PAGE_SIZE * 2) / sizeof(*dr))
dl->dev_num = n;
size = sizeof(*dl) + n * sizeof(*dr);
- err = copy_to_user((void *) arg, dl, size);
+ err = copy_to_user(arg, dl, size);
kfree(dl);
return err ? -EFAULT : 0;
}
-int hci_get_dev_info(unsigned long arg)
+int hci_get_dev_info(void __user *arg)
{
struct hci_dev *hdev;
struct hci_dev_info di;
int err = 0;
- if (copy_from_user(&di, (void *) arg, sizeof(di)))
+ if (copy_from_user(&di, arg, sizeof(di)))
return -EFAULT;
if (!(hdev = hci_dev_get(di.dev_id)))
memcpy(&di.stat, &hdev->stat, sizeof(di.stat));
memcpy(&di.features, &hdev->features, sizeof(di.features));
- if (copy_to_user((void *) arg, &di, sizeof(di)))
+ if (copy_to_user(arg, &di, sizeof(di)))
err = -EFAULT;
hci_dev_put(hdev);
{
struct hci_dev *hdev;
- hdev = kmalloc(sizeof(struct hci_dev), GFP_KERNEL);
+ hdev = kzalloc(sizeof(struct hci_dev), GFP_KERNEL);
if (!hdev)
return NULL;
- memset(hdev, 0, sizeof(struct hci_dev));
+ skb_queue_head_init(&hdev->driver_init);
return hdev;
}
+EXPORT_SYMBOL(hci_alloc_dev);
/* Free HCI device */
void hci_free_dev(struct hci_dev *hdev)
{
- /* will free via class release */
- class_device_put(&hdev->class_dev);
+ skb_queue_purge(&hdev->driver_init);
+
+ /* will free via device release */
+ put_device(&hdev->dev);
}
+EXPORT_SYMBOL(hci_free_dev);
/* Register HCI device */
int hci_register_dev(struct hci_dev *hdev)
/* Find first available device id */
list_for_each(p, &hci_dev_list) {
- if (list_entry(p, struct hci_dev, list)->id != id)
+ if (list_entry(p, struct hci_dev, list)->id != id)
break;
head = p; id++;
}
atomic_set(&hdev->refcnt, 1);
spin_lock_init(&hdev->lock);
-
+
hdev->flags = 0;
hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
hdev->link_mode = (HCI_LM_ACCEPT);
+ hdev->idle_timeout = 0;
+ hdev->sniff_max_interval = 800;
+ hdev->sniff_min_interval = 80;
+
tasklet_init(&hdev->cmd_task, hci_cmd_task,(unsigned long) hdev);
tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev);
memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
atomic_set(&hdev->promisc, 0);
-
+
write_unlock_bh(&hci_dev_list_lock);
hci_register_sysfs(hdev);
return id;
}
+EXPORT_SYMBOL(hci_register_dev);
/* Unregister HCI device */
int hci_unregister_dev(struct hci_dev *hdev)
__hci_dev_put(hdev);
return 0;
}
+EXPORT_SYMBOL(hci_unregister_dev);
/* Suspend HCI device */
int hci_suspend_dev(struct hci_dev *hdev)
hci_notify(hdev, HCI_DEV_SUSPEND);
return 0;
}
+EXPORT_SYMBOL(hci_suspend_dev);
/* Resume HCI device */
int hci_resume_dev(struct hci_dev *hdev)
{
hci_notify(hdev, HCI_DEV_RESUME);
return 0;
-}
+}
+EXPORT_SYMBOL(hci_resume_dev);
/* ---- Interface to upper protocols ---- */
return err;
}
+EXPORT_SYMBOL(hci_register_proto);
int hci_unregister_proto(struct hci_proto *hp)
{
return err;
}
+EXPORT_SYMBOL(hci_unregister_proto);
+
+int hci_register_cb(struct hci_cb *cb)
+{
+ BT_DBG("%p name %s", cb, cb->name);
+
+ write_lock_bh(&hci_cb_list_lock);
+ list_add(&cb->list, &hci_cb_list);
+ write_unlock_bh(&hci_cb_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(hci_register_cb);
+
+int hci_unregister_cb(struct hci_cb *cb)
+{
+ BT_DBG("%p name %s", cb, cb->name);
+
+ write_lock_bh(&hci_cb_list_lock);
+ list_del(&cb->list);
+ write_unlock_bh(&hci_cb_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(hci_unregister_cb);
static int hci_send_frame(struct sk_buff *skb)
{
return -ENODEV;
}
- BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+ BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
if (atomic_read(&hdev->promisc)) {
/* Time stamp */
- do_gettimeofday(&skb->stamp);
+ __net_timestamp(skb);
hci_send_to_sock(hdev, skb);
}
BT_DBG("skb len %d", skb->len);
- skb->pkt_type = HCI_COMMAND_PKT;
+ bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
skb->dev = (void *) hdev;
skb_queue_tail(&hdev->cmd_q, skb);
hci_sched_cmd(hdev);
BT_DBG("%s conn %p flags 0x%x", hdev->name, conn, flags);
skb->dev = (void *) hdev;
- skb->pkt_type = HCI_ACLDATA_PKT;
+ bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
hci_add_acl_hdr(skb, conn->handle, flags | ACL_START);
if (!(list = skb_shinfo(skb)->frag_list)) {
/* Non fragmented */
BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
-
+
skb_queue_tail(&conn->data_q, skb);
} else {
/* Fragmented */
skb = list; list = list->next;
skb->dev = (void *) hdev;
- skb->pkt_type = HCI_ACLDATA_PKT;
+ bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
hci_add_acl_hdr(skb, conn->handle, flags | ACL_CONT);
-
+
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
__skb_queue_tail(&conn->data_q, skb);
spin_unlock_bh(&conn->data_q.lock);
}
-
+
hci_sched_tx(hdev);
return 0;
}
+EXPORT_SYMBOL(hci_send_acl);
/* Send SCO data */
int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
memcpy(skb->h.raw, &hdr, HCI_SCO_HDR_SIZE);
skb->dev = (void *) hdev;
- skb->pkt_type = HCI_SCODATA_PKT;
+ bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
skb_queue_tail(&conn->data_q, skb);
hci_sched_tx(hdev);
return 0;
}
+EXPORT_SYMBOL(hci_send_sco);
/* ---- HCI TX task (outgoing data) ---- */
struct hci_conn_hash *h = &hdev->conn_hash;
struct hci_conn *conn = NULL;
int num = 0, min = ~0;
- struct list_head *p;
+ struct list_head *p;
/* We don't have to lock device here. Connections are always
* added and removed with TX task disabled. */
static inline void hci_acl_tx_to(struct hci_dev *hdev)
{
struct hci_conn_hash *h = &hdev->conn_hash;
- struct list_head *p;
+ struct list_head *p;
struct hci_conn *c;
BT_ERR("%s ACL tx timeout", hdev->name);
BT_DBG("%s", hdev->name);
- /* ACL tx timeout must be longer than maximum
- * link supervision timeout (40.9 seconds) */
- if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > (HZ * 45))
- hci_acl_tx_to(hdev);
+ if (!test_bit(HCI_RAW, &hdev->flags)) {
+ /* ACL tx timeout must be longer than maximum
+ * link supervision timeout (40.9 seconds) */
+ if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > (HZ * 45))
+ hci_acl_tx_to(hdev);
+ }
while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
+
+ hci_conn_enter_active_mode(conn);
+
hci_send_frame(skb);
hdev->acl_last_tx = jiffies;
if (conn) {
register struct hci_proto *hp;
+ hci_conn_enter_active_mode(conn);
+
/* Send to upper protocol */
if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) {
hp->recv_acldata(conn, skb, flags);
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_handle(hdev, handle);
hci_dev_unlock(hdev);
-
+
if (conn) {
register struct hci_proto *hp;
kfree_skb(skb);
}
-void hci_rx_task(unsigned long arg)
+static void hci_rx_task(unsigned long arg)
{
struct hci_dev *hdev = (struct hci_dev *) arg;
struct sk_buff *skb;
if (test_bit(HCI_INIT, &hdev->flags)) {
/* Don't process data packets in this states. */
- switch (skb->pkt_type) {
+ switch (bt_cb(skb)->pkt_type) {
case HCI_ACLDATA_PKT:
case HCI_SCODATA_PKT:
kfree_skb(skb);
}
/* Process frame */
- switch (skb->pkt_type) {
+ switch (bt_cb(skb)->pkt_type) {
case HCI_EVENT_PKT:
hci_event_packet(hdev, skb);
break;
BT_ERR("%s command tx timeout", hdev->name);
atomic_set(&hdev->cmd_cnt, 1);
}
-
+
/* Send queued commands */
if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) {
if (hdev->sent_cmd)