Index: src/zap_isdn.c
===================================================================
--- src/zap_isdn.c	(revision 495)
+++ src/zap_isdn.c	(working copy)
@@ -1113,6 +1113,7 @@
 				   0,
 				   0,
 				   mode,
+				   span->trunk_type == ZAP_TRUNK_BRI_PTMP ? Q921_PTMP : Q921_PTP,
 				   0,
 				   zap_isdn_921_21,
 				   (Q921TxCB_t)zap_isdn_921_23,
Index: src/isdn/include/Q921.h
===================================================================
--- src/isdn/include/Q921.h	(revision 495)
+++ src/isdn/include/Q921.h	(working copy)
@@ -76,52 +76,86 @@
 
 *****************************************************************************/
 
+/****************************************************************************
+ * Changes:
+ *
+ * - June 2008: Stefan Knoblich <s.knoblich@axsentis.de>:
+ *     Add PTMP TEI management
+ *     Various Cleanups
+ *
+ ****************************************************************************/
+
 #ifndef _Q921
 #define _Q921
 
 #define Q921MAXHDLCSPACE 3000
-#define L2UCHAR		unsigned char		/* Min 8 bit						*/
-#define L2INT       int                 /* Min 16 bit signed                */
-#define L2ULONG		unsigned long		/* Min 32 bit						*/
+#define L2UCHAR		unsigned char		/* Min 8 bit			*/
+#define L2USHORT	unsigned short		/* 16 bit			*/
+#define L2INT		int			/* Min 16 bit signed		*/
+#define L2ULONG		unsigned long		/* Min 32 bit			*/
 #define L2TRUNK		Q921Data_t *
 
-typedef enum					/* Network/User Mode.                   */
+typedef enum			/* Network/User Mode		*/
 {
-	Q921_TE=0,                  /*  0 : User Mode                       */
-    Q921_NT=1                   /*  1 : Network Mode                    */
+	Q921_TE=0,		/*  0 : User Mode		*/
+	Q921_NT=1		/*  1 : Network Mode		*/
 } Q921NetUser_t;
 
