[Asterisk-Users] chan_capi patch: app_capiFax modifications

Andrew Yager andrew at rwts.com.au
Sat Jan 22 20:43:54 MST 2005


Hi,

Since Carl has kindly provided us with fax support for CAPI based 
cards, we have been using it with much success. Today I have modified 
app_capiFax so that it now supports a dynamic CSID. The following 
example uses the DNID created by chan_capi on an AVM Fritz! card.

  * Receive a fax with CAPI API.
  * Usage : capiAnswerFax2(path_output_file.SFF|stationID)
  *
  * This function can be called even after a regular Answer (voice mode),
  * the channel will be changed to Fax Mode.
  *
  * Example of use :
  * line number 123, play something, if a fax tone is detected, handle it
  * line number 124, answer directly in fax mode
  *
  * [incoming]
  * exten => 123,1,Answer()
  * exten => 123,2,BackGround(jpop)
  * exten => 124,1,Goto(handle_fax,s,1)
  * exten => fax,1,Goto(handle_fax,s,1)
  *
  * [handle_fax]
  * exten => s,1,capiAnswerFax2(/tmp/${UNIQUEID}|1234567890) // Answer 
fax with CSID 1234567890
  * exten => s,2,Hangup()
  * exten => h,1,deadagi,fax.php // Run SFF2TIFF and mail it.
  *

Please note that this can only be used with the capi fax patch 
available at http://www.mlkj.net/asterisk/chan_capi-0.3.5-patch.tar.bz2 
. You will also need to modify the Makefile and add app_capiFax2.so to 
the object list.

Andrew

_________________________
Andrew Yager
Real World Technology Solutions
Real People, Real SolUtions (tm)
ph: (02) 9563 4840 fax: (02) 9563 4848
mob: 0405 15 2568
http://www.rwts.com.au/
_________________________
-------------- next part --------------
/*
 * (CAPI*)
 *
 * Receive a fax with CAPI API.
 * Usage : capiAnswerFax2(path_output_file.SFF,stationID)
 *
 * This function can be called even after a regular Answer (voice mode),
 * the channel will be changed to Fax Mode.
 * 
 * Example of use :
 * line number 123, play something, if a fax tone is detected, handle it
 * line number 124, answer directly in fax mode
 *
 * [incoming]
 * exten => 123,1,Answer()
 * exten => 123,2,BackGround(jpop)
 * exten => 124,1,Goto(handle_fax,s,1)
 * exten => fax,1,Goto(handle_fax,s,1)
 *
 * [handle_fax]
 * exten => s,1,capiAnswerFax(/tmp/${UNIQUEID},${DNID})
 * exten => s,2,Hangup()
 * exten => h,1,deadagi,fax.php // Run SFF2TIFF and mail it.
 *
 * An implementation of Common ISDN API 2.0 for Asterisk
 *
 * This program is free software and may be modified and 
 * distributed under the terms of the GNU Public License.
 */

#include <asterisk/file.h>
#include <asterisk/logger.h>
#include <asterisk/channel.h>
#include <asterisk/channel_pvt.h>
#include <asterisk/logger.h>
#include <asterisk/options.h>
#include <asterisk/pbx.h>
#include <asterisk/module.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include <pthread.h>
#include <linux/capi.h>
#include <capi20.h>
#include "chan_capi_pvt.h"
#include "chan_capi_app.h"

/* FAX Resolutions */
#define FAX_STANDARD_RESOLUTION 0
#define FAX_HIGH_RESOLUTION     1

/* FAX Formats */
#define FAX_SFF_FORMAT                  0
#define FAX_PLAIN_FORMAT                1
#define FAX_PCX_FORMAT                  2
#define FAX_DCX_FORMAT                  3
#define FAX_TIFF_FORMAT                 4
#define FAX_ASCII_FORMAT                5
#define FAX_EXTENDED_ASCII_FORMAT       6
#define FAX_BINARY_FILE_TRANSFER_FORMAT 7

