2 +++ b/pjmedia/src/pjmedia-audiodev/tapi_dev.c
4 +/******************************************************************************
7 + Lantiq Deutschland GmbH
8 + Am Campeon 3; 85579 Neubiberg, Germany
10 + For licensing information, see the file 'LICENSE' in the root folder of
11 + this software module.
13 +******************************************************************************/
14 +#include <pjmedia-audiodev/audiodev_imp.h>
15 +#include <pjmedia/errno.h>
16 +#include <pj/assert.h>
26 +#include <sys/stat.h>
28 +#include <sys/types.h>
29 +#include <sys/ioctl.h>
30 +#include <sys/select.h>
31 +#include <sys/time.h>
34 +#if PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE
37 +#include "drv_tapi_io.h"
40 +/* Maximum 2 devices*/
41 +#define TAPI_AUDIO_DEV_NUM (1)
42 +#define TAPI_AUDIO_MAX_DEV_NUM (2)
43 +#define TAPI_BASE_NAME "TAPI"
44 +#define TAPI_LL_DEV_BASE_PATH "/dev/vmmc"
45 +#define TAPI_LL_DEV_FIRMWARE_NAME "/lib/firmware/danube_firmware.bin"
47 +#define TAPI_LL_DEV_SELECT_TIMEOUT_MS (2000)
48 +#define TAPI_LL_DEV_MAX_PACKET_SIZE (800)
49 +#define TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE (12)
50 +#define TAPI_LL_DEV_ENC_FRAME_LEN_MS (20)
51 +#define TAPI_LL_DEV_ENC_SMPL_PER_SEC (8000)
52 +#define TAPI_LL_DEV_ENC_BITS_PER_SMPLS (16)
53 +#define TAPI_LL_DEV_ENC_SMPL_PER_FRAME (160)
54 +#define TAPI_LL_DEV_ENC_BYTES_PER_FRAME (TAPI_LL_DEV_ENC_SMPL_PER_FRAME * (TAPI_LL_DEV_ENC_BITS_PER_SMPLS / 8))
57 +#define FD_WIDTH_SET(fd, maxfd) (maxfd) < (fd) ? (fd) : maxfd
59 +#define THIS_FILE "tapi_dev.c"
62 +# define TRACE_(x) PJ_LOG(1,x)
70 + pj_int32_t ch_fd[TAPI_AUDIO_DEV_NUM];
71 + pj_int8_t data2phone_map[TAPI_AUDIO_DEV_NUM];
77 +struct tapi_aud_factory
79 + pjmedia_aud_dev_factory base;
81 + pj_pool_factory *pf;
83 + pj_uint32_t dev_count;
84 + pjmedia_aud_dev_info *dev_info;
89 +typedef struct tapi_aud_factory tapi_aud_factory_t;
92 + Sound stream descriptor.
94 +struct tapi_aud_stream
97 + pjmedia_aud_stream base; /**< Base class. */
99 + pj_pool_t *pool; /**< Memory pool. */
100 + /* Common settings.*/
101 + pjmedia_aud_param param; /**< Stream param. */
102 + pjmedia_aud_rec_cb rec_cb; /**< Record callback. */
103 + pjmedia_aud_play_cb play_cb; /**< Playback callback. */
104 + void *user_data; /**< Application data. */
106 + pj_thread_desc thread_desc;
107 + pj_thread_t *thread;
109 + pj_uint8_t run_flag;
110 + pj_timestamp timestamp;
113 +typedef struct tapi_aud_stream tapi_aud_stream_t;
115 +/* Factory prototypes */
116 +static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
117 +static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
118 +static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
119 +static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
121 + pjmedia_aud_dev_info *info);
122 +static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
124 + pjmedia_aud_param *param);
125 +static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
126 + const pjmedia_aud_param *param,
127 + pjmedia_aud_rec_cb rec_cb,
128 + pjmedia_aud_play_cb play_cb,
130 + pjmedia_aud_stream **p_aud_strm);
132 +/* Stream prototypes */
133 +static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
134 + pjmedia_aud_param *param);
135 +static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
136 + pjmedia_aud_dev_cap cap,
138 +static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
139 + pjmedia_aud_dev_cap cap,
140 + const void *value);
141 +static pj_status_t stream_start(pjmedia_aud_stream *strm);
142 +static pj_status_t stream_stop(pjmedia_aud_stream *strm);
143 +static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
145 +static pjmedia_aud_dev_factory_op tapi_fact_op =
149 + &factory_get_dev_count,
150 + &factory_get_dev_info,
151 + &factory_default_param,
152 + &factory_create_stream
155 +static pjmedia_aud_stream_op tapi_strm_op =
165 +static pj_int32_t tapi_dev_open(char* dev_path, const pj_int32_t ch_num)
167 + char devname[128] = {0};
169 + pj_ansi_sprintf(devname,"%s%u%u", dev_path, 1, ch_num);
171 + return open((const char*)devname, O_RDWR, 0644);
174 +static pj_status_t tapi_dev_binary_buffer_create(
176 + pj_uint8_t **ppBuf,
177 + pj_uint32_t *pBufSz)
179 + pj_status_t status = PJ_SUCCESS;
181 + struct stat file_stat;
183 + /* Open binary file for reading*/
184 + fd = fopen(pPath, "rb");
186 + TRACE_((THIS_FILE, "ERROR - binary file %s open failed!\n", pPath));
187 + return PJ_EUNKNOWN;
190 + /* Get file statistics*/
191 + if (stat(pPath, &file_stat) != 0) {
192 + TRACE_((THIS_FILE, "ERROR - file %s statistics get failed!\n", pPath));
193 + return PJ_EUNKNOWN;
196 + *ppBuf = malloc(file_stat.st_size);
197 + if (*ppBuf == NULL) {
198 + TRACE_((THIS_FILE, "ERROR - binary file %s memory allocation failed!\n", pPath));
199 + status = PJ_EUNKNOWN;
204 + if (fread (*ppBuf, sizeof(pj_uint8_t), file_stat.st_size, fd) <= 0) {
205 + TRACE_((THIS_FILE, "ERROR - file %s read failed!\n", pPath));
206 + status = PJ_EUNKNOWN;
211 + *pBufSz = file_stat.st_size;
218 + if (*ppBuf != NULL && status != PJ_SUCCESS) {
225 +static void tapi_dev_binary_buffer_delete(pj_uint8_t *pBuf)
231 +static pj_status_t tapi_dev_firmware_download(
235 + pj_status_t status = PJ_SUCCESS;
236 + pj_uint8_t *pFirmware = NULL;
237 + pj_uint32_t binSz = 0;
238 + VMMC_IO_INIT vmmc_io_init;
240 + /* Create binary buffer*/
241 + status = tapi_dev_binary_buffer_create(pPath, &pFirmware, &binSz);
242 + if (status != PJ_SUCCESS) {
243 + TRACE_((THIS_FILE, "ERROR - binary buffer create failed!\n"));
245 + return PJ_EUNKNOWN;
248 + /* Download Voice Firmware*/
249 + memset(&vmmc_io_init, 0, sizeof(VMMC_IO_INIT));
250 + vmmc_io_init.pPRAMfw = pFirmware;
251 + vmmc_io_init.pram_size = binSz;
253 + status = ioctl(fd, FIO_FW_DOWNLOAD, &vmmc_io_init);
254 + if (status != PJ_SUCCESS) {
255 + TRACE_((THIS_FILE, "ERROR - FIO_FW_DOWNLOAD ioctl failed!"));
258 + /* Delete binary buffer*/
259 + tapi_dev_binary_buffer_delete(pFirmware);
265 +static pj_status_t tapi_dev_start(tapi_aud_factory_t *f)
267 + pj_status_t status = PJ_SUCCESS;
269 + IFX_TAPI_DEV_START_CFG_t tapistart;
270 + IFX_TAPI_MAP_DATA_t datamap;
271 + IFX_TAPI_ENC_CFG_t enc_cfg;
272 + IFX_TAPI_LINE_VOLUME_t vol;
275 + f->dev_ctx.dev_fd = tapi_dev_open(TAPI_LL_DEV_BASE_PATH, 0);
277 + if (f->dev_ctx.dev_fd < 0) {
278 + TRACE_((THIS_FILE, "ERROR - TAPI device open failed!"));
279 + return PJ_EUNKNOWN;
282 + for (c = 0; c < TAPI_AUDIO_DEV_NUM; c++) {
283 + f->dev_ctx.ch_fd[c] = tapi_dev_open(TAPI_LL_DEV_BASE_PATH, TAPI_AUDIO_MAX_DEV_NUM - c);
285 + if (f->dev_ctx.dev_fd < 0) {
286 + TRACE_((THIS_FILE, "ERROR - TAPI channel%d open failed!", c));
287 + return PJ_EUNKNOWN;
290 + f->dev_ctx.data2phone_map[c] = c & 0x1 ? 0 : 1;
293 + status = tapi_dev_firmware_download(f->dev_ctx.dev_fd, TAPI_LL_DEV_FIRMWARE_NAME);
294 + if (status != PJ_SUCCESS) {
295 + TRACE_((THIS_FILE, "ERROR - Voice Firmware Download failed!"));
296 + return PJ_EUNKNOWN;
299 + memset(&tapistart, 0x0, sizeof(IFX_TAPI_DEV_START_CFG_t));
300 + tapistart.nMode = IFX_TAPI_INIT_MODE_VOICE_CODER;
303 + status = ioctl(f->dev_ctx.dev_fd, IFX_TAPI_DEV_START, &tapistart);
304 + if (status != PJ_SUCCESS) {
305 + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_DEV_START ioctl failed"));
306 + return PJ_EUNKNOWN;
310 + for (c = 0; c < TAPI_AUDIO_DEV_NUM; c++) {
311 + /* Perform mapping*/
312 + memset(&datamap, 0x0, sizeof(IFX_TAPI_MAP_DATA_t));
313 + datamap.nDstCh = f->dev_ctx.data2phone_map[c];
314 + datamap.nChType = IFX_TAPI_MAP_TYPE_PHONE;
316 + status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_MAP_DATA_ADD, &datamap);
318 + if (status != PJ_SUCCESS) {
319 + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_MAP_DATA_ADD ioctl failed"));
320 + return PJ_EUNKNOWN;
324 + status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_LINE_FEED_SET, IFX_TAPI_LINE_FEED_STANDBY);
326 + if (status != PJ_SUCCESS) {
327 + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed"));
328 + return PJ_EUNKNOWN;
331 + /* Config encoder for linear stream*/
332 + memset(&enc_cfg, 0x0, sizeof(IFX_TAPI_ENC_CFG_t));
334 + enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20;
335 + enc_cfg.nEncType = IFX_TAPI_COD_TYPE_LIN16_8;
337 + status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_ENC_CFG_SET, &enc_cfg);
338 + if (status != PJ_SUCCESS) {
339 + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_ENC_CFG_SET ioctl failed"));
340 + return PJ_EUNKNOWN;
344 + /* Suppress TAPI volume, otherwise PJSIP starts autogeneration!!!*/
348 + status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_PHONE_VOLUME_SET, &vol);
349 + if (status != PJ_SUCCESS) {
350 + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_PHONE_VOLUME_SET ioctl failed"));
351 + return PJ_EUNKNOWN;
359 +static pj_status_t tapi_dev_stop(tapi_aud_factory_t *f)
361 + pj_status_t status = PJ_SUCCESS;
364 + /* Stop TAPI device*/
365 + if (ioctl(f->dev_ctx.dev_fd, IFX_TAPI_DEV_STOP, 0) != PJ_SUCCESS) {
366 + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_DEV_STOP ioctl failed"));
367 + status = PJ_EUNKNOWN;
370 + /* Close device FD*/
371 + close(f->dev_ctx.dev_fd);
373 + /* Close channel FD*/
374 + for (c = TAPI_AUDIO_DEV_NUM; c > 0; c--) {
375 + close(f->dev_ctx.ch_fd[TAPI_AUDIO_DEV_NUM-c]);
382 +static pj_status_t tapi_dev_codec_control(pj_int32_t fd, pj_uint8_t start)
384 + if (ioctl(fd, start ? IFX_TAPI_ENC_START : IFX_TAPI_ENC_STOP, 0) != PJ_SUCCESS) {
385 + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_ENC_%s ioctl failed!",
386 + start ? "START" : "STOP"));
388 + return PJ_EUNKNOWN;
391 + if (ioctl(fd, start ? IFX_TAPI_DEC_START : IFX_TAPI_DEC_STOP, 0) != IFX_SUCCESS) {
392 + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_DEC_%s ioctl failed!",
393 + start ? "START" : "STOP"));
395 + return PJ_EUNKNOWN;
401 +static pj_status_t tapi_dev_event_ONHOOK(tapi_ctx *dev_ctx, pj_uint32_t dev_idx)
403 + PJ_LOG(1,(THIS_FILE, "TAPI: ONHOOK"));
405 + if (ioctl(dev_ctx->ch_fd[dev_idx], IFX_TAPI_LINE_FEED_SET,
406 + IFX_TAPI_LINE_FEED_STANDBY) != PJ_SUCCESS) {
407 + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed!"));
409 + return PJ_EUNKNOWN;
413 + if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 0) != PJ_SUCCESS) {
414 + TRACE_((THIS_FILE, "ERROR - codec start failed!"));
416 + return PJ_EUNKNOWN;
422 +static pj_status_t tapi_dev_event_OFFHOOK(tapi_ctx *dev_ctx, pj_uint32_t dev_idx)
424 + PJ_LOG(1,(THIS_FILE, "TAPI: OFFHOOK"));
426 + if (ioctl(dev_ctx->ch_fd[dev_idx], IFX_TAPI_LINE_FEED_SET,
427 + IFX_TAPI_LINE_FEED_ACTIVE) != PJ_SUCCESS) {
428 + TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed!"));
430 + return PJ_EUNKNOWN;
434 + if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 1) != PJ_SUCCESS) {
435 + TRACE_((THIS_FILE, "ERROR - codec start failed!"));
437 + return PJ_EUNKNOWN;
443 +static pj_status_t tapi_dev_event_handler(
444 + tapi_aud_stream_t *stream)
446 + tapi_ctx *dev_ctx = stream->dev_ctx;
447 + pj_uint32_t dev_idx = stream->param.rec_id;
448 + pj_status_t status = PJ_SUCCESS;
449 + IFX_TAPI_EVENT_t tapiEvent;
451 + memset (&tapiEvent, 0, sizeof(tapiEvent));
453 + tapiEvent.ch = dev_ctx->data2phone_map[dev_idx];
456 + status = ioctl(dev_ctx->dev_fd, IFX_TAPI_EVENT_GET, &tapiEvent);
458 + if ((status == PJ_SUCCESS) && (tapiEvent.id != IFX_TAPI_EVENT_NONE)) {
459 + switch(tapiEvent.id) {
460 + case IFX_TAPI_EVENT_FXS_ONHOOK:
461 + status = tapi_dev_event_ONHOOK(dev_ctx, dev_idx);
463 + case IFX_TAPI_EVENT_FXS_OFFHOOK:
464 + status = tapi_dev_event_OFFHOOK(dev_ctx, dev_idx);
474 +static pj_status_t tapi_dev_data_handler(
475 + tapi_aud_stream_t *stream)
477 + pj_status_t status = PJ_SUCCESS;
478 + tapi_ctx *dev_ctx = stream->dev_ctx;
479 + pj_uint32_t dev_idx = stream->param.rec_id;
480 + pj_uint8_t buf_rec[TAPI_LL_DEV_ENC_BYTES_PER_FRAME + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE]={0};
481 + pj_uint8_t buf_play[TAPI_LL_DEV_ENC_BYTES_PER_FRAME + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE]={0};
482 + pjmedia_frame frame_rec, frame_play;
485 + /* Get data from driver*/
486 + ret = read(dev_ctx->ch_fd[dev_idx], buf_rec, sizeof(buf_rec));
488 + TRACE_((THIS_FILE, "ERROR - no data available from device!"));
490 + return PJ_EUNKNOWN;
494 + frame_rec.type = PJMEDIA_FRAME_TYPE_AUDIO;
495 + frame_rec.buf = buf_rec + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE;
496 + frame_rec.size = ret - TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE;
497 + frame_rec.timestamp.u64 = stream->timestamp.u64;
499 + status = stream->rec_cb(stream->user_data, &frame_rec);
500 + if (status != PJ_SUCCESS)
502 + PJ_LOG(1, (THIS_FILE, "rec_cb() failed %d", status));
505 + frame_play.type = PJMEDIA_FRAME_TYPE_AUDIO;
506 + frame_play.buf = buf_play + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE;
507 + frame_play.size = TAPI_LL_DEV_ENC_BYTES_PER_FRAME;
508 + frame_play.timestamp.u64 = stream->timestamp.u64;
510 + status = (*stream->play_cb)(stream->user_data, &frame_play);
511 + if (status != PJ_SUCCESS)
513 + PJ_LOG(1, (THIS_FILE, "play_cb() failed %d", status));
517 + memcpy(buf_play, buf_rec, TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE);
519 + ret = write(dev_ctx->ch_fd[dev_idx], buf_play, sizeof(buf_play));
522 + PJ_LOG(1, (THIS_FILE, "ERROR - device data writing failed!"));
523 + return PJ_EUNKNOWN;
527 + PJ_LOG(1, (THIS_FILE, "ERROR - no data written to device!"));
528 + return PJ_EUNKNOWN;
532 + stream->timestamp.u64 += TAPI_LL_DEV_ENC_SMPL_PER_FRAME;
538 +/* TAPI capture and playback thread. */
539 +static int PJ_THREAD_FUNC tapi_dev_thread(void *arg)
541 + tapi_aud_stream_t *strm = (struct tapi_aud_stream*)arg;
542 + tapi_ctx *dev_ctx = strm->dev_ctx;
543 + fd_set rfds, trfds;
544 + pj_uint32_t width = 0;
546 + pj_uint32_t sretval;
547 + pj_uint32_t dev_idx;
549 + PJ_LOG(1,(THIS_FILE, "TAPI: thread starting..."));
551 + if (strm->param.rec_id != strm->param.play_id) {
552 + PJ_LOG(1,(THIS_FILE, "TAPI: thread exit - incorrect play/rec IDs"));
556 + dev_idx = strm->param.rec_id;
560 + FD_SET(dev_ctx->dev_fd, &rfds);
561 + width = FD_WIDTH_SET(dev_ctx->dev_fd, width);
563 + FD_SET(dev_ctx->ch_fd[dev_idx], &rfds);
564 + width = FD_WIDTH_SET(dev_ctx->ch_fd[dev_idx], width);
566 + tv.tv_sec = TAPI_LL_DEV_SELECT_TIMEOUT_MS / 1000;
567 + tv.tv_usec = (TAPI_LL_DEV_SELECT_TIMEOUT_MS % 1000) * 1000;
569 + strm->run_flag = 1;
573 + /* Update the local file descriptor by the copy in the task parameter */
574 + memcpy((void *) &trfds, (void*) &rfds, sizeof(fd_set));
576 + sretval = select(width + 1, &trfds, NULL, NULL, &tv);
578 + if (!strm->run_flag) {
582 + /* error or timeout on select */
583 + if (sretval <= 0) {
587 + /* Check device control channel*/
588 + if (FD_ISSET(dev_ctx->dev_fd, &trfds)) {
589 + if (tapi_dev_event_handler(strm) != PJ_SUCCESS) {
590 + PJ_LOG(1,(THIS_FILE, "TAPI: event hanldler failed!"));
595 + /* Check device data channel*/
596 + if (FD_ISSET(dev_ctx->ch_fd[dev_idx], &trfds)) {
597 + if (tapi_dev_data_handler(strm) != PJ_SUCCESS) {
598 + PJ_LOG(1,(THIS_FILE, "TAPI: data hanldler failed!"));
604 + PJ_LOG(1,(THIS_FILE, "TAPI: thread stopping..."));
609 +/****************************************************************************
611 + ****************************************************************************/
613 +/* Init tapi audio driver. */
614 +pjmedia_aud_dev_factory* pjmedia_tapi_factory(pj_pool_factory *pf)
616 + struct tapi_aud_factory *f;
619 + TRACE_((THIS_FILE, "pjmedia_tapi_factory()"));
621 + pool = pj_pool_create(pf, "tapi", 512, 512, NULL);
622 + f = PJ_POOL_ZALLOC_T(pool, struct tapi_aud_factory);
625 + f->base.op = &tapi_fact_op;
630 +/* API: init factory */
631 +static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
633 + struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
636 + TRACE_((THIS_FILE, "factory_init()"));
638 + /* Enumerate sound devices */
639 + af->dev_count = TAPI_AUDIO_DEV_NUM;
641 + af->dev_info = (pjmedia_aud_dev_info*)
642 + pj_pool_calloc(af->pool, af->dev_count, sizeof(pjmedia_aud_dev_info));
644 + for (c = 0; c < af->dev_count; c++) {
645 + pj_ansi_sprintf(af->dev_info[c].name,"%s_%02d", TAPI_BASE_NAME, c);
647 + af->dev_info[c].input_count = af->dev_info[c].output_count = 1;
648 + af->dev_info[c].default_samples_per_sec = TAPI_LL_DEV_ENC_SMPL_PER_SEC;
649 + pj_ansi_strcpy(af->dev_info[c].driver, "/dev/vmmc");
651 + af->dev_info[c].caps = PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |
652 + PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY |
653 + PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
655 + af->dev_info[c].routes = PJMEDIA_AUD_DEV_ROUTE_DEFAULT ;
658 + /* Initialize TAPI device(s)*/
659 + if (tapi_dev_start(af) != PJ_SUCCESS) {
660 + TRACE_((THIS_FILE, "ERROR - TAPI device init failed!"));
661 + return PJ_EUNKNOWN;
667 +/* API: destroy factory */
668 +static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
670 + struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
672 + pj_status_t status = PJ_SUCCESS;
674 + TRACE_((THIS_FILE, "factory_destroy()"));
676 + /* Stop TAPI device*/
677 + if (tapi_dev_stop(f) != PJ_SUCCESS) {
678 + TRACE_((THIS_FILE, "ERROR - TAPI device stop failed!"));
679 + status = PJ_EUNKNOWN;
684 + pj_pool_release(pool);
689 +/* API: get number of devices */
690 +static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
692 + struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
693 + TRACE_((THIS_FILE, "factory_get_dev_count()"));
695 + return af->dev_count;
698 +/* API: get device info */
699 +static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
701 + pjmedia_aud_dev_info *info)
703 + struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
705 + TRACE_((THIS_FILE, "factory_get_dev_info()"));
706 + PJ_ASSERT_RETURN(index < af->dev_count, PJMEDIA_EAUD_INVDEV);
708 + pj_memcpy(info, &af->dev_info[index], sizeof(*info));
713 +/* API: create default device parameter */
714 +static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
716 + pjmedia_aud_param *param)
718 + struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
719 + struct pjmedia_aud_dev_info *di = &af->dev_info[index];
721 + TRACE_((THIS_FILE, "factory_default_param."));
722 + PJ_ASSERT_RETURN(index < af->dev_count, PJMEDIA_EAUD_INVDEV);
724 + pj_bzero(param, sizeof(*param));
725 + if (di->input_count && di->output_count) {
726 + param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
727 + param->rec_id = index;
728 + param->play_id = index;
729 + } else if (di->input_count) {
730 + param->dir = PJMEDIA_DIR_CAPTURE;
731 + param->rec_id = index;
732 + param->play_id = PJMEDIA_AUD_INVALID_DEV;
733 + } else if (di->output_count) {
734 + param->dir = PJMEDIA_DIR_PLAYBACK;
735 + param->play_id = index;
736 + param->rec_id = PJMEDIA_AUD_INVALID_DEV;
738 + return PJMEDIA_EAUD_INVDEV;
741 + param->clock_rate = di->default_samples_per_sec;
742 + param->channel_count = 1;
743 + param->samples_per_frame = TAPI_LL_DEV_ENC_SMPL_PER_FRAME;
744 + param->bits_per_sample = TAPI_LL_DEV_ENC_BITS_PER_SMPLS;
745 + param->flags = PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE | di->caps;
746 + param->output_route = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
751 +/* API: create stream */
752 +static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
753 + const pjmedia_aud_param *param,
754 + pjmedia_aud_rec_cb rec_cb,
755 + pjmedia_aud_play_cb play_cb,
757 + pjmedia_aud_stream **p_aud_strm)
759 + struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
761 + struct tapi_aud_stream *strm;
762 + pj_status_t status;
764 + TRACE_((THIS_FILE, "factory_create_stream()"));
766 + /* Can only support 16bits per sample */
767 + PJ_ASSERT_RETURN(param->bits_per_sample == TAPI_LL_DEV_ENC_BITS_PER_SMPLS, PJ_EINVAL);
769 + PJ_ASSERT_RETURN(param->clock_rate == TAPI_LL_DEV_ENC_SMPL_PER_SEC, PJ_EINVAL);
771 + PJ_ASSERT_RETURN(param->samples_per_frame == TAPI_LL_DEV_ENC_SMPL_PER_FRAME, PJ_EINVAL);
773 + /* Can only support bidirectional stream */
774 + PJ_ASSERT_RETURN(param->dir & PJMEDIA_DIR_CAPTURE_PLAYBACK, PJ_EINVAL);
776 + /* Initialize our stream data */
777 + pool = pj_pool_create(af->pf, "tapi-dev", 1000, 1000, NULL);
778 + PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
780 + strm = PJ_POOL_ZALLOC_T(pool, struct tapi_aud_stream);
782 + strm->rec_cb = rec_cb;
783 + strm->play_cb = play_cb;
784 + strm->user_data = user_data;
785 + pj_memcpy(&strm->param, param, sizeof(*param));
787 + if ((strm->param.flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) == 0) {
788 + strm->param.ext_fmt.id = PJMEDIA_FORMAT_L16;
791 + strm->timestamp.u64 = 0;
792 + strm->dev_ctx = &(af->dev_ctx);
794 + /* Create and start the thread */
795 + status = pj_thread_create(pool, "tapi", &tapi_dev_thread, strm, 0, 0,
797 + if (status != PJ_SUCCESS) {
798 + stream_destroy(&strm->base);
803 + strm->base.op = &tapi_strm_op;
804 + *p_aud_strm = &strm->base;
809 +/****************************************************************************
810 + * Stream operations
812 +/* API: Get stream info. */
813 +static pj_status_t stream_get_param(pjmedia_aud_stream *s,
814 + pjmedia_aud_param *pi)
816 + struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s;
818 + PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
820 + pj_memcpy(pi, &strm->param, sizeof(*pi));
821 + /* Update the volume setting */
822 + if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
823 + &pi->output_vol) == PJ_SUCCESS)
825 + pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
828 + if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
829 + &pi->output_latency_ms) == PJ_SUCCESS)
831 + pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
834 + if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
835 + &pi->input_latency_ms) == PJ_SUCCESS)
837 + pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
843 +/* API: get capability */
844 +static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
845 + pjmedia_aud_dev_cap cap,
848 + struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s;
850 + OSStatus status = 0;
851 + PJ_ASSERT_RETURN(strm && pval, PJ_EINVAL);
853 + if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING && strm->play_strm->queue)
856 + status = AudioQueueGetParameter(strm->play_strm->queue,
857 + kAudioQueueParam_Volume, &vol);
860 + *(unsigned*)pval = (vol * 100);
864 + else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && strm->play_strm->queue)
867 + UInt32 size = sizeof(lat);
868 + status = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency,
872 + *(unsigned*)pval = lat * 1000;
876 + else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE && strm->play_strm->queue)
878 + *(pjmedia_aud_dev_route*)pval = strm->param.output_route;
881 + else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && strm->rec_strm->queue)
884 + UInt32 size = sizeof(lat);
885 + status = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputLatency,
889 + *(unsigned*)pval = lat * 1000;
895 + PJ_LOG(1, (THIS_FILE, "AudioQueueGetParameter/AudioSessionGetProperty err %d", status));
896 + return PJMEDIA_EAUD_INVCAP;
902 +/* API: set capability */
903 +static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
904 + pjmedia_aud_dev_cap cap,
907 + struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s;
909 + OSStatus status = 0;
910 + PJ_ASSERT_RETURN(strm && pval, PJ_EINVAL);
912 + if (strm->play_strm->queue)
915 + case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
917 + /* Output volume setting */
918 + unsigned vol = *(unsigned*)pval;
923 + volume = vol / 100.;
924 + status = AudioQueueSetParameter(strm->play_strm->queue, kAudioQueueParam_Volume,
928 + PJ_LOG(1, (THIS_FILE, "AudioQueueSetParameter err %d", status));
929 + return PJMEDIA_EAUD_SYSERR;
931 + strm->param.output_vol = *(unsigned*)pval;
934 + case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
936 + pjmedia_aud_dev_route r = *(const pjmedia_aud_dev_route*)pval;
937 + UInt32 route = (r == PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER ?
938 + kAudioSessionOverrideAudioRoute_Speaker :
939 + kAudioSessionOverrideAudioRoute_None);
941 + status = AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,
942 + sizeof(route), &route);
945 + PJ_LOG(1, (THIS_FILE, "AudioSessionSetProperty err %d", status));
946 + return PJMEDIA_EAUD_SYSERR;
948 + strm->param.output_route = r;
952 + return PJMEDIA_EAUD_INVCAP;
956 + return PJMEDIA_EAUD_INVCAP;
962 +/* API: Start stream. */
963 +static pj_status_t stream_start(pjmedia_aud_stream *s)
965 + struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s;
966 + tapi_ctx *dev_ctx = strm->dev_ctx;
967 + pj_uint32_t dev_idx;
969 + TRACE_((THIS_FILE, "stream_start()"));
971 + dev_idx = strm->param.rec_id;
976 +/* API: Stop stream. */
977 +static pj_status_t stream_stop(pjmedia_aud_stream *s)
979 + struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s;
980 + tapi_ctx *dev_ctx = strm->dev_ctx;
981 + pj_uint32_t dev_idx;
983 + TRACE_((THIS_FILE, "stream_stop()"));
985 + dev_idx = strm->param.rec_id;
988 + if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 0) != PJ_SUCCESS) {
989 + TRACE_((THIS_FILE, "ERROR - codec start failed!"));
991 + return PJ_EUNKNOWN;
997 +/* API: Destroy stream. */
998 +static pj_status_t stream_destroy(pjmedia_aud_stream *s)
1000 + pj_status_t state = PJ_SUCCESS;
1001 + struct tapi_aud_stream *stream = (struct tapi_aud_stream*)s;
1004 + PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1006 + TRACE_((THIS_FILE, "stream_destroy()"));
1008 + stream_stop(stream);
1010 + stream->run_flag = 0;
1012 + /* Stop the stream thread */
1013 + if (stream->thread)
1015 + pj_thread_join(stream->thread);
1016 + pj_thread_destroy(stream->thread);
1017 + stream->thread = NULL;
1020 + pool = stream->pool;
1021 + pj_bzero(stream, sizeof(stream));
1022 + pj_pool_release(pool);
1027 +#endif /* PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE */