
/*
 * lakai.h - liblakai header file
 *
 * Copyright (c) 2002-2004 Frank Neumann <franky@users.sourceforge.net>
 */

/* AKAI's extension of the SCSI opcodes (see /usr/include/scsi/scsi.h). */
/* These IDs are unallocated in that file..reserved for customer use?   */
#define S2000_MODE    0x09
#define S2000_SEND    0x0c
#define S2000_STATUS  0x0d
#define S2000_RECEIVE 0x0e

/* S1000 SysEx "command" bytes, used both for "send" and "receive" direction */
/* This is typically the fourth byte of a SysEx message */
#define LC_RSTAT   0x00  /* request status report */
#define LC_STAT    0x01  /* status report */
#define LC_RPLIST  0x02  /* request list of resident program names */
#define LC_PLIST   0x03  /* list of resident program names */
#define LC_RSLIST  0x04  /* request list of resident sample names */
#define LC_SLIST   0x05  /* list of resident sample names */
#define LC_RPDATA  0x06  /* request program common data */
#define LC_PDATA   0x07  /* program common data */
#define LC_RKDATA  0x08  /* request keygroup data */
#define LC_KDATA   0x09  /* keygroup data */
#define LC_RSDATA  0x0a  /* request sample header data */
#define LC_SDATA   0x0b  /* sample header data */
#define LC_RSPACK  0x0c  /* request sample data packet(s) */
#define LC_ASPACK  0x0d  /* accept sample data packet(s) */
#define LC_RDDATA  0x0e  /* request drum input settings */
#define LC_DDATA   0x0f  /* drum input settings */
#define LC_RMDATA  0x10  /* request miscellaneous data */
#define LC_MDATA   0x11  /* miscellaneous data */
#define LC_DELP    0x12  /* delete program and its keygroups */
#define LC_DELK    0x13  /* delete keygroup */
#define LC_DELS    0x14  /* delete sample header and data */
#define LC_SETEX   0x15  /* set Sx000 exclusive channel */
#define LC_REPLY   0x16  /* Sx000 command reply (error or ok) */
#define LC_CASPACK 0x1d  /* corrected ASPACK */

/* new operation codes for S3000 (/S2000?) */
#define LC_S3RPDATA  0x27 /* request for program header bytes */
#define LC_S3PDATA   0x28 /* program header bytes */
#define LC_S3RKDATA  0x29 /* request keygroup header bytes */
#define LC_S3KDATA   0x2a /* keygroup header bytes */
#define LC_S3RSDATA  0x2b /* request sample header bytes */
#define LC_S3SDATA   0x2c /* sample header bytes */
#define LC_S3RFDATA  0x2d /* request fx/reverb bytes */
#define LC_S3FDATA   0x2e /* fx/reverb bytes */
#define LC_S3RCLDATA 0x2f /* request Cue-list data */
#define LC_S3CLDATA  0x30 /* Cue-list data */
#define LC_S3RTLDATA 0x31 /* request for take list bytes */
#define LC_S3TLDATA  0x32 /* take list bytes */
#define LC_S3RMDATA  0x33 /* request miscellaneous bytes */
#define LC_S3MDATA   0x34 /* miscellaneous bytes */
#define LC_S3RVLI    0x35 /* request volume list item */
#define LC_S3VLI     0x36 /* volume list item (only used in response to requ) */
#define LC_S3RHDENT  0x37 /* request harddisk directory entry */
#define LC_S3HDENT   0x38 /* harddisk directory entry (only in response to req) */


/*
 * The access to all elements inside the sampler should be handled through
 * this library.
 *
 * In doing so, two different internal communication methods are used
 * (which are invisible to the user of the API, though):
 *
 * - When requesting basic, low-volume data (like program lists, sample
 *   header data etc), the protocol used is "MIDI-over-SCSI".
 *
 * - When requesting large blocks of data (typically the actual sample
 *   data), the protocol used is "raw SCSI".
 *
 * An application program will first have to use the "low-volume"
 * inquiry functions to get at all the basic information it needs
 * (what samples are in the sampler right now, where are they located,
 * how large are they), and after that can use the "high-volume"
 * functions to transfer samples to/from the sampler.
 *
 * In all of these transfers, the application has to allocate the
 * required memory structures; as the library does not know most of
 * the information until it's parsed, there would probably be too much
 * code replication because the application will have to parse the data
 * anyway (and right now I can't think of good ways to put this into
 * utility functions in the library).
 *
 */


/* An LHANDLE is your key to accessing a LAKAI device */
typedef int LHANDLE;

/* TODO: Decide if the data that gets passed back to the user program is the
 * original raw data or if it has been pre-processed for easier use.
 * I think the latter should not be too complicated. It's mostly stripping off
 * the SysEx header/EOX and filling in the data fields of the respective
 * structures.
 */

/* a lakai_status structure contains such information as OS version,
 * number of programs&samples in memory, free memory etc
 */
typedef struct
{
	int osversion;			/* OS version of currently booted Akai OS */
	int nummaxblocks;		/* maximum number of sample/program/keygroup blocks */
	int numfreeblocks;	/* number of currently free blocks */
	int nummaxsamples;	/* maximum number of sample words */
	int numfreesamples;	/* number of currently available sample words */
	int eoxchannel;		/* current MIDI channel for exclusive data */
} LakaiStatus;

/* A LakaiProgramList holds a list of currently resident program names in
 * the sampler.
 */
typedef struct
{
	int numprogs;
	char **prognames;
} LakaiProgramList;

/* A LakaiSampleList holds a list of currently resident sample names in
 * the sampler.
 */
typedef struct
{
	int numsamples;
	char **samplenames;
} LakaiSampleList;


/* A LakaiProgram structure holds all information that make up one
 * program - tuning, filter settings, keygroups etc.
 */
