1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (c) 2020, The Linux Foundation. All rights reserved. */ 3 4 #define pr_fmt(fmt) "cnss_genl: " fmt 5 6 #include <linux/err.h> 7 #include <linux/module.h> 8 #include <linux/delay.h> 9 #include <net/netlink.h> 10 #include <net/genetlink.h> 11 12 #include "main.h" 13 #include "debug.h" 14 15 #define ICNSS_GENL_FAMILY_NAME "cnss-genl" 16 #define ICNSS_GENL_MCAST_GROUP_NAME "cnss-genl-grp" 17 #define ICNSS_GENL_VERSION 1 18 #define ICNSS_GENL_DATA_LEN_MAX (15 * 1024) 19 #define ICNSS_GENL_STR_LEN_MAX 16 20 21 enum { 22 ICNSS_GENL_ATTR_MSG_UNSPEC, 23 ICNSS_GENL_ATTR_MSG_TYPE, 24 ICNSS_GENL_ATTR_MSG_FILE_NAME, 25 ICNSS_GENL_ATTR_MSG_TOTAL_SIZE, 26 ICNSS_GENL_ATTR_MSG_SEG_ID, 27 ICNSS_GENL_ATTR_MSG_END, 28 ICNSS_GENL_ATTR_MSG_DATA_LEN, 29 ICNSS_GENL_ATTR_MSG_DATA, 30 __ICNSS_GENL_ATTR_MAX, 31 }; 32 33 #define ICNSS_GENL_ATTR_MAX (__ICNSS_GENL_ATTR_MAX - 1) 34 35 enum { 36 ICNSS_GENL_CMD_UNSPEC, 37 ICNSS_GENL_CMD_MSG, 38 __ICNSS_GENL_CMD_MAX, 39 }; 40 41 #define ICNSS_GENL_CMD_MAX (__ICNSS_GENL_CMD_MAX - 1) 42 43 static struct nla_policy icnss_genl_msg_policy[ICNSS_GENL_ATTR_MAX + 1] = { 44 [ICNSS_GENL_ATTR_MSG_TYPE] = { .type = NLA_U8 }, 45 [ICNSS_GENL_ATTR_MSG_FILE_NAME] = { .type = NLA_NUL_STRING, 46 .len = ICNSS_GENL_STR_LEN_MAX }, 47 [ICNSS_GENL_ATTR_MSG_TOTAL_SIZE] = { .type = NLA_U32 }, 48 [ICNSS_GENL_ATTR_MSG_SEG_ID] = { .type = NLA_U32 }, 49 [ICNSS_GENL_ATTR_MSG_END] = { .type = NLA_U8 }, 50 [ICNSS_GENL_ATTR_MSG_DATA_LEN] = { .type = NLA_U32 }, 51 [ICNSS_GENL_ATTR_MSG_DATA] = { .type = NLA_BINARY, 52 .len = ICNSS_GENL_DATA_LEN_MAX }, 53 }; 54 55 static int icnss_genl_process_msg(struct sk_buff *skb, struct genl_info *info) 56 { 57 return 0; 58 } 59 60 static struct genl_ops icnss_genl_ops[] = { 61 { 62 .cmd = ICNSS_GENL_CMD_MSG, 63 .doit = icnss_genl_process_msg, 64 }, 65 }; 66 67 static struct genl_multicast_group icnss_genl_mcast_grp[] = { 68 { 69 .name = ICNSS_GENL_MCAST_GROUP_NAME, 70 }, 71 }; 72 73 static struct genl_family icnss_genl_family = { 74 .id = 0, 75 .hdrsize = 0, 76 .name = ICNSS_GENL_FAMILY_NAME, 77 .version = ICNSS_GENL_VERSION, 78 .maxattr = ICNSS_GENL_ATTR_MAX, 79 .policy = icnss_genl_msg_policy, 80 .module = THIS_MODULE, 81 .ops = icnss_genl_ops, 82 .n_ops = ARRAY_SIZE(icnss_genl_ops), 83 .mcgrps = icnss_genl_mcast_grp, 84 .n_mcgrps = ARRAY_SIZE(icnss_genl_mcast_grp), 85 }; 86 87 static int icnss_genl_send_data(u8 type, char *file_name, u32 total_size, 88 u32 seg_id, u8 end, u32 data_len, u8 *msg_buff) 89 { 90 struct sk_buff *skb = NULL; 91 void *msg_header = NULL; 92 int ret = 0; 93 char filename[ICNSS_GENL_STR_LEN_MAX + 1]; 94 95 icnss_pr_dbg("type: %u, file_name %s, total_size: %x, seg_id %u, end %u, data_len %u\n", 96 type, file_name, total_size, seg_id, end, data_len); 97 98 if (!file_name) 99 strlcpy(filename, "default", sizeof(filename)); 100 else 101 strlcpy(filename, file_name, sizeof(filename)); 102 103 skb = genlmsg_new(NLMSG_HDRLEN + 104 nla_total_size(sizeof(type)) + 105 nla_total_size(strlen(filename) + 1) + 106 nla_total_size(sizeof(total_size)) + 107 nla_total_size(sizeof(seg_id)) + 108 nla_total_size(sizeof(end)) + 109 nla_total_size(sizeof(data_len)) + 110 nla_total_size(data_len), GFP_KERNEL); 111 if (!skb) 112 return -ENOMEM; 113 114 msg_header = genlmsg_put(skb, 0, 0, 115 &icnss_genl_family, 0, 116 ICNSS_GENL_CMD_MSG); 117 if (!msg_header) { 118 ret = -ENOMEM; 119 goto fail; 120 } 121 122 ret = nla_put_u8(skb, ICNSS_GENL_ATTR_MSG_TYPE, type); 123 if (ret < 0) 124 goto fail; 125 ret = nla_put_string(skb, ICNSS_GENL_ATTR_MSG_FILE_NAME, filename); 126 if (ret < 0) 127 goto fail; 128 ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_TOTAL_SIZE, total_size); 129 if (ret < 0) 130 goto fail; 131 ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_SEG_ID, seg_id); 132 if (ret < 0) 133 goto fail; 134 ret = nla_put_u8(skb, ICNSS_GENL_ATTR_MSG_END, end); 135 if (ret < 0) 136 goto fail; 137 ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_DATA_LEN, data_len); 138 if (ret < 0) 139 goto fail; 140 ret = nla_put(skb, ICNSS_GENL_ATTR_MSG_DATA, data_len, msg_buff); 141 if (ret < 0) 142 goto fail; 143 144 genlmsg_end(skb, msg_header); 145 ret = genlmsg_multicast(&icnss_genl_family, skb, 0, 0, GFP_KERNEL); 146 if (ret < 0) 147 icnss_pr_err("Fail to send genl msg: %d\n", ret); 148 149 return ret; 150 fail: 151 icnss_pr_err("Fail to generate genl msg: %d\n", ret); 152 if (skb) 153 nlmsg_free(skb); 154 return ret; 155 } 156 157 int icnss_genl_send_msg(void *buff, u8 type, char *file_name, u32 total_size) 158 { 159 int ret = 0; 160 u8 *msg_buff = buff; 161 u32 remaining = total_size; 162 u32 seg_id = 0; 163 u32 data_len = 0; 164 u8 end = 0; 165 u8 retry; 166 167 icnss_pr_dbg("type: %u, total_size: %x\n", type, total_size); 168 169 while (remaining) { 170 if (remaining > ICNSS_GENL_DATA_LEN_MAX) { 171 data_len = ICNSS_GENL_DATA_LEN_MAX; 172 } else { 173 data_len = remaining; 174 end = 1; 175 } 176 177 for (retry = 0; retry < 2; retry++) { 178 ret = icnss_genl_send_data(type, file_name, total_size, 179 seg_id, end, data_len, 180 msg_buff); 181 if (ret >= 0) 182 break; 183 msleep(100); 184 } 185 186 if (ret < 0) { 187 icnss_pr_err("fail to send genl data, ret %d\n", ret); 188 return ret; 189 } 190 191 remaining -= data_len; 192 msg_buff += data_len; 193 seg_id++; 194 } 195 196 return ret; 197 } 198 199 int icnss_genl_init(void) 200 { 201 int ret = 0; 202 203 ret = genl_register_family(&icnss_genl_family); 204 if (ret != 0) 205 icnss_pr_err("genl_register_family fail: %d\n", ret); 206 207 return ret; 208 } 209 210 void icnss_genl_exit(void) 211 { 212 genl_unregister_family(&icnss_genl_family); 213 } 214