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
164struct h1_gpio_status_request {
165	u8 cmd;		/* Always CMD_KB_CHROME */
166	u8 reserved;
167	u8 sub_cmd;	/* Always SUB_CMD_H1_GPIO */
168} __packed;
169
170struct hi_gpio_status_response {
171	u8 status;	/* 0 if allowed */
172	u8 val;		/* BIT(0)=ENTRY_TO_FACT_MODE, BIT(1)=SPI_CHROME_SEL */
173} __packed;
174
175static int h1_gpio_get(void *arg, u64 *val)
176{
177	struct wilco_ec_device *ec = arg;
178	struct h1_gpio_status_request rq;
179	struct hi_gpio_status_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_H1_GPIO;
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	*val = rs.val;
200
201	return 0;
202}
203
204DEFINE_DEBUGFS_ATTRIBUTE(fops_h1_gpio, h1_gpio_get, NULL, "0x%02llx\n");
205
206/**
207 * wilco_ec_debugfs_probe() - Create the debugfs node
208 * @pdev: The platform device, probably created in core.c
209 *
210 * Try to create a debugfs node. If it fails, then we don't want to change
211 * behavior at all, this is for debugging after all. Just fail silently.
212 *
213 * Return: 0 always.
214 */
215static int wilco_ec_debugfs_probe(struct platform_device *pdev)
216{
217	struct wilco_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
218
219	debug_info = devm_kzalloc(&pdev->dev, sizeof(*debug_info), GFP_KERNEL);
220	if (!debug_info)
221		return 0;
222	debug_info->ec = ec;
223	debug_info->dir = debugfs_create_dir("wilco_ec", NULL);
224	if (!debug_info->dir)
225		return 0;
226	debugfs_create_file("raw", 0644, debug_info->dir, NULL, &fops_raw);
227	debugfs_create_file("h1_gpio", 0444, debug_info->dir, ec,
228			    &fops_h1_gpio);
229
230	return 0;
231}
232
233static int wilco_ec_debugfs_remove(struct platform_device *pdev)
234{
235	debugfs_remove_recursive(debug_info->dir);
236
237	return 0;
238}
239
240static struct platform_driver wilco_ec_debugfs_driver = {
241	.driver = {
242		.name = DRV_NAME,
243	},
244	.probe = wilco_ec_debugfs_probe,
245	.remove = wilco_ec_debugfs_remove,
246};
247
248module_platform_driver(wilco_ec_debugfs_driver);
249
250MODULE_ALIAS("platform:" DRV_NAME);
251MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
252MODULE_LICENSE("GPL v2");
253MODULE_DESCRIPTION("Wilco EC debugfs driver");