Root/package/pjsip/src/pjmedia/src/pjmedia-audiodev/tapi_dev.c

1/******************************************************************************
2
3                               Copyright (c) 2010
4                            Lantiq Deutschland GmbH
5                     Am Campeon 3; 85579 Neubiberg, Germany
6
7  For licensing information, see the file 'LICENSE' in the root folder of
8  this software module.
9
10******************************************************************************/
11#include <pjmedia-audiodev/audiodev_imp.h>
12#include <pjmedia/errno.h>
13#include <pj/assert.h>
14#include <pj/pool.h>
15#include <pj/log.h>
16#include <pj/os.h>
17
18/* Linux includes*/
19#include <stdio.h>
20#include <string.h>
21#include <stdlib.h>
22#include <ctype.h>
23#include <sys/stat.h>
24#include <fcntl.h>
25#include <sys/types.h>
26#include <sys/ioctl.h>
27#include <sys/select.h>
28#include <sys/time.h>
29#include <unistd.h>
30
31#if PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE
32
33/* TAPI includes*/
34#include "drv_tapi_io.h"
35#include "vmmc_io.h"
36
37/* Maximum 2 devices*/
38#define TAPI_AUDIO_DEV_NUM (1)
39#define TAPI_AUDIO_MAX_DEV_NUM (2)
40#define TAPI_BASE_NAME "TAPI"
41#define TAPI_LL_DEV_BASE_PATH "/dev/vmmc"
42#define TAPI_LL_DEV_FIRMWARE_NAME "/lib/firmware/danube_firmware.bin"
43
44#define TAPI_LL_DEV_SELECT_TIMEOUT_MS (2000)
45#define TAPI_LL_DEV_MAX_PACKET_SIZE (800)
46#define TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE (12)
47#define TAPI_LL_DEV_ENC_FRAME_LEN_MS (20)
48#define TAPI_LL_DEV_ENC_SMPL_PER_SEC (8000)
49#define TAPI_LL_DEV_ENC_BITS_PER_SMPLS (16)
50#define TAPI_LL_DEV_ENC_SMPL_PER_FRAME (160)
51#define TAPI_LL_DEV_ENC_BYTES_PER_FRAME (TAPI_LL_DEV_ENC_SMPL_PER_FRAME * (TAPI_LL_DEV_ENC_BITS_PER_SMPLS / 8))
52
53
54#define FD_WIDTH_SET(fd, maxfd) (maxfd) < (fd) ? (fd) : maxfd
55
56#define THIS_FILE "tapi_dev.c"
57
58#if 1
59# define TRACE_(x) PJ_LOG(1,x)
60#else
61# define TRACE_(x)
62#endif
63
64typedef struct
65{
66   pj_int32_t dev_fd;
67   pj_int32_t ch_fd[TAPI_AUDIO_DEV_NUM];
68   pj_int8_t data2phone_map[TAPI_AUDIO_DEV_NUM];
69   
70} tapi_ctx;
71
72
73/* TAPI factory */
74struct tapi_aud_factory
75{
76   pjmedia_aud_dev_factory base;
77   pj_pool_t *pool;
78   pj_pool_factory *pf;
79
80   pj_uint32_t dev_count;
81   pjmedia_aud_dev_info *dev_info;
82
83   tapi_ctx dev_ctx;
84};
85
86typedef struct tapi_aud_factory tapi_aud_factory_t;
87
88/*
89   Sound stream descriptor.
90**/
91struct tapi_aud_stream
92{
93   /* Base*/
94   pjmedia_aud_stream base; /**< Base class. */
95   /* Pool*/
96   pj_pool_t *pool; /**< Memory pool. */
97   /* Common settings.*/
98   pjmedia_aud_param param; /**< Stream param. */
99   pjmedia_aud_rec_cb rec_cb; /**< Record callback. */
100   pjmedia_aud_play_cb play_cb; /**< Playback callback. */
101   void *user_data; /**< Application data. */
102
103   pj_thread_desc thread_desc;
104   pj_thread_t *thread;
105   tapi_ctx *dev_ctx;
106   pj_uint8_t run_flag;
107   pj_timestamp timestamp;
108};
109
110typedef struct tapi_aud_stream tapi_aud_stream_t;
111
112/* Factory prototypes */
113static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
114static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
115static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
116static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
117          unsigned index,
118          pjmedia_aud_dev_info *info);
119static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
120           unsigned index,
121           pjmedia_aud_param *param);
122static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
123           const pjmedia_aud_param *param,
124           pjmedia_aud_rec_cb rec_cb,
125           pjmedia_aud_play_cb play_cb,
126           void *user_data,
127           pjmedia_aud_stream **p_aud_strm);
128
129/* Stream prototypes */
130static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
131          pjmedia_aud_param *param);
132static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
133              pjmedia_aud_dev_cap cap,
134              void *value);
135static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
136              pjmedia_aud_dev_cap cap,
137              const void *value);
138static pj_status_t stream_start(pjmedia_aud_stream *strm);
139static pj_status_t stream_stop(pjmedia_aud_stream *strm);
140static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
141
142static pjmedia_aud_dev_factory_op tapi_fact_op =
143  {
144      &factory_init,
145      &factory_destroy,
146      &factory_get_dev_count,
147      &factory_get_dev_info,
148      &factory_default_param,
149      &factory_create_stream
150  };
151
152static pjmedia_aud_stream_op tapi_strm_op =
153{
154    &stream_get_param,
155    &stream_get_cap,
156    &stream_set_cap,
157    &stream_start,
158    &stream_stop,
159    &stream_destroy
160};
161
162void (*tapi_digit_callback)(unsigned char digit) = NULL;
163void (*tapi_hook_callback)(unsigned char event) = NULL;
164
165static 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
174static 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
213on_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
225static void tapi_dev_binary_buffer_delete(pj_uint8_t *pBuf)
226{
227   if (pBuf != NULL)
228      free(pBuf);
229}
230
231static 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
265static 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
359static 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
382static 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
401static 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
422static 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
443static pj_status_t tapi_dev_event_digit(tapi_ctx *dev_ctx, pj_uint32_t dev_idx)
444{
445   PJ_LOG(1,(THIS_FILE, "TAPI: OFFHOOK"));
446
447   if (ioctl(dev_ctx->ch_fd[dev_idx], IFX_TAPI_LINE_FEED_SET,
448               IFX_TAPI_LINE_FEED_ACTIVE) != PJ_SUCCESS) {
449      TRACE_((THIS_FILE, "ERROR - IFX_TAPI_LINE_FEED_SET ioctl failed!"));
450
451      return PJ_EUNKNOWN;
452   }
453
454   /* enc/dec stop*/
455   if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 1) != PJ_SUCCESS) {
456      TRACE_((THIS_FILE, "ERROR - codec start failed!"));
457
458      return PJ_EUNKNOWN;
459   }
460
461   return PJ_SUCCESS;
462}
463
464static pj_status_t tapi_dev_event_handler(
465                     tapi_aud_stream_t *stream)
466{
467   tapi_ctx *dev_ctx = stream->dev_ctx;
468   pj_uint32_t dev_idx = stream->param.rec_id;
469   pj_status_t status = PJ_SUCCESS;
470   IFX_TAPI_EVENT_t tapiEvent;
471
472   memset (&tapiEvent, 0, sizeof(tapiEvent));
473
474   tapiEvent.ch = dev_ctx->data2phone_map[dev_idx];
475
476   /* Get event*/
477   status = ioctl(dev_ctx->dev_fd, IFX_TAPI_EVENT_GET, &tapiEvent);
478
479   if ((status == PJ_SUCCESS) && (tapiEvent.id != IFX_TAPI_EVENT_NONE)) {
480      switch(tapiEvent.id) {
481         case IFX_TAPI_EVENT_FXS_ONHOOK:
482            status = tapi_dev_event_ONHOOK(dev_ctx, dev_idx);
483        if(tapi_hook_callback)
484           tapi_hook_callback(0);
485         break;
486         case IFX_TAPI_EVENT_FXS_OFFHOOK:
487            status = tapi_dev_event_OFFHOOK(dev_ctx, dev_idx);
488        if(tapi_hook_callback)
489           tapi_hook_callback(1);
490         break;
491     case IFX_TAPI_EVENT_DTMF_DIGIT:
492        if(tapi_digit_callback)
493           tapi_digit_callback(tapiEvent.data.dtmf.ascii);
494        break;
495     default:
496            printf("%s:%s[%d]%04X\n", __FILE__, __func__, __LINE__, tapiEvent.id);
497        break;
498      }
499   }
500
501   return status;
502}
503
504static pj_status_t tapi_dev_data_handler(
505                     tapi_aud_stream_t *stream)
506{
507   pj_status_t status = PJ_SUCCESS;
508   tapi_ctx *dev_ctx = stream->dev_ctx;
509   pj_uint32_t dev_idx = stream->param.rec_id;
510   pj_uint8_t buf_rec[TAPI_LL_DEV_ENC_BYTES_PER_FRAME + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE]={0};
511   pj_uint8_t buf_play[TAPI_LL_DEV_ENC_BYTES_PER_FRAME + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE]={0};
512   pjmedia_frame frame_rec, frame_play;
513   pj_int32_t ret;
514
515   /* Get data from driver*/
516   ret = read(dev_ctx->ch_fd[dev_idx], buf_rec, sizeof(buf_rec));
517   if (ret < 0) {
518      TRACE_((THIS_FILE, "ERROR - no data available from device!"));
519
520      return PJ_EUNKNOWN;
521   }
522
523   if (ret > 0) {
524      frame_rec.type = PJMEDIA_FRAME_TYPE_AUDIO;
525      frame_rec.buf = buf_rec + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE;
526      frame_rec.size = ret - TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE;
527      frame_rec.timestamp.u64 = stream->timestamp.u64;
528
529      status = stream->rec_cb(stream->user_data, &frame_rec);
530      if (status != PJ_SUCCESS)
531      {
532        PJ_LOG(1, (THIS_FILE, "rec_cb() failed %d", status));
533      }
534
535      frame_play.type = PJMEDIA_FRAME_TYPE_AUDIO;
536      frame_play.buf = buf_play + TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE;
537      frame_play.size = TAPI_LL_DEV_ENC_BYTES_PER_FRAME;
538      frame_play.timestamp.u64 = stream->timestamp.u64;
539
540      status = (*stream->play_cb)(stream->user_data, &frame_play);
541      if (status != PJ_SUCCESS)
542      {
543         PJ_LOG(1, (THIS_FILE, "play_cb() failed %d", status));
544      }
545      else
546      {
547         memcpy(buf_play, buf_rec, TAPI_LL_DEV_RTP_HEADER_SIZE_BYTE);
548
549         ret = write(dev_ctx->ch_fd[dev_idx], buf_play, sizeof(buf_play));
550
551         if (ret < 0) {
552            PJ_LOG(1, (THIS_FILE, "ERROR - device data writing failed!"));
553            return PJ_EUNKNOWN;
554         }
555
556         if (ret == 0) {
557            PJ_LOG(1, (THIS_FILE, "ERROR - no data written to device!"));
558            return PJ_EUNKNOWN;
559         }
560      }
561
562      stream->timestamp.u64 += TAPI_LL_DEV_ENC_SMPL_PER_FRAME;
563   }
564
565   return PJ_SUCCESS;
566}
567
568/* TAPI capture and playback thread. */
569static int PJ_THREAD_FUNC tapi_dev_thread(void *arg)
570{
571   tapi_aud_stream_t *strm = (struct tapi_aud_stream*)arg;
572   tapi_ctx *dev_ctx = strm->dev_ctx;
573   fd_set rfds, trfds;
574   pj_uint32_t width = 0;
575   struct timeval tv;
576   pj_uint32_t sretval;
577   pj_uint32_t dev_idx;
578
579   PJ_LOG(1,(THIS_FILE, "TAPI: thread starting..."));
580
581   if (strm->param.rec_id != strm->param.play_id) {
582      PJ_LOG(1,(THIS_FILE, "TAPI: thread exit - incorrect play/rec IDs"));
583      return 0;
584   }
585
586   dev_idx = strm->param.rec_id;
587
588   FD_ZERO(&rfds);
589
590   FD_SET(dev_ctx->dev_fd, &rfds);
591   width = FD_WIDTH_SET(dev_ctx->dev_fd, width);
592
593   FD_SET(dev_ctx->ch_fd[dev_idx], &rfds);
594   width = FD_WIDTH_SET(dev_ctx->ch_fd[dev_idx], width);
595
596   tv.tv_sec = TAPI_LL_DEV_SELECT_TIMEOUT_MS / 1000;
597   tv.tv_usec = (TAPI_LL_DEV_SELECT_TIMEOUT_MS % 1000) * 1000;
598
599   strm->run_flag = 1;
600
601   while(1)
602   {
603      /* Update the local file descriptor by the copy in the task parameter */
604      memcpy((void *) &trfds, (void*) &rfds, sizeof(fd_set));
605
606      sretval = select(width + 1, &trfds, NULL, NULL, &tv);
607
608      if (!strm->run_flag) {
609         break;
610      }
611
612      /* error or timeout on select */
613      if (sretval <= 0) {
614         continue;
615      }
616
617      /* Check device control channel*/
618      if (FD_ISSET(dev_ctx->dev_fd, &trfds)) {
619     if (tapi_dev_event_handler(strm) != PJ_SUCCESS) {
620            PJ_LOG(1,(THIS_FILE, "TAPI: event hanldler failed!"));
621            break;
622         }
623      }
624
625      /* Check device data channel*/
626      if (FD_ISSET(dev_ctx->ch_fd[dev_idx], &trfds)) {
627     if (tapi_dev_data_handler(strm) != PJ_SUCCESS) {
628            PJ_LOG(1,(THIS_FILE, "TAPI: data hanldler failed!"));
629            break;
630         }
631      }
632   }
633
634   PJ_LOG(1,(THIS_FILE, "TAPI: thread stopping..."));
635
636   return 0;
637}
638
639/****************************************************************************
640 Factory operations
641 ****************************************************************************/
642
643/* Init tapi audio driver. */
644pjmedia_aud_dev_factory* pjmedia_tapi_factory(pj_pool_factory *pf)
645{
646    struct tapi_aud_factory *f;
647    pj_pool_t *pool;
648
649    TRACE_((THIS_FILE, "pjmedia_tapi_factory()"));
650
651    pool = pj_pool_create(pf, "tapi", 512, 512, NULL);
652    f = PJ_POOL_ZALLOC_T(pool, struct tapi_aud_factory);
653    f->pf = pf;
654    f->pool = pool;
655    f->base.op = &tapi_fact_op;
656
657    return &f->base;
658}
659
660/* API: init factory */
661static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
662{
663   struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
664   pj_uint8_t c;
665
666   TRACE_((THIS_FILE, "factory_init()"));
667
668   /* Enumerate sound devices */
669   af->dev_count = TAPI_AUDIO_DEV_NUM;
670
671   af->dev_info = (pjmedia_aud_dev_info*)
672         pj_pool_calloc(af->pool, af->dev_count, sizeof(pjmedia_aud_dev_info));
673
674   for (c = 0; c < af->dev_count; c++) {
675      pj_ansi_sprintf(af->dev_info[c].name,"%s_%02d", TAPI_BASE_NAME, c);
676
677      af->dev_info[c].input_count = af->dev_info[c].output_count = 1;
678      af->dev_info[c].default_samples_per_sec = TAPI_LL_DEV_ENC_SMPL_PER_SEC;
679      pj_ansi_strcpy(af->dev_info[c].driver, "/dev/vmmc");
680
681      af->dev_info[c].caps = PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |
682                             PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY |
683                             PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
684
685      af->dev_info[c].routes = PJMEDIA_AUD_DEV_ROUTE_DEFAULT ;
686   }
687
688   /* Initialize TAPI device(s)*/
689   if (tapi_dev_start(af) != PJ_SUCCESS) {
690      TRACE_((THIS_FILE, "ERROR - TAPI device init failed!"));
691      return PJ_EUNKNOWN;
692   }
693
694   return PJ_SUCCESS;
695}
696
697/* API: destroy factory */
698static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
699{
700   struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
701   pj_pool_t *pool;
702   pj_status_t status = PJ_SUCCESS;
703
704   TRACE_((THIS_FILE, "factory_destroy()"));
705
706   /* Stop TAPI device*/
707   if (tapi_dev_stop(f) != PJ_SUCCESS) {
708      TRACE_((THIS_FILE, "ERROR - TAPI device stop failed!"));
709      status = PJ_EUNKNOWN;
710   }
711
712   pool = af->pool;
713   af->pool = NULL;
714   pj_pool_release(pool);
715
716   return status;
717}
718
719/* API: get number of devices */
720static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
721{
722  struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
723  TRACE_((THIS_FILE, "factory_get_dev_count()"));
724
725  return af->dev_count;
726}
727
728/* API: get device info */
729static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
730          unsigned index,
731          pjmedia_aud_dev_info *info)
732{
733  struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
734
735  TRACE_((THIS_FILE, "factory_get_dev_info()"));
736  PJ_ASSERT_RETURN(index < af->dev_count, PJMEDIA_EAUD_INVDEV);
737
738  pj_memcpy(info, &af->dev_info[index], sizeof(*info));
739
740  return PJ_SUCCESS;
741}
742
743/* API: create default device parameter */
744static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
745           unsigned index,
746           pjmedia_aud_param *param)
747{
748  struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
749  struct pjmedia_aud_dev_info *di = &af->dev_info[index];
750
751  TRACE_((THIS_FILE, "factory_default_param."));
752  PJ_ASSERT_RETURN(index < af->dev_count, PJMEDIA_EAUD_INVDEV);
753
754  pj_bzero(param, sizeof(*param));
755  if (di->input_count && di->output_count) {
756    param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
757    param->rec_id = index;
758    param->play_id = index;
759  } else if (di->input_count) {
760    param->dir = PJMEDIA_DIR_CAPTURE;
761    param->rec_id = index;
762    param->play_id = PJMEDIA_AUD_INVALID_DEV;
763  } else if (di->output_count) {
764    param->dir = PJMEDIA_DIR_PLAYBACK;
765    param->play_id = index;
766    param->rec_id = PJMEDIA_AUD_INVALID_DEV;
767  } else {
768    return PJMEDIA_EAUD_INVDEV;
769  }
770
771  param->clock_rate = TAPI_LL_DEV_ENC_SMPL_PER_SEC; //di->default_samples_per_sec;
772  param->channel_count = 1;
773  param->samples_per_frame = TAPI_LL_DEV_ENC_SMPL_PER_FRAME;
774  param->bits_per_sample = TAPI_LL_DEV_ENC_BITS_PER_SMPLS;
775  param->flags = PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE | di->caps;
776  param->output_route = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
777
778  return PJ_SUCCESS;
779}
780
781/* API: create stream */
782static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
783           const pjmedia_aud_param *param,
784           pjmedia_aud_rec_cb rec_cb,
785           pjmedia_aud_play_cb play_cb,
786           void *user_data,
787           pjmedia_aud_stream **p_aud_strm)
788{
789   struct tapi_aud_factory *af = (struct tapi_aud_factory*)f;
790   pj_pool_t *pool;
791   struct tapi_aud_stream *strm;
792   pj_status_t status;
793
794   TRACE_((THIS_FILE, "factory_create_stream()"));
795
796   /* Can only support 16bits per sample */
797   PJ_ASSERT_RETURN(param->bits_per_sample == TAPI_LL_DEV_ENC_BITS_PER_SMPLS, PJ_EINVAL);
798   printf("param->clock_rate = %d, samples_per_frame = %d\n", param->clock_rate, param->samples_per_frame);
799   PJ_ASSERT_RETURN(param->clock_rate == TAPI_LL_DEV_ENC_SMPL_PER_SEC, PJ_EINVAL);
800
801   PJ_ASSERT_RETURN(param->samples_per_frame == TAPI_LL_DEV_ENC_SMPL_PER_FRAME, PJ_EINVAL);
802
803   /* Can only support bidirectional stream */
804   PJ_ASSERT_RETURN(param->dir & PJMEDIA_DIR_CAPTURE_PLAYBACK, PJ_EINVAL);
805
806   /* Initialize our stream data */
807   pool = pj_pool_create(af->pf, "tapi-dev", 1000, 1000, NULL);
808   PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
809
810   strm = PJ_POOL_ZALLOC_T(pool, struct tapi_aud_stream);
811   strm->pool = pool;
812   strm->rec_cb = rec_cb;
813   strm->play_cb = play_cb;
814   strm->user_data = user_data;
815   pj_memcpy(&strm->param, param, sizeof(*param));
816
817   if ((strm->param.flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) == 0) {
818      strm->param.ext_fmt.id = PJMEDIA_FORMAT_L16;
819   }
820
821   strm->timestamp.u64 = 0;
822   strm->dev_ctx = &(af->dev_ctx);
823
824   /* Create and start the thread */
825   status = pj_thread_create(pool, "tapi", &tapi_dev_thread, strm, 0, 0,
826                  &strm->thread);
827   if (status != PJ_SUCCESS) {
828      stream_destroy(&strm->base);
829      return status;
830   }
831
832   /* Done */
833   strm->base.op = &tapi_strm_op;
834   *p_aud_strm = &strm->base;
835
836   return PJ_SUCCESS;
837}
838
839/****************************************************************************
840 * Stream operations
841 */
842/* API: Get stream info. */
843static pj_status_t stream_get_param(pjmedia_aud_stream *s,
844                                    pjmedia_aud_param *pi)
845{
846  struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s;
847
848  PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
849
850  pj_memcpy(pi, &strm->param, sizeof(*pi));
851  /* Update the volume setting */
852  if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
853        &pi->output_vol) == PJ_SUCCESS)
854  {
855    pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
856  }
857
858  if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
859        &pi->output_latency_ms) == PJ_SUCCESS)
860  {
861    pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
862  }
863
864  if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
865        &pi->input_latency_ms) == PJ_SUCCESS)
866  {
867    pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
868  }
869
870  return PJ_SUCCESS;
871}
872
873/* API: get capability */
874static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
875          pjmedia_aud_dev_cap cap,
876          void *pval)
877{
878  struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s;
879#ifdef OLD_IMPL
880  OSStatus status = 0;
881  PJ_ASSERT_RETURN(strm && pval, PJ_EINVAL);
882
883  if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING && strm->play_strm->queue)
884  {
885    Float32 vol;
886    status = AudioQueueGetParameter(strm->play_strm->queue,
887                                    kAudioQueueParam_Volume, &vol);
888    if (!status)
889    {
890      *(unsigned*)pval = (vol * 100);
891      return PJ_SUCCESS;
892    }
893  }
894  else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && strm->play_strm->queue)
895  {
896    Float32 lat;
897    UInt32 size = sizeof(lat);
898    status = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency,
899                                     &size, &lat);
900    if (!status)
901    {
902      *(unsigned*)pval = lat * 1000;
903      return PJ_SUCCESS;
904    }
905  }
906  else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE && strm->play_strm->queue)
907  {
908    *(pjmedia_aud_dev_route*)pval = strm->param.output_route;
909    return PJ_SUCCESS;
910  }
911  else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && strm->rec_strm->queue)
912  {
913    Float32 lat;
914    UInt32 size = sizeof(lat);
915    status = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputLatency,
916                                     &size, &lat);
917    if (!status)
918    {
919      *(unsigned*)pval = lat * 1000;
920      return PJ_SUCCESS;
921    }
922  }
923
924  if (status)
925    PJ_LOG(1, (THIS_FILE, "AudioQueueGetParameter/AudioSessionGetProperty err %d", status));
926  return PJMEDIA_EAUD_INVCAP;
927#else
928  return PJ_SUCCESS;
929#endif
930}
931
932/* API: set capability */
933static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
934          pjmedia_aud_dev_cap cap,
935          const void *pval)
936{
937  struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s;
938#ifdef OLD_IMPL
939  OSStatus status = 0;
940  PJ_ASSERT_RETURN(strm && pval, PJ_EINVAL);
941
942  if (strm->play_strm->queue)
943    switch (cap)
944    {
945      case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
946      {
947        /* Output volume setting */
948        unsigned vol = *(unsigned*)pval;
949        Float32 volume;
950
951        if (vol > 100)
952          vol = 100;
953        volume = vol / 100.;
954        status = AudioQueueSetParameter(strm->play_strm->queue, kAudioQueueParam_Volume,
955                                        volume);
956        if (!status)
957        {
958          PJ_LOG(1, (THIS_FILE, "AudioQueueSetParameter err %d", status));
959          return PJMEDIA_EAUD_SYSERR;
960        }
961        strm->param.output_vol = *(unsigned*)pval;
962        return PJ_SUCCESS;
963      }
964      case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
965      {
966        pjmedia_aud_dev_route r = *(const pjmedia_aud_dev_route*)pval;
967        UInt32 route = (r == PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER ?
968            kAudioSessionOverrideAudioRoute_Speaker :
969            kAudioSessionOverrideAudioRoute_None);
970
971        status = AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,
972                                          sizeof(route), &route);
973        if (status)
974        {
975          PJ_LOG(1, (THIS_FILE, "AudioSessionSetProperty err %d", status));
976          return PJMEDIA_EAUD_SYSERR;
977        }
978        strm->param.output_route = r;
979        return PJ_SUCCESS;
980      }
981      default:
982        return PJMEDIA_EAUD_INVCAP;
983    }
984
985
986  return PJMEDIA_EAUD_INVCAP;
987#else
988  return PJ_SUCCESS;
989#endif
990}
991
992/* API: Start stream. */
993static pj_status_t stream_start(pjmedia_aud_stream *s)
994{
995   struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s;
996   tapi_ctx *dev_ctx = strm->dev_ctx;
997   pj_uint32_t dev_idx;
998
999   TRACE_((THIS_FILE, "stream_start()"));
1000
1001   dev_idx = strm->param.rec_id;
1002
1003   return PJ_SUCCESS;
1004}
1005
1006/* API: Stop stream. */
1007static pj_status_t stream_stop(pjmedia_aud_stream *s)
1008{
1009   struct tapi_aud_stream *strm = (struct tapi_aud_stream*)s;
1010   tapi_ctx *dev_ctx = strm->dev_ctx;
1011   pj_uint32_t dev_idx;
1012
1013   TRACE_((THIS_FILE, "stream_stop()"));
1014
1015   dev_idx = strm->param.rec_id;
1016
1017   /* enc/dec stop*/
1018   if (tapi_dev_codec_control(dev_ctx->ch_fd[dev_idx], 0) != PJ_SUCCESS) {
1019      TRACE_((THIS_FILE, "ERROR - codec start failed!"));
1020
1021      return PJ_EUNKNOWN;
1022   }
1023
1024   return PJ_SUCCESS;
1025}
1026
1027/* API: Destroy stream. */
1028static pj_status_t stream_destroy(pjmedia_aud_stream *s)
1029{
1030   pj_status_t state = PJ_SUCCESS;
1031   struct tapi_aud_stream *stream = (struct tapi_aud_stream*)s;
1032   pj_pool_t *pool;
1033
1034   PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1035
1036   TRACE_((THIS_FILE, "stream_destroy()"));
1037
1038   stream_stop(stream);
1039
1040   stream->run_flag = 0;
1041
1042   /* Stop the stream thread */
1043   if (stream->thread)
1044   {
1045      pj_thread_join(stream->thread);
1046      pj_thread_destroy(stream->thread);
1047      stream->thread = NULL;
1048   }
1049
1050   pool = stream->pool;
1051   pj_bzero(stream, sizeof(stream));
1052   pj_pool_release(pool);
1053
1054   return state;
1055}
1056
1057#endif /* PJMEDIA_AUDIO_DEV_HAS_TAPI_DEVICE */
1058

Archive Download this file



interactive