1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * cn_test.c |
4 | * |
5 | * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net> |
6 | * All rights reserved. |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) "cn_test: " fmt |
10 | |
11 | #include <linux/kernel.h> |
12 | #include <linux/module.h> |
13 | #include <linux/moduleparam.h> |
14 | #include <linux/skbuff.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/timer.h> |
17 | |
18 | #include <linux/connector.h> |
19 | |
20 | static struct cb_id cn_test_id = { CN_NETLINK_USERS + 3, 0x456 }; |
21 | static char cn_test_name[] = "cn_test" ; |
22 | static struct sock *nls; |
23 | static struct timer_list cn_test_timer; |
24 | |
25 | static void cn_test_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) |
26 | { |
27 | pr_info("%s: %lu: idx=%x, val=%x, seq=%u, ack=%u, len=%d: %s.\n" , |
28 | __func__, jiffies, msg->id.idx, msg->id.val, |
29 | msg->seq, msg->ack, msg->len, |
30 | msg->len ? (char *)msg->data : "" ); |
31 | } |
32 | |
33 | /* |
34 | * Do not remove this function even if no one is using it as |
35 | * this is an example of how to get notifications about new |
36 | * connector user registration |
37 | */ |
38 | #if 0 |
39 | static int cn_test_want_notify(void) |
40 | { |
41 | struct cn_ctl_msg *ctl; |
42 | struct cn_notify_req *req; |
43 | struct cn_msg *msg = NULL; |
44 | int size, size0; |
45 | struct sk_buff *skb; |
46 | struct nlmsghdr *nlh; |
47 | u32 group = 1; |
48 | |
49 | size0 = sizeof(*msg) + sizeof(*ctl) + 3 * sizeof(*req); |
50 | |
51 | size = NLMSG_SPACE(size0); |
52 | |
53 | skb = alloc_skb(size, GFP_ATOMIC); |
54 | if (!skb) { |
55 | pr_err("failed to allocate new skb with size=%u\n" , size); |
56 | return -ENOMEM; |
57 | } |
58 | |
59 | nlh = nlmsg_put(skb, 0, 0x123, NLMSG_DONE, size - sizeof(*nlh), 0); |
60 | if (!nlh) { |
61 | kfree_skb(skb); |
62 | return -EMSGSIZE; |
63 | } |
64 | |
65 | msg = nlmsg_data(nlh); |
66 | |
67 | memset(msg, 0, size0); |
68 | |
69 | msg->id.idx = -1; |
70 | msg->id.val = -1; |
71 | msg->seq = 0x123; |
72 | msg->ack = 0x345; |
73 | msg->len = size0 - sizeof(*msg); |
74 | |
75 | ctl = (struct cn_ctl_msg *)(msg + 1); |
76 | |
77 | ctl->idx_notify_num = 1; |
78 | ctl->val_notify_num = 2; |
79 | ctl->group = group; |
80 | ctl->len = msg->len - sizeof(*ctl); |
81 | |
82 | req = (struct cn_notify_req *)(ctl + 1); |
83 | |
84 | /* |
85 | * Idx. |
86 | */ |
87 | req->first = cn_test_id.idx; |
88 | req->range = 10; |
89 | |
90 | /* |
91 | * Val 0. |
92 | */ |
93 | req++; |
94 | req->first = cn_test_id.val; |
95 | req->range = 10; |
96 | |
97 | /* |
98 | * Val 1. |
99 | */ |
100 | req++; |
101 | req->first = cn_test_id.val + 20; |
102 | req->range = 10; |
103 | |
104 | NETLINK_CB(skb).dst_group = ctl->group; |
105 | //netlink_broadcast(nls, skb, 0, ctl->group, GFP_ATOMIC); |
106 | netlink_unicast(nls, skb, 0, 0); |
107 | |
108 | pr_info("request was sent: group=0x%x\n" , ctl->group); |
109 | |
110 | return 0; |
111 | } |
112 | #endif |
113 | |
114 | static u32 cn_test_timer_counter; |
115 | static void cn_test_timer_func(struct timer_list *unused) |
116 | { |
117 | struct cn_msg *m; |
118 | char data[32]; |
119 | |
120 | pr_debug("%s: timer fired\n" , __func__); |
121 | |
122 | m = kzalloc(size: sizeof(*m) + sizeof(data), GFP_ATOMIC); |
123 | if (m) { |
124 | |
125 | memcpy(&m->id, &cn_test_id, sizeof(m->id)); |
126 | m->seq = cn_test_timer_counter; |
127 | m->len = sizeof(data); |
128 | |
129 | m->len = |
130 | scnprintf(buf: data, size: sizeof(data), fmt: "counter = %u" , |
131 | cn_test_timer_counter) + 1; |
132 | |
133 | memcpy(m + 1, data, m->len); |
134 | |
135 | cn_netlink_send(msg: m, portid: 0, group: 0, GFP_ATOMIC); |
136 | kfree(objp: m); |
137 | } |
138 | |
139 | cn_test_timer_counter++; |
140 | |
141 | mod_timer(timer: &cn_test_timer, expires: jiffies + msecs_to_jiffies(m: 1000)); |
142 | } |
143 | |
144 | static int cn_test_init(void) |
145 | { |
146 | int err; |
147 | |
148 | err = cn_add_callback(id: &cn_test_id, name: cn_test_name, callback: cn_test_callback); |
149 | if (err) |
150 | goto err_out; |
151 | cn_test_id.val++; |
152 | err = cn_add_callback(id: &cn_test_id, name: cn_test_name, callback: cn_test_callback); |
153 | if (err) { |
154 | cn_del_callback(id: &cn_test_id); |
155 | goto err_out; |
156 | } |
157 | |
158 | timer_setup(&cn_test_timer, cn_test_timer_func, 0); |
159 | mod_timer(timer: &cn_test_timer, expires: jiffies + msecs_to_jiffies(m: 1000)); |
160 | |
161 | pr_info("initialized with id={%u.%u}\n" , |
162 | cn_test_id.idx, cn_test_id.val); |
163 | |
164 | return 0; |
165 | |
166 | err_out: |
167 | if (nls && nls->sk_socket) |
168 | sock_release(sock: nls->sk_socket); |
169 | |
170 | return err; |
171 | } |
172 | |
173 | static void cn_test_fini(void) |
174 | { |
175 | del_timer_sync(timer: &cn_test_timer); |
176 | cn_del_callback(id: &cn_test_id); |
177 | cn_test_id.val--; |
178 | cn_del_callback(id: &cn_test_id); |
179 | if (nls && nls->sk_socket) |
180 | sock_release(sock: nls->sk_socket); |
181 | } |
182 | |
183 | module_init(cn_test_init); |
184 | module_exit(cn_test_fini); |
185 | |
186 | MODULE_LICENSE("GPL" ); |
187 | MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>" ); |
188 | MODULE_DESCRIPTION("Connector's test module" ); |
189 | |