/* ``The contents of this file are subject to the Erlang Public License,
 * Version 1.1, (the "License"); you may not use this file except in
 * compliance with the License. You should have received a copy of the
 * Erlang Public License along with this software. If not, it can be
 * retrieved via the world wide web at http://www.erlang.org/.
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
 * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
 * AB. All Rights Reserved.''
 * 
 *     $Id$
 */
/*
 * OLD, OBSOLETE include file for erlang driver writers.
 * New drivers should use erl_driver.h instead.
 */

#ifndef __DRIVER_H__
#define __DRIVER_H__

#include <stdlib.h>
#include "driver_int.h"

#undef _ANSI_ARGS_
#undef CONST

#if ((defined(__STDC__) || defined(SABER)) && !defined(NO_PROTOTYPE)) || defined(__cplusplus) || defined(USE_PROTOTYPE)
#   define _USING_PROTOTYPES_ 1
#   define _ANSI_ARGS_(x)	x
#   define CONST const
#else
#   define _ANSI_ARGS_(x)	()
#   define CONST
#endif

#ifdef __cplusplus
#   define EXTERN extern "C"
#else
#   define EXTERN extern
#endif

/* Values for mode arg to driver_select() */

#define DO_READ	 (1 << 0)
#define DO_WRITE (1 << 1)

/* Flags for set_port_control_flags() */
#define PORT_CONTROL_FLAG_BINARY	1
#define PORT_CONTROL_FLAG_HEAVY		2

/* This macro is used to name a dynamic driver's init function in */
/* a way that doesn't lead to conflicts. This is crucial when using */
/* operating systems that has one namespace for all symbols */
/* (e.g. VxWorks). Example: if you have an dynamic driver C source */
/* file named echo_drv.c, you use the macro like this: */
/* int DRIVER_INIT(echo_drv)(void *handle) */
#if defined(VXWORKS)
#  define DRIVER_INIT(DRIVER_NAME) DRIVER_NAME  ## _init
#elif defined(__WIN32__)
#  define DRIVER_INIT(DRIVER_NAME) __declspec(dllexport) driver_init
#else 
#  define DRIVER_INIT(DRIVER_NAME)  driver_init
#endif

typedef int (*F_PTR)();    /* a function pointer */
typedef long (*L_PTR)();   /* pointer to a function returning long */

extern int null_func();

/* This structure MUST match Binary in global.h exactly!!! */
typedef struct driver_binary {
    int orig_size;        /* total length of binary */
    char orig_bytes[1];   /* the data (char instead of byte!) */
} DriverBinary;

typedef struct {
    int vsize;     /* length of vectors */
    int size;      /* total size in bytes */
    SysIOVec* iov;
    DriverBinary**  binv;
} ErlIOVec;

/*
 * OLD, OBSOLETE driver entry structure.
 */

typedef struct driver_entry {
    F_PTR init;          /* called at system start up (no args) */
    L_PTR start;         /* called when some one does an open_port
			 args: port, command (nul-terminated),
			 additional/alternate args for fd/vanilla/spawn driver.
			 return value -1 means failure, other
			 is saved and passed to the other funcs */
    F_PTR stop;          /* called when port is closed, and when the
			    emulator is halted - arg: start_return */
    F_PTR output;	 /* called when we have output from erlang to the port
		         args: start_return, buf, buflen */
    F_PTR ready_input;   /* called when we have input from one of the driver's
			 file descriptors - args: start_return, fd */
    F_PTR ready_output;  /* called when output is possible to one of the driver's
			 file descriptors - args: start_return, fd */
    char *driver_name;   /* name supplied as {driver,Name,Args} to open_port */

    F_PTR finish;        /* called before unloading (DYNAMIC DRIVERS ONLY) */
    void *handle;        /* file handle             (DYNAMIC DRIVERS ONLY) */
    F_PTR control;	 /* "ioctl" for drivers (invoked by port_command/3) */
    F_PTR timeout;       /* Reserved */
    F_PTR outputv;       /* Reserved */
    F_PTR ready_async;   /* Completion routine for driver_async */
    F_PTR padding1[3];   /* pad to match size of modern driver struct */
    int padding2[4];     /* more pad */
    F_PTR padding3[2];   /* even more padding */
} DriverEntry;


