samples: connector: from Documentation to samples directory
authorArnd Bergmann <arnd@arndb.de>
Mon, 25 Apr 2016 16:03:08 +0000 (18:03 +0200)
committerJonathan Corbet <corbet@lwn.net>
Thu, 28 Apr 2016 13:47:35 +0000 (07:47 -0600)
A small bug with the new autoksyms support showed that there are
two kernel modules in the Documentation directory that qualify
as samples, while all other samples are in the samples/ directory.

This patch was originally meant as a workaround for that bug, but
it has now been solved in a different way. However, I still think
it makes sense as a cleanup to consolidate all sample code in
one place.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
12 files changed:
Documentation/Makefile
Documentation/connector/.gitignore [deleted file]
Documentation/connector/Makefile [deleted file]
Documentation/connector/cn_test.c [deleted file]
Documentation/connector/connector.txt
Documentation/connector/ucon.c [deleted file]
samples/Kconfig
samples/Makefile
samples/connector/.gitignore [new file with mode: 0644]
samples/connector/Makefile [new file with mode: 0644]
samples/connector/cn_test.c [new file with mode: 0644]
samples/connector/ucon.c [new file with mode: 0644]

index 1207d7907650028927809c57c274d3cb962337c4..13b5ae1b87aa50d068cf74e0815e7e364a37ec25 100644 (file)
@@ -1,4 +1,4 @@
-subdir-y := accounting auxdisplay blackfin connector \
+subdir-y := accounting auxdisplay blackfin \
        filesystems filesystems ia64 laptops mic misc-devices \
        networking pcmcia prctl ptp timers vDSO video4linux \
        watchdog
diff --git a/Documentation/connector/.gitignore b/Documentation/connector/.gitignore
deleted file mode 100644 (file)
index d2b9c32..0000000
+++ /dev/null
@@ -1 +0,0 @@
-ucon
diff --git a/Documentation/connector/Makefile b/Documentation/connector/Makefile
deleted file mode 100644 (file)
index d98e4df..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-ifneq ($(CONFIG_CONNECTOR),)
-obj-m += cn_test.o
-endif
-
-# List of programs to build
-hostprogs-y := ucon
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_ucon.o += -I$(objtree)/usr/include
-
-all: modules
-
-modules clean:
-       $(MAKE) -C ../.. SUBDIRS=$(PWD) $@
diff --git a/Documentation/connector/cn_test.c b/Documentation/connector/cn_test.c
deleted file mode 100644 (file)
index d12cc94..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- *     cn_test.c
- * 
- * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
- * All rights reserved.
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#define pr_fmt(fmt) "cn_test: " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-#include <linux/timer.h>
-
-#include <linux/connector.h>
-
-static struct cb_id cn_test_id = { CN_NETLINK_USERS + 3, 0x456 };
-static char cn_test_name[] = "cn_test";
-static struct sock *nls;
-static struct timer_list cn_test_timer;
-
-static void cn_test_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
-{
-       pr_info("%s: %lu: idx=%x, val=%x, seq=%u, ack=%u, len=%d: %s.\n",
-               __func__, jiffies, msg->id.idx, msg->id.val,
-               msg->seq, msg->ack, msg->len,
-               msg->len ? (char *)msg->data : "");
-}
-
-/*
- * Do not remove this function even if no one is using it as
- * this is an example of how to get notifications about new
- * connector user registration
- */
-#if 0
-static int cn_test_want_notify(void)
-{
-       struct cn_ctl_msg *ctl;
-       struct cn_notify_req *req;
-       struct cn_msg *msg = NULL;
-       int size, size0;
-       struct sk_buff *skb;
-       struct nlmsghdr *nlh;
-       u32 group = 1;
-
-       size0 = sizeof(*msg) + sizeof(*ctl) + 3 * sizeof(*req);
-
-       size = NLMSG_SPACE(size0);
-
-       skb = alloc_skb(size, GFP_ATOMIC);
-       if (!skb) {
-               pr_err("failed to allocate new skb with size=%u\n", size);
-               return -ENOMEM;
-       }
-
-       nlh = nlmsg_put(skb, 0, 0x123, NLMSG_DONE, size - sizeof(*nlh), 0);
-       if (!nlh) {
-               kfree_skb(skb);
-               return -EMSGSIZE;
-       }
-
-       msg = nlmsg_data(nlh);
-
-       memset(msg, 0, size0);
-
-       msg->id.idx = -1;
-       msg->id.val = -1;
-       msg->seq = 0x123;
-       msg->ack = 0x345;
-       msg->len = size0 - sizeof(*msg);
-
-       ctl = (struct cn_ctl_msg *)(msg + 1);
-
-       ctl->idx_notify_num = 1;
-       ctl->val_notify_num = 2;
-       ctl->group = group;
-       ctl->len = msg->len - sizeof(*ctl);
-
-       req = (struct cn_notify_req *)(ctl + 1);
-
-       /*
-        * Idx.
-        */
-       req->first = cn_test_id.idx;
-       req->range = 10;
-
-       /*
-        * Val 0.
-        */
-       req++;
-       req->first = cn_test_id.val;
-       req->range = 10;
-
-       /*
-        * Val 1.
-        */
-       req++;
-       req->first = cn_test_id.val + 20;
-       req->range = 10;
-
-       NETLINK_CB(skb).dst_group = ctl->group;
-       //netlink_broadcast(nls, skb, 0, ctl->group, GFP_ATOMIC);
-       netlink_unicast(nls, skb, 0, 0);
-
-       pr_info("request was sent: group=0x%x\n", ctl->group);
-
-       return 0;
-}
-#endif
-
-static u32 cn_test_timer_counter;
-static void cn_test_timer_func(unsigned long __data)
-{
-       struct cn_msg *m;
-       char data[32];
-
-       pr_debug("%s: timer fired with data %lu\n", __func__, __data);
-
-       m = kzalloc(sizeof(*m) + sizeof(data), GFP_ATOMIC);
-       if (m) {
-
-               memcpy(&m->id, &cn_test_id, sizeof(m->id));
-               m->seq = cn_test_timer_counter;
-               m->len = sizeof(data);
-
-               m->len =
-                   scnprintf(data, sizeof(data), "counter = %u",
-                             cn_test_timer_counter) + 1;
-
-               memcpy(m + 1, data, m->len);
-
-               cn_netlink_send(m, 0, 0, GFP_ATOMIC);
-               kfree(m);
-       }
-
-       cn_test_timer_counter++;
-
-       mod_timer(&cn_test_timer, jiffies + msecs_to_jiffies(1000));
-}
-
-static int cn_test_init(void)
-{
-       int err;
-
-       err = cn_add_callback(&cn_test_id, cn_test_name, cn_test_callback);
-       if (err)
-               goto err_out;
-       cn_test_id.val++;
-       err = cn_add_callback(&cn_test_id, cn_test_name, cn_test_callback);
-       if (err) {
-               cn_del_callback(&cn_test_id);
-               goto err_out;
-       }
-
-       setup_timer(&cn_test_timer, cn_test_timer_func, 0);
-       mod_timer(&cn_test_timer, jiffies + msecs_to_jiffies(1000));
-
-       pr_info("initialized with id={%u.%u}\n",
-               cn_test_id.idx, cn_test_id.val);
-
-       return 0;
-
-      err_out:
-       if (nls && nls->sk_socket)
-               sock_release(nls->sk_socket);
-
-       return err;
-}
-
-static void cn_test_fini(void)
-{
-       del_timer_sync(&cn_test_timer);
-       cn_del_callback(&cn_test_id);
-       cn_test_id.val--;
-       cn_del_callback(&cn_test_id);
-       if (nls && nls->sk_socket)
-               sock_release(nls->sk_socket);
-}
-
-module_init(cn_test_init);
-module_exit(cn_test_fini);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
-MODULE_DESCRIPTION("Connector's test module");
index f6215f95149b4dafc545ec6b0c1e9421ef34403d..ab7ca897fab7ee5ada0ac87992065d550e969508 100644 (file)
@@ -186,3 +186,11 @@ only cn_test.c test module used it.
 Some work in netlink area is still being done, so things can be changed in
 2.6.15 timeframe, if it will happen, documentation will be updated for that
 kernel.
