Mon Mar 12 2012 21:31:18

Asterisk developer's documentation


chan_phone.c File Reference

Generic Linux Telephony Interface driver. More...

#include "asterisk.h"
#include <ctype.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <linux/telephony.h>
#include <linux/version.h>
#include <linux/ixjuser.h>
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/utils.h"
#include "asterisk/callerid.h"
#include "asterisk/causes.h"
#include "asterisk/stringfields.h"
#include "asterisk/musiconhold.h"
#include "chan_phone.h"
Include dependency graph for chan_phone.c:

Go to the source code of this file.

Data Structures

struct  phone_pvt

Defines

#define DEFAULT_CALLER_ID   "Unknown"
#define DEFAULT_GAIN   0x100
#define IXJ_PHONE_RING_START(x)   ioctl(p->fd, PHONE_RING_START, &x);
#define MODE_DIALTONE   1
#define MODE_FXO   3
#define MODE_FXS   4
#define MODE_IMMEDIATE   2
#define MODE_SIGMA   5
#define PHONE_MAX_BUF   480
#define QNDRV_VER   100

Functions

static void __reg_module (void)
static int __unload_module (void)
static void __unreg_module (void)
static void * do_monitor (void *data)
static int load_module (void)
static struct phone_pvtmkif (const char *iface, int mode, int txgain, int rxgain)
static int parse_gain_value (const char *gain_type, const char *value)
static int phone_answer (struct ast_channel *ast)
static int phone_call (struct ast_channel *ast, char *dest, int timeout)
static void phone_check_exception (struct phone_pvt *i)
static int phone_digit_begin (struct ast_channel *ast, char digit)
static int phone_digit_end (struct ast_channel *ast, char digit, unsigned int duration)
static struct ast_framephone_exception (struct ast_channel *ast)
static int phone_fixup (struct ast_channel *old, struct ast_channel *new)
static int phone_hangup (struct ast_channel *ast)
static int phone_indicate (struct ast_channel *chan, int condition, const void *data, size_t datalen)
static void phone_mini_packet (struct phone_pvt *i)
static struct ast_channelphone_new (struct phone_pvt *i, int state, char *cntx, const char *linkedid)
static struct ast_framephone_read (struct ast_channel *ast)
static struct ast_channelphone_request (const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause)
static int phone_send_text (struct ast_channel *ast, const char *text)
static int phone_setup (struct ast_channel *ast)
static int phone_write (struct ast_channel *ast, struct ast_frame *frame)
static int phone_write_buf (struct phone_pvt *p, const char *buf, int len, int frlen, int swap)
static int restart_monitor (void)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Linux Telephony API Support" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, }
static struct ast_module_infoast_module_info = &__mod_info
static char cid_name [AST_MAX_EXTENSION]
static char cid_num [AST_MAX_EXTENSION]
static const char config [] = "phone.conf"
static char context [AST_MAX_EXTENSION] = "default"
static struct ast_channel_techcur_tech
static int echocancel = AEC_OFF
static struct phone_pvtiflist
static ast_mutex_t iflock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 }
static char language [MAX_LANGUAGE] = ""
static unsigned int monitor
static pthread_t monitor_thread = AST_PTHREADT_NULL
static ast_mutex_t monlock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 }
static struct ast_channel_tech phone_tech
static struct ast_channel_tech phone_tech_fxs
static format_t prefformat = AST_FORMAT_G729A | AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW
static int silencesupression = 0
static const char tdesc [] = "Standard Linux Telephony API Driver"

Detailed Description

Generic Linux Telephony Interface driver.

Author:
Mark Spencer <markster@digium.com>

Definition in file chan_phone.c.


Define Documentation

#define DEFAULT_CALLER_ID   "Unknown"

Definition at line 81 of file chan_phone.c.

Referenced by phone_call().

#define DEFAULT_GAIN   0x100

Definition at line 83 of file chan_phone.c.

Referenced by load_module(), and parse_gain_value().

#define IXJ_PHONE_RING_START (   x)    ioctl(p->fd, PHONE_RING_START, &x);

Definition at line 78 of file chan_phone.c.

Referenced by phone_call().

#define MODE_DIALTONE   1

Definition at line 119 of file chan_phone.c.

Referenced by load_module(), and phone_check_exception().

#define MODE_FXO   3

Definition at line 121 of file chan_phone.c.

Referenced by load_module(), mkif(), phone_answer(), phone_exception(), and phone_hangup().

#define MODE_FXS   4
#define MODE_IMMEDIATE   2

Definition at line 120 of file chan_phone.c.

Referenced by load_module(), and phone_check_exception().

#define MODE_SIGMA   5

Definition at line 123 of file chan_phone.c.

Referenced by do_monitor(), load_module(), and phone_check_exception().

#define PHONE_MAX_BUF   480

Definition at line 82 of file chan_phone.c.

Referenced by phone_read().

#define QNDRV_VER   100

Definition at line 68 of file chan_phone.c.


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 1479 of file chan_phone.c.

static int __unload_module ( void  ) [static]

Definition at line 1302 of file chan_phone.c.

References ast_channel_unregister(), ast_free, ast_log(), ast_mutex_lock, ast_mutex_unlock, AST_PTHREADT_NULL, AST_PTHREADT_STOP, ast_softhangup(), AST_SOFTHANGUP_APPUNLOAD, phone_pvt::fd, iflist, iflock, LOG_WARNING, monlock, phone_pvt::next, and phone_pvt::owner.

Referenced by load_module(), and unload_module().

{
   struct phone_pvt *p, *pl;
   /* First, take us out of the channel loop */
   if (cur_tech)
      ast_channel_unregister(cur_tech);
   if (!ast_mutex_lock(&iflock)) {
      /* Hangup all interfaces if they have an owner */
      p = iflist;
      while(p) {
         if (p->owner)
            ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
         p = p->next;
      }
      iflist = NULL;
      ast_mutex_unlock(&iflock);
   } else {
      ast_log(LOG_WARNING, "Unable to lock the monitor\n");
      return -1;
   }
   if (!ast_mutex_lock(&monlock)) {
      if (monitor_thread > AST_PTHREADT_NULL) {
         monitor = 0;
         while (pthread_kill(monitor_thread, SIGURG) == 0)
            sched_yield();
         pthread_join(monitor_thread, NULL);
      }
      monitor_thread = AST_PTHREADT_STOP;
      ast_mutex_unlock(&monlock);
   } else {
      ast_log(LOG_WARNING, "Unable to lock the monitor\n");
      return -1;
   }

   if (!ast_mutex_lock(&iflock)) {
      /* Destroy all the interfaces and free their memory */
      p = iflist;
      while(p) {
         /* Close the socket, assuming it's real */
         if (p->fd > -1)
            close(p->fd);
         pl = p;
         p = p->next;
         /* Free associated memory */
         ast_free(pl);
      }
      iflist = NULL;
      ast_mutex_unlock(&iflock);
   } else {
      ast_log(LOG_WARNING, "Unable to lock the monitor\n");
      return -1;
   }
      
   return 0;
}
static void __unreg_module ( void  ) [static]

Definition at line 1479 of file chan_phone.c.

static void* do_monitor ( void *  data) [static]

Definition at line 1017 of file chan_phone.c.

References ast_debug, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_poll, ast_poll2(), ast_realloc, ast_tv(), ast_tvzero(), phone_pvt::dialtone, DialTone, errno, phone_pvt::fd, iflist, iflock, LOG_ERROR, LOG_WARNING, phone_pvt::mode, MODE_SIGMA, phone_pvt::next, phone_pvt::owner, phone_check_exception(), and phone_mini_packet().

Referenced by restart_monitor().