/* These are the kernel functions available for driver writers */

EXTERN int driver_select _ANSI_ARGS_((int,int,int,int));

EXTERN int driver_output _ANSI_ARGS_((int, char*, int));
EXTERN int driver_output2 _ANSI_ARGS_((int, char*, int, char*, int));
EXTERN int driver_output_binary _ANSI_ARGS_((int, char*, int,
					     DriverBinary*, int, int));
EXTERN int driver_outputv _ANSI_ARGS_((int, char*,int,ErlIOVec*,int));

EXTERN int driver_vec_to_buf _ANSI_ARGS_((ErlIOVec*, char*, int));

EXTERN int driver_set_timer _ANSI_ARGS_((int, unsigned long));
EXTERN int driver_cancel_timer _ANSI_ARGS_((int));

/*
 * The following functions are used to initiate a close of a port
 * from a driver.
 */
EXTERN int driver_failure_eof _ANSI_ARGS_((int));
EXTERN int driver_failure_atom _ANSI_ARGS_((int, char *));
EXTERN int driver_failure_posix _ANSI_ARGS_((int, int));
EXTERN int driver_failure _ANSI_ARGS_((int, int));
EXTERN int driver_exit _ANSI_ARGS_ ((int, int));

EXTERN char* erl_errno_id _ANSI_ARGS_((int error));
EXTERN void set_busy_port _ANSI_ARGS_((int, int));
EXTERN void add_driver_entry _ANSI_ARGS_((DriverEntry *));
EXTERN int remove_driver_entry _ANSI_ARGS_((DriverEntry *));
EXTERN void set_port_control_flags _ANSI_ARGS_((int, int));

/* Binary interface */
/* NOTE: DO NOT overwrite a binary with new data (if the data is delivered);
** since the binary is a shared object it MUST be written once.
*/

EXTERN DriverBinary* driver_alloc_binary _ANSI_ARGS_((int));
EXTERN DriverBinary* driver_realloc_binary _ANSI_ARGS_((DriverBinary*, int));
EXTERN void driver_free_binary _ANSI_ARGS_((DriverBinary*));


/* Queue interface */
EXTERN int driver_enqv _ANSI_ARGS_((int, ErlIOVec*, int));
EXTERN int driver_pushqv _ANSI_ARGS_((int, ErlIOVec*, int));
EXTERN int driver_deq _ANSI_ARGS_((int, int));
EXTERN SysIOVec* driver_peekq _ANSI_ARGS_((int, int*));
EXTERN int driver_sizeq _ANSI_ARGS_((int));
EXTERN int driver_enq_bin _ANSI_ARGS_((int, DriverBinary*, int, int));
EXTERN int driver_enq _ANSI_ARGS_((int, char*, int));
EXTERN int driver_pushq_bin _ANSI_ARGS_((int, DriverBinary*, int, int));
EXTERN int driver_pushq _ANSI_ARGS_((int, char*, int));

/* Memory management */
EXTERN void *driver_alloc _ANSI_ARGS_((size_t));
EXTERN void *driver_realloc _ANSI_ARGS_((void*, size_t));
EXTERN void driver_free _ANSI_ARGS_((void*));

/* Shared / dynamic link libraries */
EXTERN void *driver_dl_open _ANSI_ARGS_((char *));
EXTERN void *driver_dl_sym _ANSI_ARGS_((void *, char *));
EXTERN int driver_dl_close _ANSI_ARGS_((void *));
EXTERN char *driver_dl_error _ANSI_ARGS_((void));

/* Async IO functions */
EXTERN long driver_async _ANSI_ARGS_((int,
				      unsigned int*,
				      void (*)(void*), 
				      void *,
				      void (*)(void*)));
