1/*
2 *
3 * Intel Management Engine Interface (Intel MEI) Linux driver
4 * Copyright (c) 2012-2013, Intel Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13 * more details.
14 *
15 */
16#include <linux/slab.h>
17#include <linux/kernel.h>
18#include <linux/device.h>
19#include <linux/debugfs.h>
20
21#include <linux/mei.h>
22
23#include "mei_dev.h"
24#include "client.h"
25#include "hw.h"
26
27static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf,
28					size_t cnt, loff_t *ppos)
29{
30	struct mei_device *dev = fp->private_data;
31	struct mei_me_client *me_cl;
32	size_t bufsz = 1;
33	char *buf;
34	int i = 0;
35	int pos = 0;
36	int ret;
37
38#define HDR \
39"  |id|fix|         UUID                       |con|msg len|sb|refc|\n"
40
41	down_read(&dev->me_clients_rwsem);
42	list_for_each_entry(me_cl, &dev->me_clients, list)
43		bufsz++;
44
45	bufsz *= sizeof(HDR) + 1;
46	buf = kzalloc(bufsz, GFP_KERNEL);
47	if (!buf) {
48		up_read(&dev->me_clients_rwsem);
49		return -ENOMEM;
50	}
51
52	pos += scnprintf(buf + pos, bufsz - pos, HDR);
53
54	/*  if the driver is not enabled the list won't be consistent */
55	if (dev->dev_state != MEI_DEV_ENABLED)
56		goto out;
57
58	list_for_each_entry(me_cl, &dev->me_clients, list) {
59
60		if (mei_me_cl_get(me_cl)) {
61			pos += scnprintf(buf + pos, bufsz - pos,
62				"%2d|%2d|%3d|%pUl|%3d|%7d|%2d|%4d|\n",
63				i++, me_cl->client_id,
64				me_cl->props.fixed_address,
65				&me_cl->props.protocol_name,
66				me_cl->props.max_number_of_connections,
67				me_cl->props.max_msg_length,
68				me_cl->props.single_recv_buf,
69				atomic_read(&me_cl->refcnt.refcount));
70
71			mei_me_cl_put(me_cl);
72		}
73	}
74
75out:
76	up_read(&dev->me_clients_rwsem);
77	ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos);
78	kfree(buf);
79	return ret;
80}
81
82static const struct file_operations mei_dbgfs_fops_meclients = {
83	.open = simple_open,
84	.read = mei_dbgfs_read_meclients,
85	.llseek = generic_file_llseek,
86};
87
88static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
89					size_t cnt, loff_t *ppos)
90{
91	struct mei_device *dev = fp->private_data;
92	struct mei_cl *cl;
93	const size_t bufsz = 1024;
94	char *buf;
95	int i = 0;
96	int pos = 0;
97	int ret;
98
99	if (!dev)
100		return -ENODEV;
101
102	buf = kzalloc(bufsz, GFP_KERNEL);
103	if  (!buf)
104		return -ENOMEM;
105
106	pos += scnprintf(buf + pos, bufsz - pos,
107			"  |me|host|state|rd|wr|\n");
108
109	mutex_lock(&dev->device_lock);
110
111	/*  if the driver is not enabled the list won't be consistent */
112	if (dev->dev_state != MEI_DEV_ENABLED)
113		goto out;
114
115	list_for_each_entry(cl, &dev->file_list, link) {
116
117		pos += scnprintf(buf + pos, bufsz - pos,
118			"%2d|%2d|%4d|%5d|%2d|%2d|\n",
119			i, cl->me_client_id, cl->host_client_id, cl->state,
120			!list_empty(&cl->rd_completed), cl->writing_state);
121		i++;
122	}
123out:
124	mutex_unlock(&dev->device_lock);
125	ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos);
126	kfree(buf);
127	return ret;
128}
129
130static const struct file_operations mei_dbgfs_fops_active = {
131	.open = simple_open,
132	.read = mei_dbgfs_read_active,
133	.llseek = generic_file_llseek,
134};
135
136static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf,
137					size_t cnt, loff_t *ppos)
138{
139	struct mei_device *dev = fp->private_data;
140	const size_t bufsz = 1024;
141	char *buf = kzalloc(bufsz, GFP_KERNEL);
142	int pos = 0;
143	int ret;
144
145	if  (!buf)
146		return -ENOMEM;
147
148	pos += scnprintf(buf + pos, bufsz - pos, "dev: %s\n",
149			mei_dev_state_str(dev->dev_state));
150	pos += scnprintf(buf + pos, bufsz - pos, "hbm: %s\n",
151			mei_hbm_state_str(dev->hbm_state));
152	pos += scnprintf(buf + pos, bufsz - pos, "pg:  %s, %s\n",
153			mei_pg_is_enabled(dev) ? "ENABLED" : "DISABLED",
154			mei_pg_state_str(mei_pg_state(dev)));
155	ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos);
156	kfree(buf);
157	return ret;
158}
159static const struct file_operations mei_dbgfs_fops_devstate = {
160	.open = simple_open,
161	.read = mei_dbgfs_read_devstate,
162	.llseek = generic_file_llseek,
163};
164
165/**
166 * mei_dbgfs_deregister - Remove the debugfs files and directories
167 *
168 * @dev: the mei device structure
169 */
170void mei_dbgfs_deregister(struct mei_device *dev)
171{
172	if (!dev->dbgfs_dir)
173		return;
174	debugfs_remove_recursive(dev->dbgfs_dir);
175	dev->dbgfs_dir = NULL;
176}
177
178/**
179 * mei_dbgfs_register - Add the debugfs files
180 *
181 * @dev: the mei device structure
182 * @name: the mei device name
183 *
184 * Return: 0 on success, <0 on failure.
185 */
186int mei_dbgfs_register(struct mei_device *dev, const char *name)
187{
188	struct dentry *dir, *f;
189
190	dir = debugfs_create_dir(name, NULL);
191	if (!dir)
192		return -ENOMEM;
193
194	f = debugfs_create_file("meclients", S_IRUSR, dir,
195				dev, &mei_dbgfs_fops_meclients);
196	if (!f) {
197		dev_err(dev->dev, "meclients: registration failed\n");
198		goto err;
199	}
200	f = debugfs_create_file("active", S_IRUSR, dir,
201				dev, &mei_dbgfs_fops_active);
202	if (!f) {
203		dev_err(dev->dev, "meclients: registration failed\n");
204		goto err;
205	}
206	f = debugfs_create_file("devstate", S_IRUSR, dir,
207				dev, &mei_dbgfs_fops_devstate);
208	if (!f) {
209		dev_err(dev->dev, "devstate: registration failed\n");
210		goto err;
211	}
212	dev->dbgfs_dir = dir;
213	return 0;
214err:
215	mei_dbgfs_deregister(dev);
216	return -ENODEV;
217}
218
219