Merged current trunk to stable 1.0, prepare next release.

This commit is contained in:
MelwareDE 2007-09-27 08:09:26 +00:00
parent ac40e4c18d
commit 3e3f121c77
29 changed files with 7386 additions and 3856 deletions

13
CHANGES
View File

@ -1,6 +1,19 @@
CHANGES
=======
HEAD
------------------
- possibly fixed ECT channel hang
- added 'x' option to capicommand(ect) to have real 'explicit call transfer'
(needed by some ISDN lines)
- support CCBS (call completion on busy subscriber)
- added capicommand(chat) for CAPI based MeetMe/Conference using onboard DSPs.
- fixed ton-display in 'show capi channels' on outgoing line.
- fix for 64bit support
- Asterisk 1.4.4 adaptions
- send 'In-band info available' for progress in NT-mode
- detect KEYPAD digits in NT-mode and send call to 'K...' extension
chan_capi-1.0.1
------------------

View File

@ -1,8 +1,7 @@
#
# (CAPI*)
#
# An implementation of Common ISDN API 2.0 for
# Asterisk/OpenPBX.org
# An implementation of Common ISDN API 2.0 for Asterisk
#
# Makefile, based on the Asterisk Makefile, Coypright (C) 1999, Mark Spencer
#
@ -23,8 +22,6 @@ OSNAME=${shell uname}
.EXPORT_ALL_VARIABLES:
.PHONY: openpbx
V=0
INSTALL_PREFIX=
@ -93,7 +90,9 @@ INSTALL=install
SHAREDOS=chan_capi.so
OBJECTS=chan_capi.o c20msg.o chan_capi_rtp.o chan_capi_qsig_core.o chan_capi_qsig_ecma.o chan_capi_qsig_asn197ade.o chan_capi_qsig_asn197no.o
OBJECTS=chan_capi.o chan_capi_utils.o chan_capi_rtp.o xlaw.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
CFLAGS+=-Wno-missing-prototypes -Wno-missing-declarations
@ -110,7 +109,6 @@ all: config.h $(SHAREDOS)
clean:
rm -f config.h
rm -f *.so *.o
rm -rf openpbx
config.h:
./create_config.sh "$(ASTERISK_HEADER_DIR)"
@ -141,23 +139,3 @@ install_config: capi.conf
samples: install_config
openpbx:
@rm -rf openpbx
@mkdir -p openpbx/channels
@mkdir -p openpbx/include/openpbx
@mkdir -p openpbx/doc
@mkdir -p openpbx/configs
@( \
./preparser -c openpbx.ctrl chan_capi.c openpbx/channels/chan_capi.c; \
./preparser -c openpbx.ctrl chan_capi_rtp.c openpbx/channels/chan_capi_rtp.c; \
./preparser -c openpbx.ctrl c20msg.c openpbx/channels/c20msg.c; \
./preparser -c openpbx.ctrl chan_capi.h openpbx/include/openpbx/chan_capi.h; \
./preparser -c openpbx.ctrl chan_capi_rtp.h openpbx/include/openpbx/chan_capi_rtp.h; \
./preparser -c openpbx.ctrl chan_capi20.h openpbx/include/openpbx/chan_capi20.h; \
./preparser -c openpbx.ctrl xlaw.h openpbx/include/openpbx/xlaw.h; \
./preparser -c openpbx.ctrl README openpbx/doc/README.chan_capi; \
./preparser -c openpbx.ctrl capi.conf openpbx/configs/capi.conf.sample; \
true; \
)

117
README
View File

@ -1,5 +1,4 @@
(CAPI*) chan_capi a Common ISDN API 2.0 implementation
for Asterisk/OpenPBX
(CAPI*) chan_capi a Common ISDN API 2.0 implementation for Asterisk
Copyright (C) 2005-2007 Cytronics & Melware
Armin Schindler <armin@melware.de>
@ -8,9 +7,6 @@ for Asterisk/OpenPBX
Copyright (C) 2002-2005 Junghanns.NET GmbH
Klaus-Peter Junghanns <kpj@junghanns.net>
Ported to OpenPBX.org 22nd October 2004,
Rob Thomas, <xrobau@gmail.com>
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!
@ -72,14 +68,10 @@ This chan_capi version includes:
- CLI command "capi show channels" shows details on channel status.
- Asterisk 1.4 jitterbuffer configuration.
- some QSIG extensions (see README.qsig)
- CCBS (call completion on busy subscriber)
- CAPI CHAT (CAPI MeetMe using onboard DSPs)
- KEYPAD digits detection
Permissions
===========
OpenPBX.org, by default, runs as the non-root user/group
openpbx/openpbx. You must make sure that the /dev/capi* device files
are readable by OpenPBX.org either by changing the ownership or the
permissions of the the device files or by running OpenPBX.org as root.
The Dial string
===============
@ -104,6 +96,10 @@ The Dial string
(Useful if additional digits shall be send afterwards or together
with 'b' to get dialtone and then send the number, e.g. if otherwise
no progress tones are available)
's' : activate 'stay-online': don't disconnect CAPI connection on Hangup.
This is needed to give additional commands like CCBS after Hangup.
To really hangup the CAPI connection, use either capicommand(hangup)
or wait for chan-capi/network timeout (about 20 seconds).
If the <interface-name> is used in dialstring, be sure the name (specified
in capi.conf) does not start with 'contr' or 'g'.
@ -114,6 +110,27 @@ The Dial string
capi.conf has been removed. The callerID is also taken from the calling channel.
CLI commands
============
capi info:
Show chan-capi version info.
Show status of available B-channels.
capi debug:
Enable CAPI message verbosity.
capi no debug:
Disable CAPI message verbosity.
capi show channels:
Display detailed information on CAPI B-channels.
(Description see below)
capi chatinfo:
Show status of CAPI CHAT.
CAPI command application
========================
chan_capi provides an additional Asterisk application
@ -201,6 +218,10 @@ ECT:
exten => s,1,capicommand(hold)
exten => s,2,Wait(1)
exten => s,3,Dial(CAPI/contr1/1234,60,M(capiect))
Note: Normaly a PBX needs 'implicit call transfer', which is done by default
with this command. But if the line needs real 'explicit call transfer', use
exten => s,1,capicommand(ect|x)
instead.
3PTY:
Initiate a Three-Party Conference (must have one call on hold and one active call!).
@ -213,6 +234,58 @@ ECT:
exten => s,1,capicommand(hold)
exten => s,2,Dial(CAPI/contr1/1234,,M(capi3pty))
Peer link creation:
Create 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.
Example:
exten => s,1,capicommand(peerlink)
Hangup in mode 'stay-online':
After hangup in 'stay-online' mode, the line isn't really disconnected
until timeout or command:
exten => s,1,capicommand(hangup)
This works after capicommand(peerlink) only.
Set local party to 'busy' or 'free':
Set the local phone to status to 'busy' or 'free' when
awaiting a callback for CCBS/CCNR. If the network wants to
call you back for CCBS/CCNR, chan-capi normaly doesn't know
about the status of the extension who started the callback.
By default chan-capi assumes 'free', but you can change that
with:
exten => s,1,capicommand(ccpartybusy|${CCLINKAGEID}|yes)
or
exten => s,1,capicommand(ccpartybusy|${CCLINKAGEID}|no)
Call completion on subscriber busy (CCBS):
To receive a callback when the dialed and busy party becomes free, aka
call completion on subscriber busy, you can do the following:
Example:
exten => s,1,capicommand(peerlink) ;to let chan-capi know who is the calling channel.
exten => s,2,Dial(CAPI/contr1/123456,60,g) ;'g' to go-on with the dialplan on busy.
exten => s,3,NoOp(${CCLINKAGEID}) ;if this variable now contains a number, CCBS is possible.
;here you can ask the caller if CCBS shall be activated...
exten => s,4,capicommand(ccbs|${CCLINKAGEID}|<context>|<exten>|<priority>)
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
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.
In this context/exten/priority you should just setup a callfile to initiate an outgoing
call from your extension to
exten => s,1,Dial(CAPI/ccbs/${CCLINKAGEID}/)
Deactivate CCBS:
To deactivate a previously activated CCBS, use following command:
Example:
exten => s,1,capicommand(ccbsstop|${CCLINKAGEID})
Chat (MeetMe/Conference):
If the CAPI card/driver supports it, the caller can be put into a chat-room:
(This uses the DSPs onboard a Dialogic DIVA Server Rev.2 card.)
exten => s,1,capicommand(chat|<roomname>|<options>|controller)
Example:
exten => s,1,capicommand(chat|salesmeeting||1,3-6)
Using CLIR
==========
@ -250,6 +323,7 @@ you:
For normal PBX usage you would use the "b" option, always early B3.
Overlap sending (a.k.a. real dialtone)
======================================
When you dial an empty number, and have early B3 enabled, with:
@ -259,6 +333,7 @@ local exchange.
At this point the channel is like a legacy phone, now you can send DTMF digits
to dial.
Example context for incoming calls on MSN 12345678:
===================================================
@ -325,6 +400,11 @@ FAXFORMAT : 0 = SFF.
FAXPAGES : Number of pages received.
FAXID : The ID of the remote fax maschine.
KEYPAD digits in NT-mode
========================
If the device connected to a NT-mode port sends KEYPAD digits
instead of normal digits, this call is then send to extension
'Kxxx'. Where 'xxx' stands for the KEYPAD digits sent.
CLI command "capi show channels"
================================
@ -347,6 +427,7 @@ Column description:
ton : type of number value
number : the caller-number and destination-number
Asterisk variables used/set by chan_capi
========================================
@ -373,7 +454,17 @@ CALLINGSUBADDRESS
CALLEDSUBADDRESS
If set on dial(), the called subaddress will be set to the content.
CCBSSTATUS
When using capicommand(ccbs|....), this variable is set to either "ERROR" or
"ACTIVATED".
CCLINKAGEID
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
of a design problem in Asterisk, chan-capi is not able to set channel variables
of the calling channel.
CONNECTEDNUMBER
Can be set before answering and if set, the content is used for
IE 'Connected Number' on answering.

View File

@ -1,5 +1,4 @@
(CAPI*) chan_capi a Common ISDN API 2.0 implementation
for Asterisk/OpenPBX
(CAPI*) chan_capi a Common ISDN API 2.0 implementation for Asterisk
QSIG Extension for chan_capi
@ -36,7 +35,9 @@ To use Q.SIG with asterisk, you'll need a card like Eicon DIVA Server
The QSIG support includes:
==========================
- 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
Data is stored in Asterisk variables:
QSIG_LI2_DIVREASON Reason of divertion: 0 - unknown, 1 - unconditional, 2 - user busy, 3 - user no reply
@ -48,25 +49,56 @@ The QSIG support includes:
QSIG_LI2_ODIVNAME original diverting name
at the moment only incoming handling is supported
- Possibility to inform QSIG switch about call from public network
If you set 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.
In dialplan use: Set(__QSIG_SETUP=X) command.
The leading "__" tells asterisk, to export this variable to the outgoing channel and
its subchannels
- Simple Call Transfer
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.
Unfortunately the call will be completely released by the switch, if the target is busy.
I have to read the ECMA-300 standard, if there's a chance, to refuse the transfer in such a case.
If you want need to know, if your target is busy, you can use the call transfer feature below.
- Call Transfer (outgoing)
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(getplci). This
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
be automatically transferred. The transfer occurs after the CONNECT. If you want an 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.
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.
- 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
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)
to see the name and number of his connected peer.
This should be configurable in the next release.
- decoding of incoming Call Transfer feature
Enables inbound Path Replacement. If received, an automatic Path Replacement with asterisk internal bridging will be fired.
- 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.
- 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
Future Targets:
===============
- check code for buffer overflows
- Call Transfer
- Path Replacement
- complete path replacement features
- Call Rerouting feature [ECMA-174]
- CCBS
- AOC
- sendtext implementation (e.g. display instructions on the connected set)
- ...
How to use:
@ -74,18 +106,6 @@ How to use:
simply enable Q.SIG with following line in your capi.conf interface:
================================================================================
********** deprecated **********************************************************
#### qsig=on
#### Take care that you enable this only for interfaces, where the other end
#### understands the Q.SIG protocoll. If not, then these switches may reject the
#### entire call, because of wrong facility contents.
#### Later this will change to qsig=off or qsig=1..x where we can support some
#### pbx manufacturer specific operations.
================================================================================
Here we go with new configuration
Set qsig to one of the following values, which corresponds to your configuration.
@ -98,6 +118,6 @@ Set qsig to one of the following values, which corresponds to your configuration
ToDo List:
==========
- Invoke Identifier handling - currently i use invoke id #1, will be corrected by capi (outgoing)
- Handle later facilities - i don't know what to do with most informations now, maybe they are useless in asterisk
- Outgoing calls support only qsig type 1 (Alcatel) - add support for others on outgoing
- Support for inbound rerouting
- Enhance ASN1-97 Addressing Data Elements support - will save much code
- Allow/Disallow Path Replacement within capi.conf - partially done

335
c20msg.c
View File

