dial by name in chan_dahdi
authorTzafrir Cohen <tzafrir.cohen@xorcom.com>
Wed, 9 Jun 2010 13:17:43 +0000 (13:17 +0000)
committerTzafrir Cohen <tzafrir.cohen@xorcom.com>
Wed, 9 Jun 2010 13:17:43 +0000 (13:17 +0000)
* chan_dahdi supports dialing configuring and dialing by device file name.
  DAHDI/span-name!local!1 will use /dev/dahdi/span-name/local/1 . Likewise
  it may appear in chan_dahdi.conf as 'channel => span-name!local!1'.
* A new options for chan_dahdi.conf: 'ignore_failed_channels'. Boolean.
  False by default. If set, chan_dahdi will ignore failed 'channel' entries.
  Handy for the above name-based syntax as it does not depend on
  initialization order.
* have my_pri_make_cc_dialstring() only manupulate dial-strings of group
  (gGrR) dialing, which make it lsightly more complicated.

https://reviewboard.asterisk.org/r/535/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@269238 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
channels/chan_dahdi.c
configs/chan_dahdi.conf.sample

diff --git a/CHANGES b/CHANGES
index ebc722a..8a216ba 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -485,6 +485,13 @@ Miscellaneous
    of dynamic parkinglots.
  * chan_dahdi now supports reporting alarms over AMI either by channel or span via
    the reportalarms config option.
+ * chan_dahdi supports dialing configuring and dialing by device file name.
+   DAHDI/span-name!local!1 will use /dev/dahdi/span-name/local/1 . Likewise
+   it may appear in chan_dahdi.conf as 'channel => span-name!local!1'.
+ * A new options for chan_dahdi.conf: 'ignore_failed_channels'. Boolean.
+   False by default. If set, chan_dahdi will ignore failed 'channel' entries.
+   Handy for the above name-based syntax as it does not depend on
+   initialization order.
  * The Realtime dialplan switch now caches entries for 1 second.  This provides a
    significant increase in performance (about 3X) for installations using this switchtype.
 
