1/* $Id: q931.c,v 1.12.2.3 2004/01/13 14:31:26 keil Exp $
2 *
3 * code to decode ITU Q.931 call control messages
4 *
5 * Author       Jan den Ouden
6 * Copyright    by Jan den Ouden
7 *
8 * This software may be used and distributed according to the terms
9 * of the GNU General Public License, incorporated herein by reference.
10 *
11 * Changelog:
12 *
13 * Pauline Middelink    general improvements
14 * Beat Doebeli         cause texts, display information element
15 * Karsten Keil         cause texts, display information element for 1TR6
16 *
17 */
18
19
20#include "hisax.h"
21#include "l3_1tr6.h"
22
23void
24iecpy(u_char *dest, u_char *iestart, int ieoffset)
25{
26	u_char *p;
27	int l;
28
29	p = iestart + ieoffset + 2;
30	l = iestart[1] - ieoffset;
31	while (l--)
32		*dest++ = *p++;
33	*dest++ = '\0';
34}
35
36/*
37 * According to Table 4-2/Q.931
38 */
39static
40struct MessageType {
41	u_char nr;
42	char *descr;
43} mtlist[] = {
44
45	{
46		0x1, "ALERTING"
47	},
48	{
49		0x2, "CALL PROCEEDING"
50	},
51	{
52		0x7, "CONNECT"
53	},
54	{
55		0xf, "CONNECT ACKNOWLEDGE"
56	},
57	{
58		0x3, "PROGRESS"
59	},
60	{
61		0x5, "SETUP"
62	},
63	{
64		0xd, "SETUP ACKNOWLEDGE"
65	},
66	{
67		0x24, "HOLD"
68	},
69	{
70		0x28, "HOLD ACKNOWLEDGE"
71	},
72	{
73		0x30, "HOLD REJECT"
74	},
75	{
76		0x31, "RETRIEVE"
77	},
78	{
79		0x33, "RETRIEVE ACKNOWLEDGE"
80	},
81	{
82		0x37, "RETRIEVE REJECT"
83	},
84	{
85		0x26, "RESUME"
86	},
87	{
88		0x2e, "RESUME ACKNOWLEDGE"
89	},
90	{
91		0x22, "RESUME REJECT"
92	},
93	{
94		0x25, "SUSPEND"
95	},
96	{
97		0x2d, "SUSPEND ACKNOWLEDGE"
98	},
99	{
100		0x21, "SUSPEND REJECT"
101	},
102	{
103		0x20, "USER INFORMATION"
104	},
105	{
106		0x45, "DISCONNECT"
107	},
108	{
109		0x4d, "RELEASE"
110	},
111	{
112		0x5a, "RELEASE COMPLETE"
113	},
114	{
115		0x46, "RESTART"
116	},
117	{
118		0x4e, "RESTART ACKNOWLEDGE"
119	},
120	{
121		0x60, "SEGMENT"
122	},
123	{
124		0x79, "CONGESTION CONTROL"
125	},
126	{
127		0x7b, "INFORMATION"
128	},
129	{
130		0x62, "FACILITY"
131	},
132	{
133		0x6e, "NOTIFY"
134	},
135	{
136		0x7d, "STATUS"
137	},
138	{
139		0x75, "STATUS ENQUIRY"
140	}
141};
142
143#define MTSIZE ARRAY_SIZE(mtlist)
144
145static
146struct MessageType mt_n0[] =
147{
148	{MT_N0_REG_IND, "REGister INDication"},
149	{MT_N0_CANC_IND, "CANCel INDication"},
150	{MT_N0_FAC_STA, "FACility STAtus"},
151	{MT_N0_STA_ACK, "STAtus ACKnowledge"},
152	{MT_N0_STA_REJ, "STAtus REJect"},
153	{MT_N0_FAC_INF, "FACility INFormation"},
154	{MT_N0_INF_ACK, "INFormation ACKnowledge"},
155	{MT_N0_INF_REJ, "INFormation REJect"},
156	{MT_N0_CLOSE, "CLOSE"},
157	{MT_N0_CLO_ACK, "CLOse ACKnowledge"}
158};
159
160#define MT_N0_LEN ARRAY_SIZE(mt_n0)
161
162static
163struct MessageType mt_n1[] =
164{
165	{MT_N1_ESC, "ESCape"},
166	{MT_N1_ALERT, "ALERT"},
167	{MT_N1_CALL_SENT, "CALL SENT"},
168	{MT_N1_CONN, "CONNect"},
169	{MT_N1_CONN_ACK, "CONNect ACKnowledge"},
170	{MT_N1_SETUP, "SETUP"},
171	{MT_N1_SETUP_ACK, "SETUP ACKnowledge"},
172	{MT_N1_RES, "RESume"},
173	{MT_N1_RES_ACK, "RESume ACKnowledge"},
174	{MT_N1_RES_REJ, "RESume REJect"},
175	{MT_N1_SUSP, "SUSPend"},
176	{MT_N1_SUSP_ACK, "SUSPend ACKnowledge"},
177	{MT_N1_SUSP_REJ, "SUSPend REJect"},
178	{MT_N1_USER_INFO, "USER INFO"},
179	{MT_N1_DET, "DETach"},
180	{MT_N1_DISC, "DISConnect"},
181	{MT_N1_REL, "RELease"},
182	{MT_N1_REL_ACK, "RELease ACKnowledge"},
183	{MT_N1_CANC_ACK, "CANCel ACKnowledge"},
184	{MT_N1_CANC_REJ, "CANCel REJect"},
185	{MT_N1_CON_CON, "CONgestion CONtrol"},
186	{MT_N1_FAC, "FACility"},
187	{MT_N1_FAC_ACK, "FACility ACKnowledge"},
188	{MT_N1_FAC_CAN, "FACility CANcel"},
189	{MT_N1_FAC_REG, "FACility REGister"},
190	{MT_N1_FAC_REJ, "FACility REJect"},
191	{MT_N1_INFO, "INFOrmation"},
192	{MT_N1_REG_ACK, "REGister ACKnowledge"},
193	{MT_N1_REG_REJ, "REGister REJect"},
194	{MT_N1_STAT, "STATus"}
195};
196
197#define MT_N1_LEN ARRAY_SIZE(mt_n1)
198
199
200static int
201prbits(char *dest, u_char b, int start, int len)
202{
203	char *dp = dest;
204
205	b = b << (8 - start);
206	while (len--) {
207		if (b & 0x80)
208			*dp++ = '1';
209		else
210			*dp++ = '0';
211		b = b << 1;
212	}
213	return (dp - dest);
214}
215
216static
217u_char *
218skipext(u_char *p)
219{
220	while (!(*p++ & 0x80));
221	return (p);
222}
223
224/*
225 * Cause Values According to Q.850
226 * edescr: English description
227 * ddescr: German description used by Swissnet II (Swiss Telecom
228 *         not yet written...
229 */
230
231static
232struct CauseValue {
233	u_char nr;
234	char *edescr;
235	char *ddescr;
236} cvlist[] = {
237
238	{
239		0x01, "Unallocated (unassigned) number", "Nummer nicht zugeteilt"
240	},
241	{
242		0x02, "No route to specified transit network", ""
243	},
244	{
245		0x03, "No route to destination", ""
246	},
247	{
248		0x04, "Send special information tone", ""
249	},
250	{
251		0x05, "Misdialled trunk prefix", ""
252	},
253	{
254		0x06, "Channel unacceptable", "Kanal nicht akzeptierbar"
255	},
256	{
257		0x07, "Channel awarded and being delivered in an established channel", ""
258	},
259	{
260		0x08, "Preemption", ""
261	},
262	{
263		0x09, "Preemption - circuit reserved for reuse", ""
264	},
265	{
266		0x10, "Normal call clearing", "Normale Ausloesung"
267	},
268	{
269		0x11, "User busy", "TNB besetzt"
270	},
271	{
272		0x12, "No user responding", ""
273	},
274	{
275		0x13, "No answer from user (user alerted)", ""
276	},
277	{
278		0x14, "Subscriber absent", ""
279	},
280	{
281		0x15, "Call rejected", ""
282	},
283	{
284		0x16, "Number changed", ""
285	},
286	{
287		0x1a, "non-selected user clearing", ""
288	},
289	{
290		0x1b, "Destination out of order", ""
291	},
292	{
293		0x1c, "Invalid number format (address incomplete)", ""
294	},
295	{
296		0x1d, "Facility rejected", ""
297	},
298	{
299		0x1e, "Response to Status enquiry", ""
300	},
301	{
302		0x1f, "Normal, unspecified", ""
303	},
304	{
305		0x22, "No circuit/channel available", ""
306	},
307	{
308		0x26, "Network out of order", ""
309	},
310	{
311		0x27, "Permanent frame mode connection out-of-service", ""
312	},
313	{
314		0x28, "Permanent frame mode connection operational", ""
315	},
316	{
317		0x29, "Temporary failure", ""
318	},
319	{
320		0x2a, "Switching equipment congestion", ""
321	},
322	{
323		0x2b, "Access information discarded", ""
324	},
325	{
326		0x2c, "Requested circuit/channel not available", ""
327	},
328	{
329		0x2e, "Precedence call blocked", ""
330	},
331	{
332		0x2f, "Resource unavailable, unspecified", ""
333	},
334	{
335		0x31, "Quality of service unavailable", ""
336	},
337	{
338		0x32, "Requested facility not subscribed", ""
339	},
340	{
341		0x35, "Outgoing calls barred within CUG", ""
342	},
343	{
344		0x37, "Incoming calls barred within CUG", ""
345	},
346	{
347		0x39, "Bearer capability not authorized", ""
348	},
349	{
350		0x3a, "Bearer capability not presently available", ""
351	},
352	{
353		0x3e, "Inconsistency in designated outgoing access information and subscriber class ", " "
354	},
355	{
356		0x3f, "Service or option not available, unspecified", ""
357	},
358	{
359		0x41, "Bearer capability not implemented", ""
360	},
361	{
362		0x42, "Channel type not implemented", ""
363	},
364	{
365		0x43, "Requested facility not implemented", ""
366	},
367	{
368		0x44, "Only restricted digital information bearer capability is available", ""
369	},
370	{
371		0x4f, "Service or option not implemented", ""
372	},
373	{
374		0x51, "Invalid call reference value", ""
375	},
376	{
377		0x52, "Identified channel does not exist", ""
378	},
379	{
380		0x53, "A suspended call exists, but this call identity does not", ""
381	},
382	{
383		0x54, "Call identity in use", ""
384	},
385	{
386		0x55, "No call suspended", ""
387	},
388	{
389		0x56, "Call having the requested call identity has been cleared", ""
390	},
391	{
392		0x57, "User not member of CUG", ""
393	},
394	{
395		0x58, "Incompatible destination", ""
396	},
397	{
398		0x5a, "Non-existent CUG", ""
399	},
400	{
401		0x5b, "Invalid transit network selection", ""
402	},
403	{
404		0x5f, "Invalid message, unspecified", ""
405	},
406	{
407		0x60, "Mandatory information element is missing", ""
408	},
409	{
410		0x61, "Message type non-existent or not implemented", ""
411	},
412	{
413		0x62, "Message not compatible with call state or message type non-existent or not implemented ", " "
414	},
415	{
416		0x63, "Information element/parameter non-existent or not implemented", ""
417	},
418	{
419		0x64, "Invalid information element contents", ""
420	},
421	{
422		0x65, "Message not compatible with call state", ""
423	},
424	{
425		0x66, "Recovery on timer expiry", ""
426	},
427	{
428		0x67, "Parameter non-existent or not implemented - passed on", ""
429	},
430	{
431		0x6e, "Message with unrecognized parameter discarded", ""
432	},
433	{
434		0x6f, "Protocol error, unspecified", ""
435	},
436	{
437		0x7f, "Interworking, unspecified", ""
438	},
439};
440
441#define CVSIZE ARRAY_SIZE(cvlist)
442
443static
444int
445prcause(char *dest, u_char *p)
446{
447	u_char *end;
448	char *dp = dest;
449	int i, cause;
450
451	end = p + p[1] + 1;
452	p += 2;
453	dp += sprintf(dp, "    coding ");
454	dp += prbits(dp, *p, 7, 2);
455	dp += sprintf(dp, " location ");
456	dp += prbits(dp, *p, 4, 4);
457	*dp++ = '\n';
458	p = skipext(p);
459
460	cause = 0x7f & *p++;
461
462	/* locate cause value */
463	for (i = 0; i < CVSIZE; i++)
464		if (cvlist[i].nr == cause)
465			break;
466
467	/* display cause value if it exists */
468	if (i == CVSIZE)
469		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
470	else
471		dp += sprintf(dp, "  cause value %x : %s \n", cause, cvlist[i].edescr);
472
473	while (!0) {
474		if (p > end)
475			break;
476		dp += sprintf(dp, "    diag attribute %d ", *p++ & 0x7f);
477		dp += sprintf(dp, " rej %d ", *p & 0x7f);
478		if (*p & 0x80) {
479			*dp++ = '\n';
480			break;
481		} else
482			dp += sprintf(dp, " av %d\n", (*++p) & 0x7f);
483	}
484	return (dp - dest);
485
486}
487
488static
489struct MessageType cause_1tr6[] =
490{
491	{CAUSE_InvCRef, "Invalid Call Reference"},
492	{CAUSE_BearerNotImpl, "Bearer Service Not Implemented"},
493	{CAUSE_CIDunknown, "Caller Identity unknown"},
494	{CAUSE_CIDinUse, "Caller Identity in Use"},
495	{CAUSE_NoChans, "No Channels available"},
496	{CAUSE_FacNotImpl, "Facility Not Implemented"},
497	{CAUSE_FacNotSubscr, "Facility Not Subscribed"},
498	{CAUSE_OutgoingBarred, "Outgoing calls barred"},
499	{CAUSE_UserAccessBusy, "User Access Busy"},
500	{CAUSE_NegativeGBG, "Negative GBG"},
501	{CAUSE_UnknownGBG, "Unknown  GBG"},
502	{CAUSE_NoSPVknown, "No SPV known"},
503	{CAUSE_DestNotObtain, "Destination not obtainable"},
504	{CAUSE_NumberChanged, "Number changed"},
505	{CAUSE_OutOfOrder, "Out Of Order"},
506	{CAUSE_NoUserResponse, "No User Response"},
507	{CAUSE_UserBusy, "User Busy"},
508	{CAUSE_IncomingBarred, "Incoming Barred"},
509	{CAUSE_CallRejected, "Call Rejected"},
510	{CAUSE_NetworkCongestion, "Network Congestion"},
511	{CAUSE_RemoteUser, "Remote User initiated"},
512	{CAUSE_LocalProcErr, "Local Procedure Error"},
513	{CAUSE_RemoteProcErr, "Remote Procedure Error"},
514	{CAUSE_RemoteUserSuspend, "Remote User Suspend"},
515	{CAUSE_RemoteUserResumed, "Remote User Resumed"},
516	{CAUSE_UserInfoDiscarded, "User Info Discarded"}
517};
518
519static int cause_1tr6_len = ARRAY_SIZE(cause_1tr6);
520
521static int
522prcause_1tr6(char *dest, u_char *p)
523{
524	char *dp = dest;
525	int i, cause;
526
527	p++;
528	if (0 == *p) {
529		dp += sprintf(dp, "   OK (cause length=0)\n");
530		return (dp - dest);
531	} else if (*p > 1) {
532		dp += sprintf(dp, "    coding ");
533		dp += prbits(dp, p[2], 7, 2);
534		dp += sprintf(dp, " location ");
535		dp += prbits(dp, p[2], 4, 4);
536		*dp++ = '\n';
537	}
538	p++;
539	cause = 0x7f & *p;
540
541	/* locate cause value */
542	for (i = 0; i < cause_1tr6_len; i++)
543		if (cause_1tr6[i].nr == cause)
544			break;
545
546	/* display cause value if it exists */
547	if (i == cause_1tr6_len)
548		dp += sprintf(dp, "Unknown cause type %x!\n", cause);
549	else
550		dp += sprintf(dp, "  cause value %x : %s \n", cause, cause_1tr6[i].descr);
551
552	return (dp - dest);
553
554}
555
556static int
557prchident(char *dest, u_char *p)
558{
559	char *dp = dest;
560
561	p += 2;
562	dp += sprintf(dp, "    octet 3 ");
563	dp += prbits(dp, *p, 8, 8);
564	*dp++ = '\n';
565	return (dp - dest);
566}
567
568static int
569prcalled(char *dest, u_char *p)
570{
571	int l;
572	char *dp = dest;
573
574	p++;
575	l = *p++ - 1;
576	dp += sprintf(dp, "    octet 3 ");
577	dp += prbits(dp, *p++, 8, 8);
578	*dp++ = '\n';
579	dp += sprintf(dp, "    number digits ");
580	while (l--)
581		*dp++ = *p++;
582	*dp++ = '\n';
583	return (dp - dest);
584}
585static int
586prcalling(char *dest, u_char *p)
587{
588	int l;
589	char *dp = dest;
590
591	p++;
592	l = *p++ - 1;
593	dp += sprintf(dp, "    octet 3 ");
594	dp += prbits(dp, *p, 8, 8);
595	*dp++ = '\n';
596	if (!(*p & 0x80)) {
597		dp += sprintf(dp, "    octet 3a ");
598		dp += prbits(dp, *++p, 8, 8);
599		*dp++ = '\n';
600		l--;
601	};
602	p++;
603
604	dp += sprintf(dp, "    number digits ");
605	while (l--)
606		*dp++ = *p++;
607	*dp++ = '\n';
608	return (dp - dest);
609}
610
611static
612int
613prbearer(char *dest, u_char *p)
614{
615	char *dp = dest, ch;
616
617	p += 2;
618	dp += sprintf(dp, "    octet 3  ");
619	dp += prbits(dp, *p++, 8, 8);
620	*dp++ = '\n';
621	dp += sprintf(dp, "    octet 4  ");
622	dp += prbits(dp, *p, 8, 8);
623	*dp++ = '\n';
624	if ((*p++ & 0x1f) == 0x18) {
625		dp += sprintf(dp, "    octet 4.1 ");
626		dp += prbits(dp, *p++, 8, 8);
627		*dp++ = '\n';
628	}
629	/* check for user information layer 1 */
630	if ((*p & 0x60) == 0x20) {
631		ch = ' ';
632		do {
633			dp += sprintf(dp, "    octet 5%c ", ch);
634			dp += prbits(dp, *p, 8, 8);
635			*dp++ = '\n';
636			if (ch == ' ')
637				ch = 'a';
638			else
639				ch++;
640		}
641		while (!(*p++ & 0x80));
642	}
643	/* check for user information layer 2 */
644	if ((*p & 0x60) == 0x40) {
645		dp += sprintf(dp, "    octet 6  ");
646		dp += prbits(dp, *p++, 8, 8);
647		*dp++ = '\n';
648	}
649	/* check for user information layer 3 */
650	if ((*p & 0x60) == 0x60) {
651		dp += sprintf(dp, "    octet 7  ");
652		dp += prbits(dp, *p++, 8, 8);
653		*dp++ = '\n';
654	}
655	return (dp - dest);
656}
657
658
659static
660int
661prbearer_ni1(char *dest, u_char *p)
662{
663	char *dp = dest;
664	u_char len;
665
666	p++;
667	len = *p++;
668	dp += sprintf(dp, "    octet 3  ");
669	dp += prbits(dp, *p, 8, 8);
670	switch (*p++) {
671	case 0x80:
672		dp += sprintf(dp, " Speech");
673		break;
674	case 0x88:
675		dp += sprintf(dp, " Unrestricted digital information");
676		break;
677	case 0x90:
678		dp += sprintf(dp, " 3.1 kHz audio");
679		break;
680	default:
681		dp += sprintf(dp, " Unknown information-transfer capability");
682	}
683	*dp++ = '\n';
684	dp += sprintf(dp, "    octet 4  ");
685	dp += prbits(dp, *p, 8, 8);
686	switch (*p++) {
687	case 0x90:
688		dp += sprintf(dp, " 64 kbps, circuit mode");
689		break;
690	case 0xc0:
691		dp += sprintf(dp, " Packet mode");
692		break;
693	default:
694		dp += sprintf(dp, " Unknown transfer mode");
695	}
696	*dp++ = '\n';
697	if (len > 2) {
698		dp += sprintf(dp, "    octet 5  ");
699		dp += prbits(dp, *p, 8, 8);
700		switch (*p++) {
701		case 0x21:
702			dp += sprintf(dp, " Rate adaption\n");
703			dp += sprintf(dp, "    octet 5a ");
704			dp += prbits(dp, *p, 8, 8);
705			break;
706		case 0xa2:
707			dp += sprintf(dp, " u-law");
708			break;
709		default:
710			dp += sprintf(dp, " Unknown UI layer 1 protocol");
711		}
712		*dp++ = '\n';
713	}
714	return (dp - dest);
715}
716
717static int
718general(char *dest, u_char *p)
719{
720	char *dp = dest;
721	char ch = ' ';
722	int l, octet = 3;
723
724	p++;
725	l = *p++;
726	/* Iterate over all octets in the information element */
727	while (l--) {
728		dp += sprintf(dp, "    octet %d%c ", octet, ch);
729		dp += prbits(dp, *p++, 8, 8);
730		*dp++ = '\n';
731
732		/* last octet in group? */
733		if (*p & 0x80) {
734			octet++;
735			ch = ' ';
736		} else if (ch == ' ')
737			ch = 'a';
738		else
739			ch++;
740	}
741	return (dp - dest);
742}
743
744static int
745general_ni1(char *dest, u_char *p)
746{
747	char *dp = dest;
748	char ch = ' ';
749	int l, octet = 3;
750
751	p++;
752	l = *p++;
753	/* Iterate over all octets in the information element */
754	while (l--) {
755		dp += sprintf(dp, "    octet %d%c ", octet, ch);
756		dp += prbits(dp, *p, 8, 8);
757		*dp++ = '\n';
758
759		/* last octet in group? */
760		if (*p++ & 0x80) {
761			octet++;
762			ch = ' ';
763		} else if (ch == ' ')
764			ch = 'a';
765		else
766			ch++;
767	}
768	return (dp - dest);
769}
770
771static int
772prcharge(char *dest, u_char *p)
773{
774	char *dp = dest;
775	int l;
776
777	p++;
778	l = *p++ - 1;
779	dp += sprintf(dp, "    GEA ");
780	dp += prbits(dp, *p++, 8, 8);
781	dp += sprintf(dp, "  Anzahl: ");
782	/* Iterate over all octets in the * information element */
783	while (l--)
784		*dp++ = *p++;
785	*dp++ = '\n';
786	return (dp - dest);
787}
788static int
789prtext(char *dest, u_char *p)
790{
791	char *dp = dest;
792	int l;
793
794	p++;
795	l = *p++;
796	dp += sprintf(dp, "    ");
797	/* Iterate over all octets in the * information element */
798	while (l--)
799		*dp++ = *p++;
800	*dp++ = '\n';
801	return (dp - dest);
802}
803
804static int
805prfeatureind(char *dest, u_char *p)
806{
807	char *dp = dest;
808
809	p += 2; /* skip id, len */
810	dp += sprintf(dp, "    octet 3  ");
811	dp += prbits(dp, *p, 8, 8);
812	*dp++ = '\n';
813	if (!(*p++ & 0x80)) {
814		dp += sprintf(dp, "    octet 4  ");
815		dp += prbits(dp, *p++, 8, 8);
816		*dp++ = '\n';
817	}
818	dp += sprintf(dp, "    Status:  ");
819	switch (*p) {
820	case 0:
821		dp += sprintf(dp, "Idle");
822		break;
823	case 1:
824		dp += sprintf(dp, "Active");
825		break;
826	case 2:
827		dp += sprintf(dp, "Prompt");
828		break;
829	case 3:
830		dp += sprintf(dp, "Pending");
831		break;
832	default:
833		dp += sprintf(dp, "(Reserved)");
834		break;
835	}
836	*dp++ = '\n';
837	return (dp - dest);
838}
839
840static
841struct DTag { /* Display tags */
842	u_char nr;
843	char *descr;
844} dtaglist[] = {
845	{ 0x82, "Continuation" },
846	{ 0x83, "Called address" },
847	{ 0x84, "Cause" },
848	{ 0x85, "Progress indicator" },
849	{ 0x86, "Notification indicator" },
850	{ 0x87, "Prompt" },
851	{ 0x88, "Accumlated digits" },
852	{ 0x89, "Status" },
853	{ 0x8a, "Inband" },
854	{ 0x8b, "Calling address" },
855	{ 0x8c, "Reason" },
856	{ 0x8d, "Calling party name" },
857	{ 0x8e, "Called party name" },
858	{ 0x8f, "Orignal called name" },
859	{ 0x90, "Redirecting name" },
860	{ 0x91, "Connected name" },
861	{ 0x92, "Originating restrictions" },
862	{ 0x93, "Date & time of day" },
863	{ 0x94, "Call Appearance ID" },
864	{ 0x95, "Feature address" },
865	{ 0x96, "Redirection name" },
866	{ 0x9e, "Text" },
867};
868#define DTAGSIZE ARRAY_SIZE(dtaglist)
869
870static int
871disptext_ni1(char *dest, u_char *p)
872{
873	char *dp = dest;
874	int l, tag, len, i;
875
876	p++;
877	l = *p++ - 1;
878	if (*p++ != 0x80) {
879		dp += sprintf(dp, "    Unknown display type\n");
880		return (dp - dest);
881	}
882	/* Iterate over all tag,length,text fields */
883	while (l > 0) {
884		tag = *p++;
885		len = *p++;
886		l -= len + 2;
887		/* Don't space or skip */
888		if ((tag == 0x80) || (tag == 0x81)) p++;
889		else {
890			for (i = 0; i < DTAGSIZE; i++)
891				if (tag == dtaglist[i].nr)
892					break;
893
894			/* When not found, give appropriate msg */
895			if (i != DTAGSIZE) {
896				dp += sprintf(dp, "    %s: ", dtaglist[i].descr);
897				while (len--)
898					*dp++ = *p++;
899			} else {
900				dp += sprintf(dp, "    (unknown display tag %2x): ", tag);
901				while (len--)
902					*dp++ = *p++;
903			}
904			dp += sprintf(dp, "\n");
905		}
906	}
907	return (dp - dest);
908}
909static int
910display(char *dest, u_char *p)
911{
912	char *dp = dest;
913	char ch = ' ';
914	int l, octet = 3;
915
916	p++;
917	l = *p++;
918	/* Iterate over all octets in the * display-information element */
919	dp += sprintf(dp, "   \"");
920	while (l--) {
921		dp += sprintf(dp, "%c", *p++);
922
923		/* last octet in group? */
924		if (*p & 0x80) {
925			octet++;
926			ch = ' ';
927		} else if (ch == ' ')
928			ch = 'a';
929
930		else
931			ch++;
932	}
933	*dp++ = '\"';
934	*dp++ = '\n';
935	return (dp - dest);
936}
937
938static int
939prfacility(char *dest, u_char *p)
940{
941	char *dp = dest;
942	int l, l2;
943
944	p++;
945	l = *p++;
946	dp += sprintf(dp, "    octet 3 ");
947	dp += prbits(dp, *p++, 8, 8);
948	dp += sprintf(dp, "\n");
949	l -= 1;
950
951	while (l > 0) {
952		dp += sprintf(dp, "   octet 4 ");
953		dp += prbits(dp, *p++, 8, 8);
954		dp += sprintf(dp, "\n");
955		dp += sprintf(dp, "   octet 5 %d\n", l2 = *p++ & 0x7f);
956		l -= 2;
957		dp += sprintf(dp, "   contents ");
958		while (l2--) {
959			dp += sprintf(dp, "%2x ", *p++);
960			l--;
961		}
962		dp += sprintf(dp, "\n");
963	}
964
965	return (dp - dest);
966}
967
968static
969struct InformationElement {
970	u_char nr;
971	char *descr;
972	int (*f) (char *, u_char *);
973} ielist[] = {
974
975	{
976		0x00, "Segmented message", general
977	},
978	{
979		0x04, "Bearer capability", prbearer
980	},
981	{
982		0x08, "Cause", prcause
983	},
984	{
985		0x10, "Call identity", general
986	},
987	{
988		0x14, "Call state", general
989	},
990	{
991		0x18, "Channel identification", prchident
992	},
993	{
994		0x1c, "Facility", prfacility
995	},
996	{
997		0x1e, "Progress indicator", general
998	},
999	{
1000		0x20, "Network-specific facilities", general
1001	},
1002	{
1003		0x27, "Notification indicator", general
1004	},
1005	{
1006		0x28, "Display", display
1007	},
1008	{
1009		0x29, "Date/Time", general
1010	},
1011	{
1012		0x2c, "Keypad facility", general
1013	},
1014	{
1015		0x34, "Signal", general
1016	},
1017	{
1018		0x40, "Information rate", general
1019	},
1020	{
1021		0x42, "End-to-end delay", general
1022	},
1023	{
1024		0x43, "Transit delay selection and indication", general
1025	},
1026	{
1027		0x44, "Packet layer binary parameters", general
1028	},
1029	{
1030		0x45, "Packet layer window size", general
1031	},
1032	{
1033		0x46, "Packet size", general
1034	},
1035	{
1036		0x47, "Closed user group", general
1037	},
1038	{
1039		0x4a, "Reverse charge indication", general
1040	},
1041	{
1042		0x6c, "Calling party number", prcalling
1043	},
1044	{
1045		0x6d, "Calling party subaddress", general
1046	},
1047	{
1048		0x70, "Called party number", prcalled
1049	},
1050	{
1051		0x71, "Called party subaddress", general
1052	},
1053	{
1054		0x74, "Redirecting number", general
1055	},
1056	{
1057		0x78, "Transit network selection", general
1058	},
1059	{
1060		0x79, "Restart indicator", general
1061	},
1062	{
1063		0x7c, "Low layer compatibility", general
1064	},
1065	{
1066		0x7d, "High layer compatibility", general
1067	},
1068	{
1069		0x7e, "User-user", general
1070	},
1071	{
1072		0x7f, "Escape for extension", general
1073	},
1074};
1075
1076
1077#define IESIZE ARRAY_SIZE(ielist)
1078
1079static
1080struct InformationElement ielist_ni1[] = {
1081	{ 0x04, "Bearer Capability", prbearer_ni1 },
1082	{ 0x08, "Cause", prcause },
1083	{ 0x14, "Call State", general_ni1 },
1084	{ 0x18, "Channel Identification", prchident },
1085	{ 0x1e, "Progress Indicator", general_ni1 },
1086	{ 0x27, "Notification Indicator", general_ni1 },
1087	{ 0x2c, "Keypad Facility", prtext },
1088	{ 0x32, "Information Request", general_ni1 },
1089	{ 0x34, "Signal", general_ni1 },
1090	{ 0x38, "Feature Activation", general_ni1 },
1091	{ 0x39, "Feature Indication", prfeatureind },
1092	{ 0x3a, "Service Profile Identification (SPID)", prtext },
1093	{ 0x3b, "Endpoint Identifier", general_ni1 },
1094	{ 0x6c, "Calling Party Number", prcalling },
1095	{ 0x6d, "Calling Party Subaddress", general_ni1 },
1096	{ 0x70, "Called Party Number", prcalled },
1097	{ 0x71, "Called Party Subaddress", general_ni1 },
1098	{ 0x74, "Redirecting Number", general_ni1 },
1099	{ 0x78, "Transit Network Selection", general_ni1 },
1100	{ 0x7c, "Low Layer Compatibility", general_ni1 },
1101	{ 0x7d, "High Layer Compatibility", general_ni1 },
1102};
1103
1104
1105#define IESIZE_NI1 ARRAY_SIZE(ielist_ni1)
1106
1107static
1108struct InformationElement ielist_ni1_cs5[] = {
1109	{ 0x1d, "Operator system access", general_ni1 },
1110	{ 0x2a, "Display text", disptext_ni1 },
1111};
1112
1113#define IESIZE_NI1_CS5 ARRAY_SIZE(ielist_ni1_cs5)
1114
1115static
1116struct InformationElement ielist_ni1_cs6[] = {
1117	{ 0x7b, "Call appearance", general_ni1 },
1118};
1119
1120#define IESIZE_NI1_CS6 ARRAY_SIZE(ielist_ni1_cs6)
1121
1122static struct InformationElement we_0[] =
1123{
1124	{WE0_cause, "Cause", prcause_1tr6},
1125	{WE0_connAddr, "Connecting Address", prcalled},
1126	{WE0_callID, "Call IDentity", general},
1127	{WE0_chanID, "Channel IDentity", general},
1128	{WE0_netSpecFac, "Network Specific Facility", general},
1129	{WE0_display, "Display", general},
1130	{WE0_keypad, "Keypad", general},
1131	{WE0_origAddr, "Origination Address", prcalled},
1132	{WE0_destAddr, "Destination Address", prcalled},
1133	{WE0_userInfo, "User Info", general}
1134};
1135
1136#define WE_0_LEN ARRAY_SIZE(we_0)
1137
1138static struct InformationElement we_6[] =
1139{
1140	{WE6_serviceInd, "Service Indicator", general},
1141	{WE6_chargingInfo, "Charging Information", prcharge},
1142	{WE6_date, "Date", prtext},
1143	{WE6_facSelect, "Facility Select", general},
1144	{WE6_facStatus, "Facility Status", general},
1145	{WE6_statusCalled, "Status Called", general},
1146	{WE6_addTransAttr, "Additional Transmission Attributes", general}
1147};
1148#define WE_6_LEN ARRAY_SIZE(we_6)
1149
1150int
1151QuickHex(char *txt, u_char *p, int cnt)
1152{
1153	register int i;
1154	register char *t = txt;
1155
1156	for (i = 0; i < cnt; i++) {
1157		*t++ = ' ';
1158		*t++ = hex_asc_hi(p[i]);
1159		*t++ = hex_asc_lo(p[i]);
1160	}
1161	*t++ = 0;
1162	return (t - txt);
1163}
1164
1165void
1166LogFrame(struct IsdnCardState *cs, u_char *buf, int size)
1167{
1168	char *dp;
1169
1170	if (size < 1)
1171		return;
1172	dp = cs->dlog;
1173	if (size < MAX_DLOG_SPACE / 3 - 10) {
1174		*dp++ = 'H';
1175		*dp++ = 'E';
1176		*dp++ = 'X';
1177		*dp++ = ':';
1178		dp += QuickHex(dp, buf, size);
1179		dp--;
1180		*dp++ = '\n';
1181		*dp = 0;
1182		HiSax_putstatus(cs, NULL, "%s", cs->dlog);
1183	} else
1184		HiSax_putstatus(cs, "LogFrame: ", "warning Frame too big (%d)", size);
1185}
1186
1187void
1188dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir)
1189{
1190	u_char *bend, *buf;
1191	char *dp;
1192	unsigned char pd, cr_l, cr, mt;
1193	unsigned char sapi, tei, ftyp;
1194	int i, cset = 0, cs_old = 0, cs_fest = 0;
1195	int size, finish = 0;
1196
1197	if (skb->len < 3)
1198		return;
1199	/* display header */
1200	dp = cs->dlog;
1201	dp += jiftime(dp, jiffies);
1202	*dp++ = ' ';
1203	sapi = skb->data[0] >> 2;
1204	tei  = skb->data[1] >> 1;
1205	ftyp = skb->data[2];
1206	buf = skb->data;
1207	dp += sprintf(dp, "frame %s ", dir ? "network->user" : "user->network");
1208	size = skb->len;
1209
1210	if (tei == GROUP_TEI) {
1211		if (sapi == CTRL_SAPI) { /* sapi 0 */
1212			if (ftyp == 3) {
1213				dp += sprintf(dp, "broadcast\n");
1214				buf += 3;
1215				size -= 3;
1216			} else {
1217				dp += sprintf(dp, "no UI broadcast\n");
1218				finish = 1;
1219			}
1220		} else if (sapi == TEI_SAPI) {
1221			dp += sprintf(dp, "tei management\n");
1222			finish = 1;
1223		} else {
1224			dp += sprintf(dp, "unknown sapi %d broadcast\n", sapi);
1225			finish = 1;
1226		}
1227	} else {
1228		if (sapi == CTRL_SAPI) {
1229			if (!(ftyp & 1)) { /* IFrame */
1230				dp += sprintf(dp, "with tei %d\n", tei);
1231				buf += 4;
1232				size -= 4;
1233			} else {
1234				dp += sprintf(dp, "SFrame with tei %d\n", tei);
1235				finish = 1;
1236			}
1237		} else {
1238			dp += sprintf(dp, "unknown sapi %d tei %d\n", sapi, tei);
1239			finish = 1;
1240		}
1241	}
1242	bend = skb->data + skb->len;
1243	if (buf >= bend) {
1244		dp += sprintf(dp, "frame too short\n");
1245		finish = 1;
1246	}
1247	if (finish) {
1248		*dp = 0;
1249		HiSax_putstatus(cs, NULL, "%s", cs->dlog);
1250		return;
1251	}
1252	if ((0xfe & buf[0]) == PROTO_DIS_N0) {	/* 1TR6 */
1253		/* locate message type */
1254		pd = *buf++;
1255		cr_l = *buf++;
1256		if (cr_l)
1257			cr = *buf++;
1258		else
1259			cr = 0;
1260		mt = *buf++;
1261		if (pd == PROTO_DIS_N0) {	/* N0 */
1262			for (i = 0; i < MT_N0_LEN; i++)
1263				if (mt_n0[i].nr == mt)
1264					break;
1265			/* display message type if it exists */
1266			if (i == MT_N0_LEN)
1267				dp += sprintf(dp, "callref %d %s size %d unknown message type N0 %x!\n",
1268					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
1269					      size, mt);
1270			else
1271				dp += sprintf(dp, "callref %d %s size %d message type %s\n",
1272					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
1273					      size, mt_n0[i].descr);
1274		} else {	/* N1 */
1275			for (i = 0; i < MT_N1_LEN; i++)
1276				if (mt_n1[i].nr == mt)
1277					break;
1278			/* display message type if it exists */
1279			if (i == MT_N1_LEN)
1280				dp += sprintf(dp, "callref %d %s size %d unknown message type N1 %x!\n",
1281					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
1282					      size, mt);
1283			else
1284				dp += sprintf(dp, "callref %d %s size %d message type %s\n",
1285					      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
1286					      size, mt_n1[i].descr);
1287		}
1288
1289		/* display each information element */
1290		while (buf < bend) {
1291			/* Is it a single octet information element? */
1292			if (*buf & 0x80) {
1293				switch ((*buf >> 4) & 7) {
1294				case 1:
1295					dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
1296					cs_old = cset;
1297					cset = *buf & 7;
1298					cs_fest = *buf & 8;
1299					break;
1300				case 3:
1301					dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
1302					break;
1303				case 2:
1304					if (*buf == 0xa0) {
1305						dp += sprintf(dp, "  More data\n");
1306						break;
1307					}
1308					if (*buf == 0xa1) {
1309						dp += sprintf(dp, "  Sending complete\n");
1310					}
1311					break;
1312					/* fall through */
1313				default:
1314					dp += sprintf(dp, "  Reserved %x\n", *buf);
1315					break;
1316				}
1317				buf++;
1318				continue;
1319			}
1320			/* No, locate it in the table */
1321			if (cset == 0) {
1322				for (i = 0; i < WE_0_LEN; i++)
1323					if (*buf == we_0[i].nr)
1324						break;
1325
1326				/* When found, give appropriate msg */
1327				if (i != WE_0_LEN) {
1328					dp += sprintf(dp, "  %s\n", we_0[i].descr);
1329					dp += we_0[i].f(dp, buf);
1330				} else
1331					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
1332			} else if (cset == 6) {
1333				for (i = 0; i < WE_6_LEN; i++)
1334					if (*buf == we_6[i].nr)
1335						break;
1336
1337				/* When found, give appropriate msg */
1338				if (i != WE_6_LEN) {
1339					dp += sprintf(dp, "  %s\n", we_6[i].descr);
1340					dp += we_6[i].f(dp, buf);
1341				} else
1342					dp += sprintf(dp, "  Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
1343			} else
1344				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
1345			/* Skip to next element */
1346			if (cs_fest == 8) {
1347				cset = cs_old;
1348				cs_old = 0;
1349				cs_fest = 0;
1350			}
1351			buf += buf[1] + 2;
1352		}
1353	} else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_NI1)) {	/* NI-1 */
1354		/* locate message type */
1355		buf++;
1356		cr_l = *buf++;
1357		if (cr_l)
1358			cr = *buf++;
1359		else
1360			cr = 0;
1361		mt = *buf++;
1362		for (i = 0; i < MTSIZE; i++)
1363			if (mtlist[i].nr == mt)
1364				break;
1365
1366		/* display message type if it exists */
1367		if (i == MTSIZE)
1368			dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n",
1369				      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
1370				      size, mt);
1371		else
1372			dp += sprintf(dp, "callref %d %s size %d message type %s\n",
1373				      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
1374				      size, mtlist[i].descr);
1375
1376		/* display each information element */
1377		while (buf < bend) {
1378			/* Is it a single octet information element? */
1379			if (*buf & 0x80) {
1380				switch ((*buf >> 4) & 7) {
1381				case 1:
1382					dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
1383					cs_old = cset;
1384					cset = *buf & 7;
1385					cs_fest = *buf & 8;
1386					break;
1387				default:
1388					dp += sprintf(dp, "  Unknown single-octet IE %x\n", *buf);
1389					break;
1390				}
1391				buf++;
1392				continue;
1393			}
1394			/* No, locate it in the table */
1395			if (cset == 0) {
1396				for (i = 0; i < IESIZE_NI1; i++)
1397					if (*buf == ielist_ni1[i].nr)
1398						break;
1399
1400				/* When not found, give appropriate msg */
1401				if (i != IESIZE_NI1) {
1402					dp += sprintf(dp, "  %s\n", ielist_ni1[i].descr);
1403					dp += ielist_ni1[i].f(dp, buf);
1404				} else
1405					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
1406			} else if (cset == 5) {
1407				for (i = 0; i < IESIZE_NI1_CS5; i++)
1408					if (*buf == ielist_ni1_cs5[i].nr)
1409						break;
1410
1411				/* When not found, give appropriate msg */
1412				if (i != IESIZE_NI1_CS5) {
1413					dp += sprintf(dp, "  %s\n", ielist_ni1_cs5[i].descr);
1414					dp += ielist_ni1_cs5[i].f(dp, buf);
1415				} else
1416					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
1417			} else if (cset == 6) {
1418				for (i = 0; i < IESIZE_NI1_CS6; i++)
1419					if (*buf == ielist_ni1_cs6[i].nr)
1420						break;
1421
1422				/* When not found, give appropriate msg */
1423				if (i != IESIZE_NI1_CS6) {
1424					dp += sprintf(dp, "  %s\n", ielist_ni1_cs6[i].descr);
1425					dp += ielist_ni1_cs6[i].f(dp, buf);
1426				} else
1427					dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
1428			} else
1429				dp += sprintf(dp, "  Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
1430
1431			/* Skip to next element */
1432			if (cs_fest == 8) {
1433				cset = cs_old;
1434				cs_old = 0;
1435				cs_fest = 0;
1436			}
1437			buf += buf[1] + 2;
1438		}
1439	} else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_EURO)) { /* EURO */
1440		/* locate message type */
1441		buf++;
1442		cr_l = *buf++;
1443		if (cr_l)
1444			cr = *buf++;
1445		else
1446			cr = 0;
1447		mt = *buf++;
1448		for (i = 0; i < MTSIZE; i++)
1449			if (mtlist[i].nr == mt)
1450				break;
1451
1452		/* display message type if it exists */
1453		if (i == MTSIZE)
1454			dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n",
1455				      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
1456				      size, mt);
1457		else
1458			dp += sprintf(dp, "callref %d %s size %d message type %s\n",
1459				      cr & 0x7f, (cr & 0x80) ? "called" : "caller",
1460				      size, mtlist[i].descr);
1461
1462		/* display each information element */
1463		while (buf < bend) {
1464			/* Is it a single octet information element? */
1465			if (*buf & 0x80) {
1466				switch ((*buf >> 4) & 7) {
1467				case 1:
1468					dp += sprintf(dp, "  Shift %x\n", *buf & 0xf);
1469					break;
1470				case 3:
1471					dp += sprintf(dp, "  Congestion level %x\n", *buf & 0xf);
1472					break;
1473				case 5:
1474					dp += sprintf(dp, "  Repeat indicator %x\n", *buf & 0xf);
1475					break;
1476				case 2:
1477					if (*buf == 0xa0) {
1478						dp += sprintf(dp, "  More data\n");
1479						break;
1480					}
1481					if (*buf == 0xa1) {
1482						dp += sprintf(dp, "  Sending complete\n");
1483					}
1484					break;
1485					/* fall through */
1486				default:
1487					dp += sprintf(dp, "  Reserved %x\n", *buf);
1488					break;
1489				}
1490				buf++;
1491				continue;
1492			}
1493			/* No, locate it in the table */
1494			for (i = 0; i < IESIZE; i++)
1495				if (*buf == ielist[i].nr)
1496					break;
1497
1498			/* When not found, give appropriate msg */
1499			if (i != IESIZE) {
1500				dp += sprintf(dp, "  %s\n", ielist[i].descr);
1501				dp += ielist[i].f(dp, buf);
1502			} else
1503				dp += sprintf(dp, "  attribute %x attribute size %d\n", *buf, buf[1]);
1504
1505			/* Skip to next element */
1506			buf += buf[1] + 2;
1507		}
1508	} else {
1509		dp += sprintf(dp, "Unknown protocol %x!", buf[0]);
1510	}
1511	*dp = 0;
1512	HiSax_putstatus(cs, NULL, "%s", cs->dlog);
1513}
1514