1 /* 2 * Copyright (c) 2013-2014, 2016-2020 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 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 37 #define PROCFS_NAME "athdiagpfs" 38 #ifdef MULTI_IF_NAME 39 #define PROCFS_DIR "cld" MULTI_IF_NAME 40 #else 41 #define PROCFS_DIR "cld" 42 #endif 43 44 /** 45 * This structure hold information about the /proc file 46 * 47 */ 48 static struct proc_dir_entry *proc_file, *proc_dir; 49 50 static void *get_hif_hdl_from_file(struct file *file) 51 { 52 struct hif_opaque_softc *scn; 53 54 scn = (struct hif_opaque_softc *)PDE_DATA(file_inode(file)); 55 return (void *)scn; 56 } 57 58 static ssize_t ath_procfs_diag_read(struct file *file, char __user *buf, 59 size_t count, loff_t *pos) 60 { 61 hif_handle_t hif_hdl; 62 int rv; 63 uint8_t *read_buffer = NULL; 64 struct hif_softc *scn; 65 uint32_t offset = 0, memtype = 0; 66 struct hif_target_info *tgt_info; 67 68 hif_hdl = get_hif_hdl_from_file(file); 69 scn = HIF_GET_SOFTC(hif_hdl); 70 71 if (scn->bus_ops.hif_addr_in_boundary(hif_hdl, (uint32_t)(*pos))) 72 return -EINVAL; 73 74 read_buffer = qdf_mem_malloc(count); 75 if (!read_buffer) 76 return -ENOMEM; 77 78 HIF_DBG("rd buff 0x%pK cnt %zu offset 0x%x buf 0x%pK", 79 read_buffer, count, (int)*pos, buf); 80 81 tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl)); 82 if ((scn->bus_type == QDF_BUS_TYPE_SNOC) || 83 (scn->bus_type == QDF_BUS_TYPE_PCI && 84 ((tgt_info->target_type == TARGET_TYPE_QCA6290) || 85 (tgt_info->target_type == TARGET_TYPE_QCA6390) || 86 (tgt_info->target_type == TARGET_TYPE_QCA6490) || 87 (tgt_info->target_type == TARGET_TYPE_QCA8074) || 88 (tgt_info->target_type == TARGET_TYPE_QCA8074V2) || 89 (tgt_info->target_type == TARGET_TYPE_QCN9000) || 90 (tgt_info->target_type == TARGET_TYPE_QCA5018) || 91 (tgt_info->target_type == TARGET_TYPE_QCA6018) || 92 (tgt_info->target_type == TARGET_TYPE_QCN7605))) || 93 (scn->bus_type == QDF_BUS_TYPE_IPCI && 94 (tgt_info->target_type == TARGET_TYPE_QCA6750)) || 95 ((scn->bus_type == QDF_BUS_TYPE_USB) && 96 (tgt_info->target_type == TARGET_TYPE_QCN7605))) { 97 memtype = ((uint32_t)(*pos) & 0xff000000) >> 24; 98 offset = (uint32_t)(*pos) & 0xffffff; 99 HIF_DBG("%s: offset 0x%x memtype 0x%x, datalen %zu\n", 100 __func__, offset, memtype, count); 101 rv = pld_athdiag_read(scn->qdf_dev->dev, 102 offset, memtype, count, 103 (uint8_t *)read_buffer); 104 goto out; 105 } 106 107 if ((count == 4) && ((((uint32_t) (*pos)) & 3) == 0)) { 108 /* reading a word? */ 109 rv = hif_diag_read_access(hif_hdl, (uint32_t)(*pos), 110 (uint32_t *)read_buffer); 111 } else { 112 rv = hif_diag_read_mem(hif_hdl, (uint32_t)(*pos), 113 (uint8_t *)read_buffer, count); 114 } 115 116 out: 117 if (rv) { 118 qdf_mem_free(read_buffer); 119 return -EIO; 120 } 121 122 if (copy_to_user(buf, read_buffer, count)) { 123 qdf_mem_free(read_buffer); 124 HIF_ERROR("%s: copy_to_user error in /proc/%s", 125 __func__, PROCFS_NAME); 126 return -EFAULT; 127 } 128 qdf_mem_free(read_buffer); 129 return count; 130 } 131 132 static ssize_t ath_procfs_diag_write(struct file *file, 133 const char __user *buf, 134 size_t count, loff_t *pos) 135 { 136 hif_handle_t hif_hdl; 137 int rv; 138 uint8_t *write_buffer = NULL; 139 struct hif_softc *scn; 140 uint32_t offset = 0, memtype = 0; 141 struct hif_target_info *tgt_info; 142 143 hif_hdl = get_hif_hdl_from_file(file); 144 scn = HIF_GET_SOFTC(hif_hdl); 145 146 if (scn->bus_ops.hif_addr_in_boundary(hif_hdl, (uint32_t)(*pos))) 147 return -EINVAL; 148 149 write_buffer = qdf_mem_malloc(count); 150 if (!write_buffer) 151 return -ENOMEM; 152 153 if (copy_from_user(write_buffer, buf, count)) { 154 qdf_mem_free(write_buffer); 155 HIF_ERROR("%s: copy_to_user error in /proc/%s", 156 __func__, PROCFS_NAME); 157 return -EFAULT; 158 } 159 160 HIF_DBG("wr buff 0x%pK buf 0x%pK cnt %zu offset 0x%x value 0x%x", 161 write_buffer, buf, count, 162 (int)*pos, *((uint32_t *) write_buffer)); 163 164 tgt_info = hif_get_target_info_handle(GET_HIF_OPAQUE_HDL(hif_hdl)); 165 if ((scn->bus_type == QDF_BUS_TYPE_SNOC) || 166 ((scn->bus_type == QDF_BUS_TYPE_PCI) && 167 ((tgt_info->target_type == TARGET_TYPE_QCA6290) || 168 (tgt_info->target_type == TARGET_TYPE_QCA6390) || 169 (tgt_info->target_type == TARGET_TYPE_QCA6490) || 170 (tgt_info->target_type == TARGET_TYPE_QCA8074) || 171 (tgt_info->target_type == TARGET_TYPE_QCA8074V2) || 172 (tgt_info->target_type == TARGET_TYPE_QCN9000) || 173 (tgt_info->target_type == TARGET_TYPE_QCA5018) || 174 (tgt_info->target_type == TARGET_TYPE_QCA6018) || 175 (tgt_info->target_type == TARGET_TYPE_QCN7605))) || 176 (scn->bus_type == QDF_BUS_TYPE_IPCI && 177 (tgt_info->target_type == TARGET_TYPE_QCA6750)) || 178 ((scn->bus_type == QDF_BUS_TYPE_USB) && 179 (tgt_info->target_type == TARGET_TYPE_QCN7605))) { 180 memtype = ((uint32_t)(*pos) & 0xff000000) >> 24; 181 offset = (uint32_t)(*pos) & 0xffffff; 182 HIF_DBG("%s: offset 0x%x memtype 0x%x, datalen %zu\n", 183 __func__, offset, memtype, count); 184 rv = pld_athdiag_write(scn->qdf_dev->dev, 185 offset, memtype, count, 186 (uint8_t *)write_buffer); 187 goto out; 188 } 189 190 if ((count == 4) && ((((uint32_t) (*pos)) & 3) == 0)) { 191 /* reading a word? */ 192 uint32_t value = *((uint32_t *)write_buffer); 193 194 rv = hif_diag_write_access(hif_hdl, (uint32_t)(*pos), value); 195 } else { 196 rv = hif_diag_write_mem(hif_hdl, (uint32_t)(*pos), 197 (uint8_t *)write_buffer, count); 198 } 199 200 out: 201 202 qdf_mem_free(write_buffer); 203 if (rv == 0) 204 return count; 205 else 206 return -EIO; 207 } 208 209 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) 210 static const struct proc_ops athdiag_fops = { 211 .proc_read = ath_procfs_diag_read, 212 .proc_write = ath_procfs_diag_write, 213 }; 214 #else 215 static const struct file_operations athdiag_fops = { 216 .read = ath_procfs_diag_read, 217 .write = ath_procfs_diag_write, 218 }; 219 #endif 220 221 /* 222 * This function is called when the module is loaded 223 * 224 */ 225 int athdiag_procfs_init(void *scn) 226 { 227 proc_dir = proc_mkdir(PROCFS_DIR, NULL); 228 if (!proc_dir) { 229 remove_proc_entry(PROCFS_DIR, NULL); 230 HIF_ERROR("%s: Error: Could not initialize /proc/%s", 231 __func__, PROCFS_DIR); 232 return -ENOMEM; 233 } 234 235 proc_file = proc_create_data(PROCFS_NAME, 0600, proc_dir, 236 &athdiag_fops, (void *)scn); 237 if (!proc_file) { 238 remove_proc_entry(PROCFS_NAME, proc_dir); 239 HIF_ERROR("%s: Could not initialize /proc/%s", 240 __func__, PROCFS_NAME); 241 return -ENOMEM; 242 } 243 244 HIF_DBG("/proc/%s/%s created", PROCFS_DIR, PROCFS_NAME); 245 return 0; /* everything is ok */ 246 } 247 248 /* 249 * This function is called when the module is unloaded 250 * 251 */ 252 void athdiag_procfs_remove(void) 253 { 254 if (proc_dir) { 255 remove_proc_entry(PROCFS_NAME, proc_dir); 256 HIF_DBG("/proc/%s/%s removed", PROCFS_DIR, PROCFS_NAME); 257 remove_proc_entry(PROCFS_DIR, NULL); 258 HIF_DBG("/proc/%s removed", PROCFS_DIR); 259 proc_dir = NULL; 260 } 261 } 262 #else 263 int athdiag_procfs_init(void *scn) 264 { 265 return 0; 266 } 267 void athdiag_procfs_remove(void) {} 268 #endif 269