--- a/pjmedia/src/pjmedia-audiodev/audiodev.c +++ b/pjmedia/src/pjmedia-audiodev/audiodev.c @@ -98,6 +98,10 @@ pjmedia_aud_dev_factory* pjmedia_symb_md pjmedia_aud_dev_factory* pjmedia_null_audio_factory(pj_pool_factory *pf); #endif +#if PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE +pjmedia_aud_dev_factory* pjmedia_tapi_factory(pj_pool_factory *pf); +#endif + #define MAX_DRIVERS 16 #define MAX_DEVS 64 @@ -409,6 +413,9 @@ PJ_DEF(pj_status_t) pjmedia_aud_subsys_i #if PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_null_audio_factory; #endif +#if PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE + aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_tapi_factory; +#endif /* Initialize each factory and build the device ID list */ for (i=0; i +#include +#include +#include +#include +#include + +#if defined(PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE) && PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE + +/* Linux includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* TAPI includes */ +#include "drv_tapi_io.h" +#include "vmmc_io.h" + +/* Maximum 2 devices */ +#define TAPI_AUDIO_PORT_NUM 2 +#define TAPI_BASE_NAME "TAPI" +#define TAPI_LL_DEV_BASE_PATH "/dev/vmmc" +#define TAPI_LL_DEV_FIRMWARE_NAME "/lib/firmware/danube_firmware.bin" +#define TAPI_LL_BBD_NAME "/lib/firmware/danube_bbd_fxs.bin" + +#define TAPI_LL_DEV_SELECT_TIMEOUT_MS 2000 +#define TAPI_LL_DEV_MAX_PACKET_SIZE 800 +#define TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE 12 +#define TAPI_LL_DEV_ENC_FRAME_LEN_MS 20 +#define TAPI_LL_DEV_ENC_SMPL_PER_SEC 8000 +#define TAPI_LL_DEV_ENC_BITS_PER_SMPLS 16 +#define TAPI_LL_DEV_ENC_SMPL_PER_FRAME 160 +#define TAPI_LL_DEV_ENC_BYTES_PER_FRAME (TAPI_LL_DEV_ENC_SMPL_PER_FRAME * (TAPI_LL_DEV_ENC_BITS_PER_SMPLS / 8)) + +#define THIS_FILE "tapi_dev.c" + +/* Set to 1 to enable tracing */ +#if 1 +# define TRACE_(expr) PJ_LOG(1,expr) +#else +# define TRACE_(expr) +#endif + +pj_int32_t ch_fd[TAPI_AUDIO_PORT_NUM]; + +typedef struct +{ + pj_int32_t dev_fd; + pj_int32_t ch_fd[TAPI_AUDIO_PORT_NUM]; + pj_int8_t data2phone_map[TAPI_AUDIO_PORT_NUM]; +} tapi_ctx; + +struct tapi_aud_factory +{ + pjmedia_aud_dev_factory base; + pj_pool_t *pool; + pj_pool_factory *pf; + pj_uint32_t dev_count; + pjmedia_aud_dev_info *dev_info; + tapi_ctx dev_ctx; +}; + +typedef struct tapi_aud_factory tapi_aud_factory_t; + +struct tapi_aud_stream +{ + pjmedia_aud_stream base; + pj_pool_t *pool; + pjmedia_aud_param param; + pjmedia_aud_rec_cb rec_cb; + pjmedia_aud_play_cb play_cb; + void *user_data; + + pj_thread_desc thread_desc; + pj_thread_t *thread; + tapi_ctx *dev_ctx; + pj_uint8_t run_flag; + pj_timestamp timestamp; +}; + +typedef struct tapi_aud_stream tapi_aud_stream_t; + +/* Factory prototypes */ +static pj_status_t factory_init(pjmedia_aud_dev_factory *f); +static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f); +static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f); +static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f, + unsigned index, + pjmedia_aud_dev_info *info); +static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f, + unsigned index, + pjmedia_aud_param *param); +static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f, + const pjmedia_aud_param *param, + pjmedia_aud_rec_cb rec_cb, + pjmedia_aud_play_cb play_cb, + void *user_data, + pjmedia_aud_stream **p_aud_strm); + +/* Stream prototypes */ +static pj_status_t stream_get_param(pjmedia_aud_stream *strm, + pjmedia_aud_param *param); +static pj_status_t stream_get_cap(pjmedia_aud_stream *strm, + pjmedia_aud_dev_cap cap, + void *value); +static pj_status_t stream_set_cap(pjmedia_aud_stream *strm, + pjmedia_aud_dev_cap cap, + const void *value); +static pj_status_t stream_start(pjmedia_aud_stream *strm); +static pj_status_t stream_stop(pjmedia_aud_stream *strm); +static pj_status_t stream_destroy(pjmedia_aud_stream *strm); + +static pjmedia_aud_dev_factory_op tapi_fact_op = +{ + &factory_init, + &factory_destroy, + &factory_get_dev_count, + &factory_get_dev_info, + &factory_default_param, + &factory_create_stream +}; + +static pjmedia_aud_stream_op tapi_strm_op = +{ + &stream_get_param, + &stream_get_cap, + &stream_set_cap, + &stream_start, + &stream_stop, + &stream_destroy +}; + +/* TAPI configuration */ +static struct tapi_aud_stream streams[TAPI_AUDIO_PORT_NUM]; + +void (*tapi_digit_callback)(pj_uint8_t port, pj_uint8_t digit) = NULL; +void (*tapi_hook_callback)(pj_uint8_t port, pj_uint8_t event) = NULL; + +#define TAPI_TONE_LOCALE_NONE 32 +#define TAPI_TONE_LOCALE_BUSY_CODE 33 +#define TAPI_TONE_LOCALE_CONGESTION_CODE 34 +#define TAPI_TONE_LOCALE_DIAL_CODE 35 +#define TAPI_TONE_LOCALE_RING_CODE 36 +#define TAPI_TONE_LOCALE_WAITING_CODE 37 + +static pj_uint8_t tapi_channel_revert = 0; +static pj_uint8_t tapi_cid_type = 0; +static pj_uint8_t tapi_locale = 0; + +void tapi_revert_channels(void) +{ + tapi_channel_revert = 1; + PJ_LOG(3, (THIS_FILE, "using reverted configuration for TAPI channels")); +} + +void tapi_cid_select(char *cid) +{ + if (!stricmp(cid, "telecordia")) { + tapi_cid_type = IFX_TAPI_CID_STD_TELCORDIA; + PJ_LOG(3, (THIS_FILE, "using TELECORDIA configuration for TAPI CID")); + } else if (!stricmp(cid, "etsi_fsk")) { + tapi_cid_type = IFX_TAPI_CID_STD_ETSI_FSK; + PJ_LOG(3, (THIS_FILE, "using ETSI FSK configuration for TAPI CID")); + } else if (!stricmp(cid, "etsi_dtmf")) { + tapi_cid_type = IFX_TAPI_CID_STD_ETSI_DTMF; + PJ_LOG(3, (THIS_FILE, "using ETSI DTMF configuration for TAPI CID")); + } else if (!stricmp(cid, "sin")) { + tapi_cid_type = IFX_TAPI_CID_STD_SIN; + PJ_LOG(3, (THIS_FILE, "using SIN CID configuration for TAPI CID")); + } else if (!stricmp(cid, "ntt")) { + tapi_cid_type = IFX_TAPI_CID_STD_NTT; + PJ_LOG(3, (THIS_FILE, "using NTT configuration for TAPI CID")); + } else if (!stricmp(cid, "kpn_dtmf")) { + tapi_cid_type = IFX_TAPI_CID_STD_KPN_DTMF; + PJ_LOG(3, (THIS_FILE, "using KPN DTMF configuration for TAPI CID")); + } else if (!stricmp(cid, "kpn_dtmf_fsk")) { + tapi_cid_type = IFX_TAPI_CID_STD_KPN_DTMF_FSK; + PJ_LOG(3, (THIS_FILE, "using KPN DTMF FSK configuration for TAPI CID")); + } +} + +void tapi_locale_select(char *country) +{ + IFX_TAPI_TONE_t tone; + pj_status_t status; + pj_uint8_t c; + + tapi_locale = 1; + + if (!stricmp(country, "croatia")) { + PJ_LOG(3, (THIS_FILE, "using localized tones for Croatia")); + + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + tone.simple.index = TAPI_TONE_LOCALE_BUSY_CODE; + tone.simple.freqA = 425; + tone.simple.levelA = 0; + tone.simple.cadence[0] = 500; + tone.simple.cadence[1] = 500; + tone.simple.frequencies[0] = IFX_TAPI_TONE_FREQA; + tone.simple.frequencies[1] = IFX_TAPI_TONE_FREQNONE; + tone.simple.loop = 0; + tone.simple.pause = 0; + for (c = 0; c < TAPI_AUDIO_PORT_NUM; c++) { + status = ioctl(ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone); + if (status != PJ_SUCCESS) + TRACE_((THIS_FILE, "IFX_TAPI_TONE_TABLE_CFG_SET failed!\n")); + } + + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + tone.simple.index = TAPI_TONE_LOCALE_CONGESTION_CODE; + tone.simple.freqA = 425; + tone.simple.levelA = 0; + tone.simple.cadence[0] = 250; + tone.simple.cadence[1] = 250; + tone.simple.frequencies[0] = IFX_TAPI_TONE_FREQA; + tone.simple.frequencies[1] = IFX_TAPI_TONE_FREQNONE; + tone.simple.loop = 0; + tone.simple.pause = 0; + for (c = 0; c < TAPI_AUDIO_PORT_NUM; c++) { + status = ioctl(ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone); + if (status != PJ_SUCCESS) + TRACE_((THIS_FILE, "IFX_TAPI_TONE_TABLE_CFG_SET failed!\n")); + } + + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + tone.simple.index = TAPI_TONE_LOCALE_DIAL_CODE; + tone.simple.freqA = 425; + tone.simple.levelA = 0; + tone.simple.cadence[0] = 200; + tone.simple.cadence[1] = 300; + tone.simple.cadence[2] = 700; + tone.simple.cadence[3] = 800; + tone.simple.frequencies[0] = IFX_TAPI_TONE_FREQA; + tone.simple.frequencies[1] = IFX_TAPI_TONE_FREQNONE; + tone.simple.frequencies[2] = IFX_TAPI_TONE_FREQA; + tone.simple.frequencies[3] = IFX_TAPI_TONE_FREQNONE; + tone.simple.loop = 0; + tone.simple.pause = 0; + for (c = 0; c < TAPI_AUDIO_PORT_NUM; c++) { + status = ioctl(ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone); + if (status != PJ_SUCCESS) + TRACE_((THIS_FILE, "IFX_TAPI_TONE_TABLE_CFG_SET failed!\n")); + } + + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + tone.simple.index = TAPI_TONE_LOCALE_RING_CODE; + tone.simple.freqA = 425; + tone.simple.levelA = 0; + tone.simple.cadence[0] = 1000; + tone.simple.cadence[1] = 4000; + tone.simple.frequencies[0] = IFX_TAPI_TONE_FREQA; + tone.simple.frequencies[1] = IFX_TAPI_TONE_FREQNONE; + tone.simple.loop = 0; + tone.simple.pause = 0; + for (c = 0; c < TAPI_AUDIO_PORT_NUM; c++) { + status = ioctl(ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone); + if (status != PJ_SUCCESS) + TRACE_((THIS_FILE, "IFX_TAPI_TONE_TABLE_CFG_SET failed!\n")); + } + + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + tone.simple.index = TAPI_TONE_LOCALE_WAITING_CODE; + tone.simple.freqA = 425; + tone.simple.levelA = 0; + tone.simple.cadence[0] = 300; + tone.simple.cadence[1] = 8000; + tone.simple.frequencies[0] = IFX_TAPI_TONE_FREQA; + tone.simple.frequencies[1] = IFX_TAPI_TONE_FREQNONE; + tone.simple.loop = 0; + tone.simple.pause = 0; + for (c = 0; c < TAPI_AUDIO_PORT_NUM; c++) { + status = ioctl(ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone); + if (status != PJ_SUCCESS) + TRACE_((THIS_FILE, "IFX_TAPI_TONE_TABLE_CFG_SET failed!\n")); + } + } else if (!stricmp(country, "germany")) { + PJ_LOG(3, (THIS_FILE, "using localized tones for Germany")); + + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + tone.simple.index = TAPI_TONE_LOCALE_BUSY_CODE; + tone.simple.freqA = 425; + tone.simple.levelA = 0; + tone.simple.cadence[0] = 480; + tone.simple.cadence[1] = 480; + tone.simple.frequencies[0] = IFX_TAPI_TONE_FREQA; + tone.simple.frequencies[1] = IFX_TAPI_TONE_FREQNONE; + tone.simple.loop = 0; + tone.simple.pause = 0; + for (c = 0; c < TAPI_AUDIO_PORT_NUM; c++) { + status = ioctl(ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone); + if (status != PJ_SUCCESS) + TRACE_((THIS_FILE, "IFX_TAPI_TONE_TABLE_CFG_SET failed!\n")); + } + + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + tone.simple.index = TAPI_TONE_LOCALE_CONGESTION_CODE; + tone.simple.freqA = 425; + tone.simple.levelA = 0; + tone.simple.cadence[0] = 240; + tone.simple.cadence[1] = 240; + tone.simple.frequencies[0] = IFX_TAPI_TONE_FREQA; + tone.simple.frequencies[1] = IFX_TAPI_TONE_FREQNONE; + tone.simple.loop = 0; + tone.simple.pause = 0; + for (c = 0; c < TAPI_AUDIO_PORT_NUM; c++) { + status = ioctl(ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone); + if (status != PJ_SUCCESS) + TRACE_((THIS_FILE, "IFX_TAPI_TONE_TABLE_CFG_SET failed!\n")); + } + + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + tone.simple.index = TAPI_TONE_LOCALE_DIAL_CODE; + tone.simple.freqA = 425; + tone.simple.levelA = 0; + tone.simple.cadence[0] = 1000; + tone.simple.frequencies[0] = IFX_TAPI_TONE_FREQA; + tone.simple.loop = 0; + tone.simple.pause = 0; + for (c = 0; c < TAPI_AUDIO_PORT_NUM; c++) { + status = ioctl(ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone); + if (status != PJ_SUCCESS) + TRACE_((THIS_FILE, "IFX_TAPI_TONE_TABLE_CFG_SET failed!\n")); + } + + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + tone.simple.index = TAPI_TONE_LOCALE_RING_CODE; + tone.simple.freqA = 425; + tone.simple.levelA = 0; + tone.simple.cadence[0] = 1000; + tone.simple.cadence[1] = 4000; + tone.simple.frequencies[0] = IFX_TAPI_TONE_FREQA; + tone.simple.frequencies[1] = IFX_TAPI_TONE_FREQNONE; + tone.simple.loop = 0; + tone.simple.pause = 0; + for (c = 0; c < TAPI_AUDIO_PORT_NUM; c++) { + status = ioctl(ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone); + if (status != PJ_SUCCESS) + TRACE_((THIS_FILE, "IFX_TAPI_TONE_TABLE_CFG_SET failed!\n")); + } + + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + tone.simple.index = TAPI_TONE_LOCALE_WAITING_CODE; + tone.simple.freqA = 425; + tone.simple.levelA = 0; + tone.simple.cadence[0] = 200; + tone.simple.cadence[1] = 200; + tone.simple.cadence[2] = 200; + tone.simple.cadence[3] = 5000; + tone.simple.frequencies[0] = IFX_TAPI_TONE_FREQA; + tone.simple.frequencies[1] = IFX_TAPI_TONE_FREQNONE; + tone.simple.frequencies[2] = IFX_TAPI_TONE_FREQA; + tone.simple.frequencies[3] = IFX_TAPI_TONE_FREQNONE; + tone.simple.loop = 0; + tone.simple.pause = 0; + for (c = 0; c < TAPI_AUDIO_PORT_NUM; c++) { + status = ioctl(ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone); + if (status != PJ_SUCCESS) + TRACE_((THIS_FILE, "IFX_TAPI_TONE_TABLE_CFG_SET failed!\n")); + } + } +} + +static pj_int32_t +tapi_dev_open(char* dev_path, const pj_int32_t ch_num) +{ + char devname[128] = { 0 }; + pj_ansi_sprintf(devname,"%s%u%u", dev_path, 1, ch_num); + return open((const char*)devname, O_RDWR, 0644); +} + +static pj_status_t +tapi_dev_binary_buffer_create(const char *pPath, pj_uint8_t **ppBuf, pj_uint32_t *pBufSz) +{ + pj_status_t status = PJ_SUCCESS; + FILE *fd; + struct stat file_stat; + + fd = fopen(pPath, "rb"); + if (fd == NULL) { + TRACE_((THIS_FILE, "ERROR - binary file %s open failed!\n", pPath)); + return PJ_EUNKNOWN; + } + + if (stat(pPath, &file_stat) != 0) { + TRACE_((THIS_FILE, "ERROR - file %s statistics get failed!\n", pPath)); + return PJ_EUNKNOWN; + } + + *ppBuf = malloc(file_stat.st_size); + if (*ppBuf == NULL) { + TRACE_((THIS_FILE, "ERROR - binary file %s memory allocation failed!\n", pPath)); + status = PJ_EUNKNOWN; + goto on_exit; + } + + if (fread (*ppBuf, sizeof(pj_uint8_t), file_stat.st_size, fd) <= 0) { + TRACE_((THIS_FILE, "ERROR - file %s read failed!\n", pPath)); + status = PJ_EUNKNOWN; + goto on_exit; + } + + *pBufSz = file_stat.st_size; + +on_exit: + if (fd != NULL) + fclose(fd); + + if (*ppBuf != NULL && status != PJ_SUCCESS) + free(*ppBuf); + + return status; +} + +static void +tapi_dev_binary_buffer_delete(pj_uint8_t *pBuf) +{ + if (pBuf != NULL) + free(pBuf); +} + +static pj_status_t +tapi_dev_firmware_download(pj_int32_t fd, const char *pPath) +{ + pj_status_t status = PJ_SUCCESS; + pj_uint8_t *pFirmware = NULL; + pj_uint32_t binSz = 0; + VMMC_IO_INIT vmmc_io_init; + + status = tapi_dev_binary_buffer_create(pPath, &pFirmware, &binSz); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - binary buffer create failed!\n")); + return PJ_EUNKNOWN; + } + + memset(&vmmc_io_init, 0, sizeof(VMMC_IO_INIT)); + vmmc_io_init.pPRAMfw = pFirmware; + vmmc_io_init.pram_size = binSz; + + status = ioctl(fd, FIO_FW_DOWNLOAD, &vmmc_io_init); + if (status != PJ_SUCCESS) + TRACE_((THIS_FILE, "ERROR - FIO_FW_DOWNLOAD ioctl failed!")); + + tapi_dev_binary_buffer_delete(pFirmware); + + return status; +} + +/* NOT USED */ +#if 0 +static int +tapi_dev_bbd_download(int fd, const char *pPath) +{ + int status = PJ_SUCCESS; + unsigned char *pFirmware = NULL; + unsigned int binSz = 0; + VMMC_DWLD_t bbd_data; + + + /* Create binary buffer */ + status = tapi_dev_binary_buffer_create(pPath, &pFirmware, &binSz); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - binary buffer create failed!\n")); + return status; + } + + /* Download Voice Firmware */ + memset(&bbd_data, 0, sizeof(VMMC_DWLD_t)); + bbd_data.buf = pFirmware; + bbd_data.size = binSz; + + status = ioctl(fd, FIO_BBD_DOWNLOAD, &bbd_data); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - FIO_BBD_DOWNLOAD failed!\n")); + } + + /* Delete binary buffer */ + tapi_dev_binary_buffer_delete(pFirmware); + + return status; +} +#endif + +static pj_status_t tapi_dev_start(tapi_aud_factory_t *f) +{ + pj_uint8_t c, hook_status; + IFX_TAPI_TONE_t tone; + IFX_TAPI_DEV_START_CFG_t tapistart; + IFX_TAPI_MAP_DATA_t datamap; + IFX_TAPI_ENC_CFG_t enc_cfg; + IFX_TAPI_LINE_VOLUME_t line_vol; + IFX_TAPI_WLEC_CFG_t lec_cfg; + IFX_TAPI_JB_CFG_t jb_cfg; + IFX_TAPI_CID_CFG_t cid_cfg; + pj_status_t status; + + /* Open device */ + f->dev_ctx.dev_fd = tapi_dev_open(TAPI_LL_DEV_BASE_PATH, 0); + + if (f->dev_ctx.dev_fd < 0) { + TRACE_((THIS_FILE, "ERROR - TAPI device open failed!")); + return PJ_EUNKNOWN; + } + + for (c = 0; c < TAPI_AUDIO_PORT_NUM; c++) { + if (tapi_channel_revert) + ch_fd[c] = f->dev_ctx.ch_fd[c] = tapi_dev_open(TAPI_LL_DEV_BASE_PATH, c + 1); + else + ch_fd[c] = f->dev_ctx.ch_fd[c] = tapi_dev_open(TAPI_LL_DEV_BASE_PATH, TAPI_AUDIO_PORT_NUM - c); + + if (f->dev_ctx.dev_fd < 0) { + TRACE_((THIS_FILE, "ERROR - TAPI channel%d open failed!", c)); + return PJ_EUNKNOWN; + } + if (tapi_channel_revert) + f->dev_ctx.data2phone_map[c] = c & 0x1 ? 1 : 0; + else + f->dev_ctx.data2phone_map[c] = c & 0x1 ? 0 : 1; + } + + status = tapi_dev_firmware_download(f->dev_ctx.dev_fd, TAPI_LL_DEV_FIRMWARE_NAME); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - Voice Firmware Download failed!")); + return PJ_EUNKNOWN; + } + + /* Download coefficients */ + /* + status = tapi_dev_bbd_download(f->dev_ctx.dev_fd, TAPI_LL_BBD_NAME); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - Voice Coefficients Download failed!")); + return PJ_EUNKNOWN; + } + */ + + memset(&tapistart, 0x0, sizeof(IFX_TAPI_DEV_START_CFG_t)); + tapistart.nMode = IFX_TAPI_INIT_MODE_VOICE_CODER; + + /* Start TAPI */ + status = ioctl(f->dev_ctx.dev_fd, IFX_TAPI_DEV_START, &tapistart); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_DEV_START ioctl failed")); + return PJ_EUNKNOWN; + } + + + /* OpenWrt default tone */ + memset(&tone, 0, sizeof(IFX_TAPI_TONE_t)); + tone.simple.format = IFX_TAPI_TONE_TYPE_SIMPLE; + tone.simple.index = TAPI_TONE_LOCALE_NONE; + tone.simple.freqA = 400; + tone.simple.levelA = 0; + tone.simple.freqB = 450; + tone.simple.levelB = 0; + tone.simple.freqC = 550; + tone.simple.levelC = 0; + tone.simple.freqD = 600; + tone.simple.levelD = 0; + tone.simple.cadence[0] = 100; + tone.simple.cadence[1] = 150; + tone.simple.cadence[2] = 100; + tone.simple.cadence[3] = 150; + tone.simple.frequencies[0] = IFX_TAPI_TONE_FREQA | IFX_TAPI_TONE_FREQB; + tone.simple.frequencies[1] = IFX_TAPI_TONE_FREQNONE; + tone.simple.frequencies[2] = IFX_TAPI_TONE_FREQC | IFX_TAPI_TONE_FREQD; + tone.simple.frequencies[3] = IFX_TAPI_TONE_FREQNONE; + tone.simple.loop = 0; + tone.simple.pause = 0; + for (c = 0; c < TAPI_AUDIO_PORT_NUM; c++) { + /* OpenWrt default tone */ + status = ioctl(ch_fd[c], IFX_TAPI_TONE_TABLE_CFG_SET, &tone); + if (status != PJ_SUCCESS) + TRACE_((THIS_FILE, "IFX_TAPI_TONE_TABLE_CFG_SET failed!\n")); + + /* Perform mapping */ + memset(&datamap, 0x0, sizeof(IFX_TAPI_MAP_DATA_t)); + datamap.nDstCh = f->dev_ctx.data2phone_map[c]; + datamap.nChType = IFX_TAPI_MAP_TYPE_PHONE; + + status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_MAP_DATA_ADD, &datamap); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_MAP_DATA_ADD ioctl failed")); + return PJ_EUNKNOWN; + } + + /* Set Line feed */ + status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_LINE_FEED_SET, IFX_TAPI_LINE_FEED_STANDBY); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed")); + return PJ_EUNKNOWN; + } + + /* Configure encoder for linear stream */ + memset(&enc_cfg, 0x0, sizeof(IFX_TAPI_ENC_CFG_t)); + enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20; + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_LIN16_8; + + status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_ENC_CFG_SET, &enc_cfg); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_ENC_CFG_SET ioctl failed")); + return PJ_EUNKNOWN; + } + + /* Suppress TAPI volume, otherwise PJSIP starts autogeneration */ + memset(&line_vol, 0, sizeof(line_vol)); + line_vol.nGainRx = -8; + line_vol.nGainTx = -8; + + status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_PHONE_VOLUME_SET, &line_vol); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_PHONE_VOLUME_SET ioctl failed")); + return PJ_EUNKNOWN; + } + + /* Configure line echo canceller */ + memset(&lec_cfg, 0, sizeof(lec_cfg)); + lec_cfg.nType = IFX_TAPI_WLEC_TYPE_NFE; + lec_cfg.bNlp = IFX_TAPI_LEC_NLP_OFF; + + status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_WLEC_PHONE_CFG_SET, &lec_cfg); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_WLEC_PHONE_CFG_SET ioctl failed")); + return PJ_EUNKNOWN; + } + + /* Configure jitter buffer */ + memset(&jb_cfg, 0, sizeof(jb_cfg)); + jb_cfg.nJbType = IFX_TAPI_JB_TYPE_ADAPTIVE; + jb_cfg.nPckAdpt = IFX_TAPI_JB_PKT_ADAPT_VOICE; + jb_cfg.nLocalAdpt = IFX_TAPI_JB_LOCAL_ADAPT_ON; + jb_cfg.nScaling = 0x10; + jb_cfg.nInitialSize = 0x2d0; + jb_cfg.nMinSize = 0x50; + jb_cfg.nMaxSize = 0x5a0; + + status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_JB_CFG_SET, &jb_cfg); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_JB_CFG_SET ioctl failed")); + return PJ_EUNKNOWN; + } + + /* Configure Caller ID type */ + if (tapi_cid_type) { + memset(&cid_cfg, 0, sizeof(cid_cfg)); + cid_cfg.nStandard = tapi_cid_type; + status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_CID_CFG_SET, &cid_cfg); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_CID_CFG_SET ioctl failed")); + return PJ_EUNKNOWN; + } + } + + /* check hook status */ + hook_status = 0; + status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_LINE_HOOK_STATUS_GET, &hook_status); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_HOOK_STATUS_GET ioctl failed!")); + return PJ_EUNKNOWN; + } + + /* if off hook do initialization */ + if (hook_status) { + status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_LINE_FEED_SET, IFX_TAPI_LINE_FEED_ACTIVE); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed!")); + return PJ_EUNKNOWN; + } + status = ioctl(c, IFX_TAPI_ENC_START, 0); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_ENC_START ioctl failed!")); + return PJ_EUNKNOWN; + } + + status = ioctl(c, IFX_TAPI_DEC_START, 0); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_DEC_START ioctl failed!")); + return PJ_EUNKNOWN; + } + } + } + + return status; +} + +static pj_status_t +tapi_dev_stop(tapi_aud_factory_t *f) +{ + pj_status_t status = PJ_SUCCESS; + pj_uint8_t c; + + if (ioctl(f->dev_ctx.dev_fd, IFX_TAPI_DEV_STOP, 0) != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_DEV_STOP ioctl failed")); + status = PJ_EUNKNOWN; + } + + close(f->dev_ctx.dev_fd); + for (c = TAPI_AUDIO_PORT_NUM; c > 0; c--) + close(f->dev_ctx.ch_fd[TAPI_AUDIO_PORT_NUM-c]); + + return status; +} + +static pj_status_t +tapi_dev_codec_control(pj_int32_t fd, pj_uint8_t start) +{ + if (ioctl(fd, start ? IFX_TAPI_ENC_START : IFX_TAPI_ENC_STOP, 0) != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_ENC_%s ioctl failed!", + start ? "START" : "STOP")); + return PJ_EUNKNOWN; + } + + if (ioctl(fd, start ? IFX_TAPI_DEC_START : IFX_TAPI_DEC_STOP, 0) != IFX_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_DEC_%s ioctl failed!", + start ? "START" : "STOP")); + return PJ_EUNKNOWN; + } + + return PJ_SUCCESS; +} + +static pj_status_t tapi_dev_event_on_hook(tapi_ctx *dev_ctx, pj_uint32_t dev_idx) +{ + PJ_LOG(1,(THIS_FILE, "TAPI: ONHOOK")); + + if (ioctl(dev_ctx->ch_fd[dev_idx], IFX_TAPI_LINE_FEED_SET, + IFX_TAPI_LINE_FEED_STANDBY) != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed!")); + return PJ_EUNKNOWN; + } + + /* enc/dec stop */ + if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 0) != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - codec start failed!")); + return PJ_EUNKNOWN; + } + + return PJ_SUCCESS; +} + +static pj_status_t tapi_dev_event_off_hook(tapi_ctx *dev_ctx, pj_uint32_t dev_idx) +{ + PJ_LOG(1,(THIS_FILE, "TAPI: OFFHOOK")); + + if (ioctl(dev_ctx->ch_fd[dev_idx], IFX_TAPI_LINE_FEED_SET, + IFX_TAPI_LINE_FEED_ACTIVE) != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed!")); + return PJ_EUNKNOWN; + } + + /* enc/dec stop */ + if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 1) != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - codec start failed!")); + return PJ_EUNKNOWN; + } + + return PJ_SUCCESS; +} + +static pj_status_t +tapi_dev_event_digit(tapi_ctx *dev_ctx, pj_uint32_t dev_idx) +{ + PJ_LOG(1,(THIS_FILE, "TAPI: OFFHOOK")); + + if (ioctl(dev_ctx->ch_fd[dev_idx], IFX_TAPI_LINE_FEED_SET, + IFX_TAPI_LINE_FEED_ACTIVE) != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed!")); + return PJ_EUNKNOWN; + } + + /* enc/dec stop */ + if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 1) != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - codec start failed!")); + return PJ_EUNKNOWN; + } + + return PJ_SUCCESS; +} + +static pj_status_t +tapi_dev_event_handler(tapi_aud_stream_t *stream) +{ + IFX_TAPI_EVENT_t tapiEvent; + tapi_ctx *dev_ctx = stream->dev_ctx; + pj_status_t status = PJ_SUCCESS; + unsigned int i; + + for (i = 0; i < TAPI_AUDIO_PORT_NUM; i++) { + memset (&tapiEvent, 0, sizeof(tapiEvent)); + tapiEvent.ch = dev_ctx->data2phone_map[i]; + status = ioctl(dev_ctx->dev_fd, IFX_TAPI_EVENT_GET, &tapiEvent); + + if ((status == PJ_SUCCESS) && (tapiEvent.id != IFX_TAPI_EVENT_NONE)) { + switch(tapiEvent.id) { + case IFX_TAPI_EVENT_FXS_ONHOOK: + status = tapi_dev_event_on_hook(dev_ctx, i); + if(tapi_hook_callback) + tapi_hook_callback(i, 0); + break; + case IFX_TAPI_EVENT_FXS_OFFHOOK: + status = tapi_dev_event_off_hook(dev_ctx, i); + if(tapi_hook_callback) + tapi_hook_callback(i, 1); + break; + case IFX_TAPI_EVENT_DTMF_DIGIT: + if(tapi_digit_callback) + tapi_digit_callback(i, tapiEvent.data.dtmf.ascii); + break; + case IFX_TAPI_EVENT_PULSE_DIGIT: + if(tapi_digit_callback) + if(tapiEvent.data.pulse.digit == 0xB) + tapi_digit_callback(i, '0'); + else + tapi_digit_callback(i, '0' + tapiEvent.data.pulse.digit); + break; + case IFX_TAPI_EVENT_COD_DEC_CHG: + case IFX_TAPI_EVENT_TONE_GEN_END: + case IFX_TAPI_EVENT_CID_TX_SEQ_END: + break; + default: + PJ_LOG(1,(THIS_FILE, "unknown tapi event %08X", tapiEvent.id)); + break; + } + } + } + + return status; +} + +static pj_status_t +tapi_dev_data_handler(tapi_aud_stream_t *stream) { + pj_status_t status = PJ_SUCCESS; + tapi_ctx *dev_ctx = stream->dev_ctx; + pj_uint32_t dev_idx = stream->param.rec_id; + pj_uint8_t buf_rec[TAPI_LL_DEV_ENC_BYTES_PER_FRAME + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE]={0}; + pj_uint8_t buf_play[TAPI_LL_DEV_ENC_BYTES_PER_FRAME + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE]={0}; + pjmedia_frame frame_rec, frame_play; + pj_int32_t ret; + + /* Get data from driver */ + ret = read(dev_ctx->ch_fd[dev_idx], buf_rec, sizeof(buf_rec)); + if (ret < 0) { + TRACE_((THIS_FILE, "ERROR - no data available from device!")); + return PJ_EUNKNOWN; + } + + if (ret > 0) { + frame_rec.type = PJMEDIA_FRAME_TYPE_AUDIO; + frame_rec.buf = buf_rec + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE; + frame_rec.size = ret - TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE; + frame_rec.timestamp.u64 = stream->timestamp.u64; + + status = stream->rec_cb(stream->user_data, &frame_rec); + if (status != PJ_SUCCESS) + PJ_LOG(1, (THIS_FILE, "rec_cb() failed %d", status)); + + frame_play.type = PJMEDIA_FRAME_TYPE_AUDIO; + frame_play.buf = buf_play + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE; + frame_play.size = TAPI_LL_DEV_ENC_BYTES_PER_FRAME; + frame_play.timestamp.u64 = stream->timestamp.u64; + + status = (*stream->play_cb)(stream->user_data, &frame_play); + if (status != PJ_SUCCESS) { + PJ_LOG(1, (THIS_FILE, "play_cb() failed %d", status)); + } else { + memcpy(buf_play, buf_rec, TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE); + + ret = write(dev_ctx->ch_fd[dev_idx], buf_play, sizeof(buf_play)); + + if (ret < 0) { + PJ_LOG(1, (THIS_FILE, "ERROR - device data writing failed!")); + return PJ_EUNKNOWN; + } + + if (ret == 0) { + PJ_LOG(1, (THIS_FILE, "ERROR - no data written to device!")); + return PJ_EUNKNOWN; + } + } + + stream->timestamp.u64 += TAPI_LL_DEV_ENC_SMPL_PER_FRAME; + } + + return PJ_SUCCESS; +} + +static int +PJ_THREAD_FUNC tapi_dev_thread(void *arg) { + tapi_ctx *dev_ctx = streams[0].dev_ctx; + pj_uint32_t sretval; + struct pollfd fds[3]; + + PJ_LOG(1,(THIS_FILE, "TAPI: thread starting...")); + + streams[0].run_flag = 1; + streams[1].run_flag = 1; + + fds[0].fd = dev_ctx->dev_fd; + fds[0].events = POLLIN; + fds[1].fd = dev_ctx->ch_fd[0]; + fds[1].events = POLLIN; + fds[2].fd = dev_ctx->ch_fd[1]; + fds[2].events = POLLIN; + + while(1) + { + sretval = poll(fds, TAPI_AUDIO_PORT_NUM + 1, TAPI_LL_DEV_SELECT_TIMEOUT_MS); + + if (!streams[0].run_flag && !streams[0].run_flag) + break; + if (sretval <= 0) + continue; + + if (fds[0].revents == POLLIN) { + if (tapi_dev_event_handler(&streams[0]) != PJ_SUCCESS) { + PJ_LOG(1, (THIS_FILE, "TAPI: event hanldler failed")); + break; + } + } + + if (fds[1].revents == POLLIN) { + if (tapi_dev_data_handler(&streams[0]) != PJ_SUCCESS) { + PJ_LOG(1, (THIS_FILE, "TAPI: data hanldler failed")); + break; + } + } + + if (fds[2].revents == POLLIN) { + if (tapi_dev_data_handler(&streams[1]) != PJ_SUCCESS) { + PJ_LOG(1, (THIS_FILE, "TAPI: data hanldler failed")); + break; + } + } + } + PJ_LOG(1, (THIS_FILE, "TAPI: thread stopping...")); + + return 0; +} + +/* Factory operations */ + +pjmedia_aud_dev_factory* +pjmedia_tapi_factory(pj_pool_factory *pf) { + struct tapi_aud_factory *f; + pj_pool_t *pool; + + TRACE_((THIS_FILE, "pjmedia_tapi_factory()")); + + pool = pj_pool_create(pf, "tapi", 512, 512, NULL); + f = PJ_POOL_ZALLOC_T(pool, struct tapi_aud_factory); + f->pf = pf; + f->pool = pool; + f->base.op = &tapi_fact_op; + + return &f->base; +} + +static pj_status_t +factory_init(pjmedia_aud_dev_factory *f) +{ + struct tapi_aud_factory *af = (struct tapi_aud_factory*)f; + pj_uint8_t i; + + TRACE_((THIS_FILE, "factory_init()")); + + af->dev_count = TAPI_AUDIO_PORT_NUM; + af->dev_info = (pjmedia_aud_dev_info*) + pj_pool_calloc(af->pool, af->dev_count, sizeof(pjmedia_aud_dev_info)); + for (i = 0; i < TAPI_AUDIO_PORT_NUM; i++) { + pj_ansi_sprintf(af->dev_info[i].name,"%s_%02d", TAPI_BASE_NAME, i); + af->dev_info[i].input_count = af->dev_info[i].output_count = 1; + af->dev_info[i].default_samples_per_sec = TAPI_LL_DEV_ENC_SMPL_PER_SEC; + pj_ansi_strcpy(af->dev_info[i].driver, "/dev/vmmc"); + af->dev_info[i].caps = PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING | + PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY | + PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY; + af->dev_info[i].routes = PJMEDIA_AUD_DEV_ROUTE_DEFAULT ; + } + if (tapi_dev_start(af) != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - TAPI device init failed!")); + return PJ_EUNKNOWN; + } + + return PJ_SUCCESS; +} + +static pj_status_t +factory_destroy(pjmedia_aud_dev_factory *f) +{ + struct tapi_aud_factory *af = (struct tapi_aud_factory*)f; + pj_pool_t *pool; + pj_status_t status = PJ_SUCCESS; + + TRACE_((THIS_FILE, "factory_destroy()")); + + if (tapi_dev_stop(af) != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - TAPI device stop failed!")); + status = PJ_EUNKNOWN; + } + pool = af->pool; + af->pool = NULL; + pj_pool_release(pool); + + return status; +} + +static unsigned +factory_get_dev_count(pjmedia_aud_dev_factory *f) +{ + struct tapi_aud_factory *af = (struct tapi_aud_factory*)f; + TRACE_((THIS_FILE, "factory_get_dev_count()")); + + return af->dev_count; +} + +static pj_status_t +factory_get_dev_info(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_dev_info *info) +{ + struct tapi_aud_factory *af = (struct tapi_aud_factory*)f; + + TRACE_((THIS_FILE, "factory_get_dev_info()")); + PJ_ASSERT_RETURN(index < af->dev_count, PJMEDIA_EAUD_INVDEV); + + pj_memcpy(info, &af->dev_info[index], sizeof(*info)); + + return PJ_SUCCESS; +} + +static pj_status_t +factory_default_param(pjmedia_aud_dev_factory *f, unsigned index, pjmedia_aud_param *param) +{ + struct tapi_aud_factory *af = (struct tapi_aud_factory*)f; + struct pjmedia_aud_dev_info *di = &af->dev_info[index]; + + TRACE_((THIS_FILE, "factory_default_param.")); + PJ_ASSERT_RETURN(index < af->dev_count, PJMEDIA_EAUD_INVDEV); + + pj_bzero(param, sizeof(*param)); + if (di->input_count && di->output_count) { + param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; + param->rec_id = index; + param->play_id = index; + } else if (di->input_count) { + param->dir = PJMEDIA_DIR_CAPTURE; + param->rec_id = index; + param->play_id = PJMEDIA_AUD_INVALID_DEV; + } else if (di->output_count) { + param->dir = PJMEDIA_DIR_PLAYBACK; + param->play_id = index; + param->rec_id = PJMEDIA_AUD_INVALID_DEV; + } else { + return PJMEDIA_EAUD_INVDEV; + } + + param->clock_rate = TAPI_LL_DEV_ENC_SMPL_PER_SEC; //di->default_samples_per_sec; + param->channel_count = 1; + param->samples_per_frame = TAPI_LL_DEV_ENC_SMPL_PER_FRAME; + param->bits_per_sample = TAPI_LL_DEV_ENC_BITS_PER_SMPLS; + param->flags = PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE | di->caps; + param->output_route = PJMEDIA_AUD_DEV_ROUTE_DEFAULT; + + return PJ_SUCCESS; +} + + +static pj_status_t +factory_create_stream(pjmedia_aud_dev_factory *f, const pjmedia_aud_param *param, + pjmedia_aud_rec_cb rec_cb, pjmedia_aud_play_cb play_cb, + void *user_data, pjmedia_aud_stream **p_aud_strm) +{ + struct tapi_aud_factory *af = (struct tapi_aud_factory*)f; + pj_pool_t *pool; + pj_status_t status; + int id = param->rec_id; + struct tapi_aud_stream *strm = &streams[param->rec_id]; + TRACE_((THIS_FILE, "factory_create_stream() rec_id:%d play_id:%d", param->rec_id, param->play_id)); + + /* Can only support 16bits per sample */ + PJ_ASSERT_RETURN(param->bits_per_sample == TAPI_LL_DEV_ENC_BITS_PER_SMPLS, PJ_EINVAL); + PJ_ASSERT_RETURN(param->clock_rate == TAPI_LL_DEV_ENC_SMPL_PER_SEC, PJ_EINVAL); + PJ_ASSERT_RETURN(param->samples_per_frame == TAPI_LL_DEV_ENC_SMPL_PER_FRAME, PJ_EINVAL); + + /* Can only support bidirectional stream */ + PJ_ASSERT_RETURN(param->dir & PJMEDIA_DIR_CAPTURE_PLAYBACK, PJ_EINVAL); + + if (id == 0) { + /* Initialize our stream data */ + pool = pj_pool_create(af->pf, "tapi-dev", 1000, 1000, NULL); + PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); + + strm->pool = pool; + } else { + pool = strm->pool = streams[0].pool; + } + + strm->rec_cb = rec_cb; + strm->play_cb = play_cb; + strm->user_data = user_data; + + pj_memcpy(&strm->param, param, sizeof(*param)); + + if ((strm->param.flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) == 0) { + strm->param.ext_fmt.id = PJMEDIA_FORMAT_L16; + } + + strm->timestamp.u64 = 0; + strm->dev_ctx = &(af->dev_ctx); + + /* Create and start the thread */ + if (id == 1) { + status = pj_thread_create(pool, "tapi", &tapi_dev_thread, strm, 0, 0, &streams[0].thread); + if (status != PJ_SUCCESS) { + stream_destroy(&strm->base); + return status; + } + } + + /* Done */ + strm->base.op = &tapi_strm_op; + *p_aud_strm = &strm->base; + + return PJ_SUCCESS; +} + +static pj_status_t +stream_get_param(pjmedia_aud_stream *s, pjmedia_aud_param *pi) +{ + struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s; + + PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); + pj_memcpy(pi, &strm->param, sizeof(*pi)); + + if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, + &pi->output_vol) == PJ_SUCCESS) + pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING; + + if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, + &pi->output_latency_ms) == PJ_SUCCESS) + pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; + + if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, + &pi->input_latency_ms) == PJ_SUCCESS) + pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY; + + return PJ_SUCCESS; +} + +static pj_status_t +stream_get_cap(pjmedia_aud_stream *s, pjmedia_aud_dev_cap cap, void *pval) +{ + // struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s; + return PJ_SUCCESS; +} + +static pj_status_t +stream_set_cap(pjmedia_aud_stream *s, pjmedia_aud_dev_cap cap, const void *pval) +{ + // struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s; + return PJ_SUCCESS; +} + +static pj_status_t +stream_start(pjmedia_aud_stream *s) +{ + struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s; + pj_uint32_t dev_idx; + + TRACE_((THIS_FILE, "stream_start()")); + + dev_idx = strm->param.rec_id; + + return PJ_SUCCESS; +} + +static pj_status_t +stream_stop(pjmedia_aud_stream *s) +{ + struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s; + tapi_ctx *dev_ctx = strm->dev_ctx; + pj_uint32_t dev_idx; + + TRACE_((THIS_FILE, "stream_stop()")); + dev_idx = strm->param.rec_id; + + if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 0) != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - codec start failed!")); + return PJ_EUNKNOWN; + } + + return PJ_SUCCESS; +} + +static pj_status_t +stream_destroy(pjmedia_aud_stream *s) +{ + pj_status_t state = PJ_SUCCESS; + struct tapi_aud_stream *stream = (struct tapi_aud_stream*)s; + pj_pool_t *pool; + + PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); + TRACE_((THIS_FILE, "stream_destroy()")); + + stream_stop(&stream->base); + stream->run_flag = 0; + + if (stream->thread) + { + pj_thread_join(stream->thread); + pj_thread_destroy(stream->thread); + stream->thread = NULL; + } + + pool = stream->pool; + pj_bzero(stream, sizeof(stream)); + pj_pool_release(pool); + + return state; +} + +pj_status_t +tapi_hook_status(pj_uint8_t port, pj_int32_t *status) +{ + PJ_ASSERT_RETURN(port < TAPI_AUDIO_PORT_NUM, PJ_EINVAL); + + if (ioctl(ch_fd[port], IFX_TAPI_LINE_HOOK_STATUS_GET, status) + != PJ_SUCCESS) { + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_HOOK_STATUS_GET ioctl failed!")); + return PJ_EUNKNOWN; + } + + return PJ_SUCCESS; +} + +pj_status_t +tapi_ring(pj_uint8_t port, pj_uint8_t state, char *caller_number) +{ + PJ_ASSERT_RETURN(port < TAPI_AUDIO_PORT_NUM, PJ_EINVAL); + + if (state) { + if (tapi_cid_type && caller_number) { + IFX_TAPI_CID_MSG_t cid_msg; + IFX_TAPI_CID_MSG_ELEMENT_t cid_msg_el[1]; + memset(&cid_msg, 0, sizeof(cid_msg)); + memset(&cid_msg_el, 0, sizeof(cid_msg_el)); + + cid_msg_el[0].string.elementType = IFX_TAPI_CID_ST_CLI; + cid_msg_el[0].string.len = strlen(caller_number); + strncpy(cid_msg_el[0].string.element, caller_number, sizeof(cid_msg_el[0].string.element)); + + cid_msg.txMode = IFX_TAPI_CID_HM_ONHOOK; + cid_msg.messageType = IFX_TAPI_CID_MT_CSUP; + cid_msg.nMsgElements = 1; + cid_msg.message = cid_msg_el; + ioctl(ch_fd[port], IFX_TAPI_CID_TX_SEQ_START, &cid_msg); + } else { + ioctl(ch_fd[port], IFX_TAPI_RING_START, 0); + } + } else { + ioctl(ch_fd[port], IFX_TAPI_RING_STOP, 0); + } + + return PJ_SUCCESS; +} + +pj_status_t +tapi_tone(pj_uint8_t port, pj_uint8_t code) +{ + PJ_ASSERT_RETURN(port < TAPI_AUDIO_PORT_NUM, PJ_EINVAL); + + if (tapi_locale && code) + ioctl(ch_fd[port], IFX_TAPI_TONE_LOCAL_PLAY, code); + else if (code) + ioctl(ch_fd[port], IFX_TAPI_TONE_LOCAL_PLAY, TAPI_TONE_LOCALE_NONE); + else + ioctl(ch_fd[port], IFX_TAPI_TONE_LOCAL_PLAY, 0); + + return PJ_SUCCESS; +} + +#endif /* PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE */