| /* |
| * 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; |
| } |