/* GSI - general sound interface
 *
 * gsi.h
 *
 *
 * 1996 - 2000 by W.H.Scholten
 *
 * This file is released to the public domain.
 */


#ifndef _GSI__gsi__gsi_h
#define _GSI__gsi__gsi_h

#ifdef _cplusplus
extern "C" {
#endif /* __cplusplus */

#include <gsi/gsi_config.h>
#include <gsi/gsi_commands.h>
#include <gsi/gsi_subcommands.h>
#include <gsi/gsi_events.h>
#include <gsi/gsi_eventmasks.h>

#include <gsi/mixer_data.h>
#include <gsi/pcm_data.h>
#include <gsi/synth_data.h>

#include <gsi/version.h>
#if  !USE_NAS
#  define GSI_INTERFACE_VERSION "GSI interface "GSI_VERSION_STRING
#endif
#if  USE_NAS
#  define GSI_INTERFACE_VERSION GSI_NAS_INTERFACE_STRING
#endif




#define GSI_OK                      0
#define GSI_ERROR                  -1 /* unspecified failure */
#define GSI_ERROR_OUT_OF_MEMORY    -2
#define GSI_ERROR_OBSOLETE_LIBRARY -6
#define GSI_ERROR_OBSOLETE_SERVER  -7


/* at the moment 6 device types are used: */
#define GSIDEV_NUM_DEVICE_TYPES   6
extern char *gsi_device_type_name [ GSIDEV_NUM_DEVICE_TYPES ];


typedef struct gsi_cd_track_info {
	unsigned char min;
	unsigned char sec;
	unsigned char frame;
	unsigned char type; /* _CD_DATA, _CD_AUDIO, _CD_END = 0,1,2 */
} gsi_cd_track_info_t;

/*struct gsi_shm_block {
 *	int  start_ptr;
 *	int  write_ptr;
 *	int  data_size;
 *	char unused[4];
 *};
 */
typedef struct gsi_shm {
	char id_string[16];
	int  shmid;

	int  header_size; /* size of this struct */
	int  data_size;   /* number of bytes for the sound data */

	/* reading/writing needs locks etc. not quite done. */
	int  read_start_ptr;  /* 0 for standard slots, variable for streams */
	int  read_end_ptr;    /* where data can be written next */

	int  write_start_ptr; /* 0 for standard slots, variable for streams */
	int  write_end_ptr;   /* where data can be written next */

	int  read_size;
	int  write_size;

	int  read_lock;
	int  write_lock;

	unsigned char read_update_flag;
	unsigned char write_update_flag;

	char unused[2];
} gsi_shm_t;


/* ------------------------------------------------------------------------- *
 *      New event system (no callbacks in interface)
 * ------------------------------------------------------------------------- */
#define GSI_EVENTTYPE_GENERAL 0
#define GSI_EVENTTYPE_CD      1
#define GSI_EVENTTYPE_CHANNEL 2
#define GSI_EVENTTYPE_SLOT    3
#define GSI_EVENTTYPE_SONG    4
#define GSI_EVENTTYPE_MIXER   5 /* volume changes */
#define GSI_EVENTTYPE_DEVICE  6 /* adding/removing devices */

/*
 * prototype for the new event system
 */
typedef union gsi_event {
	int type; /* GSI_EVT_GENERAL, GSI_EVT_MIXER, GSI_EVT_CHANNEL, GSI_EVT_CD, GS_EVT_SONG, GSI_EVT_SLOT */
	struct ev_general {
		int  arg1; /* for some events */
		int  arg2; /* very few events have data1/data2 */
	};
	struct ev_mixer {
		int  channel;
		int  volume;
	};
	struct ev_channel {
		int  channel;
		int  event; /* started, unable to load etc */
	};
	struct ev_song {
		int  slot;
//		int  track; /* for multiple blocks per slot */
//		int  tracks;
		int  size;
	};

	struct ev_slot {
		int  slot;
		int  event; /* ?? e.g. stream underflow? */
	};
	struct ev_slot_info {
		int  slot;
//		int  block; /* for multiple blocks per slot */
//		int  num_blocks;
		int  samplerate;
		int  type;
		int  channels;
		int  size;
	};
} gsi_event_t;


/* How handle (large) variable sized events like SLOT_INFO / TRACK_LIST ?
 *
 * struct ev_info {
 *	unsigned char *event_data;
 * };
 * and malloc the data.
 */


typedef struct gsi_raw_event {
	int  event;
	int  type;
	unsigned char *data;
} gsi_raw_event_t;

typedef struct gsi_cd_event {
	int  event;
	int  type;
	int  cd;
	int  slot;
	int  status;
	int  num_tracks;
	gsi_cd_track_info_t *tracks;
} gsi_cd_event_t;
typedef struct gsi_channel_event {
	int  event;
	int  type;
	int  channel;
	int  status;
} gsi_channel_event_t;
typedef struct gsi_slot_event {
	int  event;
	int  type;
	int  slot;
	int  status;
} gsi_slot_event_t;
typedef struct gsi_song_event {
	int  event;
	int  type;
	int  slot;
	int  status;
} gsi_song_event_t;




#ifdef USE_SOCKETS
#  include <sys/types.h>
#  include <sys/socket.h>
#  include <netinet/in.h>
#endif
typedef struct gsi_server {
	/* global ... */
	int   gsi_on; /* = True */

	int   gsi_cd_track_no; /* = 0 */
	struct gsi_cd_track_info *gsi_cd_track_list; /* = NULL */


	/* static ... */
	int   gsi_share_permissions; /* = 0600 */

	char *gsi_server;

	int   send_fd;
	int   receive_fd;

#define _MAX_QUEUE_LENGTH  1024 /* sound commands and arguments to be sent */
	int   num_commands;
	unsigned char  gsi_command_buffer[ _MAX_QUEUE_LENGTH ];

	int   sync_flag;
	int   gsi_event_data[16]; /* May be accessed externally */

	unsigned char status_queue[1024];
	int   status_queue_len;

	int   _gsi_num_pending_events; /* = 0 */
	int   _gsi_event_copy_decoded_data; /* = False */
	int   _gsi_event_type;
	int   _gsi_event_decoded_length;

	/* For more than device per device type: */
	int   _gsi_master_volume;
	int   _gsi_default_volume; /* at which samples, songs, cd's are started */

	int  *_gsi_mix_volume[ GSIDEV_NUM_DEVICE_TYPES ];
	int   _gsi_num_devices [ GSIDEV_NUM_DEVICE_TYPES ];

	int   channel_status[8]; /* 256 bits (playing yes/no for each channel), i.e. an int must be 32 bit or more */

	/* Call-back function jump table (will be removed SOON; probably in GSI 0.9) */
	void  (*cb_function[2+256])(int channel); /* all channels and one each for songs and cd tracks */

	void  (*error_callback_function)(int error);
	void  (*info_callback_function)(int info);


#ifdef USE_SOCKETS
	int   socket_fd;			/* connected socket descriptor */

	struct hostent *hp;			/* pointer to host info for remote host */
	struct servent *sp;			/* pointer to service information */

	time_t timevar; /* connection start time */

	struct sockaddr_in myaddr_in;	/* for local socket address */
	struct sockaddr_in peeraddr_in;	/* for peer socket address */

	char *remote_host; /* = NULL */
#endif

	char server_ok; /* = False */
	int  server_execed; /* = False */
	int  server_id; /* = -1 */ /* process id of the gsi_server in case it was execed. */
	int  server_version;
	int  server_compat_version;


	/* channels, slots: */
	int  channel_lock[8];
	int  slot_lock[8];
} gsi_server_t;


/*---------------------------------------------------------------------------*
 *      Global declarations for the interface file
 *---------------------------------------------------------------------------*/
extern char *gsi_version;
/*   An identification string to use for program startup etc. */


gsi_server_t *gsi_init (char *remote_host);
/*   Initializes the GSI sound system (connects to gsi_server or the
 *   NAS server when using the NAS interface, initializes various things,
 *   such as the mixer settings when using gsi_server) and is to be called
 *   before using any other GSI function. If remote_host is "" or NULL the
 *   host is assumed to be 'localhost' (or whatever your real hostname is, as
 *   reported by hostname(3)/gethostname(2)). Returns an initialized
 *   gsi_server_t structure if all went ok, NULL if unable to connect to the
 *   server or if the protocol versions of the client (i.e. libgsi) and the
 *   server are incompatible. */

// int gsi_init (gsi_server_t **c, char *remote_host);
//   Returns 0 if all went ok, -1 if unable to connect to the server.

void gsi_close (gsi_server_t *c);

void gsi_chdir (gsi_server_t *c, char *dir);
/*   After this call, the server will load all sound lists, samples etc having
 *   a relative path from 'dir' */

void gsi_load_samples_from_list (gsi_server_t *c, char *soundfile);
/*   Soundfile may be NULL or a zero length string in which case no sounds
 *   will be loaded. */

int  gsi_init_pcm (gsi_server_t *c, int pcm_samplerate, int pcm_channels, int pcm_type, int pcm_log2_fragsize);
/*   Initializes the pcm system with the given settings. pcm_log2_fragsize may
 *   be 0, in which case the server finds a size optimal for fast response.
 *   This function can be called more than once to change all the given
 *   settings at once.
 *
 *   Returns 0 if all ok, -1 if failed (eg. if gsi_init was never called or
 *   if it failed to connect to the server) */

/*int  gsi_set_midi_devices (int midichannels, int synthchannels);
 *   What did I want this for ?? */

/* OBSOLETE:
   int  gsi_init_synth (gsi_server_t *c, int type);
   Sets the the synthesizer type you want and grabs the synth device; type is
   0 for a real synth, 254 for external midi, 255 for wavetable emulation in
   software. [ N.B. use 0 for now as this value is currently ignored and
   playing is always on the synth hardware].
 */

int  gsi_commands_pending(gsi_server_t *c);
/*   Returns number of known to be internally buffered commands (i.e. have not
 *   yet been sent to the server), this may be 1 even if 10 commands are
 *   in the buffer, but it will only return 0 if no commands are buffered
 *   internally. */

int  gsi_events_pending(gsi_server_t *c);
/*   Returns number of known to be available events, i.e. this may be 1 even
 *   if 10 events have been received from the server (but possibly not yet
 *   decoded), but it will only return 0 if no events have been received. */

int  gsi_next_event(gsi_server_t *c);
/*   Returns -1 if no event available, 0 if one was handled. */

void gsi_flush (gsi_server_t *c);
/*   Send as many cached commands to the sound server as possibly without
 *   blocking. Also checks for events and calls the relevant callback
 *   fuctions. */

void gsi_flush_all (gsi_server_t *c);
/*   Same as gsi_flush but send ALL cached commands to the sound server. */

int  gsi_sync (gsi_server_t *c);
/*   Sends a GSI_CMD_SYNC, calls gsi_flush_all(), and waits for GSI_EV_SYNC.
 *   Returns 0 if all went well, -1 if timed out (no event within 10 s)
 *   while waiting for GSI_EV_SYNC.
 *   This call can be used to make sure all data was sent before
 *   disconnecting, e.g. for cd playing, getting information from an
 *   event without having to use a callback function, and for debugging. */

/* Combine gsi_flush_all() and gsi_sync() ?
 *int  gsi_sync (int sync_event);
 */

void gsi_play_sound (gsi_server_t *c, int channel, int slot, int command);
/*   Play sounds. Commands are:
 *        GSI_CMD_PLAY = play using a new channel
 *        GSI_CMD_REPEAT = ditto, repeat the sound when finished
 *        GSI_CMD_OVERWRITE = overwrite any playing sound on the channel
 *        GSI_CMD_OVERWRITE_REPEAT = overwrite any playing sound on the channel
 *          and repeat it */

//#warning "SHOULD FIX COMMAND TO A NON LOW LEVEL COMMAND:"
// i.e. GSIPLAY, GSIPLAY_REPEAT, GSIPLAY_
// ?? change to:
//void gsi_play_sound (gsi_server_t *c, int channel, int slot, int volume, int speed, int cmd, int repeat);


/* Special compound playing commands, use ch=-1 for automatic channel
 * allocation from the unused channels.
 * Note that speeds are the distance travelled in 1/32 s (automatically
 * recalculated for the given fragment size of the pcm device, as positions are
 * adjusted each fragment).
 * ------------------------------------------------------------------------- */
void gsi_play_sound_3d (gsi_server_t *c, int ch, int slot, int command, int x, int y, int z, int dx, int dy, int dz, int doppler_on);

void gsi_play_sound_stereo (gsi_server_t *c, int ch, int slot, int command, int x, int dx, int doppler_on);

/* Add volume to the arguments? */
void gsi_play_sound_pan (gsi_server_t *c, int ch, int slot, int command, int pan);
/*   pan =  -256 = full left, 256 = full right */

/* === RENAME ? Add volume to the args? and height? (8 channel surround) */
void gsi_play_sound_at (gsi_server_t *c, int channel, int slot, int command, int pan, int depth);
/*   Same as gsi_play_sound_pan but with depth (only makes a difference on
 *   4 channel sound cards). depth also has the range -256 (= completely behind
 *   you) ... +256 (= completely in front of you). */

void gsi_kill_sound (gsi_server_t *c, int ch);
/*   Kill the sound that's playing on channel ch. */

void gsi_kill_sounds (gsi_server_t *c);
/*   Stops all sounds playing on channels. */

void gsi_set_sound_volume (gsi_server_t *c, int ch, int volume);
/*   Sets the volume of a sound started on ch to volume. */

int  gsi_get_channel_status (gsi_server_t *c, int ch);
/*   Status of channel ch, is there a sound playing on it? (0=no, 1=locked,
 *   2=playing) */

/* Sound loading: */
int  gsi_load_sample (gsi_server_t *c, int slot, char *filename);
/*   Load a sample from disk with format information read from the header
 *   (see documentation for supported formats).
 *   Equivalent to 'gsi_load_sample_from' with offset=0. */

int  gsi_load_sample_from (gsi_server_t *c, int slot, char *filename, int offset);
/*   Load a sample from disk with format information read from the header at
 *   the given offset in the file (see documentation for supported file and
 *   sample formats). */

int  gsi_load_raw_sample (gsi_server_t *c, int slot, char *filename, int offset, int length, int samplerate, int channels, int type);
/*   Load a raw sample from disk with format information given directly
 *   (see documentation for supported sample formats). */

int  gsi_send_raw_sample (gsi_server_t *c, int slot, int samplerate, int channels, int type, int size, unsigned char *data);
/*   Send a sample to the given slot.
 *   Samples must in MSB format (for 16 bit sounds), and left channel first
 *   (for stereo sounds). */


/* === USEFUL ?
 *void gsi_convert_slot (int slot, int samplerate, int channels, int type, int size);
 *   Use -1 for any of these parameters you don't want to change.
 */


/* Song loading (note that all these commands stop any playing song first): */
int  gsi_load_song (gsi_server_t *c, int slot, char *filename);
int  gsi_load_song_from (gsi_server_t *c, int slot, char *filename, int offset);

int  gsi_send_song (gsi_server_t *c, int slot, int data_len, unsigned char *data);

int  gsi_send_song_track (gsi_server_t *c, int slot, int trackno, int data_len, unsigned char *data);
/*   For music making programs */

/* Song playing: */
void gsi_play_song (gsi_server_t *c, int synth, int song, int ch, int loopno);
/*   Starts playing the currently loaded song <loopno> times. If <loopno> =0
 *   it will continue to play until it is stopped or a new song is loaded.
 *   0 <= loopno <= 255. */


/*
 *OBSOLETE void gsi_stop_song (void);
 *OBSOLETE  *   Stops playing the currently loaded song
 *
 *OBSOLETE void gsi_continue_song (void);
 *OBSOLETE  *   Continues playing the currently loaded song (if given before a
 *OBSOLETE  *   gsi_play_song, this is ignored)
 *
 * Individual track playing ??? gsi_stop_track(int track)
 *
 *int  gsi_set_song_controller (int midi_ch, int midi_ctl, int ctl_val);
 *   To set the pan, volume etc for any of the midi channels
 */


/* Custom instrument loading: */
int  gsi_load_instrument (gsi_server_t *c, int pgm, char *filename, int offset);
/*   =U= offset = where in the file to get the instrument. */

/* ADD?
 * int  gsi_load_raw_instrument (int pgm, char *filename, int offset, int instrtype);
 *     offset = where in the file to get the instrument data.
 */

int  gsi_send_instrument_data (gsi_server_t *c, int pgm, int instrtype, int instrlen, unsigned char *instrdata);
/*   Instrtype = GSI_INSTRUMENT_OPL2, GSI_INSTRUMENT_OPL3, GSI_INSTRUMENT_GUS, ..  */


void gsi_grab_mixer (gsi_server_t *c);

void gsi_grab_pcm (gsi_server_t *c);
/*   Grabs the pcm device and sets the samplerate, channels, type, fragsize as
 *   set by earlier commands. */

void gsi_grab_synth (gsi_server_t *c, int dev);
/*   Grabs the sequencer */

void gsi_grab_cd (gsi_server_t *c, int cd);
/*   Grabs the cd */

void gsi_share_cd (gsi_server_t *c, int cd);
/*   Opens the cd device if not yet done and allows other programs to give CD
 *   commands too */

void gsi_release_mixer (gsi_server_t *c);

void gsi_release_pcm (gsi_server_t *c);
/*   Releases the pcm device (to share it with other programs) */

void gsi_release_synth (gsi_server_t *c, int dev);
/*   Releases the sequencer */

void gsi_release_cd (gsi_server_t *c, int cd);
/*   Releases the cd */

void gsi_pause (gsi_server_t *c);
/*   Pause all (pcm/synth/cd) */
void gsi_continue (gsi_server_t *c);
/*   Continue all (pcm/synth/cd) */


/* ----- MIXER
 * The following mixer commands only have effect if the client has
 * (succesfully) grabbed the mixer. */

void gsi_set_balance (gsi_server_t *c, int balance);
/*   balance=-256 (full left) .. 256 full right. The default is 0 (center).
 *   This affects the master volume (perhaps also direct cd output (for
 *   headphones on cd front panel output etc?). */

/*
 * add this ?
 *int  gsi_get_balance (void);
 */

void gsi_mute (gsi_server_t *c);
/*   Disable all sound output. */
void gsi_unmute (gsi_server_t *c);
/*   Enable again. */

void gsi_set_master_volume (gsi_server_t *c, int vol);
/*   Note: If there's no master volume, it is emulated with an internal volume
 *   and all others (SYNTH, PCM, CD, ...) will be adjusted accordingly. */

void gsi_set_mixer_volume (gsi_server_t *c, int mix_ch, int dev, int vol);
/*   Sets the volume multiplier (i.e. maximum volume) for the given device. */

int  gsi_get_master_volume (gsi_server_t *c);
int  gsi_get_mixer_volume (gsi_server_t *c, int mix_ch, int dev);
/*   These functions return -1 if the setting is not yet retrieved from the
 *   server or set from the current program (should never happen). */


/*
 *OBSOLETE  * ____ Application volume (set_device_volume?)
 *OBSOLETE void gsi_set_volume (int mix_ch, int vol);
 *OBSOLETE int  gsi_get_volume (int mix_ch);
 */

/* ----- application volume  */
void gsi_set_channel_volume (gsi_server_t *c, int ch, int vol);
void gsi_set_cd_volume (gsi_server_t *c, int cd, int vol);

void gsi_set_volume (gsi_server_t *c, int volume);

/* Add convenience functions ?:
 *    gsi_set_pcm_volume(int vol) { gsi_set_volume (GSI_PCM, int vol); }
 * etc ?? */

/*void gsi_set_cdoutput_volume (int vol); */
/*   useful? (this is the volume produced by the cd player and is set with an
 *   ioctl to the CD itself, e.g. for the headphone output).
 * Could also be done with GSI_MIXER_CDOUTPUT and GSI_CMD_SET_MIXER. */


/* ----- CD */
void gsi_cd_play (gsi_server_t *c, int cd, int t1, int m1, int s1, int f1, int t2, int m2, int s2, int f2);
/*   Sends a GSI_EV_CD_TRACK_STARTED event for each track and a GSI_EV_CD_
 *   TRACK_END when the last track finishes. */

void gsi_cd_pause (gsi_server_t *c, int cd);
void gsi_cd_resume (gsi_server_t *c, int cd);
void gsi_cd_stop (gsi_server_t *c, int cd);
void gsi_eject_cd (gsi_server_t *c, int cd);
void gsi_insert_cd (gsi_server_t *c, int cd);
void gsi_change_cd (gsi_server_t *c, int cd, int cd_slot); /* For CD changers */

void gsi_cd_get_status (gsi_server_t *c, int cd);
/*   The server will send an event with the cd status (playing, paused,
 *   stopped) & current msf. */
void gsi_cd_get_tracks (gsi_server_t *c, int cd);
/*   The server will send an event with the track starting times (msf) and
 *   types (audio/data). */


/* Selecting events sent to the program: */
void gsi_enable_events (gsi_server_t *c, int bits);
void gsi_disable_events (gsi_server_t *c, int bits);

/* callback functions (will be replaced by events soon): */
/* errors: */
void *gsi_set_error_callback (gsi_server_t *c, void (*cbf)(int error));
void gsi_clear_error_callback (gsi_server_t *c); /* equivalent to gsi_set_error_callback(NULL) */

/* information/status: */
void *gsi_set_info_callback (gsi_server_t *c, void (*cbf)(int info));
void gsi_clear_info_callback (gsi_server_t *c);

/* play information (channels, ch=-1 for songs, -2 for cd): */
void *gsi_set_channel_callback (gsi_server_t *c, int ch, void (*cbf)(int ch));
void gsi_clear_channel_callback (gsi_server_t *c, int ch);

int  gsi_wait_for_event (gsi_server_t *c, int sec, int musec);
/*   Waits using select, if sec < 0, wait forever until a event arrives.
 *   Returns 0 if an event arrived within the given time, -1 if not.
 *   NB. May change to use a timeval struct. */

int  gsi_event_fd (gsi_server_t *c);
/*   Returns the file descriptor used for events (which call the callbacks)
 *   so you can use it in a select together with other descriptors. */


/* Dynamic slot & channel allocation: */
int  gsi_find_free_slot (gsi_server_t *c);
/*   Returns -1 when all slots are used, the number of a free slot (0-255)
 *   otherwise. */

int  gsi_find_free_channel (gsi_server_t *c);
/*   Returns -1 when all channels are playing sounds, the number of a free
 *   channel (0-255) otherwise. */

/* NB. These are higher level functions that require either:
 *    - synchronization between client server (bad, slows the program at least
 *   .02 - 0.04 s for most unixes as tasks must be switched twice)
 *
 *   or the current choice:
 *
 *    - locks (which must be set whenever you allocate slots/channels;
 *   callbacks will set the real channel-playing /slot-used flags.
 *   The standard commands defined in gsi.h all do this automatically, so
 *   this is only needed if you use the GSI_CMD_* commands directly with
 *   gsi_send_commands(..) (e.g. to write your own compound commands)). */

int  gsi_lock_channel (gsi_server_t *c, int ch);
int  gsi_lock_slot (gsi_server_t *c, int slot);

/* The following two functions can be called whenever you want to reuse the
 * given positions (done automatically for channels, NOT YET for slots). */
int  gsi_unlock_channel (gsi_server_t *c, int ch);
int  gsi_unlock_slot (gsi_server_t *c, int slot);


/* Shared memory (e.g. SYSV SHM) */
void gsi_send_shm_stream (gsi_server_t *c, int bytes, char * buffer);
/*   ?????????? */

int  gsi_make_shm (gsi_server_t *c, int bytes, char **shm);
/*   Returns 0 if ok, <0 if failed (error code from shmget. */

int  gsi_update_shm_write (gsi_server_t *c, int bytes, char *shmdata);
/*   Tell the server that 'bytes' were added to the buffer. */

int  gsi_update_shm_read (gsi_server_t *c, int bytes, char *shmdata);
/*   Tell the server that 'bytes' were read from the buffer (for streams from
 *   the server, i.e. recording etc.) */

int  gsi_remove_shm (gsi_server_t *c, char *shm);
/*   Detach SHM and do a IPC_RMID shmctl. */


/*---------------------------------------------------------------------------*
 * Low level:
 *---------------------------------------------------------------------------*/
void gsi_send_commands (gsi_server_t *c, int argc, ...);
/*   Send any commands to the soundserver (in byte chunks; 16 bit and larger
 *   must be sent MSB first)
 *	    eg. gsi_send_commands(5, GSI_CMD_SET_X | GSI_CMD_CURRENT, x >>8, x &255,
 *      GSI_CMD_SET_DX | GSI_CMD_CURRENT, dx); */

void gsi_send_byte (gsi_server_t *c, int k);
void gsi_send_int32 (gsi_server_t *c, int k);
void gsi_send_block (gsi_server_t *c, unsigned char *data, int len);
void gsi_send_string (gsi_server_t *c, char *string);

#ifdef _cplusplus
}
#endif /* __cplusplus */

#endif /* _GSI__gsi__gsi_h */
