The main way which programs on Enea OSE interact is through the
usage of message passing, much the same way as Erlang processes
communicate. There are two ways in which an Erlang programmer can
interact with the signals sent from other Enea OSE processes; either
through the provided
Erlang/OTP on OSE provides a erlang module called
1> P1 = ose:open("p1").
#Port>0.344>
2> ose:hunt(P1,"p2").
{#Port>0.344>,1}
3> P2 = ose:open("p2").
#Port>0.355>
4> flush().
Shell got {mailbox_up,#Port>0.344>,{#Port>0.344>,1},852189}
ok
5> ose:listen(P1,[1234]).
ok
6> ose:send(P2,ose:get_id(P1),1234,>>"hello">>).
ok
7> flush().
Shell got {message,#Port>0.344>,{852189,1245316,1234,>>"hello">>}}
ok
Writing Linked-in drivers for OSE is very similar to how it is done for Unix/Windows. It is only the way in which the driver subscribes and consumed external events that is different. In Unix (and Windows) file descriptiors (and Event Objects) are used to select on. On OSE we use signals to deliver the same functionality. There are two large differences between a signal and an fd.
In OSE it is not possible for a signal number to be a unique identifier
for a resource in the same way as an fd is. For example; let's say we
implement a driver that does an asynchronous hunt that uses signal
number 1234 as the hunt_sig. If we want to be able to have multiple
hunt ports running at the same time we have to have someway of routing
the signal to the correct port. This is achieved by supplying a secondary
id that can be retrieved through the meta-data or payload of the signal,
e.g:
ErlDrvEvent event = erl_drv_ose_event_alloc(1234,port,resolver);
The event you get back from
erl_drv_ose_event_fetch(event, &signal, &port, (void **)&extra);
example of what this could look like in
The other difference from unix is that in OSE the payload of the event
(i.e. the signal data) is already received when the ready_output/input
callbacks are called. This means that you access the data of a signal
by calling
#include "erl_driver.h"
#include "ose.h"
struct huntsig {
SIGSELECT signo;
ErlDrvPort port;
};
union SIGNAL {
SIGSELECT signo;
struct huntsig;
}
/* Here we have to get the id from the signal. In this case we use the
port id since we have control over the data structure of the signal.
It is however possible to use anything in here. The only restriction
is that the same id has to be used for all signals of the same number.*/
ErlDrvOseEventId resolver(union SIGNAL *sig) {
return (ErlDrvOseEventId)sig->huntsig.port;
}
static int drv_init(void) { return 0; };
static ErlDrvData drv_start(ErlDrvPort port, char *command) {
return (ErlDrvData)port;
}
static ErlDrvSSizeT control(ErlDrvData driver_data, unsigned int cmd,
char *buf, ErlDrvSizeT len,
char **rbuf, ErlDrvSizeT rlen) {
ErlDrvPort port = (ErlDrvPort)driver_data;
/* An example of extra data to associate with the event */
char *extra_data = driver_alloc(80);
snprintf("extra_data, "Event, sig_no: 1234, and port: %d", port);
/* Create a new event to select on */
ErlDrvOseEvent evt = erl_drv_ose_event_alloc(1234,port,resolver, extra_data);
/* Make sure to do the select call _BEFORE_ the signal arrives.
The signal might get lost if the hunt call is done before the
select. */
driver_select(port,evt,ERL_DRV_READ|ERL_DRV_USE,1);
union SIGNAL *sig = alloc(sizeof(union SIGNAL),1234);
sig->huntsig.port = port;
hunt("testprocess",0,NULL,&sig);
return 0;
}
static void ready_input(ErlDrvData driver_data, ErlDrvEvent evt) {
char *extra_data;
/* Get the first signal payload from the event */
union SIGNAL *sig = erl_drv_ose_get_signal(evt);
ErlDrvPort port = (ErlDrvPort)driver_data;
while (sig != NULL) {
if (sig->signo == 1234) {
/* Print out the string we added as the extra parameter */
erl_drv_ose_event_fetch(evt, NULL, NULL, (void **)&extra_data);
printf("We've received: %s\n", extra_data);
/* If it is our signal we send a message with the sender of the signal
to the controlling erlang process */
ErlDrvTermData reply[] = { ERL_DRV_UINT, (ErlDrvUInt)sender(&sig) };
erl_drv_send_term(port,reply,sizeof(reply) / sizeof(reply[0]));
}
/* Cleanup the signal and deselect on the event.
Note that the event itself has to be free'd in the stop_select
callback. */
free_buf(&sig);
driver_select(port,evt,ERL_DRV_READ|ERL_DRV_USE,0);
/* There could be more than one signal waiting in this event, so
we have to loop until sig == NULL */
sig = erl_drv_ose_get_signal(evt);
}
}
static void stop_select(ErlDrvEvent event, void *reserved)
{
/* Free the extra_data */
erl_drv_ose_event_fetch(evt, NULL, NULL, (void **)&extra_data);
driver_free(extra_data);
/* Free the event itself */
erl_drv_ose_event_free(event);
}
/**
* Setup the driver entry for the Erlang runtime
**/
ErlDrvEntry ose_signal_driver_entry = {
.init = drv_init,
.start = drv_start,
.stop = drv_stop,
.ready_input = ready_input,
.driver_name = DRIVER_NAME,
.control = control,
.extended_marker = ERL_DRV_EXTENDED_MARKER,
.major_version = ERL_DRV_EXTENDED_MAJOR_VERSION,
.minor_version = ERL_DRV_EXTENDED_MINOR_VERSION,
.driver_flags = ERL_DRV_FLAG_USE_PORT_LOCKING,
.stop_select = stop_select
};