at v2.6.32-rc4 481 lines 13 kB view raw
1/* 2 * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz> 3 * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 */ 9 10#include <linux/kernel.h> 11#include <linux/device.h> 12#include <linux/if.h> 13#include <linux/interrupt.h> 14#include <linux/netdevice.h> 15#include <linux/rtnetlink.h> 16#include <linux/notifier.h> 17#include <net/mac80211.h> 18#include <net/cfg80211.h> 19#include "ieee80211_i.h" 20#include "rate.h" 21#include "debugfs.h" 22#include "debugfs_netdev.h" 23 24static ssize_t ieee80211_if_read( 25 struct ieee80211_sub_if_data *sdata, 26 char __user *userbuf, 27 size_t count, loff_t *ppos, 28 ssize_t (*format)(const struct ieee80211_sub_if_data *, char *, int)) 29{ 30 char buf[70]; 31 ssize_t ret = -EINVAL; 32 33 read_lock(&dev_base_lock); 34 if (sdata->dev->reg_state == NETREG_REGISTERED) 35 ret = (*format)(sdata, buf, sizeof(buf)); 36 read_unlock(&dev_base_lock); 37 38 if (ret != -EINVAL) 39 ret = simple_read_from_buffer(userbuf, count, ppos, buf, ret); 40 41 return ret; 42} 43 44#define IEEE80211_IF_FMT(name, field, format_string) \ 45static ssize_t ieee80211_if_fmt_##name( \ 46 const struct ieee80211_sub_if_data *sdata, char *buf, \ 47 int buflen) \ 48{ \ 49 return scnprintf(buf, buflen, format_string, sdata->field); \ 50} 51#define IEEE80211_IF_FMT_DEC(name, field) \ 52 IEEE80211_IF_FMT(name, field, "%d\n") 53#define IEEE80211_IF_FMT_HEX(name, field) \ 54 IEEE80211_IF_FMT(name, field, "%#x\n") 55#define IEEE80211_IF_FMT_SIZE(name, field) \ 56 IEEE80211_IF_FMT(name, field, "%zd\n") 57 58#define IEEE80211_IF_FMT_ATOMIC(name, field) \ 59static ssize_t ieee80211_if_fmt_##name( \ 60 const struct ieee80211_sub_if_data *sdata, \ 61 char *buf, int buflen) \ 62{ \ 63 return scnprintf(buf, buflen, "%d\n", atomic_read(&sdata->field));\ 64} 65 66#define IEEE80211_IF_FMT_MAC(name, field) \ 67static ssize_t ieee80211_if_fmt_##name( \ 68 const struct ieee80211_sub_if_data *sdata, char *buf, \ 69 int buflen) \ 70{ \ 71 return scnprintf(buf, buflen, "%pM\n", sdata->field); \ 72} 73 74#define __IEEE80211_IF_FILE(name) \ 75static ssize_t ieee80211_if_read_##name(struct file *file, \ 76 char __user *userbuf, \ 77 size_t count, loff_t *ppos) \ 78{ \ 79 return ieee80211_if_read(file->private_data, \ 80 userbuf, count, ppos, \ 81 ieee80211_if_fmt_##name); \ 82} \ 83static const struct file_operations name##_ops = { \ 84 .read = ieee80211_if_read_##name, \ 85 .open = mac80211_open_file_generic, \ 86} 87 88#define IEEE80211_IF_FILE(name, field, format) \ 89 IEEE80211_IF_FMT_##format(name, field) \ 90 __IEEE80211_IF_FILE(name) 91 92/* common attributes */ 93IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); 94IEEE80211_IF_FILE(force_unicast_rateidx, force_unicast_rateidx, DEC); 95IEEE80211_IF_FILE(max_ratectrl_rateidx, max_ratectrl_rateidx, DEC); 96 97/* STA attributes */ 98IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); 99IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); 100IEEE80211_IF_FILE(capab, u.mgd.capab, HEX); 101 102/* AP attributes */ 103IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); 104IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); 105 106static ssize_t ieee80211_if_fmt_num_buffered_multicast( 107 const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) 108{ 109 return scnprintf(buf, buflen, "%u\n", 110 skb_queue_len(&sdata->u.ap.ps_bc_buf)); 111} 112__IEEE80211_IF_FILE(num_buffered_multicast); 113 114/* WDS attributes */ 115IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); 116 117#ifdef CONFIG_MAC80211_MESH 118/* Mesh stats attributes */ 119IEEE80211_IF_FILE(fwded_mcast, u.mesh.mshstats.fwded_mcast, DEC); 120IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC); 121IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC); 122IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC); 123IEEE80211_IF_FILE(dropped_frames_no_route, 124 u.mesh.mshstats.dropped_frames_no_route, DEC); 125IEEE80211_IF_FILE(estab_plinks, u.mesh.mshstats.estab_plinks, ATOMIC); 126 127/* Mesh parameters */ 128IEEE80211_IF_FILE(dot11MeshMaxRetries, 129 u.mesh.mshcfg.dot11MeshMaxRetries, DEC); 130IEEE80211_IF_FILE(dot11MeshRetryTimeout, 131 u.mesh.mshcfg.dot11MeshRetryTimeout, DEC); 132IEEE80211_IF_FILE(dot11MeshConfirmTimeout, 133 u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC); 134IEEE80211_IF_FILE(dot11MeshHoldingTimeout, 135 u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC); 136IEEE80211_IF_FILE(dot11MeshTTL, u.mesh.mshcfg.dot11MeshTTL, DEC); 137IEEE80211_IF_FILE(auto_open_plinks, u.mesh.mshcfg.auto_open_plinks, DEC); 138IEEE80211_IF_FILE(dot11MeshMaxPeerLinks, 139 u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC); 140IEEE80211_IF_FILE(dot11MeshHWMPactivePathTimeout, 141 u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC); 142IEEE80211_IF_FILE(dot11MeshHWMPpreqMinInterval, 143 u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC); 144IEEE80211_IF_FILE(dot11MeshHWMPnetDiameterTraversalTime, 145 u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC); 146IEEE80211_IF_FILE(dot11MeshHWMPmaxPREQretries, 147 u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC); 148IEEE80211_IF_FILE(path_refresh_time, 149 u.mesh.mshcfg.path_refresh_time, DEC); 150IEEE80211_IF_FILE(min_discovery_timeout, 151 u.mesh.mshcfg.min_discovery_timeout, DEC); 152#endif 153 154 155#define DEBUGFS_ADD(name, type)\ 156 sdata->debugfs.type.name = debugfs_create_file(#name, 0400,\ 157 sdata->debugfsdir, sdata, &name##_ops); 158 159static void add_sta_files(struct ieee80211_sub_if_data *sdata) 160{ 161 DEBUGFS_ADD(drop_unencrypted, sta); 162 DEBUGFS_ADD(force_unicast_rateidx, sta); 163 DEBUGFS_ADD(max_ratectrl_rateidx, sta); 164 165 DEBUGFS_ADD(bssid, sta); 166 DEBUGFS_ADD(aid, sta); 167 DEBUGFS_ADD(capab, sta); 168} 169 170static void add_ap_files(struct ieee80211_sub_if_data *sdata) 171{ 172 DEBUGFS_ADD(drop_unencrypted, ap); 173 DEBUGFS_ADD(force_unicast_rateidx, ap); 174 DEBUGFS_ADD(max_ratectrl_rateidx, ap); 175 176 DEBUGFS_ADD(num_sta_ps, ap); 177 DEBUGFS_ADD(dtim_count, ap); 178 DEBUGFS_ADD(num_buffered_multicast, ap); 179} 180 181static void add_wds_files(struct ieee80211_sub_if_data *sdata) 182{ 183 DEBUGFS_ADD(drop_unencrypted, wds); 184 DEBUGFS_ADD(force_unicast_rateidx, wds); 185 DEBUGFS_ADD(max_ratectrl_rateidx, wds); 186 187 DEBUGFS_ADD(peer, wds); 188} 189 190static void add_vlan_files(struct ieee80211_sub_if_data *sdata) 191{ 192 DEBUGFS_ADD(drop_unencrypted, vlan); 193 DEBUGFS_ADD(force_unicast_rateidx, vlan); 194 DEBUGFS_ADD(max_ratectrl_rateidx, vlan); 195} 196 197static void add_monitor_files(struct ieee80211_sub_if_data *sdata) 198{ 199} 200 201#ifdef CONFIG_MAC80211_MESH 202#define MESHSTATS_ADD(name)\ 203 sdata->mesh_stats.name = debugfs_create_file(#name, 0400,\ 204 sdata->mesh_stats_dir, sdata, &name##_ops); 205 206static void add_mesh_stats(struct ieee80211_sub_if_data *sdata) 207{ 208 sdata->mesh_stats_dir = debugfs_create_dir("mesh_stats", 209 sdata->debugfsdir); 210 MESHSTATS_ADD(fwded_mcast); 211 MESHSTATS_ADD(fwded_unicast); 212 MESHSTATS_ADD(fwded_frames); 213 MESHSTATS_ADD(dropped_frames_ttl); 214 MESHSTATS_ADD(dropped_frames_no_route); 215 MESHSTATS_ADD(estab_plinks); 216} 217 218#define MESHPARAMS_ADD(name)\ 219 sdata->mesh_config.name = debugfs_create_file(#name, 0600,\ 220 sdata->mesh_config_dir, sdata, &name##_ops); 221 222static void add_mesh_config(struct ieee80211_sub_if_data *sdata) 223{ 224 sdata->mesh_config_dir = debugfs_create_dir("mesh_config", 225 sdata->debugfsdir); 226 MESHPARAMS_ADD(dot11MeshMaxRetries); 227 MESHPARAMS_ADD(dot11MeshRetryTimeout); 228 MESHPARAMS_ADD(dot11MeshConfirmTimeout); 229 MESHPARAMS_ADD(dot11MeshHoldingTimeout); 230 MESHPARAMS_ADD(dot11MeshTTL); 231 MESHPARAMS_ADD(auto_open_plinks); 232 MESHPARAMS_ADD(dot11MeshMaxPeerLinks); 233 MESHPARAMS_ADD(dot11MeshHWMPactivePathTimeout); 234 MESHPARAMS_ADD(dot11MeshHWMPpreqMinInterval); 235 MESHPARAMS_ADD(dot11MeshHWMPnetDiameterTraversalTime); 236 MESHPARAMS_ADD(dot11MeshHWMPmaxPREQretries); 237 MESHPARAMS_ADD(path_refresh_time); 238 MESHPARAMS_ADD(min_discovery_timeout); 239} 240#endif 241 242static void add_files(struct ieee80211_sub_if_data *sdata) 243{ 244 if (!sdata->debugfsdir) 245 return; 246 247 switch (sdata->vif.type) { 248 case NL80211_IFTYPE_MESH_POINT: 249#ifdef CONFIG_MAC80211_MESH 250 add_mesh_stats(sdata); 251 add_mesh_config(sdata); 252#endif 253 break; 254 case NL80211_IFTYPE_STATION: 255 add_sta_files(sdata); 256 break; 257 case NL80211_IFTYPE_ADHOC: 258 /* XXX */ 259 break; 260 case NL80211_IFTYPE_AP: 261 add_ap_files(sdata); 262 break; 263 case NL80211_IFTYPE_WDS: 264 add_wds_files(sdata); 265 break; 266 case NL80211_IFTYPE_MONITOR: 267 add_monitor_files(sdata); 268 break; 269 case NL80211_IFTYPE_AP_VLAN: 270 add_vlan_files(sdata); 271 break; 272 default: 273 break; 274 } 275} 276 277#define DEBUGFS_DEL(name, type) \ 278 do { \ 279 debugfs_remove(sdata->debugfs.type.name); \ 280 sdata->debugfs.type.name = NULL; \ 281 } while (0) 282 283static void del_sta_files(struct ieee80211_sub_if_data *sdata) 284{ 285 DEBUGFS_DEL(drop_unencrypted, sta); 286 DEBUGFS_DEL(force_unicast_rateidx, sta); 287 DEBUGFS_DEL(max_ratectrl_rateidx, sta); 288 289 DEBUGFS_DEL(bssid, sta); 290 DEBUGFS_DEL(aid, sta); 291 DEBUGFS_DEL(capab, sta); 292} 293 294static void del_ap_files(struct ieee80211_sub_if_data *sdata) 295{ 296 DEBUGFS_DEL(drop_unencrypted, ap); 297 DEBUGFS_DEL(force_unicast_rateidx, ap); 298 DEBUGFS_DEL(max_ratectrl_rateidx, ap); 299 300 DEBUGFS_DEL(num_sta_ps, ap); 301 DEBUGFS_DEL(dtim_count, ap); 302 DEBUGFS_DEL(num_buffered_multicast, ap); 303} 304 305static void del_wds_files(struct ieee80211_sub_if_data *sdata) 306{ 307 DEBUGFS_DEL(drop_unencrypted, wds); 308 DEBUGFS_DEL(force_unicast_rateidx, wds); 309 DEBUGFS_DEL(max_ratectrl_rateidx, wds); 310 311 DEBUGFS_DEL(peer, wds); 312} 313 314static void del_vlan_files(struct ieee80211_sub_if_data *sdata) 315{ 316 DEBUGFS_DEL(drop_unencrypted, vlan); 317 DEBUGFS_DEL(force_unicast_rateidx, vlan); 318 DEBUGFS_DEL(max_ratectrl_rateidx, vlan); 319} 320 321static void del_monitor_files(struct ieee80211_sub_if_data *sdata) 322{ 323} 324 325#ifdef CONFIG_MAC80211_MESH 326#define MESHSTATS_DEL(name) \ 327 do { \ 328 debugfs_remove(sdata->mesh_stats.name); \ 329 sdata->mesh_stats.name = NULL; \ 330 } while (0) 331 332static void del_mesh_stats(struct ieee80211_sub_if_data *sdata) 333{ 334 MESHSTATS_DEL(fwded_mcast); 335 MESHSTATS_DEL(fwded_unicast); 336 MESHSTATS_DEL(fwded_frames); 337 MESHSTATS_DEL(dropped_frames_ttl); 338 MESHSTATS_DEL(dropped_frames_no_route); 339 MESHSTATS_DEL(estab_plinks); 340 debugfs_remove(sdata->mesh_stats_dir); 341 sdata->mesh_stats_dir = NULL; 342} 343 344#define MESHPARAMS_DEL(name) \ 345 do { \ 346 debugfs_remove(sdata->mesh_config.name); \ 347 sdata->mesh_config.name = NULL; \ 348 } while (0) 349 350static void del_mesh_config(struct ieee80211_sub_if_data *sdata) 351{ 352 MESHPARAMS_DEL(dot11MeshMaxRetries); 353 MESHPARAMS_DEL(dot11MeshRetryTimeout); 354 MESHPARAMS_DEL(dot11MeshConfirmTimeout); 355 MESHPARAMS_DEL(dot11MeshHoldingTimeout); 356 MESHPARAMS_DEL(dot11MeshTTL); 357 MESHPARAMS_DEL(auto_open_plinks); 358 MESHPARAMS_DEL(dot11MeshMaxPeerLinks); 359 MESHPARAMS_DEL(dot11MeshHWMPactivePathTimeout); 360 MESHPARAMS_DEL(dot11MeshHWMPpreqMinInterval); 361 MESHPARAMS_DEL(dot11MeshHWMPnetDiameterTraversalTime); 362 MESHPARAMS_DEL(dot11MeshHWMPmaxPREQretries); 363 MESHPARAMS_DEL(path_refresh_time); 364 MESHPARAMS_DEL(min_discovery_timeout); 365 debugfs_remove(sdata->mesh_config_dir); 366 sdata->mesh_config_dir = NULL; 367} 368#endif 369 370static void del_files(struct ieee80211_sub_if_data *sdata) 371{ 372 if (!sdata->debugfsdir) 373 return; 374 375 switch (sdata->vif.type) { 376 case NL80211_IFTYPE_MESH_POINT: 377#ifdef CONFIG_MAC80211_MESH 378 del_mesh_stats(sdata); 379 del_mesh_config(sdata); 380#endif 381 break; 382 case NL80211_IFTYPE_STATION: 383 del_sta_files(sdata); 384 break; 385 case NL80211_IFTYPE_ADHOC: 386 /* XXX */ 387 break; 388 case NL80211_IFTYPE_AP: 389 del_ap_files(sdata); 390 break; 391 case NL80211_IFTYPE_WDS: 392 del_wds_files(sdata); 393 break; 394 case NL80211_IFTYPE_MONITOR: 395 del_monitor_files(sdata); 396 break; 397 case NL80211_IFTYPE_AP_VLAN: 398 del_vlan_files(sdata); 399 break; 400 default: 401 break; 402 } 403} 404 405static int notif_registered; 406 407void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata) 408{ 409 char buf[10+IFNAMSIZ]; 410 411 if (!notif_registered) 412 return; 413 414 sprintf(buf, "netdev:%s", sdata->dev->name); 415 sdata->debugfsdir = debugfs_create_dir(buf, 416 sdata->local->hw.wiphy->debugfsdir); 417 add_files(sdata); 418} 419 420void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata) 421{ 422 del_files(sdata); 423 debugfs_remove(sdata->debugfsdir); 424 sdata->debugfsdir = NULL; 425} 426 427static int netdev_notify(struct notifier_block *nb, 428 unsigned long state, 429 void *ndev) 430{ 431 struct net_device *dev = ndev; 432 struct dentry *dir; 433 struct ieee80211_sub_if_data *sdata; 434 char buf[10+IFNAMSIZ]; 435 436 if (state != NETDEV_CHANGENAME) 437 return 0; 438 439 if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy) 440 return 0; 441 442 if (dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid) 443 return 0; 444 445 sdata = IEEE80211_DEV_TO_SUB_IF(dev); 446 447 dir = sdata->debugfsdir; 448 449 if (!dir) 450 return 0; 451 452 sprintf(buf, "netdev:%s", dev->name); 453 if (!debugfs_rename(dir->d_parent, dir, dir->d_parent, buf)) 454 printk(KERN_ERR "mac80211: debugfs: failed to rename debugfs " 455 "dir to %s\n", buf); 456 457 return 0; 458} 459 460static struct notifier_block mac80211_debugfs_netdev_notifier = { 461 .notifier_call = netdev_notify, 462}; 463 464void ieee80211_debugfs_netdev_init(void) 465{ 466 int err; 467 468 err = register_netdevice_notifier(&mac80211_debugfs_netdev_notifier); 469 if (err) { 470 printk(KERN_ERR 471 "mac80211: failed to install netdev notifier," 472 " disabling per-netdev debugfs!\n"); 473 } else 474 notif_registered = 1; 475} 476 477void ieee80211_debugfs_netdev_exit(void) 478{ 479 unregister_netdevice_notifier(&mac80211_debugfs_netdev_notifier); 480 notif_registered = 0; 481}