/*
 * libpri: An implementation of Primary Rate ISDN
 *
 * Written by Mark Spencer <markster@linux-support.net>
 *
 * Copyright (C) 2001, Linux Support Services, Inc.
 * All Rights Reserved.
 * Copyright (C) 2003-2006 Junghanns.NET GmbH
 * Klaus-Peter Junghanns <kpj@junghanns.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */
 
#include "compat.h"
#include "libpri.h"
#include "pri_internal.h"
#include "pri_q921.h"
#include "pri_q931.h"
#include "pri_facility.h"

#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>

#define MAX_MAND_IES 10

struct msgtype {
	int msgnum;
	char *name;
	int mandies[MAX_MAND_IES];
};

struct msgtype msgs[] = {
	/* Call establishment messages */
	{ Q931_ALERTING, "ALERTING" },
	{ Q931_CALL_PROCEEDING, "CALL PROCEEDING" },
	{ Q931_CONNECT, "CONNECT" },
	{ Q931_CONNECT_ACKNOWLEDGE, "CONNECT ACKNOWLEDGE" },
	{ Q931_PROGRESS, "PROGRESS", { Q931_PROGRESS_INDICATOR } },
	{ Q931_SETUP, "SETUP", { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT } },
	{ Q931_SETUP_ACKNOWLEDGE, "SETUP ACKNOWLEDGE" },
	
	/* Call disestablishment messages */
	{ Q931_DISCONNECT, "DISCONNECT", { Q931_CAUSE } },
	{ Q931_RELEASE, "RELEASE" },
	{ Q931_RELEASE_COMPLETE, "RELEASE COMPLETE" },
	{ Q931_RESTART, "RESTART", { Q931_RESTART_INDICATOR } },
	{ Q931_RESTART_ACKNOWLEDGE, "RESTART ACKNOWLEDGE", { Q931_RESTART_INDICATOR } },

	/* Miscellaneous */
	{ Q931_STATUS, "STATUS", { Q931_CAUSE, Q931_CALL_STATE } },
	{ Q931_STATUS_ENQUIRY, "STATUS ENQUIRY" },
	{ Q931_USER_INFORMATION, "USER_INFORMATION" },
	{ Q931_SEGMENT, "SEGMENT" },
	{ Q931_CONGESTION_CONTROL, "CONGESTION CONTROL" },
	{ Q931_INFORMATION, "INFORMATION" },
	{ Q931_FACILITY, "FACILITY" },
	{ Q931_NOTIFY, "NOTIFY", { Q931_IE_NOTIFY_IND } },

	/* Call Management */
	{ Q931_HOLD, "HOLD" },
	{ Q931_HOLD_ACKNOWLEDGE, "HOLD ACKNOWLEDGE" },
	{ Q931_HOLD_REJECT, "HOLD REJECT" },
	{ Q931_RETRIEVE, "RETRIEVE" },
	{ Q931_RETRIEVE_ACKNOWLEDGE, "RETRIEVE ACKNOWLEDGE" },
	{ Q931_RETRIEVE_REJECT, "RETRIEVE REJECT" },
	{ Q931_RESUME, "RESUME" },
	{ Q931_RESUME_ACKNOWLEDGE, "RESUME ACKNOWLEDGE", { Q931_CHANNEL_IDENT } },
	{ Q931_RESUME_REJECT, "RESUME REJECT", { Q931_CAUSE } },
	{ Q931_SUSPEND, "SUSPEND" },
	{ Q931_SUSPEND_ACKNOWLEDGE, "SUSPEND ACKNOWLEDGE" },
	{ Q931_SUSPEND_REJECT, "SUSPEND REJECT" },

	/* Maintenance */
	{ NATIONAL_SERVICE, "SERVICE" },
	{ NATIONAL_SERVICE_ACKNOWLEDGE, "SERVICE ACKNOWLEDGE" },
};

struct msgtype causes[] = {
	{ PRI_CAUSE_UNALLOCATED, "Unallocated (unassigned) number" },
	{ PRI_CAUSE_NO_ROUTE_TRANSIT_NET, "No route to specified transmit network" },
	{ PRI_CAUSE_NO_ROUTE_DESTINATION, "No route to destination" },
	{ PRI_CAUSE_CHANNEL_UNACCEPTABLE, "Channel unacceptable" },
	{ PRI_CAUSE_CALL_AWARDED_DELIVERED, "Call awarded and being delivered in an established channel" },
	{ PRI_CAUSE_NORMAL_CLEARING, "Normal Clearing" },
	{ PRI_CAUSE_USER_BUSY, "User busy" },
	{ PRI_CAUSE_NO_USER_RESPONSE, "No user responding" },
	{ PRI_CAUSE_NO_ANSWER, "User alerting, no answer" },
	{ PRI_CAUSE_CALL_REJECTED, "Call Rejected" },
	{ PRI_CAUSE_NUMBER_CHANGED, "Number changed" },
	{ PRI_CAUSE_DESTINATION_OUT_OF_ORDER, "Destination out of order" },
	{ PRI_CAUSE_INVALID_NUMBER_FORMAT, "Invalid number format" },
	{ PRI_CAUSE_FACILITY_REJECTED, "Facility rejected" },
	{ PRI_CAUSE_RESPONSE_TO_STATUS_ENQUIRY, "Response to STATus ENQuiry" },
	{ PRI_CAUSE_NORMAL_UNSPECIFIED, "Normal, unspecified" },
	{ PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION, "Circuit/channel congestion" },
	{ PRI_CAUSE_NETWORK_OUT_OF_ORDER, "Network out of order" },
	{ PRI_CAUSE_NORMAL_TEMPORARY_FAILURE, "Temporary failure" },
	{ PRI_CAUSE_SWITCH_CONGESTION, "Switching equipment congestion" },
	{ PRI_CAUSE_ACCESS_INFO_DISCARDED, "Access information discarded" },
	{ PRI_CAUSE_REQUESTED_CHAN_UNAVAIL, "Requested channel not available" },
	{ PRI_CAUSE_PRE_EMPTED, "Pre-empted" },
	{ PRI_CAUSE_FACILITY_NOT_SUBSCRIBED, "Facility not subscribed" },
	{ PRI_CAUSE_OUTGOING_CALL_BARRED, "Outgoing call barred" },
	{ PRI_CAUSE_INCOMING_CALL_BARRED, "Incoming call barred" },
	{ PRI_CAUSE_BEARERCAPABILITY_NOTAUTH, "Bearer capability not authorized" },
	{ PRI_CAUSE_BEARERCAPABILITY_NOTAVAIL, "Bearer capability not available" },
	{ PRI_CAUSE_BEARERCAPABILITY_NOTIMPL, "Bearer capability not implemented" },
	{ PRI_CAUSE_CHAN_NOT_IMPLEMENTED, "Channel not implemented" },
	{ PRI_CAUSE_FACILITY_NOT_IMPLEMENTED, "Facility not implemented" },
	{ PRI_CAUSE_INVALID_CALL_REFERENCE, "Invalid call reference value" },
	{ PRI_CAUSE_INCOMPATIBLE_DESTINATION, "Incompatible destination" },
	{ PRI_CAUSE_INVALID_MSG_UNSPECIFIED, "Invalid message unspecified" },
	{ PRI_CAUSE_MANDATORY_IE_MISSING, "Mandatory information element is missing" },
	{ PRI_CAUSE_MESSAGE_TYPE_NONEXIST, "Message type nonexist." },
	{ PRI_CAUSE_WRONG_MESSAGE, "Wrong message" },
	{ PRI_CAUSE_IE_NONEXIST, "Info. element nonexist or not implemented" },
	{ PRI_CAUSE_INVALID_IE_CONTENTS, "Invalid information element contents" },
	{ PRI_CAUSE_WRONG_CALL_STATE, "Message not compatible with call state" },
	{ PRI_CAUSE_RECOVERY_ON_TIMER_EXPIRE, "Recover on timer expiry" },
	{ PRI_CAUSE_MANDATORY_IE_LENGTH_ERROR, "Mandatory IE length error" },
	{ PRI_CAUSE_PROTOCOL_ERROR, "Protocol error, unspecified" },
	{ PRI_CAUSE_INTERWORKING, "Interworking, unspecified" },
};

struct msgtype facilities[] = {
       { PRI_NSF_SID_PREFERRED, "CPN (SID) preferred" },
       { PRI_NSF_ANI_PREFERRED, "BN (ANI) preferred" },
       { PRI_NSF_SID_ONLY, "CPN (SID) only" },
       { PRI_NSF_ANI_ONLY, "BN (ANI) only" },
       { PRI_NSF_CALL_ASSOC_TSC, "Call Associated TSC" },
       { PRI_NSF_NOTIF_CATSC_CLEARING, "Notification of CATSC Clearing or Resource Unavailable" },
       { PRI_NSF_OPERATOR, "Operator" },
       { PRI_NSF_PCCO, "Pre-subscribed Common Carrier Operator (PCCO)" },
       { PRI_NSF_SDN, "SDN (including GSDN)" },
       { PRI_NSF_TOLL_FREE_MEGACOM, "Toll Free MEGACOM" },
       { PRI_NSF_MEGACOM, "MEGACOM" },
       { PRI_NSF_ACCUNET, "ACCUNET Switched Digital Service" },
       { PRI_NSF_LONG_DISTANCE_SERVICE, "Long Distance Service" },
       { PRI_NSF_INTERNATIONAL_TOLL_FREE, "International Toll Free Service" },
       { PRI_NSF_ATT_MULTIQUEST, "AT&T MultiQuest" },
       { PRI_NSF_CALL_REDIRECTION_SERVICE, "Call Redirection Service" }
};

#define FLAG_PREFERRED 2
#define FLAG_EXCLUSIVE 4

#define RESET_INDICATOR_CHANNEL	0
#define RESET_INDICATOR_DS1		6
#define RESET_INDICATOR_PRI		7

#define TRANS_MODE_64_CIRCUIT	0x10
#define TRANS_MODE_2x64_CIRCUIT	0x11
#define TRANS_MODE_384_CIRCUIT	0x13
#define TRANS_MODE_1536_CIRCUIT	0x15
#define TRANS_MODE_1920_CIRCUIT	0x17
#define TRANS_MODE_MULTIRATE	0x18
#define TRANS_MODE_PACKET		0x40

#define RATE_ADAPT_56K			0x0f

#define LAYER_2_LAPB			0x46

#define LAYER_3_X25				0x66

/* The 4ESS uses a different audio field */
#define PRI_TRANS_CAP_AUDIO_4ESS	0x08

/* Don't forget to update PRI_PROG_xxx at libpri.h */
#define Q931_PROG_CALL_NOT_E2E_ISDN						0x01
#define Q931_PROG_CALLED_NOT_ISDN						0x02
#define Q931_PROG_CALLER_NOT_ISDN						0x03
#define Q931_PROG_CALLER_RETURNED_TO_ISDN					0x04
#define Q931_PROG_INBAND_AVAILABLE						0x08
#define Q931_PROG_DELAY_AT_INTERF						0x0a
#define Q931_PROG_INTERWORKING_WITH_PUBLIC				0x10
#define Q931_PROG_INTERWORKING_NO_RELEASE				0x11
#define Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER	0x12
#define Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER	0x13

#define CODE_CCITT					0x0
#define CODE_INTERNATIONAL 			0x1
#define CODE_NATIONAL 				0x2
#define CODE_NETWORK_SPECIFIC		0x3

#define LOC_USER					0x0
#define LOC_PRIV_NET_LOCAL_USER		0x1
#define LOC_PUB_NET_LOCAL_USER		0x2
#define LOC_TRANSIT_NET				0x3
#define LOC_PUB_NET_REMOTE_USER		0x4
#define LOC_PRIV_NET_REMOTE_USER	0x5
#define LOC_INTERNATIONAL_NETWORK	0x7
#define LOC_NETWORK_BEYOND_INTERWORKING	0xa

struct q921_call {
 int tei;	
 int proc;		
 int channel;	
 q921_call *next;
};


static char *ie2str(int ie);
static char *msg2str(int msg);


#define FUNC_DUMP(name) void ((name))(int full_ie, struct pri *pri, q931_ie *ie, int len, char prefix)
#define FUNC_RECV(name) int ((name))(int full_ie, struct pri *pri, q931_call *call, int msgtype, q931_ie *ie, int len)
#define FUNC_SEND(name) int ((name))(int full_ie, struct pri *pri, q931_call *call, int msgtype, q931_ie *ie, int len, int order)


struct ie {
	/* Maximal count of same IEs at the message (0 - any, 1..n - limited) */
	int max_count;
	/* IE code */
	int ie;
	/* IE friendly name */
	char *name;
	/* Dump an IE for debugging (preceed all lines by prefix) */
	FUNC_DUMP(*dump);
	/* Handle IE  returns 0 on success, -1 on failure */
	FUNC_RECV(*receive);
	/* Add IE to a message, return the # of bytes added or -1 on failure */
	FUNC_SEND(*transmit);
};

static char *code2str(int code, struct msgtype *codes, int max)
{
	int x;
	for (x=0;x<max; x++) 
		if (codes[x].msgnum == code)
			return codes[x].name;
	return "Unknown";
}

static void call_init(struct q931_call *c)
{
	memset(c, 0, sizeof(*c));
	c->con_acked = 0;
	c->alive = 0;
	c->sendhangupack = 0;
	c->forceinvert = -1;	
	c->cr = -1;
	c->slotmap = -1;
	c->channelno = -1;
	c->ds1no = 0;
	c->ds1explicit = 0;
	c->chanflags = 0;
	c->next = NULL;
	c->sentchannel = 0;
	c->newcall = 1;
	c->t303timer = 0;
	c->t303running = 0;
	c->aoc = 0;
	c->phones = NULL;
	c->ourcallstate = Q931_CALL_STATE_NULL;
	c->peercallstate = Q931_CALL_STATE_NULL;
	c->llc[0] = '\0';
	c->cause = -1;
	c->causecode = -1;
	c->causeloc = -1;
}

static char *binary(int b, int len) {
	static char res[33];
	int x;
	memset(res, 0, sizeof(res));
	if (len > 32)
		len = 32;
	for (x=1;x<=len;x++)
		res[x-1] = b & (1 << (len - x)) ? '1' : '0';
	return res;
}

static FUNC_RECV(receive_channel_id)
{	
	int x;
	int pos=0;
 	if ((pri->localtype != PRI_CPE) && (pri->localtype != PRI_NETWORK)) {
 //		pri_error(pri, "!! BRI type %d!?\n",ie->data[0] & 0x03);
 		call->channelno = ie->data[0] & 0x03;
 		if (call->channelno == 3) {
 		    call->channelno = -1; // any channel
 		}
  		return 0;
 	}
#ifndef NOAUTO_CHANNEL_SELECTION_SUPPORT
 	if ((pri->localtype == PRI_NETWORK) || (pri->localtype == PRI_CPE)) {
	    switch (ie->data[0] & 3) {
		case 0:
			call->justsignalling = 1;
			break;
		case 1:
			break;
		default:
			pri_error(pri, "!! Unexpected Channel selection %d\n", ie->data[0] & 3);
			return -1;
	    }
	}
#endif
	if (ie->data[0] & 0x08)
		call->chanflags = FLAG_EXCLUSIVE;
	else
		call->chanflags = FLAG_PREFERRED;
	pos++;
	if (ie->data[0] & 0x40) {
		/* DS1 specified -- stop here */
		call->ds1no = ie->data[1] & 0x7f;
		call->ds1explicit = 1;
		pos++;
	} else
		call->ds1explicit = 0;

	if (pos+2 < len) {
		/* More coming */
		if ((ie->data[pos] & 0x0f) != 3) {
			pri_error(pri, "!! Unexpected Channel Type %d\n", ie->data[1] & 0x0f);
			return -1;
		}
		if ((ie->data[pos] & 0x60) != 0) {
			pri_error(pri, "!! Invalid CCITT coding %d\n", (ie->data[1] & 0x60) >> 5);
			return -1;
		}
		if (ie->data[pos] & 0x10) {
			/* Expect Slot Map */
			call->slotmap = 0;
			pos++;
			for (x=0;x<3;x++) {
				call->slotmap <<= 8;
				call->slotmap |= ie->data[x + pos];
			}
			return 0;
		} else {
			pos++;
			/* Only expect a particular channel */
			call->channelno = ie->data[pos] & 0x7f;
			return 0;
		}
	} else
		return 0;
	return -1;
}

static FUNC_SEND(transmit_channel_id)
{
	int pos=0;

	
	/* We are ready to transmit single IE only */
	if (order > 1)
		return 0;
	
	if (call->justsignalling) {
		ie->data[pos++] = 0xac; /* Read the standards docs to figure this out
					   ECMA-165 section 7.3 */
		return pos + 2;
	}
		
	/* Start with standard stuff */
	if (pri->switchtype == PRI_SWITCH_GR303_TMC) {
		ie->data[pos] = 0x69;
	} else {
	    if ((pri->localtype == PRI_NETWORK) || (pri->localtype == PRI_CPE)) {
		ie->data[pos] = 0xa1;
	    } else {
		// BRI
		ie->data[pos] = 0x80;
	    }
	}
	/* Add exclusive flag if necessary */
	if (call->chanflags & FLAG_EXCLUSIVE)
		ie->data[pos] |= 0x08;
	else if (!(call->chanflags & FLAG_PREFERRED)) {
		/* Don't need this IE */
		return 0;
	}

	if (((pri->switchtype != PRI_SWITCH_QSIG) && (call->ds1no > 0)) || call->ds1explicit) {
		/* Note that we are specifying the identifier */
		ie->data[pos++] |= 0x40;
		/* We need to use the Channel Identifier Present thingy.  Just specify it and we're done */
		ie->data[pos++] = 0x80 | call->ds1no;
	} else
		pos++;
	if ((call->channelno > -1) || (call->slotmap != -1)) {
	    if ((pri->localtype == PRI_NETWORK) || (pri->localtype == PRI_CPE)) {
		/* We'll have the octet 8.2 and 8.3's present */
		ie->data[pos++] = 0x83;
		if (call->channelno > -1) {
			/* Channel number specified */
			ie->data[pos++] = 0x80 | call->channelno;
			return pos + 2;
		}
		/* We have to send a channel map */
		if (call->slotmap != -1) {
			ie->data[pos-1] |= 0x10;
			ie->data[pos++] = (call->slotmap & 0xff0000) >> 16;
			ie->data[pos++] = (call->slotmap & 0xff00) >> 8;
			ie->data[pos++] = (call->slotmap & 0xff);
			return pos + 2;
		}
	    } else {
		// BRI
		// pri_error(pri, "channelno %d, ds1no %d data %d\n",call->channelno,call->ds1no,ie->data[pos-1]);
		if (pri->localtype == BRI_CPE_PTMP) {
 		    if (msgtype == Q931_SETUP) {
 			// network, you decide!
 			if (call->channelno > -1) {
 //		    	    ie->data[pos-1] = 0x83;
 		    	    ie->data[pos-1] = 0x80 | call->channelno;
 			}
 		    } else {
 			if (call->channelno > -1) {
 		    	    ie->data[pos-1] |= call->channelno;
 			}
 		    }
 		} else {
 		    if (call->channelno > -1) {
 		        ie->data[pos-1] |= call->channelno;
 		    }
 		}
 		return pos + 2;
 	    }	
         } else {
 	    if (pri->localtype == BRI_CPE) {
 	        ie->data[pos++] = 0x80 | 3;
 	        return pos + 2;
 	    }
 	}	

	if (call->ds1no > 0) {
		/* We're done */
		return pos + 2;
	}
 	if (msgtype == Q931_RESTART_ACKNOWLEDGE) {
	    /* restart complete interface! */
	    return 0;
	}	
	pri_error(pri, "!! No channel map, no channel, and no ds1?  What am I supposed to identify?\n");
	return -1;
}

static FUNC_DUMP(dump_channel_id)
{
	int pos=0;
	int x;
	int res = 0;
	static const char*	msg_chan_sel[] = {
		"No channel selected", "B1 channel", "B2 channel","Any channel selected" 
		"No channel selected", "As indicated in following octets", "Reserved","Any channel selected"
	};

	pri_message(pri, "%c Channel ID (len=%2d) [ Ext: %d  IntID: %s, %s Spare: %d, %s Dchan: %d\n",
		prefix, len, (ie->data[0] & 0x80) ? 1 : 0, (ie->data[0] & 0x40) ? "Explicit" : "Implicit",
		(ie->data[0] & 0x20) ? "PRI" : "Other", (ie->data[0] & 0x10) ? 1 : 0,
		(ie->data[0] & 0x08) ? "Exclusive" : "Preferred",  (ie->data[0] & 0x04) ? 1 : 0);
	pri_message(pri, "%c                        ChanSel: %s\n",
		prefix, msg_chan_sel[(ie->data[0] & 0x3) + ((ie->data[0]>>3) & 0x4)]);
	pos++;
	len--;
	if (ie->data[0] &  0x40) {
		/* Explicitly defined DS1 */
		pri_message(pri, "%c                       Ext: %d  DS1 Identifier: %d  \n", prefix, (ie->data[pos] & 0x80) >> 7, ie->data[pos] & 0x7f);
		pos++;
	} else {
		/* Implicitly defined DS1 */
	}
	if (pos+2 < len) {
		/* Still more information here */
		pri_message(pri, "%c                       Ext: %d  Coding: %d   %s Specified   Channel Type: %d\n",
				prefix, (ie->data[pos] & 0x80) >> 7, (ie->data[pos] & 60) >> 5, 
				(ie->data[pos] & 0x10) ? "Slot Map" : "Number", ie->data[pos] & 0x0f);
		if (!(ie->data[pos] & 0x10)) {
			/* Number specified */
			pos++;
			pri_message(pri, "%c                       Ext: %d  Channel: %d ]\n", prefix, (ie->data[pos] & 0x80) >> 7, 
				(ie->data[pos]) & 0x7f);
		} else {
			pos++;
			/* Map specified */
			for (x=0;x<3;x++) {
				res <<= 8;
				res |= ie->data[pos++];
			}
			pri_message(pri, "%c                       Map: %s ]\n", prefix, binary(res, 24));
		}
	} else pri_message(pri, "                         ]\n");				
}

static char *ri2str(int ri)
{
	static struct msgtype ris[] = {
		{ 0, "Indicated Channel" },
		{ 6, "Single DS1 Facility" },
		{ 7, "All DS1 Facilities" },
	};
	return code2str(ri, ris, sizeof(ris) / sizeof(ris[0]));
}

static FUNC_DUMP(dump_restart_indicator)
{
	pri_message(pri, "%c Restart Indentifier (len=%2d) [ Ext: %d  Spare: %d  Resetting %s (%d) ]\n", 
		prefix, len, (ie->data[0] & 0x80) >> 7, (ie->data[0] & 0x78) >> 3, ri2str(ie->data[0] & 0x7), ie->data[0] & 0x7);
}

static FUNC_RECV(receive_restart_indicator)
{
	/* Pretty simple */
	call->ri = ie->data[0] & 0x7;
	return 0;
}

static FUNC_SEND(transmit_restart_indicator)
{
	/* Pretty simple */
	switch(call->ri) {
	case 0:
	case 6:
	case 7:
		ie->data[0] = 0x80 | (call->ri & 0x7);
		break;
	case 5:
		/* Switch compatibility */
		ie->data[0] = 0xA0 | (call->ri & 0x7);
		break;
	default:
		pri_error(pri, "!! Invalid restart indicator value %d\n", call->ri);
		return-1;
	}
	return 3;
}

static char *redirection_reason2str(int mode)
{
	static struct msgtype modes[] = {
		{ PRI_REDIR_UNKNOWN, "Unknown" },
		{ PRI_REDIR_FORWARD_ON_BUSY, "Forwarded on busy" },
		{ PRI_REDIR_FORWARD_ON_NO_REPLY, "Forwarded on no reply" },
		{ PRI_REDIR_DEFLECTION, "Call deflected" },
		{ PRI_REDIR_DTE_OUT_OF_ORDER, "Called DTE out of order" },
		{ PRI_REDIR_FORWARDED_BY_DTE, "Forwarded by called DTE" },
		{ PRI_REDIR_UNCONDITIONAL, "Forwarded unconditionally" },
	};
	return code2str(mode, modes, sizeof(modes) / sizeof(modes[0]));
}

