| /* | |
| * Remote Processor Framework | |
| * | |
| * This program is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU General Public License | |
| * version 2 as published by the Free Software Foundation. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| */ | |
| #include <linux/remoteproc.h> | |
| #include "remoteproc_internal.h" | |
| #define to_rproc(d) container_of(d, struct rproc, dev) | |
| /* Expose the loaded / running firmware name via sysfs */ | |
| static ssize_t firmware_show(struct device *dev, struct device_attribute *attr, | |
| char *buf) | |
| { | |
| struct rproc *rproc = to_rproc(dev); | |
| return sprintf(buf, "%s\n", rproc->firmware); | |
| } | |
| /* Change firmware name via sysfs */ | |
| static ssize_t firmware_store(struct device *dev, | |
| struct device_attribute *attr, | |
| const char *buf, size_t count) | |
| { | |
| struct rproc *rproc = to_rproc(dev); | |
| char *p; | |
| int err, len = count; | |
| err = mutex_lock_interruptible(&rproc->lock); | |
| if (err) { | |
| dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, err); | |
| return -EINVAL; | |
| } | |
| if (rproc->state != RPROC_OFFLINE) { | |
| dev_err(dev, "can't change firmware while running\n"); | |
| err = -EBUSY; | |
| goto out; | |
| } | |
| len = strcspn(buf, "\n"); | |
| p = kstrndup(buf, len, GFP_KERNEL); | |
| if (!p) { | |
| err = -ENOMEM; | |
| goto out; | |
| } | |
| kfree(rproc->firmware); | |
| rproc->firmware = p; | |
| out: | |
| mutex_unlock(&rproc->lock); | |
| return err ? err : count; | |
| } | |
| static DEVICE_ATTR_RW(firmware); | |
| /* | |
| * A state-to-string lookup table, for exposing a human readable state | |
| * via sysfs. Always keep in sync with enum rproc_state | |
| */ | |
| static const char * const rproc_state_string[] = { | |
| [RPROC_OFFLINE] = "offline", | |
| [RPROC_SUSPENDED] = "suspended", | |
| [RPROC_RUNNING] = "running", | |
| [RPROC_CRASHED] = "crashed", | |
| [RPROC_DELETED] = "deleted", | |
| [RPROC_LAST] = "invalid", | |
| }; | |
| /* Expose the state of the remote processor via sysfs */ | |
| static ssize_t state_show(struct device *dev, struct device_attribute *attr, | |
| char *buf) | |
| { | |
| struct rproc *rproc = to_rproc(dev); | |
| unsigned int state; | |
| state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state; | |
| return sprintf(buf, "%s\n", rproc_state_string[state]); | |
| } | |
| /* Change remote processor state via sysfs */ | |
| static ssize_t state_store(struct device *dev, | |
| struct device_attribute *attr, | |
| const char *buf, size_t count) | |
| { | |
| struct rproc *rproc = to_rproc(dev); | |
| int ret = 0; | |
| if (sysfs_streq(buf, "start")) { | |
| if (rproc->state == RPROC_RUNNING) | |
| return -EBUSY; | |
| ret = rproc_boot(rproc); | |
| if (ret) | |
| dev_err(&rproc->dev, "Boot failed: %d\n", ret); | |
| } else if (sysfs_streq(buf, "stop")) { | |
| if (rproc->state != RPROC_RUNNING) | |
| return -EINVAL; | |
| rproc_shutdown(rproc); | |
| } else { | |
| dev_err(&rproc->dev, "Unrecognised option: %s\n", buf); | |
| ret = -EINVAL; | |
| } | |
| return ret ? ret : count; | |
| } | |
| static DEVICE_ATTR_RW(state); | |
| static struct attribute *rproc_attrs[] = { | |
| &dev_attr_firmware.attr, | |
| &dev_attr_state.attr, | |
| NULL | |
| }; | |
| static const struct attribute_group rproc_devgroup = { | |
| .attrs = rproc_attrs | |
| }; | |
| static const struct attribute_group *rproc_devgroups[] = { | |
| &rproc_devgroup, | |
| NULL | |
| }; | |
| struct class rproc_class = { | |
| .name = "remoteproc", | |
| .dev_groups = rproc_devgroups, | |
| }; | |
| int __init rproc_init_sysfs(void) | |
| { | |
| /* create remoteproc device class for sysfs */ | |
| int err = class_register(&rproc_class); | |
| if (err) | |
| pr_err("remoteproc: unable to register class\n"); | |
| return err; | |
| } | |
| void __exit rproc_exit_sysfs(void) | |
| { | |
| class_unregister(&rproc_class); | |
| } |