/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef	_LIBIPMI_H
#define	_LIBIPMI_H

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/bmc_intf.h>
#include <sys/byteorder.h>
#include <sys/sysmacros.h>

/*
 * Private interfaces for communicating with attached services over IPMI.  This
 * library is designed for system software communicating with Sun-supported
 * service processors over /dev/bmc.  It is not a generic IPMI library.
 *
 * Documentation references refer to "Intelligent Platform Management Interface
 * Specification Second Generation v2.0", document revision 1.0 with Februrary
 * 15, 2006 Markup from "IPMI v2.0 Addenda, Errata, and Clarifications Revision
 * 3".
 */

#ifdef	__cplusplus
extern "C" {
#endif

typedef struct ipmi_handle ipmi_handle_t;

#pragma pack(1)

/*
 * Basic netfn definitions.  See section 5.1.
 */
#define	IPMI_NETFN_APP			BMC_NETFN_APP
#define	IPMI_NETFN_STORAGE		BMC_NETFN_STORAGE
#define	IPMI_NETFN_SE			BMC_NETFN_SE
#define	IPMI_NETFN_OEM			0x2e

/*
 * Error definitions
 */
#define	EIPMI_BASE	2000

enum {
	EIPMI_NOMEM = EIPMI_BASE,	/* memory allocation failure */
	EIPMI_BMC_OPEN_FAILED,		/* failed to open /dev/bmc */
	EIPMI_BMC_PUTMSG,		/* putmsg() failed */
	EIPMI_BMC_GETMSG,		/* getmsg() failed */
	EIPMI_BMC_RESPONSE,		/* response from /dev/bmc failed */
	EIPMI_INVALID_COMMAND,		/* invalid command */
	EIPMI_COMMAND_TIMEOUT,		/* command timeout */
	EIPMI_DATA_LENGTH_EXCEEDED,	/* maximum data length exceeded */
	EIPMI_SEND_FAILED,		/* failed to send BMC request */
	EIPMI_UNSPECIFIED,		/* unspecified error */
	EIPMI_UNKNOWN,			/* unknown error */
	EIPMI_BAD_RESPONSE,		/* received unexpected response */
	EIPMI_BAD_RESPONSE_LENGTH,	/* unexpected response length */
	EIPMI_INVALID_RESERVATION,	/* invalid reservation */
	EIPMI_NOT_PRESENT,		/* requested entity not present */
	EIPMI_INVALID_REQUEST,		/* malformed request */
	EIPMI_BUSY,			/* SP is busy */
	EIPMI_NOSPACE,			/* SP is out of space */
	EIPMI_UNAVAILABLE,		/* SP is present but unavailable */
	EIPMI_ACCESS			/* insufficient privileges */
};

/*
 * Basic library functions.
 *
 * The ipmi_handle is the primary interface to the library.  The library itself
 * is not MT-safe, but it is safe within a single handle.  Multithreaded clients
 * should either open multiple handles, or otherwise synchronize access to the
 * same handle.
 *
 * There is a single command response buffer that is stored with the handle, to
 * simplify memory management in the caller.  The memory referenced by a command
 * response is only valid until the next command is issued.  The caller is
 * responsible for making a copy of the response if it is needed.
 */
extern ipmi_handle_t *ipmi_open(int *, char **);
extern void ipmi_close(ipmi_handle_t *);

extern int ipmi_errno(ipmi_handle_t *);
extern const char *ipmi_errmsg(ipmi_handle_t *);

/*
 * Raw requests.  See section 5.
 */
typedef struct ipmi_cmd {
	uint8_t		ic_netfn:6;
	uint8_t		ic_lun:2;
	uint8_t		ic_cmd;
	uint16_t	ic_dlen;
	void		*ic_data;
} ipmi_cmd_t;

extern ipmi_cmd_t *ipmi_send(ipmi_handle_t *, ipmi_cmd_t *);

/*
 * Retrieve basic information about the IPMI device.  See section 20.1 "Get
 * Device ID Command".
 */
#define	IPMI_CMD_GET_DEVICEID		0x01

typedef struct ipmi_deviceid {
	uint8_t		id_devid;
	DECL_BITFIELD3(
	    id_dev_rev		:4,
	    __reserved		:3,
	    id_dev_sdrs		:1);
	DECL_BITFIELD2(
	    id_firm_major	:7,
	    id_dev_available	:1);
	uint8_t		id_firm_minor;
	uint8_t		id_ipmi_rev;
	uint8_t		id_dev_support;
	uint8_t		id_manufacturer[3];
	uint16_t	id_product;
} ipmi_deviceid_t;

#define	IPMI_OEM_SUN	0x2a

ipmi_deviceid_t *ipmi_get_deviceid(ipmi_handle_t *);

#define	ipmi_devid_manufacturer(dp)		\
	((dp)->id_manufacturer[0] |		\
	((dp)->id_manufacturer[1] << 8) |	\
	((dp)->id_manufacturer[2] << 16))

/*
 * SDR (Sensor Device Record) requests.  A cache of the current SDR repository
 * is kept as part of the IPMI handle and updated when necessary.  Routines to
 * access the raw SDR repository are also provided.
 */

/*
 * Reserve repository command.  See section 33.11.
 */
#define	IPMI_CMD_RESERVE_SDR_REPOSITORY	0x22

/*
 * Get SDR command.  See section 33.12.  This command accesses the raw SDR
 * repository.  Clients can also use the lookup functions to retrieve a
 * particular SDR record by name.
 *
 * The list of possible types is indicated in the sub-chapters of section 43.
 */
typedef struct ipmi_sdr {
	uint16_t	is_id;
	uint8_t		is_version;
	uint8_t		is_type;
	uint8_t		is_length;
	uint8_t		is_record[1];
} ipmi_sdr_t;
#define	IPMI_CMD_GET_SDR		0x23

#define	IPMI_SDR_FIRST			0x0000
#define	IPMI_SDR_LAST			0xFFFF

extern ipmi_sdr_t *ipmi_sdr_get(ipmi_handle_t *, uint16_t, uint16_t *);

/*
 * Generic Device Locator Record.  See section 43.7.
 */

#define	IPMI_SDR_TYPE_GENERIC_LOCATOR		0x10

typedef struct ipmi_sdr_generic_locator {
	/* RECORD KEY BYTES */
	DECL_BITFIELD2(
	    __reserved1		:1,
	    is_gl_accessaddr	:7);
	DECL_BITFIELD2(
	    is_gl_channel_msb	:1,
	    is_gl_slaveaddr	:7);
	DECL_BITFIELD3(
	    is_gl_bus		:3,
	    is_gl_lun		:2,
	    is_gl_channel	:3);
	/* RECORD BODY BYTES */
	DECL_BITFIELD2(
	    is_gl_span		:3,
	    __reserved2		:5);
	uint8_t		__reserved3;
	uint8_t		is_gl_type;
	uint8_t		is_gl_modifier;
	uint8_t		is_gl_entity;
	uint8_t		is_gl_instance;
	uint8_t		is_gl_oem;
	DECL_BITFIELD2(
	    is_gl_idlen		:6,
	    is_gl_idtype	:2);
	char		is_gl_idstring[1];
} ipmi_sdr_generic_locator_t;

/*
 * FRU Device Locator Record.  See section 43.8.
 */

#define	IPMI_SDR_TYPE_FRU_LOCATOR		0x11

typedef struct ipmi_sdr_fru_locator {
	/* RECORD KEY BYTES */
	DECL_BITFIELD2(
	    __reserved1		:1,
	    is_fl_accessaddr	:7);
	union {
		struct {
			uint8_t	_is_fl_devid;
		} _logical;
		struct {
			DECL_BITFIELD2(
			    __reserved		:1,
			    _is_fl_slaveaddr	:7);
		} _nonintelligent;
	} _devid_or_slaveaddr;
	DECL_BITFIELD4(
	    is_fl_bus		:3,
	    is_fl_lun		:2,
	    __reserved2		:2,
	    is_fl_logical	:1);
	DECL_BITFIELD2(
	    __reserved3		:4,
	    is_fl_channel	:4);
	/* RECORD BODY BYTES */
	uint8_t		__reserved4;
	uint8_t		is_fl_type;
	uint8_t		is_fl_modifier;
	uint8_t		is_fl_entity;
	uint8_t		is_fl_instance;
	uint8_t		is_fl_oem;
	DECL_BITFIELD2(
	    is_fl_idlen		:6,
	    is_fl_idtype	:2);
	char		is_fl_idstring[1];
} ipmi_sdr_fru_locator_t;

#define	is_fl_devid	_devid_or_slaveaddr._logical._is_fl_devid
#define	is_fl_slaveaddr	_devid_or_slaveaddr._nonintelligent._is_fl_slaveaddr

/*
 * The remaining SDR types do not have an associated structure, yet.
 */
#define	IPMI_SDR_TYPE_FULL_SENSOR		0x01
#define	IPMI_SDR_TYPE_COMPACT_SENSOR		0x02
#define	IPMI_SDR_TYPE_EVENT_ONLY		0x03
#define	IPMI_SDR_TYPE_ENTITY_ASSOCIATION	0x08
#define	IPMI_SDR_TYPE_DEVICE_RELATIVE		0x09
#define	IPMI_SDR_TYPE_MANAGEMENT_DEVICE		0x12
#define	IPMI_SDR_TYPE_MANAGEMENT_CONFIRMATION	0x13
#define	IPMI_SDR_TYPE_BMC_MESSAGE_CHANNEL	0x14
#define	IPMI_SDR_TYPE_OEM			0xC0

/*
 * Lookup the given sensor type by name.  These functions automatically read in
 * and cache the complete SDR repository.
 */
extern ipmi_sdr_fru_locator_t *ipmi_sdr_lookup_fru(ipmi_handle_t *,
    const char *);
extern ipmi_sdr_generic_locator_t *ipmi_sdr_lookup_generic(ipmi_handle_t *,
    const char *);

/*
 * Get Sensor Reading.  See section 35.14.
 */

#define	IPMI_CMD_GET_SENSOR_READING	0x2d

typedef struct ipmi_sensor_reading {
	uint8_t		isr_reading;
	DECL_BITFIELD4(
	    __reserved1			:5,
	    isr_state_unavailable	:1,
	    isr_scanning_disabled	:1,
	    isr_event_disabled		:1);
	uint16_t	isr_state;
} ipmi_sensor_reading_t;

extern ipmi_sensor_reading_t *ipmi_get_sensor_reading(ipmi_handle_t *, uint8_t);

/*
 * Set Sensor Reading.  See section 35.14.
 */
#define	IPMI_CMD_SET_SENSOR_READING	0x30

#define	IPMI_SENSOR_OP_CLEAR	0x3	/* clear '0' bits */
#define	IPMI_SENSOR_OP_SET	0x2	/* set '1' bits */
#define	IPMI_SENSOR_OP_EXACT	0x1	/* set bits exactly */

typedef struct ipmi_set_sensor_reading {
	uint8_t		iss_id;
	DECL_BITFIELD5(
	    iss_set_reading		:1,
	    __reserved			:1,
	    iss_deassrt_op		:2,
	    iss_assert_op		:2,
	    iss_data_bytes		:2);
	uint8_t		iss_sensor_reading;
	uint16_t	iss_assert_state;	/* optional */
	uint16_t	iss_deassert_state;	/* optional */
	uint8_t		iss_event_data1;	/* optional */
	uint8_t		iss_event_data2;	/* optional */
	uint8_t		iss_event_data3;	/* optional */
} ipmi_set_sensor_reading_t;

extern int ipmi_set_sensor_reading(ipmi_handle_t *,
    ipmi_set_sensor_reading_t *);

/*
 * These IPMI message id/opcodes are documented in Appendix G in the IPMI spec.
 *
 * Payloads for these two commands are described in Sections 34.1 and 34.2 of
 * the spec, respectively.
 */
#define	IPMI_CMD_GET_FRU_INV_AREA	0x10
#define	IPMI_CMD_READ_FRU_DATA		0x11

/*
 * Structs to hold the FRU Common Header and the FRU Product Info Area, as
 * described in the IPMI Platform Management FRU Information Storage
 * Definition (v1.1).
 */
typedef struct ipmi_fru_hdr
{
	uint8_t		ifh_format;
	uint8_t		ifh_int_use_off;
	uint8_t		ifh_chassis_info_off;
	uint8_t		ifh_board_info_off;
	uint8_t		ifh_product_info_off;
	uint8_t		ifh_multi_rec_off;
	uint8_t		ifh_pad;
	uint8_t		ifh_chksum;
} ipmi_fru_hdr_t;

/*
 * Because only 6 bits are used to specify the length of each field in the FRU
 * product and board info areas, the biggest string we would ever need to hold
 * would be 63 chars plus a NULL.
 */
#define	FRU_INFO_MAXLEN	64

typedef struct ipmi_fru_brd_info
{
	char	ifbi_manuf_date[3];
	char	ifbi_manuf_name[FRU_INFO_MAXLEN];
	char	ifbi_board_name[FRU_INFO_MAXLEN];
	char	ifbi_product_serial[FRU_INFO_MAXLEN];
	char	ifbi_part_number[FRU_INFO_MAXLEN];
} ipmi_fru_brd_info_t;

typedef struct ipmi_fru_prod_info
{
	char	ifpi_manuf_name[FRU_INFO_MAXLEN];
	char	ifpi_product_name[FRU_INFO_MAXLEN];
	char	ifpi_part_number[FRU_INFO_MAXLEN];
	char	ifpi_product_version[FRU_INFO_MAXLEN];
	char	ifpi_product_serial[FRU_INFO_MAXLEN];
	char	ifpi_asset_tag[FRU_INFO_MAXLEN];
} ipmi_fru_prod_info_t;

int ipmi_fru_read(ipmi_handle_t *, ipmi_sdr_fru_locator_t *, char **);
int ipmi_fru_parse_board(ipmi_handle_t *, char *, ipmi_fru_brd_info_t *);
int ipmi_fru_parse_product(ipmi_handle_t *, char *, ipmi_fru_prod_info_t *);

/*
 * User management.  The raw functions are private to libipmi, and only the
 * higher level abstraction (ipmi_user_t) is exported to consumers of the
 * library.
 */

#define	IPMI_USER_PRIV_CALLBACK		0x1
#define	IPMI_USER_PRIV_USER		0x2
#define	IPMI_USER_PRIV_OPERATOR		0x3
#define	IPMI_USER_PRIV_ADMIN		0x4
#define	IPMI_USER_PRIV_OEM		0x5
#define	IPMI_USER_PRIV_NONE		0xf

typedef struct ipmi_user {
	uint8_t		iu_uid;
	char		*iu_name;
	boolean_t	iu_enabled;
	boolean_t	iu_ipmi_msg_enable;
	boolean_t	iu_link_auth_enable;
	uint8_t		iu_priv;
	struct ipmi_user *iu_next;
} ipmi_user_t;

extern int ipmi_user_iter(ipmi_handle_t *,
    int (*)(ipmi_user_t *, void *), void *);
extern ipmi_user_t *ipmi_user_lookup_name(ipmi_handle_t *, const char *);
extern ipmi_user_t *ipmi_user_lookup_id(ipmi_handle_t *, uint8_t);
extern int ipmi_user_set_password(ipmi_handle_t *, uint8_t, const char *);

/*
 * The remaining functions are private to the implementation of the Sun ILOM
 * service processor.  These function first check the manufacturer from the IPMI
 * device ID, and will return EIPMI_NOT_SUPPORTED if attempted for non-Sun
 * devices.
 */

/*
 * Sun OEM LED requests.
 */

#define	IPMI_SUNOEM_LED_MODE_OFF	0
#define	IPMI_SUNOEM_LED_MODE_ON		1
#define	IPMI_SUNOEM_LED_MODE_STANDBY	2
#define	IPMI_SUNOEM_LED_MODE_SLOW	3
#define	IPMI_SUNOEM_LED_MODE_FAST	4

/*
 * These functions take a SDR record and construct the appropriate form of the
 * above commands.
 */
extern int ipmi_sunoem_led_set(ipmi_handle_t *,
    ipmi_sdr_generic_locator_t *, uint8_t);
extern int ipmi_sunoem_led_get(ipmi_handle_t *,
    ipmi_sdr_generic_locator_t *, uint8_t *);

/*
 * Sun OEM uptime.  Note that the underlying command returns the uptime in big
 * endian form.  This wrapper automatically converts to the appropriate native
 * form.
 */

#define	IPMI_CMD_SUNOEM_UPTIME		0x08

extern int ipmi_sunoem_uptime(ipmi_handle_t *, uint32_t *, uint32_t *);

/*
 * Sun OEM FRU update.  The FRU information is managed through a generic
 * identifier, and then a type-specific data portion.  The wrapper function will
 * automatically fill in the data length field according to which type is
 * specified.
 */

#define	IPMI_CMD_SUNOEM_FRU_UPDATE	0x16

#define	IPMI_SUNOEM_FRU_DIMM	0x00
#define	IPMI_SUNOEM_FRU_CPU	0x01
#define	IPMI_SUNOEM_FRU_BIOS	0x02
#define	IPMI_SUNOEM_FRU_DISK	0x03

typedef struct ipmi_sunoem_fru {
	uint8_t				isf_type;
	uint8_t				isf_id;
	uint8_t				isf_datalen;
	union {
		struct {
			uint8_t		isf_data[128];
		} dimm;
		struct {
			uint32_t	isf_thermtrip;
			uint32_t	isf_eax;
			char		isf_product[48];
		} cpu;
		struct {
			char		isf_part[16];
			char		isf_version[16];
		} bios;
		struct {
			char		isf_manufacturer[16];
			char		isf_model[28];
			char		isf_serial[20];
			char		isf_version[8];
			char		isf_capacity[16];
		} disk;
	} isf_data;
} ipmi_sunoem_fru_t;

int ipmi_sunoem_update_fru(ipmi_handle_t *, ipmi_sunoem_fru_t *);

#pragma pack()

#ifdef	__cplusplus
}
#endif

#endif	/* _LIBIPMI_H */