typedef struct
{
	/* TODO: Incomplete.. */
	unsigned char prident;		/* 1=Program header block identifier */
	/* a pad byte is inserted here by the compiler */
	unsigned short kgrp1;		/* 1st keygroup block address (internal use) */
	unsigned char prname[12];	/* Name */
	unsigned char prgnum;		/* MIDI program number (0-127) */
	unsigned char pmchan;		/* MIDI channel (0-15, FFh=OMNI) */
	unsigned char polyph;		/* Polyphony (1-16) */
	unsigned char priort;		/* Priority (0=low 1=normal 2=high 3=hold) */
	unsigned char playlo;		/* Play-range low (24-127 = C0-G8) */
	unsigned char playhi;		/* Play-range high (24-127 = C0-G8) */
	unsigned char oshift;		/* Play octave (keyboard) shift(+/-2) */
	unsigned char output;		/* Output number (0-7,FFh=off) */
	unsigned char stereo;		/* Left and right level (0-99) */
	unsigned char panpos;		/* Left/right balance (+/-50) */
	unsigned char prloud;		/* Basic loudness (0-99) */
	unsigned char v_loud;		/* Velocity&Loudness (+/-50) */
	unsigned char k_loud;		/* Key&Loudness (+/-50) */
	unsigned char p_loud;		/* Pressure&Loudness (+/-50) */
	unsigned char panrat;		/* Pan LFO rate (0-99) */
	unsigned char pandep;		/* Pan depth (0-99) */
	unsigned char pandel;		/* Pan LFO delay (0-99) */
	unsigned char k_panp;		/* Key&Pan position (+/-50) */
	unsigned char lforat;		/* LFO speed (0-99) */
	unsigned char lfodep;		/* LFO fixed depth (0-99) */
	unsigned char lfodel;		/* LFO delay (0-99) */
	unsigned char mwldep;		/* Modwheel&LFO depth (0-99) */
	unsigned char prsdep;		/* Pressure&LFO depth (0-99) */
	unsigned char veldep;		/* Velocity&gt;LFO depth (0-99) */
	unsigned char b_ptch;		/* Bendwheel&Pitch (0-12 semitones) */
	unsigned char p_ptch;		/* Pressure&Pitch (+/-12 semitones) */
	unsigned char kxfade;		/* Keygroup crossfade (0=off 1=on) */
	unsigned char groups;		/* number of keygroups (1-99) */
	unsigned char tpnum;			/* temporary program number (internal use) */
	unsigned char temper[12];	/* Key temperament (+/25 cents) C,C#,D,D# etc */
	unsigned char echout;		/* Echo output level (0=off 1=on) */
	unsigned char mw_pan;		/* Modwheel pan amount (+/-50) */
	unsigned char cohere;		/* Sample start coherence (0=off 1=on) */
	unsigned char desync;		/* LFO De-Sync (0=off 1=on) */
	unsigned char plaw;			/* Pitch Law (0=linear) */
	unsigned char vassoq;		/* Voice assign algorithm (0=oldest 1=quietest) */
	unsigned char sploud;		/* Soft pedal loudness reduction (0-99) */
	unsigned char spatt;			/* Soft pedal attack stretch (0-99) */
	unsigned char spfilt;		/* Soft pedal filter close (0-99) */
	unsigned short ptuno;		/* Tune offset cent:semi (+/-50.00 fraction is binary) */
	unsigned char k_lrat;		/* Key&LFO rate (+/-50) */
	unsigned char k_ldep;		/* Key&LFO depth (+/-50) */
	unsigned char k_ldel;		/* Key&LFO delay (+/-50) */
	unsigned char voscl;			/* Voice output scale (0=-6dB, 1=0dB, 2=+12dB) */
	unsigned char vsscl;			/* Stereo output scale (0=0dB, 1=+6dB) */
	unsigned char legato;		/* Mono legato mode enable (0=Off, 1=On) */
	unsigned char b_ptchd;		/* Range of decrease of Pitch by bendwheel (0..12 semitones) */
	unsigned char b_mode;		/* Bending of held notes (0=normal mode, 1=held) */
	unsigned char transpose;	/* Shift pitch of incoming MIDI (-50..+50 semitones) */
	/* Values used to represent Modulation Sources for the following mod's:
	 *
	 * 0: No Source
	 * 1: Modwheel
	 * 2: Bend
	 * 3: Pressure
	 * 4: External
	 * 5: Note-on velocity
	 * 6: Key
	 * 7: LFO1
	 * 8: LFO2
	 * 9: Env1
	 * 10: Env2
	 * 11: !Modwheel (Instantaneous value of modwheel at note-on)
	 * 12: !Bend (Instantaneous value of bendwheel at note-on)
	 * 13: !External (Instantaneous value of MIDI controller at note-on)
	 * 14: Env3
	 */ 
	unsigned char modspan1;		/* First source of assignable modulation of pan position */
	unsigned char modspan2;		/* Second source of assignable modulation of pan */
	unsigned char modspan3;		/* Third source of assignable modulation of pan */
	unsigned char modsamp1;		/* First source of assignable modulation of loudness */
	unsigned char modsamp2;		/* Second source of assignable modulation of loudness */
	unsigned char modslfot;		/* Source of assignable modulation of LFO1 speed */
	unsigned char modslfol;		/* Source of assignable modulation of LFO1 depth */
	unsigned char modslfod;		/* Source of assignable modulation of LFO1 delay */
	unsigned char modsfilt1;	/* First source of assignable modulation of filter frequency */
	unsigned char modsfilt2;	/* Second source of assignable modulation of filter frequency */
	unsigned char modsfilt3;	/* Third source of assignable modulation of filter frequency */
	unsigned char modspitch;	/* Source of assignable modulation of pitch */
	unsigned char modsamp3;		/* Third source of assignable modulation of loudness */
	unsigned char modvpan1;		/* Amount of control of pan by assignable source 1 (-50..50) */
	unsigned char modvpan2;		/* Amount of control of pan by assignable source 2 (-50..50) */
	unsigned char modvpan3;		/* Amount of control of pan by assignable source 3 (-50..50) */
	unsigned char modvamp1;		/* Amount of control of loudness by assignable source 1 (-50..50) */
	unsigned char modvamp2;		/* Amount of control of loudness by assignable source 2 (-50..50) */
	unsigned char modvlfor;		/* Amount of control of LFO1 speed (-50..50) */
	unsigned char modvlfol;		/* Amount of control of LFO1 depth (-50..50) */
	unsigned char modvlfod;		/* Amount of control of LFO1 delay (-50..50) */
	unsigned char lfo1wave;		/* LFO1 waveform (0=Triangle, 1=Sawtooth, 2=Square) */
	unsigned char lfo2wave;		/* LFO2 waveform (0=Triangle, 1=Sawtooth, 2=Square) */
	unsigned char modslflt2_1;	/* First source of assignable modulation of filter 2 frequency (only used on S3200) */
	unsigned char modslflt2_2;	/* Second source of assignable modulation of filter 2 frequency (only used on S3200) */
	unsigned char modslflt2_3;	/* Third source of assignable modulation of filter 2 frequency (only used on S3200) */
	unsigned char lfo2trig;		/* Retrigger mode for LFO2 */
	unsigned char _reserved1[7];	/* .. */
	unsigned char portime;		/* Portamento Time */
	unsigned char portype;		/* Portamento Type */
	unsigned char porten;		/* Portamento On/Off */
	unsigned char pfxchan;		/* Effects Bus Select (0=Off, 1=FX1, 2=FX2, 3=RV3, 4=RV4) */
	unsigned char pfxslev;		/* Not used */
} LakaiProgram;


