blob: 36108c547ad35725afba674d1a3b4c97c6f217bd [file] [log] [blame]
/******************************************************************************
*(C) Copyright 2014 Marvell International Ltd.
* All Rights Reserved
******************************************************************************/
/* -------------------------------------------------------------------------------------------------------------------
*
* Filename: chl_agent_ubus.c
*
* Authors: Hagai zalach
*
* Description: An agent to the CHL application
*
* HISTORY:
*
* AUG 13, 2015 - Initial Version
*
* Notes:
*
******************************************************************************/
/******************************************************************************
* Include files
******************************************************************************/
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <include/log.h>
#include <uci.h>
#include <libubox/blob.h>
#include <libubox/blobmsg.h>
#include <libubox/ustream.h>
#include <ubusmsg.h>
#include "ml_utils.h"
#include "chl_agent_ubus.h"
/******************************************************************************
* Defines
******************************************************************************/
/*Static Declarations*/
static int agt_ubus_subscribe_event(struct ubus_subscriber *s,
const char *e);
static int agt_ubus_unsubscribe_event(struct ubus_subscriber *s,
const char *e);
static int chl_agt_reload(struct ubus_context *ctx,
struct ubus_object *obj, struct ubus_request_data *req,
const char *method, struct blob_attr *msg);
static int agt_chl_indication_cb(struct ubus_context *uctx,
struct ubus_object *obj, struct ubus_request_data *req,
const char *method, struct blob_attr *msg);
static void agt_chl_register_cb(struct ubus_request *req, int type, struct blob_attr *msg);
static void agt_chl_open_cb(struct ubus_request *req, int type, struct blob_attr *msg);
static void agt_chl_unregister_cb(struct ubus_request *req, int type, struct blob_attr *msg);
static void agt_chl_close_cb(struct ubus_request *req, int type, struct blob_attr *msg);
static int agt_route_op( char *route , int pdp_id, bool add);
static void agt_chl_route_cb(struct ubus_request *req, int type, struct blob_attr *msg);
static int agt_ubus_invoke(struct blob_buf *b,
uint32_t id, const char *method, ubus_data_handler_t cb,
void *priv, unsigned int timeout);
static void agt_set_lte_dflt_cb(struct ubus_request *req, int type, struct blob_attr *msg);
static int chl_is_ready(void);
/******************************************************************************
* Structs
******************************************************************************/
static struct ubus_context *ubus_ctx;
static struct blob_buf blob;
static struct ubus_subscriber chl_subscriber;
static unsigned int chl_id;
static const struct ubus_method chl_agent_ubus_methods[] = {
UBUS_METHOD_NOARG("reload", chl_agt_reload),
};
static struct ubus_object_type agt_ubus_object_type = UBUS_OBJECT_TYPE("chl_agent", chl_agent_ubus_methods);
static struct ubus_object chl_agent_ubus_object = {
.name = "chl_agent",
.type = &agt_ubus_object_type,
.methods = chl_agent_ubus_methods,
.n_methods = ARRAY_SIZE(chl_agent_ubus_methods),
};
/******************************************************************************
* Methods
******************************************************************************/
/*Definitions*/
static int agt_ubus_subscribe_event(struct ubus_subscriber *s,
const char *e)
{
uint32_t id;
int ret;
ret = ubus_lookup_id(ubus_ctx, e, &id);
if (ret) {
AGT_ERR("event %s lookup failed\n", e);
return ret;
}
ret = ubus_subscribe(ubus_ctx, s, id);
if (ret) {
AGT_ERR("event %s subscribe failed (err: %s)\n",
e, ubus_strerror(ret));
/*try to unsubscribe*/
ret = ubus_unsubscribe(ubus_ctx, s, id);
if (ret) {
AGT_ERR("event %s unsubscribe failed (err: %s)\n",
e, ubus_strerror(ret));
return ret;
}
AGT_DBG("unsubscribe event %s success\n", e);
return 1;
}
AGT_DBG("subscribe event %s success\n", e);
return 0;
}
static int agt_ubus_unsubscribe_event(struct ubus_subscriber *s,
const char *e)
{
uint32_t id;
int ret;
ret = ubus_lookup_id(ubus_ctx, e, &id);
if (ret) {
AGT_ERR("event %s lookup failed\n", e);
return ret;
}
ret = ubus_unsubscribe(ubus_ctx, s, id);
if (ret) {
AGT_ERR("event %s unsubscribe failed (err: %s)\n",
e, ubus_strerror(ret));
return ret;
}
AGT_DBG("unsubscribe event %s success\n", e);
return 0;
}
static int chl_agt_reload(struct ubus_context *ctx,
struct ubus_object *obj, struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
AGT_INFO("chl_agent start reload method\n");
update_configuration();
return UBUS_STATUS_OK;
}
static int agt_chl_indication_cb(struct ubus_context *uctx,
struct ubus_object *obj, struct ubus_request_data *req,
const char *method, struct blob_attr *msg)
{
int ret = 0,pdp_id = -1,nw_status =-1;
struct blob_attr *tb[_CHL_IND_RESP_MAX];
AGT_DBG("got CHL indication\n");
ret = blobmsg_parse(chl_ind_pol, ARRAY_SIZE(chl_ind_pol), tb,
blob_data(msg), blob_len(msg));
if (ret || !tb[CHL_IND_ID] || !tb[CHL_IND_NW_STATUS])
{
AGT_ERR("invalid chl indication, ret=%d\n",ret);
return 1;
}
if (tb[CHL_IND_ID])
pdp_id = blobmsg_get_u32(tb[CHL_IND_ID]);
if (tb[CHL_IND_NW_STATUS])
nw_status = blobmsg_get_u32(tb[CHL_IND_NW_STATUS]);
AGT_DBG("CHL indication: id=%d,nw_status=%s\n", pdp_id, nw_status_to_str(nw_status));
ind_handler(pdp_id,nw_status);
return 0;
}
int ubus_init(void)
{
ubus_ctx = ubus_connect(NULL);
if (!ubus_ctx) {
AGT_ERR("Failed to connect to ubus\n");
return 1;
}
uloop_init();
ubus_add_uloop(ubus_ctx);
return 0;
}
int disable_chl_indications(void)
{
int ret = 0;
ret = agt_ubus_unsubscribe_event(&chl_subscriber,"chl.ind");
if (ret) {
AGT_ERR("Failed to disable indications %s\n", ubus_strerror(ret));
return ret;
}
ret = ubus_unregister_subscriber(ubus_ctx, &chl_subscriber);
if (ret)
AGT_ERR("Failed to unregister subscriber %s\n", ubus_strerror(ret));
return ret;
}
int enable_chl_indications(void)
{
int ret = 0;
ret = ubus_register_subscriber(ubus_ctx, &chl_subscriber);
if (ret) {
AGT_ERR("Failed to register subscriber %s\n", ubus_strerror(ret));
return ret;
}
chl_subscriber.cb = agt_chl_indication_cb;
ret = agt_ubus_subscribe_event(&chl_subscriber,"chl.ind");
if (ret) {
AGT_ERR("Failed to enable chl indications %s\n", ubus_strerror(ret));
return ret;
}
return ret;
}
void chl_agt_ubus_exit(int i)
{
if (i>1)
ubus_remove_object(ubus_ctx, &chl_agent_ubus_object);
if (i>0)
ubus_free(ubus_ctx);
}
static int chl_is_ready(void)
{
if (!ubus_lookup_id(ubus_ctx, "chl", &chl_id))
return 1;
return 0;
}
int chl_agt_lookup_chl(void)
{
int retries = 0;
do {
if (chl_is_ready())
goto ready;
sleep(1);
} while (retries++ < CHL_MAX_RETRIES);
AGT_ERR("Failed to look up CHL object\n");
return 1;
ready:
return 0;
}
int add_chl_agt_obj(void)
{
return ubus_add_object(ubus_ctx, &chl_agent_ubus_object);
}
int agt_register_pdp(struct agent_pdp *pdp, int num)
{
int ret;
char buf[20];
struct chl_agt_reg_response resp;
enum chl_reg_req_status req_status;
AGT_DBG("register pdp to CHL\n");
if (pdp->is_default_gw)
strcpy(resp.method,"register_internet");
else
strcpy(resp.method,"register");
sprintf (buf, "chl_agent_pdp%d", num);
ret = blob_buf_init(&blob, 0);
if (ret) {
AGT_ERR("blob_buf_init failed: %d\n", ret);
return ret;
}
blobmsg_add_string(&blob, CHL_REG_NAME_STR, buf);
blobmsg_add_string(&blob, CHL_REG_APN_STR, pdp->apn);
blobmsg_add_u32(&blob, CHL_REG_IP_TYPE_STR, pdp->ip_type);
ret = agt_ubus_invoke(&blob, chl_id, resp.method, agt_chl_register_cb, &resp, CHL_REQ_TIMEOUT);
if (ret){
AGT_ERR("invoke %s failed: %s\n",resp.method, ubus_strerror(ret));
return 1;
}
req_status = resp.req_status;
switch (req_status) {
case CHL_REG_OK:
AGT_DBG("chl %s response: id=%d req=%s\n",resp.method, resp.id,reg_req_status_to_str(req_status) );
pdp->id = resp.id;
pdp->pdp_state = DATA_PENDING;
return 0;
default:
AGT_ERR("chl %s response: id=%d req=%s\n",resp.method, resp.id,reg_req_status_to_str(req_status) );
pdp->id = resp.id;
pdp->pdp_state = DATA_UNAVAILABLE;
return 1;
}
return 0;
}
static void agt_chl_register_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
int ret;
struct blob_attr *tb_table[_CHL_REG_TABLE_MAX];
struct blob_attr *tb[_CHL_REG_RESP_MAX];
char *method = ((struct chl_agt_reg_response *)(req->priv))->method;
ret = blobmsg_parse(chl_table_pol, ARRAY_SIZE(chl_table_pol), tb_table,
blob_data(msg), blob_len(msg));
if (ret || !tb_table[CHL_REG_TABLE]){
AGT_ERR("invalid call to %s, ret=%d\n",method,ret);
AGT_ERR("unable to parse table\n");
}
ret = blobmsg_parse(chl_reg_resp_pol, ARRAY_SIZE(chl_reg_resp_pol), tb,
blobmsg_data(tb_table[CHL_REG_TABLE]), blobmsg_data_len(tb_table[CHL_REG_TABLE]));
if (ret || !tb[CHL_REG_REQ_STATUS])
AGT_ERR("invalid call to %s, ret=%d\n",method,ret);
if (tb[CHL_REG_REQ_STATUS])
((struct chl_agt_reg_response *)(req->priv))->req_status = blobmsg_get_u32(tb[CHL_REG_REQ_STATUS]);
if (tb[CHL_REG_PDP_ID])
((struct chl_agt_reg_response *)(req->priv))->id = blobmsg_get_u32(tb[CHL_REG_PDP_ID]);
}
int agt_open_pdp( struct agent_pdp *pdp )
{
int ret;
struct chl_agt_open_response resp;
enum chl_req_status req_status;
AGT_DBG("send open pdp request to CHL\n");
ret = blob_buf_init(&blob, 0);
if (ret) {
AGT_ERR("blob_buf_init failed: %d\n", ret);
return ret;
}
blobmsg_add_u32(&blob, CHL_ID_STR, pdp->id);
ret = agt_ubus_invoke(&blob, chl_id, "open", agt_chl_open_cb, &resp, CHL_REQ_TIMEOUT);
if (ret){
AGT_ERR("invoke open failed: %s\n", ubus_strerror(ret));
return 1;
}
req_status = resp.req_status;
switch (req_status) {
case CHL_OK:
AGT_DBG("chl open response: req=%s, nw_status=%s, ril_status=%d\n"
, req_status_to_str(req_status), nw_status_to_str(resp.nw_status), resp.ril_status);
pdp->pdp_state = resp.nw_status;
return 0;
default:
AGT_ERR("chl open response: req=%s, nw_status=%s, ril_status=%d\n"
, req_status_to_str(req_status), nw_status_to_str(resp.nw_status), resp.ril_status);
pdp->pdp_state = resp.nw_status;
return 1;
}
return 0;
}
static void agt_chl_open_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
int ret;
struct blob_attr *tb_table[_CHL_REG_TABLE_MAX];
struct blob_attr *tb[_CHL_OPEN_RESP_MAX];
ret = blobmsg_parse(chl_table_pol, ARRAY_SIZE(chl_table_pol), tb_table,
blob_data(msg), blob_len(msg));
if (ret || !tb_table[CHL_REG_TABLE]){
AGT_ERR("invalid call to open, ret=%d\n",ret);
AGT_ERR("unable to parse table\n");
}
ret = blobmsg_parse(chl_open_resp_pol, ARRAY_SIZE(chl_open_resp_pol), tb,
blobmsg_data(tb_table[CHL_REG_TABLE]), blobmsg_data_len(tb_table[CHL_REG_TABLE]));
if (ret || !tb[CHL_OPEN_REQ_STATUS])
AGT_ERR("invalid call to open, ret=%d\n",ret);
if (tb[CHL_OPEN_REQ_STATUS])
((struct chl_agt_open_response *)(req->priv))->req_status = blobmsg_get_u32(tb[CHL_OPEN_REQ_STATUS]);
if (tb[CHL_OPEN_NW_STATUS])
((struct chl_agt_open_response *)(req->priv))->nw_status = blobmsg_get_u32(tb[CHL_OPEN_NW_STATUS]);
if (tb[CHL_OPEN_RIL_STATUS])
((struct chl_agt_open_response *)(req->priv))->ril_status = blobmsg_get_u32(tb[CHL_OPEN_RIL_STATUS]);
}
int agt_unregister_pdp( struct agent_pdp *pdp )
{
int ret;
struct chl_agt_response resp;
enum chl_req_status req_status;
AGT_DBG("send unregister pdp request to CHL\n");
ret = blob_buf_init(&blob, 0);
if (ret) {
AGT_ERR("blob_buf_init failed: %d\n", ret);
return ret;
}
blobmsg_add_u32(&blob, CHL_ID_STR, pdp->id);
ret = agt_ubus_invoke(&blob, chl_id, "unregister", agt_chl_unregister_cb, &resp, CHL_REQ_TIMEOUT);
if (ret){
AGT_ERR("invoke unregister failed: %s\n", ubus_strerror(ret));
return 1;
}
req_status = resp.req_status;
switch (req_status) {
case CHL_OK:
AGT_DBG("chl unregister response: req=%s, nw_status=%s\n"
, req_status_to_str(req_status), nw_status_to_str(resp.nw_status) );
pdp->pdp_state = DATA_UNAVAILABLE;
return 0;
default:
AGT_ERR("chl unregister response: req=%s, nw_status=%s\n"
, req_status_to_str(req_status), nw_status_to_str(resp.nw_status) );
pdp->pdp_state = DATA_UNAVAILABLE;
return 1;
}
return 0;
}
static void agt_chl_unregister_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
int ret;
struct blob_attr *tb_table[_CHL_REG_TABLE_MAX];
struct blob_attr *tb[_CHL_UNREG_RESP_MAX];
ret = blobmsg_parse(chl_table_pol, ARRAY_SIZE(chl_table_pol), tb_table,
blob_data(msg), blob_len(msg));
if (ret || !tb_table[CHL_REG_TABLE]){
AGT_ERR("invalid call to unregister, ret=%d\n",ret);
AGT_ERR("unable to parse table\n");
}
ret = blobmsg_parse(chl_unregister_resp_pol, ARRAY_SIZE(chl_unregister_resp_pol), tb,
blobmsg_data(tb_table[CHL_REG_TABLE]), blobmsg_data_len(tb_table[CHL_REG_TABLE]));
if (ret || !tb[CHL_UNREG_REQ_STATUS] || !tb[CHL_UNREG_NW_STATUS])
AGT_ERR("invalid call to unregister, ret=%d\n",ret);
if (tb[CHL_UNREG_REQ_STATUS])
((struct chl_agt_open_response *)(req->priv))->req_status = blobmsg_get_u32(tb[CHL_UNREG_REQ_STATUS]);
if (tb[CHL_UNREG_NW_STATUS])
((struct chl_agt_open_response *)(req->priv))->nw_status = blobmsg_get_u32(tb[CHL_UNREG_NW_STATUS]);
}
int agt_close_pdp( struct agent_pdp *pdp )
{
int ret;
struct chl_agt_response resp;
enum chl_req_status req_status;
AGT_DBG("send close pdp request to CHL\n");
ret = blob_buf_init(&blob, 0);
if (ret) {
AGT_ERR("blob_buf_init failed: %d\n", ret);
return ret;
}
blobmsg_add_u32(&blob, CHL_ID_STR, pdp->id);
ret = agt_ubus_invoke(&blob, chl_id, "close", agt_chl_close_cb, &resp, CHL_REQ_TIMEOUT);
if (ret){
AGT_ERR("invoke close failed: %s\n", ubus_strerror(ret));
return 1;
}
req_status = resp.req_status;
switch (req_status) {
case CHL_OK:
AGT_DBG("chl close response: req=%s, nw_status=%s\n"
, req_status_to_str(req_status), nw_status_to_str(resp.nw_status) );
pdp->pdp_state = resp.nw_status;
return 0;
default:
AGT_ERR("chl close response: req=%s, nw_status=%s\n"
, req_status_to_str(req_status), nw_status_to_str(resp.nw_status) );
pdp->pdp_state = resp.nw_status;
return 1;
}
return 0;
}
static void agt_chl_close_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
int ret;
struct blob_attr *tb_table[_CHL_REG_TABLE_MAX];
struct blob_attr *tb[_CHL_UNREG_RESP_MAX];
ret = blobmsg_parse(chl_table_pol, ARRAY_SIZE(chl_table_pol), tb_table,
blob_data(msg), blob_len(msg));
if (ret || !tb_table[CHL_REG_TABLE]){
AGT_ERR("invalid call to close, ret=%d\n",ret);
AGT_ERR("unable to parse table\n");
}
ret = blobmsg_parse(chl_unregister_resp_pol, ARRAY_SIZE(chl_unregister_resp_pol), tb,
blobmsg_data(tb_table[CHL_REG_TABLE]), blobmsg_data_len(tb_table[CHL_REG_TABLE]));
if (ret || !tb[CHL_UNREG_REQ_STATUS] || !tb[CHL_UNREG_NW_STATUS])
AGT_ERR("invalid call to close, ret=%d\n",ret);
if (tb[CHL_UNREG_REQ_STATUS])
((struct chl_agt_open_response *)(req->priv))->req_status = blobmsg_get_u32(tb[CHL_UNREG_REQ_STATUS]);
if (tb[CHL_UNREG_NW_STATUS])
((struct chl_agt_open_response *)(req->priv))->nw_status = blobmsg_get_u32(tb[CHL_UNREG_NW_STATUS]);
}
static int agt_route_op( char *route , int pdp_id, bool add)
{
int ret;
struct chl_agt_route_response resp;
enum chl_req_status req_status;
char method[10] = {0};
if (add)
strcpy(method,"set_route");
else
strcpy(method,"del_route");
resp.add = add;
ret = blob_buf_init(&blob, 0);
if (ret) {
AGT_ERR("blob_buf_init failed: %d\n", ret);
return ret;
}
blobmsg_add_u32(&blob, CHL_ID_STR, pdp_id);
blobmsg_add_string(&blob, CHL_RT_HOST_IP_STR, route);
ret = agt_ubus_invoke(&blob, chl_id, method, agt_chl_route_cb, &resp, CHL_REQ_TIMEOUT);
if (ret){
AGT_ERR("invoke %s failed: %s\n",method, ubus_strerror(ret));
return 1;
}
req_status = resp.req_status;
switch (req_status) {
case CHL_OK:
AGT_DBG("chl %s response: req=%s\n"
, method, req_status_to_str(req_status));
return 0;
default:
AGT_ERR("chl %s response: req=%s\n"
, method, req_status_to_str(req_status));
return 1;
}
return 0;
}
static void agt_chl_route_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
int ret;
struct blob_attr *tb_table[_CHL_REG_TABLE_MAX];
struct blob_attr *tb[_CHL_UNREG_RESP_MAX];
char method[10] = {0};
if (((struct chl_agt_route_response *)(req->priv))->add)
strcpy(method,"set_route");
else
strcpy(method,"del_route");
ret = blobmsg_parse(chl_table_pol, ARRAY_SIZE(chl_table_pol), tb_table,
blob_data(msg), blob_len(msg));
if (ret || !tb_table[CHL_REG_TABLE]){
AGT_ERR("invalid call to %s, ret=%d\n", method,ret);
AGT_ERR("unable to parse table\n");
}
ret = blobmsg_parse(chl_route_pol, ARRAY_SIZE(chl_route_pol), tb,
blobmsg_data(tb_table[CHL_REG_TABLE]), blobmsg_data_len(tb_table[CHL_REG_TABLE]));
if (ret || !tb[CHL_UNREG_REQ_STATUS] )
AGT_ERR("invalid call to %s, ret=%d\n", method,ret);
if (tb[CHL_UNREG_REQ_STATUS])
((struct chl_agt_open_response *)(req->priv))->req_status = blobmsg_get_u32(tb[CHL_UNREG_REQ_STATUS]);
}
int agt_add_route( char *route, int pdp_id )
{
return agt_route_op( route , pdp_id, 1);
}
int agt_del_route( char *route, int pdp_id )
{
return agt_route_op( route , pdp_id, 0);
}
static int agt_ubus_invoke(struct blob_buf *b,
uint32_t id, const char *method, ubus_data_handler_t cb,
void *priv, unsigned int timeout)
{
int ret;
ret = ubus_invoke(ubus_ctx, id, method, b? b->head : NULL , cb, priv,
timeout);
if (ret) {
AGT_ERR("ubus_invoke failed [id=%d, method=%s, err=%s]\n", id,
method, ubus_strerror(ret));
return ret;
}
return ret;
}
int agt_set_lte_dflt(struct agent_pdp *pdp)
{
int ret;
struct chl_agt_reg_response resp;
enum chl_req_status req_status;
AGT_DBG("set lte default APN\n");
ret = blob_buf_init(&blob, 0);
if (ret) {
AGT_ERR("blob_buf_init failed: %d\n", ret);
return ret;
}
blobmsg_add_string(&blob, CHL_LTED_APN_STR, pdp->apn);
blobmsg_add_u32(&blob, CHL_LTED_IP_TYPE_STR, pdp->ip_type);
ret = agt_ubus_invoke(&blob, chl_id, "set_lte_dflt", agt_set_lte_dflt_cb, &resp, CHL_REQ_TIMEOUT);
if (ret){
AGT_ERR("invoke set_lte_dflt failed: %s\n", ubus_strerror(ret));
return 1;
}
req_status = resp.req_status;
switch (req_status) {
case CHL_OK:
AGT_DBG("chl set_lte_dflt response: req=%s\n", req_status_to_str(req_status) );
return 0;
default:
AGT_ERR("chl set_lte_dflt response: req=%s\n", req_status_to_str(req_status) );
return 1;
}
return 0;
}
static void agt_set_lte_dflt_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
int ret;
struct blob_attr *tb_table[_CHL_REG_TABLE_MAX];
struct blob_attr *tb[_CHL_LTE_DFLT_RESP_MAX];
ret = blobmsg_parse(chl_table_pol, ARRAY_SIZE(chl_table_pol), tb_table,
blob_data(msg), blob_len(msg));
if (ret || !tb_table[CHL_REG_TABLE]){
AGT_ERR("invalid call to set_lte_dflt, ret=%d\n",ret);
AGT_ERR("unable to parse table\n");
}
ret = blobmsg_parse(chl_lte_dflt_pol, ARRAY_SIZE(chl_lte_dflt_pol), tb,
blobmsg_data(tb_table[CHL_REG_TABLE]), blobmsg_data_len(tb_table[CHL_REG_TABLE]));
if (ret || !tb[CHL_LTE_DFLT_REQ_STATUS])
AGT_ERR("invalid call to set_lte_dflt, ret=%d\n",ret);
if (tb[CHL_LTE_DFLT_REQ_STATUS])
((struct chl_agt_open_response *)(req->priv))->req_status = blobmsg_get_u32(tb[CHL_LTE_DFLT_REQ_STATUS]);
}