static char *cap2str(int mode)
{
	static struct msgtype modes[] = {
		{ PRI_TRANS_CAP_SPEECH, "Speech" },
		{ PRI_TRANS_CAP_DIGITAL, "Unrestricted digital information" },
		{ PRI_TRANS_CAP_RESTRICTED_DIGITAL, "Restricted digital information" },
		{ PRI_TRANS_CAP_3_1K_AUDIO, "3.1kHz audio" },
		{ PRI_TRANS_CAP_DIGITAL_W_TONES, "Unrestricted digital information with tones/announcements" },
		{ PRI_TRANS_CAP_VIDEO, "Video" },
		{ PRI_TRANS_CAP_AUDIO_4ESS, "3.1khz audio (4ESS)" },
	};
	return code2str(mode, modes, sizeof(modes) / sizeof(modes[0]));
}

static char *mode2str(int mode)
{
	static struct msgtype modes[] = {
		{ TRANS_MODE_64_CIRCUIT, "64kbps, circuit-mode" },
		{ TRANS_MODE_2x64_CIRCUIT, "2x64kbps, circuit-mode" },
		{ TRANS_MODE_384_CIRCUIT, "384kbps, circuit-mode" },
		{ TRANS_MODE_1536_CIRCUIT, "1536kbps, circuit-mode" },
		{ TRANS_MODE_1920_CIRCUIT, "1920kbps, circuit-mode" },
		{ TRANS_MODE_MULTIRATE, "Multirate (Nx64kbps)" },
		{ TRANS_MODE_PACKET, "Packet Mode" },
	};
	return code2str(mode, modes, sizeof(modes) / sizeof(modes[0]));
}

static char *l12str(int proto)
{
	static struct msgtype protos[] = {
		{ PRI_LAYER_1_ITU_RATE_ADAPT, "ITU Rate Adaption" },
		{ PRI_LAYER_1_ULAW, "u-Law" },
		{ PRI_LAYER_1_ALAW, "A-Law" },
		{ PRI_LAYER_1_G721, "G.721 ADPCM" },
		{ PRI_LAYER_1_G722_G725, "G.722/G.725 7kHz Audio" },
		{ PRI_LAYER_1_G7XX_384K, "G.7xx 384k Video" },
		{ PRI_LAYER_1_NON_ITU_ADAPT, "Non-ITU Rate Adaption" },
		{ PRI_LAYER_1_V120_RATE_ADAPT, "V.120 Rate Adaption" },
		{ PRI_LAYER_1_X31_RATE_ADAPT, "X.31 Rate Adaption" },
	};
	return code2str(proto, protos, sizeof(protos) / sizeof(protos[0]));
}

static char *ra2str(int proto)
{
	static struct msgtype protos[] = {
		{ RATE_ADAPT_56K, "from 56kbps" },
	};
	return code2str(proto, protos, sizeof(protos) / sizeof(protos[0]));
}

static char *l22str(int proto)
{
	static struct msgtype protos[] = {
		{ LAYER_2_LAPB, "LAPB" },
	};
	return code2str(proto, protos, sizeof(protos) / sizeof(protos[0]));
}

static char *l32str(int proto)
{
	static struct msgtype protos[] = {
		{ LAYER_3_X25, "X.25" },
	};
	return code2str(proto, protos, sizeof(protos) / sizeof(protos[0]));
}

static FUNC_DUMP(dump_bearer_capability)
{
	int pos=2;
	pri_message(pri, "%c Bearer Capability (len=%2d) [ Ext: %d  Q.931 Std: %d  Info transfer capability: %s (%d)\n",
		prefix, len, (ie->data[0] & 0x80 ) >> 7, (ie->data[0] & 0x60) >> 5, cap2str(ie->data[0] & 0x1f), (ie->data[0] & 0x1f));
	pri_message(pri, "%c                              Ext: %d  Trans mode/rate: %s (%d)\n", prefix, (ie->data[1] & 0x80) >> 7, mode2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f);
	if ((ie->data[1] & 0x7f) == 0x18) {
	    pri_message(pri, "%c                              Ext: %d  Transfer rate multiplier: %d x 64\n", prefix, (ie->data[2] & 0x80) >> 7, ie->data[2] & 0x7f);
		pos++;
	}
	/* Stop here if no more */
	if (pos >= len)
		return;
	if ((ie->data[1] & 0x7f) != TRANS_MODE_PACKET) {
		/* Look for octets 5 and 5.a if present */
		pri_message(pri, "%c                              Ext: %d  User information layer 1: %s (%d)\n", prefix, (ie->data[pos] >> 7), l12str(ie->data[pos] & 0x7f), ie->data[pos] & 0x7f);
		if ((ie->data[pos] & 0x7f) == PRI_LAYER_1_ITU_RATE_ADAPT)
			pri_message(pri, "%c                                Ext: %d  Rate adaptatation: %s (%d)\n", prefix, ie->data[pos] >> 7, ra2str(ie->data[pos] & 0x7f), ie->data[pos] & 0x7f);
		pos++;
	} else {
		/* Look for octets 6 and 7 but not 5 and 5.a */
		pri_message(pri, "%c                              Ext: %d  User information layer 2: %s (%d)\n", prefix, ie->data[pos] >> 7, l22str(ie->data[pos] & 0x7f), ie->data[pos] & 0x7f);
		pos++;
		pri_message(pri, "%c                              Ext: %d  User information layer 3: %s (%d)\n", prefix, ie->data[pos] >> 7, l32str(ie->data[pos] & 0x7f), ie->data[pos] & 0x7f);
		pos++;
	}
}

static FUNC_RECV(receive_bearer_capability)
{
	int pos=2;
	if (ie->data[0] & 0x60) {
		pri_error(pri, "!! non-standard Q.931 standard field\n");
		return -1;
	}
	call->transcapability = ie->data[0] & 0x1f;
	call->transmoderate = ie->data[1] & 0x7f;
	if (call->transmoderate == PRI_TRANS_CAP_AUDIO_4ESS)
		call->transmoderate = PRI_TRANS_CAP_3_1K_AUDIO;
	if (call->transmoderate != TRANS_MODE_PACKET) {
		call->userl1 = ie->data[pos] & 0x7f;
		if (call->userl1 == PRI_LAYER_1_ITU_RATE_ADAPT) {
			call->rateadaption = ie->data[++pos] & 0x7f;
		}
		pos++;
	} else {
		/* Get 6 and 7 */
		call->userl2 = ie->data[pos++] & 0x7f;
		call->userl3 = ie->data[pos] & 0x7f;
	}
	return 0;
}

static FUNC_SEND(transmit_bearer_capability)
{
	int tc;

	/* We are ready to transmit single IE only */	
	if(order > 1)
		return 0;

	tc = call->transcapability;
	if (pri->subchannel) {
		/* Bearer capability is *hard coded* in GR-303 */
		ie->data[0] = 0x88;
		ie->data[1] = 0x90;
		return 4;
	}
	
	if (call->justsignalling) {
		ie->data[0] = 0xa8;
		ie->data[1] = 0x80;
		return 4;
	}
	
	if (pri->switchtype == PRI_SWITCH_ATT4ESS) {
		/* 4ESS uses a different trans capability for 3.1khz audio */
		if (tc == PRI_TRANS_CAP_3_1K_AUDIO)
			tc = PRI_TRANS_CAP_AUDIO_4ESS;
	}
	ie->data[0] = 0x80 | tc;
	ie->data[1] = call->transmoderate | 0x80;
	if ((tc & PRI_TRANS_CAP_DIGITAL)&&(pri->switchtype == PRI_SWITCH_EUROISDN_E1)) {
		/* Apparently EuroISDN switches don't seem to like user layer 2/3 */
		return 4;
	}
	if (call->transmoderate != TRANS_MODE_PACKET) {
		/* If you have an AT&T 4ESS, you don't send any more info */
		if ((pri->switchtype != PRI_SWITCH_ATT4ESS) && (call->userl1 > -1)) {
			ie->data[2] = call->userl1 | 0x80; /* XXX Ext bit? XXX */
			if (call->userl1 == PRI_LAYER_1_ITU_RATE_ADAPT) {
				ie->data[3] = call->rateadaption | 0x80;
				return 6;
			}
			return 5;
		} else
			return 4;
	} else {
		ie->data[2] = 0x80 | call->userl2;
		ie->data[3] = 0x80 | call->userl3;
		return 6;
	}
}

char *pri_plan2str(int plan)
{
	static struct msgtype plans[] = {
		{ PRI_INTERNATIONAL_ISDN, "International number in ISDN" },
		{ PRI_NATIONAL_ISDN, "National number in ISDN" },
		{ PRI_LOCAL_ISDN, "Local number in ISDN" },
		{ PRI_PRIVATE, "Private numbering plan" },
		{ PRI_UNKNOWN, "Unknown numbering plan" },
	};
	return code2str(plan, plans, sizeof(plans) / sizeof(plans[0]));
}

static char *npi2str(int plan)
{
	static struct msgtype plans[] = {
		{ PRI_NPI_UNKNOWN, "Unknown Number Plan" },
		{ PRI_NPI_E163_E164, "ISDN/Telephony Numbering Plan (E.164/E.163)" },
		{ PRI_NPI_X121, "Data Numbering Plan (X.121)" },
		{ PRI_NPI_F69, "Telex Numbering Plan (F.69)" },
		{ PRI_NPI_NATIONAL, "National Standard Numbering Plan" },
		{ PRI_NPI_PRIVATE, "Private Numbering Plan" },
		{ PRI_NPI_RESERVED, "Reserved Number Plan" },
	};
	return code2str(plan, plans, sizeof(plans) / sizeof(plans[0]));
}

static char *ton2str(int plan)
{
	static struct msgtype plans[] = {
		{ PRI_TON_UNKNOWN, "Unknown Number Type" },
		{ PRI_TON_INTERNATIONAL, "International Number" },
		{ PRI_TON_NATIONAL, "National Number" },
		{ PRI_TON_NET_SPECIFIC, "Network Specific Number" },
		{ PRI_TON_SUBSCRIBER, "Subscriber Number" },
		{ PRI_TON_ABBREVIATED, "Abbreviated number" },
		{ PRI_TON_RESERVED, "Reserved Number" },
	};
	return code2str(plan, plans, sizeof(plans) / sizeof(plans[0]));
}

static char *subaddrtype2str(int plan)
{
	static struct msgtype plans[] = {
		{ 0, "NSAP (X.213/ISO 8348 AD2)" },
		{ 2, "User Specified" },
	};
	return code2str(plan, plans, sizeof(plans) / sizeof(plans[0]));
}

char *pri_pres2str(int pres)
{
	static struct msgtype press[] = {
		{ PRES_ALLOWED_USER_NUMBER_NOT_SCREENED, "Presentation permitted, user number not screened" },
		{ PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, "Presentation permitted, user number passed network screening" },
		{ PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN, "Presentation permitted, user number failed network screening" },
		{ PRES_ALLOWED_NETWORK_NUMBER, "Presentation allowed of network provided number" },
		{ PRES_PROHIB_USER_NUMBER_NOT_SCREENED, "Presentation prohibited, user number not screened" },
		{ PRES_PROHIB_USER_NUMBER_PASSED_SCREEN, "Presentation prohibited, user number passed network screening" },
		{ PRES_PROHIB_USER_NUMBER_FAILED_SCREEN, "Presentation prohibited, user number failed network screening" },
		{ PRES_PROHIB_NETWORK_NUMBER, "Presentation prohibited of network provided number" },
		{ PRES_NUMBER_NOT_AVAILABLE, "Number not available" },
	};
	return code2str(pres, press, sizeof(press) / sizeof(press[0]));
}

static void q931_get_number(char *num, int maxlen, unsigned char *src, int len)
{
	if (len < 0) {
	    pri_error(NULL, "q931_get_number received invalid len = %d\n", len);
	    return;
	}
	if (len > maxlen - 1) {
		num[0] = 0;
		return;
	}
	memcpy(num, src, len);
	num[len] = 0;
}

static FUNC_DUMP(dump_called_party_number)
{
	char cnum[256];
 	if (len >= 3) {
 	    q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3);
	    pri_message(pri, "%c Called Number (len=%2d) [ Ext: %d  TON: %s (%d)  NPI: %s (%d) '%s' ]\n",
 		    prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f, cnum);
 	} else {
 	    pri_error(pri, "Called Number (len=%2d) too short.\n", len);
 	}
}

static FUNC_DUMP(dump_called_party_subaddr)
{
	char cnum[256];
 	if (len >= 3) {
 	    q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3);
 	    pri_message(pri, "%c Called Sub-Address (len=%2d) [ Ext: %d  Type: %s (%d) O: %d '%s' ]\n",
  		prefix, len, ie->data[0] >> 7,
  		subaddrtype2str((ie->data[0] & 0x70) >> 4), (ie->data[0] & 0x70) >> 4,
  		(ie->data[0] & 0x08) >> 3, cnum);
 	} else {
 	    pri_error(pri, "Called Party Subaddress (len=%2d) too short.\n", len);
 	}
}

static FUNC_DUMP(dump_calling_party_number)
{
  	char cnum[256];
 	if ((ie->data[0] & 0x80) && (len >= 3)) {
  		q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3);
 		pri_message(pri, "%c Calling Number (len=%2d) [ Ext: %d  TON: %s (%d)  NPI: %s (%d)\n", prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f);
  		pri_message(pri, "%c                           Presentation: %s (%d) '%s' ]\n", prefix, pri_pres2str(0), 0, cnum);
 	} else if (len >= 4) {
 		q931_get_number(cnum, sizeof(cnum), ie->data + 2, len - 4);
 		pri_message(pri, "%c Calling Number (len=%2d) [ Ext: %d  TON: %s (%d)  NPI: %s (%d)\n", prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f);
  		pri_message(pri, "%c                           Presentation: %s (%d) '%s' ]\n", prefix, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f, cnum);
 	} else {
 	    pri_error(pri, "Calling Party Number (len=%2d) too short.\n", len);
 	}
}

static FUNC_DUMP(dump_calling_party_subaddr)
{
	char cnum[256];
 	if (len >= 4) {
 	    q931_get_number(cnum, sizeof(cnum), ie->data + 1, len - 3);
 	    pri_message(pri, "%c Calling Sub-Address (len=%2d) [ Ext: %d  Type: %s (%d) O: %d '%s' ]\n",
  		prefix, len, ie->data[0] >> 7,
  		subaddrtype2str((ie->data[0] & 0x70) >> 4), (ie->data[0] & 0x70) >> 4,
  		(ie->data[0] & 0x08) >> 3, cnum);
 	} else {
 	    pri_error(pri, "Calling Party Subaddress (len=%2d) too short.\n", len);
 	}
}

static FUNC_DUMP(dump_colp)
{
 	char cnum[256];
 	if (len >= 4) {
 	    q931_get_number(cnum, sizeof(cnum), ie->data + 2, len - 4);
 	    pri_message(pri, "%c COLP (len=%2d) [ Ext: %d  TON: %s (%d)  NPI: %s (%d)\n", prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f);
 	    pri_message(pri, "%c                           Presentation: %s (%d) '%s' ]\n", prefix, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f, cnum);
 	} else {
 	    pri_error(pri, "COLP (len=%2d) too short.\n", len);
 	}
}

static FUNC_DUMP(dump_redirecting_number)
{
	char cnum[256];
	int i = 0;
	/* To follow Q.931 (4.5.1), we must search for start of octet 4 by
	   walking through all bytes until one with ext bit (8) set to 1 */
	do {
		switch(i) {
		case 0:	/* Octet 3 */
			pri_message(pri, "%c Redirecting Number (len=%2d) [ Ext: %d  TON: %s (%d)  NPI: %s (%d)",
				prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f);
			break;
		case 1: /* Octet 3a */
			pri_message(pri, "\n%c                               Ext: %d Presentation: %s (%d)",
				prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f);
			break;
		case 2: /* Octet 3b */
			pri_message(pri, "\n%c                               Ext: %d Reason: %s (%d)",
				prefix, ie->data[2] >> 7, redirection_reason2str(ie->data[2] & 0x7f), ie->data[2] & 0x7f);
			break;
		}
	}
	while(!(ie->data[i++]& 0x80));
	if ((ie->len - i) >= 0) {
	    q931_get_number(cnum, sizeof(cnum), ie->data + i, ie->len - i);
	    pri_message(pri, " '%s' ]\n", cnum);
	} else {
	    pri_error(pri, "Redirecting Number (len=%2d) too short.\n", len);
	}
}

static FUNC_DUMP(dump_connected_number)
{
	char cnum[256];
	int i = 0;
	/* To follow Q.931 (4.5.1), we must search for start of octet 4 by
	   walking through all bytes until one with ext bit (8) set to 1 */
	do {
		switch(i) {
		case 0:	/* Octet 3 */
			pri_message(pri, "%c Connected Number (len=%2d) [ Ext: %d  TON: %s (%d)  NPI: %s (%d)",
				prefix, len, ie->data[0] >> 7, ton2str((ie->data[0] >> 4) & 0x07), (ie->data[0] >> 4) & 0x07, npi2str(ie->data[0] & 0x0f), ie->data[0] & 0x0f);
			break;
		case 1: /* Octet 3a */
			pri_message(pri, "\n%c                             Ext: %d Presentation: %s (%d)",
				prefix, ie->data[1] >> 7, pri_pres2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f);
			break;
		}
	}
	while(!(ie->data[i++]& 0x80));
	if ((ie->len - i) >= 0) {
	    q931_get_number(cnum, sizeof(cnum), ie->data + i, ie->len - i);
	    pri_message(pri, " '%s' ]\n", cnum);
	} else {
	    pri_error(pri, "Connected Number (len=%2d) too short.\n", len);
	}
}


static FUNC_RECV(receive_redirecting_number)
{
	int i = 0;

	/* To follow Q.931 (4.5.1), we must search for start of octet 4 by
	   walking through all bytes until one with ext bit (8) set to 1 */
	do {
		switch(i) {
		case 0:
			call->redirectingplan = ie->data[i] & 0x7f;
			break;
		case 1:
			call->redirectingpres = ie->data[i] & 0x7f;
			break;
		case 2:
			call->redirectingreason = ie->data[i] & 0x0f;
			break;
		}
	}
	while(!(ie->data[i++] & 0x80));
	q931_get_number(call->redirectingnum, sizeof(call->redirectingnum), ie->data + i, ie->len - i);
	return 0;
}

static FUNC_SEND(transmit_redirecting_number)
{
	if (order > 1)
		return 0;
	if (call->redirectingnum && strlen(call->redirectingnum)) {
		ie->data[0] = call->redirectingplan;
		ie->data[1] = call->redirectingpres;
		ie->data[2] = (call->redirectingreason & 0x0f) | 0x80;
		memcpy(ie->data + 3, call->redirectingnum, strlen(call->redirectingnum));
		return strlen(call->redirectingnum) + 3 + 2;
	}
	return 0;
}

static FUNC_DUMP(dump_redirecting_subaddr)
{
	char cnum[256];
 	if (len >= 4) {
 	    q931_get_number(cnum, sizeof(cnum), ie->data + 2, len - 4);
 	    pri_message(pri, "%c Redirecting Sub-Address (len=%2d) [ Ext: %d  Type: %s (%d) O: %d '%s' ]\n",
  		prefix, len, ie->data[0] >> 7,
  		subaddrtype2str((ie->data[0] & 0x70) >> 4), (ie->data[0] & 0x70) >> 4,
  		(ie->data[0] & 0x08) >> 3, cnum);
 	} else {
 	    pri_error(pri, "Redirecting Subaddress (len=%2d) too short.\n", len);
 	}
}

static FUNC_RECV(receive_calling_party_subaddr)
{
	/* copy digits to call->callingsubaddr */
 	if (len >= 4) {
  	    q931_get_number(call->callingsubaddr, sizeof(call->callingsubaddr), ie->data + 1, len - 3);
 	} else {
	    pri_error(call->pri, "Calling Party Subaddress (len=%2d) too short.\n", len);
 	}
	return 0;
}

static FUNC_RECV(receive_called_party_number)
{
 	/* copy digits to call->callednum or call->digits */
 	if (len >= 3) {
 	    if (msgtype == Q931_INFORMATION) {
  		q931_get_number(call->digits, sizeof(call->digits), ie->data + 1, len - 3);
 	    } else {
  		q931_get_number(call->callednum, sizeof(call->callednum), ie->data + 1, len - 3);
 	    }
 	    call->calledplan = ie->data[0] & 0x7f;
 	} else {
 	    pri_error(call->pri, "Called Party Number (len=%2d) too short.\n", len);
 	}
	return 0;
}

static FUNC_SEND(transmit_called_party_number)
{
	ie->data[0] = 0x80 | call->calledplan;
	if (strlen(call->callednum)) 
		memcpy(ie->data + 1, call->callednum, strlen(call->callednum));
	return strlen(call->callednum) + 3;
}

static FUNC_RECV(receive_calling_party_number)
{
/// callerani!!
 	if (strlen(call->callernum)) {
     	    call->callerplanuser = ie->data[0] & 0x7f;
 	} else {
     	    call->callerplan = ie->data[0] & 0x7f;
 	}

        if (ie->data[0] & 0x80) {
 	  if (len >= 3) {
 	     if (strlen(call->callernum)) {
 		// got A NUM already (this is not 100% correct, but the network should do it this way to protect bad implementations
 		 q931_get_number(call->callerani, sizeof(call->callerani), ie->data + 1, len - 3);
 	        call->callerpresuser = 0; /* PI presentation allowed
 				   SI user-provided, not screened */        
 	    } else {
 		 q931_get_number(call->callernum, sizeof(call->callernum), ie->data + 1, len - 3);
 		 call->callerpres = 0; /* PI presentation allowed
  				   SI user-provided, not screened */        
 	    }
 	  } else {
 		pri_error(call->pri, "Calling Party Number (len=%2d) too short.\n", len);
 	  }
        } else {
 	  if (len >= 4) {
 	     if (strlen(call->callernum)) {
 	        q931_get_number(call->callerani, sizeof(call->callerani), ie->data + 2, len - 4);
 		 call->callerpresuser = ie->data[1] & 0x7f;
 	      } else {
 		 q931_get_number(call->callernum, sizeof(call->callernum), ie->data + 2, len - 4);
 	         call->callerpres = ie->data[1] & 0x7f;
 	    }
 	  } else {
 		pri_error(call->pri, "Calling Party Number (len=%2d) too short.\n", len);
 	  }
        }
	return 0;
}

static FUNC_SEND(transmit_calling_party_number)
{
	ie->data[0] = call->callerplan;
	ie->data[1] = 0x80 | call->callerpres;
	if (strlen(call->callernum)) 
		memcpy(ie->data + 2, call->callernum, strlen(call->callernum));
	return strlen(call->callernum) + 4;
}

static FUNC_DUMP(dump_user_user)
{
	int x;
	pri_message(pri, "%c User-User Information (len=%2d) [", prefix, len);
	for (x=0;x<ie->len;x++)
		pri_message(pri, " %02x", ie->data[x] & 0x7f);
	pri_message(pri, " ]\n");
}


static FUNC_RECV(receive_user_user)
{        
        call->useruserprotocoldisc = ie->data[0] & 0xff;
        if (call->useruserprotocoldisc == 4) { /* IA5 */
 	    if (len >= 3) {
         	q931_get_number(call->useruserinfo, sizeof(call->useruserinfo), ie->data + 1, len - 3);
 	    } else {
 		pri_error(call->pri, "User-User Information (len=%2d) too short.\n", len);
 	    }
  	}
	return 0;
}

static FUNC_RECV(receive_call_identity)
{
 	if (len >= 2) {
  	    q931_get_number(call->callid, sizeof(call->callid), ie->data, len - 2);
 	} else {
 	    pri_error(call->pri, "Call Identity (len=%2d) too short.\n", len);
 	}
  	return 0;
}
  
static FUNC_SEND(transmit_call_identity)
{
	if (strlen(call->callid)) 
		memcpy(ie->data , call->callid, strlen(call->callid));
	return strlen(call->callednum) + 3;
}

static FUNC_RECV(receive_high_layer_compat)
{
  	return 0;
}
  
static FUNC_SEND(transmit_high_layer_compat)
{
    if (call->transcapability != PRI_TRANS_CAP_RESTRICTED_DIGITAL && call->transcapability != PRI_TRANS_CAP_DIGITAL && call->transcapability != PRI_TRANS_CAP_DIGITAL_W_TONES) {
	ie->data[0] = 0x91;
	ie->data[1] = 0x81;
	return 4;
    }
    return 0;
}

