Marcel, As per our last discussion, i am attaching a patch for the generic netlink interface. I am also attaching a test program (can be compiled with -lnl ) to test the interface. I am using "flags" to bring up the device and returning "changed", which indicate the changed bits in the flags. right now the module only supports 'up', 'iscan' and 'pscan'. so i can issue a NEWHOST command with HCI_UP | HCI_PSCAN | HCI_ISCAN. I am not sure if this is the right approach. OR Do you want individual commands for operations ? Let me know what you think of this. Cheers, Alok.
From 1e5cb0bbe2c0bbffe4e4d53e863bb878395a0367 Mon Sep 17 00:00:00 2001 From: Alok Barsode <alokbarsode@xxxxxxxxx> 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 <alokbarsode@xxxxxxxxx> --- 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 <alok.barsode@xxxxxxxxxx> + */ + +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/unaligned.h> + +#include <net/genetlink.h> +#include <linux/module.h> +#include <linux/kernel.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#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 <alok.barsode@xxxxxxxxxx> + */ +#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
#include <netlink/netlink.h> #include <netlink/genl/genl.h> #include <netlink/genl/ctrl.h> #include <netlink/msg.h> #include <netlink/attr.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <bluetooth/bluetooth.h> #include <bluetooth/hci.h> #include <bluetooth/hci_lib.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 }; static inline void set_bit(int nr, void *addr) { *((uint32_t *) addr + (nr >> 5)) |= (1 << (nr & 31)); } static inline int test_bit(int nr, void *addr) { return *((uint32_t *) addr + (nr >> 5)) & (1 << (nr & 31)); } static struct nla_policy bluetooth_policy[ATTR_MAX + 1] = { [INDEX] = { .type = NLA_U16 }, [TYPE] = { .type = NLA_U16 }, [FLAGS] = { .type = NLA_U32 }, [CHANGED] = { .type = NLA_U32 }, }; static int parse_cb(struct nl_msg *msg, void *arg) { int err,i=0; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlattr *attrs[ATTR_MAX+1]; struct genlmsghdr *ghdr = nlmsg_data(nlh); err = genlmsg_parse(nlh, 0, attrs, ATTR_MAX, NULL); if (err < 0) return -EINVAL; printf("\nhello\n"); switch(ghdr->cmd) { case NEWHOST: if(!attrs[INDEX]) { printf("\nNo index\n"); return -EINVAL; } int index = nla_get_u16(attrs[INDEX]); uint32_t changed = nla_get_u16(attrs[CHANGED]); if (test_bit(HCI_UP, &changed)) printf("Hci%d UP changed %d", index, changed); break; default: printf("\nUnknown command"); break; } return 0; } int main() { struct nl_handle *sock; struct nl_msg *msg; int family; int err; uint32_t flags = 0; sock = nl_handle_alloc(); genl_connect(sock); family = genl_ctrl_resolve(sock, "bluetooth"); msg = nlmsg_alloc(); genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST, NEWHOST, VERSION); nla_put_u16(msg, INDEX, 0); set_bit(HCI_UP, &flags); set_bit(HCI_PSCAN, &flags); nla_put_u32(msg, FLAGS, flags); nl_send_auto_complete(sock, msg); nlmsg_free(msg); nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, parse_cb, NULL); err = nl_recvmsgs_default(sock); printf("\nerr = %d\n", err); return 0; }