brintos

brintos / linux-shallow public Read only

0
0
Text · 4.7 KiB · 6b12a83 Raw
238 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * ipmi_si_hotmod.c4 *5 * Handling for dynamically adding/removing IPMI devices through6 * a module parameter (and thus sysfs).7 */8 9#define pr_fmt(fmt) "ipmi_hotmod: " fmt10 11#include <linux/moduleparam.h>12#include <linux/ipmi.h>13#include <linux/atomic.h>14#include "ipmi_si.h"15#include "ipmi_plat_data.h"16 17static int hotmod_handler(const char *val, const struct kernel_param *kp);18 19module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);20MODULE_PARM_DESC(hotmod,21		 "Add and remove interfaces.  See Documentation/driver-api/ipmi.rst in the kernel sources for the gory details.");22 23/*24 * Parms come in as <op1>[:op2[:op3...]].  ops are:25 *   add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]26 * Options are:27 *   rsp=<regspacing>28 *   rsi=<regsize>29 *   rsh=<regshift>30 *   irq=<irq>31 *   ipmb=<ipmb addr>32 */33enum hotmod_op { HM_ADD, HM_REMOVE };34struct hotmod_vals {35	const char *name;36	const int  val;37};38 39static const struct hotmod_vals hotmod_ops[] = {40	{ "add",	HM_ADD },41	{ "remove",	HM_REMOVE },42	{ NULL }43};44 45static const struct hotmod_vals hotmod_si[] = {46	{ "kcs",	SI_KCS },47	{ "smic",	SI_SMIC },48	{ "bt",		SI_BT },49	{ NULL }50};51 52static const struct hotmod_vals hotmod_as[] = {53	{ "mem",	IPMI_MEM_ADDR_SPACE },54	{ "i/o",	IPMI_IO_ADDR_SPACE },55	{ NULL }56};57 58static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name,59		     const char **curr)60{61	char *s;62	int  i;63 64	s = strchr(*curr, ',');65	if (!s) {66		pr_warn("No hotmod %s given\n", name);67		return -EINVAL;68	}69	*s = '\0';70	s++;71	for (i = 0; v[i].name; i++) {72		if (strcmp(*curr, v[i].name) == 0) {73			*val = v[i].val;74			*curr = s;75			return 0;76		}77	}78 79	pr_warn("Invalid hotmod %s '%s'\n", name, *curr);80	return -EINVAL;81}82 83static int check_hotmod_int_op(const char *curr, const char *option,84			       const char *name, unsigned int *val)85{86	char *n;87 88	if (strcmp(curr, name) == 0) {89		if (!option) {90			pr_warn("No option given for '%s'\n", curr);91			return -EINVAL;92		}93		*val = simple_strtoul(option, &n, 0);94		if ((*n != '\0') || (*option == '\0')) {95			pr_warn("Bad option given for '%s'\n", curr);96			return -EINVAL;97		}98		return 1;99	}100	return 0;101}102 103static int parse_hotmod_str(const char *curr, enum hotmod_op *op,104			    struct ipmi_plat_data *h)105{106	char *s, *o;107	int rv;108	unsigned int ival;109 110	h->iftype = IPMI_PLAT_IF_SI;111	rv = parse_str(hotmod_ops, &ival, "operation", &curr);112	if (rv)113		return rv;114	*op = ival;115 116	rv = parse_str(hotmod_si, &ival, "interface type", &curr);117	if (rv)118		return rv;119	h->type = ival;120 121	rv = parse_str(hotmod_as, &ival, "address space", &curr);122	if (rv)123		return rv;124	h->space = ival;125 126	s = strchr(curr, ',');127	if (s) {128		*s = '\0';129		s++;130	}131	rv = kstrtoul(curr, 0, &h->addr);132	if (rv) {133		pr_warn("Invalid hotmod address '%s': %d\n", curr, rv);134		return rv;135	}136 137	while (s) {138		curr = s;139		s = strchr(curr, ',');140		if (s) {141			*s = '\0';142			s++;143		}144		o = strchr(curr, '=');145		if (o) {146			*o = '\0';147			o++;148		}149		rv = check_hotmod_int_op(curr, o, "rsp", &h->regspacing);150		if (rv < 0)151			return rv;152		else if (rv)153			continue;154		rv = check_hotmod_int_op(curr, o, "rsi", &h->regsize);155		if (rv < 0)156			return rv;157		else if (rv)158			continue;159		rv = check_hotmod_int_op(curr, o, "rsh", &h->regshift);160		if (rv < 0)161			return rv;162		else if (rv)163			continue;164		rv = check_hotmod_int_op(curr, o, "irq", &h->irq);165		if (rv < 0)166			return rv;167		else if (rv)168			continue;169		rv = check_hotmod_int_op(curr, o, "ipmb", &h->slave_addr);170		if (rv < 0)171			return rv;172		else if (rv)173			continue;174 175		pr_warn("Invalid hotmod option '%s'\n", curr);176		return -EINVAL;177	}178 179	h->addr_source = SI_HOTMOD;180	return 0;181}182 183static atomic_t hotmod_nr;184 185static int hotmod_handler(const char *val, const struct kernel_param *kp)186{187	int  rv;188	struct ipmi_plat_data h;189	char *str, *curr, *next;190 191	str = kstrdup(val, GFP_KERNEL);192	if (!str)193		return -ENOMEM;194 195	/* Kill any trailing spaces, as we can get a "\n" from echo. */196	for (curr = strstrip(str); curr; curr = next) {197		enum hotmod_op op;198 199		next = strchr(curr, ':');200		if (next) {201			*next = '\0';202			next++;203		}204 205		memset(&h, 0, sizeof(h));206		rv = parse_hotmod_str(curr, &op, &h);207		if (rv)208			goto out;209 210		if (op == HM_ADD) {211			ipmi_platform_add("hotmod-ipmi-si",212					  atomic_inc_return(&hotmod_nr),213					  &h);214		} else {215			struct device *dev;216 217			dev = ipmi_si_remove_by_data(h.space, h.type, h.addr);218			if (dev && dev_is_platform(dev)) {219				struct platform_device *pdev;220 221				pdev = to_platform_device(dev);222				if (strcmp(pdev->name, "hotmod-ipmi-si") == 0)223					platform_device_unregister(pdev);224			}225			put_device(dev);226		}227	}228	rv = strlen(val);229out:230	kfree(str);231	return rv;232}233 234void ipmi_si_hotmod_exit(void)235{236	ipmi_remove_platform_device_by_name("hotmod-ipmi-si");237}238