Current trunk is about to become new release. So lets put it into stable.

This commit is contained in:
MelwareDE 2009-07-23 14:11:08 +00:00
parent 628b685080
commit 28ff6d6cdf
29 changed files with 5421 additions and 424 deletions

85
CHANGES
View File

@ -1,20 +1,45 @@
CHANGES CHANGES
======= =======
HEAD
------------------
- fixed buffer length error with internal libcapi debug code.
- performance optimizations, use debug code when needed only.
- added commands for media features supported by Dialogic(R) Diva(R) Media Boards
- added variable CAPI_CIP for full access to all bearer capabilities.
- fixed CAPI 'chat' for using more than 8 controllers.
- fixed CAPI 'chat' for not exceeding maximal size of CAPI message
- add color fax
- add autodetection of fax file format for sendfax command (SFF, CFF JPEG, CFF T.43, TEXT)
- add extension for FAX paper formats and resolutions
- adjust NULL PLCI LI path
- add resource PLCI
- add echocancelpath configuration option
- fixed possible race condition while waiting for DISCONNECT_CONF
- add static half duplex conference
- add dynamic half duplex conference
- add clear channel fax
- add DTMF detection for NULL PLCI
- use direct access to vocoders without RTP framing
- play message to conference and music on hold to caller
- add commands to remove users from chat
- allow to specity the 'ETS 300 102-1' called party number octet 3
chan_capi-1.1.2 chan_capi-1.1.2
------------------ ------------------
- added config setting 'faxdetecttime' to limit the fax detection for a given amount of seconds. - added config setting 'faxdetecttime' to limit the fax detection for a given amount of seconds.
- added config option for subscriber prefix. Some lines may show local calls without area code - added config option for subscriber prefix. Some lines may show local calls without area code
signaled as subscriber-number. Here the complete prefix including area code must be added. signaled as subscriber-number. Here the complete prefix including area code must be added.
- better counting of active b-channels. - better counting of active B-channels.
- make capicommand(progress) "early-B3" usable for non NT-mode incoming channels too. - make capicommand(progress) "early-B3" usable for non NT-mode incoming channels as well.
- support early Line-Interconnect (bridge) as soon as both b-channels are up. This bridges b-channels - support early Line-Interconnect (bridge) as soon as both B-channels are up. This bridges B-channels
from beginning of call-establishment, even before calls are connected and the bridge command is received. from beginning of the call-establishment, even before calls are connected and the bridge command is received.
Dial() option 'G' is used to activate this feature. The dial() option 'G' is used to activate this feature.
- fixed big-endian issue for DATA_B3 messages in internal libcapi code. - fixed big-endian issue for DATA_B3 messages in internal libcapi code.
- fixed NULL-pointer when no digits are signaled in DID mode. - fixed NULL-pointer when no digits are signaled in DID mode.
- adapt to new Asterisk 1.6.1 changes. - adapt to new Asterisk 1.6.1 changes.
- added capicommand to set CAPI application id into an Asterisk dialplan variable. - added capicommand to set CAPI application ID into an Asterisk dialplan variable.
chan_capi-1.1.1 chan_capi-1.1.1
@ -34,8 +59,8 @@ chan_capi-1.1.0
- adapt to new asterisk 1.6 API - adapt to new asterisk 1.6 API
- fixed reading capi profile on big-endian - fixed reading capi profile on big-endian
- increased maximum number of CAPI controllers to 64 (needed for big PBX). - increased maximum number of CAPI controllers to 64 (needed for big PBX).
- if immedіate=yes is set in DID mode, accept calls to empty DNID (needed for Austrian lines). - if immediate=yes is set in DID mode, accept calls to empty DNID (needed for Austrian lines).
- use own libcapi20 implementation by default (no library for capi is needed any more). - use own libcapi20 implementation by default (no library for CAPI is needed any more).
chan_capi-1.0.2 chan_capi-1.0.2
------------------ ------------------
@ -43,7 +68,7 @@ chan_capi-1.0.2
- added 'x' option to capicommand(ect) to have real 'explicit call transfer' - added 'x' option to capicommand(ect) to have real 'explicit call transfer'
(needed by some ISDN lines) (needed by some ISDN lines)
- support CCBS (call completion on busy subscriber) - support CCBS (call completion on busy subscriber)
- added capicommand(chat) for CAPI based MeetMe/Conference using onboard DSPs. - added capicommand(chat) for CAPI-based MeetMe/Conference using onboard DSPs.
- fixed ton-display in 'show capi channels' on outgoing line. - fixed ton-display in 'show capi channels' on outgoing line.
- fix for 64bit support - fix for 64bit support
- Asterisk 1.4.4 adaptions - Asterisk 1.4.4 adaptions
@ -55,12 +80,12 @@ chan_capi-1.0.2
chan_capi-1.0.1 chan_capi-1.0.1
------------------ ------------------
- added qsig caller-name patch by Mario Goegel - added qsig caller-name patch by Mario Goegel
- recognise asterisk 1.4.1 - recognize asterisk 1.4.1
- register at CAPI with needed maxLogicalConnections only - register at CAPI with needed maxLogicalConnections only
- don't send SELECT_B_PROTOCOL more than once - do not send SELECT_B_PROTOCOL more than once
- don't send more DATA_B3 messages than allowed by CAPI_MAX_B3_BLOCKS - do not send more DATA_B3 messages than allowed by CAPI_MAX_B3_BLOCKS
- added 'k' option to capi receivefax command for not deleting bad faxes - added 'k' option to capi receivefax command for not deleting bad faxes
- added b-channel number to channel name for better identification - added B-channel number to channel name for better identification
- added variable setting REDIRECTIONNUMBER on outgoing call - added variable setting REDIRECTIONNUMBER on outgoing call
- fixed deadlock with ast_async_goto on fax tone detection - fixed deadlock with ast_async_goto on fax tone detection
- listen to CAPI supplementary information - listen to CAPI supplementary information
@ -84,15 +109,15 @@ chan_capi-1.0.0
- Read the channel frames during wait for fax finish. - Read the channel frames during wait for fax finish.
- Added progress when in faxmode to wakeup asterisk-1.2. - Added progress when in faxmode to wakeup asterisk-1.2.
(needed for e.g. auto-hangup on timeout) (needed for e.g. auto-hangup on timeout)
- Don't error on invalid controller in capi.conf, just ignore it. - Do not error on invalid controller in capi.conf, just ignore it.
- Added 3PTY patch by Simon Peter. - Added 3PTY patch by Simon Peter.
- Allow echo-cancel even with old capi configuration bit for - Allow echo-cancel even with old CAPI configuration bit for
echo-cancel. echo-cancel.
- Fix compiler warnings. - Fix compiler warnings.
- Fixed callerid on incoming call with Asterisk 1.4 (PR#25) - Fixed callerid on incoming call with Asterisk 1.4 (PR#25)
- Remove possible race condition in with hangup and DISCONNECT_IND. - Remove possible race condition in with hangup and DISCONNECT_IND.
- Rixed gain and echosquelch use according to transfercapability. - Rixed gain and echosquelch use according to transfercapability.
- Don't wait for DISCONNECT_B3_CONF in activehangup. - Do not wait for DISCONNECT_B3_CONF in activehangup.
- Reset PLCI on DISCONNECT_IND to avoid race if asterisk is too slow - Reset PLCI on DISCONNECT_IND to avoid race if asterisk is too slow
with hangup command. with hangup command.
- Added Asterisk 1.4 jitterbuffer usage. - Added Asterisk 1.4 jitterbuffer usage.
@ -126,7 +151,7 @@ chan_capi-cm-0.6.5
chan_capi-cm-0.6.4 chan_capi-cm-0.6.4
------------------ ------------------
- don't do echo-squelch or gain if transfercapability is non-voice - do not do echo-squelch or gain if transfercapability is non-voice
- fix deadlock when changing to fax mode - fix deadlock when changing to fax mode
- better capi message handling - better capi message handling
- removed double memset to zero - removed double memset to zero
@ -148,15 +173,15 @@ chan_capi-cm-0.6.2
- set some info variables when receiving fax - set some info variables when receiving fax
- added language support - added language support
- prepared devicestate(hint) support - prepared devicestate(hint) support
- don't change early-B3 setting on conf error for CONNECT_B3 - do not change early-B3 setting on conf error for CONNECT_B3
- small fixes in Makefile targets (thanks to Karsten Keil) - small fixes in Makefile targets (thanks to Karsten Keil)
- don't send audio to local exchange when in TE mode. - do not send audio to local exchange when in TE-mode.
- fix missing CONF messages when no interface is found - fix missing CONF messages when no interface is found
- small transfercap and overlapdial fix - small transfercap and overlapdial fix
- don't forward DTMF if in NT-mode and the line is not connected yet. - do not forward DTMF if in NT-mode and the line is not yet connected
- fixed line interconnect - fixed line interconnect
- b-channel handling better - B-channel handling better
- NT mode progress - NT-mode progress
- removed deadlock in faxreceive. - removed deadlock in faxreceive.
- initialize variable ocid - initialize variable ocid
- correct use of timeoutms in native bridge - correct use of timeoutms in native bridge
@ -173,26 +198,26 @@ chan_capi-cm-0.6.1
used if dial option 'd' is specified. used if dial option 'd' is specified.
- moved ast_softhangup() out of interface lock - moved ast_softhangup() out of interface lock
- use correct mutex_init call for interface lock - use correct mutex_init call for interface lock
- when 'o' option is used for overlap dialing, don't send any digits - when 'o' option is used for overlap dialing, do not send any digits
with the CONNECT_REQ. This gives better progress together with 'b'. with the CONNECT_REQ. This gives better progress together with 'b'.
- create a pseudo channel for each interface for incoming signalling - create a pseudo channel for each interface for incoming signalling
without B-channel. without B-channel.
- added channel locks - added channel locks
- fixed capi init order (thanks to Hans Petter Selasky) - fixed CAPI init order (thanks to Hans Petter Selasky)
- fixed did handling - fixed DID handling
- set RDNIS if redirecting number was received. - set RDNIS if redirecting number was received.
- simplified call to ast_exists_extension() - simplified call to ast_exists_extension()
- when check for valid extension, check the callerid as well - when check for valid extension, check the callerid as well
- changed call-waiting and deflect handling in CONNECT_IND - changed call-waiting and deflect handling in CONNECT_IND
- use 'immediate' config in MSN mode, if pbx shall be started on - use 'immediate' config in MSN mode, if PBX shall be started on
CONNECT_IND and shall not wait until SETUP/SENDING-COMPLETE was received. CONNECT_IND and shall not wait until SETUP/SENDING-COMPLETE was received.
Since info like REDIRECTINGNUMBER will come after CONNECT_IND, this may Since info like REDIRECTINGNUMBER will come after CONNECT_IND, this may
be lost then. But for some drivers/telcos/pbx, this setting is needed. be lost then. But for some drivers/telcos/pbx, this setting is needed.
- fix start of line interconnect in old mode. - fix start of line interconnect in old mode.
- start early-b3 on PROCEEDING too. - start early-b3 on PROCEEDING too.
- don't send audio data, if in fax receive mode - do not send audio data, if in fax receive mode
- disconnect on finished fax immediately - disconnect on finished fax immediately
- don't run through gain list, if gain is 1.0. - do not run through gain list, if gain is 1.0.
- use correct A-law idle value. - use correct A-law idle value.
- removed old example from capi.conf - removed old example from capi.conf
@ -224,12 +249,12 @@ chan_capi-cm-0.6
- receive a fax via CAPI is now done with capicommand(receivefax|...) and added stationid... - receive a fax via CAPI is now done with capicommand(receivefax|...) and added stationid...
- fixed call-deflection and moved this feature from separate application - fixed call-deflection and moved this feature from separate application
to capicommand(). to capicommand().
- added config option 'immediate' to start pbx if no dnid has been received yet. - added config option 'immediate' to start PBX if no dnid has been received yet.
- endian fixes - endian fixes
- compile fixes with newer Asterisk - compile fixes with newer Asterisk
- update channel name on did changes. - update channel name on did changes.
- support 'type of number' (numbering-plan). - support 'type of number' (numbering-plan).
- U-Law setting is now done in capi.conf instead of Makefile define. - u-Law setting is now done in capi.conf instead of Makefile define.
- allow using interface name in Dial(). - allow using interface name in Dial().
- on hangup, use hangupcause from other channel or from var PRI_CAUSE. - on hangup, use hangupcause from other channel or from var PRI_CAUSE.
- improved DID handling on PtP connections. - improved DID handling on PtP connections.

View File

@ -4,12 +4,12 @@ INSTALL
Modify the Makefile to fit your system, especially the path to the Asterisk Modify the Makefile to fit your system, especially the path to the Asterisk
include files. include files.
To build the driver you will need an installed Capi system, including header To build the driver you will need an installed CAPI system, including header
files. files.
By default an internal version of libcapi20 is used (you don't need libcapi20 to By default, an internal version of libcapi20 is used (you do not need libcapi20 to
be installed on your system). If you don't want this and the installed libcapi20 be installed on your system). If you do not want this and the installed libcapi20
shall be used, add the option should be used, add the option
USE_OWN_LIBCAPI=no USE_OWN_LIBCAPI=no
to the 'make' command. to the 'make' command.

View File

@ -97,7 +97,7 @@ INSTALL=install
SHAREDOS=chan_capi.so SHAREDOS=chan_capi.so
OBJECTS=chan_capi.o chan_capi_utils.o chan_capi_rtp.o xlaw.o \ OBJECTS=chan_capi.o chan_capi_utils.o chan_capi_rtp.o chan_capi_command.o xlaw.o dlist.o \
chan_capi_qsig_core.o chan_capi_qsig_ecma.o chan_capi_qsig_asn197ade.o \ chan_capi_qsig_core.o chan_capi_qsig_ecma.o chan_capi_qsig_asn197ade.o \
chan_capi_qsig_asn197no.o chan_capi_supplementary.o chan_capi_chat.o chan_capi_qsig_asn197no.o chan_capi_supplementary.o chan_capi_chat.o

142
README
View File

@ -29,7 +29,7 @@ No support for Asterisk 1.0.x any more, you need at least
Asterisk 1.2.x , 1.4.x or 1.6.x. Asterisk 1.2.x , 1.4.x or 1.6.x.
Note: Note:
Eicon DIVA Server cards are now named Dialogic(R) Diva(R). Eicon DIVA Server cards are now named Dialogic(R) Diva(R) Media Boards.
This chan_capi version includes: This chan_capi version includes:
===================================================== =====================================================
@ -44,9 +44,9 @@ This chan_capi version includes:
- Overlap sending (dialtone and additional digits) - Overlap sending (dialtone and additional digits)
- E(xplicit) C(all) T(ransfer) (...although it's done implicit-but don't tell!) - E(xplicit) C(all) T(ransfer) (...although it's done implicit-but don't tell!)
- Use asterisks internal DSP functions for DTMF - Use asterisks internal DSP functions for DTMF
- Alaw support - a-Law support
- Ulaw support! - u-Law support!
- Dialogic DSP echo cancelation (echocancel=1) - Dialogic(R) Diva(R) software DSP echo cancellation (echocancel=1)
- Reject call waiting (ACO) - Reject call waiting (ACO)
- DID for Point to Point mode (a.k.a overlap receiving) - DID for Point to Point mode (a.k.a overlap receiving)
- Rx/Tx gains using positive linar value (rxgain=1.0, txgain=1.0 means no change) - Rx/Tx gains using positive linar value (rxgain=1.0, txgain=1.0 means no change)
@ -86,26 +86,26 @@ The Dial string
using '-'. The according interface is found by searching a match with using '-'. The according interface is found by searching a match with
the 'group' specified in the capi.conf for each interface. the 'group' specified in the capi.conf for each interface.
The optional <callerid> followed by an ':' can be used to set a callerid The optional <callerid> followed by an ':' can be used to set a caller ID
for this dial() command, without changing the original channel's callerid. for this dial() command, without changing the original channel's caller ID.
'params' is an optional part to set special settings for this call. 'params' is an optional part to set special settings for this call.
The string consists of a list of characters with the following meaning: The string consists of a list of characters with the following meaning:
'b' : early B3 always. 'b' : early B3 always.
'B' : early B3 on successful calls only. 'B' : early B3 on successful calls only.
'd' : use the default caller id which is set by defaultcid= in capi.conf 'd' : use the default caller ID that is set by defaultcid= in capi.conf
'o' : use overlap sending of number. 'o' : use overlap sending of number.
(Useful if additional digits shall be send afterwards or together (Useful if additional digits should be send afterwards or together
with 'b' to get dialtone and then send the number, e.g. if otherwise with 'b' to get the dialtone and then send the number, e.g., if
no progress tones are available) no progress tones are available)
's' : activate 'stay-online': don't disconnect CAPI connection on Hangup. 's' : activate 'stay-online': do not disconnect CAPI connection on hangup.
This is needed to give additional commands like CCBS after Hangup. This is needed to give additional commands like CCBS after hangup.
To really hangup the CAPI connection, use either capicommand(hangup) To really hang up the CAPI connection, use either capicommand(hangup)
or wait for chan_capi/network timeout (about 20 seconds). or wait for chan_capi/network timeout (about 20 seconds).
'G' : early Line-Interconnect / bridge: Use Line-Interconnect as soon as 'G' : early Line-Interconnect / bridge: Use Line-Interconnect as soon as
both b-channels are up. Both channels must be of type CAPI and both B-channels are up. Both channels must be of type the CAPI and
the incoming call may need 'capicommand(progress)' to enable early-B3 the incoming call may need 'capicommand(progress)' to enable Early B3
on it as well as dial option 'b' for the outgoing channel. on it as well as the dial option 'b' for the outgoing channel.
Before Dial(), the capicommand(peerlink) must be used to signal the Before Dial(), the capicommand(peerlink) must be used to signal the
dialed channel its peer. dialed channel its peer.
@ -143,7 +143,7 @@ CAPI command application
======================================== ========================================
chan_capi provides an additional Asterisk application chan_capi provides an additional Asterisk application
capicommand() capicommand()
With this application, special capi commands and features can be used. With this application, special CAPI commands and features can be used.
Call Deflection: Call Deflection:
Forwards an unanswered call to another number. Forwards an unanswered call to another number.
@ -151,29 +151,29 @@ Call Deflection:
exten => s,1,capicommand(deflect|12345678) exten => s,1,capicommand(deflect|12345678)
Fax receive: Fax receive:
Receive a fax using CAPI. Receives a fax using CAPI.
Example: Example:
exten => s,1,capicommand(receivefax|/tmp/${UNIQUEID}|+49 6137 555123|Asterisk|k) exten => s,1,capicommand(receivefax|/tmp/${UNIQUEID}|+49 6137 555123|Asterisk|k)
(more see below) (more see below)
Fax send: Fax send:
Send a fax using CAPI. Sends a fax using CAPI.
Example: Example:
exten => s,1,capicommand(sendfax|/path/to/faxfile.sff|+49 6137 555123|Asterisk) exten => s,1,capicommand(sendfax|/path/to/faxfile.sff|+49 6137 555123|Asterisk)
(more see below) (more see below)
Enable/Disable echosquelch: Enable/Disable echosquelch:
Enable or disable a very primitive echo suppressor. Enables or disable a very primitive echo suppressor.
Disable this before you start recording voicemail or your files may get choppy. Disable this option before you start recording voicemail or your files may get choppy.
Example: Example:
exten => s,1,capicommand(echosquelch|yes) exten => s,1,capicommand(echosquelch|yes)
or or
exten => s,1,capicommand(echosquelch|no) exten => s,1,capicommand(echosquelch|no)
Enable/Disable echocancel: Enable/Disable echocancel:
Enable or disable echo-cancel provided by CAPI driver/hardware. Enables or disables echo-cancel provided by CAPI driver/hardware.
You may need to disable echo-cancel when e.g. data/fax transmission handled You might need to disable echo-cancel when the data/fax transmission is handled
by non-CAPI application. After hangup, this setting is restored to value by a non-CAPI application. After hangup, this setting is restored to the value
set in capi.conf. set in capi.conf.
Example: Example:
exten => s,1,capicommand(echocancel|yes) exten => s,1,capicommand(echocancel|yes)
@ -181,14 +181,14 @@ Enable/Disable echocancel:
exten => s,1,capicommand(echocancel|no) exten => s,1,capicommand(echocancel|no)
Malicious Call Identification: Malicious Call Identification:
Report a call of malicious nature. Reports a call of malicious nature.
Example: Example:
exten => s,1,capicommand(malicious) exten => s,1,capicommand(malicious)
Hold: Hold:
Puts an answered call on hold, this has nothing to do with asterisk's onhold Puts an answered call on hold, this has nothing to do with Asterisk's onhold
thingie (music et al). (music et al).
An optional parameter is the name of the variable which shall be set with An optional parameter is the name of the variable, which should be set with
the reference ID of the call on hold. the reference ID of the call on hold.
Example: Example:
exten => s,1,capicommand(hold) exten => s,1,capicommand(hold)
@ -196,7 +196,7 @@ Hold:
exten => s,1,capicommand(hold|MYHOLDVAR) exten => s,1,capicommand(hold|MYHOLDVAR)
Holdtype: Holdtype:
Set the type of 'hold'. When Asterisk wants to put the call on hold, the specified method Sets the type of 'hold'. When Asterisk wants to put the call on hold, the specified method
will be used. will be used.
Example: Example:
exten => s,1,capicommand(holdtype|local) ;no hold, Asterisk can play MOH exten => s,1,capicommand(holdtype|local) ;no hold, Asterisk can play MOH
@ -216,7 +216,7 @@ Retrieve:
exten => s,1,capicommand(retrieve|${MYHOLDVAR}) exten => s,1,capicommand(retrieve|${MYHOLDVAR})
ECT: ECT:
Explicit call transfer of the call on hold (must put call on hold first!) Explicit Call Transfer of the call on hold (must put call on hold first!)
Example: Example:
exten => s,1,capicommand(ect|${MYHOLDVAR}) exten => s,1,capicommand(ect|${MYHOLDVAR})
or or
@ -232,7 +232,7 @@ ECT:
instead. instead.
3PTY: 3PTY:
Initiate a Three-Party Conference (must have one call on hold and one active call!). Initiates a Three-Party Conference (must have one call on hold and one active call!).
Example: Example:
exten => s,1,capicommand(3pty_begin|${MYHOLDVAR}) exten => s,1,capicommand(3pty_begin|${MYHOLDVAR})
or or
@ -243,23 +243,23 @@ ECT:
exten => s,2,Dial(CAPI/contr1/1234,,M(capi3pty)) exten => s,2,Dial(CAPI/contr1/1234,,M(capi3pty))
Peer link creation: Peer link creation:
Create a reference for chan_capi to know who is the calling channel on Dial(). Creates a reference for chan_capi to know who is the calling channel on Dial().
This is needed if you want to use CCBS/CCNR afterwards. This is needed if you want to use CCBS/CCNR afterwards.
Example: Example:
exten => s,1,capicommand(peerlink) exten => s,1,capicommand(peerlink)
Hangup in mode 'stay-online': Hangup in mode 'stay-online':
After hangup in 'stay-online' mode, the line isn't really disconnected After hangup in 'stay-online' mode, the line is not really disconnected
until timeout or command: until timeout or command:
exten => s,1,capicommand(hangup) exten => s,1,capicommand(hangup)
This works after capicommand(peerlink) only. This works after capicommand(peerlink) only.
Set local party to 'busy' or 'free': Set local party to 'busy' or 'free':
Set the local phone to status to 'busy' or 'free' when Sets the local phone to the status 'busy' or 'free' when
awaiting a callback for CCBS/CCNR. If the network wants to awaiting a callback for CCBS/CCNR. If the network wants to
call you back for CCBS/CCNR, chan_capi normaly doesn't know call you back for CCBS/CCNR, chan_capi normaly does not know
about the status of the extension who started the callback. about the status of the extension who started the callback.
By default chan_capi assumes 'free', but you can change that By default, chan_capi assumes 'free', but you can change that
with: with:
exten => s,1,capicommand(ccpartybusy|${CCLINKAGEID}|yes) exten => s,1,capicommand(ccpartybusy|${CCLINKAGEID}|yes)
or or
@ -275,7 +275,7 @@ Call completion on subscriber busy (CCBS):
;here you can ask the caller if CCBS shall be activated... ;here you can ask the caller if CCBS shall be activated...
exten => s,4,capicommand(ccbs|${CCLINKAGEID}|<context>|<exten>|<priority>) exten => s,4,capicommand(ccbs|${CCLINKAGEID}|<context>|<exten>|<priority>)
exten => s,5,NoOp(${CCBSSTATUS}) ;if CCBS was successfully enabled, it is set to "ACTIVATED". exten => s,5,NoOp(${CCBSSTATUS}) ;if CCBS was successfully enabled, it is set to "ACTIVATED".
If the remote party becomes 'non-busy', the network initiates the callback which will be If the remote party becomes 'non-busy', the network initiates the callback that will be
sent to the provided context/exten/priority. Of course, this only happens if your local sent to the provided context/exten/priority. Of course, this only happens if your local
phone is set to 'free' with capicommand(ccpartybusy), which is the default. phone is set to 'free' with capicommand(ccpartybusy), which is the default.
In this context/exten/priority you should just setup a callfile to initiate an outgoing In this context/exten/priority you should just setup a callfile to initiate an outgoing
@ -283,27 +283,29 @@ Call completion on subscriber busy (CCBS):
exten => s,1,Dial(CAPI/ccbs/${CCLINKAGEID}/) exten => s,1,Dial(CAPI/ccbs/${CCLINKAGEID}/)
Deactivate CCBS: Deactivate CCBS:
To deactivate a previously activated CCBS, use following command: To deactivate a previously activated CCBS, use the following command:
Example: Example:
exten => s,1,capicommand(ccbsstop|${CCLINKAGEID}) exten => s,1,capicommand(ccbsstop|${CCLINKAGEID})
Chat (MeetMe/Conference): Chat (MeetMe/Conference):
If the CAPI card/driver supports it, the caller can be put into a chat-room: If the CAPI card/driver supports it, the caller can be put into a chat-room:
(This uses the DSPs onboard a Dialogic Diva Rev.2 card.) (This uses the DSPs onboard a Dialogic(R) Diva(R) Rev.2 Media Board.)
exten => s,1,capicommand(chat|<roomname>|<options>|controller) exten => s,1,capicommand(chat|<roomname>|<options>|controller)
Example: Example:
exten => s,1,capicommand(chat|salesmeeting|m|1,3-6) exten => s,1,capicommand(chat|salesmeeting|m|1,3-6)
Possible options: Possible options:
'm' = The first caller will get music-on-hold until second caller arrives. 'm' = The first caller will get music-on-hold until second caller arrives.
'o' = The caller is operator
'l' = The caller is listener
Progress / early-B3 on incoming calls: Progress / Early-B3 on incoming calls:
Activate early-B3 on incoming channels to signal progress tones Activate Early-B3 on incoming channels to signal progress tones
when in NT-mode or if the Telco-line supports this. when in NT-mode or if the Telco-line supports this.
Example: Example:
exten => s,1,capicommand(progress) exten => s,1,capicommand(progress)
Get CAPI application ID: Get CAPI application ID:
To store the CAPI application ID in an asterisk dialplan variable, use: To store the CAPI application ID in an Asterisk dialplan variable, use:
Example: Example:
exten => s,1,capicommand(getid,CAPI_ID) exten => s,1,capicommand(getid,CAPI_ID)
exten => s,2,NoOp(CAPI appl-id is ${CAPI_ID}) exten => s,2,NoOp(CAPI appl-id is ${CAPI_ID})
@ -315,7 +317,7 @@ Use the SetCallerPres() application before you dial:
exten => _X.,2,Dial(CAPI/contr1/${EXTEN}) exten => _X.,2,Dial(CAPI/contr1/${EXTEN})
Enjoying early B3 connects (inband call progress, tones and announcements) Enjoying Early B3 connects (inband call progress, tones and announcements)
========================================================================== ==========================================================================
Early B3 is configurable in the dialstring parameters. Early B3 is configurable in the dialstring parameters.
If you set a 'b', early B3 will always be used, also if the call fails, If you set a 'b', early B3 will always be used, also if the call fails,
@ -323,7 +325,7 @@ because the number is unprovisioned, etc ...
If you set a 'B', early B3 will only be used on successful calls, If you set a 'B', early B3 will only be used on successful calls,
giving you ring indication,etc... giving you ring indication,etc...
Don't use indications in the Dial command, your local exchange will do that for Do not use indications in the Dial command, your local exchange will do that for
you: you:
exten => _X.,1,Dial(CAPI/contr1/${EXTEN}/B,30) exten => _X.,1,Dial(CAPI/contr1/${EXTEN}/B,30)
(early B3 on success) (early B3 on success)
@ -342,14 +344,14 @@ you:
(early B3 on success, fake indicatons if the exchange does not give us (early B3 on success, fake indicatons if the exchange does not give us
indications) indications)
For normal PBX usage you would use the "b" option, always early B3. For normal PBX usage, you would use the "b" option, always Early B3.
Overlap sending (a.k.a. real dialtone) Overlap sending (a.k.a. real dialtone)
====================================== ======================================
When you dial an empty number, and have early B3 enabled, with: When you dial an empty number and have early B3 enabled, with:
Dial(CAPI/g1//b) Dial(CAPI/g1//b)
The channel will come up at once and give you the dialtone it gets from the the channel will come up at once and give you the dialtone it gets from the
local exchange. local exchange.
At this point the channel is like a legacy phone, now you can send DTMF digits At this point the channel is like a legacy phone, now you can send DTMF digits
to dial. to dial.
@ -365,15 +367,15 @@ exten => 12345678,2,Hangup
Short HOWTO of capicommand(receivefax...) and capicommand(sendfax...): Short HOWTO of capicommand(receivefax...) and capicommand(sendfax...):
======================================================================================== ========================================================================================
For those of you who have a CAPI card with an on-board DSP (like Dialogic Diva), For those of you who have a CAPI card with an on-board DSP (like Dialogic(R) Diva(R) Media Boards),
this allows you to receive/send faxes. this allows you to receive/send faxes.
capicommand(receivefax|<filename>[|<stationid>|<headline>|<options>]): capicommand(receivefax|<filename>[|<stationid>|<headline>|<options>]):
------------------------------------------------------------------------- -------------------------------------------------------------------------
If you want to answer a channel in fax mode, use capicommand(receivefax|...) If you want to answer a channel in fax mode, use capicommand(receivefax|...)
instead of Answer() instead of Answer(). If you use Answer(), you will be in voice mode.
If you use Answer(), you will be in voice mode. If the hardware DSP detects If the hardware DSP detects fax tone, you can switch from voice to fax mode
fax tone, you can switch from voice to fax mode by calling capicommand(receivefax|...). by calling capicommand(receivefax|...).
The parameter <filename> is mandatory and the parameters <stationid>, The parameter <filename> is mandatory and the parameters <stationid>,
<headline> and <options> are optional. <headline> and <options> are optional.
By default, if fax reception was not successful, the file is deleted. If you want even By default, if fax reception was not successful, the file is deleted. If you want even
@ -401,7 +403,7 @@ exten => h,1,deadagi,fax.php // Run sfftobmp and mail it.
The output of capicommand(receivefax|...) is a SFF file. The output of capicommand(receivefax|...) is a SFF file.
Use sfftobmp to convert it. Use sfftobmp to convert it.
With a Dialogic Diva, following features are provided: With a Dialogic(R) Diva(R) Media Board, the following features are provided:
- fax up to 33600 - fax up to 33600
- high resolution - high resolution
- Color Fax - Color Fax
@ -434,46 +436,57 @@ CLI command "capi show channels"
This CLI command shows detailed info on all CAPI channels. This CLI command shows detailed info on all CAPI channels.
Column description: Column description:
Line-Name : the name of the interface as defined in capi.conf Line-Name : the name of the interface as defined in capi.conf
NTmode : is the line in NT mode instead fo TE mode NTmode : is the line in NT-mode instead fo TE-mode
state : the state of the channel, like 'Conn', 'Disc', 'Dial', ... state : the state of the channel, like 'Conn', 'Disc', 'Dial', ...
i/o : incoming or outgoing line i/o : incoming or outgoing line
bproto : protocol on CAPI ('fax', 'trans' or 'rtp') bproto : protocol on CAPI ('fax', 'trans' or 'rtp')
isdnstate : a string which may consists of the following characters isdnstate : a string which may consists of the following characters
* = PBX is active * = PBX is active
G = Line-Interconnect (CAPI bridge) active G = Line-Interconnect (CAPI bridge) active
B = b-channel is up B = B-channel is up
b = b-channel is requested b = B-channel is requested
P = Progress was signaled P = Progress was signaled
H = this line is on hold H = this line is on hold
T = this line is in transfer (ECT) mode T = this line is in transfer (ECT) mode
S = SETUP[_ACK] was signaled S = SETUP[_ACK] was signaled
ton : type of number value ton : type of number value
number : the caller-number and destination-number number : the caller number and destination number
Asterisk variables used/set by chan_capi Asterisk variables used/set by chan_capi
========================================================== ==========================================================
BCHANNELINFO BCHANNELINFO
On incomming call, this variable is set with the B-channel information value: On incomming calls, this variable is set with the B-channel information value:
'0' : B-channel is used (default) '0' : B-channel is used (default)
'1' : D-channel is used (not implemented yet) '1' : D-channel is used (not implemented yet)
'2' : neither B nor D channel is used (e.g. call waiting) '2' : neither B nor D-channel is used (e.g., call waiting)
Call-Waiting: an incoming call with BCHANNELINFO not '0' cannot be accepted. Call-Waiting: an incoming call with BCHANNELINFO not '0' cannot be accepted.
Another connection must be dropped before accepting or use Another connection must be dropped before accepting or use
capicommand(deflect|<number>) to initiate call deflection to another destination. capicommand(deflect|<number>) to initiate call deflection to another destination.
CALLEDTON CALLEDTON
The 'type of number' value of the called number is saved in this variable on Incoming calls: 'type of number' value of the called number is saved in this variable on
incomming call. incomming call.
Outgoing calls: Allows to specity the 'ETS 300 102-1' called party number octet 3
(0x80 is used by default)
exten => _X.,1,Answer
exten => _X.,n,Set(_CALLEDTON=${CALLEDTON}) ; Use value of incoming call for outgoing call
exten => _X.,n,Dial(CAPI/ISDN3/100,10)
exten => _X.,1,Answer
exten => _X.,n,Set(_CALLEDTON=1) ; Use new value
exten => _X.,n,Dial(CAPI/ISDN3/100,10)
CALLERTON CALLERTON
The 'type of number' value to overwrite for the caller number on outgoing call. The 'type of number' value to overwrite for the caller number on outgoing call.
_CALLERHOLDID _CALLERHOLDID
If a call is put on hold (ISDN-HOLD), the reference id is saved in this variable. If a call is put on hold (ISDN-HOLD), the reference ID is saved in this variable.
This variable is inherited as CALLERHOLDID to the dialed channel and will be used This variable is inherited as CALLERHOLDID to the dialed channel and will be used
if e.g. capicommand(ect) is used to transfer the held call. if e.g., capicommand(ect) is used to transfer the held call.
CALLINGSUBADDRESS CALLINGSUBADDRESS
If set on dial(), the calling subaddress will be set to the content. If set on dial(), the calling subaddress will be set to the content.
@ -481,12 +494,17 @@ CALLINGSUBADDRESS
CALLEDSUBADDRESS CALLEDSUBADDRESS
If set on dial(), the called subaddress will be set to the content. If set on dial(), the called subaddress will be set to the content.
CAPI_CIP
The real CIP value, not the transformed 'transfercapability'.
Set on incoming call automatically. If set on outgoing call, it is used
instead of transfercapability.
CCBSSTATUS CCBSSTATUS
When using capicommand(ccbs|....), this variable is set to either "ERROR" or When using capicommand(ccbs|....), this variable is set to either "ERROR" or
"ACTIVATED". "ACTIVATED".
CCLINKAGEID CCLINKAGEID
If a Call-Linkage-Id is received for CCBS/CCNR, this variable contains this Id. If a Call-Linkage-Id is received for CCBS/CCNR, this variable contains this ID.
But you need to use capicommand(peerlink) before dialing a CAPI channel, because But you need to use capicommand(peerlink) before dialing a CAPI channel, because
of a design problem in Asterisk, chan_capi is not able to set channel variables of a design problem in Asterisk, chan_capi is not able to set channel variables
of the calling channel. of the calling channel.
@ -503,7 +521,7 @@ PRI_CAUSE
If set, this value will be used as hangup cause on hangup. If set, this value will be used as hangup cause on hangup.
REDIRECTINGNUMBER REDIRECTINGNUMBER
On incoming call, if the call was redirected to you by someone, the On incoming calls, if the call was redirected to you by someone, the
number of the redirecting party is saved in this variable. number of the redirecting party is saved in this variable.
RDNIS is set as well. RDNIS is set as well.

350
README.Diva.fax Normal file
View File

@ -0,0 +1,350 @@
+===================================================================+
| Diva fax support |
+-------------------------------------------------------------------+
| |
| Intelligent fax processing |
| FoIP/VoIP (Fax/Voice over IP) T.38 fax support |
| FoIP/VoIP clear channel fax support |
| Color fax |
| High resolution fax, non standard paper size |
| Use of fax with chan_capi |
| |
+===================================================================+
+-------------------------------------------------------------------+
| INTELLIGENT FAX PROCESSING |
+-------------------------------------------------------------------+
The fax chan_capi command set provides an easy way to access fax-related functionality.
If you use fax document processing, you need to be aware of the following problems,
that might occur:
* It is necessary to maintain a constant data stream between the application and the fax device.
Any interruption in this data stream will affect the quality of the fax document.
* It is necessary to deal with various low-level T.30 protocol settings like scan line time,
compression, error correction, etc.
* Not every application or device supports the command-set features provided by T.30 fax protocol.
This limits the functionality and may require modification of the existing application.
* The usage of fax document compression forces you to deal with compressed data (reception),
or to be able to generate compressed data stream on demand, or to provide documents in different
compression formats (transmission).
* The "classic" fax application is unable to deal with transmission speeds higher than 14400 bps.
To be able use V.34 fax transmission speeds of up to 33600 bps, the application needs to be modified.
This section explains how the Dialogic(R) Diva(R) Media Board can overcome these drawbacks and
allows you to use the chan_capi to process fax documents with a comparable level of reliability and
flexibility as a sophisticated high-level fax API.
Reliable data transfer between application and Dialogic(R) Diva(R) Media Board
------------------------------------------------------------------------------
Dialogic(R) Diva(R) Media Boards provide a high-performance block-oriented IDI (ISDN Direct Interface)
between the board hardware and the host CPU. The data transfer is performed via a BUS master DMA.
This enables a reliable data transfer between the host CPU and the Diva Media Board memory that is not
affected by the host CPU load. At the same time, using the BUS master DMA reduces the host CPU load.
The Dialogic(R) Diva(R) chan_capi/CAPI interface/drivers does not perform data processing. It is only
used to forward the data stream between the application and the IDI interface.
The entire data processing is performed on the RISC CPU of the Diva Media Board.
The reliability of the data stream is ensured by the board hardware through buffering
(up to 64 Kbytes for every channel) and block-oriented data transfer (blocks of up to 2 Kbytes)
via the BUS master DMA.
Automatic T.30 protocol parameter adjustment
--------------------------------------------
The chan_capi can ignore low-level T.30 protocol settings. The T.30 protocol stack that runs on the RISC CPU
of the Dialogic(R) Diva(R) Media Board is able to perform the required adjustment of transmission parameters
to provide reliable and fast document transmission without requiring application intervention.
You can overrule the automatic T.30 protocol parameter adjustment with global fax
configuration options in the Dialogic(R) Diva(R) WEB configuration interface.
ECM (Error Correction Mode) support
-----------------------------------
You can control ECM support via global fax configuration options in the Dialogic(R) Diva(R) WEB configuration interface.
If you use global Diva configuration options to enable ECM support, the Dialogic(R) Diva(R) Media Board will use
ECM mode for document transfer, if supported by the opposite side.
Diva Media Boards use their internal memory to store document data. They retrieve data for ECM re-transmissions
from this internal buffer (up to 64 Kbytes for every channel). This reduces the host CPU load and increases
the reliability of the fax transmission.
Document compression support
----------------------------
Dialogic(R) Diva(R) Media Boards use MR, MMR, T.6 fax document compression. In order to reduce transmission time,
Diva Media Boards select the best compression algorithm supported by the opposite side. The Diva Media Board's
RISC CPU is used to re-compress 1D-coded page data from the application to the format requested by the opposite
side (transmission) and to convert received data to 1D-coded page data that is sent to the application (reception).
The re-compression process is handled internally by the board's RISC CPU and happens fully transparent to the
application that deals only with 1D (MH) coded data.
You can adjust the compression-related T.30 protocol settings via Global fax configuration options.
Automatic detection of document format
--------------------------------------
chan_capi uses the context of the file to determine the format of the document and to apply
all necessary for transmission of the document settings.
Following media formats are detected:
SFF, CFF (Color Fax in JPEG or T.43 format), text file
Diva supports media stream consisting from pages with different media types.
V.34 (33600 bps) fax support
----------------------------
The V.34 fax support can be controlled via global fax configuration options in the Dialogic(R) Diva(R) Configuration
web interface. If the Dialogic(R) Diva(R) Media Board is able to establish a connection with a transmission speed
higher than 14400 bps (V.34), it handles this transparent to the application.
You can use the "divalogd" accounting utility that uses the Diva Media Board Management interface to get
information on the transmission speed and the used compression algorithm.
+-------------------------------------------------------------------+
| FoIP/VoIP (Fax/Voice over IP) T.38 FAX SUPPORT |
+-------------------------------------------------------------------+
You can use the Dialogic(R) Diva(R) softIP software to access T.38 functionality.
There is no need to change your chan_capi configuration. The Dialogic(R) Diva(R) softIP software exposes the
CAPI interface towards chan_capi and the SIP interface towards the IP network.
All T.38 and SIP-related configurations are handled using Diva WEB configuration interface.
This is no limitation of the voice and supplementary services functionality of chan_capi.
You can change the call flow between voice and fax or invoke the supplementary services at the CAPI interface
and the Diva softIP software handles all necessary media and SIP negotiation.
You can use the Diva hardware and the Diva softIP software in parallel on one system.
It is possible to use line interconnect (conferencing) features between the Diva hardware and
The Diva softIP software without any limitations.
You can use the Diva softIP software in virtual environments (VMWare, XEN, ...).
+-------------------------------------------------------------------+
| FoIP/VoIP CLEAR CHANNEL FAX SUPPORT |
+-------------------------------------------------------------------+
Together with the Diva(R) Dialogic(R) hardware you can use the Dialogic(R) Diva(R) softIP software or chan_capi to
access T.38 (only Diva softIP software) and Clear Channel Fax functionality.
If you use the Diva softIP software there is no need to change your chan_capi configuration.
The Dialogic(R) Diva(R) softIP software exposes the CAPI interface towards chan_capi and the SIP interface towards
the IP network. All T.38 and SIP related configurations are handled using the Diva WEB configuration interface.
To activate Clear Channel Fax support, the Diva hardware should be switched in Resource Board mode.
This is no limitation of the voice and supplementory services functionality of chan_capi.
You can change the call flow between voice and fax or invoke the supplementary services at the CAPI interface
and the Diva softIP software will handle all necessary media and SIP negotiation.
You can use the Diva hardware in TDM (E.1/T.1/S0), in Resource Board mode and the Diva softIP software in
parallel on one system. It is possible to use the line interconnect (conferencing) features between the Diva hardware and
the Diva softIP software without any limitations.
If you use chan_capi, then you can use 'resource' command to assign DSP resources to connected
by IP users. Resource PLCI allows to send and to receive fax documents over IP using
Clear Channel fax and to use the DSP resources for processing of IP media and for conferencing.
It is possible to use the line interconnect (conferencing) features between E.1/T.1/S0/PSTN and
IP peers without any limitations.
Both the Diva softIP software and chan_capi allow to use G.729, G.723, iLBC, GSM, and other codecs
supported by the Diva hardware.
You can use the Diva hardware, chan_capi, and Diva softIP software in virtual environments if access by software is supported (XEN).
+-------------------------------------------------------------------+
| COLOR FAX |
+-------------------------------------------------------------------+
The Dialogic (R) Diva(R) hardware and the Dialogic (R) Diva(R) softIP software support color fax and allow to send/receive
fax document using
Continuous tone color and gray-scale mode according to T.4 Annex E using JPEG coding
Lossless color and gray-scale mode according to T.43 using JBIG coding
Color fax documents are processed using CFF.
Each of these data formats starts with a unique pattern:
0x53 0x66 - SFF - First bytes of magic number in SFF document header, CAPI 2.0 Annex B
0xFF 0xD8 - T.4 - Annex E SOI marker
0xFF 0xA8 0 T.43 - Start marker 0xFFA8
It is possible to change between different media types in the time of fax transmission. This
allows to change media between black and white and color for every page.
After completion of transmission or reception of fax CFF (color/greyscale image format)
variable FAXFORMAT is set to 8 (native format, CFF image) and variable FAXCFFFORMAT
provides information about the image format.
+-------------------------------------------------------------------+
| High resolution fax, non standard paper size |
+-------------------------------------------------------------------+
The Dialogic (R) Diva(R) hardware and the Dialogic (R) Diva(R) softIP software provide access to following T.30 features:
Ultra/Super fine resolution
Non standard paper formats
The current COMMON-ISDN-API specification defines paper formats ISO A4, ISO B4, and ISO A3
at standard resolution (R8 x 3.85) and high resolution (R8 x 7.7).
Support for B4 and A3 is optional.
The Dialogic (R) Diva(R) software supports a COMMON-ISDN-API extension that enables FAX document transmission and
reception with paper formats ISO A4, ISO B4, and ISO A3 and the following resolutions as specified in T.30:
R8 x 3.85
R8 x 7.7
R8 x 15.4
R16 x 15.4
200 x 200 dpi
300 x 300 dpi
400 x 400 dpi
300 x 600 dpi
400 x 800 dpi
600 x 1200 dpi
600 x 600 dpi
1200 x 1200 dpi
The page format and resolution information is passed via appropriate fields in the SFF page header.
The Dialogic(R) Diva(R) SFF2TIFF utility provides conversion from SFF to TIFF format for all basic and
extended resolutions and paper formats.
+-------------------------------------------------------------------+
| Use of fax with chan_capi |
+-------------------------------------------------------------------+
Based on information from README
Reception of fax documents
--------------------------
You can use the Dialogic (R) Diva(R) chan_capi configuration to activate fax support ("Fax detection" and "Fax detection time").
The reception of the fax message is started using 'receivefax' capi command:
capicommand(receivefax|<filename>[|<stationid>|<headline>|<options>])
Parameters:
'filename' - Contains the full path and file name for the resulting fax file, mandatory
'stationid' - station ID, optional
'headline' - head line, optional
'options' - fax options, optional
'k' - keep fax document in case of errors (by default document is removed
if fax transmission was not completed with success).
'f' - allow Fine resolution
'u' - activate support for Super/Ultra fine resolutions and paper formats
'j' - enable JPEG coding
'b' - enable T.43 coding
't' - do not use T.85
'e' - do not use ECM
'm' - do not use MMR (T.6) coding
'd' - do not use MR (2D) coding
It is possible to answer the incomming call using 'receivefax' command and start reception of
the fax document directly.
It is possible to answer a call using 'Answer' in voice mode and change to reception (transmission) of the fax by
'receivefax' later, for example after the detection of the fax calling tone or after the user entered a password
using DTMF digits.
Using resource ('resource' command) PLCI allows to receive fax documents over IP using Clear Channel fax.
The 'resource' command allocates one resource PLCI for IP connections only and does not perform any actions for
E.1/T.1/S0/PSTN connections. This allows for safe use of 'resource' command in any context.
Example:
[isdn-in]
exten => _X.,1,Answer() ; Answer in voice mode
exten => _X.,n,Set(TIMEOUT(digit)=5) ; Set Digit Timeout to 5 seconds
exten => _X.,n,Set(TIMEOUT(response)=10) ; Set Response Timeout to 10 seconds
exten => _X.,n,BackGround(jpop) ; Play message and wait until detection of fax calling tone
exten => 124,1,Goto(handle_fax,s,1) ; Extension 124 is dedicated to fax, answer in fax mode
exten => fax,1,Goto(handle_fax,s,1) ; Fax calling tone detected, change to fax mode
exten => i,1,Hangup() ; Received unexpected event
[handle_fax]
exten => s,1,capicommand(receivefax|/tmp/${UNIQUEID}[|<stationid>|<headline>|<options>])
exten => s,2,Hangup()
exten => h,1,deadagi,fax.php ; Run sfftobmp and mail it
Example with Clear Channel fax support:
[handle_fax]
exten => s,1,capicommand(resource|1-4) ; Assign resource PLCI
exten => s,1,capicommand(receivefax|/tmp/${UNIQUEID}[|<stationid>|<headline>|<options>])
exten => s,2,Hangup()
exten => h,1,deadagi,fax.php ; Run sfftobmp and mail it
Transmission of fax documents
-----------------------------
The transmission of the fax message is started using 'sendfax' capi command:
capicommand(sendfax|<filename>[|<stationid>|<headline>|<options>])
Parameters:
'filename' - Contains the full path and file name to be sent, mandatory
sendfax command automatically detects the type of file
using the context of file.
Supported formats:
.sff - SFF file
.txt - text file
.cff - JPEG/T.43 (color/gray tone) coded file
'stationid' - station ID, optional
'headline' - head line, optional
'options' - fax options, optional
'f' - use Fine resolution
'u' - activate support for Super/Ultra fine resolutions and paper formats
't' - do not use T.85
'e' - do not use ECM
'm' - do not use MMR (T.6) coding
'd' - do not use MR (2D) coding
You need to start the call using the 'Dial' command in voice mode and change to transmission (reception)
of the fax by 'sendfax'. Optionally, you can wait until the user entered a password using DTMF digits.
Using resource ('resource' command) PLCI allows to transmit fax documents over IP using Clear Channel fax.
The 'resource' command allocates one resource PLCI for IP connections only and does not perform any actions for
E.1/T.1/S0/PSTN connections. This allows for safe use of 'resource' command in any context.
Example:
[dial_fax]
exten => 1,1,Dial(capi/ISDN1/1234512345,20,G(handle_sendfax,s,1))
[handle_sendfax]
exten => s,1,capicommand(sendfax|/tmp/sendfax001.sff|1234 1234 1234|Outgoing Fax)
exten => s,n,deadagi,faxlog.php ; Log result and schedule restart if necessary
exten => s,n,Hangup
Example with Clear Channel fax support:
[handle_sendfax]
exten => s,1,capicommand(resource|1-4) ; Assign resource PLCI
exten => s,n,capicommand(sendfax|/tmp/sendfax001.sff|1234 1234 1234|Outgoing Fax)
exten => s,n,deadagi,faxlog.php ; Log result and schedule restart if necessary
exten => s,n,Hangup
Results of fax transmission
---------------------------
After chan_capi completed the processing of the 'receivefax' or 'sendfax' commands, following variables are set:
FAXSTATUS - Status of fax transmission
0 - OK
1 - Error
FAXREASON - Value of B3 disconnect reason
FAXREASONTEXT - Decoded text of FAXREASON value
FAXRATE - The baud rate of the fax connection
FAXRESOLUTION - Resolution of received fax message
0 - standard
1 - high
FAXFORMAT - Format of received fax document
0 - SFF (default, description in Annex B)
1 - Plain fax format (modified Huffman coding)
2 - PCX
3 - DCX
4 - TIFF
5 - ASCII
6 - Extended ANSI
7 - Binary-File transfer
8 - Native (CFF
continuous-tone color and gray-scale T.4 Annex E using JPEG coding
lossless color and gray-scale mode according to T.43)
FAXCFFFORMAT - Valid if FAXFORMAT is set to 8 (native)
1 - continuous tone color and gray-scale mode according to T.4 Annex E using JPEG coding
2 - lossless color and gray-scale mode according to T.43 [7] using JBIG coding
FAXPAGES - Number of pages received
FAXID - The ID of the remote fax maschine

172
README.Diva.qsig Normal file
View File

@ -0,0 +1,172 @@
+===================================================================+
| QSIG Abstraction |
+===================================================================+
+-------------------------------------------------------------------+
| QSIG supplementary services |
+-------------------------------------------------------------------+
The QSIG network differentiates between four different types of call transfers,
two unassisted and two assisted.
The type of invoked unassisted call transfer depends on the call state.
The type of assisted call transfers depends on the type of the switching
equipment Dialogic(R) Diva(R) interfaces as well.
The coding of the messages and the type of the transfer invoked, depends
on the QSIG dialect and on the vendor-specific implementation of QSIG dialect.
+-------------------------------------------------------------------+
| QSIG abstraction |
+-------------------------------------------------------------------+
Dialogic(R) Diva(R) System Release Software abstraction allows to write
QSIG applications independent of the used QSIG dialect.
Unassisted call transfer:
The application can use CAPI call deflection. The Diva System Release software
will automatically translate the call deflection request in the appropriate
QSIG primitives. The translation depends on the Diva System Release software
configuration and on the call state.
You can invoke this feature in any call state, but the request may fail in
case the used switching equipment does not support the required service.
Assisted call transfer:
The application can use CAPI Explicit Call Transfer (ECT). The Diva System Release
software will automatically translate ECT in the appropriate QSIG primitive.
The translation depends on the Diva System Release configuration.
+-------------------------------------------------------------------+
| Conclusion |
+-------------------------------------------------------------------+
If reviewed in short form, you can use any ETSI application with QSIG and without
the need to change any code to access QSIG call transfers. There is no need to deal
with details of the underlying network.
The same is true not only for QSIG but for all supported signaling protocols.
The same interface can be used to access basic and supplementary services for
ETSI, QSIG, SS7 ISUP, 5ESS, R2, RBS/CAS, ...
+-------------------------------------------------------------------+
| QSIG path replacement |
+-------------------------------------------------------------------+
There are three parties involved in the path replacement procedure;
The party that initiated the call (party A), the party that initially received and
transferred the call (party B) and the party that finally received the
call after the call transfer (party C).
The goal of the call replacement procedure is to free switching equipment resources
by the replacement of the chain A->B->C with chain A->C.
To achieve this, party B will short cut the D-channel link between A and C and
let A and C exchange D-channel messages. In most cases, party B will short
cut the B-channel between A and C as well and preserve this connection until the path
replacement is complete. If the path replacement will not proceed (not supported, ...),
party B will preserve the D-channel and B-channel interconnections for the entire
duration of the call.
After party B completes the call transfer procedure, one transfer complete message
will be sent (Diva System Release softwre configuration option) and will invoke the path
replacement procedure (Diva System Release configuration option). As part of this procedure,
party A will prepare an alternative resource and send information about this resource over
the D-channel to C through B.
After the information about the alternative resource is received, C will establish a new call
directly from C to A and replace A->B->C by C->A (A->B->C still running in parallel).
After the new connection is established, A and C will establish a new B-channel connection
and will exchange D-channel messages over B and release connections A->B and B->C.
After the release is complete, C->A is preserved during the duration of the call.
The Diva System Release software supports all three parties A, B, and C in the above mentioned
path replacement procedure.
+-------------------------------------------------------------------+
| Support at party A and C endpoints |
+-------------------------------------------------------------------+
The support at A and C endpoints is transparent to the applications.
The Diva System Release software will replace one connection with another in
a fully transparent way to the application.
The data streaming between the application and the bearer resource will not be interrupted
during this operation. Only one notification about the change of the B-channel and
about the completion of the path replacement procedure is provided to the application but
does not require any action from the application (in certain environments
applications will use this notification to issue RESET_B3 REQ and restart the voice
message if any. PBX applications do not need to perform any action).
Please note that party C responds to the call replacement procedure only if the change of the
connection is supported by the underlying protocol. This change is not possible if the fax or
modem session is already in progress. In this case, C will ignore the path replacement and
the chain A->B->C will be preserved for the entire duration of the call.
The same is true if the path replacement procedure fails for any reason.
The M-Board is required to provide the path replacement operation at A and B and it is
responsible for moving the call between different connections transparently for application.
The path replacement can be performed only between Dialogic(R) Diva(R) interfaces
that belong to the same M-Board.
+-------------------------------------------------------------------+
| Support at the party B endpoint |
+-------------------------------------------------------------------+
Party B is the party that uses ECT to perform the assisted QSIG call transfer.
The type of call transfer and the messages sent after the call transfer is completed
are specified by the Diva System Release software configuration. The software detects if
compatible QSIG dialects are used for both parties of ECT and will automatically
establish the D-channel bridge.
The application is responsible to establish the B-channel bridge. The best practice is to
establish the B-channel bridge (using CAPI line interconnect command) before issuing
the CAPI ECT command. The Dialogic(R) Diva(R) CAPI will atomatically maintain the D-channel bridge.
If the M-Board is used at the party B endpoint, it is possible to activate "ECT Emulation".
If active, the M-Board will automatically establish and maintain the B-channel and D-channel bridge
once the ECT command is sent by the application. In this case, there is no need to maintain the B-channel
bridge by the application. The M-Board will maintain all necessary resources until the path
replacement procedure is completed or for the entire duration of the call.
This option is available only for calls that belong to the same M-Board.
+-------------------------------------------------------------------+
| Call state of involved in the ECT calls |
+-------------------------------------------------------------------+
In case of QSIG, it is not necessary to put one of the calls, involved in the ECT, on hold.
Moreover, it is possible to invoke the ECT if the consultation call is in alerted state
(if supported by the switching equipment).
To achieve this, independent from the state of the primary and of the consultation call operation
of the ECT, it is necessary to set the CAPIECTPLCI variable to PLCI (getplci commang provides it in
CAPIPLCI variable) of the primary call before invoking the ECT.
Example: The application accepts one call, creates the consultation call and once the alert is
received, the consultation call starts the playback of message on the first call. After an
additional command is received, the application proceeds with ECT, independent from the state
of the consultation call.
It is even possible to play the message to the first call until the path replacement is completed
and parties A and C are connected directly.
ECT Example:
/////////////////////////////////////////////////////////////////////
[macro-capiect]
; Activate suppressor of ambient noise for consultation call
exten => s,1,capicommand(noisesuppressor|yes)
; Invoke ECT command
exten => s,n,capicommand(ect)
[isdn-in]
; Send progress message on incoming call
exten => 12345,1,Progress()
exten => 12345,n,Set(TIMEOUT(digit)=1) ; Set Digit Timeout to 5 seconds
exten => 12345,n,Set(TIMEOUT(response)=5) ; Set Response Timeout to 10 seconds
; Play message to incoming call using early media mode
exten => 12345,n,Playback(demo-enterkeywords,noanswer,us)
; Accept incoming call
exten => 12345,n,Answer
; Activate suppressor of ambient noises for incoming call
exten => 12345,n,capicommand(noisesuppressor|yes)
; Save PLCI of incoming call to CAPIPLCI variable
exten => 12345,n,capicommand(getplci)
; Set CAPIECTPLCI variable to PLCI of incoming call
; and allow to export this variable to consultation call
exten => 12345,n,Set(_CAPIECTPLCI=${CAPIPLCI})
; Create consultation call and invoke ECT
; ECT procedure will detect CAPIECTPLCI is set and use saved in this variable value
; to identify the ECT party and invoke ECT independent from the state of the primary call
exten => 12345,n,Dial(CAPI/ISDN1/100,10,M(capiect))
exten => 12345,n,Hangup()
/////////////////////////////////////////////////////////////////////

1294
README.media Normal file

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ QSIG Extension for chan_capi
This program is free software and may be modified and distributed under This program is free software and may be modified and distributed under
the terms of the GNU Public License. There is _NO_ warranty for this! the terms of the GNU Public License. There is _NO_ warranty for this!
Thanks go to the debuggers, bugfixers and contributors :) Thanks go to the debuggers, bugfixers, and contributors :)
=========================================================================== ===========================================================================
None yet - you will be welcome here :-) None yet - you will be welcome here :-)
@ -23,22 +23,22 @@ Asterisk 1.2.x , 1.4.x or 1.6.x.
What is Q.SIG What is Q.SIG
============= =============
Q.SIG is an protocoll extension for ISDN. Q.SIG is a protocol extension for ISDN.
It is mainly used on connecting PBXs of different PBX vendors, which allows It is mainly used on connecting PBXs of different PBX vendors, which allows
better interoperability. better interoperability.
As example there can be a name of an extension transferred between different As example there can be a name of an extension transferred between different
PBXs, which is with standard ISDN not possible. PBXs, which is not possibile with standard ISDN.
These extensions will be transmitted as encoded facility information elements. These extensions will be transmitted as encoded facility information elements.
To use Q.SIG with asterisk, you'll need a card like Dialogic Diva To use Q.SIG with Asterisk, you willll need a card such as a Dialogic(R) Diva(R)
(BRI like PRI), which supports QSIG. Maybe others do also work, let me now. Media Board (BRI like PRI) that supports QSIG. Maybe others do also work, if so, let me now.
The QSIG support includes: The Q.SIG support includes:
========================== ==========================
- Name presentation on Call SETUP incoming like outgoing - Name presentation on Call SETUP incoming like outgoing
- ISDN LEG INFO2 field - a message which delivers informations about call diversions on incoming call to asterisk - ISDN LEG INFO2 field - a message that delivers information about call diversions on incoming calls to Asterisk
Data is stored in Asterisk variables: Data is stored in Asterisk variables:
QSIG_LI2_DIVREASON Reason of divertion: 0 - unknown, 1 - unconditional, 2 - user busy, 3 - user no reply QSIG_LI2_DIVREASON Reason of divertion: 0 - unknown, 1 - unconditional, 2 - user busy, 3 - user no reply
QSIG_LI2_ODIVREASON Reason of original divertion (like above) QSIG_LI2_ODIVREASON Reason of original divertion (like above)
@ -50,45 +50,45 @@ The QSIG support includes:
at the moment only incoming handling is supported at the moment only incoming handling is supported
- Possibility to inform QSIG switch about call from public network - Possibility to inform Q.SIG switch about a call from the public network
If you set variable QSIG_SETUP=X then the QSIG switch on the other side will know, If you set the variable QSIG_SETUP=X, then the QSIG switch on the other side will know,
this call source is public network - you will get different ring tone, etc. that this call source is the public network - you will get a different ring tone, etc.
In dialplan use: Set(__QSIG_SETUP=X) command. In dialplan use: Set(__QSIG_SETUP=X) command.
The leading "__" tells asterisk, to export this variable to the outgoing channel and The leading "__" tells Asterisk, to export this variable to the outgoing channel and
its subchannels its subchannels
- Simple Call Transfer - Simple Call Transfer
With capicommand(qsig_ct|src-id|dst-id) you can transfer an inbound call back to the qsig switch. With capicommand(qsig_ct|src-id|dst-id), you can transfer an inbound call back to the qsig switch.
The B-Channel of this call will be relased, so that the line is free for a next call. The B-channel of this call will be relased, so that the line is free for a next call.
Unfortunately the call will be completely released by the switch, if the target is busy. Unfortunately, the call will be completely released by the switch if the target is busy.
If you want need to know, if your target is busy, you can use the call transfer feature below. If you need to know whether your target is busy, you can use the call transfer feature below.
- Call Transfer (outgoing) - Call Transfer (outgoing)
You can do an outbound call transfer. You can do an outbound call transfer.
First you need the PLCI (logical channel id) of your first channel. You'll get it with capicommand(qsig_getplci). This First, you need the PLCI (logical channel ID) of your first channel. You can obtain it with capicommand(qsig_getplci). This
command returns the channel id in the variable QSIG_PLCI. Now you can enable the call transfer feature. command returns the channel ID in the variable QSIG_PLCI. Now, you can enable the call transfer feature.
Simply add "Ct<PLCI>" to QSIG_SETUP (i.e. QSIG_SETUP="X/Ct${QSIG_PLCI}" ). On the next dial command the call will Simply add "Ct<PLCI>" to QSIG_SETUP (i.e., QSIG_SETUP="X/Ct${QSIG_PLCI}" ). On the next dial command the call will
be automatically transferred. The transfer occurs after the CONNECT. If you want an transfer early on ringing you be automatically transferred. The transfer occurs after the CONNECT. If you want a transfer early on ringing, you
can use "Ctr<PLCI>". Then the target user will get the infos about the originating user, while his phone is ringing. may use "Ctr<PLCI>". Then the target user will get the information about the originating user, while his phone is ringing.
If the external switch offers an path replacement propose, it will be taken automatically in account. If the external switch offers an path replacement propose, it will be taken automatically in account.
The B-Channels will be cleared by the switch after call connection. Your channels stay free. The B-channels will be cleared by the switch after the call is conneced. Your channels stay free.
- Automatic Call Transfer and Path Replacement (if allowed/possible) on bridge/line interconnect - Automatic Call Transfer and Path Replacement (if allowed/possible) on bridge/line interconnect
If an line interconnect is set up from asterisk, chan_capi sends an Call Transfer facility out and waits for an If a line interconnect is set up from Asterisk, chan_capi sends an Call Transfer facility out and waits for an
Path Replacement Propose message - if no Path Replacement is received, the line interconnect will proceed. Path Replacement Propose message - if no Path Replacement is received, the line interconnect will proceed.
The Call Transfer allows your connected extensions in every case (if the switch supports the Call Transfer feature) The Call Transfer allows your connected extensions in every case (if the switch supports the Call Transfer feature)
to see the name and number of his connected peer. to see the name and number of its connected peer.
This should be configurable in the next release. This should be configurable in the next release.
- decoding of incoming Call Transfer feature - decoding of incoming Call Transfer feature
Enables inbound Path Replacement. If received, an automatic Path Replacement with asterisk internal bridging will be fired. Enables inbound Path Replacement. If received, an automatic Path Replacement with Asterisk internal bridging will be fired.
- Support for sending CalledName - Support for sending CalledName
If in dialplan a variable CALLEDNAME was set, it will be sent out to the switch, while the asterisk extension is ringing. If in the dialplan a variable CALLEDNAME was set, it will be sent out to the switch, while the Asterisk extension is ringing.
- Support for sending ConnectedName - Support for sending ConnectedName
If in dialplan a variable CONNECTEDNAME was set, it will be sent out to the switch AFTER connection is answered by asterisk If in the dialplan a variable CONNECTEDNAME was set, it will be sent out to the switch AFTER connection is answered by asterisk
Future Targets: Future Targets:
@ -98,7 +98,7 @@ Future Targets:
- Call Rerouting feature [ECMA-174] - Call Rerouting feature [ECMA-174]
- CCBS - CCBS
- AOC - AOC
- sendtext implementation (e.g. display instructions on the connected set) - sendtext implementation (e.g., display instructions on the connected set)
- ... - ...
How to use: How to use:
@ -110,11 +110,11 @@ Please visit: http://www.melware.org/ChanCapiConf
simply enable Q.SIG with following line in your capi.conf interface: Simply enable Q.SIG with the following line in your capi.conf interface:
Here we go with new configuration Here we go with new configuration
Set qsig to one of the following values, which corresponds to your configuration. Set Q.SIG to one of the following values, which corresponds to your configuration.
0 QSIG turned off 0 QSIG turned off
1 Alcatel (4400 & Enterprise - Maybe OXO/4200) ECMA (wrongly named ECMA - it is ETSI) variant 1 Alcatel (4400 & Enterprise - Maybe OXO/4200) ECMA (wrongly named ECMA - it is ETSI) variant

