Fw: erl_interface

Alex Ogloblin gandalf@REDACTED
Wed May 28 12:19:24 CEST 2003


Heil, Serge Aleynikov

Got the code :
=============== COMM_DRV.CC=========================
#include <vector>
using namespace std;
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#ifndef WIN32
#include <termios.h>
#endif
#include <unistd.h>
extern "C"{
#include "erl_eterm.h"
#include "erl_format.h"
#include "erl_marshal.h"
#include "erl_fix_alloc.h"
#include "erl_malloc.h"
#include "erl_rport.h"
#include "erl_connect.h"
#include "erl_start.h"
#include "erl_resolve.h"
extern void erl_init_nothreads(void *x, long y);
#define erl_init(x,y) erl_init_nothreads(x,y)
}
#include <ei.h>
#pragma pack(1)
#include <erl_driver.h>
#include "comm_drv.h"
typedef ETERM * PETERM;
static void hex_dump(char * buff,int len);

HPORT openport(char * name,int speed)
{
if(!name)
  {
#ifdef WIN32
    return INVALID_HANDLE_VALUE;
#else
    return -1;
#endif
  }

#ifdef WIN32    //WIN32 platform

COMMTIMEOUTS t_outs;
COMMCONFIG  cfg;
DWORD size;
HANDLE porthandle=CreateFile(name,GENERIC_READ | GENERIC_WRITE,NULL,NULL,
    OPEN_EXISTING,NULL,NULL);

if(porthandle==INVALID_HANDLE_VALUE ||
!GetCommConfig(porthandle,&cfg,&size))
  {
   return INVALID_HANDLE_VALUE;
  }

t_outs.ReadIntervalTimeout=1;
t_outs.ReadTotalTimeoutMultiplier=1;
t_outs.ReadTotalTimeoutConstant=1;
t_outs.WriteTotalTimeoutMultiplier=1;
t_outs.WriteTotalTimeoutConstant=1;
cfg.dcb.BaudRate=speed;
cfg.dcb.Parity=NOPARITY;
cfg.dcb.StopBits=ONESTOPBIT;
cfg.dcb.ByteSize=8;

if(!SetCommConfig(porthandle,&cfg,cfg.dwSize) ||
!SetCommTimeouts(porthandle,&t_outs))
  {
   return INVALID_HANDLE_VALUE;
  }
return porthandle;
#else   //UNIX (posix) platform
int ispeed;
int porthandle;

switch(speed)
  {
   case 115200:
     ispeed=B115200;
     break;
   case 57600:
     ispeed=B57600;
     break;
   case 38400:
     ispeed=B38400;
     break;
   case 19200:
     ispeed=B19200;
     break;
   case 9600:
     ispeed=B9600;
     break;
   case 4800:
     ispeed=B4800;
     break;
   case 2400:
     ispeed=B2400;
     break;
   case 1200:
     ispeed=B1200;
     break;
   case 600:
     ispeed=B600;
     break;
   case 300:
     ispeed=B300;
     break;
   default:
     return -1;
  }

termios Settings;
porthandle=::open(name,O_RDWR | O_NONBLOCK | O_NOCTTY);
if(porthandle == -1)
  {
   return -1;
  }

tcgetattr(porthandle, &Settings);
cfmakeraw(&Settings);
Settings.c_cflag = (CS8 | CREAD | CLOCAL | ispeed);
Settings.c_lflag =0;
//Settings.c_iflag = (IGNCR | IGNBRK | IGNPAR);
Settings.c_iflag = (IGNBRK | IGNPAR);
Settings.c_iflag &= ~INPCK;
tcflush(porthandle,TCIOFLUSH);
if(tcsetattr(porthandle, TCSANOW, &Settings) ==-1)
  {
   return -1;
  }
return porthandle;
#endif
};
void closeport(HPORT porthandle)
{
#ifdef WIN32
if(porthandle!=INVALID_HANDLE_VALUE)CloseHandle(porthandle);
#else
if(porthandle != -1)close(porthandle);
#endif
}

