blob: 4e915054bc741a06a632bfd0b73db5a9ceb1ed8c [file] [log] [blame]
/*
* Copyright (C) 2022 ASR Microelectronic Ltd.
*
* Author: Shuo Dai <shuodai@asrmicro.com>
*
* This file contains proprietary information.
* No dissemination allowed without prior written permission from
* ASR Microelectronic Ltd.
*
* File Description:
*
* This file implements eCall demo application
*/
#include <libubox/blobmsg_json.h>
#include "libubus.h"
#include <string.h>
#include <stdlib.h>
#include "include/log.h"
#include "ril.h"
#include "ril_ext.h"
#include "rilutil.h"
#include <cutils/properties.h>
#include "utlEventHandler.h"
#include "udev_monitor.h"
#include "ecall_app.h"
#ifndef UNUSEDPARAM
#define UNUSEDPARAM(param) (void)(param)
#endif
#define ECALL_APP_LOG RDBGMSG
enum {
ECALL_GPIO_EDGE_NONE = 0, /* 0: do not trigger eCall when GPIO edge raising or falling*/
ECALL_GPIO_EDGE_RAISING = 1, /* 1: trigger eCall only when GPIO edge raising*/
ECALL_GPIO_EDGE_FALLING = 2, /* 2: trigger eCall only when GPIO edge falling*/
ECALL_GPIO_EDGE_BOTH = 3, /* 3: trigger eCall when GPIO edge raising or falling*/
};
enum {
ECALL_TYPE_TEST = 0, /* 0: Test eCall */
ECALL_TYPE_RECONFIGURATION = 1, /* 1: Reconfiguration eCall*/
ECALL_TYPE_MANUAL = 2, /* 2: Manually initiated eCall*/
ECALL_TYPE_AUTO = 3, /* 3: Automatically initiated eCall*/
};
/* Global variables for UTL messages*/
static pthread_t utid;
static int ueventfd = -1;
static utlEventHandlerId_T ueventHandler;
static char ueventBuffer[UEVENT_BUFFER_SIZE];
/* Global variables for ubus messages*/
static struct ubus_context *ubus_ctx = NULL;
static struct blob_buf ril_request_blob_buf;
static struct ubus_subscriber ril_ind_event;
static uint32_t ril_request_id, ril_subscriber_id, audio_request_id;
static uint32_t gpio_edge = ECALL_GPIO_EDGE_FALLING;
static int call_dialing = 0;
/* blobmsg_policy for ubus method "trigger_ecall", key "type" with value in int32*/
const struct blobmsg_policy ubus_initiate_int32_policy[] =
{
[0] =
{
.name = "type",
.type = BLOBMSG_TYPE_INT32,
},
};
/* blobmsg_policy for ubus method "set_edge", key "type" with value in int32*/
const struct blobmsg_policy ubus_edge_int32_policy[] =
{
[0] =
{
.name = "type",
.type = BLOBMSG_TYPE_INT32,
},
};
/* parse int32 from ubus blobmsg to trigger eCall transaction*/
static int ecallApp_initiate(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
{
UNUSEDPARAM(ctx);
UNUSEDPARAM(obj);
UNUSEDPARAM(req);
UNUSEDPARAM(method);
struct blob_attr *tb[1];
struct blob_attr *cur;
unsigned int type;
int rc;
/* parsing blob to be accessed easily with tb array - parse "1" argument*/
rc = blobmsg_parse(ubus_initiate_int32_policy, 1, tb, blob_data(msg), blob_len(msg));
if (rc < 0)
{
ECALL_APP_LOG("%s: parsing fail\n", __FUNCTION__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
cur = tb[0];
if (!cur)
{
ECALL_APP_LOG("%s: missing parameter\n", __FUNCTION__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
type = blobmsg_get_u32(cur);
if (type == ECALL_TYPE_MANUAL)
HandleButtonEvent();
else if (type == ECALL_TYPE_AUTO)
HandleCrashEvent();
return 0;
}
/* parse int32 from ubus blobmsg to set gpio edge*/
static int ecallApp_set_edge(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
{
UNUSEDPARAM(ctx);
UNUSEDPARAM(obj);
UNUSEDPARAM(req);
UNUSEDPARAM(method);
struct blob_attr *tb[1];
struct blob_attr *cur;
unsigned int type;
int rc;
/* parsing blob to be accessed easily with tb array - parse "1" argument*/
rc = blobmsg_parse(ubus_edge_int32_policy, 1, tb, blob_data(msg), blob_len(msg));
if (rc < 0)
{
ECALL_APP_LOG("%s: parsing fail\n", __FUNCTION__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
cur = tb[0];
if (!cur)
{
ECALL_APP_LOG("%s: missing parameter\n", __FUNCTION__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
type = blobmsg_get_u32(cur);
ECALL_APP_LOG("%s: gpio_edge %d->%d", __FUNCTION__, gpio_edge, type);
if (type <= ECALL_GPIO_EDGE_BOTH) {
gpio_edge = type;
}
return 0;
}
/*******************************************************************************\
* ecall_app uBus commands
*
* 1) Trigger eCall: initiate eCall transaction with different type
* ubus call ecall_app trigger_ecall "{'type' : 2}" --> Manually initiated eCall
* ubus call ecall_app trigger_ecall "{'type' : 3}" --> Automatically initiated eCall
* 2) Set GPIO edge: set valid GPIO edge to trigger eCall
* ubus call ecall_app set_edge "{'type' : 0}" --> do not trigger eCall when GPIO edge raising or falling
* ubus call ecall_app set_edge "{'type' : 1}" --> trigger eCall only when GPIO edge raising
* ubus call ecall_app set_edge "{'type' : 2}" --> trigger eCall only when GPIO edge falling
* ubus call ecall_app set_edge "{'type' : 3}" --> trigger eCall when GPIO edge raising or falling
\*******************************************************************************/
static const struct ubus_method ecall_app_methods[] = {
UBUS_METHOD("trigger_ecall", ecallApp_initiate, ubus_initiate_int32_policy),
UBUS_METHOD("set_edge", ecallApp_set_edge, ubus_edge_int32_policy),
};
static struct ubus_object_type ecall_app_object_type = UBUS_OBJECT_TYPE("ecall_app", ecall_app_methods);
static struct ubus_object ecall_app_object = {
.name = "ecall_app",
.type = &ecall_app_object_type,
.methods = ecall_app_methods,
.n_methods = ARRAY_SIZE(ecall_app_methods),
};
static void ecall_add_subscriber_cb(struct uloop_timeout *timeout){
/* add subscriber for ril event*/
if (ubus_lookup_id(ubus_ctx, "ril.unsol.cc", &ril_subscriber_id))
{
ECALL_APP_LOG("%s,%d", __FUNCTION__, __LINE__);
uloop_timeout_set(timeout, 2000);
return;
}
ECALL_APP_LOG("%s,%d", __FUNCTION__, __LINE__);
ubus_subscribe(ubus_ctx, &ril_ind_event, ril_subscriber_id);
}
static struct uloop_timeout ubus_add_subscribe_timeout = {
.cb = ecall_add_subscriber_cb,
};
static void ecallApp_rilrequest_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
UNUSEDPARAM(type);
UNUSEDPARAM(req);
UNUSEDPARAM(msg);
}
/* send eCall related AT cmds*/
int ecallApp_SendAtCmd(char *data, int sync)
{
int ret;
int len = 0;
struct ubus_request *req = NULL;
ECALL_APP_LOG("%s, sync:%d, %s", __FUNCTION__, sync, (char*)data);
len = strlen(data)+1;
blob_buf_init(&ril_request_blob_buf, 0);
rilutil_makeRequestBlob(&ril_request_blob_buf, RIL_REQUEST_SEND_ATCMD, data, len);
req = (struct ubus_request *)malloc(sizeof(struct ubus_request));
if (req == NULL) {
return -2;
}
if (sync) {
ret = ubus_invoke(ubus_ctx, ril_request_id, "ril_request", ril_request_blob_buf.head, ecallApp_rilrequest_cb, data, 0);
}
else {
ret = ubus_invoke_async(ubus_ctx, ril_request_id, "ril_request", ril_request_blob_buf.head, req);
req->data_cb = ecallApp_rilrequest_cb;
req->priv = data;
ubus_complete_request_async(ubus_ctx, req);
}
return ret;
}
static int ecallApp_SendAudioCmd(const char *method, struct blob_attr *msg)
{
int rc;
struct ubus_request req;
rc = ubus_invoke_async(ubus_ctx, audio_request_id, method, msg, &req);
if (rc != 0)
return -1;
/* cancel req (on client side) because noreply is needed */
ubus_abort_request(ubus_ctx, &req);
return 0;
}
int ecallApp_ControlPlayback(char *name, int ctrl)
{
int ret = -1;
struct blob_buf outBlob, outBlob2;
memset(&outBlob, 0, sizeof(outBlob));
blob_buf_init(&outBlob, 0);
memset(&outBlob2, 0, sizeof(outBlob2));
blob_buf_init(&outBlob2, 0);
ECALL_APP_LOG("%s, ctrl:%d, name:%s", __FUNCTION__, ctrl, name);
ret = ecallApp_SendAudioCmd("play_stop", outBlob2.head);
if (ctrl) {
blobmsg_add_u32(&outBlob, "direction", 0);
blobmsg_add_u32(&outBlob, "type", 0);
blobmsg_add_u32(&outBlob, "srcdst", 1);
blobmsg_add_u32(&outBlob, "priority", 1);
blobmsg_add_u32(&outBlob, "dest", 0);
ret = ecallApp_SendAudioCmd("vcm_configure", outBlob.head);
blobmsg_add_string(&outBlob2, "path", name);
ret = ecallApp_SendAudioCmd("play_start", outBlob2.head);
}
blob_buf_free(&outBlob);
blob_buf_free(&outBlob2);
return ret;
}
/* parse urc_id and data from ECALLDATA indication, response related urc_id such as 9 and 32*/
static void ecallApp_HandleEcalldata(const char *s)
{
int urc_id, urc_data;
int *arr_p;
char urc_data_buf[10] = { 0 };
arr_p = (int*)s;
urc_id = *arr_p;
sprintf(urc_data_buf, "0x%d", *((int *)(arr_p + 1)));
urc_data = strtol(urc_data_buf, NULL, 16);
ECALL_APP_LOG("%s: urc_id %d, urc_data %d", __FUNCTION__, urc_id, urc_data);
if (urc_id == 9) {
//MSD re-send request
HandleResendRequest();
}
else if (urc_id == 32) {
//MSD send request
HandleSendRequest(urc_data);
}
else if (urc_id == 10) {
HandleMSDTransferStartEvent();
}
else if (urc_id == 16) {
HandleMSDTransferStopEvent();
}
else if (urc_id == 27) {
HandleVoiceChannelEstablishEvent();
}
}
/* parse call number and state from CLCC indication*/
static void ecallApp_HandleClcc(const char *s)
{
char *callMumber;
int direction;
RIL_Call *ril_call_status;
RIL_CallState callStat;
int callID;
ril_call_status = (RIL_Call*)s;
callMumber = ril_call_status->number;
direction = ril_call_status->isMT;
callStat = ril_call_status->state;
callID = ril_call_status->index;
ECALL_APP_LOG("%s: callStat %d, callMumber %s, direction %d, callID %d", __FUNCTION__, callStat, callMumber, direction, callID);
if(direction == 0 && callStat == 2 && strcmp(callMumber, "112") == 0)
{
//MO dialing
if(++call_dialing == 1)
HandleDialEvent();
}
}
/* watch ril indication, take care about ECALLDATA etc*/
static int ecallApp_handle_ril_ind(struct ubus_context *ctx, unsigned int rilid, unsigned int rilerror, char *data, int data_len)
{
UNUSEDPARAM(data_len);
UNUSEDPARAM(ctx);
int ret = 0;
if (rilerror) {
return -1;
}
switch (rilid)
{
case RIL_UNSOL_ECALLDATA:
ecallApp_HandleEcalldata(data);
break;
case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED_EXT:
ecallApp_HandleClcc(data);
break;
case RIL_UNSOL_CALL_NO_CARRIER_EXT:
call_dialing = 0;
break;
default:
break;
}
return ret;
}
static int ecallApp_subscriber_cb(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
{
UNUSEDPARAM(ctx);
UNUSEDPARAM(obj);
UNUSEDPARAM(req);
UNUSEDPARAM(method);
unsigned int requestid = 0;
unsigned int rilerrno = 0;
void *response = NULL;
int responselen = 0;
int ret = 0;
ret = rilutil_parseResponse(msg, &requestid, &rilerrno, &response, &responselen);
if (ret)
goto end;
ecallApp_handle_ril_ind(ctx, requestid, rilerrno, response, responselen);
end:
if (response)
rilutil_freeResponseData(requestid,response,responselen);
return 0;
}
static void ecallApp_subscriber_remove_cb(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id)
{
UNUSEDPARAM(ctx);
UNUSEDPARAM(obj);
UNUSEDPARAM(id);
ECALL_APP_LOG("%s,%d", __FUNCTION__, __LINE__);
}
/* watch UTL messages, response to GPIO edge change from ecall device*/
static int ecallApp_HandleDataFromUevent(char * buf, int buflen)
{
const char* device;
const char* action;
const char* ecall_device;
ECALL_APP_LOG("%s: %s, %d", __FUNCTION__, buf, buflen);
ecall_device = strstr(buf, "ecall");
device = search_key("DRIVER", buf, buflen);
action = search_key("ACTION", buf, buflen);
ECALL_APP_LOG("%s: ecall_device=%s, device=%s, action=%s!", __FUNCTION__, ecall_device, device, action);
if (ecall_device) {
if ((!strcmp(action, "online") && (gpio_edge == ECALL_GPIO_EDGE_RAISING || gpio_edge == ECALL_GPIO_EDGE_BOTH))
||(!strcmp(action, "offline") && (gpio_edge == ECALL_GPIO_EDGE_FALLING || gpio_edge == ECALL_GPIO_EDGE_BOTH))) {
if (strstr(ecall_device, "ecall0")) {
HandleCrashEvent();
}
else {
HandleButtonEvent();
}
}
}
ECALL_APP_LOG("%s: at finished!", __FUNCTION__);
return 0;
}
static utlReturnCode_T ecallApp_ReceiveDataFromUevent(const utlEventHandlerType_T handler_type,
const utlEventHandlerType_T event_type,
const int fd,
const utlRelativeTime_P2c period_p,
void *arg_p)
{
UNUSEDPARAM(handler_type);
UNUSEDPARAM(event_type);
UNUSEDPARAM(period_p);
UNUSEDPARAM(arg_p);
int ret;
ECALL_APP_LOG("%s,%d", __FUNCTION__, __LINE__);
ret = read(fd, ueventBuffer, UEVENT_BUFFER_SIZE);
if(ret > 0)
ecallApp_HandleDataFromUevent(ueventBuffer, ret);
return utlSUCCESS;
}
static int ecallApp_OpenEventLink(void)
{
while(ueventfd < 0)
{
ueventfd = open_uevent_socket();
if(ueventfd < 0)
ECALL_APP_LOG("%s: open EventLink error:%s\n", __FUNCTION__, strerror(errno));
}
ueventHandler = utlSetFdEventHandler(utlEVENT_HANDLER_TYPE_READ, utlEVENT_HANDLER_PRIORITY_MEDIUM, ueventfd, ecallApp_ReceiveDataFromUevent, NULL);
return ueventfd;
}
static void ecallApp_CloseEventLink(void)
{
utlDeleteEventHandler(ueventHandler);
close(ueventfd);
ueventfd = -1;
}
/*******************************************************************************\
* Function: ecallApp_UTLTask
* Description: This function is the main task for UTL message.
* It will firstly set correct audio device;
* ecallApp_ReceiveDataFromUevent() will handle the input from UTL socket.
* Returns: void
\*******************************************************************************/
static void ecallApp_UTLTask(void)
{
ECALL_APP_LOG("%s: %d", __FUNCTION__, __LINE__);
ecallApp_OpenEventLink();
if (utlEventLoop(true) != utlSUCCESS)
{
ECALL_APP_LOG("%s: Event loop reports failure!!!", __FUNCTION__);
ecallApp_CloseEventLink();
return;
}
}
/*******************************************************************************\
* Function: main
\*******************************************************************************/
int main(int argc, char **argv)
{
int ret;
UNUSEDPARAM(argc);
UNUSEDPARAM(argv);
set_service_log_tag("ecall_app");
/*create thread to listen to UTL socket*/
pthread_create(&utid, NULL, (void *)ecallApp_UTLTask, NULL);
/*create ubus loop to listen to RIL event*/
uloop_init();
while(1)
{
ubus_ctx = ubus_connect(NULL);
if (!ubus_ctx)
usleep(200000); //200ms
else
break;
}
ubus_add_uloop(ubus_ctx);
ret = ubus_add_object(ubus_ctx, &ecall_app_object);
if (ret) {
ECALL_APP_LOG("%s,%d", __FUNCTION__, __LINE__);
goto fail1;
}
/* lookup fail if ril is not ready */
while(1)
{
ret = ubus_lookup_id(ubus_ctx, "ril", &ril_request_id);
if (0 != ret)
{
ECALL_APP_LOG("%s, failed to look up ril, ret=%d\n", __FUNCTION__, ret);
usleep(200000); //200ms
}
else
break;
}
ECALL_APP_LOG("%s, ubus_lookup_id ril OK\n", __FUNCTION__);
/* lookup fail if audio_if is not ready */
while(1)
{
ret = ubus_lookup_id(ubus_ctx, "audio_if", &audio_request_id);
if (0 != ret)
{
ECALL_APP_LOG("%s, failed to look up audio_if, ret=%d\n", __FUNCTION__, ret);
usleep(200000); //200ms
}
else
break;
}
ECALL_APP_LOG("%s, ubus_lookup_id audio_if OK\n", __FUNCTION__);
//register for ril indication
ret = ubus_register_subscriber(ubus_ctx, &ril_ind_event);
if (ret) {
ECALL_APP_LOG("%s,%d", __FUNCTION__, __LINE__);
goto fail2;
}
ril_ind_event.cb = ecallApp_subscriber_cb;
ril_ind_event.remove_cb = ecallApp_subscriber_remove_cb;
uloop_timeout_set(&ubus_add_subscribe_timeout, 0);
ecallApp_Init();
uloop_run();
ECALL_APP_LOG("%s,%d", __FUNCTION__, __LINE__);
fail2:
ECALL_APP_LOG("%s,%d", __FUNCTION__, __LINE__);
ubus_remove_object(ubus_ctx, &ecall_app_object);
fail1:
ECALL_APP_LOG("%s,%d", __FUNCTION__, __LINE__);
ubus_free(ubus_ctx);
//fail:
ECALL_APP_LOG("%s,%d", __FUNCTION__, __LINE__);
uloop_done();
return 0;
}