1 /* 2 * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 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 20 /** 21 * DOC: qdf_debugfs 22 * This file provides QDF debug file system APIs 23 */ 24 25 #include <qdf_debugfs.h> 26 #include <i_qdf_debugfs.h> 27 #include <qdf_mem.h> 28 #include <qdf_trace.h> 29 #include <qdf_module.h> 30 31 /* A private structure definition to qdf sequence */ 32 struct qdf_debugfs_seq_priv { 33 bool stop; 34 }; 35 36 /* entry for root debugfs directory*/ 37 static qdf_dentry_t qdf_debugfs_root; 38 39 QDF_STATUS qdf_debugfs_init(void) 40 { 41 qdf_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); 42 43 if (!qdf_debugfs_root) 44 return QDF_STATUS_E_FAILURE; 45 46 return QDF_STATUS_SUCCESS; 47 } 48 qdf_export_symbol(qdf_debugfs_init); 49 50 void qdf_debugfs_exit(void) 51 { 52 if (!qdf_debugfs_root) 53 return; 54 55 debugfs_remove_recursive(qdf_debugfs_root); 56 qdf_debugfs_root = NULL; 57 } 58 qdf_export_symbol(qdf_debugfs_exit); 59 60 qdf_dentry_t qdf_debugfs_get_root(void) 61 { 62 return qdf_debugfs_root; 63 } 64 qdf_export_symbol(qdf_debugfs_get_root); 65 66 umode_t qdf_debugfs_get_filemode(uint16_t mode) 67 { 68 umode_t ret = 0; 69 70 if (mode & QDF_FILE_USR_READ) 71 ret |= 0400; 72 if (mode & QDF_FILE_USR_WRITE) 73 ret |= 0200; 74 75 if (mode & QDF_FILE_GRP_READ) 76 ret |= 0040; 77 if (mode & QDF_FILE_GRP_WRITE) 78 ret |= 0020; 79 80 if (mode & QDF_FILE_OTH_READ) 81 ret |= 0004; 82 if (mode & QDF_FILE_OTH_WRITE) 83 ret |= 0002; 84 85 return ret; 86 } 87 88 /* 89 * ---------------------- Implementation note --------------------------------- 90 * 91 * A read in debugfs file triggers seq_read() which calls seq_read api. A 92 * sequence begins with the call of the function start(). If the return is a non 93 * NULL value, the function next() is called. This function is an iterator, the 94 * goal is to go though all the data. Each time next() is called, the function 95 * show() is also called. It writes data values in the buffer read by the user. 96 * The function next() is called until it returns NULL. The sequence ends when 97 * next() returns NULL, then the function stop() is called. 98 * 99 * NOTE: When a sequence is finished, another one starts. That means that 100 * at the end of function stop(), the function start() is called again. This 101 * loop finishes when the function start() returns NULL. 102 * ---------------------------------------------------------------------------- 103 */ 104 105 /* .seq_start() */ 106 static void *qdf_debugfs_seq_start(struct seq_file *seq, loff_t *pos) 107 { 108 struct qdf_debugfs_seq_priv *priv; 109 110 priv = qdf_mem_malloc(sizeof(*priv)); 111 if (!priv) 112 return NULL; 113 114 priv->stop = false; 115 116 return priv; 117 } 118 119 /* .seq_next() */ 120 static void *qdf_debugfs_seq_next(struct seq_file *seq, void *v, loff_t *pos) 121 { 122 struct qdf_debugfs_seq_priv *priv = v; 123 124 if (priv) 125 ++*pos; 126 127 if (priv && priv->stop) { 128 qdf_mem_free(priv); 129 priv = NULL; 130 } 131 132 return priv; 133 } 134 135 /* .seq_stop() */ 136 static void qdf_debugfs_seq_stop(struct seq_file *seq, void *v) 137 { 138 qdf_mem_free(v); 139 } 140 141 /* .seq_show() */ 142 static int qdf_debugfs_seq_show(struct seq_file *seq, void *v) 143 { 144 struct qdf_debugfs_seq_priv *priv = v; 145 struct qdf_debugfs_fops *fops; 146 QDF_STATUS status; 147 148 fops = seq->private; 149 150 if (fops && fops->show) { 151 status = fops->show(seq, fops->priv); 152 153 if (priv && (status != QDF_STATUS_E_AGAIN)) 154 priv->stop = true; 155 } 156 157 return 0; 158 } 159 160 void qdf_debugfs_printf(qdf_debugfs_file_t file, const char *f, ...) 161 { 162 va_list args; 163 164 va_start(args, f); 165 seq_vprintf(file, f, args); 166 va_end(args); 167 } 168 169 qdf_export_symbol(qdf_debugfs_printf); 170 171 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) 172 173 void qdf_debugfs_hexdump(qdf_debugfs_file_t file, const uint8_t *buf, 174 qdf_size_t len, int rowsize, int groupsize) 175 { 176 seq_hex_dump(file, "", DUMP_PREFIX_OFFSET, rowsize, groupsize, buf, len, 177 false); 178 } 179 180 qdf_export_symbol(qdf_debugfs_hexdump); 181 #else 182 183 void qdf_debugfs_hexdump(qdf_debugfs_file_t file, const uint8_t *buf, 184 qdf_size_t len, int rowsize, int groupsize) 185 { 186 char *dst; 187 size_t dstlen, readlen, remaining = len; 188 int prefix = 0; 189 size_t commitlen; 190 191 while (remaining > 0 && (file->size > file->count)) { 192 seq_printf(file, "%.8x: ", prefix); 193 194 readlen = qdf_min(remaining, (qdf_size_t)rowsize); 195 dstlen = seq_get_buf(file, &dst); 196 hex_dump_to_buffer(buf, readlen, rowsize, groupsize, dst, 197 dstlen, false); 198 commitlen = strnlen(dst, dstlen); 199 seq_commit(file, commitlen); 200 seq_putc(file, '\n'); 201 202 remaining = (remaining > rowsize) ? remaining - rowsize : 0; 203 buf += readlen; 204 prefix += rowsize; 205 } 206 } 207 208 qdf_export_symbol(qdf_debugfs_hexdump); 209 #endif 210 211 bool qdf_debugfs_overflow(qdf_debugfs_file_t file) 212 { 213 return seq_has_overflowed(file); 214 } 215 216 qdf_export_symbol(qdf_debugfs_overflow); 217 218 void qdf_debugfs_write(qdf_debugfs_file_t file, const uint8_t *buf, 219 qdf_size_t len) 220 { 221 seq_write(file, buf, len); 222 } 223 224 /* sequential file operation table */ 225 static const struct seq_operations __qdf_debugfs_seq_ops = { 226 .start = qdf_debugfs_seq_start, 227 .next = qdf_debugfs_seq_next, 228 .stop = qdf_debugfs_seq_stop, 229 .show = qdf_debugfs_seq_show, 230 }; 231 232 /* .open() */ 233 static int qdf_seq_open(struct inode *inode, struct file *file) 234 { 235 void *private = inode->i_private; 236 struct seq_file *seq; 237 int rc; 238 239 /** 240 * Note: seq_open() will allocate a struct seq_file and store its 241 * pointer in @file->private_data. It warns if private_data is not NULL. 242 */ 243 244 rc = seq_open(file, &__qdf_debugfs_seq_ops); 245 246 if (rc == 0) { 247 seq = file->private_data; 248 seq->private = private; 249 } 250 251 return rc; 252 } 253 254 /* .write() */ 255 static ssize_t qdf_seq_write(struct file *filp, const char __user *ubuf, 256 size_t len, loff_t *ppos) 257 { 258 struct qdf_debugfs_fops *fops; 259 struct seq_file *seq; 260 u8 *buf; 261 ssize_t rc = 0; 262 263 if (len == 0) 264 return 0; 265 266 seq = filp->private_data; 267 fops = seq->private; 268 if (fops && fops->write) { 269 buf = qdf_mem_malloc(len + 1); 270 if (buf) { 271 buf[len] = '\0'; 272 rc = simple_write_to_buffer(buf, len, ppos, ubuf, len); 273 fops->write(fops->priv, buf, len + 1); 274 qdf_mem_free(buf); 275 } 276 } 277 278 return rc; 279 } 280 281 /* debugfs file operation table */ 282 static const struct file_operations __qdf_debugfs_fops = { 283 .owner = THIS_MODULE, 284 .open = qdf_seq_open, 285 .read = seq_read, 286 .llseek = seq_lseek, 287 .release = seq_release, 288 .write = qdf_seq_write, 289 }; 290 291 qdf_dentry_t qdf_debugfs_create_dir(const char *name, qdf_dentry_t parent) 292 { 293 qdf_dentry_t dir; 294 295 if (!name) 296 return NULL; 297 if (!parent) 298 parent = qdf_debugfs_get_root(); 299 300 dir = debugfs_create_dir(name, parent); 301 302 if (IS_ERR_OR_NULL(dir)) { 303 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 304 "%s creation failed", name); 305 dir = NULL; 306 } 307 308 return dir; 309 } 310 qdf_export_symbol(qdf_debugfs_create_dir); 311 312 qdf_dentry_t qdf_debugfs_create_file(const char *name, uint16_t mode, 313 qdf_dentry_t parent, 314 struct qdf_debugfs_fops *fops) 315 { 316 qdf_dentry_t file; 317 umode_t filemode; 318 319 if (!name || !fops) 320 return NULL; 321 322 if (!parent) 323 parent = qdf_debugfs_get_root(); 324 325 filemode = qdf_debugfs_get_filemode(mode); 326 file = debugfs_create_file(name, filemode, parent, fops, 327 &__qdf_debugfs_fops); 328 329 if (IS_ERR_OR_NULL(file)) { 330 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 331 "%s creation failed 0x%pK", name, file); 332 file = NULL; 333 } 334 335 return file; 336 } 337 qdf_export_symbol(qdf_debugfs_create_file); 338 339 void qdf_debugfs_create_u8(const char *name, uint16_t mode, 340 qdf_dentry_t parent, u8 *value) 341 { 342 umode_t filemode; 343 344 if (!name) 345 return; 346 347 if (!parent) 348 parent = qdf_debugfs_get_root(); 349 350 filemode = qdf_debugfs_get_filemode(mode); 351 debugfs_create_u8(name, filemode, parent, value); 352 } 353 354 void qdf_debugfs_create_u16(const char *name, uint16_t mode, 355 qdf_dentry_t parent, u16 *value) 356 { 357 umode_t filemode; 358 359 if (!name) 360 return; 361 362 if (!parent) 363 parent = qdf_debugfs_get_root(); 364 365 filemode = qdf_debugfs_get_filemode(mode); 366 debugfs_create_u16(name, filemode, parent, value); 367 } 368 369 qdf_export_symbol(qdf_debugfs_create_u16); 370 371 void qdf_debugfs_create_u32(const char *name, 372 uint16_t mode, 373 qdf_dentry_t parent, u32 *value) 374 { 375 umode_t filemode; 376 377 if (!name) 378 return; 379 380 if (!parent) 381 parent = qdf_debugfs_get_root(); 382 383 filemode = qdf_debugfs_get_filemode(mode); 384 debugfs_create_u32(name, filemode, parent, value); 385 } 386 387 qdf_export_symbol(qdf_debugfs_create_u32); 388 389 void qdf_debugfs_create_u64(const char *name, uint16_t mode, 390 qdf_dentry_t parent, u64 *value) 391 { 392 umode_t filemode; 393 394 if (!name) 395 return; 396 397 if (!parent) 398 parent = qdf_debugfs_get_root(); 399 400 filemode = qdf_debugfs_get_filemode(mode); 401 debugfs_create_u64(name, filemode, parent, value); 402 } 403 404 qdf_export_symbol(qdf_debugfs_create_u64); 405 406 void qdf_debugfs_create_atomic(const char *name, uint16_t mode, 407 qdf_dentry_t parent, qdf_atomic_t *value) 408 { 409 umode_t filemode; 410 411 if (!name) 412 return; 413 414 if (!parent) 415 parent = qdf_debugfs_get_root(); 416 417 filemode = qdf_debugfs_get_filemode(mode); 418 debugfs_create_atomic_t(name, filemode, parent, value); 419 } 420 421 qdf_export_symbol(qdf_debugfs_create_atomic); 422 423 static int qdf_debugfs_string_show(struct seq_file *seq, void *pos) 424 { 425 char *str = seq->private; 426 427 seq_puts(seq, str); 428 seq_putc(seq, '\n'); 429 430 return 0; 431 } 432 433 static int qdf_debugfs_string_open(struct inode *inode, struct file *file) 434 { 435 return single_open(file, qdf_debugfs_string_show, inode->i_private); 436 } 437 438 static const struct file_operations qdf_string_fops = { 439 .owner = THIS_MODULE, 440 .open = qdf_debugfs_string_open, 441 .read = seq_read, 442 .llseek = seq_lseek, 443 .release = single_release 444 }; 445 446 qdf_dentry_t qdf_debugfs_create_string(const char *name, uint16_t mode, 447 qdf_dentry_t parent, char *str) 448 { 449 umode_t filemode; 450 451 if (!name) 452 return NULL; 453 454 if (!parent) 455 parent = qdf_debugfs_get_root(); 456 457 filemode = qdf_debugfs_get_filemode(mode); 458 return debugfs_create_file(name, filemode, parent, str, 459 &qdf_string_fops); 460 } 461 qdf_export_symbol(qdf_debugfs_create_string); 462 463 void qdf_debugfs_remove_dir_recursive(qdf_dentry_t d) 464 { 465 debugfs_remove_recursive(d); 466 } 467 qdf_export_symbol(qdf_debugfs_remove_dir_recursive); 468 469 void qdf_debugfs_remove_dir(qdf_dentry_t d) 470 { 471 debugfs_remove(d); 472 } 473 qdf_export_symbol(qdf_debugfs_remove_dir); 474 475 void qdf_debugfs_remove_file(qdf_dentry_t d) 476 { 477 debugfs_remove(d); 478 } 479 qdf_export_symbol(qdf_debugfs_remove_file); 480 481 static int qdf_debugfs_single_show(struct seq_file *seq, void *v) 482 { 483 struct qdf_debugfs_fops *fops = seq->private; 484 485 if (fops && fops->show) 486 fops->show(seq, fops->priv); 487 488 return 0; 489 } 490 491 /* .open() */ 492 static int qdf_debugfs_single_open(struct inode *inode, struct file *file) 493 { 494 return single_open(file, qdf_debugfs_single_show, 495 inode->i_private); 496 } 497 498 /* File operations for the simplified version */ 499 static const struct file_operations qdf_debugfs_fops_simple = { 500 .owner = THIS_MODULE, 501 .open = qdf_debugfs_single_open, 502 .release = single_release, 503 .read = seq_read, 504 .llseek = seq_lseek, 505 }; 506 507 qdf_dentry_t qdf_debugfs_create_file_simplified( 508 const char *name, uint16_t mode, 509 qdf_dentry_t parent, struct qdf_debugfs_fops *fops) 510 { 511 qdf_dentry_t file; 512 umode_t filemode; 513 514 if (!name || !fops) 515 return NULL; 516 517 if (!parent) 518 parent = qdf_debugfs_get_root(); 519 520 filemode = qdf_debugfs_get_filemode(mode); 521 file = debugfs_create_file(name, filemode, parent, fops, 522 &qdf_debugfs_fops_simple); 523 524 if (IS_ERR_OR_NULL(file)) { 525 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 526 "%s creation failed 0x%pK", name, file); 527 file = NULL; 528 } 529 530 return file; 531 } 532 qdf_export_symbol(qdf_debugfs_create_file_simplified); 533 534 int qdf_debugfs_printer(void *priv, const char *fmt, ...) 535 { 536 struct seq_file *file = priv; 537 va_list args; 538 539 va_start(args, fmt); 540 seq_vprintf(file, fmt, args); 541 seq_puts(file, "\n"); 542 va_end(args); 543 544 return 0; 545 } 546 qdf_export_symbol(qdf_debugfs_printer); 547 548 qdf_dentry_t qdf_debugfs_create_blob(const char *name, umode_t mode, 549 qdf_dentry_t parent, 550 qdf_debugfs_blob_wrap_t blob) 551 { 552 return debugfs_create_blob(name, mode, parent, blob); 553 } 554 555 qdf_export_symbol(qdf_debugfs_create_blob); 556 557 qdf_dentry_t qdf_debugfs_create_entry(const char *name, uint16_t mode, 558 qdf_dentry_t parent, 559 qdf_entry_t data, 560 const qdf_file_ops_t fops) 561 { 562 qdf_dentry_t file; 563 umode_t filemode; 564 565 if (!name || !fops) 566 return NULL; 567 568 if (!parent) 569 parent = qdf_debugfs_get_root(); 570 571 filemode = qdf_debugfs_get_filemode(mode); 572 file = debugfs_create_file(name, filemode, parent, data, fops); 573 574 if (IS_ERR_OR_NULL(file)) { 575 QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, 576 "%s creation failed 0x%pK", name, file); 577 file = NULL; 578 } 579 580 return file; 581 } 582 583 qdf_export_symbol(qdf_debugfs_create_entry); 584