1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 214fbff6bSArnd Bergmann /* 314fbff6bSArnd Bergmann * cn_test.c 414fbff6bSArnd Bergmann * 514fbff6bSArnd Bergmann * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net> 614fbff6bSArnd Bergmann * All rights reserved. 714fbff6bSArnd Bergmann */ 814fbff6bSArnd Bergmann 914fbff6bSArnd Bergmann #define pr_fmt(fmt) "cn_test: " fmt 1014fbff6bSArnd Bergmann 1114fbff6bSArnd Bergmann #include <linux/kernel.h> 1214fbff6bSArnd Bergmann #include <linux/module.h> 1314fbff6bSArnd Bergmann #include <linux/moduleparam.h> 1414fbff6bSArnd Bergmann #include <linux/skbuff.h> 1514fbff6bSArnd Bergmann #include <linux/slab.h> 1614fbff6bSArnd Bergmann #include <linux/timer.h> 1714fbff6bSArnd Bergmann 1814fbff6bSArnd Bergmann #include <linux/connector.h> 1914fbff6bSArnd Bergmann 2014fbff6bSArnd Bergmann static struct cb_id cn_test_id = { CN_NETLINK_USERS + 3, 0x456 }; 2114fbff6bSArnd Bergmann static char cn_test_name[] = "cn_test"; 2214fbff6bSArnd Bergmann static struct sock *nls; 2314fbff6bSArnd Bergmann static struct timer_list cn_test_timer; 2414fbff6bSArnd Bergmann 2514fbff6bSArnd Bergmann static void cn_test_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) 2614fbff6bSArnd Bergmann { 2714fbff6bSArnd Bergmann pr_info("%s: %lu: idx=%x, val=%x, seq=%u, ack=%u, len=%d: %s.\n", 2814fbff6bSArnd Bergmann __func__, jiffies, msg->id.idx, msg->id.val, 2914fbff6bSArnd Bergmann msg->seq, msg->ack, msg->len, 3014fbff6bSArnd Bergmann msg->len ? (char *)msg->data : ""); 3114fbff6bSArnd Bergmann } 3214fbff6bSArnd Bergmann 3314fbff6bSArnd Bergmann /* 3414fbff6bSArnd Bergmann * Do not remove this function even if no one is using it as 3514fbff6bSArnd Bergmann * this is an example of how to get notifications about new 3614fbff6bSArnd Bergmann * connector user registration 3714fbff6bSArnd Bergmann */ 3814fbff6bSArnd Bergmann #if 0 3914fbff6bSArnd Bergmann static int cn_test_want_notify(void) 4014fbff6bSArnd Bergmann { 4114fbff6bSArnd Bergmann struct cn_ctl_msg *ctl; 4214fbff6bSArnd Bergmann struct cn_notify_req *req; 4314fbff6bSArnd Bergmann struct cn_msg *msg = NULL; 4414fbff6bSArnd Bergmann int size, size0; 4514fbff6bSArnd Bergmann struct sk_buff *skb; 4614fbff6bSArnd Bergmann struct nlmsghdr *nlh; 4714fbff6bSArnd Bergmann u32 group = 1; 4814fbff6bSArnd Bergmann 4914fbff6bSArnd Bergmann size0 = sizeof(*msg) + sizeof(*ctl) + 3 * sizeof(*req); 5014fbff6bSArnd Bergmann 5114fbff6bSArnd Bergmann size = NLMSG_SPACE(size0); 5214fbff6bSArnd Bergmann 5314fbff6bSArnd Bergmann skb = alloc_skb(size, GFP_ATOMIC); 5414fbff6bSArnd Bergmann if (!skb) { 5514fbff6bSArnd Bergmann pr_err("failed to allocate new skb with size=%u\n", size); 5614fbff6bSArnd Bergmann return -ENOMEM; 5714fbff6bSArnd Bergmann } 5814fbff6bSArnd Bergmann 5914fbff6bSArnd Bergmann nlh = nlmsg_put(skb, 0, 0x123, NLMSG_DONE, size - sizeof(*nlh), 0); 6014fbff6bSArnd Bergmann if (!nlh) { 6114fbff6bSArnd Bergmann kfree_skb(skb); 6214fbff6bSArnd Bergmann return -EMSGSIZE; 6314fbff6bSArnd Bergmann } 6414fbff6bSArnd Bergmann 6514fbff6bSArnd Bergmann msg = nlmsg_data(nlh); 6614fbff6bSArnd Bergmann 6714fbff6bSArnd Bergmann memset(msg, 0, size0); 6814fbff6bSArnd Bergmann 6914fbff6bSArnd Bergmann msg->id.idx = -1; 7014fbff6bSArnd Bergmann msg->id.val = -1; 7114fbff6bSArnd Bergmann msg->seq = 0x123; 7214fbff6bSArnd Bergmann msg->ack = 0x345; 7314fbff6bSArnd Bergmann msg->len = size0 - sizeof(*msg); 7414fbff6bSArnd Bergmann 7514fbff6bSArnd Bergmann ctl = (struct cn_ctl_msg *)(msg + 1); 7614fbff6bSArnd Bergmann 7714fbff6bSArnd Bergmann ctl->idx_notify_num = 1; 7814fbff6bSArnd Bergmann ctl->val_notify_num = 2; 7914fbff6bSArnd Bergmann ctl->group = group; 8014fbff6bSArnd Bergmann ctl->len = msg->len - sizeof(*ctl); 8114fbff6bSArnd Bergmann 8214fbff6bSArnd Bergmann req = (struct cn_notify_req *)(ctl + 1); 8314fbff6bSArnd Bergmann 8414fbff6bSArnd Bergmann /* 8514fbff6bSArnd Bergmann * Idx. 8614fbff6bSArnd Bergmann */ 8714fbff6bSArnd Bergmann req->first = cn_test_id.idx; 8814fbff6bSArnd Bergmann req->range = 10; 8914fbff6bSArnd Bergmann 9014fbff6bSArnd Bergmann /* 9114fbff6bSArnd Bergmann * Val 0. 9214fbff6bSArnd Bergmann */ 9314fbff6bSArnd Bergmann req++; 9414fbff6bSArnd Bergmann req->first = cn_test_id.val; 9514fbff6bSArnd Bergmann req->range = 10; 9614fbff6bSArnd Bergmann 9714fbff6bSArnd Bergmann /* 9814fbff6bSArnd Bergmann * Val 1. 9914fbff6bSArnd Bergmann */ 10014fbff6bSArnd Bergmann req++; 10114fbff6bSArnd Bergmann req->first = cn_test_id.val + 20; 10214fbff6bSArnd Bergmann req->range = 10; 10314fbff6bSArnd Bergmann 10414fbff6bSArnd Bergmann NETLINK_CB(skb).dst_group = ctl->group; 10514fbff6bSArnd Bergmann //netlink_broadcast(nls, skb, 0, ctl->group, GFP_ATOMIC); 10614fbff6bSArnd Bergmann netlink_unicast(nls, skb, 0, 0); 10714fbff6bSArnd Bergmann 10814fbff6bSArnd Bergmann pr_info("request was sent: group=0x%x\n", ctl->group); 10914fbff6bSArnd Bergmann 11014fbff6bSArnd Bergmann return 0; 11114fbff6bSArnd Bergmann } 11214fbff6bSArnd Bergmann #endif 11314fbff6bSArnd Bergmann 11414fbff6bSArnd Bergmann static u32 cn_test_timer_counter; 1150d694234SKees Cook static void cn_test_timer_func(struct timer_list *unused) 11614fbff6bSArnd Bergmann { 11714fbff6bSArnd Bergmann struct cn_msg *m; 11814fbff6bSArnd Bergmann char data[32]; 11914fbff6bSArnd Bergmann 1200d694234SKees Cook pr_debug("%s: timer fired\n", __func__); 12114fbff6bSArnd Bergmann 12214fbff6bSArnd Bergmann m = kzalloc(sizeof(*m) + sizeof(data), GFP_ATOMIC); 12314fbff6bSArnd Bergmann if (m) { 12414fbff6bSArnd Bergmann 12514fbff6bSArnd Bergmann memcpy(&m->id, &cn_test_id, sizeof(m->id)); 12614fbff6bSArnd Bergmann m->seq = cn_test_timer_counter; 12714fbff6bSArnd Bergmann m->len = sizeof(data); 12814fbff6bSArnd Bergmann 12914fbff6bSArnd Bergmann m->len = 13014fbff6bSArnd Bergmann scnprintf(data, sizeof(data), "counter = %u", 13114fbff6bSArnd Bergmann cn_test_timer_counter) + 1; 13214fbff6bSArnd Bergmann 13314fbff6bSArnd Bergmann memcpy(m + 1, data, m->len); 13414fbff6bSArnd Bergmann 13514fbff6bSArnd Bergmann cn_netlink_send(m, 0, 0, GFP_ATOMIC); 13614fbff6bSArnd Bergmann kfree(m); 13714fbff6bSArnd Bergmann } 13814fbff6bSArnd Bergmann 13914fbff6bSArnd Bergmann cn_test_timer_counter++; 14014fbff6bSArnd Bergmann 14114fbff6bSArnd Bergmann mod_timer(&cn_test_timer, jiffies + msecs_to_jiffies(1000)); 14214fbff6bSArnd Bergmann } 14314fbff6bSArnd Bergmann 14414fbff6bSArnd Bergmann static int cn_test_init(void) 14514fbff6bSArnd Bergmann { 14614fbff6bSArnd Bergmann int err; 14714fbff6bSArnd Bergmann 14814fbff6bSArnd Bergmann err = cn_add_callback(&cn_test_id, cn_test_name, cn_test_callback); 14914fbff6bSArnd Bergmann if (err) 15014fbff6bSArnd Bergmann goto err_out; 15114fbff6bSArnd Bergmann cn_test_id.val++; 15214fbff6bSArnd Bergmann err = cn_add_callback(&cn_test_id, cn_test_name, cn_test_callback); 15314fbff6bSArnd Bergmann if (err) { 15414fbff6bSArnd Bergmann cn_del_callback(&cn_test_id); 15514fbff6bSArnd Bergmann goto err_out; 15614fbff6bSArnd Bergmann } 15714fbff6bSArnd Bergmann 1580d694234SKees Cook timer_setup(&cn_test_timer, cn_test_timer_func, 0); 15914fbff6bSArnd Bergmann mod_timer(&cn_test_timer, jiffies + msecs_to_jiffies(1000)); 16014fbff6bSArnd Bergmann 16114fbff6bSArnd Bergmann pr_info("initialized with id={%u.%u}\n", 16214fbff6bSArnd Bergmann cn_test_id.idx, cn_test_id.val); 16314fbff6bSArnd Bergmann 16414fbff6bSArnd Bergmann return 0; 16514fbff6bSArnd Bergmann 16614fbff6bSArnd Bergmann err_out: 16714fbff6bSArnd Bergmann if (nls && nls->sk_socket) 16814fbff6bSArnd Bergmann sock_release(nls->sk_socket); 16914fbff6bSArnd Bergmann 17014fbff6bSArnd Bergmann return err; 17114fbff6bSArnd Bergmann } 17214fbff6bSArnd Bergmann 17314fbff6bSArnd Bergmann static void cn_test_fini(void) 17414fbff6bSArnd Bergmann { 17514fbff6bSArnd Bergmann del_timer_sync(&cn_test_timer); 17614fbff6bSArnd Bergmann cn_del_callback(&cn_test_id); 17714fbff6bSArnd Bergmann cn_test_id.val--; 17814fbff6bSArnd Bergmann cn_del_callback(&cn_test_id); 17914fbff6bSArnd Bergmann if (nls && nls->sk_socket) 18014fbff6bSArnd Bergmann sock_release(nls->sk_socket); 18114fbff6bSArnd Bergmann } 18214fbff6bSArnd Bergmann 18314fbff6bSArnd Bergmann module_init(cn_test_init); 18414fbff6bSArnd Bergmann module_exit(cn_test_fini); 18514fbff6bSArnd Bergmann 18614fbff6bSArnd Bergmann MODULE_LICENSE("GPL"); 18714fbff6bSArnd Bergmann MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>"); 18814fbff6bSArnd Bergmann MODULE_DESCRIPTION("Connector's test module"); 189