Merge branch 'tip/perf/urgent' of git://git.kernel.org/pub/scm/linux/kernel/git/roste...
[pandora-kernel.git] / drivers / misc / iwmc3200top / log.c
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 */
39 u8 iwmct_logdefs[LOG_SRC_MAX];
40 static u8 iwmct_fw_logdefs[FW_LOG_SRC_MAX];
41
42
43 static 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
59 int 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
65 int 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
72 static 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 */
94 void 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
146 static 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
162 int log_get_filter_str(char *buf, int size)
163 {
164         return _log_get_filter_str(iwmct_logdefs, LOG_SRC_MAX, buf, size);
165 }
166
167 int 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
175 ssize_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
199 exit:
200         kfree(str_buf);
201         return ret;
202 }
203
204 ssize_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
243 exit:
244         kfree(str_buf);
245         return ret;
246 }
247
248 ssize_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
273 exit:
274         kfree(str_buf);
275         return ret;
276 }
277
278 ssize_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
344 exit:
345         kfree(str_buf);
346         return ret;
347 }
348