cnss_utils.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2017, 2019, 2021 The Linux Foundation. All rights reserved.
  4. * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
  5. */
  6. #define pr_fmt(fmt) "cnss_utils: " fmt
  7. #include <linux/module.h>
  8. #include <linux/kernel.h>
  9. #include <linux/slab.h>
  10. #include <linux/etherdevice.h>
  11. #include <linux/debugfs.h>
  12. #include <linux/of.h>
  13. #ifdef CONFIG_CNSS_OUT_OF_TREE
  14. #include "cnss_utils.h"
  15. #else
  16. #include <net/cnss_utils.h>
  17. #endif
  18. #ifdef CONFIG_FEATURE_SMEM_MAILBOX
  19. #include <smem-mailbox.h>
  20. #endif
  21. #define CNSS_MAX_CH_NUM 157
  22. struct cnss_unsafe_channel_list {
  23. u16 unsafe_ch_count;
  24. u16 unsafe_ch_list[CNSS_MAX_CH_NUM];
  25. };
  26. struct cnss_dfs_nol_info {
  27. void *dfs_nol_info;
  28. u16 dfs_nol_info_len;
  29. };
  30. #define MAX_NO_OF_MAC_ADDR 4
  31. #define MAC_PREFIX_LEN 2
  32. struct cnss_wlan_mac_addr {
  33. u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN];
  34. u32 no_of_mac_addr_set;
  35. };
  36. enum mac_type {
  37. CNSS_MAC_PROVISIONED,
  38. CNSS_MAC_DERIVED,
  39. };
  40. static struct cnss_utils_priv {
  41. struct cnss_unsafe_channel_list unsafe_channel_list;
  42. struct cnss_dfs_nol_info dfs_nol_info;
  43. /* generic mutex for unsafe channel */
  44. struct mutex unsafe_channel_list_lock;
  45. /* generic spin-lock for dfs_nol info */
  46. spinlock_t dfs_nol_info_lock;
  47. int driver_load_cnt;
  48. struct cnss_wlan_mac_addr wlan_mac_addr;
  49. struct cnss_wlan_mac_addr wlan_der_mac_addr;
  50. enum cnss_utils_cc_src cc_source;
  51. struct dentry *root_dentry;
  52. /* generic mutex for device_id */
  53. struct mutex cnss_device_id_lock;
  54. enum cnss_utils_device_type cnss_device_type;
  55. #ifdef CONFIG_FEATURE_SMEM_MAILBOX
  56. bool smem_mailbox_initialized;
  57. int smem_mailbox_id;
  58. #endif
  59. } *cnss_utils_priv;
  60. int cnss_utils_set_wlan_unsafe_channel(struct device *dev,
  61. u16 *unsafe_ch_list, u16 ch_count)
  62. {
  63. struct cnss_utils_priv *priv = cnss_utils_priv;
  64. if (!priv)
  65. return -EINVAL;
  66. mutex_lock(&priv->unsafe_channel_list_lock);
  67. if (!unsafe_ch_list || ch_count > CNSS_MAX_CH_NUM) {
  68. mutex_unlock(&priv->unsafe_channel_list_lock);
  69. return -EINVAL;
  70. }
  71. priv->unsafe_channel_list.unsafe_ch_count = ch_count;
  72. if (ch_count == 0)
  73. goto end;
  74. memcpy(priv->unsafe_channel_list.unsafe_ch_list,
  75. unsafe_ch_list, ch_count * sizeof(u16));
  76. end:
  77. mutex_unlock(&priv->unsafe_channel_list_lock);
  78. return 0;
  79. }
  80. EXPORT_SYMBOL(cnss_utils_set_wlan_unsafe_channel);
  81. int cnss_utils_get_wlan_unsafe_channel(struct device *dev,
  82. u16 *unsafe_ch_list,
  83. u16 *ch_count, u16 buf_len)
  84. {
  85. struct cnss_utils_priv *priv = cnss_utils_priv;
  86. if (!priv)
  87. return -EINVAL;
  88. mutex_lock(&priv->unsafe_channel_list_lock);
  89. if (!unsafe_ch_list || !ch_count) {
  90. mutex_unlock(&priv->unsafe_channel_list_lock);
  91. return -EINVAL;
  92. }
  93. if (buf_len <
  94. (priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16))) {
  95. mutex_unlock(&priv->unsafe_channel_list_lock);
  96. return -ENOMEM;
  97. }
  98. *ch_count = priv->unsafe_channel_list.unsafe_ch_count;
  99. memcpy(unsafe_ch_list, priv->unsafe_channel_list.unsafe_ch_list,
  100. priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16));
  101. mutex_unlock(&priv->unsafe_channel_list_lock);
  102. return 0;
  103. }
  104. EXPORT_SYMBOL(cnss_utils_get_wlan_unsafe_channel);
  105. enum cnss_utils_device_type cnss_utils_update_device_type(
  106. enum cnss_utils_device_type device_type)
  107. {
  108. struct cnss_utils_priv *priv = cnss_utils_priv;
  109. if (!priv)
  110. return -EINVAL;
  111. mutex_lock(&priv->cnss_device_id_lock);
  112. pr_info("cnss_utils: device type:%d\n", device_type);
  113. if (priv->cnss_device_type == CNSS_UNSUPPORETD_DEVICE_TYPE) {
  114. priv->cnss_device_type = device_type;
  115. pr_info("cnss_utils: set device type:%d\n",
  116. priv->cnss_device_type);
  117. } else {
  118. pr_info("cnss_utils: device type already set :%d\n",
  119. priv->cnss_device_type);
  120. }
  121. mutex_unlock(&priv->cnss_device_id_lock);
  122. return priv->cnss_device_type;
  123. }
  124. EXPORT_SYMBOL(cnss_utils_update_device_type);
  125. int cnss_utils_wlan_set_dfs_nol(struct device *dev,
  126. const void *info, u16 info_len)
  127. {
  128. void *temp;
  129. void *old_nol_info;
  130. struct cnss_dfs_nol_info *dfs_info;
  131. struct cnss_utils_priv *priv = cnss_utils_priv;
  132. if (!priv)
  133. return -EINVAL;
  134. if (!info || !info_len)
  135. return -EINVAL;
  136. temp = kmemdup(info, info_len, GFP_ATOMIC);
  137. if (!temp)
  138. return -ENOMEM;
  139. spin_lock_bh(&priv->dfs_nol_info_lock);
  140. dfs_info = &priv->dfs_nol_info;
  141. old_nol_info = dfs_info->dfs_nol_info;
  142. dfs_info->dfs_nol_info = temp;
  143. dfs_info->dfs_nol_info_len = info_len;
  144. spin_unlock_bh(&priv->dfs_nol_info_lock);
  145. kfree(old_nol_info);
  146. return 0;
  147. }
  148. EXPORT_SYMBOL(cnss_utils_wlan_set_dfs_nol);
  149. int cnss_utils_wlan_get_dfs_nol(struct device *dev,
  150. void *info, u16 info_len)
  151. {
  152. int len;
  153. struct cnss_dfs_nol_info *dfs_info;
  154. struct cnss_utils_priv *priv = cnss_utils_priv;
  155. if (!priv)
  156. return -EINVAL;
  157. if (!info || !info_len)
  158. return -EINVAL;
  159. spin_lock_bh(&priv->dfs_nol_info_lock);
  160. dfs_info = &priv->dfs_nol_info;
  161. if (!dfs_info->dfs_nol_info ||
  162. dfs_info->dfs_nol_info_len == 0) {
  163. spin_unlock_bh(&priv->dfs_nol_info_lock);
  164. return -ENOENT;
  165. }
  166. len = min(info_len, dfs_info->dfs_nol_info_len);
  167. memcpy(info, dfs_info->dfs_nol_info, len);
  168. spin_unlock_bh(&priv->dfs_nol_info_lock);
  169. return len;
  170. }
  171. EXPORT_SYMBOL(cnss_utils_wlan_get_dfs_nol);
  172. void cnss_utils_increment_driver_load_cnt(struct device *dev)
  173. {
  174. struct cnss_utils_priv *priv = cnss_utils_priv;
  175. if (!priv)
  176. return;
  177. ++(priv->driver_load_cnt);
  178. }
  179. EXPORT_SYMBOL(cnss_utils_increment_driver_load_cnt);
  180. int cnss_utils_get_driver_load_cnt(struct device *dev)
  181. {
  182. struct cnss_utils_priv *priv = cnss_utils_priv;
  183. if (!priv)
  184. return -EINVAL;
  185. return priv->driver_load_cnt;
  186. }
  187. EXPORT_SYMBOL(cnss_utils_get_driver_load_cnt);
  188. static int set_wlan_mac_address(const u8 *mac_list, const uint32_t len,
  189. enum mac_type type)
  190. {
  191. struct cnss_utils_priv *priv = cnss_utils_priv;
  192. u32 no_of_mac_addr;
  193. struct cnss_wlan_mac_addr *addr = NULL;
  194. int iter;
  195. u8 *temp = NULL;
  196. if (!priv)
  197. return -EINVAL;
  198. if (len == 0 || (len % ETH_ALEN) != 0) {
  199. pr_err("Invalid length %d\n", len);
  200. return -EINVAL;
  201. }
  202. no_of_mac_addr = len / ETH_ALEN;
  203. if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) {
  204. pr_err("Exceed maximum supported MAC address %u %u\n",
  205. MAX_NO_OF_MAC_ADDR, no_of_mac_addr);
  206. return -EINVAL;
  207. }
  208. if (type == CNSS_MAC_PROVISIONED)
  209. addr = &priv->wlan_mac_addr;
  210. else
  211. addr = &priv->wlan_der_mac_addr;
  212. if (addr->no_of_mac_addr_set) {
  213. pr_err("WLAN MAC address is already set, num %d type %d\n",
  214. addr->no_of_mac_addr_set, type);
  215. return 0;
  216. }
  217. addr->no_of_mac_addr_set = no_of_mac_addr;
  218. temp = &addr->mac_addr[0][0];
  219. for (iter = 0; iter < no_of_mac_addr;
  220. ++iter, temp += ETH_ALEN, mac_list += ETH_ALEN) {
  221. ether_addr_copy(temp, mac_list);
  222. pr_debug("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
  223. temp[0], temp[1], temp[2],
  224. temp[3], temp[4], temp[5]);
  225. }
  226. return 0;
  227. }
  228. int cnss_utils_set_wlan_mac_address(const u8 *mac_list, const uint32_t len)
  229. {
  230. return set_wlan_mac_address(mac_list, len, CNSS_MAC_PROVISIONED);
  231. }
  232. EXPORT_SYMBOL(cnss_utils_set_wlan_mac_address);
  233. int cnss_utils_set_wlan_derived_mac_address(const u8 *mac_list,
  234. const uint32_t len)
  235. {
  236. return set_wlan_mac_address(mac_list, len, CNSS_MAC_DERIVED);
  237. }
  238. EXPORT_SYMBOL(cnss_utils_set_wlan_derived_mac_address);
  239. static u8 *get_wlan_mac_address(struct device *dev,
  240. u32 *num, enum mac_type type)
  241. {
  242. struct cnss_utils_priv *priv = cnss_utils_priv;
  243. struct cnss_wlan_mac_addr *addr = NULL;
  244. if (!priv)
  245. goto out;
  246. if (type == CNSS_MAC_PROVISIONED)
  247. addr = &priv->wlan_mac_addr;
  248. else
  249. addr = &priv->wlan_der_mac_addr;
  250. if (!addr->no_of_mac_addr_set) {
  251. pr_err("WLAN MAC address is not set, type %d\n", type);
  252. goto out;
  253. }
  254. *num = addr->no_of_mac_addr_set;
  255. return &addr->mac_addr[0][0];
  256. out:
  257. *num = 0;
  258. return NULL;
  259. }
  260. u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num)
  261. {
  262. return get_wlan_mac_address(dev, num, CNSS_MAC_PROVISIONED);
  263. }
  264. EXPORT_SYMBOL(cnss_utils_get_wlan_mac_address);
  265. u8 *cnss_utils_get_wlan_derived_mac_address(struct device *dev,
  266. uint32_t *num)
  267. {
  268. return get_wlan_mac_address(dev, num, CNSS_MAC_DERIVED);
  269. }
  270. EXPORT_SYMBOL(cnss_utils_get_wlan_derived_mac_address);
  271. void cnss_utils_set_cc_source(struct device *dev,
  272. enum cnss_utils_cc_src cc_source)
  273. {
  274. struct cnss_utils_priv *priv = cnss_utils_priv;
  275. if (!priv)
  276. return;
  277. priv->cc_source = cc_source;
  278. }
  279. EXPORT_SYMBOL(cnss_utils_set_cc_source);
  280. enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev)
  281. {
  282. struct cnss_utils_priv *priv = cnss_utils_priv;
  283. if (!priv)
  284. return -EINVAL;
  285. return priv->cc_source;
  286. }
  287. EXPORT_SYMBOL(cnss_utils_get_cc_source);
  288. #ifdef CONFIG_FEATURE_SMEM_MAILBOX
  289. int cnss_utils_smem_mailbox_write(struct device *dev, int flags,
  290. const __u8 *data, uint32_t len)
  291. {
  292. struct cnss_utils_priv *priv = cnss_utils_priv;
  293. if (!priv)
  294. return -EINVAL;
  295. if (!priv->smem_mailbox_initialized) {
  296. if (smem_mailbox_start(priv->smem_mailbox_id, NULL) != 1) {
  297. pr_err("Didn't init smem mailbox properly\n");
  298. return -EINVAL;
  299. } else
  300. priv->smem_mailbox_initialized = true;
  301. }
  302. return smem_mailbox_write(priv->smem_mailbox_id, flags, (__u8 *)data,
  303. len);
  304. }
  305. EXPORT_SYMBOL(cnss_utils_smem_mailbox_write);
  306. #endif
  307. static ssize_t cnss_utils_mac_write(struct file *fp,
  308. const char __user *user_buf,
  309. size_t count, loff_t *off)
  310. {
  311. struct cnss_utils_priv *priv =
  312. ((struct seq_file *)fp->private_data)->private;
  313. char buf[128];
  314. char *input, *mac_type, *mac_address;
  315. u8 *dest_mac;
  316. u8 val;
  317. const char *delim = "\n";
  318. size_t len = 0;
  319. char temp[3] = "";
  320. len = min_t(size_t, count, sizeof(buf) - 1);
  321. if (copy_from_user(buf, user_buf, len))
  322. return -EINVAL;
  323. buf[len] = '\0';
  324. input = buf;
  325. mac_type = strsep(&input, delim);
  326. if (!mac_type)
  327. return -EINVAL;
  328. if (!input)
  329. return -EINVAL;
  330. mac_address = strsep(&input, delim);
  331. if (!mac_address)
  332. return -EINVAL;
  333. if (strcmp("0x", mac_address)) {
  334. pr_err("Invalid MAC prefix\n");
  335. return -EINVAL;
  336. }
  337. len = strlen(mac_address);
  338. mac_address += MAC_PREFIX_LEN;
  339. len -= MAC_PREFIX_LEN;
  340. if (len < ETH_ALEN * 2 || len > ETH_ALEN * 2 * MAX_NO_OF_MAC_ADDR ||
  341. len % (ETH_ALEN * 2) != 0) {
  342. pr_err("Invalid MAC address length %zu\n", len);
  343. return -EINVAL;
  344. }
  345. if (!strcmp("provisioned", mac_type)) {
  346. dest_mac = &priv->wlan_mac_addr.mac_addr[0][0];
  347. priv->wlan_mac_addr.no_of_mac_addr_set = len / (ETH_ALEN * 2);
  348. } else if (!strcmp("derived", mac_type)) {
  349. dest_mac = &priv->wlan_der_mac_addr.mac_addr[0][0];
  350. priv->wlan_der_mac_addr.no_of_mac_addr_set =
  351. len / (ETH_ALEN * 2);
  352. } else {
  353. pr_err("Invalid MAC address type %s\n", mac_type);
  354. return -EINVAL;
  355. }
  356. while (len--) {
  357. temp[0] = *mac_address++;
  358. temp[1] = *mac_address++;
  359. if (kstrtou8(temp, 16, &val))
  360. return -EINVAL;
  361. *dest_mac++ = val;
  362. }
  363. return count;
  364. }
  365. static int cnss_utils_mac_show(struct seq_file *s, void *data)
  366. {
  367. u8 mac[6];
  368. int i;
  369. struct cnss_utils_priv *priv = s->private;
  370. struct cnss_wlan_mac_addr *addr = NULL;
  371. addr = &priv->wlan_mac_addr;
  372. if (addr->no_of_mac_addr_set) {
  373. seq_puts(s, "\nProvisioned MAC addresseses\n");
  374. for (i = 0; i < addr->no_of_mac_addr_set; i++) {
  375. ether_addr_copy(mac, addr->mac_addr[i]);
  376. seq_printf(s, "MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
  377. mac[0], mac[1], mac[2],
  378. mac[3], mac[4], mac[5]);
  379. }
  380. }
  381. addr = &priv->wlan_der_mac_addr;
  382. if (addr->no_of_mac_addr_set) {
  383. seq_puts(s, "\nDerived MAC addresseses\n");
  384. for (i = 0; i < addr->no_of_mac_addr_set; i++) {
  385. ether_addr_copy(mac, addr->mac_addr[i]);
  386. seq_printf(s, "MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
  387. mac[0], mac[1], mac[2],
  388. mac[3], mac[4], mac[5]);
  389. }
  390. }
  391. return 0;
  392. }
  393. static int cnss_utils_mac_open(struct inode *inode, struct file *file)
  394. {
  395. return single_open(file, cnss_utils_mac_show, inode->i_private);
  396. }
  397. static const struct file_operations cnss_utils_mac_fops = {
  398. .read = seq_read,
  399. .write = cnss_utils_mac_write,
  400. .release = single_release,
  401. .open = cnss_utils_mac_open,
  402. .owner = THIS_MODULE,
  403. .llseek = seq_lseek,
  404. };
  405. static int cnss_utils_debugfs_create(struct cnss_utils_priv *priv)
  406. {
  407. int ret = 0;
  408. struct dentry *root_dentry;
  409. root_dentry = debugfs_create_dir("cnss_utils", NULL);
  410. if (IS_ERR(root_dentry)) {
  411. ret = PTR_ERR(root_dentry);
  412. pr_err("Unable to create debugfs %d\n", ret);
  413. goto out;
  414. }
  415. priv->root_dentry = root_dentry;
  416. debugfs_create_file("mac_address", 0600, root_dentry, priv,
  417. &cnss_utils_mac_fops);
  418. out:
  419. return ret;
  420. }
  421. /**
  422. * cnss_utils_is_valid_dt_node_found - Check if valid device tree node present
  423. *
  424. * Valid device tree node means a node with "qcom,wlan" property present and
  425. * "status" property not disabled.
  426. *
  427. * Return: true if valid device tree node found, false if not found
  428. */
  429. static bool cnss_utils_is_valid_dt_node_found(void)
  430. {
  431. struct device_node *dn = NULL;
  432. for_each_node_with_property(dn, "qcom,wlan") {
  433. if (of_device_is_available(dn))
  434. break;
  435. }
  436. if (dn)
  437. return true;
  438. return false;
  439. }
  440. #ifdef CONFIG_FEATURE_SMEM_MAILBOX
  441. static void cnss_utils_smem_mailbox_init(void)
  442. {
  443. struct cnss_utils_priv *priv = cnss_utils_priv;
  444. priv->smem_mailbox_id = 0;
  445. priv->smem_mailbox_initialized = false;
  446. }
  447. static void cnss_utils_smem_mailbox_deinit(void)
  448. {
  449. struct cnss_utils_priv *priv = cnss_utils_priv;
  450. smem_mailbox_stop(priv->smem_mailbox_id);
  451. }
  452. #else
  453. static void cnss_utils_smem_mailbox_init(void)
  454. {
  455. }
  456. static void cnss_utils_smem_mailbox_deinit(void)
  457. {
  458. }
  459. #endif
  460. static int __init cnss_utils_init(void)
  461. {
  462. struct cnss_utils_priv *priv = NULL;
  463. if (!cnss_utils_is_valid_dt_node_found())
  464. return -ENODEV;
  465. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  466. if (!priv)
  467. return -ENOMEM;
  468. priv->cc_source = CNSS_UTILS_SOURCE_CORE;
  469. priv->cnss_device_type = CNSS_UNSUPPORETD_DEVICE_TYPE;
  470. mutex_init(&priv->unsafe_channel_list_lock);
  471. mutex_init(&priv->cnss_device_id_lock);
  472. spin_lock_init(&priv->dfs_nol_info_lock);
  473. cnss_utils_debugfs_create(priv);
  474. cnss_utils_priv = priv;
  475. cnss_utils_smem_mailbox_init();
  476. return 0;
  477. }
  478. static void __exit cnss_utils_exit(void)
  479. {
  480. cnss_utils_smem_mailbox_deinit();
  481. kfree(cnss_utils_priv);
  482. cnss_utils_priv = NULL;
  483. }
  484. module_init(cnss_utils_init);
  485. module_exit(cnss_utils_exit);
  486. MODULE_LICENSE("GPL v2");
  487. MODULE_DESCRIPTION("CNSS Utilities Driver");