9292 </configObject>
9393 </configFile>
9494 </configInfo>
95+ <function name="WHOZZ_LINE_STATE" language="en_US">
96+ <synopsis>
97+ Returns the line state of a WHOZZ Calling? line
98+ </synopsis>
99+ <syntax>
100+ <parameter name="line" required="true">
101+ <para>Line number</para>
102+ </parameter>
103+ </syntax>
104+ <description>
105+ <para>Returns the line state of the specified line.</para>
106+ <para>The possible values are:</para>
107+ <enumlist>
108+ <enum name="UNKNOWN"/>
109+ <enum name="ONHOOK"/>
110+ <enum name="OFFHOOK"/>
111+ <enum name="RINGING"/>
112+ </enumlist>
113+ </description>
114+ </function>
95115 ***/
96116
97117#define MODULE_NAME "res_smdr_whozz"
@@ -162,7 +182,7 @@ static int unloading = 0;
162182 ast_debug (3 , "Received %ld bytes from serial port: %s\n" , bufres , buf ); \
163183}
164184
165- static const char * state_str (enum line_state state )
185+ static const char * state_str_cli (enum line_state state )
166186{
167187 switch (state ) {
168188 case UNKNOWN :
@@ -177,6 +197,21 @@ static const char *state_str(enum line_state state)
177197 __builtin_unreachable ();
178198}
179199
200+ static const char * state_str_func (enum line_state state )
201+ {
202+ switch (state ) {
203+ case UNKNOWN :
204+ return "UNKNOWN" ;
205+ case ONHOOK :
206+ return "ONHOOK" ;
207+ case OFFHOOK :
208+ return "OFFHOOK" ;
209+ case RINGING :
210+ return "RINGING" ;
211+ }
212+ __builtin_unreachable ();
213+ }
214+
180215static int serial_getline (struct pollfd * pfd , int pollfirst , char * restrict buf , size_t len )
181216{
182217 size_t total = 0 ;
@@ -445,8 +480,92 @@ static int handle_hook(struct whozz_line *w, int outbound, int end, int duration
445480 return -1; \
446481 }
447482
483+ static void update_state (struct whozz_line * w , enum line_state newstate , enum line_state oldstate )
484+ {
485+ w -> state = newstate ;
486+ manager_event (EVENT_FLAG_CALL , "WHOZZLineStateChange" ,
487+ "LineNumber: %d\r\n"
488+ "CurrentState: %s\r\n"
489+ "OldState: %s\r\n" ,
490+ w -> lineno ,
491+ state_str_func (w -> state ),
492+ state_str_func (oldstate ));
493+ }
494+
448495static int __process_serial_read (struct whozz_line * w , int lineno , const char * action , char * buf , size_t len )
449496{
497+ enum line_state oldstate = w -> state ;
498+ /*** DOCUMENTATION
499+ <managerEvent language="en_US" name="WHOZZLineStateChange">
500+ <managerEventInstance class="EVENT_FLAG_CALL">
501+ <synopsis>Raised when the state of a phone line connected to
502+ a WHOZZ Calling? device changes.</synopsis>
503+ <syntax>
504+ <channel_snapshot/>
505+ <parameter name="LineNumber">
506+ <para>The line number</para>
507+ </parameter>
508+ <parameter name="CurrentState">
509+ <para>The current state of the line.</para>
510+ <para>The possible values are:</para>
511+ <enumlist>
512+ <enum name="UNKNOWN"/>
513+ <enum name="ONHOOK"/>
514+ <enum name="OFFHOOK"/>
515+ <enum name="RINGING"/>
516+ </enumlist>
517+ </parameter>
518+ <parameter name="OldState">
519+ <para>The old state of the line.</para>
520+ <para>The possible values are:</para>
521+ <enumlist>
522+ <enum name="UNKNOWN"/>
523+ <enum name="ONHOOK"/>
524+ <enum name="OFFHOOK"/>
525+ <enum name="RINGING"/>
526+ </enumlist>
527+ </parameter>
528+ </syntax>
529+ <description>
530+ <para>This event is raised whenever the line state of a line changes.</para>
531+ </description>
532+ </managerEventInstance>
533+ </managerEvent>
534+ <managerEvent language="en_US" name="WHOZZLineCall">
535+ <managerEventInstance class="EVENT_FLAG_CALL">
536+ <synopsis>Raised when a call begins or ends on a phone line
537+ connected to a WHOZZ Calling? device.</synopsis>
538+ <syntax>
539+ <channel_snapshot/>
540+ <parameter name="LineNumber">
541+ <para>The line number</para>
542+ </parameter>
543+ <parameter name="Direction">
544+ <para>IN or OUT.</para>
545+ </parameter>
546+ <parameter name="Duration">
547+ <para>Call duration, in seconds.</para>
548+ <para>Only provided when a call ends.</para>
549+ </parameter>
550+ <parameter name="CalledNumber">
551+ <para>The called number.</para>
552+ <para>Only provided for outgoing calls.</para>
553+ </parameter>
554+ <parameter name="CallerNumber">
555+ <para>The calling number.</para>
556+ <para>Only provided for incoming calls.</para>
557+ </parameter>
558+ <parameter name="CallerName">
559+ <para>The calling name.</para>
560+ <para>Only provided for incoming calls.</para>
561+ </parameter>
562+ </syntax>
563+ <description>
564+ <para>This event is raised whenever the line state of a line changes.</para>
565+ </description>
566+ </managerEventInstance>
567+ </managerEvent>
568+ ***/
450569 if (!strcasecmp (action , "F" )) {
451570 ast_verb (5 , "Off-hook on line %d\n" , lineno );
452571 if (w -> state == RINGING ) {
@@ -458,13 +577,13 @@ static int __process_serial_read(struct whozz_line *w, int lineno, const char *a
458577 ast_log (LOG_WARNING , "No call in progress, ignoring call answer\n" );
459578 }
460579 }
461- w -> state = OFFHOOK ;
580+ update_state ( w , OFFHOOK , oldstate ) ;
462581 } else if (!strcasecmp (action , "N" )) {
463582 ast_verb (5 , "On-hook on line %d\n" , lineno );
464- w -> state = ONHOOK ;
583+ update_state ( w , ONHOOK , oldstate ) ;
465584 } else if (!strcasecmp (action , "R" )) {
466585 ast_verb (5 , "First ring on line %d\n" , lineno );
467- w -> state = RINGING ;
586+ update_state ( w , RINGING , oldstate ) ;
468587 /* This could be followed by I S: Incoming Call Start (whether or not the line has Caller ID, and even if call is answered before first ring ends)
469588 * That, in turn, is followed by F, for off hook if somebody answers it,
470589 * or I E (Incoming Call End), if ring no answer. */
@@ -498,11 +617,62 @@ static int __process_serial_read(struct whozz_line *w, int lineno, const char *a
498617 duration = atoi (durationstr );
499618 if (callend ) {
500619 ast_log (LOG_NOTICE , "%s call %s on line %d to %s (%d s)\n" , outbound ? "Outbound" : "Inbound" , "ended" , lineno , numberstr , duration );
620+ /* If we were previously ringing, and never went off-hook,
621+ * there won't be an on-hook event that will reset the line state,
622+ * do it here. */
623+ if (oldstate == RINGING ) {
624+ update_state (w , ONHOOK , oldstate );
625+ }
501626 } else {
502627 ast_log (LOG_NOTICE , "%s call %s on line %d to %s\n" , outbound ? "Outbound" : "Inbound" , "began" , lineno , numberstr );
503628 }
504629 /* Log/store the appropriate details */
505630 handle_hook (w , outbound , callend , duration , numberstr , cnam );
631+ if (callend ) {
632+ if (outbound ) {
633+ manager_event (EVENT_FLAG_CALL , "WHOZZLineCall" ,
634+ "LineNumber: %d\r\n"
635+ "Direction: %s\r\n"
636+ "Duration: %d\r\n"
637+ "CalledNumber: %s\r\n" ,
638+ w -> lineno ,
639+ outbound ? "OUT" : "IN" ,
640+ duration ,
641+ numberstr );
642+ } else {
643+ manager_event (EVENT_FLAG_CALL , "WHOZZLineCall" ,
644+ "LineNumber: %d\r\n"
645+ "Direction: %s\r\n"
646+ "Duration: %d\r\n"
647+ "CallerNumber: %s\r\n"
648+ "CallerName: %s\r\n" ,
649+ w -> lineno ,
650+ outbound ? "OUT" : "IN" ,
651+ duration ,
652+ numberstr ,
653+ cnam );
654+ }
655+ } else {
656+ if (outbound ) {
657+ manager_event (EVENT_FLAG_CALL , "WHOZZLineCall" ,
658+ "LineNumber: %d\r\n"
659+ "Direction: %s\r\n"
660+ "CalledNumber: %s\r\n" ,
661+ w -> lineno ,
662+ outbound ? "OUT" : "IN" ,
663+ numberstr );
664+ } else {
665+ manager_event (EVENT_FLAG_CALL , "WHOZZLineCall" ,
666+ "LineNumber: %d\r\n"
667+ "Direction: %s\r\n"
668+ "CallerNumber: %s\r\n"
669+ "CallerName: %s\r\n" ,
670+ w -> lineno ,
671+ outbound ? "OUT" : "IN" ,
672+ numberstr ,
673+ cnam );
674+ }
675+ }
506676 }
507677 return 0 ;
508678}
@@ -633,6 +803,34 @@ static void *__serial_monitor(void *varg)
633803 return NULL ;
634804}
635805
806+ static int whozz_line_state_read (struct ast_channel * chan , const char * cmd , char * parse , char * buffer , size_t buflen )
807+ {
808+ int lineno ;
809+ struct whozz_line * w ;
810+
811+ if (ast_strlen_zero (parse )) {
812+ ast_log (LOG_ERROR , "Line number required for %s\n" , cmd );
813+ return -1 ;
814+ }
815+
816+ lineno = atoi (parse );
817+ AST_RWLIST_RDLOCK (& lines );
818+ AST_RWLIST_TRAVERSE (& lines , w , entry ) {
819+ if (w -> lineno == lineno ) {
820+ ast_copy_string (buffer , state_str_func (w -> state ), buflen );
821+ break ;
822+ }
823+ }
824+ AST_RWLIST_UNLOCK (& lines );
825+
826+ return w ? 0 : -1 ;
827+ }
828+
829+ static struct ast_custom_function acf_whozz = {
830+ .name = "WHOZZ_LINE_STATE" ,
831+ .read = whozz_line_state_read ,
832+ };
833+
636834static char * handle_show_whozz (struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
637835{
638836 struct whozz_line * w ;
@@ -655,7 +853,7 @@ static char *handle_show_whozz(struct ast_cli_entry *e, int cmd, struct ast_cli_
655853 ast_cli (a -> fd , "%4s %-8s %s\n" , "Line" , "State" , "Associated Device" );
656854 AST_RWLIST_RDLOCK (& lines );
657855 AST_RWLIST_TRAVERSE (& lines , w , entry ) {
658- ast_cli (a -> fd , "%4d %-8s %s\n" , w -> lineno , state_str (w -> state ), S_OR (w -> device , "" ));
856+ ast_cli (a -> fd , "%4d %-8s %s\n" , w -> lineno , state_str_cli (w -> state ), S_OR (w -> device , "" ));
659857 }
660858 AST_RWLIST_UNLOCK (& lines );
661859
@@ -763,6 +961,7 @@ static int unload_module(void)
763961
764962 unloading = 1 ;
765963 ast_cli_unregister_multiple (whozz_cli , ARRAY_LEN (whozz_cli ));
964+ ast_custom_function_unregister (& acf_whozz );
766965 if (serial_fd != -1 ) {
767966 if (serial_thread != AST_PTHREADT_NULL ) {
768967 /* Interrupt any system call */
@@ -826,6 +1025,7 @@ static int load_module(void)
8261025 return AST_MODULE_LOAD_DECLINE ;
8271026 }
8281027
1028+ ast_custom_function_register (& acf_whozz );
8291029 ast_cli_register_multiple (whozz_cli , ARRAY_LEN (whozz_cli ));
8301030 return res ;
8311031}
0 commit comments