diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 8ce700c2f059c1831ad1cadb5bed97e891b3dfe7..b694ecf9455cbd815b1c53ae3cc0ec7cc357b156 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -461,6 +461,8 @@ static const char config[] = "chan_dahdi.conf"; static int num_cadence = 4; static int user_has_defined_cadences = 0; +static int has_pseudo; + static struct dahdi_ring_cadence cadences[NUM_CADENCE_MAX] = { { { 125, 125, 2000, 4000 } }, /*!< Quick chirp followed by normal ring */ { { 250, 250, 500, 1000, 250, 250, 500, 4000 } }, /*!< British style ring */ @@ -817,6 +819,18 @@ struct dahdi_chan_conf { * \note Set from the "smdiport" string read in from chan_dahdi.conf */ char smdi_port[SMDI_MAX_FILENAME_LEN]; + + /*! + * \brief Don't create channels below this number + * \note by default is 0 (no limit) + */ + int wanted_channels_start; + + /*! + * \brief Don't create channels above this number (infinity by default) + * \note by default is 0 (special value that means "no limit"). + */ + int wanted_channels_end; }; /*! returns a new dahdi_chan_conf with default values (by-value) */ @@ -2633,7 +2647,7 @@ static int sig_pri_tone_to_dahditone(enum sig_pri_tone tone) #endif /* defined(HAVE_PRI) */ #if defined(HAVE_PRI) -static int pri_destroy_dchan(struct sig_pri_span *pri); +static void pri_destroy_span(struct sig_pri_span *pri); static void my_handle_dchan_exception(struct sig_pri_span *pri, int index) { @@ -2663,7 +2677,7 @@ static void my_handle_dchan_exception(struct sig_pri_span *pri, int index) pri_event_noalarm(pri, index, 0); break; case DAHDI_EVENT_REMOVED: - pri_destroy_dchan(pri); + pri_destroy_span(pri); break; default: break; @@ -10736,29 +10750,121 @@ static int mwi_send_process_event(struct dahdi_pvt * pvt, int event) return handled; } -/* destroy a DAHDI channel, identified by its number */ -static int dahdi_destroy_channel_bynum(int channel) +/* destroy a range DAHDI channels, identified by their number */ +static void dahdi_destroy_channel_range(int start, int end) { struct dahdi_pvt *cur; + struct dahdi_pvt *next; + int destroyed_first = 0; + int destroyed_last = 0; ast_mutex_lock(&iflock); - for (cur = iflist; cur; cur = cur->next) { - if (cur->channel == channel) { + ast_debug(1, "range: %d-%d\n", start, end); + for (cur = iflist; cur; cur = next) { + next = cur->next; + if (cur->channel >= start && cur->channel <= end) { int x = DAHDI_FLASH; + if (cur->channel > destroyed_last) { + destroyed_last = cur->channel; + } + if (destroyed_first < 1 || cur->channel < destroyed_first) { + destroyed_first = cur->channel; + } + ast_debug(3, "Destroying %d\n", cur->channel); /* important to create an event for dahdi_wait_event to register so that all analog_ss_threads terminate */ ioctl(cur->subs[SUB_REAL].dfd, DAHDI_HOOK, &x); destroy_channel(cur, 1); - ast_mutex_unlock(&iflock); ast_module_unref(ast_module_info->self); - return RESULT_SUCCESS; } } ast_mutex_unlock(&iflock); - return RESULT_FAILURE; + if (destroyed_first > start || destroyed_last < end) { + ast_debug(1, "Asked to destroy %d-%d, destroyed %d-%d,\n", + start, end, destroyed_first, destroyed_last); + } +} + +static int setup_dahdi(int reload); +static int setup_dahdi_int(int reload, struct dahdi_chan_conf *default_conf, struct dahdi_chan_conf *base_conf, struct dahdi_chan_conf *conf); + +/*! + * \internal + * \brief create a range of new DAHDI channels + * + * \param start first channel in the range + * \param end last channel in the range + * + * \retval RESULT_SUCCESS on success. + * \retval RESULT_FAILURE on error. + */ +static int dahdi_create_channel_range(int start, int end) +{ + struct dahdi_pvt *cur; + struct dahdi_chan_conf default_conf = dahdi_chan_conf_default(); + struct dahdi_chan_conf base_conf = dahdi_chan_conf_default(); + struct dahdi_chan_conf conf = dahdi_chan_conf_default(); + int i, x; + int ret = RESULT_FAILURE; /* be pessimistic */ + + ast_debug(1, "channel range caps: %d - %d\n", start, end); + ast_mutex_lock(&iflock); + for (cur = iflist; cur; cur = cur->next) { + if (cur->channel >= start && cur->channel <= end) { + ast_log(LOG_ERROR, + "channel range %d-%d is occupied\n", + start, end); + goto out; + } + } + for (x = 0; x < NUM_SPANS; x++) { +#ifdef HAVE_PRI + struct dahdi_pri *pri = pris + x; + + if (!pris[x].pri.pvts[0]) { + break; + } + for (i = 0; i < SIG_PRI_NUM_DCHANS; i++) { + int channo = pri->dchannels[i]; + + if (!channo) { + break; + } + if (!pri->pri.fds[i]) { + break; + } + if (channo >= start && channo <= end) { + ast_log(LOG_ERROR, + "channel range %d-%d is occupied by span %d\n", + start, end, x + 1); + goto out; + } + } +#endif + } + if (!default_conf.chan.cc_params || !base_conf.chan.cc_params || + !conf.chan.cc_params) { + goto out; + } + default_conf.wanted_channels_start = start; + base_conf.wanted_channels_start = start; + conf.wanted_channels_start = start; + default_conf.wanted_channels_end = end; + base_conf.wanted_channels_end = end; + conf.wanted_channels_end = end; + if (setup_dahdi_int(0, &default_conf, &base_conf, &conf) == 0) { + ret = RESULT_SUCCESS; + } +out: + ast_cc_config_params_destroy(default_conf.chan.cc_params); + ast_cc_config_params_destroy(base_conf.chan.cc_params); + ast_cc_config_params_destroy(conf.chan.cc_params); + ast_mutex_unlock(&iflock); + return ret; } + static struct dahdi_pvt *handle_init_event(struct dahdi_pvt *i, int event) { int res; @@ -11124,11 +11230,7 @@ static void *do_monitor(void *data) doomed = NULL; for (i = iflist;; i = i->next) { if (doomed) { - int res; - res = dahdi_destroy_channel_bynum(doomed->channel); - if (res != RESULT_SUCCESS) { - ast_log(LOG_WARNING, "Couldn't find channel to destroy, hopefully another destroy operation just happened.\n"); - } + dahdi_destroy_channel_range(doomed->channel, doomed->channel); doomed = NULL; } if (!i) { @@ -13574,10 +13676,21 @@ static int prepare_pri(struct dahdi_pri *pri) for (i = 0; i < SIG_PRI_NUM_DCHANS; i++) { if (!pri->dchannels[i]) break; + if (pri->pri.fds[i] >= 0) { + /* A partial range addition. Not a complete setup. */ + break; + } pri->pri.fds[i] = open("/dev/dahdi/channel", O_RDWR); + if ((pri->pri.fds[i] < 0)) { + ast_log(LOG_ERROR, "Unable to open D-channel (fd=%d) (%s)\n", + pri->pri.fds[i], strerror(errno)); + return -1; + } x = pri->dchannels[i]; - if ((pri->pri.fds[i] < 0) || (ioctl(pri->pri.fds[i],DAHDI_SPECIFY,&x) == -1)) { - ast_log(LOG_ERROR, "Unable to open D-channel %d (%s)\n", x, strerror(errno)); + res = ioctl(pri->pri.fds[i], DAHDI_SPECIFY, &x); + if (res) { + dahdi_close_pri_fd(pri, i); + ast_log(LOG_ERROR, "Unable to SPECIFY channel %d (%s)\n", x, strerror(errno)); return -1; } memset(&p, 0, sizeof(p)); @@ -13978,22 +14091,44 @@ static char *handle_pri_show_spans(struct ast_cli_entry *e, int cmd, struct ast_ * * \param pri the pri span * - * \return TRUE if the span was valid and we attempted destroying. - * * Shuts down a span and destroys its D-Channel. Further destruction * of the B-channels using dahdi_destroy_channel() would probably be required * for the B-Channels. */ -static int pri_destroy_dchan(struct sig_pri_span *pri) +static void pri_destroy_span(struct sig_pri_span *pri) { int i; + int res; + int cancel_code; struct dahdi_pri* dahdi_pri; + pthread_t master = pri->master; - if (!pri->master || (pri->master == AST_PTHREADT_NULL)) { - return 0; + if (!master || (master == AST_PTHREADT_NULL)) { + return; + } + ast_debug(2, "About to destroy DAHDI channels of span %d.\n", pri->span); + for (i = 0; i < pri->numchans; i++) { + int channel; + struct sig_pri_chan *pvt = pri->pvts[i]; + + if (!pvt) { + continue; + } + channel = pvt->channel; + ast_debug(2, "About to destroy B-channel %d.\n", channel); + dahdi_destroy_channel_range(channel, channel); } - pthread_cancel(pri->master); - pthread_join(pri->master, NULL); + + cancel_code = pthread_cancel(master); + ast_debug(4, + "Waiting to join thread of span %d " + "with pid=%p cancel_code=%d\n", + pri->span, (void *)master, cancel_code); + res = pthread_join(master, NULL); + if (res != 0) { + ast_log(LOG_NOTICE, "pthread_join failed: %d\n", res); + } + pri->master = AST_PTHREADT_NULL; /* The 'struct dahdi_pri' that contains our 'struct sig_pri_span' */ dahdi_pri = container_of(pri, struct dahdi_pri, pri); @@ -14001,17 +14136,16 @@ static int pri_destroy_dchan(struct sig_pri_span *pri) ast_debug(4, "closing pri_fd %d\n", i); dahdi_close_pri_fd(dahdi_pri, i); } - pri->pri = NULL; + sig_pri_init_pri(pri); ast_debug(1, "PRI span %d destroyed\n", pri->span); - return 1; } static char *handle_pri_destroy_span(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { int span; - int i; int res; + struct sig_pri_span *pri; switch (cmd) { case CLI_INIT: @@ -14035,25 +14169,13 @@ static char *handle_pri_destroy_span(struct ast_cli_entry *e, int cmd, a->argv[3], 1, NUM_SPANS); return CLI_SUCCESS; } - if (!pris[span - 1].pri.pri) { + pri = &pris[span - 1].pri; + if (!pri->pri) { ast_cli(a->fd, "No PRI running on span %d\n", span); return CLI_SUCCESS; } - for (i = 0; i < pris[span - 1].pri.numchans; i++) { - int channel; - struct sig_pri_chan *pvt = pris[span - 1].pri.pvts[i]; - - if (!pvt) { - continue; - } - channel = pvt->channel; - ast_debug(2, "About to destroy B-channel %d.\n", channel); - dahdi_destroy_channel_bynum(channel); - } - ast_debug(2, "About to destroy D-channel of span %d.\n", span); - pri_destroy_dchan(&pris[span - 1].pri); - + pri_destroy_span(pri); return CLI_SUCCESS; } @@ -14505,26 +14627,97 @@ static struct ast_cli_entry dahdi_mfcr2_cli[] = { #endif /* HAVE_OPENR2 */ -static char *dahdi_destroy_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +static char *dahdi_destroy_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { - int channel; - int ret; + int start; + int end; switch (cmd) { case CLI_INIT: - e->command = "dahdi destroy channel"; + e->command = "dahdi destroy channels"; e->usage = - "Usage: dahdi destroy channel <chan num>\n" + "Usage: dahdi destroy channels <from_channel> [<to_channel>]\n" " DON'T USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING. Immediately removes a given channel, whether it is in use or not\n"; return NULL; case CLI_GENERATE: return NULL; } - if (a->argc != 4) + if ((a->argc < 4) || a->argc > 5) { return CLI_SHOWUSAGE; + } + start = atoi(a->argv[3]); + if (start < 1) { + ast_cli(a->fd, "Invalid starting channel number %s.\n", + a->argv[4]); + return CLI_FAILURE; + } + if (a->argc == 5) { + end = atoi(a->argv[4]); + if (end < 1) { + ast_cli(a->fd, "Invalid ending channel number %s.\n", + a->argv[4]); + return CLI_FAILURE; + } + } else { + end = start; + } - channel = atoi(a->argv[3]); - ret = dahdi_destroy_channel_bynum(channel); - return ( RESULT_SUCCESS == ret ) ? CLI_SUCCESS : CLI_FAILURE; + if (end < start) { + ast_cli(a->fd, + "range end (%d) is smaller than range start (%d)\n", + end, start); + return CLI_FAILURE; + } + dahdi_destroy_channel_range(start, end); + return CLI_SUCCESS; +} + +static char *dahdi_create_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int start; + int end; + int ret; + + switch (cmd) { + case CLI_INIT: + e->command = "dahdi create channels"; + e->usage = "Usage: dahdi create channels <from> [<to>] - a range of channels\n" + " dahdi create channels new - add channels not yet created\n" + "For ISDN and SS7 the range should include complete spans.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + if ((a->argc < 4) || a->argc > 5) { + return CLI_SHOWUSAGE; + } + if (a->argc == 4 && !strcmp(a->argv[3], "new")) { + ret = dahdi_create_channel_range(0, 0); + return (RESULT_SUCCESS == ret) ? CLI_SUCCESS : CLI_FAILURE; + } + start = atoi(a->argv[3]); + if (start <= 0) { + ast_cli(a->fd, "Invalid starting channel number '%s'.\n", + a->argv[3]); + return CLI_FAILURE; + } + if (a->argc == 5) { + end = atoi(a->argv[4]); + if (end <= 0) { + ast_cli(a->fd, "Invalid ending channel number '%s'.\n", + a->argv[4]); + return CLI_FAILURE; + } + } else { + end = start; + } + if (end < start) { + ast_cli(a->fd, + "range end (%d) is smaller than range start (%d)\n", + end, start); + return CLI_FAILURE; + } + ret = dahdi_create_channel_range(start, end); + return (RESULT_SUCCESS == ret) ? CLI_SUCCESS : CLI_FAILURE; } static void dahdi_softhangup_all(void) @@ -14555,7 +14748,6 @@ retry: ast_mutex_unlock(&iflock); } -static int setup_dahdi(int reload); static int dahdi_restart(void) { #if defined(HAVE_PRI) || defined(HAVE_SS7) @@ -15332,7 +15524,8 @@ static struct ast_cli_entry dahdi_cli[] = { AST_CLI_DEFINE(handle_dahdi_show_cadences, "List cadences"), AST_CLI_DEFINE(dahdi_show_channels, "Show active DAHDI channels"), AST_CLI_DEFINE(dahdi_show_channel, "Show information on a channel"), - AST_CLI_DEFINE(dahdi_destroy_channel, "Destroy a channel"), + AST_CLI_DEFINE(dahdi_destroy_channels, "Destroy channels"), + AST_CLI_DEFINE(dahdi_create_channels, "Create channels"), AST_CLI_DEFINE(dahdi_restart_cmd, "Fully restart DAHDI channels"), AST_CLI_DEFINE(dahdi_show_status, "Show all DAHDI cards status"), AST_CLI_DEFINE(dahdi_show_version, "Show the DAHDI version in use"), @@ -16359,7 +16552,7 @@ static char *parse_spanchan(char *chanstr, char **subdir) return p; } -static int build_channels(struct dahdi_chan_conf *conf, const char *value, int reload, int lineno, int *found_pseudo) +static int build_channels(struct dahdi_chan_conf *conf, const char *value, int reload, int lineno) { char *c, *chan; char *subdir; @@ -16382,8 +16575,6 @@ static int build_channels(struct dahdi_chan_conf *conf, const char *value, int r finish = start; } else if (!strcasecmp(chan, "pseudo")) { finish = start = CHAN_PSEUDO; - if (found_pseudo) - *found_pseudo = 1; } else { ast_log(LOG_ERROR, "Syntax error parsing '%s' at '%s'\n", value, chan); return -1; @@ -16413,6 +16604,12 @@ static int build_channels(struct dahdi_chan_conf *conf, const char *value, int r } } } + if (conf->wanted_channels_start && + (real_channel < conf->wanted_channels_start || + real_channel > conf->wanted_channels_end) + ) { + continue; + } tmp = mkintf(real_channel, conf, reload); if (tmp) { @@ -16422,6 +16619,9 @@ static int build_channels(struct dahdi_chan_conf *conf, const char *value, int r (reload == 1) ? "reconfigure" : "register", value); return -1; } + if (real_channel == CHAN_PSEUDO) { + has_pseudo = 1; + } } } @@ -16608,7 +16808,6 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct { struct dahdi_pvt *tmp; int y; - int found_pseudo = 0; struct ast_variable *dahdichan = NULL; for (; v; v = v->next) { @@ -16621,7 +16820,7 @@ 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)) { if (confp->ignore_failed_channels) { ast_log(LOG_WARNING, "Channel '%s' failure ignored: ignore_failed_channels.\n", v->value); continue; @@ -17754,8 +17953,7 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct if (dahdichan) { /* Process the deferred dahdichan value. */ - if (build_channels(confp, dahdichan->value, reload, dahdichan->lineno, - &found_pseudo)) { + if (build_channels(confp, dahdichan->value, reload, dahdichan->lineno)) { if (confp->ignore_failed_channels) { ast_log(LOG_WARNING, "Dahdichan '%s' failure ignored: ignore_failed_channels.\n", @@ -17778,7 +17976,7 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct /*< \todo why check for the pseudo in the per-channel section. * Any actual use for manual setup of the pseudo channel? */ - if (!found_pseudo && reload != 1 && !(options & PROC_DAHDI_OPT_NOCHAN)) { + if (!has_pseudo && reload != 1 && !(options & PROC_DAHDI_OPT_NOCHAN)) { /* use the default configuration for a channel, so that any settings from real configured channels don't "leak" into the pseudo channel config @@ -17792,6 +17990,7 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct } if (tmp) { ast_verb(3, "Automatically generated pseudo channel\n"); + has_pseudo = 1; } else { ast_log(LOG_WARNING, "Unable to register pseudo channel!\n"); } @@ -18071,7 +18270,8 @@ static int setup_dahdi_int(int reload, struct dahdi_chan_conf *default_conf, str if (reload != 1) { int x; for (x = 0; x < NUM_SPANS; x++) { - if (pris[x].pri.pvts[0]) { + if (pris[x].pri.pvts[0] && + pris[x].pri.master == AST_PTHREADT_NULL) { prepare_pri(pris + x); if (sig_pri_start_pri(&pris[x].pri)) { ast_log(LOG_ERROR, "Unable to start D-channel on span %d\n", x + 1);