@@ -5,6 +5,7 @@
* Copyright (C) 2014 Kamlakant Patel <kamlakant.patel@broadcom.com>
* Copyright (C) 2015-2016 Bamvor Jian Zhang <bamv2005@gmail.com>
* Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl>
+ * Copyright (C) 2020 Bartosz Golaszewski <bgolaszewski@baylibre.com>
*/
#include <linux/debugfs.h>
@@ -14,6 +15,7 @@
#include <linux/irq.h>
#include <linux/irq_sim.h>
#include <linux/irqdomain.h>
+#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -546,8 +548,8 @@ static void gpio_mockup_unregister_devices(void)
mutex_unlock(&gpio_mockup_devices_lock);
}
-static __init char **gpio_mockup_make_line_names(const char *label,
- unsigned int num_lines)
+static char **gpio_mockup_make_line_names(const char *label,
+ unsigned int num_lines)
{
unsigned int i;
char **names;
@@ -567,7 +569,7 @@ static __init char **gpio_mockup_make_line_names(const char *label,
return names;
}
-static int __init gpio_mockup_register_device(struct property_entry *properties)
+static int gpio_mockup_register_device(struct property_entry *properties)
{
struct gpio_mockup_device *mockup_dev;
struct platform_device_info pdevinfo;
@@ -641,8 +643,7 @@ static int __init gpio_mockup_register_chips_from_params(void)
{
int num_chips, i, ret;
- if ((gpio_mockup_num_ranges < 2) ||
- (gpio_mockup_num_ranges % 2) ||
+ if ((gpio_mockup_num_ranges % 2) ||
(gpio_mockup_num_ranges > GPIO_MOCKUP_MAX_RANGES))
return -EINVAL;
@@ -669,6 +670,205 @@ static int __init gpio_mockup_register_chips_from_params(void)
return 0;
}
+/*
+ * We store all data associated with device properties in this structure. It's
+ * only needed until we register the platform device at which point the driver
+ * core makes a deep copy of all property data (even the string arrays).
+ *
+ * The reason to keep them bunched up is simple: we can have a single function
+ * to free all resources which simplifies error handling.
+ */
+struct gpio_mockup_prop_data {
+ char chip_label[GPIO_MOCKUP_LABEL_SIZE];
+ u16 ngpio;
+ bool named_lines;
+ char **line_names;
+};
+
+/* We don't free the structure itself - it's expected to live on the stack. */
+static void
+gpio_mockup_free_property_data(struct gpio_mockup_prop_data *prop_data)
+{
+ kfree_strarray(prop_data->line_names, prop_data->ngpio);
+}
+
+/*
+ * Each supported option is parsed by a separate callback - this way the
+ * 'new_device' attribute is easily exstensible.
+ */
+struct gpio_mockup_new_device_opt {
+ char *name;
+ int (*func)(const char *val, struct gpio_mockup_prop_data *prop_data,
+ struct property_entry *properties, int *prop_idx);
+ bool has_val;
+};
+
+static int
+gpio_mockup_parse_label(const char *val,
+ struct gpio_mockup_prop_data *prop_data,
+ struct property_entry *properties, int *prop_idx)
+{
+ snprintf(prop_data->chip_label, sizeof(prop_data->chip_label), val);
+ properties[(*prop_idx)++] =
+ PROPERTY_ENTRY_STRING("chip-label", prop_data->chip_label);
+
+ return 0;
+}
+
+static int gpio_mockup_parse_num_lines(const char *val,
+ struct gpio_mockup_prop_data *prop_data,
+ struct property_entry *properties, int *prop_idx)
+{
+ int ret;
+
+ ret = kstrtou16(val, 10, &prop_data->ngpio);
+ if (ret) {
+ pr_err("invalid new_lines format: %s\n", val);
+ return ret;
+ }
+
+ properties[(*prop_idx)++] = PROPERTY_ENTRY_U16("nr-gpios",
+ prop_data->ngpio);
+
+ return 0;
+}
+
+static int gpio_mockup_parse_named_lines(const char *val,
+ struct gpio_mockup_prop_data *prop_data,
+ struct property_entry *properties, int *prop_idx)
+{
+ prop_data->named_lines = true;
+
+ return 0;
+}
+
+static struct gpio_mockup_new_device_opt gpio_mockup_new_device_opts[] = {
+ {
+ .name = "label",
+ .func = gpio_mockup_parse_label,
+ .has_val = true,
+ },
+ {
+ .name = "num_lines",
+ .func = gpio_mockup_parse_num_lines,
+ .has_val = true,
+ },
+ {
+ .name = "named_lines",
+ .func = gpio_mockup_parse_named_lines,
+ .has_val = false,
+ },
+};
+
+static int
+gpio_mockup_parse_one_opt(const char *key, const char *val,
+ struct gpio_mockup_prop_data *prop_data,
+ struct property_entry *properties, int *prop_idx)
+{
+ struct gpio_mockup_new_device_opt *opt;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(gpio_mockup_new_device_opts); i++) {
+ opt = &gpio_mockup_new_device_opts[i];
+
+ if (strcmp(key, opt->name) == 0) {
+ if (opt->has_val && !val) {
+ pr_err("%s option requires an argument\n",
+ opt->name);
+ return -EINVAL;
+ }
+
+ if (!opt->has_val && val) {
+ pr_err("%s option doesn't take any arguments\n",
+ opt->name);
+ return -EINVAL;
+ }
+
+ return opt->func(val, prop_data, properties, prop_idx);
+ }
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int
+gpio_mockup_new_device_from_opts(char *opts,
+ struct gpio_mockup_prop_data *prop_data)
+{
+ struct property_entry properties[GPIO_MOCKUP_MAX_PROP];
+ int prop_idx = 0, ret;
+ char *key, *val;
+
+ memset(properties, 0, sizeof(properties));
+
+ while (*opts) {
+ if (prop_idx >= GPIO_MOCKUP_MAX_PROP)
+ return -EINVAL;
+
+ opts = next_arg(opts, &key, &val);
+
+ ret = gpio_mockup_parse_one_opt(key, val, prop_data,
+ properties, &prop_idx);
+ if (ret)
+ return ret;
+ }
+
+ /* This is the only mandatory property. */
+ if (!prop_data->ngpio) {
+ pr_err("number of lines must be specified\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Line names must be created at the end - once we know how
+ * many GPIOs there are.
+ */
+ if (prop_data->named_lines) {
+ prop_data->line_names = gpio_mockup_make_line_names(
+ prop_data->chip_label,
+ prop_data->ngpio);
+ if (!prop_data->line_names)
+ return -ENOMEM;
+
+ properties[prop_idx++] =
+ PROPERTY_ENTRY_STRING_ARRAY_LEN("gpio-line-names",
+ prop_data->line_names,
+ prop_data->ngpio);
+ }
+
+ return gpio_mockup_register_device(properties);
+}
+
+static ssize_t gpio_mockup_debugfs_new_device_write(struct file *file,
+ const char __user *usr_buf,
+ size_t size, loff_t *ppos)
+{
+ struct gpio_mockup_prop_data prop_data;
+ char opts[128];
+ int ret;
+
+ if (*ppos != 0 || size > sizeof(opts))
+ return -EINVAL;
+
+ ret = getline_from_user(opts, sizeof(opts), usr_buf, size);
+ if (ret < 0)
+ return ret;
+
+ memset(&prop_data, 0, sizeof(prop_data));
+
+ ret = gpio_mockup_new_device_from_opts(opts, &prop_data);
+ gpio_mockup_free_property_data(&prop_data);
+ return ret < 0 ? ret : size;
+}
+
+static const struct file_operations gpio_mockup_debugfs_new_device_ops = {
+ .owner = THIS_MODULE,
+ .open = gpio_mockup_debugfs_open,
+ .write = gpio_mockup_debugfs_new_device_write,
+ .llseek = no_llseek,
+ .release = single_release,
+};
+
static ssize_t gpio_mockup_debugfs_delete_device_write(struct file *file,
const char __user *usr_buf,
size_t size, loff_t *ppos)
@@ -726,6 +926,13 @@ static int __init gpio_mockup_debugfs_init(void)
if (IS_ERR(gpio_mockup_dbg_dir))
return PTR_ERR(gpio_mockup_dbg_dir);
+ entry = debugfs_create_file("new_device", 0200, gpio_mockup_dbg_dir,
+ NULL, &gpio_mockup_debugfs_new_device_ops);
+ if (IS_ERR(entry)) {
+ debugfs_remove_recursive(gpio_mockup_dbg_dir);
+ return PTR_ERR(entry);
+ }
+
entry = debugfs_create_file("delete_device", 0200, gpio_mockup_dbg_dir,
NULL, &gpio_mockup_debugfs_delete_device_ops);
if (IS_ERR(entry)) {
@@ -751,12 +958,14 @@ static int __init gpio_mockup_init(void)
return ret;
}
- ret = gpio_mockup_register_chips_from_params();
- if (ret) {
- pr_err("error registering device");
- debugfs_remove_recursive(gpio_mockup_dbg_dir);
- platform_driver_unregister(&gpio_mockup_driver);
- return ret;
+ if (gpio_mockup_ranges > 0) {
+ ret = gpio_mockup_register_chips_from_params();
+ if (ret) {
+ pr_err("error registering device");
+ debugfs_remove_recursive(gpio_mockup_dbg_dir);
+ platform_driver_unregister(&gpio_mockup_driver);
+ return ret;
+ }
}
return 0;