mirror of
				https://github.com/RangeNetworks/openbts.git
				synced 2025-11-03 21:33:15 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			622 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			622 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
* Copyright 2011, 2014 Range Networks, Inc.
 | 
						|
*
 | 
						|
* This software is distributed under multiple licenses;
 | 
						|
* see the COPYING file in the main directory for licensing
 | 
						|
* information for this specific distribution.
 | 
						|
*
 | 
						|
* This use of this software may be subject to additional restrictions.
 | 
						|
* See the LEGAL file in the main directory for details.
 | 
						|
 | 
						|
    This program is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
						|
*/
 | 
						|
 | 
						|
/**@file LLC objects, from GSM 04.64. */
 | 
						|
//#include "GPRSInternal.h"	// for LLCWARN
 | 
						|
 | 
						|
#ifndef LLC_H
 | 
						|
#define LLC_H
 | 
						|
 | 
						|
#include <ByteVector.h>
 | 
						|
#include "SgsnBase.h"
 | 
						|
#include "GPRSL3Messages.h"
 | 
						|
#include <MemoryLeak.h>
 | 
						|
//#include "TBF.h"
 | 
						|
 | 
						|
namespace GPRS { class MSInfo; }
 | 
						|
 | 
						|
namespace SGSN {
 | 
						|
struct LlcEntity;
 | 
						|
class SgsnInfo;
 | 
						|
struct Sgsn;
 | 
						|
class PdpContext;
 | 
						|
struct Sndcp;
 | 
						|
 | 
						|
// GSM04.64 6.2.3 table 2.
 | 
						|
struct LlcSapi {
 | 
						|
	enum type {
 | 
						|
		GPRSMM = 1,		// receives all control messages, both GMM an SM protocols.
 | 
						|
		TOM2 = 2,
 | 
						|
		UserData3 = 3,
 | 
						|
		UserData5 = 5,
 | 
						|
		SMS = 7,
 | 
						|
		TOM8 = 8,
 | 
						|
		UserData9 = 9,
 | 
						|
		UserData11 = 11,
 | 
						|
	};
 | 
						|
	static const char *name(type sapi);
 | 
						|
};
 | 
						|
 | 
						|
struct LLCFormat {
 | 
						|
	enum type {
 | 
						|
		Invalid, I, S, U, UI, ISack, SSack
 | 
						|
	};
 | 
						|
	static const char *name(type format);
 | 
						|
};
 | 
						|
 | 
						|
// (pat) This is a generic parity generator for up to 32 bit parity.
 | 
						|
 | 
						|
// Pats Notes: The generator-based algorithm in BitVector.h did not work
 | 
						|
// for the LLC FCS when I tried to pre-invert the remainder.
 | 
						|
// Here is a new one based on table lookups, which is better for ByteVectors.
 | 
						|
// An N-bit CRC is always N+1 bits long where the first and last bits are 1.
 | 
						|
// The CRC is the remainder of division of the input by the generator.
 | 
						|
// The complicated description of the LLC FCS is describing a normal 24-bit CRC
 | 
						|
// but they are setting the remainder to all ones beforehand (to catch the input
 | 
						|
// error of leading 0s) and inverting it after (to catch the input error of trailing 0s.)
 | 
						|
// Algorithm here is based on LSB-first algorithm from wikipedia "Computation of CRC"
 | 
						|
// So we have to pre-reverse the bits of the CRC generator.
 | 
						|
// The top bit of the CRC sets the shift-register output to 0 for the division,
 | 
						|
// so the division result comes out 0, but those bits are not relevant to the CRC,
 | 
						|
// which is the remainder, because they are all shifted away.
 | 
						|
// So we chop the top bit off.
 | 
						|
class Parity32
 | 
						|
{
 | 
						|
	uint32_t mTab[256];	// Precomputed crc remainders.
 | 
						|
	uint32_t mInvertedGenerator;
 | 
						|
	uint32_t mInitialRemainder;
 | 
						|
	uint32_t mMask;
 | 
						|
	public:
 | 
						|
	Parity32(uint32_t generator, unsigned width, bool invertFirst);
 | 
						|
	uint32_t computeCrc(unsigned char *str, int len);
 | 
						|
	uint32_t computeCrc(ByteVector &bv);
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
// 04.64 5.5 FCS [Frame Check Sequence] field, aka parity.
 | 
						|
// The CRC shall be the ones complement of the sum (modulo 2) of:
 | 
						|
// 		the remainder of xk (x23 + x22 + x21 +... + x2 + x + 1) divided (modulo 2) by
 | 
						|
//		the generator polynomial, where k is the number of bits of the dividend;
 | 
						|
// 	plus the remainder of the division (modulo 2) by the generator polynomial
 | 
						|
// 		of the product of x24 by the dividend.
 | 
						|
// The CRC-24 generator polynomial is:
 | 
						|
// G(x) = x24 + x23 + x21 + x20 + x19 + x17 + x16 + x15 + x13 + x8 + x7 + x5 + x4 + x2 + 1
 | 
						|
class LlcParity : public Parity32
 | 
						|
{
 | 
						|
	static const uint32_t sFCSGenerator =
 | 
						|
		(1<<24) + (1<<23) + (1<<21) + (1<<20) + (1<<19) + (1<<17) + (1<<16) +
 | 
						|
		(1<<15) + (1<<13) + (1<<8) + (1<<7) + (1<<5) + (1<<4) + (1<<2) + 1;
 | 
						|
	public:
 | 
						|
	LlcParity() : Parity32(sFCSGenerator,24,true) {};
 | 
						|
	void appendFCS(ByteVector &bv);
 | 
						|
	bool checkFCS(ByteVector &bv);	// true if parity ok.
 | 
						|
};
 | 
						|
extern LlcParity gLlcParity;
 | 
						|
 | 
						|
struct LlcDefs {
 | 
						|
	// sec 6.4 LLC commands for U-format frames, passed in LlcFrame::mM above.
 | 
						|
	enum U_M_Commands {	// Some are commands and some are responses.
 | 
						|
		UCMD_NULL = 0,		// null command
 | 
						|
		UCMD_DM = 1, 	// DM response: Disconnected Mode Response.
 | 
						|
						// "An LLE shall transmit a DM response to any valid command
 | 
						|
						// received that it cannot action."
 | 
						|
						// However, I think the multitech modem sends this as a command.
 | 
						|
		UCMD_DISC = 4, 	// DISC command: Disconnect - terminate ABM mode
 | 
						|
		UCMD_UA = 6, 	// UA response: acknowledge SABM or DISC command
 | 
						|
		UCMD_SABM = 7,	// SABM command: Set Async Balanced (aka acknowledged) Mode
 | 
						|
		UCMD_FRMR = 8,	// FRMR response: Frame Reject response - includes a bunch of info; see spec.
 | 
						|
		UCMD_XID = 0xb // XID command or response: Exchange Identification - used to set parameters.
 | 
						|
	};
 | 
						|
	// sec 6.4 LLC commands for S-format frames, passed in LlcFrame::mS above.
 | 
						|
	enum U_S_Commands { 
 | 
						|
		SCMD_RR = 0,
 | 
						|
		SCMD_ACK = 1,
 | 
						|
		SCMD_RNR = 2,
 | 
						|
		SCMD_SACK = 3
 | 
						|
	};
 | 
						|
};
 | 
						|
 | 
						|
struct LlcMsg {
 | 
						|
	virtual void llcProcess(LlcEntity *lle) = 0;
 | 
						|
	virtual const char *typeName() { return "generic"; }
 | 
						|
};
 | 
						|
 | 
						|
// Note: The frame formats are in 04.64 6.3.
 | 
						|
struct LlcFrame : public LlcDefs, public ByteVector
 | 
						|
{
 | 
						|
	static const unsigned addrOffset = 0;
 | 
						|
	static const unsigned controlOffset = 1;
 | 
						|
	static const unsigned UIHeaderLength = 3;	// 1 byte for addr, 2 for header.
 | 
						|
 | 
						|
	// Address fields:
 | 
						|
	unsigned getSapi() { return getByte(addrOffset) & 0xf; }
 | 
						|
	bool getLlcPD() { return getBitR1(addrOffset,8); }	// 1 means LLC frame.  Good grief.
 | 
						|
	bool getCR() { return getBitR1(addrOffset,7); }
 | 
						|
	LLCFormat::type getFormat();	// get the format from the ByteVector.
 | 
						|
 | 
						|
	LlcFrame(const ByteVector &vec) : ByteVector(vec) { }
 | 
						|
	LlcFrame(unsigned size) : ByteVector(size) { }
 | 
						|
 | 
						|
	//LlcFrame(const LlcFrame &other) { *this = other; }
 | 
						|
 | 
						|
	// Return nth control byte.  0 is the first byte after the address byte,
 | 
						|
	// ie, LLC header byte 1.
 | 
						|
	unsigned getControl(unsigned nth) {
 | 
						|
		unsigned w = nth + controlOffset;
 | 
						|
		return size() <= w ? 0 :  getByte(w);
 | 
						|
	}
 | 
						|
 | 
						|
	// Write the LLC header.
 | 
						|
	void writeAddrHeader(unsigned sapi, bool isCmd) {
 | 
						|
		setField(0,0,1);	// PD bit always 0.
 | 
						|
		setField(1,isCmd,1);	// Command/Response set to 1 for a downlink command.
 | 
						|
		setField(2,0,2);	// 2 unused bits.
 | 
						|
		setField(4,sapi,4);
 | 
						|
	}
 | 
						|
	void appendAddrHeader(unsigned sapi, bool isCmd) {
 | 
						|
		writeAddrHeader(sapi, isCmd);
 | 
						|
		setAppendP(1);
 | 
						|
	}
 | 
						|
 | 
						|
	LlcMsg *switchFrame();
 | 
						|
	void llcProcess1(LlcEntity *lle);
 | 
						|
};
 | 
						|
 | 
						|
struct LlcDlFrame : public LlcFrame
 | 
						|
{
 | 
						|
	// Allocate room for all the downlink headers that will be needed:
 | 
						|
	// The size is the needed payload size.
 | 
						|
	// Add room for all the downstream headers and trailers:
 | 
						|
	// sndcp header up to 4 bytes, but it allocates its own, so all we need are:
 | 
						|
	// llc header 3 bytes
 | 
						|
	// fcs trailer 3 bytes
 | 
						|
	// We will overkill it a bit, and and let the downstream prepend their headers.
 | 
						|
	LlcDlFrame(unsigned size) : LlcFrame(size+12) {
 | 
						|
		trimLeft(8);	// Room for headers.
 | 
						|
		setAppendP(0);
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
// 05.64 6.3
 | 
						|
struct LlcFrameI : public LlcFrame, public LlcMsg
 | 
						|
{
 | 
						|
	LlcFrameI(ByteVector &f) : LlcFrame(f) {}
 | 
						|
 | 
						|
	bool getA() { return getBit2(controlOffset,1); }
 | 
						|
	unsigned getNS() { return getField2(controlOffset,3,9); }
 | 
						|
	unsigned getNR() { return getField2(controlOffset+1,5,9); }
 | 
						|
	unsigned getS() { return getField2(controlOffset+2,6,2); }	// S1 and S2 bits.
 | 
						|
 | 
						|
	void llcProcess(LlcEntity *lle) {	// We dont handle them.
 | 
						|
		LLCWARN("LLC unexpected I frame ignored"<<LOGVAR2("S",getS()));
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
// 05.64 6.3
 | 
						|
struct LlcFrameS : public LlcFrame, public LlcMsg
 | 
						|
{
 | 
						|
	LlcFrameS(ByteVector &f) : LlcFrame(f) {}
 | 
						|
 | 
						|
	bool getA() { return getBit2(controlOffset,2); }
 | 
						|
	unsigned getNR() { return getField2(controlOffset,5,9); }
 | 
						|
	unsigned getS() { return getField2(controlOffset+1,6,2); }	// S1 and S2 bits.
 | 
						|
 | 
						|
	void llcProcess(LlcEntity *lle) {	// We dont handle them.
 | 
						|
		LLCWARN("LLC unexpected S frame ignored"<<LOGVAR2("S",getS()));
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
// 05.64 6.3
 | 
						|
struct LlcFrameUI : public LlcFrame, public LlcMsg
 | 
						|
{
 | 
						|
	const char *typeName() { return "FrameUI"; }
 | 
						|
	LlcFrameUI(ByteVector &f) : LlcFrame(f) {}
 | 
						|
	unsigned getNU() { return getField2(controlOffset,5,9); }	// frame number
 | 
						|
	bool getE() { return getField2(controlOffset+1,6,1); }	// encryption bit
 | 
						|
	bool getPM() { return getField2(controlOffset+1,7,1); }	// protected mode (crc data too?)
 | 
						|
 | 
						|
	void llcProcess(LlcEntity *lle);
 | 
						|
	void writeUIHeader(unsigned wNU /*, bool pf*/);
 | 
						|
};
 | 
						|
 | 
						|
// 05.64 6.3
 | 
						|
// These are commands using the S bits, mostly for ABM aka acknowledged mode.
 | 
						|
struct LlcFrameU : public LlcFrame, public LlcMsg
 | 
						|
{
 | 
						|
	LlcFrameU(ByteVector &f) : LlcFrame(f) {}
 | 
						|
	LlcFrameU(unsigned size) : LlcFrame(size) {}
 | 
						|
	bool getUPF() { return getBit2(controlOffset,3); }
 | 
						|
	unsigned getUM() { return getField2(controlOffset,4,4); }
 | 
						|
 | 
						|
	void appendUHeader(unsigned ucmd, bool pf) {
 | 
						|
		// Write the header.
 | 
						|
		appendField(0x7,3);	// Identifies U-format frame.
 | 
						|
		appendField(pf,1);		// P/F should nearly always be 1.
 | 
						|
		appendField(ucmd,4);	// The U-format command.
 | 
						|
	}
 | 
						|
	void llcProcess(LlcEntity *lle);
 | 
						|
};
 | 
						|
 | 
						|
struct LlcFrameSack : public LlcFrame , public LlcMsg
 | 
						|
{
 | 
						|
	LlcFrameSack(ByteVector &f) : LlcFrame(f) {}
 | 
						|
	void llcProcess(LlcEntity *lle) {
 | 
						|
		LOG(ERR) << "LLC SSACK frame ignored";
 | 
						|
		LLCWARN("LLC SSACK frame ignored");
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
// XID params are in 6.4.1.6 table 6
 | 
						|
// The "Layer-3 Parameters" are the SNDCP XID params used for compression, and vary by SAPI.
 | 
						|
// The LLC default values are in 8.9.9, and vary by SAPI:
 | 
						|
// Defaults for user SAPIS: N201-I=1503, N201-U=500.
 | 
						|
struct LlcFrameXid : public LlcFrameU
 | 
						|
{
 | 
						|
	enum XID_Type {
 | 
						|
		Version = 0,	// value 0-15
 | 
						|
		IOV_UI = 1,	// Ciphering input offset value for UI frames, for all SAPIs.
 | 
						|
		IOV_I = 2,	// Ciphering input offset value for I frames, for SAPI under negotiation
 | 
						|
		T200 = 3,
 | 
						|
		N200 = 4,
 | 
						|
		N201_U = 5,	// U frame info length 140-1520
 | 
						|
		N201_I = 6,	// I frame info length 140-1520
 | 
						|
		mD = 7,	// I frame buffer in downlink direction, 2 bytes, 0, 9 -24320
 | 
						|
		mU = 8,	// I frame buffer in uplink direction
 | 
						|
		kD = 9,	// window size, downlink
 | 
						|
		kU = 10,	// window size uplink
 | 
						|
		layer3 = 11,	// These are SNDCP XID commands, see 3GPP 04.65.
 | 
						|
						// They must go to one of the data SAPIs.
 | 
						|
		reset = 12		// Does a reset.
 | 
						|
	};
 | 
						|
	LlcFrameXid(unsigned size) : LlcFrameU(size) { }
 | 
						|
	//void writeXID(ByteVector bv, bool pf) {
 | 
						|
		//writeUHeader(bv,UCMD_XID,1);	// poll/final bit is always 1 for XID frame.
 | 
						|
	//}
 | 
						|
	void appendXidItem(unsigned xidtype, unsigned len, unsigned value)
 | 
						|
	{
 | 
						|
		if (len <= 3) {
 | 
						|
			appendField(0,1);	// XL - item length < 4.
 | 
						|
			appendField(xidtype,5);
 | 
						|
			appendField(len,2);
 | 
						|
			appendField(value,8*len);
 | 
						|
		} else {
 | 
						|
			appendField(1,1);	// XL - item length >= 4.
 | 
						|
			appendField(xidtype,5);
 | 
						|
			appendField(len,8);
 | 
						|
			appendField(0,2);			// 2 unused bits.
 | 
						|
			appendField(value,8*len);
 | 
						|
		}
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
// 3GPP 04.64 Logical Link Entity part of LLC.
 | 
						|
// There is one of these for each data LLC SAPI for each MS.
 | 
						|
// The LLC SAPIs are supposed to correspond to QoS [Quality of Service] classes;
 | 
						|
// the final NSAPI [Network SAPIs] that are connected to PDPContexts
 | 
						|
// are the on the high side of the SNDCP entity for user-data LLC SAPIs only.
 | 
						|
// There can be a many-to-one mapping of SNDCP entity to LLC SAPI.
 | 
						|
// The pdu number in the header is used to discard duplicates (04.64 8.4.2),
 | 
						|
// with a memory of 32 frames, which is redundant for data SAPIs because
 | 
						|
// SNDCP also discards PDUs, as well as assembling and reordering.
 | 
						|
// LLC has three states which affect LLC Entity:
 | 
						|
//		o TLLI Unassigned state.  Can only use UI and XID frames for SAPI = 1.
 | 
						|
//		o TLLI assigned (by the SGSN, from higher layers) - affects all entities.
 | 
						|
//		o ABM state - acknowledged data state - per entity.
 | 
						|
//			Unacknowledged frames may also be sent in ABM mode.
 | 
						|
// The SAP [Service Access Points] are defined in LlcSapi above.
 | 
						|
struct LlcEntity
 | 
						|
{
 | 
						|
	// 6.3.5.5 Unacknowledged mode state variables:
 | 
						|
	static const unsigned mSNS = 512;	// modulo arithment
 | 
						|
	unsigned mVU;	// Unconfirmed send state variable
 | 
						|
	unsigned mVUR;	// Unconfirmed receive state variable.
 | 
						|
	unsigned mN201U;	// Max number of bytes in UI data field.
 | 
						|
						// This is used by the SNDCP to split the data.
 | 
						|
	//GPRS::MSInfo *mMS;	// The MS who ultimately owns us.
 | 
						|
	//LlcEntity(GPRS::MSInfo *ms) : mMS(ms) { reset(); }
 | 
						|
	//GPRS::MSInfo *getMS() { return mMS; }
 | 
						|
	SgsnInfo *mSI;	// The SgsnInfo in which we reside.
 | 
						|
	LlcEntity(SgsnInfo *wSI) : mSI(wSI) {}
 | 
						|
 | 
						|
	//SgsnInfo *getSgsnInfo();
 | 
						|
 | 
						|
	void reset() {
 | 
						|
		mVU = mVUR = 0;
 | 
						|
		// 8.9.8: LLC layer parameter default values.
 | 
						|
		// It varies by SAPI, but for user data default is 500, max 1520.
 | 
						|
		// For other sapis length wont be exceeded anyway so dont worry about them.
 | 
						|
		mN201U = 500;
 | 
						|
	}
 | 
						|
 | 
						|
	virtual void lleUplinkData(ByteVector &payload) = 0;
 | 
						|
	virtual unsigned getLlcSapi() = 0;
 | 
						|
	//Sndcp *getSndcp(unsigned nsapi);
 | 
						|
	//void setSndcp(unsigned nsapi,Sndcp*);
 | 
						|
	void lleWriteLowSide(LlcFrame &frame);
 | 
						|
	void lleWriteHighSide(LlcDlFrame &frame, bool isCmd, const char *descr);
 | 
						|
	void lleWriteHighSide(L3GprsDlMsg &msg);
 | 
						|
	void lleWriteRaw(ByteVector &frame, const char *descr);
 | 
						|
};
 | 
						|
#if LLC_IMPLEMENTATION
 | 
						|
	//SgsnInfo *LlcEntity::getSgsnInfo() { return mSI; }
 | 
						|
#endif
 | 
						|
 | 
						|
// Attached to one of the user-data SAPIs for an MS
 | 
						|
struct LlcEntityUserData : public LlcEntity
 | 
						|
{
 | 
						|
	unsigned mLlcSapi;	// The LLC sapi of this entity.
 | 
						|
	unsigned mN201U;
 | 
						|
	LlcEntityUserData(unsigned wLlcSapi, SgsnInfo *si) :
 | 
						|
		LlcEntity(si),
 | 
						|
		mLlcSapi(wLlcSapi)
 | 
						|
	{
 | 
						|
		mN201U = 500;	// Default max size for pdus.
 | 
						|
	}
 | 
						|
 | 
						|
	unsigned getLlcSapi() { return mLlcSapi; }
 | 
						|
	unsigned getMaxPduSize() { return mN201U; }
 | 
						|
	Sndcp *getSndcp(unsigned nsapi);
 | 
						|
	void setSndcp(unsigned nsapi, Sndcp*ptr);
 | 
						|
	void lleUplinkData(ByteVector &payload);
 | 
						|
};
 | 
						|
#if LLC_IMPLEMENTATION
 | 
						|
#endif
 | 
						|
 | 
						|
// Attached to the LLC GPRSMM sapi for an MS.
 | 
						|
struct LlcEntityGmm : public LlcEntity
 | 
						|
{
 | 
						|
	LlcEntityGmm(SgsnInfo *si) : LlcEntity(si) {}
 | 
						|
	// The payload is in L3 message.
 | 
						|
	void lleUplinkData(ByteVector &payload); // calls: Sgsn::handleL3Msg(this,&payload);
 | 
						|
	unsigned getLlcSapi() { return 1; }
 | 
						|
};
 | 
						|
 | 
						|
// 3GPP 04.64: SNDCP, with yet another stupid header.
 | 
						|
// It is a miracle any data gets through at all.
 | 
						|
// The NSAPI are on the high (network) side, and the SAPI are the low side at LLC.
 | 
						|
// There can be multiple NSSAPI, each with a PDP context, attached to each SAPI.
 | 
						|
// The L3 Activate PDP context message specifies both NSAPI and LLC SAPI.
 | 
						|
struct SndcpFrame : public ByteVector
 | 
						|
{
 | 
						|
 | 
						|
	SndcpFrame(ByteVector &bv) : ByteVector(bv) { }
 | 
						|
 | 
						|
	bool getF() { return getBit(1); }	// First segment indicator.
 | 
						|
	bool getT() { return getBit(2); }	// 0 - DATA(acked) 1 - UNITDATA
 | 
						|
	bool getM() { return getBit(3); }	// More bit: 1 => more segments.
 | 
						|
	unsigned getNSapi() { return getField(4,4); }	// NSAPI on sndcp network high side.
 | 
						|
	// Dcomp and Pcomp are only extent if F bit is set.
 | 
						|
	unsigned getDcomp() { return getField2(1,0,4); } // data compression
 | 
						|
	unsigned getPcomp() { return getField2(1,4,4); } // protocol compression
 | 
						|
	ByteVector getPayload() { return tail((getT() ? 3 : 2)+(getF()?1:0)); }
 | 
						|
 | 
						|
	// For UNITDATA (unacknowledged mode) - T bit == 1
 | 
						|
	unsigned getSegmentNumber() { return getField2(getF()?2:1,0,4); }
 | 
						|
	unsigned getPduNumber() {
 | 
						|
		if (getT()) {	// unacknowledged mode.
 | 
						|
			return getField2(getF()?2:1,4,12);
 | 
						|
		} else {		// acknowledged mode.
 | 
						|
			return getField2(getF()?2:1,0,8);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// For DATA (acknowled mode) - T bit == 0
 | 
						|
	//unsigned getAMPduNumber() { return getField2(2,0,8); }
 | 
						|
};
 | 
						|
 | 
						|
class Sndcp
 | 
						|
{
 | 
						|
	enum {	// Address field bits
 | 
						|
		F_BIT = 0x40,
 | 
						|
		T_BIT = 0x20,
 | 
						|
		M_BIT = 0x10
 | 
						|
	};
 | 
						|
	// Our identifiers:
 | 
						|
	static const unsigned sUmSNS = 4096;	// Module for Unacknowledged Mode, sequence number space.
 | 
						|
	static const unsigned sAmSNS = 256;	// Module for Acknowledged Mode, sequence number space.
 | 
						|
	unsigned mSNS;		// Current modulo
 | 
						|
	unsigned mNSapi;	// 5..16
 | 
						|
	unsigned mLlcSapi;	// One of the LLC UserData sapis.
 | 
						|
	UInt_z mRecvNPdu;
 | 
						|
	UInt_z mSendNPdu;
 | 
						|
	LlcEntityUserData *mlle;
 | 
						|
 | 
						|
	public:
 | 
						|
	//PdpContext *mPdp;
 | 
						|
	//void setPdp(PdpContext *pdp) { mPdp = pdp; }
 | 
						|
	//PdpContext *getPdp() { return mPdp; }
 | 
						|
	SgsnInfo *getSgsnInfo();
 | 
						|
 | 
						|
	// I dont think it is possible in our case for an sndcp to exist in pdp-inactive state.
 | 
						|
	//bool isPdpInactive();
 | 
						|
 | 
						|
	// Data reassembly Queues:
 | 
						|
	// We are supposed to reorder pdus.
 | 
						|
	// We dont care about the order, since they are going to the internet, but the packets may
 | 
						|
	// arrive in multiple segments that have become unordered, possibly due to multi-slot transmission,
 | 
						|
	// so we need to handle it.  We will only reorder the last sMemory pdus.
 | 
						|
	static const unsigned sMemory = 32;
 | 
						|
	struct OneSdu {
 | 
						|
		UInt_z mSegCount;	// Number of segs, derived from 'm' bit.
 | 
						|
		ByteVector segs[16];	// The segments.
 | 
						|
	};
 | 
						|
	OneSdu mSegs[sMemory];		// This stuff is all deleted automatically.
 | 
						|
 | 
						|
	Sndcp(unsigned wNSapi, unsigned wLlcSapi, LlcEntityUserData *mlle);
 | 
						|
	~Sndcp();
 | 
						|
 | 
						|
	private:
 | 
						|
	// If we have all the segments for pdu num, send it off.
 | 
						|
	// If force, delete it even if incomplete.
 | 
						|
	void flush(unsigned num, bool force);
 | 
						|
	int diffSNS(int v1, int v2);
 | 
						|
	// SDU segmented to this size.  May be negotiated using XID command, which we dont implement.
 | 
						|
	unsigned getMaxPduSize();
 | 
						|
	void sndcpWriteSegment(ByteVector &pduSeg, unsigned segnum, unsigned flags);
 | 
						|
 | 
						|
	public:
 | 
						|
	// downlink data from internet comes in here.
 | 
						|
	// It needs to be segmented and sent to LLC.
 | 
						|
	void sndcpWriteHighSide(ByteVector &sdu);
 | 
						|
	// uplink data from MS comes in here.
 | 
						|
	void sndcpWriteLowSide(SndcpFrame &frame);
 | 
						|
};
 | 
						|
#if LLC_IMPLEMENTATION
 | 
						|
Sndcp::Sndcp(unsigned wNSapi, unsigned wLlcSapi, LlcEntityUserData *wlle) :
 | 
						|
	mSNS(sUmSNS),
 | 
						|
	mNSapi(wNSapi),
 | 
						|
	mLlcSapi(wLlcSapi),
 | 
						|
	mlle(wlle)
 | 
						|
	//,mPdp(0)
 | 
						|
{
 | 
						|
	mlle->setSndcp(mNSapi,this);
 | 
						|
}
 | 
						|
 | 
						|
Sndcp::~Sndcp()
 | 
						|
{
 | 
						|
	// Test mlle and mPdp and setting to 0 after are cautious overkill.
 | 
						|
	// The setSndcp() is also redundant, since our caller does it too.
 | 
						|
	if (mlle) {mlle->setSndcp(mNSapi,0); mlle = 0;}
 | 
						|
	//if (mPdp) {delete mPdp; mPdp = 0;}
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
// A connection is identified by DLCI [Data Link Connection Identifier] which is a TLLI+SAP.
 | 
						|
// However, our LlcEngine corresponds directly with an SgsnInfo which corresponds
 | 
						|
// directly with the MSInfo, so we dont use this.
 | 
						|
//struct LlcDlci {
 | 
						|
//	MSInfo *ms;
 | 
						|
//	unsigned char mSapi;
 | 
						|
//};
 | 
						|
 | 
						|
// An LLC engine for use with a single MS.
 | 
						|
struct LlcEngine {
 | 
						|
	// A set of LLC LLE [logical link entities] for use in an MS.
 | 
						|
	// These use predefined SAPIs, and here they are.
 | 
						|
	// The different user-data ones are supposed to correspond to different priorities,
 | 
						|
	// but we ignore the distinction.
 | 
						|
	LlcEntityGmm mLleGmm;
 | 
						|
	LlcEntityUserData mLleUserData3;
 | 
						|
	LlcEntityUserData mLleUserData5;
 | 
						|
	LlcEntityUserData mLleUserData9;
 | 
						|
	LlcEntityUserData mLleUserData11;
 | 
						|
 | 
						|
	// An sndcp entity for each NSAPI that is in use, ie, for each allocated pdpcontext.
 | 
						|
	// Allocated/deallocated on demand and at the same time as the PdpContext they connect with.
 | 
						|
	// The LlcEntityUserData are connected to these in a one-to-many mapping,
 | 
						|
	// ie, each LlcEntity may be tied to one or more Sndcp.
 | 
						|
#if 0==SNDCP_IN_PDP
 | 
						|
	Sndcp *mSndcp[16];	// 0-4 are reserved (same as UMTS), but we just allocate the whole array
 | 
						|
						// and index it directly with nsapi [Network SAPI]
 | 
						|
#endif
 | 
						|
 | 
						|
	LlcEngine(SgsnInfo *si) :
 | 
						|
		mLleGmm(si),
 | 
						|
		mLleUserData3(3,si),
 | 
						|
		mLleUserData5(5,si),
 | 
						|
		mLleUserData9(9,si),
 | 
						|
		mLleUserData11(11,si)
 | 
						|
	{
 | 
						|
		RN_MEMCHKNEW(LlcEngine)
 | 
						|
#if 0==SNDCP_IN_PDP
 | 
						|
		memset(mSndcp,0,sizeof(mSndcp));
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
	~LlcEngine() { RN_MEMCHKDEL(LlcEngine) }
 | 
						|
 | 
						|
	static bool isValidDataSapi(unsigned sapi) {
 | 
						|
		switch (sapi) {
 | 
						|
		case LlcSapi::UserData3:
 | 
						|
		case LlcSapi::UserData5:
 | 
						|
		case LlcSapi::UserData9:
 | 
						|
		case LlcSapi::UserData11:
 | 
						|
			return true;
 | 
						|
		default:
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	LlcEntity *getLlcEntity(unsigned sapi) {
 | 
						|
		switch (sapi) {
 | 
						|
		case LlcSapi::GPRSMM: return &mLleGmm;
 | 
						|
		case LlcSapi::UserData3: return &mLleUserData3;
 | 
						|
		case LlcSapi::UserData5: return &mLleUserData5;
 | 
						|
		case LlcSapi::UserData9: return &mLleUserData9;
 | 
						|
		case LlcSapi::UserData11: return &mLleUserData11;
 | 
						|
		default: return 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// The LLC Entities.  The GMM ones process L3 messages and the UserData ones are for...guess what?
 | 
						|
	// The LlcEntityUserData communicates to one or more Sndcp on its high side.
 | 
						|
	LlcEntityGmm *getLlcGmm();
 | 
						|
	LlcEntityUserData *getLlcEntityUserData(unsigned llcSapi);
 | 
						|
 | 
						|
	// The bv is an LLC message.
 | 
						|
	// It may cause the LLC to send some download messages,
 | 
						|
	// or it may be an information frame on a UserData sapi
 | 
						|
	// whose payload goes to GGSN via an Sndcp entity.
 | 
						|
	void llcWriteLowSide(ByteVector &bv,SgsnInfo *si);
 | 
						|
 | 
						|
	void llcWriteHighSide(ByteVector &sdu,int nsapi);
 | 
						|
 | 
						|
	void allocSndcp(SgsnInfo *si, unsigned nsapi, unsigned llcsapi);
 | 
						|
	void freeSndcp(unsigned nsapi);
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
// GSM04.64 6.2
 | 
						|
// (pat) The RLC/MAC layer knows nothing about the contents of an LlcFrame; it just passes data
 | 
						|
// back and forth between the MS and SGSN.
 | 
						|
// This is used to dump out the complete header.
 | 
						|
struct LlcFrameDump : public LlcFrame
 | 
						|
{
 | 
						|
	LLCFormat::type mFormat;
 | 
						|
 | 
						|
	// I, I+S format is for acknowledged mode.
 | 
						|
	// UI format is for unacknowledged mode.
 | 
						|
	// U format is for LLC control only, includes no data.
 | 
						|
	// Control fields.  Which are valid depends on format:
 | 
						|
	bool mE;		// UI format only
 | 
						|
	bool mPM;		// UI format only
 | 
						|
	bool mA;		// I or S format
 | 
						|
	bool mPF;		// U format only
 | 
						|
	unsigned mM;	// U format only
 | 
						|
	unsigned mS;	// I or S formats.
 | 
						|
	unsigned mK;	// I Sack format bitmap length.
 | 
						|
	unsigned mNS, mNR, mNU;
 | 
						|
 | 
						|
	// Return -1 if we dont know how long, which is S SACK format.
 | 
						|
	int headerLength();
 | 
						|
 | 
						|
	//LlcFrameDump(const ByteVector &vec) : LlcFrame(vec)
 | 
						|
	LlcFrameDump(const ByteVector &vec) : LlcFrame(vec)
 | 
						|
	{
 | 
						|
		mFormat = getFormat();
 | 
						|
		llcParseDump();
 | 
						|
	}
 | 
						|
	void llcParseDump();
 | 
						|
 | 
						|
	void textHeader(std::ostream &os);
 | 
						|
	void textContent(std::ostream &os, bool verbose);
 | 
						|
	void text(std::ostream &os);
 | 
						|
};
 | 
						|
 | 
						|
};  // namespace GPRS
 | 
						|
 | 
						|
#endif
 | 
						|
 |