static FUNC_DUMP(dump_high_layer_compat)
{
	int x;
	pri_message(pri, "%c High-layer compatibilty (len=%2d) [ ", prefix, len);
	for (x=0;x<ie->len;x++) 
		pri_message(pri, "0x%02X ", ie->data[x]);
	pri_message(pri, " ]\n");
}


static FUNC_RECV(receive_low_layer_compat)
{
	if (len > 0) {
	    if (len > 16) {
		pri_error(pri, "%d bytes LLC too long\n", len);
		call->llc[0] = 0;
	    } else {
		pri_error(pri, "copying %d bytes LLC \n", len);
		call->llc[0] = len;
	        memcpy(call->llc+1, ie->data, len);
	    }
	}
  	return 0;
}
  
static FUNC_SEND(transmit_low_layer_compat)
{
    if (call->llc[0] == 0) return 0;
    memcpy(ie->data, call->llc + 1, call->llc[0]);
    return call->llc[0] + 2;
}

static FUNC_DUMP(dump_low_layer_compat)
{
	int x;
	pri_message(pri, "%c Low-layer compatibilty (len=%2d) [ ", prefix, len);
	for (x=0;x<ie->len;x++) 
		pri_message(pri, "0x%02X ", ie->data[x]);
	pri_message(pri, " ]\n");
}

static FUNC_SEND(transmit_user_user)
{        
	int datalen = strlen(call->useruserinfo);
	if (datalen > 0) {
		/* Restricted to 35 characters */
		if (msgtype == Q931_USER_INFORMATION) {
			if (datalen > 260)
				datalen = 260;
		} else {
			if (datalen > 35)
				datalen = 35;
		}
		ie->data[0] = 4; /* IA5 characters */
		memcpy(&ie->data[1], call->useruserinfo, datalen);
		call->useruserinfo[0] = '\0';
		return datalen + 3;
	}

	return 0;
}

static char *prog2str(int prog)
{
	static struct msgtype progs[] = {
		{ Q931_PROG_CALL_NOT_E2E_ISDN, "Call is not end-to-end ISDN; further call progress information may be available inband." },
		{ Q931_PROG_CALLED_NOT_ISDN, "Called equipment is non-ISDN." },
		{ Q931_PROG_CALLER_NOT_ISDN, "Calling equipment is non-ISDN." },
		{ Q931_PROG_INBAND_AVAILABLE, "Inband information or appropriate pattern now available." },
		{ Q931_PROG_DELAY_AT_INTERF, "Delay in response at called Interface." },
		{ Q931_PROG_INTERWORKING_WITH_PUBLIC, "Interworking with a public network." },
		{ Q931_PROG_INTERWORKING_NO_RELEASE, "Interworking with a network unable to supply a release signal." },
		{ Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER, "Interworking with a network unable to supply a release signal before answer." },
		{ Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER, "Interworking with a network unable to supply a release signal after answer." },
	};
	return code2str(prog, progs, sizeof(progs) / sizeof(progs[0]));
}

static char *coding2str(int cod)
{
	static struct msgtype cods[] = {
		{ CODE_CCITT, "CCITT (ITU) standard" },
		{ CODE_INTERNATIONAL, "Non-ITU international standard" }, 
		{ CODE_NATIONAL, "National standard" }, 
		{ CODE_NETWORK_SPECIFIC, "Network specific standard" },
	};
	return code2str(cod, cods, sizeof(cods) / sizeof(cods[0]));
}

static char *loc2str(int loc)
{
	static struct msgtype locs[] = {
		{ LOC_USER, "User" },
		{ LOC_PRIV_NET_LOCAL_USER, "Private network serving the local user" },
		{ LOC_PUB_NET_LOCAL_USER, "Public network serving the local user" },
		{ LOC_TRANSIT_NET, "Transit network" },
		{ LOC_PUB_NET_REMOTE_USER, "Public network serving the remote user" },
		{ LOC_PRIV_NET_REMOTE_USER, "Private network serving the remote user" },
		{ LOC_INTERNATIONAL_NETWORK, "International network" },
		{ LOC_NETWORK_BEYOND_INTERWORKING, "Network beyond the interworking point" },
	};
	return code2str(loc, locs, sizeof(locs) / sizeof(locs[0]));
}

static FUNC_DUMP(dump_progress_indicator)
{
	pri_message(pri, "%c Progress Indicator (len=%2d) [ Ext: %d  Coding: %s (%d) 0: %d   Location: %s (%d)\n",
		prefix, len, ie->data[0] >> 7, coding2str((ie->data[0] & 0x60) >> 5), (ie->data[0] & 0x60) >> 5,
		(ie->data[0] & 0x10) >> 4, loc2str(ie->data[0] & 0xf), ie->data[0] & 0xf);
	pri_message(pri, "%c                               Ext: %d  Progress Description: %s (%d) ]\n",
		prefix, ie->data[1] >> 7, prog2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f);
}

static FUNC_RECV(receive_display)
{
	unsigned char *data;
	data = ie->data;
	if (data[0] & 0x80) {
		/* Skip over character set */
		data++;
		len--;
	}
 	if (msgtype == Q931_SETUP) {
 	    /* we treat display IEs in the SETUP msg as callername */
 	    if (len >= 2) {
 		q931_get_number(call->callername, sizeof(call->callername), data, len - 2);
 	    } else {
 		pri_error(call->pri, "Display (len=%2d) too short.\n", len);
 	    }
 	} else {
 	    /* in other msgs we will pass it as text to chan_zap */
 	    if (len >= 2) {
 		q931_get_number(call->display, sizeof(call->display), data, len - 2);
 	    } else {
 		pri_error(call->pri, "Display (len=%2d) too short.\n", len);
 	    }
 	}
	return 0;
}

static FUNC_SEND(transmit_display)
{
	int i;
	int cpe = pri->localtype == BRI_CPE || pri->localtype == BRI_CPE_PTMP || pri->localtype == PRI_CPE;
 	if ((pri->switchtype != PRI_SWITCH_NI1) && (pri->switchtype != PRI_SWITCH_QSIG) && strlen(call->callername) && !cpe) {
		i = 0;
		if(pri->switchtype != PRI_SWITCH_EUROISDN_E1) {
			ie->data[0] = 0xb1;
			++i;
		}
 	    	if (msgtype == Q931_SETUP_ACKNOWLEDGE) {
 		    memcpy(ie->data + i, call->display, strlen(call->display));
 		    return 2 + i + strlen(call->display);
 	 	} else {
 		    memcpy(ie->data + i, call->callername, strlen(call->callername));
 		    return 2 + i + strlen(call->callername);
 	    	}
	}
	return 0;
}

static FUNC_RECV(receive_progress_indicator)
{
	call->progloc = ie->data[0] & 0xf;
	call->progcode = (ie->data[0] & 0x60) >> 5;
	switch (call->progress = (ie->data[1] & 0x7f)) {
	case Q931_PROG_CALL_NOT_E2E_ISDN:
		call->progressmask |= PRI_PROG_CALL_NOT_E2E_ISDN;
		break;
	case Q931_PROG_CALLED_NOT_ISDN:
		call->progressmask |= PRI_PROG_CALLED_NOT_ISDN;
		break;
	case Q931_PROG_CALLER_NOT_ISDN:
		call->progressmask |= PRI_PROG_CALLER_NOT_ISDN;
		break;
	case Q931_PROG_CALLER_RETURNED_TO_ISDN:
		call->progressmask |= PRI_PROG_CALLER_RETURNED_TO_ISDN;
		break;
	case Q931_PROG_INBAND_AVAILABLE:
		call->progressmask |= PRI_PROG_INBAND_AVAILABLE;
		break;
	case Q931_PROG_DELAY_AT_INTERF:
		call->progressmask |= PRI_PROG_DELAY_AT_INTERF;
		break;
	case Q931_PROG_INTERWORKING_WITH_PUBLIC:
		call->progressmask |= PRI_PROG_INTERWORKING_WITH_PUBLIC;
		break;
	case Q931_PROG_INTERWORKING_NO_RELEASE:
		call->progressmask |= PRI_PROG_INTERWORKING_NO_RELEASE;
		break;
	case Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER:
		call->progressmask |= PRI_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER;
		break;
	case Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER:
		call->progressmask |= PRI_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER;
		break;
	default:
		pri_error(pri, "XXX Invalid Progress indicator value received: %02x\n",(ie->data[1] & 0x7f));
		break;
	}
	return 0;
}

 #if 0
  static FUNC_RECV(receive_facility_kpj)
   {
  	unsigned char cpt_tag, cp_len, invoke_id_tag, invoke_id_len, operation_value_len, operation_value_tag;
  	unsigned char arg_len = 0;
  	unsigned char pos = 0;
  	short invoke_id = 0, operation_value = 0;
  	if ((ie->data[pos++] & 0x1F) == 0x11) {
  	    /* service discriminator == supplementary services */
  	    cpt_tag = ie->data[pos++] & 0x1F;
  	    cp_len = ie->data[pos++];
  	    switch (cpt_tag) {
  		case 1: /* invoke */
  		        invoke_id_tag = ie->data[pos++];
  			if (invoke_id_tag != 0x02) {
  			//    pri_error(call->pri, "invoke id tag != 0x02\n");
  			    break;
  			}
  			invoke_id_len = ie->data[pos++]; // 4
  		        while (invoke_id_len > 0) {
  		    	    invoke_id = (invoke_id << 8) | (ie->data[pos++] & 0xFF);
  			    invoke_id_len--;
  			}
  			operation_value_tag = ie->data[pos++];
  			if (operation_value_tag != 0x02) {
  			//    pri_error(call->pri, "operation value tag != 0x02\n");
  			    break;
  			}
 			operation_value_len = ie->data[pos++];
 		        while (operation_value_len > 0) {
  		    	    operation_value = (operation_value << 8) | (ie->data[pos++] & 0xFF);
  			    operation_value_len--;
  			}
  			arg_len = ie->len - pos;
  			switch (operation_value) {
  			    case 0x06:	/* ECT execute */
  					call->facility = operation_value;
  					break;
  			    case 0x0D:	/* call deflection */
  			    		call->facility = operation_value;
  					/* dirty hack! */
  					arg_len -= 6;
  					if (arg_len > 0) {
  					    pos += 6;
   					    q931_get_number(call->redirectingnum, sizeof(call->redirectingnum), ie->data + pos, arg_len);
  					}
  					/* now retrieve the number */
  					break;
  			    case 0x22:  /* AOC-D */
  					break;
  			    case 0x24:  /* AOC-E */
  					break;
  			}
  		        break;
  		case 2: /* return result */
  			break;
  	        case 3: /* return error */
  			break;
  		case 4: /* reject */
  			break;
  	    }
  	} else {
  	    /* OLD DIRTY ULAW HACK */
  	    if (ie->len < 14) {
   		pri_error(call->pri, "!! Facility message shorter than 14 bytes\n");
   		return 0;
  	    }
  	    if (ie->data[13] + 14 == ie->len) {
   		q931_get_number(call->callername, sizeof(call->callername) - 1, ie->data + 14, ie->len - 14);
  	    } 
  	    call->facility = 0x0;
  	}
  	return 0;
   }
   
  static FUNC_SEND(transmit_facility_kpj)
  {
  	int i = 0;
  	return i;
  	if (call->aoc && (pri->switchtype == PRI_SWITCH_EUROISDN_E1) && ((pri->localtype == BRI_NETWORK) || (pri->localtype == BRI_NETWORK_PTMP) || (pri->localtype == PRI_NETWORK))) {
  	    ie->data[0] = 0x90;  /* PP remote operations */
  	    ie->data[i++] = 0x00; /* component tag */
  	    ie->data[i++] = 0x02; /* invoke id tag */
  	    ie->data[i++] = 0x02; /* invoke id len */
  	    ie->data[i++] = 0x34; /* invoke id */
  	    ie->data[i++] = 0x56; /* invoke id */
  	    ie->data[i++] = 0x02; /* operation value tag */
  	    ie->data[i++] = 0x01; /* operation value len */
  	    switch (msgtype) {
  		case Q931_SETUP:
 		    ie->data[i++] = 0x26; /* operation value AOC-S */
  		    break;
  		case Q931_DISCONNECT:
  		    ie->data[i++] = 0x24; /* operation value AOC-E */
  		    break;
  		default:
  		    ie->data[i++] = 0x22; /* operation value AOC-D */
  		    break;
  	    }
  	    // ARGUMENTS!	
  	}
  //	return 2 + i;
  }
#endif

static FUNC_SEND(transmit_facility)
{
	struct apdu_event *tmp;
	int i = 0;

	for (tmp = call->apdus; tmp; tmp = tmp->next) {
		if (tmp->message == msgtype)
			break;
	}

	if (!tmp)	/* No APDU found */
		return 0;

	if (tmp->apdu_len > 235) { /* TODO: find out how much sapce we can use */
		pri_message(pri, "Requested ADPU (%d bytes) is too long\n", tmp->apdu_len);


		return 0;
	}
	
	memcpy(ie->data, tmp->apdu, tmp->apdu_len);
	i += tmp->apdu_len;

	return i + 2;
}

#if 0
static FUNC_SEND(transmit_facility)
{
	int i = 0, j, first_i, compsp = 0;
	struct rose_component *comp, *compstk[10];
	unsigned char namelen = strlen(call->callername);

	if ((pri->switchtype == PRI_SWITCH_NI2) && (namelen > 15))
		namelen = 15; /* According to GR-1367, for NI2 switches it can't be > 15 characters */
	if ((namelen > 0) && ((pri->switchtype == PRI_SWITCH_QSIG) ||
			((pri->switchtype == PRI_SWITCH_NI2) && (pri->localtype == PRI_NETWORK)))) {
		do {
			first_i = i;
			ie->data[i] = 0x80 | Q932_PROTOCOL_EXTENSIONS;
			i++;
			/* Interpretation component */
			ASN1_ADD_BYTECOMP(comp, COMP_TYPE_INTERPRETATION, ie->data, i, 0x00 /* Discard unrecognized invokes */);

			/* Invoke ID */
			ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, ie->data, i);
			ASN1_PUSH(compstk, compsp, comp);

			/* Invoke component contents */
			/*	Invoke ID */
			ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, ie->data, i, ++pri->last_invoke);

			/*	Operation Tag */
			ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, ie->data, i, SS_CNID_CALLINGNAME);

			/* Arugement Tag */
			j = asn1_string_encode(ROSE_NAME_PRESENTATION_ALLOWED_SIMPLE, &ie->data[i], len - i, 15, call->callername, namelen);
			if (j < 0) {
				i = first_i;
				break;
			}
			i += j;

			/* Fix length of stacked components */
			while(compsp > 0) {
				ASN1_FIXUP(compstk, compsp, ie->data, i);
			}
		} while (0);
	}
	if (/*(pri->switchtype == PRI_SWITCH_EUROISDN_E1) &&*/ call->redirectingnum && strlen(call->redirectingnum)) {
		if (!(first_i = i)) {
			/* Add protocol information header */
			ie->data[i++] = 0x80 | Q932_PROTOCOL_ROSE;
		}

		/* ROSE invoke component */
		ASN1_ADD_SIMPLE(comp, COMP_TYPE_INVOKE, ie->data, i);
		ASN1_PUSH(compstk, compsp, comp);

		/* ROSE invokeId component */
		ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, ie->data, i, ++pri->last_invoke);

		/* ROSE operationId component */
		ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, ie->data, i, ROSE_DIVERTING_LEG_INFORMATION2);

		/* ROSE ARGUMENT component */
		ASN1_ADD_SIMPLE(comp, 0x30, ie->data, i);
		ASN1_PUSH(compstk, compsp, comp);

		/* ROSE DivertingLegInformation2.diversionCounter component */
		/* Always is 1 because other isn't available in the current design */
		ASN1_ADD_BYTECOMP(comp, ASN1_INTEGER, ie->data, i, 1);

		/* ROSE DivertingLegInformation2.diversionReason component */
		ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, ie->data, i, redirectingreason_from_q931(pri, call->redirectingreason));

		/* ROSE DivertingLegInformation2.divertingNr component */
		ASN1_ADD_SIMPLE(comp, 0xA1, ie->data, i);
		ASN1_PUSH(compstk, compsp, comp);

		/* Redirecting information always not screened */
		switch(call->redirectingpres) {
		case PRES_ALLOWED_USER_NUMBER_NOT_SCREENED:
		case PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN:
			if (call->redirectingnum && strlen(call->redirectingnum)) {
				ASN1_ADD_SIMPLE(comp, 0xA0, ie->data, i);
				ASN1_PUSH(compstk, compsp, comp);

				/* NPI of redirected number is not supported in the current design */
				ASN1_ADD_SIMPLE(comp, 0xA1, ie->data, i);
				ASN1_PUSH(compstk, compsp, comp);

				ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, ie->data, i, typeofnumber_from_q931(pri, call->redirectingplan >> 4));

				j = asn1_string_encode(ASN1_NUMERICSTRING, &ie->data[i], len - i, 20, call->redirectingnum, strlen(call->redirectingnum));
				if (j < 0) {
					i = first_i;
					goto finish2;
				}
				i += j;
				ASN1_FIXUP(compstk, compsp, ie->data, i);
				ASN1_FIXUP(compstk, compsp, ie->data, i);
				break;
			}
			/* fall through */
		case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN:
		case PRES_PROHIB_USER_NUMBER_NOT_SCREENED:
			ASN1_ADD_SIMPLE(comp, 0x81, ie->data, i);
			break;
		/* Don't know how to handle this */
		case PRES_ALLOWED_NETWORK_NUMBER:
		case PRES_PROHIB_NETWORK_NUMBER:
		case PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN:
		case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
			ASN1_ADD_SIMPLE(comp, 0x81, ie->data, i);
			break;
		default:
			pri_message(pri, "!! Undefined presentation value for redirecting number: %d\n", call->redirectingpres);
		case PRES_NUMBER_NOT_AVAILABLE:
			ASN1_ADD_SIMPLE(comp, 0x82, ie->data, i);
			break;
		}
		ASN1_FIXUP(compstk, compsp, ie->data, i);

		/* ROSE DivertingLegInformation2.originalCalledNr component */
		/* This information isn't supported by current design - duplicate divertingNr */
		ASN1_ADD_SIMPLE(comp, 0xA2, ie->data, i);
		ASN1_PUSH(compstk, compsp, comp);

		/* Redirecting information always not screened */
		switch(call->redirectingpres) {
		case PRES_ALLOWED_USER_NUMBER_NOT_SCREENED:
		case PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN:
			if (call->redirectingnum && strlen(call->redirectingnum)) {
				ASN1_ADD_SIMPLE(comp, 0xA0, ie->data, i);
				ASN1_PUSH(compstk, compsp, comp);

				ASN1_ADD_SIMPLE(comp, 0xA1, ie->data, i);
				ASN1_PUSH(compstk, compsp, comp);

				ASN1_ADD_BYTECOMP(comp, ASN1_ENUMERATED, ie->data, i, typeofnumber_from_q931(pri, call->redirectingplan >> 4));

				j = asn1_string_encode(ASN1_NUMERICSTRING, &ie->data[i], len - i, 20, call->redirectingnum, strlen(call->redirectingnum));
				if (j < 0) {
					i = first_i;
					goto finish2;
				}
				i += j;
				ASN1_FIXUP(compstk, compsp, ie->data, i);
				ASN1_FIXUP(compstk, compsp, ie->data, i);
				break;
			}
			/* fall through */
		case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN:
		case PRES_PROHIB_USER_NUMBER_NOT_SCREENED:
			ASN1_ADD_SIMPLE(comp, 0x81, ie->data, i);
			break;
		/* Don't know how to handle this */
		case PRES_ALLOWED_NETWORK_NUMBER:
		case PRES_PROHIB_NETWORK_NUMBER:
		case PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN:
		case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
			ASN1_ADD_SIMPLE(comp, 0x81, ie->data, i);
			break;
		default:
			pri_message(pri, "!! Undefined presentation value for redirecting number: %d\n", call->redirectingpres);
		case PRES_NUMBER_NOT_AVAILABLE:
			ASN1_ADD_SIMPLE(comp, 0x82, ie->data, i);
			break;
		}
		ASN1_FIXUP(compstk, compsp, ie->data, i);

		/* Fix length of stacked components */
		while(compsp > 0) {
			ASN1_FIXUP(compstk, compsp, ie->data, i);
		}
	}
finish2:
	return (i ? i+2 : 0);
}
#endif

static FUNC_RECV(receive_facility)
{
	int i = 0;
	int protocol, next_protocol;
	struct rose_component *comp = NULL;
	enum {
		Q932_STATE_NFE,				/* Network facility extension */
		Q932_STATE_NPP,				/* Network protocol profile */
		Q932_STATE_INTERPRETATION,	/* Interpretation component */
		Q932_STATE_SERVICE			/* Service component(s) */
	} state = Q932_STATE_SERVICE;
#define Q932_HANDLE_PROC(component, my_state, name, handler) \
				case component: \
					if(state > my_state) { \
						pri_error(pri, "!! %s component received in wrong place\n"); \
						break; \
					} \
					state = my_state; \
					if (pri->debug) \
						pri_message(pri, "Handle Q.932 %s component\n", name); \
					(handler)(pri, call, comp->data, comp->len); \
					break;
#define Q932_HANDLE_NULL(component, my_state, name, handle) \
				case component: \
					if(state > my_state) { \
						pri_error(pri, "!! %s component received in wrong place\n"); \
						break; \
					} \
					state = my_state; \
					if (pri->debug & PRI_DEBUG_APDU) \
						pri_message(pri, "Q.932 %s component is not handled\n", name); \
					break;

	if (ie->len < 1)
		return -1;

	if ((ie->data[i] & 0xe0) != 0x80) {
		pri_error(pri, "!! Invalid Protocol Profile field 0x%X\n", ie->data[i]);
		return -1;
	}
	switch(next_protocol = protocol = (ie->data[i] & 0x1f)) {
	case Q932_PROTOCOL_CMIP:
	case Q932_PROTOCOL_ACSE:
		if (pri->debug & PRI_DEBUG_APDU)
			pri_message(pri, "!! Don't know how to handle Q.932 Protocol Profile of type 0x%X\n", protocol);
		return -1;
	case Q932_PROTOCOL_EXTENSIONS:
		state = Q932_STATE_NFE;
		next_protocol = Q932_PROTOCOL_ROSE;
		break;
	case Q932_PROTOCOL_ROSE:
		break;
	default:
		pri_error(pri, "!! Invalid Q.932 Protocol Profile of type 0x%X received\n", protocol);
		return -1;
	}
	i++;

	if (ie->len < 3)
		return -1;
	
	while ((i+1 < ie->len) && (&ie->data[i])) {
		comp = (struct rose_component*)&ie->data[i];
		if (comp->type) {
			if (protocol == Q932_PROTOCOL_EXTENSIONS) {
				switch (comp->type) {
				Q932_HANDLE_NULL(COMP_TYPE_INTERPRETATION, Q932_STATE_INTERPRETATION, "Interpretation", NULL);
				Q932_HANDLE_NULL(COMP_TYPE_NFE, Q932_STATE_NFE, "Network facility extensions", NULL);
				Q932_HANDLE_NULL(COMP_TYPE_NETWORK_PROTOCOL_PROFILE, Q932_STATE_NPP, "Network protocol profile", NULL);
				default:
					protocol = next_protocol;
					break;
				}
			}
			switch (protocol) {
			case Q932_PROTOCOL_ROSE:
				switch (comp->type) {
				Q932_HANDLE_PROC(COMP_TYPE_INVOKE, Q932_STATE_SERVICE, "ROSE Invoke", rose_invoke_decode);
				Q932_HANDLE_NULL(COMP_TYPE_RETURN_RESULT, Q932_STATE_SERVICE, "ROSE return result", NULL);
				Q932_HANDLE_NULL(COMP_TYPE_RETURN_ERROR, Q932_STATE_SERVICE, "ROSE return error", NULL);
				Q932_HANDLE_NULL(COMP_TYPE_REJECT, Q932_STATE_SERVICE, "ROSE reject", NULL);
				default:
					if (pri->debug & PRI_DEBUG_APDU)
						pri_message(pri, "Don't know how to handle ROSE component of type 0x%X\n", comp->type);
					break;
				}
				break;
			case Q932_PROTOCOL_CMIP:
				switch (comp->type) {
				default:
					if (pri->debug & PRI_DEBUG_APDU)
						pri_message(pri, "Don't know how to handle CMIP component of type 0x%X\n", comp->type);
					break;
				}
				break;
			case Q932_PROTOCOL_ACSE:
				switch (comp->type) {
				default:
					if (pri->debug & PRI_DEBUG_APDU)
						pri_message(pri, "Don't know how to handle ACSE component of type 0x%X\n", comp->type);
					break;
				}
				break;
			}
		}
		i += (comp->len + 2);
	}
#undef Q932_HANDLE

	return 0;
}