/* A LakaiKeygroup structure holds the information for one keygroup:
 * lower/upper end of range etc.
 */
typedef struct
{
	unsigned char kgident;		/* 2=Keygroup block identifier */
	unsigned short nxtkg;		/* Next keygroup block address (internal use) */
	unsigned char lonote;		/* Keyrange low (24-127 = C0-G8) */
	unsigned char hinote;		/* Keyrange high (24-127 = C0-G8) */
	unsigned short kgtuno;		/* Tune offset cent:semi (+/-50.00 fraction is binary */
	unsigned char filfrq;		/* Basic filter frequency (0-99) */
	unsigned char k_freq;		/* Key&Filter freq (+/-24 semitones/octave) */
	unsigned char v_freq;		/*	Velocity&Filter freq (+/-50) */
	unsigned char p_freq;		/* Pressure&Filter freq (+/-50) */
	unsigned char e_freq;		/* Envelope&Filter freq (+/-50) */
	unsigned char attak1;		/* Amplitude attack (0-99) */
	unsigned char decay1;		/* Amplitude decay (0-99) */
	unsigned char sustn1;		/* Amplitude sustain level (0-99) */
	unsigned char relse1;		/* Amplitude release (0-99) */
	unsigned char v_att1;		/* Velocity&Amp attack (+/-50) */
	unsigned char v_rel1;		/* Velocity&Amp release (+/-50) */
	unsigned char o_rel1;		/* Off Vel.&Amp release (+/-50) */
	unsigned char k_dar1;		/* Key&Decay&Release (+/-50) */
	unsigned char attak2;		/* Filter attack (0-99) */
	unsigned char decay2;		/* Filter decay (0-99) */
	unsigned char sustn2; 		/* Filter sustain level (0-99) */
	unsigned char relse2;		/* Filter release (0-99) */
	unsigned char v_att2;		/* Velocity&Filter attack (+/-50) */
	unsigned char v_rel2;		/* Velocity&Filter release (+/-50) */
	unsigned char o_rel2;		/* Off Vel.&Filter relase (+/-50 */
	unsigned char k_dar2;		/* Key&Decay&Release (+/-50) */
	unsigned char v_env2;		/* Velocity&Filter envelope output (+/-50) */
	unsigned char e_ptch;		/* Envelope&Pitch (+/-50) */
	unsigned char vxfade;		/* Velocity zone crossfade (0=off 1=on) */
	unsigned char vzones;		/* Number of velocity zones in use (not used) */
	unsigned char lkxf;			/* Calculated left key crossfade factor (internal) */
	unsigned char rkxf;			/* Calculated right key crossfade factor (internal) */
	
	/* Velocity zone 1 */
	unsigned char sname1[12];	/* Sample name */
	unsigned char lovel1;		/* Velocity range low (0-127) */
	unsigned char hivel1;		/* Velocity range high (0-127) */
	unsigned short vtuno1;		/* Tune offset (+/-50.00 fraction is in binary form) */
	unsigned char vloud1;		/* Loudness offset (+/-50) */
	unsigned char vfreq1;		/* Filter frequency offset (+/-50) */
	unsigned char vpano1;		/* Pan offset (+/-50) */
	unsigned char zplay1;		/* Loop in release (0=as sample, 1-4 see below) */
	unsigned char lvxf1;			/* Low velocity crossfade factor (internal use) */
	unsigned char hvxf1;			/* High velocity crossfade factor (internal use) */
	unsigned short sbadd1;		/* Calculated sample header block address (internal) */

	/* Velocity zone 2 */
	unsigned char sname2[12];	/* See velocity zone 1 */
	unsigned char lovel2;
	unsigned char hivel2;
	unsigned short vtuno2;
	unsigned char vloud2;
	unsigned char vfreq2;
	unsigned char vpano2;
	unsigned char zplay2;
	unsigned char lvxf2;
	unsigned char hvxf2;
	unsigned short sbadd2;

	/* Velocity zone 3 */
	unsigned char sname3[12];	/* See velocity zone 1 */
	unsigned char lovel3;
	unsigned char hivel3;
	unsigned short vtuno3;
	unsigned char vloud3;
	unsigned char vfreq3;
	unsigned char vpano3;
	unsigned char zplay3;
	unsigned char lvxf3;
	unsigned char hvxf3;
	unsigned short sbadd3;
	
	/* Velocity zone 4 */
	unsigned char sname4[12];	/* See velocity zone 1 */
	unsigned char lovel4;
	unsigned char hivel4;
	unsigned short vtuno4;
	unsigned char vloud4;
	unsigned char vfreq4;
	unsigned char vpano4;
	unsigned char zplay4;
	unsigned char lvxf4;
	unsigned char hvxf4;
	unsigned short sbadd4;
	
	unsigned char kbeat;			/* Fixed rate detune (byte) */
	unsigned char ahold;			/* Attack hold until loop */
	unsigned char cp1;			/* Constant pitch for each velocity zone (0=track 1=const) */
	unsigned char cp2;
	unsigned char cp3;
	unsigned char cp4;
	unsigned char vzout1;		/* Output number offset for each velocity zone (0-7) */
	unsigned char vzout2;
	unsigned char vzout3;
	unsigned char vzout4;
	unsigned short vss1;			/* Velocity&Sample start (+/-9999) */
	unsigned short vss2;
	unsigned short vss3;
	unsigned short vss4;
	unsigned char kv_lo;			/* Velocity&Loudness offset (+/-50) */
	/* ZPLAY:- type of sample playback, values:
	 * 0 = as defined by sample header
	 * 1 = normal looping
	 * 2 = loop until release
	 * 3 = no looping
	 * 4 = play to sample end
	 */

	/* TODO: Need to proof-read this! */
	unsigned char filq;
	unsigned char l_ptch;
	unsigned char modvfilt1;
	unsigned char modvfilt2;
	unsigned char modvfilt3;
	unsigned char modvpitch;
	unsigned char modvamp3;
	unsigned char env2l1;
	unsigned char env2r2;
	unsigned char env2l2;
	unsigned char env2l4;
	unsigned char kgmute;
	unsigned char pfxchan;
	unsigned char pfxslev;
	unsigned char res1[5];
	unsigned char lsi2_on;
	unsigned char flt2gain;
	unsigned char flt2mode;
	unsigned char flt2q;
	unsigned char tonefreq;
	unsigned char toneslop;
	unsigned char modvflt2_1;
	unsigned char modvflt2_2;
	unsigned char modvflt2_3;
	unsigned char fil2fr;
	unsigned char k_frq2;
	unsigned char env3r1;
	unsigned char env3l1;
	unsigned char env3r2;
	unsigned char env3l2;
	unsigned char env3r3;
	unsigned char env3l3;
	unsigned char env3r4;
	unsigned char env3l4;
	unsigned char v_att3;
	unsigned char v_rel3;
	unsigned char o_rel3;
	unsigned char k_dar3;
	unsigned char v_env3;

} LakaiKeygroup;


