aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/drivers/ose
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/drivers/ose')
-rw-r--r--erts/emulator/drivers/ose/ose_signal_drv.c884
1 files changed, 884 insertions, 0 deletions
diff --git a/erts/emulator/drivers/ose/ose_signal_drv.c b/erts/emulator/drivers/ose/ose_signal_drv.c
new file mode 100644
index 0000000000..acf09f748e
--- /dev/null
+++ b/erts/emulator/drivers/ose/ose_signal_drv.c
@@ -0,0 +1,884 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2013-2013. All Rights Reserved.
+ *
+ * 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 online 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.
+ *
+ * %CopyrightEnd%
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "errno.h"
+#include "stdio.h"
+#include "string.h"
+#include "stddef.h"
+
+#include "sys.h"
+#include "erl_driver.h"
+#include "ose.h"
+#include "ose_spi/ose_spi.h"
+
+#define DEBUG_ATTACH 0
+#define DEBUG_HUNT 0
+#define DEBUG_SEND 0
+#define DEBUG_LISTEN 0
+
+#if 0
+#define DEBUGP(FMT,...) printf(FMT, __VA_ARGS__)
+#else
+#define DEBUGP(FMT,...)
+#endif
+
+#if DEBUG_ATTACH
+#define DEBUGP_ATTACH(...) DEBUGP( __VA_ARGS__)
+#else
+#define DEBUGP_ATTACH(...)
+#endif
+
+#if DEBUG_HUNT
+#define DEBUGP_HUNT(...) DEBUGP( __VA_ARGS__)
+#else
+#define DEBUGP_HUNT(...)
+#endif
+
+#if DEBUG_LISTEN
+#define DEBUGP_LISTEN(...) DEBUGP( __VA_ARGS__)
+#else
+#define DEBUGP_LISTEN(...)
+#endif
+
+#if DEBUG_SEND
+#define DEBUGP_SEND(...) DEBUGP( __VA_ARGS__)
+#else
+#define DEBUGP_SEND(...)
+#endif
+
+
+#define DRIVER_NAME "ose_signal_drv"
+#define GET_SPID 1
+#define GET_NAME 2
+#define HUNT 100
+#define DEHUNT 101
+#define ATTACH 102
+#define DETACH 103
+#define SEND 104
+#define SEND_W_S 105
+#define LISTEN 106
+#define OPEN 200
+
+#define REF_SEGMENT_SIZE 8
+
+struct async {
+ SIGSELECT signo;
+ ErlDrvTermData port;
+ ErlDrvTermData proc;
+ PROCESS spid;
+ PROCESS target;
+ Uint32 ref;
+};
+
+/**
+ * OSE signals
+ **/
+union SIGNAL {
+ SIGSELECT signo;
+ struct async async;
+};
+
+/**
+ * The driver's context
+ **/
+typedef struct _driver_context {
+ ErlDrvPort port;
+ PROCESS spid;
+ ErlDrvEvent perm_events[2];
+ ErlDrvEvent *events;
+ Uint32 event_cnt;
+ Uint32 ref;
+ Uint32 *outstanding_refs;
+ Uint32 outstanding_refs_max;
+ Uint32 outstanding_refs_cnt;
+} driver_context_t;
+
+/**
+ * Global variables
+ **/
+static ErlDrvTermData a_ok;
+static ErlDrvTermData a_error;
+static ErlDrvTermData a_enomem;
+static ErlDrvTermData a_enoent;
+static ErlDrvTermData a_badarg;
+static ErlDrvTermData a_mailbox_up;
+static ErlDrvTermData a_mailbox_down;
+static ErlDrvTermData a_ose_drv_reply;
+static ErlDrvTermData a_message;
+static PROCESS proxy_proc;
+
+
+/**
+ * Serialize/unserialize unsigned 32-bit values
+ **/
+static char *put_u32(unsigned int value, char *ptr) {
+ *ptr++ = (value & 0xff000000) >> 24;
+ *ptr++ = (value & 0x00ff0000) >> 16;
+ *ptr++ = (value & 0x0000ff00) >> 8;
+ *ptr++ = (value & 0xff);
+
+ return ptr;
+}
+
+static unsigned int get_u32(char *ptr) {
+ unsigned int result = 0;
+ result += (ptr[0] & 0xff) << 24;
+ result += (ptr[1] & 0xff) << 16;
+ result += (ptr[2] & 0xff) << 8;
+ result += (ptr[3] & 0xff);
+
+ return result;
+}
+
+
+/* Stolen from efile_drv.c */
+
+/* char EV_CHAR_P(ErlIOVec *ev, int p, int q) */
+#define EV_CHAR_P(ev, p, q) \
+ (((char *)(ev)->iov[(q)].iov_base) + (p))
+
+/* int EV_GET_CHAR(ErlIOVec *ev, char *p, int *pp, int *qp) */
+#define EV_GET_CHAR(ev, p, pp, qp) ev_get_char(ev, p ,pp, qp)
+static int
+ev_get_char(ErlIOVec *ev, char *p, int *pp, int *qp) {
+ if (*(pp)+1 <= (ev)->iov[*(qp)].iov_len) {
+ *(p) = *EV_CHAR_P(ev, *(pp), *(qp));
+ if (*(pp)+1 < (ev)->iov[*(qp)].iov_len)
+ *(pp) = *(pp)+1;
+ else {
+ (*(qp))++;
+ *pp = 0;
+ }
+ return !0;
+ }
+ return 0;
+}
+
+/* Uint32 EV_UINT32(ErlIOVec *ev, int p, int q)*/
+#define EV_UINT32(ev, p, q) \
+ ((Uint32) *(((unsigned char *)(ev)->iov[(q)].iov_base) + (p)))
+
+/* int EV_GET_UINT32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */
+#define EV_GET_UINT32(ev, p, pp, qp) ev_get_uint32(ev,(Uint32*)(p),pp,qp)
+static int
+ev_get_uint32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) {
+ if (*(pp)+4 <= (ev)->iov[*(qp)].iov_len) {
+ *(p) = (EV_UINT32(ev, *(pp), *(qp)) << 24)
+ | (EV_UINT32(ev, *(pp)+1, *(qp)) << 16)
+ | (EV_UINT32(ev, *(pp)+2, *(qp)) << 8)
+ | (EV_UINT32(ev, *(pp)+3, *(qp)));
+ if (*(pp)+4 < (ev)->iov[*(qp)].iov_len)
+ *(pp) = *(pp)+4;
+ else {
+ (*(qp))++;
+ *pp = 0;
+ }
+ return !0;
+ }
+ return 0;
+}
+
+/**
+ * Convinience macros
+ **/
+#define send_response(port,output) erl_drv_send_term(driver_mk_port(port),\
+ driver_caller(port), output, sizeof(output) / sizeof(output[0]));
+
+void iov_memcpy(void *dest,ErlIOVec *ev,int ind,int off);
+void iov_memcpy(void *dest,ErlIOVec *ev,int ind,int off) {
+ int i;
+ memcpy(dest,ev->iov[ind].iov_base+off,ev->iov[ind].iov_len-off);
+ for (i = ind+1; i < ev->vsize; i++)
+ memcpy(dest,ev->iov[i].iov_base,ev->iov[i].iov_len);
+}
+
+/**
+ * Reference handling
+ **/
+
+static int add_reference(driver_context_t *ctxt, Uint32 ref) {
+
+ /*
+ * Premature optimizations may be evil, but they sure are fun.
+ */
+
+ if (ctxt->outstanding_refs == NULL) {
+ /* First ref to be ignored */
+ ctxt->outstanding_refs = driver_alloc(REF_SEGMENT_SIZE*sizeof(Uint32));
+ if (!ctxt->outstanding_refs)
+ return 1;
+
+ memset(ctxt->outstanding_refs,0,REF_SEGMENT_SIZE*sizeof(Uint32));
+ ctxt->outstanding_refs_max += REF_SEGMENT_SIZE;
+ ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref;
+ } else if (ctxt->outstanding_refs_cnt == ctxt->outstanding_refs_max) {
+ /* Expand ref array */
+ Uint32 *new_array;
+ ctxt->outstanding_refs_max += REF_SEGMENT_SIZE;
+ new_array = driver_realloc(ctxt->outstanding_refs,
+ ctxt->outstanding_refs_max*sizeof(Uint32));
+
+ if (!new_array) {
+ ctxt->outstanding_refs_max -= REF_SEGMENT_SIZE;
+ return 1;
+ }
+
+ ctxt->outstanding_refs = new_array;
+
+ memset(ctxt->outstanding_refs+ctxt->outstanding_refs_cnt,0,
+ REF_SEGMENT_SIZE*sizeof(Uint32));
+ ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref;
+
+ } else {
+ /* Find an empty slot:
+ * First we try current index,
+ * then we scan for a slot.
+ */
+ if (!ctxt->outstanding_refs[ctxt->outstanding_refs_cnt]) {
+ ctxt->outstanding_refs[ctxt->outstanding_refs_cnt++] = ref;
+ } else {
+ int i;
+ ASSERT(ctxt->outstanding_refs_cnt < ctxt->outstanding_refs_max);
+ for (i = 0; i < ctxt->outstanding_refs_max; i++)
+ if (!ctxt->outstanding_refs[i])
+ break;
+ ASSERT(ctxt->outstanding_refs[i] == 0);
+ ctxt->outstanding_refs[i] = ref;
+ ctxt->outstanding_refs_cnt++;
+ }
+ }
+ return 0;
+}
+
+/* Return 0 if removed, 1 if does not exist, */
+static int remove_reference(driver_context_t *ctxt, Uint32 ref) {
+ int i,j;
+
+ if (ctxt->outstanding_refs_max == 0 && ctxt->outstanding_refs_cnt == 0) {
+ ASSERT(ctxt->outstanding_refs == NULL);
+ return 1;
+ }
+
+ for (i = 0; i < ctxt->outstanding_refs_max; i++) {
+ if (ctxt->outstanding_refs[i] == ref) {
+ ctxt->outstanding_refs[i] = 0;
+ ctxt->outstanding_refs_cnt--;
+ i = -1;
+ break;
+ }
+ }
+
+ if (i != -1)
+ return 1;
+
+ if (ctxt->outstanding_refs_cnt == 0) {
+ driver_free(ctxt->outstanding_refs);
+ ctxt->outstanding_refs = NULL;
+ ctxt->outstanding_refs_max = 0;
+ } else if (ctxt->outstanding_refs_cnt == (ctxt->outstanding_refs_max - REF_SEGMENT_SIZE)) {
+ Uint32 *new_array;
+ for (i = 0, j = 0; i < ctxt->outstanding_refs_cnt; i++) {
+ if (ctxt->outstanding_refs[i] == 0) {
+ for (j = i+1; j < ctxt->outstanding_refs_max; j++)
+ if (ctxt->outstanding_refs[j]) {
+ ctxt->outstanding_refs[i] = ctxt->outstanding_refs[j];
+ ctxt->outstanding_refs[j] = 0;
+ break;
+ }
+ }
+ }
+ ctxt->outstanding_refs_max -= REF_SEGMENT_SIZE;
+ new_array = driver_realloc(ctxt->outstanding_refs,
+ ctxt->outstanding_refs_max*sizeof(Uint32));
+ if (!new_array) {
+ ctxt->outstanding_refs_max += REF_SEGMENT_SIZE;
+ return 2;
+ }
+
+ ctxt->outstanding_refs = new_array;
+
+ }
+
+ return 0;
+}
+
+/**
+ * The OSE proxy process. This only handles ERTS_SIGNAL_OSE_DRV_ATTACH.
+ * The process is needed because signals triggered by attach ignore
+ * redir tables.
+ *
+ * We have one global proxy process to save memory. An attempt to make each
+ * port phantom into a proxy was made, but that used way to much memory.
+ */
+static OS_PROCESS(driver_proxy_process) {
+ SIGSELECT sigs[] = {1,ERTS_SIGNAL_OSE_DRV_ATTACH};
+ PROCESS master = 0;
+
+ while (1) {
+ union SIGNAL *sig = receive(sigs);
+
+ if (sig->signo == ERTS_SIGNAL_OSE_DRV_ATTACH) {
+
+ /* The first message is used to determine who to send messages to. */
+ if (master == 0)
+ master = sender(&sig);
+
+ if (sig->async.target == 0) {
+ PROCESS from = sender(&sig);
+ restore(sig);
+ DEBUGP_ATTACH("0x%x: got attach 0x%x, sending to 0x%x\n",
+ current_process(),from,master);
+ sig->async.target = from;
+ send(&sig,master);
+ } else {
+ PROCESS target = sig->async.target;
+ restore(sig);
+ sig->async.target = 0;
+ DEBUGP_ATTACH("0x%x: doing attach on 0x%x\n",current_process(),target);
+ attach(&sig,target);
+ }
+ }
+ }
+}
+
+/**
+ * Init routine for the driver
+ **/
+static int drv_init(void) {
+
+ a_ok = driver_mk_atom("ok");
+ a_error = driver_mk_atom("error");
+ a_enomem = driver_mk_atom("enomem");
+ a_enoent = driver_mk_atom("enoent");
+ a_badarg = driver_mk_atom("badarg");
+ a_mailbox_up = driver_mk_atom("mailbox_up");
+ a_mailbox_down = driver_mk_atom("mailbox_down");
+ a_ose_drv_reply = driver_mk_atom("ose_drv_reply");
+ a_message = driver_mk_atom("message");
+
+ proxy_proc = create_process(get_ptype(current_process()),
+ "ose_signal_driver_proxy",
+ driver_proxy_process, 10000,
+ get_pri(current_process()),
+ 0, 0, NULL, 0, 0);
+
+#ifdef DEBUG
+ efs_clone(proxy_proc);
+#endif
+ start(proxy_proc);
+
+ return 0;
+}
+
+/**
+ * Start routine for the driver
+ **/
+static ErlDrvData drv_start(ErlDrvPort port, char *command)
+{
+ driver_context_t *ctxt = driver_alloc(sizeof(driver_context_t));
+
+ ctxt->perm_events[0] = NULL;
+ ctxt->perm_events[1] = NULL;
+
+ ctxt->spid = 0;
+ ctxt->port = port;
+ ctxt->event_cnt = 0;
+ ctxt->events = NULL;
+ ctxt->ref = 0;
+ ctxt->outstanding_refs = NULL;
+ ctxt->outstanding_refs_max = 0;
+ ctxt->outstanding_refs_cnt = 0;
+
+
+ /* Set the communication protocol to Erlang to be binary */
+ set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
+
+ /* Everything ok */
+ return (ErlDrvData)ctxt;
+}
+
+/**
+ * Stop routine for the driver
+ **/
+static void drv_stop(ErlDrvData driver_data)
+{
+ driver_context_t *ctxt = (driver_context_t *)driver_data;
+ int i;
+
+ /* HUNT + ATTACH */
+ if (ctxt->perm_events[0])
+ driver_select(ctxt->port, ctxt->perm_events[0],
+ ERL_DRV_USE|ERL_DRV_READ, 0);
+ if (ctxt->perm_events[1])
+ driver_select(ctxt->port, ctxt->perm_events[1],
+ ERL_DRV_USE|ERL_DRV_READ, 0);
+
+ for (i = 0; i < ctxt->event_cnt; i++) {
+ driver_select(ctxt->port, ctxt->events[i], ERL_DRV_USE|ERL_DRV_READ, 0);
+ }
+
+ if (ctxt->spid != 0)
+ kill_proc(ctxt->spid);
+ DEBUGP("0x%x: stopped\n",ctxt->spid);
+ if (ctxt->events)
+ driver_free(ctxt->events);
+ if (ctxt->outstanding_refs)
+ driver_free(ctxt->outstanding_refs);
+
+ driver_free(ctxt);
+}
+
+/**
+ * Output from Erlang
+ **/
+static void outputv(ErlDrvData driver_data, ErlIOVec *ev)
+{
+ driver_context_t *ctxt = (driver_context_t *)driver_data;
+ int p = 0, q = 1;
+ char cmd;
+
+ if (! EV_GET_CHAR(ev,&cmd,&p,&q)) {
+ ErlDrvTermData output[] = {
+ ERL_DRV_ATOM, a_ose_drv_reply,
+ ERL_DRV_PORT, driver_mk_port(ctxt->port),
+ ERL_DRV_ATOM, a_badarg,
+ ERL_DRV_TUPLE, 3};
+ send_response(ctxt->port, output);
+ return;
+ }
+
+ /* Command is in the buffer's first byte */
+ switch(cmd) {
+
+ case OPEN: {
+ char *name = driver_alloc(ev->size - 1+1);
+ struct OS_redir_entry redir[2];
+
+ redir[0].sig = 1;
+ redir[0].pid = current_process();
+
+ iov_memcpy(name,ev,q,p);
+ name[ev->size-1] = '\0';
+
+ ctxt->spid = create_process(OS_PHANTOM, name, NULL, 0,
+ 0, 0, 0, redir, 0, 0);
+
+ DEBUGP("0x%x: open\n",ctxt->spid);
+
+ ctxt->perm_events[1] =
+ erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_ATTACH,(int)ctxt->spid);
+ driver_select(ctxt->port,ctxt->perm_events[1],ERL_DRV_READ|ERL_DRV_USE,1);
+
+ ctxt->perm_events[0] =
+ erl_drv_ose_event_alloc(ERTS_SIGNAL_OSE_DRV_HUNT,(int)ctxt->spid);
+ driver_select(ctxt->port,ctxt->perm_events[0],ERL_DRV_READ|ERL_DRV_USE,1);
+
+ start(ctxt->spid);
+
+ {
+ ErlDrvTermData output[] = {
+ ERL_DRV_ATOM, a_ose_drv_reply,
+ ERL_DRV_PORT, driver_mk_port(ctxt->port),
+ ERL_DRV_ATOM, a_ok,
+ ERL_DRV_TUPLE, 3};
+
+ send_response(ctxt->port, output);
+ }
+
+ break;
+
+ }
+
+ case ATTACH:
+ case HUNT:
+ {
+ union SIGNAL *sig = alloc(sizeof(union SIGNAL),
+ cmd == HUNT ? ERTS_SIGNAL_OSE_DRV_HUNT:ERTS_SIGNAL_OSE_DRV_ATTACH);
+
+ sig->async.port = driver_mk_port(ctxt->port);
+ sig->async.proc = driver_caller(ctxt->port);
+ sig->async.spid = ctxt->spid;
+ sig->async.ref = ++ctxt->ref;
+
+ if (add_reference(ctxt,ctxt->ref)) {
+ ErlDrvTermData output[] = {
+ ERL_DRV_ATOM, a_ose_drv_reply,
+ ERL_DRV_PORT, driver_mk_port(ctxt->port),
+ ERL_DRV_ATOM, a_enomem,
+ ERL_DRV_TUPLE, 3};
+ send_response(ctxt->port, output);
+ free_buf(&sig);
+ } else {
+ ErlDrvTermData output[] = {
+ ERL_DRV_ATOM, a_ose_drv_reply,
+ ERL_DRV_PORT, driver_mk_port(ctxt->port),
+ ERL_DRV_PORT, driver_mk_port(ctxt->port),
+ ERL_DRV_INT, (ErlDrvUInt)ctxt->ref,
+ ERL_DRV_TUPLE, 2,
+ ERL_DRV_TUPLE, 3};
+ send_response(ctxt->port, output);
+
+ if (cmd == HUNT) {
+ char *huntname = driver_alloc(sizeof(char)*((ev->size-1)+1));
+
+ iov_memcpy(huntname,ev,q,p);
+ huntname[ev->size-1] = '\0';
+
+ DEBUGP_HUNT("0x%x: hunt %s -> %u (%u,%u)\n",
+ ctxt->spid,huntname,ctxt->ref,
+ ctxt->outstanding_refs_cnt,
+ ctxt->outstanding_refs_max);
+
+ hunt(huntname, 0, NULL, &sig);
+
+ driver_free(huntname);
+ } else {
+ EV_GET_UINT32(ev,&sig->async.target,&p,&q);
+ DEBUGP_ATTACH("0x%x: attach %u -> %u (%u,%u)\n",
+ ctxt->spid,sig->async.target,
+ ctxt->ref,
+ ctxt->outstanding_refs_cnt,
+ ctxt->outstanding_refs_max);
+
+ send(&sig,proxy_proc);
+ }
+
+ }
+
+ break;
+ }
+
+ case DETACH:
+ case DEHUNT:
+ {
+
+ Uint32 ref;
+
+ EV_GET_UINT32(ev,&ref,&p,&q);
+ if (cmd == DETACH) {
+ DEBUGP_ATTACH("0x%x: detach %u (%u,%u)\n",ctxt->spid,ref,
+ ctxt->outstanding_refs_cnt,
+ ctxt->outstanding_refs_max);
+ } else {
+ DEBUGP_HUNT("0x%x: dehunt %u (%u,%u)\n",ctxt->spid,ref,
+ ctxt->outstanding_refs_cnt,
+ ctxt->outstanding_refs_max);
+ }
+
+ if (remove_reference(ctxt,ref)) {
+ ErlDrvTermData output[] = {
+ ERL_DRV_ATOM, a_ose_drv_reply,
+ ERL_DRV_PORT, driver_mk_port(ctxt->port),
+ ERL_DRV_ATOM, a_error,
+ ERL_DRV_ATOM, a_enoent,
+ ERL_DRV_TUPLE, 2,
+ ERL_DRV_TUPLE, 3};
+
+ send_response(ctxt->port, output);
+ } else {
+ ErlDrvTermData output[] = {
+ ERL_DRV_ATOM, a_ose_drv_reply,
+ ERL_DRV_PORT, driver_mk_port(ctxt->port),
+ ERL_DRV_ATOM, a_ok,
+ ERL_DRV_TUPLE, 3};
+
+ send_response(ctxt->port, output);
+ }
+
+ break;
+ }
+
+ case SEND:
+ case SEND_W_S:
+ {
+ PROCESS spid;
+ PROCESS sender;
+ SIGSELECT signo;
+ OSBUFSIZE size = ev->size-9;
+ union SIGNAL *sig;
+
+ EV_GET_UINT32(ev,&spid,&p,&q);
+
+ if (cmd == SEND_W_S) {
+ EV_GET_UINT32(ev,&sender,&p,&q);
+ size -= 4;
+ } else {
+ sender = ctxt->spid;
+ }
+
+ EV_GET_UINT32(ev,&signo,&p,&q);
+
+ sig = alloc(size + sizeof(SIGSELECT),signo);
+
+ if (cmd == SEND_W_S) {
+ DEBUGP_SEND("0x%x: send_w_s(%u,%u,%u)\n",ctxt->spid,spid,signo,sender);
+ } else {
+ DEBUGP_SEND("0x%x: send(%u,%u)\n",ctxt->spid,spid,signo);
+ }
+
+ iov_memcpy(((char *)&sig->signo) + sizeof(SIGSELECT),ev,q,p);
+
+ send_w_s(&sig, sender, spid);
+
+ break;
+ }
+
+ case LISTEN:
+ {
+ int i,j,event_cnt = (ev->size - 1)/4;
+ ErlDrvEvent *events = NULL;
+ SIGSELECT signo,tmp_signo;
+
+ if (event_cnt == 0) {
+ for (i = 0; i < ctxt->event_cnt; i++)
+ driver_select(ctxt->port,ctxt->events[i],ERL_DRV_READ|ERL_DRV_USE,0);
+ if (ctxt->events)
+ driver_free(ctxt->events);
+ } else {
+ events = driver_alloc(sizeof(ErlDrvEvent)*event_cnt);
+ EV_GET_UINT32(ev,&signo,&p,&q);
+ for (i = 0, j = 0; i < event_cnt || j < ctxt->event_cnt; ) {
+
+ if (ctxt->events)
+ erl_drv_ose_event_fetch(ctxt->events[j],&tmp_signo,NULL);
+
+ if (signo == tmp_signo) {
+ events[i++] = ctxt->events[j++];
+ EV_GET_UINT32(ev,&signo,&p,&q);
+ } else if (signo < tmp_signo || !ctxt->events) {
+ /* New signal to select on */
+ events[i] = erl_drv_ose_event_alloc(signo,(int)ctxt->spid);
+ driver_select(ctxt->port,events[i++],ERL_DRV_READ|ERL_DRV_USE,1);
+ EV_GET_UINT32(ev,&signo,&p,&q);
+ } else {
+ /* Remove old signal to select on */
+ driver_select(ctxt->port,ctxt->events[j++],ERL_DRV_READ|ERL_DRV_USE,0);
+ }
+ }
+ if (ctxt->events)
+ driver_free(ctxt->events);
+ }
+ ctxt->events = events;
+ ctxt->event_cnt = event_cnt;
+
+ {
+ ErlDrvTermData output[] = {
+ ERL_DRV_ATOM, a_ose_drv_reply,
+ ERL_DRV_PORT, driver_mk_port(ctxt->port),
+ ERL_DRV_ATOM, a_ok,
+ ERL_DRV_TUPLE, 3};
+ send_response(ctxt->port, output);
+ }
+ break;
+ }
+
+ default:
+ {
+ DEBUGP("Warning: 'ose_signal_drv' unknown command '%d'\n", cmd);
+ break;
+ }
+ }
+}
+
+/**
+ * Handler for when OSE signal arrives
+ **/
+static void ready_input(ErlDrvData driver_data, ErlDrvEvent event)
+{
+ driver_context_t *ctxt = (driver_context_t *)driver_data;
+ union SIGNAL *sig = erl_drv_ose_get_input_signal(event);
+
+ while (sig != NULL) {
+
+ switch(sig->signo)
+ {
+ /* Remote process is available */
+ case ERTS_SIGNAL_OSE_DRV_HUNT:
+ {
+ const PROCESS spid = sender(&sig);
+
+ if (remove_reference(ctxt,sig->async.ref)) {
+ DEBUGP_HUNT("0x%x: Got hunt from 0x%x -> %u (CANCELLED) (%u,%u)\n",
+ ctxt->spid,spid,sig->async.ref,
+ ctxt->outstanding_refs_cnt,
+ ctxt->outstanding_refs_max);
+ /* Already removed by dehunt */
+ } else {
+ ErlDrvTermData reply[] = {
+ ERL_DRV_ATOM, a_mailbox_up,
+ ERL_DRV_PORT, sig->async.port,
+ ERL_DRV_PORT, sig->async.port,
+ ERL_DRV_UINT, (ErlDrvUInt)sig->async.ref,
+ ERL_DRV_TUPLE, 2,
+ ERL_DRV_UINT, (ErlDrvUInt)spid,
+ ERL_DRV_TUPLE, 4};
+ DEBUGP_HUNT("0x%x: Got hunt from 0x%x -> %u (%u,%u)\n",
+ ctxt->spid,spid,sig->async.ref,
+ ctxt->outstanding_refs_cnt,
+ ctxt->outstanding_refs_max);
+ erl_drv_send_term(sig->async.port, sig->async.proc, reply,
+ sizeof(reply) / sizeof(reply[0]));
+ }
+ break;
+ }
+
+ /* Remote process is down */
+ case ERTS_SIGNAL_OSE_DRV_ATTACH:
+ {
+ PROCESS spid = sig->async.target;
+
+ if (remove_reference(ctxt,sig->async.ref)) {
+ DEBUGP_ATTACH("0x%x: Got attach from 0x%x -> %u (CANCELLED) (%u,%u)\n",
+ ctxt->spid,spid,sig->async.ref,
+ ctxt->outstanding_refs_cnt,
+ ctxt->outstanding_refs_max);
+ /* Already removed by detach */
+ } else {
+ ErlDrvTermData reply[] = {
+ ERL_DRV_ATOM, a_mailbox_down,
+ ERL_DRV_PORT, sig->async.port,
+ ERL_DRV_PORT, sig->async.port,
+ ERL_DRV_UINT, sig->async.ref,
+ ERL_DRV_TUPLE, 2,
+ ERL_DRV_UINT, (ErlDrvUInt)spid,
+ ERL_DRV_TUPLE, 4};
+ DEBUGP_ATTACH("0x%x: Got attach from 0x%x -> %u (%u,%u)\n",
+ ctxt->spid,spid,sig->async.ref,
+ ctxt->outstanding_refs_cnt,
+ ctxt->outstanding_refs_max);
+ erl_drv_send_term(sig->async.port, sig->async.proc, reply,
+ sizeof(reply) / sizeof(reply[0]));
+ }
+ break;
+ }
+
+ /* Received user defined signal */
+ default:
+ {
+ const PROCESS spid = sender(&sig);
+ const OSBUFSIZE size = sigsize(&sig) - sizeof(SIGSELECT);
+ const char *sig_data = ((char *)&sig->signo) + sizeof(SIGSELECT);
+
+ ErlDrvTermData reply[] = {
+ ERL_DRV_ATOM, a_message,
+ ERL_DRV_PORT, driver_mk_port(ctxt->port),
+ ERL_DRV_UINT, (ErlDrvUInt)spid,
+ ERL_DRV_UINT, (ErlDrvUInt)ctxt->spid,
+ ERL_DRV_UINT, (ErlDrvUInt)sig->signo,
+ ERL_DRV_BUF2BINARY, (ErlDrvTermData)sig_data, (ErlDrvUInt)size,
+ ERL_DRV_TUPLE, 4,
+ ERL_DRV_TUPLE, 3};
+
+ DEBUGP_SEND("0x%x: Got 0x%u\r\n", spid, sig->signo);
+
+ erl_drv_output_term(driver_mk_port(ctxt->port), reply,
+ sizeof(reply) / sizeof(reply[0]));
+ break;
+ }
+ }
+
+ free_buf(&sig);
+ sig = erl_drv_ose_get_input_signal(event);
+ }
+}
+
+/**
+ * Handler for 'port_control'
+ **/
+static ErlDrvSSizeT control(ErlDrvData driver_data, unsigned int cmd,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen)
+{
+ driver_context_t *ctxt = (driver_context_t *)driver_data;
+
+ switch(cmd)
+ {
+ case GET_SPID:
+ {
+ const PROCESS spid = ctxt->spid;
+ put_u32(spid, *rbuf);
+ return sizeof(PROCESS);
+ }
+
+ case GET_NAME:
+ {
+ const PROCESS spid = get_u32(buf);
+ char *name = (char*)get_pid_info(spid,OSE_PI_NAME);
+ int n;
+ if (!name) {
+ *rbuf = NULL;
+ return 0;
+ }
+
+ if (rlen < (n = strlen(name))) {
+ ErlDrvBinary *bin = driver_alloc_binary(n);
+ strncpy(bin->orig_bytes,name,n);
+ *rbuf = (char*)bin;
+ } else
+ strncpy(*rbuf,name,n);
+ free_buf((union SIGNAL**)&name);
+
+ return n;
+ }
+ default:
+ {
+ /* Unknown command */
+ return (ErlDrvSSizeT)ERL_DRV_ERROR_GENERAL;
+ break;
+ }
+ }
+}
+
+static void stop_select(ErlDrvEvent event, void *reserved)
+{
+ erl_drv_ose_event_free(event);
+}
+
+static int resolve_signal(OseSignal* osig, int *mode) {
+ union SIGNAL *sig = osig;
+ if (sig->signo == ERTS_SIGNAL_OSE_DRV_HUNT ||
+ sig->signo == ERTS_SIGNAL_OSE_DRV_ATTACH) {
+ return sig->async.spid;
+ }
+ DEBUGP("%p: Got signal %d sent to %p from 0x%p\n",
+ current_process(),sig->signo,addressee(&sig),sender(&sig));
+ return addressee(&sig);
+}
+
+/**
+ * Setup the driver entry for the Erlang runtime
+ **/
+ErlDrvEntry ose_signal_driver_entry = {
+ .init = drv_init,
+ .start = drv_start,
+ .stop = drv_stop,
+ .outputv = outputv,
+ .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,
+ .resolve_signal = resolve_signal
+};