Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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/module.h>
 14#include <linux/platform_data/wilco-ec.h>
 15#include <linux/platform_device.h>
 16
 17#define DRV_NAME "wilco-ec-debugfs"
 18
 19/* The raw bytes will take up more space when represented as a hex string */
 20#define FORMATTED_BUFFER_SIZE (EC_MAILBOX_DATA_SIZE * 4)
 21
 22struct wilco_ec_debugfs {
 23	struct wilco_ec_device *ec;
 24	struct dentry *dir;
 25	size_t response_size;
 26	u8 raw_data[EC_MAILBOX_DATA_SIZE];
 27	u8 formatted_data[FORMATTED_BUFFER_SIZE];
 28};
 29static struct wilco_ec_debugfs *debug_info;
 30
 31/**
 32 * parse_hex_sentence() - Convert a ascii hex representation into byte array.
 33 * @in: Input buffer of ascii.
 34 * @isize: Length of input buffer.
 35 * @out: Output buffer.
 36 * @osize: Length of output buffer, e.g. max number of bytes to parse.
 37 *
 38 * An valid input is a series of ascii hexadecimal numbers, separated by spaces.
 39 * An example valid input is
 40 * "   00 f2 0    000076 6 0  ff"
 41 *
 42 * If an individual "word" within the hex sentence is longer than MAX_WORD_SIZE,
 43 * then the sentence is illegal, and parsing will fail.
 44 *
 45 * Return: Number of bytes parsed, or negative error code on failure.
 46 */
 47static int parse_hex_sentence(const char *in, int isize, u8 *out, int osize)
 48{
 49	int n_parsed = 0;
 50	int word_start = 0;
 51	int word_end;
 52	int word_len;
 53	/* Temp buffer for holding a "word" of chars that represents one byte */
 54	#define MAX_WORD_SIZE 16
 55	char tmp[MAX_WORD_SIZE + 1];
 56	u8 byte;
 57
 58	while (word_start < isize && n_parsed < osize) {
 59		/* Find the start of the next word */
 60		while (word_start < isize && isspace(in[word_start]))
 61			word_start++;
 62		 /* reached the end of the input before next word? */
 63		if (word_start >= isize)
 64			break;
 65
 66		/* Find the end of this word */
 67		word_end = word_start;
 68		while (word_end < isize && !isspace(in[word_end]))
 69			word_end++;
 70
 71		/* Copy to a tmp NULL terminated string */
 72		word_len = word_end - word_start;
 73		if (word_len > MAX_WORD_SIZE)
 74			return -EINVAL;
 75		memcpy(tmp, in + word_start, word_len);
 76		tmp[word_len] = '\0';
 77
 78		/*
 79		 * Convert from hex string, place in output. If fails to parse,
 80		 * just return -EINVAL because specific error code is only
 81		 * relevant for this one word, returning it would be confusing.
 82		 */
 83		if (kstrtou8(tmp, 16, &byte))
 84			return -EINVAL;
 85		out[n_parsed++] = byte;
 86
 87		word_start = word_end;
 88	}
 89	return n_parsed;
 90}
 91
 92/* The message type takes up two bytes*/
 93#define TYPE_AND_DATA_SIZE ((EC_MAILBOX_DATA_SIZE) + 2)
 94
 95static ssize_t raw_write(struct file *file, const char __user *user_buf,
 96			 size_t count, loff_t *ppos)
 97{
 98	char *buf = debug_info->formatted_data;
 99	struct wilco_ec_message msg;
100	u8 request_data[TYPE_AND_DATA_SIZE];
101	ssize_t kcount;
102	int ret;
103
104	if (count > FORMATTED_BUFFER_SIZE)
105		return -EINVAL;
106
107	kcount = simple_write_to_buffer(buf, FORMATTED_BUFFER_SIZE, ppos,
108					user_buf, count);
109	if (kcount < 0)
110		return kcount;
111
112	ret = parse_hex_sentence(buf, kcount, request_data, TYPE_AND_DATA_SIZE);
113	if (ret < 0)
114		return ret;
115	/* Need at least two bytes for message type and one byte of data */
116	if (ret < 3)
117		return -EINVAL;
118
119	msg.type = request_data[0] << 8 | request_data[1];
120	msg.flags = 0;
121	msg.request_data = request_data + 2;
122	msg.request_size = ret - 2;
123	memset(debug_info->raw_data, 0, sizeof(debug_info->raw_data));
124	msg.response_data = debug_info->raw_data;
125	msg.response_size = EC_MAILBOX_DATA_SIZE;
126
127	ret = wilco_ec_mailbox(debug_info->ec, &msg);
128	if (ret < 0)
129		return ret;
130	debug_info->response_size = ret;
131
132	return count;
133}
134
135static ssize_t raw_read(struct file *file, char __user *user_buf, size_t count,
136			loff_t *ppos)
137{
138	int fmt_len = 0;
139
140	if (debug_info->response_size) {
141		fmt_len = hex_dump_to_buffer(debug_info->raw_data,
142					     debug_info->response_size,
143					     16, 1, debug_info->formatted_data,
144					     sizeof(debug_info->formatted_data),
145					     true);
146		/* Only return response the first time it is read */
147		debug_info->response_size = 0;
148	}
149
150	return simple_read_from_buffer(user_buf, count, ppos,
151				       debug_info->formatted_data, fmt_len);
152}
153
154static const struct file_operations fops_raw = {
155	.owner = THIS_MODULE,
156	.read = raw_read,
157	.write = raw_write,
158	.llseek = no_llseek,
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	if (!debug_info->dir)
255		return 0;
256	debugfs_create_file("raw", 0644, debug_info->dir, NULL, &fops_raw);
257	debugfs_create_file("h1_gpio", 0444, debug_info->dir, ec,
258			    &fops_h1_gpio);
259	debugfs_create_file("test_event", 0200, debug_info->dir, ec,
260			    &fops_test_event);
261
262	return 0;
263}
264
265static int wilco_ec_debugfs_remove(struct platform_device *pdev)
266{
267	debugfs_remove_recursive(debug_info->dir);
268
269	return 0;
270}
271
272static struct platform_driver wilco_ec_debugfs_driver = {
273	.driver = {
274		.name = DRV_NAME,
275	},
276	.probe = wilco_ec_debugfs_probe,
277	.remove = wilco_ec_debugfs_remove,
278};
279
280module_platform_driver(wilco_ec_debugfs_driver);
281
282MODULE_ALIAS("platform:" DRV_NAME);
283MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
284MODULE_LICENSE("GPL v2");
285MODULE_DESCRIPTION("Wilco EC debugfs driver");