/*
**
**	$Id: capi-usr.c,v 1.876 1996/08/13 03:16:27 chris Exp $
**
**	$Filename: arch/amigados/capi-usr.c $
**	$Author: chris $
**	$Portability: Amiga $
**
**	CAPI-User interface to the Amiga capi20.device. This interface makes the
**	application's call to the CAPI driver independant from the operating
**	system running the CAPI. For every supported OS there is a different
**	CAPI-User.c interface.
**
**	No warranty. Use at your own risk.
**
**	COPYRIGHT (C) 1993-1996 BY RELOG AG, ZUERICH. ALL RIGHTS RESERVED.
**	NO PART OF THIS SOFTWARE MAY BE COPIED, REPRODUCED, OR TRANSMITTED
**	IN ANY FORM OR BY ANY MEANS,  WITHOUT THE PRIOR WRITTEN PERMISSION
**	OF RELOG AG.
**
*/

#include "os.h"
#include "capi-usr.h"
#include "capibase.h"

#include <string.h>

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <exec/semaphores.h>
#include <proto/exec.h>

#define ObtainSemaphore(x)
#define ReleaseSemaphore(x)


/*
**	Folgende Struktur wird für jedes CAPI_REGISTER erzeugt:
*/
struct UserApp
{
	struct Node				Node;		/* Verkettung */
	struct SignalSemaphore	Lock;		/* Schützt IO */
	struct IOExtCAPI		IO;
};


/*
**	Statischer IO-Request für Applikationsunabhängige Kommandos
*/
static struct UserApp		g_app;
static BOOL					initialized;


/*
**	Liste für Mapping ApplID->UserApp
*/
static struct List			applist;


/*
**	First-Time-Initialisierung
*/
static VOID FirstTimeInit( VOID )
{
	if (!initialized)
	{
		NewList( &applist );
		InitSemaphore( &g_app.Lock );
		initialized = TRUE;
	}
}


static VOID AddUserApp( struct UserApp *app )
{
	Forbid();
	AddTail( (struct List *)&applist, &app->Node );
	Permit();
}


static VOID RemoveUserApp( struct UserApp *app )
{
	Forbid();
	Remove( &app->Node );
	Permit();
}


static struct UserApp *FindUserApp( unsigned short applID )
{
	struct UserApp *app;

	if (applID == 0)	/* 0 ist immer ungültig */
		return NULL;

	Forbid();
	DOLIST( (IC_LIST *)&applist, app )
	{
		if ((unsigned short)app->IO.IOCAPI.io_Unit == applID)
		{
			Permit();
			return app;
		}
	}
	Permit();

	return NULL;
}



/****** U_CAPI_REGISTER *****************************************************
*
*   NAME
*       U_CAPI_REGISTER -- Register an application with the CAPI
*
*   SYNOPSIS
*       result = U_CAPI_REGISTER( maxLogicalConnection, maxBDataBlocks,
*                                 maxBDataLen, pApplId );
*
*       unsigned U_CAPI_REGISTER( unsigned, unsigned,
*                                 unsigned, unsigned short * );
*
*   FUNCTION
*       This is the operation the application uses to report its presence
*       to the CAPI. By passing the four first parameters, the application
*       describes its needs.
*
*   INPUTS
*       maxLogicalConnection - Maximum number of logical connections
*       maxBDataBlocks       - Number of data blocks available simultaneously
*       maxBDataLen          - Maximum size of a data block
*       pApplId              - Pointer to the location where CAPI should place
*                              the ID of the newly registered application.
*                              A valid ID will never be zero.
*
*   RESULT
*       0 if successful, or parameter info class 0x10xx
*
*   SEE ALSO
*       U_CAPI_RELEASE()
*
****************************************************************************/

unsigned
U_CAPI_REGISTER( unsigned maxLogicalConnection, unsigned maxBDataBlocks, unsigned maxBDataLen,
					unsigned short *pApplID )
{
	struct UserApp	*app;
	unsigned		result;

	FirstTimeInit();


	/*
	**	Neuen IO-Request für diese Applikation erzeugen
	*/
	if ((app = AllocVec( sizeof( *app ), MEMF_CLEAR )) != NULL)
	{
		InitSemaphore( &app->Lock );

		app->IO.Level3Cnt	= maxLogicalConnection;
		app->IO.DataBlkCnt	= maxBDataBlocks;
		app->IO.DataBlkLen	= maxBDataLen;
		app->IO.CAPIResult	= CAPI_10_CAPI_NOT_INSTALLED;

/*		kprintf( "U_CAPI_REGISTER(): Level3Cnt=%ld, DataBlkCnt=%ld, DataBlkLen=%ld\n",
			(long)app->IO.Level3Cnt, (long)app->IO.DataBlkCnt, (long)app->IO.DataBlkLen ); */

		if (OpenDevice( CAPINAME, 0, (struct IORequest *)&app->IO, NULL) == 0)
		{
			AddUserApp( app );
			*pApplID = (unsigned short)app->IO.IOCAPI.io_Unit;
			result = CAPI_00_REQUEST_ACCEPTED;
		}
		else result = app->IO.CAPIResult;
	}
	else result = CAPI_10_OS_RESOURCE_ERROR;

	if (result != CAPI_00_REQUEST_ACCEPTED)
	{
		U_CAPI_RELEASE( (unsigned short)app );
		*pApplID = 0;
	}

	return result;
}