View File

@ -23,20 +23,20 @@ language=de ;set default language
[ISDN1] ;this example interface gets name 'ISDN1' and may be any [ISDN1] ;this example interface gets name 'ISDN1' and may be any
;name not starting with 'g' or 'contr'. ;name not starting with 'g' or 'contr'.
;Use one interface section for each isdn port! ;Use one interface section for each ISDN port!
;ntmode=yes ;if isdn card operates in nt mode, set this to yes ;ntmode=yes ;if the ISDN card operates in NT-mode, set this to 'yes'
isdnmode=msn ;'MSN' (point-to-multipoint) or 'DID' (direct inward dial) isdnmode=msn ;'MSN' (point-to-multipoint) or 'DID' (direct inward dial)
;when using NT-mode, 'DID' should be set in any case ;when using NT-mode, 'DID' should be set in any case
incomingmsn=* ;allow incoming calls to this list of MSNs/DIDs, * = any incomingmsn=* ;allow incoming calls to this list of MSNs/DIDs, * = any
;defaultcid=123 ;set a default caller id to that interface for dial-out, ;defaultcid=123 ;set a default caller ID to that interface for dial-out,
;this caller id will be used when dial option 'd' is set. ;this caller ID will be used when the dial option 'd' is set.
;controller=0 ;ISDN4BSD default ;controller=0 ;ISDN4BSD default
;controller=7 ;ISDN4BSD USB default ;controller=7 ;ISDN4BSD USB default
controller=1 ;capi controller number of this interface/port controller=1 ;CAPI controller number of this interface/port
group=1 ;dialout group group=1 ;dialout group
;prefix=0 ;set a prefix to calling number on incoming calls ;prefix=0 ;set a prefix to the calling number on incoming calls
softdtmf=on ;enable/disable software dtmf detection, recommended for AVM cards softdtmf=on ;enable/disable software DTMF detection, recommended for AVM cards
relaxdtmf=on ;in addition to softdtmf, you can use relaxed dtmf detection relaxdtmf=on ;in addition to softdtmf, you can use relaxed DTMF detection
faxdetect=off ;enable faxdetection and redirection to EXTEN 'fax' for incoming and/or faxdetect=off ;enable faxdetection and redirection to EXTEN 'fax' for incoming and/or
;outgoing calls. (default='off', possible values: 'incoming','outgoing','both') ;outgoing calls. (default='off', possible values: 'incoming','outgoing','both')
faxdetecttime=0 ;Only detect faxes during the first 'n' seconds of the call. faxdetecttime=0 ;Only detect faxes during the first 'n' seconds of the call.
@ -47,15 +47,17 @@ context=isdn-in ;context for incoming calls
;holdtype=hold ;when the PBX puts the call on hold, ISDN HOLD will be used. If ;holdtype=hold ;when the PBX puts the call on hold, ISDN HOLD will be used. If
;set to 'local' (default value), no hold is done and the PBX may ;set to 'local' (default value), no hold is done and the PBX may
;play MOH. ;play MOH.
;immediate=yes ;DID: immediate start of pbx with extension 's' if no digits were ;immediate=yes ;DID: immediate start of PBX with extension 's' if no digits were
; received on incoming call (no destination number yet) ; received on incoming call (no destination number yet)
;MSN: start pbx on CONNECT_IND and don't wait for SETUP/SENDING-COMPLETE. ;MSN: start PBX on CONNECT_IND and do not wait for SETUP/SENDING-COMPLETE.
; info like REDIRECTINGNUMBER may be lost, but this is necessary for ; info like REDIRECTINGNUMBER may be lost, but this is necessary for
; drivers/pbx/telco which does not send SETUP or SENDING-COMPLETE. ; drivers/pbx/telco which does not send SETUP or SENDING-COMPLETE.
;echosquelch=1 ;_VERY_PRIMITIVE_ echo suppression. Disable this before you start recording voicemail ;echosquelch=1 ;_VERY_PRIMITIVE_ echo suppression. Disable it before you start recording voicemail
;or your files may get choppy. (you can use capicommand(echosquelch|no) for this) ;or your files may get choppy. (you can use capicommand(echosquelch|no) for this)
;echocancel=yes ;Dialogic Diva (Capi) echo cancelation (yes=g165) ;echocancel=yes ;Dialogic(R) Diva(R) (CAPI) echo cancellation (yes=g165)
;(possible values: 'no', 'yes', 'force', 'g164', 'g165') ;(possible values: 'no', 'yes', 'force', 'g164', 'g165')
;echocancelpath=1;Dialogic(R) Diva(R) (CAPI) echo cancellation path
;(possible values: default '1' - E.1/T.1/S0, '2' - IP, '3' - both)
echocancelold=yes;use facility selector 6 instead of correct 8 (necessary for older eicon drivers) echocancelold=yes;use facility selector 6 instead of correct 8 (necessary for older eicon drivers)
;echotail=64 ;echo cancel tail setting (default=0 for maximum) ;echotail=64 ;echo cancel tail setting (default=0 for maximum)
;echocancelnlp=1 ;activate non-linear-processing; this improves echo cancel ratio, but might ;echocancelnlp=1 ;activate non-linear-processing; this improves echo cancel ratio, but might
@ -65,9 +67,9 @@ echocancelold=yes;use facility selector 6 instead of correct 8 (necessary for ol
;pickupgroup=1 ;PBX pickup group (which call groups are we allowed to pickup) ;pickupgroup=1 ;PBX pickup group (which call groups are we allowed to pickup)
;transfergroup=1 ;Controller(s) where a transfer on native bridge is allowed to. ;transfergroup=1 ;Controller(s) where a transfer on native bridge is allowed to.
;language=de ;set language for this device (overwrites default language) ;language=de ;set language for this device (overwrites default language)
;disallow=all ;RTP codec selection (valid with Dialogic Diva only) ;disallow=all ;RTP codec selection (valid with Dialogic(R) Diva(R) Media Boards only)
;allow=all ;RTP codec selection (valid with Dialogic Diva only) ;allow=all ;RTP codec selection (valid with Dialogic(R) Diva(R) Media Boards only)
devices=2 ;number of concurrent calls (b-channels) on this controller devices=2 ;number of concurrent calls (B-Channels) on this controller
;(2 makes sense for single BRI, 30/23 for PRI/T1) ;(2 makes sense for single BRI, 30/23 for PRI/T1)
;jb..... ;with Asterisk 1.4 you can configure jitterbuffer, ;jb..... ;with Asterisk 1.4 you can configure jitterbuffer,
;see Asterisk documentation for all jb* setting available. ;see Asterisk documentation for all jb* setting available.

File diff suppressed because it is too large Load Diff

View File

@ -40,6 +40,7 @@
#include "asterisk/abstract_jb.h" #include "asterisk/abstract_jb.h"
#endif #endif
#include "asterisk/musiconhold.h" #include "asterisk/musiconhold.h"
#include "dlist.h"
#ifndef _PBX_CAPI_H #ifndef _PBX_CAPI_H
#define _PBX_CAPI_H #define _PBX_CAPI_H
@ -138,6 +139,7 @@ static inline unsigned int read_capi_dword(void *m)
#define CC_BPROTO_TRANSPARENT 0 #define CC_BPROTO_TRANSPARENT 0
#define CC_BPROTO_FAXG3 1 #define CC_BPROTO_FAXG3 1
#define CC_BPROTO_RTP 2 #define CC_BPROTO_RTP 2
#define CC_BPROTO_VOCODER 3
/* FAX Resolutions */ /* FAX Resolutions */
#define FAX_STANDARD_RESOLUTION 0 #define FAX_STANDARD_RESOLUTION 0
@ -152,6 +154,7 @@ static inline unsigned int read_capi_dword(void *m)
#define FAX_ASCII_FORMAT 5 #define FAX_ASCII_FORMAT 5
#define FAX_EXTENDED_ASCII_FORMAT 6 #define FAX_EXTENDED_ASCII_FORMAT 6
#define FAX_BINARY_FILE_TRANSFER_FORMAT 7 #define FAX_BINARY_FILE_TRANSFER_FORMAT 7
#define FAX_NATIVE_FILE_TRANSFER_FORMAT 8
/* Fax struct */ /* Fax struct */
struct fax3proto3 { struct fax3proto3 {
@ -178,6 +181,7 @@ typedef struct fax3proto3 B3_PROTO_FAXG3;
#define FACILITYSELECTOR_SUPPLEMENTARY 0x0003 #define FACILITYSELECTOR_SUPPLEMENTARY 0x0003
#define FACILITYSELECTOR_LINE_INTERCONNECT 0x0005 #define FACILITYSELECTOR_LINE_INTERCONNECT 0x0005
#define FACILITYSELECTOR_ECHO_CANCEL 0x0008 #define FACILITYSELECTOR_ECHO_CANCEL 0x0008
#define PRIV_SELECTOR_DTMF_ONDATA 0x00fa
#define FACILITYSELECTOR_FAX_OVER_IP 0x00fd #define FACILITYSELECTOR_FAX_OVER_IP 0x00fd
#define FACILITYSELECTOR_VOICE_OVER_IP 0x00fe #define FACILITYSELECTOR_VOICE_OVER_IP 0x00fe
@ -191,6 +195,13 @@ typedef struct fax3proto3 B3_PROTO_FAXG3;
#define EC_OPTION_DISABLE_G164_OR_G165 (1<<1 | 1<<2) #define EC_OPTION_DISABLE_G164_OR_G165 (1<<1 | 1<<2)
#define EC_DEFAULT_TAIL 0 /* maximum */ #define EC_DEFAULT_TAIL 0 /* maximum */
/*
EC path mask
*/
#define EC_ECHOCANCEL_PATH_IFC 1 /* Default, activate EC for E.1/T.1/S0 only */
#define EC_ECHOCANCEL_PATH_IP 2 /* Activate EC for IP */
#define EC_ECHOCANCEL_PATH_BITS (EC_ECHOCANCEL_PATH_IFC | EC_ECHOCANCEL_PATH_IP)
#define CC_HOLDTYPE_LOCAL 0 #define CC_HOLDTYPE_LOCAL 0
#define CC_HOLDTYPE_HOLD 1 #define CC_HOLDTYPE_HOLD 1
#define CC_HOLDTYPE_NOTIFY 2 #define CC_HOLDTYPE_NOTIFY 2
@ -261,6 +272,10 @@ struct cc_capi_gains {
#define CAPI_CHANNELTYPE_D 1 #define CAPI_CHANNELTYPE_D 1
#define CAPI_CHANNELTYPE_NULL 2 #define CAPI_CHANNELTYPE_NULL 2
#define CAPI_RESOURCE_PLCI_NULL 0
#define CAPI_RESOURCE_PLCI_DATA 1
#define CAPI_RESOURCE_PLCI_LINE 2
/* the lower word is reserved for capi commands */ /* the lower word is reserved for capi commands */
#define CAPI_WAITEVENT_B3_UP 0x00010000 #define CAPI_WAITEVENT_B3_UP 0x00010000
#define CAPI_WAITEVENT_B3_DOWN 0x00020000 #define CAPI_WAITEVENT_B3_DOWN 0x00020000
@ -437,6 +452,21 @@ struct capi_pvt {
float rxmin; float rxmin;
float txmin; float txmin;
unsigned short divaAudioFlags;
unsigned short divaDigitalRxGain;
float divaDigitalRxGainDB;
unsigned short divaDigitalTxGain;
float divaDigitalTxGainDB;
unsigned short rxPitch;
unsigned short txPitch;
char special_tone_extension[AST_MAX_EXTENSION+1];
char channel_command_digits[AST_MAX_EXTENSION+1];
time_t channel_command_timestamp;
int channel_command_digit;
int command_pass_digits;
diva_entity_queue_t channel_command_q;
#ifdef CC_AST_HAS_VERSION_1_4 #ifdef CC_AST_HAS_VERSION_1_4
struct ast_jb_conf jbconf; struct ast_jb_conf jbconf;
char mohinterpret[MAX_MUSICCLASS]; char mohinterpret[MAX_MUSICCLASS];
@ -466,6 +496,14 @@ struct capi_pvt {
/* Q.SIG features */ /* Q.SIG features */
int qsigfeat; int qsigfeat;
struct cc_qsig_data qsig_data; struct cc_qsig_data qsig_data;
/* Resource PLCI data */
int resource_plci_type; /* NULL PLCI, DATA, LINE */
/* Resource PLCI line if data */
struct capi_pvt *line_plci;
/* Resource PLCI data data if line */
struct capi_pvt *data_plci;
/*! Next channel in list */ /*! Next channel in list */
struct capi_pvt *next; struct capi_pvt *next;
@ -528,6 +566,7 @@ struct cc_capi_conf {
struct ast_jb_conf jbconf; struct ast_jb_conf jbconf;
char mohinterpret[MAX_MUSICCLASS]; char mohinterpret[MAX_MUSICCLASS];
#endif #endif
int echocancelpath;
}; };
struct cc_capi_controller { struct cc_capi_controller {
@ -559,6 +598,9 @@ struct cc_capi_controller {
int CONF; int CONF;
/* RTP */ /* RTP */
int rtpcodec; int rtpcodec;
int divaExtendedFeaturesAvailable;
int ecPath;
}; };
@ -630,4 +672,14 @@ extern void capi_gains(struct cc_capi_gains *g, float rxgain, float txgain);
extern char chatinfo_usage[]; extern char chatinfo_usage[];
#endif #endif
typedef int (*pbx_capi_command_proc_t)(struct ast_channel *, char *);
pbx_capi_command_proc_t pbx_capi_lockup_command_by_name(const char* name);
/* DIVA specific MANUFACTURER definitions */
#define _DI_MANU_ID 0x44444944
#define _DI_ASSIGN_PLCI 0x0001
#define _DI_DSP_CTRL 0x0003
#define _DI_OPTIONS_REQUEST 0x0009
#endif #endif

View File

@ -12,67 +12,136 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <time.h>
#include <errno.h> #include <errno.h>
#include <sys/signal.h> #include <sys/signal.h>
#include "chan_capi_platform.h"
#include "chan_capi20.h" #include "chan_capi20.h"
#include "chan_capi.h" #include "chan_capi.h"
#include "chan_capi_chat.h" #include "chan_capi_chat.h"
#include "chan_capi_utils.h" #include "chan_capi_utils.h"
#include "chan_capi_command.h"
#define CHAT_FLAG_MOH 0x0001 #define CHAT_FLAG_MOH 0x0001
typedef enum {
RoomMemberDefault = 0, /* Rx/Tx by default, muted by operator */
RoomMemberListener = 1, /* Rx only, always muted */
RoomMemberOperator = 2 /* Rx/Tx, newer muted */
} room_member_type_t;
typedef enum {
RoomModeDefault = 0,
RoomModeMuted = 1
} room_mode_t;
#define PLCI_PER_LX_REQUEST 8
#define PBX_CHAT_MEMBER_INFO_RECENT 0x00000001
#define PBX_CHAT_MEMBER_INFO_REMOVE 0x00000002
struct capichat_s { struct capichat_s {
char name[16]; char name[16];
unsigned int number; unsigned int number;
int active; int active;
room_member_type_t room_member_type;
room_mode_t room_mode;
struct capi_pvt *i; struct capi_pvt *i;
struct capichat_s *next; struct capichat_s *next;
unsigned int info;
time_t time;
}; };
struct _deffered_chat_capi_message;
typedef struct _deffered_chat_capi_message {
int busy;
_cdword datapath;
capi_prestruct_t p_struct;
unsigned char p_list[254];
} deffered_chat_capi_message_t;
static struct capichat_s *chat_list = NULL; static struct capichat_s *chat_list = NULL;
AST_MUTEX_DEFINE_STATIC(chat_lock); AST_MUTEX_DEFINE_STATIC(chat_lock);
/* /*
* update the capi mixer for the given char room * LOCALS
*/ */
static void update_capi_mixer(int remove, unsigned int roomnumber, struct capi_pvt *i) static const char* room_member_type_2_name(room_member_type_t room_member_type);
/*
* partial update the capi mixer for the given char room
*/
static struct capichat_s* update_capi_mixer_part(
struct capichat_s *chat_start,
int overall_found,
deffered_chat_capi_message_t* capi_msg,
int remove,
unsigned int roomnumber,
struct capi_pvt *i)
{ {
struct capi_pvt *ii; struct capi_pvt *ii, *ii_last = NULL;
struct capichat_s *room; struct capichat_s *room;
unsigned char p_list[360]; unsigned char* p_list = &capi_msg->p_list[0];
_cdword dest; _cdword dest;
_cdword datapath; _cdword datapath;
capi_prestruct_t p_struct; capi_prestruct_t* p_struct = &capi_msg->p_struct;
unsigned int found = 0; unsigned int found = 0;
_cword j = 0; _cword j = 0;
struct capichat_s *new_chat_start = NULL;
room_member_type_t main_member_type = RoomMemberDefault;
room_mode_t room_mode = RoomModeDefault;
if (i->PLCI == 0) { room = chat_start;
cc_verbose(2, 0, VERBOSE_PREFIX_3 CC_MESSAGE_NAME while (room != 0) {
" mixer: %s: PLCI is unset, abort.\n", i->vname); if (room->i == i) {
return; main_member_type = room->room_member_type;
room_mode = room->room_mode;
break;
}
room = room->next;
} }
cc_mutex_lock(&chat_lock); if ((room_mode == RoomModeMuted) && (main_member_type == RoomMemberDefault)) {
room = chat_list; main_member_type = RoomMemberListener;
}
room = chat_start;
while (room) { while (room) {
if ((room->number == roomnumber) && if ((room->number == roomnumber) &&
(room->i != i)) { (room->i != i)) {
found++; if ((found >= PLCI_PER_LX_REQUEST) || ((j + 9) > sizeof(capi_msg->p_list))) {
if (j + 9 > sizeof(p_list)) {
/* maybe we need to split capi messages here */ /* maybe we need to split capi messages here */
new_chat_start = room;
break; break;
} }
found++;
ii = room->i; ii = room->i;
ii_last = ii;
p_list[j++] = 8; p_list[j++] = 8;
p_list[j++] = (_cbyte)(ii->PLCI); p_list[j++] = (_cbyte)(ii->PLCI);
p_list[j++] = (_cbyte)(ii->PLCI >> 8); p_list[j++] = (_cbyte)(ii->PLCI >> 8);
p_list[j++] = (_cbyte)(ii->PLCI >> 16); p_list[j++] = (_cbyte)(ii->PLCI >> 16);
p_list[j++] = (_cbyte)(ii->PLCI >> 24); p_list[j++] = (_cbyte)(ii->PLCI >> 24);
dest = (remove) ? 0x00000000 : 0x00000003; dest = (remove) ? 0x00000000 : 0x00000003;
if (ii->channeltype == CAPI_CHANNELTYPE_NULL) { if (ii->channeltype == CAPI_CHANNELTYPE_NULL && ii->line_plci == 0) {
dest |= 0x00000030; dest |= 0x00000030;
} }
if (remove == 0) {
room_member_type_t room_member_type = room->room_member_type;
if ((room_mode == RoomModeMuted) && (room_member_type == RoomMemberDefault)) {
room_member_type = RoomMemberListener;
}
if ((main_member_type == RoomMemberListener) && (room_member_type == RoomMemberListener)) {
dest &= ~3U; /* Disable data transmission between two listener */
} else if ((main_member_type == RoomMemberListener) && (room_member_type != RoomMemberListener)) {
dest &= ~1U; /* Disable data transmission from main PLCI to member PLCI */
} else if ((main_member_type != RoomMemberListener) && (room_member_type == RoomMemberListener)) {
dest &= ~2U; /* Disable data transmission from member PLCI to main PLCI */
}
}
p_list[j++] = (_cbyte)(dest); p_list[j++] = (_cbyte)(dest);
p_list[j++] = (_cbyte)(dest >> 8); p_list[j++] = (_cbyte)(dest >> 8);
p_list[j++] = (_cbyte)(dest >> 16); p_list[j++] = (_cbyte)(dest >> 16);
@ -82,45 +151,177 @@ static void update_capi_mixer(int remove, unsigned int roomnumber, struct capi_p
} }
room = room->next; room = room->next;
} }
room = chat_list;
while (room) {
if (room->number == roomnumber) {
room->active = found + ((remove) ? 0 : 1);
}
room = room->next;
}
cc_mutex_unlock(&chat_lock);
if (found) { if (found != 0) {
p_struct.wLen = j; p_struct->wLen = j;
p_struct.info = p_list; p_struct->info = p_list;
/* don't send DATA_B3 to me */ /* don't send DATA_B3 to me */
datapath = 0x00000000; datapath = 0x00000000;
if (remove) { if (remove) {
/* now we need DATA_B3 again */ /* now we need DATA_B3 again */
datapath = 0x0000000c; if (i->line_plci == 0) {
if (found == 1) { if (i->channeltype != CAPI_CHANNELTYPE_NULL) {
datapath = 0x0000000c;
} else {
datapath = 0x00000030;
}
}
if (overall_found == 1) {
/* only one left, enable DATA_B3 too */ /* only one left, enable DATA_B3 too */
p_list[5] |= 0x0c; if (ii_last->line_plci == 0) {
if (ii_last->channeltype != CAPI_CHANNELTYPE_NULL) {
p_list[5] |= 0x0c;
} else {
p_list[5] |= 0x30;
}
}
} }
} }
if (i->channeltype == CAPI_CHANNELTYPE_NULL) { if ((i->channeltype == CAPI_CHANNELTYPE_NULL) && (i->line_plci == 0)) {
if (!remove) { if (!remove) {
datapath |= 0x00000030; datapath |= 0x00000030;
} }
} }
cc_verbose(3, 1, VERBOSE_PREFIX_3 CC_MESSAGE_NAME capi_msg->busy = 1;
" mixer: %s PLCI=0x%04x LI=0x%x\n", i->vname, i->PLCI, datapath); capi_msg->datapath = datapath;
}
capi_sendf(NULL, 0, CAPI_FACILITY_REQ, i->PLCI, get_capi_MessageNumber(), return (new_chat_start);
"w(w(dc))", }
FACILITYSELECTOR_LINE_INTERCONNECT,
0x0001, /* CONNECT */ static void update_capi_mixer(int remove, unsigned int roomnumber, struct capi_pvt *i, deffered_chat_capi_message_t* update_segment)
datapath, {
&p_struct struct capichat_s *room;
); unsigned int overall_found;
unsigned int nr_segments;
if (i->PLCI == 0) {
cc_verbose(2, 0, VERBOSE_PREFIX_3 CC_MESSAGE_NAME
" mixer: %s: PLCI is unset, abort.\n", i->vname);
return;
}
if (update_segment == 0) {
cc_mutex_lock(&chat_lock);
}
/*
Get overall amount of parties
*/
for (room = chat_list, overall_found = 0; room != 0; room = room->next) {
overall_found += ((room->number == roomnumber) && (room->i != i));
}
room = chat_list;
while (room != 0) {
if (room->number == roomnumber) {
room->active = overall_found + ((remove != 0) ? 0 : 1);
}
room = room->next;
}
nr_segments = overall_found/PLCI_PER_LX_REQUEST + (overall_found%PLCI_PER_LX_REQUEST != 0);
if (nr_segments != 0) {
deffered_chat_capi_message_t __segments[nr_segments];
deffered_chat_capi_message_t* segments = update_segment == 0 ? __segments : update_segment;
struct capichat_s *chat_start;
int segment_nr, nr;
for (segment_nr = 0, chat_start = chat_list; segment_nr < nr_segments && chat_start != 0; segment_nr++) {
segments[segment_nr].busy = 0;
chat_start = update_capi_mixer_part(chat_start, overall_found, &segments[segment_nr], remove, roomnumber, i);
}
if (update_segment == 0) {
cc_mutex_unlock(&chat_lock);
}
if (chat_start != 0) {
cc_log(LOG_ERROR, "%s:%s at %d.\n", __FILE__, __FUNCTION__, __LINE__);
}
if (update_segment == 0) {
for (nr = 0; nr < segment_nr; nr++) {
if (segments[nr].busy != 0) {
cc_verbose(3, 1, VERBOSE_PREFIX_3 CC_MESSAGE_NAME
" mixer: %s PLCI=0x%04x LI=0x%x\n", i->vname, i->PLCI, segments[nr].datapath);
capi_sendf(NULL, 0, CAPI_FACILITY_REQ, i->PLCI, get_capi_MessageNumber(),
"w(w(dc))",
FACILITYSELECTOR_LINE_INTERCONNECT,
0x0001, /* CONNECT */
segments[nr].datapath,
&segments[nr].p_struct);
}
}
}
return;
}
if (update_segment == 0) {
cc_mutex_unlock(&chat_lock);
}
}
static void update_all_capi_mixers(unsigned int roomnumber)
{
struct capichat_s *room;
unsigned int overall_found;
unsigned int nr_segments;
for (room = chat_list, overall_found = 0; room != 0; room = room->next) {
overall_found += (room->number == roomnumber);
}
nr_segments = overall_found/PLCI_PER_LX_REQUEST + (overall_found%PLCI_PER_LX_REQUEST != 0);
{
deffered_chat_capi_message_t *segments, *segment;
unsigned int PLCIS[overall_found];
int i, j, nr;
segments = malloc (sizeof(*segments)*overall_found*nr_segments);
if (segments == 0) {
cc_mutex_unlock(&chat_lock);
return;
}
for (room = chat_list, i = 0; room != 0; room = room->next) {
if (room->number == roomnumber && room->i && room->i->PLCI != 0) {
segment = segments + i*nr_segments;
for (nr = 0; nr < nr_segments; nr++) {
segment[nr].busy = 0;
}
update_capi_mixer(0, roomnumber, room->i, segment);
if (segment[0].busy != 0) {
PLCIS[i++] = room->i->PLCI;
}
}
}
cc_mutex_unlock(&chat_lock);
for (j = 0; j < i; j++) {
segment = segments + j*nr_segments;
for (nr = 0; nr < nr_segments; nr++) {
if (segment[nr].busy != 0) {
cc_verbose(3, 1, VERBOSE_PREFIX_3 CC_MESSAGE_NAME
" mixer: PLCI=0x%04x LI=0x%x\n", PLCIS[j], segment[nr].datapath);
capi_sendf(NULL, 0, CAPI_FACILITY_REQ, PLCIS[j], get_capi_MessageNumber(),
"w(w(dc))",
FACILITYSELECTOR_LINE_INTERCONNECT,
0x0001, /* CONNECT */
segment[nr].datapath,
&segment[nr].p_struct);
}
}
}
free (segments);
} }
} }
@ -152,17 +353,18 @@ static void del_chat_member(struct capichat_s *room)
} }
cc_mutex_unlock(&chat_lock); cc_mutex_unlock(&chat_lock);
update_capi_mixer(1, roomnumber, i); update_capi_mixer(1, roomnumber, i, 0);
} }
/* /*
* add a new chat member * add a new chat member
*/ */
static struct capichat_s *add_chat_member(char *roomname, struct capi_pvt *i) static struct capichat_s *add_chat_member(char *roomname, struct capi_pvt *i, room_member_type_t room_member_type)
{ {
struct capichat_s *room = NULL; struct capichat_s *room = NULL;
struct capichat_s *tmproom; struct capichat_s *tmproom;
unsigned int roomnumber = 1; unsigned int roomnumber = 1;
room_mode_t room_mode = RoomModeDefault;
room = malloc(sizeof(struct capichat_s)); room = malloc(sizeof(struct capichat_s));
if (room == NULL) { if (room == NULL) {
@ -174,13 +376,15 @@ static struct capichat_s *add_chat_member(char *roomname, struct capi_pvt *i)
strncpy(room->name, roomname, sizeof(room->name)); strncpy(room->name, roomname, sizeof(room->name));
room->name[sizeof(room->name) - 1] = 0; room->name[sizeof(room->name) - 1] = 0;
room->i = i; room->i = i;
room->room_member_type = room_member_type;
cc_mutex_lock(&chat_lock); cc_mutex_lock(&chat_lock);
tmproom = chat_list; tmproom = chat_list;
while (tmproom) { while (tmproom) {
if (!strcmp(tmproom->name, roomname)) { if (!strcmp(tmproom->name, roomname)) {
roomnumber = tmproom->number; roomnumber = tmproom->number;
room_mode = tmproom->room_mode;
break; break;
} else { } else {
if (tmproom->number == roomnumber) { if (tmproom->number == roomnumber) {
@ -191,16 +395,25 @@ static struct capichat_s *add_chat_member(char *roomname, struct capi_pvt *i)
} }
room->number = roomnumber; room->number = roomnumber;
room->room_mode = room_mode;
for (tmproom = chat_list; tmproom != 0; tmproom = tmproom->next) {
if (tmproom->number == roomnumber) {
tmproom->info &= ~PBX_CHAT_MEMBER_INFO_RECENT;
}
}
room->info |= PBX_CHAT_MEMBER_INFO_RECENT;
room->time = time(0);
room->next = chat_list; room->next = chat_list;
chat_list = room; chat_list = room;
cc_mutex_unlock(&chat_lock); cc_mutex_unlock(&chat_lock);
cc_verbose(3, 0, VERBOSE_PREFIX_3 "%s: added new chat member to room '%s' (%d)\n", cc_verbose(3, 0, VERBOSE_PREFIX_3 "%s: added new chat member to room '%s' %s(%d)\n",
i->vname, roomname, roomnumber); i->vname, roomname, room_member_type_2_name(room_member_type), roomnumber);
update_capi_mixer(0, roomnumber, i); update_capi_mixer(0, roomnumber, i, 0);
return room; return room;
} }
@ -209,7 +422,7 @@ static struct capichat_s *add_chat_member(char *roomname, struct capi_pvt *i)
* loop during chat * loop during chat
*/ */
static void chat_handle_events(struct ast_channel *c, struct capi_pvt *i, static void chat_handle_events(struct ast_channel *c, struct capi_pvt *i,
struct capichat_s *room, unsigned int flags) struct capichat_s *room, unsigned int flags, struct capi_pvt* iline, FILE* voice_message)
{ {
struct ast_frame *f; struct ast_frame *f;
int ms; int ms;
@ -219,24 +432,33 @@ static void chat_handle_events(struct ast_channel *c, struct capi_pvt *i,
int nfds = 0; int nfds = 0;
struct ast_channel *rchan; struct ast_channel *rchan;
struct ast_channel *chan = c; struct ast_channel *chan = c;
int moh_active = 0; int moh_active = 0, voice_message_moh_active = 0;
int write_block_nr = 2;
ast_indicate(chan, -1); if (voice_message == NULL) {
ast_indicate(chan, -1);
}
waitfd = i->readerfd; waitfd = i->readerfd;
if (i->channeltype == CAPI_CHANNELTYPE_NULL) { if (i->channeltype == CAPI_CHANNELTYPE_NULL) {
nfds = 1; nfds = 1;
ast_set_read_format(chan, capi_capability); if (voice_message == NULL) {
ast_set_write_format(chan, capi_capability); ast_set_read_format(chan, capi_capability);
ast_set_write_format(chan, capi_capability);
}
} }
if ((flags & CHAT_FLAG_MOH) && (room->active < 2)) { if ((flags & CHAT_FLAG_MOH) && ((room->active < 2) || (voice_message != 0))) {
#if defined(CC_AST_HAS_VERSION_1_6) || defined(CC_AST_HAS_VERSION_1_4) #if defined(CC_AST_HAS_VERSION_1_6) || defined(CC_AST_HAS_VERSION_1_4)
ast_moh_start(chan, NULL, NULL); ast_moh_start(chan, NULL, NULL);
#else #else
ast_moh_start(chan, NULL); ast_moh_start(chan, NULL);
#endif #endif
moh_active = 1; if (voice_message == 0) {
moh_active = 1;
} else {
voice_message_moh_active = 1;
}
} }
while (1) { while (1) {
@ -245,6 +467,10 @@ static void chat_handle_events(struct ast_channel *c, struct capi_pvt *i,
errno = 0; errno = 0;
exception = 0; exception = 0;
if ((room->info & PBX_CHAT_MEMBER_INFO_REMOVE) != 0) {
break;
}
rchan = ast_waitfor_nandfds(&chan, 1, &waitfd, nfds, &exception, &ready_fd, &ms); rchan = ast_waitfor_nandfds(&chan, 1, &waitfd, nfds, &exception, &ready_fd, &ms);
if (rchan) { if (rchan) {
@ -262,13 +488,17 @@ static void chat_handle_events(struct ast_channel *c, struct capi_pvt *i,
} else if (f->frametype == AST_FRAME_VOICE) { } else if (f->frametype == AST_FRAME_VOICE) {
cc_verbose(5, 1, VERBOSE_PREFIX_3 "%s: chat: voice frame.\n", cc_verbose(5, 1, VERBOSE_PREFIX_3 "%s: chat: voice frame.\n",
i->vname); i->vname);
if (i->channeltype == CAPI_CHANNELTYPE_NULL) { if ((voice_message == 0) && (i->channeltype == CAPI_CHANNELTYPE_NULL)) {
capi_write_frame(i, f); capi_write_frame(i, f);
} else if (iline != 0) {
capi_write_frame(iline, f);
} }
} else if (f->frametype == AST_FRAME_NULL) { } else if (f->frametype == AST_FRAME_NULL) {
/* ignore NULL frame */ /* ignore NULL frame */
cc_verbose(5, 1, VERBOSE_PREFIX_3 "%s: chat: NULL frame, ignoring.\n", cc_verbose(5, 1, VERBOSE_PREFIX_3 "%s: chat: NULL frame, ignoring.\n",
i->vname); i->vname);
} else if ((f->frametype == AST_FRAME_DTMF_END) && (voice_message == 0)) {
pbx_capi_voicecommand_process_digit (i, c, f->subclass);
} else { } else {
cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: chat: unhandled frame %d/%d.\n", cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: chat: unhandled frame %d/%d.\n",
i->vname, f->frametype, f->subclass); i->vname, f->frametype, f->subclass);
@ -282,7 +512,28 @@ static void chat_handle_events(struct ast_channel *c, struct capi_pvt *i,
} }
f = capi_read_pipeframe(i); f = capi_read_pipeframe(i);
if (f->frametype == AST_FRAME_VOICE) { if (f->frametype == AST_FRAME_VOICE) {
ast_write(chan, f); if (voice_message == 0) {
ast_write(chan, f);
} else {
char* p = f->FRAME_DATA_PTR;
int len;
do {
if ((len = fread(p, 1, f->datalen, voice_message)) > 0) {
if (len < f->datalen) {
memset (&p[len], 0x00, f->datalen-len);
len = 0;
}
capi_write_frame(i, f);
}
} while ((write_block_nr-- != 0) && (len > 0));
if (len <= 0) {
break;
}
write_block_nr = 0;
}
} }
/* ignore other nullplci frames */ /* ignore other nullplci frames */
} else { } else {
@ -299,6 +550,9 @@ static void chat_handle_events(struct ast_channel *c, struct capi_pvt *i,
moh_active = 0; moh_active = 0;
} }
} }
if (voice_message_moh_active != 0) {
ast_moh_stop(chan);
}
} }
/* /*
@ -313,6 +567,7 @@ int pbx_capi_chat(struct ast_channel *c, char *param)
ast_group_t tmpcntr; ast_group_t tmpcntr;
unsigned long long contr = 0; unsigned long long contr = 0;
unsigned int flags = 0; unsigned int flags = 0;
room_member_type_t room_member_type = RoomMemberDefault;
roomname = strsep(&param, "|"); roomname = strsep(&param, "|");
options = strsep(&param, "|"); options = strsep(&param, "|");
@ -336,6 +591,13 @@ int pbx_capi_chat(struct ast_channel *c, char *param)
case 'm': case 'm':
flags |= CHAT_FLAG_MOH; flags |= CHAT_FLAG_MOH;
break; break;
case 'l':
room_member_type = RoomMemberListener;
break;
case 'o':
room_member_type = RoomMemberOperator;
break;
default: default:
cc_log(LOG_WARNING, "Unknown chat option '%c'.\n", cc_log(LOG_WARNING, "Unknown chat option '%c'.\n",
*options); *options);
@ -352,8 +614,12 @@ int pbx_capi_chat(struct ast_channel *c, char *param)
i = CC_CHANNEL_PVT(c); i = CC_CHANNEL_PVT(c);
} else { } else {
/* virtual CAPI channel */ /* virtual CAPI channel */
i = capi_mknullif(c, contr); i = pbx_check_resource_plci(c);
if (!i) {
if (i == NULL) {
i = capi_mknullif(c, contr);
}
if (i == NULL) {
return -1; return -1;
} }
} }
@ -367,14 +633,15 @@ int pbx_capi_chat(struct ast_channel *c, char *param)
goto out; goto out;
} }
room = add_chat_member(roomname, i); room = add_chat_member(roomname, i, room_member_type);
if (!room) { if (!room) {
cc_log(LOG_WARNING, "Unable to open " CC_MESSAGE_NAME " chat room.\n"); cc_log(LOG_WARNING, "Unable to open " CC_MESSAGE_NAME " chat room.\n");
capi_remove_nullif(i);
return -1; return -1;
} }
/* main loop */ /* main loop */
chat_handle_events(c, i, room, flags); chat_handle_events(c, i, room, flags, 0, 0);
del_chat_member(room); del_chat_member(room);
@ -384,6 +651,278 @@ out:
return 0; return 0;
} }
int pbx_capi_chat_play(struct ast_channel *c, char *param)
{
struct capi_pvt *i = NULL;
char *roomname, *options, *file_name, *controller;
char *p;
struct capichat_s *room;
ast_group_t tmpcntr;
unsigned long long contr = 0;
unsigned int flags = 0;
room_member_type_t room_member_type = RoomMemberOperator;
FILE* f;
if (param == 0 || *param == 0) {
cc_log(LOG_WARNING, CC_MESSAGE_NAME " chat_play requires parameters.\n");
return (-1);
}
roomname = strsep(&param, "|");
options = strsep(&param, "|");
file_name = strsep(&param, "|");
controller = param;
if (!roomname) {
cc_log(LOG_WARNING, CC_MESSAGE_NAME " chat_play requires room name.\n");
return -1;
}
if (!file_name || !*file_name) {
cc_log(LOG_WARNING, CC_MESSAGE_NAME " chat_play requires file name.\n");
return -1;
}
{
int chat_members;
cc_mutex_lock(&chat_lock);
for (room = chat_list, chat_members = 0; room != 0 && chat_members == 0; room = room->next) {
chat_members += (strcmp(room->name, roomname) == 0);
}
cc_mutex_unlock(&chat_lock);
if (chat_members == 0) {
return 0;
}
}
while ((options) && (*options)) {
switch (*options) {
case 'm':
flags |= CHAT_FLAG_MOH;
break;
default:
cc_log(LOG_WARNING, "Unknown chat option '%c'.\n",
*options);
break;
}
options++;
}
f = fopen(file_name, "rb");
if (f == NULL) {
cc_log(LOG_WARNING, "can't open voice file (%s)\n", strerror(errno));
return -1;
}
{
unsigned char tmp[2] = { 0, 0 };
if (fread(tmp, 1, 2, f) != 2) {
cc_log(LOG_WARNING, "can't read voice file (%s)\n", strerror(errno));
fclose(f);
return -1;
}
}
rewind(f);
if (controller) {
for (p = controller; p && *p; p++) {
if (*p == '|') *p = ',';
}
tmpcntr = ast_get_group(controller);
contr = (unsigned long long)(tmpcntr >> 1);
}
cc_verbose(3, 1, VERBOSE_PREFIX_3 CC_MESSAGE_NAME " chat_play: %s: roomname=%s "
"message=%s controller=%s (0x%llx)\n",
c->name, roomname, file_name, controller, contr);
i = capi_mknullif(c, contr);
if (i == NULL) {
fclose (f);
cc_log(LOG_WARNING, "Unable to play %s to chat room %s", file_name, roomname);
return (-1);
}
if (c->_state != AST_STATE_UP) {
ast_answer(c);
}
capi_wait_for_answered(i);
if (!(capi_wait_for_b3_up(i))) {
goto out;
}
room = add_chat_member(roomname, i, room_member_type);
if (!room) {
capi_remove_nullif(i);
fclose (f);
cc_log(LOG_WARNING, "Unable to open " CC_MESSAGE_NAME " chat room.\n");
return -1;
}
/* main loop */
chat_handle_events(c, i, room, flags, (c->tech == &capi_tech) ? (CC_CHANNEL_PVT(c)) : 0, f);
del_chat_member(room);
out:
fclose (f);
capi_remove_nullif(i);
return 0;
}
int pbx_capi_chat_command (struct ast_channel *c, char *param)
{
struct capichat_s *room, *tmproom;
struct capi_pvt *i;
unsigned int roomnumber, ret = 0;
const char* options = strsep(&param, "|");
const char* roomname = param;
unsigned int disconnect_command = 0;
if (options == 0 || *options == 0) {
cc_log(LOG_WARNING, CC_MESSAGE_NAME " chat_command requires options.\n");
return -1;
}
if (roomname == 0 && *roomname == 0) {
roomname = 0;
}
while (*options != 0) {
switch (*options) {
case 'r': /* Disconnect recent member */
disconnect_command |= 1U;
break;
case 'l': /* Disconnect all listeners */
disconnect_command |= 2U;
break;
case 'o': /* Disconnect all operators */
disconnect_command |= 4U;
break;
case 'a': /* Disconnect all users */
disconnect_command |= 8U;
break;
default:
cc_log(LOG_WARNING, "Unknown chat_disconnect option '%c'.\n", *options);
break;
}
options++;
}
if (disconnect_command != 0) {
i = pbx_check_resource_plci(c);
cc_mutex_lock(&chat_lock);
for (room = chat_list; room != 0; room = room->next) {
if (((roomname != 0 && strcmp(room->name, roomname) == 0) || (i != 0 && room->i == i)) &&
(room->i != 0 && (room->i->used == c || room->i->peer == c))) {
if (room->room_member_type == RoomMemberOperator) {
struct capichat_s *recent = 0;
time_t t = 0;
roomnumber = room->number;
cc_verbose(3, 0, VERBOSE_PREFIX_3 "%s: command %08x (%d)\n",
room->name, disconnect_command, roomnumber);
for (tmproom = chat_list; tmproom != 0; tmproom = tmproom->next) {
if (tmproom->number == roomnumber && tmproom != room) {
if ((disconnect_command & 8U) != 0) {
tmproom->info |= PBX_CHAT_MEMBER_INFO_REMOVE;
} else if ((disconnect_command & 2U) != 0 && room->room_member_type == RoomMemberListener) {
tmproom->info |= PBX_CHAT_MEMBER_INFO_REMOVE;
} else if ((disconnect_command & 4U) != 0 && room->room_member_type == RoomMemberOperator) {
tmproom->info |= PBX_CHAT_MEMBER_INFO_REMOVE;
} else if ((disconnect_command & 1U) != 0) {
if (t < tmproom->time) {
t = tmproom->time;
recent = tmproom;
}
}
}
}
if (recent != 0) {
recent->info |= PBX_CHAT_MEMBER_INFO_REMOVE;
}
} else {
cc_verbose(3, 0, VERBOSE_PREFIX_3 "%s: no permissions for command command %08x\n",
room->name, disconnect_command);
ret = -1;
}
break;
}
}
cc_mutex_unlock(&chat_lock);
}
return (ret);
}
struct capi_pvt* pbx_check_resource_plci(struct ast_channel *c)
{
struct capi_pvt *i = NULL;
const char* id = pbx_builtin_getvar_helper(c, "RESOURCEPLCI");
if (id != 0) {
i = (struct capi_pvt*)strtoul(id, NULL, 0);
if (i != 0 && capi_verify_resource_plci(i) != 0) {
cc_log(LOG_ERROR, "resource PLCI lost\n");
i = 0;
}
}
return i;
}
int pbx_capi_chat_associate_resource_plci(struct ast_channel *c, char *param)
{
struct capi_pvt *i = NULL;
char *controller;
char *p;
ast_group_t tmpcntr;
unsigned long long contr = 0;
controller = param;
if (controller) {
for (p = controller; p && *p; p++) {
if (*p == '|') *p = ',';
}
tmpcntr = ast_get_group(controller);
contr = (unsigned long long)(tmpcntr >> 1);
}
if (c->tech != &capi_tech) {
i = capi_mkresourceif(c, contr, 0);
if (i != NULL) {
char buffer[24];
snprintf(buffer, sizeof(buffer)-1, "%p", i);
/**
Not sure ast_channel pointer does not change across the
use of resource PLCI. For this reason use variable to provide
the pointer to resource PLCI to resource PLCI user
\todo This is still possible that resource PLCI will be lost.
In case this happens this will be necessary to maintain one
live time stamp on resource PLCI and automatically remove
resource LCI if time stamp exceeds certail limit.
*/
pbx_builtin_setvar_helper(c, "RESOURCEPLCI", buffer);
capi_mkresourceif(c, contr, i);
}
}
return 0; /* Always return success in case c->tech == &capi_tech or to fallback to NULL PLCI */
}
/* /*
* do command capi chatinfo * do command capi chatinfo
*/ */
@ -447,3 +986,70 @@ int pbxcli_capi_chatinfo(int fd, int argc, char *argv[])
#endif #endif
} }
static const char* room_member_type_2_name(room_member_type_t room_member_type)
{
switch (room_member_type) {
case RoomMemberListener:
return "in listener mode ";
case RoomMemberOperator:
return "in operator mode ";
default:
return "";
}
}
int pbx_capi_chat_mute(struct ast_channel *c, char *param)
{
struct capichat_s *room;
unsigned int roomnumber;
room_mode_t room_mode;
const char* roommode = strsep(&param, "|");
const char* roomname = param;
struct capi_pvt *i;
if (roommode == 0 || *roommode == 0) {
cc_log(LOG_WARNING, CC_MESSAGE_NAME " chat_mute requires room mode.\n");
return -1;
}
if (ast_true(roommode)) {
room_mode = RoomModeMuted;
} else if (ast_false(roommode)) {
room_mode = RoomModeDefault;
} else {
cc_log(LOG_WARNING, CC_MESSAGE_NAME " false parameter for chat_mute.\n");
cc_log(LOG_WARNING, "Parameter for chat_mute invalid.\n");
return -1;
}
if (roomname == 0 && *roomname == 0) {
roomname = 0;
}
i = pbx_check_resource_plci(c);
cc_mutex_lock(&chat_lock);
for (room = chat_list; room != 0; room = room->next) {
if ((roomname != 0 && strcmp(room->name, roomname) == 0) ||
(i != 0 && room->i == i) ||
(room->i != 0 && (room->i->used == c || room->i->peer == c))) {
roomnumber = room->number;
cc_verbose(3, 0, VERBOSE_PREFIX_3 "%s: change mode to %s (%d)\n",
room->name, room_mode == RoomModeDefault ? "full duplex" : "half duplex", roomnumber);
for (room = chat_list; room != 0; room = room->next) {
if (room->number == roomnumber) {
room->room_mode = room_mode;
}
}
update_all_capi_mixers(roomnumber);
return 0;
}
}
cc_mutex_unlock(&chat_lock);
return -1;
}

View File

@ -16,10 +16,15 @@
* prototypes * prototypes
*/ */
extern int pbx_capi_chat(struct ast_channel *c, char *param); extern int pbx_capi_chat(struct ast_channel *c, char *param);
extern int pbx_capi_chat_associate_resource_plci(struct ast_channel *c, char *param);
extern struct capi_pvt* pbx_check_resource_plci(struct ast_channel *c);
#ifdef CC_AST_HAS_VERSION_1_6 #ifdef CC_AST_HAS_VERSION_1_6
extern char *pbxcli_capi_chatinfo(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); extern char *pbxcli_capi_chatinfo(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
#else #else
extern int pbxcli_capi_chatinfo(int fd, int argc, char *argv[]); extern int pbxcli_capi_chatinfo(int fd, int argc, char *argv[]);
#endif #endif
extern int pbx_capi_chat_command (struct ast_channel *c, char *param);
extern int pbx_capi_chat_mute(struct ast_channel *c, char *param);
extern int pbx_capi_chat_play(struct ast_channel *c, char *param);
#endif #endif

380
chan_capi_command.c Normal file
View File

@ -0,0 +1,380 @@
/*
*
Copyright (c) Dialogic, 2009.
*
This source file is supplied for the use with
Dialogic range of DIVA Server Adapters.
*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
*
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
*
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <sys/time.h>
#include <sys/signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>
#include <sys/types.h>
#include <ctype.h>
#include "chan_capi_platform.h"
#include "xlaw.h"
#include "chan_capi20.h"
#include "chan_capi.h"
#include "chan_capi_rtp.h"
#include "chan_capi_qsig.h"
#include "chan_capi_qsig_ecma.h"
#include "chan_capi_qsig_asn197ade.h"
#include "chan_capi_qsig_asn197no.h"
#include "chan_capi_utils.h"
#include "chan_capi_supplementary.h"
#include "chan_capi_chat.h"
#include "chan_capi_command.h"
typedef struct _pbx_capi_voice_command {
diva_entity_link_t link;
pbx_capi_command_proc_t pbx_capi_command;
char channel_command_digits[AST_MAX_EXTENSION+1];
int length; /* channel_command_digits length */
char command_name[64];
char command_parameters[128];
} pbx_capi_voice_command_t;
/*
* LOCALS
*/
static const char* pbx_capi_voicecommand_digits = "1234567890ABCD*#";
static int pbx_capi_command_nop (struct ast_channel *c, char *param);
static pbx_capi_voice_command_t* pbx_capi_find_command (struct capi_pvt *i, const char* name);
static pbx_capi_voice_command_t* pbx_capi_find_command_by_key (struct capi_pvt *i, const char* key);
static pbx_capi_voice_command_t* pbx_capi_voicecommand_find_digit_command (diva_entity_queue_t* q, const char* digits, int length, int* info);
static void pbx_capi_voicecommand_insert_command (diva_entity_queue_t* q, pbx_capi_voice_command_t* cmd);
/*
* voicecommand|key|param1|param2|...
*
*/
int pbx_capi_voicecommand(struct ast_channel *c, char *param)
{
struct capi_pvt *i;
pbx_capi_voice_command_t* cmd;
const char* command[2];
const char* key[2];
size_t length;
if (c->tech == &capi_tech) {
i = CC_CHANNEL_PVT(c);
} else {
i = pbx_check_resource_plci(c);
}
if (i == 0) {
/*
Ignore command silently to ensure same context can be used to process
all types of calls or in case of fallback to NULL PLCI
*/
return 0;
}
if ((param == NULL) || (*param == 0)) { /* Remove all voice commands */
cc_mutex_lock(&i->lock);
pbx_capi_voicecommand_cleanup(i);
cc_mutex_unlock(&i->lock);
return 0;
}
command[0] = param;
command[1] = strchr (command[0], '|');
if (command[1] == 0) {
/*
* Remove command
*/
cc_mutex_lock(&i->lock);
while ((cmd = pbx_capi_find_command (i, command[0])) != 0) {
cc_verbose(2, 0, VERBOSE_PREFIX_4"%s: voicecommand:%s removed\n", i->vname, cmd->command_name);
diva_q_remove (&i->channel_command_q, &cmd->link);
free (cmd);
}
cc_mutex_unlock(&i->lock);
} else {
if ((command[1] - command[0]) < 2 || (command[1] - command[0]) >= sizeof(cmd->command_name) ||
strchr(pbx_capi_voicecommand_digits, command[1][1]) == 0) {
cc_log(LOG_WARNING, CC_MESSAGE_NAME" voicecommand requires an argument im format 'voicecommand[|key[|param1|param2|...]]'\n");
return -1;
}
key[0] = &command[1][1];
key[1] = strchr (key[0], '|');
length = 0;
if ((key[1] == 0 && strlen(key[0]) >= sizeof(cmd->channel_command_digits)) ||
(key[1] != 0 && ((key[1] - key[0]) == 0 || (key[1] - key[0]) >= sizeof(cmd->channel_command_digits) ||
key[1][1] == 0 || (length = strlen (&key[1][1])) >= sizeof(cmd->command_parameters)))) {
cc_log(LOG_WARNING, CC_MESSAGE_NAME
" voicecommand requires an argument im format 'voicecommand[|key[|param1|param2|...]]'\n");
return -1;
}
if (key[1] == 0) {
key[1] = key[0] + strlen(key[0]);
length = 0;
}
{
const char* p = key[0];
for (p = key[0]; p < key[1]; p++) {
if (strchr(pbx_capi_voicecommand_digits, *p) == 0) {
cc_log(LOG_WARNING, CC_MESSAGE_NAME
" voicecommand key can use only '%s'\n", pbx_capi_voicecommand_digits);
return -1;
}
}
}
cmd = malloc(sizeof(*cmd));
if (cmd == NULL) {
cc_log(LOG_WARNING, CC_MESSAGE_NAME " can not allocate memory for voice command\n");
return -1;
}
memcpy (cmd->command_parameters, &key[1][1], length);
cmd->command_parameters[length] = 0;
length = command[1] - command[0];
memcpy (cmd->command_name, command[0], length);
cmd->command_name[length] = 0;
length = key[1] - key[0];
memcpy (cmd->channel_command_digits, key[0], length);
cmd->channel_command_digits[length] = 0;
cmd->length = length;
cmd->pbx_capi_command = pbx_capi_lockup_command_by_name (cmd->command_name);
if ( cmd->pbx_capi_command == 0) {
cmd->pbx_capi_command = pbx_capi_command_nop; /* accept unknown commands for compatibility reason */
}
cc_verbose(2, 0, VERBOSE_PREFIX_4 "%s: %svoicecommand:%s|%s|%s\n",
i->vname, (cmd->pbx_capi_command == pbx_capi_command_nop) ? "dummy " : "",
cmd->command_name, cmd->channel_command_digits, cmd->command_parameters);
{
pbx_capi_voice_command_t* present_cmd;
cc_mutex_lock(&i->lock);
if ((present_cmd = pbx_capi_find_command_by_key (i, cmd->command_name)) != 0) {
diva_q_remove (&i->channel_command_q, &present_cmd->link);
}
pbx_capi_voicecommand_insert_command (&i->channel_command_q, cmd);
cc_mutex_unlock(&i->lock);
if (present_cmd != 0) {
free (present_cmd);
}
}
}
return 0;
}
int pbx_capi_voicecommand_transparency(struct ast_channel *c, char *param)
{
struct capi_pvt *i;
if (c->tech == &capi_tech) {
i = CC_CHANNEL_PVT(c);
} else {
i = pbx_check_resource_plci(c);
}
if (i == 0) {
/*
Ignore command silently to ensure same context can be used to process
all types of calls or in case of fallback to NULL PLCI
*/
return 0;
}
if ((param == NULL) || (*param == 0)) {
cc_log(LOG_WARNING, "Parameter for voicecommand transparency missing.\n");
return -1;
}
if (ast_true(param)) {
i->command_pass_digits = 1;
} else if (ast_false(param)) {
i->command_pass_digits = 0;
} else {
cc_log(LOG_WARNING, "Wrong parameter for voicecommand transparency.\n");
return -1;
}
return 0;
}
int pbx_capi_voicecommand_cleanup(struct capi_pvt *i)
{
diva_entity_link_t* link;
while ((link = diva_q_get_head(&i->channel_command_q)) != NULL) {
diva_q_remove(&i->channel_command_q, link);
free(link);
}
i->channel_command_digit = 0;
i->channel_command_timestamp = 0;
i->command_pass_digits = 0;
return 0;
}
static pbx_capi_voice_command_t* pbx_capi_find_command(struct capi_pvt *i, const char* name)
{
diva_entity_link_t* link;
for (link = diva_q_get_head (&i->channel_command_q); link != 0; link = diva_q_get_next(link)) {
if (strcmp(((pbx_capi_voice_command_t*)link)->command_name, name) == 0) {
return ((pbx_capi_voice_command_t*)link);
}
}
return 0;
}
static pbx_capi_voice_command_t* pbx_capi_find_command_by_key(struct capi_pvt *i, const char* key)
{
diva_entity_link_t* link;
for (link = diva_q_get_head (&i->channel_command_q); link != 0; link = diva_q_get_next(link)) {
if (strcmp (((pbx_capi_voice_command_t*)link)->channel_command_digits, key) == 0) {
return ((pbx_capi_voice_command_t*)link);
}
}
return 0;
}
/*
* Process received DTMF digit
*
* returs zero if digit should be processed as usually
* returns -1 if digit should be discarded
*/
int pbx_capi_voicecommand_process_digit(struct capi_pvt *i, struct ast_channel *owner, char digit)
{
struct ast_channel *c = (owner == 0) ? i->owner : owner;
pbx_capi_voice_command_t* cmd;
int info;
time_t t;
/*
Simple algorithm due to low amount of entries, moreover all sequences will be short, only 1 ... 2 digits
*/
if ((c == NULL) || (diva_q_get_head(&i->channel_command_q) == 0) ||
(strchr(pbx_capi_voicecommand_digits, digit) == 0)) {
i->channel_command_digit = 0;
return 0;
}
t = time(0);
if (((i->channel_command_digit != 0) && (difftime(t, i->channel_command_timestamp) > 2)) ||
(i->channel_command_digit >= (sizeof(i->channel_command_digits) - 1))) {
i->channel_command_digit = 0;
}
i->channel_command_timestamp = t;
i->channel_command_digits[i->channel_command_digit++] = digit;
i->channel_command_digits[i->channel_command_digit] = 0;
cmd = pbx_capi_voicecommand_find_digit_command(
&i->channel_command_q,
i->channel_command_digits,
i->channel_command_digit,
&info);
if (cmd != 0) {
char command_parameters_copy[sizeof( cmd->command_parameters)];
i->channel_command_digit = 0;
cc_verbose(2, 0, VERBOSE_PREFIX_4 "%s: call voicecommand:%s|%s|%s\n",
i->vname, cmd->command_name, cmd->channel_command_digits, cmd->command_parameters);
strcpy (command_parameters_copy, cmd->command_parameters);
info = ((*(cmd->pbx_capi_command))(c, command_parameters_copy));
cc_verbose(2, 0, VERBOSE_PREFIX_4 "%s: voicecommand:%s|%s|%s %s\n",
i->vname, cmd->command_name, cmd->channel_command_digits, cmd->command_parameters, info == 0 ? "OK" : "ERROR");
} else if (info == 0) {
i->channel_command_digit = 0;
return 0;
}
return ((i->command_pass_digits != 0) ? 0 : -1);
}
static pbx_capi_voice_command_t* pbx_capi_voicecommand_find_digit_command(
diva_entity_queue_t* q,
const char* digits,
int length,
int* info)
{
diva_entity_link_t* link;
for (*info = 0, link = diva_q_get_head(q);
link != 0 && length <= ((pbx_capi_voice_command_t*)link)->length;
link = diva_q_get_next (link)) {
pbx_capi_voice_command_t* cmd = (pbx_capi_voice_command_t*)link;
if (memcmp(digits, cmd->channel_command_digits, length) == 0) {
*info = 1;
if (length == cmd->length) {
return cmd;
}
}
}
return 0;
}
static int pbx_capi_command_nop(struct ast_channel *c, char *param)
{
return 0;
}
static void pbx_capi_voicecommand_insert_command(diva_entity_queue_t* q, pbx_capi_voice_command_t* cmd)
{
diva_entity_link_t* link;
for (link = diva_q_get_head (q); link != 0; link = diva_q_get_next (link)) {
if (((pbx_capi_voice_command_t*)link)->length <= cmd->length) {
diva_q_insert_before (q, link, &cmd->link);
return;
}
}
diva_q_add_tail(q, &cmd->link);
}
/*
* vim:ts=2
*/

32
chan_capi_command.h Normal file
View File

@ -0,0 +1,32 @@
/*
*
Copyright (c) Dialogic, 2009.
*
This source file is supplied for the use with
Dialogic range of DIVA Server Adapters.
*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
*
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
*
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef __CHAN_CAPI_COMMAND_H__
#define __CHAN_CAPI_COMMAND_H__
int pbx_capi_voicecommand (struct ast_channel *c, char *param);
int pbx_capi_voicecommand_transparency (struct ast_channel *c, char *param);
int pbx_capi_voicecommand_process_digit (struct capi_pvt *i, struct ast_channel *owner, char digit);
int pbx_capi_voicecommand_cleanup (struct capi_pvt *i);
#endif

25
chan_capi_platform.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef __CHAN_CAPI_PLATFORM_H__
#define __CHAN_CAPI_PLATFORM_H__
#if __GNUC__ >= 3 /* { */
#ifndef likely
#define likely(x) __builtin_expect(!!(x), 1)
#endif
#ifndef unlikely
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif
#else /* } { */
#ifndef likely
#define likely(__x__) (!!(__x__))
#endif
#ifndef unlikely
#define unlikely(__x__) (!!(__x__))
#endif
#endif /* } */
#endif

View File

@ -18,7 +18,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "chan_capi_platform.h"
#include "chan_capi20.h" #include "chan_capi20.h"
#include "chan_capi.h" #include "chan_capi.h"
#include "chan_capi_utils.h" #include "chan_capi_utils.h"
@ -127,10 +128,10 @@ unsigned int cc_qsig_asn197ade_get_pns(unsigned char *data, int *idx, struct asn
char buf[ASN197ADE_NUMDIGITS_STRSIZE+1]; char buf[ASN197ADE_NUMDIGITS_STRSIZE+1];
unsigned int buflen = sizeof(buf); unsigned int buflen = sizeof(buf);
unsigned res; unsigned res;
int numtype;
ns->partyNumber = NULL; ns->partyNumber = NULL;
ns->screeningInd = userProvidedNotScreened; ns->screeningInd = userProvidedNotScreened;
int numtype;
numtype = (data[myidx++] & 0x0F); /* defines type of Number */ numtype = (data[myidx++] & 0x0F); /* defines type of Number */

View File

@ -19,6 +19,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "chan_capi_platform.h"
#include "chan_capi20.h" #include "chan_capi20.h"
#include "chan_capi.h" #include "chan_capi.h"
#include "chan_capi_utils.h" #include "chan_capi_utils.h"

View File

@ -15,6 +15,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "chan_capi_platform.h"
#include "chan_capi20.h" #include "chan_capi20.h"
#include "chan_capi.h" #include "chan_capi.h"
#include "chan_capi_utils.h" #include "chan_capi_utils.h"
@ -574,13 +575,15 @@ signed int cc_qsig_identifyinvoke(struct cc_qsig_invokedata *invoke, int protoco
invokedescrtype = 2; invokedescrtype = 2;
datalen = invoke->oid_len; datalen = invoke->oid_len;
unsigned char *oidstr = NULL; {
oidstr = cc_qsig_asn1_oid2str(invoke->oid_bin, invoke->oid_len); unsigned char *oidstr = NULL;
if (oidstr) { oidstr = cc_qsig_asn1_oid2str(invoke->oid_bin, invoke->oid_len);
cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (%s)\n", oidstr); if (oidstr) {
free(oidstr); cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (%s)\n", oidstr);
} else { free(oidstr);
cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (unknown - OID not displayable)\n"); } else {
cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (unknown - OID not displayable)\n");
}
} }
if ((datalen) == 4) { if ((datalen) == 4) {
@ -606,13 +609,15 @@ signed int cc_qsig_identifyinvoke(struct cc_qsig_invokedata *invoke, int protoco
invokedescrtype = 2; invokedescrtype = 2;
datalen = invoke->oid_len; datalen = invoke->oid_len;
unsigned char *oidstr = NULL; {
oidstr = cc_qsig_asn1_oid2str(invoke->oid_bin, invoke->oid_len); unsigned char *oidstr = NULL;
if (oidstr) { oidstr = cc_qsig_asn1_oid2str(invoke->oid_bin, invoke->oid_len);
cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (%s)\n", oidstr); if (oidstr) {
free(oidstr); cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (%s)\n", oidstr);
} else { free(oidstr);
cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (unknown - OID not displayable)\n"); } else {
cc_qsig_verbose( 1, VERBOSE_PREFIX_3 "QSIG: INVOKE OP (unknown - OID not displayable)\n");
}
} }
if ((datalen) == 4) { if ((datalen) == 4) {
@ -944,9 +949,11 @@ unsigned int cc_qsig_add_call_setup_data(unsigned char *data, struct capi_pvt *i
i->qsig_data.calltransfer = 1; i->qsig_data.calltransfer = 1;
i->qsig_data.partner_plci = atoi(pp); i->qsig_data.partner_plci = atoi(pp);
/* set the other channel as partner to me */ /* set the other channel as partner to me */
struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci); {
if (ii) struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci);
ii->qsig_data.partner_plci = i->PLCI; if (ii)
ii->qsig_data.partner_plci = i->PLCI;
}
cc_qsig_verbose( 1, " for plci %#x\n", i->qsig_data.partner_plci); cc_qsig_verbose( 1, " for plci %#x\n", i->qsig_data.partner_plci);
} }
@ -960,9 +967,11 @@ unsigned int cc_qsig_add_call_setup_data(unsigned char *data, struct capi_pvt *i
i->qsig_data.calltransfer_onring = 1; i->qsig_data.calltransfer_onring = 1;
i->qsig_data.partner_plci = atoi(pp); i->qsig_data.partner_plci = atoi(pp);
/* set the other channel as partner to me */ /* set the other channel as partner to me */
struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci); {
if (ii) struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci);
ii->qsig_data.partner_plci = i->PLCI; if (ii)
ii->qsig_data.partner_plci = i->PLCI;
}
cc_qsig_verbose( 1, " for plci %#x\n", i->qsig_data.partner_plci); cc_qsig_verbose( 1, " for plci %#x\n", i->qsig_data.partner_plci);
} }

View File

@ -15,6 +15,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "chan_capi_platform.h"
#include "chan_capi20.h" #include "chan_capi20.h"
#include "chan_capi.h" #include "chan_capi.h"
#include "chan_capi_utils.h" #include "chan_capi_utils.h"

View File

@ -19,6 +19,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include "chan_capi_platform.h"
#include "chan_capi20.h" #include "chan_capi20.h"
#include "chan_capi.h" #include "chan_capi.h"
#include "chan_capi_rtp.h" #include "chan_capi_rtp.h"
@ -390,6 +391,10 @@ void voice_over_ip_profile(struct cc_capi_controller *cp)
cp->rtpcodec |= AST_FORMAT_G729A; cp->rtpcodec |= AST_FORMAT_G729A;
cc_verbose(3, 0, "G.729"); cc_verbose(3, 0, "G.729");
} }
if (payload1 & (1U << 27)) {
cp->rtpcodec |= AST_FORMAT_ILBC;
cc_verbose(3, 0, "iLBC");
}
cc_verbose(3, 0, "\n"); cc_verbose(3, 0, "\n");
} }

