
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * Copyright (C) IBM Corporation, 2004
 *
 * Author: Max Asböck <amax@us.ibm.com> 
 *
 */

// ibmsphalt.c
// Register for event notification with RSA service processor driver.
// If a poweroff event is received issue the /sbin/halt command to
// shutdown the OS before the service processor powers down the system.

#include <libibmasm.h>
#include <syslog.h>

#define N_IGNORE 13
static unsigned char *ignore[N_IGNORE] = {
	"\x5\x2\x1",
	"\x5\x2\x2",
	"\x5\x2\x3",
	// ignore all events but 5.2.4, i.e. poweroff event
	"\x5\x2\x5",
	"\x5\x2\x6",
	"\x5\x2\x7",
	"\x5\x2\x8",
	"\x5\x2\x9",
	"\x5\x2\xa",
	"\x5\x2\xb",
	"\x5\x2\xc",
	"\x5\x2\xd",
	"\x5\x2\xe",
};

#define BUF_LENGTH 1
static char buffer[BUF_LENGTH * 1024];

// to allow the OS to be shutdown before the service processor powers
// the system off, attempt to set the poweroff delay to at least 
// 45 seconds.
void set_poweroff_delay(int handle)
{
	int ret;
	int delay;

	// set the bytes in the poweroff delay command
	buffer[0] = 0x02;	// type = read
	buffer[1] = 0x03;	// command length
	buffer[2] = 0x00;	// data length LSB
	buffer[3] = 0x00;	// data length MSB
	buffer[4] = 0x00;	// status
	buffer[5] = 0x00;	// reserved
	buffer[6] = 0x06;	// 1st command byte - command  6.2.1
	buffer[7] = 0x02;	// 2nd cmd byte
	buffer[8] = 0x01;	// 3rd cmd byte
	buffer[9] = 0x00;

	ret = SystemDataIO(handle, buffer, 0);

	if ( (ret != RC_SUCCESS)
		|| (buffer[0] != 4)	// type != command response
		|| (buffer[1] != 3)	// command length != 3
		|| (buffer[4] != 0)	// status != 0
		|| (buffer[6] != 6)	// command 6.2.1
		|| (buffer[7] != 2)
		|| (buffer[8] != 1) )
		return;

	delay = buffer[9] + buffer[10] << 8;
	if (delay >= 45)
		return;

	// set the poweroff delay to 45 seconds
	buffer[0]  = 0x00;	// type = write
	buffer[1]  = 0x03;	// command length
	buffer[2]  = 0x02;	// data length LSB
	buffer[3]  = 0x00;	// data length MSB
	buffer[4]  = 0x00;	// status
	buffer[5]  = 0x00;	// reserved
	buffer[6]  = 0x06;	// 1st command byte - command  6.2.1
	buffer[7]  = 0x02;	// 2nd cmd byte
	buffer[8]  = 0x01;	// 3rd cmd byte
	buffer[9]  = 0x2D;	// 1st data byte 45
	buffer[10] = 0x00;	// 2nd data byte

	SystemDataIO(handle, buffer, 0);
}

// to be able to receive the poweroff event from the service processor
// we have to enable it by writing 1 in the event's data bit.
static int enable_poweroff_event(int handle)
{
	int ret;

	// enable the 5.2.4 event
	buffer[0]  = 0x00;	// type = write
	buffer[1]  = 0x03;	// command length
	buffer[2]  = 0x01;	// data length LSB
	buffer[3]  = 0x00;	// data length MSB
	buffer[4]  = 0x00;	// status
	buffer[5]  = 0x00;	// reserved
	buffer[6]  = 0x05;	// 1st command byte - event 5.2.1
	buffer[7]  = 0x02;	// 2nd command byte
	buffer[8]  = 0x04;	// 3rd command byte
	buffer[9]  = 0x01;	// 1st data byte
	buffer[10] = 0x00;	// 2nd data byte

	ret = SystemDataIO(handle, buffer, 0);
	if ( (ret != RC_SUCCESS) || (buffer[4] != 0) )
		return 0;

	return 1;
}

int main()
{
	int ret;
	int handle;

	ret = OpenSPDriver(&handle, BUF_LENGTH, 0);
	if (ret != RC_SUCCESS)
		return 1;

	set_poweroff_delay(handle);

	if (!enable_poweroff_event(handle))
		return 2;

	// wait for a poweroff event
	while (1) {
		memset(buffer, 0, 10);
		ret = RegisterForEvents(handle, buffer, N_IGNORE, ignore, 0);
		if (ret != RC_SUCCESS)
			break;

		// make sure we received a valid poweroff event
		if (   (buffer[0] == 5)	// type = event
		    && (buffer[1] == 3)	// length = 3
		    && (buffer[4] == 0)	// status = 0
		    && (buffer[6] == 5)	// command = 5.2.4, i.e poweroff
		    && (buffer[7] == 2)
		    && (buffer[8] == 4) ) {
			syslog(LOG_CRIT, "Received Power Off notification "
				"from RSA adapter - Halting system\n");
			system("/sbin/halt");
		}
	}
	CloseSPDriver(handle, 0);
	return 0;
}