/****** U_CAPI_RELEASE ******************************************************
*
*   NAME
*       U_CAPI_RELEASE -- Terminate access to the CAPI
*
*   SYNOPSIS
*       result = U_CAPI_RELEASE( applId );
*
*       unsigned U_CAPI_RELEASE( unsigned short );
*
*   FUNCTION
*       The application uses this operation to log off from CAPI.
*       CAPI will release all resources that have been allocated.
*
*   INPUTS
*       applId - Application ID as received from CAPI_REGISTER(),
*                or zero for no action.
*
*   RESULT
*       0 if successful, or parameter info class 0x11xx
*
*   SEE ALSO
*       U_CAPI_REGISTER()
*
****************************************************************************/

unsigned
U_CAPI_RELEASE( unsigned short applID )
{
	struct UserApp *app;

	FirstTimeInit();

	if ((app = FindUserApp( applID )) != NULL)
	{
		RemoveUserApp( app );

		if ((ULONG)app->IO.IOCAPI.io_Device > 0)
			CloseDevice( (struct IORequest *)&app->IO );

		FreeVec( app );
/*		app = NULL; */

		return CAPI_00_REQUEST_ACCEPTED;
	}

	return CAPI_11_ILLEGAL_APPLID;
}


/****** U_CAPI_PUT_MESSAGE **************************************************
*
*   NAME
*       U_CAPI_PUT_MESSAGE -- Send a message from the application to the CAPI
*
*   SYNOPSIS
*       result = U_CAPI_PUT_MESSAGE( applId, pCAPIMessage );
*
*       unsigned U_CAPI_PUT_MESSAGE( unsigned short, CAPI_MESSAGE * );
*
*   FUNCTION
*       With this operation the application transfers a message to the CAPI.
*       Under OS/2, the message memory area must not cross a 64 kByte
*       boundary in the flat address space because the DLL may convert the
*       passed flat pointer to a 16:16 bit segmented pointer.
*
*   INPUTS
*       applId       - Application ID as received from U_CAPI_REGISTER()
*       pCAPIMessage - Pointer to the message
*
*   RESULT
*       0 if successful, or parameter info class 0x11xx
*
*   SEE ALSO
*       U_CAPI_GET_MESSAGE()
*
****************************************************************************/

unsigned
U_CAPI_PUT_MESSAGE( unsigned short applID, CAPI_MESSAGE *pMessage )
{
	struct UserApp *app;
	unsigned result = CAPI_10_CAPI_NOT_INSTALLED;

	if ((app = FindUserApp( applID )) != NULL)
	{
		ObtainSemaphore( &app->Lock );
		app->IO.IOCAPI.io_Command = CAPIIOCMD_PUT_MESSAGE;
		app->IO.IOCAPI.io_Data = pMessage;
		(void)DoIO( (struct IORequest *)&app->IO );
		result = app->IO.CAPIResult;
		ReleaseSemaphore( &app->Lock );
	}

	return result;
}


/****** U_CAPI_GET_MESSAGE ***************************************************
*
*   NAME
*       U_CAPI_GET_MESSAGE -- Get a message from the CAPI
*
*   SYNOPSIS
*       result = U_CAPI_GET_MESSAGE( applId, ppCAPIMessage );
*
*       unsigned U_CAPI_GET_MESSAGE( unsigned short, CAPI_MESSAGE ** );
*
*   FUNCTION
*       With this operation the application retrieves a message from the CAPI.
*       The application can only retrieve those messages intended for the
*       stipulated application identification number. If there is no message
*       waiting for retrieval, the function returns immediately with an
*       error code.
*
*   INPUTS
*       applId        - Application ID as received from U_CAPI_REGISTER()
*       ppCAPIMessage - Pointer to the memory location where CAPI should
*                       place a pointer to the data of the retrieved message
*
*   RESULT
*       0 if successful, or parameter info class 0x11xx
*
*   SEE ALSO
*       U_CAPI_PUT_MESSAGE()
*
****************************************************************************/

