1514 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1514 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* $Id: q931.c,v 1.12.2.3 2004/01/13 14:31:26 keil Exp $
 | 
						|
 *
 | 
						|
 * code to decode ITU Q.931 call control messages
 | 
						|
 *
 | 
						|
 * Author       Jan den Ouden
 | 
						|
 * Copyright    by Jan den Ouden
 | 
						|
 *
 | 
						|
 * This software may be used and distributed according to the terms
 | 
						|
 * of the GNU General Public License, incorporated herein by reference.
 | 
						|
 *
 | 
						|
 * Changelog:
 | 
						|
 *
 | 
						|
 * Pauline Middelink    general improvements
 | 
						|
 * Beat Doebeli         cause texts, display information element
 | 
						|
 * Karsten Keil         cause texts, display information element for 1TR6
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
 | 
						|
#include "hisax.h"
 | 
						|
#include "l3_1tr6.h"
 | 
						|
 | 
						|
void
 | 
						|
iecpy(u_char *dest, u_char *iestart, int ieoffset)
 | 
						|
{
 | 
						|
	u_char *p;
 | 
						|
	int l;
 | 
						|
 | 
						|
	p = iestart + ieoffset + 2;
 | 
						|
	l = iestart[1] - ieoffset;
 | 
						|
	while (l--)
 | 
						|
		*dest++ = *p++;
 | 
						|
	*dest++ = '\0';
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * According to Table 4-2/Q.931
 | 
						|
 */
 | 
						|
static
 | 
						|
struct MessageType {
 | 
						|
	u_char nr;
 | 
						|
	char *descr;
 | 
						|
} mtlist[] = {
 | 
						|
 | 
						|
	{
 | 
						|
		0x1, "ALERTING"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x2, "CALL PROCEEDING"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x7, "CONNECT"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0xf, "CONNECT ACKNOWLEDGE"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x3, "PROGRESS"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x5, "SETUP"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0xd, "SETUP ACKNOWLEDGE"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x24, "HOLD"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x28, "HOLD ACKNOWLEDGE"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x30, "HOLD REJECT"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x31, "RETRIEVE"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x33, "RETRIEVE ACKNOWLEDGE"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x37, "RETRIEVE REJECT"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x26, "RESUME"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x2e, "RESUME ACKNOWLEDGE"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x22, "RESUME REJECT"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x25, "SUSPEND"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x2d, "SUSPEND ACKNOWLEDGE"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x21, "SUSPEND REJECT"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x20, "USER INFORMATION"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x45, "DISCONNECT"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x4d, "RELEASE"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x5a, "RELEASE COMPLETE"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x46, "RESTART"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x4e, "RESTART ACKNOWLEDGE"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x60, "SEGMENT"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x79, "CONGESTION CONTROL"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x7b, "INFORMATION"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x62, "FACILITY"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x6e, "NOTIFY"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x7d, "STATUS"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x75, "STATUS ENQUIRY"
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
#define MTSIZE ARRAY_SIZE(mtlist)
 | 
						|
 | 
						|
static
 | 
						|
struct MessageType mt_n0[] =
 | 
						|
{
 | 
						|
	{MT_N0_REG_IND, "REGister INDication"},
 | 
						|
	{MT_N0_CANC_IND, "CANCel INDication"},
 | 
						|
	{MT_N0_FAC_STA, "FACility STAtus"},
 | 
						|
	{MT_N0_STA_ACK, "STAtus ACKnowledge"},
 | 
						|
	{MT_N0_STA_REJ, "STAtus REJect"},
 | 
						|
	{MT_N0_FAC_INF, "FACility INFormation"},
 | 
						|
	{MT_N0_INF_ACK, "INFormation ACKnowledge"},
 | 
						|
	{MT_N0_INF_REJ, "INFormation REJect"},
 | 
						|
	{MT_N0_CLOSE, "CLOSE"},
 | 
						|
	{MT_N0_CLO_ACK, "CLOse ACKnowledge"}
 | 
						|
};
 | 
						|
 | 
						|
#define MT_N0_LEN ARRAY_SIZE(mt_n0)
 | 
						|
 | 
						|
static
 | 
						|
struct MessageType mt_n1[] =
 | 
						|
{
 | 
						|
	{MT_N1_ESC, "ESCape"},
 | 
						|
	{MT_N1_ALERT, "ALERT"},
 | 
						|
	{MT_N1_CALL_SENT, "CALL SENT"},
 | 
						|
	{MT_N1_CONN, "CONNect"},
 | 
						|
	{MT_N1_CONN_ACK, "CONNect ACKnowledge"},
 | 
						|
	{MT_N1_SETUP, "SETUP"},
 | 
						|
	{MT_N1_SETUP_ACK, "SETUP ACKnowledge"},
 | 
						|
	{MT_N1_RES, "RESume"},
 | 
						|
	{MT_N1_RES_ACK, "RESume ACKnowledge"},
 | 
						|
	{MT_N1_RES_REJ, "RESume REJect"},
 | 
						|
	{MT_N1_SUSP, "SUSPend"},
 | 
						|
	{MT_N1_SUSP_ACK, "SUSPend ACKnowledge"},
 | 
						|
	{MT_N1_SUSP_REJ, "SUSPend REJect"},
 | 
						|
	{MT_N1_USER_INFO, "USER INFO"},
 | 
						|
	{MT_N1_DET, "DETach"},
 | 
						|
	{MT_N1_DISC, "DISConnect"},
 | 
						|
	{MT_N1_REL, "RELease"},
 | 
						|
	{MT_N1_REL_ACK, "RELease ACKnowledge"},
 | 
						|
	{MT_N1_CANC_ACK, "CANCel ACKnowledge"},
 | 
						|
	{MT_N1_CANC_REJ, "CANCel REJect"},
 | 
						|
	{MT_N1_CON_CON, "CONgestion CONtrol"},
 | 
						|
	{MT_N1_FAC, "FACility"},
 | 
						|
	{MT_N1_FAC_ACK, "FACility ACKnowledge"},
 | 
						|
	{MT_N1_FAC_CAN, "FACility CANcel"},
 | 
						|
	{MT_N1_FAC_REG, "FACility REGister"},
 | 
						|
	{MT_N1_FAC_REJ, "FACility REJect"},
 | 
						|
	{MT_N1_INFO, "INFOrmation"},
 | 
						|
	{MT_N1_REG_ACK, "REGister ACKnowledge"},
 | 
						|
	{MT_N1_REG_REJ, "REGister REJect"},
 | 
						|
	{MT_N1_STAT, "STATus"}
 | 
						|
};
 | 
						|
 | 
						|
#define MT_N1_LEN ARRAY_SIZE(mt_n1)
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
prbits(char *dest, u_char b, int start, int len)
 | 
						|
{
 | 
						|
	char *dp = dest;
 | 
						|
 | 
						|
	b = b << (8 - start);
 | 
						|
	while (len--) {
 | 
						|
		if (b & 0x80)
 | 
						|
			*dp++ = '1';
 | 
						|
		else
 | 
						|
			*dp++ = '0';
 | 
						|
		b = b << 1;
 | 
						|
	}
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
u_char *
 | 
						|
skipext(u_char *p)
 | 
						|
{
 | 
						|
	while (!(*p++ & 0x80));
 | 
						|
	return (p);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Cause Values According to Q.850
 | 
						|
 * edescr: English description
 | 
						|
 * ddescr: German description used by Swissnet II (Swiss Telecom
 | 
						|
 *         not yet written...
 | 
						|
 */
 | 
						|
 | 
						|
static
 | 
						|
struct CauseValue {
 | 
						|
	u_char nr;
 | 
						|
	char *edescr;
 | 
						|
	char *ddescr;
 | 
						|
} cvlist[] = {
 | 
						|
 | 
						|
	{
 | 
						|
		0x01, "Unallocated (unassigned) number", "Nummer nicht zugeteilt"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x02, "No route to specified transit network", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x03, "No route to destination", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x04, "Send special information tone", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x05, "Misdialled trunk prefix", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x06, "Channel unacceptable", "Kanal nicht akzeptierbar"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x07, "Channel awarded and being delivered in an established channel", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x08, "Preemption", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x09, "Preemption - circuit reserved for reuse", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x10, "Normal call clearing", "Normale Ausloesung"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x11, "User busy", "TNB besetzt"
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x12, "No user responding", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x13, "No answer from user (user alerted)", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x14, "Subscriber absent", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x15, "Call rejected", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x16, "Number changed", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x1a, "non-selected user clearing", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x1b, "Destination out of order", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x1c, "Invalid number format (address incomplete)", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x1d, "Facility rejected", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x1e, "Response to Status enquiry", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x1f, "Normal, unspecified", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x22, "No circuit/channel available", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x26, "Network out of order", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x27, "Permanent frame mode connection out-of-service", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x28, "Permanent frame mode connection operational", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x29, "Temporary failure", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x2a, "Switching equipment congestion", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x2b, "Access information discarded", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x2c, "Requested circuit/channel not available", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x2e, "Precedence call blocked", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x2f, "Resource unavailable, unspecified", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x31, "Quality of service unavailable", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x32, "Requested facility not subscribed", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x35, "Outgoing calls barred within CUG", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x37, "Incoming calls barred within CUG", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x39, "Bearer capability not authorized", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x3a, "Bearer capability not presently available", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x3e, "Inconsistency in designated outgoing access information and subscriber class ", " "
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x3f, "Service or option not available, unspecified", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x41, "Bearer capability not implemented", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x42, "Channel type not implemented", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x43, "Requested facility not implemented", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x44, "Only restricted digital information bearer capability is available", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x4f, "Service or option not implemented", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x51, "Invalid call reference value", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x52, "Identified channel does not exist", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x53, "A suspended call exists, but this call identity does not", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x54, "Call identity in use", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x55, "No call suspended", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x56, "Call having the requested call identity has been cleared", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x57, "User not member of CUG", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x58, "Incompatible destination", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x5a, "Non-existent CUG", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x5b, "Invalid transit network selection", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x5f, "Invalid message, unspecified", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x60, "Mandatory information element is missing", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x61, "Message type non-existent or not implemented", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x62, "Message not compatible with call state or message type non-existent or not implemented ", " "
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x63, "Information element/parameter non-existent or not implemented", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x64, "Invalid information element contents", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x65, "Message not compatible with call state", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x66, "Recovery on timer expiry", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x67, "Parameter non-existent or not implemented - passed on", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x6e, "Message with unrecognized parameter discarded", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x6f, "Protocol error, unspecified", ""
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x7f, "Interworking, unspecified", ""
 | 
						|
	},
 | 
						|
};
 | 
						|
 | 
						|
#define CVSIZE ARRAY_SIZE(cvlist)
 | 
						|
 | 
						|
static
 | 
						|
int
 | 
						|
prcause(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	u_char *end;
 | 
						|
	char *dp = dest;
 | 
						|
	int i, cause;
 | 
						|
 | 
						|
	end = p + p[1] + 1;
 | 
						|
	p += 2;
 | 
						|
	dp += sprintf(dp, "    coding ");
 | 
						|
	dp += prbits(dp, *p, 7, 2);
 | 
						|
	dp += sprintf(dp, " location ");
 | 
						|
	dp += prbits(dp, *p, 4, 4);
 | 
						|
	*dp++ = '\n';
 | 
						|
	p = skipext(p);
 | 
						|
 | 
						|
	cause = 0x7f & *p++;
 | 
						|
 | 
						|
	/* locate cause value */
 | 
						|
	for (i = 0; i < CVSIZE; i++)
 | 
						|
		if (cvlist[i].nr == cause)
 | 
						|
			break;
 | 
						|
 | 
						|
	/* display cause value if it exists */
 | 
						|
	if (i == CVSIZE)
 | 
						|
		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
 | 
						|
	else
 | 
						|
		dp += sprintf(dp, "  cause value %x : %s \n", cause, cvlist[i].edescr);
 | 
						|
 | 
						|
	while (!0) {
 | 
						|
		if (p > end)
 | 
						|
			break;
 | 
						|
		dp += sprintf(dp, "    diag attribute %d ", *p++ & 0x7f);
 | 
						|
		dp += sprintf(dp, " rej %d ", *p & 0x7f);
 | 
						|
		if (*p & 0x80) {
 | 
						|
			*dp++ = '\n';
 | 
						|
			break;
 | 
						|
		} else
 | 
						|
			dp += sprintf(dp, " av %d\n", (*++p) & 0x7f);
 | 
						|
	}
 | 
						|
	return (dp - dest);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
struct MessageType cause_1tr6[] =
 | 
						|
{
 | 
						|
	{CAUSE_InvCRef, "Invalid Call Reference"},
 | 
						|
	{CAUSE_BearerNotImpl, "Bearer Service Not Implemented"},
 | 
						|
	{CAUSE_CIDunknown, "Caller Identity unknown"},
 | 
						|
	{CAUSE_CIDinUse, "Caller Identity in Use"},
 | 
						|
	{CAUSE_NoChans, "No Channels available"},
 | 
						|
	{CAUSE_FacNotImpl, "Facility Not Implemented"},
 | 
						|
	{CAUSE_FacNotSubscr, "Facility Not Subscribed"},
 | 
						|
	{CAUSE_OutgoingBarred, "Outgoing calls barred"},
 | 
						|
	{CAUSE_UserAccessBusy, "User Access Busy"},
 | 
						|
	{CAUSE_NegativeGBG, "Negative GBG"},
 | 
						|
	{CAUSE_UnknownGBG, "Unknown  GBG"},
 | 
						|
	{CAUSE_NoSPVknown, "No SPV known"},
 | 
						|
	{CAUSE_DestNotObtain, "Destination not obtainable"},
 | 
						|
	{CAUSE_NumberChanged, "Number changed"},
 | 
						|
	{CAUSE_OutOfOrder, "Out Of Order"},
 | 
						|
	{CAUSE_NoUserResponse, "No User Response"},
 | 
						|
	{CAUSE_UserBusy, "User Busy"},
 | 
						|
	{CAUSE_IncomingBarred, "Incoming Barred"},
 | 
						|
	{CAUSE_CallRejected, "Call Rejected"},
 | 
						|
	{CAUSE_NetworkCongestion, "Network Congestion"},
 | 
						|
	{CAUSE_RemoteUser, "Remote User initiated"},
 | 
						|
	{CAUSE_LocalProcErr, "Local Procedure Error"},
 | 
						|
	{CAUSE_RemoteProcErr, "Remote Procedure Error"},
 | 
						|
	{CAUSE_RemoteUserSuspend, "Remote User Suspend"},
 | 
						|
	{CAUSE_RemoteUserResumed, "Remote User Resumed"},
 | 
						|
	{CAUSE_UserInfoDiscarded, "User Info Discarded"}
 | 
						|
};
 | 
						|
 | 
						|
static int cause_1tr6_len = ARRAY_SIZE(cause_1tr6);
 | 
						|
 | 
						|
static int
 | 
						|
prcause_1tr6(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	char *dp = dest;
 | 
						|
	int i, cause;
 | 
						|
 | 
						|
	p++;
 | 
						|
	if (0 == *p) {
 | 
						|
		dp += sprintf(dp, "   OK (cause length=0)\n");
 | 
						|
		return (dp - dest);
 | 
						|
	} else if (*p > 1) {
 | 
						|
		dp += sprintf(dp, "    coding ");
 | 
						|
		dp += prbits(dp, p[2], 7, 2);
 | 
						|
		dp += sprintf(dp, " location ");
 | 
						|
		dp += prbits(dp, p[2], 4, 4);
 | 
						|
		*dp++ = '\n';
 | 
						|
	}
 | 
						|
	p++;
 | 
						|
	cause = 0x7f & *p;
 | 
						|
 | 
						|
	/* locate cause value */
 | 
						|
	for (i = 0; i < cause_1tr6_len; i++)
 | 
						|
		if (cause_1tr6[i].nr == cause)
 | 
						|
			break;
 | 
						|
 | 
						|
	/* display cause value if it exists */
 | 
						|
	if (i == cause_1tr6_len)
 | 
						|
		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
 | 
						|
	else
 | 
						|
		dp += sprintf(dp, "  cause value %x : %s \n", cause, cause_1tr6[i].descr);
 | 
						|
 | 
						|
	return (dp - dest);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
prchident(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	char *dp = dest;
 | 
						|
 | 
						|
	p += 2;
 | 
						|
	dp += sprintf(dp, "    octet 3 ");
 | 
						|
	dp += prbits(dp, *p, 8, 8);
 | 
						|
	*dp++ = '\n';
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
prcalled(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	int l;
 | 
						|
	char *dp = dest;
 | 
						|
 | 
						|
	p++;
 | 
						|
	l = *p++ - 1;
 | 
						|
	dp += sprintf(dp, "    octet 3 ");
 | 
						|
	dp += prbits(dp, *p++, 8, 8);
 | 
						|
	*dp++ = '\n';
 | 
						|
	dp += sprintf(dp, "    number digits ");
 | 
						|
	while (l--)
 | 
						|
		*dp++ = *p++;
 | 
						|
	*dp++ = '\n';
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
static int
 | 
						|
prcalling(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	int l;
 | 
						|
	char *dp = dest;
 | 
						|
 | 
						|
	p++;
 | 
						|
	l = *p++ - 1;
 | 
						|
	dp += sprintf(dp, "    octet 3 ");
 | 
						|
	dp += prbits(dp, *p, 8, 8);
 | 
						|
	*dp++ = '\n';
 | 
						|
	if (!(*p & 0x80)) {
 | 
						|
		dp += sprintf(dp, "    octet 3a ");
 | 
						|
		dp += prbits(dp, *++p, 8, 8);
 | 
						|
		*dp++ = '\n';
 | 
						|
		l--;
 | 
						|
	};
 | 
						|
	p++;
 | 
						|
 | 
						|
	dp += sprintf(dp, "    number digits ");
 | 
						|
	while (l--)
 | 
						|
		*dp++ = *p++;
 | 
						|
	*dp++ = '\n';
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
int
 | 
						|
prbearer(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	char *dp = dest, ch;
 | 
						|
 | 
						|
	p += 2;
 | 
						|
	dp += sprintf(dp, "    octet 3  ");
 | 
						|
	dp += prbits(dp, *p++, 8, 8);
 | 
						|
	*dp++ = '\n';
 | 
						|
	dp += sprintf(dp, "    octet 4  ");
 | 
						|
	dp += prbits(dp, *p, 8, 8);
 | 
						|
	*dp++ = '\n';
 | 
						|
	if ((*p++ & 0x1f) == 0x18) {
 | 
						|
		dp += sprintf(dp, "    octet 4.1 ");
 | 
						|
		dp += prbits(dp, *p++, 8, 8);
 | 
						|
		*dp++ = '\n';
 | 
						|
	}
 | 
						|
	/* check for user information layer 1 */
 | 
						|
	if ((*p & 0x60) == 0x20) {
 | 
						|
		ch = ' ';
 | 
						|
		do {
 | 
						|
			dp += sprintf(dp, "    octet 5%c ", ch);
 | 
						|
			dp += prbits(dp, *p, 8, 8);
 | 
						|
			*dp++ = '\n';
 | 
						|
			if (ch == ' ')
 | 
						|
				ch = 'a';
 | 
						|
			else
 | 
						|
				ch++;
 | 
						|
		}
 | 
						|
		while (!(*p++ & 0x80));
 | 
						|
	}
 | 
						|
	/* check for user information layer 2 */
 | 
						|
	if ((*p & 0x60) == 0x40) {
 | 
						|
		dp += sprintf(dp, "    octet 6  ");
 | 
						|
		dp += prbits(dp, *p++, 8, 8);
 | 
						|
		*dp++ = '\n';
 | 
						|
	}
 | 
						|
	/* check for user information layer 3 */
 | 
						|
	if ((*p & 0x60) == 0x60) {
 | 
						|
		dp += sprintf(dp, "    octet 7  ");
 | 
						|
		dp += prbits(dp, *p++, 8, 8);
 | 
						|
		*dp++ = '\n';
 | 
						|
	}
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static
 | 
						|
int
 | 
						|
prbearer_ni1(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	char *dp = dest;
 | 
						|
	u_char len;
 | 
						|
 | 
						|
	p++;
 | 
						|
	len = *p++;
 | 
						|
	dp += sprintf(dp, "    octet 3  ");
 | 
						|
	dp += prbits(dp, *p, 8, 8);
 | 
						|
	switch (*p++) {
 | 
						|
	case 0x80:
 | 
						|
		dp += sprintf(dp, " Speech");
 | 
						|
		break;
 | 
						|
	case 0x88:
 | 
						|
		dp += sprintf(dp, " Unrestricted digital information");
 | 
						|
		break;
 | 
						|
	case 0x90:
 | 
						|
		dp += sprintf(dp, " 3.1 kHz audio");
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		dp += sprintf(dp, " Unknown information-transfer capability");
 | 
						|
	}
 | 
						|
	*dp++ = '\n';
 | 
						|
	dp += sprintf(dp, "    octet 4  ");
 | 
						|
	dp += prbits(dp, *p, 8, 8);
 | 
						|
	switch (*p++) {
 | 
						|
	case 0x90:
 | 
						|
		dp += sprintf(dp, " 64 kbps, circuit mode");
 | 
						|
		break;
 | 
						|
	case 0xc0:
 | 
						|
		dp += sprintf(dp, " Packet mode");
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		dp += sprintf(dp, " Unknown transfer mode");
 | 
						|
	}
 | 
						|
	*dp++ = '\n';
 | 
						|
	if (len > 2) {
 | 
						|
		dp += sprintf(dp, "    octet 5  ");
 | 
						|
		dp += prbits(dp, *p, 8, 8);
 | 
						|
		switch (*p++) {
 | 
						|
		case 0x21:
 | 
						|
			dp += sprintf(dp, " Rate adaption\n");
 | 
						|
			dp += sprintf(dp, "    octet 5a ");
 | 
						|
			dp += prbits(dp, *p, 8, 8);
 | 
						|
			break;
 | 
						|
		case 0xa2:
 | 
						|
			dp += sprintf(dp, " u-law");
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			dp += sprintf(dp, " Unknown UI layer 1 protocol");
 | 
						|
		}
 | 
						|
		*dp++ = '\n';
 | 
						|
	}
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
general(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	char *dp = dest;
 | 
						|
	char ch = ' ';
 | 
						|
	int l, octet = 3;
 | 
						|
 | 
						|
	p++;
 | 
						|
	l = *p++;
 | 
						|
	/* Iterate over all octets in the information element */
 | 
						|
	while (l--) {
 | 
						|
		dp += sprintf(dp, "    octet %d%c ", octet, ch);
 | 
						|
		dp += prbits(dp, *p++, 8, 8);
 | 
						|
		*dp++ = '\n';
 | 
						|
 | 
						|
		/* last octet in group? */
 | 
						|
		if (*p & 0x80) {
 | 
						|
			octet++;
 | 
						|
			ch = ' ';
 | 
						|
		} else if (ch == ' ')
 | 
						|
			ch = 'a';
 | 
						|
		else
 | 
						|
			ch++;
 | 
						|
	}
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
general_ni1(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	char *dp = dest;
 | 
						|
	char ch = ' ';
 | 
						|
	int l, octet = 3;
 | 
						|
 | 
						|
	p++;
 | 
						|
	l = *p++;
 | 
						|
	/* Iterate over all octets in the information element */
 | 
						|
	while (l--) {
 | 
						|
		dp += sprintf(dp, "    octet %d%c ", octet, ch);
 | 
						|
		dp += prbits(dp, *p, 8, 8);
 | 
						|
		*dp++ = '\n';
 | 
						|
 | 
						|
		/* last octet in group? */
 | 
						|
		if (*p++ & 0x80) {
 | 
						|
			octet++;
 | 
						|
			ch = ' ';
 | 
						|
		} else if (ch == ' ')
 | 
						|
			ch = 'a';
 | 
						|
		else
 | 
						|
			ch++;
 | 
						|
	}
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
prcharge(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	char *dp = dest;
 | 
						|
	int l;
 | 
						|
 | 
						|
	p++;
 | 
						|
	l = *p++ - 1;
 | 
						|
	dp += sprintf(dp, "    GEA ");
 | 
						|
	dp += prbits(dp, *p++, 8, 8);
 | 
						|
	dp += sprintf(dp, "  Anzahl: ");
 | 
						|
	/* Iterate over all octets in the * information element */
 | 
						|
	while (l--)
 | 
						|
		*dp++ = *p++;
 | 
						|
	*dp++ = '\n';
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
static int
 | 
						|
prtext(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	char *dp = dest;
 | 
						|
	int l;
 | 
						|
 | 
						|
	p++;
 | 
						|
	l = *p++;
 | 
						|
	dp += sprintf(dp, "    ");
 | 
						|
	/* Iterate over all octets in the * information element */
 | 
						|
	while (l--)
 | 
						|
		*dp++ = *p++;
 | 
						|
	*dp++ = '\n';
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
prfeatureind(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	char *dp = dest;
 | 
						|
 | 
						|
	p += 2; /* skip id, len */
 | 
						|
	dp += sprintf(dp, "    octet 3  ");
 | 
						|
	dp += prbits(dp, *p, 8, 8);
 | 
						|
	*dp++ = '\n';
 | 
						|
	if (!(*p++ & 0x80)) {
 | 
						|
		dp += sprintf(dp, "    octet 4  ");
 | 
						|
		dp += prbits(dp, *p++, 8, 8);
 | 
						|
		*dp++ = '\n';
 | 
						|
	}
 | 
						|
	dp += sprintf(dp, "    Status:  ");
 | 
						|
	switch (*p) {
 | 
						|
	case 0:
 | 
						|
		dp += sprintf(dp, "Idle");
 | 
						|
		break;
 | 
						|
	case 1:
 | 
						|
		dp += sprintf(dp, "Active");
 | 
						|
		break;
 | 
						|
	case 2:
 | 
						|
		dp += sprintf(dp, "Prompt");
 | 
						|
		break;
 | 
						|
	case 3:
 | 
						|
		dp += sprintf(dp, "Pending");
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		dp += sprintf(dp, "(Reserved)");
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	*dp++ = '\n';
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
struct DTag { /* Display tags */
 | 
						|
	u_char nr;
 | 
						|
	char *descr;
 | 
						|
} dtaglist[] = {
 | 
						|
	{ 0x82, "Continuation" },
 | 
						|
	{ 0x83, "Called address" },
 | 
						|
	{ 0x84, "Cause" },
 | 
						|
	{ 0x85, "Progress indicator" },
 | 
						|
	{ 0x86, "Notification indicator" },
 | 
						|
	{ 0x87, "Prompt" },
 | 
						|
	{ 0x88, "Accumlated digits" },
 | 
						|
	{ 0x89, "Status" },
 | 
						|
	{ 0x8a, "Inband" },
 | 
						|
	{ 0x8b, "Calling address" },
 | 
						|
	{ 0x8c, "Reason" },
 | 
						|
	{ 0x8d, "Calling party name" },
 | 
						|
	{ 0x8e, "Called party name" },
 | 
						|
	{ 0x8f, "Original called name" },
 | 
						|
	{ 0x90, "Redirecting name" },
 | 
						|
	{ 0x91, "Connected name" },
 | 
						|
	{ 0x92, "Originating restrictions" },
 | 
						|
	{ 0x93, "Date & time of day" },
 | 
						|
	{ 0x94, "Call Appearance ID" },
 | 
						|
	{ 0x95, "Feature address" },
 | 
						|
	{ 0x96, "Redirection name" },
 | 
						|
	{ 0x9e, "Text" },
 | 
						|
};
 | 
						|
#define DTAGSIZE ARRAY_SIZE(dtaglist)
 | 
						|
 | 
						|
static int
 | 
						|
disptext_ni1(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	char *dp = dest;
 | 
						|
	int l, tag, len, i;
 | 
						|
 | 
						|
	p++;
 | 
						|
	l = *p++ - 1;
 | 
						|
	if (*p++ != 0x80) {
 | 
						|
		dp += sprintf(dp, "    Unknown display type\n");
 | 
						|
		return (dp - dest);
 | 
						|
	}
 | 
						|
	/* Iterate over all tag,length,text fields */
 | 
						|
	while (l > 0) {
 | 
						|
		tag = *p++;
 | 
						|
		len = *p++;
 | 
						|
		l -= len + 2;
 | 
						|
		/* Don't space or skip */
 | 
						|
		if ((tag == 0x80) || (tag == 0x81)) p++;
 | 
						|
		else {
 | 
						|
			for (i = 0; i < DTAGSIZE; i++)
 | 
						|
				if (tag == dtaglist[i].nr)
 | 
						|
					break;
 | 
						|
 | 
						|
			/* When not found, give appropriate msg */
 | 
						|
			if (i != DTAGSIZE) {
 | 
						|
				dp += sprintf(dp, "    %s: ", dtaglist[i].descr);
 | 
						|
				while (len--)
 | 
						|
					*dp++ = *p++;
 | 
						|
			} else {
 | 
						|
				dp += sprintf(dp, "    (unknown display tag %2x): ", tag);
 | 
						|
				while (len--)
 | 
						|
					*dp++ = *p++;
 | 
						|
			}
 | 
						|
			dp += sprintf(dp, "\n");
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
static int
 | 
						|
display(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	char *dp = dest;
 | 
						|
	char ch = ' ';
 | 
						|
	int l, octet = 3;
 | 
						|
 | 
						|
	p++;
 | 
						|
	l = *p++;
 | 
						|
	/* Iterate over all octets in the * display-information element */
 | 
						|
	dp += sprintf(dp, "   \"");
 | 
						|
	while (l--) {
 | 
						|
		dp += sprintf(dp, "%c", *p++);
 | 
						|
 | 
						|
		/* last octet in group? */
 | 
						|
		if (*p & 0x80) {
 | 
						|
			octet++;
 | 
						|
			ch = ' ';
 | 
						|
		} else if (ch == ' ')
 | 
						|
			ch = 'a';
 | 
						|
 | 
						|
		else
 | 
						|
			ch++;
 | 
						|
	}
 | 
						|
	*dp++ = '\"';
 | 
						|
	*dp++ = '\n';
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
prfacility(char *dest, u_char *p)
 | 
						|
{
 | 
						|
	char *dp = dest;
 | 
						|
	int l, l2;
 | 
						|
 | 
						|
	p++;
 | 
						|
	l = *p++;
 | 
						|
	dp += sprintf(dp, "    octet 3 ");
 | 
						|
	dp += prbits(dp, *p++, 8, 8);
 | 
						|
	dp += sprintf(dp, "\n");
 | 
						|
	l -= 1;
 | 
						|
 | 
						|
	while (l > 0) {
 | 
						|
		dp += sprintf(dp, "   octet 4 ");
 | 
						|
		dp += prbits(dp, *p++, 8, 8);
 | 
						|
		dp += sprintf(dp, "\n");
 | 
						|
		dp += sprintf(dp, "   octet 5 %d\n", l2 = *p++ & 0x7f);
 | 
						|
		l -= 2;
 | 
						|
		dp += sprintf(dp, "   contents ");
 | 
						|
		while (l2--) {
 | 
						|
			dp += sprintf(dp, "%2x ", *p++);
 | 
						|
			l--;
 | 
						|
		}
 | 
						|
		dp += sprintf(dp, "\n");
 | 
						|
	}
 | 
						|
 | 
						|
	return (dp - dest);
 | 
						|
}
 | 
						|
 | 
						|
static
 | 
						|
struct InformationElement {
 | 
						|
	u_char nr;
 | 
						|
	char *descr;
 | 
						|
	int (*f) (char *, u_char *);
 | 
						|
} ielist[] = {
 | 
						|
 | 
						|
	{
 | 
						|
		0x00, "Segmented message", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x04, "Bearer capability", prbearer
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x08, "Cause", prcause
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x10, "Call identity", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x14, "Call state", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x18, "Channel identification", prchident
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x1c, "Facility", prfacility
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x1e, "Progress indicator", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x20, "Network-specific facilities", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x27, "Notification indicator", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x28, "Display", display
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x29, "Date/Time", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x2c, "Keypad facility", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x34, "Signal", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x40, "Information rate", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x42, "End-to-end delay", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x43, "Transit delay selection and indication", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x44, "Packet layer binary parameters", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x45, "Packet layer window size", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x46, "Packet size", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x47, "Closed user group", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x4a, "Reverse charge indication", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x6c, "Calling party number", prcalling
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x6d, "Calling party subaddress", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x70, "Called party number", prcalled
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x71, "Called party subaddress", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x74, "Redirecting number", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x78, "Transit network selection", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x79, "Restart indicator", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x7c, "Low layer compatibility", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x7d, "High layer compatibility", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x7e, "User-user", general
 | 
						|
	},
 | 
						|
	{
 | 
						|
		0x7f, "Escape for extension", general
 | 
						|
	},
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
#define IESIZE ARRAY_SIZE(ielist)
 | 
						|
 | 
						|
static
 | 
						|
struct InformationElement ielist_ni1[] = {
 | 
						|
	{ 0x04, "Bearer Capability", prbearer_ni1 },
 | 
						|
	{ 0x08, "Cause", prcause },
 | 
						|
	{ 0x14, "Call State", general_ni1 },
 | 
						|
	{ 0x18, "Channel Identification", prchident },
 | 
						|
	{ 0x1e, "Progress Indicator", general_ni1 },
 | 
						|
	{ 0x27, "Notification Indicator", general_ni1 },
 | 
						|
	{ 0x2c, "Keypad Facility", prtext },
 | 
						|
	{ 0x32, "Information Request", general_ni1 },
 | 
						|
	{ 0x34, "Signal", general_ni1 },
 | 
						|
	{ 0x38, "Feature Activation", general_ni1 },
 | 
						|
	{ 0x39, "Feature Indication", prfeatureind },
 | 
						|
	{ 0x3a, "Service Profile Identification (SPID)", prtext },
 | 
						|
	{ 0x3b, "Endpoint Identifier", general_ni1 },
 | 
						|
	{ 0x6c, "Calling Party Number", prcalling },
 | 
						|
	{ 0x6d, "Calling Party Subaddress", general_ni1 },
 | 
						|
	{ 0x70, "Called Party Number", prcalled },
 | 
						|
	{ 0x71, "Called Party Subaddress", general_ni1 },
 | 
						|
	{ 0x74, "Redirecting Number", general_ni1 },
 | 
						|
	{ 0x78, "Transit Network Selection", general_ni1 },
 | 
						|
	{ 0x7c, "Low Layer Compatibility", general_ni1 },
 | 
						|
	{ 0x7d, "High Layer Compatibility", general_ni1 },
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
#define IESIZE_NI1 ARRAY_SIZE(ielist_ni1)
 | 
						|
 | 
						|
static
 | 
						|
struct InformationElement ielist_ni1_cs5[] = {
 | 
						|
	{ 0x1d, "Operator system access", general_ni1 },
 | 
						|
	{ 0x2a, "Display text", disptext_ni1 },
 | 
						|
};
 | 
						|
 | 
						|
#define IESIZE_NI1_CS5 ARRAY_SIZE(ielist_ni1_cs5)
 | 
						|
 | 
						|
static
 | 
						|
struct InformationElement ielist_ni1_cs6[] = {
 | 
						|
	{ 0x7b, "Call appearance", general_ni1 },
 | 
						|
};
 | 
						|
 | 
						|
#define IESIZE_NI1_CS6 ARRAY_SIZE(ielist_ni1_cs6)
 | 
						|
 | 
						|
static struct InformationElement we_0[] =
 | 
						|
{
 | 
						|
	{WE0_cause, "Cause", prcause_1tr6},
 | 
						|
	{WE0_connAddr, "Connecting Address", prcalled},
 | 
						|
	{WE0_callID, "Call IDentity", general},
 | 
						|
	{WE0_chanID, "Channel IDentity", general},
 | 
						|
	{WE0_netSpecFac, "Network Specific Facility", general},
 | 
						|
	{WE0_display, "Display", general},
 | 
						|
	{WE0_keypad, "Keypad", general},
 | 
						|
	{WE0_origAddr, "Origination Address", prcalled},
 | 
						|
	{WE0_destAddr, "Destination Address", prcalled},
 | 
						|
	{WE0_userInfo, "User Info", general}
 | 
						|
};
 | 
						|
 | 
						|
#define WE_0_LEN ARRAY_SIZE(we_0)
 | 
						|
 | 
						|
static struct InformationElement we_6[] =
 | 
						|
{
 | 
						|
	{WE6_serviceInd, "Service Indicator", general},
 | 
						|
	{WE6_chargingInfo, "Charging Information", prcharge},
 | 
						|
	{WE6_date, "Date", prtext},
 | 
						|
	{WE6_facSelect, "Facility Select", general},
 | 
						|
	{WE6_facStatus, "Facility Status", general},
 | 
						|
	{WE6_statusCalled, "Status Called", general},
 | 
						|
	{WE6_addTransAttr, "Additional Transmission Attributes", general}
 | 
						|
};
 | 
						|
#define WE_6_LEN ARRAY_SIZE(we_6)
 | 
						|
 | 
						|
int
 | 
						|
QuickHex(char *txt, u_char *p, int cnt)
 | 
						|
{
 | 
						|
	register int i;
 | 
						|
	register char *t = txt;
 | 
						|
 | 
						|
	for (i = 0; i < cnt; i++) {
 | 
						|
		*t++ = ' ';
 | 
						|
		*t++ = hex_asc_hi(p[i]);
 | 
						|
		*t++ = hex_asc_lo(p[i]);
 | 
						|
	}
 | 
						|
	*t++ = 0;
 | 
						|
	return (t - txt);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
LogFrame(struct IsdnCardState *cs, u_char *buf, int size)
 | 
						|
{
 | 
						|
	char *dp;
 | 
						|
 | 
						|
	if (size < 1)
 | 
						|
		return;
 | 
						|
	dp = cs->dlog;
 | 
						|
	if (size < MAX_DLOG_SPACE / 3 - 10) {
 | 
						|
		*dp++ = 'H';
 | 
						|
		*dp++ = 'E';
 | 
						|
		*dp++ = 'X';
 | 
						|
		*dp++ = ':';
 | 
						|
		dp += QuickHex(dp, buf, size);
 | 
						|
		dp--;
 | 
						|
		*dp++ = '\n';
 | 
						|
		*dp = 0;
 | 
						|
		HiSax_putstatus(cs, NULL, cs->dlog);
 | 
						|
	} else
 | 
						|
		HiSax_putstatus(cs, "LogFrame: ", "warning Frame too big (%d)", size);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir)
 | 
						|
{
 | 
						|
	u_char *bend, *buf;
 | 
						|
	char *dp;
 | 
						|
	unsigned char pd, cr_l, cr, mt;
 | 
						|
	unsigned char sapi, tei, ftyp;
 | 
						|
	int i, cset = 0, cs_old = 0, cs_fest = 0;
 | 
						|
	int size, finish = 0;
 | 
						|
 | 
						|
	if (skb->len < 3)
 | 
						|
		return;
 | 
						|
	/* display header */
 | 
						|
	dp = cs->dlog;
 | 
						|
	dp += jiftime(dp, jiffies);
 | 
						|
	*dp++ = ' ';
 | 
						|
	sapi = skb->data[0] >> 2;
 | 
						|
	tei  = skb->data[1] >> 1;
 | 
						|
	ftyp = skb->data[2];
 | 
						|
	buf = skb->data;
 | 
						|
	dp += sprintf(dp, "frame %s ", dir ? "network->user" : "user->network");
 | 
						|
	size = skb->len;
 | 
						|
 | 
						|
	if (tei == GROUP_TEI) {
 | 
						|
		if (sapi == CTRL_SAPI) { /* sapi 0 */
 | 
						|
			if (ftyp == 3) {
 | 
						|
				dp += sprintf(dp, "broadcast\n");
 | 
						|
				buf += 3;
 | 
						|
				size -= 3;
 | 
						|
			} else {
 | 
						|
				dp += sprintf(dp, "no UI broadcast\n");
 | 
						|
				finish = 1;
 | 
						|
			}
 | 
						|
		} else if (sapi == TEI_SAPI) {
 | 
						|
			dp += sprintf(dp, "tei management\n");
 | 
						|
			finish = 1;
 | 
						|
		} else {
 | 
						|
			dp += sprintf(dp, "unknown sapi %d broadcast\n", sapi);
 | 
						|
			finish = 1;
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		if (sapi == CTRL_SAPI) {
 | 
						|
			if (!(ftyp & 1)) { /* IFrame */
 | 
						|
				dp += sprintf(dp, "with tei %d\n", tei);
 | 
						|
				buf += 4;
 | 
						|
				size -= 4;
 | 
						|
			} else {
 | 
						|
				dp += sprintf(dp, "SFrame with tei %d\n", tei);
 | 
						|
				finish = 1;
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			dp += sprintf(dp, "unknown sapi %d tei %d\n", sapi, tei);
 | 
						|
			finish = 1;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	bend = skb->data + skb->len;
 | 
						|
	if (buf >= bend) {
 | 
						|
		dp += sprintf(dp, "frame too short\n");
 | 
						|
		finish = 1;
 | 
						|
	}
 | 
						|
	if (finish) {
 | 
						|
		*dp = 0;
 | 
						|
		HiSax_putstatus(cs, NULL, cs->dlog);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if ((0xfe & buf[0]) == PROTO_DIS_N0) {	/* 1TR6 */
 | 
						|
		/* locate message type */
 | 
						|
		pd = *buf++;
 | 
						|
		cr_l = *buf++;
 | 
						|
		if (cr_l)
 | 
						|
			cr = *buf++;
 | 
						|
		else
 | 
						|
			cr = 0;
 | 
						|
		mt = *buf++;
 | 
						|
		if (pd == PROTO_DIS_N0) {	/* N0 */
 | 
						|
			for (i = 0; i < MT_N0_LEN; i++)
 | 
						|
				if (mt_n0[i].nr == mt)
 | 
						|
					break;
 | 
						|
			/* display message type if it exists */
 | 
						|
			if (i == MT_N0_LEN)
 | 
						|
				dp += sprintf(dp, "callref %d %s size %d unknown message type N0 %x!\n",
 | 
						|
					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
 | 
						|
					      size, mt);
 | 
						|
			else
 | 
						|
				dp += sprintf(dp, "callref %d %s size %d message type %s\n",
 | 
						|
					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
 | 
						|
					      size, mt_n0[i].descr);
 | 
						|
		} else {	/* N1 */
 | 
						|
			for (i = 0; i < MT_N1_LEN; i++)
 | 
						|
				if (mt_n1[i].nr == mt)
 | 
						|
					break;
 | 
						|
			/* display message type if it exists */
 | 
						|
			if (i == MT_N1_LEN)
 | 
						|
				dp += sprintf(dp, "callref %d %s size %d unknown message type N1 %x!\n",
 | 
						|
					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
 | 
						|
					      size, mt);
 | 
						|
			else
 | 
						|
				dp += sprintf(dp, "callref %d %s size %d message type %s\n",
 | 
						|
					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
 | 
						|
					      size, mt_n1[i].descr);
 | 
						|
		}
 | 
						|
 | 
						|
		/* display each information element */
 | 
						|
		while (buf < bend) {
 | 
						|
			/* Is it a single octet information element? */
 | 
						|
			if (*buf & 0x80) {
 | 
						|
				switch ((*buf >> 4) & 7) {
 | 
						|
				case 1:
 | 
						|
					dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
 | 
						|
					cs_old = cset;
 | 
						|
					cset = *buf & 7;
 | 
						|
					cs_fest = *buf & 8;
 | 
						|
					break;
 | 
						|
				case 3:
 | 
						|
					dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
 | 
						|
					break;
 | 
						|
				case 2:
 | 
						|
					if (*buf == 0xa0) {
 | 
						|
						dp += sprintf(dp, "  More data\n");
 | 
						|
						break;
 | 
						|
					}
 | 
						|
					if (*buf == 0xa1) {
 | 
						|
						dp += sprintf(dp, "  Sending complete\n");
 | 
						|
					}
 | 
						|
					break;
 | 
						|
					/* fall through */
 | 
						|
				default:
 | 
						|
					dp += sprintf(dp, "  Reserved %x\n", *buf);
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				buf++;
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			/* No, locate it in the table */
 | 
						|
			if (cset == 0) {
 | 
						|
				for (i = 0; i < WE_0_LEN; i++)
 | 
						|
					if (*buf == we_0[i].nr)
 | 
						|
						break;
 | 
						|
 | 
						|
				/* When found, give appropriate msg */
 | 
						|
				if (i != WE_0_LEN) {
 | 
						|
					dp += sprintf(dp, "  %s\n", we_0[i].descr);
 | 
						|
					dp += we_0[i].f(dp, buf);
 | 
						|
				} else
 | 
						|
					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
 | 
						|
			} else if (cset == 6) {
 | 
						|
				for (i = 0; i < WE_6_LEN; i++)
 | 
						|
					if (*buf == we_6[i].nr)
 | 
						|
						break;
 | 
						|
 | 
						|
				/* When found, give appropriate msg */
 | 
						|
				if (i != WE_6_LEN) {
 | 
						|
					dp += sprintf(dp, "  %s\n", we_6[i].descr);
 | 
						|
					dp += we_6[i].f(dp, buf);
 | 
						|
				} else
 | 
						|
					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
 | 
						|
			} else
 | 
						|
				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
 | 
						|
			/* Skip to next element */
 | 
						|
			if (cs_fest == 8) {
 | 
						|
				cset = cs_old;
 | 
						|
				cs_old = 0;
 | 
						|
				cs_fest = 0;
 | 
						|
			}
 | 
						|
			buf += buf[1] + 2;
 | 
						|
		}
 | 
						|
	} else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_NI1)) {	/* NI-1 */
 | 
						|
		/* locate message type */
 | 
						|
		buf++;
 | 
						|
		cr_l = *buf++;
 | 
						|
		if (cr_l)
 | 
						|
			cr = *buf++;
 | 
						|
		else
 | 
						|
			cr = 0;
 | 
						|
		mt = *buf++;
 | 
						|
		for (i = 0; i < MTSIZE; i++)
 | 
						|
			if (mtlist[i].nr == mt)
 | 
						|
				break;
 | 
						|
 | 
						|
		/* display message type if it exists */
 | 
						|
		if (i == MTSIZE)
 | 
						|
			dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n",
 | 
						|
				      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
 | 
						|
				      size, mt);
 | 
						|
		else
 | 
						|
			dp += sprintf(dp, "callref %d %s size %d message type %s\n",
 | 
						|
				      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
 | 
						|
				      size, mtlist[i].descr);
 | 
						|
 | 
						|
		/* display each information element */
 | 
						|
		while (buf < bend) {
 | 
						|
			/* Is it a single octet information element? */
 | 
						|
			if (*buf & 0x80) {
 | 
						|
				switch ((*buf >> 4) & 7) {
 | 
						|
				case 1:
 | 
						|
					dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
 | 
						|
					cs_old = cset;
 | 
						|
					cset = *buf & 7;
 | 
						|
					cs_fest = *buf & 8;
 | 
						|
					break;
 | 
						|
				default:
 | 
						|
					dp += sprintf(dp, "  Unknown single-octet IE %x\n", *buf);
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				buf++;
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			/* No, locate it in the table */
 | 
						|
			if (cset == 0) {
 | 
						|
				for (i = 0; i < IESIZE_NI1; i++)
 | 
						|
					if (*buf == ielist_ni1[i].nr)
 | 
						|
						break;
 | 
						|
 | 
						|
				/* When not found, give appropriate msg */
 | 
						|
				if (i != IESIZE_NI1) {
 | 
						|
					dp += sprintf(dp, "  %s\n", ielist_ni1[i].descr);
 | 
						|
					dp += ielist_ni1[i].f(dp, buf);
 | 
						|
				} else
 | 
						|
					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
 | 
						|
			} else if (cset == 5) {
 | 
						|
				for (i = 0; i < IESIZE_NI1_CS5; i++)
 | 
						|
					if (*buf == ielist_ni1_cs5[i].nr)
 | 
						|
						break;
 | 
						|
 | 
						|
				/* When not found, give appropriate msg */
 | 
						|
				if (i != IESIZE_NI1_CS5) {
 | 
						|
					dp += sprintf(dp, "  %s\n", ielist_ni1_cs5[i].descr);
 | 
						|
					dp += ielist_ni1_cs5[i].f(dp, buf);
 | 
						|
				} else
 | 
						|
					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
 | 
						|
			} else if (cset == 6) {
 | 
						|
				for (i = 0; i < IESIZE_NI1_CS6; i++)
 | 
						|
					if (*buf == ielist_ni1_cs6[i].nr)
 | 
						|
						break;
 | 
						|
 | 
						|
				/* When not found, give appropriate msg */
 | 
						|
				if (i != IESIZE_NI1_CS6) {
 | 
						|
					dp += sprintf(dp, "  %s\n", ielist_ni1_cs6[i].descr);
 | 
						|
					dp += ielist_ni1_cs6[i].f(dp, buf);
 | 
						|
				} else
 | 
						|
					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
 | 
						|
			} else
 | 
						|
				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
 | 
						|
 | 
						|
			/* Skip to next element */
 | 
						|
			if (cs_fest == 8) {
 | 
						|
				cset = cs_old;
 | 
						|
				cs_old = 0;
 | 
						|
				cs_fest = 0;
 | 
						|
			}
 | 
						|
			buf += buf[1] + 2;
 | 
						|
		}
 | 
						|
	} else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_EURO)) { /* EURO */
 | 
						|
		/* locate message type */
 | 
						|
		buf++;
 | 
						|
		cr_l = *buf++;
 | 
						|
		if (cr_l)
 | 
						|
			cr = *buf++;
 | 
						|
		else
 | 
						|
			cr = 0;
 | 
						|
		mt = *buf++;
 | 
						|
		for (i = 0; i < MTSIZE; i++)
 | 
						|
			if (mtlist[i].nr == mt)
 | 
						|
				break;
 | 
						|
 | 
						|
		/* display message type if it exists */
 | 
						|
		if (i == MTSIZE)
 | 
						|
			dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n",
 | 
						|
				      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
 | 
						|
				      size, mt);
 | 
						|
		else
 | 
						|
			dp += sprintf(dp, "callref %d %s size %d message type %s\n",
 | 
						|
				      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
 | 
						|
				      size, mtlist[i].descr);
 | 
						|
 | 
						|
		/* display each information element */
 | 
						|
		while (buf < bend) {
 | 
						|
			/* Is it a single octet information element? */
 | 
						|
			if (*buf & 0x80) {
 | 
						|
				switch ((*buf >> 4) & 7) {
 | 
						|
				case 1:
 | 
						|
					dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
 | 
						|
					break;
 | 
						|
				case 3:
 | 
						|
					dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
 | 
						|
					break;
 | 
						|
				case 5:
 | 
						|
					dp += sprintf(dp, "  Repeat indicator %x\n", *buf & 0xf);
 | 
						|
					break;
 | 
						|
				case 2:
 | 
						|
					if (*buf == 0xa0) {
 | 
						|
						dp += sprintf(dp, "  More data\n");
 | 
						|
						break;
 | 
						|
					}
 | 
						|
					if (*buf == 0xa1) {
 | 
						|
						dp += sprintf(dp, "  Sending complete\n");
 | 
						|
					}
 | 
						|
					break;
 | 
						|
					/* fall through */
 | 
						|
				default:
 | 
						|
					dp += sprintf(dp, "  Reserved %x\n", *buf);
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				buf++;
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			/* No, locate it in the table */
 | 
						|
			for (i = 0; i < IESIZE; i++)
 | 
						|
				if (*buf == ielist[i].nr)
 | 
						|
					break;
 | 
						|
 | 
						|
			/* When not found, give appropriate msg */
 | 
						|
			if (i != IESIZE) {
 | 
						|
				dp += sprintf(dp, "  %s\n", ielist[i].descr);
 | 
						|
				dp += ielist[i].f(dp, buf);
 | 
						|
			} else
 | 
						|
				dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
 | 
						|
 | 
						|
			/* Skip to next element */
 | 
						|
			buf += buf[1] + 2;
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		dp += sprintf(dp, "Unknown protocol %x!", buf[0]);
 | 
						|
	}
 | 
						|
	*dp = 0;
 | 
						|
	HiSax_putstatus(cs, NULL, cs->dlog);
 | 
						|
}
 |