1 /******************************************************************************
2 *
3 * Copyright(c) 2007 - 2020 Realtek Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 *****************************************************************************/
15 #define _RTW_NLRTW_C_
16
17 #include <drv_types.h>
18 #include "nlrtw.h"
19
20 #ifdef CONFIG_RTW_NLRTW
21
22 #include <net/netlink.h>
23 #include <net/genetlink.h>
24 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
25 #include <uapi/linux/netlink.h>
26 #endif
27
28
29 enum nlrtw_cmds {
30 NLRTW_CMD_UNSPEC,
31
32 NLRTW_CMD_CHANNEL_UTILIZATION,
33
34 __NLRTW_CMD_AFTER_LAST,
35 NLRTW_CMD_MAX = __NLRTW_CMD_AFTER_LAST - 1
36 };
37
38 enum nlrtw_attrs {
39 NLRTW_ATTR_UNSPEC,
40
41 NLRTW_ATTR_WIPHY_NAME,
42 NLRTW_ATTR_CHANNEL_UTILIZATIONS,
43 NLRTW_ATTR_CHANNEL_UTILIZATION_THRESHOLD,
44
45 __NLRTW_ATTR_AFTER_LAST,
46 NUM_NLRTW_ATTR = __NLRTW_ATTR_AFTER_LAST,
47 NLRTW_ATTR_MAX = __NLRTW_ATTR_AFTER_LAST - 1
48 };
49
50 enum nlrtw_ch_util_attrs {
51 __NLRTW_ATTR_CHANNEL_UTILIZATION_INVALID,
52
53 NLRTW_ATTR_CHANNEL_UTILIZATION_VALUE,
54 NLRTW_ATTR_CHANNEL_UTILIZATION_BSSID,
55
56 __NLRTW_ATTR_CHANNEL_UTILIZATION_AFTER_LAST,
57 NUM_NLRTW_ATTR_CHANNEL_UTILIZATION = __NLRTW_ATTR_CHANNEL_UTILIZATION_AFTER_LAST,
58 NLRTW_ATTR_CHANNEL_UTILIZATION_MAX = __NLRTW_ATTR_CHANNEL_UTILIZATION_AFTER_LAST - 1
59 };
60
nlrtw_ch_util_set(struct sk_buff * skb,struct genl_info * info)61 static int nlrtw_ch_util_set(struct sk_buff *skb, struct genl_info *info)
62 {
63 unsigned int msg;
64
65 if (!info->attrs[NLRTW_ATTR_CHANNEL_UTILIZATION_THRESHOLD])
66 return -EINVAL;
67 msg = nla_get_u8(info->attrs[NLRTW_ATTR_CHANNEL_UTILIZATION_THRESHOLD]);
68
69 return 0;
70 }
71
72 static struct nla_policy nlrtw_genl_policy[NUM_NLRTW_ATTR] = {
73 [NLRTW_ATTR_CHANNEL_UTILIZATION_THRESHOLD] = { .type = NLA_U8 },
74 };
75
76 static struct genl_ops nlrtw_genl_ops[] = {
77 {
78 .cmd = NLRTW_CMD_CHANNEL_UTILIZATION,
79 .flags = 0,
80 .policy = nlrtw_genl_policy,
81 .doit = nlrtw_ch_util_set,
82 .dumpit = NULL,
83 },
84 };
85
86 enum nlrtw_multicast_groups {
87 NLRTW_MCGRP_DEFAULT,
88 };
89 static struct genl_multicast_group nlrtw_genl_mcgrp[] = {
90 [NLRTW_MCGRP_DEFAULT] = { .name = "nlrtw_default" },
91 };
92
93 /* family definition */
94 static struct genl_family nlrtw_genl_family = {
95 .hdrsize = 0,
96 .name = "nlrtw_"DRV_NAME,
97 .version = 1,
98 .maxattr = NLRTW_ATTR_MAX,
99
100 .netnsok = true,
101 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 12)
102 .module = THIS_MODULE,
103 #endif
104 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
105 .ops = nlrtw_genl_ops,
106 .n_ops = ARRAY_SIZE(nlrtw_genl_ops),
107 .mcgrps = nlrtw_genl_mcgrp,
108 .n_mcgrps = ARRAY_SIZE(nlrtw_genl_mcgrp),
109 #endif
110 };
111
nlrtw_multicast(const struct genl_family * family,struct sk_buff * skb,u32 portid,unsigned int group,gfp_t flags)112 static inline int nlrtw_multicast(const struct genl_family *family,
113 struct sk_buff *skb, u32 portid,
114 unsigned int group, gfp_t flags)
115 {
116 int ret;
117 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
118 ret = genlmsg_multicast(&nlrtw_genl_family, skb, portid, group, flags);
119 #else
120 ret = genlmsg_multicast(skb, portid, nlrtw_genl_mcgrp[group].id, flags);
121 #endif
122 return ret;
123 }
124
rtw_nlrtw_ch_util_rpt(_adapter * adapter,u8 n_rpts,u8 * val,u8 ** mac_addr)125 int rtw_nlrtw_ch_util_rpt(_adapter *adapter, u8 n_rpts, u8 *val, u8 **mac_addr)
126 {
127 struct sk_buff *skb = NULL;
128 void *msg_header = NULL;
129 struct nlattr *nl_ch_util, *nl_ch_utils;
130 struct wiphy *wiphy;
131 u8 i;
132 int ret;
133
134 wiphy = adapter_to_wiphy(adapter);
135 if (!wiphy)
136 return -EINVAL;
137
138 /* allocate memory */
139 skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
140 if (!skb) {
141 nlmsg_free(skb);
142 return -ENOMEM;
143 }
144
145 /* create the message headers */
146 msg_header = genlmsg_put(skb, 0, 0, &nlrtw_genl_family, 0,
147 NLRTW_CMD_CHANNEL_UTILIZATION);
148 if (!msg_header) {
149 ret = -ENOMEM;
150 goto err_out;
151 }
152
153 /* add attributes */
154 ret = nla_put_string(skb, NLRTW_ATTR_WIPHY_NAME, wiphy_name(wiphy));
155
156 nl_ch_utils = nla_nest_start(skb, NLRTW_ATTR_CHANNEL_UTILIZATIONS);
157 if (!nl_ch_utils) {
158 ret = -EMSGSIZE;
159 goto err_out;
160 }
161
162 for (i = 0; i < n_rpts; i++) {
163 nl_ch_util = nla_nest_start(skb, i);
164 if (!nl_ch_util) {
165 ret = -EMSGSIZE;
166 goto err_out;
167 }
168
169 ret = nla_put(skb, NLRTW_ATTR_CHANNEL_UTILIZATION_BSSID, ETH_ALEN, *(mac_addr + i));
170 if (ret != 0)
171 goto err_out;
172
173 ret = nla_put_u8(skb, NLRTW_ATTR_CHANNEL_UTILIZATION_VALUE, *(val + i));
174 if (ret != 0)
175 goto err_out;
176
177 nla_nest_end(skb, nl_ch_util);
178 }
179
180 nla_nest_end(skb, nl_ch_utils);
181
182 /* finalize the message */
183 genlmsg_end(skb, msg_header);
184
185 ret = nlrtw_multicast(&nlrtw_genl_family, skb, 0, NLRTW_MCGRP_DEFAULT, GFP_KERNEL);
186 if (ret == -ESRCH) {
187 RTW_INFO("[%s] return ESRCH(No such process)."
188 " Maybe no process waits for this msg\n", __func__);
189 return ret;
190 } else if (ret != 0) {
191 RTW_INFO("[%s] ret = %d\n", __func__, ret);
192 return ret;
193 }
194
195 return 0;
196 err_out:
197 nlmsg_free(skb);
198 return ret;
199 }
200
rtw_nlrtw_init(void)201 int rtw_nlrtw_init(void)
202 {
203 int err;
204
205 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
206 err = genl_register_family(&nlrtw_genl_family);
207 if (err)
208 return err;
209 #else
210 err = genl_register_family_with_ops(&nlrtw_genl_family, nlrtw_genl_ops, ARRAY_SIZE(nlrtw_genl_ops));
211 if (err)
212 return err;
213
214 err = genl_register_mc_group(&nlrtw_genl_family, &nlrtw_genl_mcgrp[0]);
215 if (err) {
216 genl_unregister_family(&nlrtw_genl_family);
217 return err;
218 }
219 #endif
220 RTW_INFO("[%s] %s\n", __func__, nlrtw_genl_family.name);
221 return 0;
222 }
223
rtw_nlrtw_deinit(void)224 int rtw_nlrtw_deinit(void)
225 {
226 int err;
227
228 err = genl_unregister_family(&nlrtw_genl_family);
229 RTW_INFO("[%s] err = %d\n", __func__, err);
230
231 return err;
232 }
233 #endif /* CONFIG_RTW_NLRTW */
234