View File

@ -13,6 +13,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include "chan_capi_platform.h"
#include "chan_capi20.h" #include "chan_capi20.h"
#include "chan_capi.h" #include "chan_capi.h"
#include "chan_capi_supplementary.h" #include "chan_capi_supplementary.h"
@ -194,7 +195,7 @@ static struct ccbsnr_s *get_ccbsnr_link(char type, unsigned int plci,
*/ */
static int ccbsnr_tell_activated(void *data) static int ccbsnr_tell_activated(void *data)
{ {
unsigned int handle = (unsigned int)data; unsigned int handle = (unsigned int)(unsigned long)data;
int ret = 0; int ret = 0;
unsigned int state; unsigned int state;
@ -832,7 +833,7 @@ int pbx_capi_ccbs(struct ast_channel *c, char *data)
for (a = 0; a < 7; a++) { for (a = 0; a < 7; a++) {
/* Wait for CCBS request indication */ /* Wait for CCBS request indication */
if (ast_safe_sleep_conditional(c, 500, ccbsnr_tell_activated, if (ast_safe_sleep_conditional(c, 500, ccbsnr_tell_activated,
(void *)handle) != 0) { (void *)(unsigned long)handle) != 0) {
/* we got a hangup */ /* we got a hangup */
cc_verbose(3, 1, cc_verbose(3, 1,
VERBOSE_PREFIX_3 CC_MESSAGE_NAME " ccbs: hangup.\n"); VERBOSE_PREFIX_3 CC_MESSAGE_NAME " ccbs: hangup.\n");

View File

@ -16,7 +16,9 @@
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include "chan_capi_platform.h"
#include "xlaw.h" #include "xlaw.h"
#include "chan_capi20.h" #include "chan_capi20.h"
#include "chan_capi.h" #include "chan_capi.h"
@ -45,9 +47,9 @@ static struct peerlink_s {
} peerlinkchannel[CAPI_MAX_PEERLINKCHANNELS]; } peerlinkchannel[CAPI_MAX_PEERLINKCHANNELS];
/* /*
* helper for <pbx>_verbose with different verbose settings * helper for <pbx>_verbose
*/ */
void cc_verbose(int o_v, int c_d, char *text, ...) void cc_verbose_internal(char *text, ...)
{ {
char line[4096]; char line[4096];
va_list ap; va_list ap;
@ -55,6 +57,7 @@ void cc_verbose(int o_v, int c_d, char *text, ...)
va_start(ap, text); va_start(ap, text);
vsnprintf(line, sizeof(line), text, ap); vsnprintf(line, sizeof(line), text, ap);
va_end(ap); va_end(ap);
line[sizeof(line)-1]=0;
#if 0 #if 0
{ {
@ -66,13 +69,9 @@ void cc_verbose(int o_v, int c_d, char *text, ...)
} }
#endif #endif
if ((o_v == 0) || (option_verbose > o_v)) { cc_mutex_lock(&verbose_lock);
if ((!c_d) || ((c_d) && (capidebug))) { cc_pbx_verbose(line);
cc_mutex_lock(&verbose_lock); cc_mutex_unlock(&verbose_lock);
cc_pbx_verbose(line);
cc_mutex_unlock(&verbose_lock);
}
}
} }
/* /*
@ -88,10 +87,17 @@ void capi_remove_nullif(struct capi_pvt *i)
return; return;
} }
cc_mutex_lock(&i->lock);
if (i->line_plci != 0) {
ii = i->line_plci;
i->line_plci = 0;
capi_remove_nullif(ii);
}
cc_mutex_unlock(&i->lock);
if (i->PLCI != 0) { if (i->PLCI != 0) {
/* if the interface is in use, hangup first */ /* if the interface is in use, hangup first */
cc_mutex_lock(&i->lock); cc_mutex_lock(&i->lock);
state = i->state; state = i->state;
i->state = CAPI_STATE_DISCONNECTING; i->state = CAPI_STATE_DISCONNECTING;
capi_activehangup(i, state); capi_activehangup(i, state);
@ -126,6 +132,16 @@ void capi_remove_nullif(struct capi_pvt *i)
cc_mutex_unlock(&nullif_lock); cc_mutex_unlock(&nullif_lock);
} }
int capi_verify_resource_plci(const struct capi_pvt *i) {
const struct capi_pvt *ii;
cc_mutex_lock(&nullif_lock);
for (ii = nulliflist; ii != 0 && ii != i; ii = ii->next);
cc_mutex_unlock(&nullif_lock);
return ((ii == i) ? 0 : -1);
}
/* /*
* create new null-interface * create new null-interface
*/ */
@ -135,14 +151,14 @@ struct capi_pvt *capi_mknullif(struct ast_channel *c, unsigned long long control
unsigned int controller = 1; unsigned int controller = 1;
int contrcount; int contrcount;
int channelcount = 0xffff; int channelcount = 0xffff;
int maxcontr = (CAPI_MAX_CONTROLLERS > sizeof(controllermask)) ? int maxcontr = (CAPI_MAX_CONTROLLERS > (sizeof(controllermask)*8)) ?
sizeof(controllermask) : CAPI_MAX_CONTROLLERS; (sizeof(controllermask)*8) : CAPI_MAX_CONTROLLERS;
cc_verbose(3, 1, VERBOSE_PREFIX_4 "capi_mknullif: find controller for mask 0x%lx\n", cc_verbose(3, 1, VERBOSE_PREFIX_4 "capi_mknullif: find controller for mask 0x%lx\n",
controllermask); controllermask);
/* find the next controller of mask with least plcis used */ /* find the next controller of mask with least plcis used */
for (contrcount = 0; contrcount < maxcontr; contrcount++) { for (contrcount = 0; contrcount < maxcontr; contrcount++) {
if ((controllermask & (1 << contrcount))) { if ((controllermask & (1ULL << contrcount)) != 0) {
if (controller_nullplcis[contrcount] < channelcount) { if (controller_nullplcis[contrcount] < channelcount) {
channelcount = controller_nullplcis[contrcount]; channelcount = controller_nullplcis[contrcount];
controller = contrcount + 1; controller = contrcount + 1;
@ -213,6 +229,137 @@ struct capi_pvt *capi_mknullif(struct ast_channel *c, unsigned long long control
return tmp; return tmp;
} }
struct capi_pvt *capi_mkresourceif(struct ast_channel *c, unsigned long long controllermask, struct capi_pvt *data_plci_ifc) {
struct capi_pvt *data_ifc /*, *line_ifc */;
unsigned int controller = 1;
if (data_plci_ifc == 0) {
int contrcount;
int channelcount = 0xffff;
int maxcontr = (CAPI_MAX_CONTROLLERS > (sizeof(controllermask)*8)) ?
(sizeof(controllermask)*8) : CAPI_MAX_CONTROLLERS;
cc_verbose(3, 1, VERBOSE_PREFIX_4 "capi_mkresourceif: find controller for mask 0x%lx\n",
controllermask);
/* find the next controller of mask with least plcis used */
for (contrcount = 0; contrcount < maxcontr; contrcount++) {
if ((controllermask & (1ULL << contrcount)) != 0) {
if (controller_nullplcis[contrcount] < channelcount) {
channelcount = controller_nullplcis[contrcount];
controller = contrcount + 1;
}
}
}
} else {
controller = data_plci_ifc->controller;
}
data_ifc = malloc(sizeof(struct capi_pvt));
if (data_ifc == 0) {
return NULL;
}
memset(data_ifc, 0, sizeof(struct capi_pvt));
cc_mutex_init(&data_ifc->lock);
ast_cond_init(&data_ifc->event_trigger, NULL);
snprintf(data_ifc->name, sizeof(data_ifc->name) - 1, "%s-%sPLCI", c->name, (data_plci_ifc == 0) ? "DATA" : "LINE");
snprintf(data_ifc->vname, sizeof(data_ifc->vname) - 1, "%s", data_ifc->name);
data_ifc->channeltype = CAPI_CHANNELTYPE_NULL;
data_ifc->resource_plci_type = (data_plci_ifc == 0) ? CAPI_RESOURCE_PLCI_DATA : CAPI_RESOURCE_PLCI_LINE;
data_ifc->used = c;
data_ifc->peer = c;
data_ifc->cip = CAPI_CIPI_SPEECH;
data_ifc->transfercapability = PRI_TRANS_CAP_SPEECH;
data_ifc->controller = controller;
data_ifc->doEC = 1;
data_ifc->doEC_global = 1;
data_ifc->ecOption = EC_OPTION_DISABLE_NEVER;
data_ifc->ecTail = EC_DEFAULT_TAIL;
data_ifc->isdnmode = CAPI_ISDNMODE_MSN;
data_ifc->ecSelector = FACILITYSELECTOR_ECHO_CANCEL;
data_ifc->capability = capi_capability;
data_ifc->rxgain = 1.0;
data_ifc->txgain = 1.0;
capi_gains(&data_ifc->g, 1.0, 1.0);
if (data_plci_ifc == 0) {
if (!(capi_create_reader_writer_pipe(data_ifc))) {
free(data_ifc);
return NULL;
}
} else {
data_ifc->readerfd = -1;
data_ifc->writerfd = -1;
}
data_ifc->bproto = CC_BPROTO_TRANSPARENT;
data_ifc->doB3 = CAPI_B3_DONT;
data_ifc->smoother = ast_smoother_new(CAPI_MAX_B3_BLOCK_SIZE);
data_ifc->isdnstate |= CAPI_ISDN_STATE_PBX;
cc_mutex_lock(&nullif_lock);
data_ifc->next = nulliflist; /* prepend */
nulliflist = data_ifc;
controller_nullplcis[data_ifc->controller - 1]++;
cc_mutex_unlock(&nullif_lock);
/* connect to driver */
data_ifc->outgoing = 1;
data_ifc->state = CAPI_STATE_CONNECTPENDING;
data_ifc->MessageNumber = get_capi_MessageNumber();
cc_mutex_lock(&data_ifc->lock);
capi_sendf(data_ifc,
1,
CAPI_MANUFACTURER_REQ,
controller,
data_ifc->MessageNumber,
"dw(wbb(www()()()()))",
_DI_MANU_ID,
_DI_ASSIGN_PLCI,
(data_plci_ifc == 0) ? 4 : 5, /* data */
(data_plci_ifc == 0) ? 0 : (unsigned char)(data_plci_ifc->PLCI >> 8), /* bchannel */
1, /* connect */
1, 1, 0);
cc_mutex_unlock(&data_ifc->lock);
if (data_plci_ifc != 0) {
if (data_ifc->PLCI == 0) {
cc_log(LOG_WARNING, "%s: failed to create\n", data_ifc->vname);
capi_remove_nullif(data_ifc);
data_ifc = 0;
} else {
cc_mutex_lock(&data_plci_ifc->lock);
data_plci_ifc->line_plci = data_ifc;
capi_sendf(data_plci_ifc, 1, CAPI_FACILITY_REQ, data_plci_ifc->PLCI, get_capi_MessageNumber(),
"w(w(d()))",
FACILITYSELECTOR_LINE_INTERCONNECT,
0x0001, /* CONNECT */
0x00000000 /* mask */
);
cc_mutex_unlock(&data_plci_ifc->lock);
data_ifc->data_plci = data_plci_ifc;
data_ifc->writerfd = data_plci_ifc->writerfd;
data_plci_ifc->writerfd = -1;
}
}
if (data_ifc != 0) {
cc_verbose(3, 1, VERBOSE_PREFIX_4 "%s: created %s-resource-interface on controller %d.\n",
data_ifc->vname, (data_plci_ifc == 0) ? "data" : "line", data_ifc->controller);
}
return data_ifc;
}
/* /*
* get a new capi message number atomically * get a new capi message number atomically
*/ */
@ -232,7 +379,7 @@ _cword get_capi_MessageNumber(void)
cc_mutex_unlock(&messagenumber_lock); cc_mutex_unlock(&messagenumber_lock);
return(mn); return mn;
} }
/* /*
@ -242,7 +389,7 @@ struct capi_pvt *capi_find_interface_by_plci(unsigned int plci)
{ {
struct capi_pvt *i; struct capi_pvt *i;
if (plci == 0) if (unlikely(plci == 0))
return NULL; return NULL;
for (i = capi_iflist; i; i = i->next) { for (i = capi_iflist; i; i = i->next) {
@ -315,9 +462,12 @@ MESSAGE_EXCHANGE_ERROR capi_wait_conf(struct capi_pvt *i, unsigned short wCmd)
/* /*
* log an error in sending capi message * log an error in sending capi message
*/ */
static void log_capi_error_message(MESSAGE_EXCHANGE_ERROR err, _cmsg *CMSG) static void log_capi_error_message(MESSAGE_EXCHANGE_ERROR err, unsigned char* msg)
{ {
if (err) { if (err) {
_cmsg _CMSG, *CMSG = &_CMSG;
capi_message2cmsg(CMSG, msg);
cc_log(LOG_ERROR, "CAPI error sending %s (NCCI=%#x) (error=%#x %s)\n", cc_log(LOG_ERROR, "CAPI error sending %s (NCCI=%#x) (error=%#x %s)\n",
capi_cmsg2str(CMSG), (unsigned int)HEADER_CID(CMSG), capi_cmsg2str(CMSG), (unsigned int)HEADER_CID(CMSG),
err, capi_info_string((unsigned int)err)); err, capi_info_string((unsigned int)err));
@ -353,8 +503,10 @@ static MESSAGE_EXCHANGE_ERROR _capi_put_msg(unsigned char *msg)
return -1; return -1;
} }
capi_message2cmsg(&CMSG, msg); if (cc_verbose_check(4, 1) != 0) {
log_capi_message(&CMSG); capi_message2cmsg(&CMSG, msg);
log_capi_message(&CMSG);
}
error = capi20_put_message(capi_ApplID, msg); error = capi20_put_message(capi_ApplID, msg);
@ -363,7 +515,7 @@ static MESSAGE_EXCHANGE_ERROR _capi_put_msg(unsigned char *msg)
return -1; return -1;
} }
log_capi_error_message(error, &CMSG); log_capi_error_message(error, msg);
return error; return error;
} }
@ -425,8 +577,6 @@ MESSAGE_EXCHANGE_ERROR capi_sendf(
capi_prestruct_t *s; capi_prestruct_t *s;
unsigned char msg[2048]; unsigned char msg[2048];
memset(msg, 0, sizeof(msg));
write_capi_word(&msg[2], capi_ApplID); write_capi_word(&msg[2], capi_ApplID);
msg[4] = (unsigned char)((command >> 8) & 0xff); msg[4] = (unsigned char)((command >> 8) & 0xff);
msg[5] = (unsigned char)(command & 0xff); msg[5] = (unsigned char)(command & 0xff);
@ -438,9 +588,9 @@ MESSAGE_EXCHANGE_ERROR capi_sendf(
va_start(ap, format); va_start(ap, format);
for (i = 0; format[i]; i++) { for (i = 0; format[i]; i++) {
if (((p - (&msg[0])) + 12) >= sizeof(msg)) { if (unlikely(((p - (&msg[0])) + 12) >= sizeof(msg))) {
cc_log(LOG_ERROR, "capi_sendf: message too big (%d)\n", cc_log(LOG_ERROR, "capi_sendf: message too big (%d)\n",
(p - (&msg[0]))); (int)(p - (&msg[0])));
return 0x1004; return 0x1004;
} }
switch(format[i]) { switch(format[i]) {
@ -532,7 +682,7 @@ MESSAGE_EXCHANGE_ERROR capi_sendf(
ret = capi_wait_conf(capii, (command & 0xff00) | CAPI_CONF); ret = capi_wait_conf(capii, (command & 0xff00) | CAPI_CONF);
} }
return (ret); return ret;
} }
/* /*
@ -891,7 +1041,6 @@ void show_capi_info(struct capi_pvt *i, _cword info)
cc_verbose(3, 0, VERBOSE_PREFIX_4 "%s: CAPI INFO 0x%04x: %s\n", cc_verbose(3, 0, VERBOSE_PREFIX_4 "%s: CAPI INFO 0x%04x: %s\n",
name, info, p); name, info, p);
return;
} }
/* /*
@ -931,6 +1080,50 @@ unsigned capi_ListenOnController(unsigned int CIPmask, unsigned controller)
return error; return error;
} }
/*
* Activate access to vendor specific extensions
*/
unsigned capi_ManufacturerAllowOnController(unsigned controller)
{
MESSAGE_EXCHANGE_ERROR error;
int waitcount = 50;
unsigned char manbuf[CAPI_MANUFACTURER_LEN];
_cmsg CMSG;
if (capi20_get_manufacturer(controller, manbuf) == NULL) {
error = CapiRegOSResourceErr;
goto done;
}
if ((strstr((char *)manbuf, "Eicon") == 0) &&
(strstr((char *)manbuf, "Dialogic") == 0)) {
error = 0x100F;
goto done;
}
error = capi_sendf (NULL, 0, CAPI_MANUFACTURER_REQ, controller, get_capi_MessageNumber(),
"dw(d)", _DI_MANU_ID, _DI_OPTIONS_REQUEST, 0x00000020L);
if (error)
goto done;
while (waitcount) {
error = capidev_check_wait_get_cmsg(&CMSG);
if (IS_MANUFACTURER_CONF(&CMSG) && (CMSG.ManuID == _DI_MANU_ID) &&
((CMSG.Class & 0xffff) == _DI_OPTIONS_REQUEST)) {
error = (MESSAGE_EXCHANGE_ERROR)(CMSG.Class >> 16);
break;
}
usleep(30000);
waitcount--;
}
if (!waitcount)
error = 0x100F;
done:
return error;
}
/* /*
* convert a number * convert a number
*/ */
@ -1122,7 +1315,7 @@ struct ast_frame *capi_read_pipeframe(struct capi_pvt *i)
if ((f->frametype == AST_FRAME_VOICE) && (f->datalen > 0)) { if ((f->frametype == AST_FRAME_VOICE) && (f->datalen > 0)) {
if (f->datalen > sizeof(i->frame_data)) { if (f->datalen > sizeof(i->frame_data)) {
cc_log(LOG_ERROR, "f.datalen(%d) greater than space of frame_data(%d)\n", cc_log(LOG_ERROR, "f.datalen(%d) greater than space of frame_data(%d)\n",
f->datalen, sizeof(i->frame_data)); f->datalen, (int)sizeof(i->frame_data));
f->datalen = sizeof(i->frame_data); f->datalen = sizeof(i->frame_data);
} }
readsize = read(i->readerfd, i->frame_data + AST_FRIENDLY_OFFSET, f->datalen); readsize = read(i->readerfd, i->frame_data + AST_FRIENDLY_OFFSET, f->datalen);
@ -1146,13 +1339,24 @@ int capi_write_frame(struct capi_pvt *i, struct ast_frame *f)
int txavg=0; int txavg=0;
int ret = 0; int ret = 0;
if (!i) { if (unlikely(!i)) {
cc_log(LOG_ERROR, "channel has no interface\n"); cc_log(LOG_ERROR, "channel has no interface\n");
return -1; return -1;
} }
{
struct capi_pvt *ii = i;
cc_mutex_lock(&ii->lock);
if (i->line_plci != 0)
i = i->line_plci;
cc_mutex_unlock(&ii->lock);
}
if ((!(i->isdnstate & CAPI_ISDN_STATE_B3_UP)) || (!i->NCCI) || if (unlikely((!(i->isdnstate & CAPI_ISDN_STATE_B3_UP)) || (!i->NCCI) ||
((i->isdnstate & (CAPI_ISDN_STATE_B3_CHANGE | CAPI_ISDN_STATE_LI)))) { ((i->isdnstate & (CAPI_ISDN_STATE_B3_CHANGE | CAPI_ISDN_STATE_LI))))) {
return 0; return 0;
} }
@ -1160,41 +1364,62 @@ int capi_write_frame(struct capi_pvt *i, struct ast_frame *f)
return 0; return 0;
} }
if (f->frametype == AST_FRAME_NULL) { if (unlikely(f->frametype == AST_FRAME_NULL)) {
return 0; return 0;
} }
if (f->frametype == AST_FRAME_DTMF) { if (unlikely(f->frametype == AST_FRAME_DTMF)) {
cc_log(LOG_ERROR, "dtmf frame should be written\n"); cc_log(LOG_ERROR, "dtmf frame should be written\n");
return 0; return 0;
} }
if (f->frametype != AST_FRAME_VOICE) { if (unlikely(f->frametype != AST_FRAME_VOICE)) {
cc_log(LOG_ERROR,"not a voice frame\n"); cc_log(LOG_ERROR,"not a voice frame\n");
return 0; return 0;
} }
if (i->FaxState & CAPI_FAX_STATE_ACTIVE) { if (unlikely(i->FaxState & CAPI_FAX_STATE_ACTIVE)) {
cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s: write on fax activity?\n", cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s: write on fax activity?\n",
i->vname); i->vname);
return 0; return 0;
} }
if ((!f->FRAME_DATA_PTR) || (!f->datalen)) { if (unlikely((!f->FRAME_DATA_PTR) || (!f->datalen))) {
cc_log(LOG_DEBUG, "No data for FRAME_VOICE %s\n", i->vname); cc_log(LOG_DEBUG, "No data for FRAME_VOICE %s\n", i->vname);
return 0; return 0;
} }
if (i->isdnstate & CAPI_ISDN_STATE_RTP) { if (i->isdnstate & CAPI_ISDN_STATE_RTP) {
if ((!(f->subclass & i->codec)) && if (unlikely((!(f->subclass & i->codec)) &&
(f->subclass != capi_capability)) { (f->subclass != capi_capability))) {
cc_log(LOG_ERROR, "don't know how to write subclass %s(%d)\n", cc_log(LOG_ERROR, "don't know how to write subclass %s(%d)\n",
ast_getformatname(f->subclass), f->subclass); ast_getformatname(f->subclass), f->subclass);
return 0; return 0;
} }
return capi_write_rtp(i, f); return capi_write_rtp(i, f);
} }
if (i->B3count >= CAPI_MAX_B3_BLOCKS) { if (unlikely(i->B3count >= CAPI_MAX_B3_BLOCKS)) {
cc_verbose(3, 1, VERBOSE_PREFIX_4 "%s: B3count is full, dropping packet.\n", cc_verbose(3, 1, VERBOSE_PREFIX_4 "%s: B3count is full, dropping packet.\n",
i->vname); i->vname);
return 0; return 0;
} }
if (i->bproto == CC_BPROTO_VOCODER) {
buf = &(i->send_buffer[(i->send_buffer_handle % CAPI_MAX_B3_BLOCKS) *
(CAPI_MAX_B3_BLOCK_SIZE + AST_FRIENDLY_OFFSET)]);
i->send_buffer_handle++;
memcpy (buf, f->FRAME_DATA_PTR, f->datalen);
error = capi_sendf(NULL, 0, CAPI_DATA_B3_REQ, i->NCCI, get_capi_MessageNumber(),
"dwww", buf, f->datalen, i->send_buffer_handle, 0);
if (likely(error == 0)) {
cc_mutex_lock(&i->lock);
i->B3count++;
i->B3q -= f->datalen;
if (i->B3q < 0)
i->B3q = 0;
cc_mutex_unlock(&i->lock);
}
return 0;
}
if ((!i->smoother) || (ast_smoother_feed(i->smoother, f) != 0)) { if ((!i->smoother) || (ast_smoother_feed(i->smoother, f) != 0)) {
cc_log(LOG_ERROR, "%s: failed to fill smoother\n", i->vname); cc_log(LOG_ERROR, "%s: failed to fill smoother\n", i->vname);
return 0; return 0;
@ -1242,7 +1467,7 @@ int capi_write_frame(struct capi_pvt *i, struct ast_frame *f)
i->vname, i->NCCI); i->vname, i->NCCI);
} }
if (!error) { if (likely(!error)) {
cc_mutex_lock(&i->lock); cc_mutex_lock(&i->lock);
i->B3count++; i->B3count++;
i->B3q -= fsmooth->datalen; i->B3q -= fsmooth->datalen;

View File

@ -18,7 +18,27 @@
extern int capidebug; extern int capidebug;
extern char *emptyid; extern char *emptyid;
extern void cc_verbose(int o_v, int c_d, char *text, ...); extern void cc_verbose_internal(char *text, ...);
static inline int cc_verbose_check(int o_v, int c_d)
{
if (unlikely(((o_v == 0) || (option_verbose > o_v)) &&
((!c_d) || ((c_d) && (capidebug))))) {
return 1;
}
return 0;
}
/*
* helper for <pbx>_verbose with different verbose settings
*/
#define cc_verbose(o_v,c_d,text, args...) do { \
if (cc_verbose_check(o_v, c_d) != 0) { \
cc_verbose_internal(text , ## args); \
} \
} while(0)
extern _cword get_capi_MessageNumber(void); extern _cword get_capi_MessageNumber(void);
extern struct capi_pvt *capi_find_interface_by_msgnum(unsigned short msgnum); extern struct capi_pvt *capi_find_interface_by_msgnum(unsigned short msgnum);
extern struct capi_pvt *capi_find_interface_by_plci(unsigned int plci); extern struct capi_pvt *capi_find_interface_by_plci(unsigned int plci);
@ -27,15 +47,18 @@ extern MESSAGE_EXCHANGE_ERROR capidev_check_wait_get_cmsg(_cmsg *CMSG);
extern char *capi_info_string(unsigned int info); extern char *capi_info_string(unsigned int info);
extern void show_capi_info(struct capi_pvt *i, _cword info); extern void show_capi_info(struct capi_pvt *i, _cword info);
extern unsigned capi_ListenOnController(unsigned int CIPmask, unsigned controller); extern unsigned capi_ListenOnController(unsigned int CIPmask, unsigned controller);
extern unsigned capi_ManufacturerAllowOnController(unsigned controller);
extern void capi_parse_dialstring(char *buffer, char **interface, char **dest, char **param, char **ocid); extern void capi_parse_dialstring(char *buffer, char **interface, char **dest, char **param, char **ocid);
extern char *capi_number_func(unsigned char *data, unsigned int strip, char *buf); extern char *capi_number_func(unsigned char *data, unsigned int strip, char *buf);
extern int cc_add_peer_link_id(struct ast_channel *c); extern int cc_add_peer_link_id(struct ast_channel *c);
extern struct ast_channel *cc_get_peer_link_id(const char *p); extern struct ast_channel *cc_get_peer_link_id(const char *p);
extern void capi_remove_nullif(struct capi_pvt *i); extern void capi_remove_nullif(struct capi_pvt *i);
extern struct capi_pvt *capi_mknullif(struct ast_channel *c, unsigned long long controllermask); extern struct capi_pvt *capi_mknullif(struct ast_channel *c, unsigned long long controllermask);
struct capi_pvt *capi_mkresourceif(struct ast_channel *c, unsigned long long controllermask, struct capi_pvt *data_plci_ifc);
extern int capi_create_reader_writer_pipe(struct capi_pvt *i); extern int capi_create_reader_writer_pipe(struct capi_pvt *i);
extern struct ast_frame *capi_read_pipeframe(struct capi_pvt *i); extern struct ast_frame *capi_read_pipeframe(struct capi_pvt *i);
extern int capi_write_frame(struct capi_pvt *i, struct ast_frame *f); extern int capi_write_frame(struct capi_pvt *i, struct ast_frame *f);
extern int capi_verify_resource_plci(const struct capi_pvt *i);
#define capi_number(data, strip) \ #define capi_number(data, strip) \
capi_number_func(data, strip, alloca(AST_MAX_EXTENSION)) capi_number_func(data, strip, alloca(AST_MAX_EXTENSION))
@ -52,6 +75,6 @@ typedef struct capi_prestruct_s {
*/ */
extern MESSAGE_EXCHANGE_ERROR capi_sendf( extern MESSAGE_EXCHANGE_ERROR capi_sendf(
struct capi_pvt *capii, int waitconf, struct capi_pvt *capii, int waitconf,
_cword command, _cdword Id, _cword Number, char * format, ...); _cword command, _cdword Id, _cword Number, char * format, ...);
#endif #endif

167
dlist.c Normal file
View File

@ -0,0 +1,167 @@
/*
*
Copyright (c) Dialogic, 2009.
*
This source file is supplied for the use with
Dialogic range of DIVA Server Adapters.
*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
*
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
*
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "dlist.h"
/*
** Initialize linked list
*/
void
diva_q_init (diva_entity_queue_t* q)
{
q->head = q->tail = 0;
}
/*
** Remove element from linked list
*/
void
diva_q_remove (diva_entity_queue_t* q, diva_entity_link_t* what)
{
if(!what->prev) {
if ((q->head = what->next)) {
q->head->prev = 0;
} else {
q->tail = 0;
}
} else if (!what->next) {
q->tail = what->prev;
q->tail->next = 0;
} else {
what->prev->next = what->next;
what->next->prev = what->prev;
}
what->prev = what->next = 0;
}
/*
** Add element to the tail of linked list
*/
void
diva_q_add_tail (diva_entity_queue_t* q, diva_entity_link_t* what)
{
what->next = 0;
if (!q->head) {
what->prev = 0;
q->head = q->tail = what;
} else {
what->prev = q->tail;
q->tail->next = what;
q->tail = what;
}
}
/*
** Add element to the linked list after a specified element
*/
void diva_q_insert_after (diva_entity_queue_t* q, diva_entity_link_t* prev, diva_entity_link_t* what)
{
diva_entity_link_t *next;
if((0 == prev) || (0 == (next=diva_q_get_next(prev)))){
diva_q_add_tail(q,what);
return;
}
what->prev = prev;
what->next = next;
prev->next = what;
next->prev = what;
}
/*
** Add element to the linked list before a specified element
*/
void diva_q_insert_before (diva_entity_queue_t* q, diva_entity_link_t* next, diva_entity_link_t* what)
{
diva_entity_link_t *prev;
if(!next){
diva_q_add_tail(q,what);
return;
}
if(0 == (prev=diva_q_get_prev(next))){/*next seems to be the head*/
q->head=what;
what->prev=0;
what->next=next;
next->prev=what;
}else{ /*not the head*/
what->prev = prev;
what->next = next;
prev->next = what;
next->prev = what;
}
}
diva_entity_link_t* diva_q_find (const diva_entity_queue_t* q, const void* what,
diva_q_cmp_fn_t cmp_fn)
{
diva_entity_link_t* diva_q_current = q->head;
while (diva_q_current) {
if (!(*cmp_fn)(what, diva_q_current)) {
break;
}
diva_q_current = diva_q_current->next;
}
return (diva_q_current);
}
diva_entity_link_t*
diva_q_get_head (const diva_entity_queue_t* q)
{
return (q->head);
}
diva_entity_link_t*
diva_q_get_tail (const diva_entity_queue_t* q)
{
return (q->tail);
}
diva_entity_link_t*
diva_q_get_next (const diva_entity_link_t* what)
{
return ((what) ? what->next : 0);
}
diva_entity_link_t*
diva_q_get_prev (const diva_entity_link_t* what)
{
return ((what) ? what->prev : 0);
}
int
diva_q_get_nr_of_entries (const diva_entity_queue_t* q)
{
int i = 0;
const diva_entity_link_t* diva_q_current = q->head;
while (diva_q_current) {
i++;
diva_q_current = diva_q_current->next;
}
return (i);
}

54
dlist.h Normal file
View File

@ -0,0 +1,54 @@
/*
*
Copyright (c) Dialogic, 2009.
*
This source file is supplied for the use with
Dialogic range of DIVA Server Adapters.
*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
*
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY OF ANY KIND WHATSOEVER INCLUDING ANY
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
*
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef __DIVA_LINK_H__
#define __DIVA_LINK_H__
struct _diva_entity_link;
typedef struct _diva_entity_link {
struct _diva_entity_link* prev;
struct _diva_entity_link* next;
} diva_entity_link_t;
typedef struct _diva_entity_queue {
diva_entity_link_t* head;
diva_entity_link_t* tail;
} diva_entity_queue_t;
typedef int (*diva_q_cmp_fn_t)(const void* what,
const diva_entity_link_t*);
void diva_q_remove (diva_entity_queue_t* q, diva_entity_link_t* what);
void diva_q_add_tail (diva_entity_queue_t* q, diva_entity_link_t* what);
void diva_q_insert_after (diva_entity_queue_t* q, diva_entity_link_t* prev, diva_entity_link_t* what);
void diva_q_insert_before (diva_entity_queue_t* q, diva_entity_link_t* next, diva_entity_link_t* what);
diva_entity_link_t* diva_q_find (const diva_entity_queue_t* q,
const void* what, diva_q_cmp_fn_t cmp_fn);
diva_entity_link_t* diva_q_get_head (const diva_entity_queue_t* q);
diva_entity_link_t* diva_q_get_tail (const diva_entity_queue_t* q);
diva_entity_link_t* diva_q_get_next (const diva_entity_link_t* what);
diva_entity_link_t* diva_q_get_prev (const diva_entity_link_t* what);
int diva_q_get_nr_of_entries (const diva_entity_queue_t* q);
void diva_q_init (diva_entity_queue_t* q);
#endif

View File

@ -25,6 +25,8 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netdb.h> #include <netdb.h>
#include "capi20_platform.h"
#include "capi20.h" #include "capi20.h"
@ -311,9 +313,17 @@ static void write_capi_trace(int send, unsigned char *buf, int length, int datam
} }
} }
unsigned capi20_isinstalled (void) static inline unsigned capi20_isinstalled_internal(void)
{ {
if (capi_fd >= 0) if (likely(capi_fd >= 0))
return CapiNoError;
return (capi20_isinstalled());
}
unsigned capi20_isinstalled(void)
{
if (likely(capi_fd >= 0))
return CapiNoError; return CapiNoError;
/*----- open managment link -----*/ /*----- open managment link -----*/
@ -696,10 +706,10 @@ capi20_put_message (unsigned ApplID, unsigned char *Msg)
int fd; int fd;
int datareq = 0; int datareq = 0;
if (capi20_isinstalled() != CapiNoError) if (capi20_isinstalled_internal() != CapiNoError)
return CapiRegNotInstalled; return CapiRegNotInstalled;
if (!validapplid(ApplID)) if (unlikely(!validapplid(ApplID)))
return CapiIllAppNr; return CapiIllAppNr;
fd = applid2fd(ApplID); fd = applid2fd(ApplID);
@ -797,10 +807,10 @@ capi20_get_message (unsigned ApplID, unsigned char **Buf)
size_t bufsiz; size_t bufsiz;
int rc, fd; int rc, fd;
if (capi20_isinstalled() != CapiNoError) if (capi20_isinstalled_internal() != CapiNoError)
return CapiRegNotInstalled; return CapiRegNotInstalled;
if (!validapplid(ApplID)) if (unlikely(!validapplid(ApplID)))
return CapiIllAppNr; return CapiIllAppNr;
fd = applid2fd(ApplID); fd = applid2fd(ApplID);
@ -823,7 +833,7 @@ capi20_get_message (unsigned ApplID, unsigned char **Buf)
CAPIMSG_U32(rcvbuf, 8)); CAPIMSG_U32(rcvbuf, 8));
capimsg_setu16(rcvbuf, 18, offset); /* patch datahandle */ capimsg_setu16(rcvbuf, 18, offset); /* patch datahandle */
if (sizeof(void *) == 4) { if (sizeof(void *) == 4) {
u_int32_t data = (u_int32_t)rcvbuf + CAPIMSG_LEN(rcvbuf); u_int32_t data = (u_int32_t)(unsigned long)rcvbuf + CAPIMSG_LEN(rcvbuf);
rcvbuf[12] = data & 0xff; rcvbuf[12] = data & 0xff;
rcvbuf[13] = (data >> 8) & 0xff; rcvbuf[13] = (data >> 8) & 0xff;
rcvbuf[14] = (data >> 16) & 0xff; rcvbuf[14] = (data >> 16) & 0xff;
@ -1009,10 +1019,10 @@ capi20_waitformessage(unsigned ApplID, struct timeval *TimeOut)
FD_ZERO(&rfds); FD_ZERO(&rfds);
if (capi20_isinstalled() != CapiNoError) if (capi20_isinstalled_internal() != CapiNoError)
return CapiRegNotInstalled; return CapiRegNotInstalled;
if (!validapplid(ApplID)) if (unlikely(!validapplid(ApplID)))
return CapiIllAppNr; return CapiIllAppNr;
fd = applid2fd(ApplID); fd = applid2fd(ApplID);

View File

@ -0,0 +1,24 @@
#ifndef __CAPI20_PLATFORM_H__
#define __CAPI20_PLATFORM_H__
#if __GNUC__ >= 3 /* { */
#ifndef likely
#define likely(x) __builtin_expect(!!(x), 1)
#endif
#ifndef unlikely
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif
#else /* } { */
#ifndef likely
#define likely(__x__) (!!(__x__))
#endif
#ifndef unlikely
#define unlikely(__x__) (!!(__x__))
#endif
#endif /* } */
#endif

View File

@ -343,7 +343,11 @@ static unsigned char *cpars[] = {
/*15*/ 0, /*15*/ 0,
/*16 DISCONNECT_CONF*/ (unsigned char*)"\x03\x24\x01", /*16 DISCONNECT_CONF*/ (unsigned char*)"\x03\x24\x01",
/*17 LISTEN_CONF*/ (unsigned char*)"\x03\x24\x01", /*17 LISTEN_CONF*/ (unsigned char*)"\x03\x24\x01",
#if 0
/*18 MANUFACTURER_REQ*/ (unsigned char*)"\x03\x2b\x15\x22\x2a\x01", /*18 MANUFACTURER_REQ*/ (unsigned char*)"\x03\x2b\x15\x22\x2a\x01",
#else /** \todo Need to treate manufacturer specific as plain data */
/*18 MANUFACTURER_REQ dw(...) */ (unsigned char*)"\x03\x2b\x24\x2a\x01",
#endif
/*19*/ 0, /*19*/ 0,
/*1a INFO_CONF*/ (unsigned char*)"\x03\x24\x01", /*1a INFO_CONF*/ (unsigned char*)"\x03\x24\x01",
/*1b FACILITY_CONF*/ (unsigned char*)"\x03\x24\x20\x1b\x01", /*1b FACILITY_CONF*/ (unsigned char*)"\x03\x24\x20\x1b\x01",
@ -365,7 +369,11 @@ static unsigned char *cpars[] = {
/*27 CONNECT_ACTIVE_IND*/ (unsigned char*)"\x03\x16\x17\x29\x01", /*27 CONNECT_ACTIVE_IND*/ (unsigned char*)"\x03\x16\x17\x29\x01",
/*28 DISCONNECT_IND*/ (unsigned char*)"\x03\x2d\x01", /*28 DISCONNECT_IND*/ (unsigned char*)"\x03\x2d\x01",
/*29*/ 0, /*29*/ 0,
#if 0
/*2a MANUFACTURER_CONF*/ (unsigned char*)"\x03\x2b\x15\x22\x2a\x01", /*2a MANUFACTURER_CONF*/ (unsigned char*)"\x03\x2b\x15\x22\x2a\x01",
#else /** \todo Need to treate manufacturer specific as plain data */
/*2a MANUFACTURER_CONF*/ (unsigned char*)"\x03\x2b\x15\x01",
#endif
/*2b*/ 0, /*2b*/ 0,
/*2c INFO_IND*/ (unsigned char*)"\x03\x27\x25\x01", /*2c INFO_IND*/ (unsigned char*)"\x03\x27\x25\x01",
/*2d FACILITY_IND*/ (unsigned char*)"\x03\x20\x1d\x01", /*2d FACILITY_IND*/ (unsigned char*)"\x03\x20\x1d\x01",
@ -422,10 +430,18 @@ static unsigned char *cpars[] = {
#else #else
#ifdef __bfin__ /* Blackfin */
#define wordTLcpy(x,y) memcpy(x,y,2);
#else
#define wordTLcpy(x,y) *(_cword *)(x)=*(_cword *)(y); #define wordTLcpy(x,y) *(_cword *)(x)=*(_cword *)(y);
#endif
#define dwordTLcpy(x,y) memcpy(x,y,4); #define dwordTLcpy(x,y) memcpy(x,y,4);
#ifdef __bfin__ /* Blackfin */
#define wordTRcpy(x,y) memcpy(y,x,2);
#else
#define wordTRcpy(x,y) *(_cword *)(y)=*(_cword *)(x); #define wordTRcpy(x,y) *(_cword *)(y)=*(_cword *)(x);
#endif
#define dwordTRcpy(x,y) memcpy(y,x,4); #define dwordTRcpy(x,y) memcpy(y,x,4);
#define qwordTLcpy(x,y) memcpy(x,y,8); #define qwordTLcpy(x,y) memcpy(x,y,8);
@ -548,7 +564,7 @@ unsigned capi_cmsg2message(_cmsg * cmsg, _cbyte * msg)
if ( cmsg->Command == CAPI_DATA_B3 if ( cmsg->Command == CAPI_DATA_B3
&& (cmsg->Subcommand == CAPI_REQ || cmsg->Subcommand == CAPI_IND)) { && (cmsg->Subcommand == CAPI_REQ || cmsg->Subcommand == CAPI_IND)) {
if (sizeof(void *) == 4) { if (sizeof(void *) == 4) {
cmsg->Data32 = (_cdword) cmsg->Data; cmsg->Data32 = (_cdword)(unsigned long)cmsg->Data;
cmsg->Data64 = 0; cmsg->Data64 = 0;
} else { } else {
cmsg->Data32 = 0; cmsg->Data32 = 0;
@ -570,7 +586,9 @@ unsigned capi_cmsg2message(_cmsg * cmsg, _cbyte * msg)
/*-------------------------------------------------------*/ /*-------------------------------------------------------*/
static void message_2_pars(_cmsg * cmsg) static void message_2_pars(_cmsg * cmsg)
{ {
for (; TYP != _CEND; cmsg->p++) { _cword message_length = CAPIMSG_LEN(cmsg->m);
for (; TYP != _CEND && cmsg->l < message_length; cmsg->p++) {
switch (TYP) { switch (TYP) {
case _CBYTE: case _CBYTE:
@ -621,11 +639,15 @@ static void message_2_pars(_cmsg * cmsg)
/*-------------------------------------------------------*/ /*-------------------------------------------------------*/
unsigned capi_message2cmsg(_cmsg * cmsg, _cbyte * msg) unsigned capi_message2cmsg(_cmsg * cmsg, _cbyte * msg)
{ {
memset(cmsg, 0, sizeof(_cmsg)); _cbyte Command;
byteTRcpy(msg + 4, &Command);
if (Command != CAPI_DATA_B3)
memset(cmsg, 0, sizeof(_cmsg));
cmsg->m = msg; cmsg->m = msg;
cmsg->l = 8; cmsg->l = 8;
cmsg->p = 0; cmsg->p = 0;
byteTRcpy(cmsg->m + 4, &cmsg->Command); cmsg->Command = Command;
byteTRcpy(cmsg->m + 5, &cmsg->Subcommand); byteTRcpy(cmsg->m + 5, &cmsg->Subcommand);
cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)]; cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)];
@ -634,7 +656,7 @@ unsigned capi_message2cmsg(_cmsg * cmsg, _cbyte * msg)
if ( cmsg->Command == CAPI_DATA_B3 if ( cmsg->Command == CAPI_DATA_B3
&& (cmsg->Subcommand == CAPI_REQ || cmsg->Subcommand == CAPI_IND)) { && (cmsg->Subcommand == CAPI_REQ || cmsg->Subcommand == CAPI_IND)) {
if (sizeof(void *) == 4) { if (sizeof(void *) == 4) {
cmsg->Data = (void *) cmsg->Data32; cmsg->Data = (void *)(unsigned long)cmsg->Data32;
} else { } else {
cmsg->Data = (void *)(unsigned long)cmsg->Data64; cmsg->Data = (void *)(unsigned long)cmsg->Data64;
} }