{
   struct pollfd *fds = NULL;
   int nfds = 0, inuse_fds = 0, res;
   struct phone_pvt *i;
   int tonepos = 0;
   /* The tone we're playing this round */
   struct timeval tv = { 0, 0 };
   int dotone;
   /* This thread monitors all the frame relay interfaces which are not yet in use
      (and thus do not have a separate thread) indefinitely */
   while (monitor) {
      /* Don't let anybody kill us right away.  Nobody should lock the interface list
         and wait for the monitor list, but the other way around is okay. */
      /* Lock the interface list */
      if (ast_mutex_lock(&iflock)) {
         ast_log(LOG_ERROR, "Unable to grab interface lock\n");
         return NULL;
      }
      /* Build the stuff we're going to select on, that is the socket of every
         phone_pvt that does not have an associated owner channel */
      i = iflist;
      dotone = 0;
      inuse_fds = 0;
      for (i = iflist; i; i = i->next) {
         if (!i->owner) {
            /* This needs to be watched, as it lacks an owner */
            if (inuse_fds == nfds) {
               void *tmp = ast_realloc(fds, (nfds + 1) * sizeof(*fds));
               if (!tmp) {
                  /* Avoid leaking */
                  continue;
               }
               fds = tmp;
               nfds++;
            }
            fds[inuse_fds].fd = i->fd;
            fds[inuse_fds].events = POLLIN | POLLERR;
            fds[inuse_fds].revents = 0;
            inuse_fds++;

            if (i->dialtone && i->mode != MODE_SIGMA) {
               /* Remember we're going to have to come back and play
                  more dialtones */
               if (ast_tvzero(tv)) {
                  /* If we're due for a dialtone, play one */
                  if (write(i->fd, DialTone + tonepos, 240) != 240) {
                     ast_log(LOG_WARNING, "Dial tone write error\n");
                  }
               }
               dotone++;
            }
         }
      }
      /* Okay, now that we know what to do, release the interface lock */
      ast_mutex_unlock(&iflock);

      /* Wait indefinitely for something to happen */
      if (dotone && i && i->mode != MODE_SIGMA) {
         /* If we're ready to recycle the time, set it to 30 ms */
         tonepos += 240;
         if (tonepos >= sizeof(DialTone)) {
            tonepos = 0;
         }
         if (ast_tvzero(tv)) {
            tv = ast_tv(0, 30000);
         }
         res = ast_poll2(fds, inuse_fds, &tv);
      } else {
         res = ast_poll(fds, inuse_fds, -1);
         tv = ast_tv(0, 0);
         tonepos = 0;
      }
      /* Okay, select has finished.  Let's see what happened.  */
      if (res < 0) {
         ast_debug(1, "poll returned %d: %s\n", res, strerror(errno));
         continue;
      }
      /* If there are no fd's changed, just continue, it's probably time
         to play some more dialtones */
      if (!res) {
         continue;
      }
      /* Alright, lock the interface list again, and let's look and see what has
         happened */
      if (ast_mutex_lock(&iflock)) {
         ast_log(LOG_WARNING, "Unable to lock the interface list\n");
         continue;
      }

      for (i = iflist; i; i = i->next) {
         int j;
         /* Find the record */
         for (j = 0; j < inuse_fds; j++) {
            if (fds[j].fd == i->fd) {
               break;
            }
         }

         /* Not found? */
         if (j == inuse_fds) {
            continue;
         }

         if (fds[j].revents & POLLIN) {
            if (i->owner) {
               continue;
            }
            phone_mini_packet(i);
         }
         if (fds[j].revents & POLLERR) {
            if (i->owner) {
               continue;
            }
            phone_check_exception(i);
         }
      }
      ast_mutex_unlock(&iflock);
   }
   return NULL;
}
static int load_module ( void  ) [static]

Definition at line 1363 of file chan_phone.c.

References __unload_module(), ast_callerid_split(), ast_channel_register(), ast_config_destroy(), ast_config_load, ast_copy_string(), AST_FORMAT_G723_1, AST_FORMAT_G729A, AST_FORMAT_SLINEAR, AST_FORMAT_ULAW, ast_log(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_SUCCESS, ast_mutex_lock, ast_mutex_unlock, ast_true(), ast_variable_browse(), ast_channel_tech::capabilities, CONFIG_STATUS_FILEINVALID, DEFAULT_GAIN, iflist, iflock, LOG_ERROR, LOG_WARNING, mkif(), phone_pvt::mode, MODE_DIALTONE, MODE_FXO, MODE_FXS, MODE_IMMEDIATE, MODE_SIGMA, ast_variable::name, ast_variable::next, phone_pvt::next, parse_gain_value(), phone_tech_fxs, prefformat, restart_monitor(), phone_pvt::rxgain, phone_pvt::txgain, and ast_variable::value.

{
   struct ast_config *cfg;
   struct ast_variable *v;
   struct phone_pvt *tmp;
   int mode = MODE_IMMEDIATE;
   int txgain = DEFAULT_GAIN, rxgain = DEFAULT_GAIN; /* default gain 1.0 */
   struct ast_flags config_flags = { 0 };

   if ((cfg = ast_config_load(config, config_flags)) == CONFIG_STATUS_FILEINVALID) {
      ast_log(LOG_ERROR, "Config file %s is in an invalid format.  Aborting.\n", config);
      return AST_MODULE_LOAD_DECLINE;
   }

   /* We *must* have a config file otherwise stop immediately */
   if (!cfg) {
      ast_log(LOG_ERROR, "Unable to load config %s\n", config);
      return AST_MODULE_LOAD_DECLINE;
   }
   if (ast_mutex_lock(&iflock)) {
      /* It's a little silly to lock it, but we mind as well just to be sure */
      ast_log(LOG_ERROR, "Unable to lock interface list???\n");
      return AST_MODULE_LOAD_FAILURE;
   }
   v = ast_variable_browse(cfg, "interfaces");
   while(v) {
      /* Create the interface list */
      if (!strcasecmp(v->name, "device")) {
            tmp = mkif(v->value, mode, txgain, rxgain);
            if (tmp) {
               tmp->next = iflist;
               iflist = tmp;
               
            } else {
               ast_log(LOG_ERROR, "Unable to register channel '%s'\n", v->value);
               ast_config_destroy(cfg);
               ast_mutex_unlock(&iflock);
               __unload_module();
               return AST_MODULE_LOAD_FAILURE;
            }
      } else if (!strcasecmp(v->name, "silencesupression")) {
         silencesupression = ast_true(v->value);
      } else if (!strcasecmp(v->name, "language")) {
         ast_copy_string(language, v->value, sizeof(language));
      } else if (!strcasecmp(v->name, "callerid")) {
         ast_callerid_split(v->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
      } else if (!strcasecmp(v->name, "mode")) {
         if (!strncasecmp(v->value, "di", 2)) 
            mode = MODE_DIALTONE;
         else if (!strncasecmp(v->value, "sig", 3))
            mode = MODE_SIGMA;
         else if (!strncasecmp(v->value, "im", 2))
            mode = MODE_IMMEDIATE;
         else if (!strncasecmp(v->value, "fxs", 3)) {
            mode = MODE_FXS;
            prefformat = 0x01ff0000; /* All non-voice */
         }
         else if (!strncasecmp(v->value, "fx", 2))
            mode = MODE_FXO;
         else
            ast_log(LOG_WARNING, "Unknown mode: %s\n", v->value);
      } else if (!strcasecmp(v->name, "context")) {
         ast_copy_string(context, v->value, sizeof(context));
      } else if (!strcasecmp(v->name, "format")) {
         if (!strcasecmp(v->value, "g729")) {
            prefformat = AST_FORMAT_G729A;
                        } else if (!strcasecmp(v->value, "g723.1")) {
            prefformat = AST_FORMAT_G723_1;
         } else if (!strcasecmp(v->value, "slinear")) {
            if (mode == MODE_FXS)
                prefformat |= AST_FORMAT_SLINEAR;
            else prefformat = AST_FORMAT_SLINEAR;
         } else if (!strcasecmp(v->value, "ulaw")) {
            prefformat = AST_FORMAT_ULAW;
         } else
            ast_log(LOG_WARNING, "Unknown format '%s'\n", v->value);
      } else if (!strcasecmp(v->name, "echocancel")) {
         if (!strcasecmp(v->value, "off")) {
            echocancel = AEC_OFF;
         } else if (!strcasecmp(v->value, "low")) {
            echocancel = AEC_LOW;
         } else if (!strcasecmp(v->value, "medium")) {
            echocancel = AEC_MED;
         } else if (!strcasecmp(v->value, "high")) {
            echocancel = AEC_HIGH;
         } else 
            ast_log(LOG_WARNING, "Unknown echo cancellation '%s'\n", v->value);
      } else if (!strcasecmp(v->name, "txgain")) {
         txgain = parse_gain_value(v->name, v->value);
      } else if (!strcasecmp(v->name, "rxgain")) {
         rxgain = parse_gain_value(v->name, v->value);
      }  
      v = v->next;
   }
   ast_mutex_unlock(&iflock);

   if (mode == MODE_FXS) {
      phone_tech_fxs.capabilities = prefformat;
      cur_tech = &phone_tech_fxs;
   } else
      cur_tech = (struct ast_channel_tech *) &phone_tech;

   /* Make sure we can register our Adtranphone channel type */

   if (ast_channel_register(cur_tech)) {
      ast_log(LOG_ERROR, "Unable to register channel class 'Phone'\n");
      ast_config_destroy(cfg);
      __unload_module();
      return AST_MODULE_LOAD_FAILURE;
   }
   ast_config_destroy(cfg);
   /* And start the monitor for the first time */
   restart_monitor();
   return AST_MODULE_LOAD_SUCCESS;
}
static struct phone_pvt* mkif ( const char *  iface,
int  mode,
int  txgain,
int  rxgain 
) [static, read]

Definition at line 1176 of file chan_phone.c.

References ast_calloc, ast_copy_string(), ast_debug, ast_free, ast_log(), phone_pvt::cid_name, phone_pvt::cid_num, phone_pvt::context, phone_pvt::cpt, phone_pvt::dev, phone_pvt::dialtone, errno, phone_pvt::ext, phone_pvt::fd, phone_pvt::language, phone_pvt::lastformat, phone_pvt::lastinput, LOG_WARNING, phone_pvt::ministate, phone_pvt::mode, MODE_FXO, MODE_FXS, phone_pvt::next, phone_pvt::obuflen, phone_pvt::owner, phone_pvt::rxgain, phone_pvt::silencesupression, and phone_pvt::txgain.

Referenced by load_module().

{
   /* Make a phone_pvt structure for this interface */
   struct phone_pvt *tmp;
   int flags;  
   
   tmp = ast_calloc(1, sizeof(*tmp));
   if (tmp) {
      tmp->fd = open(iface, O_RDWR);
      if (tmp->fd < 0) {
         ast_log(LOG_WARNING, "Unable to open '%s'\n", iface);
         ast_free(tmp);
         return NULL;
      }
      if (mode == MODE_FXO) {
         if (ioctl(tmp->fd, IXJCTL_PORT, PORT_PSTN)) {
            ast_debug(1, "Unable to set port to PSTN\n");
         }
      } else {
         if (ioctl(tmp->fd, IXJCTL_PORT, PORT_POTS)) 
             if (mode != MODE_FXS)
                  ast_debug(1, "Unable to set port to POTS\n");
      }
      ioctl(tmp->fd, PHONE_PLAY_STOP);
      ioctl(tmp->fd, PHONE_REC_STOP);
      ioctl(tmp->fd, PHONE_RING_STOP);
      ioctl(tmp->fd, PHONE_CPT_STOP);
      if (ioctl(tmp->fd, PHONE_PSTN_SET_STATE, PSTN_ON_HOOK))
         ast_debug(1, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n",iface, strerror(errno));
      if (echocancel != AEC_OFF)
         ioctl(tmp->fd, IXJCTL_AEC_START, echocancel);
      if (silencesupression) 
         tmp->silencesupression = 1;
#ifdef PHONE_VAD
      ioctl(tmp->fd, PHONE_VAD, tmp->silencesupression);
#endif
      tmp->mode = mode;
      flags = fcntl(tmp->fd, F_GETFL);
      fcntl(tmp->fd, F_SETFL, flags | O_NONBLOCK);
      tmp->owner = NULL;
      tmp->lastformat = -1;
      tmp->lastinput = -1;
      tmp->ministate = 0;
      memset(tmp->ext, 0, sizeof(tmp->ext));
      ast_copy_string(tmp->language, language, sizeof(tmp->language));
      ast_copy_string(tmp->dev, iface, sizeof(tmp->dev));
      ast_copy_string(tmp->context, context, sizeof(tmp->context));
      tmp->next = NULL;
      tmp->obuflen = 0;
      tmp->dialtone = 0;
      tmp->cpt = 0;
      ast_copy_string(tmp->cid_num, cid_num, sizeof(tmp->cid_num));
      ast_copy_string(tmp->cid_name, cid_name, sizeof(tmp->cid_name));
      tmp->txgain = txgain;
      ioctl(tmp->fd, PHONE_PLAY_VOLUME, tmp->txgain);
      tmp->rxgain = rxgain;
      ioctl(tmp->fd, PHONE_REC_VOLUME, tmp->rxgain);
   }
   return tmp;
}
static int parse_gain_value ( const char *  gain_type,
const char *  value 
) [static]

Definition at line 1280 of file chan_phone.c.

References ast_log(), DEFAULT_GAIN, and LOG_ERROR.

Referenced by load_module().

{
   float gain;

   /* try to scan number */
   if (sscanf(value, "%30f", &gain) != 1)
   {
      ast_log(LOG_ERROR, "Invalid %s value '%s' in '%s' config\n",
         value, gain_type, config);
      return DEFAULT_GAIN;
   }

   /* multiplicate gain by 1.0 gain value */ 
   gain = gain * (float)DEFAULT_GAIN;

   /* percentage? */
   if (value[strlen(value) - 1] == '%')
      return (int)(gain / (float)100);

   return (int)gain;
}
static int phone_answer ( struct ast_channel ast) [static]

Definition at line 457 of file chan_phone.c.

References ast_debug, ast_setstate(), AST_STATE_UP, errno, phone_pvt::fd, phone_pvt::mode, MODE_FXO, ast_channel::name, phone_setup(), ast_channel::rings, and ast_channel::tech_pvt.

{
   struct phone_pvt *p;
   p = ast->tech_pvt;
   /* In case it's a LineJack, take it off hook */
   if (p->mode == MODE_FXO) {
      if (ioctl(p->fd, PHONE_PSTN_SET_STATE, PSTN_OFF_HOOK))
         ast_debug(1, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n", ast->name, strerror(errno));
      else
         ast_debug(1, "Took linejack off hook\n");
   }
   phone_setup(ast);
   ast_debug(1, "phone_answer(%s)\n", ast->name);
   ast->rings = 0;
   ast_setstate(ast, AST_STATE_UP);
   return 0;
}
static int phone_call ( struct ast_channel ast,
char *  dest,
int  timeout 
) [static]

Definition at line 288 of file chan_phone.c.

References ast_channel::_state, AST_CONTROL_RINGING, ast_copy_string(), ast_debug, ast_localtime(), ast_log(), ast_queue_control(), ast_setstate(), AST_STATE_DOWN, AST_STATE_RESERVED, AST_STATE_RINGING, ast_strlen_zero(), ast_tvnow(), ast_channel::connected, DEFAULT_CALLER_ID, ast_channel::fds, ast_party_connected_line::id, IXJ_PHONE_RING_START, LOG_WARNING, phone_pvt::mode, MODE_FXS, ast_party_id::name, ast_channel::name, ast_party_id::number, phone_digit_end(), ast_party_name::str, ast_party_number::str, ast_channel::tech_pvt, ast_tm::tm_hour, ast_tm::tm_mday, ast_tm::tm_min, ast_tm::tm_mon, ast_party_name::valid, and ast_party_number::valid.

{
   struct phone_pvt *p;

   PHONE_CID cid;
   struct timeval UtcTime = ast_tvnow();
   struct ast_tm tm;
   int start;

   ast_localtime(&UtcTime, &tm, NULL);

   memset(&cid, 0, sizeof(PHONE_CID));
    snprintf(cid.month, sizeof(cid.month), "%02d",(tm.tm_mon + 1));
    snprintf(cid.day, sizeof(cid.day),     "%02d", tm.tm_mday);
    snprintf(cid.hour, sizeof(cid.hour),   "%02d", tm.tm_hour);
    snprintf(cid.min, sizeof(cid.min),     "%02d", tm.tm_min);
   /* the standard format of ast->callerid is:  "name" <number>, but not always complete */
   if (!ast->connected.id.name.valid
      || ast_strlen_zero(ast->connected.id.name.str)) {
      strcpy(cid.name, DEFAULT_CALLER_ID);
   } else {
      ast_copy_string(cid.name, ast->connected.id.name.str, sizeof(cid.name));
   }

   if (ast->connected.id.number.valid && ast->connected.id.number.str) {
      ast_copy_string(cid.number, ast->connected.id.number.str, sizeof(cid.number));
   }

   p = ast->tech_pvt;

   if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
      ast_log(LOG_WARNING, "phone_call called on %s, neither down nor reserved\n", ast->name);
      return -1;
   }
   ast_debug(1, "Ringing %s on %s (%d)\n", dest, ast->name, ast->fds[0]);

   start = IXJ_PHONE_RING_START(cid);
   if (start == -1)
      return -1;
   
   if (p->mode == MODE_FXS) {
      char *digit = strchr(dest, '/');
      if (digit)
      {
        digit++;
        while (*digit)
          phone_digit_end(ast, *digit++, 0);
      }
   }
 
   ast_setstate(ast, AST_STATE_RINGING);
   ast_queue_control(ast, AST_CONTROL_RINGING);
   return 0;
}
static void phone_check_exception ( struct phone_pvt i) [static]

Definition at line 924 of file chan_phone.c.

References ast_canmatch_extension(), ast_debug, ast_exists_extension(), AST_MAX_EXTENSION, ast_module_ref(), ast_module_unref(), AST_STATE_RING, ast_verbose(), phone_pvt::cid_num, phone_pvt::context, phone_pvt::cpt, phone_pvt::dialtone, phone_pvt::ext, phone_pvt::fd, phone_pvt::lastformat, phone_pvt::mode, MODE_DIALTONE, MODE_FXS, MODE_IMMEDIATE, MODE_SIGMA, phone_new(), and ast_module_info::self.

Referenced by do_monitor().

{
   int offhook=0;
   char digit[2] = {0 , 0};
   union telephony_exception phonee;
   /* XXX Do something XXX */
#if 0
   ast_debug(1, "Exception!\n");
#endif
   phonee.bytes = ioctl(i->fd, PHONE_EXCEPTION);
   if (phonee.bits.dtmf_ready)  {
      digit[0] = ioctl(i->fd, PHONE_GET_DTMF_ASCII);
      if (i->mode == MODE_DIALTONE || i->mode == MODE_FXS || i->mode == MODE_SIGMA) {
         ioctl(i->fd, PHONE_PLAY_STOP);
         ioctl(i->fd, PHONE_REC_STOP);
         ioctl(i->fd, PHONE_CPT_STOP);
         i->dialtone = 0;
         if (strlen(i->ext) < AST_MAX_EXTENSION - 1)
            strncat(i->ext, digit, sizeof(i->ext) - strlen(i->ext) - 1);
         if ((i->mode != MODE_FXS ||
              !(phonee.bytes = ioctl(i->fd, PHONE_EXCEPTION)) ||
              !phonee.bits.dtmf_ready) &&
             ast_exists_extension(NULL, i->context, i->ext, 1, i->cid_num)) {
            /* It's a valid extension in its context, get moving! */
            phone_new(i, AST_STATE_RING, i->context, NULL);
            /* No need to restart monitor, we are the monitor */
         } else if (!ast_canmatch_extension(NULL, i->context, i->ext, 1, i->cid_num)) {
            /* There is nothing in the specified extension that can match anymore.
               Try the default */
            if (ast_exists_extension(NULL, "default", i->ext, 1, i->cid_num)) {
               /* Check the default, too... */
               phone_new(i, AST_STATE_RING, "default", NULL);
               /* XXX This should probably be justified better XXX */
            }  else if (!ast_canmatch_extension(NULL, "default", i->ext, 1, i->cid_num)) {
               /* It's not a valid extension, give a busy signal */
               ast_debug(1, "%s can't match anything in %s or default\n", i->ext, i->context);
               ioctl(i->fd, PHONE_BUSY);
               i->cpt = 1;
            }
         }
#if 0
         ast_verbose("Extension is %s\n", i->ext);
#endif
      }
   }
   if (phonee.bits.hookstate) {
      offhook = ioctl(i->fd, PHONE_HOOKSTATE);
      if (offhook) {
         if (i->mode == MODE_IMMEDIATE) {
            phone_new(i, AST_STATE_RING, i->context, NULL);
         } else if (i->mode == MODE_DIALTONE) {
            ast_module_ref(ast_module_info->self);
            /* Reset the extension */
            i->ext[0] = '\0';
            /* Play the dialtone */
            i->dialtone++;
            ioctl(i->fd, PHONE_PLAY_STOP);
            ioctl(i->fd, PHONE_PLAY_CODEC, ULAW);
            ioctl(i->fd, PHONE_PLAY_START);
            i->lastformat = -1;
         } else if (i->mode == MODE_SIGMA) {
            ast_module_ref(ast_module_info->self);
            /* Reset the extension */
            i->ext[0] = '\0';
            /* Play the dialtone */
            i->dialtone++;
            ioctl(i->fd, PHONE_DIALTONE);
         }
      } else {
         if (i->dialtone)
            ast_module_unref(ast_module_info->self);
         memset(i->ext, 0, sizeof(i->ext));
         if (i->cpt)
         {
            ioctl(i->fd, PHONE_CPT_STOP);
            i->cpt = 0;
         }
         ioctl(i->fd, PHONE_PLAY_STOP);
         ioctl(i->fd, PHONE_REC_STOP);
         i->dialtone = 0;
         i->lastformat = -1;
      }
   }
   if (phonee.bits.pstn_ring) {
      ast_verbose("Unit is ringing\n");
      phone_new(i, AST_STATE_RING, i->context, NULL);
   }
   if (phonee.bits.caller_id)
      ast_verbose("We have caller ID\n");
   
   
}
static int phone_digit_begin ( struct ast_channel ast,
char  digit 
) [static]

Definition at line 240 of file chan_phone.c.

{
   /* XXX Modify this callback to let Asterisk support controlling the length of DTMF */
   return 0;
}
static int phone_digit_end ( struct ast_channel ast,
char  digit,
unsigned int  duration 
) [static]

Definition at line 246 of file chan_phone.c.

References ast_debug, ast_log(), phone_pvt::fd, phone_pvt::lastformat, LOG_WARNING, and ast_channel::tech_pvt.

Referenced by phone_call().

{
   struct phone_pvt *p;
   int outdigit;
   p = ast->tech_pvt;
   ast_debug(1, "Dialed %c\n", digit);
   switch(digit) {
   case '0':
   case '1':
   case '2':
   case '3':
   case '4':
   case '5':
   case '6':
   case '7':
   case '8':
   case '9':
      outdigit = digit - '0';
      break;
   case '*':
      outdigit = 11;
      break;
   case '#':
      outdigit = 12;
      break;
   case 'f':   /*flash*/
   case 'F':
      ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_ON_HOOK);
      usleep(320000);
      ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_OFF_HOOK);
      p->lastformat = -1;
      return 0;
   default:
      ast_log(LOG_WARNING, "Unknown digit '%c'\n", digit);
      return -1;
   }
   ast_debug(1, "Dialed %d\n", outdigit);
   ioctl(p->fd, PHONE_PLAY_TONE, outdigit);
   p->lastformat = -1;
   return 0;
}
static struct ast_frame * phone_exception ( struct ast_channel ast) [static, read]

Definition at line 489 of file chan_phone.c.

References ast_channel::_state, AST_CONTROL_ANSWER, ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, AST_FRAME_NULL, ast_log(), ast_setstate(), AST_STATE_RINGING, AST_STATE_UP, ast_tv(), ast_verbose(), ast_frame::data, ast_frame::datalen, ast_frame::delivery, phone_pvt::fd, phone_pvt::fr, ast_frame::frametype, ast_frame_subclass::integer, LOG_WARNING, ast_frame::mallocd, phone_pvt::mode, MODE_FXO, ast_frame::offset, phone_setup(), ast_frame::ptr, ast_frame::samples, ast_frame::src, ast_frame::subclass, and ast_channel::tech_pvt.

{
   int res;
   union telephony_exception phonee;
   struct phone_pvt *p = ast->tech_pvt;
   char digit;

   /* Some nice norms */
   p->fr.datalen = 0;
   p->fr.samples = 0;
   p->fr.data.ptr =  NULL;
   p->fr.src = "Phone";
   p->fr.offset = 0;
   p->fr.mallocd=0;
   p->fr.delivery = ast_tv(0,0);
   
   phonee.bytes = ioctl(p->fd, PHONE_EXCEPTION);
   if (phonee.bits.dtmf_ready)  {
      ast_debug(1, "phone_exception(): DTMF\n");
   
      /* We've got a digit -- Just handle this nicely and easily */
      digit =  ioctl(p->fd, PHONE_GET_DTMF_ASCII);
      p->fr.subclass.integer = digit;
      p->fr.frametype = AST_FRAME_DTMF;
      return &p->fr;
   }
   if (phonee.bits.hookstate) {
      ast_debug(1, "Hookstate changed\n");
      res = ioctl(p->fd, PHONE_HOOKSTATE);
      /* See if we've gone on hook, if so, notify by returning NULL */
      ast_debug(1, "New hookstate: %d\n", res);
      if (!res && (p->mode != MODE_FXO))
         return NULL;
      else {
         if (ast->_state == AST_STATE_RINGING) {
            /* They've picked up the phone */
            p->fr.frametype = AST_FRAME_CONTROL;
            p->fr.subclass.integer = AST_CONTROL_ANSWER;
            phone_setup(ast);
            ast_setstate(ast, AST_STATE_UP);
            return &p->fr;
         }  else 
            ast_log(LOG_WARNING, "Got off hook in weird state %d\n", ast->_state);
      }
   }
#if 1
   if (phonee.bits.pstn_ring)
      ast_verbose("Unit is ringing\n");
   if (phonee.bits.caller_id) {
      ast_verbose("We have caller ID\n");
   }
   if (phonee.bits.pstn_wink)
      ast_verbose("Detected Wink\n");
#endif
   /* Strange -- nothing there.. */
   p->fr.frametype = AST_FRAME_NULL;
   p->fr.subclass.integer = 0;
   return &p->fr;
}
static int phone_fixup ( struct ast_channel old,
struct ast_channel new 
) [static]

Definition at line 232 of file chan_phone.c.

References phone_pvt::owner, and ast_channel::tech_pvt.

{
   struct phone_pvt *pvt = old->tech_pvt;
   if (pvt && pvt->owner == old)
      pvt->owner = new;
   return 0;
}
static int phone_hangup ( struct ast_channel ast) [static]

Definition at line 343 of file chan_phone.c.

References ast_debug, ast_log(), ast_module_unref(), ast_setstate(), AST_STATE_DOWN, ast_verb, phone_pvt::cpt, phone_pvt::dialtone, errno, phone_pvt::ext, phone_pvt::fd, phone_pvt::lastformat, phone_pvt::lastinput, LOG_WARNING, phone_pvt::ministate, phone_pvt::mode, MODE_FXO, ast_channel::name, phone_pvt::obuflen, phone_pvt::owner, restart_monitor(), ast_module_info::self, and ast_channel::tech_pvt.

{
   struct phone_pvt *p;
   p = ast->tech_pvt;
   ast_debug(1, "phone_hangup(%s)\n", ast->name);
   if (!ast->tech_pvt) {
      ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
      return 0;
   }
   /* XXX Is there anything we can do to really hang up except stop recording? */
   ast_setstate(ast, AST_STATE_DOWN);
   if (ioctl(p->fd, PHONE_REC_STOP))
      ast_log(LOG_WARNING, "Failed to stop recording\n");
   if (ioctl(p->fd, PHONE_PLAY_STOP))
      ast_log(LOG_WARNING, "Failed to stop playing\n");
   if (ioctl(p->fd, PHONE_RING_STOP))
      ast_log(LOG_WARNING, "Failed to stop ringing\n");
   if (ioctl(p->fd, PHONE_CPT_STOP))
      ast_log(LOG_WARNING, "Failed to stop sounds\n");

   /* If it's an FXO, hang them up */
   if (p->mode == MODE_FXO) {
      if (ioctl(p->fd, PHONE_PSTN_SET_STATE, PSTN_ON_HOOK))
         ast_debug(1, "ioctl(PHONE_PSTN_SET_STATE) failed on %s (%s)\n",ast->name, strerror(errno));
   }

   /* If they're off hook, give a busy signal */
   if (ioctl(p->fd, PHONE_HOOKSTATE)) {
      ast_debug(1, "Got hunghup, giving busy signal\n");
      ioctl(p->fd, PHONE_BUSY);
      p->cpt = 1;
   }
   p->lastformat = -1;
   p->lastinput = -1;
   p->ministate = 0;
   p->obuflen = 0;
   p->dialtone = 0;
   memset(p->ext, 0, sizeof(p->ext));
   ((struct phone_pvt *)(ast->tech_pvt))->owner = NULL;
   ast_module_unref(ast_module_info->self);
   ast_verb(3, "Hungup '%s'\n", ast->name);
   ast->tech_pvt = NULL;
   ast_setstate(ast, AST_STATE_DOWN);
   restart_monitor();
   return 0;
}
static int phone_indicate ( struct ast_channel chan,
int  condition,
const void *  data,
size_t  datalen 
) [static]

Definition at line 204 of file chan_phone.c.

References AST_CONTROL_FLASH, AST_CONTROL_HOLD, AST_CONTROL_SRCUPDATE, AST_CONTROL_UNHOLD, ast_debug, ast_log(), ast_moh_start(), ast_moh_stop(), phone_pvt::fd, phone_pvt::lastformat, LOG_WARNING, ast_channel::name, and ast_channel::tech_pvt.

{
   struct phone_pvt *p = chan->tech_pvt;
   int res=-1;
   ast_debug(1, "Requested indication %d on channel %s\n", condition, chan->name);
   switch(condition) {
   case AST_CONTROL_FLASH:
      ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_ON_HOOK);
      usleep(320000);
      ioctl(p->fd, IXJCTL_PSTN_SET_STATE, PSTN_OFF_HOOK);
         p->lastformat = -1;
         res = 0;
         break;
   case AST_CONTROL_HOLD:
      ast_moh_start(chan, data, NULL);
      break;
   case AST_CONTROL_UNHOLD:
      ast_moh_stop(chan);
      break;
   case AST_CONTROL_SRCUPDATE:
      res = 0;
      break;
   default:
      ast_log(LOG_WARNING, "Condition %d is not supported on channel %s\n", condition, chan->name);
   }
   return res;
}
static void phone_mini_packet ( struct phone_pvt i) [static]

