[pjsip]
[15.05/openwrt.git] / package / pjsip / patches / 200-pjmedia_tapi_audiodev.patch
1 --- /dev/null
2 +++ b/pjmedia/src/pjmedia-audiodev/tapi_dev.c
3 @@ -0,0 +1,1024 @@
4 +/******************************************************************************
5 +
6 +                               Copyright (c) 2010
7 +                            Lantiq Deutschland GmbH
8 +                     Am Campeon 3; 85579 Neubiberg, Germany
9 +
10 +  For licensing information, see the file 'LICENSE' in the root folder of
11 +  this software module.
12 +
13 +******************************************************************************/
14 +#include <pjmedia-audiodev/audiodev_imp.h>
15 +#include <pjmedia/errno.h>
16 +#include <pj/assert.h>
17 +#include <pj/pool.h>
18 +#include <pj/log.h>
19 +#include <pj/os.h>
20 +
21 +/* Linux includes*/
22 +#include <stdio.h>
23 +#include <string.h>
24 +#include <stdlib.h>
25 +#include <ctype.h>
26 +#include <sys/stat.h> 
27 +#include <fcntl.h>
28 +#include <sys/types.h>
29 +#include <sys/ioctl.h>
30 +#include <sys/select.h>
31 +#include <sys/time.h>
32 +#include <unistd.h>
33 +
34 +#if PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE
35 +
36 +/* TAPI includes*/
37 +#include "drv_tapi_io.h"
38 +#include "vmmc_io.h"
39 +
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" 
46 +
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))
55 +
56 +
57 +#define FD_WIDTH_SET(fd, maxfd)   (maxfd) < (fd) ? (fd) : maxfd 
58 +
59 +#define THIS_FILE     "tapi_dev.c"
60 +
61 +#if 1
62 +#   define TRACE_(x)    PJ_LOG(1,x)
63 +#else
64 +#   define TRACE_(x)
65 +#endif
66 +
67 +typedef struct
68 +{
69 +   pj_int32_t dev_fd;
70 +   pj_int32_t ch_fd[TAPI_AUDIO_DEV_NUM];
71 +   pj_int8_t  data2phone_map[TAPI_AUDIO_DEV_NUM];
72 +   
73 +} tapi_ctx;
74 +
75 +
76 +/* TAPI factory */
77 +struct tapi_aud_factory
78 +{
79 +   pjmedia_aud_dev_factory  base;
80 +   pj_pool_t         *pool;
81 +   pj_pool_factory   *pf;
82 +
83 +   pj_uint32_t             dev_count;
84 +   pjmedia_aud_dev_info   *dev_info;
85 +
86 +   tapi_ctx   dev_ctx;
87 +};
88 +
89 +typedef struct tapi_aud_factory tapi_aud_factory_t;
90 +
91 +/*
92 +   Sound stream descriptor.
93 +**/
94 +struct tapi_aud_stream
95 +{
96 +   /* Base*/
97 +   pjmedia_aud_stream   base;      /**< Base class.  */
98 +   /* Pool*/
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.  */
105 +
106 +   pj_thread_desc       thread_desc;
107 +   pj_thread_t         *thread;
108 +   tapi_ctx            *dev_ctx;
109 +   pj_uint8_t           run_flag;
110 +   pj_timestamp         timestamp;
111 +};
112 +
113 +typedef struct tapi_aud_stream tapi_aud_stream_t;
114 +
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,
120 +          unsigned index,
121 +          pjmedia_aud_dev_info *info);
122 +static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
123 +           unsigned index,
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,
129 +           void *user_data,
130 +           pjmedia_aud_stream **p_aud_strm);
131 +
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,
137 +              void *value);
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);
144 +
145 +static pjmedia_aud_dev_factory_op tapi_fact_op =
146 +  {
147 +      &factory_init,
148 +      &factory_destroy,
149 +      &factory_get_dev_count,
150 +      &factory_get_dev_info,
151 +      &factory_default_param,
152 +      &factory_create_stream
153 +  };
154 +
155 +static pjmedia_aud_stream_op tapi_strm_op =
156 +{
157 +    &stream_get_param,
158 +    &stream_get_cap,
159 +    &stream_set_cap,
160 +    &stream_start,
161 +    &stream_stop,
162 +    &stream_destroy
163 +};
164 +
165 +static pj_int32_t tapi_dev_open(char* dev_path, const pj_int32_t ch_num)
166 +{
167 +   char devname[128] = {0};
168 +
169 +   pj_ansi_sprintf(devname,"%s%u%u", dev_path, 1, ch_num);
170 +
171 +   return open((const char*)devname, O_RDWR, 0644);
172 +}
173 +
174 +static pj_status_t tapi_dev_binary_buffer_create(
175 +                     const char *pPath,
176 +                     pj_uint8_t **ppBuf,
177 +                     pj_uint32_t *pBufSz)
178 +{
179 +   pj_status_t status = PJ_SUCCESS;
180 +   FILE *fd;
181 +   struct stat file_stat;
182 +
183 +   /* Open binary file for reading*/
184 +   fd = fopen(pPath, "rb");
185 +   if (fd == NULL) {
186 +      TRACE_((THIS_FILE, "ERROR -  binary file %s open failed!\n", pPath));
187 +      return PJ_EUNKNOWN;
188 +   }
189 +
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;
194 +   }
195 +
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;
200 +
201 +      goto on_exit;
202 +   }
203 +
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;
207 +
208 +      goto on_exit;
209 +   }
210 +
211 +   *pBufSz = file_stat.st_size; 
212 +
213 +on_exit:
214 +   if (fd != NULL) {
215 +      fclose(fd);
216 +   }
217 +
218 +   if (*ppBuf != NULL && status != PJ_SUCCESS) {
219 +      free(*ppBuf);
220 +   }
221 +
222 +   return status;
223 +}
224 +
225 +static void tapi_dev_binary_buffer_delete(pj_uint8_t *pBuf)
226 +{
227 +   if (pBuf != NULL)
228 +      free(pBuf);
229 +}
230 +
231 +static pj_status_t tapi_dev_firmware_download(
232 +                     pj_int32_t fd,
233 +                     const char *pPath)
234 +{
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;
239 +
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"));
244 +
245 +      return PJ_EUNKNOWN;
246 +   }
247 +
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;
252 +
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!"));
256 +   }
257 +
258 +   /* Delete binary buffer*/
259 +   tapi_dev_binary_buffer_delete(pFirmware);
260 +
261 +   return status;
262 +}
263 +
264 +
265 +static pj_status_t tapi_dev_start(tapi_aud_factory_t *f)
266 +{
267 +   pj_status_t status = PJ_SUCCESS;
268 +   pj_uint8_t c;
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;
273 +
274 +   /* Open device*/
275 +   f->dev_ctx.dev_fd = tapi_dev_open(TAPI_LL_DEV_BASE_PATH, 0);
276 +
277 +   if (f->dev_ctx.dev_fd < 0) {
278 +      TRACE_((THIS_FILE, "ERROR - TAPI device open failed!"));
279 +      return PJ_EUNKNOWN;
280 +   }
281 +
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);
284 +
285 +      if (f->dev_ctx.dev_fd < 0) {
286 +         TRACE_((THIS_FILE, "ERROR - TAPI channel%d open failed!", c));
287 +         return PJ_EUNKNOWN;
288 +      }
289 +      
290 +      f->dev_ctx.data2phone_map[c] = c & 0x1 ? 0 : 1;
291 +   }
292 +
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;
297 +   }
298 +
299 +   memset(&tapistart, 0x0, sizeof(IFX_TAPI_DEV_START_CFG_t));
300 +   tapistart.nMode = IFX_TAPI_INIT_MODE_VOICE_CODER;
301 +
302 +   /* Start TAPI*/
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;
307 +   }
308 +
309 +
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;
315 +
316 +      status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_MAP_DATA_ADD, &datamap);
317 +
318 +      if (status != PJ_SUCCESS) {
319 +         TRACE_((THIS_FILE, "ERROR - IFX_TAPI_MAP_DATA_ADD ioctl failed"));
320 +         return PJ_EUNKNOWN;
321 +      }
322 +
323 +      /* Set Line feed*/
324 +      status = ioctl(f->dev_ctx.ch_fd[c], IFX_TAPI_LINE_FEED_SET, IFX_TAPI_LINE_FEED_STANDBY);
325 +
326 +      if (status != PJ_SUCCESS) {
327 +         TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed"));
328 +         return PJ_EUNKNOWN;
329 +      }
330 +
331 +      /* Config encoder for linear stream*/
332 +      memset(&enc_cfg, 0x0, sizeof(IFX_TAPI_ENC_CFG_t));
333 +
334 +      enc_cfg.nFrameLen = IFX_TAPI_COD_LENGTH_20;
335 +      enc_cfg.nEncType  = IFX_TAPI_COD_TYPE_LIN16_8;
336 +
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;
341 +      }
342 +
343 +
344 +      /* Suppress TAPI volume, otherwise PJSIP starts autogeneration!!!*/
345 +      vol.nGainRx = -8;
346 +      vol.nGainTx = -8;
347 +
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;
352 +      }
353 +   }
354 +
355 +
356 +   return status;
357 +}
358 +
359 +static pj_status_t tapi_dev_stop(tapi_aud_factory_t *f)
360 +{
361 +   pj_status_t status = PJ_SUCCESS;
362 +   pj_uint8_t c;
363 +   
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;
368 +   }
369 +
370 +   /* Close device FD*/
371 +   close(f->dev_ctx.dev_fd);
372 +
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]);
376 +   }
377 +
378 +
379 +   return status;
380 +}
381 +
382 +static pj_status_t tapi_dev_codec_control(pj_int32_t fd, pj_uint8_t start)
383 +{
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"));
387 +
388 +      return PJ_EUNKNOWN;
389 +   }
390 +
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"));
394 +
395 +      return PJ_EUNKNOWN;
396 +   }
397 +
398 +   return PJ_SUCCESS;
399 +}
400 +
401 +static pj_status_t tapi_dev_event_ONHOOK(tapi_ctx *dev_ctx, pj_uint32_t dev_idx)
402 +{
403 +   PJ_LOG(1,(THIS_FILE, "TAPI: ONHOOK"));
404 +
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!"));
408 +
409 +      return PJ_EUNKNOWN;
410 +   }
411 +
412 +   /* enc/dec stop*/
413 +   if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 0) != PJ_SUCCESS) {
414 +      TRACE_((THIS_FILE, "ERROR - codec start failed!"));
415 +
416 +      return PJ_EUNKNOWN;
417 +   }
418 +
419 +   return PJ_SUCCESS;
420 +}
421 +
422 +static pj_status_t tapi_dev_event_OFFHOOK(tapi_ctx *dev_ctx, pj_uint32_t dev_idx)
423 +{
424 +   PJ_LOG(1,(THIS_FILE, "TAPI: OFFHOOK"));
425 +
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!"));
429 +
430 +      return PJ_EUNKNOWN;
431 +   }
432 +
433 +   /* enc/dec stop*/
434 +   if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 1) != PJ_SUCCESS) {
435 +      TRACE_((THIS_FILE, "ERROR - codec start failed!"));
436 +
437 +      return PJ_EUNKNOWN;
438 +   }
439 +
440 +   return PJ_SUCCESS;
441 +}
442 +
443 +static pj_status_t tapi_dev_event_handler(
444 +                     tapi_aud_stream_t *stream)
445 +{
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;
450 +
451 +   memset (&tapiEvent, 0, sizeof(tapiEvent));
452 +
453 +   tapiEvent.ch = dev_ctx->data2phone_map[dev_idx];
454 +
455 +   /* Get event*/
456 +   status = ioctl(dev_ctx->dev_fd, IFX_TAPI_EVENT_GET, &tapiEvent);
457 +
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);
462 +            break;
463 +         case IFX_TAPI_EVENT_FXS_OFFHOOK:
464 +            status = tapi_dev_event_OFFHOOK(dev_ctx, dev_idx);
465 +            break;
466 +         default:
467 +            break;
468 +      }
469 +   }
470 +
471 +   return status;
472 +}
473 +
474 +static pj_status_t tapi_dev_data_handler(
475 +                     tapi_aud_stream_t *stream)
476 +{
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;
483 +   pj_int32_t ret;
484 +
485 +   /* Get data from driver*/
486 +   ret = read(dev_ctx->ch_fd[dev_idx], buf_rec, sizeof(buf_rec));
487 +   if (ret < 0) {
488 +      TRACE_((THIS_FILE, "ERROR - no data available from device!"));
489 +
490 +      return PJ_EUNKNOWN;
491 +   }
492 +
493 +   if (ret > 0) {
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;
498 +
499 +      status = stream->rec_cb(stream->user_data, &frame_rec);
500 +      if (status != PJ_SUCCESS)
501 +      {
502 +        PJ_LOG(1, (THIS_FILE, "rec_cb() failed %d", status));
503 +      }
504 +
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;
509 +
510 +      status = (*stream->play_cb)(stream->user_data, &frame_play);
511 +      if (status != PJ_SUCCESS)
512 +      {
513 +         PJ_LOG(1, (THIS_FILE, "play_cb() failed %d", status));
514 +      }
515 +      else
516 +      {
517 +         memcpy(buf_play, buf_rec, TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE);
518 +
519 +         ret = write(dev_ctx->ch_fd[dev_idx], buf_play, sizeof(buf_play));
520 +
521 +         if (ret < 0) {
522 +            PJ_LOG(1, (THIS_FILE, "ERROR - device data writing failed!"));
523 +            return PJ_EUNKNOWN;
524 +         }
525 +
526 +         if (ret == 0) {
527 +            PJ_LOG(1, (THIS_FILE, "ERROR - no data written to device!"));
528 +            return PJ_EUNKNOWN;
529 +         }
530 +      }
531 +
532 +      stream->timestamp.u64 += TAPI_LL_DEV_ENC_SMPL_PER_FRAME;
533 +   }
534 +
535 +   return PJ_SUCCESS;
536 +}
537 +
538 +/* TAPI capture and playback thread. */
539 +static int PJ_THREAD_FUNC tapi_dev_thread(void *arg)
540 +{
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;
545 +   struct timeval tv;
546 +   pj_uint32_t sretval;
547 +   pj_uint32_t dev_idx;
548 +
549 +   PJ_LOG(1,(THIS_FILE, "TAPI: thread starting..."));
550 +
551 +   if (strm->param.rec_id != strm->param.play_id) {
552 +      PJ_LOG(1,(THIS_FILE, "TAPI: thread exit - incorrect play/rec IDs"));
553 +      return 0;
554 +   }
555 +
556 +   dev_idx = strm->param.rec_id;
557 +
558 +   FD_ZERO(&rfds);
559 +
560 +   FD_SET(dev_ctx->dev_fd, &rfds);
561 +   width = FD_WIDTH_SET(dev_ctx->dev_fd, width);
562 +
563 +   FD_SET(dev_ctx->ch_fd[dev_idx], &rfds);
564 +   width = FD_WIDTH_SET(dev_ctx->ch_fd[dev_idx], width);
565 +
566 +   tv.tv_sec = TAPI_LL_DEV_SELECT_TIMEOUT_MS / 1000;
567 +   tv.tv_usec = (TAPI_LL_DEV_SELECT_TIMEOUT_MS % 1000) * 1000; 
568 +
569 +   strm->run_flag = 1;
570 +
571 +   while(1)
572 +   {
573 +      /* Update the local file descriptor by the copy in the task parameter */
574 +      memcpy((void *) &trfds, (void*) &rfds, sizeof(fd_set));
575 +
576 +      sretval = select(width + 1, &trfds, NULL, NULL, &tv); 
577 +
578 +      if (!strm->run_flag) {
579 +         break;
580 +      }
581 +
582 +      /* error or timeout on select */
583 +      if (sretval <= 0) {
584 +         continue;
585 +      }
586 +
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!"));
591 +            break;
592 +         }
593 +      }
594 +
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!"));
599 +            break;
600 +         }
601 +      }
602 +   }
603 +
604 +   PJ_LOG(1,(THIS_FILE, "TAPI: thread stopping..."));
605 +
606 +   return 0;
607 +}
608 +
609 +/****************************************************************************
610 + Factory operations
611 + ****************************************************************************/
612 +
613 +/*  Init tapi audio driver. */
614 +pjmedia_aud_dev_factory* pjmedia_tapi_factory(pj_pool_factory *pf)
615 +{
616 +    struct tapi_aud_factory *f;
617 +    pj_pool_t *pool;
618 +
619 +    TRACE_((THIS_FILE, "pjmedia_tapi_factory()"));
620 +
621 +    pool = pj_pool_create(pf, "tapi", 512, 512, NULL);
622 +    f = PJ_POOL_ZALLOC_T(pool, struct tapi_aud_factory);
623 +    f->pf = pf;
624 +    f->pool = pool;
625 +    f->base.op = &tapi_fact_op;
626 +
627 +    return &f->base;
628 +}
629 +
630 +/* API: init factory */
631 +static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
632 +{
633 +   struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
634 +   pj_uint8_t c;
635 +
636 +   TRACE_((THIS_FILE, "factory_init()"));
637 +
638 +   /* Enumerate sound devices */
639 +   af->dev_count = TAPI_AUDIO_DEV_NUM;
640 +
641 +   af->dev_info = (pjmedia_aud_dev_info*)
642 +         pj_pool_calloc(af->pool, af->dev_count, sizeof(pjmedia_aud_dev_info));
643 +
644 +   for (c = 0; c < af->dev_count; c++) {
645 +      pj_ansi_sprintf(af->dev_info[c].name,"%s_%02d", TAPI_BASE_NAME, c);
646 +
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");
650 +
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;
654 +
655 +      af->dev_info[c].routes = PJMEDIA_AUD_DEV_ROUTE_DEFAULT ;
656 +   }
657 +
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;
662 +   }
663 +
664 +   return PJ_SUCCESS;
665 +}
666 +
667 +/* API: destroy factory */
668 +static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
669 +{
670 +   struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
671 +   pj_pool_t *pool;
672 +   pj_status_t status = PJ_SUCCESS;
673 +
674 +   TRACE_((THIS_FILE, "factory_destroy()"));
675 +
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;
680 +   }
681 +
682 +   pool = af->pool;
683 +   af->pool = NULL;
684 +   pj_pool_release(pool);
685 +
686 +   return status;
687 +}
688 +
689 +/* API: get number of devices */
690 +static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
691 +{
692 +  struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
693 +  TRACE_((THIS_FILE, "factory_get_dev_count()"));
694 +
695 +  return af->dev_count;
696 +}
697 +
698 +/* API: get device info */
699 +static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
700 +          unsigned index,
701 +          pjmedia_aud_dev_info *info)
702 +{
703 +  struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
704 +
705 +  TRACE_((THIS_FILE, "factory_get_dev_info()"));
706 +  PJ_ASSERT_RETURN(index < af->dev_count, PJMEDIA_EAUD_INVDEV);
707 +
708 +  pj_memcpy(info, &af->dev_info[index], sizeof(*info));
709 +
710 +  return PJ_SUCCESS;
711 +}
712 +
713 +/* API: create default device parameter */
714 +static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
715 +           unsigned index,
716 +           pjmedia_aud_param *param)
717 +{
718 +  struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
719 +  struct pjmedia_aud_dev_info *di = &af->dev_info[index];
720 +
721 +  TRACE_((THIS_FILE, "factory_default_param."));
722 +  PJ_ASSERT_RETURN(index < af->dev_count, PJMEDIA_EAUD_INVDEV);
723 +
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;
737 +  } else {
738 +    return PJMEDIA_EAUD_INVDEV;
739 +  }
740 +
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;
747 +
748 +  return PJ_SUCCESS;
749 +}
750 +
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,
756 +           void *user_data,
757 +           pjmedia_aud_stream **p_aud_strm)
758 +{
759 +   struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
760 +   pj_pool_t *pool;
761 +   struct tapi_aud_stream *strm;
762 +   pj_status_t status;
763 +
764 +   TRACE_((THIS_FILE, "factory_create_stream()"));
765 +
766 +   /* Can only support 16bits per sample */
767 +   PJ_ASSERT_RETURN(param->bits_per_sample == TAPI_LL_DEV_ENC_BITS_PER_SMPLS, PJ_EINVAL);
768 +
769 +   PJ_ASSERT_RETURN(param->clock_rate == TAPI_LL_DEV_ENC_SMPL_PER_SEC, PJ_EINVAL);
770 +
771 +   PJ_ASSERT_RETURN(param->samples_per_frame == TAPI_LL_DEV_ENC_SMPL_PER_FRAME, PJ_EINVAL);
772 +
773 +   /* Can only support bidirectional stream */
774 +   PJ_ASSERT_RETURN(param->dir & PJMEDIA_DIR_CAPTURE_PLAYBACK, PJ_EINVAL);
775 +
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);
779 +
780 +   strm = PJ_POOL_ZALLOC_T(pool, struct tapi_aud_stream);
781 +   strm->pool      = pool;
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));
786 +
787 +   if ((strm->param.flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) == 0) {
788 +      strm->param.ext_fmt.id = PJMEDIA_FORMAT_L16;
789 +   }
790 +
791 +   strm->timestamp.u64 = 0;
792 +   strm->dev_ctx = &(af->dev_ctx);
793 +
794 +   /* Create and start the thread */
795 +   status = pj_thread_create(pool, "tapi", &tapi_dev_thread, strm, 0, 0, 
796 +                             &strm->thread);
797 +   if (status != PJ_SUCCESS) {
798 +      stream_destroy(&strm->base);
799 +      return status;
800 +   }
801 +
802 +   /* Done */
803 +   strm->base.op = &tapi_strm_op;
804 +   *p_aud_strm = &strm->base;
805 +
806 +   return PJ_SUCCESS;
807 +}
808 +
809 +/****************************************************************************
810 + * Stream operations
811 + */
812 +/* API: Get stream info. */
813 +static pj_status_t stream_get_param(pjmedia_aud_stream *s,
814 +                                    pjmedia_aud_param *pi)
815 +{
816 +  struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s;
817 +
818 +  PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
819 +
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)
824 +  {
825 +    pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
826 +  }
827 +
828 +  if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
829 +        &pi->output_latency_ms) == PJ_SUCCESS)
830 +  {
831 +    pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
832 +  }
833 +
834 +  if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
835 +        &pi->input_latency_ms) == PJ_SUCCESS)
836 +  {
837 +    pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
838 +  }
839 +
840 +  return PJ_SUCCESS;
841 +}
842 +
843 +/* API: get capability */
844 +static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
845 +          pjmedia_aud_dev_cap cap,
846 +          void *pval)
847 +{
848 +  struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s;
849 +#ifdef OLD_IMPL
850 +  OSStatus status = 0;
851 +  PJ_ASSERT_RETURN(strm && pval, PJ_EINVAL);
852 +
853 +  if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING && strm->play_strm->queue)
854 +  {
855 +    Float32 vol;
856 +    status = AudioQueueGetParameter(strm->play_strm->queue,
857 +                                    kAudioQueueParam_Volume, &vol);
858 +    if (!status)
859 +    {
860 +      *(unsigned*)pval = (vol * 100);
861 +      return PJ_SUCCESS;
862 +    }
863 +  }
864 +  else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && strm->play_strm->queue)
865 +  {
866 +    Float32 lat;
867 +    UInt32 size = sizeof(lat);
868 +    status = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency,
869 +                                     &size, &lat);
870 +    if (!status)
871 +    {
872 +      *(unsigned*)pval = lat * 1000;
873 +      return PJ_SUCCESS;
874 +    }
875 +  }
876 +  else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE && strm->play_strm->queue)
877 +  {
878 +    *(pjmedia_aud_dev_route*)pval = strm->param.output_route;
879 +    return PJ_SUCCESS;
880 +  }
881 +  else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && strm->rec_strm->queue)
882 +  {
883 +    Float32 lat;
884 +    UInt32 size = sizeof(lat);
885 +    status = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputLatency,
886 +                                     &size, &lat);
887 +    if (!status)
888 +    {
889 +      *(unsigned*)pval = lat * 1000;
890 +      return PJ_SUCCESS;
891 +    }
892 +  }
893 +
894 +  if (status)
895 +    PJ_LOG(1, (THIS_FILE, "AudioQueueGetParameter/AudioSessionGetProperty err %d", status));
896 +  return PJMEDIA_EAUD_INVCAP;
897 +#else
898 +  return PJ_SUCCESS;
899 +#endif
900 +}
901 +
902 +/* API: set capability */
903 +static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
904 +          pjmedia_aud_dev_cap cap,
905 +          const void *pval)
906 +{
907 +  struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s;
908 +#ifdef OLD_IMPL
909 +  OSStatus status = 0;
910 +  PJ_ASSERT_RETURN(strm && pval, PJ_EINVAL);
911 +
912 +  if (strm->play_strm->queue)
913 +    switch (cap)
914 +    {
915 +      case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
916 +      {
917 +        /* Output volume setting */
918 +        unsigned vol = *(unsigned*)pval;
919 +        Float32 volume;
920 +
921 +        if (vol > 100)
922 +          vol = 100;
923 +        volume = vol / 100.;
924 +        status = AudioQueueSetParameter(strm->play_strm->queue, kAudioQueueParam_Volume,
925 +                                        volume);
926 +        if (!status)
927 +        {
928 +          PJ_LOG(1, (THIS_FILE, "AudioQueueSetParameter err %d", status));
929 +          return PJMEDIA_EAUD_SYSERR;
930 +        }
931 +        strm->param.output_vol = *(unsigned*)pval;
932 +        return PJ_SUCCESS;
933 +      }
934 +      case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
935 +      {
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);
940 +
941 +        status = AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,
942 +                                          sizeof(route), &route);
943 +        if (status)
944 +        {
945 +          PJ_LOG(1, (THIS_FILE, "AudioSessionSetProperty err %d", status));
946 +          return PJMEDIA_EAUD_SYSERR;
947 +        }
948 +        strm->param.output_route = r;
949 +        return PJ_SUCCESS;
950 +      }
951 +      default:
952 +        return PJMEDIA_EAUD_INVCAP;
953 +    }
954 +
955 +
956 +  return PJMEDIA_EAUD_INVCAP;
957 +#else
958 +  return PJ_SUCCESS;
959 +#endif
960 +}
961 +
962 +/* API: Start stream. */
963 +static pj_status_t stream_start(pjmedia_aud_stream *s)
964 +{
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;
968 +
969 +   TRACE_((THIS_FILE, "stream_start()"));
970 +
971 +   dev_idx = strm->param.rec_id;
972 +
973 +   return PJ_SUCCESS;
974 +}
975 +
976 +/* API: Stop stream. */
977 +static pj_status_t stream_stop(pjmedia_aud_stream *s)
978 +{
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;
982 +
983 +   TRACE_((THIS_FILE, "stream_stop()"));
984 +
985 +   dev_idx = strm->param.rec_id;
986 +
987 +   /* enc/dec stop*/
988 +   if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 0) != PJ_SUCCESS) {
989 +      TRACE_((THIS_FILE, "ERROR - codec start failed!"));
990 +
991 +      return PJ_EUNKNOWN;
992 +   }
993 +
994 +   return PJ_SUCCESS;
995 +}
996 +
997 +/* API: Destroy stream. */
998 +static pj_status_t stream_destroy(pjmedia_aud_stream *s)
999 +{
1000 +   pj_status_t state = PJ_SUCCESS;
1001 +   struct tapi_aud_stream *stream = (struct tapi_aud_stream*)s;
1002 +   pj_pool_t *pool;
1003 +
1004 +   PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1005 +
1006 +   TRACE_((THIS_FILE, "stream_destroy()"));
1007 +
1008 +   stream_stop(stream);
1009 +
1010 +   stream->run_flag = 0;
1011 +
1012 +   /* Stop the stream thread */
1013 +   if (stream->thread)
1014 +   {
1015 +      pj_thread_join(stream->thread);
1016 +      pj_thread_destroy(stream->thread);
1017 +      stream->thread = NULL;
1018 +   }
1019 +
1020 +   pool = stream->pool;
1021 +   pj_bzero(stream, sizeof(stream));
1022 +   pj_pool_release(pool);
1023 +
1024 +   return state;
1025 +}
1026 +
1027 +#endif /* PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE */