6868 <synopsis>Asterisk device</synopsis>
6969 <description>
7070 <para>Asterisk device corresponding to this SMDR line, if applicable.</para>
71+ <para>If provided, this must be a <literal>chan_dahdi</literal> FXO (FXS-signalled) device connected to the same phone line.</para>
7172 <para>If specified, SMDR entries will be ignored if this channel is in use during the logged call.
7273 This is useful if certain calls are made through an FXO port with CDR already directly logged by Asterisk directly,
7374 but other calls are made directly on the line and not through the FXO port. Setting this option appropriately
@@ -132,7 +133,8 @@ struct whozz_line {
132133 struct ast_channel * chan ; /*!< Dummy channel for CDR */
133134 const char * device ; /*!< Asterisk device */
134135 enum line_state state ; /*!< Current line state */
135- enum ast_device_state startstate ; /*!< Starting device state of associated device, if applicable */
136+ enum ast_device_state startstate ; /*!< Starting device state of associated FXO device, if applicable */
137+ enum ast_device_state answerstate ; /*!< Device state of associated FXO device, if applicable, at time of off-hook on incoming call */
136138 struct varshead varshead ; /*!< Variables to set on channel for CDR */
137139 AST_LIST_ENTRY (whozz_line ) entry ; /*!< Next channel */
138140 char data [];
@@ -354,6 +356,93 @@ static void mark_answered(struct ast_channel *chan)
354356 ast_channel_unlock (chan );
355357}
356358
359+ static int fxo_device_state (const char * device )
360+ {
361+ /* Non-ISDN DAHDI channels (e.g. analog) are always "unknown" when not in use...
362+ * not sure I agree with this, but this is the way it is currently, so account for that.
363+ *
364+ * Furthermore, the device state is misleading and CANNOT BE USED for determining the line state.
365+ * - When the connected phone line is idle, the state will be "Unknown".
366+ * - When the FXO port is in use, the state will be "In Use" (good).
367+ * - When there is an incoming call ringing to the FXO port, its device state will be... "In Use".
368+ *
369+ * To understand what's going on, recall the Asterisk fallback for computing device state simply
370+ * finds a channel associated with the device and converts its channel state to a mapped device state.
371+ *
372+ * AST_STATE_RING -> AST_DEVICE_INUSE
373+ * AST_STATE_RINGING -> AST_DEVICE_RINGING
374+ *
375+ * STATE_RING essentially corresponds to audible ring being present on the channel, while
376+ * STATE_RINGING corresponds to the device actually ringing, e.g. actual physical 90V power ring.
377+ *
378+ * However, RINGING only makes sense when Asterisk is ringing a device (whether it's DAHDI or not).
379+ * For example, when ringing a phone, its DAHDI channel will have channel state (and device state) "Ringing".
380+ *
381+ * However, on incoming calls to an FXO port, Asterisk isn't ringing it... the opposite, in fact!
382+ * The network is indicating to us that there is an incoming call. And in all other scenarios,
383+ * the incoming channel side that is ringing a phone has state "Ring". FXO ports are a special edge
384+ * case where there is something physically ringing (perhaps phones on the connected line), but it's
385+ * not really "Ringing" in the semantic sense that Asterisk uses it.
386+ *
387+ * TL;DR To distinguish between FXO port being rung and actually active and in use, do not use
388+ * ${DEVICE_STATE(DAHDI/1)}
389+ * In both cases, it will return "INUSE".
390+ * Instead, do:
391+ * ${IMPORT(DAHDI/1-1,CHANNEL(state))}
392+ * This is because chan_dahdi doesn't support call waiting on FXO ports (currently),
393+ * so there will only ever be one channel max on an FXO port, and so we know what it will be called.
394+ * Therefore, if we know the device is in use, then we can use this to check if Asterisk is off-hook on the FXO port.
395+ * If it returns "Ring", it's just ringing and the FXO port is idle.
396+ * If it returns "Up", then we're actually off-hook on the FXO port.
397+ *
398+ * The code below does the internal equivalent of ${IMPORT(DAHDI/1-1,CHANNEL(state))}
399+ */
400+
401+ /* Use the device state to get started, but for the reasons described at length above, we can't use that alone.
402+ * We need to narrow it down further to distinguish between ringing and actually in use. */
403+ enum ast_device_state devstate = ast_device_state (device );
404+ if (devstate == AST_DEVICE_INUSE ) {
405+ struct ast_channel * chan2 ;
406+ char channel_name [64 ];
407+ /* The DAHDI channel naming scheme is predictable, and FXO ports are only going to have 1 channel, currently,
408+ * so that simplifies this to a straightforward translation. */
409+ snprintf (channel_name , sizeof (channel_name ), "%s-1" , device );
410+ if ((chan2 = ast_channel_get_by_name (channel_name ))) {
411+ enum ast_channel_state state ;
412+ ast_channel_lock (chan2 );
413+ state = ast_channel_state (chan2 );
414+ ast_channel_unlock (chan2 );
415+ chan2 = ast_channel_unref (chan2 );
416+
417+ /* Based on the state, do a conversion. */
418+ ast_debug (3 , "Channel state of %s is %s\n" , channel_name , ast_state2str (state ));
419+ switch (state ) {
420+ case AST_STATE_RING :
421+ /* Normally, this would map to AST_DEVICE_INUSE.
422+ * For our purposes, we return AST_DEVICE_RINGING,
423+ * to reflect the FXO port ringing. Semantically,
424+ * this breaks with Asterisk's idea of what device state
425+ * refers to, but we're really just repurposing the enum
426+ * for something specific here. */
427+ devstate = AST_DEVICE_RINGING ;
428+ break ;
429+ default :
430+ /* Leave it alone */
431+ }
432+ } else {
433+ /* Not really any way to determine the truth... */
434+ ast_log (LOG_ERROR , "Channel %s does not exist\n" , channel_name );
435+ }
436+ } else if (devstate == AST_DEVICE_UNKNOWN ) {
437+ /* chan_dahdi doesn't set specific states for non-ISDN.
438+ * If it's unknown, then what that really indicates is not in use. */
439+ devstate = AST_DEVICE_NOT_INUSE ;
440+ }
441+ return devstate ;
442+ }
443+
444+ /* NOTE: Do not use the ast_device_state function directly below this point! Use fxo_device_state instead! */
445+
357446static int handle_hook (struct whozz_line * w , int outbound , int end , int duration , const char * numberstr , const char * cnam )
358447{
359448 if (end ) {
@@ -362,14 +451,29 @@ static int handle_hook(struct whozz_line *w, int outbound, int end, int duration
362451
363452 /* End call and finalize */
364453 if (!ast_strlen_zero (w -> device )) {
365- enum ast_device_state endstate = ast_device_state (w -> device );
366- if (w -> startstate == AST_DEVICE_INUSE && endstate == AST_DEVICE_NOT_INUSE ) {
454+ int ast_device_used ;
455+ enum ast_device_state endstate = fxo_device_state (w -> device );
456+ if (outbound ) {
457+ ast_device_used = w -> startstate == AST_DEVICE_INUSE && endstate == AST_DEVICE_NOT_INUSE ;
458+ } else {
459+ /* The inbound case is a little bit different because there's an extra step.
460+ * Initially, the state should always be AST_DEVICE_RINGING here,
461+ * because you can't answer a phone call through Asterisk before the FXO port even starts ringing!
462+ * But RINGING to start with and NOT_INUSE at the end doesn't tell us whether we answered through Asterisk or not.
463+ * The critical thing is detecting DEVICE_INUSE at some point while the line is off-hook,
464+ * and conveniently we check this right if/when an off-hook occurs. */
465+ ast_device_used = w -> answerstate == AST_DEVICE_INUSE && endstate == AST_DEVICE_NOT_INUSE ;
466+ }
467+ if (ast_device_used ) {
367468 /* Avoid a duplicate CDR record, since the call was made through Asterisk. */
368469 ast_verb (6 , "Call was made through associated device, ignoring this call for SMDR purposes\n" );
369470 __cleanup_stale_cdr (w -> chan );
370471 w -> chan = NULL ;
371472 return 0 ;
473+ } else {
474+ ast_debug (2 , "FXO state of %s was %s and is now %s\n" , w -> device , ast_devstate_str (w -> startstate ), ast_devstate_str (endstate ));
372475 }
476+ w -> startstate = w -> answerstate = AST_DEVICE_UNKNOWN ; /* Reset */
373477 }
374478
375479 /* Now, add any variables */
@@ -404,7 +508,7 @@ static int handle_hook(struct whozz_line *w, int outbound, int end, int duration
404508 * If it's in use, then we know that the call in question
405509 * is being made through Asterisk, and thus we'll probably
406510 * end up ignoring this call for CDR purposes. */
407- w -> startstate = ast_device_state (w -> device );
511+ w -> startstate = fxo_device_state (w -> device );
408512 }
409513
410514 /* Unfortunately, we cannot use a dummy channel for CDR.
@@ -573,6 +677,9 @@ static int __process_serial_read(struct whozz_line *w, int lineno, const char *a
573677 /* Answer it on the channel */
574678 if (w -> chan ) {
575679 mark_answered (w -> chan );
680+ if (!ast_strlen_zero (w -> device )) {
681+ w -> answerstate = fxo_device_state (w -> device );
682+ }
576683 } else {
577684 ast_log (LOG_WARNING , "No call in progress, ignoring call answer\n" );
578685 }
@@ -727,6 +834,7 @@ static int serial_loop(struct pollfd *pfd)
727834 for (;;) {
728835 char buf [128 ];
729836 char * pos ;
837+
730838 bufres = poll (pfd , 1 , -1 );
731839 if (bufres <= 0 ) {
732840 if (unloading ) {
@@ -904,6 +1012,10 @@ static int load_config(void)
9041012 }
9051013 } else if (!strcasecmp (var -> name , "device" )) {
9061014 device = var -> value ;
1015+ if (strncasecmp (device , "DAHDI/" , 6 )) {
1016+ ast_log (LOG_WARNING , "Setting 'device' must be a DAHDI device\n" );
1017+ device = NULL ;
1018+ }
9071019 } else if (!strcasecmp (var -> name , "setvar" )) {
9081020 continue ; /* Ignore on this pass */
9091021 } else {
0 commit comments