Definition at line 912 of file chan_phone.c.

References ast_log(), errno, phone_pvt::fd, and LOG_WARNING.

Referenced by do_monitor().

{
   int res;
   char buf[1024];
   /* Ignore stuff we read... */
   res = read(i->fd, buf, sizeof(buf));
   if (res < 1) {
      ast_log(LOG_WARNING, "Read returned %d: %s\n", res, strerror(errno));
      return;
   }
}
static struct ast_channel* phone_new ( struct phone_pvt i,
int  state,
char *  cntx,
const char *  linkedid 
) [static, read]

Definition at line 848 of file chan_phone.c.

References ast_channel_alloc(), ast_channel_set_fd(), ast_copy_string(), AST_FORMAT_SLINEAR, ast_hangup(), ast_log(), ast_module_ref(), ast_pbx_start(), AST_STATE_DOWN, AST_STATE_RING, ast_strdup, ast_string_field_set, ast_strlen_zero(), phone_pvt::cid_name, phone_pvt::cid_num, phone_pvt::context, phone_pvt::cpt, cur_tech, phone_pvt::dev, phone_pvt::ext, phone_pvt::fd, phone_pvt::language, LOG_WARNING, phone_pvt::mode, MODE_FXS, phone_pvt::owner, prefformat, and ast_module_info::self.

