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