diff options
Diffstat (limited to 'erts/emulator/drivers')
-rw-r--r-- | erts/emulator/drivers/ose/ose_signal_drv.c | 884 |
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 +}; |