+
+/*****************************************/
+Code samples
+/*****************************************/
+
+Sample code for a connector test module and user space can be found
+in samples/connector/. To build this code, enable CONFIG_CONNECTOR
+and CONFIG_SAMPLES.
diff --git a/Documentation/connector/ucon.c b/Documentation/connector/ucon.c
deleted file mode 100644 (file)
index 8a4da64..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- *     ucon.c
- *
- * Copyright (c) 2004+ Evgeniy Polyakov <zbr@ioremap.net>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <asm/types.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/poll.h>
-
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-
-#include <arpa/inet.h>
-
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <time.h>
-#include <getopt.h>
-
-#include <linux/connector.h>
-
-#define DEBUG
-#define NETLINK_CONNECTOR      11
-
-/* Hopefully your userspace connector.h matches this kernel */
-#define CN_TEST_IDX            CN_NETLINK_USERS + 3
-#define CN_TEST_VAL            0x456
-
-#ifdef DEBUG
-#define ulog(f, a...) fprintf(stdout, f, ##a)
-#else
-#define ulog(f, a...) do {} while (0)
-#endif
-
-static int need_exit;
-static __u32 seq;
-
-static int netlink_send(int s, struct cn_msg *msg)
-{
-       struct nlmsghdr *nlh;
-       unsigned int size;
-       int err;
-       char buf[128];
-       struct cn_msg *m;
-
-       size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
-
-       nlh = (struct nlmsghdr *)buf;
-       nlh->nlmsg_seq = seq++;
-       nlh->nlmsg_pid = getpid();
-       nlh->nlmsg_type = NLMSG_DONE;
-       nlh->nlmsg_len = size;
-       nlh->nlmsg_flags = 0;
-
-       m = NLMSG_DATA(nlh);
-#if 0
-       ulog("%s: [%08x.%08x] len=%u, seq=%u, ack=%u.\n",
-              __func__, msg->id.idx, msg->id.val, msg->len, msg->seq, msg->ack);
-#endif
-       memcpy(m, msg, sizeof(*m) + msg->len);
-
-       err = send(s, nlh, size, 0);
-       if (err == -1)
-               ulog("Failed to send: %s [%d].\n",
-                       strerror(errno), errno);
-
-       return err;
-}
-
-static void usage(void)
-{
-       printf(
-               "Usage: ucon [options] [output file]\n"
-               "\n"
-               "\t-h\tthis help screen\n"
-               "\t-s\tsend buffers to the test module\n"
-               "\n"
-               "The default behavior of ucon is to subscribe to the test module\n"
-               "and wait for state messages.  Any ones received are dumped to the\n"
-               "specified output file (or stdout).  The test module is assumed to\n"
-               "have an id of {%u.%u}\n"
-               "\n"
-               "If you get no output, then verify the cn_test module id matches\n"
-               "the expected id above.\n"
-               , CN_TEST_IDX, CN_TEST_VAL
-       );
-}
-
-int main(int argc, char *argv[])
-{
-       int s;
-       char buf[1024];
-       int len;
-       struct nlmsghdr *reply;
-       struct sockaddr_nl l_local;
-       struct cn_msg *data;
-       FILE *out;
-       time_t tm;
-       struct pollfd pfd;
-       bool send_msgs = false;
-
-       while ((s = getopt(argc, argv, "hs")) != -1) {
-               switch (s) {
-               case 's':
-                       send_msgs = true;
-                       break;
-
-               case 'h':
-                       usage();
-                       return 0;
-
-               default:
-                       /* getopt() outputs an error for us */
-                       usage();
-                       return 1;
-               }
-       }
-
-       if (argc != optind) {
-               out = fopen(argv[optind], "a+");
-               if (!out) {
-                       ulog("Unable to open %s for writing: %s\n",
-                               argv[1], strerror(errno));
-                       out = stdout;
-               }
-       } else
-               out = stdout;
-
-       memset(buf, 0, sizeof(buf));
-
-       s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
-       if (s == -1) {
-               perror("socket");
-               return -1;
-       }
-
-       l_local.nl_family = AF_NETLINK;
-       l_local.nl_groups = -1; /* bitmask of requested groups */
-       l_local.nl_pid = 0;
-
-       ulog("subscribing to %u.%u\n", CN_TEST_IDX, CN_TEST_VAL);
-
-       if (bind(s, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)) == -1) {
-               perror("bind");
-               close(s);
-               return -1;
-       }
-
-#if 0
-       {
-               int on = 0x57; /* Additional group number */
-               setsockopt(s, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &on, sizeof(on));
-       }
-#endif
-       if (send_msgs) {
-               int i, j;
-
-               memset(buf, 0, sizeof(buf));
-
-               data = (struct cn_msg *)buf;
-
-               data->id.idx = CN_TEST_IDX;
-               data->id.val = CN_TEST_VAL;
-               data->seq = seq++;
-               data->ack = 0;
-               data->len = 0;
-
-               for (j=0; j<10; ++j) {
-                       for (i=0; i<1000; ++i) {
-                               len = netlink_send(s, data);
-                       }
-
-                       ulog("%d messages have been sent to %08x.%08x.\n", i, data->id.idx, data->id.val);
-               }
-
-               return 0;
-       }
-
-
-       pfd.fd = s;
-
-       while (!need_exit) {
-               pfd.events = POLLIN;
-               pfd.revents = 0;
-               switch (poll(&pfd, 1, -1)) {
-                       case 0:
-                               need_exit = 1;
-                               break;
-                       case -1:
-                               if (errno != EINTR) {
-                                       need_exit = 1;
-                                       break;
-                               }
-                               continue;
-               }
-               if (need_exit)
-                       break;
-
-               memset(buf, 0, sizeof(buf));
-               len = recv(s, buf, sizeof(buf), 0);
-               if (len == -1) {
-                       perror("recv buf");
-                       close(s);
-                       return -1;
-               }
-               reply = (struct nlmsghdr *)buf;
-
-               switch (reply->nlmsg_type) {
-               case NLMSG_ERROR:
-                       fprintf(out, "Error message received.\n");
-                       fflush(out);
-                       break;
-               case NLMSG_DONE:
-                       data = (struct cn_msg *)NLMSG_DATA(reply);
-
-                       time(&tm);
-                       fprintf(out, "%.24s : [%x.%x] [%08u.%08u].\n",
-                               ctime(&tm), data->id.idx, data->id.val, data->seq, data->ack);
-                       fflush(out);
-                       break;
-               default:
-                       break;
-               }
-       }
-
-       close(s);
-       return 0;
-}
index d54f28c6dc5e5450595f1d5472f14cad3c42cee9..559a58baff6ea5be2915ea6ed551a10c7adf3877 100644 (file)
@@ -76,4 +76,13 @@ config SAMPLE_CONFIGFS
        help
          Builds a sample configfs interface.
 
