| 1 | |
| 2 | |
| 3 |
|
| 4 | Broadcom B43 wireless driver
|
| 5 |
|
| 6 | debugfs driver debugging code
|
| 7 |
|
| 8 | Copyright (c) 2005-2007 Michael Buesch <[email protected]>
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 | #include <linux/fs.h>
|
| 14 | #include <linux/debugfs.h>
|
| 15 | #include <linux/slab.h>
|
| 16 | #include <linux/netdevice.h>
|
| 17 | #include <linux/pci.h>
|
| 18 | #include <linux/mutex.h>
|
| 19 |
|
| 20 | #include "b43.h"
|
| 21 | #include "main.h"
|
| 22 | #include "debugfs.h"
|
| 23 | #include "dma.h"
|
| 24 | #include "xmit.h"
|
| 25 |
|
| 26 |
|
| 27 |
|
| 28 | static struct dentry *rootdir;
|
| 29 |
|
| 30 | struct b43_debugfs_fops {
|
| 31 | ssize_t (*read)(struct b43_wldev *dev, char *buf, size_t bufsize);
|
| 32 | int (*write)(struct b43_wldev *dev, const char *buf, size_t count);
|
| 33 |
|
| 34 | size_t file_struct_offset;
|
| 35 | };
|
| 36 |
|
| 37 | static inline
|
| 38 | struct b43_dfs_file *fops_to_dfs_file(struct b43_wldev *dev,
|
| 39 | const struct b43_debugfs_fops *dfops)
|
| 40 | {
|
| 41 | void *p;
|
| 42 |
|
| 43 | p = dev->dfsentry;
|
| 44 | p += dfops->file_struct_offset;
|
| 45 |
|
| 46 | return p;
|
| 47 | }
|
| 48 |
|
| 49 |
|
| 50 | #define fappend(fmt, x...) \
|
| 51 | do { \
|
| 52 | if (bufsize - count) \
|
| 53 | count += scnprintf(buf + count, \
|
| 54 | bufsize - count, \
|
| 55 | fmt , ##x); \
|
| 56 | else \
|
| 57 | printk(KERN_ERR "b43: fappend overflow\n"); \
|
| 58 | } while (0)
|
| 59 |
|
| 60 |
|
| 61 |
|
| 62 | #define B43_MAX_SHM_ROUTING 4
|
| 63 | #define B43_MAX_SHM_ADDR 0xFFFF
|
| 64 |
|
| 65 | static ssize_t shm16read__read_file(struct b43_wldev *dev,
|
| 66 | char *buf, size_t bufsize)
|
| 67 | {
|
| 68 | ssize_t count = 0;
|
| 69 | unsigned int routing, addr;
|
| 70 | u16 val;
|
| 71 |
|
| 72 | routing = dev->dfsentry->shm16read_routing_next;
|
| 73 | addr = dev->dfsentry->shm16read_addr_next;
|
| 74 | if ((routing > B43_MAX_SHM_ROUTING) ||
|
| 75 | (addr > B43_MAX_SHM_ADDR))
|
| 76 | return -EDESTADDRREQ;
|
| 77 |
|
| 78 | val = b43_shm_read16(dev, routing, addr);
|
| 79 | fappend("0x%04X\n", val);
|
| 80 |
|
| 81 | return count;
|
| 82 | }
|
| 83 |
|
| 84 | static int shm16read__write_file(struct b43_wldev *dev,
|
| 85 | const char *buf, size_t count)
|
| 86 | {
|
| 87 | unsigned int routing, addr;
|
| 88 | int res;
|
| 89 |
|
| 90 | res = sscanf(buf, "0x%X 0x%X", &routing, &addr);
|
| 91 | if (res != 2)
|
| 92 | return -EINVAL;
|
| 93 | if (routing > B43_MAX_SHM_ROUTING)
|
| 94 | return -EADDRNOTAVAIL;
|
| 95 | if (addr > B43_MAX_SHM_ADDR)
|
| 96 | return -EADDRNOTAVAIL;
|
| 97 | if (routing == B43_SHM_SHARED) {
|
| 98 | if ((addr % 2) != 0)
|
| 99 | return -EADDRNOTAVAIL;
|
| 100 | }
|
| 101 |
|
| 102 | dev->dfsentry->shm16read_routing_next = routing;
|
| 103 | dev->dfsentry->shm16read_addr_next = addr;
|
| 104 |
|
| 105 | return 0;
|
| 106 | }
|
| 107 |
|
| 108 | static int shm16write__write_file(struct b43_wldev *dev,
|
| 109 | const char *buf, size_t count)
|
| 110 | {
|
| 111 | unsigned int routing, addr, mask, set;
|
| 112 | u16 val;
|
| 113 | int res;
|
| 114 |
|
| 115 | res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X",
|
| 116 | &routing, &addr, &mask, &set);
|
| 117 | if (res != 4)
|
| 118 | return -EINVAL;
|
| 119 | if (routing > B43_MAX_SHM_ROUTING)
|
| 120 | return -EADDRNOTAVAIL;
|
| 121 | if (addr > B43_MAX_SHM_ADDR)
|
| 122 | return -EADDRNOTAVAIL;
|
| 123 | if (routing == B43_SHM_SHARED) {
|
| 124 | if ((addr % 2) != 0)
|
| 125 | return -EADDRNOTAVAIL;
|
| 126 | }
|
| 127 | if ((mask > 0xFFFF) || (set > 0xFFFF))
|
| 128 | return -E2BIG;
|
| 129 |
|
| 130 | if (mask == 0)
|
| 131 | val = 0;
|
| 132 | else
|
| 133 | val = b43_shm_read16(dev, routing, addr);
|
| 134 | val &= mask;
|
| 135 | val |= set;
|
| 136 | b43_shm_write16(dev, routing, addr, val);
|
| 137 |
|
| 138 | return 0;
|
| 139 | }
|
| 140 |
|
| 141 | static ssize_t shm32read__read_file(struct b43_wldev *dev,
|
| 142 | char *buf, size_t bufsize)
|
| 143 | {
|
| 144 | ssize_t count = 0;
|
| 145 | unsigned int routing, addr;
|
| 146 | u32 val;
|
| 147 |
|
| 148 | routing = dev->dfsentry->shm32read_routing_next;
|
| 149 | addr = dev->dfsentry->shm32read_addr_next;
|
| 150 | if ((routing > B43_MAX_SHM_ROUTING) ||
|
| 151 | (addr > B43_MAX_SHM_ADDR))
|
| 152 | return -EDESTADDRREQ;
|
| 153 |
|
| 154 | val = b43_shm_read32(dev, routing, addr);
|
| 155 | fappend("0x%08X\n", val);
|
| 156 |
|
| 157 | return count;
|
| 158 | }
|
| 159 |
|
| 160 | static int shm32read__write_file(struct b43_wldev *dev,
|
| 161 | const char *buf, size_t count)
|
| 162 | {
|
| 163 | unsigned int routing, addr;
|
| 164 | int res;
|
| 165 |
|
| 166 | res = sscanf(buf, "0x%X 0x%X", &routing, &addr);
|
| 167 | if (res != 2)
|
| 168 | return -EINVAL;
|
| 169 | if (routing > B43_MAX_SHM_ROUTING)
|
| 170 | return -EADDRNOTAVAIL;
|
| 171 | if (addr > B43_MAX_SHM_ADDR)
|
| 172 | return -EADDRNOTAVAIL;
|
| 173 | if (routing == B43_SHM_SHARED) {
|
| 174 | if ((addr % 2) != 0)
|
| 175 | return -EADDRNOTAVAIL;
|
| 176 | }
|
| 177 |
|
| 178 | dev->dfsentry->shm32read_routing_next = routing;
|
| 179 | dev->dfsentry->shm32read_addr_next = addr;
|
| 180 |
|
| 181 | return 0;
|
| 182 | }
|
| 183 |
|
| 184 | static int shm32write__write_file(struct b43_wldev *dev,
|
| 185 | const char *buf, size_t count)
|
| 186 | {
|
| 187 | unsigned int routing, addr, mask, set;
|
| 188 | u32 val;
|
| 189 | int res;
|
| 190 |
|
| 191 | res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X",
|
| 192 | &routing, &addr, &mask, &set);
|
| 193 | if (res != 4)
|
| 194 | return -EINVAL;
|
| 195 | if (routing > B43_MAX_SHM_ROUTING)
|
| 196 | return -EADDRNOTAVAIL;
|
| 197 | if (addr > B43_MAX_SHM_ADDR)
|
| 198 | return -EADDRNOTAVAIL;
|
| 199 | if (routing == B43_SHM_SHARED) {
|
| 200 | if ((addr % 2) != 0)
|
| 201 | return -EADDRNOTAVAIL;
|
| 202 | }
|
| 203 | if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF))
|
| 204 | return -E2BIG;
|
| 205 |
|
| 206 | if (mask == 0)
|
| 207 | val = 0;
|
| 208 | else
|
| 209 | val = b43_shm_read32(dev, routing, addr);
|
| 210 | val &= mask;
|
| 211 | val |= set;
|
| 212 | b43_shm_write32(dev, routing, addr, val);
|
| 213 |
|
| 214 | return 0;
|
| 215 | }
|
| 216 |
|
| 217 |
|
| 218 | #define B43_MAX_MMIO_ACCESS (0xF00 - 1)
|
| 219 |
|
| 220 | static ssize_t mmio16read__read_file(struct b43_wldev *dev,
|
| 221 | char *buf, size_t bufsize)
|
| 222 | {
|
| 223 | ssize_t count = 0;
|
| 224 | unsigned int addr;
|
| 225 | u16 val;
|
| 226 |
|
| 227 | addr = dev->dfsentry->mmio16read_next;
|
| 228 | if (addr > B43_MAX_MMIO_ACCESS)
|
| 229 | return -EDESTADDRREQ;
|
| 230 |
|
| 231 | val = b43_read16(dev, addr);
|
| 232 | fappend("0x%04X\n", val);
|
| 233 |
|
| 234 | return count;
|
| 235 | }
|
| 236 |
|
| 237 | static int mmio16read__write_file(struct b43_wldev *dev,
|
| 238 | const char *buf, size_t count)
|
| 239 | {
|
| 240 | unsigned int addr;
|
| 241 | int res;
|
| 242 |
|
| 243 | res = sscanf(buf, "0x%X", &addr);
|
| 244 | if (res != 1)
|
| 245 | return -EINVAL;
|
| 246 | if (addr > B43_MAX_MMIO_ACCESS)
|
| 247 | return -EADDRNOTAVAIL;
|
| 248 | if ((addr % 2) != 0)
|
| 249 | return -EINVAL;
|
| 250 |
|
| 251 | dev->dfsentry->mmio16read_next = addr;
|
| 252 |
|
| 253 | return 0;
|
| 254 | }
|
| 255 |
|
| 256 | static int mmio16write__write_file(struct b43_wldev *dev,
|
| 257 | const char *buf, size_t count)
|
| 258 | {
|
| 259 | unsigned int addr, mask, set;
|
| 260 | int res;
|
| 261 | u16 val;
|
| 262 |
|
| 263 | res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set);
|
| 264 | if (res != 3)
|
| 265 | return -EINVAL;
|
| 266 | if (addr > B43_MAX_MMIO_ACCESS)
|
| 267 | return -EADDRNOTAVAIL;
|
| 268 | if ((mask > 0xFFFF) || (set > 0xFFFF))
|
| 269 | return -E2BIG;
|
| 270 | if ((addr % 2) != 0)
|
| 271 | return -EINVAL;
|
| 272 |
|
| 273 | if (mask == 0)
|
| 274 | val = 0;
|
| 275 | else
|
| 276 | val = b43_read16(dev, addr);
|
| 277 | val &= mask;
|
| 278 | val |= set;
|
| 279 | b43_write16(dev, addr, val);
|
| 280 |
|
| 281 | return 0;
|
| 282 | }
|
| 283 |
|
| 284 | static ssize_t mmio32read__read_file(struct b43_wldev *dev,
|
| 285 | char *buf, size_t bufsize)
|
| 286 | {
|
| 287 | ssize_t count = 0;
|
| 288 | unsigned int addr;
|
| 289 | u32 val;
|
| 290 |
|
| 291 | addr = dev->dfsentry->mmio32read_next;
|
| 292 | if (addr > B43_MAX_MMIO_ACCESS)
|
| 293 | return -EDESTADDRREQ;
|
| 294 |
|
| 295 | val = b43_read32(dev, addr);
|
| 296 | fappend("0x%08X\n", val);
|
| 297 |
|
| 298 | return count;
|
| 299 | }
|
| 300 |
|
| 301 | static int mmio32read__write_file(struct b43_wldev *dev,
|
| 302 | const char *buf, size_t count)
|
| 303 | {
|
| 304 | unsigned int addr;
|
| 305 | int res;
|
| 306 |
|
| 307 | res = sscanf(buf, "0x%X", &addr);
|
| 308 | if (res != 1)
|
| 309 | return -EINVAL;
|
| 310 | if (addr > B43_MAX_MMIO_ACCESS)
|
| 311 | return -EADDRNOTAVAIL;
|
| 312 | if ((addr % 4) != 0)
|
| 313 | return -EINVAL;
|
| 314 |
|
| 315 | dev->dfsentry->mmio32read_next = addr;
|
| 316 |
|
| 317 | return 0;
|
| 318 | }
|
| 319 |
|
| 320 | static int mmio32write__write_file(struct b43_wldev *dev,
|
| 321 | const char *buf, size_t count)
|
| 322 | {
|
| 323 | unsigned int addr, mask, set;
|
| 324 | int res;
|
| 325 | u32 val;
|
| 326 |
|
| 327 | res = sscanf(buf, "0x%X 0x%X 0x%X", &addr, &mask, &set);
|
| 328 | if (res != 3)
|
| 329 | return -EINVAL;
|
| 330 | if (addr > B43_MAX_MMIO_ACCESS)
|
| 331 | return -EADDRNOTAVAIL;
|
| 332 | if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF))
|
| 333 | return -E2BIG;
|
| 334 | if ((addr % 4) != 0)
|
| 335 | return -EINVAL;
|
| 336 |
|
| 337 | if (mask == 0)
|
| 338 | val = 0;
|
| 339 | else
|
| 340 | val = b43_read32(dev, addr);
|
| 341 | val &= mask;
|
| 342 | val |= set;
|
| 343 | b43_write32(dev, addr, val);
|
| 344 |
|
| 345 | return 0;
|
| 346 | }
|
| 347 |
|
| 348 | static ssize_t txstat_read_file(struct b43_wldev *dev,
|
| 349 | char *buf, size_t bufsize)
|
| 350 | {
|
| 351 | struct b43_txstatus_log *log = &dev->dfsentry->txstatlog;
|
| 352 | ssize_t count = 0;
|
| 353 | int i, idx;
|
| 354 | struct b43_txstatus *stat;
|
| 355 |
|
| 356 | if (log->end < 0) {
|
| 357 | fappend("Nothing transmitted, yet\n");
|
| 358 | goto out;
|
| 359 | }
|
| 360 | fappend("b43 TX status reports:\n\n"
|
| 361 | "index | cookie | seq | phy_stat | frame_count | "
|
| 362 | "rts_count | supp_reason | pm_indicated | "
|
| 363 | "intermediate | for_ampdu | acked\n" "---\n");
|
| 364 | i = log->end + 1;
|
| 365 | idx = 0;
|
| 366 | while (1) {
|
| 367 | if (i == B43_NR_LOGGED_TXSTATUS)
|
| 368 | i = 0;
|
| 369 | stat = &(log->log[i]);
|
| 370 | if (stat->cookie) {
|
| 371 | fappend("%03d | "
|
| 372 | "0x%04X | 0x%04X | 0x%02X | "
|
| 373 | "0x%X | 0x%X | "
|
| 374 | "%u | %u | "
|
| 375 | "%u | %u | %u\n",
|
| 376 | idx,
|
| 377 | stat->cookie, stat->seq, stat->phy_stat,
|
| 378 | stat->frame_count, stat->rts_count,
|
| 379 | stat->supp_reason, stat->pm_indicated,
|
| 380 | stat->intermediate, stat->for_ampdu,
|
| 381 | stat->acked);
|
| 382 | idx++;
|
| 383 | }
|
| 384 | if (i == log->end)
|
| 385 | break;
|
| 386 | i++;
|
| 387 | }
|
| 388 | out:
|
| 389 |
|
| 390 | return count;
|
| 391 | }
|
| 392 |
|
| 393 | static int restart_write_file(struct b43_wldev *dev,
|
| 394 | const char *buf, size_t count)
|
| 395 | {
|
| 396 | int err = 0;
|
| 397 |
|
| 398 | if (count > 0 && buf[0] == '1') {
|
| 399 | b43_controller_restart(dev, "manually restarted");
|
| 400 | } else
|
| 401 | err = -EINVAL;
|
| 402 |
|
| 403 | return err;
|
| 404 | }
|
| 405 |
|
| 406 | static unsigned long calc_expire_secs(unsigned long now,
|
| 407 | unsigned long time,
|
| 408 | unsigned long expire)
|
| 409 | {
|
| 410 | expire = time + expire;
|
| 411 |
|
| 412 | if (time_after(now, expire))
|
| 413 | return 0;
|
| 414 | if (expire < now) {
|
| 415 |
|
| 416 | expire -= MAX_JIFFY_OFFSET;
|
| 417 | now -= MAX_JIFFY_OFFSET;
|
| 418 | }
|
| 419 | B43_WARN_ON(expire < now);
|
| 420 |
|
| 421 | return (expire - now) / HZ;
|
| 422 | }
|
| 423 |
|
| 424 | static ssize_t loctls_read_file(struct b43_wldev *dev,
|
| 425 | char *buf, size_t bufsize)
|
| 426 | {
|
| 427 | ssize_t count = 0;
|
| 428 | struct b43_txpower_lo_control *lo;
|
| 429 | int i, err = 0;
|
| 430 | struct b43_lo_calib *cal;
|
| 431 | unsigned long now = jiffies;
|
| 432 | struct b43_phy *phy = &dev->phy;
|
| 433 |
|
| 434 | if (phy->type != B43_PHYTYPE_G) {
|
| 435 | fappend("Device is not a G-PHY\n");
|
| 436 | err = -ENODEV;
|
| 437 | goto out;
|
| 438 | }
|
| 439 | lo = phy->g->lo_control;
|
| 440 | fappend("-- Local Oscillator calibration data --\n\n");
|
| 441 | fappend("HW-power-control enabled: %d\n",
|
| 442 | dev->phy.hardware_power_control);
|
| 443 | fappend("TX Bias: 0x%02X, TX Magn: 0x%02X (expire in %lu sec)\n",
|
| 444 | lo->tx_bias, lo->tx_magn,
|
| 445 | calc_expire_secs(now, lo->txctl_measured_time,
|
| 446 | B43_LO_TXCTL_EXPIRE));
|
| 447 | fappend("Power Vector: 0x%08X%08X (expires in %lu sec)\n",
|
| 448 | (unsigned int)((lo->power_vector & 0xFFFFFFFF00000000ULL) >> 32),
|
| 449 | (unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL),
|
| 450 | calc_expire_secs(now, lo->pwr_vec_read_time,
|
| 451 | B43_LO_PWRVEC_EXPIRE));
|
| 452 |
|
| 453 | fappend("\nCalibrated settings:\n");
|
| 454 | list_for_each_entry(cal, &lo->calib_list, list) {
|
| 455 | bool active;
|
| 456 |
|
| 457 | active = (b43_compare_bbatt(&cal->bbatt, &phy->g->bbatt) &&
|
| 458 | b43_compare_rfatt(&cal->rfatt, &phy->g->rfatt));
|
| 459 | fappend("BB(%d), RF(%d,%d) -> I=%d, Q=%d "
|
| 460 | "(expires in %lu sec)%s\n",
|
| 461 | cal->bbatt.att,
|
| 462 | cal->rfatt.att, cal->rfatt.with_padmix,
|
| 463 | cal->ctl.i, cal->ctl.q,
|
| 464 | calc_expire_secs(now, cal->calib_time,
|
| 465 | B43_LO_CALIB_EXPIRE),
|
| 466 | active ? " ACTIVE" : "");
|
| 467 | }
|
| 468 |
|
| 469 | fappend("\nUsed RF attenuation values: Value(WithPadmix flag)\n");
|
| 470 | for (i = 0; i < lo->rfatt_list.len; i++) {
|
| 471 | fappend("%u(%d), ",
|
| 472 | lo->rfatt_list.list[i].att,
|
| 473 | lo->rfatt_list.list[i].with_padmix);
|
| 474 | }
|
| 475 | fappend("\n");
|
| 476 | fappend("\nUsed Baseband attenuation values:\n");
|
| 477 | for (i = 0; i < lo->bbatt_list.len; i++) {
|
| 478 | fappend("%u, ",
|
| 479 | lo->bbatt_list.list[i].att);
|
| 480 | }
|
| 481 | fappend("\n");
|
| 482 |
|
| 483 | out:
|
| 484 | return err ? err : count;
|
| 485 | }
|
| 486 |
|
| 487 | #undef fappend
|
| 488 |
|
| 489 | static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,
|
| 490 | size_t count, loff_t *ppos)
|
| 491 | {
|
| 492 | struct b43_wldev *dev;
|
| 493 | const struct b43_debugfs_fops *dfops;
|
| 494 | struct b43_dfs_file *dfile;
|
| 495 | ssize_t ret;
|
| 496 | char *buf;
|
| 497 | const size_t bufsize = 1024 * 16;
|
| 498 | int err = 0;
|
| 499 |
|
| 500 | if (!count)
|
| 501 | return 0;
|
| 502 | dev = file->private_data;
|
| 503 | if (!dev)
|
| 504 | return -ENODEV;
|
| 505 |
|
| 506 | mutex_lock(&dev->wl->mutex);
|
| 507 | if (b43_status(dev) < B43_STAT_INITIALIZED) {
|
| 508 | err = -ENODEV;
|
| 509 | goto out_unlock;
|
| 510 | }
|
| 511 |
|
| 512 | dfops = debugfs_get_aux(file);
|
| 513 | if (!dfops->read) {
|
| 514 | err = -ENOSYS;
|
| 515 | goto out_unlock;
|
| 516 | }
|
| 517 | dfile = fops_to_dfs_file(dev, dfops);
|
| 518 |
|
| 519 | if (!dfile->buffer) {
|
| 520 | buf = kzalloc(bufsize, GFP_KERNEL);
|
| 521 | if (!buf) {
|
| 522 | err = -ENOMEM;
|
| 523 | goto out_unlock;
|
| 524 | }
|
| 525 | ret = dfops->read(dev, buf, bufsize);
|
| 526 | if (ret <= 0) {
|
| 527 | kfree(buf);
|
| 528 | err = ret;
|
| 529 | goto out_unlock;
|
| 530 | }
|
| 531 | dfile->data_len = ret;
|
| 532 | dfile->buffer = buf;
|
| 533 | }
|
| 534 |
|
| 535 | ret = simple_read_from_buffer(userbuf, count, ppos,
|
| 536 | dfile->buffer,
|
| 537 | dfile->data_len);
|
| 538 | if (*ppos >= dfile->data_len) {
|
| 539 | kfree(dfile->buffer);
|
| 540 | dfile->buffer = NULL;
|
| 541 | dfile->data_len = 0;
|
| 542 | }
|
| 543 | out_unlock:
|
| 544 | mutex_unlock(&dev->wl->mutex);
|
| 545 |
|
| 546 | return err ? err : ret;
|
| 547 | }
|
| 548 |
|
| 549 | static ssize_t b43_debugfs_write(struct file *file,
|
| 550 | const char __user *userbuf,
|
| 551 | size_t count, loff_t *ppos)
|
| 552 | {
|
| 553 | struct b43_wldev *dev;
|
| 554 | const struct b43_debugfs_fops *dfops;
|
| 555 | char *buf;
|
| 556 | int err = 0;
|
| 557 |
|
| 558 | if (!count)
|
| 559 | return 0;
|
| 560 | if (count > PAGE_SIZE)
|
| 561 | return -E2BIG;
|
| 562 | dev = file->private_data;
|
| 563 | if (!dev)
|
| 564 | return -ENODEV;
|
| 565 |
|
| 566 | mutex_lock(&dev->wl->mutex);
|
| 567 | if (b43_status(dev) < B43_STAT_INITIALIZED) {
|
| 568 | err = -ENODEV;
|
| 569 | goto out_unlock;
|
| 570 | }
|
| 571 |
|
| 572 | dfops = debugfs_get_aux(file);
|
| 573 | if (!dfops->write) {
|
| 574 | err = -ENOSYS;
|
| 575 | goto out_unlock;
|
| 576 | }
|
| 577 |
|
| 578 | buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
| 579 | if (!buf) {
|
| 580 | err = -ENOMEM;
|
| 581 | goto out_unlock;
|
| 582 | }
|
| 583 | if (copy_from_user(buf, userbuf, count)) {
|
| 584 | err = -EFAULT;
|
| 585 | goto out_freepage;
|
| 586 | }
|
| 587 | err = dfops->write(dev, buf, count);
|
| 588 | if (err)
|
| 589 | goto out_freepage;
|
| 590 |
|
| 591 | out_freepage:
|
| 592 | kfree(buf);
|
| 593 | out_unlock:
|
| 594 | mutex_unlock(&dev->wl->mutex);
|
| 595 |
|
| 596 | return err ? err : count;
|
| 597 | }
|
| 598 |
|
| 599 |
|
| 600 | static struct debugfs_short_fops debugfs_ops = {
|
| 601 | .read = b43_debugfs_read,
|
| 602 | .write = b43_debugfs_write,
|
| 603 | .llseek = generic_file_llseek,
|
| 604 | };
|
| 605 |
|
| 606 | #define B43_DEBUGFS_FOPS(name, _read, _write) \
|
| 607 | static struct b43_debugfs_fops fops_##name = { \
|
| 608 | .read = _read, \
|
| 609 | .write = _write, \
|
| 610 | .file_struct_offset = offsetof(struct b43_dfsentry, \
|
| 611 | file_##name), \
|
| 612 | }
|
| 613 |
|
| 614 | B43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file);
|
| 615 | B43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file);
|
| 616 | B43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file);
|
| 617 | B43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file);
|
| 618 | B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file);
|
| 619 | B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file);
|
| 620 | B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file);
|
| 621 | B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file);
|
| 622 | B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL);
|
| 623 | B43_DEBUGFS_FOPS(restart, NULL, restart_write_file);
|
| 624 | B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL);
|
| 625 |
|
| 626 |
|
| 627 | bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature)
|
| 628 | {
|
| 629 | bool enabled;
|
| 630 |
|
| 631 | enabled = (dev->dfsentry && dev->dfsentry->dyn_debug[feature]);
|
| 632 | if (unlikely(enabled)) {
|
| 633 | |
| 634 | * some dynamic debugging feature.
|
| 635 | b43_modparam_verbose = B43_VERBOSITY_MAX;
|
| 636 | }
|
| 637 |
|
| 638 | return enabled;
|
| 639 | }
|
| 640 |
|
| 641 | static void b43_add_dynamic_debug(struct b43_wldev *dev)
|
| 642 | {
|
| 643 | struct b43_dfsentry *e = dev->dfsentry;
|
| 644 |
|
| 645 | #define add_dyn_dbg(name, id, initstate) do { \
|
| 646 | e->dyn_debug[id] = (initstate); \
|
| 647 | debugfs_create_bool(name, 0600, e->subdir, \
|
| 648 | &(e->dyn_debug[id])); \
|
| 649 | } while (0)
|
| 650 |
|
| 651 | add_dyn_dbg("debug_xmitpower", B43_DBG_XMITPOWER, false);
|
| 652 | add_dyn_dbg("debug_dmaoverflow", B43_DBG_DMAOVERFLOW, false);
|
| 653 | add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, false);
|
| 654 | add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, false);
|
| 655 | add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, false);
|
| 656 | add_dyn_dbg("debug_lo", B43_DBG_LO, false);
|
| 657 | add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, false);
|
| 658 | add_dyn_dbg("debug_keys", B43_DBG_KEYS, false);
|
| 659 | add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, false);
|
| 660 |
|
| 661 | #undef add_dyn_dbg
|
| 662 | }
|
| 663 |
|
| 664 | void b43_debugfs_add_device(struct b43_wldev *dev)
|
| 665 | {
|
| 666 | struct b43_dfsentry *e;
|
| 667 | struct b43_txstatus_log *log;
|
| 668 | char devdir[16];
|
| 669 |
|
| 670 | B43_WARN_ON(!dev);
|
| 671 | e = kzalloc_obj(*e);
|
| 672 | if (!e) {
|
| 673 | b43err(dev->wl, "debugfs: add device OOM\n");
|
| 674 | return;
|
| 675 | }
|
| 676 | e->dev = dev;
|
| 677 | log = &e->txstatlog;
|
| 678 | log->log = kzalloc_objs(struct b43_txstatus, B43_NR_LOGGED_TXSTATUS);
|
| 679 | if (!log->log) {
|
| 680 | b43err(dev->wl, "debugfs: add device txstatus OOM\n");
|
| 681 | kfree(e);
|
| 682 | return;
|
| 683 | }
|
| 684 | log->end = -1;
|
| 685 |
|
| 686 | dev->dfsentry = e;
|
| 687 |
|
| 688 | snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));
|
| 689 | e->subdir = debugfs_create_dir(devdir, rootdir);
|
| 690 |
|
| 691 | e->mmio16read_next = 0xFFFF;
|
| 692 | e->mmio32read_next = 0xFFFF;
|
| 693 | e->shm16read_routing_next = 0xFFFFFFFF;
|
| 694 | e->shm16read_addr_next = 0xFFFFFFFF;
|
| 695 | e->shm32read_routing_next = 0xFFFFFFFF;
|
| 696 | e->shm32read_addr_next = 0xFFFFFFFF;
|
| 697 |
|
| 698 | #define ADD_FILE(name, mode) \
|
| 699 | do { \
|
| 700 | debugfs_create_file_aux(__stringify(name), \
|
| 701 | mode, e->subdir, dev, \
|
| 702 | &fops_##name, &debugfs_ops); \
|
| 703 | } while (0)
|
| 704 |
|
| 705 |
|
| 706 | ADD_FILE(shm16read, 0600);
|
| 707 | ADD_FILE(shm16write, 0200);
|
| 708 | ADD_FILE(shm32read, 0600);
|
| 709 | ADD_FILE(shm32write, 0200);
|
| 710 | ADD_FILE(mmio16read, 0600);
|
| 711 | ADD_FILE(mmio16write, 0200);
|
| 712 | ADD_FILE(mmio32read, 0600);
|
| 713 | ADD_FILE(mmio32write, 0200);
|
| 714 | ADD_FILE(txstat, 0400);
|
| 715 | ADD_FILE(restart, 0200);
|
| 716 | ADD_FILE(loctls, 0400);
|
| 717 |
|
| 718 | #undef ADD_FILE
|
| 719 |
|
| 720 | b43_add_dynamic_debug(dev);
|
| 721 | }
|
| 722 |
|
| 723 | void b43_debugfs_remove_device(struct b43_wldev *dev)
|
| 724 | {
|
| 725 | struct b43_dfsentry *e;
|
| 726 |
|
| 727 | if (!dev)
|
| 728 | return;
|
| 729 | e = dev->dfsentry;
|
| 730 | if (!e)
|
| 731 | return;
|
| 732 |
|
| 733 | debugfs_remove(e->subdir);
|
| 734 | kfree(e->txstatlog.log);
|
| 735 | kfree(e);
|
| 736 | }
|
| 737 |
|
| 738 | void b43_debugfs_log_txstat(struct b43_wldev *dev,
|
| 739 | const struct b43_txstatus *status)
|
| 740 | {
|
| 741 | struct b43_dfsentry *e = dev->dfsentry;
|
| 742 | struct b43_txstatus_log *log;
|
| 743 | struct b43_txstatus *cur;
|
| 744 | int i;
|
| 745 |
|
| 746 | if (!e)
|
| 747 | return;
|
| 748 | log = &e->txstatlog;
|
| 749 | i = log->end + 1;
|
| 750 | if (i == B43_NR_LOGGED_TXSTATUS)
|
| 751 | i = 0;
|
| 752 | log->end = i;
|
| 753 | cur = &(log->log[i]);
|
| 754 | memcpy(cur, status, sizeof(*cur));
|
| 755 | }
|
| 756 |
|
| 757 | void b43_debugfs_init(void)
|
| 758 | {
|
| 759 | rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
|
| 760 | }
|
| 761 |
|
| 762 | void b43_debugfs_exit(void)
|
| 763 | {
|
| 764 | debugfs_remove(rootdir);
|
| 765 | }
|