Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:53 2009

## Asterisk developer's documentation

- Main Page
- Related Pages
- Modules
- Data Structures
- Files
- Directories

# devicestate.c

Go to the documentation of this file.

    00001 /*
    00002  * Asterisk -- An open source telephony toolkit.
    00003  *
    00004  * Copyright (C) 1999 - 2006, 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 Device state management
    00022  *
    00023  */
    00024
    00025 #include <sys/types.h>
    00026 #include <unistd.h>
    00027 #include <stdlib.h>
    00028 #include <string.h>
    00029 #include <stdio.h>
    00030
    00031 #include "asterisk.h"
    00032
    00033 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 67593 $")
    00034
    00035 #include "asterisk/channel.h"
    00036 #include "asterisk/utils.h"
    00037 #include "asterisk/lock.h"
    00038 #include "asterisk/linkedlists.h"
    00039 #include "asterisk/logger.h"
    00040 #include "asterisk/devicestate.h"
    00041 #include "asterisk/pbx.h"
    00042 #include "asterisk/options.h"
    00043
    00044 static const char *devstatestring[] = {
    00045    /* 0 AST_DEVICE_UNKNOWN */ "Unknown",  /* Valid, but unknown state */
    00046    /* 1 AST_DEVICE_NOT_INUSE */  "Not in use",  /* Not used */
    00047    /* 2 AST_DEVICE IN USE */  "In use",   /* In use */
    00048    /* 3 AST_DEVICE_BUSY */    "Busy",     /* Busy */
    00049    /* 4 AST_DEVICE_INVALID */ "Invalid",  /* Invalid - not known to Asterisk */
    00050    /* 5 AST_DEVICE_UNAVAILABLE */   "Unavailable", /* Unavailable (not registred) */
    00051    /* 6 AST_DEVICE_RINGING */ "Ringing"   /* Ring, ring, ring */
    00052 };
    00053
    00054 /* ast_devstate_cb: A device state watcher (callback) */
    00055 struct devstate_cb {
    00056    void *data;
    00057    ast_devstate_cb_type callback;
    00058    AST_LIST_ENTRY(devstate_cb) list;
    00059 };
    00060
    00061 static AST_LIST_HEAD_STATIC(devstate_cbs, devstate_cb);
    00062
    00063 struct state_change {
    00064    AST_LIST_ENTRY(state_change) list;
    00065    char device[1];
    00066 };
    00067
    00068 static AST_LIST_HEAD_STATIC(state_changes, state_change);
    00069
    00070 static pthread_t change_thread = AST_PTHREADT_NULL;
    00071 static ast_cond_t change_pending;
    00072
    00073 /*--- devstate2str: Find devicestate as text message for output */
    00074 const char *devstate2str(int devstate)
    00075 {
    00076    return devstatestring[devstate];
    00077 }
    00078
    00079 /*--- ast_parse_device_state: Find out if device is active in a call or not */
    00080 int ast_parse_device_state(const char *device)
    00081 {
    00082    struct ast_channel *chan;
    00083    char match[AST_CHANNEL_NAME];
    00084    int res;
    00085
    00086    ast_copy_string(match, device, sizeof(match)-1);
    00087    strcat(match, "-");
    00088    chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
    00089
    00090    if (!chan)
    00091       return AST_DEVICE_UNKNOWN;
    00092
    00093    if (chan->_state == AST_STATE_RINGING)
    00094       res = AST_DEVICE_RINGING;
    00095    else
    00096       res = AST_DEVICE_INUSE;
    00097
    00098    ast_mutex_unlock(&chan->lock);
    00099
    00100    return res;
    00101 }
    00102
    00103 /*--- ast_device_state: Check device state through channel specific function or generic function */
    00104 int ast_device_state(const char *device)
    00105 {
    00106    char *buf;
    00107    char *tech;
    00108    char *number;
    00109    const struct ast_channel_tech *chan_tech;
    00110    int res = 0;
    00111
    00112    buf = ast_strdupa(device);
    00113    tech = strsep(&buf, "/");
    00114    number = buf;
    00115    if (!number)
    00116       return AST_DEVICE_INVALID;
    00117
    00118    chan_tech = ast_get_channel_tech(tech);
    00119    if (!chan_tech)
    00120       return AST_DEVICE_INVALID;
    00121
    00122    if (!chan_tech->devicestate)  /* Does the channel driver support device state notification? */
    00123       return ast_parse_device_state(device); /* No, try the generic function */
    00124    else {
    00125       res = chan_tech->devicestate(number);  /* Ask the channel driver for device state */
    00126       if (res == AST_DEVICE_UNKNOWN) {
    00127          res = ast_parse_device_state(device);
    00128          /* at this point we know the device exists, but the channel driver
    00129             could not give us a state; if there is no channel state available,
    00130             it must be 'not in use'
    00131          */
    00132          if (res == AST_DEVICE_UNKNOWN)
    00133             res = AST_DEVICE_NOT_INUSE;
    00134          return res;
    00135       } else
    00136          return res;
    00137    }
    00138 }
    00139
    00140 /*--- ast_devstate_add: Add device state watcher */
    00141 int ast_devstate_add(ast_devstate_cb_type callback, void *data)
    00142 {
    00143    struct devstate_cb *devcb;
    00144
    00145    if (!callback)
    00146       return -1;
    00147
    00148    devcb = calloc(1, sizeof(*devcb));
    00149    if (!devcb)
    00150       return -1;
    00151
    00152    devcb->data = data;
    00153    devcb->callback = callback;
    00154
    00155    AST_LIST_LOCK(&devstate_cbs);
    00156    AST_LIST_INSERT_HEAD(&devstate_cbs, devcb, list);
    00157    AST_LIST_UNLOCK(&devstate_cbs);
    00158
    00159    return 0;
    00160 }
    00161
    00162 /*--- ast_devstate_del: Remove device state watcher */
    00163 void ast_devstate_del(ast_devstate_cb_type callback, void *data)
    00164 {
    00165    struct devstate_cb *devcb;
    00166
    00167    AST_LIST_LOCK(&devstate_cbs);
    00168    AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_cbs, devcb, list) {
    00169       if ((devcb->callback == callback) && (devcb->data == data)) {
    00170          AST_LIST_REMOVE_CURRENT(&devstate_cbs, list);
    00171          free(devcb);
    00172          break;
    00173       }
    00174    }
    00175    AST_LIST_TRAVERSE_SAFE_END;
    00176    AST_LIST_UNLOCK(&devstate_cbs);
    00177 }
    00178
    00179 /*--- do_state_change: Notify callback watchers of change, and notify PBX core for hint updates */
    00180 static void do_state_change(const char *device)
    00181 {
    00182    int state;
    00183    struct devstate_cb *devcb;
    00184
    00185    state = ast_device_state(device);
    00186    if (option_debug > 2)
    00187       ast_log(LOG_DEBUG, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
    00188
    00189    AST_LIST_LOCK(&devstate_cbs);
    00190    AST_LIST_TRAVERSE(&devstate_cbs, devcb, list)
    00191       devcb->callback(device, state, devcb->data);
    00192    AST_LIST_UNLOCK(&devstate_cbs);
    00193
    00194    ast_hint_state_changed(device);
    00195 }
    00196
    00197 static int __ast_device_state_changed_literal(char *buf)
    00198 {
    00199    char *device;
    00200    struct state_change *change = NULL;
    00201    char *tmp = NULL;
    00202
    00203    device = buf;
    00204
    00205    tmp = strrchr(device, '-');
    00206    if (tmp)
    00207       *tmp = '\0';
    00208
    00209    if (change_thread != AST_PTHREADT_NULL)
    00210       change = calloc(1, sizeof(*change) + strlen(device));
    00211
    00212    if (!change) {
    00213       /* we could not allocate a change struct, or */
    00214       /* there is no background thread, so process the change now */
    00215       do_state_change(device);
    00216    } else {
    00217       /* queue the change */
    00218       strcpy(change->device, device);
    00219       AST_LIST_LOCK(&state_changes);
    00220       AST_LIST_INSERT_TAIL(&state_changes, change, list);
    00221       if (AST_LIST_FIRST(&state_changes) == change)
    00222          /* the list was empty, signal the thread */
    00223          ast_cond_signal(&change_pending);
    00224       AST_LIST_UNLOCK(&state_changes);
    00225    }
    00226
    00227    return 1;
    00228 }
    00229
    00230 int ast_device_state_changed_literal(const char *dev)
    00231 {
    00232    char *buf;
    00233    buf = ast_strdupa(dev);
    00234    return __ast_device_state_changed_literal(buf);
    00235 }
    00236
    00237 /*--- ast_device_state_changed: Accept change notification, add it to change queue */
    00238 int ast_device_state_changed(const char *fmt, ...)
    00239 {
    00240    char buf[AST_MAX_EXTENSION];
    00241    va_list ap;
    00242
    00243    va_start(ap, fmt);
    00244    vsnprintf(buf, sizeof(buf), fmt, ap);
    00245    va_end(ap);
    00246    return __ast_device_state_changed_literal(buf);
    00247 }
    00248
    00249 /*--- do_devstate_changes: Go through the dev state change queue and update changes in the dev state thread */
    00250 static void *do_devstate_changes(void *data)
    00251 {
    00252    struct state_change *cur;
    00253
    00254    AST_LIST_LOCK(&state_changes);
    00255    for(;;) {
    00256       /* the list lock will _always_ be held at this point in the loop */
    00257       cur = AST_LIST_REMOVE_HEAD(&state_changes, list);
    00258       if (cur) {
    00259          /* we got an entry, so unlock the list while we process it */
    00260          AST_LIST_UNLOCK(&state_changes);
    00261          do_state_change(cur->device);
    00262          free(cur);
    00263          AST_LIST_LOCK(&state_changes);
    00264       } else {
    00265          /* there was no entry, so atomically unlock the list and wait for
    00266             the condition to be signalled (returns with the lock held) */
    00267          ast_cond_wait(&change_pending, &state_changes.lock);
    00268       }
    00269    }
    00270
    00271    return NULL;
    00272 }
    00273
    00274 /*--- ast_device_state_engine_init: Initialize the device state engine in separate thread */
    00275 int ast_device_state_engine_init(void)
    00276 {
    00277    ast_cond_init(&change_pending, NULL);
    00278    if (ast_pthread_create(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
    00279       ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
    00280       return -1;
    00281    }
    00282
    00283    return 0;
    00284 }