/*
*
* Copyright (c) 2004 by FORCE Computers
* Copyright (c) 2007 by ESO Technologies.
*
* Note that this file is based on parts of OpenIPMI
* written by Corey Minyard <minyard@mvista.com>
* of MontaVista Software. Corey's code was helpful
* and many thanks go to him. He gave the permission
* to use this code in OpenHPI under BSD license.
*
* 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. This
* file and program are licensed under a BSD style license. See
* the Copying file included with the OpenHPI distribution for
* full licensing terms.
*
* Authors:
* Thomas Kanngieser <thomas.kanngieser@fci.com>
* Pierre Sangouard <psangouard@eso-tech.com>
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#if defined(__sun) && defined(__SVR4)
#include <unistd.h>
#include <stropts.h>
#include <sys/ioccom.h>
#endif
#include "ipmi_con_smi.h"
// This is an overlay for all the address types, so it's easy to
// determine the actual address type. This is kind of like addresses
// work for sockets.
#define IPMI_MAX_ADDR_SIZE 32
struct ipmi_addr
{
// Try to take these from the "Channel Medium Type" table
// in section 6.5 of the IPMI 1.5 manual.
int addr_type;
short channel;
char data[IPMI_MAX_ADDR_SIZE];
};
// When the address is not used, the type will be set to this value.
// The channel is the BMC's channel number for the channel (usually
// 0), or IPMC_BMC_CHANNEL if communicating directly with the BMC.
#define IPMI_SYSTEM_INTERFACE_ADDR_TYPE 0x0c
struct ipmi_system_interface_addr
{
int addr_type;
short channel;
unsigned char lun;
};
// An IPMB Address.
#define IPMI_IPMB_ADDR_TYPE 0x01
// Used for broadcast get device id as described in section 17.9 of the
// IPMI 1.5 manual.
#define IPMI_IPMB_BROADCAST_ADDR_TYPE 0x41
struct ipmi_ipmb_addr
{
int addr_type;
short channel;
unsigned char slave_addr;
unsigned char lun;
};
// Channel for talking directly with the BMC. When using this
// channel, This is for the system interface address type only. FIXME
// - is this right, or should we use -1?
#define IPMI_BMC_CHANNEL 0xf
#define IPMI_NUM_CHANNELS 0x10
// A raw IPMI message without any addressing. This covers both
// commands and responses. The completion code is always the first
// byte of data in the response (as the spec shows the messages laid
// out).
struct ipmi_msg
{
unsigned char netfn;
unsigned char cmd;
unsigned short data_len;
unsigned char *data;
};
// Messages sent to the interface are this format.
struct ipmi_req
{
unsigned char *addr; // Address to send the message to.
unsigned int addr_len;
long msgid; // The sequence number for the message. This
// exact value will be reported back in the
// response to this request if it is a command.
// If it is a response, this will be used as
// the sequence value for the response.
struct ipmi_msg msg;
};
// Receive types for messages coming from the receive interface. This
// is used for the receive in-kernel interface and in the receive
// IOCTL.
#define IPMI_RESPONSE_RECV_TYPE 1 // A response to a command
#define IPMI_ASYNC_EVENT_RECV_TYPE 2 // Something from the event queue
#define IPMI_CMD_RECV_TYPE 3 // A command from somewhere else
// Note that async events and received commands do not have a completion
// code as the first byte of the incoming data, unlike a response.
// Messages received from the interface are this format.
struct ipmi_recv
{
int recv_type; // Is this a command, response or an
// asyncronous event.
unsigned char *addr; // Address the message was from is put
// here. The caller must supply the
// memory.
unsigned int addr_len; // The size of the address buffer.
// The caller supplies the full buffer
// length, this value is updated to
// the actual message length when the
// message is received.
long msgid; // The sequence number specified in the request
// if this is a response. If this is a command,
// this will be the sequence number from the
// command.
struct ipmi_msg msg; // The data field must point to a buffer.
// The data_size field must be set to the
// size of the message buffer. The
// caller supplies the full buffer
// length, this value is updated to the
// actual message length when the message
// is received.
};
// Get/set the default timing values for an interface. You shouldn't
// generally mess with these.
struct ipmi_timing_parms
{
int retries;
unsigned int retry_time_ms;
};
// The magic IOCTL value for this interface.
#define IPMI_IOC_MAGIC 'i'
// Send a message to the interfaces. error values are:
// - EFAULT - an address supplied was invalid.
// - EINVAL - The address supplied was not valid, or the command
// was not allowed.
// - EMSGSIZE - The message to was too large.
// - ENOMEM - Buffers could not be allocated for the command.
#define IPMICTL_SEND_COMMAND _IOR(IPMI_IOC_MAGIC, 13, \
struct ipmi_req)
// Like RECEIVE_MSG, but if the message won't fit in the buffer, it
// will truncate the contents instead of leaving the data in the
// buffer.
#define IPMICTL_RECEIVE_MSG_TRUNC _IOWR(IPMI_IOC_MAGIC, 11, \
struct ipmi_recv)
// Set whether this interface receives events. Note that the first
// user registered for events will get all pending events for the
// interface. error values:
// - EFAULT - an address supplied was invalid.
#define IPMICTL_SET_GETS_EVENTS_CMD _IOR(IPMI_IOC_MAGIC, 16, int)
#define IPMICTL_SET_TIMING_PARMS_CMD _IOR(IPMI_IOC_MAGIC, 22, \
struct ipmi_timing_parms)
cIpmiConSmi::cIpmiConSmi( unsigned int timeout, int log_level, int if_num )
: cIpmiCon( timeout, log_level ),
m_if_num( if_num )
{
}
cIpmiConSmi::~cIpmiConSmi()
{
if ( IsOpen() )
Close();
}
int
cIpmiConSmi::OpenSmiFd( int if_num )
{
int fd;
char devname[30];
snprintf( devname, sizeof(devname), "/dev/ipmidev/%d", if_num );
fd = open( devname, O_RDWR );
if ( fd >= 0 )
return fd;
snprintf( devname, sizeof(devname), "/dev/ipmi/%d", if_num );
fd = open( devname, O_RDWR );
if ( fd >= 0 )
return fd;
snprintf( devname, sizeof(devname), "/dev/ipmi%d", if_num );
fd = open( devname, O_RDWR );
return fd;
}
int
cIpmiConSmi::IfGetMaxSeq()
{
return dMaxSeq;
}
int
cIpmiConSmi::IfOpen()
{
int fd = OpenSmiFd( m_if_num );
if ( fd < 0 )
return fd;
// struct ipmi_timing_parms parms;
int rv = 0;
// parms.retries = 0;
// parms.retry_time_ms = 1000;
/* Some sensors take longer than 1 sec, use driver default. ARCress */
// rv = ioctl( fd, IPMICTL_SET_TIMING_PARMS_CMD, &parms );
if ( rv == -1 )
stdlog << "Warning: Could not set timing parms !\n";
// we want async events
int val = 1;
rv = ioctl( fd, IPMICTL_SET_GETS_EVENTS_CMD, &val );
if ( rv == -1 )
stdlog << "Warning: Could not set gets events !\n";
return fd;
}
void
cIpmiConSmi::IfClose()
{
}
SaErrorT
cIpmiConSmi::IfSendCmd( cIpmiRequest *r )
{
cIpmiAddr send_addr = r->m_send_addr;
ipmi_addr addr;
unsigned int addr_len = 0;
addr.addr_type = (int)send_addr.m_type;
// convert addr
switch( send_addr.m_type )
{
case eIpmiAddrTypeSystemInterface:
{
ipmi_system_interface_addr *si = (ipmi_system_interface_addr *)&addr;
si->channel = send_addr.m_channel;
si->lun = send_addr.m_lun;
addr_len = sizeof( ipmi_system_interface_addr );
}
break;
case eIpmiAddrTypeIpmb:
case eIpmiAddrTypeIpmbBroadcast:
{
ipmi_ipmb_addr *ipmb = (ipmi_ipmb_addr *)&addr;
ipmb->channel = send_addr.m_channel;
ipmb->slave_addr = send_addr.m_slave_addr;
ipmb->lun = send_addr.m_lun;
addr_len = sizeof( ipmi_ipmb_addr );
}
break;
default:
return SA_ERR_HPI_INVALID_PARAMS;
}
struct ipmi_req req;
req.addr = (unsigned char *)&addr;
req.addr_len = addr_len;
req.msg.netfn = r->m_msg.m_netfn;
req.msg.cmd = r->m_msg.m_cmd;
req.msg.data_len = r->m_msg.m_data_len;
req.msg.data = r->m_msg.m_data;
req.msgid = r->m_seq;
int rv = ioctl( m_fd, IPMICTL_SEND_COMMAND, &req );
if ( rv )
return SA_ERR_HPI_INVALID_REQUEST;
return SA_OK;
}
void
cIpmiConSmi::IfReadResponse()
{
unsigned char data[dIpmiMaxMsgLength];
ipmi_addr addr;
ipmi_recv recv;
recv.msg.data = data;
recv.msg.data_len = dIpmiMaxMsgLength;
recv.addr = (unsigned char *)&addr;
recv.addr_len = sizeof( ipmi_addr );
int rv = ioctl( m_fd, IPMICTL_RECEIVE_MSG_TRUNC, &recv );
if ( rv == -1 )
{
if ( errno == EMSGSIZE )
// The message was truncated, handle it as such.
data[0] = eIpmiCcRequestedDataLengthExceeded;
else
return;
}
// convert addr
cIpmiAddr rsp_addr;
rsp_addr.m_type = (tIpmiAddrType)addr.addr_type;
switch( rsp_addr.m_type )
{
case eIpmiAddrTypeSystemInterface:
{
ipmi_system_interface_addr *si = (ipmi_system_interface_addr *)&addr;
rsp_addr.m_channel = si->channel;
rsp_addr.m_lun = si->lun;
}
break;
case eIpmiAddrTypeIpmb:
case eIpmiAddrTypeIpmbBroadcast:
{
ipmi_ipmb_addr *ipmb = (ipmi_ipmb_addr *)&addr;
rsp_addr.m_channel = ipmb->channel;
rsp_addr.m_slave_addr = ipmb->slave_addr;
rsp_addr.m_lun = ipmb->lun;
}
break;
default:
return;
}
// convert msg
cIpmiMsg rsp;
rsp.m_netfn = (tIpmiNetfn)recv.msg.netfn;
rsp.m_cmd = (tIpmiCmd)recv.msg.cmd;
rsp.m_data_len = recv.msg.data_len;
if ( rsp.m_data_len )
memcpy( rsp.m_data, recv.msg.data, rsp.m_data_len );
int seq = (int)recv.msgid;
switch( recv.recv_type )
{
case IPMI_RESPONSE_RECV_TYPE:
HandleResponse( seq, rsp_addr, rsp );
break;
case IPMI_ASYNC_EVENT_RECV_TYPE:
HandleEvent( rsp_addr, rsp );
break;
case IPMI_CMD_RECV_TYPE:
// incoming command
stdlog << "SMI: incoming ipmi command "
<< IpmiCmdToString( rsp.m_netfn, rsp.m_cmd ) << ".\n";
break;
default:
break;
}
}