[PATCH]Generic Netlink Interface

alok barsode
2009-02-20T14:02:04Z

[-- Attachment #1: Type: text/plain, Size: 577 bytes --]

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.

[-- Attachment #2: 0001-Adding-netlink-support-to-bluetooth.patch --]
[-- Type: text/x-diff, Size: 8028 bytes --]

From 1e5cb0bbe2c0bbffe4e4d53e863bb878395a0367 Mon Sep 17 00:00:00 2001
From: Alok Barsode <alokbarsode@gmail.com>
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@gmail.com>
---
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@azingo.com>
+ */
+
+#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@azingo.com>
+ */
+#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

[-- Attachment #3: test-up.c --]
[-- Type: text/x-csrc, Size: 2319 bytes --]

#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;
}