Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * debugfs attributes for Wilco EC
  4 *
  5 * Copyright 2019 Google LLC
  6 *
  7 * See Documentation/ABI/testing/debugfs-wilco-ec for usage.
  8 */
  9
 10#include <linux/ctype.h>
 11#include <linux/debugfs.h>
 12#include <linux/fs.h>
 13#include <linux/mod_devicetable.h>
 14#include <linux/module.h>
 15#include <linux/platform_data/wilco-ec.h>
 16#include <linux/platform_device.h>
 17
 18#define DRV_NAME "wilco-ec-debugfs"
 19
 20/* The raw bytes will take up more space when represented as a hex string */
 21#define FORMATTED_BUFFER_SIZE (EC_MAILBOX_DATA_SIZE * 4)
 22
 23struct wilco_ec_debugfs {
 24	struct wilco_ec_device *ec;
 25	struct dentry *dir;
 26	size_t response_size;
 27	u8 raw_data[EC_MAILBOX_DATA_SIZE];
 28	u8 formatted_data[FORMATTED_BUFFER_SIZE];
 29};
 30static struct wilco_ec_debugfs *debug_info;
 31
 32/**
 33 * parse_hex_sentence() - Convert a ascii hex representation into byte array.
 34 * @in: Input buffer of ascii.
 35 * @isize: Length of input buffer.
 36 * @out: Output buffer.
 37 * @osize: Length of output buffer, e.g. max number of bytes to parse.
 38 *
 39 * An valid input is a series of ascii hexadecimal numbers, separated by spaces.
 40 * An example valid input is
 41 * "   00 f2 0    000076 6 0  ff"
 42 *
 43 * If an individual "word" within the hex sentence is longer than MAX_WORD_SIZE,
 44 * then the sentence is illegal, and parsing will fail.
 45 *
 46 * Return: Number of bytes parsed, or negative error code on failure.
 47 */
 48static int parse_hex_sentence(const char *in, int isize, u8 *out, int osize)
 49{
 50	int n_parsed = 0;
 51	int word_start = 0;
 52	int word_end;
 53	int word_len;
 54	/* Temp buffer for holding a "word" of chars that represents one byte */
 55	#define MAX_WORD_SIZE 16
 56	char tmp[MAX_WORD_SIZE + 1];
 57	u8 byte;
 58
 59	while (word_start < isize && n_parsed < osize) {
 60		/* Find the start of the next word */
 61		while (word_start < isize && isspace(in[word_start]))
 62			word_start++;
 63		 /* reached the end of the input before next word? */
 64		if (word_start >= isize)
 65			break;
 66
 67		/* Find the end of this word */
 68		word_end = word_start;
 69		while (word_end < isize && !isspace(in[word_end]))
 70			word_end++;
 71
 72		/* Copy to a tmp NULL terminated string */
 73		word_len = word_end - word_start;
 74		if (word_len > MAX_WORD_SIZE)
 75			return -EINVAL;
 76		memcpy(tmp, in + word_start, word_len);
 77		tmp[word_len] = '\0';
 78
 79		/*
 80		 * Convert from hex string, place in output. If fails to parse,
 81		 * just return -EINVAL because specific error code is only
 82		 * relevant for this one word, returning it would be confusing.
 83		 */
 84		if (kstrtou8(tmp, 16, &byte))
 85			return -EINVAL;
 86		out[n_parsed++] = byte;
 87
 88		word_start = word_end;
 89	}
 90	return n_parsed;
 91}
 92
 93/* The message type takes up two bytes*/
 94#define TYPE_AND_DATA_SIZE ((EC_MAILBOX_DATA_SIZE) + 2)
 95
 96static ssize_t raw_write(struct file *file, const char __user *user_buf,
 97			 size_t count, loff_t *ppos)
 98{
 99	char *buf = debug_info->formatted_data;
100	struct wilco_ec_message msg;
101	u8 request_data[TYPE_AND_DATA_SIZE];
102	ssize_t kcount;
103	int ret;
104
105	if (count > FORMATTED_BUFFER_SIZE)
106		return -EINVAL;
107
108	kcount = simple_write_to_buffer(buf, FORMATTED_BUFFER_SIZE, ppos,
109					user_buf, count);
110	if (kcount < 0)
111		return kcount;
112
113	ret = parse_hex_sentence(buf, kcount, request_data, TYPE_AND_DATA_SIZE);
114	if (ret < 0)
115		return ret;
116	/* Need at least two bytes for message type and one byte of data */
117	if (ret < 3)
118		return -EINVAL;
119
120	msg.type = request_data[0] << 8 | request_data[1];
121	msg.flags = 0;
122	msg.request_data = request_data + 2;
123	msg.request_size = ret - 2;
124	memset(debug_info->raw_data, 0, sizeof(debug_info->raw_data));
125	msg.response_data = debug_info->raw_data;
126	msg.response_size = EC_MAILBOX_DATA_SIZE;
127
128	ret = wilco_ec_mailbox(debug_info->ec, &msg);
129	if (ret < 0)
130		return ret;
131	debug_info->response_size = ret;
132
133	return count;
134}
135
136static ssize_t raw_read(struct file *file, char __user *user_buf, size_t count,
137			loff_t *ppos)
138{
139	int fmt_len = 0;
140
141	if (debug_info->response_size) {
142		fmt_len = hex_dump_to_buffer(debug_info->raw_data,
143					     debug_info->response_size,
144					     16, 1, debug_info->formatted_data,
145					     sizeof(debug_info->formatted_data),
146					     true);
147		/* Only return response the first time it is read */
148		debug_info->response_size = 0;
149	}
150
151	return simple_read_from_buffer(user_buf, count, ppos,
152				       debug_info->formatted_data, fmt_len);
153}
154
155static const struct file_operations fops_raw = {
156	.owner = THIS_MODULE,
157	.read = raw_read,
158	.write = raw_write,
159};
160
161#define CMD_KB_CHROME		0x88
162#define SUB_CMD_H1_GPIO		0x0A
163#define SUB_CMD_TEST_EVENT	0x0B
164
165struct ec_request {
166	u8 cmd;		/* Always CMD_KB_CHROME */
167	u8 reserved;
168	u8 sub_cmd;
169} __packed;
170
171struct ec_response {
172	u8 status;	/* 0 if allowed */
173	u8 val;
174} __packed;
175
176static int send_ec_cmd(struct wilco_ec_device *ec, u8 sub_cmd, u8 *out_val)
177{
178	struct ec_request rq;
179	struct ec_response rs;
180	struct wilco_ec_message msg;
181	int ret;
182
183	memset(&rq, 0, sizeof(rq));
184	rq.cmd = CMD_KB_CHROME;
185	rq.sub_cmd = sub_cmd;
186
187	memset(&msg, 0, sizeof(msg));
188	msg.type = WILCO_EC_MSG_LEGACY;
189	msg.request_data = &rq;
190	msg.request_size = sizeof(rq);
191	msg.response_data = &rs;
192	msg.response_size = sizeof(rs);
193	ret = wilco_ec_mailbox(ec, &msg);
194	if (ret < 0)
195		return ret;
196	if (rs.status)
197		return -EIO;
198
199	*out_val = rs.val;
200
201	return 0;
202}
203
204/**
205 * h1_gpio_get() - Gets h1 gpio status.
206 * @arg: The wilco EC device.
207 * @val: BIT(0)=ENTRY_TO_FACT_MODE, BIT(1)=SPI_CHROME_SEL
208 */
209static int h1_gpio_get(void *arg, u64 *val)
210{
211	int ret;
212
213	ret = send_ec_cmd(arg, SUB_CMD_H1_GPIO, (u8 *)val);
214	if (ret == 0)
215		*val &= 0xFF;
216	return ret;
217}
218
219DEFINE_DEBUGFS_ATTRIBUTE(fops_h1_gpio, h1_gpio_get, NULL, "0x%02llx\n");
220
221/**
222 * test_event_set() - Sends command to EC to cause an EC test event.
223 * @arg: The wilco EC device.
224 * @val: unused.
225 */
226static int test_event_set(void *arg, u64 val)
227{
228	u8 ret;
229
230	return send_ec_cmd(arg, SUB_CMD_TEST_EVENT, &ret);
231}
232
233/* Format is unused since it is only required for get method which is NULL */
234DEFINE_DEBUGFS_ATTRIBUTE(fops_test_event, NULL, test_event_set, "%llu\n");
235
236/**
237 * wilco_ec_debugfs_probe() - Create the debugfs node
238 * @pdev: The platform device, probably created in core.c
239 *
240 * Try to create a debugfs node. If it fails, then we don't want to change
241 * behavior at all, this is for debugging after all. Just fail silently.
242 *
243 * Return: 0 always.
244 */
245static int wilco_ec_debugfs_probe(struct platform_device *pdev)
246{
247	struct wilco_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
248
249	debug_info = devm_kzalloc(&pdev->dev, sizeof(*debug_info), GFP_KERNEL);
250	if (!debug_info)
251		return 0;
252	debug_info->ec = ec;
253	debug_info->dir = debugfs_create_dir("wilco_ec", NULL);
254	debugfs_create_file("raw", 0644, debug_info->dir, NULL, &fops_raw);
255	debugfs_create_file("h1_gpio", 0444, debug_info->dir, ec,
256			    &fops_h1_gpio);
257	debugfs_create_file("test_event", 0200, debug_info->dir, ec,
258			    &fops_test_event);
259
260	return 0;
261}
262
263static void wilco_ec_debugfs_remove(struct platform_device *pdev)
264{
265	debugfs_remove_recursive(debug_info->dir);
266}
267
268static const struct platform_device_id wilco_ec_debugfs_id[] = {
269	{ DRV_NAME, 0 },
270	{}
271};
272MODULE_DEVICE_TABLE(platform, wilco_ec_debugfs_id);
273
274static struct platform_driver wilco_ec_debugfs_driver = {
275	.driver = {
276		.name = DRV_NAME,
277	},
278	.probe = wilco_ec_debugfs_probe,
279	.remove = wilco_ec_debugfs_remove,
280	.id_table = wilco_ec_debugfs_id,
281};
282
283module_platform_driver(wilco_ec_debugfs_driver);
284
285MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
286MODULE_LICENSE("GPL v2");
287MODULE_DESCRIPTION("Wilco EC debugfs driver");