linux/net/mac80211/debugfs_netdev.c
<<
>>
Prefs
   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(state, u.mgd.state, DEC);
  99IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
 100IEEE80211_IF_FILE(prev_bssid, u.mgd.prev_bssid, MAC);
 101IEEE80211_IF_FILE(ssid_len, u.mgd.ssid_len, SIZE);
 102IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);
 103IEEE80211_IF_FILE(ap_capab, u.mgd.ap_capab, HEX);
 104IEEE80211_IF_FILE(capab, u.mgd.capab, HEX);
 105IEEE80211_IF_FILE(extra_ie_len, u.mgd.extra_ie_len, SIZE);
 106IEEE80211_IF_FILE(auth_tries, u.mgd.auth_tries, DEC);
 107IEEE80211_IF_FILE(assoc_tries, u.mgd.assoc_tries, DEC);
 108IEEE80211_IF_FILE(auth_algs, u.mgd.auth_algs, HEX);
 109IEEE80211_IF_FILE(auth_alg, u.mgd.auth_alg, DEC);
 110IEEE80211_IF_FILE(auth_transaction, u.mgd.auth_transaction, DEC);
 111
 112static ssize_t ieee80211_if_fmt_flags(
 113        const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
 114{
 115        return scnprintf(buf, buflen, "%s%s%s%s%s%s%s\n",
 116                 sdata->u.mgd.flags & IEEE80211_STA_SSID_SET ? "SSID\n" : "",
 117                 sdata->u.mgd.flags & IEEE80211_STA_BSSID_SET ? "BSSID\n" : "",
 118                 sdata->u.mgd.flags & IEEE80211_STA_PREV_BSSID_SET ? "prev BSSID\n" : "",
 119                 sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED ? "AUTH\n" : "",
 120                 sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED ? "ASSOC\n" : "",
 121                 sdata->u.mgd.flags & IEEE80211_STA_PROBEREQ_POLL ? "PROBEREQ POLL\n" : "",
 122                 sdata->vif.bss_conf.use_cts_prot ? "CTS prot\n" : "");
 123}
 124__IEEE80211_IF_FILE(flags);
 125
 126/* AP attributes */
 127IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC);
 128IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC);
 129
 130static ssize_t ieee80211_if_fmt_num_buffered_multicast(
 131        const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
 132{
 133        return scnprintf(buf, buflen, "%u\n",
 134                         skb_queue_len(&sdata->u.ap.ps_bc_buf));
 135}
 136__IEEE80211_IF_FILE(num_buffered_multicast);
 137
 138/* WDS attributes */
 139IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
 140
 141#ifdef CONFIG_MAC80211_MESH
 142/* Mesh stats attributes */
 143IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC);
 144IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC);
 145IEEE80211_IF_FILE(dropped_frames_no_route,
 146                u.mesh.mshstats.dropped_frames_no_route, DEC);
 147IEEE80211_IF_FILE(estab_plinks, u.mesh.mshstats.estab_plinks, ATOMIC);
 148
 149/* Mesh parameters */
 150IEEE80211_IF_FILE(dot11MeshMaxRetries,
 151                u.mesh.mshcfg.dot11MeshMaxRetries, DEC);
 152IEEE80211_IF_FILE(dot11MeshRetryTimeout,
 153                u.mesh.mshcfg.dot11MeshRetryTimeout, DEC);
 154IEEE80211_IF_FILE(dot11MeshConfirmTimeout,
 155                u.mesh.mshcfg.dot11MeshConfirmTimeout, DEC);
 156IEEE80211_IF_FILE(dot11MeshHoldingTimeout,
 157                u.mesh.mshcfg.dot11MeshHoldingTimeout, DEC);
 158IEEE80211_IF_FILE(dot11MeshTTL, u.mesh.mshcfg.dot11MeshTTL, DEC);
 159IEEE80211_IF_FILE(auto_open_plinks, u.mesh.mshcfg.auto_open_plinks, DEC);
 160IEEE80211_IF_FILE(dot11MeshMaxPeerLinks,
 161                u.mesh.mshcfg.dot11MeshMaxPeerLinks, DEC);
 162IEEE80211_IF_FILE(dot11MeshHWMPactivePathTimeout,
 163                u.mesh.mshcfg.dot11MeshHWMPactivePathTimeout, DEC);
 164IEEE80211_IF_FILE(dot11MeshHWMPpreqMinInterval,
 165                u.mesh.mshcfg.dot11MeshHWMPpreqMinInterval, DEC);
 166IEEE80211_IF_FILE(dot11MeshHWMPnetDiameterTraversalTime,
 167                u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime, DEC);
 168IEEE80211_IF_FILE(dot11MeshHWMPmaxPREQretries,
 169                u.mesh.mshcfg.dot11MeshHWMPmaxPREQretries, DEC);
 170IEEE80211_IF_FILE(path_refresh_time,
 171                u.mesh.mshcfg.path_refresh_time, DEC);
 172IEEE80211_IF_FILE(min_discovery_timeout,
 173                u.mesh.mshcfg.min_discovery_timeout, DEC);
 174#endif
 175
 176
 177#define DEBUGFS_ADD(name, type)\
 178        sdata->debugfs.type.name = debugfs_create_file(#name, 0400,\
 179                sdata->debugfsdir, sdata, &name##_ops);
 180
 181static void add_sta_files(struct ieee80211_sub_if_data *sdata)
 182{
 183        DEBUGFS_ADD(drop_unencrypted, sta);
 184        DEBUGFS_ADD(force_unicast_rateidx, sta);
 185        DEBUGFS_ADD(max_ratectrl_rateidx, sta);
 186
 187        DEBUGFS_ADD(state, sta);
 188        DEBUGFS_ADD(bssid, sta);
 189        DEBUGFS_ADD(prev_bssid, sta);
 190        DEBUGFS_ADD(ssid_len, sta);
 191        DEBUGFS_ADD(aid, sta);
 192        DEBUGFS_ADD(ap_capab, sta);
 193        DEBUGFS_ADD(capab, sta);
 194        DEBUGFS_ADD(extra_ie_len, sta);
 195        DEBUGFS_ADD(auth_tries, sta);
 196        DEBUGFS_ADD(assoc_tries, sta);
 197        DEBUGFS_ADD(auth_algs, sta);
 198        DEBUGFS_ADD(auth_alg, sta);
 199        DEBUGFS_ADD(auth_transaction, sta);
 200        DEBUGFS_ADD(flags, sta);
 201}
 202
 203static void add_ap_files(struct ieee80211_sub_if_data *sdata)
 204{
 205        DEBUGFS_ADD(drop_unencrypted, ap);
 206        DEBUGFS_ADD(force_unicast_rateidx, ap);
 207        DEBUGFS_ADD(max_ratectrl_rateidx, ap);
 208
 209        DEBUGFS_ADD(num_sta_ps, ap);
 210        DEBUGFS_ADD(dtim_count, ap);
 211        DEBUGFS_ADD(num_buffered_multicast, ap);
 212}
 213
 214static void add_wds_files(struct ieee80211_sub_if_data *sdata)
 215{
 216        DEBUGFS_ADD(drop_unencrypted, wds);
 217        DEBUGFS_ADD(force_unicast_rateidx, wds);
 218        DEBUGFS_ADD(max_ratectrl_rateidx, wds);
 219
 220        DEBUGFS_ADD(peer, wds);
 221}
 222
 223static void add_vlan_files(struct ieee80211_sub_if_data *sdata)
 224{
 225        DEBUGFS_ADD(drop_unencrypted, vlan);
 226        DEBUGFS_ADD(force_unicast_rateidx, vlan);
 227        DEBUGFS_ADD(max_ratectrl_rateidx, vlan);
 228}
 229
 230static void add_monitor_files(struct ieee80211_sub_if_data *sdata)
 231{
 232}
 233
 234#ifdef CONFIG_MAC80211_MESH
 235#define MESHSTATS_ADD(name)\
 236        sdata->mesh_stats.name = debugfs_create_file(#name, 0400,\
 237                sdata->mesh_stats_dir, sdata, &name##_ops);
 238
 239static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
 240{
 241        sdata->mesh_stats_dir = debugfs_create_dir("mesh_stats",
 242                                sdata->debugfsdir);
 243        MESHSTATS_ADD(fwded_frames);
 244        MESHSTATS_ADD(dropped_frames_ttl);
 245        MESHSTATS_ADD(dropped_frames_no_route);
 246        MESHSTATS_ADD(estab_plinks);
 247}
 248
 249#define MESHPARAMS_ADD(name)\
 250        sdata->mesh_config.name = debugfs_create_file(#name, 0600,\
 251                sdata->mesh_config_dir, sdata, &name##_ops);
 252
 253static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
 254{
 255        sdata->mesh_config_dir = debugfs_create_dir("mesh_config",
 256                                sdata->debugfsdir);
 257        MESHPARAMS_ADD(dot11MeshMaxRetries);
 258        MESHPARAMS_ADD(dot11MeshRetryTimeout);
 259        MESHPARAMS_ADD(dot11MeshConfirmTimeout);
 260        MESHPARAMS_ADD(dot11MeshHoldingTimeout);
 261        MESHPARAMS_ADD(dot11MeshTTL);
 262        MESHPARAMS_ADD(auto_open_plinks);
 263        MESHPARAMS_ADD(dot11MeshMaxPeerLinks);
 264        MESHPARAMS_ADD(dot11MeshHWMPactivePathTimeout);
 265        MESHPARAMS_ADD(dot11MeshHWMPpreqMinInterval);
 266        MESHPARAMS_ADD(dot11MeshHWMPnetDiameterTraversalTime);
 267        MESHPARAMS_ADD(dot11MeshHWMPmaxPREQretries);
 268        MESHPARAMS_ADD(path_refresh_time);
 269        MESHPARAMS_ADD(min_discovery_timeout);
 270}
 271#endif
 272
 273static void add_files(struct ieee80211_sub_if_data *sdata)
 274{
 275        if (!sdata->debugfsdir)
 276                return;
 277
 278        switch (sdata->vif.type) {
 279        case NL80211_IFTYPE_MESH_POINT:
 280#ifdef CONFIG_MAC80211_MESH
 281                add_mesh_stats(sdata);
 282                add_mesh_config(sdata);
 283#endif
 284                break;
 285        case NL80211_IFTYPE_STATION:
 286                add_sta_files(sdata);
 287                break;
 288        case NL80211_IFTYPE_ADHOC:
 289                /* XXX */
 290                break;
 291        case NL80211_IFTYPE_AP:
 292                add_ap_files(sdata);
 293                break;
 294        case NL80211_IFTYPE_WDS:
 295                add_wds_files(sdata);
 296                break;
 297        case NL80211_IFTYPE_MONITOR:
 298                add_monitor_files(sdata);
 299                break;
 300        case NL80211_IFTYPE_AP_VLAN:
 301                add_vlan_files(sdata);
 302                break;
 303        default:
 304                break;
 305        }
 306}
 307
 308#define DEBUGFS_DEL(name, type)                                 \
 309        do {                                                    \
 310                debugfs_remove(sdata->debugfs.type.name);       \
 311                sdata->debugfs.type.name = NULL;                \
 312        } while (0)
 313
 314static void del_sta_files(struct ieee80211_sub_if_data *sdata)
 315{
 316        DEBUGFS_DEL(drop_unencrypted, sta);
 317        DEBUGFS_DEL(force_unicast_rateidx, sta);
 318        DEBUGFS_DEL(max_ratectrl_rateidx, sta);
 319
 320        DEBUGFS_DEL(state, sta);
 321        DEBUGFS_DEL(bssid, sta);
 322        DEBUGFS_DEL(prev_bssid, sta);
 323        DEBUGFS_DEL(ssid_len, sta);
 324        DEBUGFS_DEL(aid, sta);
 325        DEBUGFS_DEL(ap_capab, sta);
 326        DEBUGFS_DEL(capab, sta);
 327        DEBUGFS_DEL(extra_ie_len, sta);
 328        DEBUGFS_DEL(auth_tries, sta);
 329        DEBUGFS_DEL(assoc_tries, sta);
 330        DEBUGFS_DEL(auth_algs, sta);
 331        DEBUGFS_DEL(auth_alg, sta);
 332        DEBUGFS_DEL(auth_transaction, sta);
 333        DEBUGFS_DEL(flags, sta);
 334}
 335
 336static void del_ap_files(struct ieee80211_sub_if_data *sdata)
 337{
 338        DEBUGFS_DEL(drop_unencrypted, ap);
 339        DEBUGFS_DEL(force_unicast_rateidx, ap);
 340        DEBUGFS_DEL(max_ratectrl_rateidx, ap);
 341
 342        DEBUGFS_DEL(num_sta_ps, ap);
 343        DEBUGFS_DEL(dtim_count, ap);
 344        DEBUGFS_DEL(num_buffered_multicast, ap);
 345}
 346
 347static void del_wds_files(struct ieee80211_sub_if_data *sdata)
 348{
 349        DEBUGFS_DEL(drop_unencrypted, wds);
 350        DEBUGFS_DEL(force_unicast_rateidx, wds);
 351        DEBUGFS_DEL(max_ratectrl_rateidx, wds);
 352
 353        DEBUGFS_DEL(peer, wds);
 354}
 355
 356static void del_vlan_files(struct ieee80211_sub_if_data *sdata)
 357{
 358        DEBUGFS_DEL(drop_unencrypted, vlan);
 359        DEBUGFS_DEL(force_unicast_rateidx, vlan);
 360        DEBUGFS_DEL(max_ratectrl_rateidx, vlan);
 361}
 362
 363static void del_monitor_files(struct ieee80211_sub_if_data *sdata)
 364{
 365}
 366
 367#ifdef CONFIG_MAC80211_MESH
 368#define MESHSTATS_DEL(name)                     \
 369        do {                                            \
 370                debugfs_remove(sdata->mesh_stats.name); \
 371                sdata->mesh_stats.name = NULL;          \
 372        } while (0)
 373
 374static void del_mesh_stats(struct ieee80211_sub_if_data *sdata)
 375{
 376        MESHSTATS_DEL(fwded_frames);
 377        MESHSTATS_DEL(dropped_frames_ttl);
 378        MESHSTATS_DEL(dropped_frames_no_route);
 379        MESHSTATS_DEL(estab_plinks);
 380        debugfs_remove(sdata->mesh_stats_dir);
 381        sdata->mesh_stats_dir = NULL;
 382}
 383
 384#define MESHPARAMS_DEL(name)                    \
 385        do {                                            \
 386                debugfs_remove(sdata->mesh_config.name);        \
 387                sdata->mesh_config.name = NULL;         \
 388        } while (0)
 389
 390static void del_mesh_config(struct ieee80211_sub_if_data *sdata)
 391{
 392        MESHPARAMS_DEL(dot11MeshMaxRetries);
 393        MESHPARAMS_DEL(dot11MeshRetryTimeout);
 394        MESHPARAMS_DEL(dot11MeshConfirmTimeout);
 395        MESHPARAMS_DEL(dot11MeshHoldingTimeout);
 396        MESHPARAMS_DEL(dot11MeshTTL);
 397        MESHPARAMS_DEL(auto_open_plinks);
 398        MESHPARAMS_DEL(dot11MeshMaxPeerLinks);
 399        MESHPARAMS_DEL(dot11MeshHWMPactivePathTimeout);
 400        MESHPARAMS_DEL(dot11MeshHWMPpreqMinInterval);
 401        MESHPARAMS_DEL(dot11MeshHWMPnetDiameterTraversalTime);
 402        MESHPARAMS_DEL(dot11MeshHWMPmaxPREQretries);
 403        MESHPARAMS_DEL(path_refresh_time);
 404        MESHPARAMS_DEL(min_discovery_timeout);
 405        debugfs_remove(sdata->mesh_config_dir);
 406        sdata->mesh_config_dir = NULL;
 407}
 408#endif
 409
 410static void del_files(struct ieee80211_sub_if_data *sdata)
 411{
 412        if (!sdata->debugfsdir)
 413                return;
 414
 415        switch (sdata->vif.type) {
 416        case NL80211_IFTYPE_MESH_POINT:
 417#ifdef CONFIG_MAC80211_MESH
 418                del_mesh_stats(sdata);
 419                del_mesh_config(sdata);
 420#endif
 421                break;
 422        case NL80211_IFTYPE_STATION:
 423                del_sta_files(sdata);
 424                break;
 425        case NL80211_IFTYPE_ADHOC:
 426                /* XXX */
 427                break;
 428        case NL80211_IFTYPE_AP:
 429                del_ap_files(sdata);
 430                break;
 431        case NL80211_IFTYPE_WDS:
 432                del_wds_files(sdata);
 433                break;
 434        case NL80211_IFTYPE_MONITOR:
 435                del_monitor_files(sdata);
 436                break;
 437        case NL80211_IFTYPE_AP_VLAN:
 438                del_vlan_files(sdata);
 439                break;
 440        default:
 441                break;
 442        }
 443}
 444
 445static int notif_registered;
 446
 447void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata)
 448{
 449        char buf[10+IFNAMSIZ];
 450
 451        if (!notif_registered)
 452                return;
 453
 454        sprintf(buf, "netdev:%s", sdata->dev->name);
 455        sdata->debugfsdir = debugfs_create_dir(buf,
 456                sdata->local->hw.wiphy->debugfsdir);
 457        add_files(sdata);
 458}
 459
 460void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata)
 461{
 462        del_files(sdata);
 463        debugfs_remove(sdata->debugfsdir);
 464        sdata->debugfsdir = NULL;
 465}
 466
 467static int netdev_notify(struct notifier_block *nb,
 468                         unsigned long state,
 469                         void *ndev)
 470{
 471        struct net_device *dev = ndev;
 472        struct dentry *dir;
 473        struct ieee80211_sub_if_data *sdata;
 474        char buf[10+IFNAMSIZ];
 475
 476        if (state != NETDEV_CHANGENAME)
 477                return 0;
 478
 479        if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy)
 480                return 0;
 481
 482        if (dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid)
 483                return 0;
 484
 485        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 486
 487        dir = sdata->debugfsdir;
 488
 489        if (!dir)
 490                return 0;
 491
 492        sprintf(buf, "netdev:%s", dev->name);
 493        if (!debugfs_rename(dir->d_parent, dir, dir->d_parent, buf))
 494                printk(KERN_ERR "mac80211: debugfs: failed to rename debugfs "
 495                       "dir to %s\n", buf);
 496
 497        return 0;
 498}
 499
 500static struct notifier_block mac80211_debugfs_netdev_notifier = {
 501        .notifier_call = netdev_notify,
 502};
 503
 504void ieee80211_debugfs_netdev_init(void)
 505{
 506        int err;
 507
 508        err = register_netdevice_notifier(&mac80211_debugfs_netdev_notifier);
 509        if (err) {
 510                printk(KERN_ERR
 511                       "mac80211: failed to install netdev notifier,"
 512                       " disabling per-netdev debugfs!\n");
 513        } else
 514                notif_registered = 1;
 515}
 516
 517void ieee80211_debugfs_netdev_exit(void)
 518{
 519        unregister_netdevice_notifier(&mac80211_debugfs_netdev_notifier);
 520        notif_registered = 0;
 521}
 522