Example #1
0
static int laura_do_sync_call(lua_State *L)
{
	struct laura_node *lnode = lua_fetch_node(L, 1);
	struct aura_buffer *buf, *retbuf;
	struct aura_object *o;
	int ret;

	TRACE();

	o = aura_etable_find(lnode->node->tbl, lnode->current_call);
	if (!o)
		return luaL_error(L, "Attempt to call non-existend method");

	buf = lua_to_buffer(L, lnode->node, 2, o);
	if (!buf)
		return luaL_error(L, "Serializer failed!");

	ret = aura_core_call(lnode->node, o, &retbuf, buf);
	if (ret != 0)
		return luaL_error(L, "Call for %s failed", o->name);

	ret = buffer_to_lua(L, lnode->node, o, retbuf);
	aura_buffer_release(retbuf);
	return ret;
}
Example #2
0
int main() {
	slog_init(NULL, 18);

	int count = 5;
	int ret; 
	struct aura_node *n = aura_open("dummy", NULL);
	aura_wait_status(n, AURA_STATUS_ONLINE);

	struct aura_buffer *retbuf; 
	const struct aura_object *o; 

	aura_enable_sync_events(n, 5);

	while (count--) { 
		ret = aura_get_next_event(n, &o, &retbuf);
		slog(0, SLOG_DEBUG, "evt get ret %d", ret);
		
		aura_hexdump("Out buffer", retbuf->data, retbuf->size);
		aura_buffer_release(retbuf);
	}

	aura_close(n);

	return 0;
}
Example #3
0
static void cb_call_write_done(struct libusb_transfer *transfer)
{
	struct aura_node *node = transfer->user_data;
	struct usb_dev_info *inf = aura_get_transportdata(node);

	if (0 != check_control(transfer)) { 
		/* Put it back to queue. Core will deal with it later */
		goto requeue;
	}

	if (transfer->actual_length - LIBUSB_CONTROL_SETUP_SIZE < transfer->length) { 
		slog(0, SLOG_ERROR, "usb: short-write on call packet want %d got %d  (API mismatch?)",
		     transfer->length, transfer->actual_length);
		usb_panic_and_reset_state(node);
		goto requeue;
	}

	aura_buffer_release(node, inf->current_buffer);
	inf->current_buffer = NULL;
	slog(4, SLOG_DEBUG, "Call write done");
	return;
requeue:
	aura_requeue_buffer(&node->outbound_buffers, inf->current_buffer);
	inf->current_buffer = NULL;
}
Example #4
0
int main() {
        int ret; 
	int i; 
	int passed = 0;
	int failed = 0;
        slog_init(NULL, 0);
        struct aura_node *n = aura_open("simpleusb", "../simpleusbconfigs/susb-test.conf");

        if (!n) { 
                printf("err\n");
                return -1;
        }

	i = 100; 
	while (i--) { 
		printf("Testing %d remaining\r", i);
		fflush(stdout);
		aura_wait_status(n, AURA_STATUS_ONLINE);
		struct aura_buffer *retbuf; 
		ret = aura_call(n, "led_ctl", &retbuf, 0x100, 0x100);
		if (0 == ret) {
			aura_buffer_release(retbuf); 
			passed++;
		} else
			failed++;
	}

        aura_close(n);

	printf("Stability test (no data): %d succeeded, %d failed total %d\n",
	       passed, failed, passed + failed);

        return failed;
}
Example #5
0
int nmppsMax_16sm (const nm16s*  src, int size, int16b*  pMaxValue, nm32s* tmp)
{
	int ret;	
	struct aura_buffer *iobuf_src = aura_buffer_request(n, size*2);	
	memcpy(iobuf_src->data,src,size*2);	
	struct aura_buffer *retbuf; 
	ret = aura_call(n, "nmppsMax_16sm", &retbuf,  iobuf_src, size); 
	if (ret != 0) {
		BUG(n, "Call:nmppsMax_16sm failed!"); 
	}
	*pMaxValue = aura_buffer_get_u32(retbuf);
	ret = aura_buffer_get_u32(retbuf);
	aura_buffer_release( iobuf_src);
	aura_buffer_release( retbuf); 
	slog(3, SLOG_INFO, "ARM: Call nmppsMax_16sm -ok"); 
	return ret;
}	
Example #6
0
	int nmppsFFT512InvInitAlloc(NmppsFFTSpec** spec, const void* src,const void* dst, int settings)
	{
		struct aura_buffer *iobuf_src = aura_buffer_request(n, 512*8);	
		struct aura_buffer *iobuf_dst = aura_buffer_request(n, 512*8);	
		struct aura_buffer *retbuf; 
		int ret =  aura_call(n, "nmppsFFT512InvInitAlloc", &retbuf,  iobuf_src, iobuf_dst, settings); 
		if (ret != 0) {
			slog(0, SLOG_ERROR, "call failed, reason: %d\n", ret);
			BUG(n, "Call nmppsFFT512InvInitAlloc failed!"); 
		}
		*spec = (NmppsFFTSpec*) aura_buffer_get_u32(retbuf);
		ret   = aura_buffer_get_u32(retbuf);
		aura_buffer_release( iobuf_src); 
		aura_buffer_release( iobuf_dst); 
		aura_buffer_release( retbuf); 
		slog(3, SLOG_INFO, "ARM: Call nmppsFFT512InvInitAlloc -ok"); 
		return ret;
	}
