From 7e2aa37a35723ab72e53e78d58c348e3caf1ae78 Mon Sep 17 00:00:00 2001 From: Alok Barsode Date: Tue, 3 Mar 2009 20:57:01 +0530 Subject: [PATCH] Initial Netlink support. Signed-off-by: Alok Barsode --- include/net/bluetooth/bluetooth.h | 3 + include/net/bluetooth/hci_core.h | 2 + net/bluetooth/Makefile | 2 +- net/bluetooth/af_bluetooth.c | 6 + net/bluetooth/hci_core.c | 37 +++++ net/bluetooth/netlink.c | 307 +++++++++++++++++++++++++++++++++++++ net/bluetooth/netlink.h | 40 +++++ 7 files changed, 396 insertions(+), 1 deletions(-) create mode 100644 net/bluetooth/netlink.c create mode 100644 net/bluetooth/netlink.h diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 3ad5390..e3d792a 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -190,4 +190,7 @@ extern void bt_sysfs_cleanup(void); extern struct class *bt_class; +extern int bluetooth_netlink_init(void); +extern void bluetooth_netlink_cleanup(void); + #endif /* __BLUETOOTH_H */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 01f9316..cc5d33b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -700,4 +700,6 @@ struct hci_sec_filter { void hci_req_complete(struct hci_dev *hdev, int result); +/* FIXME: This is temporarily added to export __hci_request and hci_init_req */ +int hci_handle_request(struct hci_dev *hdev, int event, unsigned long opt); #endif /* __HCI_CORE_H */ diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index d1e433f..f014d48 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -10,4 +10,4 @@ obj-$(CONFIG_BT_BNEP) += bnep/ obj-$(CONFIG_BT_CMTP) += cmtp/ obj-$(CONFIG_BT_HIDP) += hidp/ -bluetooth-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o hci_sysfs.o lib.o +bluetooth-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o hci_sysfs.o lib.o netlink.o diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 02b9baa..987dbb9 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -422,6 +422,10 @@ static int __init bt_init(void) if (err < 0) return err; + err = bluetooth_netlink_init(); + if (err < 0) + return err; + err = sock_register(&bt_sock_family_ops); if (err < 0) { bt_sysfs_cleanup(); @@ -442,6 +446,8 @@ static void __exit bt_exit(void) sock_unregister(PF_BLUETOOTH); bt_sysfs_cleanup(); + + bluetooth_netlink_cleanup(); } subsys_initcall(bt_init); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index cd06151..aec38a0 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -48,6 +48,8 @@ #include #include +#include "netlink.h" + static void hci_cmd_task(unsigned long arg); static void hci_rx_task(unsigned long arg); static void hci_tx_task(unsigned long arg); @@ -291,6 +293,37 @@ static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt) hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy); } +int hci_handle_request(struct hci_dev *hdev, int event, unsigned long opt) +{ + int err = 0; + + if (!hdev) + return -ENODEV; + + switch (event) { + case HCI_DEV_UP: + err = __hci_request(hdev, hci_init_req, opt, + msecs_to_jiffies(HCI_INIT_TIMEOUT)); + break; + + case HCI_DEV_DOWN: + err = __hci_request(hdev, hci_reset_req, opt, + msecs_to_jiffies(250)); + break; + + case HCISETSCAN: + err = hci_request(hdev, hci_scan_req, opt, + msecs_to_jiffies(HCI_INIT_TIMEOUT)); + break; + + default: + err = -EINVAL; + break; + } + + return err; +} + /* Get HCI device by index. * Device is held on return. */ struct hci_dev *hci_dev_get(int index) @@ -902,6 +935,8 @@ int hci_register_dev(struct hci_dev *hdev) hci_notify(hdev, HCI_DEV_REG); + bluetooth_notify(hdev, HCI_DEV_REG); + return id; } EXPORT_SYMBOL(hci_register_dev); @@ -924,6 +959,8 @@ int hci_unregister_dev(struct hci_dev *hdev) hci_notify(hdev, HCI_DEV_UNREG); + bluetooth_notify(hdev, HCI_DEV_UNREG); + hci_unregister_sysfs(hdev); __hci_dev_put(hdev); diff --git a/net/bluetooth/netlink.c b/net/bluetooth/netlink.c new file mode 100644 index 0000000..c6ecafd --- /dev/null +++ b/net/bluetooth/netlink.c @@ -0,0 +1,307 @@ +/* + * This is the netlink-based bluetooth interface. + * + * Copyright 2008 Alok Barsode + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "netlink.h" + +#define BLUETOOTH_FAMILY_NAME "bluetooth" +#define BLUETOOTH_MCAST_GROUP "bluetooth-events" + +/* family definition */ +static struct genl_family family = { + .id = GENL_ID_GENERATE, + .hdrsize = 0, + .name = BLUETOOTH_FAMILY_NAME, + .version = VERSION, + .maxattr = ATTR_MAX +}; + +static struct genl_multicast_group mcgrp = { + .name = BLUETOOTH_MCAST_GROUP, +}; + +static struct nla_policy policy[ATTR_MAX + 1] = { + [INDEX] = { .type = NLA_U16 }, + [CHANGED] = { .type = NLA_U32 }, + [EVENT] = { .type = NLA_U16 }, + [GRPID] = { .type = NLA_U32 }, +}; + +static void inquiry_cache_flush(struct hci_dev *hdev) +{ + struct inquiry_cache *cache = &hdev->inq_cache; + struct inquiry_entry *next = cache->list, *e; + + BT_DBG("cache %p", cache); + + cache->list = NULL; + while ((e = next)) { + next = e->next; + kfree(e); + } +} + +static int bluetooth_event(int cmd, struct hci_dev *hdev, unsigned long changed) +{ + void *hdr; + struct sk_buff *msg=NULL; + int err = -ENOBUFS; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + goto err_out; + + hdr = genlmsg_put(msg, 0, 0, &family, 0, cmd); + if (hdr == NULL){ + nlmsg_free(msg); + goto err_out; + } + nla_put_u16(msg, INDEX, hdev->id); + nla_put_u32(msg, CHANGED, changed); + + genlmsg_end(msg, hdr); + + err = genlmsg_multicast(msg, 0, mcgrp.id, GFP_ATOMIC); + err_out: + return err; +} + +int bluetooth_notify(struct hci_dev *hdev, int event) +{ + int err = 0; + + switch (event) { + case HCI_DEV_REG: + err = bluetooth_event(NEWHOST, hdev, ~0U); + break; + case HCI_DEV_UNREG: + err = bluetooth_event(DELHOST, hdev, ~0U); + break; + case HCI_DEV_UP: + case HCI_DEV_DOWN: + err = bluetooth_event(HOST, hdev, HCI_UP|HCI_RUNNING); + break; + default: + err = -EINVAL; + printk("\nUnknown event\n"); + break; + } + return err; +} + +static int bluetooth_host(struct sk_buff *skb, struct genl_info *info) +{ + __u16 index; + int ret=0, event; + struct hci_dev *hdev; + + if (!info->attrs[INDEX]) + return -EINVAL; + + index = nla_get_u16(info->attrs[INDEX]); + + /* Nothing to do, return */ + if (!info->attrs[EVENT]) + return 0; + + /* event = UP/DOWN */ + event = nla_get_u16(info->attrs[EVENT]); + + if (!(hdev = hci_dev_get(index))) + return -ENODEV; + + printk("%s %p", hdev->name, hdev); + + if (lock(hdev)){ + ret = -EBUSY; + goto done; + } + + /* Device is already up */ + if (event & test_bit(HCI_UP, &hdev->flags)){ + ret = -EALREADY; + goto unlock; + } + + /* Device is already down */ + if (!event & !test_bit(HCI_UP, &hdev->flags)){ + ret = -EALREADY; + goto unlock; + } + + if (event == DOWN) + goto down; + + if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + set_bit(HCI_RAW, &hdev->flags); + + if (hdev->open(hdev)) { + ret = -EIO; + goto unlock; + } + + if (!test_bit(HCI_RAW, &hdev->flags)) { + atomic_set(&hdev->cmd_cnt, 1); + + set_bit(HCI_INIT, &hdev->flags); + ret = hci_handle_request(hdev, HCI_DEV_UP, 0); + clear_bit(HCI_INIT, &hdev->flags); + } + + /* Host is UP */ + if (!ret) { + hci_dev_hold(hdev); + + set_bit(HCI_UP, &hdev->flags); + ret = bluetooth_notify(hdev, HCI_DEV_UP); + goto unlock; + } + down: + if (hdev->req_status == HCI_REQ_PEND) { + hdev->req_result = ENODEV; + hdev->req_status = HCI_REQ_CANCELED; + wake_up_interruptible(&hdev->req_wait_q); + } + + if (!test_and_clear_bit(HCI_UP, &hdev->flags)){ + ret = -EBUSY; + goto unlock; + } + + /* Kill RX and TX tasks */ + tasklet_kill(&hdev->rx_task); + tasklet_kill(&hdev->tx_task); + + hci_dev_lock_bh(hdev); + inquiry_cache_flush(hdev); + hci_conn_hash_flush(hdev); + hci_dev_unlock_bh(hdev); + + if (hdev->flush) + hdev->flush(hdev); + + /* Reset device */ + skb_queue_purge(&hdev->cmd_q); + atomic_set(&hdev->cmd_cnt, 1); + if (!test_bit(HCI_RAW, &hdev->flags)) { + set_bit(HCI_INIT, &hdev->flags); + ret = hci_handle_request(hdev, HCI_DEV_DOWN, 0); + clear_bit(HCI_INIT, &hdev->flags); + } + + /* Kill cmd task */ + tasklet_kill(&hdev->cmd_task); + + /* Drop queues */ + skb_queue_purge(&hdev->rx_q); + skb_queue_purge(&hdev->cmd_q); + skb_queue_purge(&hdev->raw_q); + + /* Drop last sent command */ + if (hdev->sent_cmd) { + kfree_skb(hdev->sent_cmd); + hdev->sent_cmd = NULL; + } + + /* After this point our queues are empty + * and no tasks are scheduled. */ + hdev->close(hdev); + + /* Clear flags */ + hdev->flags = 0; + ret = bluetooth_notify(hdev, HCI_DEV_DOWN); + hci_dev_put(hdev); + unlock: + unlock(hdev); + done: + hci_dev_put(hdev); + return ret; +} + +/* Unfortunately Generic netlink doesn't provide a mechanism to get the groupID. */ +int get_grp_id(struct sk_buff *skb_2, struct genl_info *info) +{ + void *hdr; + struct sk_buff *msg; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return -ENOBUFS; + + hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, &family, 0, GETGRPID); + + if (hdr == NULL){ + nlmsg_free(msg); + return -ENOBUFS; + } + + NLA_PUT_U32(msg, GRPID, mcgrp.id); + + genlmsg_end(msg, hdr); + + return genlmsg_unicast(msg, info->snd_pid); + +nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static struct genl_ops ops[] = { + { + .cmd = HOST, + .policy = policy, + .doit = bluetooth_host, + .flags = GENL_ADMIN_PERM, + .dumpit = NULL, + }, + { + .cmd = GETGRPID, + .flags = 0, + .policy = policy, + .doit = get_grp_id, + .dumpit = NULL, + }, +}; + +/* initialisation/exit functions */ +int __init bluetooth_netlink_init(void) +{ + int err, i; + + err = genl_register_family(&family); + if (err) + return err; + + err = genl_register_mc_group(&family, &mcgrp); + if (err) + goto err_out; + + for (i = 0; i < ARRAY_SIZE(ops); i++) { + err = genl_register_ops(&family, &ops[i]); + if (err) + goto err_out; + } + return 0; + +err_out: + genl_unregister_family(&family); + return err; +} + +void bluetooth_netlink_cleanup(void) +{ + genl_unregister_family(&family); +} diff --git a/net/bluetooth/netlink.h b/net/bluetooth/netlink.h new file mode 100644 index 0000000..cffc4cf --- /dev/null +++ b/net/bluetooth/netlink.h @@ -0,0 +1,40 @@ +/* + * This is the netlink-based bluetooth interface. + * + * Copyright 2008 Alok Barsode + */ +#ifndef __NETLINK_H +#define __NETLINK_H + +#define VERSION 0x01 +#define lock(d) down_trylock(&d->req_lock) +#define unlock(d) up(&d->req_lock) + +#define DOWN 0 +#define UP 1 + +enum { + ATTR_UNSPEC, + INDEX, + CHANGED, + EVENT, + GRPID, + /* Add attributes here */ + __ATTR_MAX, + ATTR_MAX = __ATTR_MAX - 1 +}; + +enum { + CMD_UNSPEC, + NEWHOST, + DELHOST, + HOST, + GETGRPID, + /* Add command here */ + __CMD_MAX, + CMD_MAX = __CMD_MAX - 1 +}; + +int bluetooth_notify(struct hci_dev *hdev, int event); + +#endif /* __NETLINK_H */ -- 1.5.6.3