log.c 7.9 KB

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