unsigned
U_CAPI_GET_MESSAGE( unsigned short applID, CAPI_MESSAGE **ppMessage )
{
	struct UserApp *app;
	unsigned result = CAPI_10_CAPI_NOT_INSTALLED;

	if ((app = FindUserApp( applID )) != NULL)
	{
		ObtainSemaphore( &app->Lock );
		app->IO.IOCAPI.io_Command = CAPIIOCMD_GET_MESSAGE;
		(void)DoIO( (struct IORequest *)&app->IO );

		if ((result = app->IO.CAPIResult) == 0)
			*ppMessage = (CAPI_MESSAGE *)app->IO.IOCAPI.io_Actual;
		else
			*ppMessage = NULL;

		ReleaseSemaphore( &app->Lock );
	}

	return result;
}


/****** U_CAPI_SET_SIGNAL ***************************************************
*
*   NAME
*       U_CAPI_SET_SIGNAL -- Install a signalling mechanism
*
*   SYNOPSIS
*       success = U_CAPI_SET_SIGNAL( applId, callback, para );
*
*       unsigned short U_CAPI_SET_SIGNAL( unsigned short, unsigned long,
*                                         unsigned long );
*
*   FUNCTION
*       This operation is used by the application to install a mechanism
*       which signals the application the availability of a message.
*
*       In that case each time CAPI places a message in the application's
*       message queue, the specified callback function is called.
*
*       By issuing this function call with a callback value of 0 the
*       signalling mechanism is deactivated.
*
*       The called function must have the following format:
*       void callback( unsigned short applID, unsigned long para );
*
*   INPUTS
*       applId   - ID of Application posting the request
*       callback - Pointer to the function to be called
*       para     - Parameter passed to callback function (application's use)
*
*   RESULT
*       success - 0 if OK, or parameter info class 11 upon failure.
*
*   SEE ALSO
*
****************************************************************************/

unsigned short
U_CAPI_SET_SIGNAL( unsigned short applID, unsigned long callback, unsigned long para )
{
	struct UserApp *app;

	if ((app = FindUserApp( applID )) != NULL)
	{
		ObtainSemaphore( &app->Lock );
		app->IO.IOCAPI.io_Command = CAPIIOCMD_SET_SIGNAL;
		app->IO.IOCAPI.io_Data = (APTR)callback;
		app->IO.IOCAPI.io_Offset = para;
		(void)DoIO( (struct IORequest *)&app->IO );
		ReleaseSemaphore( &app->Lock );

		return CAPI_00_REQUEST_ACCEPTED;
	}

	return CAPI_11_ILLEGAL_APPLID;
}


/*
**	Capi-IO mit statischem IO-Request durchführen. Die Semaphore g_app.Lock
**	muss vor dem Aufruf schon gelockt sein, und wird hier wieder freigegeben!
*/
static unsigned DoCAPIIO( UWORD command )
{
	unsigned result;

	g_app.IO.IOCAPI.io_Command = command;

	/*
	**	Wir öffnen das device mit flags == 1, was bedeutet, dass kein CAPI_REGISTER
	**	gemacht wird.
	*/
	if (OpenDevice( CAPINAME, 0, (struct IORequest *)&g_app.IO, 1) == 0)
	{
		(void)DoIO( (struct IORequest *)&g_app.IO );
		result = g_app.IO.CAPIResult;
		CloseDevice( (struct IORequest *)&g_app.IO );
	}
	else result = CAPI_10_CAPI_NOT_INSTALLED;

	ReleaseSemaphore( &g_app.Lock );
	return result;
}


/****** U_CAPI_GET_MANUFACTURER *********************************************
*
*   NAME
*       U_CAPI_GET_MANUFACTURER -- Get manufacturer ID of the CAPI driver
*
*   SYNOPSIS
*       U_CAPI_GET_MANUFACTURER( SzBuffer );
*
*       void U_CAPI_GET_MANUFACTURER( char * );
*
*   FUNCTION
*       With this operatrion the application determines the manufacturer
*       identification of the installed CAPI driver. SzBuffer on call is
*       a pointer to a buffer of 64 bytes. CAPI copies the identification
*       string, coded as a zero-terminated ASCII string, to this buffer.
*
*   INPUTS
*       SzBuffer - Pointer to a buffer of 64 bytes
*
*   RESULT
*       none
*
*   SEE ALSO
*       U_CAPI_GET_VERSION(), U_CAPI_GET_SERIAL_NUMBER()
*
****************************************************************************/

void
U_CAPI_GET_MANUFACTURER( char *SzBuffer )
{
	FirstTimeInit();

	ObtainSemaphore( &g_app.Lock );
	g_app.IO.IOCAPI.io_Data = SzBuffer;
	(void)DoCAPIIO( CAPIIOCMD_GET_MANUFACTURER );
}