/* A LakaiSampleHeader structure contains information about the associated
 * sample, such as length, loop points
 */
typedef struct
{
	unsigned char shident;			/* 3=sample header block identifier */
	unsigned char sbandw;			/* Bandwidth (0=10kHz 1=20kHz) */
	unsigned char spitch;			/* Original pitch (24-127 = C0-G8) */
	unsigned char shname[12];		/* Name (same position as program) */
	unsigned char ssrvld;			/* Sample rate ssrate valid (80H=yes) */
	unsigned char sloops;			/* Number of loops (internal use) */
	unsigned char saloop;			/* First active loop (internal use) */
	unsigned char padbyte;			/* Spare byte */
	unsigned char sptype;			/* Playback type (see below) */
	unsigned short stuno;			/* Tune offset cent:semi (+/-50.00) */
	unsigned long slocat;			/* Data absolute start address */
	unsigned long slngth;			/* Data length (number of samples) */
	unsigned long sstart;		/* Play relative start address */
	unsigned long smpend;		/* Play relative end address */
	unsigned long loopat1;		/* Relative loop point (bits 0-5 are treated as 1) */
	unsigned long llngth1;		/* Loop length (binary) fraction:INT.LOW:INT.HIGH */
	unsigned short ldwell1;			/* Dwell time (0=no loop 1-9998=mSec 9999=hold) */

	/* TODO: All short XXX[2] should be long XXX instead */
	unsigned short loopat2[2];		/* See loop 1 */
	unsigned short llngth2[3];
	unsigned short ldwell2;

	unsigned short loopat3[2];		/* See loop 1 */
	unsigned short llngth3[3];
	unsigned short ldwell3;

	unsigned short loopat4[2];		/* See loop 1 */
	unsigned short llngth4[3];
	unsigned short ldwell4;

	unsigned short loopat5[2];		/* See loop 1 */
	unsigned short llngth5[3];
	unsigned short ldwell5;

	unsigned short loopat6[2];		/* See loop 1 */
	unsigned short llngth6[3];
	unsigned short ldwell6;

	unsigned short loopat7[2];		/* See loop 1 */
	unsigned short llngth7[3];
	unsigned short ldwell7;

	unsigned short loopat8[2];		/* See loop 1 */
	unsigned short llngth8[3];
	unsigned short ldwell8;
	
	unsigned char sspare[2];		/* Spare bytes used internally */
	unsigned short sspair;			/* Address of stereo partner (internal use) */
	unsigned short ssrate;			/* Sample rate in Hz */
	unsigned char shlto;				/* Hold loop tune offset (+/-50 cents) */
	/* Type of playback values:-
	 * 0 = normal looping
	 * 1 = Loop until release
	 * 2 = No looping
	 * 3 = Play to sample end
	 */
} LakaiSampleHeader;

/* TODO: Add here: Drum trigger unit block */
typedef struct
{
	unsigned char d1oper;
	unsigned char d1exch;
	unsigned char d1thru;
	unsigned char drname[12];

	unsigned char dchan1;
	unsigned char dnote1;
	unsigned char dsens1;
	unsigned char dtrig1;
	unsigned char dvcrv1;
	unsigned char dcatp1;
	unsigned char drcvr1;
	unsigned short dontm1;
	
	unsigned char dchan2;
	unsigned char dnote2;
	unsigned char dsens2;
	unsigned char dtrig2;
	unsigned char dvcrv2;
	unsigned char dcatp2;
	unsigned char drcvr2;
	unsigned short dontm2;
	
	unsigned char dchan3;
	unsigned char dnote3;
	unsigned char dsens3;
	unsigned char dtrig3;
	unsigned char dvcrv3;
	unsigned char dcatp3;
	unsigned char drcvr3;
	unsigned short dontm3;
	
	unsigned char dchan4;
	unsigned char dnote4;
	unsigned char dsens4;
	unsigned char dtrig4;
	unsigned char dvcrv4;
	unsigned char dcatp4;
	unsigned char drcvr4;
	unsigned short dontm4;
	
	unsigned char dchan5;
	unsigned char dnote5;
	unsigned char dsens5;
	unsigned char dtrig5;
	unsigned char dvcrv5;
	unsigned char dcatp5;
	unsigned char drcvr5;
	unsigned short dontm5;
	
	unsigned char dchan6;
	unsigned char dnote6;
	unsigned char dsens6;
	unsigned char dtrig6;
	unsigned char dvcrv6;
	unsigned char dcatp6;
	unsigned char drcvr6;
	unsigned short dontm6;
	
	unsigned char dchan7;
	unsigned char dnote7;
	unsigned char dsens7;
	unsigned char dtrig7;
	unsigned char dvcrv7;
	unsigned char dcatp7;
	unsigned char drcvr7;
	unsigned short dontm7;
	
	unsigned char dchan8;
	unsigned char dnote8;
	unsigned char dsens8;
	unsigned char dtrig8;
	unsigned char dvcrv8;
	unsigned char dcatp8;
	unsigned char drcvr8;
	unsigned short dontm8;
	
	/* unit 2 to follow here */
} LakaiDrumTrigger;

/* A LakaiMiscData structure contains some miscellaneous settings like
 * MIDI channel, OMNI setting etc.
 */
