2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Device state management
23 * \author Mark Spencer <markster@digium.com>
28 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
30 #include <sys/types.h>
36 #include "asterisk/channel.h"
37 #include "asterisk/utils.h"
38 #include "asterisk/lock.h"
39 #include "asterisk/linkedlists.h"
40 #include "asterisk/logger.h"
41 #include "asterisk/devicestate.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/options.h"
45 /*! \brief Device state strings for printing */
46 static const char *devstatestring
[] = {
47 /* 0 AST_DEVICE_UNKNOWN */ "Unknown", /*!< Valid, but unknown state */
48 /* 1 AST_DEVICE_NOT_INUSE */ "Not in use", /*!< Not used */
49 /* 2 AST_DEVICE IN USE */ "In use", /*!< In use */
50 /* 3 AST_DEVICE_BUSY */ "Busy", /*!< Busy */
51 /* 4 AST_DEVICE_INVALID */ "Invalid", /*!< Invalid - not known to Asterisk */
52 /* 5 AST_DEVICE_UNAVAILABLE */ "Unavailable", /*!< Unavailable (not registred) */
53 /* 6 AST_DEVICE_RINGING */ "Ringing" /*!< Ring, ring, ring */
56 /*! \brief A device state watcher (callback) */
59 ast_devstate_cb_type callback
;
60 AST_LIST_ENTRY(devstate_cb
) list
;
63 static AST_LIST_HEAD_STATIC(devstate_cbs
, devstate_cb
);
66 AST_LIST_ENTRY(state_change
) list
;
70 static AST_LIST_HEAD_STATIC(state_changes
, state_change
);
72 static pthread_t change_thread
= AST_PTHREADT_NULL
;
73 static ast_cond_t change_pending
;
75 /*! \brief Find devicestate as text message for output */
76 const char *devstate2str(int devstate
)
78 return devstatestring
[devstate
];
81 /*! \brief Find out if device is active in a call or not
82 \note find channels with the device's name in it
83 This function is only used for channels that does not implement
86 int ast_parse_device_state(const char *device
)
88 struct ast_channel
*chan
;
89 char match
[AST_CHANNEL_NAME
];
92 ast_copy_string(match
, device
, sizeof(match
)-1);
94 chan
= ast_get_channel_by_name_prefix_locked(match
, strlen(match
));
97 return AST_DEVICE_UNKNOWN
;
99 if (chan
->_state
== AST_STATE_RINGING
)
100 res
= AST_DEVICE_RINGING
;
102 res
= AST_DEVICE_INUSE
;
104 ast_channel_unlock(chan
);
109 /*! \brief Check device state through channel specific function or generic function */
110 int ast_device_state(const char *device
)
115 const struct ast_channel_tech
*chan_tech
;
118 buf
= ast_strdupa(device
);
119 tech
= strsep(&buf
, "/");
122 return AST_DEVICE_INVALID
;
124 chan_tech
= ast_get_channel_tech(tech
);
126 return AST_DEVICE_INVALID
;
128 if (!chan_tech
->devicestate
) /* Does the channel driver support device state notification? */
129 return ast_parse_device_state(device
); /* No, try the generic function */
131 res
= chan_tech
->devicestate(number
); /* Ask the channel driver for device state */
132 if (res
== AST_DEVICE_UNKNOWN
) {
133 res
= ast_parse_device_state(device
);
134 /* at this point we know the device exists, but the channel driver
135 could not give us a state; if there is no channel state available,
136 it must be 'not in use'
138 if (res
== AST_DEVICE_UNKNOWN
)
139 res
= AST_DEVICE_NOT_INUSE
;
146 /*! \brief Add device state watcher */
147 int ast_devstate_add(ast_devstate_cb_type callback
, void *data
)
149 struct devstate_cb
*devcb
;
151 if (!callback
|| !(devcb
= ast_calloc(1, sizeof(*devcb
))))
155 devcb
->callback
= callback
;
157 AST_LIST_LOCK(&devstate_cbs
);
158 AST_LIST_INSERT_HEAD(&devstate_cbs
, devcb
, list
);
159 AST_LIST_UNLOCK(&devstate_cbs
);
164 /*! \brief Remove device state watcher */
165 void ast_devstate_del(ast_devstate_cb_type callback
, void *data
)
167 struct devstate_cb
*devcb
;
169 AST_LIST_LOCK(&devstate_cbs
);
170 AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_cbs
, devcb
, list
) {
171 if ((devcb
->callback
== callback
) && (devcb
->data
== data
)) {
172 AST_LIST_REMOVE_CURRENT(&devstate_cbs
, list
);
177 AST_LIST_TRAVERSE_SAFE_END
;
178 AST_LIST_UNLOCK(&devstate_cbs
);
181 /*! \brief Notify callback watchers of change, and notify PBX core for hint updates */
182 static void do_state_change(const char *device
)
185 struct devstate_cb
*devcb
;
187 state
= ast_device_state(device
);
188 if (option_debug
> 2)
189 ast_log(LOG_DEBUG
, "Changing state for %s - state %d (%s)\n", device
, state
, devstate2str(state
));
191 AST_LIST_LOCK(&devstate_cbs
);
192 AST_LIST_TRAVERSE(&devstate_cbs
, devcb
, list
)
193 devcb
->callback(device
, state
, devcb
->data
);
194 AST_LIST_UNLOCK(&devstate_cbs
);
196 ast_hint_state_changed(device
);
199 static int __ast_device_state_changed_literal(char *buf
)
202 struct state_change
*change
;
205 if ((tmp
= strrchr(device
, '-')))
208 if (change_thread
== AST_PTHREADT_NULL
|| !(change
= ast_calloc(1, sizeof(*change
) + strlen(device
)))) {
209 /* we could not allocate a change struct, or */
210 /* there is no background thread, so process the change now */
211 do_state_change(device
);
213 /* queue the change */
214 strcpy(change
->device
, device
);
215 AST_LIST_LOCK(&state_changes
);
216 AST_LIST_INSERT_TAIL(&state_changes
, change
, list
);
217 if (AST_LIST_FIRST(&state_changes
) == change
)
218 /* the list was empty, signal the thread */
219 ast_cond_signal(&change_pending
);
220 AST_LIST_UNLOCK(&state_changes
);
226 int ast_device_state_changed_literal(const char *dev
)
229 buf
= ast_strdupa(dev
);
230 return __ast_device_state_changed_literal(buf
);
233 /*! \brief Accept change notification, add it to change queue */
234 int ast_device_state_changed(const char *fmt
, ...)
236 char buf
[AST_MAX_EXTENSION
];
240 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
242 return __ast_device_state_changed_literal(buf
);
245 /*! \brief Go through the dev state change queue and update changes in the dev state thread */
246 static void *do_devstate_changes(void *data
)
248 struct state_change
*cur
;
250 AST_LIST_LOCK(&state_changes
);
252 /* the list lock will _always_ be held at this point in the loop */
253 cur
= AST_LIST_REMOVE_HEAD(&state_changes
, list
);
255 /* we got an entry, so unlock the list while we process it */
256 AST_LIST_UNLOCK(&state_changes
);
257 do_state_change(cur
->device
);
259 AST_LIST_LOCK(&state_changes
);
261 /* there was no entry, so atomically unlock the list and wait for
262 the condition to be signalled (returns with the lock held) */
263 ast_cond_wait(&change_pending
, &state_changes
.lock
);
270 /*! \brief Initialize the device state engine in separate thread */
271 int ast_device_state_engine_init(void)
273 ast_cond_init(&change_pending
, NULL
);
274 if (ast_pthread_create(&change_thread
, NULL
, do_devstate_changes
, NULL
) < 0) {
275 ast_log(LOG_ERROR
, "Unable to start device state change thread.\n");