/****** U_CAPI_GET_VERSION **************************************************
*
*   NAME
*       U_CAPI_GET_VERSION -- Get CAPI version numbers
*
*   SYNOPSIS
*       result = U_CAPI_GET_VERSION( pVersionNumbers )
*
*       void U_CAPI_GET_VERSION( unsigned long * );
*
*   FUNCTION
*       With this function the appplication determines the version of the
*       CAPI as well as an internal version number.
*
*   INPUTS
*       pVersionNumbers - Pointer to an array of four U32s receiving
*                         the following four version numbers:
*
*                         1st: CAPI major version number (currently 2)
*                         2nd: CAPI minor version number (currently 0)
*                         3rd: Manufacturer specific major version number
*                         4th: Manufacturer specific minor version number
*
*   RESULT
*       none
*
*   SEE ALSO
*       U_CAPI_GET_MANUFACTURER(), U_CAPI_GET_SERIAL_NUMBER()
*
****************************************************************************/

void
U_CAPI_GET_VERSION( unsigned long *pVersionNumbers )
{
	FirstTimeInit();

	ObtainSemaphore( &g_app.Lock );
	g_app.IO.IOCAPI.io_Data = pVersionNumbers;
	(void)DoCAPIIO( CAPIIOCMD_GET_VERSION );
}


/****** U_CAPI_GET_SERIAL_NUMBER ********************************************
*
*   NAME
*       U_CAPI_GET_SERIAL_NUMBER -- Get the serial number of the CAPI
*
*   SYNOPSIS
*       result = U_CAPI_GET_SERIAL_NUMBER( SzBuffer );
*
*       void U_CAPI_GET_SERIAL_NUMBER( char * );
*
*   FUNCTION
*       With this operation the application determines the (optional) serial
*       number of the CAPI. SzBuffer on call is a pointer to a buffer of 8
*       bytes. CAPI copies the serial number string to this buffer. The serial
*       number, coded as a zero terminated ASCII string, represents a seven
*       digit number after the function has returned.
*
*   INPUTS
*       SzBuffer - Pointer to a buffer of 8 bytes
*
*   RESULT
*       none
*
*   SEE ALSO
*       U_CAPI_GET_VERSION(), U_CAPI_GET_MANUFACTURER()
*
****************************************************************************/

void
U_CAPI_GET_SERIAL_NUMBER( char *SzBuffer )
{
	FirstTimeInit();

	ObtainSemaphore( &g_app.Lock );
	g_app.IO.IOCAPI.io_Data = SzBuffer;
	(void)DoCAPIIO( CAPIIOCMD_GET_SERIAL_NUMBER );
}


/****** U_CAPI_GET_PROFILE **************************************************
*
*   NAME
*       U_CAPI_GET_PROFILE -- Get the profile of a controller
*
*   SYNOPSIS
*       result = U_CAPI_GET_PROFILE( SzBuffer, CtrlNr );
*
*       unsigned U_CAPI_GET_PROFILE( CAPI_PROFILE *, unsigned short );
*
*   FUNCTION
*       The application uses this function to get the capabilities from CAPI.
*       SzBuffer on call is a pointer to a buffer of 64 bytes. In this buffer
*       CAPI copies information about implemented features, number of
*       controllers and supported protocols. CtrlNr contains the controller
*       number (bit 0..6) for which this information is requested.
*
*   INPUTS
*       SzBuffer - Pointer to a buffer receiving the CAPI_PROFILE structure.
*       CtrlNr   - Number of controller. If 0, only the number of installed
*                  controllers is given to the application.
*
*   RESULT
*       0 if successful, or parameter info class 0x11xx
*
*   SEE ALSO
*
****************************************************************************/

unsigned
U_CAPI_GET_PROFILE( CAPI_PROFILE *SzBuffer, unsigned short CtrlNr )
{
	FirstTimeInit();

	ObtainSemaphore( &g_app.Lock );
	g_app.IO.IOCAPI.io_Data = SzBuffer;
	g_app.IO.IOCAPI.io_Offset = CtrlNr;
	return DoCAPIIO( CAPIIOCMD_GET_PROFILE );
}


/****** U_CAPI_INSTALLED ****************************************************
*
*   NAME
*       U_CAPI_INSTALLED -- Find out if a CAPI driver is installed
*
*   SYNOPSIS
*       result = U_CAPI_INSTALLED();
*
*       unsigned U_CAPI_INSTALLED( void );
*
*   FUNCTION
*       The application can use this function to find out if the CAPI
*       driver is installed.
*
*   INPUTS
*       none
*
*   RESULT
*       0 if capi is installed, non-zero otherwise.
*
*   SEE ALSO
*
****************************************************************************/

unsigned
U_CAPI_INSTALLED( void )
{
	CAPI_PROFILE profile;
	return U_CAPI_GET_PROFILE( &profile, 0 );
}



/* End of CAPI-Usr.c */