typedef struct
{
	unsigned char bmchan;		/* Basic MIDI channel (0-15) for MIDI program select */
	unsigned char bmomni;		/* Basic channel Omni (0=off 1=on) */
	unsigned char pselen;		/* MIDI program select enable (0=off 1=on) */
	unsigned char selpnm;		/*	Selected program number (0-127) */
	unsigned char omnovr;		/* MIDI play commands Omni override (0=off 1=on) */
	unsigned char exchan;		/* MIDI exclusive channel (0-127) */
} LakaiMiscData;

/* TODO: still missing here: Multi & effect structure */

/*
 * lakai_init()
 *
 * Performs some library initializations. To be called by each application
 * before it can start using the other liblakai functions.
 *
 * INPUTS:
 *  -
 * OUTPUTS:
 * -
 * RETURNS:
 * - (should always suceed)
 */

void lakai_init( void );

/*
 * open the given SCSI generic file (typically something like "/dev/sgx") for
 * I/O. This requires the "sg" driver to be available either as a module or
 * built into the kernel.
 *
 * INPUTS:
 * 	char *devname: pointer to name of the device special file to be opened
 *
 * OUTPUTS:
 * 	-
 * RETURNS:
 * 	LHANDLE: handle to be used in further calls to lakai functions
 * 	If lakai_open fails, returns -1.
 *
 */
LHANDLE lakai_open( char *devname );


/*
 * close the device special file that was previously opened with lakai_open().
 * No further actions are performed - the sampler device is NOT implictly put
 * back into NON-MIDI-OVER-SCSI mode, for instance. The user app has to do this
 * by itself first.
 * After a call to lakai_close() the LHANDLE given back earlier should not be
 * used anymore.
 *
 * INPUTS:
 * 	LHANDLE handle: handle to a previously opened device special file
 *
 * OUTPUTS:
 * 	-
 * 
 * RETURNS:
 * 	0 if lakai device was successfully closed, -1 on error.
 *
 */
int lakai_close( LHANDLE handle );


#define LAKAI_MODE_NORMAL		0x00
#define LAKAI_MODE_SCSI_MIDI	0x01
#define LAKAI_MODE_SCSI_BULK	0x02

/*
 * sets the working mode of the sampler to one of these:
 * "standard" (MIDI over serial), MIDI over SCSI, or SCSI bulk
 * 
 * "standard" MIDI means release the lock of the sampler.
 * This should always be called when an application has finished
 * exchanging data with the sampler (especially when the app quits).
 * 
 * "MIDI over SCSI" is used for getting the basic low-volume data
 * like status report, resident program/sample list, sample headers etc.
 * 
 * "SCSI bulk" is the mode used to transfer large chunk of data
 * (typically sample data)
 *
 * INPUTS: 
 * 	handle: LHANDLE to opened LAKAI device
 * 	mode: desired sampler mode, see LAKAI_MODE_* definitions above
 *
 *  RETURNS: 
 *  	0 if mode change was successful, -1 on error
 *  
 *  NOTE: 
 *  	The user could possibly change the setting himself through the panel
 *  	of the sampler (setting "MIDI via SCSI on/off" in the Global section).
 *  	However, he had better not interfere with the app here, only in cases
 *  	where the app is obviously "stuck" and cannot reset the state.
 */
int lakai_setmode(LHANDLE handle, int mode);

/*
 * Convert a string of characters from ASCII to Akai representation
 * Non-convertible characters in src get mapped to Space (' ') char in dest.
 *
 * INPUTS:
 * 	char *src: pointer to array that holds the original string
 * 	char *dest: pointer to array that holds the converted string
 * 	int len: # of characters to be converted
 *
 * 	The user has to make sure "dest" can hold "len" bytes.
 * 
 * OUTPUTS:
 * 	Converted string is in "dest" buffer.
 *
 * RETURNS:
 * 	-
 */
void lakai_asciitoakai(char *src, char *dest, int len);


/*
 * Convert a string of characters from Akai representation to ASCII 
 * Non-convertible characters in src get mapped to Space (' ') char in dest.
 *
 * INPUTS:
 * 	char *src: pointer to array that holds the original string
 * 	char *dest: pointer to array that holds the converted string
 * 	int len: # of characters to be converted
 *
 * 	The user has to make sure "dest" can hold "len" bytes.
 * 
 * OUTPUTS:
 * 	Converted string is in "dest".
 * 	
 * RETURNS:
 * 	-
 */
void lakai_akaitoascii(char *src, char *dst, int len);


#if 0

/* New thoughts, Apr 22, 2001 (sigh..):
 * I think I can get all of the "numbers" from the status report (# of program, samples, ...)
 * So, the application has to get this information first anyway, and if it then requests
 * the actual program data, it can also allocate the required space for that data itself.
 * Consequently, it will also have to free that memory by itself etc.
 * This frees the lib from doing that. Sounds good. Stick with that.
 *
 * Another advantage to that solution is that there is far less complexity in the lib
 * if it has to do memory-allocation housekeeping: Let's say the lib does the allocation
 * of data structures, it basically tells the client app "here is the data you asked for;
 * I allocated the memory for you to use it; now go and use it, and when you're finished,
 * please tell me so I can free the memory then". The client app can keep the data allocated
 * as long as it wants, requiring some kind of resource management in the lib. But I believe
 * this is the wrong approach; clasically, the app malloc()s the memory and tells the lib
 * where the buffer is so that the lib (/kernel) can fill that buffer with data. If the
 * buffer is too small, that's the client's fault, so "you wanted to shoot yourself in the
 * foot, please go ahead" from the point of view of the lib. Basta.
 *
 * The lib will get the raw (+sysex header etc) data from the device, but hands that
 * data over to the app in the "pre-chewed" form, that is, after stripping off SysEx stuff.
 *
 * When going the other direction, the app wanting to send program data etc to the sampler,
 * it only has to prepare the data structure, and the lib will take care of providing it
 * with SysEx headers, fragmentation etc. and send the packets to the sampler.
 *
 * 
 */

#define LAKAI_DEVTYPE_S1000 	0x01
#define LAKAI_DEVTYPE_S2000	0x02
/* ... more devtypes here when known.. */

/* 
 * identify a given device as Akai S1000 (or not)
 * TODO: Is this really needed? 
 * This could be integrated into the lakai_open() call..
 *
 * INPUTS:
 * 	LHANDLE to opened LAKAI device
 * 
 * RETURNS:
 * 	the type of the connected device, see LAKAI_DEVTYPE_ definitions above
 */
