log.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  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. #include <linux/kernel.h>
  27. #include <linux/mmc/sdio_func.h>
  28. #include <linux/ctype.h>
  29. #include "fw-msg.h"
  30. #include "iwmc3200top.h"
  31. #include "log.h"
  32. /* Maximal hexadecimal string size of the FW memdump message */
  33. #define LOG_MSG_SIZE_MAX 12400
  34. /* iwmct_logdefs is a global used by log macros */
  35. u8 iwmct_logdefs[LOG_SRC_MAX];
  36. static u8 iwmct_fw_logdefs[FW_LOG_SRC_MAX];
  37. static int _log_set_log_filter(u8 *logdefs, int size, u8 src, u8 logmask)
  38. {
  39. int i;
  40. if (src < size)
  41. logdefs[src] = logmask;
  42. else if (src == LOG_SRC_ALL)
  43. for (i = 0; i < size; i++)
  44. logdefs[i] = logmask;
  45. else
  46. return -1;
  47. return 0;
  48. }
  49. int iwmct_log_set_filter(u8 src, u8 logmask)
  50. {
  51. return _log_set_log_filter(iwmct_logdefs, LOG_SRC_MAX, src, logmask);
  52. }
  53. int iwmct_log_set_fw_filter(u8 src, u8 logmask)
  54. {
  55. return _log_set_log_filter(iwmct_fw_logdefs,
  56. FW_LOG_SRC_MAX, src, logmask);
  57. }
  58. static int log_msg_format_hex(char *str, int slen, u8 *ibuf,
  59. int ilen, char *pref)
  60. {
  61. int pos = 0;
  62. int i;
  63. int len;
  64. for (pos = 0, i = 0; pos < slen - 2 && pref[i] != '\0'; i++, pos++)
  65. str[pos] = pref[i];
  66. for (i = 0; pos < slen - 2 && i < ilen; pos += len, i++)
  67. len = snprintf(&str[pos], slen - pos - 1, " %2.2X", ibuf[i]);
  68. if (i < ilen)
  69. return -1;
  70. return 0;
  71. }
  72. /* NOTE: This function is not thread safe.
  73. Currently it's called only from sdio rx worker - no race there
  74. */
  75. void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len)
  76. {
  77. struct top_msg *msg;
  78. static char logbuf[LOG_MSG_SIZE_MAX];
  79. msg = (struct top_msg *)buf;
  80. if (len < sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr)) {
  81. LOG_ERROR(priv, FW_MSG, "Log message from TOP "
  82. "is too short %d (expected %zd)\n",
  83. len, sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr));
  84. return;
  85. }
  86. if (!(iwmct_fw_logdefs[msg->u.log.log_hdr.logsource] &
  87. BIT(msg->u.log.log_hdr.severity)) ||
  88. !(iwmct_logdefs[LOG_SRC_FW_MSG] & BIT(msg->u.log.log_hdr.severity)))
  89. return;
  90. switch (msg->hdr.category) {
  91. case COMM_CATEGORY_TESTABILITY:
  92. if (!(iwmct_logdefs[LOG_SRC_TST] &
  93. BIT(msg->u.log.log_hdr.severity)))
  94. return;
  95. if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
  96. le16_to_cpu(msg->hdr.length) +
  97. sizeof(msg->hdr), "<TST>"))
  98. LOG_WARNING(priv, TST,
  99. "TOP TST message is too long, truncating...");
  100. LOG_WARNING(priv, TST, "%s\n", logbuf);
  101. break;
  102. case COMM_CATEGORY_DEBUG:
  103. if (msg->hdr.opcode == OP_DBG_ZSTR_MSG)
  104. LOG_INFO(priv, FW_MSG, "%s %s", "<DBG>",
  105. ((u8 *)msg) + sizeof(msg->hdr)
  106. + sizeof(msg->u.log.log_hdr));
  107. else {
  108. if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
  109. le16_to_cpu(msg->hdr.length)
  110. + sizeof(msg->hdr),
  111. "<DBG>"))
  112. LOG_WARNING(priv, FW_MSG,
  113. "TOP DBG message is too long,"
  114. "truncating...");
  115. LOG_WARNING(priv, FW_MSG, "%s\n", logbuf);
  116. }
  117. break;
  118. default:
  119. break;
  120. }
  121. }
  122. static int _log_get_filter_str(u8 *logdefs, int logdefsz, char *buf, int size)
  123. {
  124. int i, pos, len;
  125. for (i = 0, pos = 0; (pos < size-1) && (i < logdefsz); i++) {
  126. len = snprintf(&buf[pos], size - pos - 1, "0x%02X%02X,",
  127. i, logdefs[i]);
  128. pos += len;
  129. }
  130. buf[pos-1] = '\n';
  131. buf[pos] = '\0';
  132. if (i < logdefsz)
  133. return -1;
  134. return 0;
  135. }
  136. int log_get_filter_str(char *buf, int size)
  137. {
  138. return _log_get_filter_str(iwmct_logdefs, LOG_SRC_MAX, buf, size);
  139. }
  140. int log_get_fw_filter_str(char *buf, int size)
  141. {
  142. return _log_get_filter_str(iwmct_fw_logdefs, FW_LOG_SRC_MAX, buf, size);
  143. }
  144. #define HEXADECIMAL_RADIX 16
  145. #define LOG_SRC_FORMAT 7 /* log level is in format of "0xXXXX," */
  146. ssize_t show_iwmct_log_level(struct device *d,
  147. struct device_attribute *attr, char *buf)
  148. {
  149. struct iwmct_priv *priv = dev_get_drvdata(d);
  150. char *str_buf;
  151. int buf_size;
  152. ssize_t ret;
  153. buf_size = (LOG_SRC_FORMAT * LOG_SRC_MAX) + 1;
  154. str_buf = kzalloc(buf_size, GFP_KERNEL);
  155. if (!str_buf) {
  156. LOG_ERROR(priv, DEBUGFS,
  157. "failed to allocate %d bytes\n", buf_size);
  158. ret = -ENOMEM;
  159. goto exit;
  160. }
  161. if (log_get_filter_str(str_buf, buf_size) < 0) {
  162. ret = -EINVAL;
  163. goto exit;
  164. }
  165. ret = sprintf(buf, "%s", str_buf);
  166. exit:
  167. kfree(str_buf);
  168. return ret;
  169. }
  170. ssize_t store_iwmct_log_level(struct device *d,
  171. struct device_attribute *attr,
  172. const char *buf, size_t count)
  173. {
  174. struct iwmct_priv *priv = dev_get_drvdata(d);
  175. char *token, *str_buf = NULL;
  176. long val;
  177. ssize_t ret = count;
  178. u8 src, mask;
  179. if (!count)
  180. goto exit;
  181. str_buf = kzalloc(count, GFP_KERNEL);
  182. if (!str_buf) {
  183. LOG_ERROR(priv, DEBUGFS,
  184. "failed to allocate %zd bytes\n", count);
  185. ret = -ENOMEM;
  186. goto exit;
  187. }
  188. memcpy(str_buf, buf, count);
  189. while ((token = strsep(&str_buf, ",")) != NULL) {
  190. while (isspace(*token))
  191. ++token;
  192. if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
  193. LOG_ERROR(priv, DEBUGFS,
  194. "failed to convert string to long %s\n",
  195. token);
  196. ret = -EINVAL;
  197. goto exit;
  198. }
  199. mask = val & 0xFF;
  200. src = (val & 0XFF00) >> 8;
  201. iwmct_log_set_filter(src, mask);
  202. }
  203. exit:
  204. kfree(str_buf);
  205. return ret;
  206. }
  207. ssize_t show_iwmct_log_level_fw(struct device *d,
  208. struct device_attribute *attr, char *buf)
  209. {
  210. struct iwmct_priv *priv = dev_get_drvdata(d);
  211. char *str_buf;
  212. int buf_size;
  213. ssize_t ret;
  214. buf_size = (LOG_SRC_FORMAT * FW_LOG_SRC_MAX) + 2;
  215. str_buf = kzalloc(buf_size, GFP_KERNEL);
  216. if (!str_buf) {
  217. LOG_ERROR(priv, DEBUGFS,
  218. "failed to allocate %d bytes\n", buf_size);
  219. ret = -ENOMEM;
  220. goto exit;
  221. }
  222. if (log_get_fw_filter_str(str_buf, buf_size) < 0) {
  223. ret = -EINVAL;
  224. goto exit;
  225. }
  226. ret = sprintf(buf, "%s", str_buf);
  227. exit:
  228. kfree(str_buf);
  229. return ret;
  230. }
  231. ssize_t store_iwmct_log_level_fw(struct device *d,
  232. struct device_attribute *attr,
  233. const char *buf, size_t count)
  234. {
  235. struct iwmct_priv *priv = dev_get_drvdata(d);
  236. struct top_msg cmd;
  237. char *token, *str_buf = NULL;
  238. ssize_t ret = count;
  239. u16 cmdlen = 0;
  240. int i;
  241. long val;
  242. u8 src, mask;
  243. if (!count)
  244. goto exit;
  245. str_buf = kzalloc(count, GFP_KERNEL);
  246. if (!str_buf) {
  247. LOG_ERROR(priv, DEBUGFS,
  248. "failed to allocate %zd bytes\n", count);
  249. ret = -ENOMEM;
  250. goto exit;
  251. }
  252. memcpy(str_buf, buf, count);
  253. cmd.hdr.type = COMM_TYPE_H2D;
  254. cmd.hdr.category = COMM_CATEGORY_DEBUG;
  255. cmd.hdr.opcode = CMD_DBG_LOG_LEVEL;
  256. for (i = 0; ((token = strsep(&str_buf, ",")) != NULL) &&
  257. (i < FW_LOG_SRC_MAX); i++) {
  258. while (isspace(*token))
  259. ++token;
  260. if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
  261. LOG_ERROR(priv, DEBUGFS,
  262. "failed to convert string to long %s\n",
  263. token);
  264. ret = -EINVAL;
  265. goto exit;
  266. }
  267. mask = val & 0xFF; /* LSB */
  268. src = (val & 0XFF00) >> 8; /* 2nd least significant byte. */
  269. iwmct_log_set_fw_filter(src, mask);
  270. cmd.u.logdefs[i].logsource = src;
  271. cmd.u.logdefs[i].sevmask = mask;
  272. }
  273. cmd.hdr.length = cpu_to_le16(i * sizeof(cmd.u.logdefs[0]));
  274. cmdlen = (i * sizeof(cmd.u.logdefs[0]) + sizeof(cmd.hdr));
  275. ret = iwmct_send_hcmd(priv, (u8 *)&cmd, cmdlen);
  276. if (ret) {
  277. LOG_ERROR(priv, DEBUGFS,
  278. "Failed to send %d bytes of fwcmd, ret=%zd\n",
  279. cmdlen, ret);
  280. goto exit;
  281. } else
  282. LOG_INFO(priv, DEBUGFS, "fwcmd sent (%d bytes)\n", cmdlen);
  283. ret = count;
  284. exit:
  285. kfree(str_buf);
  286. return ret;
  287. }