ipmi_si: Move the hotmod handling to another file.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
This commit is contained in:
Corey Minyard 2017-09-12 22:28:49 -05:00
parent bb398a4cb0
commit 44814ec982
4 changed files with 264 additions and 244 deletions

View File

@ -2,7 +2,8 @@
# Makefile for the ipmi drivers.
#
ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o
ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o \
ipmi_si_hotmod.o
obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o

View File

@ -20,3 +20,5 @@ void ipmi_irq_start_cleanup(struct si_sm_io *io);
int ipmi_std_irq_setup(struct si_sm_io *io);
void ipmi_irq_finish_setup(struct si_sm_io *io);
int ipmi_si_remove_by_dev(struct device *dev);
void ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
unsigned long addr);

View File

@ -0,0 +1,242 @@
/*
* ipmi_si_hotmod.c
*
* Handling for dynamically adding/removing IPMI devices through
* a module parameter (and thus sysfs).
*/
#include <linux/moduleparam.h>
#include <linux/ipmi.h>
#include "ipmi_si.h"
#define PFX "ipmi_hotmod: "
static int hotmod_handler(const char *val, struct kernel_param *kp);
module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See"
" Documentation/IPMI.txt in the kernel sources for the"
" gory details.");
/*
* Parms come in as <op1>[:op2[:op3...]]. ops are:
* add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]
* Options are:
* rsp=<regspacing>
* rsi=<regsize>
* rsh=<regshift>
* irq=<irq>
* ipmb=<ipmb addr>
*/
enum hotmod_op { HM_ADD, HM_REMOVE };
struct hotmod_vals {
const char *name;
const int val;
};
static const struct hotmod_vals hotmod_ops[] = {
{ "add", HM_ADD },
{ "remove", HM_REMOVE },
{ NULL }
};
static const struct hotmod_vals hotmod_si[] = {
{ "kcs", SI_KCS },
{ "smic", SI_SMIC },
{ "bt", SI_BT },
{ NULL }
};
static const struct hotmod_vals hotmod_as[] = {
{ "mem", IPMI_MEM_ADDR_SPACE },
{ "i/o", IPMI_IO_ADDR_SPACE },
{ NULL }
};
static int parse_str(const struct hotmod_vals *v, int *val, char *name,
char **curr)
{
char *s;
int i;
s = strchr(*curr, ',');
if (!s) {
pr_warn(PFX "No hotmod %s given.\n", name);
return -EINVAL;
}
*s = '\0';
s++;
for (i = 0; v[i].name; i++) {
if (strcmp(*curr, v[i].name) == 0) {
*val = v[i].val;
*curr = s;
return 0;
}
}
pr_warn(PFX "Invalid hotmod %s '%s'\n", name, *curr);
return -EINVAL;
}
static int check_hotmod_int_op(const char *curr, const char *option,
const char *name, int *val)
{
char *n;
if (strcmp(curr, name) == 0) {
if (!option) {
pr_warn(PFX "No option given for '%s'\n", curr);
return -EINVAL;
}
*val = simple_strtoul(option, &n, 0);
if ((*n != '\0') || (*option == '\0')) {
pr_warn(PFX "Bad option given for '%s'\n", curr);
return -EINVAL;
}
return 1;
}
return 0;
}
static int hotmod_handler(const char *val, struct kernel_param *kp)
{
char *str = kstrdup(val, GFP_KERNEL);
int rv;
char *next, *curr, *s, *n, *o;
enum hotmod_op op;
enum si_type si_type;
int addr_space;
unsigned long addr;
int regspacing;
int regsize;
int regshift;
int irq;
int ipmb;
int ival;
int len;
if (!str)
return -ENOMEM;
/* Kill any trailing spaces, as we can get a "\n" from echo. */
len = strlen(str);
ival = len - 1;
while ((ival >= 0) && isspace(str[ival])) {
str[ival] = '\0';
ival--;
}
for (curr = str; curr; curr = next) {
regspacing = 1;
regsize = 1;
regshift = 0;
irq = 0;
ipmb = 0; /* Choose the default if not specified */
next = strchr(curr, ':');
if (next) {
*next = '\0';
next++;
}
rv = parse_str(hotmod_ops, &ival, "operation", &curr);
if (rv)
break;
op = ival;
rv = parse_str(hotmod_si, &ival, "interface type", &curr);
if (rv)
break;
si_type = ival;
rv = parse_str(hotmod_as, &addr_space, "address space", &curr);
if (rv)
break;
s = strchr(curr, ',');
if (s) {
*s = '\0';
s++;
}
addr = simple_strtoul(curr, &n, 0);
if ((*n != '\0') || (*curr == '\0')) {
pr_warn(PFX "Invalid hotmod address '%s'\n", curr);
break;
}
while (s) {
curr = s;
s = strchr(curr, ',');
if (s) {
*s = '\0';
s++;
}
o = strchr(curr, '=');
if (o) {
*o = '\0';
o++;
}
rv = check_hotmod_int_op(curr, o, "rsp", &regspacing);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "rsi", &regsize);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "rsh", &regshift);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "irq", &irq);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = -EINVAL;
pr_warn(PFX "Invalid hotmod option '%s'\n", curr);
goto out;
}
if (op == HM_ADD) {
struct si_sm_io io;
memset(&io, 0, sizeof(io));
io.addr_source = SI_HOTMOD;
io.si_type = si_type;
io.addr_data = addr;
io.addr_type = addr_space;
io.addr = NULL;
io.regspacing = regspacing;
if (!io.regspacing)
io.regspacing = DEFAULT_REGSPACING;
io.regsize = regsize;
if (!io.regsize)
io.regsize = DEFAULT_REGSIZE;
io.regshift = regshift;
io.irq = irq;
if (io.irq)
io.irq_setup = ipmi_std_irq_setup;
io.slave_addr = ipmb;
rv = ipmi_si_add_smi(&io);
if (rv)
goto out;
} else {
ipmi_si_remove_by_data(addr_space, si_type, addr);
}
}
rv = len;
out:
kfree(str);
return rv;
}