Referenced by phone_check_exception(), and phone_request().

{
   struct ast_channel *tmp;
   struct phone_codec_data queried_codec;
   tmp = ast_channel_alloc(1, state, i->cid_num, i->cid_name, "", i->ext, i->context, linkedid, 0, "Phone/%s", i->dev + 5);
   if (tmp) {
      tmp->tech = cur_tech;
      ast_channel_set_fd(tmp, 0, i->fd);
      /* XXX Switching formats silently causes kernel panics XXX */
      if (i->mode == MODE_FXS &&
          ioctl(i->fd, PHONE_QUERY_CODEC, &queried_codec) == 0) {
         if (queried_codec.type == LINEAR16)
            tmp->nativeformats =
            tmp->rawreadformat =
            tmp->rawwriteformat =
            AST_FORMAT_SLINEAR;
         else {
            tmp->nativeformats =
            tmp->rawreadformat =
            tmp->rawwriteformat =
            prefformat & ~AST_FORMAT_SLINEAR;
         }
      }
      else {
         tmp->nativeformats = prefformat;
         tmp->rawreadformat = prefformat;
         tmp->rawwriteformat = prefformat;
      }
      /* no need to call ast_setstate: the channel_alloc already did its job */
      if (state == AST_STATE_RING)
         tmp->rings = 1;
      tmp->tech_pvt = i;
      ast_copy_string(tmp->context, cntx, sizeof(tmp->context));
      if (!ast_strlen_zero(i->ext))
         ast_copy_string(tmp->exten, i->ext, sizeof(tmp->exten));
      else
         strcpy(tmp->exten, "s");
      if (!ast_strlen_zero(i->language))
         ast_string_field_set(tmp, language, i->language);

      /* Don't use ast_set_callerid() here because it will
       * generate a NewCallerID event before the NewChannel event */
      if (!ast_strlen_zero(i->cid_num)) {
         tmp->caller.ani.number.valid = 1;
         tmp->caller.ani.number.str = ast_strdup(i->cid_num);
      }

      i->owner = tmp;
      ast_module_ref(ast_module_info->self);
      if (state != AST_STATE_DOWN) {
         if (state == AST_STATE_RING) {
            ioctl(tmp->fds[0], PHONE_RINGBACK);
            i->cpt = 1;
         }
         if (ast_pbx_start(tmp)) {
            ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
            ast_hangup(tmp);
         }
      }
   } else
      ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
   return tmp;
}
static struct ast_frame * phone_read ( struct ast_channel ast) [static, read]