EXTERN int driver_async_cancel _ANSI_ARGS_((long));

EXTERN int driver_lock_driver _ANSI_ARGS_((int));

/* Threads */
typedef void* erl_mutex_t;
typedef void* erl_cond_t;
typedef void* erl_thread_t;

EXTERN erl_mutex_t erts_mutex_create _ANSI_ARGS_((void));
EXTERN int erts_mutex_destroy _ANSI_ARGS_((erl_mutex_t));
EXTERN int erts_mutex_lock _ANSI_ARGS_((erl_mutex_t));
EXTERN int erts_mutex_unlock _ANSI_ARGS_((erl_mutex_t));

EXTERN erl_cond_t erts_cond_create _ANSI_ARGS_((void));
EXTERN int erts_cond_destroy _ANSI_ARGS_((erl_cond_t));
EXTERN int erts_cond_signal _ANSI_ARGS_((erl_cond_t));
EXTERN int erts_cond_broadcast _ANSI_ARGS_((erl_cond_t));
EXTERN int erts_cond_wait _ANSI_ARGS_((erl_cond_t, erl_mutex_t));
EXTERN int erts_cond_timedwait _ANSI_ARGS_((erl_cond_t, erl_mutex_t, long));

EXTERN int erts_thread_create _ANSI_ARGS_((erl_thread_t*,
					 void* (*func)(void*),
					 void* arg,
					 int detached));
EXTERN erl_thread_t erts_thread_self _ANSI_ARGS_((void));
EXTERN void erts_thread_exit _ANSI_ARGS_((void*));
EXTERN int  erts_thread_join _ANSI_ARGS_((erl_thread_t, void**));
EXTERN int  erts_thread_kill _ANSI_ARGS_((erl_thread_t));


typedef unsigned long DriverTermData;

#define TERM_DATA(x) ((DriverTermData) (x))

/* Possible types to send from driver          Argument type */
#define ERL_DRV_NIL         ((DriverTermData) 1)  /* None */
#define ERL_DRV_ATOM        ((DriverTermData) 2)  /* driver_mk_atom(string) */
#define ERL_DRV_INT         ((DriverTermData) 3)  /* int */
#define ERL_DRV_PORT        ((DriverTermData) 4)  /* driver_mk_port(ix) */
#define ERL_DRV_BINARY      ((DriverTermData) 5)  /* ErlDriverBinary*, int */
#define ERL_DRV_STRING      ((DriverTermData) 6)  /* char*, int */
#define ERL_DRV_TUPLE       ((DriverTermData) 7)  /* int */
#define ERL_DRV_LIST        ((DriverTermData) 8)  /* int */
#define ERL_DRV_STRING_CONS ((DriverTermData) 9)  /* char*, int */
#define ERL_DRV_PID         ((DriverTermData) 10) /* driver_connected,... */

/* DriverTermData is the type to use for casts when building 
 * terms that should be sent to connected process,
 * for instance a tuple on the form {tcp, Port, [Tag|Binary]}
 *
 * DriverTermData spec[] = {
 *    ERL_DRV_ATOM, driver_mk_atom("tcp"),
 *    ERL_DRV_PORT, driver_mk_port(drv->ix),
 *             ERL_DRV_INT, REPLY_TAG,
 *             ERL_DRV_BIN, 50, TERM_DATA(buffer),
 *             ERL_DRV_LIST, 2,
 *    ERL_DRV_TUPLE, 3,
 *  }       
 *             
 */

EXTERN DriverTermData driver_mk_atom _ANSI_ARGS_ ((char*));
EXTERN DriverTermData driver_mk_port _ANSI_ARGS_ ((int));
EXTERN DriverTermData driver_connected _ANSI_ARGS_((int));
EXTERN DriverTermData driver_caller _ANSI_ARGS_((int));

EXTERN int driver_output_term _ANSI_ARGS_((int, DriverTermData *, int));
EXTERN int driver_send_term _ANSI_ARGS_((int, DriverTermData, DriverTermData *, int));

#endif