bool writeport(HPORT port,char * buff,int size)
{
int rc;
if(size == 0)return true;
#ifdef WIN32
   WriteFile(port,buff,size,(DWORD *)&rc,NULL);
#else
   rc=::write(port,buff,size);
#endif
//printf("\noutwrite:%u",rc);
if(rc==size)return true;
if(rc == 0)return false;
if(rc < 0)
  {
   if(errno == EAGAIN || errno == EINTR)return false;
   return false;
  }
};
void commstep(HPORT port,ErlDrvPort dport)
{
char buff[512];
int rc;
#ifdef WIN32
if(port ==INVALID_HANDLE_VALUE)return;
bool rc2=ReadFile(port,buff,sizeof(buff),(DWORD *)&rc,NULL);
#else
if(port == -1)return -1;
rc=::read(port,buff,sizeof(buff));
#endif
if(rc == 0)return;
if(rc < 0)
  {
   if(errno == EAGAIN || errno == EINTR)return;
   return;
  }
int n=2;
ETERM ** termlist=(ETERM **)new (ETERM *)[n];
ETERM * term_int=erl_mk_int(COMM_READ);
ETERM * term_bin=erl_mk_binary(buff,rc);
termlist[0]=term_int;
termlist[1]=term_bin;
ETERM * term2=erl_mk_list(termlist,n);
int outsize=erl_term_len(term2);
unsigned char * outbuff=new unsigned char[outsize];
erl_encode(term2,outbuff);
driver_output(dport,(char *)outbuff,outsize);
erl_free_term(term2);
erl_free_term(term_int);
erl_free_term(term_bin);
delete[] termlist;
};
#pragma pack()
static void hex_dump(char * buff,int len)
{
for(int i1=0;i1 < len;i1++)
  {
   char c1=buff[i1];
   printf("%2x(%c)",c1,(isprint(c1))?c1:' ');
  }
printf("\n");
};

void send_int(ErlDrvPort port,int arg)
{
ETERM * term=erl_mk_int(arg);
int size=erl_term_len(term);
unsigned char * buff=new unsigned char[size];
erl_encode(term,buff);
driver_output(port,(char *)buff,size);
delete[] buff;
erl_free_term(term);
};

typedef struct {
ErlDrvPort port;
HPORT porthandle;
} comm_data;

static ErlDrvData comm_drv_start(ErlDrvPort port, char *buff)
{
    erl_init(NULL,0);
    comm_data* d = (comm_data*)driver_alloc(sizeof(comm_data));
    d->port = port;
    return (ErlDrvData)d;
}

static void comm_drv_stop(ErlDrvData handle)
{
    driver_free((char*)handle);
}


static void comm_drv_output(ErlDrvData handle, char *buff, int bufflen)
{
ETERM * term1;
ETERM * tport;
ETERM * tspd;
char * portname;
int spd;
ETERM * tbuff;
ETERM * tsize;
char * buff2;
int size2;
unsigned char * outbuff;
int outsize;
comm_data* d = (comm_data*)handle;
    char fn = buff[0], arg = buff[1], res;
    switch(fn)
      {
       case COMM_OPEN:
         term1=erl_decode((unsigned char *)buff+1);
((unsigned char *)term1));
         tport=erl_element(1,term1);
         tspd=erl_element(2,term1);
         portname=erl_iolist_to_string(tport);
         spd=ERL_INT_VALUE(tspd);
         d->porthandle=openport(portname,spd);
         erl_free(portname);
         erl_free_term(tspd);
         erl_free_term(tport);
         erl_free_term(term1);
         res=(d->porthandle > 0)?1:0;
         driver_output(d->port, &res, 1);
         break;
       case COMM_CLOSE:
         closeport(d->porthandle);
         res=1;
         driver_output(d->port, &res, 1);
         break;
       case COMM_STEP:
         commstep(d->porthandle,d->port);
         break;
       case COMM_WRITE:
         tbuff=erl_decode((unsigned char *)buff+1);
         buff2=(char *)ERL_BIN_PTR(tbuff);
         size2=ERL_BIN_SIZE(tbuff);
         res=(writeport(d->porthandle,buff2,size2))?1:0;
         erl_free_term(tbuff);
         driver_output(d->port, &res, 1);
         break;
       default:
         res=0;
         driver_output(d->port, &res, 1);
         break;
      };
}

ErlDrvEntry comm_driver_entry = {
    NULL,                       /* F_PTR init, N/A */
    comm_drv_start,          /* L_PTR start, called when port is opened */
    comm_drv_stop,           /* F_PTR stop, called when port is closed */
    comm_drv_output,         /* F_PTR output, called when erlang has sent */
    NULL,                       /* F_PTR ready_input, called when input
descriptor ready */
    NULL,                       /* F_PTR ready_output, called when output
descriptor ready */
    "comm_drv",              /* char *driver_name, the argument to open_port
*/
    NULL,                       /* F_PTR finish, called when unloaded */
    NULL,                       /* F_PTR control, port_command callback */
    NULL,                       /* F_PTR timeout, reserved */
    NULL                        /* F_PTR outputv, reserved */
};
extern "C"{
DRIVER_INIT(comm_drv) /* must match name in driver_entry */
{
    return &comm_driver_entry;
}
}
==================COMM_DRV.H====================
#ifndef __erlint_h__
#define __erlint_h__

#include <erl_driver.h>
#define COMM_OPEN 1
#define COMM_CLOSE 2
#define COMM_STEP 3
#define COMM_READ 4
#define COMM_WRITE 5
#define COMM_DATA 6
#define COMM_ERROR 7