Definition at line 549 of file chan_phone.c.

References ast_clear_flag, AST_FLAG_BLOCKING, AST_FORMAT_AUDIO_MASK, AST_FORMAT_PNG, AST_FORMAT_SLINEAR, ast_frame_byteswap_le, AST_FRAME_IMAGE, AST_FRAME_NULL, AST_FRAME_VIDEO, AST_FRAME_VOICE, AST_FRIENDLY_OFFSET, ast_log(), ast_tv(), phone_pvt::buf, CHECK_BLOCKING, ast_frame_subclass::codec, ast_frame::data, ast_frame::datalen, ast_frame::delivery, errno, phone_pvt::fd, phone_pvt::fr, ast_frame::frametype, phone_pvt::lastinput, LOG_WARNING, ast_frame::mallocd, phone_pvt::mode, MODE_FXS, ast_frame::offset, PHONE_MAX_BUF, ast_frame::ptr, ast_frame::samples, ast_frame::src, ast_frame::subclass, and ast_channel::tech_pvt.

{
   int res;
   struct phone_pvt *p = ast->tech_pvt;
   

   /* Some nice norms */
   p->fr.datalen = 0;
   p->fr.samples = 0;
   p->fr.data.ptr =  NULL;
   p->fr.src = "Phone";
   p->fr.offset = 0;
   p->fr.mallocd=0;
   p->fr.delivery = ast_tv(0,0);

   /* Try to read some data... */
   CHECK_BLOCKING(ast);
   res = read(p->fd, p->buf, PHONE_MAX_BUF);
   ast_clear_flag(ast, AST_FLAG_BLOCKING);
   if (res < 0) {
#if 0
      if (errno == EAGAIN) {
         ast_log(LOG_WARNING, "Null frame received\n");
         p->fr.frametype = AST_FRAME_NULL;
         p->fr.subclass = 0;
         return &p->fr;
      }
#endif
      ast_log(LOG_WARNING, "Error reading: %s\n", strerror(errno));
      return NULL;
   }
   p->fr.data.ptr = p->buf;
   if (p->mode != MODE_FXS)
   switch(p->buf[0] & 0x3) {
   case '0':
   case '1':
      /* Normal */
      break;
   case '2':
   case '3':
      /* VAD/CNG, only send two words */
      res = 4;
      break;
   }
   p->fr.samples = 240;
   p->fr.datalen = res;
   p->fr.frametype = p->lastinput <= AST_FORMAT_AUDIO_MASK ?
                          AST_FRAME_VOICE : 
           p->lastinput <= AST_FORMAT_PNG ? AST_FRAME_IMAGE 
           : AST_FRAME_VIDEO;
   p->fr.subclass.codec = p->lastinput;
   p->fr.offset = AST_FRIENDLY_OFFSET;
   /* Byteswap from little-endian to native-endian */
   if (p->fr.subclass.codec == AST_FORMAT_SLINEAR)
      ast_frame_byteswap_le(&p->fr);
   return &p->fr;
}
static struct ast_channel * phone_request ( const char *  type,
format_t  format,
const struct ast_channel requestor,
void *  data,
int *  cause 
) [static, read]

Definition at line 1237 of file chan_phone.c.

References AST_CAUSE_BUSY, AST_FORMAT_G723_1, AST_FORMAT_G729A, AST_FORMAT_SLINEAR, AST_FORMAT_ULAW, ast_getformatname_multiple(), ast_log(), ast_mutex_lock, ast_mutex_unlock, AST_STATE_DOWN, phone_pvt::context, ast_channel::data, phone_pvt::dev, format, iflist, iflock, ast_channel::linkedid, LOG_ERROR, LOG_NOTICE, phone_pvt::mode, MODE_FXS, name, phone_pvt::next, phone_pvt::owner, phone_new(), and restart_monitor().

{
   format_t oldformat;
   struct phone_pvt *p;
   struct ast_channel *tmp = NULL;
   char *name = data;

   /* Search for an unowned channel */
   if (ast_mutex_lock(&iflock)) {
      ast_log(LOG_ERROR, "Unable to lock interface list???\n");
      return NULL;
   }
   p = iflist;
   while(p) {
      if (p->mode == MODE_FXS ||
          format & (AST_FORMAT_G729A | AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW)) {
          size_t length = strlen(p->dev + 5);
         if (strncmp(name, p->dev + 5, length) == 0 &&
             !isalnum(name[length])) {
             if (!p->owner) {
                     tmp = phone_new(p, AST_STATE_DOWN, p->context, requestor ? requestor->linkedid : NULL);
                     break;
                } else
                     *cause = AST_CAUSE_BUSY;
            }
      }
      p = p->next;
   }
   ast_mutex_unlock(&iflock);
   restart_monitor();
   if (tmp == NULL) {
      oldformat = format;
      format &= (AST_FORMAT_G729A | AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW);
      if (!format) {
         char buf[256];
         ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n", ast_getformatname_multiple(buf, sizeof(buf), oldformat));
         return NULL;
      }
   }
   return tmp;
}
static int phone_send_text ( struct ast_channel ast,
const char *  text 
) [static]

Definition at line 641 of file chan_phone.c.

References phone_write_buf(), and ast_channel::tech_pvt.

{
    int length = strlen(text);
    return phone_write_buf(ast->tech_pvt, text, length, length, 0) == 
           length ? 0 : -1;
}
static int phone_setup ( struct ast_channel ast) [static]

Definition at line 390 of file chan_phone.c.

References AST_FORMAT_G723_1, AST_FORMAT_G729A, AST_FORMAT_SLINEAR, AST_FORMAT_ULAW, ast_getformatname(), ast_log(), phone_pvt::fd, phone_pvt::lastinput, LOG_WARNING, phone_pvt::mode, MODE_FXS, ast_channel::rawreadformat, and ast_channel::tech_pvt.

Referenced by phone_answer(), phone_exception(), and phone_write().

