Re: [PATCH]Generic Netlink Interface

alok barsode
2009-03-03T15:40:21Z

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

Hi Marcel,

I am attaching a modified patch for the netlink interface. The changes are:

1. Adding a HOST command. It take INDEX and EVENT as parameter. EVENT
can be UP or DOWN.
this is similar to hciconfig hci0 up/down.

2. Registering a bluetooth-events multicast group. Userspace can
subscribe to this group and receive the following events
a. HOST events (device is up or down through 'changed' flag)
b. NEWHOST (a new device is registered)
c. DELHOST (an existing device is unregistered)
All above events include the INDEX.

I am attaching a simple program (listen.c) which can be used to test
and above events.
It subscribes to bluetooth-events grp and listens to the events.

Do let me know what you think of it.

Cheers,
Alok.

On Mon, Feb 23, 2009 at 7:10 PM, alok barsode <alokbarsode@gmail.com> wrote:
> Hi Marcel,
>
> On Sat, Feb 21, 2009 at 1:37 AM, Marcel Holtmann <marcel@holtmann.org> wrote:
>> Hi Alok,
>>
>>> 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 ?
>>
>> the first thing that we have to change the try_lock() change. We can't
>> do that. It has way to many implications on the code. So why do you
>> really need the try_lock() in this case. And if, then don't change
>> current locking code. Just create a new define for the the try_lock()
>> case.
>
> I was using try_lock to safeguard the code from being re-entrant.
> will create a new define in netlink.c.
>
>>
>> I am thinking about not exposing the ->flags directly and just creating
>> a new one for the netlink interface. For example for PSCAN and ISCAN I
>> like to have clear primitives that say connectable, discoverable etc.
>>
>> We did a lot of changes in the D-Bus API for 4.x during the last month
>> and the best way would be if the netlink API reflects these changes in a
>> more closer way. So it might be better to just have primitives that map
>> 1:1 the properties powered, connectable, discoverable etc.
>
> sounds good.
>
>>
>> We could actually just have PROPERTY primitive and then turn the
>> properties into parameters. Netlink should be fine with listing multiple
>> parameters in the same message.
>
> PROPERTY can be a nested primitive which holds the parameter <name:
> value> pair.
> so NEWHOST takes a list of parameter name:value pairs.
>
> did I understand you correctly?
>
> Cheers,
> Alok.
>

[-- Attachment #2: 0001-Initial-Netlink-support.patch --]
[-- Type: text/x-diff, Size: 11608 bytes --]

From 7e2aa37a35723ab72e53e78d58c348e3caf1ae78 Mon Sep 17 00:00:00 2001
From: Alok Barsode <alokbarsode@gmail.com>
Date: Tue, 3 Mar 2009 20:57:01 +0530
Subject: [PATCH] Initial Netlink support.

Signed-off-by: Alok Barsode <alokbarsode@gmail.com>
---
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 <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>

+#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 <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_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 <alok.barsode@azingo.com>
+ */
+#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

[-- Attachment #3: listen.c --]
[-- Type: text/x-csrc, Size: 2985 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>

#define GENL_FAMILY_NAME "bluetooth"
#define GENL_VERSION 0x01

static int family;

#define DOWN 0
#define UP 1

enum {
ATTR_UNSPEC,
INDEX,
CHANGED,
EVENT,
GRPID,
/* Add attributes here */
__ATTR_MAX,
ATTR_MAX = __ATTR_MAX - 1
};

static struct nla_policy policy[ATTR_MAX + 1] = {
[INDEX] = { .type = NLA_U16 },
[CHANGED] = { .type = NLA_U32 },
[EVENT] = { .type = NLA_U16 },
[GRPID] = { .type = NLA_U32 },
};

enum {
CMD_UNSPEC,
NEWHOST,
DELHOST,
HOST,
GETGRPID,
/* Add command here */
__CMD_MAX,
CMD_MAX = __CMD_MAX - 1
};

static int parse_cb(struct nl_msg *msg, void *arg)
{
int err, ret;
int *id = arg;
int index, changed;
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;

switch (ghdr->cmd) {
case HOST:
index = nla_get_u16(attrs[INDEX]);
changed = nla_get_u32(attrs[CHANGED]);
printf("\nHCI%d : changed0x%x\n", index, changed);
ret = 0;
break;
case NEWHOST:
index = nla_get_u16(attrs[INDEX]);
changed = nla_get_u32(attrs[CHANGED]);
printf("\nNew Device HCI%d : changed0x%x\n", index, changed);
ret = 0;
break;
case DELHOST:
index = nla_get_u16(attrs[INDEX]);
changed = nla_get_u32(attrs[CHANGED]);
printf("\nRemoved Device HCI%d : changed0x%x\n", index, changed);
ret = 0;
break;
case GETGRPID:
if(!attrs[GRPID]) {
printf("\nNo Group ID\n");
ret = -EINVAL;
} else
*id = nla_get_u32(attrs[GRPID]);
ret = 0;
break;

default:
printf("\nUnknown command");
ret = -EINVAL;
break;
}

return ret;
}

static int ack_handler(struct nl_msg *msg, void *arg)
{
int *ret = arg;
*ret = 0;
return NL_STOP;
}

int get_group_id(struct nl_handle *sock)
{
int ret = 1, grpid;
struct nl_msg *msg;
struct nl_cb *cb;

msg = nlmsg_alloc();
cb = nl_cb_alloc(NL_CB_DEFAULT);

genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0,
NLM_F_REQUEST, GETGRPID, GENL_VERSION);

nl_send_auto_complete(sock, msg);

nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &ret);
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, parse_cb, &grpid);

while (ret > 0)
nl_recvmsgs(sock, cb);

nl_cb_put(cb);
nlmsg_free(msg);

return grpid;
}

int main()
{
struct nl_handle *sock;
struct nl_msg *msg;
int err;
int grp_id;

sock = nl_handle_alloc();

genl_connect(sock);

family = genl_ctrl_resolve(sock, GENL_FAMILY_NAME);
if (family < 0)
return -1;

grp_id = get_group_id(sock);
if (grp_id < 0)
return -1;

err = nl_socket_add_membership(sock, grp_id);
if (err < 0)
return -1;

nl_socket_modify_cb(sock, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, parse_cb, NULL);
while(1)
err = nl_recvmsgs_default(sock);

return 0;
}