68
68
<synopsis>Asterisk device</synopsis>
69
69
<description>
70
70
<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>
71
72
<para>If specified, SMDR entries will be ignored if this channel is in use during the logged call.
72
73
This is useful if certain calls are made through an FXO port with CDR already directly logged by Asterisk directly,
73
74
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 {
132
133
struct ast_channel * chan ; /*!< Dummy channel for CDR */
133
134
const char * device ; /*!< Asterisk device */
134
135
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 */
136
138
struct varshead varshead ; /*!< Variables to set on channel for CDR */
137
139
AST_LIST_ENTRY (whozz_line ) entry ; /*!< Next channel */
138
140
char data [];
@@ -354,6 +356,94 @@ static void mark_answered(struct ast_channel *chan)
354
356
ast_channel_unlock (chan );
355
357
}
356
358
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
+ break ;
432
+ }
433
+ } else {
434
+ /* Not really any way to determine the truth... */
435
+ ast_log (LOG_ERROR , "Channel %s does not exist\n" , channel_name );
436
+ }
437
+ } else if (devstate == AST_DEVICE_UNKNOWN ) {
438
+ /* chan_dahdi doesn't set specific states for non-ISDN.
439
+ * If it's unknown, then what that really indicates is not in use. */
440
+ devstate = AST_DEVICE_NOT_INUSE ;
441
+ }
442
+ return devstate ;
443
+ }
444
+
445
+ /* NOTE: Do not use the ast_device_state function directly below this point! Use fxo_device_state instead! */
446
+
357
447
static int handle_hook (struct whozz_line * w , int outbound , int end , int duration , const char * numberstr , const char * cnam )
358
448
{
359
449
if (end ) {
@@ -362,14 +452,29 @@ static int handle_hook(struct whozz_line *w, int outbound, int end, int duration
362
452
363
453
/* End call and finalize */
364
454
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 ) {
455
+ int ast_device_used ;
456
+ enum ast_device_state endstate = fxo_device_state (w -> device );
457
+ if (outbound ) {
458
+ ast_device_used = w -> startstate == AST_DEVICE_INUSE && endstate == AST_DEVICE_NOT_INUSE ;
459
+ } else {
460
+ /* The inbound case is a little bit different because there's an extra step.
461
+ * Initially, the state should always be AST_DEVICE_RINGING here,
462
+ * because you can't answer a phone call through Asterisk before the FXO port even starts ringing!
463
+ * But RINGING to start with and NOT_INUSE at the end doesn't tell us whether we answered through Asterisk or not.
464
+ * The critical thing is detecting DEVICE_INUSE at some point while the line is off-hook,
465
+ * and conveniently we check this right if/when an off-hook occurs. */
466
+ ast_device_used = w -> answerstate == AST_DEVICE_INUSE && endstate == AST_DEVICE_NOT_INUSE ;
467
+ }
468
+ if (ast_device_used ) {
367
469
/* Avoid a duplicate CDR record, since the call was made through Asterisk. */
368
470
ast_verb (6 , "Call was made through associated device, ignoring this call for SMDR purposes\n" );
369
471
__cleanup_stale_cdr (w -> chan );
370
472
w -> chan = NULL ;
371
473
return 0 ;
474
+ } else {
475
+ 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 ));
372
476
}
477
+ w -> startstate = w -> answerstate = AST_DEVICE_UNKNOWN ; /* Reset */
373
478
}
374
479
375
480
/* Now, add any variables */
@@ -404,7 +509,7 @@ static int handle_hook(struct whozz_line *w, int outbound, int end, int duration
404
509
* If it's in use, then we know that the call in question
405
510
* is being made through Asterisk, and thus we'll probably
406
511
* end up ignoring this call for CDR purposes. */
407
- w -> startstate = ast_device_state (w -> device );
512
+ w -> startstate = fxo_device_state (w -> device );
408
513
}
409
514
410
515
/* Unfortunately, we cannot use a dummy channel for CDR.
@@ -573,6 +678,9 @@ static int __process_serial_read(struct whozz_line *w, int lineno, const char *a
573
678
/* Answer it on the channel */
574
679
if (w -> chan ) {
575
680
mark_answered (w -> chan );
681
+ if (!ast_strlen_zero (w -> device )) {
682
+ w -> answerstate = fxo_device_state (w -> device );
683
+ }
576
684
} else {
577
685
ast_log (LOG_WARNING , "No call in progress, ignoring call answer\n" );
578
686
}
@@ -727,6 +835,7 @@ static int serial_loop(struct pollfd *pfd)
727
835
for (;;) {
728
836
char buf [128 ];
729
837
char * pos ;
838
+
730
839
bufres = poll (pfd , 1 , -1 );
731
840
if (bufres <= 0 ) {
732
841
if (unloading ) {
@@ -904,6 +1013,10 @@ static int load_config(void)
904
1013
}
905
1014
} else if (!strcasecmp (var -> name , "device" )) {
906
1015
device = var -> value ;
1016
+ if (strncasecmp (device , "DAHDI/" , 6 )) {
1017
+ ast_log (LOG_WARNING , "Setting 'device' must be a DAHDI device\n" );
1018
+ device = NULL ;
1019
+ }
907
1020
} else if (!strcasecmp (var -> name , "setvar" )) {
908
1021
continue ; /* Ignore on this pass */
909
1022
} else {
0 commit comments