1ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler/*
2ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * drivers/misc/iwmc3200top/log.c
4ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler *
5ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * Copyright (C) 2009 Intel Corporation. All rights reserved.
6ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler *
7ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * This program is free software; you can redistribute it and/or
8ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * modify it under the terms of the GNU General Public License version
9ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * 2 as published by the Free Software Foundation.
10ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler *
11ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * This program is distributed in the hope that it will be useful,
12ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * but WITHOUT ANY WARRANTY; without even the implied warranty of
13ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * GNU General Public License for more details.
15ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler *
16ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * You should have received a copy of the GNU General Public License
17ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * along with this program; if not, write to the Free Software
18ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * 02110-1301, USA.
20ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler *
21ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler *
22ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler *  -
24ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler *
25ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler */
26ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
27ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler#include <linux/kernel.h>
28ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler#include <linux/mmc/sdio_func.h>
295a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
30ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler#include <linux/ctype.h>
31ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler#include "fw-msg.h"
32ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler#include "iwmc3200top.h"
33ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler#include "log.h"
34ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
35ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler/* Maximal hexadecimal string size of the FW memdump message */
36ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler#define LOG_MSG_SIZE_MAX		12400
37ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
38ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler/* iwmct_logdefs is a global used by log macros */
39ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkleru8 iwmct_logdefs[LOG_SRC_MAX];
40ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerstatic u8 iwmct_fw_logdefs[FW_LOG_SRC_MAX];
41ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
42ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
43ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerstatic int _log_set_log_filter(u8 *logdefs, int size, u8 src, u8 logmask)
44ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler{
45ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	int i;
46ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
47ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (src < size)
48ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		logdefs[src] = logmask;
49ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	else if (src == LOG_SRC_ALL)
50ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		for (i = 0; i < size; i++)
51ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			logdefs[i] = logmask;
52ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	else
53ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		return -1;
54ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
55ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	return 0;
56ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler}
57ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
58ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
59ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerint iwmct_log_set_filter(u8 src, u8 logmask)
60ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler{
61ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	return _log_set_log_filter(iwmct_logdefs, LOG_SRC_MAX, src, logmask);
62ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler}
63ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
64ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
65ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerint iwmct_log_set_fw_filter(u8 src, u8 logmask)
66ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler{
67ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	return _log_set_log_filter(iwmct_fw_logdefs,
68ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler				   FW_LOG_SRC_MAX, src, logmask);
69ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler}
70ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
71ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
72ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerstatic int log_msg_format_hex(char *str, int slen, u8 *ibuf,
73ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			      int ilen, char *pref)
74ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler{
75ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	int pos = 0;
76ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	int i;
77ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	int len;
78ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
79ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	for (pos = 0, i = 0; pos < slen - 2 && pref[i] != '\0'; i++, pos++)
80ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		str[pos] = pref[i];
81ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
82ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	for (i = 0; pos < slen - 2 && i < ilen; pos += len, i++)
83ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		len = snprintf(&str[pos], slen - pos - 1, " %2.2X", ibuf[i]);
84ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
85ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (i < ilen)
86ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		return -1;
87ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
88ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	return 0;
89ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler}
90ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
91ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler/*	NOTE: This function is not thread safe.
92ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	Currently it's called only from sdio rx worker - no race there
93ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler*/
94ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklervoid iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len)
95ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler{
96ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	struct top_msg *msg;
97ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	static char logbuf[LOG_MSG_SIZE_MAX];
98ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
99ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	msg = (struct top_msg *)buf;
100ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
101ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (len < sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr)) {
102ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		LOG_ERROR(priv, FW_MSG, "Log message from TOP "
103ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			  "is too short %d (expected %zd)\n",
104ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			  len, sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr));
105ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		return;
106ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	}
107ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
108ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (!(iwmct_fw_logdefs[msg->u.log.log_hdr.logsource] &
109ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		BIT(msg->u.log.log_hdr.severity)) ||
110ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	    !(iwmct_logdefs[LOG_SRC_FW_MSG] & BIT(msg->u.log.log_hdr.severity)))
111ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		return;
112ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
113ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	switch (msg->hdr.category) {
114ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	case COMM_CATEGORY_TESTABILITY:
115ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		if (!(iwmct_logdefs[LOG_SRC_TST] &
116ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		      BIT(msg->u.log.log_hdr.severity)))
117ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			return;
118ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
119ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler				       le16_to_cpu(msg->hdr.length) +
120ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler				       sizeof(msg->hdr), "<TST>"))
121ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			LOG_WARNING(priv, TST,
122ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler				  "TOP TST message is too long, truncating...");
123ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		LOG_WARNING(priv, TST, "%s\n", logbuf);
124ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		break;
125ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	case COMM_CATEGORY_DEBUG:
126ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		if (msg->hdr.opcode == OP_DBG_ZSTR_MSG)
127ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			LOG_INFO(priv, FW_MSG, "%s %s", "<DBG>",
128ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler				       ((u8 *)msg) + sizeof(msg->hdr)
129ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler					+ sizeof(msg->u.log.log_hdr));
130ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		else {
131ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
132ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler					le16_to_cpu(msg->hdr.length)
133ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler						+ sizeof(msg->hdr),
134ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler					"<DBG>"))
135ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler				LOG_WARNING(priv, FW_MSG,
136ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler					"TOP DBG message is too long,"
137ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler					"truncating...");
138ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			LOG_WARNING(priv, FW_MSG, "%s\n", logbuf);
139ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		}
140ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		break;
141ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	default:
142ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		break;
143ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	}
144ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler}
145ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
146ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerstatic int _log_get_filter_str(u8 *logdefs, int logdefsz, char *buf, int size)
147ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler{
148ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	int i, pos, len;
149ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	for (i = 0, pos = 0; (pos < size-1) && (i < logdefsz); i++) {
150ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		len = snprintf(&buf[pos], size - pos - 1, "0x%02X%02X,",
151ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler				i, logdefs[i]);
152ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		pos += len;
153ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	}
154ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	buf[pos-1] = '\n';
155ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	buf[pos] = '\0';
156ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
157ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (i < logdefsz)
158ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		return -1;
159ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	return 0;
160ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler}
161ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
162ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerint log_get_filter_str(char *buf, int size)
163ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler{
164ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	return _log_get_filter_str(iwmct_logdefs, LOG_SRC_MAX, buf, size);
165ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler}
166ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
167ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerint log_get_fw_filter_str(char *buf, int size)
168ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler{
169ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	return _log_get_filter_str(iwmct_fw_logdefs, FW_LOG_SRC_MAX, buf, size);
170ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler}
171ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
172ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler#define HEXADECIMAL_RADIX	16
173ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler#define LOG_SRC_FORMAT		7 /* log level is in format of "0xXXXX," */
174ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
175ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerssize_t show_iwmct_log_level(struct device *d,
176ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler				struct device_attribute *attr, char *buf)
177ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler{
178ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	struct iwmct_priv *priv = dev_get_drvdata(d);
179ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	char *str_buf;
180ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	int buf_size;
181ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	ssize_t ret;
182ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
183ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	buf_size = (LOG_SRC_FORMAT * LOG_SRC_MAX) + 1;
184ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	str_buf = kzalloc(buf_size, GFP_KERNEL);
185ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (!str_buf) {
186ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		LOG_ERROR(priv, DEBUGFS,
187ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			"failed to allocate %d bytes\n", buf_size);
188ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		ret = -ENOMEM;
189ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		goto exit;
190ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	}
191ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
192ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (log_get_filter_str(str_buf, buf_size) < 0) {
193ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		ret = -EINVAL;
194ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		goto exit;
195ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	}
196ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
197ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	ret = sprintf(buf, "%s", str_buf);
198ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
199ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerexit:
200ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	kfree(str_buf);
201ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	return ret;
202ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler}
203ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
204ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerssize_t store_iwmct_log_level(struct device *d,
205ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			struct device_attribute *attr,
206ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			const char *buf, size_t count)
207ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler{
208ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	struct iwmct_priv *priv = dev_get_drvdata(d);
209ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	char *token, *str_buf = NULL;
210ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	long val;
211ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	ssize_t ret = count;
212ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	u8 src, mask;
213ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
214ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (!count)
215ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		goto exit;
216ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
217ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	str_buf = kzalloc(count, GFP_KERNEL);
218ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (!str_buf) {
219ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		LOG_ERROR(priv, DEBUGFS,
220ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			"failed to allocate %zd bytes\n", count);
221ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		ret = -ENOMEM;
222ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		goto exit;
223ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	}
224ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
225ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	memcpy(str_buf, buf, count);
226ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
227ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	while ((token = strsep(&str_buf, ",")) != NULL) {
228ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		while (isspace(*token))
229ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			++token;
230ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
231ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			LOG_ERROR(priv, DEBUGFS,
232ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler				  "failed to convert string to long %s\n",
233ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler				  token);
234ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			ret = -EINVAL;
235ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			goto exit;
236ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		}
237ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
238ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		mask  = val & 0xFF;
239ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		src = (val & 0XFF00) >> 8;
240ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		iwmct_log_set_filter(src, mask);
241ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	}
242ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
243ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerexit:
244ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	kfree(str_buf);
245ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	return ret;
246ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler}
247ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
248ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerssize_t show_iwmct_log_level_fw(struct device *d,
249ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			struct device_attribute *attr, char *buf)
250ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler{
251ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	struct iwmct_priv *priv = dev_get_drvdata(d);
252ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	char *str_buf;
253ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	int buf_size;
254ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	ssize_t ret;
255ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
256ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	buf_size = (LOG_SRC_FORMAT * FW_LOG_SRC_MAX) + 2;
257ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
258ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	str_buf = kzalloc(buf_size, GFP_KERNEL);
259ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (!str_buf) {
260ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		LOG_ERROR(priv, DEBUGFS,
261ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			"failed to allocate %d bytes\n", buf_size);
262ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		ret = -ENOMEM;
263ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		goto exit;
264ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	}
265ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
266ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (log_get_fw_filter_str(str_buf, buf_size) < 0) {
267ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		ret = -EINVAL;
268ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		goto exit;
269ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	}
270ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
271ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	ret = sprintf(buf, "%s", str_buf);
272ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
273ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerexit:
274ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	kfree(str_buf);
275ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	return ret;
276ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler}
277ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
278ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerssize_t store_iwmct_log_level_fw(struct device *d,
279ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			struct device_attribute *attr,
280ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			const char *buf, size_t count)
281ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler{
282ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	struct iwmct_priv *priv = dev_get_drvdata(d);
283ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	struct top_msg cmd;
284ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	char *token, *str_buf = NULL;
285ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	ssize_t ret = count;
286ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	u16 cmdlen = 0;
287ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	int i;
288ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	long val;
289ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	u8 src, mask;
290ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
291ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (!count)
292ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		goto exit;
293ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
294ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	str_buf = kzalloc(count, GFP_KERNEL);
295ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (!str_buf) {
296ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		LOG_ERROR(priv, DEBUGFS,
297ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			"failed to allocate %zd bytes\n", count);
298ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		ret = -ENOMEM;
299ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		goto exit;
300ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	}
301ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
302ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	memcpy(str_buf, buf, count);
303ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
304ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	cmd.hdr.type = COMM_TYPE_H2D;
305ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	cmd.hdr.category = COMM_CATEGORY_DEBUG;
306ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	cmd.hdr.opcode = CMD_DBG_LOG_LEVEL;
307ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
308ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	for (i = 0; ((token = strsep(&str_buf, ",")) != NULL) &&
309ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		     (i < FW_LOG_SRC_MAX); i++) {
310ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
311ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		while (isspace(*token))
312ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			++token;
313ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
314ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
315ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			LOG_ERROR(priv, DEBUGFS,
316ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler				  "failed to convert string to long %s\n",
317ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler				  token);
318ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			ret = -EINVAL;
319ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			goto exit;
320ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		}
321ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
322ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		mask  = val & 0xFF; /* LSB */
323ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		src = (val & 0XFF00) >> 8; /* 2nd least significant byte. */
324ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		iwmct_log_set_fw_filter(src, mask);
325ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
326ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		cmd.u.logdefs[i].logsource = src;
327ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		cmd.u.logdefs[i].sevmask = mask;
328ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	}
329ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
330ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	cmd.hdr.length = cpu_to_le16(i * sizeof(cmd.u.logdefs[0]));
331ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	cmdlen = (i * sizeof(cmd.u.logdefs[0]) + sizeof(cmd.hdr));
332ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
333ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	ret = iwmct_send_hcmd(priv, (u8 *)&cmd, cmdlen);
334ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	if (ret) {
335ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		LOG_ERROR(priv, DEBUGFS,
336ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			  "Failed to send %d bytes of fwcmd, ret=%zd\n",
337ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler			  cmdlen, ret);
338ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		goto exit;
339ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	} else
340ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler		LOG_INFO(priv, DEBUGFS, "fwcmd sent (%d bytes)\n", cmdlen);
341ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
342ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	ret = count;
343ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
344ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winklerexit:
345ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	kfree(str_buf);
346ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler	return ret;
347ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler}
348ab69a5ae2bdc0b5e20e935a7b75f30aa3f4c3baeTomas Winkler
349