Example #7
0
int nmppsCmpNeC_8s8um (const nm8s* src,  int8b  nCmpVal, nm8u* dst,  int size, struct NmppsTmpSpec* spec)
{
	int ret;	
	struct aura_buffer *iobuf_src = aura_buffer_request(n, size*1);	
	struct aura_buffer *iobuf_dst = aura_buffer_request(n, size*1);	
	memcpy(iobuf_src->data,src,size*1);	
	struct aura_buffer *retbuf; 
	ret = aura_call(n, "nmppsCmpNeC_8s8um", &retbuf,  iobuf_src, nCmpVal, iobuf_dst, size); 
	if (ret != 0) {
		BUG(n, "Call:nmppsCmpNeC_8s8um failed!"); 
	}
	memcpy(dst,iobuf_dst->data,size); 
	aura_buffer_release( iobuf_dst); 
	aura_buffer_release( iobuf_src);
	aura_buffer_release( retbuf); 
	slog(3, SLOG_INFO, "ARM: Call nmppsCmpNeC_8s8um -ok"); 
	return 0;
}
Example #8
0
long run_first(struct aura_node *n)
{
	struct aura_buffer *retbuf;
	int i;
	long start = current_time();
	for (i=0; i<90000; i++) {
		aura_call(n, "echo_u16", &retbuf, 0x0102);
		aura_buffer_release(retbuf);
		aura_call(n, "echo_u8", &retbuf, 0x0102);
		aura_buffer_release(retbuf);
		aura_call(n, "echo_u32", &retbuf, 0x0102);
		aura_buffer_release(retbuf);
		aura_call(n, "echo_u64", &retbuf, 0x0102);
		aura_buffer_release(retbuf);
	}
	return current_time() - start;

}
Example #9
0
	void nmppsFFT512Inv(const nm32sc* src, nm32sc* dst, const NmppsFFTSpec* spec)
	{
		int ret;	
		int size=512;
		int k=8;
		struct aura_buffer *iobuf_src = aura_buffer_request(n, size*k);	
		struct aura_buffer *iobuf_dst = aura_buffer_request(n, size*k);	
		memcpy(iobuf_src->data,src,size*k);	
		struct aura_buffer *retbuf; 
		ret = aura_call(n, "nmppsFFT512Inv", &retbuf,  iobuf_src, iobuf_dst, spec); 
		if (ret != 0) {
			slog(0, SLOG_ERROR, "call nmppsFFT512Inv  failed, reason: %d\n", ret);
			BUG(n, "Call nmppsFFT512Inv failed!"); 
		}
		memcpy(dst,iobuf_dst->data,size*k);
		aura_buffer_release( iobuf_dst); 
		aura_buffer_release( iobuf_src); 
		aura_buffer_release( retbuf); 
		slog(3, SLOG_INFO, "ARM: Call nmppsFFT512Inv -ok"); 
	}
