Deprecate *_device_state_* APIs in favor of *_devstate_* APIs
[asterisk/asterisk.git] / main / devicestate.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2008, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  * Russell Bryant <russell@digium.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 /*! \file
21  *
22  * \brief Device state management
23  *
24  * \author Mark Spencer <markster@digium.com> 
25  * \author Russell Bryant <russell@digium.com>
26  *
27  *      \arg \ref AstExtState
28  */
29
30 /*! \page AstExtState Extension and device states in Asterisk
31  *
32  * (Note that these descriptions of device states and extension
33  * states have not been updated to the way things work
34  * in Asterisk 1.6.)
35  *
36  *      Asterisk has an internal system that reports states
37  *      for an extension. By using the dialplan priority -1,
38  *      also called a \b hint, a connection can be made from an
39  *      extension to one or many devices. The state of the extension
40  *      now depends on the combined state of the devices.
41  *
42  *      The device state is basically based on the current calls.
43  *      If the devicestate engine can find a call from or to the
44  *      device, it's in use.
45  *      
46  *      Some channel drivers implement a callback function for 
47  *      a better level of reporting device states. The SIP channel
48  *      has a complicated system for this, which is improved 
49  *      by adding call limits to the configuration.
50  * 
51  *      Functions that want to check the status of an extension
52  *      register themself as a \b watcher.
53  *      Watchers in this system can subscribe either to all extensions
54  *      or just a specific extensions.
55  *
56  *      For non-device related states, there's an API called
57  *      devicestate providers. This is an extendible system for
58  *      delivering state information from outside sources or
59  *      functions within Asterisk. Currently we have providers
60  *      for app_meetme.c - the conference bridge - and call
61  *      parking (metermaids).
62  *
63  *      There are manly three subscribers to extension states 
64  *      within Asterisk:
65  *      - AMI, the manager interface
66  *      - app_queue.c - the Queue dialplan application
67  *      - SIP subscriptions, a.k.a. "blinking lamps" or 
68  *        "buddy lists"
69  *
70  *      The CLI command "show hints" show last known state
71  *
72  *      \note None of these handle user states, like an IM presence
73  *      system. res_jabber.c can subscribe and watch such states
74  *      in jabber/xmpp based systems.
75  *
76  *      \section AstDevStateArch Architecture for devicestates
77  *
78  *      When a channel driver or asterisk app changes state for 
79  *      a watched object, it alerts the core. The core queues
80  *      a change. When the change is processed, there's a query
81  *      sent to the channel driver/provider if there's a function
82  *      to handle that, otherwise a channel walk is issued to find
83  *      a channel that involves the object.
84  *      
85  *      The changes are queued and processed by a separate thread.
86  *      This thread calls the watchers subscribing to status 
87  *      changes for the object. For manager, this results 
88  *      in events. For SIP, NOTIFY requests.
89  *
90  *      - Device states
91  *              \arg \ref devicestate.c 
92  *              \arg \ref devicestate.h 
93  *
94  *      \section AstExtStateArch Architecture for extension states
95  *      
96  *      Hints are connected to extension. If an extension changes state
97  *      it checks the hint devices. If there is a hint, the callbacks into
98  *      device states are checked. The aggregated state is set for the hint
99  *      and reported back.
100  *
101  *      - Extension states
102  *              \arg \ref AstENUM ast_extension_states
103  *              \arg \ref pbx.c 
104  *              \arg \ref pbx.h 
105  *      - Structures
106  *              - \ref ast_state_cb struct.  Callbacks for watchers
107  *              - Callback ast_state_cb_type
108  *              - \ref ast_hint struct.
109  *      - Functions
110  *              - ast_extension_state_add()
111  *              - ast_extension_state_del()
112  *              - ast_get_hint()
113  *      
114  */
115
116 #include "asterisk.h"
117
118 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
119
120 #include "asterisk/_private.h"
121 #include "asterisk/channel.h"
122 #include "asterisk/utils.h"
123 #include "asterisk/lock.h"
124 #include "asterisk/linkedlists.h"
125 #include "asterisk/devicestate.h"
126 #include "asterisk/pbx.h"
127 #include "asterisk/app.h"
128 #include "asterisk/event.h"
129
130 /*! \brief Device state strings for printing */
131 static const char *devstatestring[] = {
132         /* 0 AST_DEVICE_UNKNOWN */      "Unknown",      /*!< Valid, but unknown state */
133         /* 1 AST_DEVICE_NOT_INUSE */    "Not in use",   /*!< Not used */
134         /* 2 AST_DEVICE IN USE */       "In use",       /*!< In use */
135         /* 3 AST_DEVICE_BUSY */         "Busy",         /*!< Busy */
136         /* 4 AST_DEVICE_INVALID */      "Invalid",      /*!< Invalid - not known to Asterisk */
137         /* 5 AST_DEVICE_UNAVAILABLE */  "Unavailable",  /*!< Unavailable (not registred) */
138         /* 6 AST_DEVICE_RINGING */      "Ringing",      /*!< Ring, ring, ring */
139         /* 7 AST_DEVICE_RINGINUSE */    "Ring+Inuse",   /*!< Ring and in use */
140         /* 8 AST_DEVICE_ONHOLD */       "On Hold"       /*!< On Hold */
141 };
142
143 /*!\brief Mapping for channel states to device states */
144 static const struct chan2dev {
145         enum ast_channel_state chan;
146         enum ast_device_state dev;
147 } chan2dev[] = {
148         { AST_STATE_DOWN,            AST_DEVICE_NOT_INUSE },
149         { AST_STATE_RESERVED,        AST_DEVICE_INUSE },
150         { AST_STATE_OFFHOOK,         AST_DEVICE_INUSE },
151         { AST_STATE_DIALING,         AST_DEVICE_INUSE },
152         { AST_STATE_RING,            AST_DEVICE_INUSE },
153         { AST_STATE_RINGING,         AST_DEVICE_RINGING },
154         { AST_STATE_UP,              AST_DEVICE_INUSE },
155         { AST_STATE_BUSY,            AST_DEVICE_BUSY },
156         { AST_STATE_DIALING_OFFHOOK, AST_DEVICE_INUSE },
157         { AST_STATE_PRERING,         AST_DEVICE_RINGING },
158         { -100,                      -100 },
159 };
160
161 /*! \brief  A device state provider (not a channel) */
162 struct devstate_prov {
163         char label[40];
164         ast_devstate_prov_cb_type callback;
165         AST_RWLIST_ENTRY(devstate_prov) list;
166 };
167
168 /*! \brief A list of providers */
169 static AST_RWLIST_HEAD_STATIC(devstate_provs, devstate_prov);
170
171 struct state_change {
172         AST_LIST_ENTRY(state_change) list;
173         char device[1];
174 };
175
176 /*! \brief The state change queue. State changes are queued
177         for processing by a separate thread */
178 static AST_LIST_HEAD_STATIC(state_changes, state_change);
179
180 /*! \brief The device state change notification thread */
181 static pthread_t change_thread = AST_PTHREADT_NULL;
182
183 /*! \brief Flag for the queue */
184 static ast_cond_t change_pending;
185
186 /*! \brief Whether or not to cache this device state value */
187 enum devstate_cache {
188         /*! Cache this value as it is coming from a device state provider which is
189          *  pushing up state change events to us as they happen */
190         CACHE_ON,
191         /*! Don't cache this result, since it was pulled from the device state provider.
192          *  We only want to cache results from device state providers that are being nice
193          *  and pushing state change events up to us as they happen. */
194         CACHE_OFF,
195 };
196
197 struct devstate_change {
198         AST_LIST_ENTRY(devstate_change) entry;
199         uint32_t state;
200         struct ast_eid eid;
201         char device[1];
202 };
203
204 struct {
205         pthread_t thread;
206         struct ast_event_sub *event_sub;
207         ast_cond_t cond;
208         ast_mutex_t lock;
209         AST_LIST_HEAD_NOLOCK(, devstate_change) devstate_change_q;
210 } devstate_collector = {
211         .thread = AST_PTHREADT_NULL,
212 };
213
214 /* Forward declarations */
215 static int getproviderstate(const char *provider, const char *address);
216
217 /*! \brief Find devicestate as text message for output */
218 const char *devstate2str(enum ast_device_state devstate) 
219 {
220         return devstatestring[devstate];
221 }
222
223 enum ast_device_state ast_state_chan2dev(enum ast_channel_state chanstate)
224 {
225         int i;
226         chanstate &= 0xFFFF;
227         for (i = 0; chan2dev[i].chan != -100; i++) {
228                 if (chan2dev[i].chan == chanstate) {
229                         return chan2dev[i].dev;
230                 }
231         }
232         return AST_DEVICE_UNKNOWN;
233 }
234
235 const char *ast_devstate_str(enum ast_device_state state)
236 {
237         const char *res = "UNKNOWN";
238
239         switch (state) {
240         case AST_DEVICE_UNKNOWN:
241                 break;
242         case AST_DEVICE_NOT_INUSE:
243                 res = "NOT_INUSE";
244                 break;
245         case AST_DEVICE_INUSE:
246                 res = "INUSE";
247                 break;
248         case AST_DEVICE_BUSY:
249                 res = "BUSY";
250                 break;
251         case AST_DEVICE_INVALID:
252                 res = "INVALID";
253                 break;
254         case AST_DEVICE_UNAVAILABLE:
255                 res = "UNAVAILABLE";
256                 break;
257         case AST_DEVICE_RINGING:
258                 res = "RINGING";
259                 break;
260         case AST_DEVICE_RINGINUSE:
261                 res = "RINGINUSE";
262                 break;
263         case AST_DEVICE_ONHOLD:
264                 res = "ONHOLD";
265                 break;
266         }
267
268         return res;
269 }
270
271 enum ast_device_state ast_devstate_val(const char *val)
272 {
273         if (!strcasecmp(val, "NOT_INUSE"))
274                 return AST_DEVICE_NOT_INUSE;
275         else if (!strcasecmp(val, "INUSE"))
276                 return AST_DEVICE_INUSE;
277         else if (!strcasecmp(val, "BUSY"))
278                 return AST_DEVICE_BUSY;
279         else if (!strcasecmp(val, "INVALID"))
280                 return AST_DEVICE_INVALID;
281         else if (!strcasecmp(val, "UNAVAILABLE"))
282                 return AST_DEVICE_UNAVAILABLE;
283         else if (!strcasecmp(val, "RINGING"))
284                 return AST_DEVICE_RINGING;
285         else if (!strcasecmp(val, "RINGINUSE"))
286                 return AST_DEVICE_RINGINUSE;
287         else if (!strcasecmp(val, "ONHOLD"))
288                 return AST_DEVICE_ONHOLD;
289
290         return AST_DEVICE_UNKNOWN;
291 }
292
293 /*! \brief Find out if device is active in a call or not 
294         \note find channels with the device's name in it
295         This function is only used for channels that does not implement 
296         devicestate natively
297 */
298 enum ast_device_state ast_parse_device_state(const char *device)
299 {
300         struct ast_channel *chan;
301         char match[AST_CHANNEL_NAME];
302         enum ast_device_state res;
303
304         ast_copy_string(match, device, sizeof(match)-1);
305         strcat(match, "-");
306         chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
307
308         if (!chan)
309                 return AST_DEVICE_UNKNOWN;
310
311         if (chan->_state == AST_STATE_RINGING)
312                 res = AST_DEVICE_RINGING;
313         else
314                 res = AST_DEVICE_INUSE;
315         
316         ast_channel_unlock(chan);
317
318         return res;
319 }
320
321 static enum ast_device_state devstate_cached(const char *device)
322 {
323         enum ast_device_state res = AST_DEVICE_UNKNOWN;
324         struct ast_event *event;
325
326         event = ast_event_get_cached(AST_EVENT_DEVICE_STATE,
327                 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
328                 AST_EVENT_IE_END);
329
330         if (!event)
331                 return res;
332
333         res = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
334
335         ast_event_destroy(event);
336
337         return res;
338 }
339
340 /*! \brief Check device state through channel specific function or generic function */
341 enum ast_device_state ast_device_state(const char *device)
342 {
343         char *buf;
344         char *number;
345         const struct ast_channel_tech *chan_tech;
346         enum ast_device_state res;
347         /*! \brief Channel driver that provides device state */
348         char *tech;
349         /*! \brief Another provider of device state */
350         char *provider = NULL;
351
352         /* If the last known state is cached, just return that */
353         res = devstate_cached(device);
354         if (res != AST_DEVICE_UNKNOWN)
355                 return res;
356
357         buf = ast_strdupa(device);
358         tech = strsep(&buf, "/");
359         if (!(number = buf)) {
360                 if (!(provider = strsep(&tech, ":")))
361                         return AST_DEVICE_INVALID;
362                 /* We have a provider */
363                 number = tech;
364                 tech = NULL;
365         }
366
367         if (provider)  {
368                 ast_debug(3, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
369                 return getproviderstate(provider, number);
370         }
371
372         ast_debug(4, "No provider found, checking channel drivers for %s - %s\n", tech, number);
373
374         if (!(chan_tech = ast_get_channel_tech(tech)))
375                 return AST_DEVICE_INVALID;
376
377         if (!(chan_tech->devicestate)) /* Does the channel driver support device state notification? */
378                 return ast_parse_device_state(device); /* No, try the generic function */
379
380         res = chan_tech->devicestate(number);
381
382         if (res != AST_DEVICE_UNKNOWN)
383                 return res;
384
385         res = ast_parse_device_state(device);
386
387         if (res == AST_DEVICE_UNKNOWN)
388                 return AST_DEVICE_NOT_INUSE;
389
390         return res;
391 }
392
393 /*! \brief Add device state provider */
394 int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
395 {
396         struct devstate_prov *devprov;
397
398         if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
399                 return -1;
400
401         devprov->callback = callback;
402         ast_copy_string(devprov->label, label, sizeof(devprov->label));
403
404         AST_RWLIST_WRLOCK(&devstate_provs);
405         AST_RWLIST_INSERT_HEAD(&devstate_provs, devprov, list);
406         AST_RWLIST_UNLOCK(&devstate_provs);
407
408         return 0;
409 }
410
411 /*! \brief Remove device state provider */
412 int ast_devstate_prov_del(const char *label)
413 {
414         struct devstate_prov *devcb;
415         int res = -1;
416
417         AST_RWLIST_WRLOCK(&devstate_provs);
418         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
419                 if (!strcasecmp(devcb->label, label)) {
420                         AST_RWLIST_REMOVE_CURRENT(list);
421                         ast_free(devcb);
422                         res = 0;
423                         break;
424                 }
425         }
426         AST_RWLIST_TRAVERSE_SAFE_END;
427         AST_RWLIST_UNLOCK(&devstate_provs);
428
429         return res;
430 }
431
432 /*! \brief Get provider device state */
433 static int getproviderstate(const char *provider, const char *address)
434 {
435         struct devstate_prov *devprov;
436         int res = AST_DEVICE_INVALID;
437
438         AST_RWLIST_RDLOCK(&devstate_provs);
439         AST_RWLIST_TRAVERSE(&devstate_provs, devprov, list) {
440                 ast_debug(5, "Checking provider %s with %s\n", devprov->label, provider);
441
442                 if (!strcasecmp(devprov->label, provider)) {
443                         res = devprov->callback(address);
444                         break;
445                 }
446         }
447         AST_RWLIST_UNLOCK(&devstate_provs);
448
449         return res;
450 }
451
452 static void devstate_event(const char *device, enum ast_device_state state, enum devstate_cache cache)
453 {
454         struct ast_event *event;
455
456         ast_debug(1, "device '%s' state '%d'\n", device, state);
457
458         if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE,
459                         AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
460                         AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
461                         AST_EVENT_IE_END))) {
462                 return;
463         }
464
465         if (cache == CACHE_ON) {
466                 /* Cache this event, replacing an event in the cache with the same
467                  * device name if it exists. */
468                 ast_event_queue_and_cache(event,
469                         AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR,
470                         AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, sizeof(struct ast_eid),
471                         AST_EVENT_IE_END);
472         } else {
473                 ast_event_queue(event);
474         }
475 }
476
477 /*! Called by the state change thread to find out what the state is, and then
478  *  to queue up the state change event */
479 static void do_state_change(const char *device)
480 {
481         enum ast_device_state state;
482
483         state = ast_device_state(device);
484
485         ast_debug(3, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
486
487         devstate_event(device, state, CACHE_OFF);
488 }
489
490 int ast_devstate_changed_literal(enum ast_device_state state, const char *device)
491 {
492         struct state_change *change;
493
494         ast_debug(3, "Notification of state change to be queued on device/channel %s\n", device);
495
496         if (state != AST_DEVICE_UNKNOWN) {
497                 devstate_event(device, state, CACHE_ON);
498         } else if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
499                 /* we could not allocate a change struct, or */
500                 /* there is no background thread, so process the change now */
501                 do_state_change(device);
502         } else {
503                 /* queue the change */
504                 strcpy(change->device, device);
505                 AST_LIST_LOCK(&state_changes);
506                 AST_LIST_INSERT_TAIL(&state_changes, change, list);
507                 ast_cond_signal(&change_pending);
508                 AST_LIST_UNLOCK(&state_changes);
509         }
510
511         return 1;
512 }
513
514 int ast_device_state_changed_literal(const char *dev)
515 {
516         return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, dev);
517 }
518
519 int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...) 
520 {
521         char buf[AST_MAX_EXTENSION];
522         va_list ap;
523
524         va_start(ap, fmt);
525         vsnprintf(buf, sizeof(buf), fmt, ap);
526         va_end(ap);
527
528         return ast_devstate_changed_literal(state, buf);
529 }
530
531 /*! \brief Accept change notification, add it to change queue */
532 int ast_device_state_changed(const char *fmt, ...) 
533 {
534         char buf[AST_MAX_EXTENSION];
535         va_list ap;
536
537         va_start(ap, fmt);
538         vsnprintf(buf, sizeof(buf), fmt, ap);
539         va_end(ap);
540
541         return ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, buf);
542 }
543
544 /*! \brief Go through the dev state change queue and update changes in the dev state thread */
545 static void *do_devstate_changes(void *data)
546 {
547         struct state_change *next, *current;
548
549         for (;;) {
550                 /* This basically pops off any state change entries, resets the list back to NULL, unlocks, and processes each state change */
551                 AST_LIST_LOCK(&state_changes);
552                 if (AST_LIST_EMPTY(&state_changes))
553                         ast_cond_wait(&change_pending, &state_changes.lock);
554                 next = AST_LIST_FIRST(&state_changes);
555                 AST_LIST_HEAD_INIT_NOLOCK(&state_changes);
556                 AST_LIST_UNLOCK(&state_changes);
557
558                 /* Process each state change */
559                 while ((current = next)) {
560                         next = AST_LIST_NEXT(current, list);
561                         do_state_change(current->device);
562                         ast_free(current);
563                 }
564         }
565
566         return NULL;
567 }
568
569 static void destroy_devstate_change(struct devstate_change *sc)
570 {
571         ast_free(sc);
572 }
573
574 #define MAX_SERVERS 64
575 struct change_collection {
576         struct devstate_change states[MAX_SERVERS];
577         size_t num_states;
578 };
579
580 static void devstate_cache_cb(const struct ast_event *event, void *data)
581 {
582         struct change_collection *collection = data;
583         int i;
584         const struct ast_eid *eid;
585
586         if (collection->num_states == ARRAY_LEN(collection->states)) {
587                 ast_log(LOG_ERROR, "More per-server state values than we have room for (MAX_SERVERS is %d)\n",
588                         MAX_SERVERS);
589                 return;
590         }
591
592         if (!(eid = ast_event_get_ie_raw(event, AST_EVENT_IE_EID))) {
593                 ast_log(LOG_ERROR, "Device state change event with no EID\n");
594                 return;
595         }
596
597         i = collection->num_states;
598
599         collection->states[i].state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
600         collection->states[i].eid = *eid;
601
602         collection->num_states++;
603 }
604
605 static void process_collection(const char *device, struct change_collection *collection)
606 {
607         int i;
608         struct ast_devstate_aggregate agg;
609         enum ast_device_state state;
610         struct ast_event *event;
611
612         ast_devstate_aggregate_init(&agg);
613
614         for (i = 0; i < collection->num_states; i++) {
615                 ast_debug(1, "Adding per-server state of '%s' for '%s'\n", 
616                         devstate2str(collection->states[i].state), device);
617                 ast_devstate_aggregate_add(&agg, collection->states[i].state);
618         }
619
620         state = ast_devstate_aggregate_result(&agg);
621
622         ast_debug(1, "Aggregate devstate result is %d\n", state);
623
624         event = ast_event_get_cached(AST_EVENT_DEVICE_STATE,
625                 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
626                 AST_EVENT_IE_END);
627         
628         if (event) {
629                 enum ast_device_state old_state;
630
631                 old_state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
632                 
633                 ast_event_destroy(event);
634
635                 if (state == old_state) {
636                         /* No change since last reported device state */
637                         ast_debug(1, "Aggregate state for device '%s' has not changed from '%s'\n",
638                                 device, devstate2str(state));
639                         return;
640                 }
641         }
642
643         ast_debug(1, "Aggregate state for device '%s' has changed to '%s'\n",
644                 device, devstate2str(state));
645
646         event = ast_event_new(AST_EVENT_DEVICE_STATE,
647                 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
648                 AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
649                 AST_EVENT_IE_END);
650         
651         if (!event)
652                 return;
653
654         ast_event_queue_and_cache(event,
655                 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR,
656                 AST_EVENT_IE_END);
657 }
658
659 static void handle_devstate_change(struct devstate_change *sc)
660 {
661         struct ast_event_sub *tmp_sub;
662         struct change_collection collection = {
663                 .num_states = 0,
664         };
665
666         ast_debug(1, "Processing device state change for '%s'\n", sc->device);
667
668         if (!(tmp_sub = ast_event_subscribe_new(AST_EVENT_DEVICE_STATE_CHANGE, devstate_cache_cb, &collection))) {
669                 ast_log(LOG_ERROR, "Failed to create subscription\n");
670                 return;
671         }
672
673         if (ast_event_sub_append_ie_str(tmp_sub, AST_EVENT_IE_DEVICE, sc->device)) {
674                 ast_log(LOG_ERROR, "Failed to append device IE\n");
675                 ast_event_sub_destroy(tmp_sub);
676                 return;
677         }
678
679         /* Populate the collection of device states from the cache */
680         ast_event_dump_cache(tmp_sub);
681
682         process_collection(sc->device, &collection);
683
684         ast_event_sub_destroy(tmp_sub);
685 }
686
687 static void *run_devstate_collector(void *data)
688 {
689         for (;;) {
690                 struct devstate_change *sc;
691
692                 ast_mutex_lock(&devstate_collector.lock);
693                 while (!(sc = AST_LIST_REMOVE_HEAD(&devstate_collector.devstate_change_q, entry)))
694                         ast_cond_wait(&devstate_collector.cond, &devstate_collector.lock);
695                 ast_mutex_unlock(&devstate_collector.lock);
696
697                 handle_devstate_change(sc);
698
699                 destroy_devstate_change(sc);
700         }
701
702         return NULL;
703 }
704
705 static void devstate_change_collector_cb(const struct ast_event *event, void *data)
706 {
707         struct devstate_change *sc;
708         const char *device;
709         const struct ast_eid *eid;
710         uint32_t state;
711
712         device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
713         eid = ast_event_get_ie_raw(event, AST_EVENT_IE_EID);
714         state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
715
716         if (ast_strlen_zero(device) || !eid) {
717                 ast_log(LOG_ERROR, "Invalid device state change event received\n");
718                 return;
719         }
720
721         if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(device))))
722                 return;
723
724         strcpy(sc->device, device);
725         sc->eid = *eid;
726         sc->state = state;
727
728         ast_mutex_lock(&devstate_collector.lock);
729         AST_LIST_INSERT_TAIL(&devstate_collector.devstate_change_q, sc, entry);
730         ast_cond_signal(&devstate_collector.cond);
731         ast_mutex_unlock(&devstate_collector.lock);
732 }
733
734 /*! \brief Initialize the device state engine in separate thread */
735 int ast_device_state_engine_init(void)
736 {
737         devstate_collector.event_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
738                 devstate_change_collector_cb, NULL, AST_EVENT_IE_END);
739
740         if (!devstate_collector.event_sub) {
741                 ast_log(LOG_ERROR, "Failed to create subscription for the device state change collector\n");
742                 return -1;
743         }
744
745         ast_mutex_init(&devstate_collector.lock);
746         ast_cond_init(&devstate_collector.cond, NULL);
747         if (ast_pthread_create_background(&devstate_collector.thread, NULL, run_devstate_collector, NULL) < 0) {
748                 ast_log(LOG_ERROR, "Unable to start device state collector thread.\n");
749                 return -1;
750         }
751
752         ast_cond_init(&change_pending, NULL);
753         if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
754                 ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
755                 return -1;
756         }
757
758         return 0;
759 }
760
761 void ast_devstate_aggregate_init(struct ast_devstate_aggregate *agg)
762 {
763         memset(agg, 0, sizeof(*agg));
764
765         agg->all_unavail = 1;
766         agg->all_busy = 1;
767         agg->all_free = 1;
768         agg->all_on_hold = 1;
769 }
770
771 void ast_devstate_aggregate_add(struct ast_devstate_aggregate *agg, enum ast_device_state state)
772 {
773         switch (state) {
774         case AST_DEVICE_NOT_INUSE:
775                 agg->all_unavail = 0;
776                 agg->all_busy = 0;
777                 agg->all_on_hold = 0;
778                 break;
779         case AST_DEVICE_INUSE:
780                 agg->in_use = 1;
781                 agg->all_busy = 0;
782                 agg->all_unavail = 0;
783                 agg->all_free = 0;
784                 agg->all_on_hold = 0;
785                 break;
786         case AST_DEVICE_RINGING:
787                 agg->ring = 1;
788                 agg->all_busy = 0;
789                 agg->all_unavail = 0;
790                 agg->all_free = 0;
791                 agg->all_on_hold = 0;
792                 break;
793         case AST_DEVICE_RINGINUSE:
794                 agg->in_use = 1;
795                 agg->ring = 1;
796                 agg->all_busy = 0;
797                 agg->all_unavail = 0;
798                 agg->all_free = 0;
799                 agg->all_on_hold = 0;
800                 break;
801         case AST_DEVICE_ONHOLD:
802                 agg->all_unavail = 0;
803                 agg->all_free = 0;
804                 break;
805         case AST_DEVICE_BUSY:
806                 agg->all_unavail = 0;
807                 agg->all_free = 0;
808                 agg->all_on_hold = 0;
809                 agg->busy = 1;
810                 break;
811         case AST_DEVICE_UNAVAILABLE:
812         case AST_DEVICE_INVALID:
813                 agg->all_busy = 0;
814                 agg->all_free = 0;
815                 agg->all_on_hold = 0;
816                 break;
817         case AST_DEVICE_UNKNOWN:
818                 break;
819         }
820 }
821
822 enum ast_device_state ast_devstate_aggregate_result(struct ast_devstate_aggregate *agg)
823 {
824         if (agg->all_free)
825                 return AST_DEVICE_NOT_INUSE;
826         
827         if (agg->all_on_hold)
828                 return AST_DEVICE_ONHOLD;
829         
830         if (agg->all_busy)
831                 return AST_DEVICE_BUSY;
832
833         if (agg->all_unavail)
834                 return AST_DEVICE_UNAVAILABLE;
835         
836         if (agg->ring)
837                 return agg->in_use ? AST_DEVICE_RINGINUSE : AST_DEVICE_RINGING;
838
839         if (agg->in_use)
840                 return AST_DEVICE_INUSE;
841
842         if (agg->busy)
843                 return AST_DEVICE_BUSY;
844         
845         return AST_DEVICE_NOT_INUSE;
846 }
847