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