Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:51 2009

## Asterisk developer's documentation

# chan_nbs.c

Go to the documentation of this file.

    00001 /*
    00002  * Asterisk -- An open source telephony toolkit.
    00003  *
    00004  * Copyright (C) 1999 - 2005, Digium, Inc.
    00005  *
    00006  * Mark Spencer <markster@digium.com>
    00007  *
    00008  * See http://www.asterisk.org for more information about
    00009  * the Asterisk project. Please do not directly contact
    00010  * any of the maintainers of this project for assistance;
    00011  * the project provides a web site, mailing lists and IRC
    00012  * channels for your use.
    00013  *
    00014  * This program is free software, distributed under the terms of
    00015  * the GNU General Public License Version 2. See the LICENSE file
    00016  * at the top of the source tree.
    00017  */
    00018
    00019 /*! \file
    00020  *
    00021  * \brief Network broadcast sound support channel driver
    00022  *
    00023  * \author Mark Spencer <markster@digium.com>
    00024  *
    00025  * \ingroup channel_drivers
    00026  */
    00027
    00028 #include <stdio.h>
    00029 #include <string.h>
    00030 #include <sys/socket.h>
    00031 #include <sys/time.h>
    00032 #include <errno.h>
    00033 #include <unistd.h>
    00034 #include <stdlib.h>
    00035 #include <arpa/inet.h>
    00036 #include <fcntl.h>
    00037 #include <sys/ioctl.h>
    00038 #include <nbs.h>
    00039
    00040 #include "asterisk.h"
    00041
    00042 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
    00043
    00044 #include "asterisk/lock.h"
    00045 #include "asterisk/channel.h"
    00046 #include "asterisk/config.h"
    00047 #include "asterisk/logger.h"
    00048 #include "asterisk/module.h"
    00049 #include "asterisk/pbx.h"
    00050 #include "asterisk/options.h"
    00051 #include "asterisk/utils.h"
    00052
    00053 static const char desc[] = "Network Broadcast Sound Support";
    00054 static const char type[] = "NBS";
    00055 static const char tdesc[] = "Network Broadcast Sound Driver";
    00056
    00057 static int usecnt =0;
    00058
    00059 /* Only linear is allowed */
    00060 static int prefformat = AST_FORMAT_SLINEAR;
    00061
    00062 AST_MUTEX_DEFINE_STATIC(usecnt_lock);
    00063
    00064 static char context[AST_MAX_EXTENSION] = "default";
    00065
    00066 /* NBS creates private structures on demand */
    00067
    00068 struct nbs_pvt {
    00069    NBS *nbs;
    00070    struct ast_channel *owner;    /* Channel we belong to, possibly NULL */
    00071    char app[16];              /* Our app */
    00072    char stream[80];           /* Our stream */
    00073    struct ast_frame fr;       /* "null" frame */
    00074 };
    00075
    00076 static struct ast_channel *nbs_request(const char *type, int format, void *data, int *cause);
    00077 static int nbs_call(struct ast_channel *ast, char *dest, int timeout);
    00078 static int nbs_hangup(struct ast_channel *ast);
    00079 static struct ast_frame *nbs_xread(struct ast_channel *ast);
    00080 static int nbs_xwrite(struct ast_channel *ast, struct ast_frame *frame);
    00081
    00082 static const struct ast_channel_tech nbs_tech = {
    00083    .type = type,
    00084    .description = tdesc,
    00085    .capabilities = AST_FORMAT_SLINEAR,
    00086    .requester = nbs_request,
    00087    .call = nbs_call,
    00088    .hangup = nbs_hangup,
    00089    .read = nbs_xread,
    00090    .write = nbs_xwrite,
    00091 };
    00092
    00093 static int nbs_call(struct ast_channel *ast, char *dest, int timeout)
    00094 {
    00095    struct nbs_pvt *p;
    00096
    00097    p = ast->tech_pvt;
    00098
    00099    if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
    00100       ast_log(LOG_WARNING, "nbs_call called on %s, neither down nor reserved\n", ast->name);
    00101       return -1;
    00102    }
    00103    /* When we call, it just works, really, there's no destination...  Just
    00104       ring the phone and wait for someone to answer */
    00105    if (option_debug)
    00106       ast_log(LOG_DEBUG, "Calling %s on %s\n", dest, ast->name);
    00107
    00108    /* If we can't connect, return congestion */
    00109    if (nbs_connect(p->nbs)) {
    00110       ast_log(LOG_WARNING, "NBS Connection failed on %s\n", ast->name);
    00111       ast_queue_control(ast, AST_CONTROL_CONGESTION);
    00112    } else {
    00113       ast_setstate(ast, AST_STATE_RINGING);
    00114       ast_queue_control(ast, AST_CONTROL_ANSWER);
    00115    }
    00116
    00117    return 0;
    00118 }
    00119
    00120 static void nbs_destroy(struct nbs_pvt *p)
    00121 {
    00122    if (p->nbs)
    00123       nbs_delstream(p->nbs);
    00124    free(p);
    00125 }
    00126
    00127 static struct nbs_pvt *nbs_alloc(void *data)
    00128 {
    00129    struct nbs_pvt *p;
    00130    int flags = 0;
    00131    char stream[256] = "";
    00132    char *opts;
    00133    strncpy(stream, data, sizeof(stream) - 1);
    00134    if ((opts = strchr(stream, ':'))) {
    00135       *opts = '\0';
    00136       opts++;
    00137    } else
    00138       opts = "";
    00139    p = malloc(sizeof(struct nbs_pvt));
    00140    if (p) {
    00141       memset(p, 0, sizeof(struct nbs_pvt));
    00142       if (!ast_strlen_zero(opts)) {
    00143          if (strchr(opts, 'm'))
    00144             flags |= NBS_FLAG_MUTE;
    00145          if (strchr(opts, 'o'))
    00146             flags |= NBS_FLAG_OVERSPEAK;
    00147          if (strchr(opts, 'e'))
    00148             flags |= NBS_FLAG_EMERGENCY;
    00149          if (strchr(opts, 'O'))
    00150             flags |= NBS_FLAG_OVERRIDE;
    00151       } else
    00152          flags = NBS_FLAG_OVERSPEAK;
    00153
    00154       strncpy(p->stream, stream, sizeof(p->stream) - 1);
    00155       p->nbs = nbs_newstream("asterisk", stream, flags);
    00156       if (!p->nbs) {
    00157          ast_log(LOG_WARNING, "Unable to allocate new NBS stream '%s' with flags %d\n", stream, flags);
    00158          free(p);
    00159          p = NULL;
    00160       } else {
    00161          /* Set for 8000 hz mono, 640 samples */
    00162          nbs_setbitrate(p->nbs, 8000);
    00163          nbs_setchannels(p->nbs, 1);
    00164          nbs_setblocksize(p->nbs, 640);
    00165          nbs_setblocking(p->nbs, 0);
    00166       }
    00167    }
    00168    return p;
    00169 }
    00170
    00171 static int nbs_hangup(struct ast_channel *ast)
    00172 {
    00173    struct nbs_pvt *p;
    00174    p = ast->tech_pvt;
    00175    if (option_debug)
    00176       ast_log(LOG_DEBUG, "nbs_hangup(%s)\n", ast->name);
    00177    if (!ast->tech_pvt) {
    00178       ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
    00179       return 0;
    00180    }
    00181    nbs_destroy(p);
    00182    ast->tech_pvt = NULL;
    00183    ast_setstate(ast, AST_STATE_DOWN);
    00184    return 0;
    00185 }
    00186
    00187 static struct ast_frame  *nbs_xread(struct ast_channel *ast)
    00188 {
    00189    struct nbs_pvt *p = ast->tech_pvt;
    00190
    00191
    00192    /* Some nice norms */
    00193    p->fr.datalen = 0;
    00194    p->fr.samples = 0;
    00195    p->fr.data =  NULL;
    00196    p->fr.src = type;
    00197    p->fr.offset = 0;
    00198    p->fr.mallocd=0;
    00199    p->fr.delivery.tv_sec = 0;
    00200    p->fr.delivery.tv_usec = 0;
    00201
    00202    ast_log(LOG_DEBUG, "Returning null frame on %s\n", ast->name);
    00203
    00204    return &p->fr;
    00205 }
    00206
    00207 static int nbs_xwrite(struct ast_channel *ast, struct ast_frame *frame)
    00208 {
    00209    struct nbs_pvt *p = ast->tech_pvt;
    00210    /* Write a frame of (presumably voice) data */
    00211    if (frame->frametype != AST_FRAME_VOICE) {
    00212       if (frame->frametype != AST_FRAME_IMAGE)
    00213          ast_log(LOG_WARNING, "Don't know what to do with  frame type '%d'\n", frame->frametype);
    00214       return 0;
    00215    }
    00216    if (!(frame->subclass &
    00217       (AST_FORMAT_SLINEAR))) {
    00218       ast_log(LOG_WARNING, "Cannot handle frames in %d format\n", frame->subclass);
    00219       return 0;
    00220    }
    00221    if (ast->_state != AST_STATE_UP) {
    00222       /* Don't try tos end audio on-hook */
    00223       return 0;
    00224    }
    00225    if (nbs_write(p->nbs, frame->data, frame->datalen / 2) < 0)
    00226       return -1;
    00227    return 0;
    00228 }
    00229
    00230 static struct ast_channel *nbs_new(struct nbs_pvt *i, int state)
    00231 {
    00232    struct ast_channel *tmp;
    00233    tmp = ast_channel_alloc(1);
    00234    if (tmp) {
    00235       tmp->tech = &nbs_tech;
    00236       snprintf(tmp->name, sizeof(tmp->name), "NBS/%s", i->stream);
    00237       tmp->type = type;
    00238       tmp->fds[0] = nbs_fd(i->nbs);
    00239       tmp->nativeformats = prefformat;
    00240       tmp->rawreadformat = prefformat;
    00241       tmp->rawwriteformat = prefformat;
    00242       tmp->writeformat = prefformat;
    00243       tmp->readformat = prefformat;
    00244       ast_setstate(tmp, state);
    00245       if (state == AST_STATE_RING)
    00246          tmp->rings = 1;
    00247       tmp->tech_pvt = i;
    00248       strncpy(tmp->context, context, sizeof(tmp->context)-1);
    00249       strncpy(tmp->exten, "s",  sizeof(tmp->exten) - 1);
    00250       tmp->language[0] = '\0';
    00251       i->owner = tmp;
    00252       ast_mutex_lock(&usecnt_lock);
    00253       usecnt++;
    00254       ast_mutex_unlock(&usecnt_lock);
    00255       ast_update_use_count();
    00256       if (state != AST_STATE_DOWN) {
    00257          if (ast_pbx_start(tmp)) {
    00258             ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
    00259             ast_hangup(tmp);
    00260          }
    00261       }
    00262    } else
    00263       ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
    00264    return tmp;
    00265 }
    00266
    00267
    00268 static struct ast_channel *nbs_request(const char *type, int format, void *data, int *cause)
    00269 {
    00270    int oldformat;
    00271    struct nbs_pvt *p;
    00272    struct ast_channel *tmp = NULL;
    00273
    00274    oldformat = format;
    00275    format &= (AST_FORMAT_SLINEAR);
    00276    if (!format) {
    00277       ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%d'\n", oldformat);
    00278       return NULL;
    00279    }
    00280    p = nbs_alloc(data);
    00281    if (p) {
    00282       tmp = nbs_new(p, AST_STATE_DOWN);
    00283       if (!tmp)
    00284          nbs_destroy(p);
    00285    }
    00286    return tmp;
    00287 }
    00288
    00289 static int __unload_module(void)
    00290 {
    00291    /* First, take us out of the channel loop */
    00292    ast_channel_unregister(&nbs_tech);
    00293    return 0;
    00294 }
    00295
    00296 int unload_module(void)
    00297 {
    00298    return __unload_module();
    00299 }
    00300
    00301 int load_module()
    00302 {
    00303    /* Make sure we can register our channel type */
    00304    if (ast_channel_register(&nbs_tech)) {
    00305       ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
    00306       __unload_module();
    00307       return -1;
    00308    }
    00309    return 0;
    00310 }
    00311
    00312 int usecount()
    00313 {
    00314    return usecnt;
    00315 }
    00316
    00317 char *description()
    00318 {
    00319    return (char *) desc;
    00320 }
    00321
    00322 char *key()
    00323 {
    00324    return ASTERISK_GPL_KEY;
    00325 }