static FUNC_SEND(transmit_progress_indicator)
{
	int code, mask;
	/* Can't send progress indicator on GR-303 -- EVER! */
	if (pri->subchannel)
		return 0;
	if (call->progressmask > 0) {
		if (call->progressmask & (mask = PRI_PROG_CALL_NOT_E2E_ISDN))
			code = Q931_PROG_CALL_NOT_E2E_ISDN;
		else if (call->progressmask & (mask = PRI_PROG_CALLED_NOT_ISDN))
			code = Q931_PROG_CALLED_NOT_ISDN;
		else if (call->progressmask & (mask = PRI_PROG_CALLER_NOT_ISDN))
			code = Q931_PROG_CALLER_NOT_ISDN;
		else if (call->progressmask & (mask = PRI_PROG_INBAND_AVAILABLE))
			code = Q931_PROG_INBAND_AVAILABLE;
		else if (call->progressmask & (mask = PRI_PROG_DELAY_AT_INTERF))
			code = Q931_PROG_DELAY_AT_INTERF;
		else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_WITH_PUBLIC))
			code = Q931_PROG_INTERWORKING_WITH_PUBLIC;
		else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_NO_RELEASE))
			code = Q931_PROG_INTERWORKING_NO_RELEASE;
		else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER))
			code = Q931_PROG_INTERWORKING_NO_RELEASE_PRE_ANSWER;
		else if (call->progressmask & (mask = PRI_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER))
			code = Q931_PROG_INTERWORKING_NO_RELEASE_POST_ANSWER;
		else {
			code = 0;
			pri_error(pri, "XXX Undefined progress bit: %x\n", call->progressmask);
		}
		if (code) {
			ie->data[0] = 0x80 | (call->progcode << 5)  | (call->progloc);
			ie->data[1] = 0x80 | code;
			call->progressmask &= ~mask;
			return 4;
		}
	}
	/* Leave off */
	return 0;
}
static FUNC_SEND(transmit_call_state)
{
	if (call->ourcallstate > -1 ) {
		ie->data[0] = call->ourcallstate;
		return 3;
	}
	return 0;
}

static FUNC_RECV(receive_call_state)
{
	call->sugcallstate = ie->data[0] & 0x3f;
	return 0;
}

static char *callstate2str(int callstate)
{
	static struct msgtype callstates[] = {
		{ 0, "Null" },
		{ 1, "Call Initiated" },
		{ 2, "Overlap sending" },
		{ 3, "Outgoing call  Proceeding" },
		{ 4, "Call Delivered" },
		{ 6, "Call Present" },
		{ 7, "Call Received" },
		{ 8, "Connect Request" },
		{ 9, "Incoming Call Proceeding" },
		{ 10, "Active" },
		{ 11, "Disconnect Request" },
		{ 12, "Disconnect Indication" },
		{ 15, "Suspend Request" },
		{ 17, "Resume Request" },
		{ 19, "Release Request" },
		{ 22, "Call Abort" },
		{ 25, "Overlap Receiving" },
		{ 61, "Restart Request" },
		{ 62, "Restart" },
	};
	return code2str(callstate, callstates, sizeof(callstates) / sizeof(callstates[0]));
}

static FUNC_DUMP(dump_call_state)
{
	pri_message(pri, "%c Call State (len=%2d) [ Ext: %d  Coding: %s (%d) Call state: %s (%d)\n",
		prefix, len, ie->data[0] >> 7, coding2str((ie->data[0] & 0xC0) >> 6), (ie->data[0] & 0xC0) >> 6,
		callstate2str(ie->data[0] & 0x3f), ie->data[0] & 0x3f);
}

static FUNC_DUMP(dump_call_identity)
{
	int x;
	pri_message(pri, "%c Call Identity (len=%2d) [ ", prefix, len);
	for (x=0;x<ie->len;x++) 
		pri_message(pri, "0x%02X ", ie->data[x]);
	pri_message(pri, " ]\n");
}


static FUNC_DUMP(dump_time_date)
{
	pri_message(pri, "%c Time Date (len=%2d) [ ", prefix, len);
	if (ie->len > 0)
		pri_message(pri, "%02d", ie->data[0]);
	if (ie->len > 1)
		pri_message(pri, "-%02d", ie->data[1]);
	if (ie->len > 2)
		pri_message(pri, "-%02d", ie->data[2]);
	if (ie->len > 3)
		pri_message(pri, " %02d", ie->data[3]);
	if (ie->len > 4)
		pri_message(pri, ":%02d", ie->data[4]);
	if (ie->len > 5)
		pri_message(pri, ":%02d", ie->data[5]);
	pri_message(pri, " ]\n");
}

static FUNC_DUMP(dump_keypad_facility)
{
	char tmp[64] = "";
	
	if (ie->len == 0 || ie->len > sizeof(tmp))
		return;
	
	libpri_copy_string(tmp, (char *) ie->data, sizeof(tmp));
	pri_message(pri, "%c Keypad Facility (len=%2d) [ %s ]\n", prefix, ie->len, tmp );
}

static FUNC_RECV(receive_keypad_facility)
{
	int mylen = 0;

	if (ie->len == 0)
		return -1;

	if (ie->len > (sizeof(call->digitbuf) - 1))
		mylen = sizeof(call->digitbuf) - 1;
	else
		mylen = ie->len;

	strncpy(call->digitbuf, (char *) ie->data, mylen);

	/* I must be really neurotic */
	call->digitbuf[sizeof(call->digitbuf)-1] = '\0';

	return 0;
}

static FUNC_RECV(receive_time_date)
{
    return 0;
}

static FUNC_SEND(transmit_time_date) {
    time_t now;
    struct tm *timedate;
    time(&now);
    timedate = localtime(&now);
    ie->data[0] = timedate->tm_year - 100; // 1900+
    ie->data[1] = timedate->tm_mon + 1;
    ie->data[2] = timedate->tm_mday;
    ie->data[3] = timedate->tm_hour;
    ie->data[4] = timedate->tm_min;
    return 7;
}



static FUNC_DUMP(dump_display)
{
	int x, y;
	char *buf = malloc(len + 1);
	char tmp[80]="";
	if (buf) {
		x=y=0;
		if ((x < ie->len) && (ie->data[x] & 0x80)) {
			sprintf(tmp, "Charset: %02x ", ie->data[x] & 0x7f);
			++x;
		}
		for (y=x; x<ie->len; x++) 
			buf[x] = ie->data[x] & 0x7f;
		buf[x] = '\0';
		pri_message(pri, "%c Display (len=%2d) %s[ %s ]\n", prefix, ie->len, tmp, &buf[y]);
		free(buf);
	}
}

static void dump_ie_data(struct pri *pri, unsigned char *c, int len)
{
	char tmp[1024] = "";
	int x=0;
	int lastascii = 0;
	while(len) {
		if (((*c >= 'A') && (*c <= 'Z')) ||
		    ((*c >= 'a') && (*c <= 'z')) ||
		    ((*c >= '0') && (*c <= '9'))) {
			if (!lastascii) {
				if (strlen(tmp)) { 
					tmp[x++] = ',';
					tmp[x++] = ' ';
				}
				tmp[x++] = '\'';
			}
			tmp[x++] = *c;
			lastascii = 1;
		} else {
			if (lastascii) {
				tmp[x++] = '\'';
			}
			if (strlen(tmp)) { 
				tmp[x++] = ',';
				tmp[x++] = ' ';
			}
			sprintf (tmp + x, "0x%02x", *c);
			x += 4;
			lastascii = 0;
		}
		c++;
		len--;
	}
	if (lastascii)
		tmp[x++] = '\'';
	pri_message(pri, tmp);
}

static FUNC_DUMP(dump_facility)
{
	pri_message(pri, "%c Facility (len=%2d, codeset=%d) [ ", prefix, len, Q931_IE_CODESET(full_ie));
	dump_ie_data(pri, ie->data, ie->len);
	pri_message(pri, " ]\n");
}

static FUNC_DUMP(dump_network_spec_fac)
{
	pri_message(pri, "%c Network-Specific Facilities (len=%2d) [ ", prefix, ie->len);
	if (ie->data[0] == 0x00) {
		pri_message(pri, code2str(ie->data[1], facilities, sizeof(facilities) / sizeof(facilities[0])));
	}
	else
		dump_ie_data(pri, ie->data, ie->len);
	pri_message(pri, " ]\n");
}

static FUNC_RECV(receive_network_spec_fac)
{
	return 0;
}

static FUNC_SEND(transmit_network_spec_fac)
{
	/* We are ready to transmit single IE only */
	if (order > 1)
		return 0;

	if (pri->nsf != PRI_NSF_NONE) {
		ie->data[0] = 0x00;
		ie->data[1] = pri->nsf;
		return 4;
	}
	/* Leave off */
	return 0;
}

char *pri_cause2str(int cause)
{
	return code2str(cause, causes, sizeof(causes) / sizeof(causes[0]));
}

static char *pri_causeclass2str(int cause)
{
	static struct msgtype causeclasses[] = {
		{ 0, "Normal Event" },
		{ 1, "Normal Event" },
		{ 2, "Network Congestion" },
		{ 3, "Service or Option not Available" },
		{ 4, "Service or Option not Implemented" },
		{ 5, "Invalid message" },
		{ 6, "Protocol Error" },
		{ 7, "Interworking" },
	};
	return code2str(cause, causeclasses, sizeof(causeclasses) / sizeof(causeclasses[0]));
}

static FUNC_DUMP(dump_cause)
{
	int x;
	pri_message(pri, "%c Cause (len=%2d) [ Ext: %d  Coding: %s (%d) 0: %d   Location: %s (%d)\n",
		prefix, len, ie->data[0] >> 7, coding2str((ie->data[0] & 0x60) >> 5), (ie->data[0] & 0x60) >> 5,
		(ie->data[0] & 0x10) >> 4, loc2str(ie->data[0] & 0xf), ie->data[0] & 0xf);
	pri_message(pri, "%c                  Ext: %d  Cause: %s (%d), class = %s (%d) ]\n",
		prefix, (ie->data[1] >> 7), pri_cause2str(ie->data[1] & 0x7f), ie->data[1] & 0x7f, 
			pri_causeclass2str((ie->data[1] & 0x7f) >> 4), (ie->data[1] & 0x7f) >> 4);
	if (ie->len < 3)
		return;
	/* Dump cause data in readable form */
	switch(ie->data[1] & 0x7f) {
	case PRI_CAUSE_IE_NONEXIST:
		for (x=2;x<ie->len;x++) 
			pri_message(pri, "%c              Cause data %d: %02x (%d, %s IE)\n", prefix, x-1, ie->data[x], ie->data[x], ie2str(ie->data[x]));
		break;
	case PRI_CAUSE_WRONG_CALL_STATE:
		for (x=2;x<ie->len;x++) 
			pri_message(pri, "%c              Cause data %d: %02x (%d, %s message)\n", prefix, x-1, ie->data[x], ie->data[x], msg2str(ie->data[x]));
		break;
	case PRI_CAUSE_RECOVERY_ON_TIMER_EXPIRE:
		pri_message(pri, "%c              Cause data:", prefix);
		for (x=2;x<ie->len;x++)
			pri_message(pri, " %02x", ie->data[x]);
		pri_message(pri, " (Timer T");
		for (x=2;x<ie->len;x++)
			pri_message(pri, "%c", ((ie->data[x] >= ' ') && (ie->data[x] < 0x7f)) ? ie->data[x] : '.');
		pri_message(pri, ")\n");
		break;
	default:
		for (x=2;x<ie->len;x++) 
			pri_message(pri, "%c              Cause data %d: %02x (%d)\n", prefix, x-1, ie->data[x], ie->data[x]);
		break;
	}
}

static FUNC_RECV(receive_cause)
{
	call->causeloc = ie->data[0] & 0xf;
	call->causecode = (ie->data[0] & 0x60) >> 5;
	call->cause = (ie->data[1] & 0x7f);
	return 0;
}

static FUNC_SEND(transmit_cause)
{
	/* We are ready to transmit single IE only */
	if (order > 1)
		return 0;

	if (call->cause > 0) {
		ie->data[0] = 0x80 | (call->causecode << 5)  | (call->causeloc);
		ie->data[1] = 0x80 | (call->cause);
		return 4;
	} else {
		/* Leave off */
		return 0;
	}
}

static FUNC_DUMP(dump_sending_complete)
{
	pri_message(pri, "%c Sending Complete (len=%2d)\n", prefix, len);
}

static FUNC_RECV(receive_sending_complete)
{
	/* We've got a "Complete" message: Exect no further digits. */
	call->complete = 1; 
	return 0;
}

static FUNC_SEND(transmit_sending_complete)
{
	if ((pri->overlapdial && call->complete) || /* Explicit */
		(!pri->overlapdial && ((pri->switchtype == PRI_SWITCH_EUROISDN_E1) || 
		/* Implicit */   	   (pri->switchtype == PRI_SWITCH_EUROISDN_T1)))) {
		/* Include this single-byte IE */
		return 1;
	}
	return 0;
}

static char *notify2str(int info)
{
	/* ITU-T Q.763 */
	static struct msgtype notifies[] = {
		{ PRI_NOTIFY_USER_SUSPENDED, "User suspended" },
		{ PRI_NOTIFY_USER_RESUMED, "User resumed" },
		{ PRI_NOTIFY_BEARER_CHANGE, "Bearer service change (DSS1)" },
		{ PRI_NOTIFY_ASN1_COMPONENT, "ASN.1 encoded component (DSS1)" },
		{ PRI_NOTIFY_COMPLETION_DELAY, "Call completion delay" },
		{ PRI_NOTIFY_CONF_ESTABLISHED, "Conference established" },
		{ PRI_NOTIFY_CONF_DISCONNECTED, "Conference disconnected" },
		{ PRI_NOTIFY_CONF_PARTY_ADDED, "Other party added" },
		{ PRI_NOTIFY_CONF_ISOLATED, "Isolated" },
		{ PRI_NOTIFY_CONF_REATTACHED, "Reattached" },
		{ PRI_NOTIFY_CONF_OTHER_ISOLATED, "Other party isolated" },
		{ PRI_NOTIFY_CONF_OTHER_REATTACHED, "Other party reattached" },
		{ PRI_NOTIFY_CONF_OTHER_SPLIT, "Other party split" },
		{ PRI_NOTIFY_CONF_OTHER_DISCONNECTED, "Other party disconnected" },
		{ PRI_NOTIFY_CONF_FLOATING, "Conference floating" },
		{ PRI_NOTIFY_WAITING_CALL, "Call is waiting call" },
		{ PRI_NOTIFY_DIVERSION_ACTIVATED, "Diversion activated (DSS1)" },
		{ PRI_NOTIFY_TRANSFER_ALERTING, "Call transfer, alerting" },
		{ PRI_NOTIFY_TRANSFER_ACTIVE, "Call transfer, active" },
		{ PRI_NOTIFY_REMOTE_HOLD, "Remote hold" },
		{ PRI_NOTIFY_REMOTE_RETRIEVAL, "Remote retrieval" },
		{ PRI_NOTIFY_CALL_DIVERTING, "Call is diverting" },
	};
	return code2str(info, notifies, sizeof(notifies) / sizeof(notifies[0]));
}

static FUNC_DUMP(dump_notify)
{
	pri_message(pri, "%c Notification indicator (len=%2d): Ext: %d  %s (%d)\n", prefix, len, ie->data[0] >> 7, notify2str(ie->data[0] & 0x7f), ie->data[0] & 0x7f);
}

static FUNC_RECV(receive_notify)
{
	call->notify = ie->data[0] & 0x7F;
	return 0;
}

static FUNC_SEND(transmit_notify)
{
	if (call->notify >= 0) {
		ie->data[0] = 0x80 | call->notify;
		return 3;
	}
	return 0;
}

static FUNC_DUMP(dump_shift)
{
	pri_message(pri, "%c %sLocking Shift (len=%02d): Requested codeset %d\n", prefix, (full_ie & 8) ? "Non-" : "", len, full_ie & 7);
}

static char *lineinfo2str(int info)
{
	/* NAPNA ANI II digits */
	static struct msgtype lineinfo[] = {
		{  0, "Plain Old Telephone Service (POTS)" },
		{  1, "Multiparty line (more than 2)" },
		{  2, "ANI failure" },
		{  6, "Station Level Rating" },
		{  7, "Special Operator Handling Required" },
		{ 20, "Automatic Identified Outward Dialing (AIOD)" },
		{ 23, "Coing or Non-Coin" },
		{ 24, "Toll free translated to POTS originated for non-pay station" },
		{ 25, "Toll free translated to POTS originated from pay station" },
		{ 27, "Pay station with coin control signalling" },
		{ 29, "Prison/Inmate Service" },
		{ 30, "Intercept (blank)" },
		{ 31, "Intercept (trouble)" },
		{ 32, "Intercept (regular)" },
		{ 34, "Telco Operator Handled Call" },
		{ 52, "Outward Wide Area Telecommunications Service (OUTWATS)" },
		{ 60, "TRS call from unrestricted line" },
		{ 61, "Cellular/Wireless PCS (Type 1)" },
		{ 62, "Cellular/Wireless PCS (Type 2)" },
		{ 63, "Cellular/Wireless PCS (Roaming)" },
		{ 66, "TRS call from hotel/motel" },
		{ 67, "TRS call from restricted line" },
		{ 70, "Line connected to pay station" },
		{ 93, "Private virtual network call" },
	};
	return code2str(info, lineinfo, sizeof(lineinfo) / sizeof(lineinfo[0]));
}

static FUNC_DUMP(dump_line_information)
{
	pri_message(pri, "%c Originating Line Information (len=%02d): %s (%d)\n", prefix, len, lineinfo2str(ie->data[0]), ie->data[0]);
}

static FUNC_RECV(receive_line_information)
{
	call->ani2 = ie->data[0];
	return 0;
}

static FUNC_SEND(transmit_line_information)
{
#if 0	/* XXX Is this IE possible for 4ESS only? XXX */
	if(pri->switchtype == PRI_SWITCH_ATT4ESS) {
		ie->data[0] = 0;
		return 3;
	}
#endif
	return 0;
}


static char *gdencoding2str(int encoding)
{
	static struct msgtype gdencoding[] = {
		{ 0, "BCD even" },
		{ 1, "BCD odd" },
		{ 2, "IA5" },
		{ 3, "Binary" },
	};
	return code2str(encoding, gdencoding, sizeof(gdencoding) / sizeof(gdencoding[0]));
}

static char *gdtype2str(int type)
{
	static struct msgtype gdtype[] = {
		{  0, "Account Code" },
		{  1, "Auth Code" },
		{  2, "Customer ID" },
		{  3, "Universal Access" },
		{  4, "Info Digits" },
		{  5, "Callid" },
		{  6, "Opart" },
		{  7, "TCN" },
		{  9, "Adin" },
	};
	return code2str(type, gdtype, sizeof(gdtype) / sizeof(gdtype[0]));
}

static FUNC_DUMP(dump_generic_digits)
{
	int encoding;
	int type;
	int idx;
	int value;
	if (len < 3) {
		pri_message(pri, "%c Generic Digits (len=%02d): Invalid length\n", prefix, len);
		return;
	}
	encoding = (ie->data[0] >> 5) & 7;
	type = ie->data[0] & 0x1F;
	pri_message(pri, "%c Generic Digits (len=%02d): Encoding %s  Type %s\n", prefix, len, gdencoding2str(encoding), gdtype2str(type));
	if (encoding == 3) {	/* Binary */
		pri_message(pri, "%c                            Don't know how to handle binary encoding\n");
		return;
	}
	if (len == 3)	/* No number information */
		return;
	pri_message(pri, "%c                            Digits: ");
	value = 0;
	for(idx = 3; idx < len; ++idx) {
		switch(encoding) {
		case 0:		/* BCD even */
		case 1:		/* BCD odd */
			pri_message(pri, "%d", ie->data[idx-2] & 0x0f);
			value = value * 10 + (ie->data[idx-2] & 0x0f);
			if(!encoding || (idx+1 < len)) {	/* Special handling for BCD odd */
				pri_message(pri, "%d", (ie->data[idx-2] >> 4) & 0x0f);
				value = value * 10 + ((ie->data[idx-2] >> 4) & 0x0f);
			}
			break;
		case 2:		/* IA5 */
			pri_message(pri, "%c", ie->data[idx-2]);
			value = value * 10 + ie->data[idx-2] - '0';
			break;
		}
	}
	switch(type) {
		case 4:		/* Info Digits */
			pri_message(pri, " - %s", lineinfo2str(value));
			break;
	}
	pri_message(pri, "\n");
}

static FUNC_RECV(receive_generic_digits)
{
	int encoding;
	int type;
	int idx;
	int value;
	int num_idx;
	char number[260];

	if (len < 3) {
		pri_error(pri, "Invalid length of Generic Digits IE\n");
		return -1;
	}
	encoding = (ie->data[0] >> 5) & 7;
	type = ie->data[0] & 0x1F;
	if (encoding == 3) {	/* Binary */
		pri_message(pri, "!! Unable to handle binary encoded Generic Digits IE\n");
		return 0;
	}
	if (len == 3)	/* No number information */
		return 0;
	value = 0;
	switch(type) {
	/* Integer value handling */
	case 4:		/* Info Digits */
		for(idx = 3; idx < len; ++idx) {
			switch(encoding) {
			case 0:		/* BCD even */
			case 1:		/* BCD odd */
				value = value * 10 + (ie->data[idx-2] & 0x0f);
				if(!encoding || (idx+1 < len))	/* Special handling for BCD odd */
					value = value * 10 + ((ie->data[idx-2] >> 4) & 0x0f);
				break;
			case 2:		/* IA5 */
				value = value * 10 + (ie->data[idx-2] - '0');
				break;
			}
		}
		break;
	/* String value handling */
	case 5:		/* Callid */
		num_idx = 0;
		for(idx = 3; (idx < len) && (num_idx < sizeof(number) - 4); ++idx) {
			switch(encoding) {
			case 0:		/* BCD even */
			case 1:		/* BCD odd */
				number[num_idx++] = '0' + (ie->data[idx-2] & 0x0f);
				if(!encoding || (idx+1 < len))	/* Special handling for BCD odd */
					number[num_idx++] = '0' + ((ie->data[idx-2] >> 4) & 0x0f);
				break;
			case 2:
				number[num_idx++] = ie->data[idx-2];
				break;
			}
		}
		number[num_idx] = '\0';
		break;
	}
	switch(type) {
	case 4:		/* Info Digits */
		call->ani2 = value;
		break;
#if 0
	case 5:		/* Callid */
		if (!call->callernum[0]) {
			memcpy(call->callernum, number, sizeof(call->callernum)-1);
			call->callerpres = 0;
			call->callerplan = 0;
		}
		break;
#endif
	}
	return 0;
}

static FUNC_SEND(transmit_generic_digits)
{
#if 0	/* XXX Is this IE possible for other switches? XXX */
	if (order > 1)
		return 0;

	if(pri->switchtype == PRI_SWITCH_NI1) {
		ie->data[0] = 0x04;	/* BCD even, Info Digits */
		ie->data[1] = 0x00;	/* POTS */
		return 4;
	}
#endif
	return 0;
}


static char *signal2str(int signal)
{
	/* From Q.931 4.5.8 Table 4-24 */
	static struct msgtype mtsignal[] = {
		{  0, "Dial tone" },
		{  1, "Ring back tone" },
		{  2, "Intercept tone" },
		{  3, "Network congestion tone" },
		{  4, "Busy tone" },
		{  5, "Confirm tone" },
		{  6, "Answer tone" },
		{  7, "Call waiting tone" },
		{  8, "Off-hook warning tone" },
		{  9, "Pre-emption tone" },
		{ 63, "Tones off" },
		{ 64, "Alerting on - pattern 0" },
		{ 65, "Alerting on - pattern 1" },
		{ 66, "Alerting on - pattern 2" },
		{ 67, "Alerting on - pattern 3" },
		{ 68, "Alerting on - pattern 4" },
		{ 69, "Alerting on - pattern 5" },
		{ 70, "Alerting on - pattern 6" },
		{ 71, "Alerting on - pattern 7" },
		{ 79, "Alerting off" },
	};
	return code2str(signal, mtsignal, sizeof(mtsignal) / sizeof(mtsignal[0]));
}


