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