Example #10
0
long run_second(struct aura_node *n)
{
	struct aura_buffer *buf;
	long start = current_time();
	int i;
	for (i=0; i<90000; i++) {
		buf = aura_buffer_request(n, (rand() % 512) + 1);
		aura_buffer_release(buf);
	}
	return current_time() - start;
}
Example #11
0
void nmppsFFTFree(NmppsFFTSpec* spec )
{
	int ret;	
	struct aura_buffer *retbuf; 
	ret = aura_call(n, "nmppsFFTFree", &retbuf, spec); 
	if (ret != 0) 
	    BUG(n, "Call nmppsFFTFree failed!"); 
	aura_buffer_release( retbuf); 
	slog(3, SLOG_INFO, "ARM: Call nmppsFFTFree ok"); 

}	
Example #12
0
int main() {
        int ret; 
        int i = 2; 
        slog_init(NULL, 88);
        struct aura_node *n = aura_open("simpleusb", "./simpleusbconfigs/pw-ctl.conf");

        if (!n) { 
                printf("err\n");
                return -1;
        }
        aura_wait_status(n, AURA_STATUS_ONLINE);

        struct aura_buffer *retbuf; 
        ret = aura_call(n, "bit_set", &retbuf, 12<<8 | 1, 1);
        slog(0, SLOG_DEBUG, "call ret %d", ret);
        if (0 == ret) {
                printf("====> buf pos %d len %d\n", retbuf->pos, retbuf->size);
		aura_buffer_release(n, retbuf); 
        }

	int v = 1; 	
	while(1) {
		v = !v;
		printf("<=================>\n");
		ret = aura_call(n, "bit_set", &retbuf, 12<<8, v);
		slog(0, SLOG_DEBUG, "call ret %d", ret);
		if (0 == ret) {
			printf("====> buf pos %d len %d\n", retbuf->pos, retbuf->size);
			aura_buffer_release(n, retbuf); 
		} else {
			aura_wait_status(n, AURA_STATUS_ONLINE);
			i--;
			if (!i)
				goto bailout;
		}
		sleep(1);
        }
bailout:
        aura_close(n);
        return 0;
}
Example #13
0
void test_buf(struct aura_node *n)
{
    int ret;
    struct aura_buffer *retbuf;
    struct aura_buffer *iobuf = aura_buffer_request(n, 80);
    uint32_t test = 0xdeadf00d;
    memcpy(iobuf->data, &test, sizeof(test));

    ret = aura_call(n, "echo_buf", &retbuf, iobuf);
    slog(0, SLOG_DEBUG, "call ret %d", ret);
    if (ret)
        BUG(n, "call failed");

    struct aura_buffer *tmp = aura_buffer_get_buf(retbuf);
    if (tmp != iobuf)
        BUG(n, "test not ok");

    aura_buffer_release(n, retbuf);
    aura_buffer_release(n, tmp);
    slog(0, SLOG_INFO, "BUF test passed");
}
Example #14
0
int main() {
	slog_init(NULL, 18);

	int ret; 
	struct aura_node *n = aura_open("dummy", NULL);
	struct aura_buffer *retbuf; 
	struct aura_buffer *iobuf = aura_buffer_request(n, 80);

	ret = aura_call(n, "echo_buf", &retbuf, iobuf);
	slog(0, SLOG_DEBUG, "call ret %d", ret);
	if (ret)
		BUG(n, "call failed");
	struct aura_buffer *tmp = aura_buffer_get_buf(retbuf);
	if (tmp != iobuf)
		BUG(n, "test not ok");

	aura_buffer_release(n, retbuf);
	aura_buffer_release(n, tmp);
	aura_close(n);

	return 0;
}
Example #15
0
static void cleanup_buffer_queue(struct list_head *q)
{
    int i = 0;

    struct list_head *pos, *tmp;
    list_for_each_safe(pos, tmp, q) {
        struct aura_buffer *b;
        b = list_entry(pos, struct aura_buffer, qentry);
        list_del(pos);
        aura_buffer_release(NULL, b);
        i++;
    }
    slog(6, SLOG_LIVE, "Cleaned up %d buffers", i);
}
Example #16
0
void test_u64(struct aura_node *n)
{
    int ret;
    struct aura_buffer *retbuf;
    ret = aura_call(n, "echo64", &retbuf, 0xbeefc0deb00bc0de);
    if (ret != 0)
        BUG(n, "Call failed!");

    uint64_t v = aura_buffer_get_u64(retbuf);
    if (v != 0xbeefc0deb00bc0de)
        slog(0, SLOG_ERROR, "U64 test NOT ok: %llx vs %llx", v, 0xbeefc0deb00bc0de);
    aura_buffer_release(n, retbuf);
    slog(0, SLOG_INFO, "U64 echo test passed");
}
Example #17
0
static void gpio_handle_event(struct aura_node *node, enum node_event evt, const struct aura_pollfds *fd)
{
	struct aura_buffer *buf;
	struct aura_object *o;

	while (1) {
		buf = aura_dequeue_buffer(&node->outbound_buffers);
		if (!buf)
			break;
		o = buf->object;
		handle_outbound(node, o, buf);
		aura_buffer_release(buf);
	}
}
Example #18
0
void test_u32(struct aura_node *n)
{
    int ret;
    struct aura_buffer *retbuf;
    ret = aura_call(n, "echo32", &retbuf, 0xbeefc0de);
    if (ret != 0)
        BUG(n, "Call failed!");

    uint32_t v = aura_buffer_get_u32(retbuf);
    if (v != 0xbeefc0de) {
        slog(0, SLOG_ERROR, "U32 test NOT ok: %llx vs %llx", v, 0xbeefc0de);
    }
    aura_buffer_release(n, retbuf);
    slog(0, SLOG_INFO, "U32 echo test passed");
}
Example #19
0
int main() {
	slog_init(NULL, 18);

	int ret; 
	struct aura_node *n = aura_open("dummy", NULL);
	struct aura_buffer *retbuf; 

	ret = aura_call(n, "echo_u16", &retbuf, 0x0102);
	slog(0, SLOG_DEBUG, "call ret %d", ret);
	aura_hexdump("Out buffer", retbuf->data, retbuf->size);
	aura_buffer_release(n, retbuf);
	aura_close(n);

	return 0;
}
Example #20
0
static void cb_event_readout_done(struct libusb_transfer *transfer)
{
	struct aura_node *node = transfer->user_data;
	struct usb_dev_info *inf = aura_get_transportdata(node);
	struct usb_event_packet *evt; 
	struct aura_buffer *buf = inf->current_buffer;
	struct aura_object *o; 
 
	if (0 != check_control(transfer))
		goto ignore;

	if (transfer->actual_length < sizeof(struct usb_event_packet))
		goto ignore;
	
	evt = (struct usb_event_packet *) libusb_control_transfer_get_data(transfer);
	o = aura_etable_find_id(node->tbl, evt->id); 
	if (!o) {
		slog(0, SLOG_ERROR, "usb: got bogus event id from device %d, resetting", evt->id);
		goto panic;
	}
	
	if ((transfer->actual_length - LIBUSB_CONTROL_SETUP_SIZE) < 
	    (sizeof(struct usb_event_packet) + o->retlen)) {
		slog(0, SLOG_ERROR, "usb: short read for evt %d: %d bytes expected %d got",
		     evt->id, o->retlen + sizeof(struct usb_event_packet), 
		     transfer->actual_length);
		goto panic;
	}
	
	inf->pending--;
	slog(4, SLOG_DEBUG, "Event readout completed, %d bytes, %d evt left", 
	     transfer->actual_length, inf->pending);
	buf->object = o; 
	/* Position the buffer at the start of the responses */
	buf->pos = LIBUSB_CONTROL_SETUP_SIZE + sizeof(struct usb_event_packet);
	aura_queue_buffer(&node->inbound_buffers, buf);
	return; 

panic: 
	usb_panic_and_reset_state(node);
ignore:
	aura_buffer_release(node, buf);
	inf->current_buffer = NULL;
	return;
}
Example #21
0
void test_u32u32(struct aura_node *n)
{
    int ret;
    struct aura_buffer *retbuf;
    ret = aura_call(n, "echou32u32", &retbuf, 0xbeefc0de, 0xdeadc0de);
    if (ret != 0)
        BUG(n, "Call failed!");

    uint32_t v1 = aura_buffer_get_u32(retbuf);
    uint32_t v2 = aura_buffer_get_u32(retbuf);

    if ((v1 != 0xbeefc0de) && (v2 != 0xdeadc0de)) {
        slog(0, SLOG_ERROR, "U32 test NOT ok: %llx,%llx vs %llx,%llx",
             v1, v2, 0xbeefc0de, 0xdeadc0de);
    }

    aura_buffer_release(n, retbuf);
    slog(0, SLOG_INFO, "U32U32 echo test passed");
}
Example #22
0
void test_bin(struct aura_node *n)
{
	char buf[64];
	int ret;
	FILE *fd = fopen("/dev/urandom", "r+");

	fread(buf, 64, 1, fd);
	fclose(fd);
	struct aura_buffer *retbuf;

	ret = aura_call(n, "echobin", &retbuf, buf);
	if (ret != 0)
		BUG(n, "Call failed!");

	if (0 != memcmp(buf, retbuf->data, 64))
		slog(0, SLOG_ERROR, "BIN test NOT ok");
	aura_hexdump("Out buffer", buf, 64);
	aura_hexdump("In buffer", retbuf->data, 64);
	aura_buffer_release(retbuf);
	slog(0, SLOG_INFO, "BIN test passed");
}
Example #23
0
/* This one is small, but tricky */
static void aura_handle_inbound(struct aura_node *node)
{
    while(1) {
        struct aura_buffer *buf;
        struct aura_object *o;

        buf = aura_dequeue_buffer(&node->inbound_buffers);
        if (!buf)
            break;

        o = buf->object;
        node->current_object = o;
        aura_buffer_rewind(buf);

        slog(4, SLOG_DEBUG, "Handling %s id %d (%s) sync_call_running=%d",
             object_is_method(o) ? "response" : "event",
             o->id, o->name, node->sync_call_running);

        if (object_is_method(o) && !o->pending) {
            slog(0, SLOG_WARN, "Dropping orphan call result %d (%s)",
                 o->id, o->name);
            aura_buffer_release(node, buf);
        } else if (o->calldonecb) {
            slog(4, SLOG_DEBUG, "Callback for method/event %d (%s)",
                 o->id, o->name);
            o->calldonecb(node, AURA_CALL_COMPLETED, buf, o->arg);
            aura_buffer_release(node, buf);
        } else if (object_is_method(o) && (node->sync_call_running)) {
            slog(4, SLOG_DEBUG, "Completing call for method %d (%s)",
                 o->id, o->name);
            node->sync_call_result = AURA_CALL_COMPLETED;
            node->sync_ret_buf = buf;
            o->pending--;
            if (o->pending < 0)
                BUG(node, "Internal BUG: pending evt count lesser than zero");
        } else {
            /* This one is tricky. We have an event with no callback */
            if (node->sync_event_max > 0) { /* Queue it up into event_queue if it's enabled */
                /* If we have an overrun - drop the oldest event to free up space first*/
                if (node->sync_event_max <= node->sync_event_count) {
                    struct aura_buffer *todrop;
                    const struct aura_object *dummy;
                    int ret = aura_get_next_event(node, &dummy, &todrop);
                    if (ret != 0)
                        BUG(node, "Internal bug, no next event");
                    aura_buffer_release(node, todrop);
                }

                /* Now just queue the next one */
                aura_queue_buffer(&node->event_buffers, buf);
                node->sync_event_count++;
                slog(4, SLOG_DEBUG, "Queued event %d (%s) for sync readout",
                     o->id, o->name);
            } else {
                /* Last resort - try the catch-all event callback */
                if (node->unhandled_evt_cb)
                    node->unhandled_evt_cb(node, buf, node->unhandled_evt_arg);
                else /* Or just drop it with a warning */
                    slog(0, SLOG_WARN, "Dropping event %d (%s)",
                         o->id, o->name);
                aura_buffer_release(node, buf);
            }
        }
    }

    node->current_object = NULL;
}
Example #24
0
static struct aura_buffer *lua_to_buffer(lua_State *L, struct aura_node *node, int stackpos, struct aura_object *o)
{
	int i;
	struct aura_buffer *buf;
	const char *fmt;