typedef struct fax3proto3 {
        unsigned char len;
        unsigned short resolution __attribute__ ((packed));
        unsigned short format __attribute__ ((packed));
        unsigned char Infos[100] __attribute__ ((packed));
} B3_PROTO_FAXG3;

static char *tdesc = "(CAPI*) Receive Faxes.";
static char *app = "capiAnswerFax2";
static char *synopsis = "Answer Fax with CAPI (Allow Station ID Setting)";
STANDARD_LOCAL_USER;

LOCAL_USER_DECL;

void SetupB3Config(B3_PROTO_FAXG3  *B3conf, int FAX_Format, char* stationID) {
    int len1;
    int len2;
    char *headLine  = "CAPI FAXServer";

    B3conf->resolution = FAX_HIGH_RESOLUTION;
    B3conf->format = (unsigned short)FAX_Format;
    len1 = strlen(stationID);
    B3conf->Infos[0] = (unsigned char)len1;
    strcpy((char *)&B3conf->Infos[1], stationID);
    len2 = strlen(headLine);
    B3conf->Infos[len1 + 1] = (unsigned char)len2;
    strcpy((char *)&B3conf->Infos[len1 + 2], headLine);
    B3conf->len = (unsigned char)(2 * sizeof(unsigned short) + len1 + len2 + 2);
}
static int capi_change_bchan_fax(struct ast_channel *c, char* stationID) {
	struct ast_capi_pvt *i = c->pvt->pvt;
	MESSAGE_EXCHANGE_ERROR  error;
	_cmsg                   CMSG;
	B3_PROTO_FAXG3  B3conf;

	SetupB3Config(&B3conf, FAX_SFF_FORMAT, stationID); // Format ignored by eicon cards
	
        DISCONNECT_B3_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0);
        DISCONNECT_B3_REQ_NCCI(&CMSG) = i->NCCI;

        if ((error = _capi_put_cmsg(&CMSG)) != 0) {
            ast_log(LOG_ERROR, "error sending DISCONNECT_B3_REQ NCCI=%#x\n",i->NCCI);
        } else {
            if (option_verbose > 5) {
                ast_verbose(VERBOSE_PREFIX_4 "sent DISCONNECT_B3_REQ NCCI=%#x\n",i->NCCI);
            }
        }
        // wait for the B3 layer to go down
        while (i->state != CAPI_STATE_CONNECTED) {
            usleep(10000);
        }

	SELECT_B_PROTOCOL_REQ_HEADER(&CMSG, ast_capi_ApplID, ast_capi_MessageNumber++, 0);
	SELECT_B_PROTOCOL_REQ_PLCI(&CMSG) = i->PLCI;
	SELECT_B_PROTOCOL_REQ_B1PROTOCOL(&CMSG) = 4;
	SELECT_B_PROTOCOL_REQ_B2PROTOCOL(&CMSG) = 4;
	SELECT_B_PROTOCOL_REQ_B3PROTOCOL(&CMSG) = 4;
	SELECT_B_PROTOCOL_REQ_B1CONFIGURATION(&CMSG) = NULL;
	SELECT_B_PROTOCOL_REQ_B2CONFIGURATION(&CMSG) = NULL;
	SELECT_B_PROTOCOL_REQ_B3CONFIGURATION(&CMSG) = (_cstruct)&B3conf;

	if ((error = _capi_put_cmsg(&CMSG)) != 0)
		ast_log(LOG_WARNING, "capiAnswerFax CAPI SELECT_B_PROTOCOL put_cmsg error\n");
	return 0;
}

