| /** |
| * \file mbtk_audio.c |
| * \brief A Documented file. |
| * |
| * Detailed description |
| * \Author: js.wang <js.wang@mobiletek.cn> |
| * \Version: 1.0.0 |
| * \Date: 2022-03-31 |
| */ |
| #include <fcntl.h> |
| #include <stdint.h> |
| #include <limits.h> |
| #include <termios.h> |
| #include <stdarg.h> |
| #include <dirent.h> |
| #include <sys/stat.h> |
| #include <sys/statfs.h> |
| #include <sys/types.h> |
| #include "mbtk_log.h" |
| #include "mbtk_type.h" |
| #include <libubox/blobmsg_json.h> |
| #include "libubus.h" |
| #include "mbtk_audio.h" |
| |
| /******************************************************************************\ |
| * Macros / Defines |
| \******************************************************************************/ |
| |
| #ifndef UNUSEDPARAM |
| #define UNUSEDPARAM(a) (void)(a) |
| #endif |
| |
| #define AUDIO_UBUS_REQUEST_NAME "audio_if" |
| #define mbtk_dtmf_UBUS_NAME "proslic_uBus" //Used by wake lock |
| #define DTMFCODEID "dtmfcode" |
| #define DTMFCODE_NOTIFICATION_NAME "audioif.dtmfcode" |
| #define AUDIO_UBUS_VOLUME_SET "volume_set" |
| #define AUDIO_UBUS_VOLUME_GET "volume_status" |
| #define AUDIO_UBUS_MODE_SET "audio_mode_set" |
| #define AUDIO_UBUS_PATH_ENABLE "audio_path_enable" |
| #define AUDIO_UBUS_SWITCH_PCM "switch_pcm" |
| #define AUDIO_UBUS_DSP_SET "config_dspgain" |
| #define AUDIO_UBUS_LOOPBACK_EN "loopback_enable" |
| #define AUDIO_UBUS_LOOPBACK_DIS "loopback_disable" |
| #define AUDIO_UBUS_AUDIO_GAIN_SET "audio_gain_set" |
| #define AUDIO_UBUS_AUDIO_REG_SET "audio_reg_set" |
| |
| |
| // #define DEBUG 1 |
| |
| #ifdef DEBUG |
| #define mbtk_audio_log(...) mbtk_audio_log(__VA_ARGS__) |
| #else |
| #define mbtk_audio_log(...) |
| #endif |
| |
| typedef enum { |
| PARAM_INT_POLICY_0, |
| PARAM_INT_POLICY_1, |
| PARAM_INT_POLICY_2, |
| PARAM_INT_POLICY_3, |
| PARAM_INT_POLICY_MAX, |
| } PARAM_INT_POLICY; |
| |
| #define FAILURE -1 |
| #define ID_RIFF 0x46464952 |
| #define ID_WAVE 0x45564157 |
| #define ID_FMT 0x20746d66 |
| #define ID_DATA 0x61746164 |
| #define FORMAT_PCM 1 |
| |
| struct wav_header { |
| uint32_t riff_id; |
| uint32_t riff_sz; |
| uint32_t riff_fmt; |
| uint32_t fmt_id; |
| uint32_t fmt_sz; |
| uint16_t audio_format; |
| uint16_t num_channels; |
| uint32_t sample_rate; |
| uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */ |
| uint16_t block_align; /* num_channels * bps / 8 */ |
| uint16_t bits_per_sample; |
| uint32_t data_id; |
| uint32_t data_sz; |
| }; |
| |
| const struct blobmsg_policy int_policy[] ={ |
| [PARAM_INT_POLICY_0] = { |
| .name = "param0", |
| .type = BLOBMSG_TYPE_INT32, |
| }, |
| }; |
| |
| const struct blobmsg_policy dtmf_code_policy[] = { |
| [0] = { |
| .name = DTMFCODEID, |
| .type = BLOBMSG_TYPE_INT32, |
| }, |
| }; |
| |
| static void mbtk_dtmf_add_subscriber_AudioIf(struct uloop_timeout *timeout); |
| static struct uloop_timeout mbtk_dtmf_add_subscribe_timeout_AudioIf = |
| { |
| .cb = mbtk_dtmf_add_subscriber_AudioIf, |
| }; |
| |
| static struct blob_buf audio_cm_b; |
| pthread_mutex_t mbtk_dtmf_mutex; |
| int mbtk_dtmf_PMConstraintWorks; |
| //For UBUS listening to RILD & audio_if |
| struct mbtk_audio_ubus_db_t |
| { |
| pthread_t dtmf_pthread; |
| mbtk_dtmf_cb dtmf_cb; |
| mbtk_volume_cb audio_volume_get_cb; |
| int work_state; |
| struct ubus_context *ctx; |
| |
| /* Audio_If */ |
| struct ubus_subscriber audioif_event; |
| uint32_t audioif_subscriber_id; |
| uint32_t audioif_request_id; |
| }; |
| |
| static struct mbtk_audio_ubus_db_t *mbtk_audio_ubus_db = NULL; |
| |
| #ifdef MBTK_YX_SUPPORT |
| static struct ubus_context *audio_ctx = NULL; |
| static uint32_t ubus_id_audio_if = 0; |
| #endif |
| |
| static int record_fd = 0; |
| |
| #define AUDIO_FILE_DIR "/data" |
| #define MBTK_AUD_DEMO_WAV "/data/demo.wav" |
| |
| int MBTK_wav_pcm16Le_set(int fd) |
| { |
| struct wav_header hdr; |
| |
| if (fd <= 0) |
| return -1; |
| |
| memset(&hdr, 0, sizeof(struct wav_header)); |
| |
| hdr.riff_id = ID_RIFF; |
| hdr.riff_fmt = ID_WAVE; |
| hdr.fmt_id = ID_FMT; |
| hdr.fmt_sz = 16; |
| hdr.audio_format = FORMAT_PCM; |
| hdr.num_channels = 1; |
| hdr.sample_rate = 8000; |
| hdr.bits_per_sample = 16; |
| hdr.byte_rate = (8000 * 1 * hdr.bits_per_sample) / 8; |
| hdr.block_align = (hdr.bits_per_sample * 1) / 8; |
| hdr.data_id = ID_DATA; |
| hdr.data_sz = 0; |
| |
| hdr.riff_sz = hdr.data_sz + 44 - 8; |
| if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int create_audio_dir(const char *dirname) |
| { |
| DIR *p_dir; |
| int res = -1; |
| |
| if(NULL == (p_dir = opendir((const char *)dirname))) |
| { |
| if(mkdir(dirname, 0777) == 0) |
| { |
| res = 0; |
| } |
| else |
| { |
| fprintf(stderr, "create audio dir error \n"); |
| res = -1; |
| } |
| } |
| else |
| { |
| closedir(p_dir); |
| res = 0; |
| } |
| return res; |
| } |
| int mbtk_at_play(const char *args) |
| { |
| int fd = 0; |
| int play_hdl = 0; |
| char databuf[1024]; |
| int size; |
| |
| LOGI("%s %d", __FUNCTION__, __LINE__); |
| // play_hdl = Ql_AudPlayer_Open(NULL, NULL); |
| play_hdl = mbtk_audio_open(0, 1, 8000, NULL); |
| if(0 == play_hdl) |
| printf("Ql_AudPlayer_Open fail\n"); |
| |
| fd = open(MBTK_AUD_DEMO_WAV, O_RDWR); |
| if (fd <= 0) |
| { |
| printf("file open error\n"); |
| goto err; |
| } |
| memset(databuf, 0, sizeof(databuf)); |
| while(0 < (size = read(fd, databuf, sizeof(databuf)))) |
| { |
| if(-1 == mbtk_audio_play_stream(play_hdl, databuf, size)) |
| break; |
| } |
| mbtk_audio_log("aplay Stream end \n"); |
| |
| err: |
| if(fd > 0) |
| close(fd); |
| mbtk_audio_close(play_hdl); |
| |
| return 0; |
| } |
| |
| void record_cb_func(int cb_result, char* databuf, unsigned int len) |
| { |
| int rc; |
| |
| if(NULL != databuf && len > 0 && record_fd > 0) |
| { |
| //for debug:save into file |
| rc = write(record_fd, databuf, len); |
| if (rc < 0) { |
| printf("%s: error writing to file!\n", __FUNCTION__); |
| } else if (rc < len) { |
| printf("%s: wrote less the buffer size!\n", __FUNCTION__); |
| } |
| } |
| } |
| |
| int mbtk_at_rec(const char *args) |
| { |
| LOGI("%s %d", __FUNCTION__, __LINE__); |
| //hdl = Ql_AudRecorder_Open(NULL, record_cb_func); |
| void *record_hdl = mbtk_audio_open(1, 1, 8000, NULL); |
| if (record_hdl == NULL) |
| { |
| printf("AudRecorder open error\n"); |
| return -1; |
| } |
| |
| create_audio_dir(AUDIO_FILE_DIR); |
| record_fd = open(MBTK_AUD_DEMO_WAV, O_RDWR|O_CREAT|O_TRUNC, 0644); |
| if (record_fd <= 0) |
| { |
| printf("file open error\n"); |
| goto err; |
| } |
| |
| if(0 == MBTK_wav_pcm16Le_set(record_fd))// 设置格式,否则音频播放不了 |
| { |
| if( 0 != mbtk_audio_record(record_hdl, record_cb_func, NULL)) |
| { |
| printf("file write error\n"); |
| close(record_fd); |
| record_fd = 0; |
| } |
| } |
| else |
| { |
| printf("arec set file header error\n"); |
| close(record_fd); |
| record_fd = 0; |
| } |
| |
| sleep(10);// 录音 10S |
| err: |
| mbtk_audio_close(record_hdl);// 关闭录音 |
| record_hdl = NULL; |
| if(record_fd > 0) |
| { |
| close(record_fd); |
| record_fd = 0; |
| } |
| |
| return 0; |
| } |
| |
| static void mbtk_ubus_complete_cb(struct ubus_request *req, int rc) |
| { |
| if(NULL == mbtk_audio_ubus_db) |
| { |
| printf("mbtk_dtmf_ubus not init!\n"); |
| return; |
| } |
| if (req) |
| { |
| free(req); |
| req = NULL; |
| } |
| mbtk_audio_log("%s do nothing, rc=%d\n", __FUNCTION__, rc); |
| LOGI("%s do nothing, rc=%d\n", __FUNCTION__, rc); |
| if(mbtk_audio_ubus_db->work_state) |
| mbtk_audio_ubus_db->work_state--; |
| } |
| /** |
| * @brief mbtk_audio_mode_set |
| * |
| * @details detailed description |
| * |
| * @param param |
| * "param0": UINT32 |
| * typedef enum { |
| * AUDIO_MODE_INVALID = -2, |
| * AUDIO_MODE_CURRENT = -1, |
| * AUDIO_MODE_NORMAL = 0, |
| * AUDIO_MODE_RINGTONE = 1, |
| * AUDIO_MODE_IN_CALL = 2, |
| * AUDIO_MODE_IN_COMMUNICATION=3, |
| * AUDIO_MODE_IN_VT_CALL= 4, |
| * AUDIO_MODE_CNT, |
| * AUDIO_MODE_MAX = AUDIO_MODE_CNT-1, |
| * } audio_mode_ |
| * @return return type |
| */ |
| void mbtk_audio_mode_set(int mode) |
| { |
| int rc = 0; |
| #ifndef MBTK_YX_SUPPORT |
| if(NULL == mbtk_audio_ubus_db) |
| { |
| printf("mbtk_dtmf_ubus not init!\n"); |
| return; |
| } |
| blob_buf_init(&audio_cm_b, 0); |
| blobmsg_add_u32(&audio_cm_b, "param0", mode); |
| if ((rc = ubus_invoke(mbtk_audio_ubus_db->ctx, |
| mbtk_audio_ubus_db->audioif_request_id, |
| AUDIO_UBUS_MODE_SET, |
| audio_cm_b.head, NULL, NULL, 2 * 1000)) != UBUS_STATUS_OK) |
| { |
| printf("%s, ubus_invoke_async %s failed %s\n", __FUNCTION__, AUDIO_UBUS_MODE_SET, ubus_strerror(rc)); |
| } |
| else |
| { |
| printf("%s: ubus_invoke_async success\n", __FUNCTION__); |
| } |
| #else |
| static struct ubus_context *ctx; |
| ctx = ubus_connect(NULL); |
| if (!ctx) { |
| printf("Failed to connect to ubus\n"); |
| return; |
| } |
| |
| static struct blob_buf b; |
| uint32_t id; |
| int ret; |
| ret = ubus_lookup_id(ctx, AUDIO_UBUS_REQUEST_NAME, &id); |
| if (ret) { |
| printf("ubus_lookup_id() fail.\n"); |
| return ret; |
| } |
| |
| blob_buf_init(&b, 0); |
| blobmsg_add_u32(&b, "param0", mode); |
| if((ret = ubus_invoke(ctx, id, AUDIO_UBUS_MODE_SET, b.head, NULL, NULL, 0)) != UBUS_STATUS_OK) { |
| printf("ubus_invoke fail:%d.\n", ret); |
| } else { |
| printf("ubus_invoke success.\n"); |
| } |
| #endif |
| } |
| |
| |
| void mbtk_audio_path_enable(int mode) |
| { |
| int rc = 0; |
| #ifndef MBTK_YX_SUPPORT |
| if(NULL == mbtk_audio_ubus_db) |
| { |
| printf("mbtk_dtmf_ubus not init!\n"); |
| return; |
| } |
| |
| blob_buf_init(&audio_cm_b, 0); |
| blobmsg_add_u32(&audio_cm_b, "param0", mode); |
| if ((rc = ubus_invoke(mbtk_audio_ubus_db->ctx, |
| mbtk_audio_ubus_db->audioif_request_id, |
| AUDIO_UBUS_PATH_ENABLE, |
| audio_cm_b.head, NULL, NULL, 0)) != UBUS_STATUS_OK) |
| { |
| printf("%s, ubus_invoke_async %s failed %s\n", __FUNCTION__, AUDIO_UBUS_PATH_ENABLE, ubus_strerror(rc)); |
| } |
| else |
| { |
| printf("%s: ubus_invoke_async success\n", __FUNCTION__); |
| } |
| #else |
| static struct ubus_context *ctx; |
| ctx = ubus_connect(NULL); |
| if (!ctx) { |
| printf("Failed to connect to ubus\n"); |
| return; |
| } |
| |
| static struct blob_buf b; |
| uint32_t id; |
| int ret; |
| ret = ubus_lookup_id(ctx, AUDIO_UBUS_REQUEST_NAME, &id); |
| if (ret) { |
| printf("ubus_lookup_id() fail.\n"); |
| return ret; |
| } |
| |
| blob_buf_init(&b, 0); |
| blobmsg_add_u32(&b, "param0", mode); |
| if((ret = ubus_invoke(ctx, id, AUDIO_UBUS_PATH_ENABLE, b.head, NULL, NULL, 0)) != UBUS_STATUS_OK) { |
| printf("ubus_invoke fail:%d.\n", ret); |
| } else { |
| printf("ubus_invoke success.\n"); |
| } |
| |
| #endif |
| } |
| |
| |
| /** |
| * @brief mbtk_audio_switch_pcm |
| * |
| * @details detailed description |
| * |
| * @param param |
| * "param0":UINT32 mode |
| * 0: Turn off MSA PCM |
| * 1: Turn on MSA PCM as NB PCM(i.e. 8KHz sample rate) |
| * 2: Turn on MSA PCM as WB PCM(i.e. 16KHz sample rate) |
| * @return return type |
| */ |
| void mbtk_audio_switch_pcm(int mode) |
| { |
| int rc = 0; |
| #ifndef MBTK_YX_SUPPORT |
| if(NULL == mbtk_audio_ubus_db) |
| { |
| printf("mbtk_dtmf_ubus not init!\n"); |
| return; |
| } |
| |
| blob_buf_init(&audio_cm_b, 0); |
| blobmsg_add_u32(&audio_cm_b, "param0", mode); |
| if ((rc = ubus_invoke(mbtk_audio_ubus_db->ctx, |
| mbtk_audio_ubus_db->audioif_request_id, |
| AUDIO_UBUS_SWITCH_PCM, |
| audio_cm_b.head, NULL, NULL, 0/*2 * 1000*/)) != UBUS_STATUS_OK) |
| { |
| printf("%s, ubus_invoke %s failed %s\n", __FUNCTION__, AUDIO_UBUS_SWITCH_PCM, ubus_strerror(rc)); |
| } |
| else |
| { |
| printf("%s: ubus_invoke success\n", __FUNCTION__); |
| } |
| #else |
| |
| static struct blob_buf b; |
| int ret; |
| if(audio_ctx == NULL || ubus_id_audio_if == 0) { |
| return; |
| } |
| |
| blob_buf_init(&b, 0); |
| blobmsg_add_u32(&b, "param0", mode); |
| if((ret = ubus_invoke(audio_ctx, ubus_id_audio_if, AUDIO_UBUS_SWITCH_PCM, b.head, NULL, NULL, 2000)) != UBUS_STATUS_OK) { |
| printf("ubus_invoke fail:%d.\n", ret); |
| } else { |
| printf("ubus_invoke success.\n"); |
| } |
| |
| #endif |
| } |
| |
| int mbtk_audio_dsp_set(int type, int gain) |
| { |
| LOGE("1type:%d, gain:%d\n", type, gain); |
| int rc = 0; |
| struct ubus_request *req = NULL; |
| |
| if(NULL == mbtk_audio_ubus_db) |
| { |
| LOGE("mbtk_dtmf_ubus not init!\n"); |
| return -1; |
| } |
| req = (struct ubus_request *)malloc(sizeof(struct ubus_request)); |
| if (req == NULL) |
| { |
| LOGE("leave %s: lack of memory\n", __FUNCTION__); |
| return -1; |
| } |
| |
| memset(req, 0, sizeof(struct ubus_request)); |
| blob_buf_init(&audio_cm_b, 0); |
| blobmsg_add_u32(&audio_cm_b, "type", type); |
| blobmsg_add_u32(&audio_cm_b, "gain", gain); |
| if ((rc = ubus_invoke_async(mbtk_audio_ubus_db->ctx, |
| mbtk_audio_ubus_db->audioif_request_id, |
| AUDIO_UBUS_DSP_SET, |
| audio_cm_b.head, req)) != UBUS_STATUS_OK) |
| { |
| free(req); |
| LOGE("%s, ubus_invoke_async %s failed %s\n", __FUNCTION__, AUDIO_UBUS_DSP_SET, ubus_strerror(rc)); |
| return -1; |
| } |
| else |
| { |
| LOGE("%s: ubus_invoke_async success\n", __FUNCTION__); |
| mbtk_audio_ubus_db->work_state++; |
| req->complete_cb = mbtk_ubus_complete_cb; |
| ubus_complete_request_async(mbtk_audio_ubus_db->ctx, req); |
| } |
| return 0; |
| } |
| /** |
| * @brief mbtk_audio_loopback_start |
| * |
| * @details detailed description |
| * |
| * @param param |
| * device: UINT32 |
| * 0: earpiece |
| * 1: speaker |
| * 2: headset |
| * @return return type |
| */ |
| static void mbtk_audio_loopback_start(int device) |
| { |
| int rc = 0; |
| struct ubus_request *req = NULL; |
| |
| if(NULL == mbtk_audio_ubus_db) |
| { |
| LOGI("mbtk_dtmf_ubus not init!\n"); |
| return; |
| } |
| req = (struct ubus_request *)malloc(sizeof(struct ubus_request)); |
| if (req == NULL) |
| { |
| LOGI("leave %s: lack of memory\n", __FUNCTION__); |
| return; |
| } |
| memset(req, 0, sizeof(struct ubus_request)); |
| blob_buf_init(&audio_cm_b, 0); |
| blobmsg_add_u32(&audio_cm_b, "param0", device); |
| if ((rc = ubus_invoke_async(mbtk_audio_ubus_db->ctx, |
| mbtk_audio_ubus_db->audioif_request_id, |
| AUDIO_UBUS_LOOPBACK_EN, |
| audio_cm_b.head, req)) != UBUS_STATUS_OK) |
| { |
| free(req); |
| LOGI("%s, ubus_invoke_async volume get failed %s\n", __FUNCTION__, ubus_strerror(rc)); |
| } |
| else |
| { |
| LOGI("%s: ubus_invoke_async success\n", __FUNCTION__); |
| req->complete_cb = mbtk_ubus_complete_cb; |
| ubus_complete_request_async(mbtk_audio_ubus_db->ctx, req); |
| } |
| } |
| |
| static void mbtk_audio_loopback_stop(void) |
| { |
| int rc = 0; |
| |
| if(NULL == mbtk_audio_ubus_db) |
| { |
| printf("mbtk_dtmf_ubus not init!\n"); |
| return; |
| } |
| blob_buf_init(&audio_cm_b, 0); |
| |
| if ((rc = ubus_invoke(mbtk_audio_ubus_db->ctx, |
| mbtk_audio_ubus_db->audioif_request_id, |
| AUDIO_UBUS_LOOPBACK_DIS, |
| audio_cm_b.head, NULL, 0, 0)) != UBUS_STATUS_OK) |
| { |
| LOGI("%s, ubus_invoke volume get failed %s\n", __FUNCTION__, ubus_strerror(rc)); |
| } |
| else |
| { |
| LOGI("%s: ubus_invoke success\n", __FUNCTION__); |
| } |
| } |
| |
| int mbtk_at_loopback(int type) |
| { |
| static mbtk_audio_client_handle_type audio_handle = 0; |
| int ret = 0; |
| |
| LOGI("%s %d:%d", __FUNCTION__, __LINE__, type); |
| if(0 == type) // Stop |
| { |
| mbtk_audio_loopback_stop(); |
| if(audio_handle) |
| { |
| mbtk_audio_ubus_client_deinit(audio_handle); |
| audio_handle = 0; |
| } |
| } |
| else // Start |
| { |
| if(NULL == mbtk_audio_ubus_db) |
| ret = mbtk_audio_ubus_client_init(&audio_handle, NULL); |
| if(ret) |
| { |
| return -1; |
| } |
| mbtk_audio_mode_set(0); |
| mbtk_audio_loopback_start(2); |
| } |
| |
| return 0; |
| } |
| |
| /***************************************************** \ |
| * Retry mechanism to add subscriber for Audio_if |
| \*****************************************************/ |
| static void mbtk_dtmf_add_subscriber_AudioIf(struct uloop_timeout *timeout) |
| { |
| /* add subscriber for Audio_If */ |
| if (ubus_lookup_id(mbtk_audio_ubus_db->ctx, DTMFCODE_NOTIFICATION_NAME, &mbtk_audio_ubus_db->audioif_subscriber_id)) |
| { |
| printf("%s, Failed to look up %s\n", __FUNCTION__, DTMFCODE_NOTIFICATION_NAME); |
| uloop_timeout_set(timeout, 2000); |
| return; |
| } |
| |
| ubus_subscribe(mbtk_audio_ubus_db->ctx, &mbtk_audio_ubus_db->audioif_event, mbtk_audio_ubus_db->audioif_subscriber_id); |
| mbtk_audio_log("Audio_If subscribe %s object ok\n", DTMFCODE_NOTIFICATION_NAME); |
| return; |
| } |
| /*******************************************************************************\ |
| * Function: mbtk_dtmf_uBus_pmConstraint |
| * Description: Lock/unlock system power management. |
| * 0: unlock |
| * 1: lock |
| * Returns: |
| \*******************************************************************************/ |
| static void mbtk_dtmf_uBus_PMConstraint(int set) |
| { |
| FILE *flk; |
| if (mbtk_dtmf_PMConstraintWorks != 0) |
| return; //wake_lock unavailable or not exist. Do nothing |
| |
| #ifdef PROSLIC_ENABLEFREERUN |
| mbtk_audio_log("%s(%d): %s\n", __FUNCTION__, set, set ? "no sleep" : "sleep after 7s"); |
| #else |
| mbtk_audio_log("%s(%d): free-run is disabled, no sleep\n", __FUNCTION__, set); |
| #endif |
| |
| /* Do Not check fopen success. It must be ok, if failed |
| ** (for any reason) better to crash for logging and recovery |
| */ |
| flk = fopen("/sys/power/wake_lock", "w"); |
| if(set) |
| { |
| fprintf(flk, mbtk_dtmf_UBUS_NAME); |
| } |
| else |
| { |
| #ifdef PROSLIC_ENABLEFREERUN |
| /* Clear constraint but with TimeOut enough |
| ** Stay awake for (mbtk_dtmf_DIAL_TIMER + 1) seconds, wait for ringing timer expire |
| */ |
| fprintf(flk, mbtk_dtmf_UBUS_NAME " 7000000000"/*nsec*/); |
| #endif |
| } |
| |
| fclose(flk); |
| } |
| |
| /*******************************************************************************\ |
| * Function: mbtk_dtmf_uBusInd_AudioIf_remove |
| * Description: Handle upon Audio_if remove indication. |
| * Returns: |
| \*******************************************************************************/ |
| static void mbtk_dtmf_uBusInd_AudioIf_remove(struct ubus_context *ctx, struct ubus_subscriber *s, |
| uint32_t id) |
| { |
| UNUSEDPARAM(ctx); |
| UNUSEDPARAM(s); |
| UNUSEDPARAM(id); |
| |
| mbtk_audio_log("%s\n", __FUNCTION__); |
| uloop_timeout_set(&mbtk_dtmf_add_subscribe_timeout_AudioIf, 2000); |
| } |
| |
| |
| /*******************************************************************************\ |
| * Function: mbtk_dtmf_uBusInd_AudioIf |
| * Description: Handle upon Audio_If incoming indication. |
| * Returns: 0 on success |
| \*******************************************************************************/ |
| static int mbtk_dtmf_uBusInd_AudioIf(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); |
| |
| char DTMFCode = 0; |
| struct blob_attr *tb[1]; |
| int rc = 0; |
| |
| //Lock from mbtk_dtmf interrupt handler (mbtk_dtmf_MainLoop) |
| pthread_mutex_lock(&mbtk_dtmf_mutex); |
| mbtk_audio_log("==================================%s : Begin==================================\n", __FUNCTION__); |
| mbtk_dtmf_uBus_PMConstraint(1); |
| |
| /*parsing blob to be accessed easily with tb array - parse "1" argument*/ |
| rc = blobmsg_parse(dtmf_code_policy, 1, tb, blob_data(msg), blob_len(msg)); |
| |
| if (rc < 0) { |
| printf("%s: parsing fail, rc = 0x%x\n", __FUNCTION__, rc); |
| } |
| else { |
| DTMFCode = (char)blobmsg_get_u32(tb[0]); |
| if(mbtk_audio_ubus_db->dtmf_cb) |
| mbtk_audio_ubus_db->dtmf_cb(DTMFCode); |
| mbtk_audio_log("%s got DTMF Code: Char[%c]=ASCII[0x%x]\n", __FUNCTION__, DTMFCode, DTMFCode); |
| } |
| |
| mbtk_dtmf_uBus_PMConstraint(0); |
| mbtk_audio_log("==================================%s : End==================================", __FUNCTION__); |
| pthread_mutex_unlock(&mbtk_dtmf_mutex); |
| |
| return rc; |
| } |
| |
| /*******************************************************************************\ |
| * Function: mbtk_dtmf_uBusRegisterNotifications |
| * Description: Register UBUS notifications of RIL and Audio_If. |
| * Returns: 0 on success |
| \*******************************************************************************/ |
| static int mbtk_dtmf_uBusRegisterNotifications(void) |
| { |
| int ret = 0; |
| |
| /* register UBUS notifications of Audio_If */ |
| ret = ubus_register_subscriber(mbtk_audio_ubus_db->ctx, &mbtk_audio_ubus_db->audioif_event); |
| if (ret) |
| { |
| printf("%s Failed to add audio_if watch handler: %s\n", __FUNCTION__, ubus_strerror(ret)); |
| return -1; |
| } |
| |
| /* specify callback to handle notifications */ |
| mbtk_audio_ubus_db->audioif_event.remove_cb = mbtk_dtmf_uBusInd_AudioIf_remove; |
| mbtk_audio_ubus_db->audioif_event.cb = mbtk_dtmf_uBusInd_AudioIf; |
| |
| /* add subscribe */ |
| uloop_timeout_set(&mbtk_dtmf_add_subscribe_timeout_AudioIf, 0); |
| |
| mbtk_audio_log("%s: Make use of DTMF detection in modem.\n", __FUNCTION__); |
| |
| return 0; |
| } |
| |
| void mbtk_switch_dtmf_detection_cb(struct ubus_request *req, int rc) |
| { |
| if (req) |
| { |
| free(req); |
| req = NULL; |
| } |
| mbtk_audio_log("%s do nothing, rc=%d\n", __FUNCTION__, rc); |
| } |
| /** |
| * @brief mbtk_switch_dtmf_detection |
| * |
| * @details detailed description |
| * |
| * @param param |
| * “param0": UINT32 onoff |
| * 0: disbale Tx DTMF detection |
| * 1: enable Tx DTMF detection |
| * 2: disbale Rx DTMF detection |
| * 3: enable Rx DTMF detection |
| * @return return type |
| */ |
| void mbtk_switch_dtmf_detection(unsigned short on_off, unsigned short param1, |
| unsigned short param2, unsigned short param3) |
| { |
| #if PCM_CTRL_OVER_VCM |
| vcm_DTMFDetection(on_off, param1, param2, param3); |
| #else |
| int rc = 0; |
| struct ubus_request *req = NULL; |
| |
| if(NULL == mbtk_audio_ubus_db) |
| { |
| printf("mbtk_dtmf_ubus not init!\n"); |
| return; |
| } |
| req = (struct ubus_request *)malloc(sizeof(struct ubus_request)); |
| if (req == NULL) |
| { |
| printf("leave %s: lack of memory\n", __FUNCTION__); |
| return; |
| } |
| memset(req, 0, sizeof(struct ubus_request)); |
| blob_buf_init(&audio_cm_b, 0); |
| blobmsg_add_u32(&audio_cm_b, "param0", on_off); |
| blobmsg_add_u32(&audio_cm_b, "param1", param1); |
| blobmsg_add_u32(&audio_cm_b, "param2", param2); |
| blobmsg_add_u32(&audio_cm_b, "param3", param3); |
| |
| if ((rc = ubus_invoke_async(mbtk_audio_ubus_db->ctx, mbtk_audio_ubus_db->audioif_request_id, "dtmf_detection", audio_cm_b.head, req)) != UBUS_STATUS_OK) |
| { |
| free(req); |
| printf("%s, ubus_invoke dtmf_detection failed %s\n", __FUNCTION__, ubus_strerror(rc)); |
| } |
| else |
| { |
| mbtk_audio_log("%s: ubus_invoke_async success\n", __FUNCTION__); |
| req->complete_cb = mbtk_switch_dtmf_detection_cb; |
| ubus_complete_request_async(mbtk_audio_ubus_db->ctx, req); |
| } |
| #endif |
| } |
| /*******************************************************************************\ |
| * Function: mbtk_dtmf_uBusInit |
| * Description: Init UBUS context and register UBUS notifications of RIL. |
| * Returns: 0 on success |
| \*******************************************************************************/ |
| int mbtk_dtmf_uBusInit(void) |
| { |
| int rc = 0; |
| |
| uloop_init(); |
| |
| mbtk_audio_ubus_db->ctx = ubus_connect(NULL); |
| if (mbtk_audio_ubus_db->ctx == NULL) |
| { |
| printf(" %s Failed to connect to ubus\n", __FUNCTION__); |
| return -1; |
| } |
| |
| ubus_add_uloop(mbtk_audio_ubus_db->ctx); |
| |
| /* lookup fail if audio_if is not ready */ |
| while(1) |
| { |
| rc = ubus_lookup_id(mbtk_audio_ubus_db->ctx, AUDIO_UBUS_REQUEST_NAME, &mbtk_audio_ubus_db->audioif_request_id); |
| if (0 != rc) |
| { |
| printf("%s, Failed to look up(%s), rc=%d\n", __FUNCTION__, AUDIO_UBUS_REQUEST_NAME, rc); |
| usleep(600000); //600ms |
| } |
| else |
| break; |
| } |
| mbtk_audio_log("%s, ubus_lookup_id(%s) OK\n", __FUNCTION__, AUDIO_UBUS_REQUEST_NAME); |
| |
| /* subscribe for RIL & audio_if notifications */ |
| if (mbtk_dtmf_uBusRegisterNotifications() != 0) |
| return -1; |
| |
| mbtk_audio_log("%s success!\n", __FUNCTION__); |
| return 0; |
| } |
| |
| |
| /*******************************************************************************\ |
| * Function: mbtk_dtmf_uBusRun |
| * Description: Start running of UBUS. |
| * Returns: |
| \*******************************************************************************/ |
| static void mbtk_audio_ubus_thread(void* hdl) |
| { |
| pthread_detach(pthread_self()); |
| uloop_run(); |
| mbtk_audio_log("%s exit!\n", __FUNCTION__); |
| pthread_exit(NULL); |
| } |
| |
| int mbtk_audio_ubus_client_init(mbtk_audio_client_handle_type *ph_audio, mbtk_dtmf_cb cb) |
| { |
| #ifndef MBTK_YX_SUPPORT |
| // Set call handle. |
| if(ph_audio == NULL || mbtk_audio_ubus_db != NULL) |
| { |
| printf("ARG error or has inited."); |
| return -1; |
| } |
| mbtk_audio_ubus_db = malloc(sizeof(struct mbtk_audio_ubus_db_t)); |
| if(NULL == mbtk_audio_ubus_db) |
| { |
| printf("malloc memory error\n"); |
| } |
| memset(mbtk_audio_ubus_db, 0, sizeof(struct mbtk_audio_ubus_db_t)); |
| |
| mbtk_dtmf_PMConstraintWorks = access("/sys/power/wake_lock", R_OK | W_OK); |
| mbtk_dtmf_uBusInit(); |
| mbtk_audio_ubus_db->dtmf_cb = cb; |
| |
| mbtk_switch_dtmf_detection(3, 50, 4, 3); |
| pthread_create(&mbtk_audio_ubus_db->dtmf_pthread, NULL, (void *)mbtk_audio_ubus_thread, (void *)mbtk_audio_ubus_db); |
| |
| *ph_audio = mbtk_audio_ubus_db; |
| LOGI("%s %d:%x", __FUNCTION__, __LINE__, mbtk_audio_ubus_db); |
| #else |
| audio_ctx = ubus_connect(NULL); |
| if (!audio_ctx) { |
| printf("Failed to connect to ubus\n"); |
| return -1; |
| } |
| |
| int ret = ubus_lookup_id(audio_ctx, AUDIO_UBUS_REQUEST_NAME, &ubus_id_audio_if); |
| if (ret) { |
| printf("ubus_lookup_id() fail.\n"); |
| return ret; |
| } |
| #endif |
| return 0; |
| } |
| |
| int mbtk_audio_ubus_client_deinit(mbtk_audio_client_handle_type h_audio) |
| { |
| int ret; |
| #ifndef MBTK_YX_SUPPORT |
| struct mbtk_audio_ubus_db_t *audio_ubus = (struct mbtk_audio_ubus_db_t *)h_audio; |
| |
| mbtk_audio_log("%s \n", __FUNCTION__); |
| if(h_audio == NULL || mbtk_audio_ubus_db == NULL) |
| { |
| printf("ARG error or not inited."); |
| return -1; |
| } |
| LOGI("%s %d:%x", __FUNCTION__, __LINE__, audio_ubus); |
| ret = pthread_cancel(audio_ubus->dtmf_pthread); |
| mbtk_audio_log("kill pthread : %d \n", ret); |
| pthread_join(audio_ubus->dtmf_pthread, NULL); |
| do{ |
| ret = pthread_kill(audio_ubus->dtmf_pthread, 0); |
| mbtk_audio_log("kill pthread: %d \n", ret); |
| if(ret == ESRCH) |
| mbtk_audio_log("The specified thread does not exist or has terminated\n"); |
| else if(ret == EINVAL) |
| mbtk_audio_log("Useless signal\n"); |
| else |
| mbtk_audio_log("The thread exists\n"); |
| usleep(100000); |
| }while(0 == ret); |
| LOGI("%s %d", __FUNCTION__, __LINE__); |
| |
| mbtk_switch_dtmf_detection(2, 0, 0, 0); |
| /*unregister uloop*/ |
| /*thread will get here only if uloop stopped*/ |
| ubus_free(mbtk_audio_ubus_db->ctx); |
| uloop_done(); |
| LOGI("%s %d", __FUNCTION__, __LINE__); |
| free(h_audio); |
| mbtk_audio_ubus_db = NULL; |
| #else |
| if(audio_ctx) { |
| ubus_free(audio_ctx); |
| audio_ctx = NULL; |
| } |
| #endif |
| return 0; |
| } |
| |
| /**********************************************************************\ |
| * Function: APP_Audio_VolumeSet |
| * Description: This function is called to send command into audio_if. |
| * unsigned int volume(0~100) |
| * Returns: 0 on success |
| \**********************************************************************/ |
| void mbtk_audio_ubus_volume_set(unsigned int volume) |
| { |
| int rc = 0; |
| struct ubus_request *req = NULL; |
| |
| if(NULL == mbtk_audio_ubus_db) |
| { |
| printf("mbtk_dtmf_ubus not init!\n"); |
| return; |
| } |
| req = (struct ubus_request *)malloc(sizeof(struct ubus_request)); |
| if (req == NULL) |
| { |
| printf("leave %s: lack of memory\n", __FUNCTION__); |
| return; |
| } |
| memset(req, 0, sizeof(struct ubus_request)); |
| blob_buf_init(&audio_cm_b, 0); |
| blobmsg_add_u32(&audio_cm_b, "param0", volume); |
| |
| if ((rc = ubus_invoke_async(mbtk_audio_ubus_db->ctx, mbtk_audio_ubus_db->audioif_request_id, AUDIO_UBUS_VOLUME_SET, audio_cm_b.head, req)) != UBUS_STATUS_OK) |
| { |
| free(req); |
| printf("%s, ubus_invoke_async volume set failed: %s\n", __FUNCTION__, ubus_strerror(rc)); |
| } |
| else |
| { |
| mbtk_audio_log("%s: ubus_invoke_async success\n", __FUNCTION__); |
| req->complete_cb = mbtk_ubus_complete_cb; |
| ubus_complete_request_async(mbtk_audio_ubus_db->ctx, req); |
| } |
| } |
| |
| #define MBTK_AUDIO_GAIN_NUM 11 |
| void mbtk_audio_ubus_gain_set(mbtk_audio_gain_type_enum type, uint16 *gain, int gain_num) |
| { |
| int rc = 0; |
| struct ubus_request *req = NULL; |
| |
| req = (struct ubus_request *)malloc(sizeof(struct ubus_request)); |
| if (req == NULL) |
| { |
| printf("leave %s: lack of memory\n", __FUNCTION__); |
| return; |
| } |
| if((type == AUDIO_GAIN_TYPE_RX_CODECGAIN || type == AUDIO_GAIN_TYPE_RX_DSPGAIN) |
| && gain_num != MBTK_AUDIO_GAIN_NUM) { |
| printf("gain_num error:type = %d, gain_name = %d.\n", type, gain_num); |
| return; |
| } |
| if((type == AUDIO_GAIN_TYPE_TX_CODECGAIN || type == AUDIO_GAIN_TYPE_TX_DSPGAIN) |
| && gain_num != 1) { |
| printf("gain_num error:type = %d, gain_name = %d.\n", type, gain_num); |
| return; |
| } |
| memset(req, 0, sizeof(struct ubus_request)); |
| blob_buf_init(&audio_cm_b, 0); |
| |
| // Set RX |
| blobmsg_add_u16(&audio_cm_b, "volume_gain_type", type); |
| if(type == AUDIO_GAIN_TYPE_TX_CODECGAIN || type == AUDIO_GAIN_TYPE_TX_DSPGAIN) { |
| blobmsg_add_u16(&audio_cm_b, "volume_gain_0", gain[0]); |
| } else { |
| char name[20]; |
| int i = 0; |
| // Set TX |
| for(; i < gain_num; i++) { |
| memset(name, 0x0, 20); |
| sprintf(name, "volume_gain_%d", i); |
| blobmsg_add_u16(&audio_cm_b, name, gain[i]); |
| } |
| } |
| |
| #if 1 |
| if ((rc = ubus_invoke_async(mbtk_audio_ubus_db->ctx, mbtk_audio_ubus_db->audioif_request_id, AUDIO_UBUS_AUDIO_GAIN_SET, audio_cm_b.head, req)) != UBUS_STATUS_OK) |
| { |
| free(req); |
| printf("%s, ubus_invoke_async volume set failed: %s\n", __FUNCTION__, ubus_strerror(rc)); |
| } |
| else |
| { |
| printf("%s: ubus_invoke_async success\n", __FUNCTION__); |
| req->complete_cb = mbtk_ubus_complete_cb; |
| ubus_complete_request_async(mbtk_audio_ubus_db->ctx, req); |
| } |
| #else |
| if ((rc = ubus_invoke(APP_ctx, APP_audio_request_id, AUDIO_UBUS_AUDIO_GAIN_SET, audio_cm_b.head, NULL, 0, 0)) != UBUS_STATUS_OK) |
| { |
| printf("%s, ubus_invoke_async volume set failed: %s\n", __FUNCTION__, ubus_strerror(rc)); |
| } |
| else |
| { |
| printf("%s: ubus_invoke_async success\n", __FUNCTION__); |
| } |
| free(req); |
| #endif |
| } |
| |
| void mbtk_audio_ubus_reg_set(int reg_addr, int reg_value) |
| { |
| int rc = 0; |
| struct ubus_request *req = NULL; |
| |
| req = (struct ubus_request *)malloc(sizeof(struct ubus_request)); |
| if (req == NULL) |
| { |
| printf("leave %s: lack of memory\n", __FUNCTION__); |
| return; |
| } |
| |
| memset(req, 0, sizeof(struct ubus_request)); |
| blob_buf_init(&audio_cm_b, 0); |
| blobmsg_add_u32(&audio_cm_b, "reg_addr", reg_addr); |
| blobmsg_add_u32(&audio_cm_b, "reg_value", reg_value); |
| |
| #if 1 |
| if ((rc = ubus_invoke_async(mbtk_audio_ubus_db->ctx, mbtk_audio_ubus_db->audioif_request_id, AUDIO_UBUS_AUDIO_REG_SET, audio_cm_b.head, req)) != UBUS_STATUS_OK) |
| { |
| free(req); |
| printf("%s, ubus_invoke_async volume set failed: %s\n", __FUNCTION__, ubus_strerror(rc)); |
| } |
| else |
| { |
| printf("%s: ubus_invoke_async success\n", __FUNCTION__); |
| req->complete_cb = mbtk_ubus_complete_cb; |
| ubus_complete_request_async(mbtk_audio_ubus_db->ctx, req); |
| } |
| #else |
| if ((rc = ubus_invoke(APP_ctx, APP_audio_request_id, AUDIO_UBUS_AUDIO_GAIN_SET, audio_cm_b.head, NULL, 0, 0)) != UBUS_STATUS_OK) |
| { |
| printf("%s, ubus_invoke_async volume set failed: %s\n", __FUNCTION__, ubus_strerror(rc)); |
| } |
| else |
| { |
| printf("%s: ubus_invoke_async success\n", __FUNCTION__); |
| } |
| free(req); |
| #endif |
| } |
| |
| static void audio_volume_get_data_cb (struct ubus_request *req, int type, struct blob_attr *msg) |
| { |
| UNUSEDPARAM(req); |
| UNUSEDPARAM(type); |
| struct blob_attr *tb[1]; |
| unsigned int volume = 888; |
| int rc = 0; |
| |
| rc = blobmsg_parse(int_policy, 1, tb, blob_data(msg), blob_len(msg)); |
| if (rc < 0) |
| { |
| printf("[%s] parsing blobmsg failed\n", __FUNCTION__); |
| return; |
| } |
| |
| if(tb[0]) |
| volume = blobmsg_get_u32(tb[0]); |
| if (mbtk_audio_ubus_db->audio_volume_get_cb) |
| mbtk_audio_ubus_db->audio_volume_get_cb(volume); |
| mbtk_audio_log("%s; volume:%d\n", __FUNCTION__, volume); |
| } |
| |
| void mbtk_audio_ubus_volume_get(mbtk_volume_cb cb) |
| { |
| int rc = 0; |
| struct ubus_request *req = NULL; |
| |
| if(NULL == mbtk_audio_ubus_db || NULL == cb) |
| { |
| printf("mbtk_dtmf_ubus not init or callback invalid!\n"); |
| return; |
| } |
| mbtk_audio_ubus_db->audio_volume_get_cb = cb; |
| req = (struct ubus_request *)malloc(sizeof(struct ubus_request)); |
| if (req == NULL) |
| { |
| printf("leave %s: lack of memory\n", __FUNCTION__); |
| return; |
| } |
| memset(req, 0, sizeof(struct ubus_request)); |
| blob_buf_init(&audio_cm_b, 0); |
| |
| if ((rc = ubus_invoke_async(mbtk_audio_ubus_db->ctx, |
| mbtk_audio_ubus_db->audioif_request_id, |
| AUDIO_UBUS_VOLUME_GET, |
| audio_cm_b.head, req)) != UBUS_STATUS_OK) |
| { |
| free(req); |
| printf("%s, ubus_invoke_async volume get failed %s\n", __FUNCTION__, ubus_strerror(rc)); |
| } |
| else |
| { |
| mbtk_audio_log("%s: ubus_invoke_async success\n", __FUNCTION__); |
| req->data_cb = audio_volume_get_data_cb; |
| req->complete_cb = mbtk_ubus_complete_cb; |
| ubus_complete_request_async(mbtk_audio_ubus_db->ctx, req); |
| } |
| } |