{
   struct phone_pvt *p;
   p = ast->tech_pvt;
   ioctl(p->fd, PHONE_CPT_STOP);
   /* Nothing to answering really, just start recording */
   if (ast->rawreadformat == AST_FORMAT_G729A) {
      /* Prefer g729 */
      ioctl(p->fd, PHONE_REC_STOP);
      if (p->lastinput != AST_FORMAT_G729A) {
         p->lastinput = AST_FORMAT_G729A;
         if (ioctl(p->fd, PHONE_REC_CODEC, G729)) {
            ast_log(LOG_WARNING, "Failed to set codec to g729\n");
            return -1;
         }
      }
        } else if (ast->rawreadformat == AST_FORMAT_G723_1) {
      ioctl(p->fd, PHONE_REC_STOP);
      if (p->lastinput != AST_FORMAT_G723_1) {
         p->lastinput = AST_FORMAT_G723_1;
         if (ioctl(p->fd, PHONE_REC_CODEC, G723_63)) {
            ast_log(LOG_WARNING, "Failed to set codec to g723.1\n");
            return -1;
         }
      }
   } else if (ast->rawreadformat == AST_FORMAT_SLINEAR) {
      ioctl(p->fd, PHONE_REC_STOP);
      if (p->lastinput != AST_FORMAT_SLINEAR) {
         p->lastinput = AST_FORMAT_SLINEAR;
         if (ioctl(p->fd, PHONE_REC_CODEC, LINEAR16)) {
            ast_log(LOG_WARNING, "Failed to set codec to signed linear 16\n");
            return -1;
         }
      }
   } else if (ast->rawreadformat == AST_FORMAT_ULAW) {
      ioctl(p->fd, PHONE_REC_STOP);
      if (p->lastinput != AST_FORMAT_ULAW) {
         p->lastinput = AST_FORMAT_ULAW;
         if (ioctl(p->fd, PHONE_REC_CODEC, ULAW)) {
            ast_log(LOG_WARNING, "Failed to set codec to uLaw\n");
            return -1;
         }
      }
   } else if (p->mode == MODE_FXS) {
      ioctl(p->fd, PHONE_REC_STOP);
      if (p->lastinput != ast->rawreadformat) {
         p->lastinput = ast->rawreadformat;
         if (ioctl(p->fd, PHONE_REC_CODEC, ast->rawreadformat)) {
            ast_log(LOG_WARNING, "Failed to set codec to %s\n", 
               ast_getformatname(ast->rawreadformat));
            return -1;
         }
      }
   } else {
      ast_log(LOG_WARNING, "Can't do format %s\n", ast_getformatname(ast->rawreadformat));
      return -1;
   }
   if (ioctl(p->fd, PHONE_REC_START)) {
      ast_log(LOG_WARNING, "Failed to start recording\n");
      return -1;
   }
   /* set the DTMF times (the default is too short) */
   ioctl(p->fd, PHONE_SET_TONE_ON_TIME, 300);
   ioctl(p->fd, PHONE_SET_TONE_OFF_TIME, 200);
   return 0;
}
static int phone_write ( struct ast_channel ast,
struct ast_frame frame 
) [static]

Definition at line 648 of file chan_phone.c.

References ast_channel::_state, AST_FORMAT_G723_1, AST_FORMAT_G729A, AST_FORMAT_SLINEAR, AST_FORMAT_ULAW, AST_FRAME_IMAGE, AST_FRAME_VOICE, ast_getformatname(), ast_log(), ast_setstate(), AST_STATE_UP, ast_frame_subclass::codec, ast_frame::data, ast_frame::datalen, errno, phone_pvt::fd, ast_frame::frametype, phone_pvt::lastformat, phone_pvt::lastinput, LOG_WARNING, phone_pvt::mode, MODE_FXS, phone_pvt::obuflen, phone_setup(), phone_write_buf(), ast_frame::ptr, phone_pvt::silencesupression, ast_frame::subclass, and ast_channel::tech_pvt.

{
   struct phone_pvt *p = ast->tech_pvt;
   int res;
   int maxfr=0;
   char *pos;
   int sofar;
   int expected;
   int codecset = 0;
   char tmpbuf[4];
   /* Write a frame of (presumably voice) data */
   if (frame->frametype != AST_FRAME_VOICE && p->mode != MODE_FXS) {
      if (frame->frametype != AST_FRAME_IMAGE)
         ast_log(LOG_WARNING, "Don't know what to do with  frame type '%d'\n", frame->frametype);
      return 0;
   }
   if (!(frame->subclass.codec &
      (AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW | AST_FORMAT_G729A)) && 
       p->mode != MODE_FXS) {
      ast_log(LOG_WARNING, "Cannot handle frames in %s format\n", ast_getformatname(frame->subclass.codec));
      return -1;
   }
#if 0
   /* If we're not in up mode, go into up mode now */
   if (ast->_state != AST_STATE_UP) {
      ast_setstate(ast, AST_STATE_UP);
      phone_setup(ast);
   }
#else
   if (ast->_state != AST_STATE_UP) {
      /* Don't try tos end audio on-hook */
      return 0;
   }
#endif   
   if (frame->subclass.codec == AST_FORMAT_G729A) {
      if (p->lastformat != AST_FORMAT_G729A) {
         ioctl(p->fd, PHONE_PLAY_STOP);
         ioctl(p->fd, PHONE_REC_STOP);
         if (ioctl(p->fd, PHONE_PLAY_CODEC, G729)) {
            ast_log(LOG_WARNING, "Unable to set G729 mode\n");
            return -1;
         }
         if (ioctl(p->fd, PHONE_REC_CODEC, G729)) {
            ast_log(LOG_WARNING, "Unable to set G729 mode\n");
            return -1;
         }
         p->lastformat = AST_FORMAT_G729A;
         p->lastinput = AST_FORMAT_G729A;
         /* Reset output buffer */
         p->obuflen = 0;
         codecset = 1;
      }
      if (frame->datalen > 80) {
         ast_log(LOG_WARNING, "Frame size too large for G.729 (%d bytes)\n", frame->datalen);
         return -1;
      }
      maxfr = 80;
        } else if (frame->subclass.codec == AST_FORMAT_G723_1) {
      if (p->lastformat != AST_FORMAT_G723_1) {
         ioctl(p->fd, PHONE_PLAY_STOP);
         ioctl(p->fd, PHONE_REC_STOP);
         if (ioctl(p->fd, PHONE_PLAY_CODEC, G723_63)) {
            ast_log(LOG_WARNING, "Unable to set G723.1 mode\n");
            return -1;
         }
         if (ioctl(p->fd, PHONE_REC_CODEC, G723_63)) {
            ast_log(LOG_WARNING, "Unable to set G723.1 mode\n");
            return -1;
         }
         p->lastformat = AST_FORMAT_G723_1;
         p->lastinput = AST_FORMAT_G723_1;
         /* Reset output buffer */
         p->obuflen = 0;
         codecset = 1;
      }
      if (frame->datalen > 24) {
         ast_log(LOG_WARNING, "Frame size too large for G.723.1 (%d bytes)\n", frame->datalen);
         return -1;
      }
      maxfr = 24;
   } else if (frame->subclass.codec == AST_FORMAT_SLINEAR) {
      if (p->lastformat != AST_FORMAT_SLINEAR) {
         ioctl(p->fd, PHONE_PLAY_STOP);
         ioctl(p->fd, PHONE_REC_STOP);
         if (ioctl(p->fd, PHONE_PLAY_CODEC, LINEAR16)) {
            ast_log(LOG_WARNING, "Unable to set 16-bit linear mode\n");
            return -1;
         }
         if (ioctl(p->fd, PHONE_REC_CODEC, LINEAR16)) {
            ast_log(LOG_WARNING, "Unable to set 16-bit linear mode\n");
            return -1;
         }
         p->lastformat = AST_FORMAT_SLINEAR;
         p->lastinput = AST_FORMAT_SLINEAR;
         codecset = 1;
         /* Reset output buffer */
         p->obuflen = 0;
      }
      maxfr = 480;
   } else if (frame->subclass.codec == AST_FORMAT_ULAW) {
      if (p->lastformat != AST_FORMAT_ULAW) {
         ioctl(p->fd, PHONE_PLAY_STOP);
         ioctl(p->fd, PHONE_REC_STOP);
         if (ioctl(p->fd, PHONE_PLAY_CODEC, ULAW)) {
            ast_log(LOG_WARNING, "Unable to set uLaw mode\n");
            return -1;
         }
         if (ioctl(p->fd, PHONE_REC_CODEC, ULAW)) {
            ast_log(LOG_WARNING, "Unable to set uLaw mode\n");
            return -1;
         }
         p->lastformat = AST_FORMAT_ULAW;
         p->lastinput = AST_FORMAT_ULAW;
         codecset = 1;
         /* Reset output buffer */
         p->obuflen = 0;
      }
      maxfr = 240;
   } else {
      if (p->lastformat != frame->subclass.codec) {
         ioctl(p->fd, PHONE_PLAY_STOP);
         ioctl(p->fd, PHONE_REC_STOP);
         if (ioctl(p->fd, PHONE_PLAY_CODEC, (int) frame->subclass.codec)) {
            ast_log(LOG_WARNING, "Unable to set %s mode\n",
               ast_getformatname(frame->subclass.codec));
            return -1;
         }
         if (ioctl(p->fd, PHONE_REC_CODEC, (int) frame->subclass.codec)) {
            ast_log(LOG_WARNING, "Unable to set %s mode\n",
               ast_getformatname(frame->subclass.codec));
            return -1;
         }
         p->lastformat = frame->subclass.codec;
         p->lastinput = frame->subclass.codec;
         codecset = 1;
         /* Reset output buffer */
         p->obuflen = 0;
      }
      maxfr = 480;
   }
   if (codecset) {
      ioctl(p->fd, PHONE_REC_DEPTH, 3);
      ioctl(p->fd, PHONE_PLAY_DEPTH, 3);
      if (ioctl(p->fd, PHONE_PLAY_START)) {
         ast_log(LOG_WARNING, "Failed to start playback\n");
         return -1;
      }
      if (ioctl(p->fd, PHONE_REC_START)) {
         ast_log(LOG_WARNING, "Failed to start recording\n");
         return -1;
      }
   }
   /* If we get here, we have a frame of Appropriate data */
   sofar = 0;
   pos = frame->data.ptr;
   while(sofar < frame->datalen) {
      /* Write in no more than maxfr sized frames */
      expected = frame->datalen - sofar;
      if (maxfr < expected)
         expected = maxfr;
      /* XXX Internet Phone Jack does not handle the 4-byte VAD frame properly! XXX 
         we have to pad it to 24 bytes still.  */
      if (frame->datalen == 4) {
         if (p->silencesupression) {
            memcpy(tmpbuf, frame->data.ptr, 4);
            expected = 24;
            res = phone_write_buf(p, tmpbuf, expected, maxfr, 0);
         }
         res = 4;
         expected=4;
      } else {
         int swap = 0;
#if __BYTE_ORDER == __BIG_ENDIAN
         if (frame->subclass.codec == AST_FORMAT_SLINEAR)
            swap = 1; /* Swap big-endian samples to little-endian as we copy */
#endif
         res = phone_write_buf(p, pos, expected, maxfr, swap);
      }
      if (res != expected) {
         if ((errno != EAGAIN) && (errno != EINTR)) {
            if (res < 0) 
               ast_log(LOG_WARNING, "Write returned error (%s)\n", strerror(errno));
   /*
    * Card is in non-blocking mode now and it works well now, but there are
    * lot of messages like this. So, this message is temporarily disabled.
    */
#if 0
            else
               ast_log(LOG_WARNING, "Only wrote %d of %d bytes\n", res, frame->datalen);
#endif
            return -1;
         } else /* Pretend it worked */
            res = expected;
      }
      sofar += res;
      pos += res;
   }
   return 0;
}
static int phone_write_buf ( struct phone_pvt p,
const char *  buf,
int  len,
int  frlen,
int  swap 
) [static]