index e40d116..a71f3ab 100644 (file)
@@ -1240,6 +1240,8 @@ struct dahdi_chan_conf {
 #endif
        struct dahdi_params timing;
        int is_sig_auto; /*!< Use channel signalling from DAHDI? */
+       /*! Continue configuration even if a channel is not there. */
+       int ignore_failed_channels;
 
        /*!
         * \brief The serial port to listen for SMDI data on
@@ -2900,14 +2902,12 @@ static void my_set_rdnis(void *pvt, const char *rdnis)
  *
  * \details
  * original dialstring:
- * DAHDI/[i<span>-]<channel#>[c|r<cadance#>|d][/extension[/options]]
  * DAHDI/[i<span>-](g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]]
  *
  * The modified dialstring will have prefixed the channel-group section
  * with the ISDN channel restriction.
  *
  * buf:
- * DAHDI/i<span>-<channel#>[c|r<cadance#>|d][/extension[/options]]
  * DAHDI/i<span>-(g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]]
  *
  * The routine will check to see if the ISDN channel restriction is already
@@ -2939,8 +2939,9 @@ static void my_pri_make_cc_dialstring(void *priv, char *buf, size_t buf_size)
                snprintf(buf, buf_size, "%s/i%d-", args.tech, pvt->pri->span);
                return;
        }
-       if (args.group[0] == 'i') {
-               /* The ISDN span channel restriction is already in the dialstring. */
+       if (isdigit(args.group[0]) || args.group[0] == 'i' || strchr(args.group, '!')) {
+               /* The ISDN span channel restriction is not needed or already
+                * in the dialstring. */
                ast_copy_string(buf, pvt->dialstring, buf_size);
                return;
        }
@@ -11508,6 +11509,38 @@ static int sigtype_to_signalling(int sigtype)
 
 /*!
  * \internal
+ * \brief Get file name and channel number from (subdir,number)
+ *
+ * \param subdir name of the subdirectory under /dev/dahdi/
+ * \param channel name of device file under /dev/dahdi/<subdir>/
+ * \param path buffer to put file name in
+ * \param pathlen maximal length of path
+ *
+ * \retval minor number of dahdi channel.
+ * \retval -errno on error.
+ */
+static int device2chan(const char *subdir, int channel, char *path, int pathlen)
+{
+       struct stat     stbuf;
+       int             num;
+
+       snprintf(path, pathlen, "/dev/dahdi/%s/%d", subdir, channel);
+       if (stat(path, &stbuf) < 0) {
+               ast_log(LOG_ERROR, "stat(%s) failed: %s\n", path, strerror(errno));
+               return -errno;
+       }
+       if (!S_ISCHR(stbuf.st_mode)) {
+               ast_log(LOG_ERROR, "%s: Not a character device file\n", path);
+               return -EINVAL;
+       }
+       num = minor(stbuf.st_rdev);
+       ast_log(LOG_DEBUG, "%s -> %d\n", path, num);
+       return num;
+
+}
+
+/*!
+ * \internal
  * \brief Initialize/create a channel interface.
  *
  * \param channel Channel interface number to initialize/create.
@@ -12686,6 +12719,7 @@ static struct dahdi_pvt *determine_starting_point(const char *data, struct dahdi
        int x;
        int res = 0;
        struct dahdi_pvt *p;
+       char *subdir = NULL;
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(group);     /* channel/group token */
                //AST_APP_ARG(ext);     /* extension token */
@@ -12696,8 +12730,9 @@ static struct dahdi_pvt *determine_starting_point(const char *data, struct dahdi
        /*
         * data is ---v
         * Dial(DAHDI/pseudo[/extension[/options]])
-        * Dial(DAHDI/[i<span>-]<channel#>[c|r<cadance#>|d][/extension[/options]])
+        * Dial(DAHDI/<channel#>[c|r<cadance#>|d][/extension[/options]])
         * Dial(DAHDI/[i<span>-](g|G|r|R)<group#(0-63)>[c|r<cadance#>|d][/extension[/options]])
+        * Dial(DAHDI/<subdir>!<channel#>[c|r<cadance#>|d][/extension[/options]])
         *
         * i - ISDN span channel restriction.
         *     Used by CC to ensure that the CC recall goes out the same span.
@@ -12728,7 +12763,16 @@ static struct dahdi_pvt *determine_starting_point(const char *data, struct dahdi
        memset(param, 0, sizeof(*param));
        param->channelmatch = -1;
 
-       if (args.group[0] == 'i') {
+       if (strchr(args.group, '!') != NULL) {
+               char *prev = args.group;
+               while ((s = strchr(prev, '!')) != NULL) {
+                       *s++ = '/';
+                       prev = s;
+               }
+               *(prev - 1) = '\0';
+               subdir = args.group;
+               args.group = prev;
+       } else if (args.group[0] == 'i') {
                /* Extract the ISDN span channel restriction specifier. */
                res = sscanf(args.group + 1, "%30d", &x);
                if (res < 1) {
@@ -12796,6 +12840,24 @@ static struct dahdi_pvt *determine_starting_point(const char *data, struct dahdi
                                param->channelmatch = x;
                        }
                }
+               if (subdir) {
+                       char path[PATH_MAX];
+                       struct stat stbuf;
+
+                       snprintf(path, sizeof(path), "/dev/dahdi/%s/%d",
+                                       subdir, param->channelmatch);
+                       if (stat(path, &stbuf) < 0) {
+                               ast_log(LOG_WARNING, "stat(%s) failed: %s\n",
+                                               path, strerror(errno));
+                               return NULL;
+                       }
+                       if (!S_ISCHR(stbuf.st_mode)) {
+                               ast_log(LOG_ERROR, "%s: Not a character device file\n",
+                                               path);
+                               return NULL;
+                       }
+                       param->channelmatch = minor(stbuf.st_rdev);
+               }
 
                p = iflist;
        }
@@ -15807,9 +15869,33 @@ static int unload_module(void)
        return __unload_module();
 }
 