+typedef enum			/* Type of connection		*/
+{
+	Q921_PTP=0,		/* 0 : Point-To-Point		*/
+	Q921_PTMP=1		/* 1 : Point-To-Multipoint	*/
+} Q921NetType_t;
+
+typedef enum			/* State of TEI */
+{
+	Q921_TEI_UNASSIGNED = 0,	/* */
+	Q921_TEI_ASSIGNED = 1		/* */
+} Q921TeiState_t;
+
+
 typedef struct Q921Data Q921Data_t;
 typedef int (*Q921TxCB_t) (void *, L2UCHAR *, L2INT);
 
 #define INITIALIZED_MAGIC 42
 struct Q921Data
 {
-    L2UCHAR HDLCInQueue[Q921MAXHDLCSPACE];
+	L2UCHAR HDLCInQueue[Q921MAXHDLCSPACE];
 	L2INT initialized;
 	L2UCHAR va;
-    L2UCHAR vs;
-    L2UCHAR vr;
-    L2INT state;
+	L2UCHAR vs;
+	L2UCHAR vr;
+	L2INT state;
 	L2UCHAR sapi;
 	L2UCHAR tei;
+#ifdef BRI_PTMP_SUPPORT
+	L2USHORT ri;
+	Q921TeiState_t TeiState;
+#endif
 	Q921NetUser_t NetUser;
+	Q921NetType_t NetType;
 	L2ULONG T200;
 	L2ULONG T203;
 	L2ULONG T200Timeout;
 	L2ULONG T203Timeout;
+#ifdef BRI_PTMP_SUPPORT
+	L2ULONG T201;
+	L2ULONG T202;
+	L2ULONG T201Timeout;
+	L2ULONG T202Timeout;
+#endif
 	Q921TxCB_t Q921Tx21Proc;
 	Q921TxCB_t Q921Tx23Proc;
 	void *PrivateData21;
 	void *PrivateData23;
 	L2INT Q921HeaderSpace;
-
 };
 
 void Q921_InitTrunk(L2TRUNK trunk,
 					L2UCHAR sapi,
 					L2UCHAR tei,
 					Q921NetUser_t NetUser,
+					Q921NetType_t NetType,
 					L2INT hsize,
 					Q921TxCB_t cb21,
 					Q921TxCB_t cb23,
@@ -141,19 +175,81 @@
 void Q921T200TimerStart(L2TRUNK trunk);
 void Q921T200TimerStop(L2TRUNK trunk);
 void Q921T200TimerReset(L2TRUNK trunk);
+void Q921T200TimerExpire(L2TRUNK trunk);
+
+#ifdef BRI_PTMP_SUPPORT
+void Q921T201TimerStart(L2TRUNK trunk);
+void Q921T201TimerStop(L2TRUNK trunk);
+void Q921T201TimerReset(L2TRUNK trunk);
+void Q921T201TimerExpire(L2TRUNK trunk);
+
+void Q921T202TimerStart(L2TRUNK trunk);
+void Q921T202TimerStop(L2TRUNK trunk);
+void Q921T202TimerReset(L2TRUNK trunk);
+void Q921T202TimerExpire(L2TRUNK trunk);
+#endif
+
 void Q921T203TimerStart(L2TRUNK trunk);
 void Q921T203TimerStop(L2TRUNK trunk);
 void Q921T203TimerReset(L2TRUNK trunk);
-void Q921T200TimerExpire(L2TRUNK trunk);
 void Q921T203TimerExpire(L2TRUNK trunk);
+
 int Q921SendI(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR *mes, L2INT size);
+int Q921SendS(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR sv, L2UCHAR *mes, L2INT size);
+int Q921SendU(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR m, L2UCHAR *mes, L2INT size);
+
 int Q921SendRNR(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf);
 int Q921SendREJ(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf);
 int Q921SendSABME(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf);
 int Q921SendDM(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf);
 int Q921SendDISC(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf);
 int Q921SendUA(L2TRUNK trunk, int Sapi, int cr, int Tei, int pf);
+
 int Q921ProcSABME(L2TRUNK trunk, L2UCHAR *mes, L2INT size);
 
+/*
+ * Helper macros
+ */
+#define Q921_UFRAME_HEADER_SIZE		3
+#define Q921_UFRAME_DATA_OFFSET(tr)	((tr)->Q921HeaderSpace + Q921_UFRAME_HEADER_SIZE)
+
+#define Q921_SFRAME_HEADER_SIZE		4
+#define Q921_SFRAME_DATA_OFFSET(tr)	((tr)->Q921HeaderSpace + Q921_SFRAME_HEADER_SIZE)
+
+#define Q921_IFRAME_HEADER_SIZE		4
+#define Q921_IFRAME_DATA_OFFSET(tr)	((tr)->Q921HeaderSpace + Q921_IFRAME_HEADER_SIZE)
+
+/*
+ * dynamic TEI handling
+ */
+#ifdef BRI_PTMP_SUPPORT
+#define Q921_SAPI_TEI		63	/* SAPI for all TEI Messages */
+#define Q921_LAYER_ENT_ID_TEI	0x0f	/* UN Layer Management Entity ID for TEI Mgmt */
+
+#define Q921_TEI_BCAST		127
+#define Q921_TEI_MAX		Q921_TEI_BCAST
+
+#define Q921_TEI_DYN_MIN	64
+#define Q921_TEI_DYN_MAX	126
+
+#define Q921_IS_PTMP(x)		((x)->NetType == Q921_PTMP)
+#define Q921_IS_PTMP_TE(x)	((x)->NetType == Q921_PTMP && (x)->NetUser == Q921_TE)
+#define Q921_IS_PTMP_NT(x)	((x)->NetType == Q921_PTMP && (x)->NetUser == Q921_NT)
+
+#define Q921_IS_READY(tr)	(Q921_IS_PTMP_TE(tr) ? (tr)->TeiState == Q921_TEI_ASSIGNED : 1)
+
+typedef enum {
+	Q921_TEI_ID_REQUEST = 1,
+	Q921_TEI_ID_ASSIGNED,
+	Q921_TEI_ID_DENIED,
+	Q921_TEI_ID_CHECKREQ,
+	Q921_TEI_ID_CHECKRESP,
+	Q921_TEI_ID_REMOVE,
+	Q921_TEI_ID_VERIFY
+} Q921TeiMessageType_t;
+
+#else
+#define Q921_IS_READY(tr)		(1)
 #endif
 
+#endif
Index: src/isdn/include/Q931.h
===================================================================
--- src/isdn/include/Q931.h	(revision 495)
+++ src/isdn/include/Q931.h	(working copy)
@@ -506,6 +506,7 @@
     Q931_TrType_T1=1,				/*  1 : T1 Trunk                        */
     Q931_TrType_J1=2,				/*  2 : J1 Trunk                        */
     Q931_TrType_BRI=3				/*  3 : BRI Trunk                       */
+    Q931_TrType_BRI_PTMP=4			/*  3 : BRI Trunk                       */
 } Q931_TrunkType_t;
 
 typedef enum						/* Trunk State							*/
Index: src/isdn/Q931api.c
===================================================================
--- src/isdn/Q931api.c	(revision 495)
+++ src/isdn/Q931api.c	(working copy)
@@ -97,6 +97,7 @@
 		break;
 
 	case Q931_TrType_BRI:
+	case Q931_TrType_BRI_PTMP:
 		dchannel = 3;
 		maxchans = 3;
 		break;
Index: src/isdn/Q921.c
===================================================================
--- src/isdn/Q921.c	(revision 495)
+++ src/isdn/Q921.c	(working copy)
@@ -56,6 +56,7 @@
 					L2UCHAR sapi,
 					L2UCHAR tei,
 					Q921NetUser_t NetUser,
+					Q921NetType_t NetType,
 					L2INT hsize,
 					Q921TxCB_t cb21,
 					Q921TxCB_t cb23,
@@ -73,6 +74,7 @@
 	trunk->sapi = sapi;
 	trunk->tei = tei;
 	trunk->NetUser = NetUser;
+	trunk->NetType = NetType;
 	trunk->Q921Tx21Proc = cb21;
 	trunk->Q921Tx23Proc = cb23;
 	trunk->PrivateData21 = priv21;
@@ -80,8 +82,38 @@
 	trunk->Q921HeaderSpace = hsize;
 	trunk->T200 = 0;
 	trunk->T203 = 0;
