wl1251_netlink.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. /*
  2. * This file is part of wl1251
  3. *
  4. * Copyright (C) 2008 Nokia Corporation
  5. *
  6. * Contact: Kalle Valo <kalle.valo@nokia.com>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * version 2 as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA
  21. *
  22. */
  23. #include "wl1251_netlink.h"
  24. #include <linux/mutex.h>
  25. #include <linux/socket.h>
  26. #include <net/net_namespace.h>
  27. #include <net/sock.h>
  28. #include <net/genetlink.h>
  29. #include <net/wireless.h>
  30. #include <net/mac80211.h>
  31. #include "wl1251.h"
  32. #include "wl1251_spi.h"
  33. #include "wl1251_acx.h"
  34. /* FIXME: this should be changed as soon as user space catches up */
  35. #define WL1251_NL_NAME "wl1251"
  36. #define WL1251_NL_VERSION 1
  37. #define WL1251_MAX_TEST_LENGTH 1024
  38. #define WL1251_MAX_NVS_LENGTH 1024
  39. enum wl1251_nl_commands {
  40. WL1251_NL_CMD_UNSPEC,
  41. WL1251_NL_CMD_TEST,
  42. WL1251_NL_CMD_INTERROGATE,
  43. WL1251_NL_CMD_CONFIGURE,
  44. WL1251_NL_CMD_PHY_REG_READ,
  45. WL1251_NL_CMD_NVS_PUSH,
  46. WL1251_NL_CMD_REG_WRITE,
  47. WL1251_NL_CMD_REG_READ,
  48. WL1251_NL_CMD_SET_PLT_MODE,
  49. __WL1251_NL_CMD_AFTER_LAST
  50. };
  51. #define WL1251_NL_CMD_MAX (__WL1251_NL_CMD_AFTER_LAST - 1)
  52. enum wl1251_nl_attrs {
  53. WL1251_NL_ATTR_UNSPEC,
  54. WL1251_NL_ATTR_IFNAME,
  55. WL1251_NL_ATTR_CMD_TEST_PARAM,
  56. WL1251_NL_ATTR_CMD_TEST_ANSWER,
  57. WL1251_NL_ATTR_CMD_IE,
  58. WL1251_NL_ATTR_CMD_IE_LEN,
  59. WL1251_NL_ATTR_CMD_IE_BUFFER,
  60. WL1251_NL_ATTR_CMD_IE_ANSWER,
  61. WL1251_NL_ATTR_REG_ADDR,
  62. WL1251_NL_ATTR_REG_VAL,
  63. WL1251_NL_ATTR_NVS_BUFFER,
  64. WL1251_NL_ATTR_NVS_LEN,
  65. WL1251_NL_ATTR_PLT_MODE,
  66. __WL1251_NL_ATTR_AFTER_LAST
  67. };
  68. #define WL1251_NL_ATTR_MAX (__WL1251_NL_ATTR_AFTER_LAST - 1)
  69. static struct genl_family wl1251_nl_family = {
  70. .id = GENL_ID_GENERATE,
  71. .name = WL1251_NL_NAME,
  72. .hdrsize = 0,
  73. .version = WL1251_NL_VERSION,
  74. .maxattr = WL1251_NL_ATTR_MAX,
  75. };
  76. static struct net_device *ifname_to_netdev(struct net *net,
  77. struct genl_info *info)
  78. {
  79. char *ifname;
  80. if (!info->attrs[WL1251_NL_ATTR_IFNAME])
  81. return NULL;
  82. ifname = nla_data(info->attrs[WL1251_NL_ATTR_IFNAME]);
  83. wl1251_debug(DEBUG_NETLINK, "Looking for %s", ifname);
  84. return dev_get_by_name(net, ifname);
  85. }
  86. static struct wl1251 *ifname_to_wl1251(struct net *net, struct genl_info *info)
  87. {
  88. struct net_device *netdev;
  89. struct wireless_dev *wdev;
  90. struct wiphy *wiphy;
  91. struct ieee80211_hw *hw;
  92. netdev = ifname_to_netdev(net, info);
  93. if (netdev == NULL) {
  94. wl1251_error("Wrong interface");
  95. return NULL;
  96. }
  97. wdev = netdev->ieee80211_ptr;
  98. if (wdev == NULL) {
  99. wl1251_error("ieee80211_ptr is NULL");
  100. return NULL;
  101. }
  102. wiphy = wdev->wiphy;
  103. if (wiphy == NULL) {
  104. wl1251_error("wiphy is NULL");
  105. return NULL;
  106. }
  107. hw = wiphy_priv(wiphy);
  108. if (hw == NULL) {
  109. wl1251_error("hw is NULL");
  110. return NULL;
  111. }
  112. dev_put(netdev);
  113. return hw->priv;
  114. }
  115. static int wl1251_nl_test_cmd(struct sk_buff *skb, struct genl_info *info)
  116. {
  117. struct wl1251 *wl;
  118. struct wl1251_command *cmd;
  119. char *buf;
  120. int buf_len, ret, cmd_len;
  121. u8 answer;
  122. if (!info->attrs[WL1251_NL_ATTR_CMD_TEST_PARAM])
  123. return -EINVAL;
  124. wl = ifname_to_wl1251(&init_net, info);
  125. if (wl == NULL) {
  126. wl1251_error("wl1251 not found");
  127. return -EINVAL;
  128. }
  129. cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
  130. if (!cmd)
  131. return -ENOMEM;
  132. buf = nla_data(info->attrs[WL1251_NL_ATTR_CMD_TEST_PARAM]);
  133. buf_len = nla_len(info->attrs[WL1251_NL_ATTR_CMD_TEST_PARAM]);
  134. answer = nla_get_u8(info->attrs[WL1251_NL_ATTR_CMD_TEST_ANSWER]);
  135. cmd->header.id = CMD_TEST;
  136. memcpy(cmd->parameters, buf, buf_len);
  137. cmd_len = sizeof(struct wl1251_cmd_header) + buf_len;
  138. mutex_lock(&wl->mutex);
  139. ret = wl1251_cmd_test(wl, cmd, cmd_len, answer);
  140. mutex_unlock(&wl->mutex);
  141. if (ret < 0) {
  142. wl1251_error("%s() failed", __func__);
  143. goto out;
  144. }
  145. if (answer) {
  146. struct sk_buff *msg;
  147. void *hdr;
  148. msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
  149. if (!msg) {
  150. ret = -ENOMEM;
  151. goto out;
  152. }
  153. hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
  154. &wl1251_nl_family, 0, WL1251_NL_CMD_TEST);
  155. if (IS_ERR(hdr)) {
  156. ret = PTR_ERR(hdr);
  157. goto nla_put_failure;
  158. }
  159. NLA_PUT_STRING(msg, WL1251_NL_ATTR_IFNAME,
  160. nla_data(info->attrs[WL1251_NL_ATTR_IFNAME]));
  161. NLA_PUT(msg, WL1251_NL_ATTR_CMD_TEST_ANSWER,
  162. sizeof(*cmd), cmd);
  163. ret = genlmsg_end(msg, hdr);
  164. if (ret < 0) {
  165. wl1251_error("%s() failed", __func__);
  166. goto nla_put_failure;
  167. }
  168. wl1251_debug(DEBUG_NETLINK, "TEST cmd sent, answer");
  169. ret = genlmsg_reply(msg, info);
  170. goto out;
  171. nla_put_failure:
  172. nlmsg_free(msg);
  173. } else
  174. wl1251_debug(DEBUG_NETLINK, "TEST cmd sent");
  175. out:
  176. kfree(cmd);
  177. return ret;
  178. }
  179. static int wl1251_nl_interrogate(struct sk_buff *skb, struct genl_info *info)
  180. {
  181. struct wl1251 *wl;
  182. struct sk_buff *msg;
  183. int ret = -ENOBUFS, cmd_ie, cmd_ie_len;
  184. struct wl1251_command *cmd;
  185. void *hdr;
  186. if (!info->attrs[WL1251_NL_ATTR_CMD_IE])
  187. return -EINVAL;
  188. if (!info->attrs[WL1251_NL_ATTR_CMD_IE_LEN])
  189. return -EINVAL;
  190. cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
  191. if (!cmd)
  192. return -ENOMEM;
  193. msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
  194. if (!msg)
  195. return -ENOMEM;
  196. wl = ifname_to_wl1251(&init_net, info);
  197. if (wl == NULL) {
  198. wl1251_error("wl1251 not found");
  199. ret = -EINVAL;
  200. goto nla_put_failure;
  201. }
  202. /* acx id */
  203. cmd_ie = nla_get_u32(info->attrs[WL1251_NL_ATTR_CMD_IE]);
  204. /* maximum length of acx, including all headers */
  205. cmd_ie_len = nla_get_u32(info->attrs[WL1251_NL_ATTR_CMD_IE_LEN]);
  206. wl1251_debug(DEBUG_NETLINK, "Getting IE 0x%x (len %d)",
  207. cmd_ie, cmd_ie_len);
  208. mutex_lock(&wl->mutex);
  209. ret = wl1251_cmd_interrogate(wl, cmd_ie, cmd, cmd_ie_len);
  210. mutex_unlock(&wl->mutex);
  211. if (ret < 0) {
  212. wl1251_error("%s() failed", __func__);
  213. goto nla_put_failure;
  214. }
  215. hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
  216. &wl1251_nl_family, 0, WL1251_NL_CMD_INTERROGATE);
  217. if (IS_ERR(hdr)) {
  218. ret = PTR_ERR(hdr);
  219. goto nla_put_failure;
  220. }
  221. NLA_PUT_STRING(msg, WL1251_NL_ATTR_IFNAME,
  222. nla_data(info->attrs[WL1251_NL_ATTR_IFNAME]));
  223. NLA_PUT(msg, WL1251_NL_ATTR_CMD_IE_ANSWER, cmd_ie_len, cmd);
  224. ret = genlmsg_end(msg, hdr);
  225. if (ret < 0) {
  226. wl1251_error("%s() failed", __func__);
  227. goto nla_put_failure;
  228. }
  229. kfree(cmd);
  230. return genlmsg_reply(msg, info);
  231. nla_put_failure:
  232. kfree(cmd);
  233. nlmsg_free(msg);
  234. return ret;
  235. }
  236. static int wl1251_nl_configure(struct sk_buff *skb, struct genl_info *info)
  237. {
  238. int ret = 0, cmd_ie_len, acx_len;
  239. struct acx_header *acx = NULL;
  240. struct sk_buff *msg;
  241. struct wl1251 *wl;
  242. void *cmd_ie;
  243. u16 *id;
  244. if (!info->attrs[WL1251_NL_ATTR_CMD_IE_BUFFER])
  245. return -EINVAL;
  246. if (!info->attrs[WL1251_NL_ATTR_CMD_IE_LEN])
  247. return -EINVAL;
  248. msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
  249. if (!msg)
  250. return -ENOMEM;
  251. wl = ifname_to_wl1251(&init_net, info);
  252. if (wl == NULL) {
  253. wl1251_error("wl1251 not found");
  254. ret = -EINVAL;
  255. goto nla_put_failure;
  256. }
  257. /* contains the acx header but not the cmd header */
  258. cmd_ie = nla_data(info->attrs[WL1251_NL_ATTR_CMD_IE_BUFFER]);
  259. cmd_ie_len = nla_get_u32(info->attrs[WL1251_NL_ATTR_CMD_IE_LEN]);
  260. /* acx id is in the first two bytes */
  261. id = cmd_ie;
  262. /* need to add acx_header before cmd_ie, so create a new command */
  263. acx_len = sizeof(struct acx_header) + cmd_ie_len;
  264. acx = kzalloc(acx_len, GFP_KERNEL);
  265. if (!acx) {
  266. ret = -ENOMEM;
  267. goto nla_put_failure;
  268. }
  269. /* copy the acx header and the payload */
  270. memcpy(&acx->id, cmd_ie, cmd_ie_len);
  271. mutex_lock(&wl->mutex);
  272. ret = wl1251_cmd_configure(wl, *id, acx, acx_len);
  273. mutex_unlock(&wl->mutex);
  274. if (ret < 0) {
  275. wl1251_error("%s() failed", __func__);
  276. goto nla_put_failure;
  277. }
  278. wl1251_debug(DEBUG_NETLINK, "CONFIGURE cmd sent");
  279. nla_put_failure:
  280. kfree(acx);
  281. nlmsg_free(msg);
  282. return ret;
  283. }
  284. static int wl1251_nl_phy_reg_read(struct sk_buff *skb, struct genl_info *info)
  285. {
  286. struct wl1251 *wl;
  287. struct sk_buff *msg;
  288. u32 reg_addr, *reg_value = NULL;
  289. int ret = 0;
  290. void *hdr;
  291. if (!info->attrs[WL1251_NL_ATTR_REG_ADDR])
  292. return -EINVAL;
  293. msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
  294. if (!msg)
  295. return -ENOMEM;
  296. wl = ifname_to_wl1251(&init_net, info);
  297. if (wl == NULL) {
  298. wl1251_error("wl1251 not found");
  299. ret = -EINVAL;
  300. goto nla_put_failure;
  301. }
  302. reg_value = kmalloc(sizeof(*reg_value), GFP_KERNEL);
  303. if (!reg_value) {
  304. ret = -ENOMEM;
  305. goto nla_put_failure;
  306. }
  307. reg_addr = nla_get_u32(info->attrs[WL1251_NL_ATTR_REG_ADDR]);
  308. wl1251_debug(DEBUG_NETLINK, "Reading PHY reg 0x%x", reg_addr);
  309. mutex_lock(&wl->mutex);
  310. ret = wl1251_cmd_read_memory(wl, reg_addr, reg_value,
  311. sizeof(*reg_value));
  312. mutex_unlock(&wl->mutex);
  313. if (ret < 0) {
  314. wl1251_error("%s() failed", __func__);
  315. goto nla_put_failure;
  316. }
  317. hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
  318. &wl1251_nl_family, 0, WL1251_NL_CMD_PHY_REG_READ);
  319. if (IS_ERR(hdr)) {
  320. ret = PTR_ERR(hdr);
  321. goto nla_put_failure;
  322. }
  323. NLA_PUT_STRING(msg, WL1251_NL_ATTR_IFNAME,
  324. nla_data(info->attrs[WL1251_NL_ATTR_IFNAME]));
  325. NLA_PUT_U32(msg, WL1251_NL_ATTR_REG_VAL, *reg_value);
  326. ret = genlmsg_end(msg, hdr);
  327. if (ret < 0) {
  328. wl1251_error("%s() failed", __func__);
  329. goto nla_put_failure;
  330. }
  331. kfree(reg_value);
  332. return genlmsg_reply(msg, info);
  333. nla_put_failure:
  334. nlmsg_free(msg);
  335. kfree(reg_value);
  336. return ret;
  337. }
  338. static int wl1251_nl_nvs_push(struct sk_buff *skb, struct genl_info *info)
  339. {
  340. struct wl1251 *wl;
  341. int ret = 0;
  342. if (!info->attrs[WL1251_NL_ATTR_NVS_BUFFER])
  343. return -EINVAL;
  344. if (!info->attrs[WL1251_NL_ATTR_NVS_LEN])
  345. return -EINVAL;
  346. wl = ifname_to_wl1251(&init_net, info);
  347. if (wl == NULL) {
  348. wl1251_error("wl1251 not found");
  349. return -EINVAL;
  350. }
  351. mutex_lock(&wl->mutex);
  352. wl->nvs_len = nla_get_u32(info->attrs[WL1251_NL_ATTR_NVS_LEN]);
  353. if (wl->nvs_len % 4) {
  354. wl1251_error("NVS size is not multiple of 32: %d", wl->nvs_len);
  355. ret = -EILSEQ;
  356. goto out;
  357. }
  358. /* If we already have an NVS, we should free it */
  359. kfree(wl->nvs);
  360. wl->nvs = kzalloc(wl->nvs_len, GFP_KERNEL);
  361. if (wl->nvs == NULL) {
  362. wl1251_error("Can't allocate NVS");
  363. ret = -ENOMEM;
  364. goto out;
  365. }
  366. memcpy(wl->nvs,
  367. nla_data(info->attrs[WL1251_NL_ATTR_NVS_BUFFER]),
  368. wl->nvs_len);
  369. wl1251_debug(DEBUG_NETLINK, "got NVS from userspace, %d bytes",
  370. wl->nvs_len);
  371. out:
  372. mutex_unlock(&wl->mutex);
  373. return ret;
  374. }
  375. static int wl1251_nl_reg_read(struct sk_buff *skb, struct genl_info *info)
  376. {
  377. struct wl1251 *wl;
  378. u32 addr, val;
  379. int ret = 0;
  380. struct sk_buff *msg;
  381. void *hdr;
  382. if (!info->attrs[WL1251_NL_ATTR_REG_ADDR])
  383. return -EINVAL;
  384. msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
  385. if (!msg)
  386. return -ENOMEM;
  387. wl = ifname_to_wl1251(&init_net, info);
  388. if (wl == NULL) {
  389. wl1251_error("wl1251 not found");
  390. return -EINVAL;
  391. }
  392. addr = nla_get_u32(info->attrs[WL1251_NL_ATTR_REG_ADDR]);
  393. mutex_lock(&wl->mutex);
  394. val = wl1251_reg_read32(wl, addr);
  395. mutex_unlock(&wl->mutex);
  396. hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
  397. &wl1251_nl_family, 0, WL1251_NL_CMD_PHY_REG_READ);
  398. if (IS_ERR(hdr)) {
  399. ret = PTR_ERR(hdr);
  400. goto nla_put_failure;
  401. }
  402. NLA_PUT_STRING(msg, WL1251_NL_ATTR_IFNAME,
  403. nla_data(info->attrs[WL1251_NL_ATTR_IFNAME]));
  404. NLA_PUT_U32(msg, WL1251_NL_ATTR_REG_VAL, val);
  405. ret = genlmsg_end(msg, hdr);
  406. if (ret < 0) {
  407. wl1251_error("%s() failed", __func__);
  408. goto nla_put_failure;
  409. }
  410. return genlmsg_reply(msg, info);
  411. nla_put_failure:
  412. nlmsg_free(msg);
  413. return ret;
  414. }
  415. static int wl1251_nl_reg_write(struct sk_buff *skb, struct genl_info *info)
  416. {
  417. struct wl1251 *wl;
  418. u32 addr, val;
  419. if (!info->attrs[WL1251_NL_ATTR_REG_ADDR])
  420. return -EINVAL;
  421. if (!info->attrs[WL1251_NL_ATTR_REG_VAL])
  422. return -EINVAL;
  423. wl = ifname_to_wl1251(&init_net, info);
  424. if (wl == NULL) {
  425. wl1251_error("wl1251 not found");
  426. return -EINVAL;
  427. }
  428. addr = nla_get_u32(info->attrs[WL1251_NL_ATTR_REG_ADDR]);
  429. val = nla_get_u32(info->attrs[WL1251_NL_ATTR_REG_VAL]);
  430. mutex_lock(&wl->mutex);
  431. wl1251_reg_write32(wl, addr, val);
  432. mutex_unlock(&wl->mutex);
  433. return 0;
  434. }
  435. static int wl1251_nl_set_plt_mode(struct sk_buff *skb, struct genl_info *info)
  436. {
  437. struct wl1251 *wl;
  438. u32 val;
  439. int ret;
  440. if (!info->attrs[WL1251_NL_ATTR_PLT_MODE])
  441. return -EINVAL;
  442. wl = ifname_to_wl1251(&init_net, info);
  443. if (wl == NULL) {
  444. wl1251_error("wl1251 not found");
  445. return -EINVAL;
  446. }
  447. val = nla_get_u32(info->attrs[WL1251_NL_ATTR_PLT_MODE]);
  448. switch (val) {
  449. case 0:
  450. ret = wl1251_plt_stop(wl);
  451. break;
  452. case 1:
  453. ret = wl1251_plt_start(wl);
  454. break;
  455. default:
  456. ret = -EINVAL;
  457. break;
  458. }
  459. return ret;
  460. }
  461. static struct nla_policy wl1251_nl_policy[WL1251_NL_ATTR_MAX + 1] = {
  462. [WL1251_NL_ATTR_IFNAME] = { .type = NLA_NUL_STRING,
  463. .len = IFNAMSIZ-1 },
  464. [WL1251_NL_ATTR_CMD_TEST_PARAM] = { .type = NLA_BINARY,
  465. .len = WL1251_MAX_TEST_LENGTH },
  466. [WL1251_NL_ATTR_CMD_TEST_ANSWER] = { .type = NLA_U8 },
  467. [WL1251_NL_ATTR_CMD_IE] = { .type = NLA_U32 },
  468. [WL1251_NL_ATTR_CMD_IE_LEN] = { .type = NLA_U32 },
  469. [WL1251_NL_ATTR_CMD_IE_BUFFER] = { .type = NLA_BINARY,
  470. .len = WL1251_MAX_TEST_LENGTH },
  471. [WL1251_NL_ATTR_CMD_IE_ANSWER] = { .type = NLA_BINARY,
  472. .len = WL1251_MAX_TEST_LENGTH },
  473. [WL1251_NL_ATTR_REG_ADDR] = { .type = NLA_U32 },
  474. [WL1251_NL_ATTR_REG_VAL] = { .type = NLA_U32 },
  475. [WL1251_NL_ATTR_NVS_BUFFER] = { .type = NLA_BINARY,
  476. .len = WL1251_MAX_NVS_LENGTH },
  477. [WL1251_NL_ATTR_NVS_LEN] = { .type = NLA_U32 },
  478. [WL1251_NL_ATTR_PLT_MODE] = { .type = NLA_U32 },
  479. };
  480. static struct genl_ops wl1251_nl_ops[] = {
  481. {
  482. .cmd = WL1251_NL_CMD_TEST,
  483. .doit = wl1251_nl_test_cmd,
  484. .policy = wl1251_nl_policy,
  485. .flags = GENL_ADMIN_PERM,
  486. },
  487. {
  488. .cmd = WL1251_NL_CMD_INTERROGATE,
  489. .doit = wl1251_nl_interrogate,
  490. .policy = wl1251_nl_policy,
  491. .flags = GENL_ADMIN_PERM,
  492. },
  493. {
  494. .cmd = WL1251_NL_CMD_CONFIGURE,
  495. .doit = wl1251_nl_configure,
  496. .policy = wl1251_nl_policy,
  497. .flags = GENL_ADMIN_PERM,
  498. },
  499. {
  500. .cmd = WL1251_NL_CMD_PHY_REG_READ,
  501. .doit = wl1251_nl_phy_reg_read,
  502. .policy = wl1251_nl_policy,
  503. .flags = GENL_ADMIN_PERM,
  504. },
  505. {
  506. .cmd = WL1251_NL_CMD_NVS_PUSH,
  507. .doit = wl1251_nl_nvs_push,
  508. .policy = wl1251_nl_policy,
  509. .flags = GENL_ADMIN_PERM,
  510. },
  511. {
  512. .cmd = WL1251_NL_CMD_REG_WRITE,
  513. .doit = wl1251_nl_reg_write,
  514. .policy = wl1251_nl_policy,
  515. .flags = GENL_ADMIN_PERM,
  516. },
  517. {
  518. .cmd = WL1251_NL_CMD_REG_READ,
  519. .doit = wl1251_nl_reg_read,
  520. .policy = wl1251_nl_policy,
  521. .flags = GENL_ADMIN_PERM,
  522. },
  523. {
  524. .cmd = WL1251_NL_CMD_SET_PLT_MODE,
  525. .doit = wl1251_nl_set_plt_mode,
  526. .policy = wl1251_nl_policy,
  527. .flags = GENL_ADMIN_PERM,
  528. },
  529. };
  530. int wl1251_nl_register(void)
  531. {
  532. int err, i;
  533. err = genl_register_family(&wl1251_nl_family);
  534. if (err)
  535. return err;
  536. for (i = 0; i < ARRAY_SIZE(wl1251_nl_ops); i++) {
  537. err = genl_register_ops(&wl1251_nl_family, &wl1251_nl_ops[i]);
  538. if (err)
  539. goto err_out;
  540. }
  541. return 0;
  542. err_out:
  543. genl_unregister_family(&wl1251_nl_family);
  544. return err;
  545. }
  546. void wl1251_nl_unregister(void)
  547. {
  548. genl_unregister_family(&wl1251_nl_family);
  549. }