static int capi_answer_fax(struct ast_channel *c, char* stationID) {
    struct ast_capi_pvt *i = c->pvt->pvt; 
    MESSAGE_EXCHANGE_ERROR  error;
    _cmsg                   CMSG;
    char buf[AST_MAX_EXTENSION];
    char *dnid;
    B3_PROTO_FAXG3  B3conf;
    
    if (i->isdnmode && (strlen(i->incomingmsn)<strlen(i->dnid)))
        dnid = i->dnid + strlen(i->incomingmsn);
    else
        dnid = i->dnid;

    SetupB3Config(&B3conf, FAX_SFF_FORMAT, stationID); // Format ignored by eicon cards

    CONNECT_RESP_HEADER(&CMSG, ast_capi_ApplID, i->MessageNumber, 0);
    CONNECT_RESP_PLCI(&CMSG) = i->PLCI;
    CONNECT_RESP_REJECT(&CMSG) = 0;
    buf[0] = strlen(dnid)+2;
    buf[1] = 0x0;
    buf[2] = 0x80;
    strncpy(&buf[3],dnid,sizeof(buf)-4);
    CONNECT_RESP_CONNECTEDNUMBER(&CMSG) = buf;
    CONNECT_RESP_CONNECTEDSUBADDRESS(&CMSG) = NULL;
    CONNECT_RESP_LLC(&CMSG) = NULL;
    CONNECT_RESP_B1PROTOCOL(&CMSG) = 4; // T.30 modem for Group 3 fax
    CONNECT_RESP_B2PROTOCOL(&CMSG) = 4; // T.30 for Group 3 fax
    CONNECT_RESP_B3PROTOCOL(&CMSG) = 4; // T.30 for Group 3 fax
    CONNECT_RESP_B3CONFIGURATION(&CMSG) = (_cstruct)&B3conf;
 
    if (option_verbose > 3)
        ast_verbose(VERBOSE_PREFIX_3 "CAPI Answering in fax mode for MSN %s\n", dnid);
    if ((error = _capi_put_cmsg(&CMSG)) != 0) {
	ast_log(LOG_WARNING, "capiAnswerFax CAPI put_cmsg error\n");
        return -1;
    } else {
        if (option_verbose > 5) {
            if (capidebug)
                ast_verbose(VERBOSE_PREFIX_4 "sent CONNECT_RESP PLCI = %#x DNID = %s\n",i->PLCI,i->dnid);
        }
    }

    i->state = CAPI_STATE_ANSWERING;
    i->doB3 = AST_CAPI_B3_DONT;
    i->outgoing = 0;
    i->earlyB3 = -1;

    return 0;
}

static int capianswerfax_exec(struct ast_channel *chan, void *data)
{
	int res=0;
	struct localuser *u;
	char *vdata, *fname, *stationID;
	struct ast_capi_pvt *i = chan->pvt->pvt;

	if (!data) { /* no data implies no filename or anything is present */
		ast_log(LOG_WARNING, "capiAnswerFax requires an argument (filename)\n");
		return -1;
	}
	vdata = ast_strdupa(data);

	LOCAL_USER_ADD(u);
	fname = vdata;
	if (fname) {
		stationID = strchr(vdata, '|');
		if (stationID) {
			*stationID='\0';
			stationID++;
		} else  stationID = "00000000";
	} else stationID = "00000000";

	if (strcasecmp("CAPI", chan->type) == 0) {
		i->fFax = fopen(vdata, "wb");
		if(i->fFax == NULL) {
			ast_log(LOG_WARNING, "capiAnswerFax can't create the output file (%s)\n", strerror(errno));
			res = -1;
		} else {
			if(i->state != CAPI_STATE_BCONNECTED)
				capi_answer_fax(chan, stationID);
			else
				capi_change_bchan_fax(chan, stationID);
			while (i->state != CAPI_STATE_DISCONNECTED) {
				sleep(1);
			}
		}
	} else {
		ast_log(LOG_WARNING, "capiFax only works on CAPI channels, check your extensions.conf!\n");
		res = -1;
	}
	LOCAL_USER_REMOVE(u);
	return res;
}

int unload_module(void)
{
	STANDARD_HANGUP_LOCALUSERS;
	return ast_unregister_application(app);
}

int load_module(void)
{
	return ast_register_application(app, capianswerfax_exec,synopsis,tdesc);
}

char *description(void)
{
	return tdesc;
}

int usecount(void)
{
	int res;
	STANDARD_USECOUNT(res);
	return res;
}

char *key()
{
	return ASTERISK_GPL_KEY;
}


More information about the asterisk-users mailing list