aboutsummaryrefslogtreecommitdiffstats
path: root/samples/connector
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2016-04-25 12:03:08 -0400
committerJonathan Corbet <corbet@lwn.net>2016-04-28 09:47:35 -0400
commit14fbff6b4e48a529c90e771598ac12bffd445ff4 (patch)
treeebbd42b6baecbba583bf00410b6c75be09b96f4c /samples/connector
parent715cda787c47458c94b2d012e6e9ab0988409f22 (diff)
samples: connector: from Documentation to samples directory
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>
Diffstat (limited to 'samples/connector')
-rw-r--r--samples/connector/.gitignore1
-rw-r--r--samples/connector/Makefile16
-rw-r--r--samples/connector/cn_test.c201
-rw-r--r--samples/connector/ucon.c250
4 files changed, 468 insertions, 0 deletions
diff --git a/samples/connector/.gitignore b/samples/connector/.gitignore
new file mode 100644
index 000000000000..d2b9c32accd4
--- /dev/null
+++ b/samples/connector/.gitignore
@@ -0,0 +1 @@
ucon
diff --git a/samples/connector/Makefile b/samples/connector/Makefile
new file mode 100644
index 000000000000..04b9622b6f51
--- /dev/null
+++ b/samples/connector/Makefile
@@ -0,0 +1,16 @@
1obj-$(CONFIG_SAMPLE_CONNECTOR) += cn_test.o
2
3# List of programs to build
4ifdef CONFIG_SAMPLE_CONNECTOR
5hostprogs-y := ucon
6endif
7
8# Tell kbuild to always build the programs
9always := $(hostprogs-y)
10
11HOSTCFLAGS_ucon.o += -I$(objtree)/usr/include
12
13all: modules
14
15modules clean:
16 $(MAKE) -C ../.. SUBDIRS=$(PWD) $@
diff --git a/samples/connector/cn_test.c b/samples/connector/cn_test.c
new file mode 100644
index 000000000000..d12cc944b696
--- /dev/null
+++ b/samples/connector/cn_test.c
@@ -0,0 +1,201 @@
1/*
2 * cn_test.c
3 *
4 * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
5 * All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#define pr_fmt(fmt) "cn_test: " fmt
23
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/moduleparam.h>
27#include <linux/skbuff.h>
28#include <linux/slab.h>
29#include <linux/timer.h>
30
31#include <linux/connector.h>
32
33static struct cb_id cn_test_id = { CN_NETLINK_USERS + 3, 0x456 };
34static char cn_test_name[] = "cn_test";
35static struct sock *nls;
36static struct timer_list cn_test_timer;
37
38static void cn_test_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
39{
40 pr_info("%s: %lu: idx=%x, val=%x, seq=%u, ack=%u, len=%d: %s.\n",
41 __func__, jiffies, msg->id.idx, msg->id.val,
42 msg->seq, msg->ack, msg->len,
43 msg->len ? (char *)msg->data : "");
44}
45
46/*
47 * Do not remove this function even if no one is using it as
48 * this is an example of how to get notifications about new
49 * connector user registration
50 */
51#if 0
52static int cn_test_want_notify(void)
53{
54 struct cn_ctl_msg *ctl;
55 struct cn_notify_req *req;
56 struct cn_msg *msg = NULL;
57 int size, size0;
58 struct sk_buff *skb;
59 struct nlmsghdr *nlh;
60 u32 group = 1;
61
62 size0 = sizeof(*msg) + sizeof(*ctl) + 3 * sizeof(*req);
63
64 size = NLMSG_SPACE(size0);
65
66 skb = alloc_skb(size, GFP_ATOMIC);
67 if (!skb) {
68 pr_err("failed to allocate new skb with size=%u\n", size);
69 return -ENOMEM;
70 }
71
72 nlh = nlmsg_put(skb, 0, 0x123, NLMSG_DONE, size - sizeof(*nlh), 0);
73 if (!nlh) {
74 kfree_skb(skb);
75 return -EMSGSIZE;
76 }
77
78 msg = nlmsg_data(nlh);
79
80 memset(msg, 0, size0);
81
82 msg->id.idx = -1;
83 msg->id.val = -1;
84 msg->seq = 0x123;
85 msg->ack = 0x345;
86 msg->len = size0 - sizeof(*msg);
87
88 ctl = (struct cn_ctl_msg *)(msg + 1);
89
90 ctl->idx_notify_num = 1;
91 ctl->val_notify_num = 2;
92 ctl->group = group;
93 ctl->len = msg->len - sizeof(*ctl);
94
95 req = (struct cn_notify_req *)(ctl + 1);
96
97 /*
98 * Idx.
99 */
100 req->first = cn_test_id.idx;
101 req->range = 10;
102
103 /*
104 * Val 0.
105 */
106 req++;
107 req->first = cn_test_id.val;
108 req->range = 10;
109
110 /*
111 * Val 1.
112 */
113 req++;
114 req->first = cn_test_id.val + 20;
115 req->range = 10;
116
117 NETLINK_CB(skb).dst_group = ctl->group;
118 //netlink_broadcast(nls, skb, 0, ctl->group, GFP_ATOMIC);
119 netlink_unicast(nls, skb, 0, 0);
120
121 pr_info("request was sent: group=0x%x\n", ctl->group);
122
123 return 0;
124}
125#endif
126
127static u32 cn_test_timer_counter;
128static void cn_test_timer_func(unsigned long __data)
129{
130 struct cn_msg *m;
131 char data[32];
132
133 pr_debug("%s: timer fired with data %lu\n", __func__, __data);
134
135 m = kzalloc(sizeof(*m) + sizeof(data), GFP_ATOMIC);
136 if (m) {
137
138 memcpy(&m->id, &cn_test_id, sizeof(m->id));
139 m->seq = cn_test_timer_counter;
140 m->len = sizeof(data);
141
142 m->len =
143 scnprintf(data, sizeof(data), "counter = %u",
144 cn_test_timer_counter) + 1;
145
146 memcpy(m + 1, data, m->len);
147
148 cn_netlink_send(m, 0, 0, GFP_ATOMIC);
149 kfree(m);
150 }
151
152 cn_test_timer_counter++;
153
154 mod_timer(&cn_test_timer, jiffies + msecs_to_jiffies(1000));
155}
156
157static int cn_test_init(void)
158{
159 int err;
160
161 err = cn_add_callback(&cn_test_id, cn_test_name, cn_test_callback);
162 if (err)
163 goto err_out;
164 cn_test_id.val++;
165 err = cn_add_callback(&cn_test_id, cn_test_name, cn_test_callback);
166 if (err) {
167 cn_del_callback(&cn_test_id);
168 goto err_out;
169 }
170
171 setup_timer(&cn_test_timer, cn_test_timer_func, 0);
172 mod_timer(&cn_test_timer, jiffies + msecs_to_jiffies(1000));
173
174 pr_info("initialized with id={%u.%u}\n",
175 cn_test_id.idx, cn_test_id.val);
176
177 return 0;
178
179 err_out:
180 if (nls && nls->sk_socket)
181 sock_release(nls->sk_socket);
182
183 return err;
184}
185
186static void cn_test_fini(void)
187{
188 del_timer_sync(&cn_test_timer);
189 cn_del_callback(&cn_test_id);
190 cn_test_id.val--;
191 cn_del_callback(&cn_test_id);
192 if (nls && nls->sk_socket)
193 sock_release(nls->sk_socket);
194}
195
196module_init(cn_test_init);
197module_exit(cn_test_fini);
198
199MODULE_LICENSE("GPL");
200MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
201MODULE_DESCRIPTION("Connector's test module");
diff --git a/samples/connector/ucon.c b/samples/connector/ucon.c
new file mode 100644
index 000000000000..8a4da64e02a8
--- /dev/null
+++ b/samples/connector/ucon.c
@@ -0,0 +1,250 @@
1/*
2 * ucon.c
3 *
4 * Copyright (c) 2004+ Evgeniy Polyakov <zbr@ioremap.net>
5 *
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <asm/types.h>
23
24#include <sys/types.h>
25#include <sys/socket.h>
26#include <sys/poll.h>
27
28#include <linux/netlink.h>
29#include <linux/rtnetlink.h>
30
31#include <arpa/inet.h>
32
33#include <stdbool.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <unistd.h>
37#include <string.h>
38#include <errno.h>
39#include <time.h>
40#include <getopt.h>
41
42#include <linux/connector.h>
43
44#define DEBUG
45#define NETLINK_CONNECTOR 11
46
47/* Hopefully your userspace connector.h matches this kernel */
48#define CN_TEST_IDX CN_NETLINK_USERS + 3
49#define CN_TEST_VAL 0x456
50
51#ifdef DEBUG
52#define ulog(f, a...) fprintf(stdout, f, ##a)
53#else
54#define ulog(f, a...) do {} while (0)
55#endif
56
57static int need_exit;
58static __u32 seq;
59
60static int netlink_send(int s, struct cn_msg *msg)
61{
62 struct nlmsghdr *nlh;
63 unsigned int size;
64 int err;
65 char buf[128];
66 struct cn_msg *m;
67
68 size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
69
70 nlh = (struct nlmsghdr *)buf;
71 nlh->nlmsg_seq = seq++;
72 nlh->nlmsg_pid = getpid();
73 nlh->nlmsg_type = NLMSG_DONE;
74 nlh->nlmsg_len = size;
75 nlh->nlmsg_flags = 0;
76
77 m = NLMSG_DATA(nlh);
78#if 0
79 ulog("%s: [%08x.%08x] len=%u, seq=%u, ack=%u.\n",
80 __func__, msg->id.idx, msg->id.val, msg->len, msg->seq, msg->ack);
81#endif
82 memcpy(m, msg, sizeof(*m) + msg->len);
83
84 err = send(s, nlh, size, 0);
85 if (err == -1)
86 ulog("Failed to send: %s [%d].\n",
87 strerror(errno), errno);
88
89 return err;
90}
91
92static void usage(void)
93{
94 printf(
95 "Usage: ucon [options] [output file]\n"
96 "\n"
97 "\t-h\tthis help screen\n"
98 "\t-s\tsend buffers to the test module\n"
99 "\n"
100 "The default behavior of ucon is to subscribe to the test module\n"
101 "and wait for state messages. Any ones received are dumped to the\n"
102 "specified output file (or stdout). The test module is assumed to\n"
103 "have an id of {%u.%u}\n"
104 "\n"
105 "If you get no output, then verify the cn_test module id matches\n"
106 "the expected id above.\n"
107 , CN_TEST_IDX, CN_TEST_VAL
108 );
109}
110
111int main(int argc, char *argv[])
112{
113 int s;
114 char buf[1024];
115 int len;
116 struct nlmsghdr *reply;
117 struct sockaddr_nl l_local;
118 struct cn_msg *data;
119 FILE *out;
120 time_t tm;
121 struct pollfd pfd;
122 bool send_msgs = false;
123
124 while ((s = getopt(argc, argv, "hs")) != -1) {
125 switch (s) {
126 case 's':
127 send_msgs = true;
128 break;
129
130 case 'h':
131 usage();
132 return 0;
133
134 default:
135 /* getopt() outputs an error for us */
136 usage();
137 return 1;
138 }
139 }
140
141 if (argc != optind) {
142 out = fopen(argv[optind], "a+");
143 if (!out) {
144 ulog("Unable to open %s for writing: %s\n",
145 argv[1], strerror(errno));
146 out = stdout;
147 }
148 } else
149 out = stdout;
150
151 memset(buf, 0, sizeof(buf));
152
153 s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
154 if (s == -1) {
155 perror("socket");
156 return -1;
157 }
158
159 l_local.nl_family = AF_NETLINK;
160 l_local.nl_groups = -1; /* bitmask of requested groups */
161 l_local.nl_pid = 0;
162
163 ulog("subscribing to %u.%u\n", CN_TEST_IDX, CN_TEST_VAL);
164
165 if (bind(s, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)) == -1) {
166 perror("bind");
167 close(s);
168 return -1;
169 }
170
171#if 0
172 {
173 int on = 0x57; /* Additional group number */
174 setsockopt(s, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &on, sizeof(on));
175 }
176#endif
177 if (send_msgs) {
178 int i, j;
179
180 memset(buf, 0, sizeof(buf));
181
182 data = (struct cn_msg *)buf;
183
184 data->id.idx = CN_TEST_IDX;
185 data->id.val = CN_TEST_VAL;
186 data->seq = seq++;
187 data->ack = 0;
188 data->len = 0;
189
190 for (j=0; j<10; ++j) {
191 for (i=0; i<1000; ++i) {
192 len = netlink_send(s, data);
193 }
194
195 ulog("%d messages have been sent to %08x.%08x.\n", i, data->id.idx, data->id.val);
196 }
197
198 return 0;
199 }
200
201
202 pfd.fd = s;
203
204 while (!need_exit) {
205 pfd.events = POLLIN;
206 pfd.revents = 0;
207 switch (poll(&pfd, 1, -1)) {
208 case 0:
209 need_exit = 1;
210 break;
211 case -1:
212 if (errno != EINTR) {
213 need_exit = 1;
214 break;
215 }
216 continue;
217 }
218 if (need_exit)
219 break;
220
221 memset(buf, 0, sizeof(buf));
222 len = recv(s, buf, sizeof(buf), 0);
223 if (len == -1) {
224 perror("recv buf");
225 close(s);
226 return -1;
227 }
228 reply = (struct nlmsghdr *)buf;
229
230 switch (reply->nlmsg_type) {
231 case NLMSG_ERROR:
232 fprintf(out, "Error message received.\n");
233 fflush(out);
234 break;
235 case NLMSG_DONE:
236 data = (struct cn_msg *)NLMSG_DATA(reply);
237
238 time(&tm);
239 fprintf(out, "%.24s : [%x.%x] [%08u.%08u].\n",
240 ctime(&tm), data->id.idx, data->id.val, data->seq, data->ack);
241 fflush(out);
242 break;
243 default:
244 break;
245 }
246 }
247
248 close(s);
249 return 0;
250}