	fmt = o->arg_fmt;
	if (lua_gettop(L) - stackpos + 1 != o->num_args) {
		slog(0, SLOG_ERROR, "Invalid argument count for %s: %d / %d",
		     o->name, lua_gettop(L) - stackpos, o->num_args);
		return NULL;
	}

	buf = aura_buffer_request(node, o->arglen);
	if (!buf) {
		slog(0, SLOG_ERROR, "Epic fail during buffer allocation");
		return NULL;
	}

	/* Let's serialize the data, arguments are on the stack,
	 * Starting from #3.
	 */

	for (i = stackpos; i <= lua_gettop(L); i++) {
		double tmp;

		switch (*fmt++) {
		case URPC_U8:
			tmp = lua_tonumber(L, i);
			aura_buffer_put_u8(buf, tmp);
			break;
		case URPC_S8:
			tmp = lua_tonumber(L, i);
			aura_buffer_put_s8(buf, tmp);
			break;
		case URPC_U16:
			tmp = lua_tonumber(L, i);
			aura_buffer_put_u16(buf, (uint16_t)tmp);
			break;
		case URPC_S16:
			tmp = lua_tonumber(L, i);
			aura_buffer_put_s16(buf, tmp);
			break;
		case URPC_U32:
			tmp = lua_tonumber(L, i);
			aura_buffer_put_u32(buf, tmp);
			break;
		case URPC_S32:
			tmp = lua_tonumber(L, i);
			aura_buffer_put_s32(buf, tmp);
			break;
		case URPC_S64:
			tmp = lua_tonumber(L, i);
			aura_buffer_put_s64(buf, tmp);
			break;
		case URPC_U64:
			tmp = lua_tonumber(L, i);
			aura_buffer_put_u64(buf, tmp);
			break;

		/* Binary is the tricky part. String or usata? */
		case URPC_BIN:
		{
			const char *srcbuf = NULL;
			int len = 0;
			int blen;
			if (lua_isstring(L, i)) {
				srcbuf = lua_tostring(L, i);
				len = strlen(srcbuf);
			} else if (lua_isuserdata(L, i)) {
				srcbuf = lua_touserdata(L, i);
			}

			blen = atoi(fmt);

			if (blen == 0) {
				slog(0, SLOG_ERROR, "Internal serilizer bug processing: %s", fmt);
				goto err;
			}

			if (!srcbuf) {
				slog(0, SLOG_ERROR, "Internal bug fetching src pointer");
				goto err;
			}

			if (blen < len)
				len = blen;

			aura_buffer_put_bin(buf, srcbuf, len);

			while (*fmt && (*fmt++ != '.'));

			break;
		}
		default:
			BUG(node, "Unknown token: %c\n", *(--fmt));
			break;
		}
	}