static FUNC_DUMP(dump_signal)
{
	pri_message(pri, "%c Signal (len=%02d): ", prefix, len);
	if (len < 3) {
		pri_message(pri, "Invalid length\n");
		return;
	}
	pri_message(pri, "Signal %s (%d)\n", signal2str(ie->data[0]), ie->data[0]);
}


struct ie ies[] = {
	/* Codeset 0 - Common */
	{ 1, NATIONAL_CHANGE_STATUS, "Change Status" },
	{ 0, Q931_LOCKING_SHIFT, "Locking Shift", dump_shift },
	{ 0, Q931_BEARER_CAPABILITY, "Bearer Capability", dump_bearer_capability, receive_bearer_capability, transmit_bearer_capability },
	{ 0, Q931_CAUSE, "Cause", dump_cause, receive_cause, transmit_cause },
	{ 1, Q931_CALL_STATE, "Call State", dump_call_state, receive_call_state, transmit_call_state },
	{ 0, Q931_CHANNEL_IDENT, "Channel Identification", dump_channel_id, receive_channel_id, transmit_channel_id },
	{ 0, Q931_PROGRESS_INDICATOR, "Progress Indicator", dump_progress_indicator, receive_progress_indicator, transmit_progress_indicator },
	{ 0, Q931_NETWORK_SPEC_FAC, "Network-Specific Facilities", dump_network_spec_fac, receive_network_spec_fac, transmit_network_spec_fac },
	{ 1, Q931_INFORMATION_RATE, "Information Rate" },
	{ 1, Q931_TRANSIT_DELAY, "End-to-End Transit Delay" },
	{ 1, Q931_TRANS_DELAY_SELECT, "Transmit Delay Selection and Indication" },
	{ 1, Q931_BINARY_PARAMETERS, "Packet-layer Binary Parameters" },
	{ 1, Q931_WINDOW_SIZE, "Packet-layer Window Size" },
	{ 1, Q931_CLOSED_USER_GROUP, "Closed User Group" },
	{ 1, Q931_REVERSE_CHARGE_INDIC, "Reverse Charging Indication" },
	{ 1, Q931_CALLING_PARTY_NUMBER, "Calling Party Number", dump_calling_party_number, receive_calling_party_number, transmit_calling_party_number },
	{ 1, Q931_CALLING_PARTY_SUBADDR, "Calling Party Subaddress", dump_calling_party_subaddr, receive_calling_party_subaddr },
	{ 1, Q931_CALLED_PARTY_NUMBER, "Called Party Number", dump_called_party_number, receive_called_party_number, transmit_called_party_number },
	{ 1, Q931_CALLED_PARTY_SUBADDR, "Called Party Subaddress", dump_called_party_subaddr },
	{ 0, Q931_REDIRECTING_NUMBER, "Redirecting Number", dump_redirecting_number, receive_redirecting_number, transmit_redirecting_number },
	{ 1, Q931_REDIRECTING_SUBADDR, "Redirecting Subaddress", dump_redirecting_subaddr },
	{ 0, Q931_TRANSIT_NET_SELECT, "Transit Network Selection" },
	{ 1, Q931_RESTART_INDICATOR, "Restart Indicator", dump_restart_indicator, receive_restart_indicator, transmit_restart_indicator },
	{ 0, Q931_LOW_LAYER_COMPAT, "Low-layer Compatibility" , dump_low_layer_compat, receive_low_layer_compat, transmit_low_layer_compat },
	{ 1, Q931_HIGH_LAYER_COMPAT, "High-layer Compatibility" , dump_high_layer_compat, receive_high_layer_compat, transmit_high_layer_compat },
	{ 1, Q931_PACKET_SIZE, "Packet Size" },
	{ 1, Q931_IE_FACILITY, "Facility" , dump_facility, receive_facility, transmit_facility },
	{ 1, Q931_IE_REDIRECTION_NUMBER, "Redirection Number" },
	{ 1, Q931_IE_REDIRECTION_SUBADDR, "Redirection Subaddress" },
	{ 1, Q931_IE_FEATURE_ACTIVATE, "Feature Activation" },
	{ 1, Q931_IE_INFO_REQUEST, "Feature Request" },
	{ 1, Q931_IE_FEATURE_IND, "Feature Indication" },
	{ 1, Q931_IE_SEGMENTED_MSG, "Segmented Message" },
 	{ 1, Q931_IE_CALL_IDENTITY, "Call Identity", dump_call_identity, receive_call_identity, transmit_call_identity },
	{ 1, Q931_IE_ENDPOINT_ID, "Endpoint Identification" },
	{ 1, Q931_IE_NOTIFY_IND, "Notification Indicator", dump_notify, receive_notify, transmit_notify },
	{ 1, Q931_DISPLAY, "Display", dump_display, receive_display, transmit_display },
 	{ 1, Q931_IE_TIME_DATE, "Date/Time", dump_time_date, receive_time_date, transmit_time_date },
	{ 1, Q931_IE_KEYPAD_FACILITY, "Keypad Facility", dump_keypad_facility, receive_keypad_facility },
	{ 0, Q931_IE_SIGNAL, "Signal", dump_signal },
	{ 1, Q931_IE_SWITCHHOOK, "Switch-hook" },
	{ 1, Q931_IE_USER_USER, "User-User", dump_user_user, receive_user_user, transmit_user_user },
	{ 1, Q931_IE_ESCAPE_FOR_EXT, "Escape for Extension" },
	{ 1, Q931_IE_CALL_STATUS, "Call Status" },
	{ 1, Q931_IE_CHANGE_STATUS, "Change Status" },
	{ 1, Q931_COLP, "Connect Line ID Presentation", dump_colp},
	{ 1, Q931_IE_CONNECTED_ADDR, "Connected Number", dump_connected_number },
	{ 1, Q931_IE_CONNECTED_NUM, "Connected Number", dump_connected_number },
	{ 1, Q931_IE_ORIGINAL_CALLED_NUMBER, "Original Called Number", dump_redirecting_number, receive_redirecting_number, transmit_redirecting_number },
	{ 1, Q931_IE_USER_USER_FACILITY, "User-User Facility" },
	{ 1, Q931_IE_UPDATE, "Update" },
	{ 1, Q931_SENDING_COMPLETE, "Sending Complete", dump_sending_complete, receive_sending_complete, transmit_sending_complete },
	/* Codeset 6 - Network specific */
	{ 1, Q931_IE_ORIGINATING_LINE_INFO, "Originating Line Information", dump_line_information, receive_line_information, transmit_line_information },
	{ 1, Q931_IE_FACILITY | Q931_CODESET(6), "Facility", dump_facility, receive_facility, transmit_facility },
	{ 1, Q931_DISPLAY | Q931_CODESET(6), "Display (CS6)", dump_display, receive_display, transmit_display },
	{ 0, Q931_IE_GENERIC_DIGITS, "Generic Digits", dump_generic_digits, receive_generic_digits, transmit_generic_digits },
	/* Codeset 7 */
};

static char *ie2str(int ie) 
{
	unsigned int x;

	/* Special handling for Locking/Non-Locking Shifts */
	switch (ie & 0xf8) {
	case Q931_LOCKING_SHIFT:
		switch (ie & 7) {
		case 0:
			return "!! INVALID Locking Shift To Codeset 0";
		case 1:
			return "Locking Shift To Codeset 1";
		case 2:
			return "Locking Shift To Codeset 2";
		case 3:
			return "Locking Shift To Codeset 3";
		case 4:
			return "Locking Shift To Codeset 4";
		case 5:
			return "Locking Shift To Codeset 5";
		case 6:
			return "Locking Shift To Codeset 6";
		case 7:
			return "Locking Shift To Codeset 7";
		}
	case Q931_NON_LOCKING_SHIFT:
		switch (ie & 7) {
		case 0:
			return "Non-Locking Shift To Codeset 0";
		case 1:
			return "Non-Locking Shift To Codeset 1";
		case 2:
			return "Non-Locking Shift To Codeset 2";
		case 3:
			return "Non-Locking Shift To Codeset 3";
		case 4:
			return "Non-Locking Shift To Codeset 4";
		case 5:
			return "Non-Locking Shift To Codeset 5";
		case 6:
			return "Non-Locking Shift To Codeset 6";
		case 7:
			return "Non-Locking Shift To Codeset 7";
		}
	default:
		for (x=0;x<sizeof(ies) / sizeof(ies[0]); x++) 
			if (ie == ies[x].ie)
				return ies[x].name;
		return "Unknown Information Element";
	}
}	

static inline unsigned int ielen(q931_ie *ie)
{
	if ((ie->ie & 0x80) != 0)
		return 1;
	else 
		return 2 + ie->len;
}

static char *msg2str(int msg)
{
	unsigned int x;
	for (x=0;x<sizeof(msgs) / sizeof(msgs[0]); x++) 
		if (msgs[x].msgnum == msg)
			return msgs[x].name;
	return "Unknown Message Type";
}

static inline int q931_cr(q931_h *h)
{
	int cr = 0;
	int x;
	if (h->crlen > 3) {
		pri_error(NULL, "Call Reference Length Too long: %d\n", h->crlen);
		return -1;
	}
	switch (h->crlen) {
		case 2: 
			for (x=0;x<h->crlen;x++) {
				cr <<= 8;
				cr |= h->crv[x];
			}
			break;
		case 1:
			cr = h->crv[0];
		/*	if (cr & 0x80) {
				cr &= ~0x80;
				cr |= 0x8000;
			} */
			break;
		default:
			pri_error(NULL, "Call Reference Length not supported: %d\n", h->crlen);
	}
	return cr;
}

static inline void q931_dumpie(struct pri *pri, int codeset, q931_ie *ie, char prefix)
{
	unsigned int x;
	int full_ie = Q931_FULL_IE(codeset, ie->ie);
	int base_ie;

	pri_message(pri, "%c [", prefix);
	pri_message(pri, "%02x", ie->ie);
	if (!(ie->ie & 0x80)) {
		pri_message(pri, " %02x", ielen(ie)-2);
		for (x = 0; x + 2 < ielen(ie); ++x)
			pri_message(pri, " %02x", ie->data[x]);
	}
	pri_message(pri, "]\n");

	/* Special treatment for shifts */
	if((full_ie & 0xf0) == Q931_LOCKING_SHIFT)
		full_ie &= 0xff;

	base_ie = (((full_ie & ~0x7f) == Q931_FULL_IE(0, 0x80)) && ((full_ie & 0x70) != 0x20)) ? full_ie & ~0x0f : full_ie;

	for (x=0;x<sizeof(ies) / sizeof(ies[0]); x++) 
		if (ies[x].ie == base_ie) {
			if (ies[x].dump)
				ies[x].dump(full_ie, pri, ie, ielen(ie), prefix);
			else
				pri_message(pri, "%c IE: %s (len = %d)\n", prefix, ies[x].name, ielen(ie));
			return;
		}
	
	pri_error(pri, "!! %c Unknown IE %d (len = %d)\n", prefix, base_ie, ielen(ie));
}

static q921_call *q921_getcall(struct pri *pri, struct q931_call *c, int tei)
{
	q921_call *cur;
	cur = c->phones;
	while(cur) {
	    if (cur->tei == tei) {
		    return cur;
	    } 
	    cur = cur->next;
	}
	/* No call exists, make a new one */
	if (pri->debug & PRI_DEBUG_Q921_STATE)
		pri_message(pri, "-- Making new q921 call for cref %d tei %d\n", c->cr, tei);
	cur = malloc(sizeof(struct q921_call));
	memset(cur, 0, sizeof(cur));
	cur->tei = tei;
	cur->proc = 0;
	cur->channel = -1;
	cur->next = c->phones;
	c->phones = cur;
	return cur;
}

static q931_call *q931_getcall(struct pri *pri, int cr, int tei)
{
	q931_call *cur, *prev;
	cur = *pri->callpool;
	prev = NULL;
	while(cur) {
		if ((pri->localtype == BRI_NETWORK_PTMP) && (tei >= 0)) {
		    // hmm...ok, we might be the 1st responding to the setup
		    // or it is really our call
		    if ((cur->cr == cr) && ((cur->tei == tei) || (cur->tei == 127)))
			    return cur;
	    	    // or we might not be the 1st responding, then we need to clone
		    // the call struct to hangup properly
		} else {
		    if (cur->cr == cr)
			    return cur;
 		}
		prev = cur;
		cur = cur->next;
	}
	/* No call exists, make a new one */
	if (pri->debug & PRI_DEBUG_Q931_STATE)
		pri_message(pri, "-- Making new call for cr %d\n", cr);
	cur = malloc(sizeof(struct q931_call));
	if (cur) {
		call_init(cur);
		/* Call reference */
		cur->cr = cr;
		cur->pri = pri;
		cur->tei = tei;
		/* Append to end of list */
		if (prev)
			prev->next = cur;
		else
			*pri->callpool = cur;
	}
	return cur;
}

q931_call *q931_new_call(struct pri *pri)
{
	q931_call *cur;
	do {
		cur = *pri->callpool;
		pri->cref++;
		if ((pri->localtype == PRI_NETWORK) || (pri->localtype == PRI_CPE)) {
		    if (pri->cref > 32767)
			    pri->cref = 1;
		} else {
		    // BRI
		    if (pri->cref > 255)
			    pri->cref = 1;
 		}
		while(cur) {
			if ((pri->localtype == PRI_NETWORK) || (pri->localtype == PRI_CPE)) {
			    if (cur->cr == (0x8000 | pri->cref))
				    break;
			} else {
			    // BRIs have only 1 bye cref
			    if (cur->cr == (0x80 | pri->cref))
				    break;
			}
			cur = cur->next;
		}
	} while(cur);
	if ((pri->localtype == PRI_NETWORK) || (pri->localtype == PRI_CPE)) {
	    return q931_getcall(pri, pri->cref | 0x8000, 0);
	} else {
	    // BRI
	    return q931_getcall(pri, pri->cref | 0x80, 0);
 	}
}

static void q931_destroy(struct pri *pri, int cr, q931_call *c, int tei)
{
	q931_call *cur, *prev;
	prev = NULL;
	cur = *pri->callpool;
	while(cur) {
//		if ((c && (cur == c)) || (!c && (cur->cr == cr))) {
		if ((c && (cur == c)) || (!c && ((cur->cr == cr) && ((pri->localtype != BRI_NETWORK_PTMP) || (cur->tei == tei))))) {
			if (prev)
				prev->next = cur->next;
			else
				*pri->callpool = cur->next;
			if (pri->debug & PRI_DEBUG_Q931_STATE)
				pri_message(pri, "NEW_HANGUP DEBUG: Destroying the call, ourstate %s, peerstate %s\n",callstate2str(cur->ourcallstate),callstate2str(cur->peercallstate));
			if (cur->retranstimer)
				pri_schedule_del(pri, cur->retranstimer);
			if (cur->t303timer)
				pri_schedule_del(pri, cur->t303timer);
			pri_call_apdu_queue_cleanup(cur);
			free(cur);
			return;
		}
		prev = cur;
		cur = cur->next;
	}
	pri_error(pri, "Can't destroy call %d!\n", cr);
}

static void q931_destroycall(struct pri *pri, int cr, int tei)
{
	return q931_destroy(pri, cr, NULL, tei);
}


void __q931_destroycall(struct pri *pri, q931_call *c) 
{
	if (pri && c)
		q931_destroy(pri,0, c, c->tei);
	return;
}

static int add_ie(struct pri *pri, q931_call *call, int msgtype, int ie, q931_ie *iet, int maxlen, int *codeset)
{
	unsigned int x;
	int res, total_res;
	int have_shift;
	int ies_count, order;
	for (x=0;x<sizeof(ies) / sizeof(ies[0]);x++) {
		if (ies[x].ie == ie) {
			/* This is our baby */
			if (ies[x].transmit) {
				/* Prepend with CODE SHIFT IE if required */
				if (*codeset != Q931_IE_CODESET(ies[x].ie)) {
					/* Locking shift to codeset 0 isn't possible */
					iet->ie = Q931_IE_CODESET(ies[x].ie) | (Q931_IE_CODESET(ies[x].ie) ? Q931_LOCKING_SHIFT : Q931_NON_LOCKING_SHIFT);
					have_shift = 1;
					iet = (q931_ie *)((char *)iet + 1);
					maxlen--;
				}
				else
					have_shift = 0;
				ies_count = ies[x].max_count;
				if (ies_count == 0)
					ies_count = INT_MAX;
				order = 0;
				total_res = 0;
				do {
					iet->ie = ie;
					res = ies[x].transmit(ie, pri, call, msgtype, iet, maxlen, ++order);
					/* Error if res < 0 or ignored if res == 0 */
					if (res < 0)
						return res;
					if (res > 0) {
						if ((iet->ie & 0x80) == 0) /* Multibyte IE */
							iet->len = res - 2;
						total_res += res;
						maxlen -= res;
						iet = (q931_ie *)((char *)iet + res);
					}
				}
				while (res > 0 && order < ies_count);
				if (have_shift && total_res) {
					if (Q931_IE_CODESET(ies[x].ie))
						*codeset = Q931_IE_CODESET(ies[x].ie);
					return total_res + 1; /* Shift is single-byte IE */
				}
				return total_res;
			} else {
				pri_error(pri, "!! Don't know how to add an IE %s (%d)\n", ie2str(ie), ie);
				return -1;
			}
		}
	}
	pri_error(pri, "!! Unknown IE %d (%s)\n", ie, ie2str(ie));
	return -1;
}

static char *disc2str(int disc)
{
	static struct msgtype discs[] = {
		{ Q931_PROTOCOL_DISCRIMINATOR, "Q.931" },
		{ GR303_PROTOCOL_DISCRIMINATOR, "GR-303" },
		{ 0x3, "AT&T Maintenance" },
		{ 0x43, "New AT&T Maintenance" },
	};
	return code2str(disc, discs, sizeof(discs) / sizeof(discs[0]));
}

void q931_dump(struct pri *pri, q931_h *h, int len, int txrx)
{
	q931_mh *mh;
	char c;
	int x=0, r;
	int cur_codeset;
	int codeset;
	c = txrx ? '>' : '<';
	pri_message(pri, "%c Protocol Discriminator: %s (%d)  len=%d\n", c, disc2str(h->pd), h->pd, len);
	pri_message(pri, "%c Call Ref: len=%2d (reference %d/0x%X) (%s)\n", c, h->crlen, q931_cr(h) & 0x7FFF, q931_cr(h) & 0x7FFF, (h->crv[0] & 0x80) ? "Terminator" : "Originator");
	/* Message header begins at the end of the call reference number */
	mh = (q931_mh *)(h->contents + h->crlen);
	pri_message(pri, "%c Message type: %s (%d)\n", c, msg2str(mh->msg), mh->msg);
	/* Drop length of header, including call reference */
	len -= (h->crlen + 3);
	codeset = cur_codeset = 0;
	while(x < len) {
		r = ielen((q931_ie *)(mh->data + x));
		q931_dumpie(pri, cur_codeset, (q931_ie *)(mh->data + x), c);
		switch (mh->data[x] & 0xf8) {
		case Q931_LOCKING_SHIFT:
			if ((mh->data[x] & 7) > 0)
				codeset = cur_codeset = mh->data[x] & 7;
			break;
		case Q931_NON_LOCKING_SHIFT:
			cur_codeset = mh->data[x] & 7;
			break;
		default:
			/* Reset temporary codeset change */
			cur_codeset = codeset;
		}
		x += r;
	}
	if (x > len) 
		pri_error(pri, "XXX Message longer than it should be?? XXX\n");
}