-	trunk->T200Timeout = 1000;
-	trunk->T203Timeout = 10000;
+	trunk->T200Timeout = 1000;	/*  1 second */
+	trunk->T203Timeout = 10000;	/* 10 seconds */
+
+#ifdef BRI_PTMP_SUPPORT
+	trunk->T202 = 0;
+	trunk->T201 = 0;
+	trunk->T202Timeout = 2000;	/*   2 seconds */
+	trunk->T201Timeout = 200000;	/* 200 seconds */
+	trunk->TeiState = Q921_TEI_ASSIGNED;
+	trunk->ri = 0;
+
+	if(Q921_IS_PTMP_TE(trunk)) {
+		/*
+		 * Request new TEI from Master
+		 */
+		trunk->tei = 0;
+		trunk->TeiState = Q921_TEI_UNASSIGNED;
+
+		Q921TeiSendAssignRequest(trunk);
+	}
+
+	if(Q921_IS_PTMP_NT(trunk)) {
+		/*
+		 * Clear list of assigned TEIs, send TEI reset command
+		 */
+		trunk->tei = 0;
+
+//		memset(trunk->tei_list, 0, Q921_TEI_MAX);
+
+		Q921TeiSendRemoveRequest(trunk, Q921_TEI_BCAST);	/* Revoke all TEIs in use */
+	}
+#endif
 }
 
 int Q921Tx21Proc(L2TRUNK trunk, L2UCHAR *Msg, L2INT size)
@@ -123,6 +155,9 @@
     return tNow;
 }
 
