ath_procfs.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. /*
  2. * Copyright (c) 2013-2014, 2016-2021 The Linux Foundation. All rights reserved.
  3. * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for
  6. * any purpose with or without fee is hereby granted, provided that the
  7. * above copyright notice and this permission notice appear in all
  8. * copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
  11. * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
  12. * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
  13. * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  14. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  15. * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  16. * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  17. * PERFORMANCE OF THIS SOFTWARE.
  18. */
  19. #if defined(CONFIG_ATH_PROCFS_DIAG_SUPPORT)
  20. #include <linux/module.h> /* Specifically, a module */
  21. #include <linux/kernel.h> /* We're doing kernel work */
  22. #include <linux/version.h> /* We're doing kernel work */
  23. #include <linux/proc_fs.h> /* Necessary because we use the proc fs */
  24. #include <linux/uaccess.h> /* for copy_from_user */
  25. #include "hif.h"
  26. #include "hif_main.h"
  27. #if defined(HIF_USB)
  28. #include "if_usb.h"
  29. #endif
  30. #if defined(HIF_SDIO)
  31. #include "if_sdio.h"
  32. #endif
  33. #include "hif_debug.h"
  34. #include "pld_common.h"
  35. #include "target_type.h"
  36. #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 17, 0))
  37. /*
  38. * Commit 359745d78351 ("proc: remove PDE_DATA() completely")
  39. * Replaced PDE_DATA() with pde_data()
  40. */
  41. #define pde_data(inode) PDE_DATA(inode)
  42. #endif
  43. #define PROCFS_NAME "athdiagpfs"
  44. #ifdef MULTI_IF_NAME
  45. #define PROCFS_DIR "cld" MULTI_IF_NAME
  46. #else
  47. #define PROCFS_DIR "cld"
  48. #endif
  49. /*
  50. * Get op_type, mem_type and offset fields from pos of procfs
  51. * It will reuse pos, which is long long type
  52. *
  53. * op_type: 4 bits
  54. * memtype: 8 bits
  55. * reserve1: 20 bits
  56. * offset: 32 bits
  57. */
  58. #define OP_TYPE_LEGACY 0
  59. #define OP_TYPE_EXT_QMI 1
  60. #define OP_TYPE_EXT_DIRECT 2
  61. #define ATH_DIAG_EXT_OP_TYPE_BITS 4
  62. #define ATH_DIAG_EXT_OP_TYPE_INDEX 60
  63. #define ATH_DIAG_EXT_MEM_TYPE_BITS 8
  64. #define ATH_DIAG_EXT_MEM_TYPE_INDEX 52
  65. #define ATH_DIAG_EXT_OFFSET_BITS 32
  66. #define ATH_DIAG_EXT_OFFSET_INDEX 0
  67. /*
  68. * This structure hold information about the /proc file
  69. *
  70. */
  71. static struct proc_dir_entry *proc_file, *proc_dir;
  72. static void *get_hif_hdl_from_file(struct file *file)
  73. {
  74. struct hif_opaque_softc *scn;
  75. scn = (struct hif_opaque_softc *)pde_data(file_inode(file));
  76. return (void *)scn;
  77. }
  78. static ssize_t ath_procfs_diag_read_legacy(struct file *file,
  79. char __user *buf,
  80. size_t count, loff_t *pos)
  81. {
  82. hif_handle_t hif_hdl;
  83. int rv;
  84. uint8_t *read_buffer = NULL;
  85. struct hif_softc *scn;
  86. uint32_t offset = 0, memtype = 0;
  87. struct hif_target_info *tgt_info;
  88. hif_hdl = get_hif_hdl_from_file(file);
  89. scn = HIF_GET_SOFTC(hif_hdl);
  90. read_buffer = qdf_mem_malloc(count);
  91. if (!read_buffer)
  92. return -ENOMEM;
  93. hif_debug("rd buff 0x%pK cnt %zu offset 0x%x buf 0x%pK",
  94. read_buffer, count, (int)*pos, buf);
  95. tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
  96. if ((scn->bus_type == QDF_BUS_TYPE_SNOC) ||
  97. (scn->bus_type == QDF_BUS_TYPE_PCI &&
  98. ((tgt_info->target_type == TARGET_TYPE_QCA6290) ||
  99. (tgt_info->target_type == TARGET_TYPE_QCA6390) ||
  100. (tgt_info->target_type == TARGET_TYPE_QCA6490) ||
  101. (tgt_info->target_type == TARGET_TYPE_QCA8074) ||
  102. (tgt_info->target_type == TARGET_TYPE_QCA8074V2) ||
  103. (tgt_info->target_type == TARGET_TYPE_QCA9574) ||
  104. (tgt_info->target_type == TARGET_TYPE_QCN9000) ||
  105. (tgt_info->target_type == TARGET_TYPE_QCN9224) ||
  106. (tgt_info->target_type == TARGET_TYPE_QCN6122) ||
  107. (tgt_info->target_type == TARGET_TYPE_QCN9160) ||
  108. (tgt_info->target_type == TARGET_TYPE_QCN6432) ||
  109. (tgt_info->target_type == TARGET_TYPE_QCA5018) ||
  110. (tgt_info->target_type == TARGET_TYPE_QCA5332) ||
  111. (tgt_info->target_type == TARGET_TYPE_QCA6018) ||
  112. (tgt_info->target_type == TARGET_TYPE_QCN7605) ||
  113. (tgt_info->target_type == TARGET_TYPE_KIWI) ||
  114. (tgt_info->target_type == TARGET_TYPE_MANGO) ||
  115. (tgt_info->target_type == TARGET_TYPE_PEACH))) ||
  116. (scn->bus_type == QDF_BUS_TYPE_IPCI &&
  117. (tgt_info->target_type == TARGET_TYPE_QCA6750)) ||
  118. ((scn->bus_type == QDF_BUS_TYPE_USB) &&
  119. (tgt_info->target_type == TARGET_TYPE_QCN7605))) {
  120. memtype = ((uint32_t)(*pos) & 0xff000000) >> 24;
  121. offset = (uint32_t)(*pos) & 0xffffff;
  122. hif_debug("offset 0x%x memtype 0x%x, datalen %zu",
  123. offset, memtype, count);
  124. rv = pld_athdiag_read(scn->qdf_dev->dev,
  125. offset, memtype, count,
  126. (uint8_t *)read_buffer);
  127. goto out;
  128. }
  129. if ((count == 4) && ((((uint32_t) (*pos)) & 3) == 0)) {
  130. /* reading a word? */
  131. rv = hif_diag_read_access(hif_hdl, (uint32_t)(*pos),
  132. (uint32_t *)read_buffer);
  133. } else {
  134. rv = hif_diag_read_mem(hif_hdl, (uint32_t)(*pos),
  135. (uint8_t *)read_buffer, count);
  136. }
  137. out:
  138. if (rv) {
  139. qdf_mem_free(read_buffer);
  140. return -EIO;
  141. }
  142. if (copy_to_user(buf, read_buffer, count)) {
  143. qdf_mem_free(read_buffer);
  144. hif_err("copy_to_user error in /proc/%s", PROCFS_NAME);
  145. return -EFAULT;
  146. }
  147. qdf_mem_free(read_buffer);
  148. return count;
  149. }
  150. static ssize_t ath_procfs_diag_write_legacy(struct file *file,
  151. const char __user *buf,
  152. size_t count, loff_t *pos)
  153. {
  154. hif_handle_t hif_hdl;
  155. int rv;
  156. uint8_t *write_buffer = NULL;
  157. struct hif_softc *scn;
  158. uint32_t offset = 0, memtype = 0;
  159. struct hif_target_info *tgt_info;
  160. hif_hdl = get_hif_hdl_from_file(file);
  161. scn = HIF_GET_SOFTC(hif_hdl);
  162. write_buffer = qdf_mem_malloc(count);
  163. if (!write_buffer)
  164. return -ENOMEM;
  165. if (copy_from_user(write_buffer, buf, count)) {
  166. qdf_mem_free(write_buffer);
  167. hif_err("copy_to_user error in /proc/%s", PROCFS_NAME);
  168. return -EFAULT;
  169. }
  170. hif_debug("wr buff 0x%pK buf 0x%pK cnt %zu offset 0x%x value 0x%x",
  171. write_buffer, buf, count,
  172. (int)*pos, *((uint32_t *) write_buffer));
  173. tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
  174. if ((scn->bus_type == QDF_BUS_TYPE_SNOC) ||
  175. ((scn->bus_type == QDF_BUS_TYPE_PCI) &&
  176. ((tgt_info->target_type == TARGET_TYPE_QCA6290) ||
  177. (tgt_info->target_type == TARGET_TYPE_QCA6390) ||
  178. (tgt_info->target_type == TARGET_TYPE_QCA6490) ||
  179. (tgt_info->target_type == TARGET_TYPE_QCA8074) ||
  180. (tgt_info->target_type == TARGET_TYPE_QCA8074V2) ||
  181. (tgt_info->target_type == TARGET_TYPE_QCA9574) ||
  182. (tgt_info->target_type == TARGET_TYPE_QCN9000) ||
  183. (tgt_info->target_type == TARGET_TYPE_QCN9224) ||
  184. (tgt_info->target_type == TARGET_TYPE_QCN6122) ||
  185. (tgt_info->target_type == TARGET_TYPE_QCN9160) ||
  186. (tgt_info->target_type == TARGET_TYPE_QCN6432) ||
  187. (tgt_info->target_type == TARGET_TYPE_QCA5018) ||
  188. (tgt_info->target_type == TARGET_TYPE_QCA5332) ||
  189. (tgt_info->target_type == TARGET_TYPE_QCA6018) ||
  190. (tgt_info->target_type == TARGET_TYPE_QCN7605) ||
  191. (tgt_info->target_type == TARGET_TYPE_KIWI) ||
  192. (tgt_info->target_type == TARGET_TYPE_MANGO) ||
  193. (tgt_info->target_type == TARGET_TYPE_PEACH))) ||
  194. (scn->bus_type == QDF_BUS_TYPE_IPCI &&
  195. (tgt_info->target_type == TARGET_TYPE_QCA6750)) ||
  196. ((scn->bus_type == QDF_BUS_TYPE_USB) &&
  197. (tgt_info->target_type == TARGET_TYPE_QCN7605))) {
  198. memtype = ((uint32_t)(*pos) & 0xff000000) >> 24;
  199. offset = (uint32_t)(*pos) & 0xffffff;
  200. hif_debug("offset 0x%x memtype 0x%x, datalen %zu",
  201. offset, memtype, count);
  202. rv = pld_athdiag_write(scn->qdf_dev->dev,
  203. offset, memtype, count,
  204. (uint8_t *)write_buffer);
  205. goto out;
  206. }
  207. if ((count == 4) && ((((uint32_t) (*pos)) & 3) == 0)) {
  208. /* reading a word? */
  209. uint32_t value = *((uint32_t *)write_buffer);
  210. rv = hif_diag_write_access(hif_hdl, (uint32_t)(*pos), value);
  211. } else {
  212. rv = hif_diag_write_mem(hif_hdl, (uint32_t)(*pos),
  213. (uint8_t *)write_buffer, count);
  214. }
  215. out:
  216. qdf_mem_free(write_buffer);
  217. if (rv == 0)
  218. return count;
  219. else
  220. return -EIO;
  221. }
  222. #ifdef ATH_DIAG_EXT_DIRECT
  223. /* Used to dump register or SRAM from target directly */
  224. static int ath_procfs_direct_read(struct hif_softc *scn, uint32_t offset,
  225. uint8_t *buf, size_t count)
  226. {
  227. size_t remaining = count;
  228. uint32_t *p_val = (uint32_t *)buf;
  229. uint32_t val;
  230. uint8_t *buf_d, *buf_s;
  231. if (!scn->bus_ops.hif_reg_read32)
  232. return -EIO;
  233. while (remaining >= 4) {
  234. *p_val++ = scn->bus_ops.hif_reg_read32(scn,
  235. offset);
  236. offset += 4;
  237. remaining -= 4;
  238. }
  239. if (remaining) {
  240. val = scn->bus_ops.hif_reg_read32(scn,
  241. offset);
  242. buf_d = (uint8_t *)p_val;
  243. buf_s = (uint8_t *)&val;
  244. while (remaining) {
  245. *buf_d++ = *buf_s++;
  246. remaining--;
  247. }
  248. }
  249. return 0;
  250. }
  251. /* Used to write register or SRAM to target directly */
  252. static int ath_procfs_direct_write(struct hif_softc *scn, uint32_t offset,
  253. uint8_t *buf, size_t count)
  254. {
  255. size_t remaining = count;
  256. uint32_t *p_val = (uint32_t *)buf;
  257. uint32_t val;
  258. uint8_t *buf_d, *buf_s;
  259. if (!scn->bus_ops.hif_reg_write32 || !scn->bus_ops.hif_reg_read32)
  260. return -EIO;
  261. while (remaining >= 4) {
  262. scn->bus_ops.hif_reg_write32(scn,
  263. offset,
  264. *p_val++);
  265. offset += 4;
  266. remaining -= 4;
  267. }
  268. if (remaining) {
  269. val = scn->bus_ops.hif_reg_read32(scn,
  270. offset);
  271. buf_s = (uint8_t *)p_val;
  272. buf_d = (uint8_t *)&val;
  273. while (remaining) {
  274. *buf_d++ = *buf_s++;
  275. remaining--;
  276. }
  277. scn->bus_ops.hif_reg_write32(scn,
  278. offset,
  279. val);
  280. }
  281. return 0;
  282. }
  283. #else
  284. static int ath_procfs_direct_read(struct hif_softc *scn, uint32_t offset,
  285. uint8_t *buf, size_t count)
  286. {
  287. return -EIO;
  288. }
  289. /* Used to write register or SRAM to target directly */
  290. static int ath_procfs_direct_write(struct hif_softc *scn, uint32_t offset,
  291. uint8_t *buf, size_t count)
  292. {
  293. return -EIO;
  294. }
  295. #endif
  296. static ssize_t ath_procfs_diag_read_ext(struct file *file, char __user *buf,
  297. size_t count,
  298. uint32_t op_type,
  299. uint32_t memtype,
  300. uint32_t offset)
  301. {
  302. hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
  303. int rv = -EINVAL;
  304. uint8_t *read_buffer;
  305. struct hif_softc *scn;
  306. struct hif_target_info *tgt_info;
  307. if (!hif_hdl)
  308. return -EINVAL;
  309. read_buffer = qdf_mem_malloc(count);
  310. if (!read_buffer)
  311. return -ENOMEM;
  312. scn = HIF_GET_SOFTC(hif_hdl);
  313. tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
  314. switch (scn->bus_type) {
  315. case QDF_BUS_TYPE_PCI:
  316. switch (tgt_info->target_type) {
  317. case TARGET_TYPE_QCA6390:
  318. case TARGET_TYPE_QCA6490:
  319. case TARGET_TYPE_KIWI:
  320. case TARGET_TYPE_PEACH:
  321. case TARGET_TYPE_MANGO:
  322. if (op_type == OP_TYPE_EXT_DIRECT)
  323. rv = ath_procfs_direct_read(scn,
  324. offset,
  325. read_buffer,
  326. count);
  327. else
  328. rv = pld_athdiag_read(scn->qdf_dev->dev,
  329. offset,
  330. memtype,
  331. count,
  332. read_buffer);
  333. break;
  334. default:
  335. hif_err("Unrecognized target type %d",
  336. tgt_info->target_type);
  337. }
  338. break;
  339. default:
  340. hif_err("Unrecognized bus type %d", scn->bus_type);
  341. break;
  342. }
  343. if (rv) {
  344. hif_err("fail to read from target %d", rv);
  345. } else {
  346. rv = count;
  347. if (copy_to_user(buf, read_buffer, count)) {
  348. hif_err("copy_to_user error in /proc/%s",
  349. PROCFS_NAME);
  350. rv = -EFAULT;
  351. }
  352. }
  353. qdf_mem_free(read_buffer);
  354. return rv;
  355. }
  356. static ssize_t ath_procfs_diag_write_ext(struct file *file,
  357. const char __user *buf,
  358. size_t count,
  359. uint32_t op_type,
  360. uint32_t memtype,
  361. uint32_t offset)
  362. {
  363. hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
  364. int rv = -EINVAL;
  365. uint8_t *write_buffer;
  366. struct hif_softc *scn;
  367. struct hif_target_info *tgt_info;
  368. if (!hif_hdl)
  369. return -EINVAL;
  370. scn = HIF_GET_SOFTC(hif_hdl);
  371. write_buffer = qdf_mem_malloc(count);
  372. if (!write_buffer)
  373. return -ENOMEM;
  374. if (copy_from_user(write_buffer, buf, count)) {
  375. qdf_mem_free(write_buffer);
  376. hif_err("copy_to_user error in /proc/%s",
  377. PROCFS_NAME);
  378. return -EFAULT;
  379. }
  380. tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl));
  381. switch (scn->bus_type) {
  382. case QDF_BUS_TYPE_PCI:
  383. switch (tgt_info->target_type) {
  384. case TARGET_TYPE_QCA6390:
  385. case TARGET_TYPE_QCA6490:
  386. case TARGET_TYPE_KIWI:
  387. case TARGET_TYPE_MANGO:
  388. case TARGET_TYPE_PEACH:
  389. if (op_type == OP_TYPE_EXT_DIRECT)
  390. rv = ath_procfs_direct_write(scn,
  391. offset,
  392. write_buffer,
  393. count);
  394. else
  395. rv = pld_athdiag_write(scn->qdf_dev->dev,
  396. offset,
  397. memtype,
  398. count,
  399. write_buffer);
  400. break;
  401. default:
  402. hif_err("Unrecognized target type %d",
  403. tgt_info->target_type);
  404. }
  405. break;
  406. default:
  407. hif_err("Unrecognized bus type %d", scn->bus_type);
  408. break;
  409. }
  410. qdf_mem_free(write_buffer);
  411. return (rv == 0) ? count : -EIO;
  412. }
  413. static void get_fields_from_pos(loff_t pos,
  414. uint32_t *op_type,
  415. uint32_t *memtype,
  416. uint32_t *offset)
  417. {
  418. *op_type = QDF_GET_BITS64(pos, ATH_DIAG_EXT_OP_TYPE_INDEX,
  419. ATH_DIAG_EXT_OP_TYPE_BITS);
  420. *memtype = QDF_GET_BITS64(pos, ATH_DIAG_EXT_MEM_TYPE_INDEX,
  421. ATH_DIAG_EXT_MEM_TYPE_BITS);
  422. *offset = QDF_GET_BITS64(pos, ATH_DIAG_EXT_OFFSET_INDEX,
  423. ATH_DIAG_EXT_OFFSET_BITS);
  424. }
  425. static ssize_t ath_procfs_diag_read(struct file *file, char __user *buf,
  426. size_t count, loff_t *pos)
  427. {
  428. hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
  429. int rv = -EINVAL;
  430. struct hif_softc *scn;
  431. uint32_t offset, memtype;
  432. uint32_t op_type;
  433. if (!hif_hdl)
  434. return -EINVAL;
  435. get_fields_from_pos(*pos, &op_type, &memtype, &offset);
  436. scn = HIF_GET_SOFTC(hif_hdl);
  437. if (scn->bus_ops.hif_addr_in_boundary(scn, offset))
  438. return -EINVAL;
  439. if (offset & 0x3)
  440. return -EINVAL;
  441. hif_debug("rd cnt %zu offset 0x%x op_type %d type %d pos %llx",
  442. count, offset, op_type, memtype, *pos);
  443. switch (op_type) {
  444. case OP_TYPE_LEGACY:
  445. rv = ath_procfs_diag_read_legacy(file, buf, count, pos);
  446. break;
  447. case OP_TYPE_EXT_QMI:
  448. case OP_TYPE_EXT_DIRECT:
  449. rv = ath_procfs_diag_read_ext(file, buf, count, op_type,
  450. memtype, offset);
  451. break;
  452. default:
  453. hif_err("Unrecognized op type %d", op_type);
  454. break;
  455. }
  456. return rv;
  457. }
  458. static ssize_t ath_procfs_diag_write(struct file *file,
  459. const char __user *buf,
  460. size_t count, loff_t *pos)
  461. {
  462. hif_handle_t hif_hdl = get_hif_hdl_from_file(file);
  463. int rv = -EINVAL;
  464. struct hif_softc *scn;
  465. uint32_t offset, memtype;
  466. uint32_t op_type;
  467. if (!hif_hdl)
  468. return -EINVAL;
  469. get_fields_from_pos(*pos, &op_type, &memtype, &offset);
  470. scn = HIF_GET_SOFTC(hif_hdl);
  471. if (scn->bus_ops.hif_addr_in_boundary(scn, offset))
  472. return -EINVAL;
  473. if (offset & 0x3)
  474. return -EINVAL;
  475. hif_debug("wr cnt %zu offset 0x%x op_type %d mem_type %d",
  476. count, offset, op_type, memtype);
  477. switch (op_type) {
  478. case OP_TYPE_LEGACY:
  479. rv = ath_procfs_diag_write_legacy(file, buf, count, pos);
  480. break;
  481. case OP_TYPE_EXT_QMI:
  482. case OP_TYPE_EXT_DIRECT:
  483. rv = ath_procfs_diag_write_ext(file, buf, count, op_type,
  484. memtype, offset);
  485. break;
  486. default:
  487. hif_err("Unrecognized op type %d", op_type);
  488. break;
  489. }
  490. return rv;
  491. }
  492. #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0))
  493. static const struct proc_ops athdiag_fops = {
  494. .proc_read = ath_procfs_diag_read,
  495. .proc_write = ath_procfs_diag_write,
  496. .proc_lseek = default_llseek,
  497. };
  498. #else
  499. static const struct file_operations athdiag_fops = {
  500. .read = ath_procfs_diag_read,
  501. .write = ath_procfs_diag_write,
  502. };
  503. #endif
  504. /*
  505. * This function is called when the module is loaded
  506. *
  507. */
  508. int athdiag_procfs_init(void *scn)
  509. {
  510. proc_dir = proc_mkdir(PROCFS_DIR, NULL);
  511. if (!proc_dir) {
  512. remove_proc_entry(PROCFS_DIR, NULL);
  513. hif_err("Could not initialize /proc/%s", PROCFS_DIR);
  514. return -ENOMEM;
  515. }
  516. proc_file = proc_create_data(PROCFS_NAME, 0600, proc_dir,
  517. &athdiag_fops, (void *)scn);
  518. if (!proc_file) {
  519. remove_proc_entry(PROCFS_NAME, proc_dir);
  520. hif_err("Could not initialize /proc/%s", PROCFS_NAME);
  521. return -ENOMEM;
  522. }
  523. hif_debug("/proc/%s/%s created", PROCFS_DIR, PROCFS_NAME);
  524. return 0;
  525. }
  526. /*
  527. * This function is called when the module is unloaded
  528. *
  529. */
  530. void athdiag_procfs_remove(void)
  531. {
  532. if (proc_dir) {
  533. remove_proc_entry(PROCFS_NAME, proc_dir);
  534. hif_debug("/proc/%s/%s removed", PROCFS_DIR, PROCFS_NAME);
  535. remove_proc_entry(PROCFS_DIR, NULL);
  536. hif_debug("/proc/%s removed", PROCFS_DIR);
  537. proc_dir = NULL;
  538. }
  539. }
  540. #else
  541. int athdiag_procfs_init(void *scn)
  542. {
  543. return 0;
  544. }
  545. void athdiag_procfs_remove(void) {}
  546. #endif