+static void string_replace(char *str, int char1, int char2)
+{
+       for (; *str; str++) {
+               if (*str == char1) {
+                       *str = char2;
+               }
+       }
+}
+
+static char *parse_spanchan(char *chanstr, char **subdir)
+{
+       char *p;
+
+       if ((p = strrchr(chanstr, '!')) == NULL) {
+               *subdir = NULL;
+               return chanstr;
+       }
+       *p++ = '\0';
+       string_replace(chanstr, '!', '/');
+       *subdir = chanstr;
+       return p;
+}
+
 static int build_channels(struct dahdi_chan_conf *conf, const char *value, int reload, int lineno, int *found_pseudo)
 {
        char *c, *chan;
+       char *subdir;
        int x, start, finish;
        struct dahdi_pvt *tmp;
 
@@ -15819,6 +15905,7 @@ static int build_channels(struct dahdi_chan_conf *conf, const char *value, int r
        }
 
        c = ast_strdupa(value);
+       c = parse_spanchan(c, &subdir);
 
        while ((chan = strsep(&c, ","))) {
                if (sscanf(chan, "%30d-%30d", &start, &finish) == 2) {
@@ -15842,13 +15929,30 @@ static int build_channels(struct dahdi_chan_conf *conf, const char *value, int r
                }
 
                for (x = start; x <= finish; x++) {
-                       tmp = mkintf(x, conf, reload);
+                       char fn[PATH_MAX];
+                       int real_channel = x;
+
+                       if (!ast_strlen_zero(subdir)) {
+                               real_channel = device2chan(subdir, x, fn, sizeof(fn));
+                               if (real_channel < 0) {
+                                       if (conf->ignore_failed_channels) {
+                                               ast_log(LOG_WARNING, "Failed configuring %s!%d, (got %d). But moving on to others.\n",
+                                                               subdir, x, real_channel);
+                                               continue;
+                                       } else {
+                                               ast_log(LOG_ERROR, "Failed configuring %s!%d, (got %d).\n",
+                                                               subdir, x, real_channel);
+                                               return -1;
+                                       }
+                               }
+                       }
+                       tmp = mkintf(real_channel, conf, reload);
 
                        if (tmp) {
-                               ast_verb(3, "%s channel %d, %s signalling\n", reload ? "Reconfigured" : "Registered", x, sig2str(tmp->sig));
+                               ast_verb(3, "%s channel %d, %s signalling\n", reload ? "Reconfigured" : "Registered", real_channel, sig2str(tmp->sig));
                        } else {
                                ast_log(LOG_ERROR, "Unable to %s channel '%s'\n",
-                                       (reload == 1) ? "reconfigure" : "register", value);
+                                               (reload == 1) ? "reconfigure" : "register", value);
                                return -1;
                        }
                }
@@ -15939,9 +16043,17 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                                ast_log(LOG_WARNING, "Channel '%s' ignored.\n", v->value);
                                continue;
                        }
-                       if (build_channels(confp, v->value, reload, v->lineno, &found_pseudo))
+                       if (build_channels(confp, v->value, reload, v->lineno, &found_pseudo)) {
+                               if (confp->ignore_failed_channels) {
+                                       ast_log(LOG_WARNING, "Channel '%s' failure ignored: ignore_failed_channels.\n", v->value);
+                                       continue;
+                               } else {
                                        return -1;
+                               }
+                       }
                        ast_log(LOG_DEBUG, "Channel '%s' configured.\n", v->value);
+               } else if (!strcasecmp(v->name, "ignore_failed_channels")) {
+                       confp->ignore_failed_channels = ast_true(v->value);
                } else if (!strcasecmp(v->name, "buffers")) {
                        if (parse_buffers_policy(v->value, &confp->chan.buf_no, &confp->chan.buf_policy)) {
                                ast_log(LOG_WARNING, "Using default buffer policy.\n");
index 0187555..00df580 100644 (file)
@@ -915,6 +915,15 @@ pickupgroup=1
 ;minunused=2
 ;minidle=1
 ;
+;
+; ignore_failed_channels: Continue even if some channels failed to configure.
+; False by default, as if even a single channel failed to configure, it might
+; mean other channels are misplaced and having them work may not be a good
+; idea. If enabled (set to true), chan_dahdi will nevertheless attempt to
+; configure other channels rather than giving up. This normally makes sense
+; only if you use names (<subdir>!<number>) for DAHDI channels.
+;ignore_failed_channels = true
+;
 ; Configure jitter buffers in DAHDI (each one is 20ms, default is 4)
 ; This is set globally, rather than per-channel.
 ;
@@ -1027,8 +1036,24 @@ pickupgroup=1
 ; signalling = pri_cpe
 ; group = 2
 ; channel => 1-23
-
 ;
+; Alternatively, the number of the channel may be replaced with a relative
+; path to a device file under /dev/dahdi . The final element of that file
+; must be a number, though. The directory separator is '!', as we can't
+; use '/' in a dial string. So if we have
+;
+;   /dev/dahdi/span-name/pstn/00/1
+;   /dev/dahdi/span-name/pstn/00/2
+;   /dev/dahdi/span-name/pstn/00/3
+;   /dev/dahdi/span-name/pstn/00/4
+;
+; we could use:
+;channel => span-name!pstn!00!1-4
+;
+; or:
+;channel => span-name!pstn!00!1,2,3,4
+;
+; See also ignore_failed_channels above.
 
 ;  Used for distinctive ring support for x100p.
 ;  You can see the dringX patterns is to set any one of the dringXcontext fields