static int q931_handle_ie(int codeset, struct pri *pri, q931_call *c, int msg, q931_ie *ie)
{
	unsigned int x;
	int full_ie = Q931_FULL_IE(codeset, ie->ie);
	if (ielen(ie) > Q931_IE_MAX_LEN) {
	    pri_error(pri, "!! Invalid IE length %d (len = %d)\n", full_ie, ielen(ie));
	    return -1;
	}
	if (pri->debug & PRI_DEBUG_Q931_STATE)
		pri_message(pri, "-- Processing IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie));
	for (x=0;x<sizeof(ies) / sizeof(ies[0]);x++) {
		if (full_ie == ies[x].ie) {
			if (ies[x].receive)
				return ies[x].receive(full_ie, pri, c, msg, ie, ielen(ie));
			else {
				if (pri->debug & PRI_DEBUG_Q931_ANOMALY)
					pri_error(pri, "!! No handler for IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie));
				return -1;
			}
		}
	}
	pri_message(pri, "!! Unknown IE %d (cs%d, %s)\n", ie->ie, codeset, ie2str(full_ie));
	return -1;
}

static void init_header(struct pri *pri, q931_call *call, unsigned char *buf, q931_h **hb, q931_mh **mhb, int *len, int briflag)
{
	/* Returns header and message header and modifies length in place */
	q931_h *h = (q931_h *)buf;
 	q931_mh *mh;
	h->pd = pri->protodisc;
	h->x0 = 0;		/* Reserved 0 */

 	if (briflag == 1) {
 	    mh = (q931_mh *)(h->contents + 1);
  	    h->crlen = 1;	/* One bytes of Call Reference.  Invert the top bit to make it from our sense */
 	    if (call->cr || call->forceinvert) {
 		h->crv[0] = (call->cr ^ 0x80);
 	    } else {
 		/* Unless of course this has no call reference */
 		h->crv[0] = 0;
 	    }
 	    *len -= 4;
 	} else {
 	    mh = (q931_mh *)(h->contents + 2);
      	    h->crlen = 2;	/* Two bytes of Call Reference.  Invert the top bit to make it from our sense */
  	    if (call->cr || call->forceinvert) {
		h->crv[0] = ((call->cr ^ 0x8000) & 0xff00) >> 8;
		h->crv[1] = (call->cr & 0xff);
	    } else {
		/* Unless of course this has no call reference */
		h->crv[0] = 0;
		h->crv[1] = 0;
	    }
	    *len -= 5;
	}
	if (pri->subchannel) {
		/* On GR-303, top bit is always 0 */
		h->crv[0] &= 0x7f;
	}
	mh->f = 0;
	*hb = h;
	*mhb = mh;
}

static int q931_xmit(struct pri *pri, q931_h *h, int len, int cr, int tei)
{
	q931_mh *mh;
	if (pri->localtype == BRI_NETWORK_PTMP) {
	    mh = (q931_mh *)(h->contents + 1);
	    if (mh->msg == Q931_SETUP) {
		q921_transmit_uframe(pri, h, len, cr, tei);
	    } else {
		q921_transmit_iframe(pri, h, len, cr, tei);
	    }
	} else if (pri->localtype == BRI_CPE_PTMP) {
	    q921_transmit_iframe(pri, h, len, cr, pri->tei);
	} else {
	    q921_transmit_iframe(pri, h, len, cr, tei);
	}
	/* The transmit operation might dump the q921 header, so logging the q931
	   message body after the transmit puts the sections of the message in the
	   right order in the log */
	if (pri->debug & PRI_DEBUG_Q931_DUMP)
		q931_dump(pri, h, len, 1);
#ifdef LIBPRI_COUNTERS
	pri->q931_txcount++;
#endif
	return 0;
}

static int send_message(struct pri *pri, q931_call *c, int msgtype, int ies[])
{
	unsigned char buf[1024];
	q931_h *h;
	q931_mh *mh;
	int len;
	int res;
	int offset=0;
	int x;
	int codeset;
	struct apdu_event *facevent = c->apdus;
	
	memset(buf, 0, sizeof(buf));
	len = sizeof(buf);
	if ((pri->localtype == PRI_NETWORK) || (pri->localtype == PRI_CPE)) {
	    init_header(pri, c, buf, &h, &mh, &len, 0);
	} else {
	    init_header(pri, c, buf, &h, &mh, &len, 1);
	}
	mh->msg = msgtype;
	x=0;
	codeset = 0;
	while(ies[x] > -1) {
		if (ies[x] == Q931_IE_FACILITY) {
			res = 0;
			while (facevent) {
				if (!facevent->sent && (facevent->message == msgtype)) { 
					int tmpres;
					tmpres = add_ie(pri, c, mh->msg, ies[x], (q931_ie *)(mh->data + offset), len, &codeset);
					if (tmpres < 0) {
						pri_error(pri, "!! Unable to add IE '%s'\n", ie2str(ies[x]));
						return -1;
					}
					res += tmpres;
					facevent->sent = 1;
				}
				facevent = facevent->next;
			}
		} else {
			res = add_ie(pri, c, mh->msg, ies[x], (q931_ie *)(mh->data + offset), len, &codeset);
		}

		if (res < 0) {
			pri_error(pri, "!! Unable to add IE '%s'\n", ie2str(ies[x]));
			return -1;
		}

		offset += res;
		len -= res;
		x++;
	}
	/* Invert the logic */
	len = sizeof(buf) - len;
	if (pri->localtype == BRI_CPE_PTMP) {
	    q931_xmit(pri, h, len, 1, pri->tei);
	} else {
	    q931_xmit(pri, h, len, 1, c->tei);
	}
	c->acked = 1;
	return 0;
}

static int facility_ies[] = { Q931_IE_FACILITY, -1 };

#if 0
int q931_facility_kpj(struct pri *pri, q931_call *c, int operation, char *arguments)
{
	switch (operation) {
	    case 0x26:	c->aoc = 1;
			break;
	    case 0x24:	c->aoc = 1;
			break;
	    case 0x22:	c->aoc = 1;
			break;
	    default:
		return -1;
	}
	return send_message(pri, c, Q931_FACILITY, facility_ies);
}
#endif

static int status_ies[] = { Q931_CAUSE, Q931_CALL_STATE, -1 };

static int q931_status(struct pri *pri, q931_call *c, int cause)
{
	q931_call *cur = NULL;
	if (!cause)
		cause = PRI_CAUSE_RESPONSE_TO_STATUS_ENQUIRY;
	if (c->cr > -1)
		cur = *pri->callpool;
	while(cur) {
		if (cur->cr == c->cr) {
			cur->cause=cause;
			cur->causecode = CODE_CCITT;
			cur->causeloc = LOC_USER;
			break;
		}
		cur = cur->next;
	}
	if (!cur) {
		pri_message(pri, "YYY Here we get reset YYY\n");
		/* something went wrong, respond with "no such call" */
		c->ourcallstate = Q931_CALL_STATE_NULL;
		c->peercallstate = Q931_CALL_STATE_NULL;
		cur=c;
	}
	return send_message(pri, cur, Q931_STATUS, status_ies);
}

static int information_ies[] = { Q931_CALLED_PARTY_NUMBER, -1 };

int q931_information(struct pri *pri, q931_call *c, char digit)
{
	c->callednum[0]=digit;
	c->callednum[1]='\0';
	return send_message(pri, c, Q931_INFORMATION, information_ies);
}

static int information_display_ies[] = { Q931_DISPLAY, -1 };

int q931_information_display(struct pri *pri, q931_call *c, char *display)
{
	int res=0;
	char temp[256];
	if (!display) return -1;
	strncpy(temp, c->callername, sizeof(temp));
	strncpy(c->callername, display, sizeof(c->callername));
	res = send_message(pri, c, Q931_INFORMATION, information_display_ies);
	strncpy(c->callername, temp, sizeof(c->callername));
	return res;
}

int q931_add_display(struct pri *pri, q931_call *c, char *display)
{
	strncpy(c->display, display, sizeof(c->display));
	return 0;
}

/* static int information_special_ies[] = { Q931_IE_SPECIAL, -1 };
static int q931_information_special(struct pri *pri, q931_call *c)
{
	return send_message(pri, c, Q931_FACILITY, information_special_ies);
}
*/


static int restart_ack_ies[] = { Q931_CHANNEL_IDENT, Q931_RESTART_INDICATOR, -1 };

static int restart_ack(struct pri *pri, q931_call *c)
{
	c->ourcallstate = Q931_CALL_STATE_NULL;
	c->peercallstate = Q931_CALL_STATE_NULL;
        c->chanflags &= ~FLAG_PREFERRED;
        c->chanflags |= FLAG_EXCLUSIVE;
	return send_message(pri, c, Q931_RESTART_ACKNOWLEDGE, restart_ack_ies);
}

int q931_facility(struct pri*pri, q931_call *c)
{
	return send_message(pri, c, Q931_FACILITY, facility_ies);
}

static int notify_ies[] = { Q931_IE_NOTIFY_IND, -1 };

int q931_notify(struct pri *pri, q931_call *c, int channel, int info)
{
	if ((pri->switchtype == PRI_SWITCH_EUROISDN_T1) || (pri->switchtype != PRI_SWITCH_EUROISDN_E1)) {
		if ((info > 0x2) || (info < 0x00))
			return 0;
	}
	if (info >= 0)
		c->notify = info & 0x7F;
	else
		c->notify = -1;
	return send_message(pri, c, Q931_NOTIFY, notify_ies);
}

#ifdef ALERTING_NO_PROGRESS
static int call_progress_ies[] = { -1 };
#else
static int call_progress_ies[] = { Q931_PROGRESS_INDICATOR, -1 };
#endif

int q931_call_progress(struct pri *pri, q931_call *c, int channel, int info)
{
	if (channel) { 
		c->ds1no = (channel & 0xff00) >> 8;
		c->ds1explicit = (channel & 0x10000) >> 16;
		channel &= 0xff;
		c->channelno = channel;		
	}
	if (info) {
		c->progloc = LOC_PRIV_NET_LOCAL_USER;
		c->progcode = CODE_CCITT;
		c->progressmask = PRI_PROG_INBAND_AVAILABLE;
	} else {
		/* PI is mandatory IE for PROGRESS message - Q.931 3.1.8 */
		pri_error(pri, "XXX Progress message requested but no information is provided\n");
		c->progressmask = 0;
	}
	c->alive = 1;
	return send_message(pri, c, Q931_PROGRESS, call_progress_ies);
}

#ifdef ALERTING_NO_PROGRESS
static int call_proceeding_ies[] = { Q931_CHANNEL_IDENT, -1 };
#else
static int call_proceeding_ies[] = { Q931_CHANNEL_IDENT, Q931_PROGRESS_INDICATOR, -1 };
#endif

int q931_call_proceeding(struct pri *pri, q931_call *c, int channel, int info)
{
	// never send two PROCEEDINGs!
	if (c->proc > 0) return 0;		
	if (channel) { 
		c->ds1no = (channel & 0xff00) >> 8;
		c->ds1explicit = (channel & 0x10000) >> 16;
		channel &= 0xff;
		c->channelno = channel;		
	}
	c->chanflags &= ~FLAG_PREFERRED;
	c->chanflags |= FLAG_EXCLUSIVE;
	c->ourcallstate = Q931_CALL_STATE_INCOMING_CALL_PROCEEDING;
	c->peercallstate = Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING;
	if (info) {
		c->progloc = LOC_PRIV_NET_LOCAL_USER;
		c->progcode = CODE_CCITT;
		c->progressmask = PRI_PROG_INBAND_AVAILABLE;
	} else
		c->progressmask = 0;
	c->proc = 1;
	c->alive = 1;
	return send_message(pri, c, Q931_CALL_PROCEEDING, call_proceeding_ies);
}
#ifndef ALERTING_NO_PROGRESS
static int alerting_ies[] = { Q931_PROGRESS_INDICATOR, Q931_IE_USER_USER,  -1 };
#else
static int alerting_ies[] = { -1 };
#endif

static int alerting_BRI_ies[] = { -1 };

int q931_alerting(struct pri *pri, q931_call *c, int channel, int info)
{
	// never send two ALERTINGs!
	if (c->alert > 0) return 0;		
	if (!c->proc) 
		q931_call_proceeding(pri, c, channel, 0);
	if (info) {
		c->progloc = LOC_PRIV_NET_LOCAL_USER;
		c->progcode = CODE_CCITT;
		c->progressmask = PRI_PROG_INBAND_AVAILABLE;
	} else
		c->progressmask = 0;
	c->ourcallstate = Q931_CALL_STATE_CALL_RECEIVED;
	c->peercallstate = Q931_CALL_STATE_CALL_DELIVERED;
	c->alert = 1;
	c->alive = 1;
	if ((pri->localtype == PRI_NETWORK) || (pri->localtype == PRI_CPE)) {
	    return send_message(pri, c, Q931_ALERTING, alerting_ies);
	} else {
	    if ((pri->localtype == BRI_NETWORK) || (pri->localtype == BRI_NETWORK_PTMP)) {
		return send_message(pri, c, Q931_ALERTING, alerting_ies);
    	    } else {
		/* no PROGRESS_INDICATOR for BRI please */
		return send_message(pri, c, Q931_ALERTING, alerting_BRI_ies);
	    }
	}
}

static int hold_acknowledge_ies[] = { -1 };

int q931_hold_acknowledge(struct pri *pri, q931_call *c)
{
	return send_message(pri, c, Q931_HOLD_ACKNOWLEDGE, hold_acknowledge_ies);
}

static int hold_reject_ies[] = { Q931_CAUSE, -1 };

int q931_hold_reject(struct pri *pri, q931_call *c)
{
	c->cause = 12;
	c->causecode = CODE_CCITT;
	c->causeloc = LOC_PRIV_NET_LOCAL_USER;
	return send_message(pri, c, Q931_HOLD_REJECT, hold_reject_ies);
}

static int retrieve_acknowledge_ies[] = { Q931_CHANNEL_IDENT, -1 };

int q931_retrieve_acknowledge(struct pri *pri, q931_call *c, int channel)
{
	if (channel)
		c->channelno = channel;
	c->chanflags &= ~FLAG_PREFERRED;
	c->chanflags |= FLAG_EXCLUSIVE;
	return send_message(pri, c, Q931_RETRIEVE_ACKNOWLEDGE, retrieve_acknowledge_ies);
}

static int retrieve_reject_ies[] = { -1 };

int q931_retrieve_reject(struct pri *pri, q931_call *c)
{
	return send_message(pri, c, Q931_RETRIEVE_REJECT, retrieve_reject_ies);
}

static int suspend_acknowledge_ies[] = { Q931_DISPLAY, -1 };

int q931_suspend_acknowledge(struct pri *pri, q931_call *c, char *display)
{
	char tempcallername[256];
	int res;
	c->ourcallstate = Q931_CALL_STATE_NULL;
	c->peercallstate = Q931_CALL_STATE_NULL;
	strncpy(tempcallername,c->callername,sizeof(tempcallername));
	strncpy(c->callername,display,sizeof(c->callername));
	res = send_message(pri, c, Q931_SUSPEND_ACKNOWLEDGE, suspend_acknowledge_ies);
	strncpy(c->callername,tempcallername,sizeof(c->callername));
	__q931_destroycall(pri, c);
	return res;
}

static int suspend_reject_ies[] = { Q931_DISPLAY, Q931_CAUSE, -1 };

int q931_suspend_reject(struct pri *pri, q931_call *c, char *display)
{
	char tempcallername[256];
	int res;
	strncpy(tempcallername,c->callername,sizeof(tempcallername));
	strncpy(c->callername,display,sizeof(c->callername));
	c->cause = 12;
	c->causecode = CODE_CCITT;
	c->causeloc = LOC_PRIV_NET_LOCAL_USER;
	res = send_message(pri, c, Q931_SUSPEND_REJECT, suspend_reject_ies);
	strncpy(c->callername,tempcallername,sizeof(c->callername));
	return res;
}

static int resume_reject_ies[] = { Q931_CAUSE, Q931_DISPLAY,  -1 };

int q931_resume_reject(struct pri *pri, q931_call *c, char *display)
{
	char tempcallername[256];
	int res;
	c->cause = 12;
	c->causecode = CODE_CCITT;
	c->causeloc = LOC_PRIV_NET_LOCAL_USER;
	strncpy(tempcallername,c->callername,sizeof(tempcallername));
	strncpy(c->callername,display,sizeof(c->callername));
	res = send_message(pri, c, Q931_RESUME_REJECT, resume_reject_ies);
	strncpy(c->callername,tempcallername,sizeof(c->callername));
	return res;
}

static int resume_acknowledge_ies[] = { Q931_CHANNEL_IDENT, Q931_DISPLAY,  -1 };

int q931_resume_acknowledge(struct pri *pri, q931_call *c, int channel, char *display)
{
	char tempcallername[256];
	int res;
	if (channel)
		c->channelno = channel;
	c->chanflags &= ~FLAG_PREFERRED;
	c->chanflags |= FLAG_EXCLUSIVE;
	c->alive = 1;
	c->ourcallstate = Q931_CALL_STATE_ACTIVE;
	c->peercallstate = Q931_CALL_STATE_ACTIVE;
	strncpy(tempcallername,c->callername,sizeof(tempcallername));
	strncpy(c->callername,display,sizeof(c->callername));
	res = send_message(pri, c, Q931_RESUME_ACKNOWLEDGE, resume_acknowledge_ies);
	strncpy(c->callername,tempcallername,sizeof(c->callername));
	return res;
}


static int connect_ies[] = {  Q931_CHANNEL_IDENT, Q931_PROGRESS_INDICATOR, Q931_DISPLAY, -1 };
static int connect_NET_ies[] = {  Q931_CHANNEL_IDENT, Q931_PROGRESS_INDICATOR, Q931_IE_TIME_DATE, -1 };
 
int q931_setup_ack(struct pri *pri, q931_call *c, int channel, int nonisdn)
{
	int network = pri->localtype == PRI_NETWORK || pri->localtype == BRI_NETWORK || pri->localtype == BRI_NETWORK_PTMP;
	if (channel) { 
		c->ds1no = (channel & 0xff00) >> 8;
		c->ds1explicit = (channel & 0x10000) >> 16;
		channel &= 0xff;
		c->channelno = channel;		
	}
	c->chanflags &= ~FLAG_PREFERRED;
	c->chanflags |= FLAG_EXCLUSIVE;
	if (nonisdn && (pri->switchtype != PRI_SWITCH_DMS100)) {
		c->progloc  = LOC_PRIV_NET_LOCAL_USER;
		c->progcode = CODE_CCITT;
		c->progressmask = PRI_PROG_CALLED_NOT_ISDN;
	} else
		c->progressmask = 0;
	c->ourcallstate = Q931_CALL_STATE_OVERLAP_RECEIVING;
	c->peercallstate = Q931_CALL_STATE_OVERLAP_SENDING;
	c->alive = 1;
	if (network) {
		c->progloc = LOC_PRIV_NET_LOCAL_USER;
		c->progcode = CODE_CCITT;
		c->progressmask = Q931_PROG_INBAND_AVAILABLE;
 	}
	return send_message(pri, c, Q931_SETUP_ACKNOWLEDGE, connect_ies);
}

static void pri_setup_response_timeout(void *data)
{
	struct q931_call *c = data;
	struct pri *pri = NULL;
	if (!c) return;
	pri = c->pri;
	if (!pri) return;
	c->alive = 1;
	c->cause = PRI_CAUSE_NO_USER_RESPONSE;
	if (pri->debug & PRI_DEBUG_Q931_STATE)
		pri_message(pri, "No response to SETUP message\n");
	pri->schedev = 1;
	pri->ev.e = PRI_EVENT_HANGUP;
	pri->ev.hangup.channel = c->channelno;
	pri->ev.hangup.cref = c->cr;
	pri->ev.hangup.aoc_units = -1;
	if (c->cause == -1) {
	    pri->ev.hangup.cause = PRI_CAUSE_SWITCH_CONGESTION;
	} else {
	    pri->ev.hangup.cause = c->cause;
	}
	pri->ev.hangup.call = c;
	q931_hangup(pri, c, c->cause);
}

static void pri_connect_timeout(void *data)
{
	struct q931_call *c = data;
	struct pri *pri = c->pri;
	if (pri->debug & PRI_DEBUG_Q931_STATE)
		pri_message(pri, "Timed out looking for connect acknowledge\n");
	q931_disconnect(pri, c, PRI_CAUSE_NORMAL_CLEARING);
	
}

static void pri_release_timeout(void *data)
{
	struct q931_call *c = data;
	struct pri *pri = c->pri;
	if (pri->debug & PRI_DEBUG_Q931_STATE)
		pri_message(pri, "Timed out looking for release complete\n");
	c->t308_timedout++;
	c->alive = 1;
	q931_release(pri, c, PRI_CAUSE_NORMAL_CLEARING);
}

static void pri_release_finaltimeout(void *data)
{
	struct q931_call *c = data;
	struct pri *pri = c->pri;
	c->alive = 1;
	if (pri->debug & PRI_DEBUG_Q931_STATE)
		pri_message(pri, "Final time-out looking for release complete\n");
	c->t308_timedout++;
	c->ourcallstate = Q931_CALL_STATE_NULL;
	c->peercallstate = Q931_CALL_STATE_NULL;
	pri->schedev = 1;
	pri->ev.e = PRI_EVENT_HANGUP_ACK;
	pri->ev.hangup.channel = c->channelno;
	pri->ev.hangup.cref = c->cr;
	pri->ev.hangup.cause = c->cause;
	pri->ev.hangup.call = c;
	q931_hangup(pri, c, c->cause);
}

static void pri_disconnect_timeout(void *data)
{
	struct q931_call *c = data;
	struct pri *pri = c->pri;
	if (pri->debug & PRI_DEBUG_Q931_STATE)
		pri_message(pri, "Timed out looking for release\n");
	c->alive = 1;
	q931_release(pri, c, PRI_CAUSE_NORMAL_CLEARING);
}

int q931_connect(struct pri *pri, q931_call *c, int channel, int nonisdn)
{
	int network = pri->localtype == PRI_NETWORK || pri->localtype == BRI_NETWORK || pri->localtype == BRI_NETWORK_PTMP;
	if (channel) { 
		c->ds1no = (channel & 0xff00) >> 8;
		c->ds1explicit = (channel & 0x10000) >> 16;
		channel &= 0xff;
		c->channelno = channel;		
	}
	c->chanflags &= ~FLAG_PREFERRED;
	c->chanflags |= FLAG_EXCLUSIVE;
	if (nonisdn && (pri->switchtype != PRI_SWITCH_DMS100)) {
		c->progloc  = LOC_PRIV_NET_LOCAL_USER;
		c->progcode = CODE_CCITT;
		c->progressmask = PRI_PROG_CALLED_NOT_ISDN;
	} else
		c->progressmask = 0;
	if (network) {
		/* WE decide when the call is up and active */
		c->ourcallstate = Q931_CALL_STATE_ACTIVE;
	} else {
	    c->ourcallstate = Q931_CALL_STATE_CONNECT_REQUEST;
	}
	c->peercallstate = Q931_CALL_STATE_ACTIVE;
	c->alive = 1;
	c->con_acked = 0;
	/* Setup timer */
	if (c->retranstimer)
		pri_schedule_del(pri, c->retranstimer);
	c->retranstimer = 0;
	if (!network && (!pri->subchannel)) {
		/* make sure that CPEs get a CONNECT_ACKNOWLEDGE */
		c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T313], pri_connect_timeout, c);
	}
	if ((pri->localtype != PRI_CPE) && (pri->localtype != BRI_CPE) && (pri->localtype != BRI_CPE_PTMP)) {
	    // networks may send datetime
	    return send_message(pri, c, Q931_CONNECT, connect_NET_ies);
	} else {
	    return send_message(pri, c, Q931_CONNECT, connect_ies);
	}
}

static int release_aoce_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, Q931_IE_FACILITY, -1 };
static int release_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, -1 };

int q931_release(struct pri *pri, q931_call *c, int cause)
{
	int network = pri->localtype == PRI_NETWORK || pri->localtype == BRI_NETWORK || pri->localtype == BRI_NETWORK_PTMP;
	c->ourcallstate = Q931_CALL_STATE_RELEASE_REQUEST;
	/* c->peercallstate stays the same */
	if (c->alive) {
		c->alive = 0;
		c->cause = cause;
		c->causecode = CODE_CCITT;
		c->causeloc = LOC_PRIV_NET_LOCAL_USER;
		if (c->acked) {
			if (c->retranstimer)
				pri_schedule_del(pri, c->retranstimer);
			if (!c->t308_timedout) {
				c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T308], pri_release_timeout, c);
			} else {
				c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T308], pri_release_finaltimeout, c);
			}
			if (network && (c->aoc_units > -1)) {
			    /* include FACILITY IE for AOC-E */
			    aoc_aoce_charging_unit_encode(pri, c , c->aoc_units, Q931_RELEASE);
			    c->aoc_units = -1;
			    return send_message(pri, c, Q931_RELEASE, release_aoce_ies);
			} else {
			    return send_message(pri, c, Q931_RELEASE, release_ies);
			}
		} else
			return send_message(pri, c, Q931_RELEASE_COMPLETE, release_ies); /* Yes, release_ies, not release_complete_ies */
	} else
		return 0;
}

static int restart_ies[] = { Q931_CHANNEL_IDENT, Q931_RESTART_INDICATOR, -1 };

int q931_restart(struct pri *pri, int channel)
{
	struct q931_call *c;
	c = q931_getcall(pri, 0 | 0x8000, 0);
	if (!c)
		return -1;
	if (!channel)
		return -1;
	c->ri = 0;
	c->ds1no = (channel & 0xff00) >> 8;
	c->ds1explicit = (channel & 0x10000) >> 16;
	channel &= 0xff;
	c->channelno = channel;		
	c->chanflags &= ~FLAG_PREFERRED;
	c->chanflags |= FLAG_EXCLUSIVE;
	c->ourcallstate = Q931_CALL_STATE_RESTART;
	c->peercallstate = Q931_CALL_STATE_RESTART_REQUEST;
	return send_message(pri, c, Q931_RESTART, restart_ies);
}

static int disconnect_aoce_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, Q931_IE_FACILITY, -1 };
static int disconnect_ies[] = { Q931_CAUSE, Q931_IE_USER_USER, -1 };

int q931_disconnect(struct pri *pri, q931_call *c, int cause)
{
	int network = pri->localtype == PRI_NETWORK || pri->localtype == BRI_NETWORK || pri->localtype == BRI_NETWORK_PTMP;
	c->ourcallstate = Q931_CALL_STATE_DISCONNECT_REQUEST;
	c->peercallstate = Q931_CALL_STATE_DISCONNECT_INDICATION;
	if (c->alive) {
		c->alive = 0;
		c->cause = cause;
		c->causecode = CODE_CCITT;
		c->causeloc = LOC_PRIV_NET_LOCAL_USER;
		c->sendhangupack = 1;
		if (c->retranstimer)
			pri_schedule_del(pri, c->retranstimer);
		c->retranstimer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T305], pri_disconnect_timeout, c);
		if (network && (c->aoc_units > -1)) {
		    /* include FACILITY IE for AOC-E */
		    aoc_aoce_charging_unit_encode(pri, c , c->aoc_units, Q931_DISCONNECT);
		    c->aoc_units = -1;
		    return send_message(pri, c, Q931_DISCONNECT, disconnect_aoce_ies);
		} else {
		    return send_message(pri, c, Q931_DISCONNECT, disconnect_ies);
		}
	} else
		return 0;
}

//static int setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_DISPLAY, Q931_PROGRESS_INDICATOR,
//	 Q931_IE_SIGNAL, Q931_CALLING_PARTY_NUMBER, Q931_CALLED_PARTY_NUMBER, Q931_SENDING_COMPLETE, -1 };

static int setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_PROGRESS_INDICATOR, Q931_NETWORK_SPEC_FAC, Q931_DISPLAY, Q931_IE_USER_USER,
	Q931_CALLING_PARTY_NUMBER, Q931_CALLED_PARTY_NUMBER, Q931_REDIRECTING_NUMBER, Q931_SENDING_COMPLETE, Q931_IE_ORIGINATING_LINE_INFO, Q931_IE_GENERIC_DIGITS, -1 };

static int setup_bri_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_PROGRESS_INDICATOR, Q931_NETWORK_SPEC_FAC, Q931_DISPLAY, Q931_IE_USER_USER,
	Q931_CALLING_PARTY_NUMBER, Q931_CALLED_PARTY_NUMBER, Q931_REDIRECTING_NUMBER, Q931_SENDING_COMPLETE, Q931_IE_ORIGINATING_LINE_INFO, Q931_IE_GENERIC_DIGITS, Q931_HIGH_LAYER_COMPAT, Q931_LOW_LAYER_COMPAT, -1 };

static int gr303_setup_ies[] =  { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, -1 };

static int cis_setup_ies[] = { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT, Q931_IE_FACILITY, Q931_CALLED_PARTY_NUMBER, -1 };