#ifdef WIN32
#include <windows.h>
typedef HANDLE HPORT;
#else
typedef int HPORT;
#endif

HPORT openport(char * name,int speed);
void closeport(HPORT port);
bool writeport(HPORT port,char * buff,int size);
void commstep(HPORT port,ErlDrvPort dport);

#endif
=================COMM.HRL====================
-define(COMM_OPEN,1).
-define(COMM_CLOSE,2).
-define(COMM_STEP,3).
-define(COMM_READ,4).
-define(COMM_WRITE,5).
==================COMM.ERL=======================
-module(comm).
-export([start/2,stop/1]).
-export([close/1,open/3,write/2]).
-export([encode/1,init/3,loop/2]).
-compile({inline,30}).
-include("comm.hrl").

call_port(Pid,Msg) ->
    Pid ! {call, self(), Msg},
    receive
    {Pid, Result} ->
        Result
    end.

encode({comm_open,Name,Speed}) -> [?COMM_OPEN,term_to_binary({Name,Speed})];
encode({comm_close}) -> [?COMM_CLOSE];
encode({comm_step}) -> [?COMM_STEP];
encode({comm_write,Bin}) -> [?COMM_WRITE,term_to_binary(Bin)].

loop(Port,Client) ->
    receive
% command interface to COMM_DRV
        {call, Caller, Msg} ->
            Port ! {self(), {command, encode(Msg)}},
            receive
            {Port, {data, Data}} ->
                Caller ! {self(), Data}
            end,
        loop(Port,Client);

% periodic poll serial driver
        {comm_step,Delay} ->
            Port ! {self(), {command, [?COMM_STEP]}},
            timer:send_after(Delay,self(),{comm_step,Delay}),
            loop(Port,Client);

% data from serial driver
        {Port,{data,List}}->
         Bin1=list_to_binary(List),
         case binary_to_term(Bin1) of
          [?COMM_READ,Bin2]->
              case catch Client ! {?COMM_READ,Bin2} of
               {'EXIT',Reason}->
       io:fwrite("~nCLIENT ~w invalid.~w",[Client,Reason]);
       _->ok
     end,
              io:fwrite("~nREAD:~w",[Bin2]);
    X ->
              io:fwrite("~nREAD?:~w",[X])
         end,
            loop(Port,Client);

        {comm_read,Bin} ->
            io:fwrite("~nREAD:~w",[Bin]),
            loop(Port,Client);

% stop the driver
     stop ->
         Port ! {self(), close},
         receive
          {Port, closed} ->
              exit(normal)
         end;

% abort in the C++ code
     {'EXIT', Port, Reason} ->
         io:fwrite("~nEXIT:~w ~n", [Reason]),
         exit(port_terminated);

     X->
      io:fwrite("~nLOOP1:~w",[X]),
      loop(Port,Client)
    end.

init(SharedLib,Client,Period) ->
    Port = open_port({spawn, SharedLib}, []),
    timer:send_after(Period,self(),{comm_step,Period}),
    loop(Port,Client).


% load and started serial driver with pid Client and integer Period
% Client - pid of receiver of data
% Period - millisecond
start(Client,Period) ->
    case erl_ddll:load_driver("./","comm_drv") of
        ok -> ok;
        {error, already_loaded} -> ok;
        {error,A} ->exit({error,A,"comm_drv"})
    end,
    Pid=spawn(?MODULE, init, ["comm_drv",Client,Period]).

%close serial port
close(Pid) ->
    call_port(Pid,{comm_close}).

% open serial port with Name and Speed
% Name - string name of serial port
% Speed - integer speed in baud
open(Pid,Name,Speed) ->
    call_port(Pid,{comm_open,Name,Speed}).

write(Pid,Bin) ->
    io:fwrite("~nERL:~w",[Bin]),
 call_port(Pid,{comm_write,Bin}).

% stop and unload serial driver
stop(Pid) ->
    Pid ! {call, self(), {comm_close}},
    Pid ! stop,
    erl_ddll:unload_driver("comm_drv").

loop2() ->
 receive
  Data ->
   io:fwrite("~n~w",[Data]),
   loop2()
 end.

start2() ->
 Pid=spawn(?MODULE,loop2,[]).

> I wonder if someone could share erl_interface-based examples with me.
> I've been trying to write some code that passes either a tuple/list, or
> an integer from C to Erlang, and I am having a problem that in case of
> integers (see iterate() and finalize() below) Erlangs gets a response
> correctly, but in case of tuple/list (see initialize()), looks like
> Erlang is waiting for something in addition to what the port replies.  I
> can see in debugger that the Port sends everything normally, and then
> blocks on the read() function as expected.
>
> Serge





More information about the erlang-questions mailing list