Definition at line 607 of file chan_phone.c.

References ast_log(), ast_swapcopy_samples(), phone_pvt::fd, len(), LOG_WARNING, phone_pvt::obuf, and phone_pvt::obuflen.

Referenced by phone_send_text(), and phone_write().

{
   int res;
   /* Store as much of the buffer as we can, then write fixed frames */
   int space = sizeof(p->obuf) - p->obuflen;
   /* Make sure we have enough buffer space to store the frame */
   if (space < len)
      len = space;
   if (swap)
      ast_swapcopy_samples(p->obuf+p->obuflen, buf, len/2);
   else
      memcpy(p->obuf + p->obuflen, buf, len);
   p->obuflen += len;
   while(p->obuflen > frlen) {
      res = write(p->fd, p->obuf, frlen);
      if (res != frlen) {
         if (res < 1) {
/*
 * Card is in non-blocking mode now and it works well now, but there are
 * lot of messages like this. So, this message is temporarily disabled.
 */
            return 0;
         } else {
            ast_log(LOG_WARNING, "Only wrote %d of %d bytes\n", res, frlen);
         }
      }
      p->obuflen -= frlen;
      /* Move memory if necessary */
      if (p->obuflen) 
         memmove(p->obuf, p->obuf + frlen, p->obuflen);
   }
   return len;
}
static int restart_monitor ( void  ) [static]

Definition at line 1139 of file chan_phone.c.

References ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_pthread_create_background, AST_PTHREADT_NULL, AST_PTHREADT_STOP, do_monitor(), iflock, LOG_ERROR, LOG_WARNING, and monlock.

Referenced by load_module(), phone_hangup(), and phone_request().

{
   /* If we're supposed to be stopped -- stay stopped */
   if (monitor_thread == AST_PTHREADT_STOP)
      return 0;
   if (ast_mutex_lock(&monlock)) {
      ast_log(LOG_WARNING, "Unable to lock monitor\n");
      return -1;
   }
   if (monitor_thread == pthread_self()) {
      ast_mutex_unlock(&monlock);
      ast_log(LOG_WARNING, "Cannot kill myself\n");
      return -1;
   }
   if (monitor_thread != AST_PTHREADT_NULL) {
      if (ast_mutex_lock(&iflock)) {
         ast_mutex_unlock(&monlock);
         ast_log(LOG_WARNING, "Unable to lock the interface list\n");
         return -1;
      }
      monitor = 0;
      while (pthread_kill(monitor_thread, SIGURG) == 0)
         sched_yield();
      pthread_join(monitor_thread, NULL);
      ast_mutex_unlock(&iflock);
   }
   monitor = 1;
   /* Start a new monitor */
   if (ast_pthread_create_background(&monitor_thread, NULL, do_monitor, NULL) < 0) {
      ast_mutex_unlock(&monlock);
      ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
      return -1;
   }
   ast_mutex_unlock(&monlock);
   return 0;
}
static int unload_module ( void  ) [static]

Definition at line 1358 of file chan_phone.c.

References __unload_module().

{
   return __unload_module();
}

Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Linux Telephony API Support" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static]

Definition at line 1479 of file chan_phone.c.

Definition at line 1479 of file chan_phone.c.

char cid_name[AST_MAX_EXTENSION] [static]

Definition at line 152 of file chan_phone.c.

char cid_num[AST_MAX_EXTENSION] [static]

Definition at line 151 of file chan_phone.c.

const char config[] = "phone.conf" [static]

Definition at line 86 of file chan_phone.c.

char context[AST_MAX_EXTENSION] = "default" [static]

Definition at line 89 of file chan_phone.c.

struct ast_channel_tech* cur_tech [static]

Definition at line 202 of file chan_phone.c.

Referenced by phone_new().

int echocancel = AEC_OFF [static]

Definition at line 94 of file chan_phone.c.

ast_mutex_t iflock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } [static]
char language[MAX_LANGUAGE] = "" [static]

Definition at line 92 of file chan_phone.c.

pthread_t monitor_thread = AST_PTHREADT_NULL [static]

Definition at line 112 of file chan_phone.c.

ast_mutex_t monlock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } [static]

Definition at line 105 of file chan_phone.c.

Referenced by __unload_module(), and restart_monitor().

struct ast_channel_tech phone_tech [static]

Definition at line 167 of file chan_phone.c.

Definition at line 184 of file chan_phone.c.

Referenced by load_module().

format_t prefformat = AST_FORMAT_G729A | AST_FORMAT_G723_1 | AST_FORMAT_SLINEAR | AST_FORMAT_ULAW [static]

Definition at line 98 of file chan_phone.c.

Referenced by load_module(), and phone_new().

int silencesupression = 0 [static]

Definition at line 96 of file chan_phone.c.

const char tdesc[] = "Standard Linux Telephony API Driver" [static]

Definition at line 85 of file chan_phone.c.