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 
_log_set_log_filter(u8 * logdefs,int size,u8 src,u8 logmask)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 
iwmct_log_set_filter(u8 src,u8 logmask)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 
iwmct_log_set_fw_filter(u8 src,u8 logmask)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 
log_msg_format_hex(char * str,int slen,u8 * ibuf,int ilen,char * pref)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 */
iwmct_log_top_message(struct iwmct_priv * priv,u8 * buf,int len)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 
_log_get_filter_str(u8 * logdefs,int logdefsz,char * buf,int size)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 
log_get_filter_str(char * buf,int size)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 
log_get_fw_filter_str(char * buf,int size)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 
show_iwmct_log_level(struct device * d,struct device_attribute * attr,char * buf)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 
store_iwmct_log_level(struct device * d,struct device_attribute * attr,const char * buf,size_t count)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 
show_iwmct_log_level_fw(struct device * d,struct device_attribute * attr,char * buf)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 
store_iwmct_log_level_fw(struct device * d,struct device_attribute * attr,const char * buf,size_t count)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 
349