@ -1,335 +0,0 @@
#include <stdio.h>
/*
* decode capi 2.0 info word
*/
char *capi_info_string(unsigned int info)
{
switch (info) {
/* informative values (corresponding message was processed) */
case 0x0001:
return "NCPI not supported by current protocol, NCPI ignored";
case 0x0002:
return "Flags not supported by current protocol, flags ignored";
case 0x0003:
return "Alert already sent by another application";
/* error information concerning CAPI_REGISTER */
case 0x1001:
return "Too many applications";
case 0x1002:
return "Logical block size to small, must be at least 128 Bytes";
case 0x1003:
return "Buffer exceeds 64 kByte";
case 0x1004:
return "Message buffer size too small, must be at least 1024 Bytes";
case 0x1005:
return "Max. number of logical connections not supported";
case 0x1006:
return "Reserved";
case 0x1007:
return "The message could not be accepted because of an internal busy condition";
case 0x1008:
return "OS resource error (no memory ?)";
case 0x1009:
return "CAPI not installed";
case 0x100A:
return "Controller does not support external equipment";
case 0x100B:
return "Controller does only support external equipment";
/* error information concerning message exchange functions */
case 0x1101:
return "Illegal application number";
case 0x1102:
return "Illegal command or subcommand or message length less than 12 bytes";
case 0x1103:
return "The message could not be accepted because of a queue full condition !! The error code does not imply that CAPI cannot receive messages directed to another controller, PLCI or NCCI";
case 0x1104:
return "Queue is empty";
case 0x1105:
return "Queue overflow, a message was lost !! This indicates a configuration error. The only recovery from this error is to perform a CAPI_RELEASE";
case 0x1106:
return "Unknown notification parameter";
case 0x1107:
return "The Message could not be accepted because of an internal busy condition";
case 0x1108:
return "OS Resource error (no memory ?)";
case 0x1109:
return "CAPI not installed";
case 0x110A:
return "Controller does not support external equipment";
case 0x110B:
return "Controller does only support external equipment";
/* error information concerning resource / coding problems */
case 0x2001:
return "Message not supported in current state";
case 0x2002:
return "Illegal Controller / PLCI / NCCI";
case 0x2003:
return "Out of PLCIs";
case 0x2004:
return "Out of NCCIs";
case 0x2005:
return "Out of LISTEN requests";
case 0x2006:
return "Out of FAX resources (protocol T.30)";
case 0x2007:
return "Illegal message parameter coding";
/* error information concerning requested services */
case 0x3001:
return "B1 protocol not supported";
case 0x3002:
return "B2 protocol not supported";
case 0x3003:
return "B3 protocol not supported";
case 0x3004:
return "B1 protocol parameter not supported";
case 0x3005:
return "B2 protocol parameter not supported";
case 0x3006:
return "B3 protocol parameter not supported";
case 0x3007:
return "B protocol combination not supported";
case 0x3008:
return "NCPI not supported";
case 0x3009:
return "CIP Value unknown";
case 0x300A:
return "Flags not supported (reserved bits)";
case 0x300B:
return "Facility not supported";
case 0x300C:
return "Data length not supported by current protocol";
case 0x300D:
return "Reset procedure not supported by current protocol";
case 0x300E:
return "TEI assignment failed or supplementary service not supported";
case 0x3010:
return "Request not allowed in this state";
/* informations about the clearing of a physical connection */
case 0x3301:
return "Protocol error layer 1 (broken line or B-channel removed by signalling protocol)";
case 0x3302:
return "Protocol error layer 2";
case 0x3303:
return "Protocol error layer 3";
case 0x3304:
return "Another application got that call";
/* T.30 specific reasons */
case 0x3311:
return "Connecting not successful (remote station is no FAX G3 machine)";
case 0x3312:
return "Connecting not successful (training error)";
case 0x3313:
return "Disconnected before transfer (remote station does not support transfer mode, e.g. resolution)";
case 0x3314:
return "Disconnected during transfer (remote abort)";
case 0x3315:
return "Disconnected during transfer (remote procedure error, e.g. unsuccessful repetition of T.30 commands)";
case 0x3316:
return "Disconnected during transfer (local tx data underrun)";
case 0x3317:
return "Disconnected during transfer (local rx data overflow)";
case 0x3318:
return "Disconnected during transfer (local abort)";
case 0x3319:
return "Illegal parameter coding (e.g. SFF coding error)";
/* disconnect causes from the network according to ETS 300 102-1/Q.931 */
case 0x3481:
return "Unallocated (unassigned) number";
case 0x3482:
return "No route to specified transit network";
case 0x3483:
return "No route to destination";
case 0x3486:
return "Channel unacceptable";
case 0x3487:
return "Call awarded and being delivered in an established channel";
case 0x3490:
return "Normal call clearing";
case 0x3491:
return "User busy";
case 0x3492:
return "No user responding";
case 0x3493:
return "No answer from user (user alerted)";
case 0x3495:
return "Call rejected";
case 0x3496:
return "Number changed";
case 0x349A:
return "Non-selected user clearing";
case 0x349B:
return "Destination out of order";
case 0x349C:
return "Invalid number format";
case 0x349D:
return "Facility rejected";
case 0x349E:
return "Response to STATUS ENQUIRY";
case 0x349F:
return "Normal, unspecified";
case 0x34A2:
return "No circuit / channel available";
case 0x34A6:
return "Network out of order";
case 0x34A9:
return "Temporary failure";
case 0x34AA:
return "Switching equipment congestion";
case 0x34AB:
return "Access information discarded";
case 0x34AC:
return "Requested circuit / channel not available";
case 0x34AF:
return "Resources unavailable, unspecified";
case 0x34B1:
return "Quality of service unavailable";
case 0x34B2:
return "Requested facility not subscribed";
case 0x34B9:
return "Bearer capability not authorized";
case 0x34BA:
return "Bearer capability not presently available";
case 0x34BF:
return "Service or option not available, unspecified";
case 0x34C1:
return "Bearer capability not implemented";
case 0x34C2:
return "Channel type not implemented";
case 0x34C5:
return "Requested facility not implemented";
case 0x34C6:
return "Only restricted digital information bearer capability is available";
case 0x34CF:
return "Service or option not implemented, unspecified";
case 0x34D1:
return "Invalid call reference value";
case 0x34D2:
return "Identified channel does not exist";
case 0x34D3:
return "A suspended call exists, but this call identity does not";
case 0x34D4:
return "Call identity in use";
case 0x34D5:
return "No call suspended";
case 0x34D6:
return "Call having the requested call identity has been cleared";
case 0x34D8:
return "Incompatible destination";
case 0x34DB:
return "Invalid transit network selection";
case 0x34DF:
return "Invalid message, unspecified";
case 0x34E0:
return "Mandatory information element is missing";
case 0x34E1:
return "Message type non-existent or not implemented";
case 0x34E2:
return "Message not compatible with call state or message type non-existent or not implemented";
case 0x34E3:
return "Information element non-existent or not implemented";
case 0x34E4:
return "Invalid information element contents";
case 0x34E5:
return "Message not compatible with call state";
case 0x34E6:
return "Recovery on timer expiry";
case 0x34EF:
return "Protocol error, unspecified";
case 0x34FF:
return "Interworking, unspecified";
/* B3 protocol 7 (Modem) */
case 0x3500:
return "Normal end of connection";
case 0x3501:
return "Carrier lost";
case 0x3502:
return "Error on negotiation, i.e. no modem with error correction at other end";
case 0x3503:
return "No answer to protocol request";
case 0x3504:
return "Remote modem only works in synchronous mode";
case 0x3505:
return "Framing fails";
case 0x3506:
return "Protocol negotiation fails";
case 0x3507:
return "Other modem sends wrong protocol request";
case 0x3508:
return "Sync information (data or flags) missing";
case 0x3509:
return "Normal end of connection from the other modem";
case 0x350a:
return "No answer from other modem";
case 0x350b:
return "Protocol error";
case 0x350c:
return "Error on compression";
case 0x350d:
return "No connect (timeout or wrong modulation)";
case 0x350e:
return "No protocol fall-back allowed";
case 0x350f:
return "No modem or fax at requested number";
case 0x3510:
return "Handshake error";
/* error info concerning the requested supplementary service */
case 0x3600:
return "Supplementary service not subscribed";
case 0x3603:
return "Supplementary service not available";
case 0x3604:
return "Supplementary service not implemented";
case 0x3606:
return "Invalid served user number";
case 0x3607:
return "Invalid call state";
case 0x3608:
return "Basic service not provided";
case 0x3609:
return "Supplementary service not requested for an incoming call";
case 0x360a:
return "Supplementary service interaction not allowed";
case 0x360b:
return "Resource unavailable";
/* error info concerning the context of a supplementary service request */
case 0x3700:
return "Duplicate invocation";
case 0x3701:
return "Unrecognized operation";
case 0x3702:
return "Mistyped argument";
case 0x3703:
return "Resource limitation";
case 0x3704:
return "Initiator releasing";
case 0x3705:
return "Unrecognized linked-ID";
case 0x3706:
return "Linked response unexpected";
case 0x3707:
return "Unexpected child operation";
/* Line Interconnect */
case 0x3800:
return "PLCI has no B-channel";
case 0x3801:
return "Lines not compatible";
case 0x3802:
return "PLCI(s) is (are) not in any or not in the same interconnection";
default:
return NULL;
}
}

View File

