brintos

brintos / linux-shallow public Read only

0
0
Text · 7.3 KiB · 9948608 Raw
288 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * debugfs attributes for Wilco EC4 *5 * Copyright 2019 Google LLC6 *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 is41 * "   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 1656	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 only82		 * 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		0x88162#define SUB_CMD_H1_GPIO		0x0A163#define SUB_CMD_TEST_EVENT	0x0B164 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_SEL208 */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 node238 * @pdev: The platform device, probably created in core.c239 *240 * Try to create a debugfs node. If it fails, then we don't want to change241 * 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_new = 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");288