View File

@ -1310,13 +1310,6 @@ static unsigned int num_slave_addrs;
static const char * const addr_space_to_str[] = { "i/o", "mem" };
static int hotmod_handler(const char *val, struct kernel_param *kp);
module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See"
" Documentation/IPMI.txt in the kernel sources for the"
" gory details.");
#ifdef CONFIG_ACPI
module_param_named(tryacpi, si_tryacpi, bool, 0);
MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the"
@ -1689,86 +1682,6 @@ static int mem_setup(struct si_sm_io *io)
return 0;
}
/*
* Parms come in as <op1>[:op2[:op3...]]. ops are:
* add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]
* Options are:
* rsp=<regspacing>
* rsi=<regsize>
* rsh=<regshift>
* irq=<irq>
* ipmb=<ipmb addr>
*/
enum hotmod_op { HM_ADD, HM_REMOVE };
struct hotmod_vals {
const char *name;
const int val;
};
static const struct hotmod_vals hotmod_ops[] = {
{ "add", HM_ADD },
{ "remove", HM_REMOVE },
{ NULL }
};
static const struct hotmod_vals hotmod_si[] = {
{ "kcs", SI_KCS },
{ "smic", SI_SMIC },
{ "bt", SI_BT },
{ NULL }
};
static const struct hotmod_vals hotmod_as[] = {
{ "mem", IPMI_MEM_ADDR_SPACE },
{ "i/o", IPMI_IO_ADDR_SPACE },
{ NULL }
};
static int parse_str(const struct hotmod_vals *v, int *val, char *name,
char **curr)
{
char *s;
int i;
s = strchr(*curr, ',');
if (!s) {
pr_warn(PFX "No hotmod %s given.\n", name);
return -EINVAL;
}
*s = '\0';
s++;
for (i = 0; v[i].name; i++) {
if (strcmp(*curr, v[i].name) == 0) {
*val = v[i].val;
*curr = s;
return 0;
}
}
pr_warn(PFX "Invalid hotmod %s '%s'\n", name, *curr);
return -EINVAL;
}
static int check_hotmod_int_op(const char *curr, const char *option,
const char *name, int *val)
{
char *n;
if (strcmp(curr, name) == 0) {
if (!option) {
pr_warn(PFX "No option given for '%s'\n", curr);
return -EINVAL;
}
*val = simple_strtoul(option, &n, 0);
if ((*n != '\0') || (*option == '\0')) {
pr_warn(PFX "Bad option given for '%s'\n", curr);
return -EINVAL;
}
return 1;
}
return 0;
}
static struct smi_info *smi_info_alloc(void)
{
struct smi_info *info = kzalloc(sizeof(*info), GFP_KERNEL);
@ -1778,162 +1691,6 @@ static struct smi_info *smi_info_alloc(void)
return info;
}
static int hotmod_handler(const char *val, struct kernel_param *kp)
{
char *str = kstrdup(val, GFP_KERNEL);
int rv;
char *next, *curr, *s, *n, *o;
enum hotmod_op op;
enum si_type si_type;
int addr_space;
unsigned long addr;
int regspacing;
int regsize;
int regshift;
int irq;
int ipmb;
int ival;
int len;
if (!str)
return -ENOMEM;
/* Kill any trailing spaces, as we can get a "\n" from echo. */
len = strlen(str);
ival = len - 1;
while ((ival >= 0) && isspace(str[ival])) {
str[ival] = '\0';
ival--;
}
for (curr = str; curr; curr = next) {
regspacing = 1;
regsize = 1;
regshift = 0;
irq = 0;
ipmb = 0; /* Choose the default if not specified */
next = strchr(curr, ':');
if (next) {
*next = '\0';
next++;
}
rv = parse_str(hotmod_ops, &ival, "operation", &curr);
if (rv)
break;
op = ival;
rv = parse_str(hotmod_si, &ival, "interface type", &curr);
if (rv)
break;
si_type = ival;
rv = parse_str(hotmod_as, &addr_space, "address space", &curr);
if (rv)
break;
s = strchr(curr, ',');
if (s) {
*s = '\0';
s++;
}
addr = simple_strtoul(curr, &n, 0);
if ((*n != '\0') || (*curr == '\0')) {
pr_warn(PFX "Invalid hotmod address '%s'\n", curr);
break;
}
while (s) {
curr = s;
s = strchr(curr, ',');
if (s) {
*s = '\0';
s++;
}
o = strchr(curr, '=');
if (o) {
*o = '\0';
o++;
}
rv = check_hotmod_int_op(curr, o, "rsp", &regspacing);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "rsi", &regsize);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "rsh", &regshift);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "irq", &irq);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = -EINVAL;
pr_warn(PFX "Invalid hotmod option '%s'\n", curr);
goto out;
}
if (op == HM_ADD) {
struct si_sm_io io;
memset(&io, 0, sizeof(io));
io.addr_source = SI_HOTMOD;
io.si_type = si_type;
io.addr_data = addr;
io.addr_type = addr_space;
io.addr = NULL;
io.regspacing = regspacing;
if (!io.regspacing)
io.regspacing = DEFAULT_REGSPACING;
io.regsize = regsize;
if (!io.regsize)
io.regsize = DEFAULT_REGSIZE;
io.regshift = regshift;
io.irq = irq;
if (io.irq)
io.irq_setup = ipmi_std_irq_setup;
io.slave_addr = ipmb;
rv = ipmi_si_add_smi(&io);
if (rv)
goto out;
} else {
/* remove */
struct smi_info *e, *tmp_e;
mutex_lock(&smi_infos_lock);
list_for_each_entry_safe(e, tmp_e, &smi_infos, link) {
if (e->io.addr_type != addr_space)
continue;
if (e->io.si_type != si_type)
continue;
if (e->io.addr_data == addr)
cleanup_one_si(e);
}
mutex_unlock(&smi_infos_lock);
}
}
rv = len;
out:
kfree(str);
return rv;
}
static int hardcode_find_bmc(void)
{
int ret = -ENODEV;
@ -3779,6 +3536,24 @@ int ipmi_si_remove_by_dev(struct device *dev)
return rv;
}
void ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
unsigned long addr)
{
/* remove */
struct smi_info *e, *tmp_e;
mutex_lock(&smi_infos_lock);
list_for_each_entry_safe(e, tmp_e, &smi_infos, link) {
if (e->io.addr_type != addr_space)
continue;
if (e->io.si_type != si_type)
continue;
if (e->io.addr_data == addr)
cleanup_one_si(e);
}
mutex_unlock(&smi_infos_lock);
}
static void cleanup_ipmi_si(void)
{
struct smi_info *e, *tmp_e;