@ -67,5 +67,5 @@ devices=2 ;number of concurrent calls (b-channels) on this controller
;jb..... ;with Asterisk 1.4 you can configure jitterbuffer,
;see Asterisk documentation for all jb* setting available.
;mohinterpret=default ;Asterisk 1.4: default music on hold class when placed on hold.
;qsig=on ;enable use of Q.SIG extensions.
;qsig=1 ;enable use of Q.SIG extensions. ECMA Variant
;qsig_prnum=1234 ;enable inbound bridging - this should be an QSIG-network-wide unique number

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,7 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for
* Asterisk / OpenPBX.org
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2005-2007 Cytronics & Melware
*
@ -16,6 +15,33 @@
* This program is free software and may be modified and
* distributed under the terms of the GNU Public License.
*/
#include "config.h"
#ifdef CC_AST_HAS_VERSION_1_4
#include <asterisk.h>
#endif
#include <asterisk/lock.h>
#include <asterisk/frame.h>
#include <asterisk/channel.h>
#include <asterisk/logger.h>
#include <asterisk/module.h>
#include <asterisk/pbx.h>
#include <asterisk/config.h>
#include <asterisk/options.h>
#include <asterisk/features.h>
#include <asterisk/utils.h>
#include <asterisk/cli.h>
#include <asterisk/rtp.h>
#include <asterisk/causes.h>
#include <asterisk/strings.h>
#include <asterisk/dsp.h>
#include <asterisk/devicestate.h>
#ifdef CC_AST_HAS_VERSION_1_4
#include "asterisk/abstract_jb.h"
#include "asterisk/musiconhold.h"
#endif
#ifndef _PBX_CAPI_H
#define _PBX_CAPI_H
@ -37,11 +63,6 @@
#define CAPI_MAX_FACILITYDATAARRAY_SIZE 300
#ifndef CONNECT_RESP_GLOBALCONFIGURATION
#define CC_HAVE_NO_GLOBALCONFIGURATION
#warning If you dont update your libcapi20, some fax features are not available
#endif
/* some helper functions */
static inline void write_capi_word(void *m, unsigned short val)
{
@ -83,11 +104,6 @@ static inline unsigned int read_capi_dword(void *m)
#define cc_pbx_verbose(x...) ast_verbose(x)
#define cc_copy_string(dst, src, size) ast_copy_string(dst, src, size)
#ifdef PBX_IS_OPBX
#define CC_CHANNEL_PVT(c) (c)->tech_pvt
#else /* PBX_IS_OPBX */
#ifndef AST_MUTEX_DEFINE_STATIC
#define AST_MUTEX_DEFINE_STATIC(mutex) \
static cc_mutex_t mutex = AST_MUTEX_INITIALIZER
@ -99,15 +115,12 @@ static inline unsigned int read_capi_dword(void *m)
#define CC_CHANNEL_PVT(c) (c)->tech_pvt
#define CC_BRIDGE_RETURN enum ast_bridge_result
#endif /* PBX_IS_OPBX */
/*
* prototypes
*/
extern unsigned capi_ApplID;
extern MESSAGE_EXCHANGE_ERROR _capi_put_cmsg(_cmsg *CMSG);
extern _cword get_capi_MessageNumber(void);
extern void cc_verbose(int o_v, int c_d, char *text, ...);
/* */
#define return_on_no_interface(x) \
if (!i) { \
cc_verbose(4, 1, "CAPI: %s no interface for PLCI=%#x\n", x, PLCI); \
return; \
}
/*
* B protocol settings
@ -157,6 +170,16 @@ typedef struct fax3proto3 B3_PROTO_FAXG3;
#define FACILITYSELECTOR_FAX_OVER_IP 0x00fd
#define FACILITYSELECTOR_VOICE_OVER_IP 0x00fe
#define EC_FUNCTION_ENABLE 1
#define EC_FUNCTION_DISABLE 2
#define EC_FUNCTION_FREEZE 3
#define EC_FUNCTION_RESUME 4
#define EC_FUNCTION_RESET 5
#define EC_OPTION_DISABLE_NEVER 0
#define EC_OPTION_DISABLE_G165 (1<<2)
#define EC_OPTION_DISABLE_G164_OR_G165 (1<<1 | 1<<2)
#define EC_DEFAULT_TAIL 0 /* maximum */
#define CC_HOLDTYPE_LOCAL 0
#define CC_HOLDTYPE_HOLD 1
#define CC_HOLDTYPE_NOTIFY 2
@ -218,19 +241,47 @@ struct cc_capi_gains {
#define CAPI_ISDN_STATE_EC 0x00002000
#define CAPI_ISDN_STATE_DTMF 0x00004000
#define CAPI_ISDN_STATE_B3_SELECT 0x00008000
#define CAPI_ISDN_STATE_3PTY 0x10000000
#define CAPI_ISDN_STATE_STAYONLINE 0x00010000
#define CAPI_ISDN_STATE_ISDNPROGRESS 0x00020000
#define CAPI_ISDN_STATE_3PTY 0x10000000
#define CAPI_ISDN_STATE_PBX_DONT 0x40000000
#define CAPI_ISDN_STATE_PBX 0x80000000
#define CAPI_CHANNELTYPE_B 0
#define CAPI_CHANNELTYPE_D 1
#define CAPI_CHANNELTYPE_NONE 2
#define CAPI_CHANNELTYPE_NULL 2
/* the lower word is reserved for capi commands */
#define CAPI_WAITEVENT_B3_UP 0x00010000
#define CAPI_WAITEVENT_B3_DOWN 0x00020000
#define CAPI_WAITEVENT_ANSWER_FINISH 0x00030000
/* Private qsig data for capi device */
struct cc_qsig_data {
int calltransfer_active;
int calltransfer;
int calltransfer_onring;
unsigned int callmark;
char *dnameid;
/* Path Replacement */
int pr_propose_sendback; /* send back an prior received PR PROPOSE on Connect */
int pr_propose_sentback; /* set to 1 after sending an PR PROPOSE */
int pr_propose_active;
int pr_propose_doinboundbridge; /* We have to to bridge a call back to asterisk */
char *pr_propose_cid; /* Call identity */
char *pr_propose_pn; /* Party Number */
char if_pr_propose_pn[AST_MAX_EXTENSION]; /* configured interface Party Number */
/* Partner Channel - needed for many features */
struct capi_pvt *partner_ch;
unsigned int partner_plci;
ast_cond_t event_trigger;
unsigned int waitevent;
};
/* ! Private data for a capi device */
struct capi_pvt {
cc_mutex_t lock;
@ -247,8 +298,12 @@ struct capi_pvt {
char vname[CAPI_MAX_STRING];
unsigned char tmpbuf[CAPI_MAX_STRING];
/*! Channel who used us, possibly NULL */
struct ast_channel *used;
/*! Channel we belong to, possibly NULL */
struct ast_channel *owner;
/*! Channel who called us, possibly NULL */
struct ast_channel *peer;
/* capi message number */
_cword MessageNumber;
@ -274,7 +329,7 @@ struct capi_pvt {
/* which b-protocol is active */
int bproto;
char context[AST_MAX_EXTENSION];
/*! Multiple Subscriber Number we listen to (, seperated list) */
char incomingmsn[CAPI_MAX_STRING];
@ -335,12 +390,16 @@ struct capi_pvt {
/* Common ISDN Profile (CIP) */
int cip;
unsigned short transfercapability;
/* if not null, receiving a fax */
FILE *fFax;
/* Fax status */
unsigned int FaxState;
/* handle for CCBS/CCNR callback */
unsigned int ccbsnrhandle;
/* not all codecs supply frames in nice 160 byte chunks */
struct ast_smoother *smoother;
@ -369,6 +428,10 @@ struct capi_pvt {
unsigned int reason;
unsigned int reasonb3;
/* deferred tasks */
time_t whentohangup;
time_t whentoqueuehangup;
/* RTP */
struct ast_rtp *rtp;
int capability;
@ -378,7 +441,8 @@ struct capi_pvt {
/* Q.SIG features */
int qsigfeat;
struct cc_qsig_data qsig_data;
/*! Next channel in list */
struct capi_pvt *next;
};
@ -397,6 +461,10 @@ struct cc_capi_profile {
unsigned int manufacturer[5];
} __attribute__((__packed__));
struct cc_capi_qsig_conf {
char if_pr_propose_pn[AST_MAX_EXTENSION];
};
struct cc_capi_conf {
char name[CAPI_MAX_STRING];
char language[MAX_LANGUAGE];
@ -421,6 +489,7 @@ struct cc_capi_conf {
int bridge;
int amaflags;
int qsigfeat;
struct cc_capi_qsig_conf qsigconf;
unsigned int faxsetting;
ast_group_t callgroup;
ast_group_t pickupgroup;
@ -512,4 +581,21 @@ struct cc_capi_controller {
#define PRI_TRANS_CAP_DIGITAL_W_TONES 0x11
#define PRI_TRANS_CAP_VIDEO 0x18
/*
* prototypes
*/
extern const struct ast_channel_tech capi_tech;
extern int capi_capability;
extern unsigned capi_ApplID;
extern struct capi_pvt *capi_iflist;
extern void cc_start_b3(struct capi_pvt *i);
extern unsigned char capi_tcap_is_digital(unsigned short tcap);
extern void capi_queue_cause_control(struct capi_pvt *i, int control);
extern void capidev_handle_connection_conf(struct capi_pvt **i, unsigned int PLCI,
unsigned short wInfo, unsigned short wMsgNum);
extern void capi_wait_for_answered(struct capi_pvt *i);
extern int capi_wait_for_b3_up(struct capi_pvt *i);
extern void capi_activehangup(struct capi_pvt *i, int state);
extern void capi_gains(struct cc_capi_gains *g, float rxgain, float txgain);
#endif

390
chan_capi_chat.c Normal file
View File

@ -0,0 +1,390 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2005-2007 Cytronics & Melware
*
* Armin Schindler <armin@melware.de>
*
* This program is free software and may be modified and
* distributed under the terms of the GNU Public License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/signal.h>
#include "chan_capi20.h"
#include "chan_capi.h"
#include "chan_capi_chat.h"
#include "chan_capi_utils.h"
struct capichat_s {
char name[16];
unsigned int number;
struct capi_pvt *i;
struct capichat_s *next;
};
static struct capichat_s *chat_list = NULL;
AST_MUTEX_DEFINE_STATIC(chat_lock);
/*
* update the capi mixer for the given char room
*/
static void update_capi_mixer(int remove, unsigned int roomnumber, struct capi_pvt *i)
{
struct capi_pvt *ii;
struct capichat_s *room;
unsigned char p_list[360];
_cdword dest;
_cdword datapath;
capi_prestruct_t p_struct;
unsigned int found = 0;
_cword j = 0;
if (i->PLCI == 0) {
cc_verbose(2, 0, VERBOSE_PREFIX_3 "capi mixer: %s: PLCI is unset, abort.\n",
i->vname);
return;
}
cc_mutex_lock(&chat_lock);
room = chat_list;
while (room) {
if ((room->number == roomnumber) &&
(room->i != i)) {
found++;
if (j + 9 > sizeof(p_list)) {
/* maybe we need to split capi messages here */
break;
}
ii = room->i;
p_list[j++] = 8;
p_list[j++] = (_cbyte)(ii->PLCI);
p_list[j++] = (_cbyte)(ii->PLCI >> 8);
p_list[j++] = (_cbyte)(ii->PLCI >> 16);
p_list[j++] = (_cbyte)(ii->PLCI >> 24);
dest = (remove) ? 0x00000000 : 0x00000003;
if (ii->channeltype == CAPI_CHANNELTYPE_NULL) {
dest |= 0x00000030;
}
p_list[j++] = (_cbyte)(dest);
p_list[j++] = (_cbyte)(dest >> 8);
p_list[j++] = (_cbyte)(dest >> 16);
p_list[j++] = (_cbyte)(dest >> 24);
cc_verbose(3, 1, VERBOSE_PREFIX_3 "capi mixer: listed %s PLCI=0x%04x LI=0x%x\n",
ii->vname, ii->PLCI, dest);
}
room = room->next;
}
cc_mutex_unlock(&chat_lock);
if (found) {
p_struct.wLen = j;
p_struct.info = p_list;
/* don't send DATA_B3 to me */
datapath = 0x00000000;
if (remove) {
/* now we need DATA_B3 again */
datapath = 0x0000000c;
if (found == 1) {
/* only one left, enable DATA_B3 too */
p_list[5] |= 0x0c;
}
}
if (i->channeltype == CAPI_CHANNELTYPE_NULL) {
if (!remove) {
datapath |= 0x00000030;
}
}
cc_verbose(3, 1, VERBOSE_PREFIX_3 "capi mixer: %s PLCI=0x%04x LI=0x%x\n",
i->vname, i->PLCI, datapath);
capi_sendf(NULL, 0, CAPI_FACILITY_REQ, i->PLCI, get_capi_MessageNumber(),
"w(w(dc))",
FACILITYSELECTOR_LINE_INTERCONNECT,
0x0001, /* CONNECT */
datapath,
&p_struct
);
}
}
/*
* delete a chat member
*/
static void del_chat_member(struct capichat_s *room)
{
struct capichat_s *tmproom;
struct capichat_s *tmproom2 = NULL;
unsigned int roomnumber = room->number;
struct capi_pvt *i = room->i;
cc_mutex_lock(&chat_lock);
tmproom = chat_list;
while (tmproom) {
if (tmproom == room) {
if (!tmproom2) {
chat_list = tmproom->next;
} else {
tmproom2->next = tmproom->next;
}
cc_verbose(3, 0, VERBOSE_PREFIX_3 "%s: removed chat member from room '%s' (%d)\n",
room->i->vname, room->name, room->number);
free(room);
}
tmproom2 = tmproom;
tmproom = tmproom->next;
}
cc_mutex_unlock(&chat_lock);
update_capi_mixer(1, roomnumber, i);
}
/*
* add a new chat member
*/
static struct capichat_s *add_chat_member(char *roomname, struct capi_pvt *i)
{
struct capichat_s *room = NULL;
struct capichat_s *tmproom;
unsigned int roomnumber = 1;
room = malloc(sizeof(struct capichat_s));
if (room == NULL) {
cc_log(LOG_ERROR, "Unable to allocate capi chat struct.\n");
return NULL;
}
memset(room, 0, sizeof(struct capichat_s));
strncpy(room->name, roomname, sizeof(room->name));
room->name[sizeof(room->name) - 1] = 0;
room->i = i;
cc_mutex_lock(&chat_lock);
tmproom = chat_list;
while (tmproom) {
if (!strcmp(tmproom->name, roomname)) {
roomnumber = tmproom->number;
break;
} else {
if (tmproom->number == roomnumber) {
roomnumber++;
}
}
tmproom = tmproom->next;
}
room->number = roomnumber;
room->next = chat_list;
chat_list = room;
cc_mutex_unlock(&chat_lock);
cc_verbose(3, 0, VERBOSE_PREFIX_3 "%s: added new chat member to room '%s' (%d)\n",
i->vname, roomname, roomnumber);
update_capi_mixer(0, roomnumber, i);
return room;
}
/*
* loop during chat
*/
static void chat_handle_events(struct ast_channel *c, struct capi_pvt *i)
{
struct ast_frame *f;
int ms;
int exception;
int ready_fd;
int waitfd;
int nfds = 0;
struct ast_channel *rchan;
struct ast_channel *chan = c;
waitfd = i->readerfd;
if (i->channeltype == CAPI_CHANNELTYPE_NULL) {
nfds = 1;
ast_indicate(chan, -1);
ast_set_read_format(chan, capi_capability);
ast_set_write_format(chan, capi_capability);
}
while (1) {
ready_fd = 0;
ms = 100;
errno = 0;
exception = 0;
rchan = ast_waitfor_nandfds(&chan, 1, &waitfd, nfds, &exception, &ready_fd, &ms);
if (rchan) {
f = ast_read(chan);
if (!f) {
cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: chat: no frame, hangup.\n",
i->vname);
break;
}
if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: chat: hangup frame.\n",
i->vname);
ast_frfree(f);
break;
} else if (f->frametype == AST_FRAME_VOICE) {
cc_verbose(5, 1, VERBOSE_PREFIX_3 "%s: chat: voice frame.\n",
i->vname);
if (i->channeltype == CAPI_CHANNELTYPE_NULL) {
capi_write_frame(i, f);
}
} else if (f->frametype == AST_FRAME_NULL) {
/* ignore NULL frame */
cc_verbose(5, 1, VERBOSE_PREFIX_3 "%s: chat: NULL frame, ignoring.\n",
i->vname);
} else {
cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: chat: unhandled frame %d/%d.\n",
i->vname, f->frametype, f->subclass);
}
ast_frfree(f);
} else if (ready_fd == i->readerfd) {
if (exception) {
cc_verbose(1, 0, VERBOSE_PREFIX_3 "%s: chat: exception on readerfd\n",
i->vname);
break;
}
f = capi_read_pipeframe(i);
if (f->frametype == AST_FRAME_VOICE) {
ast_write(chan, f);
}
/* ignore other nullplci frames */
} else {
if ((ready_fd < 0) && ms) {
if (errno == 0 || errno == EINTR)
continue;
cc_log(LOG_WARNING, "%s: Wait failed (%s).\n",
chan->name, strerror(errno));
break;
}
}
}
}
/*
* start the chat
*/
int pbx_capi_chat(struct ast_channel *c, char *param)
{
struct capi_pvt *i = NULL;
char *roomname, *controller, *options;
char *p;
struct capichat_s *room;
ast_group_t tmpcntr;
unsigned long contr = 0;
roomname = strsep(&param, "|");
options = strsep(&param, "|");
controller = param;
if (!roomname) {
cc_log(LOG_WARNING, "capi chat requires room name.\n");
return -1;
}
if (controller) {
for (p = controller; p && *p; p++) {
if (*p == '|') *p = ',';
}
tmpcntr = ast_get_group(controller);
contr = (unsigned long)(tmpcntr >> 1);
}
cc_verbose(3, 1, VERBOSE_PREFIX_3 "capi chat: %s: roomname=%s "
"options=%s controller=%s (0x%x)\n",
c->name, roomname, options, controller, contr);
if (c->tech == &capi_tech) {
i = CC_CHANNEL_PVT(c);
} else {
/* virtual CAPI channel */
i = capi_mknullif(c, contr);
if (!i) {
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);
if (!room) {
cc_log(LOG_WARNING, "Unable to open capi chat room.\n");
return -1;
}
/* main loop */
chat_handle_events(c, i);
del_chat_member(room);
out:
capi_remove_nullif(i);
return 0;
}
/*
* do command capi chatinfo
*/
int pbxcli_capi_chatinfo(int fd, int argc, char *argv[])
{
struct capichat_s *room = NULL;
struct ast_channel *c;
if (argc != 2)
return RESULT_SHOWUSAGE;
if (chat_list == NULL) {
ast_cli(fd, "There are no members in CAPI CHAT.\n");
return RESULT_SUCCESS;
}
ast_cli(fd, "CAPI CHAT\n");
ast_cli(fd, "Room# Roomname Member Caller\n");
cc_mutex_lock(&chat_lock);
room = chat_list;
while (room) {
c = room->i->owner;
if (!c) {
c = room->i->used;
}
if (!c) {
ast_cli(fd, "%3d %-12s%-30s\"%s\" <%s>\n",
room->number, room->name, room->i->vname,
"?", "?");
} else {
ast_cli(fd, "%3d %-12s%-30s\"%s\" <%s>\n",
room->number, room->name, c->name,
(c->cid.cid_name) ? c->cid.cid_name:"", c->cid.cid_num);
}
room = room->next;
}
cc_mutex_unlock(&chat_lock);
return RESULT_SUCCESS;
}

23
chan_capi_chat.h Normal file
View File

@ -0,0 +1,23 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2006-2007 Cytronics & Melware
*
* Armin Schindler <armin@melware.de>
*
* This program is free software and may be modified and
* distributed under the terms of the GNU Public License.
*/
#ifndef _PBX_CAPI_CHAT_H
#define _PBX_CAPI_CHAT_H
/*
* prototypes
*/
extern int pbx_capi_chat(struct ast_channel *c, char *param);
extern int pbxcli_capi_chatinfo(int fd, int argc, char *argv[]);
#endif

View File

@ -20,6 +20,9 @@
#define QSIG_TYPE_ALCATEL_ECMA 0x01 /* use additional Alcatel ECMA */
#define QSIG_TYPE_HICOM_ECMAV2 0x02 /* use additional Hicom ECMA V2 */
#define CAPI_QSIG_WAITEVENT_PRPROPOSE 0x01000000
#define Q932_PROTOCOL_ROSE 0x11 /* X.219 & X.229 */
#define Q932_PROTOCOL_CMIP 0x12 /* Q.941 */
#define Q932_PROTOCOL_ACSE 0x13 /* X.217 & X.227 */
@ -38,6 +41,9 @@
#define APDUINTERPRETATION_CLEARCALL 0x01
#define APDUINTERPRETATION_REJECT 0x02
/* const char* APDU_STR[] = { "IGNORE APDU", "CLEARCALL-IF-UNKNOWN", "REJECT APDU" }; */
/* ASN.1 Identifier Octet - Data types */
#define ASN1_TYPE_MASK 0x1f
#define ASN1_BOOLEAN 0x01
@ -86,8 +92,20 @@
#define CNIP_NAMEUSERPROVIDED 0x00 /* Name is User-provided, unvalidated */
#define CNIP_NAMEUSERPROVIDEDV 0x01 /* Name is User-provided and validated */
/* QSIG Operations += 1000 */
#define CCQSIG__ECMA__NAMEPRES 1000 /* Setting an own constant for ECMA Operation/Namepresentation, others will follow */
#define CCQSIG__ECMA__LEGINFO2 1011 /* LEG INFORMATION2 */
#define CCQSIG__ECMA__PRPROPOSE 1004 /* Path Replacement Propose */
#define CCQSIG__ECMA__CTCOMPLETE 1012 /* Call Transfer Complete */
#define CCQSIG__ECMA__LEGINFO2 1021 /* LEG INFORMATION2 */
#define CCQSIG__ECMA__LEGINFO3 1022 /* LEG INFORMATION3 */
#define CCQSIG_TIMER_WAIT_PRPROPOSE 1 /* Wait x seconds */
#define free_null(x) { free(x); x = NULL; }
/* Common QSIG structs */
/*
* INVOKE Data struct, contains data for further operations
@ -125,7 +143,6 @@ struct cc_qsig_nfe {
};
/*
* prototypes
*/
@ -134,12 +151,12 @@ struct cc_qsig_nfe {
*** QSIG Core Functions
*/
extern int cc_qsig_build_facility_struct(unsigned char * buf, unsigned int *idx, int apdu_interpr, struct cc_qsig_nfe *nfe);
extern int cc_qsig_add_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke);
extern int cc_qsig_build_facility_struct(unsigned char * buf, unsigned int *idx, int protocolvar, int apdu_interpr, struct cc_qsig_nfe *nfe);
extern int cc_qsig_add_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
extern unsigned int cc_qsig_asn1_get_string(unsigned char *buf, int buflen, unsigned char *data);
extern unsigned int cc_qsig_asn1_get_integer(unsigned char *data, int *idx);
extern unsigned char cc_qsig_asn1_get_oid(unsigned char *data, int *idx);
extern unsigned char *cc_qsig_asn1_oid2str(unsigned char *data, int size);
extern unsigned int cc_qsig_asn1_add_string(unsigned char *buf, int *idx, char *data, int datalen);
extern unsigned int cc_qsig_asn1_add_integer(unsigned char *buf, int *idx, int value);
@ -149,23 +166,30 @@ extern signed int cc_qsig_check_invoke(unsigned char *data, int *idx);
extern signed int cc_qsig_get_invokeid(unsigned char *data, int *idx, struct cc_qsig_invokedata *invoke);
extern signed int cc_qsig_fill_invokestruct(unsigned char *data, int *idx, struct cc_qsig_invokedata *invoke, int apduval);
extern unsigned int cc_qsig_handle_capiind(unsigned char *data, struct capi_pvt *i);
extern unsigned int cc_qsig_handle_capi_facilityind(unsigned char *data, struct capi_pvt *i);
extern unsigned int cc_qsig_add_call_setup_data(unsigned char *data, struct capi_pvt *i, struct ast_channel *c);
extern unsigned int cc_qsig_add_call_answer_data(unsigned char *data, struct capi_pvt *i, struct ast_channel *c);
extern unsigned int cc_qsig_add_call_alert_data(unsigned char *data, struct capi_pvt *i, struct ast_channel *c);
extern unsigned int cc_qsig_add_call_facility_data(unsigned char *data, struct capi_pvt *i, int facility);
extern signed int cc_qsig_identifyinvoke(struct cc_qsig_invokedata *invoke, int protocol);
extern unsigned int cc_qsig_handle_invokeoperation(int invokeident, struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
extern unsigned int cc_qsig_do_facility(unsigned char *fac, struct ast_channel *c, char *param, unsigned int factype);
extern unsigned int cc_qsig_do_facility(unsigned char *fac, struct ast_channel *c, char *param, unsigned int factype, int info1);
/*
*** ECMA QSIG Functions
*/
extern int pbx_capi_qsig_getplci(struct ast_channel *c, char *param);
extern int pbx_capi_qsig_ssct(struct ast_channel *c, char *param);
extern int pbx_capi_qsig_ct(struct ast_channel *c, char *param);
extern int pbx_capi_qsig_callmark(struct ast_channel *c, char *param);
extern int pbx_capi_qsig_bridge(struct capi_pvt *i0, struct capi_pvt *i1);
extern int pbx_capi_qsig_sendtext(struct ast_channel *c, const char *text);
extern void cc_qsig_op_ecma_isdn_namepres(struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
extern int cc_qsig_encode_ecma_name_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, int nametype);
extern void cc_qsig_op_ecma_isdn_leginfo2(struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
extern void cc_qsig_interface_init(struct cc_capi_conf *conf, struct capi_pvt *tmp);
extern void cc_pbx_qsig_conf_interface_value(struct cc_capi_conf *conf, struct ast_variable *v);
extern void interface_cleanup_qsig(struct capi_pvt *i);
extern void pbx_capi_qsig_unload_module(struct capi_pvt *i);
extern void pbx_capi_qsig_handle_info_indication(_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt *i);
extern void cc_qsig_encode_ecma_sscalltransfer(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, char *param);
#endif

View File

@ -1,8 +1,7 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for
* Asterisk / OpenPBX.org
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2005-2007 Cytronics & Melware
* Copyright (C) 2007 Mario Goegel
@ -26,7 +25,9 @@
#include <asterisk/options.h>
#include "chan_capi20.h"
#include "chan_capi.h"
#include "chan_capi_utils.h"
#include "chan_capi_qsig.h"
#include "chan_capi_qsig_ecma.h"
#include "chan_capi_qsig_asn197ade.h"
/*
@ -49,12 +50,17 @@ unsigned int cc_qsig_asn197ade_get_partynumber(char *buf, int buflen, int *idx,
numtype = (data[myidx++] & 0x0F); /* defines type of Number: numDigits, publicPartyNum, nsapEncNum, dataNumDigits */
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * num type %i\n", numtype); */
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * num type %i,%i\n", numtype, myidx); */
switch (numtype){
case 0:
if (data[myidx++] > 0) /* length of this context data */
if (data[myidx++] == ASN1_TC_CONTEXTSPEC)
myidx += cc_qsig_asn197ade_get_numdigits(buf, buflen, &myidx, data) + 1;
if (data[myidx] > 0) { /* length of this context data */
if (data[myidx+1] == ASN1_TC_CONTEXTSPEC) {
myidx += 2;
myidx += cc_qsig_asn197ade_get_numdigits(buf, buflen, &myidx, data);
} else {
myidx += cc_qsig_asn197ade_get_numdigits(buf, buflen, &myidx, data);
}
}
break;
case 1: /* publicPartyNumber (E.164) not supported yet */
return 0;
@ -63,11 +69,17 @@ unsigned int cc_qsig_asn197ade_get_partynumber(char *buf, int buflen, int *idx,
return 0;
break;
case 3:
if (data[myidx++] > 0) /* length of this context data */
if (data[myidx++] == ASN1_TC_CONTEXTSPEC)
myidx += cc_qsig_asn197ade_get_numdigits(buf, buflen, &myidx, data) + 1;
if (data[myidx++] > 0) { /* length of this context data */
if (data[myidx+1] == ASN1_TC_CONTEXTSPEC) {
myidx += 2;
myidx += cc_qsig_asn197ade_get_numdigits(buf, buflen, &myidx, data);
} else {
myidx += cc_qsig_asn197ade_get_numdigits(buf, buflen, &myidx, data);
}
}
break;
};
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * num type %i,%i\n", numtype, myidx); */
return myidx - *idx;
}
@ -85,7 +97,7 @@ unsigned int cc_qsig_asn197ade_get_numdigits(char *buf, int buflen, int *idx, un
memcpy(buf, &data[myidx], strsize);
buf[strsize] = 0;
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * string length %i\n", strsize); */
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * string length %i,%i\n", strsize, *idx); */
return strsize;
}
@ -106,3 +118,72 @@ unsigned int cc_qsig_asn197ade_add_numdigits(char *buf, int buflen, int *idx, un
myidx = 1 + strlen(buf);
return myidx;
}
/*
* Returns an "PresentedNumberScreened" from an string, encoded as in addressing-data-elements-asn1-97
* data is pointer to PresentedNumberScreened struct
* return:
* index counter
*/
unsigned int cc_qsig_asn197ade_get_pns(unsigned char *data, int *idx, struct asn197ade_numberscreened *ns)
{ /* sample data: a0 08 80 03>513<0a 01 00 */
int myidx = *idx;
char buf[ASN197ADE_NUMDIGITS_STRSIZE+1];
unsigned int buflen = sizeof(buf);
unsigned res;
ns->partyNumber = NULL;
ns->screeningInd = userProvidedNotScreened;
int numtype;
numtype = (data[myidx++] & 0x0F); /* defines type of Number */
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * num type %i,%i\n", numtype, myidx); */
switch (numtype){
case 0:
/* myidx points now to length */
res = cc_qsig_asn197ade_get_partynumber(buf, buflen, &myidx, data);
/* cc_verbose(1, 1, VERBOSE_PREFIX_4 " * res %i\n", numtype); */
if (!res)
return 0;
myidx += res;
if (strlen(buf)) {
ns->partyNumber = strdup(buf);
}
/* get screening indicator */
if (data[myidx] == ASN1_ENUMERATED) { /* HACK: this is not safe - check length of this parameter */
myidx++;
ns->screeningInd = cc_qsig_asn1_get_integer(data, &myidx);
}
break;
case 1: /* presentation restricted */
myidx += data[myidx] + 1; /* this val should be zero */
break;
case 2: /* number not available due to interworking */
myidx += data[myidx] + 1; /* this val should be zero */
break;
case 3:
/* myidx points now to length */
res = cc_qsig_asn197ade_get_partynumber(buf, buflen, &myidx, data);
if (!res)
return 0;
myidx += res;
if (strlen(buf)) {
ns->partyNumber = strdup(buf);
}
/* get screening indicator */
if (data[myidx] == ASN1_ENUMERATED) { /* HACK: this is not safe - check length of this parameter */
myidx++;
ns->screeningInd = cc_qsig_asn1_get_integer(data, &myidx);
}
break;
};
return myidx - *idx;
}

View File

@ -1,8 +1,7 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for
* Asterisk / OpenPBX.org
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2005-2007 Cytronics & Melware
* Copyright (C) 2007 Mario Goegel
@ -23,9 +22,21 @@
#define ASN197ADE_NUMDIGITS_STRSIZE 20
struct asn197ade_numberscreened {
char *partyNumber;
enum {
userProvidedNotScreened,
userProvidedVerifiedAndPassed,
userProvidedVerifiedAndFailed,
networkProvided
} screeningInd;
};
extern unsigned int cc_qsig_asn197ade_get_partynumber(char *buf, int buflen, int *idx, unsigned char *data);
extern unsigned int cc_qsig_asn197ade_get_numdigits(char *buf, int buflen, int *idx, unsigned char *data);
extern unsigned int cc_qsig_asn197ade_add_numdigits(char *buf, int buflen, int *idx, unsigned char *data);
extern unsigned int cc_qsig_asn197ade_get_pns(unsigned char *data, int *idx, struct asn197ade_numberscreened *ns);
#endif

View File

@ -1,8 +1,7 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for
* Asterisk / OpenPBX.org
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2005-2007 Cytronics & Melware
* Copyright (C) 2007 Mario Goegel
@ -26,7 +25,9 @@
#include <asterisk/options.h>
#include "chan_capi20.h"
#include "chan_capi.h"
#include "chan_capi_utils.h"
#include "chan_capi_qsig.h"
#include "chan_capi_qsig_ecma.h"
#include "chan_capi_qsig_asn197no.h"
/*
@ -73,7 +74,7 @@ unsigned int cc_qsig_asn197no_get_name(char *buf, int buflen, unsigned int *bufd
if (data[myidx++] == ASN1_OCTETSTRING) {
/* should be so */
namelength = cc_qsig_asn1_get_string((unsigned char *)buf, buflen, &data[myidx]);
myidx += data[myidx-1]; /* is this safe? */
myidx += namelength + 1;
} else {
cc_verbose(1, 1, VERBOSE_PREFIX_4 " Namestruct not ECMA conform (String expected)\n");
break;

View File

@ -1,8 +1,7 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for
* Asterisk / OpenPBX.org
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2005-2007 Cytronics & Melware
* Copyright (C) 2007 Mario Goegel

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,7 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for
* Asterisk / OpenPBX.org
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2005-2007 Cytronics & Melware
* Copyright (C) 2007 Mario Goegel
@ -23,7 +22,9 @@
#include <asterisk/pbx.h>
#include "chan_capi20.h"
#include "chan_capi.h"
#include "chan_capi_utils.h"
#include "chan_capi_qsig.h"
#include "chan_capi_qsig_ecma.h"
#include "chan_capi_qsig_asn197ade.h"
#include "chan_capi_qsig_asn197no.h"
@ -46,18 +47,55 @@ void cc_qsig_op_ecma_isdn_namepres(struct cc_qsig_invokedata *invoke, struct cap
unsigned int namelength = 0;
unsigned int datalength;
int myidx = 0;
char *nametype = NULL;
cc_verbose(1, 1, VERBOSE_PREFIX_4 "Handling Name Operation (id# %#x)\n", invoke->id);
callername[0] = 0;
datalength = invoke->datalen;
myidx = cc_qsig_asn197no_get_name(callername, ASN197NO_NAME_STRSIZE, &namelength, &myidx, invoke->data );
if (namelength > 0) {
/* TODO: Maybe we do some charset conversions */
i->owner->cid.cid_name = strdup(callername);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * received name (%i byte(s)): \"%s\"\n", namelength, callername);
if (namelength == 0) {
return;
}
/* TODO: Maybe we do some charset conversions */
switch (invoke->type) {
case 0: /* Calling Name */
nametype = "CALLING NAME";
break;
case 1: /* Called Name */
nametype = "CALLED NAME";
break;
case 2: /* Connected Name */
nametype = "CONNECTED NAME";
break;
case 3: /* Busy Name */
nametype = "BUSY NAME";
break;
}
switch (invoke->type) {
case 0: /* Calling Name */
i->owner->cid.cid_name = strdup(callername); /* Save name to callerid */
break;
case 1: /* Called Name */
case 2: /* Connected Name */
case 3: /* Busy Name */
if (i->qsig_data.dnameid) { /* this facility may come more than once - if so, then update this value */
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * deleting previously received name.\n", nametype, namelength, callername);
free(i->qsig_data.dnameid);
}
i->qsig_data.dnameid = strdup(callername); /* save name as destination in qsig specific fields */
/* there's no similarly field in asterisk */
break;
default:
break;
}
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Got %s: \"%s\" (%i byte(s))\n", nametype, callername, namelength);
/* if there was an sequence tag, we have more informations here, but we will ignore it at the moment */
@ -78,17 +116,15 @@ void cc_qsig_op_ecma_isdn_namepres(struct cc_qsig_invokedata *invoke, struct cap
* returns
* always 0
*/
int cc_qsig_encode_ecma_name_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, int nametype)
int cc_qsig_encode_ecma_name_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, int nametype, char * name)
{
const unsigned char oid[] = {0x2b,0x0c,0x09,0x00}; /* 1.3.12.9.0 */
int oid_len = sizeof(oid);
unsigned char namebuf[51];
unsigned char data[255];
int dataidx = 0;
int namelen = 0;
if (i->owner->cid.cid_name)
namelen = strlen(i->owner->cid.cid_name);
if (name)
namelen = strlen(name);
if (namelen < 1) { /* There's no name available, try to take Interface-Name */
if (i->name) {
@ -102,17 +138,12 @@ int cc_qsig_encode_ecma_name_invoke(unsigned char * buf, unsigned int *idx, stru
} else {
if (namelen > 50)
namelen = 50;
memcpy(namebuf, i->owner->cid.cid_name, namelen);
memcpy(namebuf, name, namelen);
}
invoke->id = 1;
invoke->descr_type = ASN1_OBJECTIDENTIFIER;
invoke->oid_len = oid_len;
memcpy(invoke->oid_bin, oid, oid_len);
/* HACK: */
if (nametype)
invoke->oid_bin[3] = 2;
invoke->descr_type = -1; /* Let others do the work: qsig_add_invoke */
invoke->type = (nametype % 4); /* Invoke Operation Number, if OID it's the last byte*/
if (namelen>0) {
data[dataidx++] = 0x80; /* We send only simple Name, Namepresentation allowed */
@ -127,8 +158,78 @@ int cc_qsig_encode_ecma_name_invoke(unsigned char * buf, unsigned int *idx, stru
invoke->datalen = dataidx;
memcpy(invoke->data, data, dataidx);
/* qsig_add_invoke(buf, idx, invoke); */
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Sending \"%s\": (%i byte(s))\n", namebuf, namelen);
return 0;
}
/*
* Encode Operation: 1.3.12.9.22 ECMA/ISDN/LEG_INFO3
*
* This function encodes the namepresentation facility
* The name will be copied from the cid.cid_name field of the asterisk channel struct.
* We create an invoke struct with the complete encoded invoke.
*
* parameters
* buf is pointer to facility array, not used now
* idx current idx in facility array, not used now
* invoke struct, which contains encoded data for facility
* i is pointer to capi channel
* returns
* always 0
*/
int cc_qsig_encode_ecma_isdn_leginfo3_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, char *name)
{
unsigned char namebuf[51];
unsigned char data[255];
int dataidx = 0;
int namelen = 0;
if (name)
namelen = strlen(name);
if (namelen < 1) { /* There's no name available, try to take Interface-Name */
if (i->name) {
if (strlen(i->name) >= 1) {
if (namelen > 50)
namelen = 50;
namelen = strlen(i->name);
memcpy(namebuf, i->name, namelen);
}
}
} else {
if (namelen > 50)
namelen = 50;
memcpy(namebuf, name, namelen);
}
invoke->id = 1;
invoke->descr_type = -1; /* Let others do the work: qsig_add_invoke */
invoke->type = 22; /* Invoke Operation Number, if OID it's the last byte*/
data[dataidx++] = ASN1_TF_CONSTRUCTED | ASN1_SEQUENCE;
data[dataidx++] = 5 + namelen;
data[dataidx++] = ASN1_BOOLEAN; /* PresentationAllowedIndicator */
data[dataidx++] = 1;
data[dataidx++] = 1;
if (namelen>0) {
data[dataidx++] = 0x80; /* We send only simple Name, Namepresentation allowed */
data[dataidx++] = namelen;
memcpy(&data[dataidx], namebuf, namelen);
dataidx += namelen;
} else {
data[dataidx++] = 0x84; /* Name not available */
data[dataidx++] = 0;
}
invoke->datalen = dataidx;
memcpy(invoke->data, data, dataidx);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Sending QSIG_LEG_INFO3 \"%s\": (%i byte(s))\n", namebuf, namelen);
return 0;
}
@ -159,6 +260,7 @@ void cc_qsig_op_ecma_isdn_leginfo2(struct cc_qsig_invokedata *invoke, struct cap
char tempstr[5];
char divertNum[ASN197ADE_NUMDIGITS_STRSIZE+1];
char origCalledNum[ASN197ADE_NUMDIGITS_STRSIZE+1];
struct asn197ade_numberscreened divertPNS, origPNS;
char divertName[ASN197NO_NAME_STRSIZE+1];
char origCalledName[ASN197NO_NAME_STRSIZE+1];
unsigned int temp = 0;
@ -202,49 +304,46 @@ void cc_qsig_op_ecma_isdn_leginfo2(struct cc_qsig_invokedata *invoke, struct cap
orgDivReason = cc_qsig_asn1_get_integer(invoke->data, &myidx);
break;
case 1:
temp = cc_qsig_asn197ade_get_partynumber(divertNum, ASN197ADE_NUMDIGITS_STRSIZE, &myidx, invoke->data);
if (temp) {
myidx += temp;
}
temp = invoke->data[myidx++]; /* keep the length of this info - maybe we don't get all data now */
cc_qsig_asn197ade_get_pns(invoke->data, &myidx, &divertPNS);
myidx += temp;
break;
case 2:
temp = cc_qsig_asn197ade_get_partynumber(origCalledNum, ASN197ADE_NUMDIGITS_STRSIZE, &myidx, invoke->data);
if (temp) {
myidx += temp;
}
temp = invoke->data[myidx++]; /* keep the length of this info - maybe we don't get all data now */
cc_qsig_asn197ade_get_pns(invoke->data, &myidx, &origPNS);
myidx += temp;
break;
case 3:
/* Redirecting Name */
myidx++;
temp = cc_qsig_asn197no_get_name(divertName, ASN197NO_NAME_STRSIZE, &temp2, &myidx, invoke->data);
if (temp) {
myidx += temp;
}
temp = invoke->data[myidx++]; /* keep the length of this info - maybe we don't get all data now */
cc_qsig_asn197no_get_name(divertName, ASN197NO_NAME_STRSIZE, &temp2, &myidx, invoke->data);
myidx += temp;
break;
case 4:
/* origCalled Name */
myidx++;
temp = cc_qsig_asn197no_get_name(origCalledName, ASN197NO_NAME_STRSIZE, &temp2, &myidx, invoke->data);
if (temp) {
myidx += temp;
}
temp = invoke->data[myidx++]; /* keep the length of this info - maybe we don't get all data now */
cc_qsig_asn197no_get_name(origCalledName, ASN197NO_NAME_STRSIZE, &temp2, &myidx, invoke->data);
myidx += temp;
break;
default:
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * unknown parameter %i\n", parameter);
break;
}
}
snprintf(tempstr, 5, "%i", divReason);
pbx_builtin_setvar_helper(i->owner, "QSIG_LI2_DIVREASON", tempstr);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_DIVREASON", tempstr);
snprintf(tempstr, 5, "%i", orgDivReason);
pbx_builtin_setvar_helper(i->owner, "QSIG_LI2_ODIVREASON", tempstr);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_ODIVREASON", tempstr);
snprintf(tempstr, 5, "%i", divCount);
pbx_builtin_setvar_helper(i->owner, "QSIG_LI2_DIVCOUNT", tempstr);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_DIVCOUNT", tempstr);
pbx_builtin_setvar_helper(i->owner, "QSIG_LI2_DIVNUM", divertNum);
pbx_builtin_setvar_helper(i->owner, "QSIG_LI2_ODIVNUM", origCalledNum);
pbx_builtin_setvar_helper(i->owner, "QSIG_LI2_DIVNAME", divertName);
pbx_builtin_setvar_helper(i->owner, "QSIG_LI2_ODIVNAME", origCalledName);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_DIVNUM", divertPNS.partyNumber);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_ODIVNUM", origPNS.partyNumber);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_DIVNAME", divertName);
pbx_builtin_setvar_helper(i->owner, "_QSIG_LI2_ODIVNAME", origCalledName);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * QSIG_LEG_INFO2: %i(%i), %ix %s->%s, %s->%s\n", divReason, orgDivReason, divCount, origCalledNum, divertNum, origCalledName, divertName);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Got QSIG_LEG_INFO2: %i(%i), %ix %s->%s, %s->%s\n", divReason, orgDivReason, divCount, origPNS.partyNumber, divertPNS.partyNumber, origCalledName, divertName);
return;
@ -252,9 +351,240 @@ void cc_qsig_op_ecma_isdn_leginfo2(struct cc_qsig_invokedata *invoke, struct cap
/*
* Encode Operation: 1.3.12.9.99 ECMA/ISDN/SIMPLECALLTRANSFER
* Encode Operation: 1.3.12.9.12 ECMA/ISDN/CALLTRANSFER
*
* This function encodes the simple call transfer facility
* This function encodes the call transfer facility
*
* We create an invoke struct with the complete encoded invoke.
*
* parameters
* buf is pointer to facility array, not used now
* idx current idx in facility array, not used now
* invoke struct, which contains encoded data for facility
* i is pointer to capi channel
* param is parameter from capicommand
* info this facility is part of 2, 0 is facility 1, 1 is facility 2
* returns
* always 0
*/
void cc_qsig_encode_ecma_calltransfer(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, char *param, int info)
{
char *cid, *ccanswer;
char *name = NULL;
int icanswer = 0;
int cidlen = 0;
int namelength = 0;
int seqlen = 13;
char c[255];
int ix = 0;
if (param) { /* got Call Transfer Parameters */
if (info) {
cid = strsep(&param, "|");
cidlen = strlen(cid);
if (cidlen > 20) /* HACK: stop action here, maybe we have invalid data */
cidlen = 20;
} else {
char *tmp = strsep(&param, "|");
tmp = NULL;
cid = strsep(&param, "|");
cidlen = strlen(cid);
if (cidlen > 20) /* HACK: stop action here, maybe we have invalid data */
cidlen = 20;
ccanswer = strsep(&param, "|");
if (ccanswer[0])
icanswer = ccanswer[0] - 0x30;
}
} else {
/* cid = strdup(i->owner->cid.cid_num);*/ /* Here we get the Asterisk extension */
if (info) { /* info is >0 on outbound channel (second facility) */
struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci);
cid = strdup(i->cid);
cidlen = strlen(cid);
if (ii) {
/* send callers name to user B */
if (ii->owner->cid.cid_name) {
name = ast_strdupa(ii->owner->cid.cid_name);
namelength = strlen(name);
}
}
} else { /* have to build first facility - send destination number back to inbound channel */
struct capi_pvt *ii = capi_find_interface_by_plci(i->qsig_data.partner_plci);
cid = strdup(ii->dnid);
cidlen = strlen(cid);
if (ii) {
/* send destination name to user A */
if (ii->qsig_data.dnameid) {
name = ast_strdupa(ii->qsig_data.dnameid);
namelength = strlen(name);
}
}
}
if (!info)
icanswer = i->qsig_data.calltransfer_onring % 1;
}
seqlen += cidlen;
if (namelength)
seqlen += 4 + namelength;
c[ix++] = ASN1_SEQUENCE | ASN1_TF_CONSTRUCTED; /* start of SEQUENCE */
c[ix++] = seqlen;
c[ix++] = ASN1_ENUMERATED; /* End Designation */
c[ix++] = 1; /* length */
c[ix++] = info;
c[ix++] = (ASN1_TC_CONTEXTSPEC | ASN1_TF_CONSTRUCTED) + 0; /* val 0 - Source Caller ID struct */
c[ix++] = 5 + cidlen;
c[ix++] = ASN1_TC_CONTEXTSPEC; /* CallerID */
c[ix++] = cidlen;
memcpy(&c[ix], cid, cidlen);
ix += cidlen;
c[ix++] = ASN1_ENUMERATED; /* Screening Indicator */
c[ix++] = 1; /* length */
c[ix++] = 1; /* 01 = userProvidedVerifiedAndPassed ...we hope so */
{
if (namelength) {
c[ix++] = (ASN1_TC_CONTEXTSPEC | ASN1_TF_CONSTRUCTED) + 1; /* val 1 - Source Caller ID struct */
c[ix++] = 2 + namelength;
c[ix++] = ASN1_OCTETSTRING; /* CallerID */
c[ix++] = namelength;
memcpy(&c[ix], name, namelength);
ix += namelength;
}
}
c[ix++] = ASN1_ENUMERATED; /* val 3 - wait for connect ? */
c[ix++] = 1;
c[ix++] = icanswer;
/* end of SEQUENCE */
/* there are optional data possible here */
invoke->id = 12;
invoke->descr_type = -1;
invoke->type = 12; /* Invoke Operation Code */
invoke->datalen = ix;
memcpy(invoke->data, c, ix);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Sending QSIG_CT: %i->%s\n", info, cid);
if (cid)
free(cid);
}
/*
* Decode Operation: 1.3.12.9.12 ECMA/ISDN/CALLTRANSFER
*
* This function decodes the call transfer facility
*
* We create an invoke struct with the complete encoded invoke.
*
* parameters
* buf is pointer to facility array, not used now
* idx current idx in facility array, not used now
* invoke struct, which contains encoded data from facility
* i is pointer to capi channel
* returns
* transfer to destination number
*/
unsigned int cc_qsig_decode_ecma_calltransfer(struct cc_qsig_invokedata *invoke, struct capi_pvt *i, struct cc_qsig_ctcomplete *ctc)
{
unsigned int datalength;
unsigned int seqlength = 0;
unsigned char *data = invoke->data;
int myidx = 0;
/* TODO: write more code */
char *ct_status_txt[] = { "ANSWERED", "ALERTING" };
char ct_name[ASN197NO_NAME_STRSIZE+1] = { "EMPTY" };
unsigned int namelength = 0;
int temp = 0;
ctc->endDesignation = primaryEnd;
ctc->redirectionNumber.partyNumber = NULL;
ctc->redirectionNumber.screeningInd = userProvidedNotScreened;
ctc->basicCallInfoElements = NULL;
ctc->redirectionName = NULL;
ctc->callStatus = answered;
ctc->argumentExtension = NULL; /* unhandled yet */
#define ct_err(x...) { cc_verbose(1, 1, VERBOSE_PREFIX_4 " * not Handling QSIG CALL TRANSFER - "x); return 0; }
cc_verbose(1, 1, VERBOSE_PREFIX_4 "Handling QSIG CALL TRANSFER (id# %#x)\n", invoke->id);
if (data[myidx++] != (ASN1_SEQUENCE | ASN1_TC_UNIVERSAL | ASN1_TF_CONSTRUCTED)) { /* 0x30 */
/* We do not handle this, because it should start with an sequence tag */
ct_err("not a sequence\n");
}
/* This facility is encoded as SEQUENCE */
seqlength = data[myidx++];
datalength = invoke->datalen;
if (datalength < (seqlength+1)) {
ct_err("buffer error\n");
}
if (data[myidx++] == ASN1_ENUMERATED) {
ctc->endDesignation = cc_qsig_asn1_get_integer(data, &myidx);
} else {
ct_err("no endDesignation information.\n");
}
temp = cc_qsig_asn197ade_get_pns(data, &myidx, &ctc->redirectionNumber);
if (!temp) {
ct_err("error on decoding PresentedNumberScreened value.\n");
}
myidx += temp;
if (myidx < datalength) {
if (data[myidx] == ASN1_TC_APPLICATION) {
myidx++;
/* TODO: check size -> could be bigger than 256 bytes - MSB is set then */
ctc->basicCallInfoElements = malloc(data[myidx]);
if (ctc->basicCallInfoElements) {
memcpy(ctc->basicCallInfoElements, &data[myidx+1], data[myidx] );
} else {
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * QSIG CALL TRANSFER - couldn't allocate memory for basicCallInfoElements.\n", (int)data[myidx]);
}
myidx += data[myidx] + 1;
}
}
if (myidx < datalength) {
if (data[myidx] != ASN1_ENUMERATED) { /* Maybe we get an name (OPTIONAL) */
myidx += cc_qsig_asn197no_get_name(ct_name, ASN197NO_NAME_STRSIZE+1, &namelength, &myidx, data );
if (namelength)
ctc->redirectionName = strdup(ct_name);
}
}
if (myidx < datalength) {
if (data[myidx++] == ASN1_ENUMERATED) { /* Call Status */
ctc->callStatus = cc_qsig_asn1_get_integer(data, &myidx);
}
}
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Got QSIG CALL TRANSFER endDesignation: %i partyNumber: %s (ScreeningInd: %i), partyName: \"%s\", Call state: %s\n",
ctc->endDesignation, ctc->redirectionNumber.partyNumber, ctc->redirectionNumber.screeningInd, ctc->redirectionName, ct_status_txt[ctc->callStatus]);
return 1;
#undef ct_err
}
/*
* Encode Operation: 1.3.12.9.99 ECMA/ISDN/SINGLESTEPCALLTRANSFER
*
* This function encodes the single step call transfer facility
*
* We create an invoke struct with the complete encoded invoke.
*
@ -269,7 +599,6 @@ void cc_qsig_op_ecma_isdn_leginfo2(struct cc_qsig_invokedata *invoke, struct cap
*/
void cc_qsig_encode_ecma_sscalltransfer(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, char *param)
{
const unsigned char oid[] = {0x2b,0x0c,0x09,0x63}; /* 1.3.12.9.99 */
char *cidsrc, *ciddst;
int srclen, dstlen;
int seqlen = 12;
@ -311,16 +640,155 @@ void cc_qsig_encode_ecma_sscalltransfer(unsigned char * buf, unsigned int *idx,
c[ix++] = 1;
c[ix++] = 0;
/* end of SEQUENCE */
/* end of SEQUENCE */
/* there are optional data possible here */
invoke->id = 99;
invoke->descr_type = ASN1_OBJECTIDENTIFIER;
invoke->oid_len = sizeof(oid);
memcpy(invoke->oid_bin, oid, sizeof(oid));
invoke->descr_type = -1;
invoke->type = 99;
invoke->datalen = ix;
memcpy(invoke->data, c, ix);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * QSIG_CT: %s->%s\n", cidsrc, ciddst);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Sending QSIG_SSCT: %s->%s\n", cidsrc, ciddst);
}
/*
* Handle Operation: 1.3.12.9.19 ECMA/ISDN/PATH REPLACEMENT PROPOSE
*
* This function decodes the PATH REPLACEMENT PROPOSE facility
* The datas will be copied in the some capi_pvt channel variables
*
* parameters
* invoke struct, which contains encoded data from facility
* i is pointer to capi channel
* returns
* nothing
*/
void cc_qsig_op_ecma_isdn_prpropose(struct cc_qsig_invokedata *invoke, struct capi_pvt *i)
{
unsigned int datalength;
unsigned int seqlength = 0;
int myidx = 0;
/* TODO: write more code */
char callid[4+1];
char reroutingnr[ASN197ADE_NUMDIGITS_STRSIZE+1];
int temp = 0;
callid[0] = 0;
reroutingnr[0] = 0;
cc_verbose(1, 1, VERBOSE_PREFIX_4 "Handling QSIG PATH REPLACEMENT PROPOSE (id# %#x)\n", invoke->id);
if (invoke->data[myidx++] != (ASN1_SEQUENCE | ASN1_TC_UNIVERSAL | ASN1_TF_CONSTRUCTED)) { /* 0x30 */
/* We do not handle this, because it should start with an sequence tag */
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * not Handling QSIG REPLACEMENT PROPOSE - not a sequence\n");
return;
}
/* This facility is encoded as SEQUENCE */
seqlength = invoke->data[myidx++];
datalength = invoke->datalen;
if (datalength < (seqlength+1)) {
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * not Handling QSIG REPLACEMENT PROPOSE - buffer error\n");
return;
}
if (invoke->data[myidx++] == ASN1_NUMERICSTRING) {
int strsize;
strsize = cc_qsig_asn1_get_string((unsigned char*)&callid, sizeof(callid), &invoke->data[myidx]);
myidx += strsize +1;
} else {
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * not Handling QSIG REPLACEMENT PROPOSE - NUMERICSTRING expected\n");
return;
}
if (invoke->data[myidx++] == ASN1_TC_CONTEXTSPEC)
temp = cc_qsig_asn1_get_string((unsigned char*)&reroutingnr, sizeof(reroutingnr), &invoke->data[myidx]);
if (temp) {
myidx += temp;
} else {
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * not Handling QSIG REPLACEMENT PROPOSE - partyNumber expected (%i)\n", myidx);
return;
}
i->qsig_data.pr_propose_cid = strdup(callid);
i->qsig_data.pr_propose_pn = strdup(reroutingnr);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Got QSIG_PATHREPLACEMENT_PROPOSE Call identity: %s, Party number: %s (%i)\n", callid, reroutingnr, temp);
return;
}
/*
* Encode Operation: 1.3.12.9.19 ECMA/ISDN/PATH REPLACEMENT PROPOSE
*
* This function encodes the path replacement propose
*
* We create an invoke struct with the complete encoded invoke.
*
* parameters
* buf is pointer to facility array, not used now
* idx current idx in facility array, not used now
* invoke struct, which contains encoded data for facility
* i is pointer to capi channel
* param is parameter from capicommand
* returns
* always 0
*/
void cc_qsig_encode_ecma_prpropose(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, char *param)
{
/* TODO: write code */
int invokeop = 4;
char *callid, *reroutingnr;
int cidlen, rrnlen;
int seqlen = 4;
char c[255];
int ix = 0;
if (!i->qsig_data.pr_propose_cid)
return ;
if (!i->qsig_data.pr_propose_pn)
return ;
callid = i->qsig_data.pr_propose_cid;
reroutingnr = i->qsig_data.pr_propose_pn;
cidlen = strlen(callid);
rrnlen = strlen(reroutingnr);
seqlen += cidlen + rrnlen;
c[ix++] = ASN1_SEQUENCE | ASN1_TF_CONSTRUCTED; /* start of SEQUENCE */
c[ix++] = seqlen;
c[ix++] = ASN1_NUMERICSTRING; /* val 1 - CallID */
c[ix++] = cidlen;
memcpy(&c[ix], callid, cidlen);
ix += cidlen;
c[ix++] = ASN1_TC_CONTEXTSPEC; /* val 2 - Rerouting number*/
c[ix++] = rrnlen;
memcpy(&c[ix], reroutingnr, rrnlen);
ix += rrnlen;
/* end of SEQUENCE */
/* there are optional data possible here */
invoke->id = invokeop;
invoke->descr_type = -1;
invoke->type = invokeop;
invoke->datalen = ix;
memcpy(invoke->data, c, ix);
cc_verbose(1, 1, VERBOSE_PREFIX_4 " * Sending QSIG_PATHREPLACEMENT_PROPOSE: Call identity: %s, Party number: %s\n", callid, reroutingnr);
return;
}

58
chan_capi_qsig_ecma.h Normal file
View File

@ -0,0 +1,58 @@
/*
* (QSIG)
*
* Implementation of QSIG extensions for CHAN_CAPI
*
* Copyright 2006-2007 (c) Mario Goegel
*
* Mario Goegel <m.goegel@gmx.de>
*
* This program is free software and may be modified and
* distributed under the terms of the GNU Public License.
*/
#include "chan_capi_qsig_asn197ade.h"
#ifndef PBX_QSIG_ECMA_H
#define PBX_QSIG_ECMA_H
/* ECMA Features structs */
/* Call Transfer Complete struct */
struct cc_qsig_ctcomplete {
enum {
primaryEnd, /* 0 */
secondaryEnd /* 1 */
} endDesignation;
struct asn197ade_numberscreened redirectionNumber;
char *basicCallInfoElements; /* OPTIONAL: ASN1_APPLICATION Type */
char *redirectionName; /* OPTIONAL */
enum {
answered,
alerting
} callStatus; /* DEFAULT: answered */
char *argumentExtension; /* OPTIONAL: ASN1_SEQUENCE - manufacturer specific extension */
};
/*
*** ECMA QSIG Functions
*/
extern void cc_qsig_op_ecma_isdn_namepres(struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
extern int cc_qsig_encode_ecma_name_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, int nametype, char *name);
extern int cc_qsig_encode_ecma_isdn_leginfo3_invoke(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, char *name);
extern void cc_qsig_op_ecma_isdn_leginfo2(struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
extern void cc_qsig_op_ecma_isdn_prpropose(struct cc_qsig_invokedata *invoke, struct capi_pvt *i);
extern void cc_qsig_encode_ecma_prpropose(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, char *param);
extern void cc_qsig_encode_ecma_sscalltransfer(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, char *param);
extern void cc_qsig_encode_ecma_calltransfer(unsigned char * buf, unsigned int *idx, struct cc_qsig_invokedata *invoke, struct capi_pvt *i, char *param, int info);
extern unsigned int cc_qsig_decode_ecma_calltransfer(struct cc_qsig_invokedata *invoke, struct capi_pvt *i, struct cc_qsig_ctcomplete *ctc);
#endif

View File

@ -1,8 +1,7 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for
* Asterisk / OpenPBX.org
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2006-2007 Cytronics & Melware
*
@ -11,11 +10,6 @@
* This program is free software and may be modified and
* distributed under the terms of the GNU Public License.
*/
#ifdef PBX_IS_OPBX
#ifdef HAVE_CONFIG_H
#include "confdefs.h"
#endif
#endif
#include <stdlib.h>
#include <stdio.h>
@ -27,41 +21,10 @@
#include <sys/types.h>
#include <sys/socket.h>
#ifdef PBX_IS_OPBX
#include "openpbx/lock.h"
#include "openpbx/frame.h"
#include "openpbx/channel.h"
#include "openpbx/logger.h"
#include "openpbx/module.h"
#include "openpbx/pbx.h"
#include "openpbx/config.h"
#include "openpbx/options.h"
#include "openpbx/features.h"
#include "openpbx/utils.h"
#include "openpbx/rtp.h"
#include "openpbx/strings.h"
#include "openpbx/chan_capi20.h"
#include "openpbx/chan_capi.h"
#include "openpbx/chan_capi_rtp.h"
#else
#include "config.h"
#include <asterisk/lock.h>
#include <asterisk/frame.h>
#include <asterisk/channel.h>
#include <asterisk/logger.h>
#include <asterisk/module.h>
#include <asterisk/pbx.h>
#include <asterisk/config.h>
#include <asterisk/options.h>
#include <asterisk/features.h>
#include <asterisk/utils.h>
#include <asterisk/rtp.h>
#include <asterisk/strings.h>
#include "chan_capi20.h"
#include "chan_capi.h"
#include "chan_capi_rtp.h"
#endif
#include "chan_capi_utils.h"
/* RTP settings / NCPI RTP struct */
@ -225,10 +188,8 @@ int capi_alloc_rtp(struct capi_pvt *i)
/*
* write rtp for a channel
*/
int capi_write_rtp(struct ast_channel *c, struct ast_frame *f)
int capi_write_rtp(struct capi_pvt *i, struct ast_frame *f)
{
struct capi_pvt *i = CC_CHANNEL_PVT(c);
_cmsg CMSG;
struct sockaddr_in us;
int len;
socklen_t uslen;
@ -281,14 +242,13 @@ int capi_write_rtp(struct ast_channel *c, struct ast_frame *f)
i->vname, i->NCCI, len, f->datalen, ast_getformatname(f->subclass),
i->timestamp);
DATA_B3_REQ_HEADER(&CMSG, capi_ApplID, get_capi_MessageNumber(), 0);
DATA_B3_REQ_NCCI(&CMSG) = i->NCCI;
DATA_B3_REQ_FLAGS(&CMSG) = 0;
DATA_B3_REQ_DATAHANDLE(&CMSG) = i->send_buffer_handle;
DATA_B3_REQ_DATALENGTH(&CMSG) = len;
DATA_B3_REQ_DATA(&CMSG) = (buf);
_capi_put_cmsg(&CMSG);
capi_sendf(NULL, 0, CAPI_DATA_B3_REQ, i->NCCI, get_capi_MessageNumber(),
"dwww",
buf,
len,
i->send_buffer_handle,
0
);
}
return 0;
@ -352,11 +312,11 @@ void voice_over_ip_profile(struct cc_capi_controller *cp)
unsigned short info = 0;
unsigned int payload1, payload2;
FACILITY_REQ_HEADER(&CMSG, capi_ApplID, get_capi_MessageNumber(), 0);
FACILITY_REQ_CONTROLLER(&CMSG) = cp->controller;
FACILITY_REQ_FACILITYSELECTOR(&CMSG) = FACILITYSELECTOR_VOICE_OVER_IP;
FACILITY_REQ_FACILITYREQUESTPARAMETER(&CMSG) = (_cstruct)&fac;
_capi_put_cmsg(&CMSG);
capi_sendf(NULL, 0, CAPI_FACILITY_REQ, cp->controller, get_capi_MessageNumber(),
"ws",
FACILITYSELECTOR_VOICE_OVER_IP,
&fac
);
tv.tv_sec = 1;
tv.tv_usec = 0;

View File

@ -1,8 +1,7 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for
* Asterisk / OpenPBX.org
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2006-2007 Cytronics & Melware
*
@ -20,7 +19,7 @@
*/
extern int capi_alloc_rtp(struct capi_pvt *i);
extern void voice_over_ip_profile(struct cc_capi_controller *cp);
extern int capi_write_rtp(struct ast_channel *c, struct ast_frame *f);
extern int capi_write_rtp(struct capi_pvt *i, struct ast_frame *f);
extern struct ast_frame *capi_read_rtp(struct capi_pvt *i, unsigned char *buf, int len);
extern _cstruct capi_rtp_ncpi(struct capi_pvt *i);

845
chan_capi_supplementary.c Normal file
View File

@ -0,0 +1,845 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2005-2007 Cytronics & Melware
*
* Armin Schindler <armin@melware.de>
*
* This program is free software and may be modified and
* distributed under the terms of the GNU Public License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "chan_capi20.h"
#include "chan_capi.h"
#include "chan_capi_supplementary.h"
#include "chan_capi_utils.h"
#define CCBSNR_TYPE_CCBS 1
#define CCBSNR_TYPE_CCNR 2
#define CCBSNR_AVAILABLE 1
#define CCBSNR_REQUESTED 2
#define CCBSNR_ACTIVATED 3
struct ccbsnr_s {
char type;
_cword id;
unsigned int plci;
unsigned int state;
unsigned int handle;
_cword mode;
_cword rbref;
char partybusy;
char context[AST_MAX_CONTEXT];
char exten[AST_MAX_EXTENSION];
int priority;
time_t age;
struct ccbsnr_s *next;
};
static struct ccbsnr_s *ccbsnr_list = NULL;
AST_MUTEX_DEFINE_STATIC(ccbsnr_lock);
/*
* remove too old CCBS/CCNR entries
* (must be called with ccbsnr_lock held)
*/
static void del_old_ccbsnr(void)
{
struct ccbsnr_s *ccbsnr;
struct ccbsnr_s *tmp = NULL;
ccbsnr = ccbsnr_list;
while (ccbsnr) {
if ((ccbsnr->age + 86400) < time(NULL)) {
cc_verbose(1, 1, VERBOSE_PREFIX_3 "CAPI: CCBS/CCNR handle=%d timeout.\n",
ccbsnr->handle);
if (!tmp) {
ccbsnr_list = ccbsnr->next;
} else {
tmp->next = ccbsnr->next;
}
free(ccbsnr);
break;
}
tmp = ccbsnr;
ccbsnr = ccbsnr->next;
}
}
/*
* cleanup CCBS/CCNR ids
*/
void cleanup_ccbsnr(void)
{
struct ccbsnr_s *ccbsnr;
struct ccbsnr_s *tmp = NULL;
cc_mutex_lock(&ccbsnr_lock);
ccbsnr = ccbsnr_list;
while (ccbsnr) {
tmp = ccbsnr;
ccbsnr = ccbsnr->next;
free(tmp);
}
cc_mutex_unlock(&ccbsnr_lock);
}
/*
* return the controller of ccbsnr handle
*/
unsigned int capi_get_ccbsnrcontroller(unsigned int handle)
{
unsigned int contr = 0;
struct ccbsnr_s *ccbsnr;
cc_mutex_lock(&ccbsnr_lock);
ccbsnr = ccbsnr_list;
while (ccbsnr) {
if (ccbsnr->handle == handle) {
contr = (ccbsnr->plci & 0xff);
break;
}
ccbsnr = ccbsnr->next;
}
cc_mutex_unlock(&ccbsnr_lock);
return contr;
}
/*
* a new CCBS/CCNR id was received
*/
static void new_ccbsnr_id(char type, unsigned int plci,
_cword id, struct capi_pvt *i)
{
char buffer[CAPI_MAX_STRING];
struct ccbsnr_s *ccbsnr;
ccbsnr = malloc(sizeof(struct ccbsnr_s));
if (ccbsnr == NULL) {
cc_log(LOG_ERROR, "Unable to allocate CCBS/CCNR struct.\n");
return;
}
memset(ccbsnr, 0, sizeof(struct ccbsnr_s));
ccbsnr->age = time(NULL);
ccbsnr->type = type;
ccbsnr->id = id;
ccbsnr->rbref = 0xdead;
ccbsnr->plci = plci;
ccbsnr->state = CCBSNR_AVAILABLE;
ccbsnr->handle = (id | ((plci & 0xff) << 16) | (type << 28));
if (i->peer) {
snprintf(buffer, CAPI_MAX_STRING-1, "%u", ccbsnr->handle);
pbx_builtin_setvar_helper(i->peer, "CCLINKAGEID", buffer);
} else {
cc_log(LOG_NOTICE, "No peerlink found to set CCBS/CCNR linkage ID.\n");
}
cc_mutex_lock(&ccbsnr_lock);
del_old_ccbsnr();
ccbsnr->next = ccbsnr_list;
ccbsnr_list = ccbsnr;
cc_mutex_unlock(&ccbsnr_lock);
cc_verbose(1, 1, VERBOSE_PREFIX_3
"%s: PLCI=%#x CCBS/CCNR new id=0x%04x handle=%d\n",
i->vname, plci, id, ccbsnr->handle);
/* if the hangup frame was deferred, it can be done now and here */
if (i->whentoqueuehangup) {
i->whentoqueuehangup = 0;
capi_queue_cause_control(i, 1);
}
}
/*
* return the pointer to ccbsnr structure by handle
*/
static struct ccbsnr_s *get_ccbsnr_link(char type, unsigned int plci,
unsigned int handle, _cword ref, unsigned int *state, char *busy)
{
struct ccbsnr_s *ret;
cc_mutex_lock(&ccbsnr_lock);
ret = ccbsnr_list;
while (ret) {
if (((handle) && (ret->handle == handle)) ||
((ref != 0xffff) && (ret->rbref == ref) &&
(ret->type == type) && ((ret->plci & 0xff) == (plci & 0xff)))) {
if (state) {
*state = ret->state;
}
if (busy) {
*busy = ret->partybusy;
}
break;
}
ret = ret->next;
}
cc_mutex_unlock(&ccbsnr_lock);
return ret;
}
/*
* function to tell if CCBSNR is activated
*/
static int ccbsnr_tell_activated(void *data)
{
unsigned int handle = (unsigned int)data;
int ret = 0;
unsigned int state;
if (get_ccbsnr_link(0, 0, handle, 0xffff, &state, NULL) != NULL) {
if (state == CCBSNR_REQUESTED) {
ret = 1;
}
}
return ret;
}
/*
* select CCBS/CCNR id
*/
static unsigned int select_ccbsnr_id(unsigned int id, char type,
char *context, char *exten, int priority)
{
struct ccbsnr_s *ccbsnr;
int ret = 0;
cc_mutex_lock(&ccbsnr_lock);
ccbsnr = ccbsnr_list;
while (ccbsnr) {
if (((ccbsnr->plci & 0xff) == ((id >> 16) & 0xff)) &&
(ccbsnr->id == (id & 0xffff)) &&
(ccbsnr->type == type) &&
(ccbsnr->state == CCBSNR_AVAILABLE)) {
strncpy(ccbsnr->context, context, sizeof(ccbsnr->context) - 1);
strncpy(ccbsnr->exten, exten, sizeof(ccbsnr->exten) - 1);
ccbsnr->priority = priority;
ccbsnr->state = CCBSNR_REQUESTED;
ret = ccbsnr->handle;
cc_verbose(1, 1, VERBOSE_PREFIX_3 "CAPI: request CCBS/NR id=0x%x handle=%d (%s,%s,%d)\n",
id, ret, context, exten, priority);
break;
}
ccbsnr = ccbsnr->next;
}
cc_mutex_unlock(&ccbsnr_lock);
return ret;
}
/*
* a CCBS/CCNR ref was removed
*/
static void del_ccbsnr_ref(unsigned int plci, _cword ref)
{
struct ccbsnr_s *ccbsnr;
struct ccbsnr_s *tmp = NULL;
cc_mutex_lock(&ccbsnr_lock);
ccbsnr = ccbsnr_list;
while (ccbsnr) {
if (((ccbsnr->plci & 0xff) == (plci & 0xff)) &&
(ccbsnr->rbref == ref)) {
if (!tmp) {
ccbsnr_list = ccbsnr->next;
} else {
tmp->next = ccbsnr->next;
}
free(ccbsnr);
cc_verbose(1, 1, VERBOSE_PREFIX_3 "CAPI: PLCI=%#x CCBS/CCNR removed "
"ref=0x%04x\n", plci, ref);
break;
}
tmp = ccbsnr;
ccbsnr = ccbsnr->next;
}
cc_mutex_unlock(&ccbsnr_lock);
}
/*
* return rbref of CCBS/CCNR and delete entry
*/
_cword capi_ccbsnr_take_ref(unsigned int handle)
{
unsigned int plci = 0;
_cword rbref = 0xdead;
struct ccbsnr_s *ccbsnr;
cc_mutex_lock(&ccbsnr_lock);
ccbsnr = ccbsnr_list;
while (ccbsnr) {
if (ccbsnr->handle == handle) {
plci = ccbsnr->plci;
rbref = ccbsnr->rbref;
break;
}
ccbsnr = ccbsnr->next;
}
cc_mutex_unlock(&ccbsnr_lock);
if (rbref != 0xdead) {
del_ccbsnr_ref(plci, rbref);
}
return rbref;
}
/*
* a CCBS/CCNR id was removed
*/
static void del_ccbsnr_id(unsigned int plci, _cword id)
{
struct ccbsnr_s *ccbsnr;
struct ccbsnr_s *tmp = NULL;
unsigned int oldstate;
cc_mutex_lock(&ccbsnr_lock);
ccbsnr = ccbsnr_list;
while (ccbsnr) {
if (((ccbsnr->plci & 0xff) == (plci & 0xff)) &&
(ccbsnr->id == id)) {
oldstate = ccbsnr->state;
if (ccbsnr->state == CCBSNR_AVAILABLE) {
if (!tmp) {
ccbsnr_list = ccbsnr->next;
} else {
tmp->next = ccbsnr->next;
}
free(ccbsnr);
cc_verbose(1, 1, VERBOSE_PREFIX_3 "CAPI: PLCI=%#x CCBS/CCNR removed "
"id=0x%04x state=%d\n", plci, id, oldstate);
} else {
/* just deactivate the linkage id */
ccbsnr->id = 0xdead;
cc_verbose(1, 1, VERBOSE_PREFIX_3 "CAPI: PLCI=%#x CCBS/CCNR erase-only "
"id=0x%04x state=%d\n", plci, id, ccbsnr->state);
}
break;
}
tmp = ccbsnr;
ccbsnr = ccbsnr->next;
}
cc_mutex_unlock(&ccbsnr_lock);
}
/*
* on an activated CCBS, the remote party is now free
*/
static void ccbsnr_remote_user_free(_cmsg *CMSG, char type, unsigned int PLCI, _cword rbref)
{
struct ast_channel *c;
struct ccbsnr_s *ccbsnr;
char handlename[CAPI_MAX_STRING];
int state = AST_STATE_DOWN;
/* XXX start alerting , when answered use CCBS call */
cc_mutex_lock(&ccbsnr_lock);
ccbsnr = ccbsnr_list;
while (ccbsnr) {
if ((ccbsnr->type == type) &&
((ccbsnr->plci & 0xff) == (PLCI & 0xff)) &&
(ccbsnr->rbref == rbref)) {
break;
}
ccbsnr = ccbsnr->next;
}
cc_mutex_unlock(&ccbsnr_lock);
if (!(ccbsnr)) {
cc_log(LOG_ERROR, "CAPI CCBS/CCBR reference not found!\n");
return;
}
snprintf(handlename, CAPI_MAX_STRING-1, "%u", ccbsnr->handle);
#ifdef CC_AST_HAS_EXT_CHAN_ALLOC
c = ast_channel_alloc(0, state, handlename, NULL,
#ifdef CC_AST_HAS_EXT2_CHAN_ALLOC
0, ccbsnr->exten, ccbsnr->context, 0,
#endif
"CCBSNR/%x", ccbsnr->handle);
#else
c = ast_channel_alloc(0);
#endif
if (c == NULL) {
cc_log(LOG_ERROR, "Unable to allocate channel!\n");
return;
}
#ifndef CC_AST_HAS_EXT_CHAN_ALLOC
#ifdef CC_AST_HAS_STRINGFIELD_IN_CHANNEL
ast_string_field_build(c, name, "CCBSNR/%x", ccbsnr->handle);
#else
snprintf(c->name, sizeof(c->name) - 1, "CCBSNR/%x",
ccbsnr->handle);
#endif
#endif
#ifndef CC_AST_HAS_VERSION_1_4
c->type = "CCBS/CCNR";
#endif
c->priority = ccbsnr->priority;
if (c->cid.cid_num) {
free(c->cid.cid_num);
}
c->cid.cid_num = strdup(handlename);
if (c->cid.cid_dnid) {
free(c->cid.cid_dnid);
}
c->cid.cid_dnid = strdup(ccbsnr->exten);
#ifndef CC_AST_HAS_EXT2_CHAN_ALLOC
cc_copy_string(c->context, ccbsnr->context, sizeof(c->context));
cc_copy_string(c->exten, ccbsnr->exten, sizeof(c->exten));
#endif
#ifndef CC_AST_HAS_EXT_CHAN_ALLOC
ast_setstate(c, state);
#endif
if (ast_pbx_start(c)) {
cc_log(LOG_ERROR, "capi CCBS/CCNR: Unable to start pbx!\n");
} else {
cc_verbose(2, 1, VERBOSE_PREFIX_2 "contr%d: started PBX for CCBS/CCNR callback (%s/%s/%d)\n",
PLCI & 0xff, ccbsnr->context, ccbsnr->exten, ccbsnr->priority);
}
}
/*
* send Listen for supplementary to specified controller
*/
void ListenOnSupplementary(unsigned controller)
{
_cmsg CMSG;
MESSAGE_EXCHANGE_ERROR error;
int waitcount = 50;
error = capi_sendf(NULL, 0, CAPI_FACILITY_REQ, controller, get_capi_MessageNumber(),
"w(w(d))",
FACILITYSELECTOR_SUPPLEMENTARY,
0x0001, /* LISTEN */
0x0000079f
);
while (waitcount) {
error = capidev_check_wait_get_cmsg(&CMSG);
if (IS_FACILITY_CONF(&CMSG)) {
break;
}
usleep(30000);
waitcount--;
}
if (!waitcount) {
cc_log(LOG_ERROR,"Unable to supplementary-listen on contr%d (error=0x%x)\n",
controller, error);
}
}
/*
* CAPI FACILITY_IND supplementary services
*/
int handle_facility_indication_supplementary(
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt *i)
{
_cword function;
_cword infoword = 0xffff;
unsigned char length;
_cdword handle;
_cword mode;
_cword rbref;
struct ccbsnr_s *ccbsnrlink;
char partybusy = 0;
int ret = 0;
function = read_capi_word(&FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[1]);
length = FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[3];
if (length >= 2) {
infoword = read_capi_word(&FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[4]);
}
/* first check functions without interface needed */
switch (function) {
case 0x000f: /* CCBS request */
handle = read_capi_dword(&FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[6]);
mode = read_capi_word(&FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[10]);
rbref = read_capi_dword(&FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[12]);
cc_verbose(1, 1, VERBOSE_PREFIX_3 "contr%d: PLCI=%#x CCBS request reason=0x%04x "
"handle=%d mode=0x%x rbref=0x%x\n",
PLCI & 0xff, PLCI, infoword, handle, mode, rbref);
show_capi_info(NULL, infoword);
if ((ccbsnrlink = get_ccbsnr_link(0, 0, handle, 0xffff, NULL, NULL)) == NULL) {
cc_log(LOG_WARNING, "capi ccbs request indication without request!\n");
break;
}
if (infoword == 0) {
/* success */
ccbsnrlink->state = CCBSNR_ACTIVATED;
ccbsnrlink->rbref = rbref;
ccbsnrlink->mode = mode;
} else {
/* error */
ccbsnrlink->state = CCBSNR_AVAILABLE;
}
break;
case 0x0010: /* CCBS deactivate */
handle = read_capi_dword(&FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[6]);
cc_verbose(1, 1, VERBOSE_PREFIX_3 "contr%d: PLCI=%#x CCBS deactivate handle=0x%x reason=0x%x\n",
PLCI & 0xff, PLCI, handle, infoword);
show_capi_info(NULL, infoword);
if ((ccbsnrlink = get_ccbsnr_link(0, 0, handle, 0xffff, NULL, NULL)) == NULL) {
cc_log(LOG_WARNING, "capi ccbs deactivate indication without request!\n");
break;
}
if (infoword == 0) {
/* success */
ccbsnrlink->state = CCBSNR_AVAILABLE;
ccbsnrlink->rbref = 0xdead;
ccbsnrlink->id = 0xdead;
ccbsnrlink->mode = 0;
}
break;
case 0x800d: /* CCBS erase call linkage ID */
cc_verbose(1, 1, VERBOSE_PREFIX_3 "contr%d: PLCI=%#x CCBS/CCNR erase id=0x%04x\n",
PLCI & 0xff, PLCI, infoword);
del_ccbsnr_id(PLCI, infoword);
break;
case 0x800e: /* CCBS status */
rbref = read_capi_word(&FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[6]);
cc_verbose(1, 1, VERBOSE_PREFIX_3 "contr%d: PLCI=%#x CCBS status ref=0x%04x mode=0x%x\n",
PLCI & 0xff, PLCI, rbref, infoword);
if (get_ccbsnr_link(CCBSNR_TYPE_CCBS, PLCI, 0, rbref, NULL, &partybusy) == NULL) {
cc_log(LOG_WARNING, "capi CCBS status reference not found!\n");
}
capi_sendf(NULL, 0, CAPI_FACILITY_RESP, PLCI, HEADER_MSGNUM(CMSG),
"w(w(w))",
FACILITYSELECTOR_SUPPLEMENTARY,
0x800e, /* CCBS status */
(partybusy) ? 0x0000 : 0x0001
);
ret = 1;
break;
case 0x800f: /* CCBS remote user free */
rbref = read_capi_word(&FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[6]);
cc_verbose(1, 1, VERBOSE_PREFIX_3 "contr%d: PLCI=%#x CCBS remote user free ref=0x%04x mode=0x%x\n",
PLCI & 0xff, PLCI, rbref, infoword);
ccbsnr_remote_user_free(CMSG, CCBSNR_TYPE_CCBS, PLCI, rbref);
break;
case 0x8010: /* CCBS B-free */
rbref = read_capi_word(&FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[6]);
cc_verbose(1, 1, VERBOSE_PREFIX_3 "contr%d: PLCI=%#x CCBS B-free ref=0x%04x mode=0x%x\n",
PLCI & 0xff, PLCI, rbref, infoword);
break;
case 0x8011: /* CCBS erase (ref), deactivated by network */
rbref = read_capi_word(&FACILITY_IND_FACILITYINDICATIONPARAMETER(CMSG)[6]);
cc_verbose(1, 1, VERBOSE_PREFIX_3 "contr%d: PLCI=%#x CCBS deactivate ref=0x%04x mode=0x%x\n",
PLCI & 0xff, PLCI, rbref, infoword);
del_ccbsnr_ref(PLCI, rbref);
break;
case 0x8012: /* CCBS stop alerting */
cc_verbose(1, 1, VERBOSE_PREFIX_3 "contr%d: PLCI=%#x CCBS B-free ref=0x%04x\n",
PLCI & 0xff, PLCI, infoword);
break;
}
if (!i) {
cc_verbose(4, 1, "CAPI: FACILITY_IND SUPPLEMENTARY "
"no interface for PLCI=%#x\n", PLCI);
return ret;
}
/* now functions bound to interface */
switch (function) {
case 0x0002: /* HOLD */
if (infoword != 0) {
/* reason != 0x0000 == problem */
i->onholdPLCI = 0;
cc_log(LOG_WARNING, "%s: unable to put PLCI=%#x onhold, REASON = 0x%04x, maybe you need to subscribe for this...\n",
i->vname, PLCI, infoword);
show_capi_info(i, infoword);
} else {
/* reason = 0x0000 == call on hold */
i->state = CAPI_STATE_ONHOLD;
cc_verbose(1, 1, VERBOSE_PREFIX_3 "%s: PLCI=%#x put onhold\n",
i->vname, PLCI);
}
break;
case 0x0003: /* RETRIEVE */
if (infoword != 0) {
cc_log(LOG_WARNING, "%s: unable to retrieve PLCI=%#x, REASON = 0x%04x\n",
i->vname, PLCI, infoword);
show_capi_info(i, infoword);
} else {
i->state = CAPI_STATE_CONNECTED;
i->PLCI = i->onholdPLCI;
i->onholdPLCI = 0;
cc_verbose(1, 1, VERBOSE_PREFIX_3 "%s: PLCI=%#x retrieved\n",
i->vname, PLCI);
cc_start_b3(i);
}
break;
case 0x0006: /* ECT */
cc_verbose(1, 1, VERBOSE_PREFIX_3 "%s: PLCI=%#x ECT Reason=0x%04x\n",
i->vname, PLCI, infoword);
show_capi_info(i, infoword);
break;
case 0x0007: /* 3PTY begin */
cc_verbose(1, 1, VERBOSE_PREFIX_3 "%s: PLCI=%#x 3PTY begin Reason=0x%04x\n",
i->vname, PLCI, infoword);
show_capi_info(i, infoword);
break;
case 0x0008: /* 3PTY end */
cc_verbose(1, 1, VERBOSE_PREFIX_3 "%s: PLCI=%#x 3PTY end Reason=0x%04x\n",
i->vname, PLCI, infoword);
show_capi_info(i, infoword);
break;
case 0x8013: /* CCBS info retain */
cc_verbose(1, 1, VERBOSE_PREFIX_3 "%s: PLCI=%#x CCBS unique id=0x%04x\n",
i->vname, PLCI, infoword);
new_ccbsnr_id(CCBSNR_TYPE_CCBS, PLCI, infoword, i);
break;
case 0x8015: /* CCNR info retain */
cc_verbose(1, 1, VERBOSE_PREFIX_3 "%s: PLCI=%#x CCNR unique id=0x%04x\n",
i->vname, PLCI, infoword);
new_ccbsnr_id(CCBSNR_TYPE_CCNR, PLCI, infoword, i);
break;
case 0x000e: /* CCBS status */
case 0x000f: /* CCBS request */
case 0x800f: /* CCBS remote user free */
case 0x800d: /* CCBS erase call linkage ID */
case 0x8010: /* CCBS B-free */
case 0x8011: /* CCBS erase (ref), deactivated by network */
case 0x8012: /* CCBS stop alerting */
/* handled above */
break;
default:
cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: unhandled FACILITY_IND supplementary function %04x\n",
i->vname, function);
}
return ret;
}
/*
* CAPI FACILITY_CONF supplementary
*/
void handle_facility_confirmation_supplementary(
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt **i)
{
_cword function;
_cword serviceinfo;
char name[64];
if (*i) {
strncpy(name, (*i)->vname, sizeof(name) - 1);
} else {
snprintf(name, sizeof(name) - 1, "contr%d", PLCI & 0xff);
}
function = read_capi_word(&FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[1]);
serviceinfo = read_capi_word(&FACILITY_CONF_FACILITYCONFIRMATIONPARAMETER(CMSG)[4]);
switch(function) {
case 0x0002: /* HOLD */
if (serviceinfo == 0) {
cc_verbose(2, 0, VERBOSE_PREFIX_3 "%s: Call on hold (PLCI=%#x)\n",
name, PLCI);
}
break;
case 0x0003: /* RETRIEVE */
if (serviceinfo == 0) {
cc_verbose(2, 0, VERBOSE_PREFIX_3 "%s: Call retreived (PLCI=%#x)\n",
name, PLCI);
}
break;
case 0x000f: /* CCBS request */
cc_verbose(2, 1, VERBOSE_PREFIX_3 "%s: CCBS request confirmation (0x%04x) (PLCI=%#x)\n",
name, serviceinfo, PLCI);
break;
case 0x0012: /* CCBS call */
cc_verbose(2, 1, VERBOSE_PREFIX_3 "%s: CCBS call confirmation (0x%04x) (PLCI=%#x)\n",
name, serviceinfo, PLCI);
capidev_handle_connection_conf(i, PLCI, FACILITY_CONF_INFO(CMSG), HEADER_MSGNUM(CMSG));
break;
default:
cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: unhandled FACILITY_CONF supplementary function %04x\n",
name, function);
}
}
/*
* capicommand 'ccpartybusy'
*/
int pbx_capi_ccpartybusy(struct ast_channel *c, char *data)
{
char *slinkageid, *yesno;
unsigned int linkid = 0;
struct ccbsnr_s *ccbsnr;
char partybusy = 0;
slinkageid = strsep(&data, "|");
yesno = data;
if (slinkageid) {
linkid = (unsigned int)strtoul(slinkageid, NULL, 0);
}
if ((yesno) && ast_true(yesno)) {
partybusy = 1;
}
cc_mutex_lock(&ccbsnr_lock);
ccbsnr = ccbsnr_list;
while (ccbsnr) {
if (((ccbsnr->plci & 0xff) == ((linkid >> 16) & 0xff)) &&
(ccbsnr->id == (linkid & 0xffff))) {
ccbsnr->partybusy = partybusy;
cc_verbose(1, 1, VERBOSE_PREFIX_3 "CAPI: CCBS/NR id=0x%x busy set to %d\n",
linkid, partybusy);
break;
}
ccbsnr = ccbsnr->next;
}
cc_mutex_unlock(&ccbsnr_lock);
return 0;
}
/*
* capicommand 'ccbsstop'
*/
int pbx_capi_ccbsstop(struct ast_channel *c, char *data)
{
char *slinkageid;
unsigned int linkid = 0;
unsigned int handle = 0;
MESSAGE_EXCHANGE_ERROR error;
_cword ref = 0xdead;
struct ccbsnr_s *ccbsnr;
slinkageid = data;
if (slinkageid) {
linkid = (unsigned int)strtoul(slinkageid, NULL, 0);
}
cc_verbose(3, 1, VERBOSE_PREFIX_3 "capi ccbsstop: '%d'\n",
linkid);
cc_mutex_lock(&ccbsnr_lock);
ccbsnr = ccbsnr_list;
while (ccbsnr) {
if (((ccbsnr->plci & 0xff) == ((linkid >> 16) & 0xff)) &&
(ccbsnr->id == (linkid & 0xffff)) &&
(ccbsnr->type == CCBSNR_TYPE_CCBS) &&
(ccbsnr->state == CCBSNR_ACTIVATED)) {
ref = ccbsnr->rbref;
handle = ccbsnr->handle;
break;
}
ccbsnr = ccbsnr->next;
}
cc_mutex_unlock(&ccbsnr_lock);
if (ref != 0xdead) {
error = capi_sendf(NULL, 0, CAPI_FACILITY_REQ, (linkid >> 16) & 0xff,
get_capi_MessageNumber(),
"w(w(dw))",
FACILITYSELECTOR_SUPPLEMENTARY,
0x0010, /* CCBS deactivate */
handle, /* handle */
ref /* CCBS reference */
);
} else {
cc_verbose(3, 1, VERBOSE_PREFIX_3, "capi ccbsstop: linkid %d not found in table.\n",
linkid);
}
return 0;
}
/*
* capicommand 'ccbs'
*/
int pbx_capi_ccbs(struct ast_channel *c, char *data)
{
char *slinkageid, *context, *exten, *priority;
unsigned int linkid = 0;
unsigned int handle, a;
char *result = "ERROR";
char *goodresult = "ACTIVATED";
MESSAGE_EXCHANGE_ERROR error;
unsigned int ccbsnrstate;
slinkageid = strsep(&data, "|");
context = strsep(&data, "|");
exten = strsep(&data, "|");
priority = data;
if (slinkageid) {
linkid = (unsigned int)strtoul(slinkageid, NULL, 0);
}
if ((!context) || (!exten) || (!priority)) {
cc_log(LOG_WARNING, "capi ccbs requires <context>|<exten>|<priority>\n");
return -1;
}
cc_verbose(3, 1, VERBOSE_PREFIX_3 "capi ccbs: '%d' '%s' '%s' '%s'\n",
linkid, context, exten, priority);
handle = select_ccbsnr_id(linkid, CCBSNR_TYPE_CCBS,
context, exten, (int)strtol(priority, NULL, 0));
if (handle > 0) {
error = capi_sendf(NULL, 0, CAPI_FACILITY_REQ, (linkid >> 16) & 0xff,
get_capi_MessageNumber(),
"w(w(dw))",
FACILITYSELECTOR_SUPPLEMENTARY,
0x000f, /* CCBS request */
handle, /* handle */
(linkid & 0xffff) /* CCBS linkage ID */
);
for (a = 0; a < 7; a++) {
/* Wait for CCBS request indication */
if (ast_safe_sleep_conditional(c, 500, ccbsnr_tell_activated,
(void *)handle) != 0) {
/* we got a hangup */
cc_verbose(3, 1,
VERBOSE_PREFIX_3 "capi ccbs: hangup.\n");
break;
}
}
if (get_ccbsnr_link(0, 0, handle, 0xffff, &ccbsnrstate, NULL) != NULL) {
if (ccbsnrstate == CCBSNR_ACTIVATED) {
result = goodresult;
}
}
} else {
cc_verbose(3, 1, VERBOSE_PREFIX_3, "capi ccbs: linkid %d not found in table.\n",
linkid);
}
pbx_builtin_setvar_helper(c, "CCBSSTATUS", result);
return 0;
}

32
chan_capi_supplementary.h Normal file
View File

@ -0,0 +1,32 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2006-2007 Cytronics & Melware
*
* Armin Schindler <armin@melware.de>
*
* This program is free software and may be modified and
* distributed under the terms of the GNU Public License.
*/
#ifndef _PBX_CAPI_SUPP_H
#define _PBX_CAPI_SUPP_H
/*
* prototypes
*/
extern void ListenOnSupplementary(unsigned controller);
extern int handle_facility_indication_supplementary(
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt *i);
extern void handle_facility_confirmation_supplementary(
_cmsg *CMSG, unsigned int PLCI, unsigned int NCCI, struct capi_pvt **i);
extern int pbx_capi_ccbs(struct ast_channel *c, char *data);
extern int pbx_capi_ccbsstop(struct ast_channel *c, char *data);
extern int pbx_capi_ccpartybusy(struct ast_channel *c, char *data);
extern void cleanup_ccbsnr(void);
extern unsigned int capi_get_ccbsnrcontroller(unsigned int handle);
extern _cword capi_ccbsnr_take_ref(unsigned int handle);
#endif

1252
chan_capi_utils.c Normal file

File diff suppressed because it is too large Load Diff

59
chan_capi_utils.h Normal file
View File

@ -0,0 +1,59 @@
/*
* (CAPI*)
*
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2006-2007 Cytronics & Melware
*
* Armin Schindler <armin@melware.de>
*
* This program is free software and may be modified and
* distributed under the terms of the GNU Public License.
*/
#ifndef _PBX_CAPI_UTILS_H
#define _PBX_CAPI_UTILS_H
/*
* prototypes
*/
extern int capidebug;
extern char *emptyid;
extern void cc_verbose(int o_v, int c_d, char *text, ...);
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_plci(unsigned int plci);
extern MESSAGE_EXCHANGE_ERROR capi_wait_conf(struct capi_pvt *i, unsigned short wCmd);
extern MESSAGE_EXCHANGE_ERROR capidev_check_wait_get_cmsg(_cmsg *CMSG);
extern char *capi_info_string(unsigned int info);
extern void show_capi_info(struct capi_pvt *i, _cword info);
extern unsigned capi_ListenOnController(unsigned int CIPmask, unsigned controller);
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 int cc_add_peer_link_id(struct ast_channel *c);
extern struct ast_channel *cc_get_peer_link_id(const char *p);
extern void capi_remove_nullif(struct capi_pvt *i);
extern struct capi_pvt *capi_mknullif(struct ast_channel *c, unsigned long controllermask);
extern int capi_create_reader_writer_pipe(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);
#define capi_number(data, strip) \
capi_number_func(data, strip, alloca(AST_MAX_EXTENSION))
typedef struct capi_prestruct_s {
unsigned short wLen;
unsigned char *info;
} capi_prestruct_t;
/*
* Eicon's capi_sendf() function to create capi messages easily
* and send this message.
* Copyright by Eicon Networks / Dialogic
*/
extern MESSAGE_EXCHANGE_ERROR capi_sendf(
struct capi_pvt *capii, int waitconf,
_cword command, _cdword Id, _cword Number, char * format, ...);
#endif

View File

@ -5,7 +5,7 @@
# Script to create config.h for compatibility with
# different asterisk versions.
#
# (C) 2005 Cytronics & Melware
# (C) 2005-2007 Cytronics & Melware
# Armin Schindler <armin@melware.de>
#
@ -83,6 +83,15 @@ else
echo " * no extended ast_channel_alloc"
fi
if grep -q "ast_channel_alloc.*amaflag" $INCLUDEDIR/channel.h; then
echo "#define CC_AST_HAS_EXT2_CHAN_ALLOC" >>$CONFIGFILE
echo " * found second extended ast_channel_alloc"
else
echo "#undef CC_AST_HAS_EXT2_CHAN_ALLOC" >>$CONFIGFILE
echo " * no second extended ast_channel_alloc"
fi
if grep -q "send_digit_end.*duration" $INCLUDEDIR/channel.h; then
echo "#define CC_AST_HAS_SEND_DIGIT_END_DURATION" >>$CONFIGFILE
echo " * found send_digit_end with duration"

View File

@ -1,151 +0,0 @@
#define PBX_IS_OPBX
#undef CC_AST_HAS_STRINGFIELD_IN_CHANNEL
#define CC_AST_CUSTOM_FUNCTION
#undef CC_AST_HAS_INDICATE_DATA
#define ast_channel opbx_channel
#define ast_frame opbx_frame
#define ast_channel_alloc opbx_channel_alloc
#define ast_channel_free opbx_channel_free
#define ast_channel_tech opbx_channel_tech
#define ast_change_name opbx_change_name
#define ast_setstate opbx_setstate
#define ast_exists_extension opbx_exists_extension
#define ast_canmatch_extension opbx_canmatch_extension
#define ast_getformatname_multiple opbx_getformatname_multiple
#define ast_getformatname opbx_getformatname
#define ast_hostent opbx_hostent
#define ast_gethostbyname opbx_gethostbyname
#define ast_rtp_new_with_bindaddr opbx_rtp_new_with_bindaddr
#define ast_rtp_get_us opbx_rtp_get_us
#define ast_rtp_set_peer opbx_rtp_set_peer
#define ast_inet_ntoa opbx_inet_ntoa
#define ast_rtp_write opbx_rtp_write
#define ast_rtp_read opbx_rtp_read
#define ast_rtp_fd opbx_rtp_fd
#define ast_rtp_destroy opbx_rtp_destroy
#define ast_rtp opbx_rtp
#define ast_pbx_verbose opbx_verbose
#define ast_set_read_format opbx_set_read_format
#define ast_set_write_format opbx_set_write_format
#define ast_channel_unregister opbx_channel_unregister
#define ast_channel_register opbx_channel_register
#define ast_cli_unregister opbx_cli_unregister
#define ast_cli_register opbx_cli_register
#define ast_cli_entry opbx_cli_entry
#define ast_cli opbx_cli
#define ast_strdupa opbx_strdupa
#define ast_check_hangup opbx_check_hangup
#define ast_hangup opbx_hangup
#define ast_softhangup opbx_softhangup
#define ast_pbx_start opbx_pbx_start
#define ast_queue_frame opbx_queue_frame
#define ast_frame_dump opbx_frame_dump
#define ast_queue_hangup opbx_queue_hangup
#define ast_async_goto opbx_async_goto
#define ast_waitfor_n opbx_waitfor_n
#define ast_pthread_create opbx_pthread_create
#define ast_unregister_application opbx_unregister_application
#define ast_custom_function_unregister opbx_custom_function_unregister
#define ast_custom_function_register opbx_custom_function_register
#define ast_custom_function opbx_custom_function
#define ast_register_application opbx_register_application
#define ast_config_destroy opbx_config_destroy
#define ast_config_load opbx_config_load
#define ast_config opbx_config
#define ast_smoother_feed opbx_smoother_feed
#define ast_smoother_read opbx_smoother_read
#define ast_smoother_reset opbx_smoother_reset
#define ast_smoother_new opbx_smoother_new
#define ast_smoother_free opbx_smoother_free
#define ast_smoother opbx_smoother
#define ast_category_browse opbx_category_browse
#define ast_variable_browse opbx_variable_browse
#define ast_variable opbx_variable
#define ast_parse_allow_disallow opbx_parse_allow_disallow
#define ast_true opbx_true
#define ast_false opbx_false
#define ast_frfree opbx_frfree
#define ast_strlen_zero opbx_strlen_zero
#define ast_dsp_process opbx_dsp_process
#define ast_dsp_free opbx_dsp_free
#define ast_dsp_new opbx_dsp_new
#define ast_dsp_set_features opbx_dsp_set_features
#define ast_dsp_digitmode opbx_dsp_digitmode
#define ast_dsp opbx_dsp
#define ast_sendtext opbx_sendtext
#define ast_get_group opbx_get_group
#define ast_group_t opbx_group_t
#define ast_write opbx_write
#define ast_read opbx_read
#define ast_best_codec opbx_best_codec
#define ast_update_use_count opbx_update_use_count
#define ast_codec_pref opbx_codec_pref
#define ast_mutex_t opbx_mutex_t
#define ast_mutex_init opbx_mutex_init
#define ast_mutex_lock opbx_mutex_lock
#define ast_mutex_unlock opbx_mutex_unlock
#define ast_mutex_destroy opbx_mutex_destroy
#define ast_cond_t opbx_cond_t
#define ast_cond_init opbx_cond_init
#define ast_cond_destroy opbx_cond_destroy
#define ast_cond_signal opbx_cond_signal
#define ast_cond_broadcast opbx_cond_broadcast
#define ast_cond_wait opbx_cond_wait
#define ast_cond_timedwait opbx_cond_timedwait
#define ast_log opbx_log
#define ast_verbose opbx_verbose
#define ast_copy_string opbx_copy_string
#define AST_FORMAT_ALAW OPBX_FORMAT_ALAW
#define AST_FORMAT_ULAW OPBX_FORMAT_ULAW
#define AST_FORMAT_GSM OPBX_FORMAT_GSM
#define AST_FORMAT_G723_1 OPBX_FORMAT_G723_1
#define AST_FORMAT_G726 OPBX_FORMAT_G726
#define AST_FORMAT_G729A OPBX_FORMAT_G729A
#define AST_FRAME_VOICE OPBX_FRAME_VOICE
#define AST_FRAME_CONTROL OPBX_FRAME_CONTROL
#define AST_FRAME_DTMF OPBX_FRAME_DTMF
#define AST_FRAME_NULL OPBX_FRAME_NULL
#define AST_FRAME_TEXT OPBX_FRAME_TEXT
#define AST_CONTROL_RINGING OPBX_CONTROL_RINGING
#define AST_CONTROL_PROCEEDING OPBX_CONTROL_PROCEEDING
#define AST_CONTROL_BUSY OPBX_CONTROL_BUSY
#define AST_CONTROL_CONGESTION OPBX_CONTROL_CONGESTION
#define AST_CONTROL_PROGRESS OPBX_CONTROL_PROGRESS
#define AST_CONTROL_HOLD OPBX_CONTROL_HOLD
#define AST_CONTROL_UNHOLD OPBX_CONTROL_UNHOLD
#define AST_CONTROL_ANSWER OPBX_CONTROL_ANSWER
#define AST_CONTROL_HANGUP OPBX_CONTROL_HANGUP
#define AST_STATE_RING OPBX_STATE_RING
#define AST_STATE_UP OPBX_STATE_UP
#define AST_STATE_DOWN OPBX_STATE_DOWN
#define AST_STATE_DIALING OPBX_STATE_DIALING
#define AST_STATE_RESERVED OPBX_STATE_RESERVED
#define AST_FRIENDLY_OFFSET OPBX_FRIENDLY_OFFSET
#define AST_SOFTHANGUP_DEV OPBX_SOFTHANGUP_DEV
#define AST_DEVICE_UNKNOWN OPBX_DEVICE_UNKNOWN
#define AST_CHANNEL_NAME OPBX_CHANNEL_NAME
#define AST_BRIDGE_DTMF_CHANNEL_0 OPBX_BRIDGE_DTMF_CHANNEL_0
#define AST_BRIDGE_DTMF_CHANNEL_1 OPBX_BRIDGE_DTMF_CHANNEL_1
#define AST_MAX_EXTENSION OPBX_MAX_EXTENSION
#define AST_CAUSE_INVALID_NUMBER_FORMAT OPBX_CAUSE_INVALID_NUMBER_FORMAT
#define AST_CAUSE_REQUESTED_CHAN_UNAVAIL OPBX_CAUSE_REQUESTED_CHAN_UNAVAIL
#define AST_CAUSE_NORMAL_CIRCUIT_CONGESTION OPBX_CAUSE_NORMAL_CIRCUIT_CONGESTION
#define AST_CAUSE_NO_USER_RESPONSE OPBX_CAUSE_NO_USER_RESPONSE
#define AST_CAUSE_NO_ANSWER OPBX_CAUSE_NO_ANSWER
#define AST_CAUSE_USER_BUSY OPBX_CAUSE_USER_BUSY
#define AST_CAUSE_NORMAL_CLEARING OPBX_CAUSE_NORMAL_CLEARING
#define AST_MUTEX_DEFINE_STATIC OPBX_MUTEX_DEFINE_STATIC
#define AST_MUTEX_DEFINE_EXPORTED OPBX_MUTEX_DEFINE_EXPORTED
#define CC_BRIDGE_RETURN enum opbx_bridge_result
#define AST_BRIDGE_COMPLETE OPBX_BRIDGE_COMPLETE
#define AST_BRIDGE_FAILED OPBX_BRIDGE_FAILED
#define AST_BRIDGE_FAILED_NOWARN OPBX_BRIDGE_FAILED_NOWARN
#define AST_BRIDGE_RETRY OPBX_BRIDGE_RETRY

BIN
preparser

Binary file not shown.

1653
xlaw.c Normal file

File diff suppressed because it is too large Load Diff

1657
xlaw.h

File diff suppressed because it is too large Load Diff