linux/drivers/net/wireless/realtek/rtlwifi/debug.c
<<
>>
Prefs
   1/******************************************************************************
   2 *
   3 * Copyright(c) 2009-2012  Realtek Corporation.
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms of version 2 of the GNU General Public License as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful, but WITHOUT
  10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  12 * more details.
  13 *
  14 * The full GNU General Public License is included in this distribution in the
  15 * file called LICENSE.
  16 *
  17 * Contact Information:
  18 * wlanfae <wlanfae@realtek.com>
  19 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
  20 * Hsinchu 300, Taiwan.
  21 *
  22 * Larry Finger <Larry.Finger@lwfinger.net>
  23 *****************************************************************************/
  24
  25#include "wifi.h"
  26#include "cam.h"
  27
  28#include <linux/moduleparam.h>
  29#include <linux/vmalloc.h>
  30
  31#ifdef CONFIG_RTLWIFI_DEBUG
  32void _rtl_dbg_trace(struct rtl_priv *rtlpriv, u64 comp, int level,
  33                    const char *fmt, ...)
  34{
  35        if (unlikely((comp & rtlpriv->cfg->mod_params->debug_mask) &&
  36                     level <= rtlpriv->cfg->mod_params->debug_level)) {
  37                struct va_format vaf;
  38                va_list args;
  39
  40                va_start(args, fmt);
  41
  42                vaf.fmt = fmt;
  43                vaf.va = &args;
  44
  45                pr_info(":<%lx> %pV", in_interrupt(), &vaf);
  46
  47                va_end(args);
  48        }
  49}
  50EXPORT_SYMBOL_GPL(_rtl_dbg_trace);
  51
  52void _rtl_dbg_print(struct rtl_priv *rtlpriv, u64 comp, int level,
  53                    const char *fmt, ...)
  54{
  55        if (unlikely((comp & rtlpriv->cfg->mod_params->debug_mask) &&
  56                     level <= rtlpriv->cfg->mod_params->debug_level)) {
  57                struct va_format vaf;
  58                va_list args;
  59
  60                va_start(args, fmt);
  61
  62                vaf.fmt = fmt;
  63                vaf.va = &args;
  64
  65                pr_info("%pV", &vaf);
  66
  67                va_end(args);
  68        }
  69}
  70EXPORT_SYMBOL_GPL(_rtl_dbg_print);
  71
  72void _rtl_dbg_print_data(struct rtl_priv *rtlpriv, u64 comp, int level,
  73                         const char *titlestring,
  74                         const void *hexdata, int hexdatalen)
  75{
  76        if (unlikely(((comp) & rtlpriv->cfg->mod_params->debug_mask) &&
  77                     ((level) <= rtlpriv->cfg->mod_params->debug_level))) {
  78                pr_info("In process \"%s\" (pid %i): %s\n",
  79                        current->comm, current->pid, titlestring);
  80                print_hex_dump_bytes("", DUMP_PREFIX_NONE,
  81                                     hexdata, hexdatalen);
  82        }
  83}
  84EXPORT_SYMBOL_GPL(_rtl_dbg_print_data);
  85
  86struct rtl_debugfs_priv {
  87        struct rtl_priv *rtlpriv;
  88        int (*cb_read)(struct seq_file *m, void *v);
  89        ssize_t (*cb_write)(struct file *filp, const char __user *buffer,
  90                            size_t count, loff_t *loff);
  91        u32 cb_data;
  92};
  93
  94static struct dentry *debugfs_topdir;
  95
  96static int rtl_debug_get_common(struct seq_file *m, void *v)
  97{
  98        struct rtl_debugfs_priv *debugfs_priv = m->private;
  99
 100        return debugfs_priv->cb_read(m, v);
 101}
 102
 103static int dl_debug_open_common(struct inode *inode, struct file *file)
 104{
 105        return single_open(file, rtl_debug_get_common, inode->i_private);
 106}
 107
 108static const struct file_operations file_ops_common = {
 109        .open = dl_debug_open_common,
 110        .read = seq_read,
 111        .llseek = seq_lseek,
 112        .release = seq_release,
 113};
 114
 115static int rtl_debug_get_mac_page(struct seq_file *m, void *v)
 116{
 117        struct rtl_debugfs_priv *debugfs_priv = m->private;
 118        struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv;
 119        u32 page = debugfs_priv->cb_data;
 120        int i, n;
 121        int max = 0xff;
 122
 123        for (n = 0; n <= max; ) {
 124                seq_printf(m, "\n%8.8x  ", n + page);
 125                for (i = 0; i < 4 && n <= max; i++, n += 4)
 126                        seq_printf(m, "%8.8x    ",
 127                                   rtl_read_dword(rtlpriv, (page | n)));
 128        }
 129        seq_puts(m, "\n");
 130        return 0;
 131}
 132
 133#define RTL_DEBUG_IMPL_MAC_SERIES(page, addr)                   \
 134static struct rtl_debugfs_priv rtl_debug_priv_mac_ ##page = {   \
 135        .cb_read = rtl_debug_get_mac_page,                      \
 136        .cb_data = addr,                                        \
 137}
 138
 139RTL_DEBUG_IMPL_MAC_SERIES(0, 0x0000);
 140RTL_DEBUG_IMPL_MAC_SERIES(1, 0x0100);
 141RTL_DEBUG_IMPL_MAC_SERIES(2, 0x0200);
 142RTL_DEBUG_IMPL_MAC_SERIES(3, 0x0300);
 143RTL_DEBUG_IMPL_MAC_SERIES(4, 0x0400);
 144RTL_DEBUG_IMPL_MAC_SERIES(5, 0x0500);
 145RTL_DEBUG_IMPL_MAC_SERIES(6, 0x0600);
 146RTL_DEBUG_IMPL_MAC_SERIES(7, 0x0700);
 147RTL_DEBUG_IMPL_MAC_SERIES(10, 0x1000);
 148RTL_DEBUG_IMPL_MAC_SERIES(11, 0x1100);
 149RTL_DEBUG_IMPL_MAC_SERIES(12, 0x1200);
 150RTL_DEBUG_IMPL_MAC_SERIES(13, 0x1300);
 151RTL_DEBUG_IMPL_MAC_SERIES(14, 0x1400);
 152RTL_DEBUG_IMPL_MAC_SERIES(15, 0x1500);
 153RTL_DEBUG_IMPL_MAC_SERIES(16, 0x1600);
 154RTL_DEBUG_IMPL_MAC_SERIES(17, 0x1700);
 155
 156static int rtl_debug_get_bb_page(struct seq_file *m, void *v)
 157{
 158        struct rtl_debugfs_priv *debugfs_priv = m->private;
 159        struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv;
 160        struct ieee80211_hw *hw = rtlpriv->hw;
 161        u32 page = debugfs_priv->cb_data;
 162        int i, n;
 163        int max = 0xff;
 164
 165        for (n = 0; n <= max; ) {
 166                seq_printf(m, "\n%8.8x  ", n + page);
 167                for (i = 0; i < 4 && n <= max; i++, n += 4)
 168                        seq_printf(m, "%8.8x    ",
 169                                   rtl_get_bbreg(hw, (page | n), 0xffffffff));
 170        }
 171        seq_puts(m, "\n");
 172        return 0;
 173}
 174
 175#define RTL_DEBUG_IMPL_BB_SERIES(page, addr)                    \
 176static struct rtl_debugfs_priv rtl_debug_priv_bb_ ##page = {    \
 177        .cb_read = rtl_debug_get_bb_page,                       \
 178        .cb_data = addr,                                        \
 179}
 180
 181RTL_DEBUG_IMPL_BB_SERIES(8, 0x0800);
 182RTL_DEBUG_IMPL_BB_SERIES(9, 0x0900);
 183RTL_DEBUG_IMPL_BB_SERIES(a, 0x0a00);
 184RTL_DEBUG_IMPL_BB_SERIES(b, 0x0b00);
 185RTL_DEBUG_IMPL_BB_SERIES(c, 0x0c00);
 186RTL_DEBUG_IMPL_BB_SERIES(d, 0x0d00);
 187RTL_DEBUG_IMPL_BB_SERIES(e, 0x0e00);
 188RTL_DEBUG_IMPL_BB_SERIES(f, 0x0f00);
 189RTL_DEBUG_IMPL_BB_SERIES(18, 0x1800);
 190RTL_DEBUG_IMPL_BB_SERIES(19, 0x1900);
 191RTL_DEBUG_IMPL_BB_SERIES(1a, 0x1a00);
 192RTL_DEBUG_IMPL_BB_SERIES(1b, 0x1b00);
 193RTL_DEBUG_IMPL_BB_SERIES(1c, 0x1c00);
 194RTL_DEBUG_IMPL_BB_SERIES(1d, 0x1d00);
 195RTL_DEBUG_IMPL_BB_SERIES(1e, 0x1e00);
 196RTL_DEBUG_IMPL_BB_SERIES(1f, 0x1f00);
 197
 198static int rtl_debug_get_reg_rf(struct seq_file *m, void *v)
 199{
 200        struct rtl_debugfs_priv *debugfs_priv = m->private;
 201        struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv;
 202        struct ieee80211_hw *hw = rtlpriv->hw;
 203        enum radio_path rfpath = debugfs_priv->cb_data;
 204        int i, n;
 205        int max = 0x40;
 206
 207        if (IS_HARDWARE_TYPE_8822B(rtlpriv))
 208                max = 0xff;
 209
 210        seq_printf(m, "\nPATH(%d)", rfpath);
 211
 212        for (n = 0; n <= max; ) {
 213                seq_printf(m, "\n%8.8x  ", n);
 214                for (i = 0; i < 4 && n <= max; n += 1, i++)
 215                        seq_printf(m, "%8.8x    ",
 216                                   rtl_get_rfreg(hw, rfpath, n, 0xffffffff));
 217        }
 218        seq_puts(m, "\n");
 219        return 0;
 220}
 221
 222#define RTL_DEBUG_IMPL_RF_SERIES(page, addr)                    \
 223static struct rtl_debugfs_priv rtl_debug_priv_rf_ ##page = {    \
 224        .cb_read = rtl_debug_get_reg_rf,                        \
 225        .cb_data = addr,                                        \
 226}
 227
 228RTL_DEBUG_IMPL_RF_SERIES(a, RF90_PATH_A);
 229RTL_DEBUG_IMPL_RF_SERIES(b, RF90_PATH_B);
 230
 231static int rtl_debug_get_cam_register(struct seq_file *m, void *v)
 232{
 233        struct rtl_debugfs_priv *debugfs_priv = m->private;
 234        struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv;
 235        int start = debugfs_priv->cb_data;
 236        u32 target_cmd = 0;
 237        u32 target_val = 0;
 238        u8 entry_i = 0;
 239        u32 ulstatus;
 240        int i = 100, j = 0;
 241        int end = (start + 11 > TOTAL_CAM_ENTRY ? TOTAL_CAM_ENTRY : start + 11);
 242
 243        /* This dump the current register page */
 244        seq_printf(m,
 245                   "\n#################### SECURITY CAM (%d-%d) ##################\n",
 246                   start, end - 1);
 247
 248        for (j = start; j < end; j++) {
 249                seq_printf(m, "\nD:  %2x > ", j);
 250                for (entry_i = 0; entry_i < CAM_CONTENT_COUNT; entry_i++) {
 251                        /* polling bit, and No Write enable, and address  */
 252                        target_cmd = entry_i + CAM_CONTENT_COUNT * j;
 253                        target_cmd = target_cmd | BIT(31);
 254
 255                        /* Check polling bit is clear */
 256                        while ((i--) >= 0) {
 257                                ulstatus =
 258                                    rtl_read_dword(rtlpriv,
 259                                                   rtlpriv->cfg->maps[RWCAM]);
 260                                if (ulstatus & BIT(31))
 261                                        continue;
 262                                else
 263                                        break;
 264                        }
 265
 266                        rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM],
 267                                        target_cmd);
 268                        target_val = rtl_read_dword(rtlpriv,
 269                                                    rtlpriv->cfg->maps[RCAMO]);
 270                        seq_printf(m, "%8.8x ", target_val);
 271                }
 272        }
 273        seq_puts(m, "\n");
 274        return 0;
 275}
 276
 277#define RTL_DEBUG_IMPL_CAM_SERIES(page, addr)                   \
 278static struct rtl_debugfs_priv rtl_debug_priv_cam_ ##page = {   \
 279        .cb_read = rtl_debug_get_cam_register,                  \
 280        .cb_data = addr,                                        \
 281}
 282
 283RTL_DEBUG_IMPL_CAM_SERIES(1, 0);
 284RTL_DEBUG_IMPL_CAM_SERIES(2, 11);
 285RTL_DEBUG_IMPL_CAM_SERIES(3, 22);
 286
 287static int rtl_debug_get_btcoex(struct seq_file *m, void *v)
 288{
 289        struct rtl_debugfs_priv *debugfs_priv = m->private;
 290        struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv;
 291
 292        if (rtlpriv->cfg->ops->get_btc_status())
 293                rtlpriv->btcoexist.btc_ops->btc_display_bt_coex_info(rtlpriv,
 294                                                                     m);
 295
 296        seq_puts(m, "\n");
 297
 298        return 0;
 299}
 300
 301static struct rtl_debugfs_priv rtl_debug_priv_btcoex = {
 302        .cb_read = rtl_debug_get_btcoex,
 303        .cb_data = 0,
 304};
 305
 306static ssize_t rtl_debugfs_set_write_reg(struct file *filp,
 307                                         const char __user *buffer,
 308                                         size_t count, loff_t *loff)
 309{
 310        struct rtl_debugfs_priv *debugfs_priv = filp->private_data;
 311        struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv;
 312        char tmp[32 + 1];
 313        int tmp_len;
 314        u32 addr, val, len;
 315        int num;
 316
 317        if (count < 3)
 318                return -EFAULT;
 319
 320        tmp_len = (count > sizeof(tmp) - 1 ? sizeof(tmp) - 1 : count);
 321
 322        if (!buffer || copy_from_user(tmp, buffer, tmp_len))
 323                return count;
 324
 325        tmp[tmp_len] = '\0';
 326
 327        /* write BB/MAC register */
 328        num = sscanf(tmp, "%x %x %x", &addr, &val, &len);
 329
 330        if (num !=  3)
 331                return count;
 332
 333        switch (len) {
 334        case 1:
 335                rtl_write_byte(rtlpriv, addr, (u8)val);
 336                break;
 337        case 2:
 338                rtl_write_word(rtlpriv, addr, (u16)val);
 339                break;
 340        case 4:
 341                rtl_write_dword(rtlpriv, addr, val);
 342                break;
 343        default:
 344                /*printk("error write length=%d", len);*/
 345                break;
 346        }
 347
 348        return count;
 349}
 350
 351static struct rtl_debugfs_priv rtl_debug_priv_write_reg = {
 352        .cb_write = rtl_debugfs_set_write_reg,
 353};
 354
 355static ssize_t rtl_debugfs_set_write_h2c(struct file *filp,
 356                                         const char __user *buffer,
 357                                         size_t count, loff_t *loff)
 358{
 359        struct rtl_debugfs_priv *debugfs_priv = filp->private_data;
 360        struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv;
 361        struct ieee80211_hw *hw = rtlpriv->hw;
 362        char tmp[32 + 1];
 363        int tmp_len;
 364        u8 h2c_len, h2c_data_packed[8];
 365        int h2c_data[8];        /* idx 0: cmd */
 366        int i;
 367
 368        if (count < 3)
 369                return -EFAULT;
 370
 371        tmp_len = (count > sizeof(tmp) - 1 ? sizeof(tmp) - 1 : count);
 372
 373        if (!buffer || copy_from_user(tmp, buffer, tmp_len))
 374                return count;
 375
 376        tmp[tmp_len] = '\0';
 377
 378        h2c_len = sscanf(tmp, "%X %X %X %X %X %X %X %X",
 379                         &h2c_data[0], &h2c_data[1],
 380                         &h2c_data[2], &h2c_data[3],
 381                         &h2c_data[4], &h2c_data[5],
 382                         &h2c_data[6], &h2c_data[7]);
 383
 384        if (h2c_len <= 0)
 385                return count;
 386
 387        for (i = 0; i < h2c_len; i++)
 388                h2c_data_packed[i] = (u8)h2c_data[i];
 389
 390        rtlpriv->cfg->ops->fill_h2c_cmd(hw, h2c_data_packed[0],
 391                                        h2c_len - 1,
 392                                        &h2c_data_packed[1]);
 393
 394        return count;
 395}
 396
 397static struct rtl_debugfs_priv rtl_debug_priv_write_h2c = {
 398        .cb_write = rtl_debugfs_set_write_h2c,
 399};
 400
 401static ssize_t rtl_debugfs_set_write_rfreg(struct file *filp,
 402                                           const char __user *buffer,
 403                                            size_t count, loff_t *loff)
 404{
 405        struct rtl_debugfs_priv *debugfs_priv = filp->private_data;
 406        struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv;
 407        struct ieee80211_hw *hw = rtlpriv->hw;
 408        char tmp[32 + 1];
 409        int tmp_len;
 410        int num;
 411        int path;
 412        u32 addr, bitmask, data;
 413
 414        if (count < 3)
 415                return -EFAULT;
 416
 417        tmp_len = (count > sizeof(tmp) - 1 ? sizeof(tmp) - 1 : count);
 418
 419        if (!buffer || copy_from_user(tmp, buffer, tmp_len))
 420                return count;
 421
 422        tmp[tmp_len] = '\0';
 423
 424        num = sscanf(tmp, "%X %X %X %X",
 425                     &path, &addr, &bitmask, &data);
 426
 427        if (num != 4) {
 428                RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
 429                         "Format is <path> <addr> <mask> <data>\n");
 430                return count;
 431        }
 432
 433        rtl_set_rfreg(hw, path, addr, bitmask, data);
 434
 435        return count;
 436}
 437
 438static struct rtl_debugfs_priv rtl_debug_priv_write_rfreg = {
 439        .cb_write = rtl_debugfs_set_write_rfreg,
 440};
 441
 442static int rtl_debugfs_close(struct inode *inode, struct file *filp)
 443{
 444        return 0;
 445}
 446
 447static ssize_t rtl_debugfs_common_write(struct file *filp,
 448                                        const char __user *buffer,
 449                                        size_t count, loff_t *loff)
 450{
 451        struct rtl_debugfs_priv *debugfs_priv = filp->private_data;
 452
 453        return debugfs_priv->cb_write(filp, buffer, count, loff);
 454}
 455
 456static const struct file_operations file_ops_common_write = {
 457        .owner = THIS_MODULE,
 458        .write = rtl_debugfs_common_write,
 459        .open = simple_open,
 460        .release = rtl_debugfs_close,
 461};
 462
 463#define RTL_DEBUGFS_ADD_CORE(name, mode, fopname)                          \
 464        do {                                                               \
 465                rtl_debug_priv_ ##name.rtlpriv = rtlpriv;                  \
 466                if (!debugfs_create_file(#name, mode,                      \
 467                                         parent, &rtl_debug_priv_ ##name,  \
 468                                         &file_ops_ ##fopname))            \
 469                        pr_err("Unable to initialize debugfs:%s/%s\n",     \
 470                               rtlpriv->dbg.debugfs_name,                  \
 471                               #name);                                     \
 472        } while (0)
 473
 474#define RTL_DEBUGFS_ADD(name)                                              \
 475                RTL_DEBUGFS_ADD_CORE(name, S_IFREG | 0444, common)
 476#define RTL_DEBUGFS_ADD_W(name)                                            \
 477                RTL_DEBUGFS_ADD_CORE(name, S_IFREG | 0222, common_write)
 478
 479void rtl_debug_add_one(struct ieee80211_hw *hw)
 480{
 481        struct rtl_priv *rtlpriv = rtl_priv(hw);
 482        struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
 483        struct dentry *parent;
 484
 485        snprintf(rtlpriv->dbg.debugfs_name, 18, "%pMF", rtlefuse->dev_addr);
 486
 487        rtlpriv->dbg.debugfs_dir =
 488                debugfs_create_dir(rtlpriv->dbg.debugfs_name, debugfs_topdir);
 489        if (!rtlpriv->dbg.debugfs_dir) {
 490                pr_err("Unable to init debugfs:/%s/%s\n", rtlpriv->cfg->name,
 491                       rtlpriv->dbg.debugfs_name);
 492                return;
 493        }
 494
 495        parent = rtlpriv->dbg.debugfs_dir;
 496
 497        RTL_DEBUGFS_ADD(mac_0);
 498        RTL_DEBUGFS_ADD(mac_1);
 499        RTL_DEBUGFS_ADD(mac_2);
 500        RTL_DEBUGFS_ADD(mac_3);
 501        RTL_DEBUGFS_ADD(mac_4);
 502        RTL_DEBUGFS_ADD(mac_5);
 503        RTL_DEBUGFS_ADD(mac_6);
 504        RTL_DEBUGFS_ADD(mac_7);
 505        RTL_DEBUGFS_ADD(bb_8);
 506        RTL_DEBUGFS_ADD(bb_9);
 507        RTL_DEBUGFS_ADD(bb_a);
 508        RTL_DEBUGFS_ADD(bb_b);
 509        RTL_DEBUGFS_ADD(bb_c);
 510        RTL_DEBUGFS_ADD(bb_d);
 511        RTL_DEBUGFS_ADD(bb_e);
 512        RTL_DEBUGFS_ADD(bb_f);
 513        RTL_DEBUGFS_ADD(mac_10);
 514        RTL_DEBUGFS_ADD(mac_11);
 515        RTL_DEBUGFS_ADD(mac_12);
 516        RTL_DEBUGFS_ADD(mac_13);
 517        RTL_DEBUGFS_ADD(mac_14);
 518        RTL_DEBUGFS_ADD(mac_15);
 519        RTL_DEBUGFS_ADD(mac_16);
 520        RTL_DEBUGFS_ADD(mac_17);
 521        RTL_DEBUGFS_ADD(bb_18);
 522        RTL_DEBUGFS_ADD(bb_19);
 523        RTL_DEBUGFS_ADD(bb_1a);
 524        RTL_DEBUGFS_ADD(bb_1b);
 525        RTL_DEBUGFS_ADD(bb_1c);
 526        RTL_DEBUGFS_ADD(bb_1d);
 527        RTL_DEBUGFS_ADD(bb_1e);
 528        RTL_DEBUGFS_ADD(bb_1f);
 529        RTL_DEBUGFS_ADD(rf_a);
 530        RTL_DEBUGFS_ADD(rf_b);
 531
 532        RTL_DEBUGFS_ADD(cam_1);
 533        RTL_DEBUGFS_ADD(cam_2);
 534        RTL_DEBUGFS_ADD(cam_3);
 535
 536        RTL_DEBUGFS_ADD(btcoex);
 537
 538        RTL_DEBUGFS_ADD_W(write_reg);
 539        RTL_DEBUGFS_ADD_W(write_h2c);
 540        RTL_DEBUGFS_ADD_W(write_rfreg);
 541}
 542EXPORT_SYMBOL_GPL(rtl_debug_add_one);
 543
 544void rtl_debug_remove_one(struct ieee80211_hw *hw)
 545{
 546        struct rtl_priv *rtlpriv = rtl_priv(hw);
 547
 548        debugfs_remove_recursive(rtlpriv->dbg.debugfs_dir);
 549        rtlpriv->dbg.debugfs_dir = NULL;
 550}
 551EXPORT_SYMBOL_GPL(rtl_debug_remove_one);
 552
 553void rtl_debugfs_add_topdir(void)
 554{
 555        debugfs_topdir = debugfs_create_dir("rtlwifi", NULL);
 556}
 557
 558void rtl_debugfs_remove_topdir(void)
 559{
 560        debugfs_remove_recursive(debugfs_topdir);
 561}
 562
 563#endif
 564