int lakai_identifydevice(LHANDLE handle);


/* 
 * test if the connected unit is ready to receive commands or send
 * data to the computer.
 * TODO: Do we really need this?
 *
 * INPUTS:
 * 	LHANDLE to opened LAKAI device
 *
 * RETURNS:
 * 	FALSE (0) or not ready, TRUE else.
 */
int lakai_testunitready(LHANDLE handle);


/*
 * gets the number of bytes available to be transferred back
 * to the host
 *
 * INPUTS:
 * 	handle: LHANDLE to opened LAKAI device
 *
 * RETURNS:
 * 	numbytes: number of waiting bytes
 */

/* #### TODO: Do I really need this? */
long lakai_getavailable(LHANDLE handle);

#endif

/*
 * reads a sample of 'len' bytes of data from the sampler into
 * the supplied buffer. The user has to take care that the buffer
 * is sufficiently large. Also, the user has to find out in advance
 * how large the expected sample is going ot be (in bytes) through
 * the lakai_get_sample_header() function.
 *
 * INPUTS:
 * 	handle: LHANDLE to opened LAKAI device
 * 	buffer: buffer to store transferred data into
 * 	len: number of bytes to be transferred
 *
 * RETURNS:
 * 	number of bytes that have actually been transferred,
 * 	0 if an error occurred.
 */
long lakai_get_sample(LHANDLE handle, int samplenum, char *buffer, long locat, long len);


#if 0

/*
 * writes a block of 'len' bytes of data to the sampler from
 * the supplied buffer. The user has to take care that the buffer
 * is sufficiently large and that not too much data gets sent
 * (does the Sx000 have memory protection? Probably not..)
 *
 * INPUTS:
 * 	handle: LHANDLE to opened LAKAI device
 * 	buffer: buffer to read data to be transferred from
 * 	len: number of bytes to be transferred
 *
 * RETURNS:
 * 	number of bytes that have actually been transferred,
 * 	0 if an error occurred.
 */
long lakai_putdatablock(LHANDLE handle, char *buffer, long len);


/*
 * (internal?) low-level functions:
 *
 * (RequestErrorSense?) - check if the last operation yielded an error..
 * (ScanForSampler?) - check /dev/sg0../dev/sg15 for an Akai S1000
 *
 * IdentifyDevice(): check model number of SCSI device, ...
 *
 * TestUnitReady(): see if the unit is able to answer requests
 *
 * SetMidiMode(..): sets to either "normal" (MIDI over serial cable), 
 * SCSI_MIDI (MIDI over SCSI) or SCSI_BULK (bulk data transfer over SCSI).
 *
 * GetNumAvailable(): get the number of bytes waiting to be received from sampler
 * 
 * GetDataBlock(): read a block of data from sampler using SCSI_BULK mode
 *
 * PutDataBlock(): write a block of data to the sampler using SCSI_BULK mode
 *
 * ### for GetDataBlock() and PutDataBlock(): In case of large transfers, might
 * make sense to give the user feedback (progress indicator). This requires a callback
 * mechanism, or asynchronous operation (thread?).. todo, later..
 * 
 * I will not support bulk transfers in non-bulk mode, that's just too plain slow
 * (even using MIDI over SCSI, it comes in 120-byte packages, producing far too 
 * much overhead - I got around 10 - 20 kBytes/sec this way..)
 * 
 * Design question: How much work do I want to put into the lib/the client?
 * That is, who should have to process resident sample/program lists, the lib
 * or the client?
 * 1) If it's the lib, the client program can be kept very short&simple; but it
 * might not be flexible enough this way.
 * 2) If it's in the client, it has got more work to do, the lib becomes smaller,
 * and the client is more flexible.
 *
 * Right now I think 2) is better suited. That would mean the lib does not do
 * much more but:
 * - Init functions, TestUnitReady
 * - bulk send/receive
 * - SCSI_MIDI receiving of resident program/sample/etc lists without parsing stuff
 *
 * The client just requests this data and has to do things like name translation
 * itself..really? Doesn't seem to make sense..
 * 
 *
 *
 *
 * HIGH-LEVEL FUNCTIONS: (directly related to the corresponding MIDI SysEx messages)
 * R: data that is only read from sampler
 * RW: data that can be both read and written)
 *
 * All of these functions should get their data in MIDI-over-SCSI mode, so they need
 * to make sure the sampler is in that mode before talking to it.
 */

#endif

/*
 * get the current status from the S1000; includes information such as current
 * number of used programs, samples...
 *
 * INPUTS:
 * 	LHANDLE handle: handle to previously opened device special file
 * 	LAKAI_STATUS: pointer to LAKAI_STATUS structure to fill in from sampler
 *
 * RETURNS:
 */
int lakai_get_status_report(LHANDLE handle, LakaiStatus *ls);


/*
 * Retrieves the current name list of resident program.
 *
 * INPUTS:
 * 	LHANDLE handle: handle to previously opened Lakai device
 * 	LakaiProgramList *lp: pointer to struct which will be filled in with
 * 	program names data
 *
 * OUTPUTS:
 * 	lp->prognames[] is filled with the resident program name list
 * 	
 * RETURNS:
 * 	int: number of program names that have been received from sampler
 */
int lakai_get_program_list(LHANDLE handle, LakaiProgramList *lp);


/*
 * free a program list that was previously allocated & retrieved through
 * lakai_get_programlist().
 *
 * INPUTS:
 * 	LakaiProgramList *lp: pointer to LakaiProgramList struct
 *
 * OUTPUTS:
 * 	All program name memory allocated in the LakaiProgramList struct will
 * 	be freed.
 *
 * RETURNS:
 * 	-
 */
void lakai_free_program_list(LakaiProgramList *lp);
	

/*
 * Retrieves the current name list of resident samples.
 *
 * INPUTS:
 * 	LHANDLE handle: handle to previously opened Lakai device
 * 	LakaiSampleList *ls: pointer to struct which will be filled in with
 * 	sample names data
 *
 * OUTPUTS:
 * 	lp->sampnames[] is filled with the resident sample name list
 * 	
 * RETURNS:
 * 	int: number of sample names that have been received from sampler
 */
int lakai_get_sample_list(LHANDLE handle, LakaiSampleList *ls);
	

/*
 * free a sample name list that was previously allocated & retrieved through
 * lakai_get_samplelist().
 *
 * INPUTS:
 * 	LakaiSampleList *lp: pointer to LakaiSampleList struct
 *
 * OUTPUTS:
 * 	All sample name memory allocated in the LakaiSampleList struct will
 * 	be freed.
 *
 * RETURNS:
 * 	-
 */