int q931_setup(struct pri *pri, q931_call *c, struct pri_sr *req)
{
	int res;

 	if (pri->localtype == BRI_NETWORK_PTMP) {
 	    c->tei = 127;
 	} else {
 	    c->tei = 0;
 	}
	
	c->transcapability = req->transmode;
	c->transmoderate = TRANS_MODE_64_CIRCUIT;
	if (!req->userl1)
		req->userl1 = PRI_LAYER_1_ULAW;
	c->userl1 = req->userl1;
	c->ds1no = (req->channel & 0xff00) >> 8;
	c->ds1explicit = (req->channel & 0x10000) >> 16;
	req->channel &= 0xff;
	if ((pri->localtype == PRI_CPE) && pri->subchannel) {
		req->channel = 0;
		req->exclusive = 0;
	}
		
	c->channelno = req->channel;		
	c->slotmap = -1;
	c->nonisdn = req->nonisdn;
	c->newcall = 0;
	c->justsignalling = req->justsignalling;		
	c->complete = req->numcomplete; 
	if (req->exclusive) 
		c->chanflags = FLAG_EXCLUSIVE;
	else if (c->channelno)
		c->chanflags = FLAG_PREFERRED;
	memcpy(c->llc, req->llc, sizeof(c->llc));
	if (req->caller) {
		libpri_copy_string(c->callernum, req->caller, sizeof(c->callernum));
		c->callerplan = req->callerplan;
		if (req->callername)
			libpri_copy_string(c->callername, req->callername, sizeof(c->callername));
		else
			c->callername[0] = '\0';
		if ((pri->switchtype == PRI_SWITCH_DMS100) ||
		    (pri->switchtype == PRI_SWITCH_ATT4ESS)) {
			/* Doesn't like certain presentation types */
			if (!(req->callerpres & 0x7c))
				req->callerpres = PRES_ALLOWED_NETWORK_NUMBER;
		}
		c->callerpres = req->callerpres;
	} else {
		c->callernum[0] = '\0';
		c->callername[0] = '\0';
		c->callerplan = PRI_UNKNOWN;
		c->callerpres = PRES_NUMBER_NOT_AVAILABLE;
	}
	if (req->redirectingnum) {
		libpri_copy_string(c->redirectingnum, req->redirectingnum, sizeof(c->redirectingnum));
		c->redirectingplan = req->redirectingplan;
		if ((pri->switchtype == PRI_SWITCH_DMS100) ||
		    (pri->switchtype == PRI_SWITCH_ATT4ESS)) {
			/* Doesn't like certain presentation types */
			if (!(req->redirectingpres & 0x7c))
				req->redirectingpres = PRES_ALLOWED_NETWORK_NUMBER;
		}
		c->redirectingpres = req->redirectingpres;
		c->redirectingreason = req->redirectingreason;
	} else {
		c->redirectingnum[0] = '\0';
		c->redirectingplan = PRI_UNKNOWN;
		c->redirectingpres = PRES_NUMBER_NOT_AVAILABLE;
		c->redirectingreason = PRI_REDIR_UNKNOWN;
	}
	if (req->called) {
		libpri_copy_string(c->callednum, req->called, sizeof(c->callednum));
		c->calledplan = req->calledplan;
	} else
		return -1;

	if (req->useruserinfo)
		libpri_copy_string(c->useruserinfo, req->useruserinfo, sizeof(c->useruserinfo));
	else
		c->useruserinfo[0] = '\0';

	if (req->nonisdn && (pri->switchtype == PRI_SWITCH_NI2))
		c->progressmask = PRI_PROG_CALLER_NOT_ISDN;
	else
		c->progressmask = 0;

	pri_call_add_standard_apdus(pri, c);

	if (pri->subchannel)
		res = send_message(pri, c, Q931_SETUP, gr303_setup_ies);
	else if (c->justsignalling)
		res = send_message(pri, c, Q931_SETUP, cis_setup_ies);
	else if (pri->localtype == BRI_NETWORK || pri->localtype == BRI_NETWORK_PTMP) 
		res = send_message(pri, c, Q931_SETUP, setup_bri_ies);
	else
		res = send_message(pri, c, Q931_SETUP, setup_ies);

	if (!res) {
		c->alive = 1;
		/* make sure we call PRI_EVENT_HANGUP_ACK once we send/receive RELEASE_COMPLETE */
		c->sendhangupack = 1;
		c->ourcallstate = Q931_CALL_STATE_CALL_INITIATED;
		c->peercallstate = Q931_CALL_STATE_OVERLAP_SENDING;	
		c->t303timer = pri_schedule_event(pri, pri->timers[PRI_TIMER_T303], pri_setup_response_timeout, c);
		c->t303running = 1;
	}
	return res;
	
}

static int release_complete_ies[] = { Q931_IE_USER_USER, -1 };

static int q931_release_complete(struct pri *pri, q931_call *c, int cause)
{
	int res = 0;
	c->ourcallstate = Q931_CALL_STATE_NULL;
	c->peercallstate = Q931_CALL_STATE_NULL;
	if (cause > -1) {
		c->cause = cause;
		c->causecode = CODE_CCITT;
		if ((pri->localtype == PRI_NETWORK) || (pri->localtype == BRI_NETWORK) || (pri->localtype == BRI_NETWORK_PTMP)) {
		    c->causeloc = LOC_PRIV_NET_LOCAL_USER;
		} else {
		    c->causeloc = LOC_USER;
		}
		/* release_ies has CAUSE in it */
		res = send_message(pri, c, Q931_RELEASE_COMPLETE, release_ies);
	} else
		res = send_message(pri, c, Q931_RELEASE_COMPLETE, release_complete_ies);
	c->alive = 0;
	/* release the structure */
	res += q931_hangup(pri,c,cause);
	return res;
}

static int connect_acknowledge_ies[] = { -1 };

static int gr303_connect_acknowledge_ies[] = { Q931_CHANNEL_IDENT, -1 };

static int q931_connect_acknowledge(struct pri *pri, q931_call *c)
{
	if (pri->subchannel) {
		if (pri->localtype == PRI_CPE)
			return send_message(pri, c, Q931_CONNECT_ACKNOWLEDGE, gr303_connect_acknowledge_ies);
	} else
		return send_message(pri, c, Q931_CONNECT_ACKNOWLEDGE, connect_acknowledge_ies);
	return 0;
}

/* here we cleanly hangup the phones that responded to our call but didnt get the call  */
int q921_hangup(struct pri *pri, q931_call *c, int tei)
{
 q921_call *cur,*prev;
 int tc;
 int ttei;
 int res=0;

 if (!pri || !c)
    return -1;
 
 if (pri->localtype != BRI_NETWORK_PTMP){
	return 0;
 }
//	pri_error(pri, "q921_hangup(%d, %d)\n", c->cr, tei);

 if (tei == 127) {
	tei = c->tei;
 }
//	pri_error(pri, "tei %d\n", tei);

 cur = c->phones;
 
 tc = c->cause;
 ttei = c->tei;
 while (cur) {
	if (cur->tei != tei) {
	    c->cause = PRI_CAUSE_NORMAL_CLEARING;
	    c->tei = cur->tei;
	    if (pri->debug & PRI_DEBUG_Q921_STATE)
		pri_message(pri, "sending RELEASE for TEI %d\n", cur->tei);
	    send_message(pri, c, Q931_RELEASE, release_ies);
	}
	prev = cur;
	cur = cur->next;
	if (prev) {
	    free(prev);
	    prev = NULL;
	}
 }
     c->phones = NULL;
     c->tei = ttei;
     c->cause = tc;

 if (c->tei == 127) {
	q931_destroycall(pri, c->cr, c->tei);
	// make sure * frees the channel
/*	pri_error(pri, "returning PRI_EVENT_HANGUP_ACK\n");
	res = Q931_RES_HAVEEVENT;
	pri->ev.hangup.channel = c->channelno;
	pri->ev.e = PRI_EVENT_HANGUP_ACK;   */
 } 
 return res; 
}

/* here we handle release_completes from the phones 
    because some (elmeg) phones do not send a disconnect
    message when the phone is busy */
int q921_handle_hangup(struct pri *pri, q931_call *c, int tei)
{
     q921_call *cur,*match,*prev=NULL;
     int left=0;
     int res=0;
     
     if (!pri || !c)
 	return -1;
 
     if (pri->localtype != BRI_NETWORK_PTMP){
 	return 0;
     }
 
     cur = c->phones;
     
     while (cur) {
 	if (cur->tei == tei) {
 	    match = cur;
 	    if (prev) {
 		prev->next = cur->next;
 		cur = prev;
 	    } else {
 		c->phones = cur->next;
 	    }
 	    free(match);
 	}
 	prev = cur;
 	if (cur) cur = cur->next;
     }
 
     cur = c->phones;
     
     while (cur) {
 	left++;
 	cur = cur->next;
     }
     
     // if all phones have signalled busy AND the timer is not running anymore!
#ifdef FASTBUSYONBUSY
    if ((c->cause == PRI_CAUSE_USER_BUSY) && (c->t303timer)) {
 	    c->t303running = 0;
 	    pri_schedule_del(pri, c->t303timer);
    }
#endif

     if ((left==0) && (c->cause == PRI_CAUSE_USER_BUSY) && (c->t303running == 0)) {
 //	pri_error(pri, "q921_handle_hangup(%d, %d, %d)\n", c->cr, tei, c->tei);
 	// make sure * frees the channel
 	res = Q931_RES_HAVEEVENT;
 	pri->ev.hangup.cause = PRI_CAUSE_USER_BUSY;
 	pri->ev.hangup.channel = c->channelno | (c->ds1no << 8);
 	pri->ev.hangup.cref = c->cr;          		
 	pri->ev.hangup.call = c;
	pri->ev.hangup.aoc_units = 0;
 	pri->ev.e = PRI_EVENT_HANGUP; 
     } 
     return res; 
 }

 

int q931_hangup(struct pri *pri, q931_call *c, int cause)
{
	int disconnect = 1;
	int release_compl = 0;
	if (pri->debug & PRI_DEBUG_Q931_STATE)
		pri_message(pri, "NEW_HANGUP DEBUG: Calling q931_hangup, ourstate %s, peerstate %s\n",callstate2str(c->ourcallstate),callstate2str(c->peercallstate));
	if (!pri || !c)
		return -1;
	/* If mandatory IE was missing, insist upon that cause code */
	if (c->cause == PRI_CAUSE_MANDATORY_IE_MISSING)
		cause = c->cause;
	if (cause == 34 || cause == 44 || cause == 82 || cause == 1 || cause == 81 || cause == 17) {
		/* We'll send RELEASE_COMPLETE with these causes */
		disconnect = 0;
		release_compl = 1;
	}
	if (cause == 6 || cause == 7 || cause == 26) {
		/* We'll send RELEASE with these causes */
		disconnect = 0;
	}
	/* All other causes we send with DISCONNECT */
	switch(c->ourcallstate) {
	case Q931_CALL_STATE_NULL:
		if (c->peercallstate == Q931_CALL_STATE_NULL)
			/* free the resources if we receive or send REL_COMPL */
			q931_destroycall(pri, c->cr, c->tei);
		else if (c->peercallstate == Q931_CALL_STATE_RELEASE_REQUEST)
			q931_release_complete(pri,c,cause);
		break;
	case Q931_CALL_STATE_CALL_INITIATED:
		/* we sent SETUP */
	case Q931_CALL_STATE_OVERLAP_SENDING:
		/* received SETUP_ACKNOWLEDGE */
	case Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING:
		/* received CALL_PROCEEDING */
	case Q931_CALL_STATE_CALL_DELIVERED:
		/* received ALERTING */
	case Q931_CALL_STATE_CALL_PRESENT:
		/* received SETUP */
	case Q931_CALL_STATE_CALL_RECEIVED:
		/* sent ALERTING */
	case Q931_CALL_STATE_CONNECT_REQUEST:
		/* sent CONNECT */
	case Q931_CALL_STATE_INCOMING_CALL_PROCEEDING:
		/* we sent CALL_PROCEEDING */
	case Q931_CALL_STATE_ACTIVE:
		/* received CONNECT */
	case Q931_CALL_STATE_OVERLAP_RECEIVING:
		/* received SETUP_ACKNOWLEDGE */
		/* send DISCONNECT in general */
		if (c->peercallstate != Q931_CALL_STATE_NULL && c->peercallstate != Q931_CALL_STATE_DISCONNECT_REQUEST && c->peercallstate != Q931_CALL_STATE_DISCONNECT_INDICATION && c->peercallstate != Q931_CALL_STATE_RELEASE_REQUEST && c->peercallstate != Q931_CALL_STATE_RESTART_REQUEST && c->peercallstate != Q931_CALL_STATE_RESTART) {
			if (pri->localtype == BRI_NETWORK_PTMP) {
			    if (c->tei == 127) {
			        break;
			    }
			} 
			if (disconnect)
				q931_disconnect(pri,c,cause);
			else if (release_compl)
				q931_release_complete(pri,c,cause);
			else
				q931_release(pri,c,cause);
		} else 
			pri_error(pri, "Wierd, doing nothing but this shouldn't happen, ourstate %s, peerstate %s\n",callstate2str(c->ourcallstate),callstate2str(c->peercallstate));
		break;
	case Q931_CALL_STATE_DISCONNECT_REQUEST:
		/* sent DISCONNECT */
		q931_release(pri,c,cause);
		break;
	case Q931_CALL_STATE_DISCONNECT_INDICATION:
		/* received DISCONNECT */
		if (pri->localtype == BRI_NETWORK_PTMP) {
		    if (c->tei == 127) {
			break;
		    }
		} 
		if (c->peercallstate == Q931_CALL_STATE_DISCONNECT_REQUEST) {
			c->alive = 1;
//		    pri_error(pri, "sending release to %d\n", c->tei);
			q931_release(pri,c,cause);
		}
		break;
	case Q931_CALL_STATE_RELEASE_REQUEST:
		/* sent RELEASE */
		/* don't do anything, waiting for RELEASE_COMPLETE */
		break;
	case Q931_CALL_STATE_RESTART:
	case Q931_CALL_STATE_RESTART_REQUEST:
		/* sent RESTART */
		pri_error(pri, "q931_hangup shouldn't be called in this state, ourstate %s, peerstate %s\n",callstate2str(c->ourcallstate),callstate2str(c->peercallstate));
		break;
	default:
		pri_error(pri, "We're not yet handling hanging up when our state is %d, contact support@digium.com, ourstate %s, peerstate %s\n",callstate2str(c->ourcallstate),callstate2str(c->peercallstate));
		return -1;
	}
	/* we did handle hangup properly at this point */
	return 0;
}

