File: ipmi_con_smi.cpp

package info (click to toggle)
openhpi 3.8.0-2.3
  • links: PTS
  • area: main
  • in suites: sid, trixie
  • size: 31,888 kB
  • sloc: ansic: 225,326; cpp: 63,687; java: 16,472; cs: 15,161; python: 11,884; sh: 11,508; makefile: 4,945; perl: 1,529; xml: 36; asm: 13
file content (425 lines) | stat: -rw-r--r-- 11,211 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
/*
 *
 * 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;
    }
}