void lakai_free_sample_list(LakaiSampleList *ls);



/*
 * gets the program common data for one program. This is a fixed data
 * structure (might differ between old and more recent sampler models - S1000
 * seems to have a length of 150 bytes, while S2000/2800 etc have 192 bytes here.
 * However, if I send an S1000 "RPDATA" command, I should only look at the first 150
 * bytes.
 * If the requested program does not exist, returns an error.
 *
 * INPUTS:
 * 	LHANDLE handle: handle to previously opened Lakai device
 * 	int prognum: number of program whose common data to retrieve
 *
 * OUTPUTS:
 * 	stores the program common data for this program into lpr
 *
 * RETURNS:
 * 	-1 if an error occurred (program does not exist etc),
 * 	number of bytes transferred otherwise (150/192)
 */
int lakai_get_program(LHANDLE handle, int prognum, unsigned char *data);



/*
 * gets the keygroup data for a keygroup in a program. This is a fixed data structure
 * (might differ between old and more recent sampler models?).
 * If the keygroup does not exist, returns an error.
 *
 * INPUTS:
 * 	LHANDLE handle: handle to previously opened device special file
 * 	int prognum: number of program in whose keygroups we're interested
 * 	int keygroupnum: number of keygroup whose data to retrieve
 *
 * OUTPUTS:
 * 	stores the keygroup data for this keygroup into lk
 *
 * RETURNS:
 * 	-1 if an error occurred (keygroup does not exist etc)
 * 	number of bytes transferred otherwise
 */
int lakai_get_keygroup(LHANDLE handle, int prognum, int keygroupnum, unsigned char *data);


/*
 * gets the sample header data for one sample header. This is a fixed
 * data structure (might differ between old and more recent sampler models?).
 * If the sample header does not exist, returns an error.
 *
 * INPUTS:
 * 	LHANDLE handle: handle to previously opened device special file
 * 	int headernum: number of sample header whose common data to retrieve
 *
 * OUTPUTS:
 * 	stores the sample header data for this sampler header into lsh
 *
 * RETURNS:
 * 	-1 if an error occurred (sample header does not exist etc)
 * 	number of bytes transferred otherwise
 */
int lakai_get_sample_header(LHANDLE handle, int samplenum, unsigned char *data);


/*
 * sends a sample header structure of 'len' bytes from the supplied buffer
 * to the sampler. The user has to take care that the buffer
 * is prefilled with data. 
 *
 * INPUTS:
 * 	handle: LHANDLE to opened LAKAI device
 * 	samplenum: Number of the sample to overwrite/create. If the number is
 * 	higher than the highest number of a resident sample header, a new
 * 	sample entry is being created.
 * 	len: Length of sample header data that follows
 * 	buffer: buffer that holds the sample header data to be transferred
 *
 * RETURNS:
 * 	number of bytes that have actually been transferred,
 * 	0 if an error occurred.
 */
int lakai_put_sample_header(LHANDLE handle, int samplenum, unsigned char *data, int len);


/*
 * Deletes the program "prognum". If program does not exist, returns an error.
 * Otherwise, that program and all its keygroups will be deleted.
 * Of course this does not delete any sample header or sample data.
 *
 * INPUTS:
 * 	LHANDLE handle: handle to previously opened device special file
 * 	int prognum: number of program to be deleted
 *
 * OUTPUTS:
 *    -
 *
 * RETURNS:
 * 	-1 if an error occurred (program does not exist etc)
 * 	0 (or program number?) otherwise
 */
int lakai_delete_program(LHANDLE handle, int prognum);


/*
 * Deletes the keygroup "kgrpnum" in the program "prognum". If the keygroup 
 * in this program (or that program itself) does not exist, returns an error.
 * Otherwise, the keygroup will be deleted.
 * Of course this does not delete any sample header or sample data.
 *
 * INPUTS:
 * 	LHANDLE handle: handle to previously opened device special file
 * 	int prognum: number of program 
 * 	int kgrpnum: Number of keygroup to be deleted
 *
 * OUTPUTS:
 *    -
 *
 * RETURNS:
 * 	-1 if an error occurred (program does not exist etc)
 * 	0 (?) otherwise
 */
int lakai_delete_keygroup(LHANDLE handle, int prognum, int kgrpnum);


/*
 * Deletes the sample "samplenum". If that sample does not exist, 
 * returns an error.  Otherwise, the sample and its sample header data
 * will be deleted.
 * Programs using this sample will be unaffected, but this may create
 * unpleasant situations (to be checked..)
 *
 * INPUTS:
 * 	LHANDLE handle: handle to previously opened device special file
 * 	int samplenum: number of sample to delete
 *
 * OUTPUTS:
 *    -
 *
 * RETURNS:
 * 	-1 if an error occurred (sample does not exist etc)
 * 	0 (?) otherwise
 */
int lakai_delete_sample(LHANDLE handle, int samplenum);

/*
 * Retrieves the "miscellaneous" data from the sampler. Contents of this
 * data field seems to depend heavily on the sampler model used.
 *
 * INPUTS:
 * 	LHANDLE handle: handle to previously opened device special file
 * 	data: pointer to a unsigned char buffer that can hold at least TODO xxx
 * 	bytes.
 *
 * OUTPUTS:
 *    The buffer is filled with the misc data
 *
 * RETURNS:
 * 	int res: Number of bytes transferred by the call
 *
 */
int lakai_get_miscdata(LHANDLE handle, unsigned char *data);

/*
 * Replaces an existing program or creates a new program in the sampler.
 * From the SysEx docs: "If the program number is above the highest existing 
 * program number, a new program will be created (if sufficient blocks are 
 * free - one for the program common block and one for each keygroup as 
 * specified by the parameter GROUPS). The created program will have dummy 
 * keygroups with unspecified data; the appropriate number of keygroup data
 * messages should be given immediately. If the program name in data is
 * the same as that of any existing program, that program will be deleted
 * first. If the program number is of an existing program, the existing
 * data will be replaced but the parameter GROUPS must be correct. This 
 * allows complete freedem to change parameters - the use of a duplicate
 * program name should be avoided. If either error situation occurs, an error
 * message will be given, otherwise an OK message will be given".
 *
 * gets the program common data for one program. This is a fixed data
 * structure (might differ between old and more recent sampler models - S1000
 * seems to have a length of 150 bytes, while S2000/2800 etc have 192 bytes here.
 * However, if I send an S1000 "RPDATA" command, I should only look at the first 150
 * bytes.
 * If the requested program does not exist, returns an error.
 *
 * INPUTS:
 * 	LHANDLE handle: handle to previously opened Lakai device
 * 	int prognum: number of program to overwrite/create
 * 	int len: Length of the data block following in "data"
 * 	unsigned char *data: Pointer to program data
 *
 * OUTPUTS:
 * 	-
 *
 * RETURNS:
 * 	-1 if an error occurred (no mem, GROUPS does not match etc),
 * 	0 otherwise
 */
