From 1e5cb0bbe2c0bbffe4e4d53e863bb878395a0367 Mon Sep 17 00:00:00 2001 From: Alok Barsode Date: Wed, 18 Feb 2009 19:54:20 +0530 Subject: [PATCH] Adding netlink support to bluetooth. Adding files netlink.c and netlink.h to net/bluetooth for netlink support. Adding support for NEWHOST. Redefining hci_req_lock to use down_trylock Signed-off-by: Alok Barsode --- include/net/bluetooth/hci_core.h | 4 +- net/bluetooth/Makefile | 2 +- net/bluetooth/hci_core.c | 31 ++++++ net/bluetooth/netlink.c | 212 ++++++++++++++++++++++++++++++++++++++ net/bluetooth/netlink.h | 31 ++++++ 5 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 net/bluetooth/netlink.c create mode 100644 net/bluetooth/netlink.h diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 01f9316..094c5dd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -695,9 +695,11 @@ struct hci_sec_filter { #define HCI_REQ_PEND 1 #define HCI_REQ_CANCELED 2 -#define hci_req_lock(d) down(&d->req_lock) +#define hci_req_lock(d) down_trylock(&d->req_lock) #define hci_req_unlock(d) up(&d->req_lock) 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/hci_core.c b/net/bluetooth/hci_core.c index ba78cc1..5b8e890 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -291,6 +291,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) diff --git a/net/bluetooth/netlink.c b/net/bluetooth/netlink.c new file mode 100644 index 0000000..97be0b6 --- /dev/null +++ b/net/bluetooth/netlink.c @@ -0,0 +1,212 @@ +/* + * This is the netlink-based bluetooth interface. + * + * Copyright 2008 Alok Barsode + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "netlink.h" + +#define BLUETOOTH_GENL_FAMILY_NAME "bluetooth" + +/* family definition */ +static struct genl_family family = { + .id = GENL_ID_GENERATE, + .hdrsize = 0, + .name = BLUETOOTH_GENL_FAMILY_NAME, + .version = VERSION, + .maxattr = ATTR_MAX +}; + +static struct nla_policy policy[ATTR_MAX + 1] = { + [INDEX] = { .type = NLA_U16 }, + [TYPE] = { .type = NLA_U16 }, + [FLAGS] = { .type = NLA_U32 }, + [CHANGED] = { .type = NLA_U32 }, +}; + +static int bluetooth_newhost(struct sk_buff *skb, struct genl_info *info) +{ + __u16 index; + void *hdr; + int ret=0; + unsigned long flags = 0, changed = 0; + struct hci_dev *hdev; + struct sk_buff *msg; + + if (!info->attrs[INDEX]) + return -EINVAL; + + index = nla_get_u16(info->attrs[INDEX]); + + if (!(hdev = hci_dev_get(index))) + return -ENODEV; + + printk("%s %p", hdev->name, hdev); + + if (hci_req_lock(hdev)) + return -EBUSY; + + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) { + ret = -ENOBUFS; + goto done; + } + + hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, &family, 0, NEWHOST); + if (hdr == NULL){ + nlmsg_free(msg); + ret = -ENOBUFS; + goto done; + } + + NLA_PUT_U16(msg, INDEX, index); + + if (!info->attrs[FLAGS]) + goto proceed; + + flags = nla_get_u16(info->attrs[FLAGS]); + + if (!test_bit(HCI_UP, &flags) && test_bit(HCI_UP, &hdev->flags)) + goto proceed; + + if (!test_bit(HCI_UP, &flags) && !test_bit(HCI_UP, &hdev->flags)){ + ret = -EHOSTDOWN; + goto done; + } + + if (test_bit(HCI_UP, &hdev->flags)) { + ret = -EALREADY; + 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; + } + + 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); + } + + if (!ret) { + hci_dev_hold(hdev); + set_bit(HCI_UP, &hdev->flags); + set_bit(HCI_UP, &changed); + goto proceed; + } + + /* Init failed, cleanup */ + tasklet_kill(&hdev->rx_task); + tasklet_kill(&hdev->tx_task); + tasklet_kill(&hdev->cmd_task); + + skb_queue_purge(&hdev->cmd_q); + skb_queue_purge(&hdev->rx_q); + + if (hdev->flush) + hdev->flush(hdev); + + if (hdev->sent_cmd) { + kfree_skb(hdev->sent_cmd); + hdev->sent_cmd = NULL; + } + + hdev->close(hdev); + hdev->flags = 0; + goto done; + + proceed: + if (test_bit(HCI_PSCAN, &flags)) { + ret = hci_handle_request(hdev, HCISETSCAN, SCAN_PAGE); + if (!ret) + set_bit(HCI_PSCAN, &changed); + } + + if (test_bit(HCI_ISCAN, &flags)) { + ret = hci_handle_request(hdev, HCISETSCAN, SCAN_INQUIRY); + if (!ret) + set_bit(HCI_ISCAN, &changed); + } + + NLA_PUT_U32(msg, CHANGED, changed); + + genlmsg_end(msg, hdr); + goto done; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + ret = -EMSGSIZE; + done: + hci_req_unlock(hdev); + hci_dev_put(hdev); + + if (ret < 0) + return ret; + + return genlmsg_unicast(msg, info->snd_pid); +} + +static int bluetooth_delhost(struct sk_buff *skb, struct genl_info *info) +{ + return 0; +} + +static struct genl_ops ops[] = { + { .cmd = NEWHOST, + .policy = policy, + .doit = bluetooth_newhost, + .flags = GENL_ADMIN_PERM, + .dumpit = NULL, + }, + { .cmd = DELHOST, + .policy = policy, + .doit = bluetooth_delhost, + .flags = GENL_ADMIN_PERM, + .dumpit = NULL, + }, +}; + +/* initialisation/exit functions */ +int __init bluetooth_netlink_init(void) +{ + int err, i; + + err = genl_register_family(&family); + if (err) + return err; + + 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..ea99c79 --- /dev/null +++ b/net/bluetooth/netlink.h @@ -0,0 +1,31 @@ +/* + * This is the netlink-based bluetooth interface. + * + * Copyright 2008 Alok Barsode + */ +#ifndef __NETLINK_H +#define __NETLINK_H + +#define VERSION 0x01 + +enum bluetooth_attr { + ATTR_UNSPEC, + INDEX, + TYPE, + FLAGS, + CHANGED, + /* Add attributes here */ + __ATTR_MAX, + ATTR_MAX = __ATTR_MAX - 1 +}; + +enum bluetooth_cmds { + CMD_UNSPEC, + NEWHOST, + DELHOST, + /* Add command here */ + __CMD_MAX, + CMD_MAX = __CMD_MAX - 1 +}; + +#endif /* __NETLINK_H */ -- 1.5.6.3