int q931_receive(struct pri *pri, q931_h *h, int len, int tei)
{
	q931_mh *mh;
 	q921_call *l2c;
	q931_call *c;
	q931_ie *ie;
	unsigned int x;
	int y;
	int res;
	int r;
	int mandies[MAX_MAND_IES];
	int missingmand;
	int codeset, cur_codeset;
	int last_ie[8];
	struct apdu_event *cur = NULL;
	int network = pri->localtype == PRI_NETWORK || pri->localtype == BRI_NETWORK || pri->localtype == BRI_NETWORK_PTMP;

	memset(last_ie, 0, sizeof(last_ie));
	if (pri->debug & PRI_DEBUG_Q931_DUMP)
		q931_dump(pri, h, len, 0);
#ifdef LIBPRI_COUNTERS
	pri->q931_rxcount++;
#endif
	mh = (q931_mh *)(h->contents + h->crlen);
	if ((h->pd == 0x3) || (h->pd == 0x43)) {
		/* This is the weird maintenance stuff.  We majorly
		   KLUDGE this by changing byte 4 from a 0xf (SERVICE) 
		   to a 0x7 (SERVICE ACKNOWLEDGE) */
		h->raw[h->crlen + 2] -= 0x8;
		q931_xmit(pri, h, len, 1, tei);
		return 0;
	} else if (h->pd != pri->protodisc) {
		pri_error(pri, "Warning: unknown/inappropriate protocol discriminator received (%02x/%d)\n", h->pd, h->pd);
		return 0;
	}
	c = q931_getcall(pri, q931_cr(h), tei);
	if (!c) {
		pri_error(pri, "Unable to locate call %d\n", q931_cr(h));
		return -1;
	}
	/* Preliminary handling */
	switch(mh->msg) {
	case Q931_RESTART:
		if (pri->debug & PRI_DEBUG_Q931_STATE)
			pri_message(pri, "-- Processing Q.931 Restart\n");
		/* Reset information */
		c->channelno = -1;
		c->slotmap = -1;
		c->chanflags = 0;
		c->ds1no = 0;
		c->ri = -1;
		break;
	case Q931_FACILITY:
		c->callername[0] = '\0';
		break;
	case Q931_SETUP:
		if (pri->debug & PRI_DEBUG_Q931_STATE)
			pri_message(pri, "-- Processing Q.931 Call Setup\n");
		c->tei = tei;
		c->channelno = -1;
		c->slotmap = -1;
		c->chanflags = 0;
		c->ds1no = 0;
		c->ri = -1;
		c->transcapability = -1;
		c->transmoderate = -1;
		c->transmultiple = -1;
		c->userl1 = -1;
		c->userl2 = -1;
		c->userl3 = -1;
		c->rateadaption = -1;
		c->calledplan = -1;
		c->callerplan = -1;
		c->callerpres = -1;
		c->callernum[0] = '\0';
		c->callednum[0] = '\0';
		c->callername[0] = '\0';
		c->callerani[0] = '\0';
		c->callerplanani = -1;
                c->redirectingplan = -1;
                c->redirectingpres = -1;
                c->redirectingreason = -1;
                c->origcalledplan = -1;
                c->origcalledpres = -1;
                c->origredirectingreason = -1;
		c->redirectingnum[0] = '\0';
		c->origcallednum[0] = '\0';
		c->redirectingname[0] = '\0';
		c->origcalledname[0] = '\0';
                c->useruserprotocoldisc = -1; 
		c->useruserinfo[0] = '\0';
		c->complete = 0;
		c->nonisdn = 0;
		c->aoc_units = -1;
 		c->digits[0] = '\0';
 		c->display[0] = '\0';
		c->progress = -1;
		c->progressmask = 0;
		break;
	case Q931_CONNECT:
 		if (c->t303timer) {
 		    c->t303running = 0;
 		    pri_schedule_del(pri, c->t303timer);
     		}
 		c->useruserinfo[0] = '\0';
		c->t303timer = 0;
 		c->progress = -1;
 		break;
	case Q931_ALERTING:
	case Q931_PROGRESS:
 		c->useruserinfo[0] = '\0';
	case Q931_CALL_PROCEEDING:
 		if (c->t303timer) {
 		    c->t303running = 0;
 		    pri_schedule_del(pri, c->t303timer);
 		}
 		c->t303timer = 0;
		c->progress = -1;
		c->progressmask = 0;
		break;
	case Q931_CONNECT_ACKNOWLEDGE:
		if (c->retranstimer)
			pri_schedule_del(pri, c->retranstimer);
		c->retranstimer = 0;
		break;
	case Q931_RELEASE:
	case Q931_DISCONNECT:
//		c->aoc_units = -1;
 		c->useruserinfo[0] = '\0';
		if (c->retranstimer)
			pri_schedule_del(pri, c->retranstimer);
		c->retranstimer = 0;
		if (c->t303timer) {
		    c->t303running = 0;
		    pri_schedule_del(pri, c->t303timer);
    		}
		c->t303timer = 0;
		break;
	case Q931_RELEASE_COMPLETE:
		if (c->retranstimer)
		    pri_schedule_del(pri, c->retranstimer);
		c->retranstimer = 0;
 		c->useruserinfo[0] = '\0';
	case Q931_STATUS:
		c->cause = -1;
		c->causecode = -1;
		c->causeloc = -1;
		c->sugcallstate = -1;
		c->aoc_units = -1;
		break;
	case Q931_RESTART_ACKNOWLEDGE:
		c->channelno = -1;
		break;
	case Q931_INFORMATION:
		c->callednum[0] = '\0';
		break;
	case Q931_STATUS_ENQUIRY:
		break;
	case Q931_SETUP_ACKNOWLEDGE:
		if (c->t303timer) {
		    c->t303running = 0;
		    pri_schedule_del(pri, c->t303timer);
		}
		c->t303timer = 0;
		break;
	case Q931_NOTIFY:
		break;
	case Q931_HOLD:
		break;
	case Q931_RETRIEVE:
		break;
	case Q931_RESUME:
		c->tei = tei;
		break;
	case Q931_SUSPEND:
		break;
	case Q931_USER_INFORMATION:
	case Q931_SEGMENT:
	case Q931_CONGESTION_CONTROL:
	case Q931_HOLD_ACKNOWLEDGE:
	case Q931_HOLD_REJECT:
	case Q931_RETRIEVE_ACKNOWLEDGE:
	case Q931_RETRIEVE_REJECT:
	case Q931_RESUME_ACKNOWLEDGE:
	case Q931_RESUME_REJECT:
	case Q931_SUSPEND_ACKNOWLEDGE:
	case Q931_SUSPEND_REJECT:
		pri_error(pri, "!! Not yet handling pre-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg);
		/* Fall through */
	default:
		pri_error(pri, "!! Don't know how to post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg);
		q931_status(pri,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST);
		if (c->newcall) 
			q931_destroycall(pri,c->cr,c->tei);
		return -1;
	}
	memset(mandies, 0, sizeof(mandies));
	missingmand = 0;
	for (x=0;x<sizeof(msgs) / sizeof(msgs[0]); x++)  {
		if (msgs[x].msgnum == mh->msg) {
			memcpy(mandies, msgs[x].mandies, sizeof(mandies));
		}
	}
	x = 0;
	/* Do real IE processing */
	len -= (h->crlen + 3);
	codeset = cur_codeset = 0;
	while(len) {
		ie = (q931_ie *)(mh->data + x);
		for (y=0;y<MAX_MAND_IES;y++) {
			if (mandies[y] == Q931_FULL_IE(cur_codeset, ie->ie))
				mandies[y] = 0;
		}
		r = ielen(ie);
		if (r > len) {
			pri_error(pri, "XXX Message longer than it should be?? XXX\n");
			return -1;
		}
		/* Special processing for codeset shifts */
		switch (ie->ie & 0xf8) {
		case Q931_LOCKING_SHIFT:
			y = ie->ie & 7;	/* Requested codeset */
			/* Locking shifts couldn't go to lower codeset, and couldn't follows non-locking shifts - verify this */
			if ((cur_codeset != codeset) && (pri->debug & PRI_DEBUG_Q931_ANOMALY))
				pri_message(pri, "XXX Locking shift immediately follows non-locking shift (from %d through %d to %d) XXX\n", codeset, cur_codeset, y);
			if (y > 0) {
				if ((y < codeset) && (pri->debug & PRI_DEBUG_Q931_ANOMALY))
					pri_error(pri, "!! Trying to locked downshift codeset from %d to %d !!\n", codeset, y);
				codeset = cur_codeset = y;
			}
			else {
				/* Locking shift to codeset 0 is forbidden by all specifications */
				pri_error(pri, "!! Invalid locking shift to codeset 0 !!\n");
			}
			break;
		case Q931_NON_LOCKING_SHIFT:
			cur_codeset = ie->ie & 7;
			break;
		default:
			/* Sanity check for IE code order */
			if (!(ie->ie & 0x80)) {
				if (last_ie[cur_codeset] > ie->ie) {
					if ((pri->debug & PRI_DEBUG_Q931_ANOMALY))
						pri_message(pri, "XXX Out-of-order IE %d at codeset %d (last was %d)\n", ie->ie, cur_codeset, last_ie[cur_codeset]);
				}
				else
					last_ie[cur_codeset] = ie->ie;
			}
			/* Ignore non-locking shifts for TR41459-based signalling */
			switch (pri->switchtype) {
			case PRI_SWITCH_LUCENT5E:
			case PRI_SWITCH_ATT4ESS:
				if (cur_codeset != codeset) {
					if ((pri->debug & PRI_DEBUG_Q931_DUMP))
						pri_message(pri, "XXX Ignoring IE %d for temporary codeset %d XXX\n", ie->ie, cur_codeset);
					break;
				}
				/* Fall through */
			default:
				y = q931_handle_ie(cur_codeset, pri, c, mh->msg, ie);
				/* XXX Applicable to codeset 0 only? XXX */
				if (!cur_codeset && !(ie->ie & 0xf0) && (y < 0))
					mandies[MAX_MAND_IES - 1] = Q931_FULL_IE(cur_codeset, ie->ie);
			}
			/* Reset current codeset */
			cur_codeset = codeset;
		}
		x += r;
		len -= r;
	}
	missingmand = 0;
	for (x=0;x<MAX_MAND_IES;x++) {
		if (mandies[x]) {
		    /* check if there is no channel identification when we're configured as network -> that's not an error */
 		    if (network) {
			if (((mh->msg == Q931_SETUP) && (mandies[x] == Q931_CHANNEL_IDENT)) ||
			     ((mh->msg == Q931_PROGRESS) && (mandies[x] == Q931_PROGRESS_INDICATOR))) {
			    /* according to ets 300 102-1 a progress indicator is mandatory, but so what? ;-)  */
			} else {
			    pri_error(pri, "XXX Missing handling for mandatory IE %d (cs%d, %s) XXX\n", Q931_IE_IE(mandies[x]), Q931_IE_CODESET(mandies[x]), ie2str(mandies[x]));
			    missingmand++;
			}
		    } else {
			pri_error(pri, "XXX Missing handling for mandatory IE %d (cs%d, %s) XXX\n", Q931_IE_IE(mandies[x]), Q931_IE_CODESET(mandies[x]), ie2str(mandies[x]));
			missingmand++;
		    }
		}
	}
	
	/* Post handling */
	switch(mh->msg) {
	case Q931_RESTART:
		if (missingmand) {
			q931_status(pri, c, PRI_CAUSE_MANDATORY_IE_MISSING);
			q931_destroycall(pri, c->cr, c->tei);
			break;
		}
		c->ourcallstate = Q931_CALL_STATE_RESTART;
		c->peercallstate = Q931_CALL_STATE_RESTART_REQUEST;
		/* Send back the Restart Acknowledge */
		restart_ack(pri, c);
		/* Notify user of restart event */
		pri->ev.e = PRI_EVENT_RESTART;
		pri->ev.restart.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16);
		return Q931_RES_HAVEEVENT;
	case Q931_SETUP:
		if (missingmand) {
			q931_release_complete(pri, c, PRI_CAUSE_MANDATORY_IE_MISSING);
			break;
		}
		/* Must be new call */
		if (!c->newcall) {
			pri_error(pri, "received SETUP message for call that is not a new call (retransmission). \n");
			break;
		}
		if (c->progressmask & PRI_PROG_CALLER_NOT_ISDN)
			c->nonisdn = 1;
		c->newcall = 0;
		c->ourcallstate = Q931_CALL_STATE_CALL_PRESENT;
		c->peercallstate = Q931_CALL_STATE_CALL_INITIATED;
		/* it's not yet a call since higher level can respond with RELEASE or RELEASE_COMPLETE */
		c->alive = 0;
		if (c->transmoderate != TRANS_MODE_64_CIRCUIT) {
			q931_release_complete(pri, c, PRI_CAUSE_BEARERCAPABILITY_NOTIMPL);
			break;
		}
		pri->ev.e = PRI_EVENT_RING;
		pri->ev.ring.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16);
		pri->ev.ring.callingpres = c->callerpres;
		pri->ev.ring.callingplan = c->callerplan;
		pri->ev.ring.callingplanrdnis = c->redirectingplan;
		pri->ev.ring.callingplanorigcalled = c->origcalledplan;
		pri->ev.ring.ani2 = c->ani2;
		libpri_copy_string(pri->ev.ring.callingani, c->callerani, sizeof(pri->ev.ring.callingani));
		pri->ev.ring.callingplanani = c->callerplanani;
		libpri_copy_string(pri->ev.ring.callingnum, c->callernum, sizeof(pri->ev.ring.callingnum));
		libpri_copy_string(pri->ev.ring.callingname, c->callername, sizeof(pri->ev.ring.callingname));
		pri->ev.ring.calledplan = c->calledplan;
		libpri_copy_string(pri->ev.ring.callingsubaddr, c->callingsubaddr, sizeof(pri->ev.ring.callingsubaddr));
		if (!strlen(c->callednum) && strlen(c->digitbuf)) {
		    libpri_copy_string(pri->ev.ring.callednum, c->digitbuf, sizeof(pri->ev.ring.callednum));
		} else {
		    libpri_copy_string(pri->ev.ring.callednum, c->callednum, sizeof(pri->ev.ring.callednum));
		}
		libpri_copy_string(pri->ev.ring.origcalledname, c->origcalledname, sizeof(pri->ev.ring.origcalledname));
		libpri_copy_string(pri->ev.ring.origcallednum, c->origcallednum, sizeof(pri->ev.ring.origcallednum));
                libpri_copy_string(pri->ev.ring.redirectingnum, c->redirectingnum, sizeof(pri->ev.ring.redirectingnum));
                libpri_copy_string(pri->ev.ring.redirectingname, c->redirectingname, sizeof(pri->ev.ring.redirectingname));
                libpri_copy_string(pri->ev.ring.useruserinfo, c->useruserinfo, sizeof(pri->ev.ring.useruserinfo));
		c->useruserinfo[0] = '\0';
		pri->ev.ring.redirectingreason = c->redirectingreason;
		pri->ev.ring.origredirectingreason = c->origredirectingreason;
		pri->ev.ring.flexible = ! (c->chanflags & FLAG_EXCLUSIVE);
		pri->ev.ring.tei = c->tei;
		pri->ev.ring.call = c;
		pri->ev.ring.cref = c->cr;
		pri->ev.ring.layer1 = c->userl1;
		pri->ev.ring.complete = c->complete; 
		pri->ev.ring.ctype = c->transcapability;
		memcpy(pri->ev.ring.lowlayercompat, c->llc, sizeof(pri->ev.ring.lowlayercompat));
		pri->ev.ring.redirectingreason = c->redirectingreason;
		pri->ev.ring.progress = c->progress;
		pri->ev.ring.progressmask = c->progressmask;
		return Q931_RES_HAVEEVENT;
	case Q931_ALERTING:
		if (c->newcall) {
			q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
			break;
		}
		if (pri->localtype == BRI_NETWORK_PTMP) {
		    l2c = q921_getcall(pri, c, tei);
		}
		c->ourcallstate = Q931_CALL_STATE_CALL_DELIVERED;
		c->peercallstate = Q931_CALL_STATE_CALL_RECEIVED;
		pri->ev.e = PRI_EVENT_RINGING;
		pri->ev.ringing.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16);
		pri->ev.ringing.cref = c->cr;
		pri->ev.ringing.call = c;
		pri->ev.ringing.progress = c->progress;
		pri->ev.ringing.progressmask = c->progressmask;
                libpri_copy_string(pri->ev.ringing.useruserinfo, c->useruserinfo, sizeof(pri->ev.ring.useruserinfo));
		c->useruserinfo[0] = '\0';
		return Q931_RES_HAVEEVENT;
	case Q931_CONNECT:
		if (c->newcall) {
			q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
			break;
		}
		if (c->ourcallstate == Q931_CALL_STATE_ACTIVE) {
			q931_status(pri, c, PRI_CAUSE_WRONG_MESSAGE);
			break;
		}
  		/* TEI got the call */
  		c->tei = tei;
		c->ourcallstate = Q931_CALL_STATE_ACTIVE;
		c->peercallstate = Q931_CALL_STATE_CONNECT_REQUEST;
		pri->ev.e = PRI_EVENT_ANSWER;
		pri->ev.answer.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16);
		pri->ev.answer.cref = c->cr;
		pri->ev.answer.call = c;
 		pri->ev.answer.tei = c->tei;
		pri->ev.answer.progress = c->progress;
		pri->ev.answer.progressmask = c->progressmask;
                libpri_copy_string(pri->ev.answer.useruserinfo, c->useruserinfo, sizeof(pri->ev.ring.useruserinfo));
		c->useruserinfo[0] = '\0';
		q931_connect_acknowledge(pri, c);
 		if (pri->localtype == BRI_NETWORK_PTMP) {
		    /* Release all other TEIs */
  		    q921_hangup(pri, c, tei);
  		}
		if (c->justsignalling) {  /* Make sure WE release when we initiatie a signalling only connection */
			q931_release(pri, c, PRI_CAUSE_NORMAL_CLEARING);
			break;
		} else
			return Q931_RES_HAVEEVENT;
	case Q931_FACILITY:
		if (c->newcall) {
  			if ((pri->localtype == PRI_CPE) || (pri->localtype == PRI_NETWORK)) {
  			    q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
  			} else {
  			    // BRI uses the dummy cref for sservices like ccnr
  			}
 			break;
 		}
  		if (c->facility > 0) {
  		    pri->ev.e = PRI_EVENT_FACILITY;
  		    pri->ev.facility.channel = c->channelno | (c->ds1no << 8);
  		    pri->ev.facility.cref = c->cr;
  		    pri->ev.facility.tei = c->tei;
  		    pri->ev.facility.call = c; 
  		    switch (c->facility) {
  			case 0x06: /* ECT execute */
  				pri->ev.facility.operation = 0x06; 
  			    break;
  			case 0x0D: /* CD */
  				pri->ev.facility.operation = 0x0D; 
  				libpri_copy_string(pri->ev.facility.forwardnum, c->redirectingnum,  sizeof(pri->ev.facility.forwardnum));
  			    break;
  			default:
  				pri->ev.facility.operation = c->facility; 
  		    }
  		} else {
  		    pri->ev.e = PRI_EVENT_FACNAME;
  		    libpri_copy_string(pri->ev.facname.callingname, c->callername, sizeof(pri->ev.facname.callingname));
  		    libpri_copy_string(pri->ev.facname.callingnum, c->callernum, sizeof(pri->ev.facname.callingname));
  		    pri->ev.facname.channel = c->channelno | (c->ds1no << 8);
  		    pri->ev.facname.cref = c->cr;
  		    pri->ev.facname.call = c; 
   		}
		return Q931_RES_HAVEEVENT;
	case Q931_PROGRESS:
		if (missingmand) {
			q931_status(pri, c, PRI_CAUSE_MANDATORY_IE_MISSING);
			q931_destroycall(pri, c->cr, c->tei);
			break;
		}
		pri->ev.e = PRI_EVENT_PROGRESS;
		pri->ev.proceeding.cause = c->cause;
		/* Fall through */
	case Q931_CALL_PROCEEDING:
		if (c->newcall) {
			q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
			break;
		}
		if ((c->ourcallstate != Q931_CALL_STATE_CALL_INITIATED) &&
		    (c->ourcallstate != Q931_CALL_STATE_OVERLAP_SENDING) && 
		    (c->ourcallstate != Q931_CALL_STATE_CALL_DELIVERED) && 
		    (c->ourcallstate != Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING)) {
			q931_status(pri,c,PRI_CAUSE_WRONG_MESSAGE);
			break;
		}
 		if (pri->localtype == BRI_NETWORK_PTMP) {
 		    l2c = q921_getcall(pri, c, tei);
 		    l2c->proc = 1;
 		    l2c->channel = c->channelno;
		}
		pri->ev.proceeding.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16);
		if (mh->msg == Q931_CALL_PROCEEDING) {
			pri->ev.e = PRI_EVENT_PROCEEDING;
			c->ourcallstate = Q931_CALL_STATE_OUTGOING_CALL_PROCEEDING;
			c->peercallstate = Q931_CALL_STATE_INCOMING_CALL_PROCEEDING;
		}
		pri->ev.proceeding.progress = c->progress;
		pri->ev.proceeding.progressmask = c->progressmask;
		pri->ev.proceeding.cref = c->cr;
		pri->ev.proceeding.call = c;
		return Q931_RES_HAVEEVENT;
	case Q931_CONNECT_ACKNOWLEDGE:
		if (c->newcall) {
			q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
			break;
		}
		if (c->ourcallstate != Q931_CALL_STATE_CONNECT_REQUEST) {
		    if ((c->ourcallstate == Q931_CALL_STATE_ACTIVE) && (c->con_acked == 0) && (network)) {
			/* sending a CONNECT_ACKNOWLEDGE is optional for CPEs */
		    } else {
			q931_status(pri,c,PRI_CAUSE_WRONG_MESSAGE);
		    }
		    break;
		}
		c->ourcallstate = Q931_CALL_STATE_ACTIVE;
		c->peercallstate = Q931_CALL_STATE_ACTIVE;
		c->con_acked = 1;
		break;
	case Q931_STATUS:
		if (missingmand) {
			q931_status(pri, c, PRI_CAUSE_MANDATORY_IE_MISSING);
			q931_destroycall(pri, c->cr, c->tei);
			break;
		}
		if (c->newcall) {
			if (c->cr & 0x7fff)
				q931_release_complete(pri,c,PRI_CAUSE_WRONG_CALL_STATE);
			break;
		}
		/* Do nothing */
		/* Also when the STATUS asks for the call of an unexisting reference send RELEASE_COMPL */
		if ((pri->debug & PRI_DEBUG_Q931_ANOMALY) &&
		    (c->cause != PRI_CAUSE_INTERWORKING)) 
			pri_error(pri, "Received unsolicited status: %s\n", pri_cause2str(c->cause));
		/* Workaround for S-12 ver 7.3 - it responds for invalid/non-implemented IEs at SETUP with null call state */
		if (!c->sugcallstate && (c->ourcallstate != Q931_CALL_STATE_CALL_INITIATED)) {
			pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16);
			pri->ev.hangup.cref = c->cr;          		
			pri->ev.hangup.cause = c->cause;      		
			pri->ev.hangup.call = c;              		
			/* Free resources */
			c->ourcallstate = Q931_CALL_STATE_NULL;
			c->peercallstate = Q931_CALL_STATE_NULL;
			if (c->alive) {
				pri->ev.e = PRI_EVENT_HANGUP;
				res = Q931_RES_HAVEEVENT;
				c->alive = 0;
			} else if (c->sendhangupack) {
				res = Q931_RES_HAVEEVENT;
				pri->ev.e = PRI_EVENT_HANGUP_ACK;
				q931_hangup(pri, c, c->cause);
			} else {
				q931_hangup(pri, c, c->cause);
				res = 0;
			}
			if (res)
				return res;
		}
		if (c->ourcallstate != c->sugcallstate) {
            	    pri_error(pri, "updating callstate, peercallstate %d to %d\n", c->peercallstate, c->sugcallstate);
        	    c->ourcallstate = c->sugcallstate;
            	    if ((c->sugcallstate != Q931_CALL_STATE_ACTIVE) &&(c->sugcallstate != Q931_CALL_STATE_CALL_INITIATED)) {
                        /* pass hangup to upper layer! */
                        if (c->alive) {
                                pri->ev.e = PRI_EVENT_HANGUP;
                                res = Q931_RES_HAVEEVENT;
                                c->alive = 0;
                        } else if (c->sendhangupack) {
                                res = Q931_RES_HAVEEVENT;
                                pri->ev.e = PRI_EVENT_HANGUP_ACK;
                                q931_hangup(pri, c, c->cause);
                        } else {
                                q931_hangup(pri, c, c->cause);
                                res = 0;
			if (res)
				return res;
                        }
		    }
                }
                break;
	case Q931_RELEASE_COMPLETE:
    		if (pri->localtype != BRI_NETWORK_PTMP) {
		    /* only stop the T303 timer if WE are not a BRI PTMP network */
		    if (c->t303timer) {
 			c->t303running = 0;
 			pri_schedule_del(pri, c->t303timer);
 		        c->t303timer = 0;
		    }
     		}

 		if ((pri->localtype != BRI_NETWORK_PTMP) || (c->tei == tei)) {
 		    c->ourcallstate = Q931_CALL_STATE_NULL;
 		    c->peercallstate = Q931_CALL_STATE_NULL;
 		    pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16);
 		    pri->ev.hangup.cref = c->cr;          		
 		    pri->ev.hangup.cause = c->cause;      		
 		    pri->ev.hangup.call = c;              		
        	    libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.ring.useruserinfo));
		    c->useruserinfo[0] = '\0';
 		    /* Free resources */
 		    if (c->alive) {
  			pri->ev.e = PRI_EVENT_HANGUP;
  			res = Q931_RES_HAVEEVENT;
  			c->alive = 0;
 		    } else if (c->sendhangupack) {
  			res = Q931_RES_HAVEEVENT;
  			pri->ev.e = PRI_EVENT_HANGUP_ACK;
  			pri_hangup(pri, c, c->cause, -1);
 		    } else
  			res = 0;
 		    if (res)
  			return res;
 		    else
  			q931_hangup(pri,c,c->cause);
 		} else {
 		    // BRI_NET_PTMP
 		    // ignoring relase_complete
 		    res = q921_handle_hangup(pri,c,tei);
 		    if (res)
 			return res;
 		}
		break;
	case Q931_RELEASE:
		if (missingmand) {
			/* Force cause to be mandatory IE missing */
			c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
		}
		if (c->ourcallstate == Q931_CALL_STATE_RELEASE_REQUEST) 
			c->peercallstate = Q931_CALL_STATE_NULL;
		else {
			c->peercallstate = Q931_CALL_STATE_RELEASE_REQUEST;
		}
		c->ourcallstate = Q931_CALL_STATE_NULL;
		pri->ev.e = PRI_EVENT_HANGUP;
		pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16);
		pri->ev.hangup.cref = c->cr;
		pri->ev.hangup.tei = c->tei;
		pri->ev.hangup.cause = c->cause;
		pri->ev.hangup.call = c;
		pri->ev.hangup.aoc_units = c->aoc_units;
                libpri_copy_string(pri->ev.hangup.useruserinfo, c->useruserinfo, sizeof(pri->ev.ring.useruserinfo));
		c->useruserinfo[0] = '\0';
		/* Don't send release complete if they send us release 
		   while we sent it, assume a NULL state */
		if (c->newcall)
			q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
		else 
			return Q931_RES_HAVEEVENT;
		break;
	case Q931_DISCONNECT:
		if (missingmand) {
			/* Still let user call release */
			c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
		}
		if (c->newcall) {
			q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
			break;
		}
		c->ourcallstate = Q931_CALL_STATE_DISCONNECT_INDICATION;
		c->peercallstate = Q931_CALL_STATE_DISCONNECT_REQUEST;
		c->sendhangupack = 1;
		/* Return such an event */
		pri->ev.e = PRI_EVENT_HANGUP_REQ;
		pri->ev.hangup.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16);
		pri->ev.hangup.cref = c->cr;
		pri->ev.hangup.tei = c->tei;
		pri->ev.hangup.cause = c->cause;
		pri->ev.hangup.call = c;
		pri->ev.hangup.aoc_units = c->aoc_units;
		if (c->progressmask & PRI_PROG_INBAND_AVAILABLE) {
		    pri->ev.hangup.inband_progress = 1;
		} else {
		    pri->ev.hangup.inband_progress = 0;
		}
		pri->ev.hangup.aoc_units = c->aoc_units;
		if (c->alive)
			return Q931_RES_HAVEEVENT;
		else
			q931_hangup(pri,c,c->cause);
		break;
	case Q931_RESTART_ACKNOWLEDGE:
		c->ourcallstate = Q931_CALL_STATE_NULL;
		c->peercallstate = Q931_CALL_STATE_NULL;
		pri->ev.e = PRI_EVENT_RESTART_ACK;
		pri->ev.restartack.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16);
		return Q931_RES_HAVEEVENT;
	case Q931_INFORMATION:
		/* XXX We're handling only INFORMATION messages that contain
		       overlap dialing received digit
		       +  the "Complete" msg which is basically an EOF on further digits
		   XXX */
		if (c->newcall) {
			q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
			break;
		}
		if (c->ourcallstate != Q931_CALL_STATE_OVERLAP_RECEIVING) {
			pri->ev.e = PRI_EVENT_KEYPAD_DIGIT;
			pri->ev.digit.call = c;
			pri->ev.digit.channel = c->channelno | (c->ds1no << 8);
			libpri_copy_string(pri->ev.digit.digits, c->digitbuf, sizeof(pri->ev.digit.digits));
			return Q931_RES_HAVEEVENT;
		}
		pri->ev.e = PRI_EVENT_INFO_RECEIVED;
		pri->ev.ring.call = c;
		pri->ev.ring.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16);
 		libpri_copy_string(pri->ev.ring.callednum, c->digits, sizeof(pri->ev.ring.callednum) );
		libpri_copy_string(pri->ev.ring.callingsubaddr, c->callingsubaddr, sizeof(pri->ev.ring.callingsubaddr));
		pri->ev.ring.complete = c->complete; 	/* this covers IE 33 (Sending Complete) */
		return Q931_RES_HAVEEVENT;
	case Q931_STATUS_ENQUIRY:
		if (c->newcall) {
			q931_release_complete(pri, c, PRI_CAUSE_INVALID_CALL_REFERENCE);
		} else
			q931_status(pri,c, 0);
		break;
	case Q931_SETUP_ACKNOWLEDGE:
		if (c->newcall) {
			q931_release_complete(pri,c,PRI_CAUSE_INVALID_CALL_REFERENCE);
			break;
		}
		c->ourcallstate = Q931_CALL_STATE_OVERLAP_SENDING;
		c->peercallstate = Q931_CALL_STATE_OVERLAP_RECEIVING;
		pri->ev.e = PRI_EVENT_SETUP_ACK;
		pri->ev.setup_ack.channel = c->channelno | (c->ds1no << 8) | (c->ds1explicit << 16);
		pri->ev.setup_ack.call = c;
		cur = c->apdus;
		while (cur) {
			if (!cur->sent && cur->message == Q931_FACILITY) {
				q931_facility(pri, c);
				break;
			}
			cur = cur->next;
		}

		return Q931_RES_HAVEEVENT;
	case Q931_NOTIFY:
		pri->ev.e = PRI_EVENT_NOTIFY;
		pri->ev.notify.channel = c->channelno;
		pri->ev.notify.info = c->notify;
		return Q931_RES_HAVEEVENT;
	case Q931_HOLD:
		pri->ev.e = PRI_EVENT_HOLD_REQ;
		pri->ev.hold_req.call = c;
		pri->ev.hold_req.cref = c->cr;
		pri->ev.hold_req.tei = c->tei;
		pri->ev.hold_req.channel = c->channelno;
		return Q931_RES_HAVEEVENT;
		break;
	case Q931_RETRIEVE:
		pri->ev.e = PRI_EVENT_RETRIEVE_REQ;
		pri->ev.retrieve_req.call = c;
		pri->ev.retrieve_req.cref = c->cr;
		pri->ev.retrieve_req.tei = c->tei;
		pri->ev.retrieve_req.channel = c->channelno;
		return Q931_RES_HAVEEVENT;
		break;
	case Q931_SUSPEND:
		pri->ev.e = PRI_EVENT_SUSPEND_REQ;
		pri->ev.suspend_req.call = c;
		pri->ev.suspend_req.cref = c->cr;
		pri->ev.suspend_req.tei = c->tei;
		pri->ev.suspend_req.channel = c->channelno;
		strncpy(pri->ev.suspend_req.callid, c->callid, sizeof(pri->ev.suspend_req.callid) - 1);
 		return Q931_RES_HAVEEVENT;
		break;
	case Q931_RESUME:
		if (pri->localtype == BRI_NETWORK_PTMP) {
		    l2c = q921_getcall(pri, c, tei);
		}
		c->newcall = 0;
		pri->ev.e = PRI_EVENT_RESUME_REQ;
		pri->ev.resume_req.call = c;
		pri->ev.resume_req.cref = c->cr;
		pri->ev.resume_req.tei = c->tei;
		pri->ev.resume_req.channel = c->channelno;
		strncpy(pri->ev.resume_req.callid, c->callid, sizeof(pri->ev.resume_req.callid) - 1);
		return Q931_RES_HAVEEVENT;
		break;
	case Q931_USER_INFORMATION:
	case Q931_SEGMENT:
	case Q931_CONGESTION_CONTROL:
	case Q931_HOLD_ACKNOWLEDGE:
	case Q931_HOLD_REJECT:
	case Q931_RETRIEVE_ACKNOWLEDGE:
	case Q931_RETRIEVE_REJECT:
	case Q931_RESUME_ACKNOWLEDGE:
	case Q931_RESUME_REJECT:
	case Q931_SUSPEND_ACKNOWLEDGE:
	case Q931_SUSPEND_REJECT:
		pri_error(pri, "!! Not yet handling post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg);
		/* Fall through */
	default:
		
		pri_error(pri, "!! Don't know how to post-handle message type %s (%d)\n", msg2str(mh->msg), mh->msg);
		q931_status(pri,c, PRI_CAUSE_MESSAGE_TYPE_NONEXIST);
		if (c->newcall) 
			q931_destroycall(pri,c->cr,c->tei);
		return -1;
	}
	return 0;
}

int q931_call_getcrv(struct pri *pri, q931_call *call, int *callmode)
{
	if (callmode)
		*callmode = call->cr & 0x7;
	return ((call->cr & 0x7fff) >> 3);
}

int q931_call_setcrv(struct pri *pri, q931_call *call, int crv, int callmode)
{
	call->cr = (crv << 3) & 0x7fff;
	call->cr |= (callmode & 0x7);
	return 0;
}