	return buf;
err:
	aura_buffer_release(buf);
	return NULL;
}
Example #25
0
static int laura_do_async_call(lua_State *L)
{
	struct laura_node *lnode = NULL;
	const char *name;
	struct aura_buffer *buf;
	struct aura_object *o;
	int ret;
	int callback_ref;

	TRACE();

	/* Sanity */
	lnode = lua_fetch_node(L, 1);
	if (!lnode) {
		lua_stackdump(L);
		return aura_typeerror(L, 1, "userdata (node)");
	}

	if (!lua_isstring(L, 2)) {
		lua_stackdump(L);
		return aura_typeerror(L, 2, "string (object name)");
	}

	if (!lua_isfunction(L, 3)) {
		lua_stackdump(L);
		return aura_typeerror(L, 3, "function (callback)");
	}

	name = lua_tostring(L, 2);

	o = aura_etable_find(lnode->node->tbl, name);
	if (!o)
		return luaL_error(L, "Attempt to call non-existend method");

	if (!object_is_method(o)) {
		lua_stackdump(L);
		return luaL_error(L, "Attempt to call an event");
	}


	/* Now we're sane! */
	buf = lua_to_buffer(L, lnode->node, 5, o);
	if (!buf)
		return luaL_error(L, "Serializer failed!");

	/* Let's create a table to store our callback and arg */
	lua_newtable(L);

	/* Push the callback function there */
	lua_pushnumber(L, 1);
	lua_pushvalue(L, 3);
	lua_settable(L, -3);

	/* And the user argument */
	lua_pushnumber(L, 2);
	lua_pushvalue(L, 4);
	lua_settable(L, -3);

	/* And fetch the reference to out table that we'll use in callback */
	callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
	slog(4, SLOG_DEBUG, "Callback tbl reference: %d", callback_ref);

	ret = aura_core_start_call(lnode->node, o, calldone_cb, (void *)(long)callback_ref, buf);
	if (ret != 0) {
		aura_buffer_release(buf);
		return luaL_error(L, "Async call for %s failed: %s code (%d)", o->name, strerror(-ret), ret);
	}

	return 0;
}