linux/drivers/misc/iwmc3200top/log.c
<<
>>
Prefs
   1/*
   2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
   3 * drivers/misc/iwmc3200top/log.c
   4 *
   5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License version
   9 * 2 as published by the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write to the Free Software
  18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19 * 02110-1301, USA.
  20 *
  21 *
  22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
  23 *  -
  24 *
  25 */
  26
  27#include <linux/kernel.h>
  28#include <linux/mmc/sdio_func.h>
  29#include <linux/slab.h>
  30#include <linux/ctype.h>
  31#include "fw-msg.h"
  32#include "iwmc3200top.h"
  33#include "log.h"
  34
  35/* Maximal hexadecimal string size of the FW memdump message */
  36#define LOG_MSG_SIZE_MAX                12400
  37
  38/* iwmct_logdefs is a global used by log macros */
  39u8 iwmct_logdefs[LOG_SRC_MAX];
  40static u8 iwmct_fw_logdefs[FW_LOG_SRC_MAX];
  41
  42
  43static int _log_set_log_filter(u8 *logdefs, int size, u8 src, u8 logmask)
  44{
  45        int i;
  46
  47        if (src < size)
  48                logdefs[src] = logmask;
  49        else if (src == LOG_SRC_ALL)
  50                for (i = 0; i < size; i++)
  51                        logdefs[i] = logmask;
  52        else
  53                return -1;
  54
  55        return 0;
  56}
  57
  58
  59int iwmct_log_set_filter(u8 src, u8 logmask)
  60{
  61        return _log_set_log_filter(iwmct_logdefs, LOG_SRC_MAX, src, logmask);
  62}
  63
  64
  65int iwmct_log_set_fw_filter(u8 src, u8 logmask)
  66{
  67        return _log_set_log_filter(iwmct_fw_logdefs,
  68                                   FW_LOG_SRC_MAX, src, logmask);
  69}
  70
  71
  72static int log_msg_format_hex(char *str, int slen, u8 *ibuf,
  73                              int ilen, char *pref)
  74{
  75        int pos = 0;
  76        int i;
  77        int len;
  78
  79        for (pos = 0, i = 0; pos < slen - 2 && pref[i] != '\0'; i++, pos++)
  80                str[pos] = pref[i];
  81
  82        for (i = 0; pos < slen - 2 && i < ilen; pos += len, i++)
  83                len = snprintf(&str[pos], slen - pos - 1, " %2.2X", ibuf[i]);
  84
  85        if (i < ilen)
  86                return -1;
  87
  88        return 0;
  89}
  90
  91/*      NOTE: This function is not thread safe.
  92        Currently it's called only from sdio rx worker - no race there
  93*/
  94void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len)
  95{
  96        struct top_msg *msg;
  97        static char logbuf[LOG_MSG_SIZE_MAX];
  98
  99        msg = (struct top_msg *)buf;
 100
 101        if (len < sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr)) {
 102                LOG_ERROR(priv, FW_MSG, "Log message from TOP "
 103                          "is too short %d (expected %zd)\n",
 104                          len, sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr));
 105                return;
 106        }
 107
 108        if (!(iwmct_fw_logdefs[msg->u.log.log_hdr.logsource] &
 109                BIT(msg->u.log.log_hdr.severity)) ||
 110            !(iwmct_logdefs[LOG_SRC_FW_MSG] & BIT(msg->u.log.log_hdr.severity)))
 111                return;
 112
 113        switch (msg->hdr.category) {
 114        case COMM_CATEGORY_TESTABILITY:
 115                if (!(iwmct_logdefs[LOG_SRC_TST] &
 116                      BIT(msg->u.log.log_hdr.severity)))
 117                        return;
 118                if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
 119                                       le16_to_cpu(msg->hdr.length) +
 120                                       sizeof(msg->hdr), "<TST>"))
 121                        LOG_WARNING(priv, TST,
 122                                  "TOP TST message is too long, truncating...");
 123                LOG_WARNING(priv, TST, "%s\n", logbuf);
 124                break;
 125        case COMM_CATEGORY_DEBUG:
 126                if (msg->hdr.opcode == OP_DBG_ZSTR_MSG)
 127                        LOG_INFO(priv, FW_MSG, "%s %s", "<DBG>",
 128                                       ((u8 *)msg) + sizeof(msg->hdr)
 129                                        + sizeof(msg->u.log.log_hdr));
 130                else {
 131                        if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
 132                                        le16_to_cpu(msg->hdr.length)
 133                                                + sizeof(msg->hdr),
 134                                        "<DBG>"))
 135                                LOG_WARNING(priv, FW_MSG,
 136                                        "TOP DBG message is too long,"
 137                                        "truncating...");
 138                        LOG_WARNING(priv, FW_MSG, "%s\n", logbuf);
 139                }
 140                break;
 141        default:
 142                break;
 143        }
 144}
 145
 146static int _log_get_filter_str(u8 *logdefs, int logdefsz, char *buf, int size)
 147{
 148        int i, pos, len;
 149        for (i = 0, pos = 0; (pos < size-1) && (i < logdefsz); i++) {
 150                len = snprintf(&buf[pos], size - pos - 1, "0x%02X%02X,",
 151                                i, logdefs[i]);
 152                pos += len;
 153        }
 154        buf[pos-1] = '\n';
 155        buf[pos] = '\0';
 156
 157        if (i < logdefsz)
 158                return -1;
 159        return 0;
 160}
 161
 162int log_get_filter_str(char *buf, int size)
 163{
 164        return _log_get_filter_str(iwmct_logdefs, LOG_SRC_MAX, buf, size);
 165}
 166
 167int log_get_fw_filter_str(char *buf, int size)
 168{
 169        return _log_get_filter_str(iwmct_fw_logdefs, FW_LOG_SRC_MAX, buf, size);
 170}
 171
 172#define HEXADECIMAL_RADIX       16
 173#define LOG_SRC_FORMAT          7 /* log level is in format of "0xXXXX," */
 174
 175ssize_t show_iwmct_log_level(struct device *d,
 176                                struct device_attribute *attr, char *buf)
 177{
 178        struct iwmct_priv *priv = dev_get_drvdata(d);
 179        char *str_buf;
 180        int buf_size;
 181        ssize_t ret;
 182
 183        buf_size = (LOG_SRC_FORMAT * LOG_SRC_MAX) + 1;
 184        str_buf = kzalloc(buf_size, GFP_KERNEL);
 185        if (!str_buf) {
 186                LOG_ERROR(priv, DEBUGFS,
 187                        "failed to allocate %d bytes\n", buf_size);
 188                ret = -ENOMEM;
 189                goto exit;
 190        }
 191
 192        if (log_get_filter_str(str_buf, buf_size) < 0) {
 193                ret = -EINVAL;
 194                goto exit;
 195        }
 196
 197        ret = sprintf(buf, "%s", str_buf);
 198
 199exit:
 200        kfree(str_buf);
 201        return ret;
 202}
 203
 204ssize_t store_iwmct_log_level(struct device *d,
 205                        struct device_attribute *attr,
 206                        const char *buf, size_t count)
 207{
 208        struct iwmct_priv *priv = dev_get_drvdata(d);
 209        char *token, *str_buf = NULL;
 210        long val;
 211        ssize_t ret = count;
 212        u8 src, mask;
 213
 214        if (!count)
 215                goto exit;
 216
 217        str_buf = kzalloc(count, GFP_KERNEL);
 218        if (!str_buf) {
 219                LOG_ERROR(priv, DEBUGFS,
 220                        "failed to allocate %zd bytes\n", count);
 221                ret = -ENOMEM;
 222                goto exit;
 223        }
 224
 225        memcpy(str_buf, buf, count);
 226
 227        while ((token = strsep(&str_buf, ",")) != NULL) {
 228                while (isspace(*token))
 229                        ++token;
 230                if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
 231                        LOG_ERROR(priv, DEBUGFS,
 232                                  "failed to convert string to long %s\n",
 233                                  token);
 234                        ret = -EINVAL;
 235                        goto exit;
 236                }
 237
 238                mask  = val & 0xFF;
 239                src = (val & 0XFF00) >> 8;
 240                iwmct_log_set_filter(src, mask);
 241        }
 242
 243exit:
 244        kfree(str_buf);
 245        return ret;
 246}
 247
 248ssize_t show_iwmct_log_level_fw(struct device *d,
 249                        struct device_attribute *attr, char *buf)
 250{
 251        struct iwmct_priv *priv = dev_get_drvdata(d);
 252        char *str_buf;
 253        int buf_size;
 254        ssize_t ret;
 255
 256        buf_size = (LOG_SRC_FORMAT * FW_LOG_SRC_MAX) + 2;
 257
 258        str_buf = kzalloc(buf_size, GFP_KERNEL);
 259        if (!str_buf) {
 260                LOG_ERROR(priv, DEBUGFS,
 261                        "failed to allocate %d bytes\n", buf_size);
 262                ret = -ENOMEM;
 263                goto exit;
 264        }
 265
 266        if (log_get_fw_filter_str(str_buf, buf_size) < 0) {
 267                ret = -EINVAL;
 268                goto exit;
 269        }
 270
 271        ret = sprintf(buf, "%s", str_buf);
 272
 273exit:
 274        kfree(str_buf);
 275        return ret;
 276}
 277
 278ssize_t store_iwmct_log_level_fw(struct device *d,
 279                        struct device_attribute *attr,
 280                        const char *buf, size_t count)
 281{
 282        struct iwmct_priv *priv = dev_get_drvdata(d);
 283        struct top_msg cmd;
 284        char *token, *str_buf = NULL;
 285        ssize_t ret = count;
 286        u16 cmdlen = 0;
 287        int i;
 288        long val;
 289        u8 src, mask;
 290
 291        if (!count)
 292                goto exit;
 293
 294        str_buf = kzalloc(count, GFP_KERNEL);
 295        if (!str_buf) {
 296                LOG_ERROR(priv, DEBUGFS,
 297                        "failed to allocate %zd bytes\n", count);
 298                ret = -ENOMEM;
 299                goto exit;
 300        }
 301
 302        memcpy(str_buf, buf, count);
 303
 304        cmd.hdr.type = COMM_TYPE_H2D;
 305        cmd.hdr.category = COMM_CATEGORY_DEBUG;
 306        cmd.hdr.opcode = CMD_DBG_LOG_LEVEL;
 307
 308        for (i = 0; ((token = strsep(&str_buf, ",")) != NULL) &&
 309                     (i < FW_LOG_SRC_MAX); i++) {
 310
 311                while (isspace(*token))
 312                        ++token;
 313
 314                if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
 315                        LOG_ERROR(priv, DEBUGFS,
 316                                  "failed to convert string to long %s\n",
 317                                  token);
 318                        ret = -EINVAL;
 319                        goto exit;
 320                }
 321
 322                mask  = val & 0xFF; /* LSB */
 323                src = (val & 0XFF00) >> 8; /* 2nd least significant byte. */
 324                iwmct_log_set_fw_filter(src, mask);
 325
 326                cmd.u.logdefs[i].logsource = src;
 327                cmd.u.logdefs[i].sevmask = mask;
 328        }
 329
 330        cmd.hdr.length = cpu_to_le16(i * sizeof(cmd.u.logdefs[0]));
 331        cmdlen = (i * sizeof(cmd.u.logdefs[0]) + sizeof(cmd.hdr));
 332
 333        ret = iwmct_send_hcmd(priv, (u8 *)&cmd, cmdlen);
 334        if (ret) {
 335                LOG_ERROR(priv, DEBUGFS,
 336                          "Failed to send %d bytes of fwcmd, ret=%zd\n",
 337                          cmdlen, ret);
 338                goto exit;
 339        } else
 340                LOG_INFO(priv, DEBUGFS, "fwcmd sent (%d bytes)\n", cmdlen);
 341
 342        ret = count;
 343
 344exit:
 345        kfree(str_buf);
 346        return ret;
 347}
 348
 349