457 lines
12 KiB
C++
457 lines
12 KiB
C++
#include <chrono>
|
|
|
|
#include "libdi-php.h"
|
|
#include "socketcan.h"
|
|
#include "constants.h"
|
|
|
|
|
|
#include <di/rpc.h>
|
|
#include <di/errno.h>
|
|
#include <di/version.h>
|
|
|
|
#include <ifaddrs.h>
|
|
#include <ext/standard/info.h>
|
|
|
|
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<int>(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<void *>(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<int>(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<char *>(APPLICATION_VERSION));
|
|
add_assoc_string(return_value, "build.commit", const_cast<char *>(APPLICATION_COMMIT));
|
|
add_assoc_string(return_value, "build.datetime", const_cast<char *>(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<char *>(msg->msg), static_cast<uint>(size));
|
|
else
|
|
add_assoc_null(return_value, "msg");
|
|
}
|
|
|
|
PHP_FUNCTION(di_can_recv) {
|
|
zval *zc;
|
|
|
|
if (zend_parse_parameters(static_cast<int>(ZEND_NUM_ARGS()), "r", &zc) == FAILURE)
|
|
RETURN_NULL();
|
|
|
|
php_di_can_ctx *c;
|
|
|
|
c = static_cast<php_di_can_ctx *>(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'] = "<binary>", 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<int>(ZEND_NUM_ARGS()), "ral", &zc, &zmsg, &interval_ms) == FAILURE)
|
|
RETURN_NULL();
|
|
|
|
c = static_cast<php_di_can_ctx *>(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<mutex> 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<int>(ZEND_NUM_ARGS()), "rl", &zc, &token) == FAILURE)
|
|
RETURN_NULL();
|
|
|
|
c = static_cast<php_di_can_ctx *>(zend_fetch_resource(Z_RES_P(zc), le_di_can_ctx_name, le_di_can_ctx_resid));
|
|
if (!c)
|
|
RETURN_FALSE;
|
|
|
|
lock_guard<mutex> lock(*c->lock);
|
|
|
|
auto it = c->periodic.queue->find(static_cast<unsigned int>(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'] = "<messagepack data>", or NULL
|
|
*/
|
|
PHP_FUNCTION(di_can_send) {
|
|
zval *zc;
|
|
zval *zv;
|
|
|
|
php_di_can_msg msg;
|
|
|
|
if (zend_parse_parameters(static_cast<int>(ZEND_NUM_ARGS()), "ra", &zc, &zv) == FAILURE)
|
|
RETURN_NULL();
|
|
|
|
struct php_di_can_ctx *c;
|
|
|
|
c = static_cast<php_di_can_ctx *>(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<int>(ZEND_NUM_ARGS()), "ra", &zc, &zv) == FAILURE)
|
|
RETURN_NULL();
|
|
|
|
struct php_di_can_ctx *c;
|
|
|
|
c = static_cast<php_di_can_ctx *>(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'] = "<messagepack 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<int>(ZEND_NUM_ARGS()), "ral", &zc, &zv, &timeout_ms) == FAILURE)
|
|
RETURN_NULL();
|
|
|
|
struct php_di_can_ctx *c;
|
|
|
|
c = static_cast<php_di_can_ctx *>(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<uint32_t>(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'] = "<messagepack 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<int>(ZEND_NUM_ARGS()), "rall", &zc, &zv, &timeout_ms, &retries) == FAILURE)
|
|
RETURN_NULL();
|
|
|
|
struct php_di_can_ctx *c;
|
|
|
|
c = static_cast<php_di_can_ctx *>(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<uint32_t>(timeout_ms), static_cast<size_t>(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<php_di_can_ctx *>(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<int>(ZEND_NUM_ARGS()), "rz", &zc, &zv) == FAILURE)
|
|
RETURN_NULL();
|
|
|
|
c = static_cast<php_di_can_ctx *>(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)
|