Commit c14979b9 authored by Corey Minyard's avatar Corey Minyard Committed by Linus Torvalds

[PATCH] ipmi: add per-channel IPMB addresses

IPMI allows multiple IPMB channels on a single interface, and each channel
might have a different IPMB address.  However, the driver has only one IPMB
address that it uses for everything.  This patch adds new IOCTLS and a new
internal interface for setting per-channel IPMB addresses and LUNs.  New
systems are coming out with support for multiple IPMB channels, and they are
broken without this patch.
Signed-off-by: default avatarCorey Minyard <minyard@acm.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent b224cd3a
......@@ -411,6 +411,7 @@ static int ipmi_ioctl(struct inode *inode,
break;
}
/* The next four are legacy, not per-channel. */
case IPMICTL_SET_MY_ADDRESS_CMD:
{
unsigned int val;
......@@ -420,22 +421,25 @@ static int ipmi_ioctl(struct inode *inode,
break;
}
ipmi_set_my_address(priv->user, val);
rv = 0;
rv = ipmi_set_my_address(priv->user, 0, val);
break;
}
case IPMICTL_GET_MY_ADDRESS_CMD:
{
unsigned int val;
unsigned int val;
unsigned char rval;
rv = ipmi_get_my_address(priv->user, 0, &rval);
if (rv)
break;
val = ipmi_get_my_address(priv->user);
val = rval;
if (copy_to_user(arg, &val, sizeof(val))) {
rv = -EFAULT;
break;
}
rv = 0;
break;
}
......@@ -448,24 +452,94 @@ static int ipmi_ioctl(struct inode *inode,
break;
}
ipmi_set_my_LUN(priv->user, val);
rv = 0;
rv = ipmi_set_my_LUN(priv->user, 0, val);
break;
}
case IPMICTL_GET_MY_LUN_CMD:
{
unsigned int val;
unsigned int val;
unsigned char rval;
rv = ipmi_get_my_LUN(priv->user, 0, &rval);
if (rv)
break;
val = ipmi_get_my_LUN(priv->user);
val = rval;
if (copy_to_user(arg, &val, sizeof(val))) {
rv = -EFAULT;
break;
}
rv = 0;
break;
}
case IPMICTL_SET_MY_CHANNEL_ADDRESS_CMD:
{
struct ipmi_channel_lun_address_set val;
if (copy_from_user(&val, arg, sizeof(val))) {
rv = -EFAULT;
break;
}
return ipmi_set_my_address(priv->user, val.channel, val.value);
break;
}
case IPMICTL_GET_MY_CHANNEL_ADDRESS_CMD:
{
struct ipmi_channel_lun_address_set val;
if (copy_from_user(&val, arg, sizeof(val))) {
rv = -EFAULT;
break;
}
rv = ipmi_get_my_address(priv->user, val.channel, &val.value);
if (rv)
break;
if (copy_to_user(arg, &val, sizeof(val))) {
rv = -EFAULT;
break;
}
break;
}
case IPMICTL_SET_MY_CHANNEL_LUN_CMD:
{
struct ipmi_channel_lun_address_set val;
if (copy_from_user(&val, arg, sizeof(val))) {
rv = -EFAULT;
break;
}
rv = ipmi_set_my_LUN(priv->user, val.channel, val.value);
break;
}
case IPMICTL_GET_MY_CHANNEL_LUN_CMD:
{
struct ipmi_channel_lun_address_set val;
if (copy_from_user(&val, arg, sizeof(val))) {
rv = -EFAULT;
break;
}
rv = ipmi_get_my_LUN(priv->user, val.channel, &val.value);
if (rv)
break;
if (copy_to_user(arg, &val, sizeof(val))) {
rv = -EFAULT;
break;
}
break;
}
case IPMICTL_SET_TIMING_PARMS_CMD:
{
struct ipmi_timing_parms parms;
......
......@@ -124,6 +124,14 @@ struct ipmi_channel
{
unsigned char medium;
unsigned char protocol;
/* My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR,
but may be changed by the user. */
unsigned char address;
/* My LUN. This should generally stay the SMS LUN, but just in
case... */
unsigned char lun;
};
#ifdef CONFIG_PROC_FS
......@@ -135,7 +143,7 @@ struct ipmi_proc_entry
#endif
#define IPMI_IPMB_NUM_SEQ 64
#define IPMI_MAX_CHANNELS 8
#define IPMI_MAX_CHANNELS 16
struct ipmi_smi
{
/* What interface number are we? */
......@@ -199,14 +207,6 @@ struct ipmi_smi
this is registered. */
ipmi_user_t all_cmd_rcvr;
/* My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR,
but may be changed by the user. */
unsigned char my_address;
/* My LUN. This should generally stay the SMS LUN, but just in
case... */
unsigned char my_lun;
/* The event receiver for my BMC, only really used at panic
shutdown as a place to store this. */
unsigned char event_receiver;
......@@ -766,26 +766,44 @@ void ipmi_get_version(ipmi_user_t user,
*minor = user->intf->version_minor;
}
void ipmi_set_my_address(ipmi_user_t user,
unsigned char address)
int ipmi_set_my_address(ipmi_user_t user,
unsigned int channel,
unsigned char address)
{
user->intf->my_address = address;
if (channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
user->intf->channels[channel].address = address;
return 0;
}
unsigned char ipmi_get_my_address(ipmi_user_t user)
int ipmi_get_my_address(ipmi_user_t user,
unsigned int channel,
unsigned char *address)
{
return user->intf->my_address;
if (channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
*address = user->intf->channels[channel].address;
return 0;
}
void ipmi_set_my_LUN(ipmi_user_t user,
unsigned char LUN)
int ipmi_set_my_LUN(ipmi_user_t user,
unsigned int channel,
unsigned char LUN)
{
user->intf->my_lun = LUN & 0x3;
if (channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
user->intf->channels[channel].lun = LUN & 0x3;
return 0;
}
unsigned char ipmi_get_my_LUN(ipmi_user_t user)
int ipmi_get_my_LUN(ipmi_user_t user,
unsigned int channel,
unsigned char *address)
{
return user->intf->my_lun;
if (channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
*address = user->intf->channels[channel].lun;
return 0;
}
int ipmi_set_gets_events(ipmi_user_t user, int val)
......@@ -1213,7 +1231,7 @@ static inline int i_ipmi_request(ipmi_user_t user,
unsigned char ipmb_seq;
long seqid;
if (addr->channel > IPMI_NUM_CHANNELS) {
if (addr->channel >= IPMI_NUM_CHANNELS) {
spin_lock_irqsave(&intf->counter_lock, flags);
intf->sent_invalid_commands++;
spin_unlock_irqrestore(&intf->counter_lock, flags);
......@@ -1346,6 +1364,18 @@ static inline int i_ipmi_request(ipmi_user_t user,
return rv;
}
static int check_addr(ipmi_smi_t intf,
struct ipmi_addr *addr,
unsigned char *saddr,
unsigned char *lun)
{
if (addr->channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
*lun = intf->channels[addr->channel].lun;
*saddr = intf->channels[addr->channel].address;
return 0;
}
int ipmi_request_settime(ipmi_user_t user,
struct ipmi_addr *addr,
long msgid,
......@@ -1355,6 +1385,12 @@ int ipmi_request_settime(ipmi_user_t user,
int retries,
unsigned int retry_time_ms)
{
unsigned char saddr, lun;
int rv;
rv = check_addr(user->intf, addr, &saddr, &lun);
if (rv)
return rv;
return i_ipmi_request(user,
user->intf,
addr,
......@@ -1363,8 +1399,8 @@ int ipmi_request_settime(ipmi_user_t user,
user_msg_data,
NULL, NULL,
priority,
user->intf->my_address,
user->intf->my_lun,
saddr,
lun,
retries,
retry_time_ms);
}
......@@ -1378,6 +1414,12 @@ int ipmi_request_supply_msgs(ipmi_user_t user,
struct ipmi_recv_msg *supplied_recv,
int priority)
{
unsigned char saddr, lun;
int rv;
rv = check_addr(user->intf, addr, &saddr, &lun);
if (rv)
return rv;
return i_ipmi_request(user,
user->intf,
addr,
......@@ -1387,8 +1429,8 @@ int ipmi_request_supply_msgs(ipmi_user_t user,
supplied_smi,
supplied_recv,
priority,
user->intf->my_address,
user->intf->my_lun,
saddr,
lun,
-1, 0);
}
......@@ -1397,8 +1439,15 @@ static int ipmb_file_read_proc(char *page, char **start, off_t off,
{
char *out = (char *) page;
ipmi_smi_t intf = data;
int i;
int rv= 0;
return sprintf(out, "%x\n", intf->my_address);
for (i=0; i<IPMI_MAX_CHANNELS; i++)
rv += sprintf(out+rv, "%x ", intf->channels[i].address);
out[rv-1] = '\n'; /* Replace the final space with a newline */
out[rv] = '\0';
rv++;
return rv;
}
static int version_file_read_proc(char *page, char **start, off_t off,
......@@ -1592,8 +1641,8 @@ send_channel_info_cmd(ipmi_smi_t intf, int chan)
NULL,
NULL,
0,
intf->my_address,
intf->my_lun,
intf->channels[0].address,
intf->channels[0].lun,
-1, 0);
}
......@@ -1696,11 +1745,13 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
new_intf->intf_num = i;
new_intf->version_major = version_major;
new_intf->version_minor = version_minor;
if (slave_addr == 0)
new_intf->my_address = IPMI_BMC_SLAVE_ADDR;
else
new_intf->my_address = slave_addr;
new_intf->my_lun = 2; /* the SMS LUN. */
for (j=0; j<IPMI_MAX_CHANNELS; j++) {
new_intf->channels[j].address
= IPMI_BMC_SLAVE_ADDR;
new_intf->channels[j].lun = 2;
}
if (slave_addr != 0)
new_intf->channels[0].address = slave_addr;
rwlock_init(&(new_intf->users_lock));
INIT_LIST_HEAD(&(new_intf->users));
new_intf->handlers = handlers;
......@@ -1985,7 +2036,7 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf,
msg->data[3] = msg->rsp[6];
msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3);
msg->data[5] = ipmb_checksum(&(msg->data[3]), 2);
msg->data[6] = intf->my_address;
msg->data[6] = intf->channels[msg->rsp[3] & 0xf].address;
/* rqseq/lun */
msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3);
msg->data[8] = msg->rsp[8]; /* cmd */
......@@ -2919,8 +2970,8 @@ static void send_panic_events(char *str)
&smi_msg,
&recv_msg,
0,
intf->my_address,
intf->my_lun,
intf->channels[0].address,
intf->channels[0].lun,
0, 1); /* Don't retry, and don't wait. */
}
......@@ -2965,8 +3016,8 @@ static void send_panic_events(char *str)
&smi_msg,
&recv_msg,
0,
intf->my_address,
intf->my_lun,
intf->channels[0].address,
intf->channels[0].lun,
0, 1); /* Don't retry, and don't wait. */
if (intf->local_event_generator) {
......@@ -2985,8 +3036,8 @@ static void send_panic_events(char *str)
&smi_msg,
&recv_msg,
0,
intf->my_address,
intf->my_lun,
intf->channels[0].address,
intf->channels[0].lun,
0, 1); /* no retry, and no wait. */
}
intf->null_user_handler = NULL;
......@@ -2996,7 +3047,7 @@ static void send_panic_events(char *str)
be zero, and it must not be my address. */
if (((intf->event_receiver & 1) == 0)
&& (intf->event_receiver != 0)
&& (intf->event_receiver != intf->my_address))
&& (intf->event_receiver != intf->channels[0].address))
{
/* The event receiver is valid, send an IPMB
message. */
......@@ -3031,7 +3082,7 @@ static void send_panic_events(char *str)
data[0] = 0;
data[1] = 0;
data[2] = 0xf0; /* OEM event without timestamp. */
data[3] = intf->my_address;
data[3] = intf->channels[0].address;
data[4] = j++; /* sequence # */
/* Always give 11 bytes, so strncpy will fill
it with zeroes for me. */
......@@ -3047,8 +3098,8 @@ static void send_panic_events(char *str)
&smi_msg,
&recv_msg,
0,
intf->my_address,
intf->my_lun,
intf->channels[0].address,
intf->channels[0].lun,
0, 1); /* no retry, and no wait. */
}
}
......
......@@ -298,13 +298,19 @@ void ipmi_get_version(ipmi_user_t user,
this user, so it will affect all users of this interface. This is
so some initialization code can come in and do the OEM-specific
things it takes to determine your address (if not the BMC) and set
it for everyone else. */
void ipmi_set_my_address(ipmi_user_t user,
unsigned char address);
unsigned char ipmi_get_my_address(ipmi_user_t user);
void ipmi_set_my_LUN(ipmi_user_t user,
unsigned char LUN);
unsigned char ipmi_get_my_LUN(ipmi_user_t user);
it for everyone else. Note that each channel can have its own address. */
int ipmi_set_my_address(ipmi_user_t user,
unsigned int channel,
unsigned char address);
int ipmi_get_my_address(ipmi_user_t user,
unsigned int channel,
unsigned char *address);
int ipmi_set_my_LUN(ipmi_user_t user,
unsigned int channel,
unsigned char LUN);
int ipmi_get_my_LUN(ipmi_user_t user,
unsigned int channel,
unsigned char *LUN);
/*
* Like ipmi_request, but lets you specify the number of retries and
......@@ -585,6 +591,16 @@ struct ipmi_cmdspec
* things it takes to determine your address (if not the BMC) and set
* it for everyone else. You should probably leave the LUN alone.
*/
struct ipmi_channel_lun_address_set
{
unsigned short channel;
unsigned char value;
};
#define IPMICTL_SET_MY_CHANNEL_ADDRESS_CMD _IOR(IPMI_IOC_MAGIC, 24, struct ipmi_channel_lun_address_set)
#define IPMICTL_GET_MY_CHANNEL_ADDRESS_CMD _IOR(IPMI_IOC_MAGIC, 25, struct ipmi_channel_lun_address_set)
#define IPMICTL_SET_MY_CHANNEL_LUN_CMD _IOR(IPMI_IOC_MAGIC, 26, struct ipmi_channel_lun_address_set)
#define IPMICTL_GET_MY_CHANNEL_LUN_CMD _IOR(IPMI_IOC_MAGIC, 27, struct ipmi_channel_lun_address_set)
/* Legacy interfaces, these only set IPMB 0. */
#define IPMICTL_SET_MY_ADDRESS_CMD _IOR(IPMI_IOC_MAGIC, 17, unsigned int)
#define IPMICTL_GET_MY_ADDRESS_CMD _IOR(IPMI_IOC_MAGIC, 18, unsigned int)
#define IPMICTL_SET_MY_LUN_CMD _IOR(IPMI_IOC_MAGIC, 19, unsigned int)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment