#include #include "libdi-php.h" #include "socketcan.h" #include "constants.h" #include #include #include #include #include using namespace std; static const char *extension_version = APPLICATION_VERSION; #define le_di_can_ctx_name "di_can_ctx" static int le_di_can_ctx_resid; PHP_FUNCTION(di_can_list) { struct ifaddrs *ifaddr, *ifa; int n; if (getifaddrs(&ifaddr) == -1) RETURN_NULL(); array_init(return_value); for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) { if (strncmp("can", ifa->ifa_name, 3) == 0 || strncmp("vcan", ifa->ifa_name, 4) == 0) { add_next_index_string(return_value, ifa->ifa_name); } } freeifaddrs(ifaddr); } PHP_FUNCTION(di_can_open) { zend_string *iface; struct php_di_can_ctx *c; if (zend_parse_parameters(static_cast(ZEND_NUM_ARGS()), "S", &iface) == FAILURE) RETURN_NULL(); c = php_can_alloc(iface->val); if (!c) RETURN_NULL(); di_can_init(&c->ctx); di_can_set_private_data(&c->ctx, c); /* Glue can stack recv/send with hardware peripherial */ di_can_callback_set_recv(&c->ctx, socketcan_recv_cb); di_can_callback_set_send(&c->ctx, socketcan_send_cb); di_can_frame_init(&c->ctx, c->mm.di_can_frame_pool, DI_CAN_RX_FRAMES); di_buffer_init_array(c->mm.di_can_msg_buf, c->mm.di_can_msg_buf_data, DI_CAN_MESSAGES, DI_CAN_MSG_SIZE); di_can_msg_init_array(&c->ctx, c->mm.di_can_msg_pool, DI_CAN_MESSAGES, c->mm.di_can_msg_buf, DI_CAN_MESSAGES); di_can_msg_init(&c->ctx, c->mm.di_can_msg_pool, DI_CAN_MESSAGES); c->ctx.frame.timeout = UINT32_MAX; c->ctx.msg.timeout = UINT32_MAX; di_can_msg_recv_set_blocking(&c->ctx, true); di_can_msg_recv_set_timeout(&c->ctx, 10); di_can_msg_enable_dst_id_filter(&c->ctx, false); c->running = true; int rc = pthread_create(&c->thr, NULL, php_can_thread_func, reinterpret_cast(c)); if (rc) { php_can_free(&c); RETURN_NULL(); } RETURN_RES(zend_register_resource(c, le_di_can_ctx_resid)); } PHP_FUNCTION(di_can_close) { zval *zc; if (zend_parse_parameters(static_cast(ZEND_NUM_ARGS()), "r", &zc) == FAILURE) RETURN_NULL(); zend_list_delete(Z_RES_P(zc)); RETURN_TRUE; } PHP_FUNCTION(di_version) { array_init(return_value); add_assoc_string(return_value, "build.version", const_cast(APPLICATION_VERSION)); add_assoc_string(return_value, "build.commit", const_cast(APPLICATION_COMMIT)); add_assoc_string(return_value, "build.datetime", const_cast(APPLICATION_BUILD_DATETIME)); } /** Convert di_can_msg struct to zval */ static void php_di_can_msg_to_zval(zval *return_value, const struct di_can_msg *msg) { array_init(return_value); add_assoc_long(return_value, "canid", msg->canid); add_assoc_long(return_value, "ttype", DI_CAN_GET_TRANSFERTYPE(msg->canid)); add_assoc_long(return_value, "msgtype", DI_CAN_GET_MSGTYPE(msg->canid)); if (di_can_msg_emflag_rt(msg)) { add_assoc_bool(return_value, "rt", true); add_assoc_long(return_value, "rt:seqnr", di_can_msg_read_emdata(msg)); } add_assoc_long(return_value, "src_id", msg->src_id); add_assoc_long(return_value, "dst_id", msg->dst_id); add_assoc_long(return_value, "dtype", DI_CAN_GET_DATATYPE(msg->canid)); add_assoc_long(return_value, "ptype", msg->ptype); size_t size = 0; /* We shop of the '\0' character when the payload is a string */ if (msg->ptype == DI_CAN_PTYPE_STRING) size = msg->size - 1; else if (msg->size) size = msg->size; add_assoc_long(return_value, "size", msg->size); if (msg->ptype == DI_CAN_PTYPE_I8 || msg->ptype == DI_CAN_PTYPE_U8) add_assoc_long(return_value, "msg", mpack_load_u8((const char *)msg->msg)); else if (msg->ptype == DI_CAN_PTYPE_I16 || msg->ptype == DI_CAN_PTYPE_U16) add_assoc_long(return_value, "msg", mpack_load_u16((const char *)msg->msg)); else if (msg->ptype == DI_CAN_PTYPE_I32 || msg->ptype == DI_CAN_PTYPE_U32 || msg->ptype == DI_CAN_PTYPE_DI_ERRNO) add_assoc_long(return_value, "msg", mpack_load_u32((const char *)msg->msg)); else if (msg->ptype == DI_CAN_PTYPE_I64 || msg->ptype == DI_CAN_PTYPE_U64) add_assoc_long(return_value, "msg", mpack_load_u64((const char *)msg->msg)); else if (msg->size) add_assoc_stringl(return_value, "msg", reinterpret_cast(msg->msg), static_cast(size)); else add_assoc_null(return_value, "msg"); } PHP_FUNCTION(di_can_recv) { zval *zc; if (zend_parse_parameters(static_cast(ZEND_NUM_ARGS()), "r", &zc) == FAILURE) RETURN_NULL(); php_di_can_ctx *c; c = static_cast(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid)); if (c == NULL) RETURN_NULL(); struct di_can_msg *msg; msg = di_can_recv(&c->ctx, DI_CAN_MSGTYPE_ANY); if (!msg) RETURN_NULL(); php_di_can_msg_to_zval(return_value, msg); di_can_msg_free(&msg); } /** * Queue raw periodic msg with milliseconds * di_can_send_queue(iface(resource), milliseconds(long), msg(array)) * msg['ttype'] = "pub", "req", "rep" * msg['dtype'] = 0x123 (code...) * msg['data'] = "", or NULL */ PHP_FUNCTION(di_can_send_queue) { uint64_t interval_ms = 0; struct php_di_can_ctx *c; zval *zc; zval *zmsg; if (zend_parse_parameters(static_cast(ZEND_NUM_ARGS()), "ral", &zc, &zmsg, &interval_ms) == FAILURE) RETURN_NULL(); c = static_cast(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid)); if (!c) RETURN_FALSE; struct php_can_periodic_item i; php_di_can_send_args_get(zmsg, &i.msg, &c->ctx); lock_guard lock(*c->lock); c->periodic.token++; i.interval = std::chrono::milliseconds(interval_ms); i.deadline = std::chrono::system_clock::now(); (*c->periodic.queue)[c->periodic.token] = i; RETURN_LONG(c->periodic.token); } /** * Dequeue periodic msg */ PHP_FUNCTION(di_can_send_dequeue) { uint64_t token = 0; php_di_can_ctx *c; zval *zc; if (zend_parse_parameters(static_cast(ZEND_NUM_ARGS()), "rl", &zc, &token) == FAILURE) RETURN_NULL(); c = static_cast(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid)); if (!c) RETURN_FALSE; lock_guard lock(*c->lock); auto it = c->periodic.queue->find(static_cast(token)); if (it == c->periodic.queue->end()) RETURN_FALSE; php_di_can_send_args_free(&it->second.msg); c->periodic.queue->erase(it); RETURN_TRUE; } /** * di_can_send($can0, $msg) * $can0 = di_can_open("can0") * msg['ttype'] = "pub", "req", "rep" * msg['dtype'] = "config:set" etc... (the string is automatic converted to binary dtype id) * msg['data'] = "", or NULL */ PHP_FUNCTION(di_can_send) { zval *zc; zval *zv; php_di_can_msg msg; if (zend_parse_parameters(static_cast(ZEND_NUM_ARGS()), "ra", &zc, &zv) == FAILURE) RETURN_NULL(); struct php_di_can_ctx *c; c = static_cast(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid)); if (!c) RETURN_NULL(); php_di_can_send_args_get(zv, &msg, &c->ctx); struct di_can_msg *_msg = &msg.msg; auto ret = di_can_send(&_msg); php_di_can_send_args_free(&msg); if (ret != DNOK) { RETURN_FALSE; } RETURN_TRUE; } PHP_FUNCTION(di_can_send_empty_reply) { zval *zc; zval *zv; php_di_can_msg msg; if (zend_parse_parameters(static_cast(ZEND_NUM_ARGS()), "ra", &zc, &zv) == FAILURE) RETURN_NULL(); struct php_di_can_ctx *c; c = static_cast(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid)); if (!c) RETURN_NULL(); php_di_can_send_args_get(zv, &msg, &c->ctx); struct di_can_msg *_msg = &msg.msg; auto ret = di_can_send_empty_reply(_msg); php_di_can_send_args_free(&msg); if (ret != DNOK) { RETURN_FALSE; } RETURN_TRUE; } /** * di_can_reqrep($can0, $msg, $timeout_ms) * $can0 = di_can_open("can0") * msg['ttype'] = "pub", "req", "rep" * msg['dtype'] = "config:set" etc... (the string is automatic converted to binary dtype id) * msg['data'] = "", or NULL */ PHP_FUNCTION(di_can_reqrep) { zval *zc; zval *zv; int64_t timeout_ms; php_di_can_msg msg; if (zend_parse_parameters(static_cast(ZEND_NUM_ARGS()), "ral", &zc, &zv, &timeout_ms) == FAILURE) RETURN_NULL(); struct php_di_can_ctx *c; c = static_cast(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid)); if (!c) RETURN_NULL(); php_di_can_send_args_get(zv, &msg, &c->ctx); struct di_can_msg *_msg = &msg.msg; di_errno_t ret = di_can_reqrep(&_msg, static_cast(timeout_ms)); php_di_can_send_args_free(&msg); if (ret != DNOK) { RETURN_NULL(); } php_di_can_msg_to_zval(return_value, _msg); di_can_msg_free(&_msg); } /** * di_can_reqrep_with_retry($can0, $msg, $timeout_ms, $retries) * $can0 = di_can_open("can0") * msg['ttype'] = "pub", "req", "rep" * msg['dtype'] = "config:set" etc... (the string is automatic converted to binary dtype id) * msg['data'] = "", or NULL */ PHP_FUNCTION(di_can_reqrep_with_retry) { zval *zc; zval *zv; int64_t timeout_ms; int64_t retries; php_di_can_msg msg; if (zend_parse_parameters(static_cast(ZEND_NUM_ARGS()), "rall", &zc, &zv, &timeout_ms, &retries) == FAILURE) RETURN_NULL(); struct php_di_can_ctx *c; c = static_cast(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid)); if (!c) RETURN_NULL(); php_di_can_send_args_get(zv, &msg, &c->ctx); struct di_can_msg *_msg = &msg.msg; di_errno_t ret = di_can_reqrep_with_retry(&_msg, static_cast(timeout_ms), static_cast(retries)); php_di_can_send_args_free(&msg); if (ret != DNOK) RETURN_NULL(); php_di_can_msg_to_zval(return_value, _msg); di_can_msg_free(&_msg); } static void di_minfo_can_interfaces(void) { struct ifaddrs *ifaddr, *ifa; int n; if (getifaddrs(&ifaddr) == -1) return; for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) { if (strncmp("can", ifa->ifa_name, 3) == 0 || strncmp("vcan", ifa->ifa_name, 4) == 0) { php_info_print_table_row(2, "Interface", ifa->ifa_name); } } freeifaddrs(ifaddr); } ZEND_RSRC_DTOR_FUNC(php_di_can_ctx_dtor) { php_di_can_ctx *c = reinterpret_cast(res->ptr); if (c) php_can_free(&c); } PHP_MINFO_FUNCTION(di) { char *version; spprintf(&version, 0, "%s", APPLICATION_VERSION); php_info_print_table_start(); php_info_print_table_row(2, "libdi-php Version", version); di_minfo_can_interfaces(); php_info_print_table_end(); } PHP_MINIT_FUNCTION(di) { le_di_can_ctx_resid = zend_register_list_destructors_ex(php_di_can_ctx_dtor, NULL, le_di_can_ctx_name, module_number); constants_register(module_number); return SUCCESS; } PHP_FUNCTION(di_can_set_nodeid) { zval *zc; zval *zv; struct php_di_can_ctx *c; if (zend_parse_parameters(static_cast(ZEND_NUM_ARGS()), "rz", &zc, &zv) == FAILURE) RETURN_NULL(); c = static_cast(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid)); if (!c) return; if (Z_TYPE_P(zv) != IS_STRING) RETURN_FALSE; di_can_set_nodeid(&c->ctx, Z_STRVAL(*zv)); RETURN_TRUE; } static zend_function_entry di_functions[] = { PHP_FE(di_version, NULL) PHP_FE(di_can_list, NULL) PHP_FE(di_can_open, NULL) PHP_FE(di_can_close, NULL) PHP_FE(di_can_recv, NULL) PHP_FE(di_can_send, NULL) PHP_FE(di_can_send_empty_reply, NULL) PHP_FE(di_can_reqrep, NULL) PHP_FE(di_can_reqrep_with_retry, NULL) PHP_FE(di_can_send_queue, NULL) PHP_FE(di_can_send_dequeue, NULL) PHP_FE(di_can_set_nodeid, NULL) PHP_FE_END }; static zend_module_entry di_module_entry = { STANDARD_MODULE_HEADER, PHP_DI_PHP_EXTNAME, di_functions, PHP_MINIT(di), NULL, NULL, NULL, PHP_MINFO(di), extension_version, STANDARD_MODULE_PROPERTIES }; ZEND_GET_MODULE(di)