+config SAMPLE_CONNECTOR
+       tristate "Build connector sample -- loadable modules only"
+       depends on CONNECTOR && m
+       help
+         When enabled, this builds both a sample kernel module for
+         the connector interface and a user space tool to communicate
+         with it.
+         See also Documentation/connector/connector.txt
+
 endif # SAMPLES
index 48001d7e23f042a7f777ec810ab2ff29e7db5f52..594ef7d9fa2ade558b4cf4451701b3f0ba30006d 100644 (file)
@@ -2,4 +2,4 @@
 
 obj-$(CONFIG_SAMPLES)  += kobject/ kprobes/ trace_events/ livepatch/ \
                           hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \
-                          configfs/
+                          configfs/ connector/
diff --git a/samples/connector/.gitignore b/samples/connector/.gitignore
new file mode 100644 (file)
index 0000000..d2b9c32
--- /dev/null
@@ -0,0 +1 @@
+ucon
diff --git a/samples/connector/Makefile b/samples/connector/Makefile
new file mode 100644 (file)
index 0000000..04b9622
--- /dev/null
@@ -0,0 +1,16 @@
+obj-$(CONFIG_SAMPLE_CONNECTOR) += cn_test.o
+
+# List of programs to build
+ifdef CONFIG_SAMPLE_CONNECTOR
+hostprogs-y := ucon
+endif
+
+# Tell kbuild to always build the programs
+always := $(hostprogs-y)
+
+HOSTCFLAGS_ucon.o += -I$(objtree)/usr/include
+
+all: modules
+
+modules clean:
+       $(MAKE) -C ../.. SUBDIRS=$(PWD) $@
diff --git a/samples/connector/cn_test.c b/samples/connector/cn_test.c
new file mode 100644 (file)
index 0000000..d12cc94
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ *     cn_test.c
+ * 
+ * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
+ * All rights reserved.
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define pr_fmt(fmt) "cn_test: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+
+#include <linux/connector.h>
+
+static struct cb_id cn_test_id = { CN_NETLINK_USERS + 3, 0x456 };
+static char cn_test_name[] = "cn_test";
+static struct sock *nls;
+static struct timer_list cn_test_timer;
+
+static void cn_test_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
+{
+       pr_info("%s: %lu: idx=%x, val=%x, seq=%u, ack=%u, len=%d: %s.\n",
+               __func__, jiffies, msg->id.idx, msg->id.val,
+               msg->seq, msg->ack, msg->len,
+               msg->len ? (char *)msg->data : "");
+}
+
+/*
+ * Do not remove this function even if no one is using it as
+ * this is an example of how to get notifications about new
+ * connector user registration
+ */
+#if 0
+static int cn_test_want_notify(void)
+{
+       struct cn_ctl_msg *ctl;
+       struct cn_notify_req *req;
+       struct cn_msg *msg = NULL;
+       int size, size0;
+       struct sk_buff *skb;
+       struct nlmsghdr *nlh;
+       u32 group = 1;
+
+       size0 = sizeof(*msg) + sizeof(*ctl) + 3 * sizeof(*req);
+
+       size = NLMSG_SPACE(size0);
+
+       skb = alloc_skb(size, GFP_ATOMIC);
+       if (!skb) {
+               pr_err("failed to allocate new skb with size=%u\n", size);
+               return -ENOMEM;
+       }
+
+       nlh = nlmsg_put(skb, 0, 0x123, NLMSG_DONE, size - sizeof(*nlh), 0);
+       if (!nlh) {
+               kfree_skb(skb);
+               return -EMSGSIZE;
+       }
+
+       msg = nlmsg_data(nlh);
+
+       memset(msg, 0, size0);
+
+       msg->id.idx = -1;
+       msg->id.val = -1;
+       msg->seq = 0x123;
+       msg->ack = 0x345;
+       msg->len = size0 - sizeof(*msg);
+
+       ctl = (struct cn_ctl_msg *)(msg + 1);
+
+       ctl->idx_notify_num = 1;
+       ctl->val_notify_num = 2;
+       ctl->group = group;
+       ctl->len = msg->len - sizeof(*ctl);
+
+       req = (struct cn_notify_req *)(ctl + 1);
+
+       /*
+        * Idx.
+        */
+       req->first = cn_test_id.idx;
+       req->range = 10;
+
+       /*
+        * Val 0.
+        */
+       req++;
+       req->first = cn_test_id.val;
+       req->range = 10;
+
+       /*
+        * Val 1.
+        */
+       req++;
+       req->first = cn_test_id.val + 20;
+       req->range = 10;
+
+       NETLINK_CB(skb).dst_group = ctl->group;
+       //netlink_broadcast(nls, skb, 0, ctl->group, GFP_ATOMIC);
+       netlink_unicast(nls, skb, 0, 0);
+
+       pr_info("request was sent: group=0x%x\n", ctl->group);
+
+       return 0;
+}
+#endif
+
+static u32 cn_test_timer_counter;
+static void cn_test_timer_func(unsigned long __data)
+{
+       struct cn_msg *m;
+       char data[32];
+
+       pr_debug("%s: timer fired with data %lu\n", __func__, __data);
+
+       m = kzalloc(sizeof(*m) + sizeof(data), GFP_ATOMIC);
+       if (m) {
+
+               memcpy(&m->id, &cn_test_id, sizeof(m->id));
+               m->seq = cn_test_timer_counter;
+               m->len = sizeof(data);
+
+               m->len =
+                   scnprintf(data, sizeof(data), "counter = %u",
+                             cn_test_timer_counter) + 1;
+
+               memcpy(m + 1, data, m->len);
+
+               cn_netlink_send(m, 0, 0, GFP_ATOMIC);
+               kfree(m);
+       }
+
+       cn_test_timer_counter++;
+
+       mod_timer(&cn_test_timer, jiffies + msecs_to_jiffies(1000));
+}
+
+static int cn_test_init(void)
+{
+       int err;
+
+       err = cn_add_callback(&cn_test_id, cn_test_name, cn_test_callback);
+       if (err)
+               goto err_out;
+       cn_test_id.val++;
+       err = cn_add_callback(&cn_test_id, cn_test_name, cn_test_callback);
+       if (err) {
+               cn_del_callback(&cn_test_id);
+               goto err_out;
+       }
+
+       setup_timer(&cn_test_timer, cn_test_timer_func, 0);
+       mod_timer(&cn_test_timer, jiffies + msecs_to_jiffies(1000));
+
+       pr_info("initialized with id={%u.%u}\n",
+               cn_test_id.idx, cn_test_id.val);
+
+       return 0;
+
+      err_out:
+       if (nls && nls->sk_socket)
+               sock_release(nls->sk_socket);
+
+       return err;
+}
+
+static void cn_test_fini(void)
+{
+       del_timer_sync(&cn_test_timer);
+       cn_del_callback(&cn_test_id);
+       cn_test_id.val--;
+       cn_del_callback(&cn_test_id);
+       if (nls && nls->sk_socket)
+               sock_release(nls->sk_socket);
+}
+
+module_init(cn_test_init);
+module_exit(cn_test_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
+MODULE_DESCRIPTION("Connector's test module");
diff --git a/samples/connector/ucon.c b/samples/connector/ucon.c
new file mode 100644 (file)
index 0000000..8a4da64
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ *     ucon.c
+ *
+ * Copyright (c) 2004+ Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <asm/types.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <arpa/inet.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <getopt.h>
+
+#include <linux/connector.h>
+
+#define DEBUG
+#define NETLINK_CONNECTOR      11
+
+/* Hopefully your userspace connector.h matches this kernel */
+#define CN_TEST_IDX            CN_NETLINK_USERS + 3
+#define CN_TEST_VAL            0x456
+
+#ifdef DEBUG
+#define ulog(f, a...) fprintf(stdout, f, ##a)
+#else
+#define ulog(f, a...) do {} while (0)
+#endif
+
+static int need_exit;
+static __u32 seq;
+
+static int netlink_send(int s, struct cn_msg *msg)
+{
+       struct nlmsghdr *nlh;
+       unsigned int size;
+       int err;
+       char buf[128];
+       struct cn_msg *m;
+
+       size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
+
+       nlh = (struct nlmsghdr *)buf;
+       nlh->nlmsg_seq = seq++;
+       nlh->nlmsg_pid = getpid();
+       nlh->nlmsg_type = NLMSG_DONE;
+       nlh->nlmsg_len = size;
+       nlh->nlmsg_flags = 0;
+
+       m = NLMSG_DATA(nlh);
+#if 0
+       ulog("%s: [%08x.%08x] len=%u, seq=%u, ack=%u.\n",
+              __func__, msg->id.idx, msg->id.val, msg->len, msg->seq, msg->ack);
+#endif
+       memcpy(m, msg, sizeof(*m) + msg->len);
+
+       err = send(s, nlh, size, 0);
+       if (err == -1)
+               ulog("Failed to send: %s [%d].\n",
+                       strerror(errno), errno);
+
+       return err;
+}
+
+static void usage(void)
+{
+       printf(
+               "Usage: ucon [options] [output file]\n"
+               "\n"
+               "\t-h\tthis help screen\n"
+               "\t-s\tsend buffers to the test module\n"
+               "\n"
+               "The default behavior of ucon is to subscribe to the test module\n"
+               "and wait for state messages.  Any ones received are dumped to the\n"
+               "specified output file (or stdout).  The test module is assumed to\n"
+               "have an id of {%u.%u}\n"
+               "\n"
+               "If you get no output, then verify the cn_test module id matches\n"
+               "the expected id above.\n"
+               , CN_TEST_IDX, CN_TEST_VAL
+       );
+}
+
+int main(int argc, char *argv[])
+{
+       int s;
+       char buf[1024];
+       int len;
+       struct nlmsghdr *reply;
+       struct sockaddr_nl l_local;
+       struct cn_msg *data;
+       FILE *out;
+       time_t tm;
+       struct pollfd pfd;
+       bool send_msgs = false;
+
+       while ((s = getopt(argc, argv, "hs")) != -1) {
+               switch (s) {
+               case 's':
+                       send_msgs = true;
+                       break;
+
+               case 'h':
+                       usage();
+                       return 0;
+
+               default:
+                       /* getopt() outputs an error for us */
+                       usage();
+                       return 1;
+               }
+       }
+
+       if (argc != optind) {
+               out = fopen(argv[optind], "a+");
+               if (!out) {
+                       ulog("Unable to open %s for writing: %s\n",
+                               argv[1], strerror(errno));
+                       out = stdout;
+               }
+       } else
+               out = stdout;
+
+       memset(buf, 0, sizeof(buf));
+
+       s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
+       if (s == -1) {
+               perror("socket");
+               return -1;
+       }
+
+       l_local.nl_family = AF_NETLINK;
+       l_local.nl_groups = -1; /* bitmask of requested groups */
+       l_local.nl_pid = 0;
+
+       ulog("subscribing to %u.%u\n", CN_TEST_IDX, CN_TEST_VAL);
+
+       if (bind(s, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)) == -1) {
+               perror("bind");
+               close(s);
+               return -1;
+       }
+
+#if 0
+       {
+               int on = 0x57; /* Additional group number */
+               setsockopt(s, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &on, sizeof(on));
+       }
+#endif
+       if (send_msgs) {
+               int i, j;
+
+               memset(buf, 0, sizeof(buf));
+
+               data = (struct cn_msg *)buf;
+
+               data->id.idx = CN_TEST_IDX;
+               data->id.val = CN_TEST_VAL;
+               data->seq = seq++;
+               data->ack = 0;
+               data->len = 0;
+
+               for (j=0; j<10; ++j) {
+                       for (i=0; i<1000; ++i) {
+                               len = netlink_send(s, data);
+                       }
+
+                       ulog("%d messages have been sent to %08x.%08x.\n", i, data->id.idx, data->id.val);
+               }
+
+               return 0;
+       }
+
+
+       pfd.fd = s;
+
+       while (!need_exit) {
+               pfd.events = POLLIN;
+               pfd.revents = 0;
+               switch (poll(&pfd, 1, -1)) {
+                       case 0:
+                               need_exit = 1;
+                               break;
+                       case -1:
+                               if (errno != EINTR) {
+                                       need_exit = 1;
+                                       break;
+                               }
+                               continue;
+               }
+               if (need_exit)
+                       break;
+
+               memset(buf, 0, sizeof(buf));
+               len = recv(s, buf, sizeof(buf), 0);
+               if (len == -1) {
+                       perror("recv buf");
+                       close(s);
+                       return -1;
+               }
+               reply = (struct nlmsghdr *)buf;
+
+               switch (reply->nlmsg_type) {
+               case NLMSG_ERROR:
+                       fprintf(out, "Error message received.\n");
+                       fflush(out);
+                       break;
+               case NLMSG_DONE:
+                       data = (struct cn_msg *)NLMSG_DATA(reply);
+
+                       time(&tm);
+                       fprintf(out, "%.24s : [%x.%x] [%08u.%08u].\n",
+                               ctime(&tm), data->id.idx, data->id.val, data->seq, data->ack);
+                       fflush(out);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       close(s);
+       return 0;
+}