int lakai_put_program(LHANDLE handle, int prognum, unsigned char *data, int len);


/*
 * Replaces an existing keygroup in a program, or creates a new keygroup.
 * From The SysEx docs: "In the case of transmitting to the S1000, if
 * the keygroup number is above the highest existing keygroup number, a new
 * keygroup will be created if a block is free, otherwise the existing 
 * keygroup will be replaced. The use of program number 255 is a special 
 * case where the keygroup data will be installed in the program previously 
 * created. This avoids the need to read the program list to find out what 
 * number was allocated to that program. If there are no free blocks for a 
 * new keygroup, an error message will be given".
 * 
 * INPUTS:
 * 	LHANDLE handle: handle to previously opened Lakai device
 * 	int prognum: number of program iin which to overwrite/create a keygroup
 * 	int kgrpnum: number of keygroup to create/overwrite
 * 	unsigned char *data: Pointer to keygroup data
 *
 * OUTPUTS:
 * 	-
 *
 * RETURNS:
 * 	-1 if an error occurred (no mem, GROUPS does not match etc),
 * 	0 otherwise
 */
int lakai_put_keygroup(LHANDLE handle, int prognum, int kgrpnum, unsigned char *data, int len);


/*
 * sends a sample of 'len' bytes of data from the supplied buffer
 * to the sampler. The user has to take care that the buffer
 * is prefilled with data. Also, the user has to create a new sample
 * entry first before sending data into it.
 *
 * INPUTS:
 * 	handle: LHANDLE to opened LAKAI device
 * 	buffer: buffer that holds the data to be transferred
 * 	len: number of bytes to be transferred
 *
 * RETURNS:
 * 	number of bytes that have actually been transferred,
 * 	0 if an error occurred.
 */
long lakai_put_sample(LHANDLE handle, int samplenum, char *buffer, long locat, long len);


#if 0
/*
 * S1000/900 (original sampler):
 * (F) = returns fixed number of bytes, (V) returns variable number of bytes
 * lakai_get_status_report()  (F)
 * lakai_get_program_names()  (V)
 * lakai_get_sample_names()   (V)
 * lakai_get_program()        (V...depends on # of keygroups in program..?)
 * lakai_get_keygroup()       (F)
 * lakai_get_sample_header()  (F)
 * lakai_get_drum_settings()  (F)  (later..)
 * lakai_get_misc_data()      (F)
 *
 * Note: RSPACK (Request Sample Data Packet(s)) and ASPACK (Accept Sample Data
 * Packet(s)) are not implemented because we use the faster way of directly
 * transferring blocks of memory from/to the sampler, bypassing the MIDI protocol.
 * 
 * lakai_put_program()
 * lakai_put_keygroup()
 * lakai_put_sample_header()
 * lakai_put_drum_settings()   (later..)
 * lakai_put_misc_data()
 *
 * lakai_delete_program()   (also deletes the associated keygroups)
 * lakai_delete_keygroup(programnum, keygroupnum)
 * lakai_delete_sample()   (also deletes the associated sampler header data)
 * lakai_set_exclusive()   (do I really need this?)
 *
 * S2000/S3000XL/S3200XL:
 * lakai_get_multi()
 * lakai_put_multi()
 *
 * S2800/S3000/S3200:
 * ???
 *
 * S5000/S6000:
 * ?? (need more info)
 *
 * yet unsorted:
 * FX/reverb bytes (R/W)
 * CueList bytes (R/W)
 * TakeList entries (R/W)
 * VolumeList entry (R/W)
 * Harddisk Directory entry (R/W)
 */


 /*
  * design question: when returning a MIDI message like from lakai_get_sample_names(),
  * should I strip off the SysEx header/EOX myself or pass this all over to the app?
  * Right now I'm for passing it all over to the app..
  */


/*
 * More new thoughts, 31.10.2002 :-)
 *
 * open function: can handle up to 16 simultaneous connections. Returns as "handle"
 * an index into an array like this:
 * #define MAX_LHANDLES 16
 * typedef struct
 * {
 *   int fd;
 *   ...possibly more maintenance data goes here..
 * } Lakai_handle;
 * Lakai_handle lhandles[MAX_LHANDLES];
 *
 *
 * Error handling: How does libsndfile do this?
 * Like this:
 * "sf_error_str () returns the current error message to the caller in the
 * string buffer provided."
 *
 * int      sf_error_str   (SNDFILE *sndfile, char* str, size_t len) ;
 *
 * The error string list looks like this:
 * typedef struct
 *	{  int   error ;
 *		char  *str ;
 *	} ErrorStruct ;
 *
 *	static
 *	ErrorStruct SndfileErrors [] =
 *	{
 *		{  SFE_NO_ERROR, 	"No Error." },
 *		{  SFE_BAD_FILE,	"File does not exist or is not a regular file (possibly a pipe?)." },
 *		{  SFE_BAD_FILE_READ    , "File exists but no data could be read." },
 * ...
 * }
 *
 *
 *
 * Anyway, if a function runs into an error, it will usually give a return
 * code of NULL or 0 and set a "lakai_err" variable (either a static one..
 * no, not thread-safe) or a call-by-ref variable of the caller.
 * The call can then do with this whatever it wants, like ignore, print
 * error string to stdout, show an error window or whatever..
 * Example (as seen from user perspective):
 * ret = lakai_xxxx(params, ..., &err);
 * if (!ret)
 *   fprintf(stderr, "Error while reading: %s\n", lakai_errstr(err));
 *   exit(5);
 *
 *  -----------------------------------------------
 *  Another q: Do I need a mostly centralized "do the actual scsi command" function?
 *  If some kind of set-up of data structures is repeated in several locations over and
 *  over, this makes sense.
 *  What parameter would this function take then?
 *
 */

#endif