+/*
+ * T200 handling
+ */
 void Q921T200TimerStart(L2TRUNK trunk)
 {
 	if (!trunk->T200) {
@@ -141,6 +176,9 @@
 	Q921T200TimerStart(trunk);
 }
 
+/*
+ * T203 handling
+ */
 void Q921T203TimerStart(L2TRUNK trunk)
 {
 	if (!trunk->T203) {
@@ -159,6 +197,54 @@
 	Q921T203TimerStart(trunk);
 }
 
+#ifdef BRI_PTMP_SUPPORT
+/*
+ * T202 handling (TEI message timeout)
+ */
+void Q921T202TimerStart(L2TRUNK trunk)
+{
+	if (!trunk->T202) {
+		trunk->T202 = Q921GetTime() + trunk->T202Timeout;
+	}
+}
+
+void Q921T202TimerStop(L2TRUNK trunk)
+{
+	trunk->T202 = 0;
+}
+
+void Q921T202TimerReset(L2TRUNK trunk)
+{
+	Q921T202TimerStop(trunk);
+	Q921T202TimerStart(trunk);
+}
+
+
+/*
+ * T201 handling (TEI management (NT side))
+ */
+void Q921T201TimerStart(L2TRUNK trunk)
+{
+	if (!trunk->T201) {
+		trunk->T201 = Q921GetTime() + trunk->T201Timeout;
+	}
+}
+
+void Q921T201TimerStop(L2TRUNK trunk)
+{
+	trunk->T201 = 0;
+}
+
+void Q921T201TimerReset(L2TRUNK trunk)
+{
+	Q921T201TimerStop(trunk);
+	Q921T201TimerStart(trunk);
+}
+#endif
+
+/*
+ * Expiry callbacks
+ */
 void Q921T200TimerExpire(L2TRUNK trunk)
 {
 	(void)trunk;
@@ -170,6 +256,31 @@
 	Q921SendRR(trunk, trunk->sapi, trunk->NetUser == Q921_TE ? 0 : 1, trunk->tei, 1);
 }
 
+#ifdef BRI_PTMP_SUPPORT
+void Q921T202TimerExpire(L2TRUNK trunk)
+{
+	Q921T202TimerReset(trunk);
+
+	/* todo: implement resend counter */
+
+	if(trunk->TeiState == Q921_TEI_ASSIGNED) {
+		/* Tei identity verify timeout */
+		Q921TeiSendVerifyRequest(trunk);
+	} else {
+		/* Tei assignment request timeout */
+		Q921TeiSendAssignRequest(trunk);
+	}
+}
+
+void Q921T201TimerExpire(L2TRUNK trunk)
+{
+	(void)trunk;
+}
+#endif
+
+/*
+ * Timer Tick function
+ */
 void Q921TimerTick(L2TRUNK trunk)
 {
 	L2ULONG tNow = Q921GetTime();
@@ -179,9 +290,17 @@
 	if (trunk->T203 && tNow > trunk->T203) {
 		Q921T203TimerExpire(trunk);		
 	}
+
+#ifdef BRI_PTMP_SUPPORT
+	if (trunk->T201 && tNow > trunk->T201) {
+		Q921T201TimerExpire(trunk);
+	}
+	if (trunk->T202 && tNow > trunk->T202) {
+		Q921T202TimerExpire(trunk);
+	}
+#endif
 }
 
-
 void Q921SetGetTimeCB(L2ULONG (*callback)(void))
 {
     Q921GetTimeProc = callback;
@@ -232,15 +351,54 @@
 *****************************************************************************/
 int Q921SendI(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR *mes, L2INT size)
 {
+#ifdef BRI_PTMP_SUPPORT
+	if(Q921_IS_PTMP_TE(trunk) && trunk->TeiState == Q921_TEI_UNASSIGNED) {
+		/* don't even bother trying */
+		return 0;
+	}
+#endif
+
     mes[trunk->Q921HeaderSpace+0] = (Sapi&0xfc) | ((cr<<1)&0x02);
     mes[trunk->Q921HeaderSpace+1] = (Tei<<1) | 0x01;
     mes[trunk->Q921HeaderSpace+2] = trunk->vs<<1;
     mes[trunk->Q921HeaderSpace+3] = (trunk->vr<<1) | (pf & 0x01);
     trunk->vs++;
 
+//  mes[trunk->Q921HeaderSpace+2] = trunk->vs[Tei]<<1;
+//  mes[trunk->Q921HeaderSpace+3] = (trunk->vr[Tei]<<1) | (pf & 0x01);
+//  trunk->vs[Tei]++;
+
     return Q921Tx21Proc(trunk, mes, size);
 }
 
+int Q921SendS(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCAHR sv, L2UCHAR *mes, L2INT size)
+{
+#ifdef BRI_PTMP_SUPPORT
+	if(Q921_IS_PTMP_TE(trunk) && trunk->TeiState == Q921_TEI_UNASSIGNED) {
+		/* don't even bother trying */
+		return 0;
+	}
+#endif
+
+	mes[trunk->Q921HeaderSpace+0] = (Sapi&0xfc) | ((cr<<1)&0x02);
+	mes[trunk->Q921HeaderSpace+1] = (Tei<<1) | 0x01;
+	mes[trunk->Q921HeaderSpace+2] = ((sv<<2)&0x0c) | 0x01;
+	mes[trunk->Q921HeaderSpace+3] = (trunk->vr<<1) | (pf & 0x01);
+
+//	mes[trunk->Q921HeaderSpace+3] = (trunk->vr[Tei]<<1) | (pf & 0x01);
+
+	return Q921Tx21Proc(trunk, mes, size);
+}
+
+int Q921SendU(L2TRUNK trunk, L2UCHAR Sapi, char cr, L2UCHAR Tei, char pf, L2UCHAR m, L2UCHAR *mes, L2INT size)
+{
+	mes[trunk->Q921HeaderSpace+0] = (Sapi&0xfc) | ((cr<<1)&0x02);
+	mes[trunk->Q921HeaderSpace+1] = (Tei<<1) | 0x01;
+	mes[trunk->Q921HeaderSpace+2] = ((m<<3)&0xe0) | ((pf<<4)&0x10) | ((m<<2)&0x0c) | 0x3;
+
+	return Q921Tx21Proc(trunk, mes, size);
+}
+
 int Q921Rx32(L2TRUNK trunk, L2UCHAR * Mes, L2INT Size)
 {
 	return Q921SendI(trunk, 
@@ -277,6 +435,7 @@
     mes[trunk->Q921HeaderSpace+3] = (L2UCHAR)((trunk->vr<<1) | (pf & 0x01));
 
     return Q921Tx21Proc(trunk, mes, trunk->Q921HeaderSpace+4);
+//  return Q921SendS(trunk, Sapi, cr, Tei, pf, 0x00, mes, trunk->Q921HeaderSpace+4);
 }
 
 /*****************************************************************************
@@ -304,6 +463,7 @@
     mes[trunk->Q921HeaderSpace+3] = (L2UCHAR)((trunk->vr<<1) | (pf & 0x01));
 
     return Q921Tx21Proc(trunk, mes, trunk->Q921HeaderSpace+4);
+//  return Q921SendS(trunk, Sapi, cr, Tei, pf, 0x01, mes, trunk->Q921HeaderSpace+4);
 }
 
 /*****************************************************************************
@@ -331,6 +491,7 @@
     mes[trunk->Q921HeaderSpace+3] = (L2UCHAR)((trunk->vr<<1) | (pf & 0x01));
 
     return Q921Tx21Proc(trunk, mes, trunk->Q921HeaderSpace+4);
+//  return Q921SendS(trunk, Sapi, cr, Tei, pf, 0x03, mes, trunk->Q921HeaderSpace+4);
 }
 
 /*****************************************************************************
@@ -357,6 +518,7 @@
     mes[trunk->Q921HeaderSpace+2] = (L2UCHAR)(0x6f | ((pf<<4)&0x10));
 
     return Q921Tx21Proc(trunk, mes, trunk->Q921HeaderSpace+3);
+//  return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x0f, mes, trunk->Q921HeaderSpace+3);
 }
 
 int Q921Start(L2TRUNK trunk)
@@ -372,7 +534,7 @@
 
   Function:     Q921SendDM
 
-  Description:  Comose and Send DM (Disconnected Mode)
+  Description:  Compose and Send DM (Disconnected Mode)
 
   Parameters:   trunk       trunk #
                 Sapi        Sapi
@@ -392,6 +554,7 @@
     mes[trunk->Q921HeaderSpace+2] = (L2UCHAR)(0x0f | ((pf<<4)&0x10));
 
     return Q921Tx21Proc(trunk, mes, trunk->Q921HeaderSpace+3);
+//  return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x03, mes, trunk->Q921HeaderSpace+3);
 }
 
 /*****************************************************************************
@@ -418,6 +581,7 @@
     mes[trunk->Q921HeaderSpace+2] = (L2UCHAR)(0x43 | ((pf<<4)&0x10));
 
     return Q921Tx21Proc(trunk, mes, trunk->Q921HeaderSpace+3);
+//  return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x08, mes, trunk->Q921HeaderSpace+3);
 }
 
 /*****************************************************************************
@@ -444,6 +608,7 @@
     mes[trunk->Q921HeaderSpace+2] = (L2UCHAR)(0x63 | ((pf<<4)&0x10));
 
     return Q921Tx21Proc(trunk, mes, trunk->Q921HeaderSpace+3);
+//  return Q921SendU(trunk, Sapi, cr, Tei, pf, 0x0c, mes, trunk->Q921HeaderSpace+3);
 }
 
 int Q921ProcSABME(L2TRUNK trunk, L2UCHAR *mes, L2INT size)
@@ -452,11 +617,11 @@
 	(void)mes;
 	(void)size;
 
-    trunk->vr=0;
-    trunk->vs=0;
+	trunk->vr=0;
+	trunk->vs=0;
 	trunk->va=0;
 
-    return 1;
+	return 1;
 }
 
 /*****************************************************************************
@@ -479,80 +644,500 @@
 *****************************************************************************/
 int Q921Rx12(L2TRUNK trunk)
 {
-    L2UCHAR *mes;
-    L2INT rs,size;     /* receive size & Q921 frame size*/
-    L2UCHAR *smes = MFIFOGetMesPtr(trunk->HDLCInQueue, &size);
-	L2UCHAR nr;
+	L2INT size;     /* receive size & Q921 frame size*/
+	L2UCHAR *smes = MFIFOGetMesPtr(trunk->HDLCInQueue, &size);
 
-    if(smes != NULL)
-    {
-        rs = size - trunk->Q921HeaderSpace;
-        mes = &smes[trunk->Q921HeaderSpace];
+	if(smes)
+	{
+		L2UCHAR sapi, tei, cr;
+		L2UCHAR *mes;
+		L2INT rs;
 
-		if ((mes[2] & 3) != 3) {
+		rs  = size - trunk->Q921HeaderSpace;
+		mes = &smes[trunk->Q921HeaderSpace];
+
+		/* common fields: get sapi, tei and cr */
+		sapi = (mes[0] & 0xfc) >> 2;
+		cr   = (mes[0] & 0x02) >> 1;
+		tei  = (mes[1] & 0xfe) >> 1;
+
+#ifdef BRI_PTMP_SUPPORT
+		if(Q921_IS_PTMP_TE(trunk) && (
+			 (trunk->TeiState == Q921_TEI_ASSIGNED && tei != trunk->tei && tei != Q921_TEI_BCAST) ||		/* Assigned TEI: Only BCAST and directed */
+			 (trunk->TeiState == Q921_TEI_UNASSIGNED && tei == Q921_TEI_BCAST)))					/* No assigned TEI: Only BCAST */
+		{
+			/* Ignore Messages with foreign TEIs */
+			goto out;
+		}
+#endif
+
+		/* counter handling */
+		if ((mes[2] & 0x03) != 0x03) {
+			L2UCHAR nr;
+
+#ifdef BRI_PTMP_SUPPORT
+			if(!Q921_IS_READY(trunk)) {
+				/* Don't handle S and I frames if L2 is not ready (PTMP TE mode only) (???) */
+				goto out;
+			}
+#endif
+
 			/* we have an S or I frame */
 			/* if nr is between va and vs, update our va counter */
 			nr = (mes[3] >> 1);
 			if (nr >= trunk->va && nr <= trunk->vs) {
+//			if (nr >= trunk->va[tei] && nr <= trunk->vs[tei]) {
 				trunk->va = nr;
+//				trunk->va[tei] = nr;
 			}
 		}
 
-        /* check for I frame */
-        if((mes[2] & 0x01) == 0)
-        {
+		/* check for I frame */
+		if((mes[2] & 0x01) == 0x00)
+		{
+			L2UCHAR pf = mes[3] & 0x01;	/* poll / final flag */
+			L2UCHAR nr = mes[3] >> 1;	/* receive sequence number */
+			L2UCHAR ns = mes[2] >> 1;	/* send sequence number */
 
 			/* we increment before we "really" know its good so that if we send in the callback, we use the right nr */
 			trunk->vr++;
-            if(Q921Tx23Proc(trunk, smes, size-2)) /* -2 to clip away CRC */
-            {
-				Q921SendRR(trunk, (mes[0]&0xfc)>>2, (mes[0]>>1)&0x01, mes[1]>>1, mes[3]&0x01);
+//			trunk->vr[tei]++;
+
+			if(Q921Tx23Proc(trunk, smes, size-2)) /* -2 to clip away CRC */
+			{
+				Q921SendRR(trunk, sapi, cr, tei, pf);
 			}
-            else
-            {
-                /* todo: whatever*/
-				/* trunk->vr--; */
+			else
+			{
+	                	/* todo: whatever */
+				/* trunk->vr[tei]--; */
 				/* for now, lets just respond for easier debugging on errors */
-				Q921SendRR(trunk, (mes[0]&0xfc)>>2, (mes[0]>>1)&0x01, mes[1]>>1, mes[3]&0x01);
-           }
-        }
+				Q921SendRR(trunk, sapi, cr, tei, pf);
+			}
+		}
+	
+		/* check for S frame */
+		else if(mes[2] & 0x03 == 0x01)
+		{
+			L2UCHAR sv = (mes[2] & 0x0c) >> 2;	/* supervisory format id */
+			L2UCHAR pf = mes[3] & 0x01;		/* poll / final flag */
+			L2UCHAR nr = mes[3] >> 1;		/* receive sequence number */
 
-        /* check for RR */
-        else if(mes[2] ==0x01)
-		{
-			if (((mes[0]>>1)&0x01) == (trunk->NetUser == Q921_TE ? 1 : 0)) { /* if this is a command */
-				Q921SendRR(trunk, (mes[0]&0xfc)>>2, (mes[0]>>1)&0x01, mes[1]>>1, mes[2]&0x01);
+			switch(sv) {
+			case 0x00:	/* RR : Receive Ready */
+				if ((cr == (trunk->NetUser == Q921_TE ? 1 : 0)) { /* if this is a command */
+					Q921SendRR(trunk, sapi, cr, tei, pf);
+				}
+				break;
+
+			case 0x02:	/* RNR : Receive Not Ready */
+				break;
+
+			case 0x04:	/* REJ : Reject */
+				break;
+
+			default:	/* Invalid / Unknown */
+				break;
 			}
+		}
 
-        }
-		
-        /* check for RNR */
-        /* check for REJ */
-        /* check for SABME */
-        else if((mes[2] & 0xef) == 0x6f)
-        {
-            Q921ProcSABME(trunk, mes, rs);
-            Q921SendUA(trunk, (mes[0]&0xfc)>>2, (mes[0]>>1)&0x01,mes[1]>>1, (mes[2]&0x10)>>4);
-        }
+		/* check for U frame */
+		else if(mes[2] & 0x03 == 0x03)
+		{
+			L2UCHAR m  = (mes[2] & 0xe0) >> 3 | (mes[2] & 0x0c) >> 2;	/* modifier function id */
+			L2UCHAR pf = (mes[2] & 0x10) >> 4;				/* poll / final flag */
 
-        /* check for DM */
-        /* check for UI */
-        /* check for DISC */
-        /* check for UA */
-        /* check for FRMR */
-        /* check for XID */
+			switch(m) {
+			case 0x00:	/* UN : Unnumbered Information */
+#ifdef BRI_PTMP_SUPPORT
+				if(mes[3] == Q921_LAYER_ENT_ID_TEI)
+				{
+					if(!Q921_IS_PTMP(trunk)) {
+						/* wtf? nice try */
+						goto out;
+					}
 
-        else
-        {
-            /* what the ? Issue an error */
+					switch(mes[6]) {
+					case Q921_TEI_ID_REQUEST:	/* NT */
+						Q921TeiProcAssignRequest(trunk, mes, size);
+						break;
+
+					case Q921_TEI_ID_ASSIGNED:	/* TE */
+					case Q921_TEI_ID_DENIED:
+						Q921TeiProcAssignResponse(trunk, mes, size);
+						break;
+
+					case Q921_TEI_ID_CECKREQ:	/* TE */
+						Q921TeiProcCheckRequest(trunk, mes, size);
+						break;
+
+					case Q921_TEI_ID_CECKRESP:	/* NT */
+						Q921TeiProcCheckResponse(trunk, mes, size);
+						break;
+
+					case Q921_TEI_ID_REMOVE:	/* TE */
+						Q921TeiProcRemoveRequest(trunk, mes, size);
+						break;
+
+					case Q921_TEI_ID_VERIFY:	/* TE */
+						Q921TeiProcVerifyRequest(trunk, mes, size);
+						break;
+
+					default:			/* Invalid / Unknown */
+						break;
+					}
+				}
+#endif
+				break;
+
+			case 0x03:	/* DM : Disconnect Mode */
+				break;
+
+			case 0x08:	/* DISC : Disconnect */
+				break;
+
+			case 0x0c:	/* UA : Unnumbered Acknowledgement */
+				break;
+
+			case 0x0f:	/* SABME  : Set Asynchronous Balanced Mode Extend */
+				Q921ProcSABME(trunk, mes, rs);
+				Q921SendUA(trunk, sapi, cr, tei, pf);
+				break;
+
+			case 0x11:	/* FRMR : Frame Reject */
+				break;
+
+			case 0x17:	/* XID : Exchange Identification */
+				break;
+
+			default:	/* Unknown / Invalid */
+				break;
+			}
+		}
+
+		/* unknown / invalid */
+		else
+		{
+			/* what the ? Issue an error */
 			/* Q921ErrorProc(trunk, Q921_UNKNOWNFRAME, mes, rs); */
-            /* todo: REJ or FRMR */
-        }
+			/* todo: REJ or FRMR */
+		}
 
-        MFIFOKillNext(trunk->HDLCInQueue);
+out:
+		MFIFOKillNext(trunk->HDLCInQueue);
 
-        return 1;
-    }
-    return 0;
+		return 1;
+	}
+
+	return 0;
 }
 
+
+#ifdef BRI_PTMP_SUPPORT
+/*
+ * TEI Management functions
+ */
+
+/*
+ * Ask for new TEI (TE mode only)
+ */
+static int Q921TeiSendAssignRequest(L2TRUNK trunk)
+{
+	L2UCHAR mes[25];
+	L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk);
+
+	if (!Q921_IS_PTMP_TE(trunk)) {
+		/* only ptmp te mode*/
+		return 0;
+	}
+
+	/* Request running? */
+	if (trunk->T202) {
+		return 0;
+	}
+
+	trunk->ri = (L2USHORT)(random() % 0xffff);
+
+	mes[offset + 0] = Q921_LAYER_ENT_ID_TEI;	/* layer management entity identifier */
+	mes[offset + 1] = (trunk->ri & 0xff00) >> 8;	/* reference number upper part */
+	mes[offset + 2] = trunk->ri & 0xff;		/* reference number lower part */
+	mes[offset + 3] = Q921_TEI_ID_REQUEST;		/* message type: Identity Request */
+	mes[offset + 4] = 127 << 1;			/* action indicator */
+
+	/* start T202 */
+	Q921T202Start(trunk);
+
+	return Q921SendU(trunk, Q921_SAPI_TEI, 1, Q921_TEI_BCAST, 0, 0x00, mes, Q921_UFRAME_DATA_OFFSET(trunk) + 5);	/* TEI Assignment Request UN Frame */
+}
+
+
+/*
+ * Process assign response (TE mode only)
+ */
+static int Q921TeiProcAssignResponse(L2TRUNK trunk, L2UCHAR *mes, L2INT size)
+{
+	L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk);
+	L2USHORT ri = 0;
+
+	if (!Q921_IS_PTMP_TE(trunk)) {
+		/* does not make sense */
+		return 0;
+	}
+
+	ri = (mes[offset + 1] << 8) | mes[offset + 2];
+
+	if (ri != trunk->ri) {
+		/* hmmm ..., not our response i guess */
+		return 0;
+	}
+
+	switch(mes[offset + 3]) {
+	case Q921_TEI_ID_ASSIGNED:
+		/* Yay, use the new TEI and change state to assigned */
+		trunk->tei      = mes[offset + 4] >> 1;
+		trunk->TeiState = Q921_TEI_ASSIGNED;
+		break;
+
+	case Q921_TEI_ID_DENIED:
+		/* oops, what to do now? */
+		if ((mes[offset + 4] >> 1) == 127) {
+			/* No more free TEIs? Be a bastard and force a reclaim! */
+		} else {
+			/* other reason, this is fatal, shutdown link */
+		}
+		break;
+
+	default:
+		return 0;
+	}
+
+	/* stop T202 */
+	Q921T202Stop(trunk);
+
+	return 1;
+}
+
+
+/*
+ * Verify TEI (TE mode only)
+ */
+static int Q921TeiSendVerifyRequest(L2TRUNK trunk)
+{
+	L2UCHAR mes[25];
+	L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk);
+
+	if (!Q921_IS_PTMP_TE(trunk)) {
+		/* only ptmp te mode*/
+		return 0;
+	}
+
+	/* Request running? */
+	if (trunk->T202) {
+		return 0;
+	}
+
+	mes[offset + 0] = Q921_LAYER_ENT_ID_TEI;	/* layer management entity identifier */
+	mes[offset + 1] = (trunk->ri & 0xff00) >> 8;	/* reference number upper part */
+	mes[offset + 2] = trunk->ri & 0xff;		/* reference number lower part */
+	mes[offset + 3] = Q921_TEI_ID_VERIFY;		/* message type: Identity Request */
+	mes[offset + 4] = trunk->tei << 1;		/* action indicator: our TEI */
+
+	/* start T202 */
+	Q921T202Start(trunk);
+
+	return Q921SendU(trunk, Q921_SAPI_TEI, 1, Q921_TEI_BCAST, 0, 0x00, mes, Q921_UFRAME_DATA_OFFSET(trunk) + 5);	/* TEI Assignment Request UN Frame */
+}
+
+
+/*
+ * Process Check Request (TE mode only)
+ */
+static int Q921TeiProcCheckRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size)
+{
+	L2UCHAR resp[25];
+	L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk);
+
+	if (!Q921_IS_PTMP_TE(trunk)) {
+		/* ptmp te mode only */
+		return 0;
+	}
+
+	if ((mes[offset + 4] >> 1) == Q921_TEI_BCAST || (mes[offset + 4] >> 1) == trunk->tei)
+	{
+		/*
+		 * Broadcast TEI check or for our assigned TEI
+		 */
+		trunk->ri = (L2USHORT)(random() % 0xffff);
+
+		resp[offset + 0] = Q921_LAYER_ENT_ID_TEI;	/* layer management entity identifier */
+		resp[offset + 1] = (trunk->ri & 0xff00) >> 8;	/* reference number upper part */
+		resp[offset + 2] = trunk->ri & 0xff;		/* reference number lower part */
+		resp[offset + 3] = Q921_TEI_ID_CHECKRESP;	/* message type: Check Response */
+		resp[offset + 4] = trunk->tei << 1;		/* action indicator: our TEI */
+
+		return Q921SendU(trunk, Q921_SAPI_TEI, 1, Q921_TEI_BCAST, 0, 0x00, resp, Q921_UFRAME_DATA_OFFSET(trunk) + 5);	/* TEI Check Response UN Frame */
+	}
+
+	return 0;
+}
+
+
+/*
+ * Process remove Request (TE mode only)
+ */
+static int Q921TeiProcRemoveRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size)
+{
+	L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk);
+
+	if (!Q921_IS_PTMP_TE(trunk)) {
+		/* ptmp te mode only */
+		return 0;
+	}
+
+	if ((mes[offset + 4] >> 1) == Q921_TEI_BCAST || (mes[offset + 4] >> 1) == trunk->tei)
+	{
+		/*
+		 * Broadcast TEI remove or for our assigned TEI
+		 */
+		trunk->tei = 0;
+		trunk->TeiState = Q921_TEI_UNASSIGNED;
+
+		/* hmm, request new one ? */
+		return Q921TeiSendAssignRequest(trunk);
+	}
+
+	return 0;
+}
+
+
+/*
+ * Process assign request from peer (NT mode only)
+ */
+static int Q921TeiProcAssignRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size)
+{
+	if (!Q921_IS_PTMP_NT(trunk)) {
+		/* does not make sense */
+		return 0;
+	}
+
+	/* todo: process message */
+
+	return 1;
+}
+
+/*
+ * Send check request (NT mode only)
+ */
+static int Q921TeiSendCheckRequest(L2TRUNK trunk, L2UCHAR tei)
+{
+	L2UCHAR mes[25];
+	L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk);
+
+	if (!Q921_IS_PTMP_NT(trunk)) {
+		/* does not make sense */
+		return 0;
+	}
+
+	mes[offset + 0] = Q921_LAYER_ENT_ID_TEI;	/* layer management entity identifier */
+	mes[offset + 1] = 0;				/* reference number upper part */
+	mes[offset + 2] = 0;				/* reference number lower part */
+	mes[offset + 3] = Q921_TEI_ID_CHECKREQ;		/* message type: Check Request */
+	mes[offset + 4] = trunk->tei << 1;		/* action indicator: the TEI to check */
+
+	/* Start NT timer, todo: this one is probably per TEI too */
+	Q921T201Start(trunk);
+
+	return Q921SendU(trunk, Q921_SAPI_TEI, 1, Q921_TEI_BCAST, 0, 0x00, mes, Q921_UFRAME_DATA_OFFSET(trunk) + 5);	/* TEI Check Request UN Frame */
+}
+
+/*
+ * Process Check Response (NT mode only)
+ */
+static int Q921TeiProcCheckResponse(L2TRUNK trunk, L2UCHAR *mes, L2INT size)
+{
+	L2UCHAR resp[25];
+	L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk);
+	L2USHORT ri = 0;
+	L2UCHAR tei = 0;
+
+	if (!Q921_IS_PTMP_NT(trunk)) {
+		/* ptmp te mode only */
+		return 0;
+	}
+
+	ri  = (mes[offset + 1] << 8) | mes[offset + 2];
+	tei = mes[offset + 4] >> 1;
+
+	/* todo: handle response... check assigned TEI(s) */
+
+	return 0;
+}
+
+
+/*
+ * Process Verify Request (NT mode only)
+ */
+static int Q921TeiProcVerifyRequest(L2TRUNK trunk, L2UCHAR *mes, L2INT size)
+{
+	L2UCHAR resp[25];
+	L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk);
+	L2UCHAR tei = 0;
+
+	if (!Q921_IS_PTMP_NT(trunk)) {
+		/* ptmp te mode only */
+		return 0;
+	}
+
+	tei = mes[offset + 4] >> 1;
+
+	/* todo: handle response... verify assigned TEI */
+
+	return 0;
+}
+
+/*
+ * Send Deny Response (NT mode only)
+ */
+static int Q921TeiSendDenyResponse(L2TRUNK trunk, L2UCHAR tei, L2USHORT ri)
+{
+	L2UCHAR mes[25];
+	L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk);
+
+	if (!Q921_IS_PTMP_NT(trunk)) {
+		/* does not make sense */
+		return 0;
+	}
+
+	mes[offset + 0] = Q921_LAYER_ENT_ID_TEI;	/* layer management entity identifier */
+	mes[offset + 1] = (trunk->ri & 0xff00) >> 8;	/* reference number upper part */
+	mes[offset + 2] = trunk->ri & 0xff;		/* reference number lower part */
+	mes[offset + 3] = Q921_TEI_ID_DENIED;		/* message type: Deny Response */
+	mes[offset + 4] = tei << 1;			/* action indicator: the TEI to deny */
+
+	return Q921SendU(trunk, Q921_SAPI_TEI, 1, Q921_TEI_BCAST, 0, 0x00, mes, Q921_UFRAME_DATA_OFFSET(trunk) + 5);	/* TEI Assignment Deny Response UN Frame */
+}
+
+
+/*
+ * Send Assigned Response (NT mode only)
+ */
+static int Q921TeiSendAssignedResponse(L2TRUNK trunk, L2UCHAR tei, L2USHORT ri)
+{
+	L2UCHAR mes[25];
+	L2UCHAR offset = Q921_UFRAME_DATA_OFFSET(trunk);
+
+	if (!Q921_IS_PTMP_NT(trunk)) {
+		/* does not make sense */
+		return 0;
+	}
+
+	mes[offset + 0] = Q921_LAYER_ENT_ID_TEI;	/* layer management entity identifier */
+	mes[offset + 1] = (trunk->ri & 0xff00) >> 8;	/* reference number upper part */
+	mes[offset + 2] = trunk->ri & 0xff;		/* reference number lower part */
+	mes[offset + 3] = Q921_TEI_ID_ASSIGNED;		/* message type: Assigned Response */
+	mes[offset + 4] = tei << 1;			/* action indicator: the TEI to assign */
+
+	return Q921SendU(trunk, Q921_SAPI_TEI, 1, Q921_TEI_BCAST, 0, 0x00, mes, Q921_UFRAME_DATA_OFFSET(trunk) + 5);	/* TEI Assigned Response UN Frame */
+}
+
+#endif
Index: src/include/zap_types.h
===================================================================
--- src/include/zap_types.h	(revision 495)
+++ src/include/zap_types.h	(working copy)
@@ -117,11 +117,12 @@
 	ZAP_TRUNK_T1,
 	ZAP_TRUNK_J1,
 	ZAP_TRUNK_BRI,
+	ZAP_TRUNK_BRI_PTMP,
 	ZAP_TRUNK_FXO,
 	ZAP_TRUNK_FXS,
 	ZAP_TRUNK_NONE
 } zap_trunk_type_t;
-#define TRUNK_STRINGS "E1", "T1", "J1", "BRI", "FXO", "FXS", "NONE"
+#define TRUNK_STRINGS "E1", "T1", "J1", "BRI", "BRI_PTMP", "FXO", "FXS", "NONE"
 ZAP_STR2ENUM_P(zap_str2zap_trunk_type, zap_trunk_type2str, zap_trunk_type_t)
 
 typedef enum {
