mirror of
				https://github.com/RangeNetworks/openbts.git
				synced 2025-11-03 21:33:15 +00:00 
			
		
		
		
	sync of openbts
git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@6168 19bc5d8c-e614-43d4-8b26-e1612bc8e597
This commit is contained in:
		
							
								
								
									
										2
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								AUTHORS
									
									
									
									
									
								
							@@ -190,3 +190,5 @@ Alon Levy, alonlevy1@gmail.com
 | 
				
			|||||||
    RRLPMessages.h
 | 
					    RRLPMessages.h
 | 
				
			||||||
    RRLPTest.cpp
 | 
					    RRLPTest.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pat Thompson
 | 
				
			||||||
 | 
					    GPRS/*
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +0,0 @@
 | 
				
			|||||||
This file contains example Asterisk configuration files for the OpenBTS Asterisk server.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This file does not contain REAL configuration files, since those would include real IMSIs.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -1,148 +0,0 @@
 | 
				
			|||||||
;
 | 
					 | 
				
			||||||
; Asterisk Call Detail Record engine configuration
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; CDR is Call Detail Record, which provides logging services via a variety of
 | 
					 | 
				
			||||||
; pluggable backend modules.  Detailed call information can be recorded to
 | 
					 | 
				
			||||||
; databases, files, etc.  Useful for billing, fraud prevention, compliance with
 | 
					 | 
				
			||||||
; Sarbanes-Oxley aka The Enron Act, QOS evaluations, and more.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[general]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; Define whether or not to use CDR logging.  Setting this to "no" will override
 | 
					 | 
				
			||||||
; any loading of backend CDR modules.  Default is "yes".
 | 
					 | 
				
			||||||
;enable=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; Define whether or not to log unanswered calls. Setting this to "yes" will
 | 
					 | 
				
			||||||
; report every attempt to ring a phone in dialing attempts, when it was not 
 | 
					 | 
				
			||||||
; answered. For example, if you try to dial 3 extensions, and this option is "yes",
 | 
					 | 
				
			||||||
; you will get 3 CDR's, one for each phone that was rung. Default is "no". Some
 | 
					 | 
				
			||||||
; find this information horribly useless. Others find it very valuable. Note, in "yes"
 | 
					 | 
				
			||||||
; mode, you will see one CDR, with one of the call targets on one side, and the originating
 | 
					 | 
				
			||||||
; channel on the other, and then one CDR for each channel attempted. This may seem 
 | 
					 | 
				
			||||||
; redundant, but cannot be helped.
 | 
					 | 
				
			||||||
;unanswered = no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; Define the CDR batch mode, where instead of posting the CDR at the end of
 | 
					 | 
				
			||||||
; every call, the data will be stored in a buffer to help alleviate load on the
 | 
					 | 
				
			||||||
; asterisk server.  Default is "no".
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; WARNING WARNING WARNING
 | 
					 | 
				
			||||||
; Use of batch mode may result in data loss after unsafe asterisk termination
 | 
					 | 
				
			||||||
; ie. software crash, power failure, kill -9, etc.
 | 
					 | 
				
			||||||
; WARNING WARNING WARNING
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
;batch=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; Define the maximum number of CDRs to accumulate in the buffer before posting
 | 
					 | 
				
			||||||
; them to the backend engines.  'batch' must be set to 'yes'.  Default is 100.
 | 
					 | 
				
			||||||
;size=100
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; Define the maximum time to accumulate CDRs in the buffer before posting them
 | 
					 | 
				
			||||||
; to the backend engines.  If this time limit is reached, then it will post the
 | 
					 | 
				
			||||||
; records, regardless of the value defined for 'size'.  'batch' must be set to
 | 
					 | 
				
			||||||
; 'yes'.  Note that time is in seconds.  Default is 300 (5 minutes).
 | 
					 | 
				
			||||||
;time=300
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; The CDR engine uses the internal asterisk scheduler to determine when to post
 | 
					 | 
				
			||||||
; records.  Posting can either occur inside the scheduler thread, or a new
 | 
					 | 
				
			||||||
; thread can be spawned for the submission of every batch.  For small batches,
 | 
					 | 
				
			||||||
; it might be acceptable to just use the scheduler thread, so set this to "yes".
 | 
					 | 
				
			||||||
; For large batches, say anything over size=10, a new thread is recommended, so
 | 
					 | 
				
			||||||
; set this to "no".  Default is "no".
 | 
					 | 
				
			||||||
;scheduleronly=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; When shutting down asterisk, you can block until the CDRs are submitted.  If
 | 
					 | 
				
			||||||
; you don't, then data will likely be lost.  You can always check the size of
 | 
					 | 
				
			||||||
; the CDR batch buffer with the CLI "cdr status" command.  To enable blocking on
 | 
					 | 
				
			||||||
; submission of CDR data during asterisk shutdown, set this to "yes".  Default
 | 
					 | 
				
			||||||
; is "yes".
 | 
					 | 
				
			||||||
;safeshutdown=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; Normally, CDR's are not closed out until after all extensions are finished
 | 
					 | 
				
			||||||
; executing.  By enabling this option, the CDR will be ended before executing
 | 
					 | 
				
			||||||
; the "h" extension so that CDR values such as "end" and "billsec" may be
 | 
					 | 
				
			||||||
; retrieved inside of of this extension.
 | 
					 | 
				
			||||||
;endbeforehexten=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; CHOOSING A CDR "BACKEND"  (what kind of output to generate)
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; To choose a backend, you have to make sure either the right category is 
 | 
					 | 
				
			||||||
; defined in this file, or that the appropriate config file exists, and has the 
 | 
					 | 
				
			||||||
; proper definitions in it. If there are any problems, usually, the entry will
 | 
					 | 
				
			||||||
; silently ignored, and you get no output.
 | 
					 | 
				
			||||||
; 
 | 
					 | 
				
			||||||
; Also, please note that you can generate CDR records in as many formats as you 
 | 
					 | 
				
			||||||
; wish. If you configure 5 different CDR formats, then each event will be logged
 | 
					 | 
				
			||||||
; in 5 different places! In the example config files, all formats are commented
 | 
					 | 
				
			||||||
; out except for the cdr-csv format.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; Here are all the possible back ends:
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
;   csv, custom, manager, odbc, pgsql, radius, sqlite, tds 
 | 
					 | 
				
			||||||
;    (also, mysql is available via the asterisk-addons, due to licensing
 | 
					 | 
				
			||||||
;     requirements)
 | 
					 | 
				
			||||||
;   (please note, also, that other backends can be created, by creating
 | 
					 | 
				
			||||||
;    a new backend module in the source cdr/ directory!)
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; Some of the modules required to provide these backends will not build or install
 | 
					 | 
				
			||||||
; unless some dependency requirements are met. Examples of this are pgsql, odbc,
 | 
					 | 
				
			||||||
; etc. If you are not getting output as you would expect, the first thing to do
 | 
					 | 
				
			||||||
; is to run the command "make menuselect", and check what modules are available,
 | 
					 | 
				
			||||||
; by looking in the "2. Call Detail Recording" option in the main menu. If your
 | 
					 | 
				
			||||||
; backend is marked with XXX, you know that the "configure" command could not find
 | 
					 | 
				
			||||||
; the required libraries for that option.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; To get CDRs to be logged to the plain-jane /var/log/asterisk/cdr-csv/Master.csv 
 | 
					 | 
				
			||||||
; file, define the [csv] category in this file. No database necessary. The example
 | 
					 | 
				
			||||||
; config files are set up to provide this kind of output by default.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; To get custom csv CDR records, make sure the cdr_custom.conf file
 | 
					 | 
				
			||||||
; is present, and contains the proper [mappings] section. The advantage to
 | 
					 | 
				
			||||||
; using this backend, is that you can define which fields to output, and in
 | 
					 | 
				
			||||||
; what order. By default, the example configs are set up to mimic the cdr-csv
 | 
					 | 
				
			||||||
; output. If you don't make any changes to the mappings, you are basically generating
 | 
					 | 
				
			||||||
; the same thing as cdr-csv, but expending more CPU cycles to do so!
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; To get manager events generated, make sure the cdr_manager.conf file exists,
 | 
					 | 
				
			||||||
; and the [general] section is defined, with the single variable 'enabled = yes'.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; For odbc, make sure all the proper libs are installed, that "make menuselect"
 | 
					 | 
				
			||||||
; shows that the modules are available, and the cdr_odbc.conf file exists, and
 | 
					 | 
				
			||||||
; has a [global] section with the proper variables defined.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; For pgsql, make sure all the proper libs are installed, that "make menuselect"
 | 
					 | 
				
			||||||
; shows that the modules are available, and the cdr_pgsql.conf file exists, and
 | 
					 | 
				
			||||||
; has a [global] section with the proper variables defined.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; For logging to radius databases, make sure all the proper libs are installed, that 
 | 
					 | 
				
			||||||
; "make menuselect" shows that the modules are available, and the [radius]
 | 
					 | 
				
			||||||
; category is defined in this file, and in that section, make sure the 'radiuscfg'
 | 
					 | 
				
			||||||
; variable is properly pointing to an existing radiusclient.conf file.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; For logging to sqlite databases, make sure the 'cdr.db' file exists in the log directory,
 | 
					 | 
				
			||||||
; which is usually /var/log/asterisk. Of course, the proper libraries should be available
 | 
					 | 
				
			||||||
; during the 'configure' operation.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; For tds logging, make sure the proper libraries are available during the 'configure' 
 | 
					 | 
				
			||||||
; phase, and that cdr_tds.conf exists and is properly set up with a [global] category.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; Also, remember, that if you wish to log CDR info to a database, you will have to define
 | 
					 | 
				
			||||||
; a specific table in that databse to make things work! See the doc directory for more details
 | 
					 | 
				
			||||||
; on how to create this table in each database.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[csv]
 | 
					 | 
				
			||||||
usegmtime=yes    ; log date/time in GMT.  Default is "no"
 | 
					 | 
				
			||||||
loguniqueid=yes  ; log uniqueid.  Default is "no
 | 
					 | 
				
			||||||
loguserfield=yes ; log user field.  Default is "no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
;[radius]
 | 
					 | 
				
			||||||
;usegmtime=yes    ; log date/time in GMT
 | 
					 | 
				
			||||||
;loguniqueid=yes  ; log uniqueid
 | 
					 | 
				
			||||||
;loguserfield=yes ; log user field
 | 
					 | 
				
			||||||
; Set this to the location of the radiusclient-ng configuration file
 | 
					 | 
				
			||||||
; The default is /etc/radiusclient-ng/radiusclient.conf
 | 
					 | 
				
			||||||
;radiuscfg => /usr/local/etc/radiusclient-ng/radiusclient.conf
 | 
					 | 
				
			||||||
@@ -1,57 +0,0 @@
 | 
				
			|||||||
[globals]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[default]
 | 
					 | 
				
			||||||
; This is the context for handsets that are allowed to attached via open registration.
 | 
					 | 
				
			||||||
; Normally, this context is only used for testing.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; These are test extensions that you might want to disable after installation.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; Create an extension, 2600, for evaluating echo latency.
 | 
					 | 
				
			||||||
exten => 2600,1,Answer()                     ; Do the echo test
 | 
					 | 
				
			||||||
exten => 2600,n,Echo                     ; Do the echo test
 | 
					 | 
				
			||||||
exten => 2600,n,Hangup
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; The 2101 extension is used for factory testing with zoiper.
 | 
					 | 
				
			||||||
exten => 2101,1,Dial(SIP/zoiper)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; The 2100 extension is for factory testing with the test SIM.
 | 
					 | 
				
			||||||
exten => 2100,1,Dial(SIP/IMSI001010000000000)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[outbound-trunk]
 | 
					 | 
				
			||||||
; If you had an external trunk, you would dial it here.
 | 
					 | 
				
			||||||
exten => _N.,1,Answer()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[phones]
 | 
					 | 
				
			||||||
; This is the context for handsets provisioned through the realtime database.
 | 
					 | 
				
			||||||
; This assumes that OpenBTS units all are running their SIP interfaces on port 5062.
 | 
					 | 
				
			||||||
exten => _N.,1,Set(Name=${ODBC_SQL(select dial from dialdata_table where exten = \"${EXTEN}\")})
 | 
					 | 
				
			||||||
exten => _N.,n,GotoIf($["${Name}" = ""] ?outbound-trunk,${EXTEN},1)
 | 
					 | 
				
			||||||
exten => _N.,n,Set(IPAddr=${ODBC_SQL(select ipaddr from sip_buddies where name = \"${Name}\")})
 | 
					 | 
				
			||||||
exten => _N.,n,GotoIf($["${IPAddr}" = ""] ?outbound-trunk,${EXTEN},1)
 | 
					 | 
				
			||||||
exten => _N.,n,Dial(SIP/${Name}@${IPAddr}:5062)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[sip-local]
 | 
					 | 
				
			||||||
; This context is the union of all of the in-network contexts.
 | 
					 | 
				
			||||||
include => default
 | 
					 | 
				
			||||||
include => phones
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[sip-external]
 | 
					 | 
				
			||||||
; This is the top-level context that gives access to out-of-network calling.
 | 
					 | 
				
			||||||
; also includes the in-network calling.
 | 
					 | 
				
			||||||
include => sip-local
 | 
					 | 
				
			||||||
include => outbound-trunk
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -1,733 +0,0 @@
 | 
				
			|||||||
; indications.conf
 | 
					 | 
				
			||||||
; Configuration file for location specific tone indications
 | 
					 | 
				
			||||||
; used by the pbx_indications module.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; NOTE:
 | 
					 | 
				
			||||||
;    When adding countries to this file, please keep them in alphabetical
 | 
					 | 
				
			||||||
;    order according to the 2-character country codes!
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; The [general] category is for certain global variables.
 | 
					 | 
				
			||||||
; All other categories are interpreted as location specific indications
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
[general]
 | 
					 | 
				
			||||||
country=us		; default location
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; [example]
 | 
					 | 
				
			||||||
; description = string
 | 
					 | 
				
			||||||
;      The full name of your country, in English.
 | 
					 | 
				
			||||||
; alias = iso[,iso]*
 | 
					 | 
				
			||||||
;      List of other countries 2-letter iso codes, which have the same
 | 
					 | 
				
			||||||
;      tone indications.
 | 
					 | 
				
			||||||
; ringcadence = num[,num]*
 | 
					 | 
				
			||||||
;      List of durations the physical bell rings.
 | 
					 | 
				
			||||||
; dial = tonelist
 | 
					 | 
				
			||||||
;      Set of tones to be played when one picks up the hook.
 | 
					 | 
				
			||||||
; busy = tonelist
 | 
					 | 
				
			||||||
;      Set of tones played when the receiving end is busy.
 | 
					 | 
				
			||||||
; congestion = tonelist
 | 
					 | 
				
			||||||
;      Set of tones played when there is some congestion (on the network?)
 | 
					 | 
				
			||||||
; callwaiting = tonelist
 | 
					 | 
				
			||||||
;      Set of tones played when there is a call waiting in the background.
 | 
					 | 
				
			||||||
; dialrecall = tonelist
 | 
					 | 
				
			||||||
;      Not well defined; many phone systems play a recall dial tone after hook
 | 
					 | 
				
			||||||
;      flash.
 | 
					 | 
				
			||||||
; record = tonelist
 | 
					 | 
				
			||||||
;      Set of tones played when call recording is in progress.
 | 
					 | 
				
			||||||
; info = tonelist
 | 
					 | 
				
			||||||
;      Set of tones played with special information messages (e.g., "number is
 | 
					 | 
				
			||||||
;      out of service")
 | 
					 | 
				
			||||||
; 'name' = tonelist
 | 
					 | 
				
			||||||
;      Every other variable will be available as a shortcut for the "PlayList" command
 | 
					 | 
				
			||||||
;      but will not be used automatically by Asterisk.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; The tonelist itself is defined by a comma-separated sequence of elements.
 | 
					 | 
				
			||||||
; Each element consist of a frequency (f) with an optional duration (in ms)
 | 
					 | 
				
			||||||
; attached to it (f/duration). The frequency component may be a mixture of two
 | 
					 | 
				
			||||||
; frequencies (f1+f2) or a frequency modulated by another frequency (f1*f2).
 | 
					 | 
				
			||||||
; The implicit modulation depth is fixed at 90%, though.
 | 
					 | 
				
			||||||
; If the list element starts with a !, that element is NOT repeated,
 | 
					 | 
				
			||||||
; therefore, only if all elements start with !, the tonelist is time-limited,
 | 
					 | 
				
			||||||
; all others will repeat indefinitely.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; concisely:
 | 
					 | 
				
			||||||
;   element = [!]freq[+|*freq2][/duration]
 | 
					 | 
				
			||||||
;   tonelist = element[,element]*
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; Please note that SPACES ARE NOT ALLOWED in tone lists!
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[at]
 | 
					 | 
				
			||||||
description = Austria
 | 
					 | 
				
			||||||
ringcadence = 1000,5000
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
dial = 420
 | 
					 | 
				
			||||||
busy = 420/400,0/400
 | 
					 | 
				
			||||||
ring = 420/1000,0/5000
 | 
					 | 
				
			||||||
congestion = 420/200,0/200
 | 
					 | 
				
			||||||
callwaiting = 420/40,0/1960
 | 
					 | 
				
			||||||
dialrecall = 420
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/80,0/14920
 | 
					 | 
				
			||||||
info = 950/330,1450/330,1850/330,0/1000
 | 
					 | 
				
			||||||
stutter = 380+420
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[au]
 | 
					 | 
				
			||||||
description = Australia
 | 
					 | 
				
			||||||
; Reference http://www.acif.org.au/__data/page/3303/S002_2001.pdf
 | 
					 | 
				
			||||||
; Normal Ring
 | 
					 | 
				
			||||||
ringcadence = 400,200,400,2000
 | 
					 | 
				
			||||||
; Distinctive Ring 1 - Forwarded Calls
 | 
					 | 
				
			||||||
; 400,400,200,200,400,1400
 | 
					 | 
				
			||||||
; Distinctive Ring 2 - Selective Ring 2 + Operator + Recall
 | 
					 | 
				
			||||||
; 400,400,200,2000
 | 
					 | 
				
			||||||
; Distinctive Ring 3 - Multiple Subscriber Number 1
 | 
					 | 
				
			||||||
; 200,200,400,2200
 | 
					 | 
				
			||||||
; Distinctive Ring 4 - Selective Ring 1 + Centrex
 | 
					 | 
				
			||||||
; 400,2600
 | 
					 | 
				
			||||||
; Distinctive Ring 5 - Selective Ring 3
 | 
					 | 
				
			||||||
; 400,400,200,400,200,1400
 | 
					 | 
				
			||||||
; Distinctive Ring 6 - Multiple Subscriber Number 2
 | 
					 | 
				
			||||||
; 200,400,200,200,400,1600
 | 
					 | 
				
			||||||
; Distinctive Ring 7 - Multiple Subscriber Number 3 + Data Privacy
 | 
					 | 
				
			||||||
; 200,400,200,400,200,1600
 | 
					 | 
				
			||||||
; Tones
 | 
					 | 
				
			||||||
dial = 413+438
 | 
					 | 
				
			||||||
busy = 425/375,0/375
 | 
					 | 
				
			||||||
ring = 413+438/400,0/200,413+438/400,0/2000
 | 
					 | 
				
			||||||
; XXX Congestion: Should reduce by 10 db every other cadence XXX
 | 
					 | 
				
			||||||
congestion = 425/375,0/375,420/375,0/375
 | 
					 | 
				
			||||||
callwaiting = 425/200,0/200,425/200,0/4400
 | 
					 | 
				
			||||||
dialrecall = 413+438
 | 
					 | 
				
			||||||
; Record tone used for Call Intrusion/Recording or Conference
 | 
					 | 
				
			||||||
record = !425/1000,!0/15000,425/360,0/15000
 | 
					 | 
				
			||||||
info = 425/2500,0/500
 | 
					 | 
				
			||||||
; Other Australian Tones
 | 
					 | 
				
			||||||
; The STD "pips" indicate the call is not an untimed local call
 | 
					 | 
				
			||||||
std = !525/100,!0/100,!525/100,!0/100,!525/100,!0/100,!525/100,!0/100,!525/100
 | 
					 | 
				
			||||||
; Facility confirmation tone (eg. Call Forward Activated)
 | 
					 | 
				
			||||||
facility = 425
 | 
					 | 
				
			||||||
; Message Waiting "stutter" dialtone
 | 
					 | 
				
			||||||
stutter = 413+438/100,0/40
 | 
					 | 
				
			||||||
; Ringtone for calls to Telstra mobiles
 | 
					 | 
				
			||||||
ringmobile = 400+450/400,0/200,400+450/400,0/2000
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[bg]
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
description = Bulgaria
 | 
					 | 
				
			||||||
ringdance = 1000,4000
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/500,0/500
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/250,0/250
 | 
					 | 
				
			||||||
callwaiting = 425/150,0/150,425/150,0/4000
 | 
					 | 
				
			||||||
dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
 | 
					 | 
				
			||||||
record = 1400/425,0/15000
 | 
					 | 
				
			||||||
info = 950/330,1400/330,1800/330,0/1000
 | 
					 | 
				
			||||||
stutter = 425/1500,0/100
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[br]
 | 
					 | 
				
			||||||
description = Brazil
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/250,0/250
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/250,0/250,425/750,0/250
 | 
					 | 
				
			||||||
callwaiting = 425/50,0/1000
 | 
					 | 
				
			||||||
; Dialrecall not used in Brazil standard (using UK standard)
 | 
					 | 
				
			||||||
dialrecall = 350+440
 | 
					 | 
				
			||||||
; Record tone is not used in Brazil, use busy tone
 | 
					 | 
				
			||||||
record = 425/250,0/250
 | 
					 | 
				
			||||||
; Info not used in Brazil standard (using UK standard)
 | 
					 | 
				
			||||||
info = 950/330,1400/330,1800/330
 | 
					 | 
				
			||||||
stutter = 350+440
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[be]
 | 
					 | 
				
			||||||
description = Belgium
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
ringcadence = 1000,3000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/500,0/500
 | 
					 | 
				
			||||||
ring = 425/1000,0/3000
 | 
					 | 
				
			||||||
congestion = 425/167,0/167
 | 
					 | 
				
			||||||
callwaiting = 1400/175,0/175,1400/175,0/3500
 | 
					 | 
				
			||||||
; DIALRECALL - not specified
 | 
					 | 
				
			||||||
dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440"
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = 900/330,1400/330,1800/330,0/1000
 | 
					 | 
				
			||||||
stutter = 425/1000,0/250
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[ch]
 | 
					 | 
				
			||||||
description = Switzerland
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/500,0/500
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/200,0/200
 | 
					 | 
				
			||||||
callwaiting = 425/200,0/200,425/200,0/4000
 | 
					 | 
				
			||||||
; DIALRECALL - not specified
 | 
					 | 
				
			||||||
dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/80,0/15000
 | 
					 | 
				
			||||||
info = 950/330,1400/330,1800/330,0/1000
 | 
					 | 
				
			||||||
stutter = 425+340/1100,0/1100
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[cl]
 | 
					 | 
				
			||||||
description = Chile
 | 
					 | 
				
			||||||
; According to specs from Telefonica CTC Chile
 | 
					 | 
				
			||||||
ringcadence = 1000,3000
 | 
					 | 
				
			||||||
dial = 400
 | 
					 | 
				
			||||||
busy = 400/500,0/500
 | 
					 | 
				
			||||||
ring = 400/1000,0/3000
 | 
					 | 
				
			||||||
congestion = 400/200,0/200
 | 
					 | 
				
			||||||
callwaiting = 400/250,0/8750
 | 
					 | 
				
			||||||
dialrecall = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = 950/333,1400/333,1800/333,0/1000
 | 
					 | 
				
			||||||
stutter = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[cn]
 | 
					 | 
				
			||||||
description = China
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 450
 | 
					 | 
				
			||||||
busy = 450/350,0/350
 | 
					 | 
				
			||||||
ring = 450/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 450/700,0/700
 | 
					 | 
				
			||||||
callwaiting = 450/400,0/4000
 | 
					 | 
				
			||||||
dialrecall = 450
 | 
					 | 
				
			||||||
record = 950/400,0/10000
 | 
					 | 
				
			||||||
info = 450/100,0/100,450/100,0/100,450/100,0/100,450/400,0/400
 | 
					 | 
				
			||||||
; STUTTER - not specified
 | 
					 | 
				
			||||||
stutter = 450+425
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[cz]
 | 
					 | 
				
			||||||
description = Czech Republic
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425/330,0/330,425/660,0/660
 | 
					 | 
				
			||||||
busy = 425/330,0/330
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/165,0/165
 | 
					 | 
				
			||||||
callwaiting = 425/330,0/9000
 | 
					 | 
				
			||||||
; DIALRECALL - not specified
 | 
					 | 
				
			||||||
dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425/330,0/330,425/660,0/660
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/500,0/14000
 | 
					 | 
				
			||||||
info = 950/330,0/30,1400/330,0/30,1800/330,0/1000
 | 
					 | 
				
			||||||
; STUTTER - not specified
 | 
					 | 
				
			||||||
stutter = 425/450,0/50
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[de]
 | 
					 | 
				
			||||||
description = Germany
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/480,0/480
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/240,0/240
 | 
					 | 
				
			||||||
callwaiting = !425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,0
 | 
					 | 
				
			||||||
; DIALRECALL - not specified
 | 
					 | 
				
			||||||
dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/80,0/15000
 | 
					 | 
				
			||||||
info = 950/330,1400/330,1800/330,0/1000
 | 
					 | 
				
			||||||
stutter = 425+400
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[dk]
 | 
					 | 
				
			||||||
description = Denmark
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/500,0/500
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/200,0/200
 | 
					 | 
				
			||||||
callwaiting = !425/200,!0/600,!425/200,!0/3000,!425/200,!0/200,!425/200,0
 | 
					 | 
				
			||||||
; DIALRECALL - not specified
 | 
					 | 
				
			||||||
dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/80,0/15000
 | 
					 | 
				
			||||||
info = 950/330,1400/330,1800/330,0/1000
 | 
					 | 
				
			||||||
; STUTTER - not specified
 | 
					 | 
				
			||||||
stutter = 425/450,0/50
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[ee]
 | 
					 | 
				
			||||||
description = Estonia
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/300,0/300
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/200,0/200
 | 
					 | 
				
			||||||
; CALLWAIT not in accordance to ITU
 | 
					 | 
				
			||||||
callwaiting = 950/650,0/325,950/325,0/30,1400/1300,0/2600
 | 
					 | 
				
			||||||
; DIALRECALL - not specified
 | 
					 | 
				
			||||||
dialrecall = 425/650,0/25
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
; INFO not in accordance to ITU
 | 
					 | 
				
			||||||
info = 950/650,0/325,950/325,0/30,1400/1300,0/2600
 | 
					 | 
				
			||||||
; STUTTER not specified
 | 
					 | 
				
			||||||
stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[es]
 | 
					 | 
				
			||||||
description = Spain
 | 
					 | 
				
			||||||
ringcadence = 1500,3000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/200,0/200
 | 
					 | 
				
			||||||
ring = 425/1500,0/3000
 | 
					 | 
				
			||||||
congestion = 425/200,0/200,425/200,0/200,425/200,0/600
 | 
					 | 
				
			||||||
callwaiting = 425/175,0/175,425/175,0/3500
 | 
					 | 
				
			||||||
dialrecall = !425/200,!0/200,!425/200,!0/200,!425/200,!0/200,425
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = 950/330,0/1000
 | 
					 | 
				
			||||||
dialout = 500
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[fi]
 | 
					 | 
				
			||||||
description = Finland
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/300,0/300
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/200,0/200
 | 
					 | 
				
			||||||
callwaiting = 425/150,0/150,425/150,0/8000
 | 
					 | 
				
			||||||
dialrecall = 425/650,0/25
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = 950/650,0/325,950/325,0/30,1400/1300,0/2600
 | 
					 | 
				
			||||||
stutter = 425/650,0/25
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[fr]
 | 
					 | 
				
			||||||
description = France
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
ringcadence = 1500,3500
 | 
					 | 
				
			||||||
; Dialtone can also be 440+330
 | 
					 | 
				
			||||||
dial = 440
 | 
					 | 
				
			||||||
busy = 440/500,0/500
 | 
					 | 
				
			||||||
ring = 440/1500,0/3500
 | 
					 | 
				
			||||||
; CONGESTION - not specified
 | 
					 | 
				
			||||||
congestion = 440/250,0/250
 | 
					 | 
				
			||||||
callwait = 440/300,0/10000
 | 
					 | 
				
			||||||
; DIALRECALL - not specified
 | 
					 | 
				
			||||||
dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = !950/330,!1400/330,!1800/330
 | 
					 | 
				
			||||||
stutter = !440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,440
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[gr]
 | 
					 | 
				
			||||||
description = Greece
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425/200,0/300,425/700,0/800
 | 
					 | 
				
			||||||
busy = 425/300,0/300
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/200,0/200
 | 
					 | 
				
			||||||
callwaiting = 425/150,0/150,425/150,0/8000
 | 
					 | 
				
			||||||
dialrecall = 425/650,0/25
 | 
					 | 
				
			||||||
record = 1400/400,0/15000
 | 
					 | 
				
			||||||
info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
 | 
					 | 
				
			||||||
stutter = 425/650,0/25
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[hu]
 | 
					 | 
				
			||||||
description = Hungary
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
ringcadence = 1250,3750
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/300,0/300
 | 
					 | 
				
			||||||
ring = 425/1250,0/3750
 | 
					 | 
				
			||||||
congestion = 425/300,0/300
 | 
					 | 
				
			||||||
callwaiting = 425/40,0/1960
 | 
					 | 
				
			||||||
dialrecall = 425+450
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/400,0/15000
 | 
					 | 
				
			||||||
info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
 | 
					 | 
				
			||||||
stutter = 350+375+400
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[il]
 | 
					 | 
				
			||||||
description = Israel
 | 
					 | 
				
			||||||
ringcadence = 1000,3000
 | 
					 | 
				
			||||||
dial = 414
 | 
					 | 
				
			||||||
busy = 414/500,0/500
 | 
					 | 
				
			||||||
ring = 414/1000,0/3000
 | 
					 | 
				
			||||||
congestion = 414/250,0/250
 | 
					 | 
				
			||||||
callwaiting = 414/100,0/100,414/100,0/100,414/600,0/3000
 | 
					 | 
				
			||||||
dialrecall = !414/100,!0/100,!414/100,!0/100,!414/100,!0/100,414
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = 1000/330,1400/330,1800/330,0/1000
 | 
					 | 
				
			||||||
stutter = !414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,414
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[in]
 | 
					 | 
				
			||||||
description = India
 | 
					 | 
				
			||||||
ringcadence = 400,200,400,2000
 | 
					 | 
				
			||||||
dial = 400*25
 | 
					 | 
				
			||||||
busy = 400/750,0/750
 | 
					 | 
				
			||||||
ring = 400*25/400,0/200,400*25/400,0/2000
 | 
					 | 
				
			||||||
congestion = 400/250,0/250
 | 
					 | 
				
			||||||
callwaiting = 400/200,0/100,400/200,0/7500
 | 
					 | 
				
			||||||
dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = !950/330,!1400/330,!1800/330,0/1000
 | 
					 | 
				
			||||||
stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[it]
 | 
					 | 
				
			||||||
description = Italy
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425/200,0/200,425/600,0/1000
 | 
					 | 
				
			||||||
busy = 425/500,0/500
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/200,0/200
 | 
					 | 
				
			||||||
callwaiting = 425/400,0/100,425/250,0/100,425/150,0/14000
 | 
					 | 
				
			||||||
dialrecall = 470/400,425/400
 | 
					 | 
				
			||||||
record = 1400/400,0/15000
 | 
					 | 
				
			||||||
info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
 | 
					 | 
				
			||||||
stutter = 470/400,425/400
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[lt]
 | 
					 | 
				
			||||||
description = Lithuania
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/350,0/350
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/200,0/200
 | 
					 | 
				
			||||||
callwaiting = 425/150,0/150,425/150,0/4000
 | 
					 | 
				
			||||||
; DIALRECALL - not specified
 | 
					 | 
				
			||||||
dialrecall = 425/500,0/50
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
 | 
					 | 
				
			||||||
; STUTTER - not specified
 | 
					 | 
				
			||||||
stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[jp]
 | 
					 | 
				
			||||||
description = Japan
 | 
					 | 
				
			||||||
ringcadence = 1000,2000
 | 
					 | 
				
			||||||
dial = 400
 | 
					 | 
				
			||||||
busy = 400/500,0/500
 | 
					 | 
				
			||||||
ring = 400+15/1000,0/2000
 | 
					 | 
				
			||||||
congestion = 400/500,0/500
 | 
					 | 
				
			||||||
callwaiting = 400+16/500,0/8000
 | 
					 | 
				
			||||||
dialrecall = !400/200,!0/200,!400/200,!0/200,!400/200,!0/200,400
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = !950/330,!1400/330,!1800/330,0
 | 
					 | 
				
			||||||
stutter = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[mx]
 | 
					 | 
				
			||||||
description = Mexico
 | 
					 | 
				
			||||||
ringcadence = 2000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/250,0/250
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/250,0/250
 | 
					 | 
				
			||||||
callwaiting = 425/200,0/600,425/200,0/10000
 | 
					 | 
				
			||||||
dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = 950/330,0/30,1400/330,0/30,1800/330,0/1000
 | 
					 | 
				
			||||||
stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[my]
 | 
					 | 
				
			||||||
description = Malaysia
 | 
					 | 
				
			||||||
ringcadence = 2000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/500,0/500
 | 
					 | 
				
			||||||
ring = 425/400,0/200
 | 
					 | 
				
			||||||
congestion = 425/500,0/500
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[nl]
 | 
					 | 
				
			||||||
description = Netherlands
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
; Most of these 425's can also be 450's
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/500,0/500
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/250,0/250
 | 
					 | 
				
			||||||
callwaiting = 425/500,0/9500
 | 
					 | 
				
			||||||
; DIALRECALL - not specified
 | 
					 | 
				
			||||||
dialrecall = 425/500,0/50
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = 950/330,1400/330,1800/330,0/1000
 | 
					 | 
				
			||||||
stutter = 425/500,0/50
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[no]
 | 
					 | 
				
			||||||
description = Norway
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/500,0/500
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/200,0/200
 | 
					 | 
				
			||||||
callwaiting = 425/200,0/600,425/200,0/10000
 | 
					 | 
				
			||||||
dialrecall = 470/400,425/400
 | 
					 | 
				
			||||||
record = 1400/400,0/15000
 | 
					 | 
				
			||||||
info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0
 | 
					 | 
				
			||||||
stutter = 470/400,425/400
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[nz]
 | 
					 | 
				
			||||||
description = New Zealand
 | 
					 | 
				
			||||||
;NOTE - the ITU has different tonesets for NZ, but according to some residents there,
 | 
					 | 
				
			||||||
;      this is, indeed, the correct way to do it.
 | 
					 | 
				
			||||||
ringcadence = 400,200,400,2000
 | 
					 | 
				
			||||||
dial = 400
 | 
					 | 
				
			||||||
busy = 400/250,0/250
 | 
					 | 
				
			||||||
ring = 400+450/400,0/200,400+450/400,0/2000
 | 
					 | 
				
			||||||
congestion = 400/375,0/375
 | 
					 | 
				
			||||||
callwaiting = !400/200,!0/3000,!400/200,!0/3000,!400/200,!0/3000,!400/200
 | 
					 | 
				
			||||||
dialrecall = !400/100!0/100,!400/100,!0/100,!400/100,!0/100,400
 | 
					 | 
				
			||||||
record = 1400/425,0/15000
 | 
					 | 
				
			||||||
info = 400/750,0/100,400/750,0/100,400/750,0/100,400/750,0/400
 | 
					 | 
				
			||||||
stutter = !400/100!0/100,!400/100,!0/100,!400/100,!0/100,!400/100!0/100,!400/100,!0/100,!400/100,!0/100,400
 | 
					 | 
				
			||||||
unobtainable = 400/75,0/100,400/75,0/100,400/75,0/100,400/75,0/400
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[ph]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; reference http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
description = Philippines
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 480+620/500,0/500
 | 
					 | 
				
			||||||
ring = 425+480/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 480+620/250,0/250
 | 
					 | 
				
			||||||
callwaiting = 440/300,0/10000
 | 
					 | 
				
			||||||
; DIALRECALL - not specified
 | 
					 | 
				
			||||||
dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
; INFO - not specified
 | 
					 | 
				
			||||||
info = !950/330,!1400/330,!1800/330,0
 | 
					 | 
				
			||||||
; STUTTER - not specified
 | 
					 | 
				
			||||||
stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[pl]
 | 
					 | 
				
			||||||
description = Poland
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/500,0/500
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/500,0/500
 | 
					 | 
				
			||||||
callwaiting = 425/150,0/150,425/150,0/4000
 | 
					 | 
				
			||||||
; DIALRECALL - not specified
 | 
					 | 
				
			||||||
dialrecall = 425/500,0/50
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
; 950/1400/1800 3x0.33 on 1.0 off  repeated 3 times 
 | 
					 | 
				
			||||||
info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000
 | 
					 | 
				
			||||||
; STUTTER - not specified
 | 
					 | 
				
			||||||
stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[pt]
 | 
					 | 
				
			||||||
description = Portugal
 | 
					 | 
				
			||||||
ringcadence = 1000,5000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/500,0/500
 | 
					 | 
				
			||||||
ring = 425/1000,0/5000
 | 
					 | 
				
			||||||
congestion = 425/200,0/200
 | 
					 | 
				
			||||||
callwaiting = 440/300,0/10000
 | 
					 | 
				
			||||||
dialrecall = 425/1000,0/200
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = 950/330,1400/330,1800/330,0/1000
 | 
					 | 
				
			||||||
stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[ru]
 | 
					 | 
				
			||||||
; References:
 | 
					 | 
				
			||||||
;	http://www.minsvyaz.ru/site.shtml?id=1806
 | 
					 | 
				
			||||||
;	http://www.aboutphone.info/lib/gost/45-223-2001.html
 | 
					 | 
				
			||||||
description = Russian Federation / ex Soviet Union
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/350,0/350
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/175,0/175
 | 
					 | 
				
			||||||
callwaiting = 425/200,0/5000
 | 
					 | 
				
			||||||
record = 1400/400,0/15000
 | 
					 | 
				
			||||||
info = 950/330,1400/330,1800/330,0/1000
 | 
					 | 
				
			||||||
dialrecall = 425/400,0/40
 | 
					 | 
				
			||||||
stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[se]
 | 
					 | 
				
			||||||
description = Sweden
 | 
					 | 
				
			||||||
ringcadence = 1000,5000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/250,0/250
 | 
					 | 
				
			||||||
ring = 425/1000,0/5000
 | 
					 | 
				
			||||||
congestion = 425/250,0/750
 | 
					 | 
				
			||||||
callwaiting = 425/200,0/500,425/200,0/9100
 | 
					 | 
				
			||||||
dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = !950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,0
 | 
					 | 
				
			||||||
stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425
 | 
					 | 
				
			||||||
; stutter = 425/320,0/20        ; Real swedish standard, not used for now
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[sg]
 | 
					 | 
				
			||||||
description = Singapore
 | 
					 | 
				
			||||||
; Singapore
 | 
					 | 
				
			||||||
; Reference: http://www.ida.gov.sg/idaweb/doc/download/I397/ida_ts_pstn1_i4r2.pdf 
 | 
					 | 
				
			||||||
; Frequency specs are:   425 Hz +/- 20Hz; 24 Hz +/- 2Hz; modulation depth 100%; SIT +/- 50Hz
 | 
					 | 
				
			||||||
ringcadence = 400,200,400,2000
 | 
					 | 
				
			||||||
dial        = 425
 | 
					 | 
				
			||||||
ring        = 425*24/400,0/200,425*24/400,0/2000     ; modulation should be 100%, not 90%
 | 
					 | 
				
			||||||
busy        = 425/750,0/750
 | 
					 | 
				
			||||||
congestion  = 425/250,0/250
 | 
					 | 
				
			||||||
callwaiting = 425*24/300,0/200,425*24/300,0/3200
 | 
					 | 
				
			||||||
stutter     = !425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,425
 | 
					 | 
				
			||||||
info        = 950/330,1400/330,1800/330,0/1000       ; not currently in use acc. to reference
 | 
					 | 
				
			||||||
dialrecall  = 425*24/500,0/500,425/500,0/2500        ; unspecified in IDA reference, use repeating Holding Tone A,B
 | 
					 | 
				
			||||||
record      = 1400/500,0/15000                       ; unspecified in IDA reference, use 0.5s tone every 15s
 | 
					 | 
				
			||||||
; additionally defined in reference
 | 
					 | 
				
			||||||
nutone      = 425/2500,0/500
 | 
					 | 
				
			||||||
intrusion   = 425/250,0/2000
 | 
					 | 
				
			||||||
warning     = 425/624,0/4376                         ; end of period tone, warning
 | 
					 | 
				
			||||||
acceptance  = 425/125,0/125
 | 
					 | 
				
			||||||
holdinga    = !425*24/500,!0/500                     ; followed by holdingb
 | 
					 | 
				
			||||||
holdingb    = !425/500,!0/2500
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[th]
 | 
					 | 
				
			||||||
description = Thailand
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
dial = 400*50
 | 
					 | 
				
			||||||
busy = 400/500,0/500
 | 
					 | 
				
			||||||
ring = 420/1000,0/5000
 | 
					 | 
				
			||||||
congestion = 400/300,0/300
 | 
					 | 
				
			||||||
callwaiting = 1000/400,10000/400,1000/400
 | 
					 | 
				
			||||||
; DIALRECALL - not specified - use special dial tone instead.
 | 
					 | 
				
			||||||
dialrecall = 400*50/400,0/100,400*50/400,0/100
 | 
					 | 
				
			||||||
; RECORDTONE - not specified
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
; INFO - specified as an announcement - use special information tones instead
 | 
					 | 
				
			||||||
info = 950/330,1400/330,1800/330
 | 
					 | 
				
			||||||
; STUTTER - not specified
 | 
					 | 
				
			||||||
stutter = !400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,400
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[uk]
 | 
					 | 
				
			||||||
description = United Kingdom
 | 
					 | 
				
			||||||
ringcadence = 400,200,400,2000
 | 
					 | 
				
			||||||
; These are the official tones taken from BT SIN350. The actual tones
 | 
					 | 
				
			||||||
; used by BT include some volume differences so sound slightly different
 | 
					 | 
				
			||||||
; from Asterisk-generated ones.
 | 
					 | 
				
			||||||
dial = 350+440
 | 
					 | 
				
			||||||
; Special dial is the intermittent dial tone heard when, for example,
 | 
					 | 
				
			||||||
; you have a divert active on the line
 | 
					 | 
				
			||||||
specialdial = 350+440/750,440/750
 | 
					 | 
				
			||||||
; Busy is also called "Engaged"
 | 
					 | 
				
			||||||
busy = 400/375,0/375
 | 
					 | 
				
			||||||
; "Congestion" is the Beep-bip engaged tone
 | 
					 | 
				
			||||||
congestion = 400/400,0/350,400/225,0/525
 | 
					 | 
				
			||||||
; "Special Congestion" is not used by BT very often if at all
 | 
					 | 
				
			||||||
specialcongestion = 400/200,1004/300
 | 
					 | 
				
			||||||
unobtainable = 400
 | 
					 | 
				
			||||||
ring = 400+450/400,0/200,400+450/400,0/2000
 | 
					 | 
				
			||||||
callwaiting = 400/100,0/4000
 | 
					 | 
				
			||||||
; BT seem to use "Special Call Waiting" rather than just "Call Waiting" tones
 | 
					 | 
				
			||||||
specialcallwaiting = 400/250,0/250,400/250,0/250,400/250,0/5000
 | 
					 | 
				
			||||||
; "Pips" used by BT on payphones. (Sounds wrong, but this is what BT claim it
 | 
					 | 
				
			||||||
; is and I've not used a payphone for years)
 | 
					 | 
				
			||||||
creditexpired = 400/125,0/125
 | 
					 | 
				
			||||||
; These two are used to confirm/reject service requests on exchanges that
 | 
					 | 
				
			||||||
; don't do voice announcements.
 | 
					 | 
				
			||||||
confirm = 1400
 | 
					 | 
				
			||||||
switching = 400/200,0/400,400/2000,0/400
 | 
					 | 
				
			||||||
; This is the three rising tones Doo-dah-dee "Special Information Tone",
 | 
					 | 
				
			||||||
; usually followed by the BT woman saying an appropriate message.
 | 
					 | 
				
			||||||
info = 950/330,0/15,1400/330,0/15,1800/330,0/1000
 | 
					 | 
				
			||||||
; Not listed in SIN350
 | 
					 | 
				
			||||||
record = 1400/500,0/60000
 | 
					 | 
				
			||||||
stutter = 350+440/750,440/750
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[us]
 | 
					 | 
				
			||||||
description = United States / North America
 | 
					 | 
				
			||||||
ringcadence = 2000,4000
 | 
					 | 
				
			||||||
dial = 350+440
 | 
					 | 
				
			||||||
busy = 480+620/500,0/500
 | 
					 | 
				
			||||||
ring = 440+480/2000,0/4000
 | 
					 | 
				
			||||||
congestion = 480+620/250,0/250
 | 
					 | 
				
			||||||
callwaiting = 440/300,0/10000
 | 
					 | 
				
			||||||
dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = !950/330,!1400/330,!1800/330,0
 | 
					 | 
				
			||||||
stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[us-old]
 | 
					 | 
				
			||||||
description = United States Circa 1950/ North America
 | 
					 | 
				
			||||||
ringcadence = 2000,4000
 | 
					 | 
				
			||||||
dial = 600*120
 | 
					 | 
				
			||||||
busy = 500*100/500,0/500
 | 
					 | 
				
			||||||
ring = 420*40/2000,0/4000
 | 
					 | 
				
			||||||
congestion = 500*100/250,0/250
 | 
					 | 
				
			||||||
callwaiting = 440/300,0/10000
 | 
					 | 
				
			||||||
dialrecall = !600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = !950/330,!1400/330,!1800/330,0
 | 
					 | 
				
			||||||
stutter = !600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[tw]
 | 
					 | 
				
			||||||
description = Taiwan
 | 
					 | 
				
			||||||
; http://nemesis.lonestar.org/reference/telecom/signaling/dialtone.html
 | 
					 | 
				
			||||||
; http://nemesis.lonestar.org/reference/telecom/signaling/busy.html
 | 
					 | 
				
			||||||
; http://www.iproducts.com.tw/ee/kylink/06ky-1000a.htm
 | 
					 | 
				
			||||||
; http://www.pbx-manufacturer.com/ky120dx.htm
 | 
					 | 
				
			||||||
; http://www.nettwerked.net/tones.txt
 | 
					 | 
				
			||||||
; http://www.cisco.com/univercd/cc/td/doc/product/tel_pswt/vco_prod/taiw_sup/taiw2.htm
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; busy tone 480+620Hz 0.5 sec. on ,0.5 sec. off
 | 
					 | 
				
			||||||
; reorder tone 480+620Hz 0.25 sec. on,0.25 sec. off
 | 
					 | 
				
			||||||
; ringing tone 440+480Hz 1 sec. on ,2 sec. off
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 350+440
 | 
					 | 
				
			||||||
busy = 480+620/500,0/500
 | 
					 | 
				
			||||||
ring = 440+480/1000,0/2000
 | 
					 | 
				
			||||||
congestion = 480+620/250,0/250
 | 
					 | 
				
			||||||
callwaiting = 350+440/250,0/250,350+440/250,0/3250
 | 
					 | 
				
			||||||
dialrecall = 300/1500,0/500
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = !950/330,!1400/330,!1800/330,0
 | 
					 | 
				
			||||||
stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[ve]
 | 
					 | 
				
			||||||
; Tone definition source for ve found on 
 | 
					 | 
				
			||||||
; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
 | 
					 | 
				
			||||||
description = Venezuela / South America
 | 
					 | 
				
			||||||
ringcadence = 1000,4000
 | 
					 | 
				
			||||||
dial = 425
 | 
					 | 
				
			||||||
busy = 425/500,0/500
 | 
					 | 
				
			||||||
ring = 425/1000,0/4000
 | 
					 | 
				
			||||||
congestion = 425/250,0/250
 | 
					 | 
				
			||||||
callwaiting = 400+450/300,0/6000
 | 
					 | 
				
			||||||
dialrecall = 425
 | 
					 | 
				
			||||||
record = 1400/500,0/15000
 | 
					 | 
				
			||||||
info = !950/330,!1440/330,!1800/330,0/1000
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[za]
 | 
					 | 
				
			||||||
description = South Africa
 | 
					 | 
				
			||||||
; http://www.cisco.com/univercd/cc/td/doc/product/tel_pswt/vco_prod/safr_sup/saf02.htm
 | 
					 | 
				
			||||||
; (definitions for other countries can also be found there)
 | 
					 | 
				
			||||||
; Note, though, that South Africa uses two switch types in their network --
 | 
					 | 
				
			||||||
; Alcatel switches -- mainly in the Western Cape, and Siemens elsewhere.
 | 
					 | 
				
			||||||
; The former use 383+417 in dial, ringback etc.  The latter use 400*33
 | 
					 | 
				
			||||||
; I've provided both, uncomment the ones you prefer
 | 
					 | 
				
			||||||
ringcadence = 400,200,400,2000
 | 
					 | 
				
			||||||
; dial/ring/callwaiting for the Siemens switches:
 | 
					 | 
				
			||||||
dial = 400*33
 | 
					 | 
				
			||||||
ring = 400*33/400,0/200,400*33/400,0/2000
 | 
					 | 
				
			||||||
callwaiting = 400*33/250,0/250,400*33/250,0/250,400*33/250,0/250,400*33/250,0/250
 | 
					 | 
				
			||||||
; dial/ring/callwaiting for the Alcatel switches:
 | 
					 | 
				
			||||||
; dial = 383+417
 | 
					 | 
				
			||||||
; ring = 383+417/400,0/200,383+417/400,0/2000
 | 
					 | 
				
			||||||
; callwaiting = 383+417/250,0/250,383+417/250,0/250,383+417/250,0/250,383+417/250,0/250
 | 
					 | 
				
			||||||
congestion = 400/250,0/250
 | 
					 | 
				
			||||||
busy = 400/500,0/500
 | 
					 | 
				
			||||||
dialrecall = 350+440
 | 
					 | 
				
			||||||
; XXX Not sure about the RECORDTONE
 | 
					 | 
				
			||||||
record = 1400/500,0/10000
 | 
					 | 
				
			||||||
info = 950/330,1400/330,1800/330,0/330
 | 
					 | 
				
			||||||
stutter = !400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,400*33
 | 
					 | 
				
			||||||
@@ -1,69 +0,0 @@
 | 
				
			|||||||
;
 | 
					 | 
				
			||||||
; Logging Configuration
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; In this file, you configure logging to files or to
 | 
					 | 
				
			||||||
; the syslog system.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; "logger reload" at the CLI will reload configuration
 | 
					 | 
				
			||||||
; of the logging system.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[general]
 | 
					 | 
				
			||||||
; Customize the display of debug message time stamps
 | 
					 | 
				
			||||||
; this example is the ISO 8601 date format (yyyy-mm-dd HH:MM:SS)
 | 
					 | 
				
			||||||
; see strftime(3) Linux manual for format specifiers
 | 
					 | 
				
			||||||
;dateformat=%F %T
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; This appends the hostname to the name of the log files.
 | 
					 | 
				
			||||||
;appendhostname = yes
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; This determines whether or not we log queue events to a file
 | 
					 | 
				
			||||||
; (defaults to yes).
 | 
					 | 
				
			||||||
;queue_log = no
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; This determines whether or not we log generic events to a file
 | 
					 | 
				
			||||||
; (defaults to yes).
 | 
					 | 
				
			||||||
;event_log = no
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; For each file, specify what to log.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; For console logging, you set options at start of
 | 
					 | 
				
			||||||
; Asterisk with -v for verbose and -d for debug
 | 
					 | 
				
			||||||
; See 'asterisk -h' for more information.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; Directory for log files is configures in asterisk.conf
 | 
					 | 
				
			||||||
; option astlogdir
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
[logfiles]
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; Format is "filename" and then "levels" of debugging to be included:
 | 
					 | 
				
			||||||
;    debug
 | 
					 | 
				
			||||||
;    notice
 | 
					 | 
				
			||||||
;    warning
 | 
					 | 
				
			||||||
;    error
 | 
					 | 
				
			||||||
;    verbose
 | 
					 | 
				
			||||||
;    dtmf
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; Special filename "console" represents the system console
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; We highly recommend that you DO NOT turn on debug mode if you are simply
 | 
					 | 
				
			||||||
; running a production system.  Debug mode turns on a LOT of extra messages,
 | 
					 | 
				
			||||||
; most of which you are unlikely to understand without an understanding of
 | 
					 | 
				
			||||||
; the underlying code.  Do NOT report debug messages as code issues, unless
 | 
					 | 
				
			||||||
; you have a specific issue that you are attempting to debug.  They are
 | 
					 | 
				
			||||||
; messages for just that -- debugging -- and do not rise to the level of
 | 
					 | 
				
			||||||
; something that merit your attention as an Asterisk administrator.  Debug
 | 
					 | 
				
			||||||
; messages are also very verbose and can and do fill up logfiles quickly;
 | 
					 | 
				
			||||||
; this is another reason not to have debug mode on a production system unless
 | 
					 | 
				
			||||||
; you are in the process of debugging a specific issue.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
;debug => debug
 | 
					 | 
				
			||||||
console => notice,warning,error
 | 
					 | 
				
			||||||
;console => notice,warning,error,debug
 | 
					 | 
				
			||||||
;messages => notice,warning,error
 | 
					 | 
				
			||||||
;full => notice,warning,error,debug,verbose
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
;syslog keyword : This special keyword logs to syslog facility 
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
;syslog.local0 => notice,warning,error
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
@@ -1,36 +0,0 @@
 | 
				
			|||||||
;
 | 
					 | 
				
			||||||
; Asterisk configuration file
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; Module Loader configuration file
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[modules]
 | 
					 | 
				
			||||||
autoload=yes
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; Any modules that need to be loaded before the Asterisk core has been
 | 
					 | 
				
			||||||
; initialized (just after the logger has been initialized) can be loaded
 | 
					 | 
				
			||||||
; using 'preload'. This will frequently be needed if you wish to map all
 | 
					 | 
				
			||||||
; module configuration files into Realtime storage, since the Realtime
 | 
					 | 
				
			||||||
; driver will need to be loaded before the modules using those configuration
 | 
					 | 
				
			||||||
; files are initialized.
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; An example of loading ODBC support would be:
 | 
					 | 
				
			||||||
;preload => res_odbc.so
 | 
					 | 
				
			||||||
;preload => res_config_odbc.so
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; Uncomment the following if you wish to use the Speech Recognition API
 | 
					 | 
				
			||||||
;preload => res_speech.so
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; If you want, load the GTK console right away.  
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
noload => pbx_gtkconsole.so
 | 
					 | 
				
			||||||
;load => pbx_gtkconsole.so
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
noload => res_musiconhold.so
 | 
					 | 
				
			||||||
;load => res_musiconhold.so
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
; Load either OSS or ALSA, not both
 | 
					 | 
				
			||||||
; By default, load OSS only (automatically) and do not load ALSA
 | 
					 | 
				
			||||||
;
 | 
					 | 
				
			||||||
noload => chan_alsa.so
 | 
					 | 
				
			||||||
;noload => chan_oss.so
 | 
					 | 
				
			||||||
@@ -1,91 +0,0 @@
 | 
				
			|||||||
[general]
 | 
					 | 
				
			||||||
bindport=5060			; asterisk 1.6
 | 
					 | 
				
			||||||
				; UDP Port to bind to (SIP standard port for unencrypted UDP 
 | 
					 | 
				
			||||||
				; and TCP sessions is 5060)
 | 
					 | 
				
			||||||
				; bindport is the local UDP port that Asterisk will listen on
 | 
					 | 
				
			||||||
bindaddr=0.0.0.0		; asterisk 1.6
 | 
					 | 
				
			||||||
				; IP address to bind UDP listen socket to (0.0.0.0 binds to all)
 | 
					 | 
				
			||||||
				; You can specify port here too, like 123.123.123.123:5080
 | 
					 | 
				
			||||||
udpbindaddr=0.0.0.0		; asterisk 1.8
 | 
					 | 
				
			||||||
				; IP address to bind UDP listen socket to (0.0.0.0 binds to all)
 | 
					 | 
				
			||||||
				; Optionally add a port number, 192.168.1.1:5062 (default is port 5060)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
tos_sip=cs3			; Sets TOS for SIP packets.
 | 
					 | 
				
			||||||
tos_audio=ef			; Sets TOS for RTP audio packets.
 | 
					 | 
				
			||||||
tos_video=af41			; Sets TOS for RTP video packets.
 | 
					 | 
				
			||||||
tos_text=af41			; Sets TOS for RTP text packets.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
cos_sip=3			; Sets 802.1p priority for SIP packets.
 | 
					 | 
				
			||||||
cos_audio=5			; Sets 802.1p priority for RTP audio packets.
 | 
					 | 
				
			||||||
cos_video=4			; Sets 802.1p priority for RTP video packets.
 | 
					 | 
				
			||||||
cos_text=3			; Sets 802.1p priority for RTP text packets.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
maxexpiry=3600			; Maximum allowed time of incoming registrations
 | 
					 | 
				
			||||||
				; and subscriptions (seconds)
 | 
					 | 
				
			||||||
minexpiry=60			; Minimum length of registrations/subscriptions (default 60)
 | 
					 | 
				
			||||||
defaultexpiry=3600		; Default length of incoming/outgoing registration
 | 
					 | 
				
			||||||
dynamic_exclude_static=yes	; Disallow all dynamic hosts from registering
 | 
					 | 
				
			||||||
				; as any IP address used for staticly defined
 | 
					 | 
				
			||||||
				; hosts.  This helps avoid the configuration
 | 
					 | 
				
			||||||
				; error of allowing your users to register at
 | 
					 | 
				
			||||||
				; the same address as a SIP provider.
 | 
					 | 
				
			||||||
use_q850_reason=yes		; Set to yes add Reason header and use Reason header if it is available.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
;t1min=100			; Minimum roundtrip time for messages to monitored hosts
 | 
					 | 
				
			||||||
				; Defaults to 100 ms
 | 
					 | 
				
			||||||
;timert1=500			; Default T1 timer
 | 
					 | 
				
			||||||
				; Defaults to 500 ms or the measured round-trip
 | 
					 | 
				
			||||||
				; time to a peer (qualify=yes).
 | 
					 | 
				
			||||||
;timerb=32000			; Call setup timer. If a provisional response is not received
 | 
					 | 
				
			||||||
				; in this amount of time, the call will autocongest
 | 
					 | 
				
			||||||
				; Defaults to 64*timert1
 | 
					 | 
				
			||||||
rtptimeout=60			; Terminate call if 60 seconds of no RTP or RTCP activity
 | 
					 | 
				
			||||||
				; on the audio channel
 | 
					 | 
				
			||||||
				; when we're not on hold. This is to be able to hangup
 | 
					 | 
				
			||||||
				; a call in the case of a phone disappearing from the net,
 | 
					 | 
				
			||||||
				; like a powerloss or grandma tripping over a cable.
 | 
					 | 
				
			||||||
rtpholdtimeout=300		; Terminate call if 300 seconds of no RTP or RTCP activity
 | 
					 | 
				
			||||||
				; on the audio channel
 | 
					 | 
				
			||||||
				; when we're on hold (must be > rtptimeout)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
;allowguest=no			; Allow or reject guest calls (default is yes)
 | 
					 | 
				
			||||||
autocreatepeer=yes		; The Autocreatepeer option allows, 
 | 
					 | 
				
			||||||
				; if set to Yes, any SIP ua to register with your Asterisk PBX as a peer. 
 | 
					 | 
				
			||||||
				; This peer's settings will be based on global options. 
 | 
					 | 
				
			||||||
				; The peer's name will be based on the user part of the Contact: header field's URL. 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
context=phones			; Default context for incoming calls
 | 
					 | 
				
			||||||
allowoverlap=no			; Disable overlap dialing support. (Default is yes)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
disallow=all			; need to disallow=all before we can use allow=
 | 
					 | 
				
			||||||
allow=gsm			; GSM
 | 
					 | 
				
			||||||
allow=ulaw			; ISDN US
 | 
					 | 
				
			||||||
allow=alaw			; ISDN EU
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
relaxdtmf=yes			; Relax dtmf handling
 | 
					 | 
				
			||||||
dtmfmode=auto			; Set default dtmfmode for sending DTMF. Default: rfc2833
 | 
					 | 
				
			||||||
				; Other options:
 | 
					 | 
				
			||||||
				; info : SIP INFO messages (application/dtmf-relay)
 | 
					 | 
				
			||||||
				; shortinfo : SIP INFO messages (application/dtmf)
 | 
					 | 
				
			||||||
				; inband : Inband audio (requires 64 kbit codec -alaw, ulaw)
 | 
					 | 
				
			||||||
				; auto : Use rfc2833 if offered, inband otherwise
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; Zoiper is used as a fixture for factory testing.
 | 
					 | 
				
			||||||
[zoiper]
 | 
					 | 
				
			||||||
secret=3078923984
 | 
					 | 
				
			||||||
callerid=2101
 | 
					 | 
				
			||||||
canreinvite=no
 | 
					 | 
				
			||||||
type=friend
 | 
					 | 
				
			||||||
context=sip-local
 | 
					 | 
				
			||||||
host=dynamic
 | 
					 | 
				
			||||||
dtmfmode=auto
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
; This is a test SIM provided with the BTS.
 | 
					 | 
				
			||||||
[IMSI001010000000000]
 | 
					 | 
				
			||||||
callerid=2100
 | 
					 | 
				
			||||||
canreinvite=no
 | 
					 | 
				
			||||||
type=friend
 | 
					 | 
				
			||||||
context=sip-local
 | 
					 | 
				
			||||||
host=dynamic
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										748
									
								
								CLI/CLI.cpp
									
									
									
									
									
								
							
							
						
						
									
										748
									
								
								CLI/CLI.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										24
									
								
								CLI/CLI.h
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								CLI/CLI.h
									
									
									
									
									
								
							@@ -1,25 +1,15 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
					* Copyright 2009 Free Software Foundation, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
*
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					    GNU General Public License for more details.
 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -71,7 +61,13 @@ class Parser {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	private:
 | 
						private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Parse and execute a command string. */
 | 
						/**
 | 
				
			||||||
 | 
							Parse and execute a command string.
 | 
				
			||||||
 | 
							@line a writeable copy of the original line
 | 
				
			||||||
 | 
							@cline the original line
 | 
				
			||||||
 | 
							@os output stream
 | 
				
			||||||
 | 
							@return status code
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
	int execute(char* line, std::ostream& os) const;
 | 
						int execute(char* line, std::ostream& os) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								COPYING
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								COPYING
									
									
									
									
									
								
							@@ -693,6 +693,14 @@ Interaction; Use with the GNU General Public License").  This exemption of
 | 
				
			|||||||
interfaces other than the GSM air interface from the requirements of Section 13
 | 
					interfaces other than the GSM air interface from the requirements of Section 13
 | 
				
			||||||
is an additional permission granted to you.  
 | 
					is an additional permission granted to you.  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2.  GSM "A5" cipher stream generation libraries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Notwithstanding any other provision of this License, you have
 | 
				
			||||||
 | 
					permission to link the Program with GSM "A5" cipher-stream generation
 | 
				
			||||||
 | 
					libraries provided under any license that allows redistribution of
 | 
				
			||||||
 | 
					those libraries in binary form, provided that the function of any such
 | 
				
			||||||
 | 
					library is limited to the generation of cipher-steam bits.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Non-Permissive Terms Supplementing The License
 | 
					Non-Permissive Terms Supplementing The License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -704,11 +712,9 @@ license does not include the right to use the OpenBTS trademark in commerce.
 | 
				
			|||||||
This additional non-permissive term is consistent with Section 7 of the AGPLv3
 | 
					This additional non-permissive term is consistent with Section 7 of the AGPLv3
 | 
				
			||||||
license.
 | 
					license.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
END OF ADDITIONAL TERMS
 | 
					END OF ADDITIONAL TERMS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
How to comply with Section 13 of the AGPLv3 license.
 | 
					How to comply with Section 13 of the AGPLv3 license.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The recommended method for compliance with Section 13 of the AGPLv3 license is
 | 
					The recommended method for compliance with Section 13 of the AGPLv3 license is
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,26 +2,18 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
* Copyright 2011 Range Networks, Inc.
 | 
					* Copyright 2011, 2012 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -96,6 +88,9 @@ unsigned allocateRTPPorts()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
	Force clearing on the GSM side.
 | 
						Force clearing on the GSM side.
 | 
				
			||||||
	@param transaction The call transaction record.
 | 
						@param transaction The call transaction record.
 | 
				
			||||||
@@ -104,17 +99,22 @@ unsigned allocateRTPPorts()
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
void forceGSMClearing(TransactionEntry *transaction, GSM::LogicalChannel *LCH, const GSM::L3Cause& cause)
 | 
					void forceGSMClearing(TransactionEntry *transaction, GSM::LogicalChannel *LCH, const GSM::L3Cause& cause)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						LOG(DEBUG);
 | 
				
			||||||
	LOG(INFO) << "Q.931 state " << transaction->GSMState();
 | 
						LOG(INFO) << "Q.931 state " << transaction->GSMState();
 | 
				
			||||||
	// Already cleared?
 | 
						// Already cleared?
 | 
				
			||||||
	if (transaction->GSMState()==GSM::NullState) return;
 | 
						if (transaction->GSMState()==GSM::NullState) return;
 | 
				
			||||||
	// Clearing not started?  Start it.
 | 
						// Clearing not started?  Start it.
 | 
				
			||||||
	if (!transaction->clearingGSM()) LCH->send(GSM::L3Disconnect(transaction->L3TI(),cause));
 | 
						if (!transaction->clearingGSM()) LCH->send(GSM::L3Disconnect(transaction->L3TI(),cause));
 | 
				
			||||||
	// Force the rest.
 | 
						// Force the rest.
 | 
				
			||||||
 | 
						LOG(DEBUG);
 | 
				
			||||||
	LCH->send(GSM::L3ReleaseComplete(transaction->L3TI()));
 | 
						LCH->send(GSM::L3ReleaseComplete(transaction->L3TI()));
 | 
				
			||||||
	LCH->send(GSM::L3ChannelRelease());
 | 
						LCH->send(GSM::L3ChannelRelease());
 | 
				
			||||||
 | 
						LOG(DEBUG);
 | 
				
			||||||
	transaction->resetTimers();
 | 
						transaction->resetTimers();
 | 
				
			||||||
	transaction->GSMState(GSM::NullState);
 | 
						transaction->GSMState(GSM::NullState);
 | 
				
			||||||
	LCH->send(GSM::RELEASE);
 | 
						LOG(DEBUG);
 | 
				
			||||||
 | 
						//LCH->send(GSM::RELEASE);
 | 
				
			||||||
 | 
						//LOG(DEBUG);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -129,9 +129,10 @@ void forceSIPClearing(TransactionEntry *transaction)
 | 
				
			|||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOG(DEBUG);
 | 
				
			||||||
	SIP::SIPState state = transaction->SIPState();
 | 
						SIP::SIPState state = transaction->SIPState();
 | 
				
			||||||
	LOG(INFO) << "SIP state " << state;
 | 
						LOG(INFO) << "SIP state " << state;
 | 
				
			||||||
	//why aren't we checking for failed here? -kurtis
 | 
						//why aren't we checking for failed here? -kurtis ; we are now. -david
 | 
				
			||||||
	if (transaction->SIPFinished()) return;
 | 
						if (transaction->SIPFinished()) return;
 | 
				
			||||||
	if (state==SIP::Active){
 | 
						if (state==SIP::Active){
 | 
				
			||||||
		//Changes state to clearing
 | 
							//Changes state to clearing
 | 
				
			||||||
@@ -283,6 +284,7 @@ bool assignTCHF(TransactionEntry *transaction, GSM::LogicalChannel *DCCH, GSM::T
 | 
				
			|||||||
	
 | 
						
 | 
				
			||||||
	// Shut down the SIP side of the call.
 | 
						// Shut down the SIP side of the call.
 | 
				
			||||||
	forceSIPClearing(transaction);
 | 
						forceSIPClearing(transaction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Indicate failure.
 | 
						// Indicate failure.
 | 
				
			||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -301,7 +303,7 @@ bool assignTCHF(TransactionEntry *transaction, GSM::LogicalChannel *DCCH, GSM::T
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
bool callManagementDispatchGSM(TransactionEntry *transaction, GSM::LogicalChannel* LCH, const GSM::L3Message *message)
 | 
					bool callManagementDispatchGSM(TransactionEntry *transaction, GSM::LogicalChannel* LCH, const GSM::L3Message *message)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	LOG(DEBUG) << "from " << transaction->subscriber() << " message " << *message;
 | 
						if (message) { LOG(DEBUG) << "from " << transaction->subscriber() << " message " << *message; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// FIXME -- This dispatch section should be something more efficient with PD and MTI swtiches.
 | 
						// FIXME -- This dispatch section should be something more efficient with PD and MTI swtiches.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -365,7 +367,7 @@ bool callManagementDispatchGSM(TransactionEntry *transaction, GSM::LogicalChanne
 | 
				
			|||||||
			LOG(NOTICE) << "abnormal terminatation: " << *disc;
 | 
								LOG(NOTICE) << "abnormal terminatation: " << *disc;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		/* late RLLP request */
 | 
							/* late RLLP request */
 | 
				
			||||||
		if (normal && !early && gConfig.defines("Control.Call.QueryRRLP.Late")) {
 | 
							if (normal && !early && gConfig.getBool("Control.Call.QueryRRLP.Late")) {
 | 
				
			||||||
			// Query for RRLP
 | 
								// Query for RRLP
 | 
				
			||||||
			if (!sendRRLP(transaction->subscriber(), LCH)) {
 | 
								if (!sendRRLP(transaction->subscriber(), LCH)) {
 | 
				
			||||||
				LOG(INFO) << "RRLP request failed";
 | 
									LOG(INFO) << "RRLP request failed";
 | 
				
			||||||
@@ -408,11 +410,14 @@ bool callManagementDispatchGSM(TransactionEntry *transaction, GSM::LogicalChanne
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Release (2nd step of MTD)
 | 
						// Release (2nd step of MTD)
 | 
				
			||||||
	if (dynamic_cast<const GSM::L3Release*>(message)) {
 | 
						if (const GSM::L3Release *rls = dynamic_cast<const GSM::L3Release*>(message)) {
 | 
				
			||||||
		LOG(INFO) << "GSM Release " << *transaction;
 | 
							LOG(INFO) << "GSM Release " << *transaction;
 | 
				
			||||||
		gReports.incr("OpenBTS.GSM.CC.MTD.Release");
 | 
							gReports.incr("OpenBTS.GSM.CC.MTD.Release");
 | 
				
			||||||
 | 
							if (rls->haveCause() && (rls->cause().cause() > 0x10)) {
 | 
				
			||||||
 | 
								LOG(NOTICE) << "abnormal terminatation: " << *rls;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		/* late RLLP request */
 | 
							/* late RLLP request */
 | 
				
			||||||
		if (gConfig.defines("Control.Call.QueryRRLP.Late")) {
 | 
							if (gConfig.getBool("Control.Call.QueryRRLP.Late")) {
 | 
				
			||||||
			// Query for RRLP
 | 
								// Query for RRLP
 | 
				
			||||||
			if (!sendRRLP(transaction->subscriber(), LCH)) {
 | 
								if (!sendRRLP(transaction->subscriber(), LCH)) {
 | 
				
			||||||
				LOG(INFO) << "RRLP request failed";
 | 
									LOG(INFO) << "RRLP request failed";
 | 
				
			||||||
@@ -524,6 +529,14 @@ bool callManagementDispatchGSM(TransactionEntry *transaction, GSM::LogicalChanne
 | 
				
			|||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dynamic_cast<const GSM::L3CipheringModeComplete*>(message)) {
 | 
				
			||||||
 | 
							LOG(DEBUG) << "received Ciphering Mode Complete on " << *LCH << " for " << transaction->subscriber();
 | 
				
			||||||
 | 
							// Although the spec (04.08 3.4.7) says you can start ciphering the downlink at this time,
 | 
				
			||||||
 | 
							// it also says you can start when you successfully decrypt an uplink layer 2 frame,
 | 
				
			||||||
 | 
							// which is what we do.
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Stubs for unsupported features.
 | 
						// Stubs for unsupported features.
 | 
				
			||||||
	// We need to answer the handset so it doesn't hang.
 | 
						// We need to answer the handset so it doesn't hang.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -535,8 +548,11 @@ bool callManagementDispatchGSM(TransactionEntry *transaction, GSM::LogicalChanne
 | 
				
			|||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (message)  { LOG(NOTICE) << "no support for message " << *message << " from " << transaction->subscriber(); }
 | 
						if (message) {
 | 
				
			||||||
	else { LOG(NOTICE) << "no support for unrecognized message from " << transaction->subscriber(); }
 | 
							LOG(NOTICE) << "no support for message " << *message << " from " << transaction->subscriber();
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							LOG(NOTICE) << "no support for unrecognized message from " << transaction->subscriber();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If we got here, we're ignoring the message.
 | 
						// If we got here, we're ignoring the message.
 | 
				
			||||||
@@ -641,6 +657,7 @@ bool updateSIPSignalling(TransactionEntry *transaction, GSM::LogicalChannel *LCH
 | 
				
			|||||||
	if (transaction->SIPFinished()) return true;
 | 
						if (transaction->SIPFinished()) return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool GSMClearedOrClearing = GSMCleared || transaction->clearingGSM();
 | 
						bool GSMClearedOrClearing = GSMCleared || transaction->clearingGSM();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//only checking for Clearing because the call is active at this state. Should not cancel
 | 
						//only checking for Clearing because the call is active at this state. Should not cancel
 | 
				
			||||||
	if (transaction->MTDCheckBYE() == SIP::MTDClearing) {
 | 
						if (transaction->MTDCheckBYE() == SIP::MTDClearing) {
 | 
				
			||||||
		LOG(DEBUG) << "got SIP BYE " << *transaction;
 | 
							LOG(DEBUG) << "got SIP BYE " << *transaction;
 | 
				
			||||||
@@ -679,6 +696,61 @@ bool updateSignalling(TransactionEntry *transaction, GSM::LogicalChannel *LCH, u
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool outboundHandoverTransfer(TransactionEntry* transaction, GSM::TCHFACCHLogicalChannel *TCH)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// By returning true, this function indicates to its caller that the call is cleared
 | 
				
			||||||
 | 
						// and no longer needs a channel on this BTS.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// In this method, we are "BS1" in the ladder diagram.
 | 
				
			||||||
 | 
						// BS2 has alrady accepted the handover request.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Send the handover command.
 | 
				
			||||||
 | 
						TCH->send(GSM::L3HandoverCommand(
 | 
				
			||||||
 | 
							transaction->outboundCell(),
 | 
				
			||||||
 | 
							transaction->outboundChannel(),
 | 
				
			||||||
 | 
							transaction->outboundReference(),
 | 
				
			||||||
 | 
							transaction->outboundPowerCmd(),
 | 
				
			||||||
 | 
							transaction->outboundSynch()
 | 
				
			||||||
 | 
							));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Start a timer for T3103, the handover failure timer.
 | 
				
			||||||
 | 
						GSM::Z100Timer T3103(gConfig.getNum("GSM.Timer.T3103"));
 | 
				
			||||||
 | 
						T3103.set();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The next step for the MS is to send Handover Access to BS2.
 | 
				
			||||||
 | 
						// The next step for us is to wait for the Handover Complete message
 | 
				
			||||||
 | 
						// and see that the phone doesn't come back to us.
 | 
				
			||||||
 | 
						// BS2 is doing most of the work now.
 | 
				
			||||||
 | 
						// We will get a handover complete once it's over, but we don't really need it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Q: What about transferring audio packets?
 | 
				
			||||||
 | 
						// A: There should not be any after we send the Handover Command.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get the response.
 | 
				
			||||||
 | 
						// This is supposed to time out on successful handover, similar to the early assignment channel transfer..
 | 
				
			||||||
 | 
						GSM::L3Frame *result = TCH->recv(T3103.remaining());
 | 
				
			||||||
 | 
						if (result) {
 | 
				
			||||||
 | 
							// If we got here, the handover failed and we just keep running the call.
 | 
				
			||||||
 | 
							LOG(NOTICE) << "failed handover, received " << *result;
 | 
				
			||||||
 | 
							delete result;
 | 
				
			||||||
 | 
							// Restore the call state.
 | 
				
			||||||
 | 
							transaction->GSMState(GSM::Active);
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If the phone doesn't come back, either the handover succeeded or
 | 
				
			||||||
 | 
						// the phone dropped the connection.  Either way, we are clearing the call.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Invalidate local cache entry for this IMSI in the subscriber registry.
 | 
				
			||||||
 | 
						string imsi = string("IMSI").append(transaction->subscriber().digits());
 | 
				
			||||||
 | 
						gSubscriberRegistry.removeUser(imsi.c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOG(INFO) "timeout following outbound handover; exiting normally";
 | 
				
			||||||
 | 
						TCH->send(GSM::HARDRELEASE);
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
	Poll for activity while in a call.
 | 
						Poll for activity while in a call.
 | 
				
			||||||
	Sleep if needed to prevent fast spinning.
 | 
						Sleep if needed to prevent fast spinning.
 | 
				
			||||||
@@ -689,9 +761,11 @@ bool updateSignalling(TransactionEntry *transaction, GSM::LogicalChannel *LCH, u
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
bool pollInCall(TransactionEntry *transaction, GSM::TCHFACCHLogicalChannel *TCH)
 | 
					bool pollInCall(TransactionEntry *transaction, GSM::TCHFACCHLogicalChannel *TCH)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// See if the radio link disappeared.
 | 
						// See if the radio link disappeared.
 | 
				
			||||||
	if (TCH->radioFailure()) {
 | 
						if (TCH->radioFailure()) {
 | 
				
			||||||
		LOG(NOTICE) << "radio link failure, dropped call";
 | 
							LOG(NOTICE) << "radio link failure, dropped call";
 | 
				
			||||||
 | 
							gReports.incr("OpenBTS.GSM.CC.DroppedCalls");
 | 
				
			||||||
		forceSIPClearing(transaction);
 | 
							forceSIPClearing(transaction);
 | 
				
			||||||
		return true;
 | 
							return true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -700,6 +774,10 @@ bool pollInCall(TransactionEntry *transaction, GSM::TCHFACCHLogicalChannel *TCH)
 | 
				
			|||||||
	// If this returns true, it means the call is fully cleared.
 | 
						// If this returns true, it means the call is fully cleared.
 | 
				
			||||||
	if (updateSignalling(transaction,TCH)) return true;
 | 
						if (updateSignalling(transaction,TCH)) return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check for outbound handover.
 | 
				
			||||||
 | 
						if (transaction->GSMState() == GSM::HandoverOutbound)
 | 
				
			||||||
 | 
							return outboundHandoverTransfer(transaction,TCH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Did an outside process request a termination?
 | 
						// Did an outside process request a termination?
 | 
				
			||||||
	if (transaction->terminationRequested()) {
 | 
						if (transaction->terminationRequested()) {
 | 
				
			||||||
		// Cause 25 is "pre-emptive clearing".
 | 
							// Cause 25 is "pre-emptive clearing".
 | 
				
			||||||
@@ -714,6 +792,7 @@ bool pollInCall(TransactionEntry *transaction, GSM::TCHFACCHLogicalChannel *TCH)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Transfer vocoder data.
 | 
						// Transfer vocoder data.
 | 
				
			||||||
	// If anything happened, then the call is still up.
 | 
						// If anything happened, then the call is still up.
 | 
				
			||||||
 | 
						// This is a blocking call, blocking 20 ms on average.
 | 
				
			||||||
	if (updateCallTraffic(transaction,TCH)) return false;
 | 
						if (updateCallTraffic(transaction,TCH)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If nothing happened, sleep so we don't burn up the CPU cycles.
 | 
						// If nothing happened, sleep so we don't burn up the CPU cycles.
 | 
				
			||||||
@@ -749,9 +828,28 @@ bool waitInCall(TransactionEntry *transaction, GSM::TCHFACCHLogicalChannel *TCH,
 | 
				
			|||||||
	@param transaction The transaction record for this call, will be cleared on exit.
 | 
						@param transaction The transaction record for this call, will be cleared on exit.
 | 
				
			||||||
	@param TCH The TCH+FACCH for the call.
 | 
						@param TCH The TCH+FACCH for the call.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
void callManagementLoop(TransactionEntry *transaction, GSM::TCHFACCHLogicalChannel* TCH)
 | 
					void Control::callManagementLoop(TransactionEntry *transaction, GSM::TCHFACCHLogicalChannel* TCH)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	LOG(INFO) << " call connected " << *transaction;
 | 
						LOG(INFO) << " call connected " << *transaction;
 | 
				
			||||||
 | 
						if (gConfig.getBool("GSM.Cipher.Encrypt")) {
 | 
				
			||||||
 | 
							int encryptionAlgorithm = gTMSITable.getPreferredA5Algorithm(transaction->subscriber().digits());
 | 
				
			||||||
 | 
							if (!encryptionAlgorithm) {
 | 
				
			||||||
 | 
								LOG(DEBUG) << "A5/3 and A5/1 not supported: NOT sending Ciphering Mode Command on " << *TCH << " for " << transaction->subscriber();
 | 
				
			||||||
 | 
							} else if (TCH->decryptUplink_maybe(transaction->subscriber().digits(), encryptionAlgorithm)) {
 | 
				
			||||||
 | 
								// send Ciphering Mode Command
 | 
				
			||||||
 | 
								// start reception in new mode (GSM 04.08, 3.4.7)
 | 
				
			||||||
 | 
								// The spec says to start decrypting uplink at this time, but that would cause us to
 | 
				
			||||||
 | 
								// start decrypting before the Ciphering Mode Command is acknowledged, so we start
 | 
				
			||||||
 | 
								// maybe decrypting - try decoding without decrypting, and when a frame comes along
 | 
				
			||||||
 | 
								// that fails, we try decrypting, and if that passes than we start decrypting everything.
 | 
				
			||||||
 | 
								LOG(DEBUG) << "sending Ciphering Mode Command on " << *TCH << " for " << transaction->subscriber();
 | 
				
			||||||
 | 
								TCH->send(GSM::L3CipheringModeCommand(
 | 
				
			||||||
 | 
									GSM::L3CipheringModeSetting(true, encryptionAlgorithm),
 | 
				
			||||||
 | 
									GSM::L3CipheringModeResponse(false)));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								LOG(DEBUG) << "no ki: NOT sending Ciphering Mode Command on " << *TCH << " for " << transaction->subscriber();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	gReports.incr("OpenBTS.GSM.CC.CallMinutes");
 | 
						gReports.incr("OpenBTS.GSM.CC.CallMinutes");
 | 
				
			||||||
	// poll everything until the call is finished
 | 
						// poll everything until the call is finished
 | 
				
			||||||
	// A rough count of frames.
 | 
						// A rough count of frames.
 | 
				
			||||||
@@ -769,6 +867,7 @@ void callManagementLoop(TransactionEntry *transaction, GSM::TCHFACCHLogicalChann
 | 
				
			|||||||
		// Every minute, reset the watchdog timer.
 | 
							// Every minute, reset the watchdog timer.
 | 
				
			||||||
		if ((fCount%(60*50))==0) {
 | 
							if ((fCount%(60*50))==0) {
 | 
				
			||||||
			LOG(DEBUG) << fCount << " cycles of call management loop; resetting watchdog";
 | 
								LOG(DEBUG) << fCount << " cycles of call management loop; resetting watchdog";
 | 
				
			||||||
 | 
								gResetWatchdog();
 | 
				
			||||||
			gReports.incr("OpenBTS.GSM.CC.CallMinutes");
 | 
								gReports.incr("OpenBTS.GSM.CC.CallMinutes");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -840,13 +939,12 @@ void Control::MOCStarter(const GSM::L3CMServiceRequest* req, GSM::LogicalChannel
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		throw UnexpectedMessage();
 | 
							throw UnexpectedMessage();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	gReports.incr("OpenBTS.GSM.CC.MOC.Setup");
 | 
						gReports.incr("OpenBTS.GSM.CC.MOC.Setup");
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	/* early RLLP request */
 | 
						/* early RLLP request */
 | 
				
			||||||
	/* this seems to need to be sent after initial call setup
 | 
						/* this seems to need to be sent after initial call setup
 | 
				
			||||||
	   -kurtis */
 | 
						   -kurtis */
 | 
				
			||||||
	if (gConfig.defines("Control.Call.QueryRRLP.Early")) {
 | 
						if (gConfig.getBool("Control.Call.QueryRRLP.Early")) {
 | 
				
			||||||
		// Query for RRLP
 | 
							// Query for RRLP
 | 
				
			||||||
		if (!sendRRLP(mobileID, LCH)) {
 | 
							if (!sendRRLP(mobileID, LCH)) {
 | 
				
			||||||
			LOG(INFO) << "RRLP request failed";
 | 
								LOG(INFO) << "RRLP request failed";
 | 
				
			||||||
@@ -1074,6 +1172,8 @@ void Control::MOCController(TransactionEntry *transaction, GSM::TCHFACCHLogicalC
 | 
				
			|||||||
	transaction->MOCInitRTP();
 | 
						transaction->MOCInitRTP();
 | 
				
			||||||
	transaction->MOCSendACK();
 | 
						transaction->MOCSendACK();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// FIXME -- We need to watch for a repeated OK in case the ACK got lost.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get the Connect Acknowledge message.
 | 
						// Get the Connect Acknowledge message.
 | 
				
			||||||
	while (transaction->GSMState()!=GSM::Active) {
 | 
						while (transaction->GSMState()!=GSM::Active) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1107,7 +1207,7 @@ void Control::MTCStarter(TransactionEntry *transaction, GSM::LogicalChannel *LCH
 | 
				
			|||||||
	if (LCH->type()==GSM::FACCHType) veryEarly=true;
 | 
						if (LCH->type()==GSM::FACCHType) veryEarly=true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* early RLLP request */
 | 
						/* early RLLP request */
 | 
				
			||||||
	if (gConfig.defines("Control.Call.QueryRRLP.Early")) {
 | 
						if (gConfig.getBool("Control.Call.QueryRRLP.Early")) {
 | 
				
			||||||
		// Query for RRLP
 | 
							// Query for RRLP
 | 
				
			||||||
		if (!sendRRLP(transaction->subscriber(), LCH)) {
 | 
							if (!sendRRLP(transaction->subscriber(), LCH)) {
 | 
				
			||||||
			LOG(INFO) << "RRLP request failed";
 | 
								LOG(INFO) << "RRLP request failed";
 | 
				
			||||||
@@ -1197,7 +1297,7 @@ void Control::MTCStarter(TransactionEntry *transaction, GSM::LogicalChannel *LCH
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	else {
 | 
						else {
 | 
				
			||||||
		// For late assignment, send the TCH assignment now.
 | 
							// For late assignment, send the TCH assignment now.
 | 
				
			||||||
		// This dispatcher on the next channel will continue the transaction.
 | 
							// The dispatcher on the next channel will continue the transaction.
 | 
				
			||||||
		assert(TCH);
 | 
							assert(TCH);
 | 
				
			||||||
		assignTCHF(transaction,LCH,TCH);
 | 
							assignTCHF(transaction,LCH,TCH);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -1233,7 +1333,7 @@ void Control::MTCController(TransactionEntry *transaction, GSM::TCHFACCHLogicalC
 | 
				
			|||||||
		if (transaction->MTCCheckForCancel()==SIP::MTDCanceling) {
 | 
							if (transaction->MTCCheckForCancel()==SIP::MTDCanceling) {
 | 
				
			||||||
			LOG(INFO) << "MTCCheckForCancel return Canceling";
 | 
								LOG(INFO) << "MTCCheckForCancel return Canceling";
 | 
				
			||||||
			transaction->MTDSendCANCELOK();
 | 
								transaction->MTDSendCANCELOK();
 | 
				
			||||||
			//should probably send a 487 here -kurtis
 | 
								//should probably send a 487 here
 | 
				
			||||||
			// Cause 0x15 is "rejected"
 | 
								// Cause 0x15 is "rejected"
 | 
				
			||||||
			return abortAndRemoveCall(transaction,TCH,GSM::L3Cause(0x15));
 | 
								return abortAndRemoveCall(transaction,TCH,GSM::L3Cause(0x15));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -1285,6 +1385,21 @@ void Control::MTCController(TransactionEntry *transaction, GSM::TCHFACCHLogicalC
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Control::TestCall(TransactionEntry *transaction, GSM::LogicalChannel *LCH)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						assert(LCH);
 | 
				
			||||||
 | 
						LOG(INFO) << LCH->type() << " transaction: "<< *transaction;
 | 
				
			||||||
 | 
						assert(transaction->L3TI()<7);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Mark the call as active.
 | 
				
			||||||
 | 
						transaction->GSMState(GSM::Active);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOG(INFO) << "starting test call";
 | 
				
			||||||
 | 
						while (!transaction->terminationRequested()) { sleep(1); }
 | 
				
			||||||
 | 
						LOG(INFO) << "ending test call";
 | 
				
			||||||
 | 
						LCH->send(GSM::L3ChannelRelease());
 | 
				
			||||||
 | 
						gTransactionTable.remove(transaction);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,16 @@
 | 
				
			|||||||
/**@file GSM/SIP Call Control -- GSM 04.08, ISDN ITU-T Q.931, SIP IETF RFC-3261, RTP IETF RFC-3550. */
 | 
					/**@file GSM/SIP Call Control -- GSM 04.08, ISDN ITU-T Q.931, SIP IETF RFC-3261, RTP IETF RFC-3550. */
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -45,8 +36,6 @@ class TransactionEntry;
 | 
				
			|||||||
void MOCStarter(const GSM::L3CMServiceRequest*, GSM::LogicalChannel*);
 | 
					void MOCStarter(const GSM::L3CMServiceRequest*, GSM::LogicalChannel*);
 | 
				
			||||||
/** Complete the MOC connection. */
 | 
					/** Complete the MOC connection. */
 | 
				
			||||||
void MOCController(TransactionEntry*, GSM::TCHFACCHLogicalChannel*);
 | 
					void MOCController(TransactionEntry*, GSM::TCHFACCHLogicalChannel*);
 | 
				
			||||||
/** Set up an emergency call, assuming very early assignment. */
 | 
					 | 
				
			||||||
void EmergencyCall(const GSM::L3CMServiceRequest*, GSM::LogicalChannel*);
 | 
					 | 
				
			||||||
//@}
 | 
					//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -70,6 +59,14 @@ void initiateMTTransaction(TransactionEntry* transaction,
 | 
				
			|||||||
		GSM::ChannelType chanType, unsigned pageTime);
 | 
							GSM::ChannelType chanType, unsigned pageTime);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
						This is the standard call manangement loop, regardless of the origination type.
 | 
				
			||||||
 | 
						This function returns when the call is cleared and the channel is released.
 | 
				
			||||||
 | 
						@param transaction The transaction record for this call, will be cleared on exit.
 | 
				
			||||||
 | 
						@param TCH The TCH+FACCH for the call.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					void callManagementLoop(TransactionEntry *transaction, GSM::TCHFACCHLogicalChannel* TCH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,24 +3,16 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -37,6 +29,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <SIPEngine.h>
 | 
					#include <SIPEngine.h>
 | 
				
			||||||
#include <SIPInterface.h>
 | 
					#include <SIPInterface.h>
 | 
				
			||||||
 | 
					#include <Sgsn.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <Logger.h>
 | 
					#include <Logger.h>
 | 
				
			||||||
#include <Reporting.h>
 | 
					#include <Reporting.h>
 | 
				
			||||||
@@ -60,25 +53,29 @@ L3Message* getMessageCore(LogicalChannel *LCH, unsigned SAPI)
 | 
				
			|||||||
	unsigned timeout_ms = LCH->N200() * T200ms;
 | 
						unsigned timeout_ms = LCH->N200() * T200ms;
 | 
				
			||||||
	L3Frame *rcv = LCH->recv(timeout_ms,SAPI);
 | 
						L3Frame *rcv = LCH->recv(timeout_ms,SAPI);
 | 
				
			||||||
	if (rcv==NULL) {
 | 
						if (rcv==NULL) {
 | 
				
			||||||
		LOG(NOTICE) << "timeout";
 | 
							LOG(NOTICE) << "L3 read timeout";
 | 
				
			||||||
		throw ChannelReadTimeout();
 | 
							throw ChannelReadTimeout();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	LOG(DEBUG) << "received " << *rcv;
 | 
						LOG(DEBUG) << "received " << *rcv;
 | 
				
			||||||
	Primitive primitive = rcv->primitive();
 | 
						Primitive primitive = rcv->primitive();
 | 
				
			||||||
	if (primitive!=DATA) {
 | 
						if (primitive!=DATA) {
 | 
				
			||||||
		LOG(NOTICE) << "unexpected primitive " << primitive;
 | 
							LOG(ERR) << "unexpected primitive " << primitive;
 | 
				
			||||||
		delete rcv;
 | 
							delete rcv;
 | 
				
			||||||
		throw UnexpectedPrimitive();
 | 
							throw UnexpectedPrimitive();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	L3Message *msg = parseL3(*rcv);
 | 
						L3Message *msg = parseL3(*rcv);
 | 
				
			||||||
	delete rcv;
 | 
					 | 
				
			||||||
	if (msg==NULL) {
 | 
						if (msg==NULL) {
 | 
				
			||||||
		LOG(NOTICE) << "unparsed message";
 | 
							LOG(WARNING) << "unparsed message:" << *rcv;
 | 
				
			||||||
		throw UnsupportedMessage();
 | 
							delete rcv;
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
							//throw UnsupportedMessage();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						delete rcv;
 | 
				
			||||||
 | 
						LOG(DEBUG) << *msg;
 | 
				
			||||||
	return msg;
 | 
						return msg;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FIXME -- getMessage should return an L3Frame, not an L3Message.
 | 
					// FIXME -- getMessage should return an L3Frame, not an L3Message.
 | 
				
			||||||
// This will mean moving all of the parsing into the control layer.
 | 
					// This will mean moving all of the parsing into the control layer.
 | 
				
			||||||
// FIXME -- This needs an adjustable timeout.
 | 
					// FIXME -- This needs an adjustable timeout.
 | 
				
			||||||
@@ -86,13 +83,33 @@ L3Message* getMessageCore(LogicalChannel *LCH, unsigned SAPI)
 | 
				
			|||||||
L3Message* Control::getMessage(LogicalChannel *LCH, unsigned SAPI)
 | 
					L3Message* Control::getMessage(LogicalChannel *LCH, unsigned SAPI)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	L3Message *msg = getMessageCore(LCH,SAPI);
 | 
						L3Message *msg = getMessageCore(LCH,SAPI);
 | 
				
			||||||
	// Handsets should not be sending us GPRS suspension requests.
 | 
						// Handsets should not be sending us GPRS suspension requests when GPRS support is not enabled.
 | 
				
			||||||
	// But if they do, we should ignore them.
 | 
						// But if they do, we should ignore them.
 | 
				
			||||||
	// They should not send more than one in any case, but we need to be
 | 
						// They should not send more than one in any case, but we need to be
 | 
				
			||||||
	// ready for whatever crazy behavior they throw at us.
 | 
						// ready for whatever crazy behavior they throw at us.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The suspend procedure includes MS<->BSS and BSS<->SGSN messages.
 | 
				
			||||||
 | 
						// GSM44.018 3.4.25 GPRS Suspension Procedure and 9.1.13b: GPRS Suspension Request message.
 | 
				
			||||||
 | 
						// Also 23.060 16.2.1.1 Suspend/Resume procedure general.
 | 
				
			||||||
 | 
						// GSM08.18: Suspend Procedure talks about communication between the BSS and SGSN,
 | 
				
			||||||
 | 
						// and is not applicable to us when using the internal SGSN.
 | 
				
			||||||
 | 
						// Note: When call is finished the RR is supposed to include a GPRS resumption IE, but if it does not,
 | 
				
			||||||
 | 
						// 23.060 16.2.1.1.1 says the MS will do a GPRS RoutingAreaUpdate to get the
 | 
				
			||||||
 | 
						// GPRS service back, so we are not worrying about it.
 | 
				
			||||||
 | 
						// (pat 3-2012) Send the message to the internal SGSN.
 | 
				
			||||||
 | 
						// It returns true if GPRS and the internal SGSN are enabled.
 | 
				
			||||||
 | 
						// If we are using an external SGSN, we could send the GPRS suspend request to the SGSN via the BSSG,
 | 
				
			||||||
 | 
						// but that has no hope of doing anything useful. See ticket #613.
 | 
				
			||||||
 | 
						// First, We are supposed to automatically detect when we should do the Resume procedure.
 | 
				
			||||||
 | 
						// Second: An RA-UPDATE, which gets send to the SGSN, does something to the CC state
 | 
				
			||||||
 | 
						// that I dont understand yet.
 | 
				
			||||||
 | 
						// We dont do any of the above.
 | 
				
			||||||
	unsigned count = gConfig.getNum("GSM.Control.GPRSMaxIgnore");
 | 
						unsigned count = gConfig.getNum("GSM.Control.GPRSMaxIgnore");
 | 
				
			||||||
	while (count && dynamic_cast<const GSM::L3GPRSSuspensionRequest*>(msg)) {
 | 
						const GSM::L3GPRSSuspensionRequest *srmsg;
 | 
				
			||||||
 | 
						while (count && (srmsg = dynamic_cast<const GSM::L3GPRSSuspensionRequest*>(msg))) {
 | 
				
			||||||
 | 
							if (! SGSN::Sgsn::handleGprsSuspensionRequest(srmsg->mTLLI,srmsg->mRaId)) {
 | 
				
			||||||
			LOG(NOTICE) << "ignoring GPRS suspension request";
 | 
								LOG(NOTICE) << "ignoring GPRS suspension request";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		msg = getMessageCore(LCH,SAPI);
 | 
							msg = getMessageCore(LCH,SAPI);
 | 
				
			||||||
		count--;
 | 
							count--;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -100,6 +117,10 @@ L3Message* Control::getMessage(LogicalChannel *LCH, unsigned SAPI)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Resolve a mobile ID to an IMSI and return TMSI if it is assigned. */
 | 
					/* Resolve a mobile ID to an IMSI and return TMSI if it is assigned. */
 | 
				
			||||||
unsigned  Control::resolveIMSI(bool sameLAI, L3MobileIdentity& mobileID, LogicalChannel* LCH)
 | 
					unsigned  Control::resolveIMSI(bool sameLAI, L3MobileIdentity& mobileID, LogicalChannel* LCH)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -108,7 +129,10 @@ unsigned  Control::resolveIMSI(bool sameLAI, L3MobileIdentity& mobileID, Logical
 | 
				
			|||||||
	LOG(DEBUG) << "resolving mobile ID " << mobileID << ", sameLAI: " << sameLAI;
 | 
						LOG(DEBUG) << "resolving mobile ID " << mobileID << ", sameLAI: " << sameLAI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// IMSI already?  See if there's a TMSI already, too.
 | 
						// IMSI already?  See if there's a TMSI already, too.
 | 
				
			||||||
	if (mobileID.type()==IMSIType) return gTMSITable.TMSI(mobileID.digits());
 | 
						if (mobileID.type()==IMSIType) {
 | 
				
			||||||
 | 
							GPRS::GPRSNotifyGsmActivity(mobileID.digits());
 | 
				
			||||||
 | 
							return gTMSITable.TMSI(mobileID.digits());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// IMEI?  WTF?!
 | 
						// IMEI?  WTF?!
 | 
				
			||||||
	// FIXME -- Should send MM Reject, cause 0x60, "invalid mandatory information".
 | 
						// FIXME -- Should send MM Reject, cause 0x60, "invalid mandatory information".
 | 
				
			||||||
@@ -121,6 +145,7 @@ unsigned  Control::resolveIMSI(bool sameLAI, L3MobileIdentity& mobileID, Logical
 | 
				
			|||||||
	if (sameLAI) IMSI = gTMSITable.IMSI(TMSI);
 | 
						if (sameLAI) IMSI = gTMSITable.IMSI(TMSI);
 | 
				
			||||||
	if (IMSI) {
 | 
						if (IMSI) {
 | 
				
			||||||
		// We assigned this TMSI already; the TMSI/IMSI pair is already in the table.
 | 
							// We assigned this TMSI already; the TMSI/IMSI pair is already in the table.
 | 
				
			||||||
 | 
							GPRS::GPRSNotifyGsmActivity(IMSI);
 | 
				
			||||||
		mobileID = L3MobileIdentity(IMSI);
 | 
							mobileID = L3MobileIdentity(IMSI);
 | 
				
			||||||
		LOG(DEBUG) << "resolving mobile ID (table): " << mobileID;
 | 
							LOG(DEBUG) << "resolving mobile ID (table): " << mobileID;
 | 
				
			||||||
		free(IMSI);
 | 
							free(IMSI);
 | 
				
			||||||
@@ -153,16 +178,15 @@ unsigned  Control::resolveIMSI(bool sameLAI, L3MobileIdentity& mobileID, Logical
 | 
				
			|||||||
void  Control::resolveIMSI(L3MobileIdentity& mobileIdentity, LogicalChannel* LCH)
 | 
					void  Control::resolveIMSI(L3MobileIdentity& mobileIdentity, LogicalChannel* LCH)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// Are we done already?
 | 
						// Are we done already?
 | 
				
			||||||
	if (mobileIdentity.type()==IMSIType){
 | 
						if (mobileIdentity.type()==IMSIType) return;
 | 
				
			||||||
		//Cause the tmsi table to be touched
 | 
					 | 
				
			||||||
		gTMSITable.TMSI(mobileIdentity.digits());
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If we got a TMSI, find the IMSI.
 | 
						// If we got a TMSI, find the IMSI.
 | 
				
			||||||
	if (mobileIdentity.type()==TMSIType) {
 | 
						if (mobileIdentity.type()==TMSIType) {
 | 
				
			||||||
		char *IMSI = gTMSITable.IMSI(mobileIdentity.TMSI());
 | 
							char *IMSI = gTMSITable.IMSI(mobileIdentity.TMSI());
 | 
				
			||||||
		if (IMSI) mobileIdentity = L3MobileIdentity(IMSI);
 | 
							if (IMSI) {
 | 
				
			||||||
 | 
								GPRS::GPRSNotifyGsmActivity(IMSI);
 | 
				
			||||||
 | 
								mobileIdentity = L3MobileIdentity(IMSI);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		free(IMSI);
 | 
							free(IMSI);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -178,6 +202,9 @@ void  Control::resolveIMSI(L3MobileIdentity& mobileIdentity, LogicalChannel* LCH
 | 
				
			|||||||
			throw UnexpectedMessage();
 | 
								throw UnexpectedMessage();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		mobileIdentity = resp->mobileID();
 | 
							mobileIdentity = resp->mobileID();
 | 
				
			||||||
 | 
							if (mobileIdentity.type()==IMSIType) {
 | 
				
			||||||
 | 
								GPRS::GPRSNotifyGsmActivity(mobileIdentity.digits());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		delete msg;
 | 
							delete msg;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,27 +1,19 @@
 | 
				
			|||||||
/**@file Declarations for common-use control-layer functions. */
 | 
					/**@file Declarations for common-use control-layer functions. */
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008-2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
* Copyright 2011 Range Networks, Inc.
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -114,8 +106,8 @@ GSM::L3Message* getMessage(GSM::LogicalChannel* LCH, unsigned SAPI=0);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**@name Dispatch controllers for specific channel types. */
 | 
					/**@name Dispatch controllers for specific channel types. */
 | 
				
			||||||
//@{
 | 
					//@{
 | 
				
			||||||
void FACCHDispatcher(GSM::TCHFACCHLogicalChannel *TCHFACCH);
 | 
					//void FACCHDispatcher(GSM::TCHFACCHLogicalChannel *TCHFACCH);
 | 
				
			||||||
void SDCCHDispatcher(GSM::SDCCHLogicalChannel *SDCCH);
 | 
					//void SDCCHDispatcher(GSM::SDCCHLogicalChannel *SDCCH);
 | 
				
			||||||
void DCCHDispatcher(GSM::LogicalChannel *DCCH);
 | 
					void DCCHDispatcher(GSM::LogicalChannel *DCCH);
 | 
				
			||||||
//@}
 | 
					//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -141,6 +133,12 @@ void  resolveIMSI(GSM::L3MobileIdentity& mobID, GSM::LogicalChannel* LCH);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
						SMSCB sender function
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					void *SMSCBSender(void*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -213,6 +211,8 @@ class RemovedTransaction : public ControlLayerException {
 | 
				
			|||||||
		:ControlLayerException(wTransactionID)
 | 
							:ControlLayerException(wTransactionID)
 | 
				
			||||||
	{}
 | 
						{}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//@}
 | 
					//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,27 +1,19 @@
 | 
				
			|||||||
/**@file Idle-mode dispatcher for dedicated control channels. */
 | 
					/**@file Idle-mode dispatcher for dedicated control channels. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009, 2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2011 Range Networks, Inc.
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -85,8 +77,6 @@ void DCCHDispatchRR(const L3RRMessage* req, LogicalChannel *DCCH)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	LOG(DEBUG) << "checking MTI"<< (L3RRMessage::MessageType)req->MTI();
 | 
						LOG(DEBUG) << "checking MTI"<< (L3RRMessage::MessageType)req->MTI();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO SMS -- This needs to handle SACCH Measurement Reports.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	assert(req);
 | 
						assert(req);
 | 
				
			||||||
	L3RRMessage::MessageType MTI = (L3RRMessage::MessageType)req->MTI();
 | 
						L3RRMessage::MessageType MTI = (L3RRMessage::MessageType)req->MTI();
 | 
				
			||||||
	switch (MTI) {
 | 
						switch (MTI) {
 | 
				
			||||||
@@ -94,7 +84,8 @@ void DCCHDispatchRR(const L3RRMessage* req, LogicalChannel *DCCH)
 | 
				
			|||||||
			PagingResponseHandler(dynamic_cast<const L3PagingResponse*>(req),DCCH);
 | 
								PagingResponseHandler(dynamic_cast<const L3PagingResponse*>(req),DCCH);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		case L3RRMessage::AssignmentComplete:
 | 
							case L3RRMessage::AssignmentComplete:
 | 
				
			||||||
			AssignmentCompleteHandler(dynamic_cast<const L3AssignmentComplete*>(req),
 | 
								AssignmentCompleteHandler(
 | 
				
			||||||
 | 
									dynamic_cast<const L3AssignmentComplete*>(req),
 | 
				
			||||||
				dynamic_cast<TCHFACCHLogicalChannel*>(DCCH));
 | 
									dynamic_cast<TCHFACCHLogicalChannel*>(DCCH));
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
@@ -123,23 +114,41 @@ void DCCHDispatchMessage(const L3Message* msg, LogicalChannel* DCCH)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Example of a closed-loop, persistent-thread control function for the DCCH. */
 | 
					/** Example of a closed-loop, persistent-thread control function for the DCCH. */
 | 
				
			||||||
 | 
					// (pat) DCCH is a TCHFACCHLogicalChannel or SDCCHLogicalChannel
 | 
				
			||||||
void Control::DCCHDispatcher(LogicalChannel *DCCH)
 | 
					void Control::DCCHDispatcher(LogicalChannel *DCCH)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	while (1) {
 | 
						while (1) {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			// Wait for a transaction to start.
 | 
								// Wait for a transaction to start.
 | 
				
			||||||
			LOG(DEBUG) << "waiting for " << *DCCH << " ESTABLISH";
 | 
								LOG(DEBUG) << "waiting for " << *DCCH << " ESTABLISH or HANDOVER_ACCESS";
 | 
				
			||||||
			DCCH->waitForPrimitive(ESTABLISH);
 | 
								L3Frame *frame = DCCH->waitForEstablishOrHandover();
 | 
				
			||||||
 | 
								LOG(DEBUG) << *DCCH << " received " << *frame;
 | 
				
			||||||
 | 
								gResetWatchdog();
 | 
				
			||||||
 | 
								Primitive prim = frame->primitive();
 | 
				
			||||||
 | 
								delete frame;
 | 
				
			||||||
 | 
								LOG(DEBUG) << "received primtive " << prim;
 | 
				
			||||||
 | 
								switch (prim) {
 | 
				
			||||||
 | 
									case ESTABLISH: {
 | 
				
			||||||
					// Pull the first message and dispatch a new transaction.
 | 
										// Pull the first message and dispatch a new transaction.
 | 
				
			||||||
					gReports.incr("OpenBTS.GSM.RR.ChannelSiezed");
 | 
										gReports.incr("OpenBTS.GSM.RR.ChannelSiezed");
 | 
				
			||||||
					const L3Message *message = getMessage(DCCH);
 | 
										const L3Message *message = getMessage(DCCH);
 | 
				
			||||||
			LOG(DEBUG) << *DCCH << " received " << *message;
 | 
										LOG(INFO) << *DCCH << " received establishing messaage " << *message;
 | 
				
			||||||
					DCCHDispatchMessage(message,DCCH);
 | 
										DCCHDispatchMessage(message,DCCH);
 | 
				
			||||||
					delete message;
 | 
										delete message;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									case HANDOVER_ACCESS: {
 | 
				
			||||||
 | 
										ProcessHandoverAccess(dynamic_cast<GSM::TCHFACCHLogicalChannel*>(DCCH));
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									default: assert(0);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Catch the various error cases.
 | 
							// Catch the various error cases.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		catch (RemovedTransaction except) {
 | 
							catch (RemovedTransaction except) {
 | 
				
			||||||
			LOG(ERR) << "attempt to use removed transaciton " << except.transactionID();
 | 
								LOG(ERR) << "attempt to use removed transaciton " << except.transactionID();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,9 +38,12 @@ libcontrol_la_SOURCES = \
 | 
				
			|||||||
	MobilityManagement.cpp \
 | 
						MobilityManagement.cpp \
 | 
				
			||||||
	RadioResource.cpp \
 | 
						RadioResource.cpp \
 | 
				
			||||||
	DCCHDispatch.cpp \
 | 
						DCCHDispatch.cpp \
 | 
				
			||||||
 | 
						SMSCB.cpp \
 | 
				
			||||||
	RRLPServer.cpp
 | 
						RRLPServer.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# TODO - move CollectMSInfo.cpp and RRLPQueryController.cpp to RRLP directory.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
noinst_HEADERS = \
 | 
					noinst_HEADERS = \
 | 
				
			||||||
	ControlCommon.h \
 | 
						ControlCommon.h \
 | 
				
			||||||
	SMSControl.h \
 | 
						SMSControl.h \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,26 +1,18 @@
 | 
				
			|||||||
/**@file GSM/SIP Mobility Management, GSM 04.08. */
 | 
					/**@file GSM/SIP Mobility Management, GSM 04.08. */
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2011 Range Networks, Inc.
 | 
					* Copyright 2011, 2012 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -97,9 +89,11 @@ void Control::IMSIDetachController(const L3IMSIDetachIndication* idi, LogicalCha
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// The IMSI detach maps to a SIP unregister with the local Asterisk server.
 | 
						// The IMSI detach maps to a SIP unregister with the local Asterisk server.
 | 
				
			||||||
	try { 
 | 
						try { 
 | 
				
			||||||
		// FIXME -- Resolve TMSIs to IMSIs.
 | 
							// FIXME -- Resolve TMSIs to IMSIs.  (pat) And when you do call GPRSNotifyGsmActivity() on it.
 | 
				
			||||||
		if (idi->mobileID().type()==IMSIType) {
 | 
							if (idi->mobileID().type()==IMSIType) {
 | 
				
			||||||
			SIPEngine engine(gConfig.getStr("SIP.Proxy.Registration").c_str(), idi->mobileID().digits());
 | 
								const char *digits = idi->mobileID().digits();
 | 
				
			||||||
 | 
								GPRS::GPRSNotifyGsmActivity(digits);
 | 
				
			||||||
 | 
								SIPEngine engine(gConfig.getStr("SIP.Proxy.Registration").c_str(), digits);
 | 
				
			||||||
			engine.unregister();
 | 
								engine.unregister();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -122,7 +116,8 @@ void Control::IMSIDetachController(const L3IMSIDetachIndication* idi, LogicalCha
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
bool sendWelcomeMessage(const char* messageName, const char* shortCodeName, const char *IMSI, LogicalChannel* DCCH, const char *whiteListCode = NULL)
 | 
					bool sendWelcomeMessage(const char* messageName, const char* shortCodeName, const char *IMSI, LogicalChannel* DCCH, const char *whiteListCode = NULL)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!gConfig.defines(messageName)) return false;
 | 
						if (!gConfig.defines(messageName) || !gConfig.defines(shortCodeName)) return false;
 | 
				
			||||||
 | 
						if (!gConfig.getStr(messageName).length() || !gConfig.getStr(shortCodeName).length()) return false;
 | 
				
			||||||
	LOG(INFO) << "sending " << messageName << " message to handset";
 | 
						LOG(INFO) << "sending " << messageName << " message to handset";
 | 
				
			||||||
	ostringstream message;
 | 
						ostringstream message;
 | 
				
			||||||
	message << gConfig.getStr(messageName) << " IMSI:" << IMSI;
 | 
						message << gConfig.getStr(messageName) << " IMSI:" << IMSI;
 | 
				
			||||||
@@ -137,6 +132,56 @@ bool sendWelcomeMessage(const char* messageName, const char* shortCodeName, cons
 | 
				
			|||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
						Check if a phone is white-listed.
 | 
				
			||||||
 | 
						@param name name of subscriber
 | 
				
			||||||
 | 
						@return true if phone was already white-listed
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					bool isPhoneWhiteListed(string name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// if name isn't in SR, then put it in with white-list flag off
 | 
				
			||||||
 | 
						string id = gSubscriberRegistry.imsiGet(name, "id");
 | 
				
			||||||
 | 
						if (id.empty()) {
 | 
				
			||||||
 | 
							//we used to create a user here, but that's almost certainly wrong. -kurtis
 | 
				
			||||||
 | 
							LOG(CRIT) << "Checking whitelist of a user that doesn't exist. Reject";
 | 
				
			||||||
 | 
							//return not-white-listed
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// check flag
 | 
				
			||||||
 | 
						string whiteListFlag = gSubscriberRegistry.imsiGet(name, "whiteListFlag");
 | 
				
			||||||
 | 
						if (whiteListFlag.empty()){
 | 
				
			||||||
 | 
							LOG(CRIT) << "SR query error";
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return (whiteListFlag == "0");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
						Generate white-list code.
 | 
				
			||||||
 | 
						@param name name of subscriber
 | 
				
			||||||
 | 
						@return the white-list code.
 | 
				
			||||||
 | 
						Also put it into the SR database.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					string genWhiteListCode(string name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// generate code
 | 
				
			||||||
 | 
						uint32_t wlc = (uint32_t)rand();
 | 
				
			||||||
 | 
						ostringstream os2;
 | 
				
			||||||
 | 
						os2 << hex << wlc;
 | 
				
			||||||
 | 
						string whiteListCode = os2.str();
 | 
				
			||||||
 | 
						// write to SR
 | 
				
			||||||
 | 
						if (gSubscriberRegistry.imsiSet(name, "whiteListCode", whiteListCode)){
 | 
				
			||||||
 | 
							LOG(CRIT) << "SR update error";
 | 
				
			||||||
 | 
							return "";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// and return it
 | 
				
			||||||
 | 
						return whiteListCode;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
	Controller for the Location Updating transaction, GSM 04.08 4.4.4.
 | 
						Controller for the Location Updating transaction, GSM 04.08 4.4.4.
 | 
				
			||||||
	@param lur The location updating request.
 | 
						@param lur The location updating request.
 | 
				
			||||||
@@ -170,13 +215,38 @@ void Control::LocationUpdatingController(const L3LocationUpdatingRequest* lur, L
 | 
				
			|||||||
	unsigned newTMSI = 0;
 | 
						unsigned newTMSI = 0;
 | 
				
			||||||
	if (!preexistingTMSI) newTMSI = gTMSITable.assign(IMSI,lur);
 | 
						if (!preexistingTMSI) newTMSI = gTMSITable.assign(IMSI,lur);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// White-listing.
 | 
				
			||||||
 | 
						const string name = "IMSI" + string(IMSI);
 | 
				
			||||||
 | 
						if (gConfig.getBool("Control.LUR.WhiteList")) {
 | 
				
			||||||
 | 
							LOG(INFO) << "checking white-list for " << name;
 | 
				
			||||||
 | 
							if (!isPhoneWhiteListed(name)) {
 | 
				
			||||||
 | 
								// not white-listed.  reject phone.
 | 
				
			||||||
 | 
								LOG(INFO) << "is NOT white-listed";
 | 
				
			||||||
 | 
								DCCH->send(L3LocationUpdatingReject(gConfig.getNum("Control.LUR.WhiteListing.RejectCause")));
 | 
				
			||||||
 | 
								if (!preexistingTMSI) {
 | 
				
			||||||
 | 
									// generate code (and put in SR) and send message if first time.
 | 
				
			||||||
 | 
									string whiteListCode = genWhiteListCode(name);
 | 
				
			||||||
 | 
									LOG(INFO) << "generated white-list code: " << whiteListCode;
 | 
				
			||||||
 | 
									sendWelcomeMessage("Control.LUR.WhiteListing.Message", "Control.LUR.WhiteListing.ShortCode", IMSI, DCCH, whiteListCode.c_str());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// Release the channel and return.
 | 
				
			||||||
 | 
								DCCH->send(L3ChannelRelease());
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								LOG(INFO) << "IS white-listed";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							LOG(INFO) << "not checking white-list for " << name;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Try to register the IMSI.
 | 
						// Try to register the IMSI.
 | 
				
			||||||
	// This will be set true if registration succeeded in the SIP world.
 | 
						// This will be set true if registration succeeded in the SIP world.
 | 
				
			||||||
	bool success = false;
 | 
						bool success = false;
 | 
				
			||||||
 | 
						string RAND;
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		SIPEngine engine(gConfig.getStr("SIP.Proxy.Registration").c_str(),IMSI);
 | 
							SIPEngine engine(gConfig.getStr("SIP.Proxy.Registration").c_str(),IMSI);
 | 
				
			||||||
		LOG(DEBUG) << "waiting for registration of " << IMSI << " on " << gConfig.getStr("SIP.Proxy.Registration");
 | 
							LOG(DEBUG) << "waiting for registration of " << IMSI << " on " << gConfig.getStr("SIP.Proxy.Registration");
 | 
				
			||||||
		success = engine.Register(SIPEngine::SIPRegister); 
 | 
							success = engine.Register(SIPEngine::SIPRegister, DCCH, &RAND); 
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	catch(SIPTimeout) {
 | 
						catch(SIPTimeout) {
 | 
				
			||||||
		LOG(ALERT) "SIP registration timed out.  Is the proxy running at " << gConfig.getStr("SIP.Proxy.Registration");
 | 
							LOG(ALERT) "SIP registration timed out.  Is the proxy running at " << gConfig.getStr("SIP.Proxy.Registration");
 | 
				
			||||||
@@ -191,58 +261,10 @@ void Control::LocationUpdatingController(const L3LocationUpdatingRequest* lur, L
 | 
				
			|||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// This allows us to configure Open Registration
 | 
					 | 
				
			||||||
	bool openRegistration = false;
 | 
					 | 
				
			||||||
	if (gConfig.defines("Control.LUR.OpenRegistration")) {
 | 
					 | 
				
			||||||
		if (!gConfig.defines("Control.LUR.OpenRegistration.Message")) {
 | 
					 | 
				
			||||||
			gConfig.set("Control.LUR.OpenRegistration.Message","Welcome to the test network.  Your IMSI is ");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		Regexp rxp(gConfig.getStr("Control.LUR.OpenRegistration").c_str());
 | 
					 | 
				
			||||||
		openRegistration = rxp.match(IMSI);
 | 
					 | 
				
			||||||
		if (gConfig.defines("Control.LUR.OpenRegistration.Reject")) {
 | 
					 | 
				
			||||||
			Regexp rxpReject(gConfig.getStr("Control.LUR.OpenRegistration.Reject").c_str());
 | 
					 | 
				
			||||||
			bool openRegistrationReject = rxpReject.match(IMSI);
 | 
					 | 
				
			||||||
			openRegistration = openRegistration && !openRegistrationReject;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Query for IMEI?
 | 
					 | 
				
			||||||
	if (gConfig.defines("Control.LUR.QueryIMEI")) {
 | 
					 | 
				
			||||||
		DCCH->send(L3IdentityRequest(IMEIType));
 | 
					 | 
				
			||||||
		L3Message* msg = getMessage(DCCH);
 | 
					 | 
				
			||||||
		L3IdentityResponse *resp = dynamic_cast<L3IdentityResponse*>(msg);
 | 
					 | 
				
			||||||
		if (!resp) {
 | 
					 | 
				
			||||||
			if (msg) {
 | 
					 | 
				
			||||||
				LOG(WARNING) << "Unexpected message " << *msg;
 | 
					 | 
				
			||||||
				delete msg;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			throw UnexpectedMessage();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		LOG(INFO) << *resp;
 | 
					 | 
				
			||||||
		string new_imei = resp->mobileID().digits();
 | 
					 | 
				
			||||||
		if (!gTMSITable.IMEI(IMSI,new_imei.c_str())){
 | 
					 | 
				
			||||||
			LOG(WARNING) << "failed access to TMSITable";
 | 
					 | 
				
			||||||
		} 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		//query subscriber registry for old imei, update if neccessary
 | 
					 | 
				
			||||||
		string name = string("IMSI") + IMSI;
 | 
					 | 
				
			||||||
		string old_imei = gSubscriberRegistry.imsiGet(name, "hardware");
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		//if we have a new imei and either there's no old one, or it is different...
 | 
					 | 
				
			||||||
		if (!new_imei.empty() && (old_imei.empty() || old_imei != new_imei)){
 | 
					 | 
				
			||||||
			LOG(INFO) << "Updating IMSI" << IMSI << " to IMEI:" << new_imei;
 | 
					 | 
				
			||||||
			if (gSubscriberRegistry.imsiSet(name,"RRLPSupported", "1")) {
 | 
					 | 
				
			||||||
			 	LOG(INFO) << "SR RRLPSupported update problem";
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (gSubscriberRegistry.imsiSet(name,"hardware", new_imei)) {
 | 
					 | 
				
			||||||
				LOG(INFO) << "SR hardware update problem";
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		delete msg;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Query for classmark?
 | 
						// Query for classmark?
 | 
				
			||||||
	if (IMSIAttach && gConfig.defines("Control.LUR.QueryClassmark")) {
 | 
						// This needs to happen before encryption.
 | 
				
			||||||
 | 
						// It can't be optional if encryption is desired.
 | 
				
			||||||
 | 
						if (IMSIAttach && (gConfig.getBool("GSM.Cipher.Encrypt") || gConfig.getBool("Control.LUR.QueryClassmark"))) {
 | 
				
			||||||
		DCCH->send(L3ClassmarkEnquiry());
 | 
							DCCH->send(L3ClassmarkEnquiry());
 | 
				
			||||||
		L3Message* msg = getMessage(DCCH);
 | 
							L3Message* msg = getMessage(DCCH);
 | 
				
			||||||
		L3ClassmarkChange *resp = dynamic_cast<L3ClassmarkChange*>(msg);
 | 
							L3ClassmarkChange *resp = dynamic_cast<L3ClassmarkChange*>(msg);
 | 
				
			||||||
@@ -260,10 +282,125 @@ void Control::LocationUpdatingController(const L3LocationUpdatingRequest* lur, L
 | 
				
			|||||||
		delete msg;
 | 
							delete msg;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Did we get a RAND for challenge-response?
 | 
				
			||||||
 | 
						if (RAND.length() != 0) {
 | 
				
			||||||
 | 
							// Get the mobile's SRES.
 | 
				
			||||||
 | 
							LOG(INFO) << "sending " << RAND << " to mobile";
 | 
				
			||||||
 | 
							uint64_t uRAND;
 | 
				
			||||||
 | 
							uint64_t lRAND;
 | 
				
			||||||
 | 
							gSubscriberRegistry.stringToUint(RAND, &uRAND, &lRAND);
 | 
				
			||||||
 | 
							gReports.incr("OpenBTS.GSM.MM.Authenticate.Request");
 | 
				
			||||||
 | 
							DCCH->send(L3AuthenticationRequest(0,L3RAND(uRAND,lRAND)));
 | 
				
			||||||
 | 
							L3Message* msg = getMessage(DCCH);
 | 
				
			||||||
 | 
							L3AuthenticationResponse *resp = dynamic_cast<L3AuthenticationResponse*>(msg);
 | 
				
			||||||
 | 
							if (!resp) {
 | 
				
			||||||
 | 
								if (msg) {
 | 
				
			||||||
 | 
									LOG(WARNING) << "Unexpected message " << *msg;
 | 
				
			||||||
 | 
									delete msg;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// FIXME -- We should differentiate between wrong message and no message at all.
 | 
				
			||||||
 | 
								throw UnexpectedMessage();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							LOG(INFO) << *resp;
 | 
				
			||||||
 | 
							uint32_t mobileSRES = resp->SRES().value();
 | 
				
			||||||
 | 
							delete msg;
 | 
				
			||||||
 | 
							// verify SRES 
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								SIPEngine engine(gConfig.getStr("SIP.Proxy.Registration").c_str(),IMSI);
 | 
				
			||||||
 | 
								LOG(DEBUG) << "waiting for authentication of " << IMSI << " on " << gConfig.getStr("SIP.Proxy.Registration");
 | 
				
			||||||
 | 
								ostringstream os;
 | 
				
			||||||
 | 
								os << hex << mobileSRES;
 | 
				
			||||||
 | 
								string SRESstr = os.str();
 | 
				
			||||||
 | 
								success = engine.Register(SIPEngine::SIPRegister, DCCH, &RAND, IMSI, SRESstr.c_str()); 
 | 
				
			||||||
 | 
								if (!success) {
 | 
				
			||||||
 | 
									gReports.incr("OpenBTS.GSM.MM.Authenticate.Failure");
 | 
				
			||||||
 | 
									LOG(CRIT) << "authentication failure for IMSI " << IMSI;
 | 
				
			||||||
 | 
									DCCH->send(L3AuthenticationReject());
 | 
				
			||||||
 | 
									DCCH->send(L3ChannelRelease());
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (gConfig.getBool("GSM.Cipher.Encrypt")) {
 | 
				
			||||||
 | 
									int encryptionAlgorithm = gTMSITable.getPreferredA5Algorithm(IMSI);
 | 
				
			||||||
 | 
									if (!encryptionAlgorithm) {
 | 
				
			||||||
 | 
										LOG(DEBUG) << "A5/3 and A5/1 not supported: NOT sending Ciphering Mode Command on " << *DCCH << " for " << mobileID;
 | 
				
			||||||
 | 
									} else if (DCCH->decryptUplink_maybe(mobileID.digits(), encryptionAlgorithm)) {
 | 
				
			||||||
 | 
										LOG(DEBUG) << "sending Ciphering Mode Command on " << *DCCH << " for " << mobileID;
 | 
				
			||||||
 | 
										DCCH->send(GSM::L3CipheringModeCommand(
 | 
				
			||||||
 | 
											GSM::L3CipheringModeSetting(true, encryptionAlgorithm),
 | 
				
			||||||
 | 
											GSM::L3CipheringModeResponse(false)));
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										LOG(DEBUG) << "no ki: NOT sending Ciphering Mode Command on " << *DCCH << " for " << mobileID;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								gReports.incr("OpenBTS.GSM.MM.Authenticate.Success");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							catch(SIPTimeout) {
 | 
				
			||||||
 | 
								LOG(ALERT) "SIP authentication timed out.  Is the proxy running at " << gConfig.getStr("SIP.Proxy.Registration");
 | 
				
			||||||
 | 
								// Reject with a "network failure" cause code, 0x11.
 | 
				
			||||||
 | 
								DCCH->send(L3LocationUpdatingReject(0x11));
 | 
				
			||||||
 | 
								// HACK -- wait long enough for a response
 | 
				
			||||||
 | 
								// FIXME -- Why are we doing this?
 | 
				
			||||||
 | 
								sleep(4);
 | 
				
			||||||
 | 
								// Release the channel and return.
 | 
				
			||||||
 | 
								DCCH->send(L3ChannelRelease());
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This allows us to configure Open Registration
 | 
				
			||||||
 | 
						bool openRegistration = false;
 | 
				
			||||||
 | 
						string openRegistrationPattern = gConfig.getStr("Control.LUR.OpenRegistration");
 | 
				
			||||||
 | 
						if (openRegistrationPattern.length()) {
 | 
				
			||||||
 | 
							Regexp rxp(openRegistrationPattern.c_str());
 | 
				
			||||||
 | 
							openRegistration = rxp.match(IMSI);
 | 
				
			||||||
 | 
							string openRegistrationRejectPattern = gConfig.getStr("Control.LUR.OpenRegistration.Reject");
 | 
				
			||||||
 | 
							if (openRegistrationRejectPattern.length()) {
 | 
				
			||||||
 | 
								Regexp rxpReject(openRegistrationRejectPattern.c_str());
 | 
				
			||||||
 | 
								bool openRegistrationReject = rxpReject.match(IMSI);
 | 
				
			||||||
 | 
								openRegistration = openRegistration && !openRegistrationReject;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Query for IMEI?
 | 
				
			||||||
 | 
						// FIXME -- This needs to happen before sending the REGISTER method, so we can put it in a SIP header.
 | 
				
			||||||
 | 
						// See ticket #1101.
 | 
				
			||||||
 | 
						unsigned rejectCause = gConfig.getNum("Control.LUR.UnprovisionedRejectCause");
 | 
				
			||||||
 | 
						if (gConfig.getBool("Control.LUR.QueryIMEI")) {
 | 
				
			||||||
 | 
							DCCH->send(L3IdentityRequest(IMEIType));
 | 
				
			||||||
 | 
							L3Message* msg = getMessage(DCCH);
 | 
				
			||||||
 | 
							L3IdentityResponse *resp = dynamic_cast<L3IdentityResponse*>(msg);
 | 
				
			||||||
 | 
							if (!resp) {
 | 
				
			||||||
 | 
								if (msg) {
 | 
				
			||||||
 | 
									LOG(WARNING) << "Unexpected message " << *msg;
 | 
				
			||||||
 | 
									delete msg;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								throw UnexpectedMessage();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							LOG(INFO) << *resp;
 | 
				
			||||||
 | 
							string new_imei = resp->mobileID().digits();
 | 
				
			||||||
 | 
							if (!gTMSITable.IMEI(IMSI,new_imei.c_str())){
 | 
				
			||||||
 | 
								LOG(WARNING) << "failed access to TMSITable";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							//query subscriber registry for old imei, update if necessary
 | 
				
			||||||
 | 
							string old_imei = gSubscriberRegistry.imsiGet(name, "hardware");
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							//if we have a new imei and either there's no old one, or it is different...
 | 
				
			||||||
 | 
							if (!new_imei.empty() && (old_imei.empty() || old_imei != new_imei)){
 | 
				
			||||||
 | 
								LOG(INFO) << "Updating IMSI" << IMSI << " to IMEI:" << new_imei;
 | 
				
			||||||
 | 
								if (gSubscriberRegistry.imsiSet(name,"RRLPSupported", "1")) {
 | 
				
			||||||
 | 
								 	LOG(INFO) << "SR RRLPSupported update problem";
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (gSubscriberRegistry.imsiSet(name,"hardware", new_imei)) {
 | 
				
			||||||
 | 
									LOG(INFO) << "SR hardware update problem";
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							delete msg;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// We fail closed unless we're configured otherwise
 | 
						// We fail closed unless we're configured otherwise
 | 
				
			||||||
	if (!success && !openRegistration) {
 | 
						if (!success && !openRegistration) {
 | 
				
			||||||
		LOG(INFO) << "registration FAILED: " << mobileID;
 | 
							LOG(INFO) << "registration FAILED: " << mobileID;
 | 
				
			||||||
		DCCH->send(L3LocationUpdatingReject(gConfig.getNum("Control.LUR.UnprovisionedRejectCause")));
 | 
							DCCH->send(L3LocationUpdatingReject(rejectCause));
 | 
				
			||||||
		if (!preexistingTMSI) {
 | 
							if (!preexistingTMSI) {
 | 
				
			||||||
			sendWelcomeMessage( "Control.LUR.FailedRegistration.Message",
 | 
								sendWelcomeMessage( "Control.LUR.FailedRegistration.Message",
 | 
				
			||||||
				"Control.LUR.FailedRegistration.ShortCode", IMSI,DCCH);
 | 
									"Control.LUR.FailedRegistration.ShortCode", IMSI,DCCH);
 | 
				
			||||||
@@ -289,7 +426,7 @@ void Control::LocationUpdatingController(const L3LocationUpdatingRequest* lur, L
 | 
				
			|||||||
		DCCH->send(L3MMInformation(gConfig.getStr("GSM.Identity.ShortName").c_str()));
 | 
							DCCH->send(L3MMInformation(gConfig.getStr("GSM.Identity.ShortName").c_str()));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Accept. Make a TMSI assignment, too, if needed.
 | 
						// Accept. Make a TMSI assignment, too, if needed.
 | 
				
			||||||
	if (preexistingTMSI || !gConfig.defines("Control.LUR.SendTMSIs")) {
 | 
						if (preexistingTMSI || !gConfig.getBool("Control.LUR.SendTMSIs")) {
 | 
				
			||||||
		DCCH->send(L3LocationUpdatingAccept(gBTS.LAI()));
 | 
							DCCH->send(L3LocationUpdatingAccept(gBTS.LAI()));
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		assert(newTMSI);
 | 
							assert(newTMSI);
 | 
				
			||||||
@@ -305,7 +442,7 @@ void Control::LocationUpdatingController(const L3LocationUpdatingRequest* lur, L
 | 
				
			|||||||
		delete resp;
 | 
							delete resp;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (gConfig.defines("Control.LUR.QueryRRLP")) {
 | 
						if (gConfig.getBool("Control.LUR.QueryRRLP")) {
 | 
				
			||||||
		// Query for RRLP
 | 
							// Query for RRLP
 | 
				
			||||||
		if (!sendRRLP(mobileID, DCCH)) {
 | 
							if (!sendRRLP(mobileID, DCCH)) {
 | 
				
			||||||
			LOG(INFO) << "RRLP request failed";
 | 
								LOG(INFO) << "RRLP request failed";
 | 
				
			||||||
@@ -321,6 +458,9 @@ void Control::LocationUpdatingController(const L3LocationUpdatingRequest* lur, L
 | 
				
			|||||||
			sendWelcomeMessage( "Control.LUR.OpenRegistration.Message",
 | 
								sendWelcomeMessage( "Control.LUR.OpenRegistration.Message",
 | 
				
			||||||
				"Control.LUR.OpenRegistration.ShortCode", IMSI, DCCH);
 | 
									"Control.LUR.OpenRegistration.ShortCode", IMSI, DCCH);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// set unix time of most recent registration
 | 
				
			||||||
 | 
							// No - this happending in the registration proxy.
 | 
				
			||||||
 | 
							//gSubscriberRegistry.setRegTime(name);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Release the channel and return.
 | 
						// Release the channel and return.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,24 +3,14 @@
 | 
				
			|||||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -98,9 +98,10 @@ RRLPServer::RRLPServer(L3MobileIdentity wMobileID, LogicalChannel *wDCCH)
 | 
				
			|||||||
	DCCH = wDCCH;
 | 
						DCCH = wDCCH;
 | 
				
			||||||
	// name of subscriber
 | 
						// name of subscriber
 | 
				
			||||||
	name = string("IMSI") + mobileID.digits();
 | 
						name = string("IMSI") + mobileID.digits();
 | 
				
			||||||
 | 
						// don't continue if IMSI has a RRLP support flag and it's false
 | 
				
			||||||
	//if IMEI tagging enabled, check if this IMEI (which is updated elsewhere) has RRLP disabled
 | 
						//if IMEI tagging enabled, check if this IMEI (which is updated elsewhere) has RRLP disabled
 | 
				
			||||||
	//otherwise just go on
 | 
						//otherwise just go on
 | 
				
			||||||
	if (gConfig.defines("Control.LUR.QueryIMEI")){
 | 
						if (gConfig.getBool("Control.LUR.QueryIMEI")){
 | 
				
			||||||
		//check supported bit
 | 
							//check supported bit
 | 
				
			||||||
		string supported= gSubscriberRegistry.imsiGet(name, "RRLPSupported");
 | 
							string supported= gSubscriberRegistry.imsiGet(name, "RRLPSupported");
 | 
				
			||||||
		if(supported.empty() || supported == "0"){
 | 
							if(supported.empty() || supported == "0"){
 | 
				
			||||||
@@ -192,6 +193,7 @@ bool RRLPServer::transact()
 | 
				
			|||||||
		// bounce off mobile
 | 
							// bounce off mobile
 | 
				
			||||||
		if (apdus.size() == 0) {
 | 
							if (apdus.size() == 0) {
 | 
				
			||||||
			LOG(INFO) << "missing apdu for mobile";
 | 
								LOG(INFO) << "missing apdu for mobile";
 | 
				
			||||||
 | 
								LOG(ERR) << "MS did not respond gracefully to RRLP message.";
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		string apdu = apdus[0];
 | 
							string apdu = apdus[0];
 | 
				
			||||||
@@ -199,6 +201,7 @@ bool RRLPServer::transact()
 | 
				
			|||||||
		BitVector bv(apdu.size()*4);
 | 
							BitVector bv(apdu.size()*4);
 | 
				
			||||||
		if (!bv.unhex(apdu.c_str())) {
 | 
							if (!bv.unhex(apdu.c_str())) {
 | 
				
			||||||
			LOG(INFO) << "BitVector::unhex problem";
 | 
								LOG(INFO) << "BitVector::unhex problem";
 | 
				
			||||||
 | 
								LOG(ERR) << "MS did not respond gracefully to RRLP message.";
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -206,13 +209,15 @@ bool RRLPServer::transact()
 | 
				
			|||||||
		// Receive an L3 frame with a timeout.  Timeout loc req response time max + 2 seconds.
 | 
							// Receive an L3 frame with a timeout.  Timeout loc req response time max + 2 seconds.
 | 
				
			||||||
		L3Frame* resp = DCCH->recv(130000);
 | 
							L3Frame* resp = DCCH->recv(130000);
 | 
				
			||||||
		if (!resp) {
 | 
							if (!resp) {
 | 
				
			||||||
 | 
								LOG(INFO) << "timed out";
 | 
				
			||||||
 | 
								LOG(ERR) << "MS did not respond gracefully to RRLP message.";
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		LOG(INFO) << "RRLPQuery returned " << *resp;
 | 
							LOG(INFO) << "RRLPQuery returned " << *resp;
 | 
				
			||||||
		if (resp->primitive() != DATA) {
 | 
							if (resp->primitive() != DATA) {
 | 
				
			||||||
			LOG(INFO) << "didn't receive data";
 | 
								LOG(INFO) << "didn't receive data";
 | 
				
			||||||
			switch (resp->primitive()) {
 | 
								switch (resp->primitive()) {
 | 
				
			||||||
				case ESTABLISH: LOG(INFO) << "channel establihsment"; break;
 | 
									case ESTABLISH: LOG(INFO) << "channel establishment"; break;
 | 
				
			||||||
				case RELEASE: LOG(INFO) << "normal channel release"; break;
 | 
									case RELEASE: LOG(INFO) << "normal channel release"; break;
 | 
				
			||||||
				case DATA: LOG(INFO) << "multiframe data transfer"; break;
 | 
									case DATA: LOG(INFO) << "multiframe data transfer"; break;
 | 
				
			||||||
				case UNIT_DATA: LOG(INFO) << "datagram-type data transfer"; break;
 | 
									case UNIT_DATA: LOG(INFO) << "datagram-type data transfer"; break;
 | 
				
			||||||
@@ -221,6 +226,7 @@ bool RRLPServer::transact()
 | 
				
			|||||||
				default: LOG(INFO) << "unrecognized primitive response"; break;
 | 
									default: LOG(INFO) << "unrecognized primitive response"; break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			delete resp;
 | 
								delete resp;
 | 
				
			||||||
 | 
								LOG(ERR) << "MS did not respond gracefully to RRLP message.";
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		const unsigned PD_RR = 6;
 | 
							const unsigned PD_RR = 6;
 | 
				
			||||||
@@ -228,17 +234,20 @@ bool RRLPServer::transact()
 | 
				
			|||||||
		if (resp->PD() != PD_RR) {
 | 
							if (resp->PD() != PD_RR) {
 | 
				
			||||||
			LOG(INFO) << "didn't receive an RR message";
 | 
								LOG(INFO) << "didn't receive an RR message";
 | 
				
			||||||
			delete resp;
 | 
								delete resp;
 | 
				
			||||||
 | 
								LOG(ERR) << "MS did not respond gracefully to RRLP message.";
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		const unsigned MTI_RR_STATUS = 18;
 | 
							const unsigned MTI_RR_STATUS = 18;
 | 
				
			||||||
		if (resp->MTI() == MTI_RR_STATUS) {
 | 
							if (resp->MTI() == MTI_RR_STATUS) {
 | 
				
			||||||
 | 
								ostringstream os2;
 | 
				
			||||||
			int cause = resp->peekField(16, 8);
 | 
								int cause = resp->peekField(16, 8);
 | 
				
			||||||
			delete resp;
 | 
								delete resp;
 | 
				
			||||||
			switch (cause) {
 | 
								switch (cause) {
 | 
				
			||||||
				case 97:
 | 
									case 97:
 | 
				
			||||||
					LOG(INFO) << "MS says: message not implemented";
 | 
										LOG(INFO) << "MS says: message not implemented";
 | 
				
			||||||
					//Rejection code only useful if we're gathering IMEIs
 | 
										//Rejection code only useful if we're gathering IMEIs
 | 
				
			||||||
					if (gConfig.defines("Control.LUR.QueryIMEI")){
 | 
										if (gConfig.getBool("Control.LUR.QueryIMEI")){
 | 
				
			||||||
 | 
											// flag unsupported in SR so we don't waste time on it again
 | 
				
			||||||
						// flag unsupported in SR so we don't waste time on it again
 | 
											// flag unsupported in SR so we don't waste time on it again
 | 
				
			||||||
						if (gSubscriberRegistry.imsiSet(name, "RRLPSupported", "0")) {
 | 
											if (gSubscriberRegistry.imsiSet(name, "RRLPSupported", "0")) {
 | 
				
			||||||
							LOG(INFO) << "SR update problem";
 | 
												LOG(INFO) << "SR update problem";
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										23
									
								
								Control/RRLP_PDU_Test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								Control/RRLP_PDU_Test.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					#include "Configuration.h"
 | 
				
			||||||
 | 
					#include "RRLPQueryController.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace GSM;
 | 
				
			||||||
 | 
					using namespace GSM::RRLP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// compile one liner:
 | 
				
			||||||
 | 
					// g++ -DRRLP_TEST_HACK -L../GSM/.libs -L../CommonLibs/.libs -I../CLI -I../Globals -I../GSM -I../CommonLibs RRLPQueryController.cpp RRLP_PDU_Test.cpp -lcommon -lGSM -lpthread -o RRLP_PDU_Test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Required by various stuff - I think a TODO is to allow tests to work without
 | 
				
			||||||
 | 
					// having to copy this line around.
 | 
				
			||||||
 | 
					ConfigurationTable gConfig("OpenBTS.config");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // This is a MsrPositionRsp with valid coordinates
 | 
				
			||||||
 | 
					    // 38.28411340713501,237.95414686203003,22 (as parsed by the erlang automatically generated
 | 
				
			||||||
 | 
					    // implementation - checked against a map).
 | 
				
			||||||
 | 
					    BitVector test("0000011000111000000000000001011000100010000100011111111111111111010010101111101111011110101101100100000011010110110100111000111010100011110010010100111000000000010001000001100000011000110010000010000100010000");
 | 
				
			||||||
 | 
					    RRLPQueryController c(test);
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1,29 +1,21 @@
 | 
				
			|||||||
/**@file GSM Radio Resource procedures, GSM 04.18 and GSM 04.08. */
 | 
					/**@file GSM Radio Resource procedures, GSM 04.18 and GSM 04.08. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
* Copyright 2011 Range Networks, Inc.
 | 
					* Copyright 2011, 2012 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -33,6 +25,7 @@
 | 
				
			|||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <list>
 | 
					#include <list>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <Defines.h>
 | 
				
			||||||
#include "ControlCommon.h"
 | 
					#include "ControlCommon.h"
 | 
				
			||||||
#include "TransactionTable.h"
 | 
					#include "TransactionTable.h"
 | 
				
			||||||
#include "RadioResource.h"
 | 
					#include "RadioResource.h"
 | 
				
			||||||
@@ -41,6 +34,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <GSMLogicalChannel.h>
 | 
					#include <GSMLogicalChannel.h>
 | 
				
			||||||
#include <GSMConfig.h>
 | 
					#include <GSMConfig.h>
 | 
				
			||||||
 | 
					#include "../GPRS/GPRSExport.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <NeighborTable.h>
 | 
				
			||||||
 | 
					#include <Peering.h>
 | 
				
			||||||
 | 
					#include <SIPEngine.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <Reporting.h>
 | 
					#include <Reporting.h>
 | 
				
			||||||
#include <Logger.h>
 | 
					#include <Logger.h>
 | 
				
			||||||
@@ -49,12 +48,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
using namespace std;
 | 
					using namespace std;
 | 
				
			||||||
using namespace GSM;
 | 
					using namespace GSM;
 | 
				
			||||||
using namespace Control;
 | 
					using namespace Control;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern unsigned allocateRTPPorts();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,6 +66,7 @@ using namespace Control;
 | 
				
			|||||||
	@param RA The request reference from the channel request message.
 | 
						@param RA The request reference from the channel request message.
 | 
				
			||||||
	@return channel type code, undefined if not a supported service
 | 
						@return channel type code, undefined if not a supported service
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					static
 | 
				
			||||||
ChannelType decodeChannelNeeded(unsigned RA)
 | 
					ChannelType decodeChannelNeeded(unsigned RA)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// This code is based on GSM 04.08 Table 9.9.
 | 
						// This code is based on GSM 04.08 Table 9.9.
 | 
				
			||||||
@@ -74,6 +74,7 @@ ChannelType decodeChannelNeeded(unsigned RA)
 | 
				
			|||||||
	unsigned RA4 = RA>>4;
 | 
						unsigned RA4 = RA>>4;
 | 
				
			||||||
	unsigned RA5 = RA>>5;
 | 
						unsigned RA5 = RA>>5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Answer to paging, Table 9.9a.
 | 
						// Answer to paging, Table 9.9a.
 | 
				
			||||||
	// We don't support TCH/H, so it's wither SDCCH or TCH/F.
 | 
						// We don't support TCH/H, so it's wither SDCCH or TCH/F.
 | 
				
			||||||
	// The spec allows for "SDCCH-only" MS.  We won't support that here.
 | 
						// The spec allows for "SDCCH-only" MS.  We won't support that here.
 | 
				
			||||||
@@ -82,14 +83,15 @@ ChannelType decodeChannelNeeded(unsigned RA)
 | 
				
			|||||||
	if (RA4 == 0x01) return SDCCHType;		// SDCCH
 | 
						if (RA4 == 0x01) return SDCCHType;		// SDCCH
 | 
				
			||||||
	if (RA4 == 0x02) return TCHFType;		// TCH/F
 | 
						if (RA4 == 0x02) return TCHFType;		// TCH/F
 | 
				
			||||||
	if (RA4 == 0x03) return TCHFType;		// TCH/F
 | 
						if (RA4 == 0x03) return TCHFType;		// TCH/F
 | 
				
			||||||
 | 
						if ((RA&0xf8) == 0x78 && RA != 0x7f) return PSingleBlock1PhaseType;
 | 
				
			||||||
 | 
						if ((RA&0xf8) == 0x70) return PSingleBlock2PhaseType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int NECI = gConfig.getNum("GSM.CellSelection.NECI");
 | 
						int NECI = gConfig.getNum("GSM.CellSelection.NECI");
 | 
				
			||||||
	if (NECI==0) {
 | 
						if (NECI==0) {
 | 
				
			||||||
		if (RA5 == 0x07) return SDCCHType;		// MOC or SDCCH procedures
 | 
							if (RA5 == 0x07) return SDCCHType;		// MOC or SDCCH procedures
 | 
				
			||||||
		if (RA5 == 0x00) return SDCCHType;		// location updating
 | 
							if (RA5 == 0x00) return SDCCHType;		// location updating
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		assert(NECI==1);
 | 
							if (gConfig.getBool("Control.VEA")) {
 | 
				
			||||||
		if (gConfig.defines("Control.VEA")) {
 | 
					 | 
				
			||||||
			// Very Early Assignment
 | 
								// Very Early Assignment
 | 
				
			||||||
			if (RA5 == 0x07) return TCHFType;		// MOC for TCH/F
 | 
								if (RA5 == 0x07) return TCHFType;		// MOC for TCH/F
 | 
				
			||||||
			if (RA4 == 0x04) return TCHFType;		// MOC, TCH/H sufficient
 | 
								if (RA4 == 0x04) return TCHFType;		// MOC, TCH/H sufficient
 | 
				
			||||||
@@ -103,12 +105,13 @@ ChannelType decodeChannelNeeded(unsigned RA)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Anything else falls through to here.
 | 
						// Anything else falls through to here.
 | 
				
			||||||
	// We are still ignoring data calls, GPRS, LMU.
 | 
						// We are still ignoring data calls, LMU.
 | 
				
			||||||
	return UndefinedCHType;
 | 
						return UndefinedCHType;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Return true if RA indicates LUR. */
 | 
					/** Return true if RA indicates LUR. */
 | 
				
			||||||
 | 
					static
 | 
				
			||||||
bool requestingLUR(unsigned RA)
 | 
					bool requestingLUR(unsigned RA)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int NECI = gConfig.getNum("GSM.CellSelection.NECI");
 | 
						int NECI = gConfig.getNum("GSM.CellSelection.NECI");
 | 
				
			||||||
@@ -117,10 +120,8 @@ bool requestingLUR(unsigned RA)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Decode RACH bits and send an immediate assignment; may block waiting for a channel for an SOS call. */
 | 
				
			||||||
 | 
					static
 | 
				
			||||||
 | 
					 | 
				
			||||||
/** Decode RACH bits and send an immediate assignment; may block waiting for a channel. */
 | 
					 | 
				
			||||||
void AccessGrantResponder(
 | 
					void AccessGrantResponder(
 | 
				
			||||||
		unsigned RA, const GSM::Time& when,
 | 
							unsigned RA, const GSM::Time& when,
 | 
				
			||||||
		float RSSI, float timingError)
 | 
							float RSSI, float timingError)
 | 
				
			||||||
@@ -149,18 +150,39 @@ void AccessGrantResponder(
 | 
				
			|||||||
	static const int maxAge = GSM::RACHSpreadSlots[txInteger] + GSM::RACHWaitSParam[txInteger];
 | 
						static const int maxAge = GSM::RACHSpreadSlots[txInteger] + GSM::RACHWaitSParam[txInteger];
 | 
				
			||||||
	// Check burst age.
 | 
						// Check burst age.
 | 
				
			||||||
	int age = gBTS.time() - when;
 | 
						int age = gBTS.time() - when;
 | 
				
			||||||
	LOG(INFO) << "RA=0x" << hex << RA << dec
 | 
						ChannelType chtype = decodeChannelNeeded(RA);
 | 
				
			||||||
		<< " when=" << when << " age=" << age
 | 
						int lur = requestingLUR(RA);
 | 
				
			||||||
		<< " delay=" << timingError << " RSSI=" << RSSI;
 | 
						int gprs = (chtype == PSingleBlock1PhaseType) || (chtype == PSingleBlock2PhaseType); 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This is for debugging.
 | 
				
			||||||
 | 
						if (GPRS::GPRSDebug && gprs) {
 | 
				
			||||||
 | 
							Time now = gBTS.time();
 | 
				
			||||||
 | 
							LOG(NOTICE) << "RACH" <<LOGVAR(now) <<LOGVAR(chtype) <<LOGVAR(lur) <<LOGVAR(gprs)
 | 
				
			||||||
 | 
							<<LOGVAR(when)<<LOGVAR(age)<<LOGVAR2("TE",timingError)<<LOGVAR(RSSI)<<LOGHEX(RA);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
							LOG(INFO) << "**Incoming Burst**"<<LOGVAR(lur)<<LOGVAR(gprs)
 | 
				
			||||||
 | 
							<<LOGVAR(when)<<LOGVAR(age)<<LOGVAR2("TE",timingError)<<LOGVAR(RSSI)<<LOGHEX(RA);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//LOG(INFO) << "Incoming Burst: RA=0x" << hex << RA << dec
 | 
				
			||||||
 | 
						//	<<LOGVAR(when) <<LOGVAR(age)
 | 
				
			||||||
 | 
						//	<< " delay=" << timingError <<LOGVAR(chtype);
 | 
				
			||||||
	if (age>maxAge) {
 | 
						if (age>maxAge) {
 | 
				
			||||||
		LOG(WARNING) << "ignoring RACH bust with age " << age;
 | 
							LOG(WARNING) << "ignoring RACH bust with age " << age;
 | 
				
			||||||
		gBTS.growT3122()/1000;
 | 
							// FIXME -- What was supposed to be happening here?
 | 
				
			||||||
 | 
							gBTS.growT3122()/1000;		// Hmmm...
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Screen for delay.
 | 
						// Screen for delay.
 | 
				
			||||||
	if (timingError>gConfig.getNum("GSM.MS.TA.Max")) {
 | 
						if (timingError>gConfig.getNum("GSM.MS.TA.Max")) {
 | 
				
			||||||
		LOG(WARNING) << "ignoring RACH burst with delay " << timingError;
 | 
							LOG(NOTICE) << "ignoring RACH burst with delay="<<timingError<<LOGVAR(chtype);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (chtype == PSingleBlock1PhaseType || chtype == PSingleBlock2PhaseType) {
 | 
				
			||||||
 | 
							// This is a request for a GPRS TBF.  It will get queued in the GPRS code
 | 
				
			||||||
 | 
							// and handled when the GPRS MAC service loop gets around to it.
 | 
				
			||||||
 | 
							// If GPRS is not enabled or is busy, it may just get dropped.
 | 
				
			||||||
 | 
							GPRS::GPRSProcessRACH(RA,when,RSSI,timingError);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -169,18 +191,21 @@ void AccessGrantResponder(
 | 
				
			|||||||
	// Someone had better have created a least one AGCH.
 | 
						// Someone had better have created a least one AGCH.
 | 
				
			||||||
	assert(AGCH);
 | 
						assert(AGCH);
 | 
				
			||||||
	// Check AGCH load now.
 | 
						// Check AGCH load now.
 | 
				
			||||||
	if (AGCH->load()>gConfig.getNum("GSM.CCCH.AGCH.QMax")) {
 | 
						// (pat) The default value is 5, so about 1.25 second for a system
 | 
				
			||||||
		LOG(WARNING) "AGCH congestion";
 | 
						// with a C0T0 beacon with only one CCCH.
 | 
				
			||||||
 | 
						if ((int)AGCH->load()>gConfig.getNum("GSM.CCCH.AGCH.QMax")) {
 | 
				
			||||||
 | 
							LOG(CRIT) << "AGCH congestion";
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Check for location update.
 | 
						// Check for location update.
 | 
				
			||||||
	// This gives LUR a lower priority than other services.
 | 
						// This gives LUR a lower priority than other services.
 | 
				
			||||||
 | 
						// (pat): LUR = Location Update Request Message
 | 
				
			||||||
	if (requestingLUR(RA)) {
 | 
						if (requestingLUR(RA)) {
 | 
				
			||||||
		// Don't answer this LUR if it will not leave enough channels open for other operations.
 | 
							// Don't answer this LUR if it will not leave enough channels open for other operations.
 | 
				
			||||||
		if ((int)gBTS.SDCCHAvailable()<=gConfig.getNum("GSM.Channels.SDCCHReserve")) {
 | 
							if ((int)gBTS.SDCCHAvailable()<=gConfig.getNum("GSM.Channels.SDCCHReserve")) {
 | 
				
			||||||
			unsigned waitTime = gBTS.growT3122()/1000;
 | 
								unsigned waitTime = gBTS.growT3122()/1000;
 | 
				
			||||||
			LOG(WARNING) << "LUR congestion, RA=" << RA << " T3122=" << waitTime;
 | 
								LOG(CRIT) << "LUR congestion, RA=" << RA << " T3122=" << waitTime;
 | 
				
			||||||
			const L3ImmediateAssignmentReject reject(L3RequestReference(RA,when),waitTime);
 | 
								const L3ImmediateAssignmentReject reject(L3RequestReference(RA,when),waitTime);
 | 
				
			||||||
			LOG(DEBUG) << "LUR rejection, sending " << reject;
 | 
								LOG(DEBUG) << "LUR rejection, sending " << reject;
 | 
				
			||||||
			AGCH->send(reject);
 | 
								AGCH->send(reject);
 | 
				
			||||||
@@ -191,9 +216,24 @@ void AccessGrantResponder(
 | 
				
			|||||||
	// Allocate the channel according to the needed type indicated by RA.
 | 
						// Allocate the channel according to the needed type indicated by RA.
 | 
				
			||||||
	// The returned channel is already open and ready for the transaction.
 | 
						// The returned channel is already open and ready for the transaction.
 | 
				
			||||||
	LogicalChannel *LCH = NULL;
 | 
						LogicalChannel *LCH = NULL;
 | 
				
			||||||
	switch (decodeChannelNeeded(RA)) {
 | 
						switch (chtype) {
 | 
				
			||||||
		case TCHFType: LCH = gBTS.getTCH(); break;
 | 
							case TCHFType: LCH = gBTS.getTCH(); break;
 | 
				
			||||||
		case SDCCHType: LCH = gBTS.getSDCCH(); break;
 | 
							case SDCCHType: LCH = gBTS.getSDCCH(); break;
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					        // GSM04.08 sec 3.5.2.1.2
 | 
				
			||||||
 | 
					        case PSingleBlock1PhaseType:
 | 
				
			||||||
 | 
					        case PSingleBlock2PhaseType:
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                L3RRMessage *msg = GPRS::GPRSProcessRACH(chtype,
 | 
				
			||||||
 | 
					                        L3RequestReference(RA,when),
 | 
				
			||||||
 | 
					                        RSSI,timingError,AGCH);
 | 
				
			||||||
 | 
					                if (msg) {
 | 
				
			||||||
 | 
					                    AGCH->send(*msg);
 | 
				
			||||||
 | 
					                    delete msg;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		// If we don't support the service, assign to an SDCCH and we can reject it in L3.
 | 
							// If we don't support the service, assign to an SDCCH and we can reject it in L3.
 | 
				
			||||||
		case UndefinedCHType:
 | 
							case UndefinedCHType:
 | 
				
			||||||
			LOG(NOTICE) << "RACH burst for unsupported service RA=" << RA;
 | 
								LOG(NOTICE) << "RACH burst for unsupported service RA=" << RA;
 | 
				
			||||||
@@ -206,18 +246,22 @@ void AccessGrantResponder(
 | 
				
			|||||||
	// Nothing available?
 | 
						// Nothing available?
 | 
				
			||||||
	if (!LCH) {
 | 
						if (!LCH) {
 | 
				
			||||||
		// Rejection, GSM 04.08 3.3.1.1.3.2.
 | 
							// Rejection, GSM 04.08 3.3.1.1.3.2.
 | 
				
			||||||
		// But since we recognize SOS calls already,
 | 
					 | 
				
			||||||
		// we might as well save some AGCH bandwidth.
 | 
					 | 
				
			||||||
		unsigned waitTime = gBTS.growT3122()/1000;
 | 
							unsigned waitTime = gBTS.growT3122()/1000;
 | 
				
			||||||
		LOG(WARNING) << "congestion, RA=" << RA << " T3122=" << waitTime;
 | 
							// TODO: If all channels are statically allocated for gprs, dont throw an alert.
 | 
				
			||||||
 | 
							LOG(CRIT) << "congestion, RA=" << RA << " T3122=" << waitTime;
 | 
				
			||||||
		const L3ImmediateAssignmentReject reject(L3RequestReference(RA,when),waitTime);
 | 
							const L3ImmediateAssignmentReject reject(L3RequestReference(RA,when),waitTime);
 | 
				
			||||||
		LOG(DEBUG) << "rejection, sending " << reject;
 | 
							LOG(DEBUG) << "rejection, sending " << reject;
 | 
				
			||||||
		AGCH->send(reject);
 | 
							AGCH->send(reject);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// (pat) gprs todo: Notify GPRS that the MS is getting a voice channel.
 | 
				
			||||||
 | 
						// It may imply abandonment of packet contexts, if the phone does not
 | 
				
			||||||
 | 
						// support DTM (Dual Transfer Mode.)  There may be other housekeeping
 | 
				
			||||||
 | 
						// for DTM phones; haven't looked into it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Set the channel physical parameters from the RACH burst.
 | 
						// Set the channel physical parameters from the RACH burst.
 | 
				
			||||||
	LCH->setPhy(RSSI,timingError);
 | 
						LCH->setPhy(RSSI,timingError,gBTS.clock().systime(when.FN()));
 | 
				
			||||||
	gReports.incr("OpenBTS.GSM.RR.RACH.TA.Accepted",(int)(timingError));
 | 
						gReports.incr("OpenBTS.GSM.RR.RACH.TA.Accepted",(int)(timingError));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Assignment, GSM 04.08 3.3.1.1.3.1.
 | 
						// Assignment, GSM 04.08 3.3.1.1.3.1.
 | 
				
			||||||
@@ -231,7 +275,11 @@ void AccessGrantResponder(
 | 
				
			|||||||
		LCH->channelDescription(),
 | 
							LCH->channelDescription(),
 | 
				
			||||||
		L3TimingAdvance(initialTA)
 | 
							L3TimingAdvance(initialTA)
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
	LOG(INFO) << "sending " << assign;
 | 
						LOG(INFO) << "sending L3ImmediateAssignment " << assign;
 | 
				
			||||||
 | 
						// (pat) This call appears to block.
 | 
				
			||||||
 | 
						// (david) Not anymore. It got fixed in the trunk while you were working on GPRS.
 | 
				
			||||||
 | 
						// (doug) Adding in a delay to make sure SI5/6 get out before IA.
 | 
				
			||||||
 | 
						sleepFrames(20);
 | 
				
			||||||
	AGCH->send(assign);
 | 
						AGCH->send(assign);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// On successful allocation, shrink T3122.
 | 
						// On successful allocation, shrink T3122.
 | 
				
			||||||
@@ -254,6 +302,256 @@ void* Control::AccessGrantServiceLoop(void*)
 | 
				
			|||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void abortInboundHandover(TransactionEntry* transaction, unsigned cause, GSM::LogicalChannel *LCH=NULL)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						LOG(DEBUG) << "aborting inbound handover " << *transaction;
 | 
				
			||||||
 | 
						char ind[100];
 | 
				
			||||||
 | 
						unsigned holdoff = gConfig.getNum("GSM.Handover.FailureHoldoff");
 | 
				
			||||||
 | 
						sprintf(ind,"IND HANDOVER_FAILURE %u %u %u", transaction->ID(),cause,holdoff);
 | 
				
			||||||
 | 
						gPeerInterface.sendUntilAck(transaction,ind);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (LCH) LCH->send(HARDRELEASE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gTransactionTable.remove(transaction);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool Control::SaveHandoverAccess(unsigned handoverReference, float RSSI, float timingError, const GSM::Time& timestamp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// In this function, we are "BS2" in the ladder diagram.
 | 
				
			||||||
 | 
						// This is called from L1 when a handover burst arrives.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// We will need to use the transaction record to carry the parameters.
 | 
				
			||||||
 | 
						// We put this here to avoid dealing with the transaction table in L1.
 | 
				
			||||||
 | 
						TransactionEntry *transaction = gTransactionTable.inboundHandover(handoverReference);
 | 
				
			||||||
 | 
						if (!transaction) {
 | 
				
			||||||
 | 
							LOG(ERR) << "no inbound handover with reference " << handoverReference;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (timingError > gConfig.getNum("GSM.MS.TA.Max")) {
 | 
				
			||||||
 | 
							// Handover failure.
 | 
				
			||||||
 | 
							LOG(NOTICE) << "handover failure on due to TA=" << timingError << " for " << *transaction;
 | 
				
			||||||
 | 
							// RR cause 8: Handover impossible, timing advance out of range
 | 
				
			||||||
 | 
							abortInboundHandover(transaction,8);
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOG(INFO) << "saving handover access for " << *transaction;
 | 
				
			||||||
 | 
						transaction->setInboundHandover(RSSI,timingError,gBTS.clock().systime(timestamp));
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Control::ProcessHandoverAccess(GSM::TCHFACCHLogicalChannel *TCH)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// In this function, we are "BS2" in the ladder diagram.
 | 
				
			||||||
 | 
						// This is called from the DCCH dispatcher when it gets a HANDOVER_ACCESS primtive.
 | 
				
			||||||
 | 
						// The information it needs was saved in the transaction table by Control::SaveHandoverAccess.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert(TCH);
 | 
				
			||||||
 | 
						LOG(DEBUG) << *TCH;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TransactionEntry *transaction = gTransactionTable.inboundHandover(TCH);
 | 
				
			||||||
 | 
						if (!transaction) {
 | 
				
			||||||
 | 
							LOG(WARNING) << "handover access with no inbound transaction on " << *TCH;
 | 
				
			||||||
 | 
							TCH->send(HARDRELEASE);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// clear handover in transceiver
 | 
				
			||||||
 | 
						LOG(DEBUG) << *transaction;
 | 
				
			||||||
 | 
						transaction->channel()->handoverPending(false);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						// Respond to handset with physical information until we get Handover Complete.
 | 
				
			||||||
 | 
						int TA = (int)(transaction->inboundTimingError() + 0.5F);
 | 
				
			||||||
 | 
						if (TA<0) TA=0;
 | 
				
			||||||
 | 
						if (TA>62) TA=62;
 | 
				
			||||||
 | 
						unsigned repeatTimeout = gConfig.getNum("GSM.Timer.T3105");
 | 
				
			||||||
 | 
						unsigned sendCount = gConfig.getNum("GSM.Ny1");
 | 
				
			||||||
 | 
						L3Frame* frame = NULL;
 | 
				
			||||||
 | 
						while (!frame && sendCount) {
 | 
				
			||||||
 | 
							TCH->send(L3PhysicalInformation(L3TimingAdvance(TA)),GSM::UNIT_DATA);
 | 
				
			||||||
 | 
							sendCount--;
 | 
				
			||||||
 | 
							frame = TCH->recv(repeatTimeout);
 | 
				
			||||||
 | 
							if (frame && frame->primitive() == HANDOVER_ACCESS) {
 | 
				
			||||||
 | 
								LOG(NOTICE) << "flushing HANDOVER_ACCESS while waiting for Handover Complete";
 | 
				
			||||||
 | 
								delete frame;
 | 
				
			||||||
 | 
								frame = NULL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Timed out?
 | 
				
			||||||
 | 
						if (!frame) {
 | 
				
			||||||
 | 
							LOG(NOTICE) << "timed out waiting for Handover Complete on " << *TCH << " for " << *transaction;
 | 
				
			||||||
 | 
							// RR cause 4: Abnormal release, no activity on the radio path
 | 
				
			||||||
 | 
							abortInboundHandover(transaction,4,TCH);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Screwed up channel?
 | 
				
			||||||
 | 
						if (frame->primitive()!=ESTABLISH) {
 | 
				
			||||||
 | 
							LOG(NOTICE) << "unexpected primitive waiting for Handover Complete on "
 | 
				
			||||||
 | 
								<< *TCH << ": " << *frame << " for " << *transaction;
 | 
				
			||||||
 | 
							delete frame;
 | 
				
			||||||
 | 
							// RR cause 0x62: Message not compatible with protocol state
 | 
				
			||||||
 | 
							abortInboundHandover(transaction,0x62,TCH);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get the next frame, should be HandoverComplete.
 | 
				
			||||||
 | 
						delete frame;
 | 
				
			||||||
 | 
						frame = TCH->recv();
 | 
				
			||||||
 | 
						L3Message* msg = parseL3(*frame);
 | 
				
			||||||
 | 
						if (!msg) {
 | 
				
			||||||
 | 
							LOG(NOTICE) << "unparsable message waiting for Handover Complete on "
 | 
				
			||||||
 | 
								<< *TCH << ": " << *frame << " for " << *transaction;
 | 
				
			||||||
 | 
							delete frame;
 | 
				
			||||||
 | 
							// RR cause 0x62: Message not compatible with protocol state
 | 
				
			||||||
 | 
							TCH->send(L3ChannelRelease(0x62));
 | 
				
			||||||
 | 
							abortInboundHandover(transaction,0x62,TCH);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						delete frame;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3HandoverComplete* complete = dynamic_cast<L3HandoverComplete*>(msg);
 | 
				
			||||||
 | 
						if (!complete) {
 | 
				
			||||||
 | 
							LOG(NOTICE) << "expecting for Handover Complete on "
 | 
				
			||||||
 | 
								<< *TCH << "but got: " << *msg << " for " << *transaction;
 | 
				
			||||||
 | 
							delete frame;
 | 
				
			||||||
 | 
							// RR cause 0x62: Message not compatible with protocol state
 | 
				
			||||||
 | 
							TCH->send(L3ChannelRelease(0x62));
 | 
				
			||||||
 | 
							abortInboundHandover(transaction,0x62,TCH);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						delete msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Send re-INVITE to the remote party.
 | 
				
			||||||
 | 
						unsigned RTPPort = allocateRTPPorts();
 | 
				
			||||||
 | 
						SIP::SIPState st = transaction->inboundHandoverSendINVITE(RTPPort);
 | 
				
			||||||
 | 
						if (st == SIP::Fail) {
 | 
				
			||||||
 | 
							abortInboundHandover(transaction,4,TCH);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						transaction->GSMState(GSM::HandoverProgress);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (1) {
 | 
				
			||||||
 | 
							// FIXME - the sip engine should be doing this
 | 
				
			||||||
 | 
							// FIXME - and checking for timeout
 | 
				
			||||||
 | 
							// FIXME - and checking for proceeding (stop sending the resends)
 | 
				
			||||||
 | 
							st = transaction->inboundHandoverCheckForOK();
 | 
				
			||||||
 | 
							if (st == SIP::Active) break;
 | 
				
			||||||
 | 
							if (st == SIP::Fail) {
 | 
				
			||||||
 | 
								LOG(NOTICE) << "received Fail while waiting for OK";
 | 
				
			||||||
 | 
								abortInboundHandover(transaction,4,TCH);
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						st = transaction->inboundHandoverSendACK();
 | 
				
			||||||
 | 
						LOG(DEBUG) << "status of inboundHandoverSendACK: " << st << " for " << *transaction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Send completion to peer BTS.
 | 
				
			||||||
 | 
						char ind[100];
 | 
				
			||||||
 | 
						sprintf(ind,"IND HANDOVER_COMPLETE %u", transaction->ID());
 | 
				
			||||||
 | 
						gPeerInterface.sendUntilAck(transaction,ind);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Update subscriber registry to reflect new registration.
 | 
				
			||||||
 | 
						if (transaction->SRIMSI().length() && transaction->SRCALLID().length()) {
 | 
				
			||||||
 | 
							gSubscriberRegistry.addUser(transaction->SRIMSI().c_str(), transaction->SRCALLID().c_str());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The call is running.
 | 
				
			||||||
 | 
						LOG(INFO) << "succesful inbound handover " << *transaction;
 | 
				
			||||||
 | 
						transaction->GSMState(GSM::Active);
 | 
				
			||||||
 | 
						callManagementLoop(transaction,TCH);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Control::HandoverDetermination(const L3MeasurementResults& measurements, SACCHLogicalChannel* SACCH)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// This is called from the SACCH service loop.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Valid measurements?
 | 
				
			||||||
 | 
						if (measurements.MEAS_VALID()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Got neighbors?
 | 
				
			||||||
 | 
						unsigned N = measurements.NO_NCELL();
 | 
				
			||||||
 | 
						if (N==0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (N == 7) {
 | 
				
			||||||
 | 
							LOG(DEBUG) << "neighbor cell information not available";
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Is our current signal OK?
 | 
				
			||||||
 | 
						int myRxLevel = measurements.RXLEV_SUB_SERVING_CELL_dBm();
 | 
				
			||||||
 | 
						int localRSSIMin = gConfig.getNum("GSM.Handover.LocalRSSIMin");
 | 
				
			||||||
 | 
						LOG(DEBUG) << "myRxLevel=" << myRxLevel << " dBm localRSSIMin=" << localRSSIMin << " dBm";
 | 
				
			||||||
 | 
						if (myRxLevel > localRSSIMin) return;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Look at neighbor cell rx levels
 | 
				
			||||||
 | 
						int best = 0;
 | 
				
			||||||
 | 
						int bestRxLevel = measurements.RXLEV_NCELL_dBm(best);
 | 
				
			||||||
 | 
						for (unsigned int i=1; i<N; i++) {
 | 
				
			||||||
 | 
							int thisRxLevel = measurements.RXLEV_NCELL_dBm(i);
 | 
				
			||||||
 | 
							if (thisRxLevel>bestRxLevel) {
 | 
				
			||||||
 | 
								bestRxLevel = thisRxLevel;
 | 
				
			||||||
 | 
								best = i;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Does the best exceed the current by more than the threshold?
 | 
				
			||||||
 | 
						int threshold = gConfig.getNum("GSM.Handover.ThresholdDelta");
 | 
				
			||||||
 | 
						LOG(DEBUG) << "myRxLevel=" << myRxLevel << " dBm, best neighbor=" <<
 | 
				
			||||||
 | 
							bestRxLevel << " dBm, threshold=" << threshold << " dB";
 | 
				
			||||||
 | 
						if (bestRxLevel < (myRxLevel + threshold)) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// OK.  So we will initiate a handover.
 | 
				
			||||||
 | 
						LOG(DEBUG) << measurements;
 | 
				
			||||||
 | 
						int BCCH_FREQ_NCELL = measurements.BCCH_FREQ_NCELL(best);
 | 
				
			||||||
 | 
						int BSIC = measurements.BSIC_NCELL(best);
 | 
				
			||||||
 | 
						char* peer = gNeighborTable.getAddress(BCCH_FREQ_NCELL,BSIC);
 | 
				
			||||||
 | 
						if (!peer) {
 | 
				
			||||||
 | 
							LOG(CRIT) << "measurement for unknown neighbor BCCH_FREQ_NCELL " << BCCH_FREQ_NCELL << " BSIC " << BSIC;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (gNeighborTable.holdingOff(peer)) {
 | 
				
			||||||
 | 
							LOG(NOTICE) << "skipping handover to " << peer << " due to holdoff";
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Find the transaction record.
 | 
				
			||||||
 | 
						TransactionEntry* transaction = gTransactionTable.findBySACCH(SACCH);
 | 
				
			||||||
 | 
						if (!transaction) {
 | 
				
			||||||
 | 
							LOG(ERR) << "active SACCH with no transaction record: " << *SACCH;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (transaction->GSMState() != GSM::Active) {
 | 
				
			||||||
 | 
							LOG(DEBUG) << "skipping handover for transaction " << transaction->ID()
 | 
				
			||||||
 | 
								<< " due to state " << transaction->GSMState();
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						LOG(INFO) << "preparing handover of " << transaction->ID()
 | 
				
			||||||
 | 
							<< " to " << peer << " with downlink RSSI " << bestRxLevel << " dbm";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The handover reference will be generated by the other BTS.
 | 
				
			||||||
 | 
						// We don't set the handover reference or state until we get RSP HANDOVER.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Form and send the message.
 | 
				
			||||||
 | 
						string msg = string("REQ HANDOVER ") + transaction->handoverString();
 | 
				
			||||||
 | 
						struct sockaddr_in peerAddr;
 | 
				
			||||||
 | 
						if (!resolveAddress(&peerAddr,peer)) {
 | 
				
			||||||
 | 
							LOG(ALERT) << "cannot resolve peer address " << peer;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						gPeerInterface.sendMessage(&peerAddr,msg.c_str());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -270,6 +568,9 @@ void Control::PagingResponseHandler(const L3PagingResponse* resp, LogicalChannel
 | 
				
			|||||||
		char *IMSI = gTMSITable.IMSI(mobileID.TMSI());
 | 
							char *IMSI = gTMSITable.IMSI(mobileID.TMSI());
 | 
				
			||||||
		if (IMSI) {
 | 
							if (IMSI) {
 | 
				
			||||||
			mobileID = L3MobileIdentity(IMSI);
 | 
								mobileID = L3MobileIdentity(IMSI);
 | 
				
			||||||
 | 
								// (pat) Whenever the MS RACHes, we need to alert the SGSN.
 | 
				
			||||||
 | 
								// Not sure this is necessary in this particular case, but be safe.
 | 
				
			||||||
 | 
								GPRS::GPRSNotifyGsmActivity(IMSI);
 | 
				
			||||||
			free(IMSI);
 | 
								free(IMSI);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			// Don't try too hard to resolve.
 | 
								// Don't try too hard to resolve.
 | 
				
			||||||
@@ -314,6 +615,9 @@ void Control::PagingResponseHandler(const L3PagingResponse* resp, LogicalChannel
 | 
				
			|||||||
		case L3CMServiceType::MobileTerminatedShortMessage:
 | 
							case L3CMServiceType::MobileTerminatedShortMessage:
 | 
				
			||||||
			MTSMSController(transaction, DCCH);
 | 
								MTSMSController(transaction, DCCH);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
 | 
							case L3CMServiceType::TestCall:
 | 
				
			||||||
 | 
								TestCall(transaction, DCCH);
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			// Flush stray MOC entries.
 | 
								// Flush stray MOC entries.
 | 
				
			||||||
			// There should not be any, but...
 | 
								// There should not be any, but...
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,27 +1,19 @@
 | 
				
			|||||||
/**@file GSM Radio Resource procedures, GSM 04.18 and GSM 04.08. */
 | 
					/**@file GSM Radio Resource procedures, GSM 04.18 and GSM 04.08. */
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008-2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
* Copyright 2011 Range Networks, Inc.
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,13 +22,17 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <list>
 | 
					#include <list>
 | 
				
			||||||
#include <GSML3CommonElements.h>
 | 
					#include <GSML3CommonElements.h>
 | 
				
			||||||
 | 
					#include <Interthread.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace GSM {
 | 
					namespace GSM {
 | 
				
			||||||
class Time;
 | 
					class Time;
 | 
				
			||||||
class TCHFACCHLogicalChannel;
 | 
					class TCHFACCHLogicalChannel;
 | 
				
			||||||
 | 
					class SACCHLogicalChannel;
 | 
				
			||||||
class L3PagingResponse;
 | 
					class L3PagingResponse;
 | 
				
			||||||
class L3AssignmentComplete;
 | 
					class L3AssignmentComplete;
 | 
				
			||||||
 | 
					class L3HandoverComplete;
 | 
				
			||||||
 | 
					class L3HandoverAccess;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Control {
 | 
					namespace Control {
 | 
				
			||||||
@@ -46,12 +42,25 @@ class TransactionEntry;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Find and compelte the in-process transaction associated with a paging repsonse. */
 | 
					/**
 | 
				
			||||||
 | 
						Determine whether or not to initiate a handover.
 | 
				
			||||||
 | 
						@param measurements The measurement results from the SACCH.
 | 
				
			||||||
 | 
						@param SACCH The SACCH in question.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					void HandoverDetermination(const GSM::L3MeasurementResults &measurements, GSM::SACCHLogicalChannel* SACCH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Find and complete the in-process transaction associated with a paging repsonse. */
 | 
				
			||||||
void PagingResponseHandler(const GSM::L3PagingResponse*, GSM::LogicalChannel*);
 | 
					void PagingResponseHandler(const GSM::L3PagingResponse*, GSM::LogicalChannel*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Find and compelte the in-process transaction associated with a completed assignment. */
 | 
					/** Find and compelte the in-process transaction associated with a completed assignment. */
 | 
				
			||||||
void AssignmentCompleteHandler(const GSM::L3AssignmentComplete*, GSM::TCHFACCHLogicalChannel*);
 | 
					void AssignmentCompleteHandler(const GSM::L3AssignmentComplete*, GSM::TCHFACCHLogicalChannel*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Save handover parameters from L1 in the proper transaction record. */
 | 
				
			||||||
 | 
					bool SaveHandoverAccess(unsigned handoverReference, float RSSI, float timingError, const GSM::Time& timestamp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Process the handover access; returns when the transaction is cleared. */
 | 
				
			||||||
 | 
					void ProcessHandoverAccess(GSM::TCHFACCHLogicalChannel *TCH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**@ Access Grant mechanisms */
 | 
					/**@ Access Grant mechanisms */
 | 
				
			||||||
//@{
 | 
					//@{
 | 
				
			||||||
@@ -177,7 +186,7 @@ class Pager {
 | 
				
			|||||||
		const GSM::L3MobileIdentity& addID,
 | 
							const GSM::L3MobileIdentity& addID,
 | 
				
			||||||
		GSM::ChannelType chanType,
 | 
							GSM::ChannelType chanType,
 | 
				
			||||||
		TransactionEntry& transaction,
 | 
							TransactionEntry& transaction,
 | 
				
			||||||
		unsigned wLife=gConfig.getNum("SIP.Timer.B")
 | 
							unsigned wLife=gConfig.getNum("GSM.Timer.T3113")
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
@@ -214,6 +223,7 @@ public:
 | 
				
			|||||||
void *PagerServiceLoopAdapter(Pager*);
 | 
					void *PagerServiceLoopAdapter(Pager*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//@}	// paging mech
 | 
					//@}	// paging mech
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										199
									
								
								Control/SMSCB.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								Control/SMSCB.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,199 @@
 | 
				
			|||||||
 | 
					/**@file SMSCB Control (L3), GSM 03.41. */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ControlCommon.h"
 | 
				
			||||||
 | 
					#include <GSMLogicalChannel.h>
 | 
				
			||||||
 | 
					#include <GSMConfig.h>
 | 
				
			||||||
 | 
					#include <GSMSMSCBL3Messages.h>
 | 
				
			||||||
 | 
					#include <Reporting.h>
 | 
				
			||||||
 | 
					#include <sqlite3.h>
 | 
				
			||||||
 | 
					#include <sqlite3util.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char* createSMSCBTable = {
 | 
				
			||||||
 | 
						"CREATE TABLE IF NOT EXISTS SMSCB ("
 | 
				
			||||||
 | 
							"GS INTEGER NOT NULL, "
 | 
				
			||||||
 | 
							"MESSAGE_CODE INTEGER NOT NULL, "
 | 
				
			||||||
 | 
							"UPDATE_NUMBER INTEGER NOT NULL, "
 | 
				
			||||||
 | 
							"MSGID INTEGER NOT NULL, "
 | 
				
			||||||
 | 
							"LANGUAGE_CODE INTEGER NOT NULL, "
 | 
				
			||||||
 | 
							"MESSAGE TEXT NOT NULL, "
 | 
				
			||||||
 | 
							"SEND_TIME INTEGER DEFAULT 0, "
 | 
				
			||||||
 | 
							"SEND_COUNT INTEGER DEFAULT 0"
 | 
				
			||||||
 | 
						")"
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sqlite3* SMSCBConnectDatabase(const char* path, sqlite3 **DB)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc = sqlite3_open(path,DB);
 | 
				
			||||||
 | 
						if (rc) {
 | 
				
			||||||
 | 
							LOG(EMERG) << "Cannot open SMSCB database on path " << path << ": " << sqlite3_errmsg(*DB);
 | 
				
			||||||
 | 
							sqlite3_close(*DB);
 | 
				
			||||||
 | 
							*DB = NULL;
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!sqlite3_command(*DB,createSMSCBTable)) {
 | 
				
			||||||
 | 
							LOG(EMERG) << "Cannot create SMSCB table";
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Set high-concurrency WAL mode.
 | 
				
			||||||
 | 
						if (!sqlite3_command(*DB,enableWAL)) {
 | 
				
			||||||
 | 
							LOG(EMERG) << "Cannot enable WAL mode on database at " << path << ", error message: " << sqlite3_errmsg(*DB);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return *DB;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void encode7(char mc, int &shift, unsigned int &dp, int &buf, char *thisPage)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						buf |= (mc & 0x7F) << shift--;
 | 
				
			||||||
 | 
						if (shift < 0) {
 | 
				
			||||||
 | 
							shift = 7;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							thisPage[dp++] = buf & 0xFF;
 | 
				
			||||||
 | 
							buf = buf >> 8;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SMSCBSendMessage(sqlite3* DB, sqlite3_stmt* stmt, GSM::CBCHLogicalChannel* CBCH)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Get the message parameters.
 | 
				
			||||||
 | 
						// These column numbers need to line up with the argeuments to the SELEECT.
 | 
				
			||||||
 | 
						unsigned GS = (unsigned)sqlite3_column_int(stmt,0);
 | 
				
			||||||
 | 
						unsigned messageCode = (unsigned)sqlite3_column_int(stmt,1);
 | 
				
			||||||
 | 
						unsigned updateNumber = (unsigned)sqlite3_column_int(stmt,2);
 | 
				
			||||||
 | 
						unsigned messageID = (unsigned)sqlite3_column_int(stmt,3);
 | 
				
			||||||
 | 
						char* messageText = strdup((const char*)sqlite3_column_text(stmt,4));
 | 
				
			||||||
 | 
						unsigned languageCode = (unsigned)sqlite3_column_int(stmt,5);
 | 
				
			||||||
 | 
						unsigned sendCount = (unsigned)sqlite3_column_int(stmt,6);
 | 
				
			||||||
 | 
						unsigned rowid = (unsigned)sqlite3_column_int(stmt,7);
 | 
				
			||||||
 | 
						// Done with the database entry.
 | 
				
			||||||
 | 
						// Finalize ASAP to unlock the database.
 | 
				
			||||||
 | 
						sqlite3_finalize(stmt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Figure out how many pages to send.
 | 
				
			||||||
 | 
						const unsigned maxLen = 40*15;
 | 
				
			||||||
 | 
						unsigned messageLen = strlen((const char*)messageText);
 | 
				
			||||||
 | 
						if (messageLen>maxLen) {
 | 
				
			||||||
 | 
							LOG(ALERT) << "SMSCB message ID " << messageID << " to long; truncating to " << maxLen << " char.";
 | 
				
			||||||
 | 
							messageLen = maxLen;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						unsigned numPages = messageLen / 40;
 | 
				
			||||||
 | 
						if (messageLen % 40) numPages++;
 | 
				
			||||||
 | 
						unsigned mp = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOG(INFO) << "sending message ID=" << messageID << " code=" << messageCode << " in " << numPages << " pages: " << messageText;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Break into pages and send each page.
 | 
				
			||||||
 | 
						for (unsigned page=0; page<numPages; page++) {
 | 
				
			||||||
 | 
							// Encode the mesage into pages.
 | 
				
			||||||
 | 
							// We use UCS2 encoding for the message,
 | 
				
			||||||
 | 
							// even though the input text is ASCII for now.
 | 
				
			||||||
 | 
							char thisPage[82];
 | 
				
			||||||
 | 
							unsigned dp = 0;
 | 
				
			||||||
 | 
							int codingScheme;
 | 
				
			||||||
 | 
							if (false) { // in case anybody wants to make the encoding selectable
 | 
				
			||||||
 | 
								codingScheme = 0x11; // UCS2
 | 
				
			||||||
 | 
								thisPage[dp++] = languageCode >> 8;
 | 
				
			||||||
 | 
								thisPage[dp++] = languageCode & 0x0ff;
 | 
				
			||||||
 | 
								while (dp<82 && mp<messageLen) {
 | 
				
			||||||
 | 
									thisPage[dp++] = 0;
 | 
				
			||||||
 | 
									thisPage[dp++] = messageText[mp++];
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								while (dp<82)  { thisPage[dp++] = 0; thisPage[dp++]='\r'; }
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// 03.38 section 5
 | 
				
			||||||
 | 
								codingScheme = 0x10; // 7'
 | 
				
			||||||
 | 
								int buf = 0;
 | 
				
			||||||
 | 
								int shift = 0;
 | 
				
			||||||
 | 
								// The spec (above) says to put this language stuff in, but it doesn't work on my samsung galaxy y.
 | 
				
			||||||
 | 
								// encode7(languageCode >> 8, shift, dp, buf, thisPage);
 | 
				
			||||||
 | 
								// encode7(languageCode & 0xFF, shift, dp, buf, thisPage);
 | 
				
			||||||
 | 
								// encode7('\r', shift, dp, buf, thisPage);
 | 
				
			||||||
 | 
								while (dp<81 && mp<messageLen) {
 | 
				
			||||||
 | 
									encode7(messageText[mp++], shift, dp, buf, thisPage);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								while (dp<81)  { encode7('\r', shift, dp, buf, thisPage); }
 | 
				
			||||||
 | 
								thisPage[dp++] = buf;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Format the page into an L3 message.
 | 
				
			||||||
 | 
							GSM::L3SMSCBMessage message(
 | 
				
			||||||
 | 
								GSM::L3SMSCBSerialNumber(GS,messageCode,updateNumber),
 | 
				
			||||||
 | 
								GSM::L3SMSCBMessageIdentifier(messageID),
 | 
				
			||||||
 | 
								GSM::L3SMSCBDataCodingScheme(codingScheme),
 | 
				
			||||||
 | 
								GSM::L3SMSCBPageParameter(page+1,numPages),
 | 
				
			||||||
 | 
								GSM::L3SMSCBContent(thisPage)
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
							// Send it.
 | 
				
			||||||
 | 
							LOG(DEBUG) << "sending L3 message page " << page+1 << ": " << message;
 | 
				
			||||||
 | 
							CBCH->send(message);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						free(messageText);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Update send count and send time in the database.
 | 
				
			||||||
 | 
						char query[100];
 | 
				
			||||||
 | 
						sprintf(query,"UPDATE SMSCB SET SEND_TIME = %u, SEND_COUNT = %u WHERE ROWID == %u",
 | 
				
			||||||
 | 
							(unsigned)time(NULL), sendCount+1, rowid);
 | 
				
			||||||
 | 
						if (!sqlite3_command(DB,query)) LOG(ALERT) << "timestamp update failed: " << sqlite3_errmsg(DB);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void* Control::SMSCBSender(void*)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Connect to the database.
 | 
				
			||||||
 | 
						// Just keep trying until it connects.
 | 
				
			||||||
 | 
						sqlite3 *DB;
 | 
				
			||||||
 | 
						while (!SMSCBConnectDatabase(gConfig.getStr("Control.SMSCB.Table").c_str(),&DB)) { sleep(1); }
 | 
				
			||||||
 | 
						LOG(NOTICE) << "SMSCB service starting";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get a channel.
 | 
				
			||||||
 | 
						GSM::CBCHLogicalChannel* CBCH = gBTS.getCBCH();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (1) {
 | 
				
			||||||
 | 
							// Get the next message ready to send.
 | 
				
			||||||
 | 
							const char* query =
 | 
				
			||||||
 | 
								"SELECT"
 | 
				
			||||||
 | 
								" GS,MESSAGE_CODE,UPDATE_NUMBER,MSGID,MESSAGE,LANGUAGE_CODE,SEND_COUNT,ROWID"
 | 
				
			||||||
 | 
								" FROM SMSCB"
 | 
				
			||||||
 | 
								" WHERE SEND_TIME==(SELECT min(SEND_TIME) FROM SMSCB)";
 | 
				
			||||||
 | 
							sqlite3_stmt *stmt;
 | 
				
			||||||
 | 
							if (sqlite3_prepare_statement(DB,&stmt,query)) {
 | 
				
			||||||
 | 
								LOG(ALERT) << "Cannot access SMSCB database: " << sqlite3_errmsg(DB);
 | 
				
			||||||
 | 
								sleep(1);
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Send the message or sleep briefly.
 | 
				
			||||||
 | 
							int result = sqlite3_run_query(DB,stmt);
 | 
				
			||||||
 | 
							if (result==SQLITE_ROW) SMSCBSendMessage(DB,stmt,CBCH);
 | 
				
			||||||
 | 
							else sleep(1);
 | 
				
			||||||
 | 
							// Log errors.
 | 
				
			||||||
 | 
							if ((result!=SQLITE_ROW) && (result!=SQLITE_DONE))
 | 
				
			||||||
 | 
								 LOG(ALERT) << "SCSCB database failure: " << sqlite3_errmsg(DB);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// keep the compiler from whining
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// vim: ts=4 sw=4
 | 
				
			||||||
@@ -1,25 +1,19 @@
 | 
				
			|||||||
/**@file SMS Control (L3), GSM 03.40, 04.11. */
 | 
					/**@file SMS Control (L3), GSM 03.40, 04.11. */
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009, 2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
* Copyright 2011 Range Networks, Inc.
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -141,12 +135,17 @@ bool handleRPDU(TransactionEntry *transaction, const RLFrame& RPDU)
 | 
				
			|||||||
				
 | 
									
 | 
				
			||||||
				body << submit.UD().decode();
 | 
									body << submit.UD().decode();
 | 
				
			||||||
			} else if (contentType == "application/vnd.3gpp.sms") {
 | 
								} else if (contentType == "application/vnd.3gpp.sms") {
 | 
				
			||||||
 | 
									LOG(DEBUG) << "RPDU: " << RPDU;
 | 
				
			||||||
				RPDU.hex(body);
 | 
									RPDU.hex(body);
 | 
				
			||||||
 | 
									LOG(DEBUG) << "RPDU result: " << body;
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				LOG(ALERT) << "\"" << contentType << "\" is not a valid SMS payload type";
 | 
									LOG(ALERT) << "\"" << contentType << "\" is not a valid SMS payload type";
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			const char* address = NULL;
 | 
								const char* address = NULL;
 | 
				
			||||||
			if (gConfig.defines("SIP.SMSC")) address = gConfig.getStr("SIP.SMSC").c_str();
 | 
								string tmpAddress = gConfig.getStr("SIP.SMSC");
 | 
				
			||||||
 | 
								if (tmpAddress.length()) {
 | 
				
			||||||
 | 
									address = tmpAddress.c_str();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/* The SMSC is not defined, we are using an older version */
 | 
								/* The SMSC is not defined, we are using an older version */
 | 
				
			||||||
			if (address == NULL) {
 | 
								if (address == NULL) {
 | 
				
			||||||
@@ -293,7 +292,7 @@ void Control::MOSMSController(const GSM::L3CMServiceRequest *req, GSM::LogicalCh
 | 
				
			|||||||
	gReports.incr("OpenBTS.GSM.SMS.MOSMS.Complete");
 | 
						gReports.incr("OpenBTS.GSM.SMS.MOSMS.Complete");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* MOSMS RLLP request */
 | 
						/* MOSMS RLLP request */
 | 
				
			||||||
	if (gConfig.defines("Control.SMS.QueryRRLP")) {
 | 
						if (gConfig.getBool("Control.SMS.QueryRRLP")) {
 | 
				
			||||||
		// Query for RRLP
 | 
							// Query for RRLP
 | 
				
			||||||
		if (!sendRRLP(mobileID, LCH)) {
 | 
							if (!sendRRLP(mobileID, LCH)) {
 | 
				
			||||||
			LOG(INFO) << "RRLP request failed";
 | 
								LOG(INFO) << "RRLP request failed";
 | 
				
			||||||
@@ -413,7 +412,6 @@ bool Control::deliverSMSToMS(const char *callingPartyDigits, const char* message
 | 
				
			|||||||
		throw UnexpectedMessage();
 | 
							throw UnexpectedMessage();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// FIXME -- Check L3 TI.
 | 
						// FIXME -- Check L3 TI.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Parse to check for RP-ACK.
 | 
						// Parse to check for RP-ACK.
 | 
				
			||||||
@@ -482,7 +480,7 @@ void Control::MTSMSController(TransactionEntry *transaction, GSM::LogicalChannel
 | 
				
			|||||||
	LOG(INFO) << "transaction: "<< *transaction;
 | 
						LOG(INFO) << "transaction: "<< *transaction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* MTSMS RLLP request */
 | 
						/* MTSMS RLLP request */
 | 
				
			||||||
	if (gConfig.defines("Control.SMS.QueryRRLP")) {
 | 
						if (gConfig.getBool("Control.SMS.QueryRRLP")) {
 | 
				
			||||||
		// Query for RRLP
 | 
							// Query for RRLP
 | 
				
			||||||
		if(!sendRRLP(transaction->subscriber(), LCH)){
 | 
							if(!sendRRLP(transaction->subscriber(), LCH)){
 | 
				
			||||||
	  		LOG(INFO) << "RRLP request failed";
 | 
						  		LOG(INFO) << "RRLP request failed";
 | 
				
			||||||
@@ -617,7 +615,7 @@ void Control::InCallMOSMSController(const CPData *cpData, TransactionEntry* tran
 | 
				
			|||||||
	   here -kurtis */
 | 
						   here -kurtis */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* MOSMS RLLP request */
 | 
						/* MOSMS RLLP request */
 | 
				
			||||||
	if (gConfig.defines("Control.SMS.QueryRRLP")) {
 | 
						if (gConfig.getBool("Control.SMS.QueryRRLP")) {
 | 
				
			||||||
		// Query for RRLP
 | 
							// Query for RRLP
 | 
				
			||||||
		if (!sendRRLP(transaction->subscriber(), LCH)) {
 | 
							if (!sendRRLP(transaction->subscriber(), LCH)) {
 | 
				
			||||||
			LOG(INFO) << "RRLP request failed";
 | 
								LOG(INFO) << "RRLP request failed";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,19 @@
 | 
				
			|||||||
/**@file Declarations for common-use control-layer functions. */
 | 
					/**@file Declarations for common-use control-layer functions. */
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008-2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,17 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -38,6 +30,8 @@
 | 
				
			|||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
#include <iomanip>
 | 
					#include <iomanip>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <sys/stat.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace std;
 | 
					using namespace std;
 | 
				
			||||||
using namespace Control;
 | 
					using namespace Control;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -57,8 +51,12 @@ static const char* createTMSITable = {
 | 
				
			|||||||
		"PREV_MCC INTEGER, "			// previous network MCC
 | 
							"PREV_MCC INTEGER, "			// previous network MCC
 | 
				
			||||||
		"PREV_MNC INTEGER, "			// previous network MNC
 | 
							"PREV_MNC INTEGER, "			// previous network MNC
 | 
				
			||||||
		"PREV_LAC INTEGER, "			// previous network LAC
 | 
							"PREV_LAC INTEGER, "			// previous network LAC
 | 
				
			||||||
 | 
							"RANDUPPER INTEGER, "			// authentication token
 | 
				
			||||||
 | 
							"RANDLOWER INTEGER, "			// authentication token
 | 
				
			||||||
 | 
							"SRES INTEGER, "				// authentication token
 | 
				
			||||||
		"DEG_LAT FLOAT, "				// RRLP result
 | 
							"DEG_LAT FLOAT, "				// RRLP result
 | 
				
			||||||
		"DEG_LONG FLOAT "				// RRLP result
 | 
							"DEG_LONG FLOAT, "				// RRLP result
 | 
				
			||||||
 | 
							"kc varchar(33) default '' "
 | 
				
			||||||
	")"
 | 
						")"
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,7 +65,22 @@ static const char* createTMSITable = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
int TMSITable::open(const char* wPath) 
 | 
					int TMSITable::open(const char* wPath) 
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						// FIXME -- We can't call the logger here because it has not been initialized yet.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int rc = sqlite3_open(wPath,&mDB);
 | 
						int rc = sqlite3_open(wPath,&mDB);
 | 
				
			||||||
 | 
						if (rc) {
 | 
				
			||||||
 | 
							// (pat) Gee, how about if we create the directory first?
 | 
				
			||||||
 | 
							// OpenBTS crashes if the directory does not exist because the LOG()
 | 
				
			||||||
 | 
							// below will crash because the Logger class has not been initialized yet.
 | 
				
			||||||
 | 
							char dirpath[strlen(wPath)+100];
 | 
				
			||||||
 | 
							strcpy(dirpath,wPath);
 | 
				
			||||||
 | 
							char *sp = strrchr(dirpath,'/');
 | 
				
			||||||
 | 
							if (sp) {
 | 
				
			||||||
 | 
								*sp = 0;
 | 
				
			||||||
 | 
								mkdir(dirpath,0777);
 | 
				
			||||||
 | 
								rc = sqlite3_open(wPath,&mDB);	// try try again.
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if (rc) {
 | 
						if (rc) {
 | 
				
			||||||
		LOG(EMERG) << "Cannot open TMSITable database at " << wPath << ": " << sqlite3_errmsg(mDB);
 | 
							LOG(EMERG) << "Cannot open TMSITable database at " << wPath << ": " << sqlite3_errmsg(mDB);
 | 
				
			||||||
		sqlite3_close(mDB);
 | 
							sqlite3_close(mDB);
 | 
				
			||||||
@@ -78,6 +91,10 @@ int TMSITable::open(const char* wPath)
 | 
				
			|||||||
		LOG(EMERG) << "Cannot create TMSI table";
 | 
							LOG(EMERG) << "Cannot create TMSI table";
 | 
				
			||||||
        return 1;
 | 
					        return 1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// Set high-concurrency WAL mode.
 | 
				
			||||||
 | 
						if (!sqlite3_command(mDB,enableWAL)) {
 | 
				
			||||||
 | 
							LOG(EMERG) << "Cannot enable WAL mode on database at " << wPath << ", error message: " << sqlite3_errmsg(mDB);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -198,7 +215,7 @@ void printAge(unsigned seconds, ostream& os)
 | 
				
			|||||||
void TMSITable::dump(ostream& os) const
 | 
					void TMSITable::dump(ostream& os) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sqlite3_stmt *stmt;
 | 
						sqlite3_stmt *stmt;
 | 
				
			||||||
	if (sqlite3_prepare_statement(mDB,&stmt,"SELECT TMSI,IMSI,CREATED,ACCESSED FROM TMSI_TABLE")) {
 | 
						if (sqlite3_prepare_statement(mDB,&stmt,"SELECT TMSI,IMSI,CREATED,ACCESSED FROM TMSI_TABLE ORDER BY ACCESSED DESC")) {
 | 
				
			||||||
		LOG(ERR) << "sqlite3_prepare_statement failed";
 | 
							LOG(ERR) << "sqlite3_prepare_statement failed";
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -245,13 +262,97 @@ bool TMSITable::classmark(const char* IMSI, const GSM::L3MobileStationClassmark2
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int TMSITable::getPreferredA5Algorithm(const char* IMSI)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char query[200];
 | 
				
			||||||
 | 
						sprintf(query, "SELECT A5_SUPPORT from TMSI_TABLE WHERE IMSI=\"%s\"", IMSI);
 | 
				
			||||||
 | 
						sqlite3_stmt *stmt;
 | 
				
			||||||
 | 
						if (sqlite3_prepare_statement(mDB,&stmt,query)) {
 | 
				
			||||||
 | 
							LOG(ERR) << "sqlite3_prepare_statement failed for " << query;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (sqlite3_run_query(mDB,stmt)!=SQLITE_ROW) {
 | 
				
			||||||
 | 
							// Returning false here just means the IMSI is not there yet.
 | 
				
			||||||
 | 
							sqlite3_finalize(stmt);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						int cm = sqlite3_column_int(stmt,0);
 | 
				
			||||||
 | 
						sqlite3_finalize(stmt);
 | 
				
			||||||
 | 
						if (cm&1) return 3;
 | 
				
			||||||
 | 
						// if (cm&2) return 2; not supported
 | 
				
			||||||
 | 
						if (cm&4) return 1;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TMSITable::putAuthTokens(const char* IMSI, uint64_t upperRAND, uint64_t lowerRAND, uint32_t SRES)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char query[300];
 | 
				
			||||||
 | 
						sprintf(query,"UPDATE TMSI_TABLE SET RANDUPPER=%llu,RANDLOWER=%llu,SRES=%u,ACCESSED=%u WHERE IMSI=\"%s\"",
 | 
				
			||||||
 | 
							upperRAND,lowerRAND,SRES,(unsigned)time(NULL),IMSI);
 | 
				
			||||||
 | 
						if (!sqlite3_command(mDB,query)) {
 | 
				
			||||||
 | 
							LOG(ALERT) << "cannot write to TMSI table";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool TMSITable::getAuthTokens(const char* IMSI, uint64_t& upperRAND, uint64_t& lowerRAND, uint32_t& SRES)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char query[200];
 | 
				
			||||||
 | 
						sprintf(query,"SELECT RANDUPPER,RANDLOWER,SRES FROM TMSI_TABLE WHERE IMSI=\"%s\"",IMSI);
 | 
				
			||||||
 | 
						sqlite3_stmt *stmt;
 | 
				
			||||||
 | 
						if (sqlite3_prepare_statement(mDB,&stmt,query)) {
 | 
				
			||||||
 | 
							LOG(ERR) << "sqlite3_prepare_statement failed for " << query;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (sqlite3_run_query(mDB,stmt)!=SQLITE_ROW) {
 | 
				
			||||||
 | 
							// Returning false here just means the IMSI is not there yet.
 | 
				
			||||||
 | 
							sqlite3_finalize(stmt);
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						upperRAND = sqlite3_column_int64(stmt,0);
 | 
				
			||||||
 | 
						lowerRAND = sqlite3_column_int64(stmt,1);
 | 
				
			||||||
 | 
						SRES = sqlite3_column_int(stmt,2);
 | 
				
			||||||
 | 
						sqlite3_finalize(stmt);
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TMSITable::putKc(const char* IMSI, string Kc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char query[300];
 | 
				
			||||||
 | 
						sprintf(query,"UPDATE TMSI_TABLE SET kc=\"%s\" WHERE IMSI=\"%s\"", Kc.c_str(), IMSI);
 | 
				
			||||||
 | 
						if (!sqlite3_command(mDB,query)) {
 | 
				
			||||||
 | 
							LOG(ALERT) << "cannot write Kc to TMSI table";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					string TMSITable::getKc(const char* IMSI)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char *Kc;
 | 
				
			||||||
 | 
						if (!sqlite3_single_lookup(mDB, "TMSI_TABLE", "IMSI", IMSI, "kc", Kc)) {
 | 
				
			||||||
 | 
							LOG(ERR) << "sqlite3_single_lookup failed to find kc for " << IMSI;
 | 
				
			||||||
 | 
							return "";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						string Kcs = string(Kc);
 | 
				
			||||||
 | 
						free(Kc);
 | 
				
			||||||
 | 
						return Kcs;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
unsigned TMSITable::nextL3TI(const char* IMSI)
 | 
					unsigned TMSITable::nextL3TI(const char* IMSI)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// FIXME -- This should be a single atomic operation.
 | 
						// FIXME -- This should be a single atomic operation.
 | 
				
			||||||
	unsigned l3ti;
 | 
						unsigned l3ti;
 | 
				
			||||||
	if (!sqlite3_single_lookup(mDB,"TMSI_TABLE","IMSI",IMSI,"L3TI",l3ti)) {
 | 
						if (!sqlite3_single_lookup(mDB,"TMSI_TABLE","IMSI",IMSI,"L3TI",l3ti)) {
 | 
				
			||||||
		LOG(ERR) << "cannot read L3TI from TMSI_TABLE, using random L3TI";
 | 
							LOG(ERR) << "cannot read L3TI from TMSI_TABLE, using random L3TI";
 | 
				
			||||||
		return random() % 8;
 | 
							return random() % 7;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Note that TI=7 is a reserved value, so value values are 0-6.  See GSM 04.07 11.2.3.1.3.
 | 
						// Note that TI=7 is a reserved value, so value values are 0-6.  See GSM 04.07 11.2.3.1.3.
 | 
				
			||||||
	unsigned next = (l3ti+1) % 7;
 | 
						unsigned next = (l3ti+1) % 7;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,17 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008-2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -98,6 +91,21 @@ class TMSITable {
 | 
				
			|||||||
	/** Set the classmark. */
 | 
						/** Set the classmark. */
 | 
				
			||||||
	bool classmark(const char* IMSI, const GSM::L3MobileStationClassmark2& classmark);
 | 
						bool classmark(const char* IMSI, const GSM::L3MobileStationClassmark2& classmark);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Get the preferred A5 algorithm (3, 1, or 0). */
 | 
				
			||||||
 | 
						int getPreferredA5Algorithm(const char* IMSI);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Save a RAND/SRES pair. */
 | 
				
			||||||
 | 
						void putAuthTokens(const char* IMSI, uint64_t upperRAND, uint64_t lowerRAND, uint32_t SRES);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Get a RAND/SRES pair. */
 | 
				
			||||||
 | 
						bool getAuthTokens(const char* IMSI, uint64_t &upperRAND, uint64_t &lowerRAND, uint32_t &SRES);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Save Kc. */
 | 
				
			||||||
 | 
						void putKc(const char* IMSI, std::string Kc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Get Kc. */
 | 
				
			||||||
 | 
						std::string getKc(const char* IMSI);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Get the next TI value to use for this IMSI or TMSI. */
 | 
						/** Get the next TI value to use for this IMSI or TMSI. */
 | 
				
			||||||
	unsigned nextL3TI(const char* IMSI);
 | 
						unsigned nextL3TI(const char* IMSI);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,6 +29,8 @@
 | 
				
			|||||||
#include <GSML3MMMessages.h>
 | 
					#include <GSML3MMMessages.h>
 | 
				
			||||||
#include <GSMConfig.h>
 | 
					#include <GSMConfig.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <Peering.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <sqlite3.h>
 | 
					#include <sqlite3.h>
 | 
				
			||||||
#include <sqlite3util.h>
 | 
					#include <sqlite3util.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -100,8 +102,7 @@ TransactionEntry::TransactionEntry(
 | 
				
			|||||||
	const L3CMServiceType& wService,
 | 
						const L3CMServiceType& wService,
 | 
				
			||||||
	const L3CallingPartyBCDNumber& wCalling,
 | 
						const L3CallingPartyBCDNumber& wCalling,
 | 
				
			||||||
	GSM::CallState wState,
 | 
						GSM::CallState wState,
 | 
				
			||||||
	const char *wMessage,
 | 
						const char *wMessage)
 | 
				
			||||||
	bool wFake)
 | 
					 | 
				
			||||||
	:mID(gTransactionTable.newID()),
 | 
						:mID(gTransactionTable.newID()),
 | 
				
			||||||
	mSubscriber(wSubscriber),mService(wService),
 | 
						mSubscriber(wSubscriber),mService(wService),
 | 
				
			||||||
	mL3TI(gTMSITable.nextL3TI(wSubscriber.digits())),
 | 
						mL3TI(gTMSITable.nextL3TI(wSubscriber.digits())),
 | 
				
			||||||
@@ -111,9 +112,8 @@ TransactionEntry::TransactionEntry(
 | 
				
			|||||||
	mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
 | 
						mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
 | 
				
			||||||
	mChannel(wChannel),
 | 
						mChannel(wChannel),
 | 
				
			||||||
	mTerminationRequested(false),
 | 
						mTerminationRequested(false),
 | 
				
			||||||
	mRemoved(false),
 | 
						mHandoverOtherBSTransactionID(0),
 | 
				
			||||||
	mFake(wFake)
 | 
						mRemoved(false)
 | 
				
			||||||
	 
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (wMessage) mMessage.assign(wMessage); //strncpy(mMessage,wMessage,160);
 | 
						if (wMessage) mMessage.assign(wMessage); //strncpy(mMessage,wMessage,160);
 | 
				
			||||||
	else mMessage.assign(""); //mMessage[0]='\0';
 | 
						else mMessage.assign(""); //mMessage[0]='\0';
 | 
				
			||||||
@@ -137,8 +137,8 @@ TransactionEntry::TransactionEntry(
 | 
				
			|||||||
	mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
 | 
						mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
 | 
				
			||||||
	mChannel(wChannel),
 | 
						mChannel(wChannel),
 | 
				
			||||||
	mTerminationRequested(false),
 | 
						mTerminationRequested(false),
 | 
				
			||||||
	mRemoved(false),
 | 
						mHandoverOtherBSTransactionID(0),
 | 
				
			||||||
	mFake(false)
 | 
						mRemoved(false)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	assert(mSubscriber.type()==GSM::IMSIType);
 | 
						assert(mSubscriber.type()==GSM::IMSIType);
 | 
				
			||||||
	mMessage.assign(""); //mMessage[0]='\0';
 | 
						mMessage.assign(""); //mMessage[0]='\0';
 | 
				
			||||||
@@ -146,29 +146,6 @@ TransactionEntry::TransactionEntry(
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Form for SOS transactions.
 | 
					 | 
				
			||||||
TransactionEntry::TransactionEntry(
 | 
					 | 
				
			||||||
	const char* proxy,
 | 
					 | 
				
			||||||
	const L3MobileIdentity& wSubscriber,
 | 
					 | 
				
			||||||
	GSM::LogicalChannel* wChannel,
 | 
					 | 
				
			||||||
	const L3CMServiceType& wService,
 | 
					 | 
				
			||||||
	unsigned wL3TI)
 | 
					 | 
				
			||||||
	:mID(gTransactionTable.newID()),
 | 
					 | 
				
			||||||
	mSubscriber(wSubscriber),mService(wService),
 | 
					 | 
				
			||||||
	mL3TI(wL3TI),
 | 
					 | 
				
			||||||
	mSIP(proxy,mSubscriber.digits()),
 | 
					 | 
				
			||||||
	mGSMState(GSM::MOCInitiated),
 | 
					 | 
				
			||||||
	mNumSQLTries(2*gConfig.getNum("Control.NumSQLTries")),
 | 
					 | 
				
			||||||
	mChannel(wChannel),
 | 
					 | 
				
			||||||
	mTerminationRequested(false),
 | 
					 | 
				
			||||||
	mRemoved(false),
 | 
					 | 
				
			||||||
	mFake(false)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	mMessage.assign(""); //mMessage[0]='\0';
 | 
					 | 
				
			||||||
	initTimers();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Form for MO-SMS transactions.
 | 
					// Form for MO-SMS transactions.
 | 
				
			||||||
TransactionEntry::TransactionEntry(
 | 
					TransactionEntry::TransactionEntry(
 | 
				
			||||||
	const char* proxy,
 | 
						const char* proxy,
 | 
				
			||||||
@@ -185,8 +162,8 @@ TransactionEntry::TransactionEntry(
 | 
				
			|||||||
	mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
 | 
						mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
 | 
				
			||||||
	mChannel(wChannel),
 | 
						mChannel(wChannel),
 | 
				
			||||||
	mTerminationRequested(false),
 | 
						mTerminationRequested(false),
 | 
				
			||||||
	mRemoved(false),
 | 
						mHandoverOtherBSTransactionID(0),
 | 
				
			||||||
	mFake(false)
 | 
						mRemoved(false)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	assert(mSubscriber.type()==GSM::IMSIType);
 | 
						assert(mSubscriber.type()==GSM::IMSIType);
 | 
				
			||||||
	if (wMessage!=NULL) mMessage.assign(wMessage); //strncpy(mMessage,wMessage,160);
 | 
						if (wMessage!=NULL) mMessage.assign(wMessage); //strncpy(mMessage,wMessage,160);
 | 
				
			||||||
@@ -208,8 +185,8 @@ TransactionEntry::TransactionEntry(
 | 
				
			|||||||
	mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
 | 
						mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
 | 
				
			||||||
	mChannel(wChannel),
 | 
						mChannel(wChannel),
 | 
				
			||||||
	mTerminationRequested(false),
 | 
						mTerminationRequested(false),
 | 
				
			||||||
	mRemoved(false),
 | 
						mHandoverOtherBSTransactionID(0),
 | 
				
			||||||
	mFake(false)
 | 
						mRemoved(false)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	assert(mSubscriber.type()==GSM::IMSIType);
 | 
						assert(mSubscriber.type()==GSM::IMSIType);
 | 
				
			||||||
	mMessage[0]='\0';
 | 
						mMessage[0]='\0';
 | 
				
			||||||
@@ -217,11 +194,142 @@ TransactionEntry::TransactionEntry(
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Form for inbound handovers.
 | 
				
			||||||
 | 
					TransactionEntry::TransactionEntry(const struct sockaddr_in* peer,
 | 
				
			||||||
 | 
						unsigned wHandoverReference,
 | 
				
			||||||
 | 
						SimpleKeyValue ¶ms,
 | 
				
			||||||
 | 
						const char *proxy,
 | 
				
			||||||
 | 
						GSM::LogicalChannel *wChannel,
 | 
				
			||||||
 | 
						unsigned wHandoverOtherBSTransactionID)
 | 
				
			||||||
 | 
						:mID(gTransactionTable.newID()),
 | 
				
			||||||
 | 
						mService(GSM::L3CMServiceType::HandoverCall),
 | 
				
			||||||
 | 
						mSIP(proxy),
 | 
				
			||||||
 | 
						mGSMState(GSM::HandoverInbound),
 | 
				
			||||||
 | 
						mInboundReference(wHandoverReference),
 | 
				
			||||||
 | 
						mNumSQLTries(gConfig.getNum("Control.NumSQLTries")),
 | 
				
			||||||
 | 
						mChannel(wChannel),
 | 
				
			||||||
 | 
						mTerminationRequested(false),
 | 
				
			||||||
 | 
						mHandoverOtherBSTransactionID(wHandoverOtherBSTransactionID),
 | 
				
			||||||
 | 
						mRemoved(false)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// This is used for inbound handovers.
 | 
				
			||||||
 | 
						// We are "BS2" in the handover ladder diagram.
 | 
				
			||||||
 | 
						// The message string was formed by the handoverString method.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Save the peer address.
 | 
				
			||||||
 | 
						bcopy(peer,&mInboundPeer,sizeof(mInboundPeer));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Break into space-delimited tokens, stuff into a SimpleKeyValue and then unpack it.
 | 
				
			||||||
 | 
						//SimpleKeyValue params;
 | 
				
			||||||
 | 
						//params.addItems(args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* IMSI = params.get("IMSI");
 | 
				
			||||||
 | 
						if (IMSI) mSubscriber = GSM::L3MobileIdentity(IMSI);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* called = params.get("called");
 | 
				
			||||||
 | 
						if (called) {
 | 
				
			||||||
 | 
							mCalled = GSM::L3CallingPartyBCDNumber(called);
 | 
				
			||||||
 | 
							mService = GSM::L3CMServiceType::MobileOriginatedCall;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* calling = params.get("calling");
 | 
				
			||||||
 | 
						if (calling) {
 | 
				
			||||||
 | 
							mCalling = GSM::L3CallingPartyBCDNumber(calling);
 | 
				
			||||||
 | 
							mService = GSM::L3CMServiceType::MobileTerminatedCall;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* ref = params.get("ref");
 | 
				
			||||||
 | 
						if (ref) mInboundReference = strtol(ref,NULL,10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* L3TI = params.get("L3TI");
 | 
				
			||||||
 | 
						if (L3TI) mL3TI = strtol(L3TI,NULL,10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set the SIP state.
 | 
				
			||||||
 | 
						mSIP.state(SIP::HandoverInbound);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* codec = params.get("codec");
 | 
				
			||||||
 | 
						if (codec) mCodec = atoi(codec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* remoteUsername = params.get("remoteUsername");
 | 
				
			||||||
 | 
						if (remoteUsername) mRemoteUsername = strdup(remoteUsername);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* remoteDomain = params.get("remoteDomain");
 | 
				
			||||||
 | 
						if (remoteDomain) mRemoteDomain = strdup(remoteDomain);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* SIPUsername = params.get("SIPUsername");
 | 
				
			||||||
 | 
						if (SIPUsername) mSIPUsername = strdup(SIPUsername);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* SIPDisplayname = params.get("SIPDisplayname");
 | 
				
			||||||
 | 
						if (SIPDisplayname) mSIPDisplayname = strdup(SIPDisplayname);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* FromTag = params.get("FromTag");
 | 
				
			||||||
 | 
						if (FromTag) mFromTag = strdup(FromTag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* FromUsername = params.get("FromUsername");
 | 
				
			||||||
 | 
						if (FromUsername) mFromUsername = strdup(FromUsername);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* FromIP = params.get("FromIP");
 | 
				
			||||||
 | 
						if (FromIP) mFromIP = strdup(FromIP);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* ToTag = params.get("ToTag");
 | 
				
			||||||
 | 
						if (ToTag) mToTag = strdup(ToTag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* ToUsername = params.get("ToUsername");
 | 
				
			||||||
 | 
						if (ToUsername) mToUsername = strdup(ToUsername);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* ToIP = params.get("ToIP");
 | 
				
			||||||
 | 
						if (ToIP) mToIP = strdup(ToIP);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char* CSeq = params.get("CSeq");
 | 
				
			||||||
 | 
						if (CSeq) mCSeq = atoi(CSeq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char * CallID = params.get("CallID");
 | 
				
			||||||
 | 
						if (CallID) mCallID = CallID;
 | 
				
			||||||
 | 
						mSIP.callID(CallID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char * CallIP = params.get("CallIP");
 | 
				
			||||||
 | 
						if (CallIP) mCallIP = CallIP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char * RTPState = params.get("RTPState");
 | 
				
			||||||
 | 
						if (RTPState) mRTPState = RTPState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char * SessionID = params.get("SessionID");
 | 
				
			||||||
 | 
						if (SessionID) mSessionID = SessionID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char * SessionVersion = params.get("SessionVersion");
 | 
				
			||||||
 | 
						if (SessionVersion) mSessionVersion = SessionVersion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char * RTPRemPort = params.get("RTPRemPort");
 | 
				
			||||||
 | 
						if (RTPRemPort) mRTPRemPort = atoi(RTPRemPort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char * RTPRemIP = params.get("RTPRemIP");
 | 
				
			||||||
 | 
						if (RTPRemIP) mRTPRemIP = RTPRemIP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char * RmtIP = params.get("RmtIP");
 | 
				
			||||||
 | 
						if (RmtIP) mRmtIP = RmtIP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char * RmtPort = params.get("RmtPort");
 | 
				
			||||||
 | 
						if (RmtPort) mRmtPort = atoi(RmtPort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char * SRIMSI = params.get("SRIMSI");
 | 
				
			||||||
 | 
						if (SRIMSI) mSRIMSI = SRIMSI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char * SRCALLID = params.get("SRCALLID");
 | 
				
			||||||
 | 
						if (SRCALLID) mSRCALLID = SRCALLID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						initTimers();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TransactionEntry::~TransactionEntry()
 | 
					TransactionEntry::~TransactionEntry()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// This should go out of scope before the object is actually destroyed.
 | 
						// This should go out of scope before the object is actually destroyed.
 | 
				
			||||||
	ScopedLock lock(mLock);
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Remove any FIFO from the gPeerInterface.
 | 
				
			||||||
 | 
						gPeerInterface.removeFIFO(mID);
 | 
				
			||||||
	// Remove the associated SIP message FIFO.
 | 
						// Remove the associated SIP message FIFO.
 | 
				
			||||||
	gSIPInterface.removeCall(mSIP.callID());
 | 
						gSIPInterface.removeCall(mSIP.callID());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -332,6 +440,8 @@ bool TransactionEntry::dead() const
 | 
				
			|||||||
	if (age < 30*1000) return false;
 | 
						if (age < 30*1000) return false;
 | 
				
			||||||
	// Failed?
 | 
						// Failed?
 | 
				
			||||||
	if (lSIPState==SIP::Fail) return true;
 | 
						if (lSIPState==SIP::Fail) return true;
 | 
				
			||||||
 | 
						// Bad handover?
 | 
				
			||||||
 | 
						if (lSIPState==SIP::HandoverInbound) return true;
 | 
				
			||||||
	// SIP Null state?
 | 
						// SIP Null state?
 | 
				
			||||||
	if (lSIPState==SIP::NullState) return true;
 | 
						if (lSIPState==SIP::NullState) return true;
 | 
				
			||||||
	// SIP stuck in proceeding?
 | 
						// SIP stuck in proceeding?
 | 
				
			||||||
@@ -411,9 +521,7 @@ void TransactionEntry::messageType(const char *wContentType)
 | 
				
			|||||||
void TransactionEntry::runQuery(const char* query) const
 | 
					void TransactionEntry::runQuery(const char* query) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// Caller should hold mLock and should have already checked mRemoved..
 | 
						// Caller should hold mLock and should have already checked mRemoved..
 | 
				
			||||||
	for (unsigned i=0; i<mNumSQLTries; i++) {
 | 
						if (sqlite3_command(gTransactionTable.DB(),query,mNumSQLTries)) return;
 | 
				
			||||||
		if (sqlite3_command(gTransactionTable.DB(),query)) return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	LOG(ALERT) << "transaction table access failed after " << mNumSQLTries << "attempts. query:" << query << " error: " << sqlite3_errmsg(gTransactionTable.DB());
 | 
						LOG(ALERT) << "transaction table access failed after " << mNumSQLTries << "attempts. query:" << query << " error: " << sqlite3_errmsg(gTransactionTable.DB());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -598,16 +706,6 @@ SIP::SIPState TransactionEntry::MOCSendACK()
 | 
				
			|||||||
	return state;
 | 
						return state;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SIP::SIPState TransactionEntry::SOSSendINVITE(short rtpPort, unsigned codec)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (mRemoved) throw RemovedTransaction(mID);
 | 
					 | 
				
			||||||
	ScopedLock lock(mLock);
 | 
					 | 
				
			||||||
	SIP::SIPState state = mSIP.SOSSendINVITE(rtpPort,codec,channel());
 | 
					 | 
				
			||||||
	echoSIPState(state);
 | 
					 | 
				
			||||||
	return state;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SIP::SIPState TransactionEntry::MTCSendTrying()
 | 
					SIP::SIPState TransactionEntry::MTCSendTrying()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (mRemoved) throw RemovedTransaction(mID);
 | 
						if (mRemoved) throw RemovedTransaction(mID);
 | 
				
			||||||
@@ -865,6 +963,120 @@ bool TransactionEntry::terminationRequested()
 | 
				
			|||||||
	return retVal;
 | 
						return retVal;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					string TransactionEntry::handoverString() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// This string is a set of key-value pairs.
 | 
				
			||||||
 | 
						// It needs to carry all of the information of the GSM Abis Handover Request message,
 | 
				
			||||||
 | 
						// as well as all of the information of the SIP REFER message.
 | 
				
			||||||
 | 
						// We call this as "BS1" in the handover ladder diagram.
 | 
				
			||||||
 | 
						// It is decoded at the other end by a TransactionEnty constructor.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mRemoved) throw RemovedTransaction(mID);
 | 
				
			||||||
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
 | 
						ostringstream os;
 | 
				
			||||||
 | 
						os << mID;
 | 
				
			||||||
 | 
						os << " IMSI=" << mSubscriber.digits();
 | 
				
			||||||
 | 
						if (mGSMState==GSM::HandoverInbound) os << " inbound-ref=" << mInboundReference;
 | 
				
			||||||
 | 
						if (mGSMState==GSM::HandoverOutbound) os << " outbound-ref=" << mOutboundReference.value();
 | 
				
			||||||
 | 
						os << " L3TI=" << mL3TI;
 | 
				
			||||||
 | 
						if (mCalled.digits()[0]) os << " called=" << mCalled.digits();
 | 
				
			||||||
 | 
						if (mCalling.digits()[0]) os << " calling=" << mCalling.digits();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osip_message_t *ok = mSIP.LastResponse();
 | 
				
			||||||
 | 
						if (!ok) ok = mSIP.INVITE();
 | 
				
			||||||
 | 
						osip_cseq_t *cseq = osip_message_get_cseq(ok);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						char *cseqStr;
 | 
				
			||||||
 | 
						osip_cseq_to_str(cseq, &cseqStr);
 | 
				
			||||||
 | 
						os << " CSeq=" << cseqStr;
 | 
				
			||||||
 | 
						// FIXME - this should be extracted from a= attribute of sdp message
 | 
				
			||||||
 | 
						os << " codec=" << SIP::RTPGSM610;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os << " CallID=" << osip_call_id_get_number(ok->call_id);
 | 
				
			||||||
 | 
						if (osip_call_id_get_host(ok->call_id)) {
 | 
				
			||||||
 | 
							os << " CallIP=" << osip_call_id_get_host(ok->call_id);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							os << " CallIP=";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char *fromLabel = " From";
 | 
				
			||||||
 | 
						const char *toLabel = " To";
 | 
				
			||||||
 | 
						// FIXME? - is there a better way to detect moc vs mtc?
 | 
				
			||||||
 | 
						if (!mSIP.LastResponse()) {
 | 
				
			||||||
 | 
							fromLabel = " To";
 | 
				
			||||||
 | 
							toLabel = " From";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						osip_from_t *from = osip_message_get_from(ok);
 | 
				
			||||||
 | 
						char *fromStr;
 | 
				
			||||||
 | 
						osip_from_to_str(from, &fromStr);
 | 
				
			||||||
 | 
						char *fromTag = index(fromStr, ';');
 | 
				
			||||||
 | 
						// FIXME? - is there a better way to get the tag?
 | 
				
			||||||
 | 
						os << " " << fromLabel << "Tag=" << fromTag+5;
 | 
				
			||||||
 | 
						os << " " << fromLabel << "Username=" << osip_uri_get_username(ok->from->url);
 | 
				
			||||||
 | 
						os << " " << fromLabel << "IP=" << osip_uri_get_host(ok->from->url);
 | 
				
			||||||
 | 
						osip_to_t *to = osip_message_get_to(ok);
 | 
				
			||||||
 | 
						char *toStr;
 | 
				
			||||||
 | 
						osip_to_to_str(to, &toStr);
 | 
				
			||||||
 | 
						char *toTag = index(toStr, ';');
 | 
				
			||||||
 | 
						// FIXME? - is there a better way to get the tag?
 | 
				
			||||||
 | 
						os << " " << toLabel << "Tag=" << toTag+5;
 | 
				
			||||||
 | 
						os << " " << toLabel << "Username=" << osip_uri_get_username(ok->to->url);
 | 
				
			||||||
 | 
						os << " " << toLabel << "IP=" << osip_uri_get_host(ok->to->url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// FIXME? - is there a better way to extract this info?
 | 
				
			||||||
 | 
						osip_body_t * osipBodyT;
 | 
				
			||||||
 | 
						osip_message_get_body (ok, 0, &osipBodyT);
 | 
				
			||||||
 | 
						char *osipBodyTStr;
 | 
				
			||||||
 | 
						size_t osipBodyTStrLth;
 | 
				
			||||||
 | 
						osip_body_to_str (osipBodyT, &osipBodyTStr, &osipBodyTStrLth);
 | 
				
			||||||
 | 
						char *SessionIDStr = index(osipBodyTStr, ' ')+1;
 | 
				
			||||||
 | 
						char *SessionVersionStr = index(SessionIDStr, ' ')+1;
 | 
				
			||||||
 | 
						long SessionID = strtol(SessionIDStr, NULL, 10);
 | 
				
			||||||
 | 
						long SessionVersion = strtol(SessionVersionStr, NULL, 10)+1;
 | 
				
			||||||
 | 
						os << " SessionID=" << SessionID;
 | 
				
			||||||
 | 
						os << " SessionVersion=" << SessionVersion;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// getting the remote port from the m= line of the OK
 | 
				
			||||||
 | 
						char d_ip_addr[20];
 | 
				
			||||||
 | 
						char d_port[10];
 | 
				
			||||||
 | 
						SIP::get_rtp_params(ok, d_port, d_ip_addr);
 | 
				
			||||||
 | 
						os << " RTPRemIP=" << d_ip_addr;
 | 
				
			||||||
 | 
						os << " RTPRemPort=" << d_port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// proxy
 | 
				
			||||||
 | 
						os << " Proxy=" << mSIP.proxyIP() << ":" << mSIP.proxyPort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// remote ip and port
 | 
				
			||||||
 | 
						osip_contact_t * con = (osip_contact_t*)osip_list_get(&ok->contacts, 0);
 | 
				
			||||||
 | 
						os << " RmtIP=" << osip_uri_get_host(con->url);
 | 
				
			||||||
 | 
						os << " RmtPort=" << osip_uri_get_port(con->url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os << " RTPState=" <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.snd_time_offset << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.snd_ts_offset << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.snd_rand_offset << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.snd_last_ts << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.rcv_time_offset << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.rcv_ts_offset << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.rcv_query_ts_offset << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.rcv_last_ts << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.rcv_last_app_ts << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.rcv_last_ret_ts << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.hwrcv_extseq << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.hwrcv_seq_at_last_SR << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.hwrcv_since_last_SR << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.last_rcv_SR_ts << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.last_rcv_SR_time.tv_sec << "," << mSIP.RTPSession()->rtp.last_rcv_SR_time.tv_usec << "," << 
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.snd_seq << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.last_rtcp_report_snt_r << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.last_rtcp_report_snt_s << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.rtcp_report_snt_interval << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.last_rtcp_packet_count << "," <<
 | 
				
			||||||
 | 
							mSIP.RTPSession()->rtp.sent_payload_bytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return os.str();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void TransactionTable::init(const char* path)
 | 
					void TransactionTable::init(const char* path)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// This assumes the main application uses sdevrandom.
 | 
						// This assumes the main application uses sdevrandom.
 | 
				
			||||||
@@ -881,11 +1093,51 @@ void TransactionTable::init(const char* path)
 | 
				
			|||||||
	if (!sqlite3_command(mDB,createTransactionTable)) {
 | 
						if (!sqlite3_command(mDB,createTransactionTable)) {
 | 
				
			||||||
		LOG(ALERT) << "Cannot create Transaction Table";
 | 
							LOG(ALERT) << "Cannot create Transaction Table";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// Set high-concurrency WAL mode.
 | 
				
			||||||
 | 
						if (!sqlite3_command(mDB,enableWAL)) {
 | 
				
			||||||
 | 
							LOG(ALERT) << "Cannot enable WAL mode on database at " << path << ", error message: " << sqlite3_errmsg(mDB);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	// Clear any previous entires.
 | 
						// Clear any previous entires.
 | 
				
			||||||
	if (!sqlite3_command(gTransactionTable.DB(),"DELETE FROM TRANSACTION_TABLE"))
 | 
						if (!sqlite3_command(gTransactionTable.DB(),"DELETE FROM TRANSACTION_TABLE"))
 | 
				
			||||||
		LOG(WARNING) << "cannot clear previous transaction table";
 | 
							LOG(WARNING) << "cannot clear previous transaction table";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TransactionEntry::setOutboundHandover(
 | 
				
			||||||
 | 
						const GSM::L3HandoverReference& reference,
 | 
				
			||||||
 | 
						const GSM::L3CellDescription& cell,
 | 
				
			||||||
 | 
						const GSM::L3ChannelDescription2& chan,
 | 
				
			||||||
 | 
						const GSM::L3PowerCommandAndAccessType& pwrCmd,
 | 
				
			||||||
 | 
						const GSM::L3SynchronizationIndication& synch
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (mRemoved) throw RemovedTransaction(mID);
 | 
				
			||||||
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
 | 
						mOutboundReference = reference;
 | 
				
			||||||
 | 
						mOutboundCell = cell;
 | 
				
			||||||
 | 
						mOutboundChannel = chan;
 | 
				
			||||||
 | 
						mOutboundPowerCmd = pwrCmd;
 | 
				
			||||||
 | 
						mOutboundSynch = synch;
 | 
				
			||||||
 | 
						GSMState(GSM::HandoverOutbound);
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TransactionEntry::setInboundHandover(float RSSI, float timingError, double timestamp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (mRemoved) throw RemovedTransaction(mID);
 | 
				
			||||||
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
 | 
						mChannel->setPhy(RSSI,timingError,timestamp);
 | 
				
			||||||
 | 
						mInboundRSSI = RSSI;
 | 
				
			||||||
 | 
						mInboundTimingError = timingError;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TransactionTable::~TransactionTable()
 | 
					TransactionTable::~TransactionTable()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// Don't bother disposing of the memory,
 | 
						// Don't bother disposing of the memory,
 | 
				
			||||||
@@ -962,10 +1214,7 @@ bool TransactionTable::removePaging(unsigned key)
 | 
				
			|||||||
	if (itr==mTable.end()) return false;
 | 
						if (itr==mTable.end()) return false;
 | 
				
			||||||
	if (itr->second->removed()) return true;
 | 
						if (itr->second->removed()) return true;
 | 
				
			||||||
	if (itr->second->GSMState()!=GSM::Paging) return false;
 | 
						if (itr->second->GSMState()!=GSM::Paging) return false;
 | 
				
			||||||
	//no one to respond to if we're fake
 | 
					 | 
				
			||||||
	if (!itr->second->fake()){
 | 
					 | 
				
			||||||
	itr->second->MODSendERROR(NULL, 480, "Temporarily Unavailable", true);
 | 
						itr->second->MODSendERROR(NULL, 480, "Temporarily Unavailable", true);
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	itr->second->remove();
 | 
						itr->second->remove();
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1095,7 +1344,6 @@ bool TransactionTable::isBusy(const L3MobileIdentity& mobileID)
 | 
				
			|||||||
		if (itr->second->subscriber() != mobileID) continue;
 | 
							if (itr->second->subscriber() != mobileID) continue;
 | 
				
			||||||
		GSM::L3CMServiceType service = itr->second->service();
 | 
							GSM::L3CMServiceType service = itr->second->service();
 | 
				
			||||||
		bool speech =
 | 
							bool speech =
 | 
				
			||||||
			service==GSM::L3CMServiceType::EmergencyCall ||
 | 
					 | 
				
			||||||
			service==GSM::L3CMServiceType::MobileOriginatedCall ||
 | 
								service==GSM::L3CMServiceType::MobileOriginatedCall ||
 | 
				
			||||||
			service==GSM::L3CMServiceType::MobileTerminatedCall;
 | 
								service==GSM::L3CMServiceType::MobileTerminatedCall;
 | 
				
			||||||
		if (!speech) continue;
 | 
							if (!speech) continue;
 | 
				
			||||||
@@ -1155,6 +1403,7 @@ TransactionEntry* TransactionTable::find(const L3MobileIdentity& mobileID, unsig
 | 
				
			|||||||
	ScopedLock lock(mLock);
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
 | 
						for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
 | 
				
			||||||
		if (itr->second->deadOrRemoved()) continue;
 | 
							if (itr->second->deadOrRemoved()) continue;
 | 
				
			||||||
 | 
							if (itr->second->HandoverOtherBSTransactionID() != transactionID) continue;
 | 
				
			||||||
		if (itr->second->subscriber() != mobileID) continue;
 | 
							if (itr->second->subscriber() != mobileID) continue;
 | 
				
			||||||
		return itr->second;
 | 
							return itr->second;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -1246,7 +1495,6 @@ TransactionEntry* TransactionTable::findLongestCall()
 | 
				
			|||||||
	for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
 | 
						for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
 | 
				
			||||||
		if (itr->second->deadOrRemoved()) continue;
 | 
							if (itr->second->deadOrRemoved()) continue;
 | 
				
			||||||
		if (!(itr->second->channel())) continue;
 | 
							if (!(itr->second->channel())) continue;
 | 
				
			||||||
		if (itr->second->service() == GSM::L3CMServiceType::EmergencyCall) continue;
 | 
					 | 
				
			||||||
		if (itr->second->GSMState() != GSM::Active) continue;
 | 
							if (itr->second->GSMState() != GSM::Active) continue;
 | 
				
			||||||
		long runTime = itr->second->stateAge();
 | 
							long runTime = itr->second->stateAge();
 | 
				
			||||||
		if (runTime > longTime) {
 | 
							if (runTime > longTime) {
 | 
				
			||||||
@@ -1274,6 +1522,47 @@ bool TransactionTable::RTPAvailable(short rtpPort)
 | 
				
			|||||||
	return avail;
 | 
						return avail;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TransactionEntry* TransactionTable::inboundHandover(unsigned ref)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Yes, it's linear time.
 | 
				
			||||||
 | 
						// Even in a 6-ARFCN system, it should rarely be more than a dozen entries.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Since clearDeadEntries is also linear, do that here, too.
 | 
				
			||||||
 | 
						clearDeadEntries();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Brute force search.
 | 
				
			||||||
 | 
						for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
 | 
				
			||||||
 | 
							if (itr->second->deadOrRemoved()) continue;
 | 
				
			||||||
 | 
							if (itr->second->GSMState() != GSM::HandoverInbound) continue;
 | 
				
			||||||
 | 
							if (itr->second->inboundReference() == ref) {
 | 
				
			||||||
 | 
								return itr->second;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TransactionEntry* TransactionTable::inboundHandover(const GSM::LogicalChannel* chan)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Yes, it's linear time.
 | 
				
			||||||
 | 
						// Even in a 6-ARFCN system, it should rarely be more than a dozen entries.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Since clearDeadEntries is also linear, do that here, too.
 | 
				
			||||||
 | 
						clearDeadEntries();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Brute force search.
 | 
				
			||||||
 | 
						for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
 | 
				
			||||||
 | 
							if (itr->second->deadOrRemoved()) continue;
 | 
				
			||||||
 | 
							if (itr->second->GSMState() != GSM::HandoverInbound) continue;
 | 
				
			||||||
 | 
							if (itr->second->channel() == chan) return itr->second;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool TransactionTable::duplicateMessage(const GSM::L3MobileIdentity& mobileID, const std::string& wMessage)
 | 
					bool TransactionTable::duplicateMessage(const GSM::L3MobileIdentity& mobileID, const std::string& wMessage)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1292,4 +1581,31 @@ bool TransactionTable::duplicateMessage(const GSM::L3MobileIdentity& mobileID, c
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					bool TransactionTable::outboundReferenceUsed(unsigned ref)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Called is expected to hold mLock.
 | 
				
			||||||
 | 
						for (TransactionMap::iterator itr = mTable.begin(); itr!=mTable.end(); ++itr) {
 | 
				
			||||||
 | 
							if (itr->second->deadOrRemoved()) continue;
 | 
				
			||||||
 | 
							if (itr->second->GSMState() != GSM::HandoverOutbound) continue;
 | 
				
			||||||
 | 
							if (itr->second->handoverReference() == ref) return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned TransactionTable::generateHandoverReference(TransactionEntry *transaction)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
 | 
						clearDeadEntries();
 | 
				
			||||||
 | 
						unsigned ref = random() % 256;
 | 
				
			||||||
 | 
						while (outboundReferenceUsed(ref)) { ref = (ref+1) % 256; }
 | 
				
			||||||
 | 
						transaction->handoverReference(ref);
 | 
				
			||||||
 | 
						return ref;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// vim: ts=4 sw=4
 | 
					// vim: ts=4 sw=4
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,15 +90,59 @@ class TransactionEntry {
 | 
				
			|||||||
	Timeval mStateTimer;					///< timestamp of last state change.
 | 
						Timeval mStateTimer;					///< timestamp of last state change.
 | 
				
			||||||
	TimerTable mTimers;						///< table of Z100-type state timers
 | 
						TimerTable mTimers;						///< table of Z100-type state timers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**@name Handover parameters */
 | 
				
			||||||
 | 
						//@{
 | 
				
			||||||
 | 
						/**@name Inbound */
 | 
				
			||||||
 | 
						//@{
 | 
				
			||||||
 | 
						struct ::sockaddr_in mInboundPeer;		///< other BTS in inbound handover
 | 
				
			||||||
 | 
						unsigned mInboundReference;			///< handover reference
 | 
				
			||||||
 | 
						float mInboundRSSI;				///< access burst RSSI in dB wrt full scale
 | 
				
			||||||
 | 
						float mInboundTimingError;			///< access burst timing error in symbol periods
 | 
				
			||||||
 | 
						unsigned mCSeq;
 | 
				
			||||||
 | 
						string mCallID;
 | 
				
			||||||
 | 
						unsigned mCodec;
 | 
				
			||||||
 | 
						string mRTPState;
 | 
				
			||||||
 | 
						string mSessionID;
 | 
				
			||||||
 | 
						string mSessionVersion;
 | 
				
			||||||
 | 
						string mRemoteUsername;
 | 
				
			||||||
 | 
						string mRemoteDomain;
 | 
				
			||||||
 | 
						string mSIPDisplayname;
 | 
				
			||||||
 | 
						string mSIPUsername;
 | 
				
			||||||
 | 
						string mFromTag;
 | 
				
			||||||
 | 
						string mFromUsername;
 | 
				
			||||||
 | 
						string mFromIP;
 | 
				
			||||||
 | 
						string mToTag;
 | 
				
			||||||
 | 
						string mToUsername;
 | 
				
			||||||
 | 
						string mToIP;
 | 
				
			||||||
 | 
						string mCallIP;
 | 
				
			||||||
 | 
						short mRTPRemPort;
 | 
				
			||||||
 | 
						string mRTPRemIP;
 | 
				
			||||||
 | 
						short mRmtPort;
 | 
				
			||||||
 | 
						string mRmtIP;
 | 
				
			||||||
 | 
						string mSRIMSI;
 | 
				
			||||||
 | 
						string mSRCALLID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
 | 
						/**@name Outbound */
 | 
				
			||||||
 | 
						//@{
 | 
				
			||||||
 | 
						struct ::sockaddr_in mOutboundPeer;		///< other BTS in outbound handover
 | 
				
			||||||
 | 
						GSM::L3CellDescription mOutboundCell;
 | 
				
			||||||
 | 
						GSM::L3ChannelDescription2 mOutboundChannel;
 | 
				
			||||||
 | 
						GSM::L3HandoverReference mOutboundReference;
 | 
				
			||||||
 | 
						GSM::L3PowerCommandAndAccessType mOutboundPowerCmd;
 | 
				
			||||||
 | 
						GSM::L3SynchronizationIndication mOutboundSynch;
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unsigned mNumSQLTries;					///< number of SQL tries for DB operations
 | 
						unsigned mNumSQLTries;					///< number of SQL tries for DB operations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GSM::LogicalChannel *mChannel;			///< current channel of the transaction
 | 
						GSM::LogicalChannel *mChannel;			///< current channel of the transaction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool mTerminationRequested;
 | 
						bool mTerminationRequested;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	volatile bool mRemoved;			///< true if ready for removal
 | 
						unsigned mHandoverOtherBSTransactionID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool mFake;					///true if this is a fake message generated internally	
 | 
						volatile bool mRemoved;			///< true if ready for removal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -109,8 +153,7 @@ class TransactionEntry {
 | 
				
			|||||||
		const GSM::L3CMServiceType& wService,
 | 
							const GSM::L3CMServiceType& wService,
 | 
				
			||||||
		const GSM::L3CallingPartyBCDNumber& wCalling,
 | 
							const GSM::L3CallingPartyBCDNumber& wCalling,
 | 
				
			||||||
		GSM::CallState wState = GSM::NullState,
 | 
							GSM::CallState wState = GSM::NullState,
 | 
				
			||||||
		const char *wMessage = NULL,
 | 
							const char *wMessage = NULL);
 | 
				
			||||||
		bool wFake=false);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** This form is used for MOC, setting mGSMState to MOCInitiated. */
 | 
						/** This form is used for MOC, setting mGSMState to MOCInitiated. */
 | 
				
			||||||
	TransactionEntry(const char* proxy,
 | 
						TransactionEntry(const char* proxy,
 | 
				
			||||||
@@ -120,13 +163,6 @@ class TransactionEntry {
 | 
				
			|||||||
		unsigned wL3TI,
 | 
							unsigned wL3TI,
 | 
				
			||||||
		const GSM::L3CalledPartyBCDNumber& wCalled);
 | 
							const GSM::L3CalledPartyBCDNumber& wCalled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** This form is used for SOS calls, setting mGSMState to MOCInitiated. */
 | 
					 | 
				
			||||||
	TransactionEntry(const char* proxy,
 | 
					 | 
				
			||||||
		const GSM::L3MobileIdentity& wSubscriber,
 | 
					 | 
				
			||||||
		GSM::LogicalChannel* wChannel,
 | 
					 | 
				
			||||||
		const GSM::L3CMServiceType& wService,
 | 
					 | 
				
			||||||
		unsigned wL3TI);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Form for MO-SMS; sets yet-unknown TI to 7 and GSM state to SMSSubmitting */
 | 
						/** Form for MO-SMS; sets yet-unknown TI to 7 and GSM state to SMSSubmitting */
 | 
				
			||||||
	TransactionEntry(const char* proxy,
 | 
						TransactionEntry(const char* proxy,
 | 
				
			||||||
		const GSM::L3MobileIdentity& wSubscriber,
 | 
							const GSM::L3MobileIdentity& wSubscriber,
 | 
				
			||||||
@@ -139,6 +175,14 @@ class TransactionEntry {
 | 
				
			|||||||
		const GSM::L3MobileIdentity& wSubscriber,
 | 
							const GSM::L3MobileIdentity& wSubscriber,
 | 
				
			||||||
		GSM::LogicalChannel* wChannel);
 | 
							GSM::LogicalChannel* wChannel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Form used for handover requests; argument is taken from the message string. */
 | 
				
			||||||
 | 
						TransactionEntry(const struct ::sockaddr_in* peer,
 | 
				
			||||||
 | 
							unsigned wHandoverReference,
 | 
				
			||||||
 | 
							SimpleKeyValue ¶ms,
 | 
				
			||||||
 | 
							const char *proxy,
 | 
				
			||||||
 | 
							GSM::LogicalChannel* wChannel,
 | 
				
			||||||
 | 
							unsigned otherTransactionID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Delete the database entry upon destruction. */
 | 
						/** Delete the database entry upon destruction. */
 | 
				
			||||||
	~TransactionEntry();
 | 
						~TransactionEntry();
 | 
				
			||||||
@@ -162,8 +206,6 @@ class TransactionEntry {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	const GSM::L3CallingPartyBCDNumber& calling() const { return mCalling; }
 | 
						const GSM::L3CallingPartyBCDNumber& calling() const { return mCalling; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool fake() const {return mFake; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const char* message() const { return mMessage.c_str(); }
 | 
						const char* message() const { return mMessage.c_str(); }
 | 
				
			||||||
	void message(const char *wMessage, size_t length);
 | 
						void message(const char *wMessage, size_t length);
 | 
				
			||||||
	const char* messageType() const { return mContentType.c_str(); }
 | 
						const char* messageType() const { return mContentType.c_str(); }
 | 
				
			||||||
@@ -174,6 +216,64 @@ class TransactionEntry {
 | 
				
			|||||||
	GSM::CallState GSMState() const;
 | 
						GSM::CallState GSMState() const;
 | 
				
			||||||
	void GSMState(GSM::CallState wState);
 | 
						void GSMState(GSM::CallState wState);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Inbound reference set in the constructor, no lock needed. */
 | 
				
			||||||
 | 
						// FIXME -- These should probably all the const references.
 | 
				
			||||||
 | 
						unsigned inboundReference() const { return mInboundReference; }
 | 
				
			||||||
 | 
						unsigned Codec() { return mCodec; }
 | 
				
			||||||
 | 
						unsigned CSeq() { return mCSeq; }
 | 
				
			||||||
 | 
						string CallID() { return mCallID; }
 | 
				
			||||||
 | 
						string RemoteUsername() { return mRemoteUsername; }
 | 
				
			||||||
 | 
						string RemoteDomain() { return mRemoteDomain; }
 | 
				
			||||||
 | 
						string SIPUsername() { return mSIPUsername; }
 | 
				
			||||||
 | 
						string SIPDisplayname() { return mSIPDisplayname; }
 | 
				
			||||||
 | 
						string FromTag() { return mFromTag; }
 | 
				
			||||||
 | 
						string FromUsername() { return mFromUsername; }
 | 
				
			||||||
 | 
						string FromIP() { return mFromIP; }
 | 
				
			||||||
 | 
						string ToTag() { return mToTag; }
 | 
				
			||||||
 | 
						string ToUsername() { return mToUsername; }
 | 
				
			||||||
 | 
						string ToIP() { return mToIP; }
 | 
				
			||||||
 | 
						string CallIP() { return mCallIP; }
 | 
				
			||||||
 | 
						short RTPRemPort() { return mRTPRemPort; }
 | 
				
			||||||
 | 
						string RTPRemIP() { return mRTPRemIP; }
 | 
				
			||||||
 | 
						string RTPState() { return mRTPState; }
 | 
				
			||||||
 | 
						string SessionID() { return mSessionID; }
 | 
				
			||||||
 | 
						string SessionVersion() { return mSessionVersion; }
 | 
				
			||||||
 | 
						short RmtPort() { return mRmtPort; }
 | 
				
			||||||
 | 
						string RmtIP() { return mRmtIP; }
 | 
				
			||||||
 | 
						string SRIMSI() { return mSRIMSI; }
 | 
				
			||||||
 | 
						string SRCALLID() { return mSRCALLID; }
 | 
				
			||||||
 | 
						unsigned HandoverOtherBSTransactionID() { return mHandoverOtherBSTransactionID; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GSM::L3HandoverReference outboundReference() const { ScopedLock lock(mLock); return mOutboundReference; }
 | 
				
			||||||
 | 
						GSM::L3CellDescription outboundCell() const { ScopedLock lock(mLock); return mOutboundCell; }
 | 
				
			||||||
 | 
						GSM::L3ChannelDescription2 outboundChannel() const { ScopedLock lock(mLock); return mOutboundChannel; }
 | 
				
			||||||
 | 
						GSM::L3PowerCommandAndAccessType outboundPowerCmd() const { ScopedLock lock(mLock); return mOutboundPowerCmd; }
 | 
				
			||||||
 | 
						GSM::L3SynchronizationIndication outboundSynch() const { ScopedLock lock(mLock); return mOutboundSynch; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Set the outbound handover parameters and set the state to HandoverOutbound. */
 | 
				
			||||||
 | 
						void setOutboundHandover(
 | 
				
			||||||
 | 
							const GSM::L3HandoverReference& reference,
 | 
				
			||||||
 | 
							const GSM::L3CellDescription& cell,
 | 
				
			||||||
 | 
							const GSM::L3ChannelDescription2& chan,
 | 
				
			||||||
 | 
							const GSM::L3PowerCommandAndAccessType& pwrCmd,
 | 
				
			||||||
 | 
							const GSM::L3SynchronizationIndication& synch
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Set the inbound handover parameters on the channel; state should alread be HandoverInbound. */
 | 
				
			||||||
 | 
						void setInboundHandover(
 | 
				
			||||||
 | 
							float wRSSI,
 | 
				
			||||||
 | 
							float wTimingError,
 | 
				
			||||||
 | 
							double wTimestamp
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This is thread-safe because mInboundPeer is only modified in the constructor.
 | 
				
			||||||
 | 
						const struct ::sockaddr_in* inboundPeer() const { return &mInboundPeer; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float inboundTimingError() const { ScopedLock lock(mLock); return mInboundTimingError; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Initiate the termination process. */
 | 
						/** Initiate the termination process. */
 | 
				
			||||||
	void terminate() { ScopedLock lock(mLock); mTerminationRequested=true; }
 | 
						void terminate() { ScopedLock lock(mLock); mTerminationRequested=true; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -194,13 +294,6 @@ class TransactionEntry {
 | 
				
			|||||||
	SIP::SIPState MOCSendACK();
 | 
						SIP::SIPState MOCSendACK();
 | 
				
			||||||
	void MOCInitRTP() { ScopedLock lock(mLock); return mSIP.MOCInitRTP(); }
 | 
						void MOCInitRTP() { ScopedLock lock(mLock); return mSIP.MOCInitRTP(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SIP::SIPState SOSSendINVITE(short rtpPort, unsigned codec);
 | 
					 | 
				
			||||||
	SIP::SIPState SOSResendINVITE() { return MOCResendINVITE(); }
 | 
					 | 
				
			||||||
	SIP::SIPState SOSCheckForOK() { return MOCCheckForOK(); }
 | 
					 | 
				
			||||||
	SIP::SIPState SOSSendACK() { return MOCSendACK(); }
 | 
					 | 
				
			||||||
	void SOSInitRTP() { MOCInitRTP(); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	SIP::SIPState MTCSendTrying();
 | 
						SIP::SIPState MTCSendTrying();
 | 
				
			||||||
	SIP::SIPState MTCSendRinging();
 | 
						SIP::SIPState MTCSendRinging();
 | 
				
			||||||
	SIP::SIPState MTCCheckForACK();
 | 
						SIP::SIPState MTCCheckForACK();
 | 
				
			||||||
@@ -230,6 +323,13 @@ class TransactionEntry {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	SIP::SIPState MTSMSSendOK();
 | 
						SIP::SIPState MTSMSSendOK();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SIP::SIPState inboundHandoverSendINVITE(unsigned RTPPort)
 | 
				
			||||||
 | 
							{ ScopedLock lock(mLock); return mSIP.inboundHandoverSendINVITE(this, RTPPort); }
 | 
				
			||||||
 | 
						SIP::SIPState inboundHandoverCheckForOK()
 | 
				
			||||||
 | 
							{ ScopedLock lock(mLock); return mSIP.inboundHandoverCheckForOK(&mLock); }
 | 
				
			||||||
 | 
						SIP::SIPState inboundHandoverSendACK()
 | 
				
			||||||
 | 
							{ ScopedLock lock(mLock); return mSIP.inboundHandoverSendACK(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool sendINFOAndWaitForOK(unsigned info);
 | 
						bool sendINFOAndWaitForOK(unsigned info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void txFrame(unsigned char* frame) { ScopedLock lock(mLock); return mSIP.txFrame(frame); }
 | 
						void txFrame(unsigned char* frame) { ScopedLock lock(mLock); return mSIP.txFrame(frame); }
 | 
				
			||||||
@@ -287,6 +387,9 @@ class TransactionEntry {
 | 
				
			|||||||
	/** Dump information as text for debugging. */
 | 
						/** Dump information as text for debugging. */
 | 
				
			||||||
	void text(std::ostream&) const;
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Genrate an encoded string for handovers. */
 | 
				
			||||||
 | 
						std::string handoverString() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private:
 | 
						private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	friend class TransactionTable;
 | 
						friend class TransactionTable;
 | 
				
			||||||
@@ -372,6 +475,13 @@ class TransactionTable {
 | 
				
			|||||||
	*/
 | 
						*/
 | 
				
			||||||
	bool RTPAvailable(short rtpPort);
 | 
						bool RTPAvailable(short rtpPort);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
							Fand an entry by its handover reference.
 | 
				
			||||||
 | 
							@param ref The 8-bit handover reference.
 | 
				
			||||||
 | 
							@return NULL if ID is not found or was dead
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
 | 
						TransactionEntry* inboundHandover(unsigned ref);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
		Remove an entry from the table and from gSIPMessageMap.
 | 
							Remove an entry from the table and from gSIPMessageMap.
 | 
				
			||||||
		@param wID The transaction ID to search.
 | 
							@param wID The transaction ID to search.
 | 
				
			||||||
@@ -398,6 +508,9 @@ class TransactionTable {
 | 
				
			|||||||
	*/
 | 
						*/
 | 
				
			||||||
	TransactionEntry* find(const GSM::LogicalChannel *chan);
 | 
						TransactionEntry* find(const GSM::LogicalChannel *chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Find a transaction in the HandoverInbound state on the given channel. */
 | 
				
			||||||
 | 
						TransactionEntry* inboundHandover(const GSM::LogicalChannel *chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
		Find an entry by its SACCH channel pointer; returns first entry found.
 | 
							Find an entry by its SACCH channel pointer; returns first entry found.
 | 
				
			||||||
		Also clears dead entries during search.
 | 
							Also clears dead entries during search.
 | 
				
			||||||
@@ -458,6 +571,9 @@ class TransactionTable {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	size_t dump(std::ostream& os, bool showAll=false) const;
 | 
						size_t dump(std::ostream& os, bool showAll=false) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Generate a unique handover reference. */
 | 
				
			||||||
 | 
						//unsigned generateInboundHandoverReference(TransactionEntry* transaction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private:
 | 
						private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	friend class TransactionEntry;
 | 
						friend class TransactionEntry;
 | 
				
			||||||
@@ -478,6 +594,9 @@ class TransactionTable {
 | 
				
			|||||||
	void innerRemove(TransactionMap::iterator);
 | 
						void innerRemove(TransactionMap::iterator);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Check to see if a given outbound handover reference is in use. */
 | 
				
			||||||
 | 
						//bool outboundReferenceUsed(unsigned ref);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										370
									
								
								GPRS/BSSG.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										370
									
								
								GPRS/BSSG.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,370 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Defines.h"
 | 
				
			||||||
 | 
					#include "GPRSInternal.h"	// For GPRSLOG()
 | 
				
			||||||
 | 
					#include "GSMConfig.h"
 | 
				
			||||||
 | 
					#include "Threads.h"
 | 
				
			||||||
 | 
					#include "BSSGMessages.h"
 | 
				
			||||||
 | 
					#include "BSSG.h"
 | 
				
			||||||
 | 
					#include "Utils.h"
 | 
				
			||||||
 | 
					#include "errno.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <sys/socket.h>
 | 
				
			||||||
 | 
					#include <arpa/inet.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace BSSG {
 | 
				
			||||||
 | 
					BSSGMain gBSSG;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const unsigned rbufSize = 3000;	// Much bigger than any PDU message.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if _UNUSED_
 | 
				
			||||||
 | 
					static int BSTLVParse(ByteType *data, int &rp,
 | 
				
			||||||
 | 
						IEIType::type expected_ieitype, int expected_length)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int received_ieitype = data[rp++];
 | 
				
			||||||
 | 
						int received_length = data[rp++];
 | 
				
			||||||
 | 
						if (received_ieitype != expected_ieitype || received_length != expected_length) {
 | 
				
			||||||
 | 
							int bstype = data[NSMsg::UnitDataHeaderLen];
 | 
				
			||||||
 | 
							LOG(ERR) << "Error in BSSG msg type="<<bstype
 | 
				
			||||||
 | 
								<<LOGVAR(expected_ieitype) <<LOGVAR(expected_length)
 | 
				
			||||||
 | 
								<<LOGVAR(received_ieitype) <<LOGVAR(received_length);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						int result;
 | 
				
			||||||
 | 
						switch (received_length) {
 | 
				
			||||||
 | 
						case 1: result = data[rp++]; break;
 | 
				
			||||||
 | 
						case 2: result = getntohs(&data[rp]); rp+=2; break;
 | 
				
			||||||
 | 
						case 4: result = getntohl(&data[rp]); rp+=4; break;
 | 
				
			||||||
 | 
						default: assert(0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void BsRecvSignallingMsg(ByteType *data, int nsize)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rp = NSMsg::UnitDataHeaderLen;
 | 
				
			||||||
 | 
						int bstype = data[rp++];
 | 
				
			||||||
 | 
						switch (bstype) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// PDUs between NM SAPs:
 | 
				
			||||||
 | 
						// We will not use the flow control stuff, block, unblock, etc.
 | 
				
			||||||
 | 
						// However, we will generate the appropriate ACKs to keep the link happy.
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::BVC_BLOCK:
 | 
				
			||||||
 | 
							BSSGWriteLowSide(BVCFactory(BSPDUType::BVC_BLOCK_ACK,0));
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::BVC_BLOCK_ACK:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::BVC_RESET:		// network->BSS request reset everything.
 | 
				
			||||||
 | 
							// Dont know what we should do about reset.
 | 
				
			||||||
 | 
							BSSGWriteLowSide(BVCFactory(BSPDUType::BVC_RESET_ACK,0));
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::BVC_RESET_ACK:	// BSS->network and network->BSS?
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::BVC_UNBLOCK:
 | 
				
			||||||
 | 
							BSSGWriteLowSide(BVCFactory(BSPDUType::BVC_UNBLOCK_ACK,0));
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::BVC_UNBLOCK_ACK:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// We ignore all these:
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::SUSPEND_ACK:		// network->MS ACK
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::SUSPEND_NACK:		// network->MS NACK
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::RESUME_ACK:		// network->MS ACK
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::RESUME_NACK:		// network->MS NACK
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::FLUSH_LL:		// newtork->BSS forget this MS (it moved to another cell.)
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::SGSN_INVOKE_TRACE:	// network->BSS request trace an MS
 | 
				
			||||||
 | 
							LOG(WARNING) << "Unimplemented BSSG message:" << BSPDUType::name(bstype);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::SUSPEND:			// MS->network request to suspend GPRS service.
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::RESUME:			// MS->network request to resume GPRS service.
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::FLUSH_LL_ACK:	// BSS->network
 | 
				
			||||||
 | 
						case BSSG::BSPDUType::LLC_DISCARDED:	// BSS->network notification of lost PDUs (probably expired)
 | 
				
			||||||
 | 
							LOG(ERR) << "Invalid BSSG message:" << BSPDUType::name(bstype);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void NsRecvMsg(unsigned char *data, int nsize)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						NSPDUType::type nstype = (NSPDUType::type) data[0];
 | 
				
			||||||
 | 
						// We dont need to see all the keep alive messages.
 | 
				
			||||||
 | 
						if (nstype != NSPDUType::NS_UNITDATA) { GPRSLOG(4) << "BSSG NsRecvMsg "<<nstype<<LOGVAR(nsize); }
 | 
				
			||||||
 | 
						switch (nstype) {
 | 
				
			||||||
 | 
						case NSPDUType::NS_UNITDATA: {
 | 
				
			||||||
 | 
							int bvci = getntohs(&data[2]);
 | 
				
			||||||
 | 
							if (bvci == BVCI::SIGNALLING) {
 | 
				
			||||||
 | 
								GPRSLOG(4) << "BSSG <=== signalling "<<nstype<<LOGVAR(nsize) <<timestr();
 | 
				
			||||||
 | 
								BsRecvSignallingMsg(data, nsize);
 | 
				
			||||||
 | 
							} else if (bvci == BVCI::PTM) {
 | 
				
			||||||
 | 
								GPRSLOG(4) << "BSSG <=== "<<nstype<<LOGVAR(nsize)<<" ignored" <<timestr();
 | 
				
			||||||
 | 
								// Not implemented
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// Send data to the MAC
 | 
				
			||||||
 | 
								// We left the NS header intact.
 | 
				
			||||||
 | 
								BSSGDownlinkMsg *dlmsg = new BSSGDownlinkMsg(data,nsize);
 | 
				
			||||||
 | 
								//GPRSLOG(1) << "BSSG <=== queued "<<dlmsg->str() <<timestr();
 | 
				
			||||||
 | 
								GPRSLOG(1) << "BSSG <=== queued size="<<dlmsg->size() <<timestr();
 | 
				
			||||||
 | 
								gBSSG.mbsRxQ.write(dlmsg);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						case NSPDUType::NS_RESET:
 | 
				
			||||||
 | 
							gBSSG.mbsResetReceived = true;
 | 
				
			||||||
 | 
							BSSGWriteLowSide(NsFactory(NSPDUType::NS_RESET_ACK));
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NSPDUType::NS_RESET_ACK:
 | 
				
			||||||
 | 
							gBSSG.mbsResetAckReceived = true;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NSPDUType::NS_BLOCK:
 | 
				
			||||||
 | 
							gBSSG.mbsBlocked = true;
 | 
				
			||||||
 | 
							BSSGWriteLowSide(NsFactory(NSPDUType::NS_BLOCK_ACK));
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NSPDUType::NS_BLOCK_ACK:
 | 
				
			||||||
 | 
							// ignored.
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NSPDUType::NS_UNBLOCK:
 | 
				
			||||||
 | 
							gBSSG.mbsBlocked = false;
 | 
				
			||||||
 | 
							BSSGWriteLowSide(NsFactory(NSPDUType::NS_UNBLOCK_ACK));
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NSPDUType::NS_UNBLOCK_ACK:
 | 
				
			||||||
 | 
							gBSSG.mbsBlocked = false;
 | 
				
			||||||
 | 
							// ignored.
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NSPDUType::NS_STATUS:
 | 
				
			||||||
 | 
							// This happens when the sgsn is stopped and restarted.
 | 
				
			||||||
 | 
							// It probably happens for other reasons too, but lets just
 | 
				
			||||||
 | 
							// assume that is what happened and reset the BSSG link.
 | 
				
			||||||
 | 
							gBSSG.BSSGReset();
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NSPDUType::NS_ALIVE:
 | 
				
			||||||
 | 
							gBSSG.mbsAliveReceived = true;
 | 
				
			||||||
 | 
							BSSGWriteLowSide(NsFactory(NSPDUType::NS_ALIVE_ACK));
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NSPDUType::NS_ALIVE_ACK:
 | 
				
			||||||
 | 
							gBSSG.mbsAliveAckReceived = true;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							LOG(INFO) << "unrecognized NS message received, type "<<nstype;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Pull messages out of the BSSG socket and put them in the BSSG queue.
 | 
				
			||||||
 | 
					void *recvServiceLoop(void *arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BSSGMain *bssgp = (BSSGMain*)arg;
 | 
				
			||||||
 | 
						static unsigned char *buf = NULL;
 | 
				
			||||||
 | 
						if (buf == NULL) { buf = (unsigned char*) malloc(rbufSize); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bssgp->mbsIsOpen = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int failures = 0;
 | 
				
			||||||
 | 
						while (bssgp->mbsIsOpen && ++failures < 10) {
 | 
				
			||||||
 | 
							ssize_t rsize = recv(bssgp->mbsSGSockfd,buf,rbufSize,0);
 | 
				
			||||||
 | 
							if (rsize > 0) {
 | 
				
			||||||
 | 
								failures = 0;
 | 
				
			||||||
 | 
								NsRecvMsg(buf,rsize);
 | 
				
			||||||
 | 
							} else if (rsize == -1) {
 | 
				
			||||||
 | 
								LOG(ERR) << "Received -1 from BSSG recv(), error:"<<strerror(errno);
 | 
				
			||||||
 | 
								LOG(ERR) << "Above error may mean SGSN is not responding";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Oops.  Close the BSSG and kill the other thread.
 | 
				
			||||||
 | 
						LOG(ERR) << "BSSGP aborting; too many failures in a row";
 | 
				
			||||||
 | 
						bssgp->mbsIsOpen = false;
 | 
				
			||||||
 | 
						// Send a message to the sendServiceLoop so that it will notice
 | 
				
			||||||
 | 
						// we have died and die also.
 | 
				
			||||||
 | 
						BSSGWriteLowSide(NsFactory(NSPDUType::NS_BLOCK));
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The send probably does not need to be in a separate thread.
 | 
				
			||||||
 | 
					// We could also have used a select or poll system call.
 | 
				
			||||||
 | 
					// But it was easier to use two threads.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// OLD: Send this loop an NS_BLOCK message to kill this thread off;
 | 
				
			||||||
 | 
					// and we dont normally use that NS message.
 | 
				
			||||||
 | 
					// There is a BSSG-level BVC_BLOCK message that we would use to do a temporary data block.
 | 
				
			||||||
 | 
					void *sendServiceLoop(void *arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BSSGMain *bssgp = (BSSGMain*)arg;
 | 
				
			||||||
 | 
						NSPDUType::type nstype = NSPDUType::NS_RESET;	// init to anything.
 | 
				
			||||||
 | 
						do {
 | 
				
			||||||
 | 
							NSMsg *ulmsg = bssgp->mbsTxQ.read();
 | 
				
			||||||
 | 
							// It is already wrapped up in an NS protocol.
 | 
				
			||||||
 | 
							int msgsize = ulmsg->size();
 | 
				
			||||||
 | 
							ssize_t result = send(bssgp->mbsSGSockfd,ulmsg->begin(),msgsize,0);
 | 
				
			||||||
 | 
							nstype = ulmsg->getNSPDUType();
 | 
				
			||||||
 | 
							int debug_level = 1; //(nstype == NSPDUType::NS_UNITDATA) ? 1 : 4;
 | 
				
			||||||
 | 
							if (GPRS::GPRSDebug & debug_level) {
 | 
				
			||||||
 | 
								GPRSLOG(debug_level) << "BSSG ===> sendServiceLoop sent "
 | 
				
			||||||
 | 
									<<nstype<<LOGVAR(msgsize)<<ulmsg->str()<<timestr();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (result != msgsize) {
 | 
				
			||||||
 | 
								LOG(ERR) << "BSSGP invalid send result" << LOGVAR(result) << LOGVAR(msgsize);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							delete ulmsg;
 | 
				
			||||||
 | 
						} while (bssgp->mbsIsOpen /*&& nstype != NSPDUType::NS_BLOCK*/);
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int opensock(uint32_t sgsnIp, int sgsnPort /*,int bssgPort*/ )
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						//int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
 | 
				
			||||||
 | 
						int sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
 | 
				
			||||||
 | 
						if (sockfd < 0) {
 | 
				
			||||||
 | 
							LOG(ERR) << "Could not create socket for BSSGP";
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/******
 | 
				
			||||||
 | 
						{	// We dont want to bind here.  connect will pick a port for us.
 | 
				
			||||||
 | 
							int32_t bssgIp = INADDR_ANY;
 | 
				
			||||||
 | 
							struct sockaddr_in myAddr;
 | 
				
			||||||
 | 
							memset(&myAddr,0,sizeof(myAddr));	// be safe.
 | 
				
			||||||
 | 
							myAddr.sin_family = AF_INET;
 | 
				
			||||||
 | 
							myAddr.sin_addr.s_addr = htonl(bssgIp);
 | 
				
			||||||
 | 
							myAddr.sin_port = htons(bssgPort);
 | 
				
			||||||
 | 
							if (0 != bind(sockfd,(sockaddr*)&myAddr,sizeof(myAddr))) {
 | 
				
			||||||
 | 
								LOG(ERR) << "Could not bind NS socket to" 
 | 
				
			||||||
 | 
									<< LOGVAR(bssgIp) << LOGVAR(bssgPort) << LOGVAR(errno);
 | 
				
			||||||
 | 
								close(sockfd);
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						****/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct sockaddr_in sgsnAddr;
 | 
				
			||||||
 | 
						memset(&sgsnAddr,0,sizeof(sgsnAddr));
 | 
				
			||||||
 | 
						sgsnAddr.sin_family = AF_INET;
 | 
				
			||||||
 | 
						sgsnAddr.sin_addr.s_addr = sgsnIp;	// This is already in network order.
 | 
				
			||||||
 | 
						sgsnAddr.sin_port = htons(sgsnPort);
 | 
				
			||||||
 | 
						if (0 != connect(sockfd,(sockaddr*)&sgsnAddr,sizeof(sgsnAddr))) {
 | 
				
			||||||
 | 
							LOG(ERR) << "Could not connect NS socket to"
 | 
				
			||||||
 | 
								<< LOGVAR(sgsnIp) << LOGVAR(sgsnPort) << LOGVAR(errno);
 | 
				
			||||||
 | 
							close(sockfd);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							GPRSLOG(1) << "connected to SGSN at "<< inet_ntoa(sgsnAddr.sin_addr) <<" port "<<sgsnPort;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return sockfd;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NOTES: The sgsn uses UDP.
 | 
				
			||||||
 | 
					// It does not accept connections using listen() and accept().
 | 
				
			||||||
 | 
					// Rather it waits for a packet containing an NS_RESET message,
 | 
				
			||||||
 | 
					// then it remembers the IP&port from the IP header, (via recvfrom)
 | 
				
			||||||
 | 
					// for future communication with the BTS.
 | 
				
			||||||
 | 
					// BEGINCONFIG
 | 
				
			||||||
 | 
					// 'GPRS.SGSN.Host','127.0.0.1',0,0, 'IP address of the SGSN required for GPRS service.  The default value is the localhost, ie, SGSN runs on same machine as OpenBTS.'
 | 
				
			||||||
 | 
					// 'GPRS.SGSN.port',1920,0,0,'Port number of the SGSN required for GPRS service.  This must match the port specified in the SGSN config file, currently osmo_sgsn.cfg'
 | 
				
			||||||
 | 
					// ENDCONFIG
 | 
				
			||||||
 | 
					bool BSSGMain::BSSGOpen()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (mbsIsOpen) return true;
 | 
				
			||||||
 | 
						// TODO: Look up the proper default sgsn port.  Maybe 3386. See pat.txt
 | 
				
			||||||
 | 
						// Originally I specified the BSSG port, but now I let the O.S. pick
 | 
				
			||||||
 | 
						// a free port via connect() call.
 | 
				
			||||||
 | 
						//int bssgPort = gConfig.getNum("GPRS.BSSG.port",1921);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Default BVCI to the first available value.
 | 
				
			||||||
 | 
						// The user may want to specify this some day far away.
 | 
				
			||||||
 | 
						mbsBVCI = BVCI::PTP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int sgsnPort = gConfig.getNum("GPRS.SGSN.port");
 | 
				
			||||||
 | 
						std::string sgsnHost = gConfig.getStr("GPRS.SGSN.Host");	// Default: localhost
 | 
				
			||||||
 | 
						uint32_t sgsnIp = inet_addr(sgsnHost.c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sgsnIp == INADDR_NONE) {
 | 
				
			||||||
 | 
							LOG(ERR) << "Config GPRS.SGSN.Host value is not a valid IP address: " << sgsnHost << "\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						//mbsSGSockfd = openudp(sgsnIp,sgsnPort,bssgPort);
 | 
				
			||||||
 | 
						if (mbsSGSockfd < 0) {
 | 
				
			||||||
 | 
							mbsSGSockfd = opensock(sgsnIp,sgsnPort);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (mbsSGSockfd < 0) {
 | 
				
			||||||
 | 
							LOG(ERR) << "Could not init BSSGP due to socket failure";
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mbsBlocked = true;
 | 
				
			||||||
 | 
						mbsRecvThread.start(recvServiceLoop,this);
 | 
				
			||||||
 | 
						mbsSendThread.start(sendServiceLoop,this);
 | 
				
			||||||
 | 
						return BSSGReset();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool BSSGMain::BSSGReset()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// BSSG starts out blocked until it receives a reset.
 | 
				
			||||||
 | 
						mbsBlocked = true;
 | 
				
			||||||
 | 
						mbsResetReceived = false;
 | 
				
			||||||
 | 
						mbsResetAckReceived = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Start communication with SGSN.
 | 
				
			||||||
 | 
						// Initiate NS Reset procedure: GSM 08.16 7.3
 | 
				
			||||||
 | 
						// We are supposed to send NS_STATUS, but it doesnt matter with our sgsn.
 | 
				
			||||||
 | 
						BSSGWriteLowSide(NsFactory(NSPDUType::NS_RESET));
 | 
				
			||||||
 | 
						BSSGWriteLowSide(NsFactory(NSPDUType::NS_UNBLOCK));
 | 
				
			||||||
 | 
						// Wait a bit for the sgsn to respond.
 | 
				
			||||||
 | 
						// Note: The first time we talk to the SGSN it sends us an NS_RESET,
 | 
				
			||||||
 | 
						// but if OpenBTS crashes, the second time it inits it doesnt send NS_RESET,
 | 
				
			||||||
 | 
						// which may be a bug in the SGSN, but in any event we dont want
 | 
				
			||||||
 | 
						// to wait for a RESET msg.
 | 
				
			||||||
 | 
						for (int i = 0; 1; i++) {
 | 
				
			||||||
 | 
							if (mbsResetAckReceived && !mbsBlocked) { break; }
 | 
				
			||||||
 | 
							Utils::sleepf(0.1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (i >= 40) {	// wait 4 seconds
 | 
				
			||||||
 | 
								GPRSLOG(1) << LOGVAR(mbsResetReceived)
 | 
				
			||||||
 | 
									<<LOGVAR(mbsResetAckReceived) <<LOGVAR(mbsBlocked);
 | 
				
			||||||
 | 
								LOG(INFO) << "SGSN failed to respond\n";
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GSM 08.18 8.4: Reset the BVC.  You must do this after verifying NS layer is working.
 | 
				
			||||||
 | 
						// Must reset each BVCI separately.
 | 
				
			||||||
 | 
						// I'm not going to bother to check for acks - if the NS protocol inited,
 | 
				
			||||||
 | 
						// the sgsn is fine and this will init ok too.
 | 
				
			||||||
 | 
						BSSGWriteLowSide(BVCFactory(BSPDUType::BVC_RESET,BVCI::SIGNALLING));
 | 
				
			||||||
 | 
						BSSGWriteLowSide(BVCFactory(BSPDUType::BVC_RESET,gBSSG.mbsBVCI));
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BSSGDownlinkMsg *BSSGMain::BSSGReadLowSide()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return mbsRxQ.readNoBlock();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BSSGWriteLowSide(NSMsg *ulmsg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (gBSSG.mbsTestQ) {
 | 
				
			||||||
 | 
							// For testing, deliver messages to this queue instead:
 | 
				
			||||||
 | 
							gBSSG.mbsTestQ->write(ulmsg);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							GPRSLOG(1) << "BSSG ===> writelowside " <<ulmsg->str()<<timestr();
 | 
				
			||||||
 | 
							gBSSG.mbsTxQ.write(ulmsg);	// normal mode; block is headed for the SGSN.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										116
									
								
								GPRS/BSSG.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								GPRS/BSSG.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef BSSG_H
 | 
				
			||||||
 | 
					#define BSSG_H
 | 
				
			||||||
 | 
					#include "Interthread.h"
 | 
				
			||||||
 | 
					#include "Defines.h"
 | 
				
			||||||
 | 
					#include "ByteVector.h"
 | 
				
			||||||
 | 
					#include "ScalarTypes.h"
 | 
				
			||||||
 | 
					#include "BSSGMessages.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BSSG.cpp handles link layer communication to the SGSN.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace BSSG {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM 08.16 describes tne NS layer.
 | 
				
			||||||
 | 
					// GSM 08.18 sec 10 describes the PDU messages that the SGSN can send to the BSS.
 | 
				
			||||||
 | 
					// GSM 48.018 is the updated spec with PS-HANDOVER related commands.
 | 
				
			||||||
 | 
					// Definitions:
 | 
				
			||||||
 | 
					// NSE - Network Service Entity.  There is one or more NSE inside the BSS for signaling.
 | 
				
			||||||
 | 
					// NSEI == Network Service Entity Indicator
 | 
				
			||||||
 | 
					// BVC = BSSGP Virtual Connection, transport peer-to-peer through the BTS.
 | 
				
			||||||
 | 
					// LSP = Link Selector Parameter, points to one MS, eg, the MS's TLLI.
 | 
				
			||||||
 | 
					// NSVCI = Network Service Virtual Connection Identifier.
 | 
				
			||||||
 | 
					// NSEI = Network Service Entity Identifier
 | 
				
			||||||
 | 
					// BVCI - BSSGP Virtual Connection ID. BVCI 0 = signaling, BVCI 1 = PTM (point-to-multipoint),
 | 
				
			||||||
 | 
					//	all other values are point-to-point.
 | 
				
			||||||
 | 
					//	GSM08.18sec5.4.1: "This parameter is not part of the BSSGP PDU across
 | 
				
			||||||
 | 
					//	the Gb interface, but is used by the network service entity across the Gb."
 | 
				
			||||||
 | 
					//	It corresponds to a cell, and can be used instead of routing area id
 | 
				
			||||||
 | 
					//	at operators discretion.
 | 
				
			||||||
 | 
					// LSP - Link Selector Parameter, something used only inside the BSS or SGSN,
 | 
				
			||||||
 | 
					//     		and not transmitted, to uniquely identify NS-VC.  We wont use it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Just so you dont have to read all the manuals, here is how it works:
 | 
				
			||||||
 | 
					// The NS and BSSG layers are for BTS to SGSN communication.
 | 
				
			||||||
 | 
					// The NS protocol is the inner-most layer.  This stuff was designed for frame relay,
 | 
				
			||||||
 | 
					// so there is an NSVCI which, despite the name, identifies a specific physical connection,
 | 
				
			||||||
 | 
					// and the NSEI, which identifies a group of NSVCIs that connect the BTS to the SGSN.
 | 
				
			||||||
 | 
					// The idea is you can take down individual NSVCs without taking down the complete communication link.
 | 
				
			||||||
 | 
					// You can also block/unblock individual NS connections.
 | 
				
			||||||
 | 
					// The next layer up is the BSSG layer.  Endpoints are identfied by BVCI.
 | 
				
			||||||
 | 
					// The first two BVCIs are reserved: BVCI 0 for signaling messages handled directly by the BSSG controller,
 | 
				
			||||||
 | 
					// BVCI 1 for point-to-multipoint messages, and all other BVCIs are for BTSs that share this BSSG+NS link.
 | 
				
			||||||
 | 
					// So in a normal system, multiple BTS could communicate over a single NSEI consisting of
 | 
				
			||||||
 | 
					// a group of NSVCI.  Each BTS would have a BVCI assigned,
 | 
				
			||||||
 | 
					// and the network connections have NSVCIs and NSEIs assigned.
 | 
				
			||||||
 | 
					// All this is entirely irrelevant to us, because our BTS talks to the SGSN using UDP/IP,
 | 
				
			||||||
 | 
					// and the SGSN actually identifies the connection by remembering the endpoint UDP/IP address
 | 
				
			||||||
 | 
					// of the BTS when it first calls in, which puts a hard limit of something like 2**48 BTS per SGSN,
 | 
				
			||||||
 | 
					// which should be enough.  (That was a joke.)
 | 
				
			||||||
 | 
					// However, we still use the NS and BSSG messages that reset and block/unblock the lines,
 | 
				
			||||||
 | 
					// so we have to assign dummy numbers for the NSVCI, NSEI, and BVCI.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The messages supported at the BSSG level include signaling messages (to BVCI 0)
 | 
				
			||||||
 | 
					// that support, eg, blocking/unblocking of an individual BTS, UL_UNITDATA and DL_UNITDATA
 | 
				
			||||||
 | 
					// to transfer PDUs (Packet Data Units), which consist of L3 Messages or user data
 | 
				
			||||||
 | 
					// wrapped in LLC layer wrappers that flow through the BTS as RLCData blocks to L3 in the MS,
 | 
				
			||||||
 | 
					// and a few special messages that go to the BTS, like paging and MS capabilities.
 | 
				
			||||||
 | 
					// See BSSGMessages.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const unsigned SGSNTimeout = 1000;	// In msecs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The main interface to the SGSN.
 | 
				
			||||||
 | 
					class BSSGMain {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// Only BSSG downlink messages are put on the receive queue; other types are handled immediately.
 | 
				
			||||||
 | 
						// The transmit queue may have any type of BSSG/NS message.
 | 
				
			||||||
 | 
						InterthreadQueue<BSSGDownlinkMsg> mbsRxQ;
 | 
				
			||||||
 | 
						InterthreadQueue<NSMsg> mbsTxQ;
 | 
				
			||||||
 | 
						InterthreadQueue<NSMsg> *mbsTestQ;	// Only used for testing
 | 
				
			||||||
 | 
						Thread mbsRecvThread;
 | 
				
			||||||
 | 
						Thread mbsSendThread;
 | 
				
			||||||
 | 
						int mbsSGSockfd;
 | 
				
			||||||
 | 
						Bool_z mbsIsOpen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Bool_z mbsResetReceived;
 | 
				
			||||||
 | 
						Bool_z mbsResetAckReceived;
 | 
				
			||||||
 | 
						Bool_z mbsAliveReceived;
 | 
				
			||||||
 | 
						Bool_z mbsAliveAckReceived;
 | 
				
			||||||
 | 
						Bool_z mbsBlocked;	// We dont implement blocking, but we track the state.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// These are identifiers for the BSC and NS link, which we dont use.
 | 
				
			||||||
 | 
						UInt_z mbsBVCI;		// Our BTS identifire.  Must not be 0 or 1.
 | 
				
			||||||
 | 
						UInt_z mbsNSVCI;
 | 
				
			||||||
 | 
						UInt_z mbsNSEI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						BSSGMain() { mbsSGSockfd = -1; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool BSSGOpen();
 | 
				
			||||||
 | 
						bool BSSGReset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						BSSGDownlinkMsg *BSSGReadLowSide();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BSSGWriteLowSide(NSMsg *ulmsg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern BSSGMain gBSSG;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										618
									
								
								GPRS/BSSGMessages.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										618
									
								
								GPRS/BSSGMessages.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,618 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "BSSG.h"
 | 
				
			||||||
 | 
					#include "BSSGMessages.h"
 | 
				
			||||||
 | 
					#include "GPRSInternal.h"
 | 
				
			||||||
 | 
					#include "Globals.h"
 | 
				
			||||||
 | 
					#include "LLC.h"
 | 
				
			||||||
 | 
					#define CASENAME(x) case x: return #x;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace BSSG {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *BSPDUType::name(int val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch ((type)val) {
 | 
				
			||||||
 | 
							CASENAME(DL_UNITDATA)
 | 
				
			||||||
 | 
							CASENAME(UL_UNITDATA)
 | 
				
			||||||
 | 
							CASENAME(RA_CAPABILITY)
 | 
				
			||||||
 | 
							CASENAME(PTM_UNITDATA)
 | 
				
			||||||
 | 
							CASENAME(PAGING_PS)
 | 
				
			||||||
 | 
							CASENAME(PAGING_CS)
 | 
				
			||||||
 | 
							CASENAME(RA_CAPABILITY_UPDATE)
 | 
				
			||||||
 | 
							CASENAME(RA_CAPABILITY_UPDATE_ACK)
 | 
				
			||||||
 | 
							CASENAME(RADIO_STATUS)
 | 
				
			||||||
 | 
							CASENAME(SUSPEND)
 | 
				
			||||||
 | 
							CASENAME(SUSPEND_ACK)
 | 
				
			||||||
 | 
							CASENAME(SUSPEND_NACK)
 | 
				
			||||||
 | 
							CASENAME(RESUME)
 | 
				
			||||||
 | 
							CASENAME(RESUME_ACK)
 | 
				
			||||||
 | 
							CASENAME(RESUME_NACK)
 | 
				
			||||||
 | 
							CASENAME(BVC_BLOCK)
 | 
				
			||||||
 | 
							CASENAME(BVC_BLOCK_ACK)
 | 
				
			||||||
 | 
							CASENAME(BVC_RESET)
 | 
				
			||||||
 | 
							CASENAME(BVC_RESET_ACK)
 | 
				
			||||||
 | 
							CASENAME(BVC_UNBLOCK)
 | 
				
			||||||
 | 
							CASENAME(BVC_UNBLOCK_ACK)
 | 
				
			||||||
 | 
							CASENAME(FLOW_CONTROL_BVC)
 | 
				
			||||||
 | 
							CASENAME(FLOW_CONTROL_BVC_ACK)
 | 
				
			||||||
 | 
							CASENAME(FLOW_CONTROL_MS)
 | 
				
			||||||
 | 
							CASENAME(FLOW_CONTROL_MS_ACK)
 | 
				
			||||||
 | 
							CASENAME(FLUSH_LL)
 | 
				
			||||||
 | 
							CASENAME(FLUSH_LL_ACK)
 | 
				
			||||||
 | 
							CASENAME(LLC_DISCARDED)
 | 
				
			||||||
 | 
							CASENAME(SGSN_INVOKE_TRACE)
 | 
				
			||||||
 | 
							CASENAME(STATUS)
 | 
				
			||||||
 | 
							CASENAME(DOWNLOAD_BSS_PFC)
 | 
				
			||||||
 | 
							CASENAME(CREATE_BSS_PFC)
 | 
				
			||||||
 | 
							CASENAME(CREATE_BSS_PFC_ACK)
 | 
				
			||||||
 | 
							CASENAME(CREATE_BSS_PFC_NACK)
 | 
				
			||||||
 | 
							CASENAME(MODIFY_BSS_PFC)
 | 
				
			||||||
 | 
							CASENAME(MODIFY_BSS_PFC_ACK)
 | 
				
			||||||
 | 
							CASENAME(DELETE_BSS_PFC)
 | 
				
			||||||
 | 
							CASENAME(DELETE_BSS_PFC_ACK)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "unrecognized PDU";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const BSPDUType::type val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << "PDU_Type=" <<(int)val <<"=" <<BSPDUType::name(val);
 | 
				
			||||||
 | 
						return os;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM 08.18 table 5.4 says what BVCI to use for each message.
 | 
				
			||||||
 | 
					// This is kinda dopey, but we have to do it.
 | 
				
			||||||
 | 
					// We are ignoring PTM messages.
 | 
				
			||||||
 | 
					// There is a note the PAGING_PS and PAGING_CS may be BVCI::SIGNALLING
 | 
				
			||||||
 | 
					const unsigned BSPDUType::LookupBVCI(BSPDUType::type bstype)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (bstype) {
 | 
				
			||||||
 | 
						case BSPDUType::SUSPEND:
 | 
				
			||||||
 | 
						case BSPDUType::SUSPEND_ACK:
 | 
				
			||||||
 | 
						case BSPDUType::SUSPEND_NACK:
 | 
				
			||||||
 | 
						case BSPDUType::RESUME:
 | 
				
			||||||
 | 
						case BSPDUType::RESUME_ACK:
 | 
				
			||||||
 | 
						case BSPDUType::RESUME_NACK:
 | 
				
			||||||
 | 
						case BSPDUType::FLUSH_LL:
 | 
				
			||||||
 | 
						case BSPDUType::FLUSH_LL_ACK:
 | 
				
			||||||
 | 
						case BSPDUType::LLC_DISCARDED:
 | 
				
			||||||
 | 
						case BSPDUType::BVC_BLOCK:
 | 
				
			||||||
 | 
						case BSPDUType::BVC_BLOCK_ACK:
 | 
				
			||||||
 | 
						case BSPDUType::BVC_UNBLOCK:
 | 
				
			||||||
 | 
						case BSPDUType::BVC_UNBLOCK_ACK:
 | 
				
			||||||
 | 
						case BSPDUType::BVC_RESET:
 | 
				
			||||||
 | 
						case BSPDUType::BVC_RESET_ACK:
 | 
				
			||||||
 | 
						case BSPDUType::SGSN_INVOKE_TRACE:
 | 
				
			||||||
 | 
							return BVCI::SIGNALLING;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return gBSSG.mbsBVCI;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *IEIType::name(int val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch ((type)val) {
 | 
				
			||||||
 | 
							CASENAME(AlignmentOctets)
 | 
				
			||||||
 | 
							CASENAME(BmaxDefaultMS)
 | 
				
			||||||
 | 
							CASENAME(BSSAreaIndication)
 | 
				
			||||||
 | 
							CASENAME(BucketLeakRate)
 | 
				
			||||||
 | 
							CASENAME(BVCI)
 | 
				
			||||||
 | 
							CASENAME(BVCBucketSize)
 | 
				
			||||||
 | 
							CASENAME(BVCMeasurement)
 | 
				
			||||||
 | 
							CASENAME(Cause)
 | 
				
			||||||
 | 
							CASENAME(CellIdentifier)
 | 
				
			||||||
 | 
							CASENAME(ChannelNeeded)
 | 
				
			||||||
 | 
							CASENAME(DRXParameters)
 | 
				
			||||||
 | 
							CASENAME(eMLPPPriority)
 | 
				
			||||||
 | 
							CASENAME(FlushAction)
 | 
				
			||||||
 | 
							CASENAME(IMSI)
 | 
				
			||||||
 | 
							CASENAME(LLCPDU)
 | 
				
			||||||
 | 
							CASENAME(LLCFramesDiscarded)
 | 
				
			||||||
 | 
							CASENAME(LocationArea)
 | 
				
			||||||
 | 
							CASENAME(MobileId)
 | 
				
			||||||
 | 
							CASENAME(MSBucketSize)
 | 
				
			||||||
 | 
							CASENAME(MSRadioAccessCapability)
 | 
				
			||||||
 | 
							CASENAME(OMCId)
 | 
				
			||||||
 | 
							CASENAME(PDUInError)
 | 
				
			||||||
 | 
							CASENAME(PDULifetime)
 | 
				
			||||||
 | 
							CASENAME(Priority)
 | 
				
			||||||
 | 
							CASENAME(QoSProfile)
 | 
				
			||||||
 | 
							CASENAME(RadioCause)
 | 
				
			||||||
 | 
							CASENAME(RACapUPDCause)
 | 
				
			||||||
 | 
							CASENAME(RouteingArea)
 | 
				
			||||||
 | 
							CASENAME(RDefaultMS)
 | 
				
			||||||
 | 
							CASENAME(SuspendReferenceNumber)
 | 
				
			||||||
 | 
							CASENAME(Tag)
 | 
				
			||||||
 | 
							CASENAME(TLLI)
 | 
				
			||||||
 | 
							CASENAME(TMSI)
 | 
				
			||||||
 | 
							CASENAME(TraceReference)
 | 
				
			||||||
 | 
							CASENAME(TraceType)
 | 
				
			||||||
 | 
							CASENAME(TransactionId)
 | 
				
			||||||
 | 
							CASENAME(TriggerId)
 | 
				
			||||||
 | 
							CASENAME(NumberOfOctetsAffected)
 | 
				
			||||||
 | 
							CASENAME(LSAIdentifierList)
 | 
				
			||||||
 | 
							CASENAME(LSAInformation)
 | 
				
			||||||
 | 
							CASENAME(PacketFlowIdentifier)
 | 
				
			||||||
 | 
							CASENAME(PacketFlowTimer)
 | 
				
			||||||
 | 
							CASENAME(AggregateBSSQoSProfile)
 | 
				
			||||||
 | 
							CASENAME(FeatureBitmap)
 | 
				
			||||||
 | 
							CASENAME(BucketFullRatio)
 | 
				
			||||||
 | 
							CASENAME(ServiceUTRANCCO)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "unrecognized IEI";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const IEIType::type val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << "IEI_Type=" <<(int)val <<"=" <<IEIType::name(val);
 | 
				
			||||||
 | 
						return os;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *NSPDUType::name(int val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch ((type)val) {
 | 
				
			||||||
 | 
							CASENAME(NS_UNITDATA)
 | 
				
			||||||
 | 
							CASENAME(NS_RESET)
 | 
				
			||||||
 | 
							CASENAME(NS_RESET_ACK)
 | 
				
			||||||
 | 
							CASENAME(NS_BLOCK)
 | 
				
			||||||
 | 
							CASENAME(NS_BLOCK_ACK)
 | 
				
			||||||
 | 
							CASENAME(NS_UNBLOCK)
 | 
				
			||||||
 | 
							CASENAME(NS_UNBLOCK_ACK)
 | 
				
			||||||
 | 
							CASENAME(NS_STATUS)
 | 
				
			||||||
 | 
							CASENAME(NS_ALIVE)
 | 
				
			||||||
 | 
							CASENAME(NS_ALIVE_ACK)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "unrecognized NSPDUType";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const NSPDUType::type val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << "NSPDU_Type=" <<(int)val <<"=" <<NSPDUType::name(val);
 | 
				
			||||||
 | 
						return os;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					/** GSM 04.60 11.2 */
 | 
				
			||||||
 | 
					BSSGDownlinkMsg* BSSGDownlinkParse(ByteVector &src)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BSSGDownlinkMsg *result = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// NS PDUType is byte 0.
 | 
				
			||||||
 | 
						NSPDUType::type nstype = (NSPDUType::type) src.getByte(0);
 | 
				
			||||||
 | 
						if (nstype != NSPDUType::NS_UNITDATA) {
 | 
				
			||||||
 | 
							LOG(INFO) << "Unrecognized NS PDU Type=" << nstype ;
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// BSSG PDUType is byte 5.
 | 
				
			||||||
 | 
						BSPDUType::type msgType = (BSPDUType::type) src.getByte(5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (msgType) {
 | 
				
			||||||
 | 
							case BSPDUType::DL_UNITDATA:
 | 
				
			||||||
 | 
								result = new BSSGMsgDLUnitData(src);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BSPDUType::RA_CAPABILITY:
 | 
				
			||||||
 | 
							case BSPDUType::PAGING_PS:
 | 
				
			||||||
 | 
							case BSPDUType::PAGING_CS:
 | 
				
			||||||
 | 
							case BSPDUType::RA_CAPABILITY_UPDATE_ACK:
 | 
				
			||||||
 | 
							case BSPDUType::SUSPEND_ACK:
 | 
				
			||||||
 | 
							case BSPDUType::SUSPEND_NACK:
 | 
				
			||||||
 | 
							case BSPDUType::RESUME_ACK:
 | 
				
			||||||
 | 
							case BSPDUType::RESUME_NACK:
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								LOG(INFO) << "unimplemented BSSG downlink message, type=" << msgType;
 | 
				
			||||||
 | 
								return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					BSSGDownlinkMsg* BSSGDownlinkMessageParse(ByteVector&src)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BSSGDownlinkMsg *result = NULL;
 | 
				
			||||||
 | 
						unsigned pdutype = src.getByte(sizeof(NSMsg::UnitDataHeaderLen));
 | 
				
			||||||
 | 
						switch (pdutype) {
 | 
				
			||||||
 | 
							case BSPDUType::DL_UNITDATA:
 | 
				
			||||||
 | 
								result = new BSSGMsgDLUnitData(src); 
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case BSPDUType::RA_CAPABILITY_UPDATE_ACK:
 | 
				
			||||||
 | 
							case BSPDUType::RA_CAPABILITY:
 | 
				
			||||||
 | 
							case BSPDUType::FLUSH_LL:
 | 
				
			||||||
 | 
								// todo
 | 
				
			||||||
 | 
								LOG(INFO) << "unsuppported BSSG downlink PDU type=" << pdutype;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								LOG(INFO) << "unsuppported BSSG downlink PDU type=" << pdutype;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BSSGMsgDLUnitData::parseDLUnitDataBody(ByteVector &src, size_t &wp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						wp++;	// Skip over the pdutype.
 | 
				
			||||||
 | 
						mbdTLLI = src.getUInt32(wp); wp+=4;
 | 
				
			||||||
 | 
						mbdQoS.qosRead(src,wp);
 | 
				
			||||||
 | 
						// The rest of the fields use TLV format:
 | 
				
			||||||
 | 
						unsigned len = src.size() - 1;	// wp cant actually get anywhere near size() because
 | 
				
			||||||
 | 
									// the TLV headers take at least 2 bytes.
 | 
				
			||||||
 | 
						while (wp < len) {
 | 
				
			||||||
 | 
							unsigned iei = src.getByte(wp++);
 | 
				
			||||||
 | 
							unsigned length = src.readLI(wp);
 | 
				
			||||||
 | 
							unsigned nextwp = wp + length;
 | 
				
			||||||
 | 
							switch (iei) {
 | 
				
			||||||
 | 
								case IEIType::PDULifetime:
 | 
				
			||||||
 | 
									mbdPDULifetime = src.getUInt16(wp);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case IEIType::IMSI:
 | 
				
			||||||
 | 
									mbdIMSI.set(src.segment(wp,length));
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case IEIType::MSRadioAccessCapability:
 | 
				
			||||||
 | 
									mbdRACap.set(src.segment(wp,length));
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case IEIType::LLCPDU:
 | 
				
			||||||
 | 
									// Finally, here is the data:
 | 
				
			||||||
 | 
									mbdPDU.set(src.segment(wp,length));
 | 
				
			||||||
 | 
									goto done;
 | 
				
			||||||
 | 
								case IEIType::TLLI:		// old TLLI
 | 
				
			||||||
 | 
									mbdHaveOldTLLI = true;
 | 
				
			||||||
 | 
									mbdOldTLLI = src.getUInt32(wp);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case IEIType::Priority:
 | 
				
			||||||
 | 
								case IEIType::DRXParameters:
 | 
				
			||||||
 | 
								case IEIType::PacketFlowIdentifier:
 | 
				
			||||||
 | 
								case IEIType::LSAInformation:
 | 
				
			||||||
 | 
								case IEIType::AlignmentOctets:
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									// ignored.
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							wp = nextwp;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						done:;
 | 
				
			||||||
 | 
						//...
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//std::ostream& operator<<(std::ostream& os, const BSSGMsgDLUnitData &val)
 | 
				
			||||||
 | 
					//{
 | 
				
			||||||
 | 
						//val.text(os);
 | 
				
			||||||
 | 
						//return os;
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void NsAddCause(ByteVector *vec, unsigned cause)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec->appendByte(NsIEIType::IEINsCause);
 | 
				
			||||||
 | 
						vec->appendLI(1);
 | 
				
			||||||
 | 
						vec->appendByte(cause);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void NsAddBVCI(ByteVector *vec, BVCI::type  bvci)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec->appendByte(NsIEIType::IEINsBVCI);
 | 
				
			||||||
 | 
						vec->appendLI(2);
 | 
				
			||||||
 | 
						vec->appendUInt16(bvci);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void NsAddVCI(ByteVector *vec)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec->appendByte(NsIEIType::IEINsVCI);
 | 
				
			||||||
 | 
						vec->appendLI(2);
 | 
				
			||||||
 | 
						vec->appendUInt16(gBSSG.mbsNSVCI);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void NsAddNSEI(ByteVector *vec)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec->appendByte(NsIEIType::IEINsNSEI);
 | 
				
			||||||
 | 
						vec->appendLI(2);
 | 
				
			||||||
 | 
						vec->appendUInt16(gBSSG.mbsNSEI);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NSMsg *NsFactory(NSPDUType::type nstype, int cause)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						NSMsg *vec = new NSMsg(80);	// Big enough for any message.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec->setAppendP(0);			// Setup vec for appending.
 | 
				
			||||||
 | 
						vec->appendByte(nstype);	// First byte of message is NS PDUType.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (nstype) {
 | 
				
			||||||
 | 
						case NSPDUType::NS_RESET:
 | 
				
			||||||
 | 
							NsAddCause(vec,cause);
 | 
				
			||||||
 | 
							NsAddVCI(vec);
 | 
				
			||||||
 | 
							NsAddNSEI(vec);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NSPDUType::NS_RESET_ACK:
 | 
				
			||||||
 | 
							NsAddVCI(vec);
 | 
				
			||||||
 | 
							NsAddNSEI(vec);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NSPDUType::NS_BLOCK:
 | 
				
			||||||
 | 
							NsAddCause(vec,cause);
 | 
				
			||||||
 | 
							NsAddVCI(vec);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NSPDUType::NS_BLOCK_ACK:
 | 
				
			||||||
 | 
							NsAddVCI(vec);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NSPDUType::NS_UNBLOCK:
 | 
				
			||||||
 | 
							break;	// 1 byte messages is finished.
 | 
				
			||||||
 | 
						case NSPDUType::NS_UNBLOCK_ACK:
 | 
				
			||||||
 | 
							break;	// 1 byte messages is finished.
 | 
				
			||||||
 | 
						case NSPDUType::NS_STATUS:
 | 
				
			||||||
 | 
							NsAddCause(vec,cause);
 | 
				
			||||||
 | 
							switch (cause) {
 | 
				
			||||||
 | 
								case NsCause::NSVCBlocked:
 | 
				
			||||||
 | 
								case NsCause::NSVCUnknown:
 | 
				
			||||||
 | 
									NsAddVCI(vec);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case NsCause::SemanticallyIncorrectPDU:
 | 
				
			||||||
 | 
								case NsCause::PduNotCompatible:
 | 
				
			||||||
 | 
								case NsCause::ProtocolError:
 | 
				
			||||||
 | 
								case NsCause::InvalidEssentialIE:
 | 
				
			||||||
 | 
								case NsCause::MissingEssentialIE:
 | 
				
			||||||
 | 
									// unimplemented.
 | 
				
			||||||
 | 
									assert(0);	// In these cases need to append PDU.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case NsCause::BVCIUnknown:
 | 
				
			||||||
 | 
									// We dont really know what the cause was,
 | 
				
			||||||
 | 
									// and this wont happen, so just make up a BVCI
 | 
				
			||||||
 | 
									NsAddBVCI(vec,BVCI::PTP);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case NsCause::TransitNetworkFailure:
 | 
				
			||||||
 | 
								case NsCause::OAndMIntervention:
 | 
				
			||||||
 | 
								case NsCause::EquipmentFailure:
 | 
				
			||||||
 | 
									break;	// nothing more needed.
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NSPDUType::NS_ALIVE:
 | 
				
			||||||
 | 
							break;	// 1 byte messages is finished.
 | 
				
			||||||
 | 
						case NSPDUType::NS_ALIVE_ACK:
 | 
				
			||||||
 | 
							break;	// 1 byte messages is finished.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case NSPDUType::NS_UNITDATA:
 | 
				
			||||||
 | 
							assert(0);		// Not handled by this routine.
 | 
				
			||||||
 | 
						default: assert(0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return vec;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void BVCAddBVCI(ByteVector *vec, BSPDUType::type bstype)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec->appendByte(IEIType::BVCI);
 | 
				
			||||||
 | 
						vec->appendLI(2);
 | 
				
			||||||
 | 
						vec->appendUInt16(BSPDUType::LookupBVCI(bstype));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void BVCAddCause(ByteVector *vec, int cause)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec->appendByte(IEIType::Cause);
 | 
				
			||||||
 | 
						vec->appendLI(1);
 | 
				
			||||||
 | 
						vec->appendByte(cause);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void BVCAddTag(ByteVector *vec, int tag)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec->appendByte(IEIType::Tag);
 | 
				
			||||||
 | 
						vec->appendLI(1);
 | 
				
			||||||
 | 
						vec->appendByte(tag);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM 08.18 10.4.12 and 11.3.9
 | 
				
			||||||
 | 
					static void BVCAddCellIdentifier(ByteVector *vec)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						vec->appendByte(IEIType::CellIdentifier);
 | 
				
			||||||
 | 
						vec->appendLI(8);
 | 
				
			||||||
 | 
						// Add Routing Area Identification IE from GSM 04.08 10.5.5.15, excluding IEI type and length bytes.
 | 
				
			||||||
 | 
						// Another fine example of Object Oriented programming preventing sharing of code:
 | 
				
			||||||
 | 
						// this information is wrapped up in the middle of an inapplicable class hierarchy.
 | 
				
			||||||
 | 
						const char*mMCC = gConfig.getStr("GSM.Identity.MCC").c_str();
 | 
				
			||||||
 | 
						const char*mMNC = gConfig.getStr("GSM.Identity.MNC").c_str();
 | 
				
			||||||
 | 
						unsigned mLAC = gConfig.getNum("GSM.Identity.LAC");
 | 
				
			||||||
 | 
						unsigned mRAC = gConfig.getNum("GPRS.RAC");
 | 
				
			||||||
 | 
						vec->appendByte(((mMCC[1]-'0')<<4) | (mMCC[0]-'0'));	// MCC digit 2, MCC digit 1
 | 
				
			||||||
 | 
						vec->appendByte(((mMNC[2]-'0')<<4) | (mMCC[2]-'0'));	// MNC digit 3, MCC digit 3
 | 
				
			||||||
 | 
						vec->appendByte(((mMNC[1]-'0')<<4) | (mMNC[0]-'0')); // MNC digit 2, MNC digit 1
 | 
				
			||||||
 | 
						vec->appendUInt16(mLAC);
 | 
				
			||||||
 | 
						vec->appendByte(mRAC);
 | 
				
			||||||
 | 
						// Add Routing Area Identification IE from GSM 04.08 10.5.5.15, excluding IEI type and length bytes.
 | 
				
			||||||
 | 
						// Add Cell Identity IE GSM 04.08 10.5.1.1, excluding IEI type
 | 
				
			||||||
 | 
						unsigned mCI = gConfig.getNum("GSM.Identity.CI");
 | 
				
			||||||
 | 
						vec->appendUInt16(mCI);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM 08.18 sec 10 describes the PDU messages that the SGSN can send to the BSS.
 | 
				
			||||||
 | 
					// Cause values: 08.18 sec 11.3.8
 | 
				
			||||||
 | 
					BSSGUplinkMsg *BVCFactory(BSPDUType::type bstype,
 | 
				
			||||||
 | 
						int arg1)	// For reset, the bvci to reset; for others may be cause or tag .
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BSSGUplinkMsg *vec = new BSSGUplinkMsg(80);	// Big enough for any message.
 | 
				
			||||||
 | 
						BVCI::type bvci;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vec->setAppendP(0);			// Setup vec for appending.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Add the NS header.
 | 
				
			||||||
 | 
						vec->appendByte(NSPDUType::NS_UNITDATA);
 | 
				
			||||||
 | 
						vec->appendByte(0);	// unused byte.
 | 
				
			||||||
 | 
						vec->appendUInt16(gBSSG.mbsNSEI);
 | 
				
			||||||
 | 
						// Add the BSSG message type
 | 
				
			||||||
 | 
						vec->appendByte((ByteType)bstype);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (bstype) {
 | 
				
			||||||
 | 
						case BSPDUType::BVC_RESET:
 | 
				
			||||||
 | 
							// See GSM 08.18 sec 8.4: BVC-RESET procedure; and 10.4.12: BVC-RESET message.
 | 
				
			||||||
 | 
							bvci = (BVCI::type) arg1;
 | 
				
			||||||
 | 
							vec->appendByte(IEIType::BVCI);
 | 
				
			||||||
 | 
							vec->appendLI(2);
 | 
				
			||||||
 | 
							vec->appendUInt16(bvci);
 | 
				
			||||||
 | 
							BVCAddCause(vec,0x8);	// Cause 8: O&M Intervention
 | 
				
			||||||
 | 
							if (bvci != BVCI::SIGNALLING) {
 | 
				
			||||||
 | 
								BVCAddCellIdentifier(vec);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// We dont use the feature bitmap.
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case BSPDUType::BVC_RESET_ACK:
 | 
				
			||||||
 | 
							BVCAddBVCI(vec,bstype);
 | 
				
			||||||
 | 
							// There could be a cell identifier
 | 
				
			||||||
 | 
							// There coulde be a feature bitmap.
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case BSPDUType::BVC_BLOCK:
 | 
				
			||||||
 | 
							BVCAddBVCI(vec,bstype);
 | 
				
			||||||
 | 
							BVCAddCause(vec,arg1);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case BSPDUType::BVC_BLOCK_ACK:	// fall through
 | 
				
			||||||
 | 
						case BSPDUType::BVC_UNBLOCK:	// fall through
 | 
				
			||||||
 | 
						case BSPDUType::BVC_UNBLOCK_ACK:
 | 
				
			||||||
 | 
							BVCAddBVCI(vec,bstype);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case BSPDUType::FLOW_CONTROL_BVC_ACK:
 | 
				
			||||||
 | 
							BVCAddTag(vec,arg1);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default: assert(0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return vec;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Length Indicator GSM 08.16 10.1.2
 | 
				
			||||||
 | 
					// And I quote:
 | 
				
			||||||
 | 
					//		"The BSS or SGSN shall not consider the presence of octet 2a in a received IE
 | 
				
			||||||
 | 
					// 		as an error when the IE is short enough for the length to be coded
 | 
				
			||||||
 | 
					// 		in octet 2 only."
 | 
				
			||||||
 | 
					// (pat) If the length is longer than 127, it is written simply
 | 
				
			||||||
 | 
					// as a 16 bit number in network order, which is high byte first,
 | 
				
			||||||
 | 
					// so the upper most bit is 0 if the value is <= 32767
 | 
				
			||||||
 | 
					static unsigned IEILength(unsigned int len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (len < 127) return 0x80 + len;
 | 
				
			||||||
 | 
						assert(0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void NSMsg::textNSHeader(std::ostream&os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int nstype = (int)getNSPDUType();
 | 
				
			||||||
 | 
						if (nstype == NSPDUType::NS_UNITDATA) {
 | 
				
			||||||
 | 
							os <<"NSPDUType="<<nstype <<"="<<NSPDUType::name(nstype)<<" BVCI="<<getUInt16(2);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							os <<"NSPDUType="<<nstype <<"="<<NSPDUType::name(nstype);
 | 
				
			||||||
 | 
							// The rest of the message is not interesting to us, so dont bother.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void NSMsg::text(std::ostream&os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os <<" NSMsg:"; textNSHeader(os);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BSSGMsg::text(std::ostream&os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						textNSHeader(os);
 | 
				
			||||||
 | 
						os << " BSPDUType="<<getPDUType() <<" size="<<size();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string BSSGMsg::briefDescription() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return BSPDUType::name(getPDUType());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BSSGMsgULUnitData::BSSGMsgULUnitData(unsigned wLen,	// Length of the PDU to be sent.
 | 
				
			||||||
 | 
						uint32_t wTLLI)
 | 
				
			||||||
 | 
						: BSSGUplinkMsg(wLen + HeaderLength)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						QoSProfile qos;	// Both we and the SGSN ignore the contents of this.
 | 
				
			||||||
 | 
										// Note there is a different QoS format included in
 | 
				
			||||||
 | 
										// PDP Context activation messages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setAppendP(0);			// Setup vec for appending.
 | 
				
			||||||
 | 
						// Write the NS header.
 | 
				
			||||||
 | 
						appendByte(NSPDUType::NS_UNITDATA);
 | 
				
			||||||
 | 
						appendByte(0);	// unused byte.
 | 
				
			||||||
 | 
						// The NS UNITDATA message puts the BVCI in the NS header where the NSEI normally goes.
 | 
				
			||||||
 | 
						appendUInt16(gBSSG.mbsBVCI);
 | 
				
			||||||
 | 
						// End of NS Header, start of BSSG message.
 | 
				
			||||||
 | 
						appendByte(BSPDUType::UL_UNITDATA);
 | 
				
			||||||
 | 
						appendUInt32(wTLLI);
 | 
				
			||||||
 | 
						qos.qosAppend(this);
 | 
				
			||||||
 | 
						BVCAddCellIdentifier(this);
 | 
				
			||||||
 | 
						// We need a two byte alignment IEI to get to a 32 bit boundary.
 | 
				
			||||||
 | 
						appendByte(IEIType::AlignmentOctets);
 | 
				
			||||||
 | 
						appendByte(IEILength(0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The PDU IEI itself comes next.
 | 
				
			||||||
 | 
						appendByte(IEIType::LLCPDU);
 | 
				
			||||||
 | 
						// We are allowed to use a 16 bit length IEI even for elements
 | 
				
			||||||
 | 
						// less than 128 bytes long, so we will.
 | 
				
			||||||
 | 
						mLengthPosition = size();		// Where the length goes.
 | 
				
			||||||
 | 
						appendUInt16(0);	// A spot for the length.
 | 
				
			||||||
 | 
						// Followed by the PDU data.
 | 
				
			||||||
 | 
						assert(size() == HeaderLength);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Call this when the PDU is finished to set the length in the BSSG header.
 | 
				
			||||||
 | 
					void BSSGMsgULUnitData::setLength()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// The PDU begins immediately after the 2 byte length.
 | 
				
			||||||
 | 
						assert(size() >= HeaderLength);
 | 
				
			||||||
 | 
						unsigned pdulen = size() - HeaderLength;
 | 
				
			||||||
 | 
						GPRSLOG(1) << "setLength:"<<LOGVAR(pdulen) << LOGVAR(mLengthPosition) << LOGVAR(size());
 | 
				
			||||||
 | 
						setUInt16(mLengthPosition,pdulen);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BSSGMsgULUnitData::text(std::ostream&os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os <<"BSSGMsgULUnitData=("; 
 | 
				
			||||||
 | 
						os <<"tbf=TBF#"<<mTBFId<<" ";
 | 
				
			||||||
 | 
						BSSGUplinkMsg::text(os);
 | 
				
			||||||
 | 
						unsigned TLLI = getTLLI();
 | 
				
			||||||
 | 
						os << LOGHEX(TLLI);
 | 
				
			||||||
 | 
						// The payload is 0 length only during debugging when we send in empty messages.
 | 
				
			||||||
 | 
						ByteVector payload(tail(HeaderLength));
 | 
				
			||||||
 | 
						if (payload.size()) {
 | 
				
			||||||
 | 
							//GPRS::LLCFrame llcmsg(*const_cast<BSSGMsgULUnitData*>(this));
 | 
				
			||||||
 | 
							os << " LLC UL payload="<<payload;
 | 
				
			||||||
 | 
							SGSN::LlcFrameDump llcmsg(payload);
 | 
				
			||||||
 | 
							os << " ";
 | 
				
			||||||
 | 
							llcmsg.text(os);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						//os << " payload=" <<payload;
 | 
				
			||||||
 | 
						os <<")";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string BSSGMsgDLUnitData::briefDescription() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::ostringstream ss;
 | 
				
			||||||
 | 
						if (mbdPDU.size()) {
 | 
				
			||||||
 | 
							SGSN::LlcFrameDump llcmsg(mbdPDU);
 | 
				
			||||||
 | 
							llcmsg.textContent(ss,false);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ss.str();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void BSSGMsgDLUnitData::text(std::ostream &os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << "BSSGMsgDLUnitData:";
 | 
				
			||||||
 | 
						BSSGDownlinkMsg::text(os);
 | 
				
			||||||
 | 
						os << " BSPDUType="<<getPDUType();
 | 
				
			||||||
 | 
						os<<LOGHEX(mbdTLLI) <<LOGVAR(mbdPDULifetime);
 | 
				
			||||||
 | 
						os <<" QoS skipped"; // Skip qos for now.
 | 
				
			||||||
 | 
						if (mbdHaveOldTLLI) { os << LOGHEX(mbdOldTLLI); }
 | 
				
			||||||
 | 
						os <<" RACap=(" <<mbdRACap <<")";
 | 
				
			||||||
 | 
						os <<" IMSI=(" <<mbdIMSI <<")";
 | 
				
			||||||
 | 
						//os <<" PDU=(" <<mbdPDU <<")";
 | 
				
			||||||
 | 
						if (mbdPDU.size()) {
 | 
				
			||||||
 | 
							os << " LLC DL payload="<<mbdPDU;
 | 
				
			||||||
 | 
							SGSN::LlcFrameDump llcmsg(mbdPDU);
 | 
				
			||||||
 | 
							os << " ";
 | 
				
			||||||
 | 
							llcmsg.text(os);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						os <<"\n";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										523
									
								
								GPRS/BSSGMessages.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										523
									
								
								GPRS/BSSGMessages.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,523 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef BSSGMESSAGES_H
 | 
				
			||||||
 | 
					#define BSSGMESSAGES_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This file includes both the NS and BSSG layer messages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For a downlink PDU, we know the size when we receive it.
 | 
				
			||||||
 | 
					// For an uplink data PDU, which comes from RLCEngine, we will allocate the
 | 
				
			||||||
 | 
					// maximum size right off the bat, and let the RLCEngine write the data
 | 
				
			||||||
 | 
					// directly into it.  There is no real reason to downsize it; these dont last long.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GPRS { extern unsigned GPRSDebug; };
 | 
				
			||||||
 | 
					#include "GPRSExport.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Defines.h"
 | 
				
			||||||
 | 
					#include "ScalarTypes.h"
 | 
				
			||||||
 | 
					#include "ByteVector.h"
 | 
				
			||||||
 | 
					#include "Utils.h"
 | 
				
			||||||
 | 
					//#ifndef OFFSETOF	// This is a standard definition in many header files.
 | 
				
			||||||
 | 
					//#define OFFSETOF(type, field) ((int) ((char *) &((type *) 0)->field))
 | 
				
			||||||
 | 
					//#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace BSSG {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BVCI defined GSM 08.18 5.4.1
 | 
				
			||||||
 | 
					// See table 5.4 for which BVCI to use with each message.
 | 
				
			||||||
 | 
					// The SIGNALLING and PTP numbers are reserved.
 | 
				
			||||||
 | 
					// See LookupBVCI(BSPDUType::type bstype);
 | 
				
			||||||
 | 
					class BVCI {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						enum type {
 | 
				
			||||||
 | 
							SIGNALLING = 0,	// For BSSG BVC messages other than data.
 | 
				
			||||||
 | 
							PTM = 1,	// Point to multipoint
 | 
				
			||||||
 | 
							PTP = 2		// Any other value is a base station designator for Point-To-Point data.
 | 
				
			||||||
 | 
										// We only use one value, gBSSG.mbsBVCI, which must be >- PTP.
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**********************************************************
 | 
				
			||||||
 | 
					  BSSGSP Messages we need to support eventually:
 | 
				
			||||||
 | 
					    DL-UNITDATA
 | 
				
			||||||
 | 
					      Includes PDU type (DL-UNITDATA), TLLI, QoS Profile, PDU Lifetime, PDU.
 | 
				
			||||||
 | 
					      optional: IMSI, oldTLLI, PFI (Packet Flow Identifier), etc.
 | 
				
			||||||
 | 
					    UL-UNITDATA
 | 
				
			||||||
 | 
					      Includes PDU type (UL-UNITDATA), TLLI, BVCI, Cell Identifier, PDU.
 | 
				
			||||||
 | 
					    GMM-PAGING-PS/GMM-PAGING-CS (for packet or voice)
 | 
				
			||||||
 | 
					      Includes PDU type (PAGING-PS), QoS Profile, P-TMSI <or> IMSI.
 | 
				
			||||||
 | 
					      Note: If TLLI is specified and already exists within a Radio Context in BSS
 | 
				
			||||||
 | 
					      [because MS has communicated previously] it is used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      BVCI <or> Location Area <or> Routing Area <or> BSSArea Indication
 | 
				
			||||||
 | 
					      Optional: P-TMSI, BVCI, Location area, Routing area.
 | 
				
			||||||
 | 
					    GMM-RA-CAPABILITY, GMM-RA-CAPABILITY-UPDATE
 | 
				
			||||||
 | 
					      Astonishingly, the BSS asks the SGSN for this info.
 | 
				
			||||||
 | 
						  It is because the MS may be moving from BTS to BTS, so the SGSN
 | 
				
			||||||
 | 
						  is a slightly more permanent repository, and makes handover easier between BTSs.
 | 
				
			||||||
 | 
						  But note that the MS can also travel from SGSN to SGSN, so I think the location
 | 
				
			||||||
 | 
						  of the MS info is arbitrary.
 | 
				
			||||||
 | 
						  We are talking about information that is only kept around a short while anyway.
 | 
				
			||||||
 | 
					    GMM-RADIO-STATUS
 | 
				
			||||||
 | 
					    GMM-SUSPEND
 | 
				
			||||||
 | 
					    GMM-RESUME
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 Note that there are alot of messages to control the data-rate on the BSSG connection.
 | 
				
			||||||
 | 
					 None of them are implemented in the SGSN, so we dont support them either.
 | 
				
			||||||
 | 
					**********************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BSPDUType {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// GSM08.18 sec11.3.26 table11.27: PDU Types
 | 
				
			||||||
 | 
						// GSM08.18 table 5.4 defines the BVCI to be used with each message.
 | 
				
			||||||
 | 
						// BVCIs are: PTP, PTM, SIG
 | 
				
			||||||
 | 
						enum type {
 | 
				
			||||||
 | 
							// PDUs between RL and BSSGP SAPs:
 | 
				
			||||||
 | 
							DL_UNITDATA = 0,	// PTP network->MS
 | 
				
			||||||
 | 
							UL_UNITDATA = 1,	// PTP MS->network
 | 
				
			||||||
 | 
							// PDUs between GMM SAPs.
 | 
				
			||||||
 | 
							RA_CAPABILITY = 2,	// PTP network->BSS
 | 
				
			||||||
 | 
							PTM_UNITDATA = 3,	// PTM not currently used
 | 
				
			||||||
 | 
							// PDUs between GMM SAPs:
 | 
				
			||||||
 | 
							PAGING_PS = 6,	// PTP or SIG network->BSS request to page MS for packet connection.
 | 
				
			||||||
 | 
							PAGING_CS = 7,	// PTP or SIG network->BSS request to page MS for RR connection.
 | 
				
			||||||
 | 
							RA_CAPABILITY_UPDATE = 8,	// PTP BSS->network request for MS Radio Access Capabilities.
 | 
				
			||||||
 | 
							RA_CAPABILITY_UPDATE_ACK = 9,// PTP network->BSS Radio Access Capability and IMSI.
 | 
				
			||||||
 | 
							RADIO_STATUS = 0x0a,	// PTP BSS->SGSN notification of error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							SUSPEND = 0x0b,			// SIG MS->network request to suspend GPRS service.
 | 
				
			||||||
 | 
							SUSPEND_ACK = 0x0c,		// SIG network->MS ACK
 | 
				
			||||||
 | 
							SUSPEND_NACK = 0x0d,	// SIG network->MS NACK
 | 
				
			||||||
 | 
							RESUME = 0xe,			// SIG MS->network request to resume GPRS service.
 | 
				
			||||||
 | 
							RESUME_ACK = 0xf,		// SIG network->MS ACK
 | 
				
			||||||
 | 
							RESUME_NACK = 0x10,		// SIG network->MS NACK
 | 
				
			||||||
 | 
							// PDUs between NM SAPs:
 | 
				
			||||||
 | 
							// We will not use the flow control stuff, block, unblock, etc.
 | 
				
			||||||
 | 
							BVC_BLOCK = 0x20,		// SIG
 | 
				
			||||||
 | 
							BVC_BLOCK_ACK = 0x21,	// SIG
 | 
				
			||||||
 | 
							BVC_RESET = 0x22,		// SIG network->BSS request reset everything.
 | 
				
			||||||
 | 
							BVC_RESET_ACK = 0x23,	// SIG BSS->network and network->BSS?
 | 
				
			||||||
 | 
							BVC_UNBLOCK = 0x24,		// SIG
 | 
				
			||||||
 | 
							BVC_UNBLOCK_ACK = 0x25,		// SIG
 | 
				
			||||||
 | 
							FLOW_CONTROL_BVC = 0x26,	// PTP BSS->network inform maximum throughput on Gb I/F
 | 
				
			||||||
 | 
							FLOW_CONTROL_BVC_ACK = 0x27, // PTP network->BSS
 | 
				
			||||||
 | 
							FLOW_CONTROL_MS = 0x28,		// PTP BSS->network inform maximum throughput for MS.
 | 
				
			||||||
 | 
							FLOW_CONTROL_MS_ACK = 0x29,	// PTP network->BSS
 | 
				
			||||||
 | 
							FLUSH_LL = 0x2a,		// SIG network->BSS forget this MS (it moved to another cell.)
 | 
				
			||||||
 | 
							FLUSH_LL_ACK = 0x2b,	// SIG BSS->network
 | 
				
			||||||
 | 
							LLC_DISCARDED = 0x2c,	// SIG BSS->network notification of lost PDUs (probably expired)
 | 
				
			||||||
 | 
							// We ignore all these:
 | 
				
			||||||
 | 
							SGSN_INVOKE_TRACE = 0x40,	// network->BSS request trace an MS
 | 
				
			||||||
 | 
							STATUS = 0x41,			// SIG BSS->network or network->BSS report error condition.
 | 
				
			||||||
 | 
							DOWNLOAD_BSS_PFC = 0x50,	// PTP
 | 
				
			||||||
 | 
							CREATE_BSS_PFC = 0x51,	// PTP
 | 
				
			||||||
 | 
							CREATE_BSS_PFC_ACK = 0x52,	// PTP
 | 
				
			||||||
 | 
							CREATE_BSS_PFC_NACK = 0x53,	// PTP
 | 
				
			||||||
 | 
							MODIFY_BSS_PFC = 0x54,	// PTP
 | 
				
			||||||
 | 
							MODIFY_BSS_PFC_ACK = 0x55,	// PTP
 | 
				
			||||||
 | 
							DELETE_BSS_PFC = 0x56,	// PTP
 | 
				
			||||||
 | 
							DELETE_BSS_PFC_ACK = 0x57 	// PTP
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						static const char *name(int val);
 | 
				
			||||||
 | 
						static const unsigned LookupBVCI(BSPDUType::type bstype);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const BSPDUType::type val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NsIEIType {
 | 
				
			||||||
 | 
						// GSM08.18 sec10.3 NS protocol IEI Types.
 | 
				
			||||||
 | 
						// The NS protocol doesnt do much.   It specifies a procedure
 | 
				
			||||||
 | 
						// to make sure the link is alive, to reset it after failure,
 | 
				
			||||||
 | 
						// and to turn the entire link on and off (block/unblock.)
 | 
				
			||||||
 | 
						public: enum type {
 | 
				
			||||||
 | 
							IEINsCause,
 | 
				
			||||||
 | 
							IEINsVCI,
 | 
				
			||||||
 | 
							IEINsPDU,
 | 
				
			||||||
 | 
							IEINsBVCI,
 | 
				
			||||||
 | 
							IEINsNSEI,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NsCause {
 | 
				
			||||||
 | 
						public: enum type {
 | 
				
			||||||
 | 
							TransitNetworkFailure,
 | 
				
			||||||
 | 
							OAndMIntervention,
 | 
				
			||||||
 | 
							EquipmentFailure,
 | 
				
			||||||
 | 
							NSVCBlocked,
 | 
				
			||||||
 | 
							NSVCUnknown,
 | 
				
			||||||
 | 
							BVCIUnknown,
 | 
				
			||||||
 | 
							SemanticallyIncorrectPDU = 8,
 | 
				
			||||||
 | 
							PduNotCompatible = 10,
 | 
				
			||||||
 | 
							ProtocolError = 11,
 | 
				
			||||||
 | 
							InvalidEssentialIE = 12,
 | 
				
			||||||
 | 
							MissingEssentialIE = 13
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class IEIType {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// GSM08.18 sec11.3 table11.1: IEI Types
 | 
				
			||||||
 | 
						enum type {
 | 
				
			||||||
 | 
							AlignmentOctets = 0x00,
 | 
				
			||||||
 | 
							BmaxDefaultMS = 0x01,
 | 
				
			||||||
 | 
							BSSAreaIndication = 0x02,
 | 
				
			||||||
 | 
							BucketLeakRate = 0x03,
 | 
				
			||||||
 | 
							BVCI = 0x04,
 | 
				
			||||||
 | 
							BVCBucketSize = 0x05,
 | 
				
			||||||
 | 
							BVCMeasurement = 0x06,
 | 
				
			||||||
 | 
							Cause = 0x07,
 | 
				
			||||||
 | 
							CellIdentifier = 0x08,
 | 
				
			||||||
 | 
							ChannelNeeded = 0x09,
 | 
				
			||||||
 | 
							DRXParameters = 0x0a,
 | 
				
			||||||
 | 
							eMLPPPriority = 0x0b,
 | 
				
			||||||
 | 
							FlushAction = 0x0c,
 | 
				
			||||||
 | 
							IMSI = 0x0d,
 | 
				
			||||||
 | 
							LLCPDU = 0x0e,
 | 
				
			||||||
 | 
							LLCFramesDiscarded = 0x0f,
 | 
				
			||||||
 | 
							LocationArea = 0x10,
 | 
				
			||||||
 | 
							MobileId = 0x11,
 | 
				
			||||||
 | 
							MSBucketSize = 0x12,
 | 
				
			||||||
 | 
							MSRadioAccessCapability = 0x13,
 | 
				
			||||||
 | 
							OMCId = 0x14,
 | 
				
			||||||
 | 
							PDUInError = 0x15,
 | 
				
			||||||
 | 
							PDULifetime = 0x16,
 | 
				
			||||||
 | 
							Priority = 0x17,
 | 
				
			||||||
 | 
							QoSProfile = 0x18,
 | 
				
			||||||
 | 
							RadioCause = 0x19,
 | 
				
			||||||
 | 
							RACapUPDCause = 0x1a,
 | 
				
			||||||
 | 
							RouteingArea = 0x1b,
 | 
				
			||||||
 | 
							RDefaultMS = 0x1c,
 | 
				
			||||||
 | 
							SuspendReferenceNumber = 0x1d,
 | 
				
			||||||
 | 
							Tag = 0x1e,
 | 
				
			||||||
 | 
							TLLI = 0x1f,
 | 
				
			||||||
 | 
							TMSI = 0x20,
 | 
				
			||||||
 | 
							TraceReference = 0x21,
 | 
				
			||||||
 | 
							TraceType = 0x22,
 | 
				
			||||||
 | 
							TransactionId = 0x23,
 | 
				
			||||||
 | 
							TriggerId = 0x24,
 | 
				
			||||||
 | 
							NumberOfOctetsAffected = 0x25,
 | 
				
			||||||
 | 
							LSAIdentifierList = 0x26,
 | 
				
			||||||
 | 
							LSAInformation = 0x27,
 | 
				
			||||||
 | 
							PacketFlowIdentifier = 0x28,
 | 
				
			||||||
 | 
							PacketFlowTimer = 0x29,
 | 
				
			||||||
 | 
							AggregateBSSQoSProfile = 0x3a, // (ABQP) 
 | 
				
			||||||
 | 
							FeatureBitmap = 0x3b,
 | 
				
			||||||
 | 
							BucketFullRatio = 0x3c,
 | 
				
			||||||
 | 
							ServiceUTRANCCO = 0x3d			// (Cell Change Order) 
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						static const char *name(int val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const IEIType::type val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Notes:
 | 
				
			||||||
 | 
					// BVC = BSSG Virtual Connection
 | 
				
			||||||
 | 
					// NS SDU = the BSSG data packet transmitted over NS.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM08.16 sec 10.3.7: Network Service PDU Type
 | 
				
			||||||
 | 
					class NSPDUType {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						enum type {
 | 
				
			||||||
 | 
							NS_UNITDATA = 0,
 | 
				
			||||||
 | 
							NS_RESET = 2,
 | 
				
			||||||
 | 
							NS_RESET_ACK = 3,
 | 
				
			||||||
 | 
							NS_BLOCK = 4,
 | 
				
			||||||
 | 
							NS_BLOCK_ACK = 5,
 | 
				
			||||||
 | 
							NS_UNBLOCK = 6,
 | 
				
			||||||
 | 
							NS_UNBLOCK_ACK = 7,
 | 
				
			||||||
 | 
							NS_STATUS = 8,
 | 
				
			||||||
 | 
							NS_ALIVE = 10,
 | 
				
			||||||
 | 
							NS_ALIVE_ACK = 11
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						static const char *name(int val);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const NSPDUType::type val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM08.16 sec 9.2.10
 | 
				
			||||||
 | 
					struct RN_PACKED NSUnitDataHeader {
 | 
				
			||||||
 | 
						ByteType mNSPDUType;
 | 
				
			||||||
 | 
						ByteType mUnused;
 | 
				
			||||||
 | 
						ByteType mBVCI[2];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NSMsg : public ByteVector, public Utils::Text2Str
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// This is the NS header length only for NS_UNIT_DATA messages,
 | 
				
			||||||
 | 
						// which are the only ones we care about because they are the
 | 
				
			||||||
 | 
						// only ones that have BSSG and potentially user data in them.
 | 
				
			||||||
 | 
						static const unsigned UnitDataHeaderLen = sizeof(struct NSUnitDataHeader);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// wlen should include the NS header + BSSG header + data
 | 
				
			||||||
 | 
						NSMsg(ByteType *wdata, int wlen) // Make one from downlink data.
 | 
				
			||||||
 | 
							: ByteVector(wdata,wlen)
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// wlen should include the NS header + BSSG header + data
 | 
				
			||||||
 | 
						NSMsg(int wlen) 		// Make one for uplink data.
 | 
				
			||||||
 | 
							: ByteVector(wlen + NSMsg::UnitDataHeaderLen)	// But we will add it in anyway
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Zero out the NS header; the type will be filled in later.
 | 
				
			||||||
 | 
							fill(0,0,NSMsg::UnitDataHeaderLen);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Make a new message from some other, taking over the ByteVector.
 | 
				
			||||||
 | 
						// Used to change the type of a BSSG message.
 | 
				
			||||||
 | 
						NSMsg(NSMsg *src) : ByteVector(*src) {
 | 
				
			||||||
 | 
							//assert(src->isOwner());
 | 
				
			||||||
 | 
							//move(*src); 	// Grab the memory from src.
 | 
				
			||||||
 | 
							//assert(!src->isOwner());	no longer true with refcnts.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Passify the brain-dead compiler:
 | 
				
			||||||
 | 
					#define NSMsgConstructors(type1,type2) \
 | 
				
			||||||
 | 
						type1(ByteType *data, int len) : type2(data,len) {} \
 | 
				
			||||||
 | 
						type1(int wlen) : type2(wlen) {} \
 | 
				
			||||||
 | 
						type1(NSMsg *src) : type2(src) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Fields in the 4 byte NS Header:
 | 
				
			||||||
 | 
						void setNSPDUType(NSPDUType::type nstype) { setByte(0,nstype); }
 | 
				
			||||||
 | 
						NSPDUType::type getNSPDUType() const { return (NSPDUType::type) getByte(0); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void textNSHeader(std::ostream&os) const;
 | 
				
			||||||
 | 
						virtual void text(std::ostream&os) const;
 | 
				
			||||||
 | 
						std::string str() const { return this->Text2Str::str(); } // Disambigute
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BSSGMsg : public NSMsg {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						NSMsgConstructors(BSSGMsg,NSMsg)
 | 
				
			||||||
 | 
						// Common fields in the BSSG Header:
 | 
				
			||||||
 | 
						void setPDUType(BSPDUType::type type) { setByte(NSMsg::UnitDataHeaderLen,(ByteType)type); }
 | 
				
			||||||
 | 
						BSPDUType::type getPDUType() const { return (BSPDUType::type) getByte(NSMsg::UnitDataHeaderLen); }
 | 
				
			||||||
 | 
						virtual void text(std::ostream &os) const;
 | 
				
			||||||
 | 
						virtual std::string briefDescription() const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BSSGUplinkMsgElt {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						//TODO: virtual void text(std::ostream&) const;
 | 
				
			||||||
 | 
						virtual void parseElement(const char *src, size_t &rp);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					class BSSGDownlinkMsgElt {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						//TODO: virtual void text(std::ostream&) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Note that the ByteVector in NSMsg is allocated, and all the other ones in downlink messages
 | 
				
			||||||
 | 
					// are segments referring to this one.
 | 
				
			||||||
 | 
					//class BSSGDownlinkMsg : public NSMsg, public BSSGMsg
 | 
				
			||||||
 | 
					class BSSGDownlinkMsg : public BSSGMsg
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						NSMsgConstructors(BSSGDownlinkMsg,BSSGMsg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtual void text(std::ostream &os) const { BSSGMsg::text(os); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BSSGUplinkMsg : public BSSGMsg
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						NSMsgConstructors(BSSGUplinkMsg,BSSGMsg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtual void text(std::ostream &os) const { BSSGMsg::text(os); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is the QoS Profile in the header, when not including the 2 byte IEI prefix.
 | 
				
			||||||
 | 
					// GSM08.18 sec 11.3.28
 | 
				
			||||||
 | 
					struct QoSProfile {
 | 
				
			||||||
 | 
						// Coded as value part of Bucket Leak Rate 'R' from sec 11.3.4
 | 
				
			||||||
 | 
						// And I quote:
 | 
				
			||||||
 | 
						// The R field is the binary encoding of the rate information expressed in 100 bits/sec
 | 
				
			||||||
 | 
						// increments starting from 0 x 100 bits/sec until 65535 * 100 bits/sec (6Mbps)
 | 
				
			||||||
 | 
						// Note a) Bit Rate 0 means "Best Effort".
 | 
				
			||||||
 | 
						unsigned mPeakBitRate:16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// These are the bits of byte 3.
 | 
				
			||||||
 | 
						unsigned mSpare:2;
 | 
				
			||||||
 | 
						unsigned mCR:1;
 | 
				
			||||||
 | 
						unsigned mT:1;
 | 
				
			||||||
 | 
						unsigned mA:1;
 | 
				
			||||||
 | 
						unsigned mPrecedence:3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ByteType getB3() { return (mCR<<5)|(mT<<4)|(mA<<3)|mPrecedence; }
 | 
				
			||||||
 | 
						void setB3(ByteType bits) {
 | 
				
			||||||
 | 
							mPrecedence = bits & 0x7;
 | 
				
			||||||
 | 
							mA = (bits>>3)&1;
 | 
				
			||||||
 | 
							mT = (bits>>4)&1;
 | 
				
			||||||
 | 
							mCR = (bits>>5)&1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						QoSProfile() {	// Create a default QoSProfile
 | 
				
			||||||
 | 
							mPeakBitRate = 0;
 | 
				
			||||||
 | 
							setB3(0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get from the ByteVector, which is in network order:
 | 
				
			||||||
 | 
						void qosRead(ByteVector &src, size_t &wp) {
 | 
				
			||||||
 | 
							mPeakBitRate = src.getUInt16(wp); wp+=2;
 | 
				
			||||||
 | 
							ByteType byte3 = src.getByte(wp++);
 | 
				
			||||||
 | 
							setB3(byte3);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Write to ByteVector in network order.
 | 
				
			||||||
 | 
						void qosAppend(ByteVector *dest) {
 | 
				
			||||||
 | 
							dest->appendUInt16(mPeakBitRate);
 | 
				
			||||||
 | 
							dest->appendByte(getB3());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM 08.18 sec 10.2.1  From SGSN to BSS.
 | 
				
			||||||
 | 
					class BSSGMsgDLUnitData : public BSSGDownlinkMsg
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// Mandatory elements:
 | 
				
			||||||
 | 
						UInt32_z mbdTLLI;
 | 
				
			||||||
 | 
						UInt16_z mbdPDULifetime;
 | 
				
			||||||
 | 
						QoSProfile mbdQoS;		// 3 bytes
 | 
				
			||||||
 | 
						Bool_z mbdHaveOldTLLI;
 | 
				
			||||||
 | 
						UInt32_z mbdOldTLLI;
 | 
				
			||||||
 | 
						// Optional elements:
 | 
				
			||||||
 | 
						//RLCMsgEltMSRACapabilityValuePart mbdRACap;
 | 
				
			||||||
 | 
						ByteVector mbdRACap;
 | 
				
			||||||
 | 
						ByteVector mbdIMSI;
 | 
				
			||||||
 | 
						ByteVector mbdPDU;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						BSSGMsgDLUnitData(BSSGDownlinkMsg*src) : BSSGDownlinkMsg(src) {
 | 
				
			||||||
 | 
							size_t wp = NSMsg::UnitDataHeaderLen;
 | 
				
			||||||
 | 
							parseDLUnitDataBody(*this,wp);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Parse body, excluding the NS header.
 | 
				
			||||||
 | 
						void parseDLUnitDataBody(ByteVector &src, size_t &wp);
 | 
				
			||||||
 | 
						void text(std::ostream &os) const;
 | 
				
			||||||
 | 
						std::string briefDescription() const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BSSGDownlinkMsg* BSSGDownlinkMessageParse(ByteVector&src);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Routing Area Identification: GSM04.08 sec 10.5.5.15.
 | 
				
			||||||
 | 
					// 6 bytes.  This is just a magic cookie as far as we are concerned.
 | 
				
			||||||
 | 
					struct RN_PACKED RoutingAreaID {
 | 
				
			||||||
 | 
						// First three bytes identify MCC Mobile Country Code and MNC Mobile Network Code.
 | 
				
			||||||
 | 
						// Weird encoding; see spec.
 | 
				
			||||||
 | 
						ByteType mMCCDigits12;	// MCC Digit2, MCC Digit1.
 | 
				
			||||||
 | 
						ByteType mHighDigits;	// MNC Digit3, MCC Digit3.
 | 
				
			||||||
 | 
						ByteType mMNCDigits12;	// MNC Digit2, MNC Digit1.
 | 
				
			||||||
 | 
						unsigned mLAC:16;	// Location Area Code.
 | 
				
			||||||
 | 
						unsigned mRAC:8;	// Routing Area Code.
 | 
				
			||||||
 | 
						RoutingAreaID(unsigned MCC, unsigned MNC, unsigned LAC, unsigned RAC) {
 | 
				
			||||||
 | 
							// TODO, but maybe no one cares.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Cell Identifier: GSM 08.18 sec 11.2.9
 | 
				
			||||||
 | 
					struct RN_PACKED CellIdentifier {
 | 
				
			||||||
 | 
						// Routing Area Identification: GSM 04.08 sec 10.5.5.15.
 | 
				
			||||||
 | 
						uint64_t RoutingAreaIdentification:48;
 | 
				
			||||||
 | 
						// Cell Identity: GSM04.08 sec 10.5.1.1
 | 
				
			||||||
 | 
						// It is just a two byte int whose value determined by the administrator.
 | 
				
			||||||
 | 
						unsigned CellIdentity:16;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct RN_PACKED CellIdentifierIEI {
 | 
				
			||||||
 | 
						ByteType iei;
 | 
				
			||||||
 | 
						ByteType length;
 | 
				
			||||||
 | 
						ByteType RoutingAreaID[6];
 | 
				
			||||||
 | 
						ByteType CellIdentity[2];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is the specific UlUnitData format that we use.
 | 
				
			||||||
 | 
					struct RN_PACKED BSSGMsgULUnitDataHeader {
 | 
				
			||||||
 | 
						NSUnitDataHeader mbuNS;
 | 
				
			||||||
 | 
						ByteType mbuPDUType;
 | 
				
			||||||
 | 
						ByteType mbuTLLI[4];
 | 
				
			||||||
 | 
						ByteType mbuQoS[3];
 | 
				
			||||||
 | 
						ByteType mbuCellIdIEI[10];
 | 
				
			||||||
 | 
						ByteType mbuAlignmentIEI[2];	// spacer required to make size div by 4.
 | 
				
			||||||
 | 
						ByteType mLLCIEIType;
 | 
				
			||||||
 | 
						ByteType mLLCPDULength[2];
 | 
				
			||||||
 | 
						// PDU data starts after this..
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//class BSSGMsgBVCReset {
 | 
				
			||||||
 | 
					//	NSUnitDataHeader mbuNS;
 | 
				
			||||||
 | 
					//};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM 08.18 sec 10.2.2  Packet Data from BSS to SGSN.
 | 
				
			||||||
 | 
					// It could be user data or a GMM or other message from a higher
 | 
				
			||||||
 | 
					// layer in the MS to the SGSN.
 | 
				
			||||||
 | 
					// There are several optional IEIs we do not include.
 | 
				
			||||||
 | 
					// Alignment octets are necessary so the start of the PDU IEI
 | 
				
			||||||
 | 
					// starts on a 32bit boundary, which is totally dopey because the PDU
 | 
				
			||||||
 | 
					// itself is unaligned inside the PDU IEI.  Whatever.
 | 
				
			||||||
 | 
					// This structure is write-only; the internal fields are in network order and
 | 
				
			||||||
 | 
					// we dont need to read them, so we dont.
 | 
				
			||||||
 | 
					// The RLCEngine is the primary producer of these things, and always allocates
 | 
				
			||||||
 | 
					// a maximum size (1502 bytes plus headers) so it can just append the data in.
 | 
				
			||||||
 | 
					// The lifetime of these things is quite short; they live in the BSSG outgoing
 | 
				
			||||||
 | 
					// queue until the service thread sends them to the SGSN.
 | 
				
			||||||
 | 
					class BSSGMsgULUnitData : public BSSGUplinkMsg
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						static const unsigned HeaderLength = sizeof(BSSGMsgULUnitDataHeader);
 | 
				
			||||||
 | 
						int mTBFId;	// For debugging, the associated tbf that processed us.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						BSSGMsgULUnitData(unsigned wLen, uint32_t wTLLI);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Return the pointer to the header within the ByteVector (its just 0)
 | 
				
			||||||
 | 
						// what a horrible language.
 | 
				
			||||||
 | 
						BSSGMsgULUnitDataHeader *getHeader() { return (BSSGMsgULUnitDataHeader*) begin(); }
 | 
				
			||||||
 | 
						BSSGMsgULUnitDataHeader *getHeader() const { return const_cast<BSSGMsgULUnitData*>(this)->getHeader(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//unused: void setBVCI(int wBVCI) { setUInt16(2,wBVCI); }
 | 
				
			||||||
 | 
						unsigned getBVCI() const { return getUInt16(2); }
 | 
				
			||||||
 | 
						//unused: void setTLLI(int wTLLI) { sethtonl(getHeader()->mbuTLLI,wTLLI); }
 | 
				
			||||||
 | 
						int getTLLI() const { return getntohl(getHeader()->mbuTLLI); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ByteVector getPayload() {
 | 
				
			||||||
 | 
							// Now the return value also owns memory.
 | 
				
			||||||
 | 
							//return tail(HeaderLength);
 | 
				
			||||||
 | 
							ByteVector result = *this;	// Increments refcnt.
 | 
				
			||||||
 | 
							result.trimLeft(HeaderLength);
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// We set the length after assembling the complete pdu.
 | 
				
			||||||
 | 
						unsigned mLengthPosition;	// Where the PDU Length goes in the ByteVector.
 | 
				
			||||||
 | 
						void setLength();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void text(std::ostream&os) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Make a short BSSG Protocol signaling message.
 | 
				
			||||||
 | 
					BSSGUplinkMsg *BVCFactory(BSPDUType::type bssgtype, int arg1=0);
 | 
				
			||||||
 | 
					// Make a short NS Protocol message.
 | 
				
			||||||
 | 
					NSMsg *NsFactory(NSPDUType::type nstype, int cause=0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//class BSSGMsgULUnitData : public BSSGUplinkMsg {
 | 
				
			||||||
 | 
					//	unsigned mbPDUType:8;
 | 
				
			||||||
 | 
					//	unsigned mTLLI:32;
 | 
				
			||||||
 | 
					//	QoSProfile mQoS;
 | 
				
			||||||
 | 
					//	CellIdentifierIEI mCellIdentifier;	// 10 bytes
 | 
				
			||||||
 | 
					//};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										669
									
								
								GPRS/ByteVector.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										669
									
								
								GPRS/ByteVector.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,669 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under the terms of the GNU Affero Public License.
 | 
				
			||||||
 | 
					* See the COPYING file in the main directory for details.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
						it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
						the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
						(at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
						but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
						MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
						GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
						along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#include "ByteVector.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Set the char[2] array at ip to a 16-bit int value, swizzling bytes as needed for network order.
 | 
				
			||||||
 | 
					void sethtons(ByteType *cp,unsigned value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint16_t tmp = htons(value);
 | 
				
			||||||
 | 
						ByteType *tp = (ByteType*)&tmp;
 | 
				
			||||||
 | 
						cp[0]=tp[0]; cp[1]=tp[1];	// Overkill but safe.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Set the char[4] array at ip to a 32-bit int value, swizzling bytes as needed for network order.
 | 
				
			||||||
 | 
					void sethtonl(ByteType *cp,unsigned value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint32_t tmp = htonl(value);
 | 
				
			||||||
 | 
						ByteType *tp = (ByteType*)&tmp;
 | 
				
			||||||
 | 
						cp[0]=tp[0]; cp[1]=tp[1]; cp[2]=tp[2]; cp[3]=tp[3];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint16_t getntohs(ByteType *cp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint16_t tmp;
 | 
				
			||||||
 | 
						ByteType *tp = (ByteType*)&tmp;
 | 
				
			||||||
 | 
						tp[0]=cp[0]; tp[1]=cp[1];
 | 
				
			||||||
 | 
						return ntohs(tmp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t getntohl(ByteType *cp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint32_t tmp;
 | 
				
			||||||
 | 
						ByteType *tp = (ByteType*)&tmp;
 | 
				
			||||||
 | 
						tp[0]=cp[0]; tp[1]=cp[1]; tp[2]=cp[2]; tp[3]=cp[3];
 | 
				
			||||||
 | 
						return ntohl(tmp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ByteVector::clear()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (mData) {
 | 
				
			||||||
 | 
					#if BYTEVECTOR_REFCNT
 | 
				
			||||||
 | 
							if (decRefCnt() <= 0) { delete[] mData; RN_MEMCHKDEL(ByteVectorData) }
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
							delete[] mData;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mSizeBits = 0;
 | 
				
			||||||
 | 
						mData = NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ByteVector::init(size_t size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						//mBitInd = 0;
 | 
				
			||||||
 | 
						if (size == 0) {
 | 
				
			||||||
 | 
							mData = mStart = 0;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
					#if BYTEVECTOR_REFCNT
 | 
				
			||||||
 | 
							RN_MEMCHKNEW(ByteVectorData)
 | 
				
			||||||
 | 
							mData = new ByteType[size + mDataOffset];
 | 
				
			||||||
 | 
							setRefCnt(1);
 | 
				
			||||||
 | 
							mStart = mData + mDataOffset;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
							mData = new ByteType[size];
 | 
				
			||||||
 | 
							mStart = mData;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mAllocEnd = mStart + size;
 | 
				
			||||||
 | 
						mSizeBits = size*8;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Make a full memory copy of other.
 | 
				
			||||||
 | 
					// We clone only the filled in area, not the unused allocated area.
 | 
				
			||||||
 | 
					void ByteVector::clone(const ByteVector &other)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						clear();
 | 
				
			||||||
 | 
						init(other.size());
 | 
				
			||||||
 | 
						memcpy(mStart,other.mStart,other.size());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Make this a copy of other.
 | 
				
			||||||
 | 
					// It it owns memory, share it using refcnts.
 | 
				
			||||||
 | 
					// Formerly: moved ownership of allocated data to ourself.
 | 
				
			||||||
 | 
					void ByteVector::dup(const ByteVector &other)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						clear();
 | 
				
			||||||
 | 
						mData=other.mData;
 | 
				
			||||||
 | 
						mStart=other.mStart;
 | 
				
			||||||
 | 
						mSizeBits=other.mSizeBits;
 | 
				
			||||||
 | 
						mAllocEnd = other.mAllocEnd;
 | 
				
			||||||
 | 
						//mBitInd = other.mBitInd;
 | 
				
			||||||
 | 
					#if BYTEVECTOR_REFCNT
 | 
				
			||||||
 | 
						if (mData) incRefCnt();
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						other.mData=NULL;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return a segment of a ByteVector that shares the same memory as the original.
 | 
				
			||||||
 | 
					ByteVector ByteVector::segment(size_t start, size_t span) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#if NEW_SEGMENT_SEMANTICS
 | 
				
			||||||
 | 
						BVASSERT(start+span <= size());
 | 
				
			||||||
 | 
						ByteVector result(*this);
 | 
				
			||||||
 | 
						result.mStart = mStart + start;
 | 
				
			||||||
 | 
						result.mSizeBits = span*8;
 | 
				
			||||||
 | 
						//result.mEnd = result.mStart + span;
 | 
				
			||||||
 | 
						//BVASSERT(result.mEnd<=mEnd);
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						ByteType* wStart = mStart + start;
 | 
				
			||||||
 | 
						ByteType* wEnd = wStart + span;
 | 
				
			||||||
 | 
						BVASSERT(wEnd<=mEnd);
 | 
				
			||||||
 | 
						return ByteVector(wStart,wEnd);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This returns a segment that does not share ownership of the original memory,
 | 
				
			||||||
 | 
					// so when the original is deleted, this is destroyed also, and without warning.
 | 
				
			||||||
 | 
					// Very easy to insert bugs in your code, which is why it is called segmentTemp to indicate
 | 
				
			||||||
 | 
					// that it is a ByteVector for temporary use only.
 | 
				
			||||||
 | 
					const ByteVectorTemp ByteVector::segmentTemp(size_t start, size_t span) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BVASSERT(start+span <= size());
 | 
				
			||||||
 | 
						ByteType* wStart = mStart + start;
 | 
				
			||||||
 | 
						ByteType* wEnd = wStart + span;
 | 
				
			||||||
 | 
						//BVASSERT(wEnd<=mEnd);
 | 
				
			||||||
 | 
						return ByteVectorTemp(wStart,wEnd);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Copy other to this starting at start.
 | 
				
			||||||
 | 
					// The 'this' ByteVector must be allocated large enough to hold other.
 | 
				
			||||||
 | 
					// Unlike Vector, the size() is increased to make it fit, up to the allocated size.
 | 
				
			||||||
 | 
					void ByteVector::setSegment(size_t start, ByteVector&other)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BVASSERT(start <= size());	// If start == size(), nothing is copied.
 | 
				
			||||||
 | 
						BVASSERT(bitind() == 0);	// This function only allowed on byte-aligned data.
 | 
				
			||||||
 | 
						ByteType* base = mStart + start;
 | 
				
			||||||
 | 
						int othersize = other.size();
 | 
				
			||||||
 | 
						BVASSERT(mAllocEnd - base >= othersize);
 | 
				
			||||||
 | 
						memcpy(base,other.mStart,othersize);
 | 
				
			||||||
 | 
						//if (mEnd - base < othersize) { mEnd = base + othersize; }	// Grow size() if necessary.
 | 
				
			||||||
 | 
						if (mSizeBits/8 < start+othersize) { mSizeBits = (start+othersize)*8; }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Copy part of this ByteVector to a segment of another.
 | 
				
			||||||
 | 
					// The specified span must not exceed our size, and it must fit in the target ByteVector.
 | 
				
			||||||
 | 
					// Unlike Vector, the size() of other is increased to make it fit, up to the allocated size.
 | 
				
			||||||
 | 
					void ByteVector::copyToSegment(ByteVector& other, size_t start, size_t span) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ByteType* base = other.mStart + start;
 | 
				
			||||||
 | 
						BVASSERT(start <= other.size());	// If start == size(), nothing is copied.
 | 
				
			||||||
 | 
						BVASSERT(base+span<=other.mAllocEnd);
 | 
				
			||||||
 | 
						//BVASSERT(mStart+span<=mEnd);
 | 
				
			||||||
 | 
						//BVASSERT(base+span<=other.mAllocEnd);
 | 
				
			||||||
 | 
						memcpy(base,mStart,span);
 | 
				
			||||||
 | 
						//if (base+span > other.mEnd) { other.mEnd = base+span; }	// Increase other.size() if necessary.
 | 
				
			||||||
 | 
						if (other.size() < start+span) { other.mSizeBits = (start+span)*8; }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Copy all of this Vector to a segment of another Vector. */
 | 
				
			||||||
 | 
					void ByteVector::copyToSegment(ByteVector& other, size_t start /*=0*/) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						copyToSegment(other,start,size());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ByteVector::append(const ByteType *bytes, unsigned len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						memcpy(&mStart[grow(len)],bytes,len);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Does change size().
 | 
				
			||||||
 | 
					void ByteVector::appendFill(ByteType byte, size_t span)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						memset(&mStart[grow(span)],byte,span);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ByteVector::append(const ByteVector&other)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						append(other.mStart,other.size());
 | 
				
			||||||
 | 
						//BVASSERT(othersize <= mAllocEnd - mEnd);
 | 
				
			||||||
 | 
						//memcpy(mEnd,other.mStart,othersize);
 | 
				
			||||||
 | 
						//mEnd += othersize;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// append a BitVector to this, converting the BitVector back to bytes.
 | 
				
			||||||
 | 
					void ByteVector::append(const BitVector&other)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int othersizebits = other.size();
 | 
				
			||||||
 | 
						int bitindex = bitind();
 | 
				
			||||||
 | 
						if (bitindex) {
 | 
				
			||||||
 | 
							// Heck with it.  Optimize this if you want to use it.
 | 
				
			||||||
 | 
							int iself = growBits(othersizebits);	// index into this.
 | 
				
			||||||
 | 
							int iother = 0;							// index into other
 | 
				
			||||||
 | 
							// First partial byte
 | 
				
			||||||
 | 
							int rem = 8-bitindex;
 | 
				
			||||||
 | 
							if (rem > othersizebits) rem = othersizebits;
 | 
				
			||||||
 | 
							setField(iself,other.peekField(iother,rem),rem);
 | 
				
			||||||
 | 
							iself += rem; iother += rem;
 | 
				
			||||||
 | 
							// Copy whole bytes.
 | 
				
			||||||
 | 
							for (; othersizebits-iother>=8; iother+=8, iself+=8) {
 | 
				
			||||||
 | 
								setByte(iself/8,other.peekField(iother,8));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Final partial byte.
 | 
				
			||||||
 | 
							rem = othersizebits-iother;
 | 
				
			||||||
 | 
							if (rem) {
 | 
				
			||||||
 | 
								setField(iself,other.peekField(iother,rem),rem);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							other.pack(&mStart[growBits(othersizebits)/8]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						//BVASSERT(othersize <= mAllocEnd - mEnd);
 | 
				
			||||||
 | 
						//other.pack(mEnd);
 | 
				
			||||||
 | 
						//mEnd += othersize;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Length Indicator: GSM08.16 sec 10.1.2
 | 
				
			||||||
 | 
					// The length indicator may be 1 or 2 bytes, depending on bit 8,
 | 
				
			||||||
 | 
					// which is 0 to indicate a 15 bit length, or 1 to indicate a 7 bit length.
 | 
				
			||||||
 | 
					unsigned ByteVector::readLI(size_t &wp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned byte1 = getByte(wp++);
 | 
				
			||||||
 | 
						if (byte1 & 0x80) { return byte1 & 0x7f; }
 | 
				
			||||||
 | 
						return (byte1 * 256) + getByte(wp++);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is a two byte length indicator as per GSM 08.16 10.1.2
 | 
				
			||||||
 | 
					void ByteVector::appendLI(unsigned len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (len < 255) {
 | 
				
			||||||
 | 
							appendByte(len | 0x80);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							BVASSERT(len <= 32767);
 | 
				
			||||||
 | 
							appendByte(0x7f&(len>>8));
 | 
				
			||||||
 | 
							appendByte(0xff&(len>>8));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The inverse of trimRight.  Like an append but the new area is uninitialized.
 | 
				
			||||||
 | 
					void ByteVector::growRight(unsigned amt)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BVASSERT(!bitind());
 | 
				
			||||||
 | 
						BVASSERT(amt <= size());
 | 
				
			||||||
 | 
						mSizeBits += 8*amt;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The inverse of trimLeft
 | 
				
			||||||
 | 
					ByteType* ByteVector::growLeft(unsigned amt)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ByteType *newstart = mStart - amt;
 | 
				
			||||||
 | 
						BVASSERT(newstart >= mData + mDataOffset);
 | 
				
			||||||
 | 
						mSizeBits += 8*amt;
 | 
				
			||||||
 | 
						return mStart = newstart;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ByteVector::trimLeft(unsigned amt)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BVASSERT(amt <= size());
 | 
				
			||||||
 | 
						mStart += amt;
 | 
				
			||||||
 | 
						mSizeBits -= 8*amt;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ByteVector::trimRight(unsigned amt)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BVASSERT(!bitind());
 | 
				
			||||||
 | 
						BVASSERT(amt <= size());
 | 
				
			||||||
 | 
						//mEnd -= amt;
 | 
				
			||||||
 | 
						mSizeBits -= 8*amt;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For appending.
 | 
				
			||||||
 | 
					// Grow the vector by the specified amount of bytes and return the index of that location.
 | 
				
			||||||
 | 
					unsigned ByteVector::grow(unsigned amt)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned writeIndex = sizeBytes(); // relative to mStart.
 | 
				
			||||||
 | 
						BVASSERT(bitind() == 0);	// If it is not byte-aligned, cant use these functions; use setField instead.
 | 
				
			||||||
 | 
						setSizeBits(mSizeBits + 8*amt);
 | 
				
			||||||
 | 
						//BVASSERT(amt < sizeRemaining());
 | 
				
			||||||
 | 
						//mSizeBits += 8*amt;
 | 
				
			||||||
 | 
						//unsigned writeIndex = mEnd - mStart;
 | 
				
			||||||
 | 
						//mEnd += amt;
 | 
				
			||||||
 | 
						//BVASSERT(mEnd <= mAllocEnd);
 | 
				
			||||||
 | 
						return writeIndex;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For appending.
 | 
				
			||||||
 | 
					// Grow the vector by amt in bits; return the old size in bits.
 | 
				
			||||||
 | 
					unsigned ByteVector::growBits(unsigned amt)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int oldsizebits = sizeBits();
 | 
				
			||||||
 | 
						setSizeBits(oldsizebits + amt);
 | 
				
			||||||
 | 
						return oldsizebits;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM04.60 10.0b.3.1: Note that fields in RLC blocks use network order,
 | 
				
			||||||
 | 
					// meaning most significant byte first (cause they started on Sun workstations.)
 | 
				
			||||||
 | 
					// It is faster to use htons, etc, than unpacking these ourselves.
 | 
				
			||||||
 | 
					void ByteVector::setUInt16(size_t writeIndex,unsigned value) {	// 2 byte value
 | 
				
			||||||
 | 
						BVASSERT(writeIndex <= size() - 2);
 | 
				
			||||||
 | 
						sethtons(&mStart[writeIndex],value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ByteVector::setUInt32(size_t writeIndex, unsigned value) {	// 4 byte value
 | 
				
			||||||
 | 
						BVASSERT(writeIndex <= size() - 4);
 | 
				
			||||||
 | 
						sethtonl(&mStart[writeIndex],value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Does not change size().
 | 
				
			||||||
 | 
					void ByteVector::fill(ByteType byte, size_t start, size_t span) {
 | 
				
			||||||
 | 
						ByteType *dp=mStart+start;
 | 
				
			||||||
 | 
						ByteType *end=dp+span;
 | 
				
			||||||
 | 
						BVASSERT(end<=mAllocEnd);
 | 
				
			||||||
 | 
						while (dp<end) { *dp++ = byte; }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned ByteVector::getUInt16(size_t readIndex) const {	// 2 byte value
 | 
				
			||||||
 | 
						BVASSERT(readIndex <= size() - 2);
 | 
				
			||||||
 | 
						uint16_t tmp;
 | 
				
			||||||
 | 
						ByteType *tp = (ByteType*)&tmp;
 | 
				
			||||||
 | 
						ByteType *cp = &mStart[readIndex];
 | 
				
			||||||
 | 
						tp[0]=cp[0]; tp[1]=cp[1];
 | 
				
			||||||
 | 
						return ntohs(tmp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					unsigned ByteVector::readUInt16(size_t &rp) {
 | 
				
			||||||
 | 
						unsigned result = getUInt16(rp);
 | 
				
			||||||
 | 
						rp+=2;
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned ByteVector::getUInt32(size_t readIndex) const {	// 4 byte value
 | 
				
			||||||
 | 
						BVASSERT(readIndex <= size() - 4);
 | 
				
			||||||
 | 
						uint32_t tmp;
 | 
				
			||||||
 | 
						ByteType *tp = (unsigned char*)&tmp;
 | 
				
			||||||
 | 
						ByteType *cp = &mStart[readIndex];
 | 
				
			||||||
 | 
						tp[0]=cp[0]; tp[1]=cp[1]; tp[2]=cp[2]; tp[3]=cp[3];
 | 
				
			||||||
 | 
						return ntohl(tmp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					unsigned ByteVector::readUInt32(size_t &rp) {
 | 
				
			||||||
 | 
						unsigned result = getUInt32(rp);
 | 
				
			||||||
 | 
						rp+=4;
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This function returns just the payload part as a hex string.
 | 
				
			||||||
 | 
					// Also works if the ByteVector is encoded as BCD.
 | 
				
			||||||
 | 
					// Probably not efficient, but we are using C++.
 | 
				
			||||||
 | 
					std::string ByteVector::hexstr() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int b = 0, numBits = (int) sizeBits();		// Must be int, not unsigned
 | 
				
			||||||
 | 
						std::string ss;
 | 
				
			||||||
 | 
						while (numBits > 0) {
 | 
				
			||||||
 | 
							char ch = getNibble(b,1);
 | 
				
			||||||
 | 
							ss.push_back(ch + (ch > 9 ? ('A'-10) : '0'));
 | 
				
			||||||
 | 
							numBits -= 4;
 | 
				
			||||||
 | 
							if (numBits >= 4) {
 | 
				
			||||||
 | 
								ch = getNibble(b,0);
 | 
				
			||||||
 | 
								ss.push_back(ch + (ch > 9 ? ('A'-10) : '0'));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							b++;
 | 
				
			||||||
 | 
							numBits -= 4;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ss;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This function returns "ByteVector(size=... data:...)"
 | 
				
			||||||
 | 
					std::string ByteVector::str() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::ostringstream ss;
 | 
				
			||||||
 | 
						ss << *this;
 | 
				
			||||||
 | 
						return ss.str();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream&os, const ByteVector&vec)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i, size=vec.size(); char buf[10];
 | 
				
			||||||
 | 
						os <<"ByteVector(size=" <<size <<" data:";
 | 
				
			||||||
 | 
						for (i=0; i < size; i++) {
 | 
				
			||||||
 | 
							sprintf(buf," %02x",vec.getByte(i));
 | 
				
			||||||
 | 
							os << buf;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						os <<")";
 | 
				
			||||||
 | 
						return os;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static ByteType bitMasks[8] = { 0x80, 0x40, 0x20, 0x10, 8, 4, 2, 1 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get a bit from the specified byte, numbered like this:
 | 
				
			||||||
 | 
					// bits: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
 | 
				
			||||||
 | 
					bool ByteVector::getBit2(size_t byteIndex, unsigned bitIndex) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BVASSERT(bitIndex >= 0 && bitIndex <= 7);
 | 
				
			||||||
 | 
						//return !!(getByte(byteIndex) & (1 << (7-bitIndex)));
 | 
				
			||||||
 | 
						return !!(getByte(byteIndex) & bitMasks[bitIndex]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get a bit from the specified byte, numbered like this:
 | 
				
			||||||
 | 
					// bits: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
 | 
				
			||||||
 | 
					void ByteVector::setBit2(size_t byteIndex, unsigned bitIndex, unsigned val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BVASSERT(bitIndex >= 0 && bitIndex <= 7);
 | 
				
			||||||
 | 
						BVASSERT(byteIndex < size());
 | 
				
			||||||
 | 
						ByteType mask = bitMasks[bitIndex];
 | 
				
			||||||
 | 
						mStart[byteIndex] = val ? (mStart[byteIndex] | mask) : (mStart[byteIndex] & ~mask);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef MIN
 | 
				
			||||||
 | 
					#define MIN(a,b) ((a)<(b)?(a):(b))
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Write a bit field starting at specified byte and bit, each numbered from 0 
 | 
				
			||||||
 | 
					void ByteVector::setField2(size_t byteIndex, size_t bitIndex, uint64_t value,unsigned lengthBits)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BVASSERT(bitIndex >= 0 && bitIndex <= 7);
 | 
				
			||||||
 | 
						// Example: bitIndex = 2, length = 2;
 | 
				
			||||||
 | 
						// 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
 | 
				
			||||||
 | 
						// 0   0   X   X   0   0   0   0
 | 
				
			||||||
 | 
						// endpos = 4; nbytes = 0; lastbit = 4; nbits = 2; mask = 3; shift = 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned endpos = bitIndex + lengthBits;	// 1 past the 0-based index of the last bit.
 | 
				
			||||||
 | 
						unsigned nbytes = (endpos-1) / 8;		// number of bytes that will be modified, minus 1.
 | 
				
			||||||
 | 
						ByteType *dp = mStart + byteIndex + nbytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned lastbit = endpos % 8;	// index of first bit not to be replaced, or 0.
 | 
				
			||||||
 | 
						// Number of bits to modify in the current byte, starting at the last byte.
 | 
				
			||||||
 | 
						unsigned nbits = lastbit ?  MIN(lengthBits,lastbit) : MIN(lengthBits,8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (int len = lengthBits; len > 0; dp--) {
 | 
				
			||||||
 | 
							// Mask of number of bits to be modified in this byte, starting from LSB.
 | 
				
			||||||
 | 
							unsigned mask = (1 << nbits) - 1;
 | 
				
			||||||
 | 
							ByteType val = value & mask;
 | 
				
			||||||
 | 
							value >>= nbits;
 | 
				
			||||||
 | 
							if (lastbit) {
 | 
				
			||||||
 | 
								// Shift val and mask so they are aligned with the bits to modify in the last byte,
 | 
				
			||||||
 | 
								// noting that we modify the last byte first, since we work backwards.
 | 
				
			||||||
 | 
								int shift = 8 - lastbit;
 | 
				
			||||||
 | 
								mask <<= shift;
 | 
				
			||||||
 | 
								val <<= shift;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							*dp = (*dp & ~mask) | (val & mask);
 | 
				
			||||||
 | 
							len -= nbits;
 | 
				
			||||||
 | 
							nbits = MIN(len,8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							lastbit = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ByteVector::appendField(uint64_t value,unsigned lengthBits)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						setField(growBits(lengthBits),value,lengthBits);
 | 
				
			||||||
 | 
						/*** old
 | 
				
			||||||
 | 
						int endpos = mBitInd + lengthBits;	// 1 past the 0-based index of the last bit.
 | 
				
			||||||
 | 
						int nbytes = (endpos-1) / 8;		// number of new bytes needed.
 | 
				
			||||||
 | 
						if (mBitInd == 0) nbytes++;			// if at 0, the next byte has not been alloced yet.
 | 
				
			||||||
 | 
						setField2(grow(nbytes),mBitInd,value,lengthBits);
 | 
				
			||||||
 | 
						mBitInd = endpos % 8;
 | 
				
			||||||
 | 
						***/
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Read a bit field starting at specified byte and bit, each numbered from 0.
 | 
				
			||||||
 | 
					// Bit numbering is from high to low, like this:
 | 
				
			||||||
 | 
					//  getField bitIndex: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
 | 
				
			||||||
 | 
					// Note that this is inverted with respect to the numbering scheme used
 | 
				
			||||||
 | 
					// in many GSM specs, which looks like this:
 | 
				
			||||||
 | 
					//  GSM specs: 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1
 | 
				
			||||||
 | 
					// Note that some GSM specs use low-to-high and some use high-to-low numbering.
 | 
				
			||||||
 | 
					// Generally, where the BitVector class is used they use low-to-high numbering,
 | 
				
			||||||
 | 
					// which is rectified in the FEC classes by the byteswapping the BitVector before being used.
 | 
				
			||||||
 | 
					uint64_t ByteVector::getField2(size_t byteIndex, size_t bitIndex, unsigned lengthBits) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ByteType *dp = mStart + byteIndex;
 | 
				
			||||||
 | 
						int len = (int) lengthBits;
 | 
				
			||||||
 | 
						BVASSERT(bitIndex >= 0 && bitIndex <= 7);
 | 
				
			||||||
 | 
						// Get first byte:
 | 
				
			||||||
 | 
						// This was for bitIndex running from low bit to high bit:
 | 
				
			||||||
 | 
						// int nbits = bitIndex+1;	// Number of bits saved from byte.
 | 
				
			||||||
 | 
						// This is for bitIndex running from 0=>high bit to 7=>low bit:
 | 
				
			||||||
 | 
						int nbits = 8-bitIndex;	// Number of bits saved from byte, ignoring len restriction.
 | 
				
			||||||
 | 
						// Example: bitIndex=3 => 0 | 0 | 0 | X | X | X | X | X  => AND with 0x1f
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint64_t accum = *dp++ & (0x0ff >> (8-nbits));	// Preserve right-most bits.
 | 
				
			||||||
 | 
						if (len < nbits) { accum >>= (nbits - len); return accum; }
 | 
				
			||||||
 | 
						len -= nbits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get the full bytes:
 | 
				
			||||||
 | 
						for (; len >= 8; len -= 8) { accum = (accum << 8) | *dp++; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Append high bits of last byte:
 | 
				
			||||||
 | 
						if (len>0) { accum = (accum << len) | (*dp >> (8-len)); }
 | 
				
			||||||
 | 
						return accum;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is static - there is no 'this' argument.
 | 
				
			||||||
 | 
					int ByteVector::compare(const ByteVector &bv1, const ByteVector &bv2)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned bv1size = bv1.sizeBits(), bv2size = bv2.sizeBits();
 | 
				
			||||||
 | 
						unsigned minsize = MIN(bv1size,bv2size);
 | 
				
			||||||
 | 
						unsigned bytes = minsize/8;
 | 
				
			||||||
 | 
						int result;
 | 
				
			||||||
 | 
						// Compare the full bytes.
 | 
				
			||||||
 | 
						if (bytes) {
 | 
				
			||||||
 | 
							if ((result = memcmp(bv1.begin(),bv2.begin(),bytes))) {return result;}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Compare the partial byte, if any.
 | 
				
			||||||
 | 
						unsigned rem = minsize%8;
 | 
				
			||||||
 | 
						if (rem) {
 | 
				
			||||||
 | 
							if ((result = (int) bv1.getField2(bytes,0,rem) - (int) bv2.getField2(bytes,0,rem))) {return result;}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// All bits the same.  The longer guy wins.
 | 
				
			||||||
 | 
						return (int)bv1size - (int)bv2size;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// We assume that if the last byte is a partial byte (ie bitsize % 8 != 0)
 | 
				
			||||||
 | 
					// then the remaining unused bits are all equal, should be 0.
 | 
				
			||||||
 | 
					// If they were set with setField, that will be the case.
 | 
				
			||||||
 | 
					bool ByteVector::eql(const ByteVector &other) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (sizeBits() != other.sizeBits()) {return false;}	// Quick check to avoid full compare.
 | 
				
			||||||
 | 
						return 0 == compare(*this,other);
 | 
				
			||||||
 | 
						//unsigned bytes = bvsize/8;
 | 
				
			||||||
 | 
						//ByteType *b1 = mStart, *b2 = other.mStart;
 | 
				
			||||||
 | 
						//for (int i = size(); i > 0; i--) { if (*b1++ != *b2++) return false; }
 | 
				
			||||||
 | 
						//return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef TEST
 | 
				
			||||||
 | 
					void ByteVectorTest()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned byten, bitn, l, i;
 | 
				
			||||||
 | 
						const unsigned bvlen = 20;
 | 
				
			||||||
 | 
						ByteVector bv(bvlen), bv2(bvlen), pat(bvlen);
 | 
				
			||||||
 | 
						BitVector bitv(64);
 | 
				
			||||||
 | 
						int printall = 0;
 | 
				
			||||||
 | 
						int tests = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ByteVector bctest = ByteVector("12345");
 | 
				
			||||||
 | 
						for (i = 0; i < 5; i++) {
 | 
				
			||||||
 | 
							assert(bctest.getByte(i) == '1'+i);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bv.fill(3);
 | 
				
			||||||
 | 
						for (i = 0; i < bvlen; i++) {
 | 
				
			||||||
 | 
							assert(bv.getByte(i) == 3);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (byten = 0; byten <= 1; byten++) {
 | 
				
			||||||
 | 
							for (bitn = 0; bitn <= 7; bitn++) {
 | 
				
			||||||
 | 
								for (l = 1; l <= 33; l++) {
 | 
				
			||||||
 | 
									tests++;
 | 
				
			||||||
 | 
									uint64_t val = 0xffffffffffull & ((1ull<<l)-1);
 | 
				
			||||||
 | 
									// Test setField vs getField.
 | 
				
			||||||
 | 
									bv.fill(3);	// Pattern we can check for after the test.
 | 
				
			||||||
 | 
									pat.fill(3);
 | 
				
			||||||
 | 
									bv.setField2(byten,bitn,val,l);
 | 
				
			||||||
 | 
									if (printall) {
 | 
				
			||||||
 | 
										std::cout<<"ok:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)
 | 
				
			||||||
 | 
											<<LOGVAR(val)<<" result="<<bv<<"\n";
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if (bv.getField2(byten,bitn,l) != val) {
 | 
				
			||||||
 | 
										std::cout<<"setField fail:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)
 | 
				
			||||||
 | 
											<<LOGVAR(val)<<" result="<<bv<<"\n";
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Make sure the pattern was not disturbed elsewhere.
 | 
				
			||||||
 | 
									if (byten == 1) { assert(bv.getByte(0) == 3); }
 | 
				
			||||||
 | 
									if (bitn) { assert(bv.getField2(byten,0,bitn) == pat.getField2(byten,0,bitn)); }
 | 
				
			||||||
 | 
									int endbit = byten*8 + bitn + l;
 | 
				
			||||||
 | 
									assert(bv.getField(endbit,30) == pat.getField(endbit,30));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Test getBit vs getField.
 | 
				
			||||||
 | 
									for (int b1=0; b1<3; b1++) {
 | 
				
			||||||
 | 
										for (int k = 0; k <= 7; k++) {
 | 
				
			||||||
 | 
											tests++;
 | 
				
			||||||
 | 
											if (bv.getBit2(b1,k) != bv.getField2(b1,k,1)) {
 | 
				
			||||||
 | 
												std::cout<<"getBit fail:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<"\n";
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// Test setField vs BitVector::fillField
 | 
				
			||||||
 | 
									bitv.zero();
 | 
				
			||||||
 | 
									bitv.fillField(byten*8+bitn,val,l);
 | 
				
			||||||
 | 
									bitv.pack(bv2.begin());
 | 
				
			||||||
 | 
									bv.fill(0);
 | 
				
			||||||
 | 
									bv.setField(byten*8+bitn,val,l);
 | 
				
			||||||
 | 
									if (bv != bv2) {
 | 
				
			||||||
 | 
										std::cout<<"ByteVector BitVector mismatch:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<LOGVAR(val)<<"\n";
 | 
				
			||||||
 | 
										std::cout<<"bv="<<bv<<" bv2="<<bv2<<"\n";
 | 
				
			||||||
 | 
										std::cout <<"bitv="<<bitv<<"\n";
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Test fields with large bit counts.
 | 
				
			||||||
 | 
							for (bitn = 1; bitn <= 40; bitn++) {
 | 
				
			||||||
 | 
								for (l = 1; l <= 33; l++) {
 | 
				
			||||||
 | 
									uint64_t val = 0xffffffffffull & ((1ull<<l)-1);
 | 
				
			||||||
 | 
									uint64_t result, expected;
 | 
				
			||||||
 | 
									// Test appendField
 | 
				
			||||||
 | 
									for (int start = 0; start <= 17; start++) {	// start bit for append test
 | 
				
			||||||
 | 
										ByteVector bv(20);
 | 
				
			||||||
 | 
										bv.fill(0);
 | 
				
			||||||
 | 
										bv.setAppendP(byten);
 | 
				
			||||||
 | 
										BVASSERT(bv.size() == byten);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										bv.appendField(0,start);	// Move the starting append position.
 | 
				
			||||||
 | 
										if ((result=bv.sizeBits()) != (expected=(byten*8)+start)) {
 | 
				
			||||||
 | 
											std::cout<<"appendField length1 error"<<LOGVAR(expected)<<LOGVAR(result)<<"\n";
 | 
				
			||||||
 | 
											std::cout<<"at:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<LOGVAR(val)<<LOGVAR(start)<<"\n";
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										bv.appendField(val,bitn);	// This is the test.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if ((result=bv.sizeBits()) != (expected=(byten*8)+start+bitn)) {
 | 
				
			||||||
 | 
											std::cout<<"appendField length2 error"<<LOGVAR(expected)<<LOGVAR(result)<<"\n";
 | 
				
			||||||
 | 
											std::cout<<"at:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<LOGVAR(val)<<LOGVAR(start)<<"\n";
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if (bv.getField(0,byten*8+start) != 0) {
 | 
				
			||||||
 | 
											std::cout<<"appendField start error\n";
 | 
				
			||||||
 | 
											std::cout<<"at:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<LOGVAR(val)<<LOGVAR(start)<<"\n";
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if ((result=bv.getField(byten*8+start,bitn)) != (expected=(val & ((1ull<<bitn)-1)))) {
 | 
				
			||||||
 | 
											bv.setAppendP(10);	// Needed to read beyond end of our test.
 | 
				
			||||||
 | 
											std::cout<<"appendField value error"<<LOGVAR(expected)<<LOGVAR(result)<<"\n";
 | 
				
			||||||
 | 
											std::cout<<"at:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<LOGVAR(val)<<LOGVAR(start)<<"\n";
 | 
				
			||||||
 | 
											std::cout<<"bv="<<bv.segmentTemp(0,6)<<"\n";
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if (bv.getField(byten*8+start+bitn,32) != 0) {
 | 
				
			||||||
 | 
											std::cout<<"appendField overrun error\n";
 | 
				
			||||||
 | 
											std::cout<<"at:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<LOGVAR(val)<<LOGVAR(start)<<"\n";
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						std::cout<<"Finished ByteVector "<<tests<<" tests\n";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ByteVectorTest();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif	// ifdef TEST
 | 
				
			||||||
							
								
								
									
										382
									
								
								GPRS/ByteVector.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								GPRS/ByteVector.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,382 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef _BYTEVECTOR_H_
 | 
				
			||||||
 | 
					#define _BYTEVECTOR_H_
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <arpa/inet.h>
 | 
				
			||||||
 | 
					#include "MemoryLeak.h"
 | 
				
			||||||
 | 
					#include "BitVector.h"
 | 
				
			||||||
 | 
					#include "ScalarTypes.h"
 | 
				
			||||||
 | 
					#include "Logger.h"
 | 
				
			||||||
 | 
					// Originally based on BitVector, based on Vector
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ByteVector is like a Vector but for objects of type... guess what?
 | 
				
			||||||
 | 
					// ByteVector also has an efficient append facility.
 | 
				
			||||||
 | 
					// A ByteVector consists of packed memory that is byte-aligned on the left side
 | 
				
			||||||
 | 
					// and bit-aligned on the right side.  Both the left and right side can be moved
 | 
				
			||||||
 | 
					// back and forth within the constraints of the originally allocated memory.
 | 
				
			||||||
 | 
					// See: trimLeft, trimRight, growLeft, and the many append functions.
 | 
				
			||||||
 | 
					// The basic strategy is that ByteVectors are always allocated initially
 | 
				
			||||||
 | 
					// such that size() is the full allocated size, so if you want to use the append
 | 
				
			||||||
 | 
					// feature you must call setAppendP() (or conceivably trimRight) to set the location
 | 
				
			||||||
 | 
					// where you want to start appending.
 | 
				
			||||||
 | 
					// Exceeding the edges of the allocated memory area throws ByteVectorError
 | 
				
			||||||
 | 
					// There are two classes defined here:
 | 
				
			||||||
 | 
					// o ByteVector points to a memory area that it manages.
 | 
				
			||||||
 | 
					// All segments derived from a ByteVector share the same memory using refcnts.
 | 
				
			||||||
 | 
					// When the last segment is deleted, the memory is freed.
 | 
				
			||||||
 | 
					// o ByteVectorTemp is identical but does not 'own' the memory, rather it points into
 | 
				
			||||||
 | 
					// an area of memory that it does not manage.  It allows you to use the rather extensive
 | 
				
			||||||
 | 
					// set of ByteVector manipulation functions on some other memory, or derive a segment
 | 
				
			||||||
 | 
					// using segmentTemp when you know for sure that the derived segment is temporary and
 | 
				
			||||||
 | 
					// will not outlive the original ByteVector.
 | 
				
			||||||
 | 
					// It is unwise to expand the left or right side of a ByteVectorTemp because there is
 | 
				
			||||||
 | 
					// no protection for exceeding the bounds of the memory area, however those functions
 | 
				
			||||||
 | 
					// are not currently over-ridden in ByteVectorTemp to remove them, but they should be eliminated.
 | 
				
			||||||
 | 
					// ByteVector is the base class and can refer to either ByteVectors that own memory,
 | 
				
			||||||
 | 
					// or ByteVectorTemps that do not.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// I started inheriting from Vector but we could only reuse a few lines of code
 | 
				
			||||||
 | 
					// from there so it is not worth the trouble.  It would be better to push
 | 
				
			||||||
 | 
					// the appending ability down into Vector.  But appending creates a different
 | 
				
			||||||
 | 
					// concept of size() than Vector which would be a major change.
 | 
				
			||||||
 | 
					// To avoid confusion with Vector type functions resize() is not defined in ByteVector.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define NEW_SEGMENT_SEMANTICS 1
 | 
				
			||||||
 | 
					#define BYTEVECTOR_REFCNT 1		// ByteVectors now use reference counting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define BVASSERT(expr) {if (!(expr)) throw ByteVectorError();}
 | 
				
			||||||
 | 
					class ByteVectorError {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef uint8_t ByteType;
 | 
				
			||||||
 | 
					void sethtonl(ByteType *cp,unsigned value);
 | 
				
			||||||
 | 
					void sethtons(ByteType *cp,unsigned value);
 | 
				
			||||||
 | 
					uint16_t getntohs(ByteType *cp);
 | 
				
			||||||
 | 
					uint32_t getntohl(ByteType *cp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ItemWithSize {
 | 
				
			||||||
 | 
						virtual size_t sizeBits() const =0;
 | 
				
			||||||
 | 
						virtual size_t sizeBytes() const =0;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ByteVectorTemp;
 | 
				
			||||||
 | 
					class Dorky {};	// Used to force use of a particular constructor.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ByteVector //: public Vector<ByteType>
 | 
				
			||||||
 | 
						: public ItemWithSize
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ByteType* mData;		///< allocated data block, if any
 | 
				
			||||||
 | 
						ByteType* mStart;		///< start of useful data, always >=mData and <=mAllocEnd
 | 
				
			||||||
 | 
						unsigned mSizeBits;		///< size of useful data in bits.
 | 
				
			||||||
 | 
						ByteType *mAllocEnd;	///< end of allocated data + 1.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//unsigned mBitInd;
 | 
				
			||||||
 | 
						unsigned bitind() { return mSizeBits % 8; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if BYTEVECTOR_REFCNT
 | 
				
			||||||
 | 
						// The first mDataOffset bytes of mData is a short reference count of the number
 | 
				
			||||||
 | 
						// of ByteVectors pointing at it.
 | 
				
			||||||
 | 
						static const int mDataOffset = sizeof(short);
 | 
				
			||||||
 | 
						int setRefCnt(int val) { return ((short*)mData)[0] = val; }
 | 
				
			||||||
 | 
						int decRefCnt() { return setRefCnt(((short*)mData)[0] - 1); }
 | 
				
			||||||
 | 
						void incRefCnt() { setRefCnt(((short*)mData)[0] + 1); }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void init(size_t newSize);	/** set size and allocated size to that specified */
 | 
				
			||||||
 | 
						void initstr(const char *wdata, unsigned wlen) { init(wlen); setAppendP(0); append(wdata,wlen); }
 | 
				
			||||||
 | 
						void initstr(const char *wdata) { initstr(wdata,strlen(wdata)); }
 | 
				
			||||||
 | 
						void dup(const ByteVector& other);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Constructor: A ByteVector that does not own any memory, but is just a segment
 | 
				
			||||||
 | 
						// of some other memory.  This constructor is private.
 | 
				
			||||||
 | 
						// The public way to do this is to use segmentTemp or create a ByteVectorTemp.
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
						ByteVector(Dorky,ByteType*wstart,ByteType*wend)
 | 
				
			||||||
 | 
							: mData(0), mStart(wstart), mSizeBits(8*(wend-wstart)), mAllocEnd(wend) {}
 | 
				
			||||||
 | 
						//: mData(0), mStart(wstart), mEnd(wend), mAllocEnd(wend), mBitInd(0) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						void clear();	// Release the memory used by this ByteVector.
 | 
				
			||||||
 | 
						// clone semantics are weird: copies data from other to self.
 | 
				
			||||||
 | 
						void clone(const ByteVector& other); /** Copy data from another vector. */
 | 
				
			||||||
 | 
					#if BYTEVECTOR_REFCNT
 | 
				
			||||||
 | 
						int getRefCnt() { return mData ? ((short*)mData)[0] : 0; }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const ByteType* begin() const { return mStart; }
 | 
				
			||||||
 | 
						ByteType*begin() { return mStart; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This is the allocSize from mStart, not from mData.
 | 
				
			||||||
 | 
						size_t allocSize() const { return mAllocEnd - mStart; }
 | 
				
			||||||
 | 
						//size_t sizeBytes() const { return mEnd - mStart + !!mBitInd; }	// size in bytes
 | 
				
			||||||
 | 
						size_t sizeBytes() const { return (mSizeBits+7)/8; }	// size in bytes
 | 
				
			||||||
 | 
						size_t size() const { return sizeBytes(); }	// size in bytes
 | 
				
			||||||
 | 
						//size_t sizeBits() const { return size() * 8 + mBitInd; }	// size in bits
 | 
				
			||||||
 | 
						size_t sizeBits() const { return mSizeBits; }	// size in bits
 | 
				
			||||||
 | 
						size_t sizeRemaining() const { // Remaining full bytes for appends
 | 
				
			||||||
 | 
							return (mAllocEnd - mStart) - sizeBytes();
 | 
				
			||||||
 | 
							//int result = mAllocEnd - mEnd - !!mBitInd;
 | 
				
			||||||
 | 
							//return result >= 0 ? (size_t) result : 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void resetSize() { mSizeBits = 8*allocSize(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set the Write Position for appends.  This is equivalent to setting size().
 | 
				
			||||||
 | 
						void setAppendP(size_t bytep, unsigned bitp=0) {
 | 
				
			||||||
 | 
							BVASSERT(bytep + !!bitp <= allocSize());
 | 
				
			||||||
 | 
							mSizeBits = bytep*8 + bitp;
 | 
				
			||||||
 | 
							//mEnd=mStart+bytep; mBitInd=bitp;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void setSizeBits(size_t bits) {
 | 
				
			||||||
 | 
							BVASSERT((bits+7)/8 <= allocSize());
 | 
				
			||||||
 | 
							mSizeBits = bits;
 | 
				
			||||||
 | 
							//mEnd=mStart+(bits/8); mBitInd=bits%8;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Concat unimplemented.
 | 
				
			||||||
 | 
						//ByteVector(const ByteVector& source1, const ByteVector source2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Constructor: An empty Vector of a given size. */
 | 
				
			||||||
 | 
						ByteVector(size_t wSize=0) { RN_MEMCHKNEW(ByteVector); init(wSize); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Constructor: A ByteVector whose contents is from a string with optional length specified.
 | 
				
			||||||
 | 
						// A copy of the string is malloced into the ByteVector.
 | 
				
			||||||
 | 
						// ByteType is "unsigned char" so we need both signed and unsigned versions to passify C++.
 | 
				
			||||||
 | 
						ByteVector(const ByteType *wdata,int wlen) { RN_MEMCHKNEW(ByteVector) initstr((const char*)wdata,wlen); }
 | 
				
			||||||
 | 
						ByteVector(const ByteType *wdata) { RN_MEMCHKNEW(ByteVector) initstr((const char*)wdata); }
 | 
				
			||||||
 | 
						ByteVector(const char *wdata,int wlen) { RN_MEMCHKNEW(ByteVector) initstr(wdata,wlen); }
 | 
				
			||||||
 | 
						ByteVector(const char *wdata) { RN_MEMCHKNEW(ByteVector) initstr(wdata); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Constructor: A ByteVector which is a duplicate of some other.
 | 
				
			||||||
 | 
						// They both 'own' the memory using refcounts.
 | 
				
			||||||
 | 
						// The const is tricky - other is modified despite the 'const', because only the ByteVector itself is 'const',
 | 
				
			||||||
 | 
						// the memory it points to is not.
 | 
				
			||||||
 | 
						// See also: alias.
 | 
				
			||||||
 | 
						ByteVector(ByteVector& other) : mData(0) { RN_MEMCHKNEW(ByteVector) dup(other); }
 | 
				
			||||||
 | 
						ByteVector(const ByteVector&other) : mData(0) { RN_MEMCHKNEW(ByteVector) dup(other); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ByteVector(const BitVector &other) { RN_MEMCHKNEW(ByteVector) init((other.size()+7)/8); setAppendP(0); append(other); }
 | 
				
			||||||
 | 
						virtual ~ByteVector() { RN_MEMCHKDEL(ByteVector) clear(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Make a duplicate of the vector where both point into shared memory, and increment the refcnt.
 | 
				
			||||||
 | 
						// Use clone if you want a completely distinct copy.
 | 
				
			||||||
 | 
						void operator=(ByteVector& other) { dup(other); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The BitVector class implements this with a clone().
 | 
				
			||||||
 | 
						// However, this gets called if a hidden intermediary variable is required
 | 
				
			||||||
 | 
						// to implement the assignment, for example: x = segment(...);
 | 
				
			||||||
 | 
						// In this case it is safer for the class to call clone to be safe,
 | 
				
			||||||
 | 
						// however, that is probably never what is wanted by the calling code,
 | 
				
			||||||
 | 
						// so I am leaving it out of here.  Only use this type of assignment if the
 | 
				
			||||||
 | 
						// other does not own the memory.
 | 
				
			||||||
 | 
						// Update: With refcnts, these issues evaporate, and the two forms
 | 
				
			||||||
 | 
						// of assignment are identical.
 | 
				
			||||||
 | 
						void operator=(const ByteVector& other) {
 | 
				
			||||||
 | 
					#if BYTEVECTOR_REFCNT
 | 
				
			||||||
 | 
							dup(other);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
							BVASSERT(other.mData == NULL);	// Dont use assignment in this case.
 | 
				
			||||||
 | 
							set(other);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static int compare(const ByteVector &bv1, const ByteVector &bv2);
 | 
				
			||||||
 | 
						bool eql(const ByteVector &other) const;
 | 
				
			||||||
 | 
						bool operator==(const ByteVector &other) const { return eql(other); }
 | 
				
			||||||
 | 
						bool operator!=(const ByteVector &other) const { return !eql(other); }
 | 
				
			||||||
 | 
						// This is here so you put ByteVectors in a map, which needs operator< defined.
 | 
				
			||||||
 | 
						bool operator<(const ByteVector &other) const { return compare(*this,other)<0; }
 | 
				
			||||||
 | 
						bool operator>(const ByteVector &other) const { return compare(*this,other)>0; }
 | 
				
			||||||
 | 
						bool operator<=(const ByteVector &other) const { return compare(*this,other)<=0; }
 | 
				
			||||||
 | 
						bool operator>=(const ByteVector &other) const { return compare(*this,other)>=0; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**@name Functions identical to Vector: */
 | 
				
			||||||
 | 
						// Return a segment of a ByteVector that shares the same memory as the original.
 | 
				
			||||||
 | 
						// The const is tricky - the original ByteVector itself is not modified, but the memory it points to is.
 | 
				
			||||||
 | 
						ByteVector segment(size_t start, size_t span) const;
 | 
				
			||||||
 | 
						// For the const version, since we are not allowed to modify the original
 | 
				
			||||||
 | 
						// to change the refcnt, we have to either return a segment that does not own memory,
 | 
				
			||||||
 | 
						// or a completely new piece of memory.  So I am using a different name so that
 | 
				
			||||||
 | 
						// it is obvious that the memory ownership is being stripped off the ByteVector.
 | 
				
			||||||
 | 
						const ByteVectorTemp segmentTemp(size_t start, size_t span) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Copy other to this starting at start.
 | 
				
			||||||
 | 
						// The 'this' ByteVector must be allocated large enough to hold other.
 | 
				
			||||||
 | 
						void setSegment(size_t start, ByteVector&other);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool isOwner() { return !!mData; }	// Do we own any memory ourselves?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Trim specified number of bytes from left or right in place.
 | 
				
			||||||
 | 
						// growLeft is the opposite: move the mStart backward by amt, throw error if no room.
 | 
				
			||||||
 | 
						// New space is uninitialized.
 | 
				
			||||||
 | 
						// These are for byte-aligned only, so we assert bit index is 0.
 | 
				
			||||||
 | 
						void trimLeft(unsigned amt);
 | 
				
			||||||
 | 
						void trimRight(unsigned amt);
 | 
				
			||||||
 | 
						ByteType *growLeft(unsigned amt);
 | 
				
			||||||
 | 
						void growRight(unsigned amt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void fill(ByteType byte, size_t start, size_t span);
 | 
				
			||||||
 | 
						void fill(ByteType byte, size_t start) { fill(byte,start,size()-start); }
 | 
				
			||||||
 | 
						void fill(ByteType byte) { fill(byte,0,size()); }
 | 
				
			||||||
 | 
						void appendFill(ByteType byte, size_t span);
 | 
				
			||||||
 | 
						// Zero out the rest of the ByteVector:
 | 
				
			||||||
 | 
						void appendZero() {
 | 
				
			||||||
 | 
							if (bitind()) appendField(0,8-bitind());	// 0 fill partial byte.
 | 
				
			||||||
 | 
							appendFill(0,allocSize() - size());		// 0 fill remaining bytes.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Copy part of this ByteVector to a segment of another.
 | 
				
			||||||
 | 
						// The specified span must not exceed our size, and it must fit in the target ByteVector.
 | 
				
			||||||
 | 
						void copyToSegment(ByteVector& other, size_t start, size_t span) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Copy all of this Vector to a segment of another Vector. */
 | 
				
			||||||
 | 
						void copyToSegment(ByteVector& other, size_t start=0) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// pat 2-2012: I am modifying this to use the refcnts, so to get
 | 
				
			||||||
 | 
						// a segment with an incremented refcnt, use: alias().segment(...)
 | 
				
			||||||
 | 
						//ByteVector alias() { return segment(0,size()); }
 | 
				
			||||||
 | 
						ByteVector head(size_t span) const { return segment(0,span); }
 | 
				
			||||||
 | 
						//const ByteVector headTemp(size_t span) const { return segmentTemp(0,span); }
 | 
				
			||||||
 | 
						ByteVector tail(size_t start) const { return segment(start,size()-start); }
 | 
				
			||||||
 | 
						//const ByteVector tailTemp(size_t start) const { return segmentTemp(start,size()-start); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GSM04.60 10.0b.3.1: Note that fields in RLC blocks use network order,
 | 
				
			||||||
 | 
						// meaning most significant byte first (cause they started on Sun workstations.)
 | 
				
			||||||
 | 
						// It is faster to use htons, etc, than unpacking these ourselves.
 | 
				
			||||||
 | 
						// Note that this family of functions all assume byte-aligned fields.
 | 
				
			||||||
 | 
						// See setField/appendField for bit-aligned fields.
 | 
				
			||||||
 | 
						unsigned grow(unsigned amt);
 | 
				
			||||||
 | 
						unsigned growBits(unsigned amt);
 | 
				
			||||||
 | 
						void setByte(size_t ind, ByteType byte) { BVASSERT(ind < size()); mStart[ind] = byte; }
 | 
				
			||||||
 | 
						void setUInt16(size_t writeIndex,unsigned value);	// 2 byte value
 | 
				
			||||||
 | 
						void setUInt32(size_t writeIndex, unsigned value);	// 4 byte value
 | 
				
			||||||
 | 
						void appendByte(unsigned value) { BVASSERT(bitind()==0); setByte(grow(1),value); }
 | 
				
			||||||
 | 
						void appendUInt16(unsigned value) { BVASSERT(bitind()==0); setUInt16(grow(2),value); }
 | 
				
			||||||
 | 
						void appendUInt32(unsigned value) { BVASSERT(bitind()==0); setUInt32(grow(4),value); }
 | 
				
			||||||
 | 
						ByteType getByte(size_t ind) const { BVASSERT(ind < size()); return mStart[ind]; }
 | 
				
			||||||
 | 
						ByteType getNibble(size_t ind,int hi) const {
 | 
				
			||||||
 | 
							ByteType val = getByte(ind); return hi ? (val>>4) : val & 0xf;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned getUInt16(size_t readIndex) const;	// 2 byte value
 | 
				
			||||||
 | 
						unsigned getUInt32(size_t readIndex) const;	// 4 byte value
 | 
				
			||||||
 | 
						ByteType readByte(size_t &rp) { return getByte(rp++); }
 | 
				
			||||||
 | 
						unsigned readUInt16(size_t &rp);
 | 
				
			||||||
 | 
						unsigned readUInt32(size_t &rp);
 | 
				
			||||||
 | 
						unsigned readLI(size_t &rp);	// GSM8.16 1 or 2 octet length indicator.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void append(const ByteVector&other);
 | 
				
			||||||
 | 
						void append(const BitVector&other);
 | 
				
			||||||
 | 
						void append(const ByteType*bytes, unsigned len);
 | 
				
			||||||
 | 
						void append(const char*bytes, unsigned len) { append((const ByteType*)bytes,len); }
 | 
				
			||||||
 | 
						void appendLI(unsigned len);	// GSM8.16 1 or 2 octet length indicator.
 | 
				
			||||||
 | 
						void append(const ByteVector*other) { append(*other); }
 | 
				
			||||||
 | 
						void append(const BitVector*other) { append(*other); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set from another ByteVector.
 | 
				
			||||||
 | 
						// The other is typically a segment which does not own the memory, ie:
 | 
				
			||||||
 | 
						// v1.set(v2.segment())  The other is not a reference because
 | 
				
			||||||
 | 
						// the result of segment() is not a variable to which
 | 
				
			||||||
 | 
						// the reference operator can be applied.
 | 
				
			||||||
 | 
						// This is not really needed any more because the refcnts take care of these cases.
 | 
				
			||||||
 | 
						void set(ByteVector other)	// That's right.  No ampersand.
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
					#if BYTEVECTOR_REFCNT
 | 
				
			||||||
 | 
							// Its ok, everything will work.
 | 
				
			||||||
 | 
							dup(other);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
							BVASSERT(other.mData == NULL);	// Dont use set() in this case.
 | 
				
			||||||
 | 
							clear();
 | 
				
			||||||
 | 
							mStart=other.mStart; mEnd=other.mEnd; mAllocEnd = other.mAllocEnd; mBitInd = other.mBitInd;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Bit aligned operations.
 | 
				
			||||||
 | 
						// The "2" suffix versions specify both byte and bit index in the range 0..7.
 | 
				
			||||||
 | 
						// The "R1" suffix versions are identical to the "2" suffix versions,
 | 
				
			||||||
 | 
						// but with start bit numbered from 1 like this: 8 | 7 | ... | 1
 | 
				
			||||||
 | 
						// This is just a convenience because all the specs number the bits this weird way.
 | 
				
			||||||
 | 
						// The no suffix versions take a bit pos ranging from 0 to 8*size()-1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get a bit from the specified byte, numbered like this: 0 | 1 | ... | 7
 | 
				
			||||||
 | 
						bool getBit2(size_t byteIndex, unsigned bitIndex) const;
 | 
				
			||||||
 | 
						// Get a bit in same bit order, but with start bit numbered from 1 like this: 8 | 7 | ... | 1
 | 
				
			||||||
 | 
						// Many GSM L3 specs specify numbering this way.
 | 
				
			||||||
 | 
						bool getBitR1(size_t byteIndex, unsigned bitIndex) const {
 | 
				
			||||||
 | 
							return getBit2(byteIndex,8-bitIndex);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool getBit(unsigned bitPos) const { return getBit2(bitPos/8,bitPos%8); }
 | 
				
			||||||
 | 
						void setBit2(size_t byteIndex, unsigned bitIndex, unsigned val);
 | 
				
			||||||
 | 
						void setBit(unsigned bitPos, unsigned val) { setBit2(bitPos/8,bitPos%8,val); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set/get fields giving both byte and bit index.
 | 
				
			||||||
 | 
						void setField2(size_t byteIndex, size_t bitIndex, uint64_t value,unsigned lengthBits);
 | 
				
			||||||
 | 
						uint64_t getField2(size_t byteIndex, size_t bitIndex, unsigned lengthBits) const;
 | 
				
			||||||
 | 
						uint64_t getFieldR1(size_t byteIndex, size_t bitIndex, unsigned length) const {
 | 
				
			||||||
 | 
							return getField2(byteIndex,8-bitIndex,length);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set/get bit field giving bit position treating the entire ByteVector as a string of bits.
 | 
				
			||||||
 | 
						void setField(size_t bitPos, uint64_t value, unsigned lengthBits) {
 | 
				
			||||||
 | 
							setField2(bitPos/8,bitPos%8,value,lengthBits);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						uint64_t getField(size_t bitPos, unsigned lengthBits) const {	// aka peekField
 | 
				
			||||||
 | 
							return getField2(bitPos/8,bitPos%8,lengthBits);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Identical to getField, but add lengthBits to readIndex.
 | 
				
			||||||
 | 
						uint64_t readField(size_t& readIndex, unsigned lengthBits) const {
 | 
				
			||||||
 | 
							uint64_t result = getField(readIndex,lengthBits);
 | 
				
			||||||
 | 
							readIndex += lengthBits;
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void appendField(uint64_t value,unsigned lengthBits);
 | 
				
			||||||
 | 
						// This works for Field<> data types.
 | 
				
			||||||
 | 
						void appendField(ItemWithValueAndWidth &item) {
 | 
				
			||||||
 | 
							appendField(item.getValue(),item.getWidth());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::string str() const;
 | 
				
			||||||
 | 
						std::string hexstr() const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ByteVectorTemp : public ByteVector
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						ByteVectorTemp(ByteType*wstart,ByteType*wend) : ByteVector(Dorky(),wstart,wend) {}
 | 
				
			||||||
 | 
						ByteVectorTemp(size_t) { assert(0); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Constructor: A ByteVector whose contents is from a string with optional length specified.
 | 
				
			||||||
 | 
						// These cannot be const because the ByteVectorTemp points into the original memory
 | 
				
			||||||
 | 
						// and has methods to modify it.
 | 
				
			||||||
 | 
						// ByteType is "unsigned char" so we need both signed and unsigned versions to passify C++.
 | 
				
			||||||
 | 
						// All the explicit casts are required.  This is so brain dead.
 | 
				
			||||||
 | 
						ByteVectorTemp(ByteType *wdata,int wlen) : ByteVector(Dorky(),wdata,wdata+wlen) {}
 | 
				
			||||||
 | 
						ByteVectorTemp(ByteType *wdata) : ByteVector(Dorky(),wdata,wdata+strlen((char*)wdata)) {}
 | 
				
			||||||
 | 
						ByteVectorTemp(char *wdata,int wlen)  : ByteVector(Dorky(),(ByteType*)wdata,(ByteType*)wdata+wlen) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ByteVectorTemp(char *wdata) : ByteVector(Dorky(),(ByteType*)wdata,(ByteType*)wdata+strlen((char*)wdata)) {}
 | 
				
			||||||
 | 
						ByteVectorTemp(ByteVector& other) : ByteVector(Dorky(),other.begin(),other.begin()+other.size()) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ByteVectorTemp(BitVector &) { assert(0); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Warning: C++ prefers an operator<< that is const to one that is not.
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream&os, const ByteVector&vec);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										59
									
								
								GPRS/CS4.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								GPRS/CS4.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					SACCH Encoding:
 | 
				
			||||||
 | 
					(Parity(0x10004820009ULL,40,224)
 | 
				
			||||||
 | 
						Set bits: 0,3,17,23,26,40 
 | 
				
			||||||
 | 
						g(D) = 1 + D3 + D17 + D23 + D26 + D40
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					From GSM05.03 sec5.1
 | 
				
			||||||
 | 
					184 bits
 | 
				
			||||||
 | 
							Parity:
 | 
				
			||||||
 | 
							  a) Parity bits:
 | 
				
			||||||
 | 
								 The block of 184 information bits is protected by 40 extra bits used
 | 
				
			||||||
 | 
								 for error correction and detection. These bits are added to the 184 bits
 | 
				
			||||||
 | 
								 according to a shortened binary cyclic code (FIRE code) using the generator
 | 
				
			||||||
 | 
								 polynomial:
 | 
				
			||||||
 | 
									  g(D) = (D23 + 1)*(D17 + D3 + 1)
 | 
				
			||||||
 | 
								 The encoding of the cyclic code is performed in a systematic form, which means that, in GF(2), the polynomial:
 | 
				
			||||||
 | 
								 d(0)D223 + d(1)D222 +...+d(183)D40 + p(1)D38 +...+p(38)D + p(39)
 | 
				
			||||||
 | 
								 where {p(0),p(1),...,p(39)} are the parity bits , when divided by g(D) yields a remainder equal to:
 | 
				
			||||||
 | 
									  1 + D + D2 +...+ D39.
 | 
				
			||||||
 | 
							) Tail bits
 | 
				
			||||||
 | 
								 Four tail bits equal to 0 are added to the information and parity bits, the result being a block of 228 bits.
 | 
				
			||||||
 | 
									  u(k) = d(k)       for k= 0,1,...,183
 | 
				
			||||||
 | 
									  u(k) = p(k-184) for k = 184,185,...,223
 | 
				
			||||||
 | 
									  u(k) = 0          for k = 224,225,226,227 (tail bits)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					From GSM03.64 sec6.5.5
 | 
				
			||||||
 | 
					CS-4: 431 bits.
 | 
				
			||||||
 | 
					----
 | 
				
			||||||
 | 
							a) USF precoding:
 | 
				
			||||||
 | 
							   The first three bits d(0),d(1),d(2) are block coded into twelve bits u'(0),u'(1),...,u'(11) according to the following
 | 
				
			||||||
 | 
							   table:
 | 
				
			||||||
 | 
																d(0),d(1),d(2)              u'(0),u'(1),...,u'(11)
 | 
				
			||||||
 | 
					                                         000                     000 000 000 000
 | 
				
			||||||
 | 
					                                         001                     000 011 011 101
 | 
				
			||||||
 | 
					                                         010                     001 101 110 110
 | 
				
			||||||
 | 
					                                         011                     001 110 101 011
 | 
				
			||||||
 | 
					                                         100                     110 100 001 011
 | 
				
			||||||
 | 
					                                         101                     110 111 010 110
 | 
				
			||||||
 | 
					                                         110                     111 001 111 101
 | 
				
			||||||
 | 
					                                         111                     111 010 100 000
 | 
				
			||||||
 | 
							b) Parity bits:
 | 
				
			||||||
 | 
							   Sixteen parity bits p(0),p(1),...,p(15) are defined in such a way that in GF(2) the binary polynomial:
 | 
				
			||||||
 | 
								   d(0)D446 +...+ d(430)D16 + p(0)D15 +...+ p(15), when divided by:
 | 
				
			||||||
 | 
								   D16 + D12 + D5 + 1, yields a remainder equal to:
 | 
				
			||||||
 | 
								   D15 + D14 + D13 + D12 + D11 + D10 + D9 + D8 + D7 + D6 + D5 + D4 + D3 + D2 + D+1.
 | 
				
			||||||
 | 
							   The result is a block of 456 coded bits, {c(0),c(1),...,c(455)}:
 | 
				
			||||||
 | 
								   c(k)    = u'(k)        for k = 0,1,...,11
 | 
				
			||||||
 | 
								   c(k)    = d(k-9)       for k = 12,13,...,439
 | 
				
			||||||
 | 
								   c(k)    = p(k-440)     for k = 440,441,...,455
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							5.1.4.5          Mapping on a burst
 | 
				
			||||||
 | 
							The mapping is given by the rule:
 | 
				
			||||||
 | 
								  e(B,j) = i(B,j) and e(B,59+j) = i(B,57+j) for j = 0,1,...,56
 | 
				
			||||||
 | 
							and
 | 
				
			||||||
 | 
								  e(B+m,57) = q(2m) and e(B+m,58) = q(2m+1) for m = 0,1,2,3
 | 
				
			||||||
 | 
							where
 | 
				
			||||||
 | 
							q(0),q(1),...,q(7) = 0,0,0,1,0,1,1,0 identifies the coding scheme CS-4.
 | 
				
			||||||
							
								
								
									
										918
									
								
								GPRS/FEC.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										918
									
								
								GPRS/FEC.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,918 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "GPRSInternal.h"
 | 
				
			||||||
 | 
					#include "RLCMessages.h"
 | 
				
			||||||
 | 
					#include "RLCEngine.h"
 | 
				
			||||||
 | 
					#include "FEC.h"
 | 
				
			||||||
 | 
					#include "GSMTAPDump.h"
 | 
				
			||||||
 | 
					#include "../TransceiverRAD1/Transceiver.h"	// For Transceiver::IGPRS
 | 
				
			||||||
 | 
					#define FEC_DEBUG 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
					static BitVector *decodeLowSide(const RxBurst &inBurst, int B, GprsDecoder &decoder, ChannelCodingType *ccPtr);
 | 
				
			||||||
 | 
					//static int sFecDebug = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//static Mutex testlock;	Did not help.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const int GPRSUSFEncoding[8] = {
 | 
				
			||||||
 | 
						// from table at GSM05.03 sec 5.1.4.2, specified in octal.
 | 
				
			||||||
 | 
						// 3 bits in, 12 bits out.
 | 
				
			||||||
 | 
						// Note that the table is defined as encoding bits 0,1,2 of usf,
 | 
				
			||||||
 | 
						// which is the post-byte-swapped version, not the original usf.
 | 
				
			||||||
 | 
						00000, // 000 000 000 000
 | 
				
			||||||
 | 
						00335, // 000 011 011 101
 | 
				
			||||||
 | 
						01566, // 001 101 110 110
 | 
				
			||||||
 | 
						01653, // 001 110 101 011
 | 
				
			||||||
 | 
						06413, // 110 100 001 011
 | 
				
			||||||
 | 
						06726, // 110 111 010 110
 | 
				
			||||||
 | 
						07175, // 111 001 111 101
 | 
				
			||||||
 | 
						07240  // 111 010 100 000
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Do the reverse encoding on usf, and return the reversed usf,
 | 
				
			||||||
 | 
					// ie, the returned usf is byte-swapped.
 | 
				
			||||||
 | 
					static int decodeUSF(SoftVector &mC)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// TODO: Make this more robust.
 | 
				
			||||||
 | 
						// Update: No dont bother, should always be zero anyway.
 | 
				
			||||||
 | 
						return (mC.bit(0)<<2) | (mC.bit(6)<<1) | mC.bit(4);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ARFCNManager *PDCHCommon::getRadio() { return mchParent->mchOldFec->getRadio(); }
 | 
				
			||||||
 | 
					unsigned PDCHCommon::ARFCN() { return mchParent->mchOldFec->ARFCN(); }
 | 
				
			||||||
 | 
					unsigned PDCHCommon::CN() { return mchParent->mchOldFec->CN(); }
 | 
				
			||||||
 | 
					unsigned PDCHCommon::TN() { return mchParent->mchOldFec->TN(); }
 | 
				
			||||||
 | 
					PDCHL1Uplink *PDCHCommon::uplink() { return mchParent->mchUplink; }
 | 
				
			||||||
 | 
					PDCHL1Downlink *PDCHCommon::downlink() { return mchParent->mchDownlink; }
 | 
				
			||||||
 | 
					PDCHL1FEC *PDCHCommon::parent() { return mchParent; }
 | 
				
			||||||
 | 
					void PDCHL1FEC::debug_test() { /*printf("dt\n"); devassert(*mReservations[3]._vptr != NULL);*/ }
 | 
				
			||||||
 | 
					L1Decoder* PDCHCommon::getDecoder() const { return mchParent->mchOldFec->decoder(); }
 | 
				
			||||||
 | 
					void PDCHCommon::countGoodFrame() { getDecoder()->countGoodFrame(); }
 | 
				
			||||||
 | 
					void PDCHCommon::countBadFrame() { getDecoder()->countBadFrame(); }
 | 
				
			||||||
 | 
					float PDCHCommon::FER() const { return getDecoder()->FER(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Print out the USFs bracketing bsn on either side.
 | 
				
			||||||
 | 
					const char *PDCHCommon::getAnsweringUsfText(char *buf,RLCBSN_t bsn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						PDCHL1FEC *pdch = parent();
 | 
				
			||||||
 | 
						sprintf(buf," AnsweringUsf=%d %d [%d] %d %d",
 | 
				
			||||||
 | 
							pdch->getUsf(bsn-2),pdch->getUsf(bsn-1), pdch->getUsf(bsn),pdch->getUsf(bsn+1),pdch->getUsf(bsn+2));
 | 
				
			||||||
 | 
						return buf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Must return true if ch1 is before ch2.
 | 
				
			||||||
 | 
					bool chCompareFunc(PDCHCommon*ch1, PDCHCommon*ch2)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (ch1->ARFCN() < ch2->ARFCN()) return true;
 | 
				
			||||||
 | 
						if (ch1->ARFCN() > ch2->ARFCN()) return false;
 | 
				
			||||||
 | 
						if (ch1->TN() < ch2->TN()) return true;
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PDCHL1FEC::mchStart() {
 | 
				
			||||||
 | 
						getRadio()->setSlot(TN(),Transceiver::IGPRS);
 | 
				
			||||||
 | 
						// Load up the GPRS filler idle burst tables in the transceiver.
 | 
				
			||||||
 | 
						// We could use any consecutive bsn, but lets use ones around the current time
 | 
				
			||||||
 | 
						// just to make sure they get through in case someone is triaging somewhere.
 | 
				
			||||||
 | 
						// Sending all 12 blocks is 2x overkill because the modulus in Transceiver::setModulus
 | 
				
			||||||
 | 
						// for type IGPRS is set the same as type I which is only 26, not 52.
 | 
				
			||||||
 | 
						RLCBSN_t bsn = FrameNumber2BSN(gBTS.time().FN()) + 1;
 | 
				
			||||||
 | 
						for (int i = 0; i < 12; i++, bsn = bsn + 1) {
 | 
				
			||||||
 | 
							GPRSLOG(1) <<"sendIdleFrame"<<LOGVAR2("TN",TN())<<LOGVAR(bsn)<<LOGVAR(i);
 | 
				
			||||||
 | 
							mchDownlink->sendIdleFrame(bsn);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mchOldFec->setGPRS(true,this);
 | 
				
			||||||
 | 
						debug_test();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void PDCHL1FEC::mchStop() {
 | 
				
			||||||
 | 
						getRadio()->setSlot(TN(),Transceiver::I);
 | 
				
			||||||
 | 
						mchOldFec->setGPRS(false,NULL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PDCHL1FEC::PDCHL1FEC(TCHFACCHLogicalChannel *wlogchan) :
 | 
				
			||||||
 | 
						PDCHCommon(this), mchOldFec(wlogchan->debugGetL1()), mchLogChan(wlogchan), mchTFIs(gTFIs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Warning: These initializations use some of the variables in the init list above.
 | 
				
			||||||
 | 
						mchUplink = new PDCHL1Uplink(this);
 | 
				
			||||||
 | 
						mchDownlink = new PDCHL1Downlink(this);
 | 
				
			||||||
 | 
						Stats.countPDCH++;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, PDCHL1FEC *ch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << (ch ? ch->shortId() : "PDCH#(null)");
 | 
				
			||||||
 | 
						return os;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					PDCHL1FEC::PDCHL1FEC()
 | 
				
			||||||
 | 
						: PDCHCommon(this)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mchUplink = new PDCHL1Uplink(this);
 | 
				
			||||||
 | 
						mchDownlink = new PDCHL1Downlink(this);
 | 
				
			||||||
 | 
						mchOldFec = NULL;
 | 
				
			||||||
 | 
						mchLogChan = NULL;
 | 
				
			||||||
 | 
						mchTFIs = gTFIs;
 | 
				
			||||||
 | 
						//mchOpen();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// We dont really need to remember the LogicalChannel, but maybe we'll need it in the future.
 | 
				
			||||||
 | 
					void PDCHL1FEC::mchOpen(TCHFACCHLogicalChannel *wlogchan)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// setGPRS has two affects: getTCH will consider the channel in-use;
 | 
				
			||||||
 | 
						// and bursts start being delivered to us.
 | 
				
			||||||
 | 
						// Note that the getTCH function normally doesnt even pay attention to whether
 | 
				
			||||||
 | 
						// the channel is 'open' or not; it calls recyclable() that checks timers
 | 
				
			||||||
 | 
						// and reuses the chan if they have expired.
 | 
				
			||||||
 | 
						mchLogChan = wlogchan;
 | 
				
			||||||
 | 
						devassert(mchLogChan->inUseByGPRS());
 | 
				
			||||||
 | 
						mchOldFec = mchLogChan->debugGetL1();
 | 
				
			||||||
 | 
						//mchReady = true;		// finally
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PDCHL1FEC::~PDCHL1FEC() {
 | 
				
			||||||
 | 
						gL2MAC.macForgetCh(this);
 | 
				
			||||||
 | 
						delete mchUplink;
 | 
				
			||||||
 | 
						delete mchDownlink;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PDCHL1FEC::mchDump(std::ostream&os, bool verbose)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << " PDCH"<<LOGVAR2("ARFCN", ARFCN())
 | 
				
			||||||
 | 
							<< LOGVAR2("TN",TN()) << LOGVAR2("FER",fmtfloat2(100.0*FER())) << "%" << "\n";
 | 
				
			||||||
 | 
						if (verbose) {
 | 
				
			||||||
 | 
							os <<"\t"; dumpReservations(os);
 | 
				
			||||||
 | 
							os <<"\t"; usfDump(os);
 | 
				
			||||||
 | 
							os <<"\t"; mchTFIs->tfiDump(os);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//void PDCHL1Downlink::rollForward()
 | 
				
			||||||
 | 
					//{
 | 
				
			||||||
 | 
					//	// Calculate the TDMA paramters for the next transmission.
 | 
				
			||||||
 | 
					//	// This implements GSM 05.02 Clause 7 for the transmit side.
 | 
				
			||||||
 | 
					//	mchPrevWriteTime = mchNextWriteTime;
 | 
				
			||||||
 | 
					//	mchTotalBursts++;
 | 
				
			||||||
 | 
					//	mchNextWriteTime.rollForward(mchMapping.frameMapping(mchTotalBursts),mchMapping.repeatLength());
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if FEC_DEBUG
 | 
				
			||||||
 | 
					GprsDecoder debugDecoder;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Send the specified BitVector at the specified block time.
 | 
				
			||||||
 | 
					void PDCHL1Downlink::transmit(RLCBSN_t bsn, BitVector *mI, const int *qbits, int transceiverflags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						parent()->debug_test();
 | 
				
			||||||
 | 
						// Format the bits into the bursts.
 | 
				
			||||||
 | 
						// GSM 05.03 4.1.5, 05.02 5.2.3
 | 
				
			||||||
 | 
						// NO! Dont do a wait here.  The MAC serviceloop does this for all channels.
 | 
				
			||||||
 | 
						// waitToSend();		// Don't get too far ahead of the clock.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ARFCNManager *radio = getRadio();
 | 
				
			||||||
 | 
						if (!radio) {
 | 
				
			||||||
 | 
							// For some testing, we might not have a radio connected.
 | 
				
			||||||
 | 
							// That's OK, as long as we know it.
 | 
				
			||||||
 | 
							GLOG(INFO) << "XCCHL1Encoder with no radio, dumping frames";
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int fn = bsn.FN();
 | 
				
			||||||
 | 
						int tn = TN();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (int qi=0,B=0; B<4; B++) {
 | 
				
			||||||
 | 
							Time nextWriteTime(fn,tn | transceiverflags);
 | 
				
			||||||
 | 
							mchBurst.time(nextWriteTime);
 | 
				
			||||||
 | 
							// Copy in the "encrypted" bits, GSM 05.03 4.1.5, 05.02 5.2.3.
 | 
				
			||||||
 | 
							//OBJLOG(DEBUG) << "transmit mI["<<B<<"]=" << mI[B];
 | 
				
			||||||
 | 
							mI[B].segment(0,57).copyToSegment(mchBurst,3);
 | 
				
			||||||
 | 
							mI[B].segment(57,57).copyToSegment(mchBurst,88);
 | 
				
			||||||
 | 
							mchBurst.Hl(qbits[qi++]);
 | 
				
			||||||
 | 
							mchBurst.Hu(qbits[qi++]);
 | 
				
			||||||
 | 
							// Send it to the radio.
 | 
				
			||||||
 | 
							//OBJLOG(DEBUG) << "transmit mchBurst=" << mchBurst;
 | 
				
			||||||
 | 
							if (gConfig.getBool("Control.GSMTAP.GPRS")) {
 | 
				
			||||||
 | 
								// Send to GSMTAP.
 | 
				
			||||||
 | 
								gWriteGSMTAP(ARFCN(),TN(),gBSNNext.FN(),
 | 
				
			||||||
 | 
										TDMA_PDCH,
 | 
				
			||||||
 | 
										false,	// not SACCH
 | 
				
			||||||
 | 
										false,	// this is a downlink
 | 
				
			||||||
 | 
										mchBurst,
 | 
				
			||||||
 | 
										GSMTAP_TYPE_UM_BURST);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					#if FEC_DEBUG
 | 
				
			||||||
 | 
							if (1) {
 | 
				
			||||||
 | 
								// Try decoding the frame we just encoded to see if it is correct.
 | 
				
			||||||
 | 
								devassert(mchBurst.size() == gSlotLen);
 | 
				
			||||||
 | 
								RxBurst rxb(mchBurst);
 | 
				
			||||||
 | 
								ChannelCodingType cc;
 | 
				
			||||||
 | 
								decodeLowSide(rxb, B, debugDecoder, &cc);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							radio->writeHighSideTx(mchBurst,"GPRS");
 | 
				
			||||||
 | 
							fn++;	// This cannot overflow because it is within an RLC block.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static GSM::TypeAndOffset frame2GsmTapType(BitVector &frame)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (frame.peekField(0,2)) {	// Mac control field.
 | 
				
			||||||
 | 
							case MACPayloadType::RLCControl:
 | 
				
			||||||
 | 
								return TDMA_PACCH;
 | 
				
			||||||
 | 
							case MACPayloadType::RLCData:
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return TDMA_PDCH;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PDCHL1Downlink::send1Frame(BitVector& frame,ChannelCodingType encoding, bool idle)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!idle && gConfig.getBool("Control.GSMTAP.GPRS")) {
 | 
				
			||||||
 | 
							// Send to GSMTAP.
 | 
				
			||||||
 | 
							gWriteGSMTAP(ARFCN(),TN(),gBSNNext.FN(),
 | 
				
			||||||
 | 
									frame2GsmTapType(frame),
 | 
				
			||||||
 | 
									false,	// not SACCH
 | 
				
			||||||
 | 
									false,	// this is a downlink
 | 
				
			||||||
 | 
									frame);	// The data.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (encoding) {
 | 
				
			||||||
 | 
						case ChannelCodingCS1:
 | 
				
			||||||
 | 
							// Process the 184 bit (23 byte) frame, leave result in mI.
 | 
				
			||||||
 | 
							//mchCS1Enc.encodeFrame41(frame,0);
 | 
				
			||||||
 | 
							//transmit(gBSNNext,mchCS1Enc.mI,qCS1,0);
 | 
				
			||||||
 | 
							mchEnc.encodeCS1(frame);
 | 
				
			||||||
 | 
							transmit(gBSNNext,mchEnc.mI,qCS1,0);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case ChannelCodingCS4:
 | 
				
			||||||
 | 
							//std::cout << "WARNING: Using CS4\n";
 | 
				
			||||||
 | 
							// This did not help the 3105/3101 errors:
 | 
				
			||||||
 | 
							//mchCS4Enc.initCS4();	// DEBUG TEST!!  Didnt help.
 | 
				
			||||||
 | 
							//mchCS4Enc.encodeCS4(frame);	// Result left in mI[].
 | 
				
			||||||
 | 
							//transmit(gBSNNext,mchCS4Enc.mI,qCS4,0);
 | 
				
			||||||
 | 
							mchEnc.encodeCS4(frame);	// Result left in mI[].
 | 
				
			||||||
 | 
							transmit(gBSNNext,mchEnc.mI,qCS4,0);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							LOG(ERR) << "unrecognized GPRS channel coding " << (int)encoding;
 | 
				
			||||||
 | 
							devassert(0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return true if we send a block on the downlink.
 | 
				
			||||||
 | 
					bool PDCHL1Downlink::send1DataFrame(
 | 
				
			||||||
 | 
						RLCDownEngine *engdown,
 | 
				
			||||||
 | 
						RLCDownlinkDataBlock *block,	// block to send.
 | 
				
			||||||
 | 
						int makeres,					// 0 = no res, 1 = optional res, 2 = required res.
 | 
				
			||||||
 | 
						MsgTransactionType mttype,	// Type of reservation
 | 
				
			||||||
 | 
						unsigned *pcounter)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						//ScopedLock lock(testlock);
 | 
				
			||||||
 | 
						TBF *tbf = engdown->getTBF();
 | 
				
			||||||
 | 
						if (! setMACFields(block,mchParent,tbf,makeres,mttype,pcounter)) { return false; }
 | 
				
			||||||
 | 
						// The rest of the RLC header is already set, but we did not know the tfi
 | 
				
			||||||
 | 
						// when we created the RLCDownlinkDataBlocks (because tbf not yet attached)
 | 
				
			||||||
 | 
						// so set tfi now that we know.  Update 8-2012: Above comment is stale because we
 | 
				
			||||||
 | 
						// make the RLCDownlinkBlocks on the fly now.
 | 
				
			||||||
 | 
						block->mTFI = tbf->mtTFI;
 | 
				
			||||||
 | 
						// block->mPR = 1;	// DEBUG test; made no diff.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tbf->talkedDown();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						BitVector tobits = block->getBitVector(); // tobits deallocated when this function exits.
 | 
				
			||||||
 | 
						if (block->mChannelCoding == 0) { devassert(tobits.size() == 184); }
 | 
				
			||||||
 | 
						if (GPRSDebug & 1) {
 | 
				
			||||||
 | 
							RLCBlockReservation *res = mchParent->getReservation(gBSNNext);
 | 
				
			||||||
 | 
							std::ostringstream sshdr;
 | 
				
			||||||
 | 
							block->text(sshdr,false); //block->RLCDownlinkDataBlockHeader::text(sshdr);
 | 
				
			||||||
 | 
							ByteVector content(tobits);
 | 
				
			||||||
 | 
							GPRSLOG(1) << "send1DataFrame "<<parent()<<" "<<tbf<<LOGVAR(tbf->mtExpectedAckBSN)
 | 
				
			||||||
 | 
								<< " "<<sshdr.str()
 | 
				
			||||||
 | 
								<<" "<<(res ? res->str() : "")
 | 
				
			||||||
 | 
								<< LOGVAR2("content",content);
 | 
				
			||||||
 | 
							//<< " enc="<<tbf->mtChannelCoding <<" "<<os.str() << "\nbits:" <<bits.str();
 | 
				
			||||||
 | 
							//<<" " <<block->str() <<"\nbits:" <<tobits.hexstr();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#if FEC_DEBUG
 | 
				
			||||||
 | 
						BitVector copybits; copybits.clone(tobits);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						send1Frame(tobits,block->mChannelCoding,0);
 | 
				
			||||||
 | 
					#if FEC_DEBUG
 | 
				
			||||||
 | 
						BitVector *result = debugDecoder.getResult();
 | 
				
			||||||
 | 
						devassert(result);
 | 
				
			||||||
 | 
						devassert(copybits == tobits);
 | 
				
			||||||
 | 
						if (result && !(*result == tobits)) {
 | 
				
			||||||
 | 
							int diffbit = -1;
 | 
				
			||||||
 | 
							char thing[500];
 | 
				
			||||||
 | 
							for (int i = 0; i < (int)result->size(); i++) {
 | 
				
			||||||
 | 
								thing[i] = '-';
 | 
				
			||||||
 | 
								if (result->bit(i) != tobits.bit(i)) {
 | 
				
			||||||
 | 
									if (diffbit == -1) diffbit = i;
 | 
				
			||||||
 | 
									thing[i] = '0' + result->bit(i);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							thing[result->size()] = 0;
 | 
				
			||||||
 | 
							GPRSLOG(1) <<"encoding error" <<LOGVAR2("cs",(int)debugDecoder.getCS())
 | 
				
			||||||
 | 
								<<LOGVAR(diffbit)
 | 
				
			||||||
 | 
								<<LOGVAR2("in:size",tobits.size()) <<LOGVAR2("out:size",result->size())
 | 
				
			||||||
 | 
								<<"\n"<<tobits
 | 
				
			||||||
 | 
								<<"\n"<<*result
 | 
				
			||||||
 | 
								<<"\n"<<thing;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							//GPRSLOG(1) <<"encoding ok" <<LOGVAR2("cs",(int)debugDecoder.getCS());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Send the idle frame at specified Transceiver::SET_FILLER_FRAME)
 | 
				
			||||||
 | 
					// Do not call send1msgframe, because it would try to set MAC fields.
 | 
				
			||||||
 | 
					void PDCHL1Downlink::sendIdleFrame(RLCBSN_t bsn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						RLCMsgPacketDownlinkDummyControlBlock *msg = new RLCMsgPacketDownlinkDummyControlBlock();
 | 
				
			||||||
 | 
						devassert(msg->mUSF == 0);
 | 
				
			||||||
 | 
						BitVector tobits(RLCBlockSizeInBits[ChannelCodingCS1]);
 | 
				
			||||||
 | 
						msg->write(tobits);
 | 
				
			||||||
 | 
						delete msg;
 | 
				
			||||||
 | 
						//mchCS1Enc.encodeFrame41(tobits,0);
 | 
				
			||||||
 | 
						//transmit(bsn,mchCS1Enc.mI,qCS1,Transceiver::SET_FILLER_FRAME);
 | 
				
			||||||
 | 
						mchEnc.encodeCS1(tobits);
 | 
				
			||||||
 | 
						transmit(bsn,mchEnc.mI,qCS1,Transceiver::SET_FILLER_FRAME);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PDCHL1Downlink::bugFixIdleFrame()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// DEBUG: We are only using this function to fix this problem for now.
 | 
				
			||||||
 | 
						if (gFixIdleFrame) {
 | 
				
			||||||
 | 
							// For this debug purpose, the mssage is sent on the next frame
 | 
				
			||||||
 | 
							// TODO: debug purpose only! This only works for one channel!
 | 
				
			||||||
 | 
							//Time tnext(gBSNNext.FN());
 | 
				
			||||||
 | 
							//gBTS.clock().wait(tnext);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Did we make it in time?
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						Time tnow = gBTS.time();
 | 
				
			||||||
 | 
						int fn = tnow.FN();
 | 
				
			||||||
 | 
						int mfn = (fn / 13);			// how many 13-multiframes
 | 
				
			||||||
 | 
						int rem = (fn - (mfn*13));	// how many blocks within the last multiframe.
 | 
				
			||||||
 | 
						int tbsn = mfn * 3 + ((rem==12) ? 2 : (rem/4));
 | 
				
			||||||
 | 
						GPRSLOG(2) <<"idleframe"<<LOGVAR(fn)<<LOGVAR(tbsn)<<LOGVAR(rem);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/***
 | 
				
			||||||
 | 
						if (mchIdleFrame.size() == 0) {
 | 
				
			||||||
 | 
							RLCMsgPacketDownlinkDummyControlBlock *dummymsg = new RLCMsgPacketDownlinkDummyControlBlock();
 | 
				
			||||||
 | 
							mchIdleFrame.set(BitVector(RLCBlockSizeInBits[ChannelCodingCS1]));
 | 
				
			||||||
 | 
							dummymsg->write(mchIdleFrame);
 | 
				
			||||||
 | 
							delete dummymsg;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						send1Frame(mchIdleFrame,ChannelCodingCS1,true);
 | 
				
			||||||
 | 
						***/
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return true if we send a block on the downlink.
 | 
				
			||||||
 | 
					bool PDCHL1Downlink::send1MsgFrame(
 | 
				
			||||||
 | 
						TBF *tbf,					// The TBF sending the message, or NULL for an idle frame.
 | 
				
			||||||
 | 
						RLCDownlinkMessage *msg,	// The message.
 | 
				
			||||||
 | 
						int makeres,			// 0 = no res, 1 = optional res, 2 = required res.
 | 
				
			||||||
 | 
						MsgTransactionType mttype,	// Type of reservation
 | 
				
			||||||
 | 
						unsigned *pcounter)		// If non-null, incremented if a reservation is made.
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (! setMACFields(msg,mchParent,msg->mTBF,makeres,mttype,pcounter)) {
 | 
				
			||||||
 | 
							delete msg;	// oh well.  
 | 
				
			||||||
 | 
							return false;	// This allows some other tbf to try to use this downlink block.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						bool dummy = msg->mMessageType == RLCDownlinkMessage::PacketDownlinkDummyControlBlock;
 | 
				
			||||||
 | 
						bool idle = dummy && msg->isMacUnused();
 | 
				
			||||||
 | 
						if (idle && 0 == gConfig.getNum("GPRS.SendIdleFrames")) {
 | 
				
			||||||
 | 
							delete msg;		// Let the transceiver send an idle frame.
 | 
				
			||||||
 | 
							return false;	// This return value will not be checked.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (tbf) { tbf->talkedDown(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Convert to a BitVector.  Messages always use CS-1 encoding.
 | 
				
			||||||
 | 
						BitVector tobits(RLCBlockSizeInBits[ChannelCodingCS1]);
 | 
				
			||||||
 | 
						msg->write(tobits);
 | 
				
			||||||
 | 
						// The possible downlink debug things we want to see are:
 | 
				
			||||||
 | 
						// 2: Only non-dummy messages.
 | 
				
			||||||
 | 
						// 32: include messages with non-idle MAC header, means mUSF or mSP.
 | 
				
			||||||
 | 
						// 1024: all messages including dummy ones.
 | 
				
			||||||
 | 
						if (GPRSDebug) {
 | 
				
			||||||
 | 
							if ((!dummy && (GPRSDebug&2)) || (!idle && (GPRSDebug&32)) || (GPRSDebug&1024)) {
 | 
				
			||||||
 | 
								ByteVector content(tobits);
 | 
				
			||||||
 | 
								GPRSLOG(2|32|1024) << "send1MsgFrame "<<parent()
 | 
				
			||||||
 | 
									<<" "<<msg->mTBF<< " "<<msg->str()
 | 
				
			||||||
 | 
									<< " " <<LOGVAR2("content",content);
 | 
				
			||||||
 | 
									// The res is unrelated to the message, and confusing, so dont print it:
 | 
				
			||||||
 | 
									//<<" "<<(res ? res->str() : "");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
						// The below is what went out in release 3.0:
 | 
				
			||||||
 | 
						if (GPRSDebug & (1|32)) {
 | 
				
			||||||
 | 
							//RLCBlockReservation *res = mchParent->getReservation(gBSNNext);
 | 
				
			||||||
 | 
							//std::ostringstream ssres;
 | 
				
			||||||
 | 
							//if (res) ssres << res;
 | 
				
			||||||
 | 
							if (! idle || (GPRSDebug & 1024)) {
 | 
				
			||||||
 | 
								//ostringstream bits;
 | 
				
			||||||
 | 
								//tobits.hex(bits);
 | 
				
			||||||
 | 
								//GPRSLOG(1) << "send1MsgFrame "<<msg->mTBF<< " "<<msg->str() << "\nbits:"<<tobits.hexstr();
 | 
				
			||||||
 | 
								ByteVector content(tobits);
 | 
				
			||||||
 | 
								GPRSLOG(1) << "send1MsgFrame "<<parent()<<" "<<msg->mTBF<< " "<<msg->str() <<" "
 | 
				
			||||||
 | 
									<< LOGVAR2("content",content);
 | 
				
			||||||
 | 
								// This res is unrelated to the message, and confusing, so dont print it:
 | 
				
			||||||
 | 
									//<<" "<<(res ? res->str() : "");
 | 
				
			||||||
 | 
							} else if (msg->mUSF) {
 | 
				
			||||||
 | 
								GPRSLOG(32) << "send1MsgFrame "<<parent()<<" "<<msg->mTBF<< " "<<msg->str() <<" ";
 | 
				
			||||||
 | 
									//<<" "<<(res ? res->str() : "");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						delete msg;
 | 
				
			||||||
 | 
						send1Frame(tobits,ChannelCodingCS1,idle);
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PDCHL1Downlink::initBursts(L1FEC *oldfec)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// unused: mchFillerBurst = GSM::TxBurst(GSM::gDummyBurst);	// TODO: This may not be correct.
 | 
				
			||||||
 | 
																// Should probably be RLC Dummy control message.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set up the training sequence since they'll be the same for all bursts.
 | 
				
			||||||
 | 
						// training sequence, GSM 05.02 5.2.3
 | 
				
			||||||
 | 
						// (pat) Set from the BSC color-code from the original GSM channel.
 | 
				
			||||||
 | 
						GSM::gTrainingSequence[oldfec->TSC()].copyToSegment(mchBurst,61);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Determine CS from the qbits.
 | 
				
			||||||
 | 
					ChannelCodingType GprsDecoder::getCS()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// TODO: Make this more robust.
 | 
				
			||||||
 | 
						// Currently we only support CS1 or CS4, so just look at the first bit.
 | 
				
			||||||
 | 
						if (qbits[0]) {
 | 
				
			||||||
 | 
							return ChannelCodingCS1;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return ChannelCodingCS4;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BitVector *GprsDecoder::getResult()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (getCS()) {
 | 
				
			||||||
 | 
						case ChannelCodingCS4:
 | 
				
			||||||
 | 
							return &mD_CS4;
 | 
				
			||||||
 | 
						case ChannelCodingCS1:
 | 
				
			||||||
 | 
							return &mD;
 | 
				
			||||||
 | 
						default: devassert(0);	// Others not supported yet.
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool GprsDecoder::decodeCS4()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Incoming data is in SoftVector mC(456) and has already been deinterleaved.
 | 
				
			||||||
 | 
						// Convert the SoftVector directly into bits: data + parity:
 | 
				
			||||||
 | 
						// The first 12 bits need to be reconverted to 3 bits of usf.
 | 
				
			||||||
 | 
						// Yes, they do this even on uplink, where there is no usf 5.03 sec 5.1.
 | 
				
			||||||
 | 
						// Parity is run on the remaining 447 (=456-12+3) bits, which consists
 | 
				
			||||||
 | 
						// of 424 of useful data, 7 bits of zeros, and 16 bits of parity.
 | 
				
			||||||
 | 
						unsigned reverseUsf = decodeUSF(mC);
 | 
				
			||||||
 | 
						mDP_CS4.fillField(0,reverseUsf,3);
 | 
				
			||||||
 | 
						// We are grubbing into the arrays.  TODO: move this into the classes somewhere.
 | 
				
			||||||
 | 
						float *in = mC.begin() + 12;
 | 
				
			||||||
 | 
						char *out = mDP_CS4.begin() + 3;
 | 
				
			||||||
 | 
						for (int i = 12; i < 456; i++) {
 | 
				
			||||||
 | 
							*out++ = *in++ > 0.5 ? 1 : 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						BitVector parity(mDP_CS4.segment(440-12+3,16));
 | 
				
			||||||
 | 
						parity.invert();
 | 
				
			||||||
 | 
						unsigned syndrome = mBlockCoder_CS4.syndrome(mDP_CS4);
 | 
				
			||||||
 | 
						// Result is in mD_CS4.
 | 
				
			||||||
 | 
						return (syndrome==0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Process the 184 bit frame, starting at offset, add parity, encode.
 | 
				
			||||||
 | 
					// Result is left in mI, representing 4 radio bursts.
 | 
				
			||||||
 | 
					void GprsEncoder::encodeCS1(const BitVector &src)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						encodeFrame41(src,0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static BitVector mCcopy;
 | 
				
			||||||
 | 
					void GprsEncoder::encodeCS4(const BitVector &src)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						//if (sFecDebug) GPRSLOG(1) <<"encodeCS4 src\n"<<src;
 | 
				
			||||||
 | 
						src.copyToSegment(mD_CS4,0,53*8);
 | 
				
			||||||
 | 
						//if (sFecDebug) GPRSLOG(1) <<"encodeCS4 mD_CS4\n"<<mD_CS4;
 | 
				
			||||||
 | 
						// mC.zero();	// DEBUG TEST!!  Did not help.
 | 
				
			||||||
 | 
						mD_CS4.fillField(53*8,0,7);		// zero out 7 spare bits.
 | 
				
			||||||
 | 
						mD_CS4.LSB8MSB();	// Ignores the last incomplete byte of 7 zero bits.
 | 
				
			||||||
 | 
						//if (sFecDebug) GPRSLOG(1) <<"mC before parity\n"<<mC;
 | 
				
			||||||
 | 
						// Parity is computed on original D before doing the USF translation above.
 | 
				
			||||||
 | 
						mBlockCoder_CS4.writeParityWord(mD_CS4,mP_CS4);
 | 
				
			||||||
 | 
						// Note that usf has been moved to the first three bits by the byte swapping above,
 | 
				
			||||||
 | 
						// so when we write the 12 bits of GPRSUSFEncoding for usf into mC, it will overwrite
 | 
				
			||||||
 | 
						// the original 3 parity bits.
 | 
				
			||||||
 | 
						int reverseUsf = mD_CS4.peekField(0,3);
 | 
				
			||||||
 | 
						// mU overwrites the first 3 bits of mD within mC.
 | 
				
			||||||
 | 
						mU_CS4.fillField(0,GPRS::GPRSUSFEncoding[reverseUsf],12);
 | 
				
			||||||
 | 
						// Result is left in mC.
 | 
				
			||||||
 | 
						devassert(mC.peekField(0,12) == (unsigned) GPRS::GPRSUSFEncoding[reverseUsf]);
 | 
				
			||||||
 | 
						devassert(mC.peekField(433,7) == 0);	// unused bits not modified.
 | 
				
			||||||
 | 
						//if (sFecDebug) GPRSLOG(1) <<"mC before interleave\n"<<mC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						interleave41();	// Interleaves mC into mI.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return decoded frame if success and B == 3, otherwise NULL.
 | 
				
			||||||
 | 
					static BitVector *decodeLowSide(const RxBurst &inBurst, int B, GprsDecoder &decoder, ChannelCodingType *ccPtr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						inBurst.data1().copyToSegment(decoder.mI[B],0);
 | 
				
			||||||
 | 
						inBurst.data2().copyToSegment(decoder.mI[B],57);
 | 
				
			||||||
 | 
						// Save the stealing bits:
 | 
				
			||||||
 | 
						// TODO: Save these as floats and do a correlation to pick the encoding.
 | 
				
			||||||
 | 
						decoder.qbits[2*B] = inBurst.Hl();
 | 
				
			||||||
 | 
						decoder.qbits[2*B+1] = inBurst.Hu();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (B != 3) { return NULL; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						decoder.deinterleave();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool success;
 | 
				
			||||||
 | 
						BitVector *result;
 | 
				
			||||||
 | 
						switch ((*ccPtr = decoder.getCS())) {
 | 
				
			||||||
 | 
						case ChannelCodingCS4:
 | 
				
			||||||
 | 
							success = decoder.decodeCS4();
 | 
				
			||||||
 | 
							LOG(DEBUG) << "CS-4 success=" << success;
 | 
				
			||||||
 | 
							result = &decoder.mD_CS4;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case ChannelCodingCS1:
 | 
				
			||||||
 | 
							success = decoder.decode();
 | 
				
			||||||
 | 
							LOG(DEBUG) << "CS-1 success=" << success;
 | 
				
			||||||
 | 
							result = &decoder.mD;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default: devassert(0);	// Others not supported yet.
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (success) {
 | 
				
			||||||
 | 
							result->LSB8MSB();
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0	// Moved to SoftVector
 | 
				
			||||||
 | 
					// 1 is perfect and 0 means all the bits were 0.5
 | 
				
			||||||
 | 
					static float getEnergy(const SoftVector &vec,float &low)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int len = vec.size();
 | 
				
			||||||
 | 
						avg = 0; low = 1;
 | 
				
			||||||
 | 
						for (int i = 0; i < len; i++) {
 | 
				
			||||||
 | 
							float bit = vec[i];
 | 
				
			||||||
 | 
							float energy = 2*((bit < 0.5) ? (0.5-bit) : (bit-0.5));
 | 
				
			||||||
 | 
							if (energy < low) low = energy;
 | 
				
			||||||
 | 
							avg += energy/len;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// WARNING: This func runs in a separate thread.
 | 
				
			||||||
 | 
					void PDCHL1Uplink::writeLowSideRx(const RxBurst &inBurst)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						float low, avg = inBurst.getEnergy(&low);
 | 
				
			||||||
 | 
						//if (avg > 0.7) { OBJLOG(DEBUG) << "PDCHL1Uplink " << inBurst; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//ScopedLock lock(testlock);
 | 
				
			||||||
 | 
						int burstfn = inBurst.time().FN();
 | 
				
			||||||
 | 
						int mfn = (burstfn / 13);			// how many 13-multiframes
 | 
				
			||||||
 | 
						int rem = (burstfn - (mfn*13));	// how many blocks within the last multiframe.
 | 
				
			||||||
 | 
						int B = rem % 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (avg > 0.5) { GPRSLOG(256) << "FEC:"<<LOGVAR(B)<<" "<<inBurst<<LOGVAR(avg); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ChannelCodingType cc;
 | 
				
			||||||
 | 
						BitVector *result = decodeLowSide(inBurst,B,mchCS14Dec,&cc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (B == 3) {
 | 
				
			||||||
 | 
							int burst_fn=burstfn-3;	// First fn in rlc block.
 | 
				
			||||||
 | 
							RLCBSN_t bsn = FrameNumber2BSN(burst_fn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (GPRSDebug) {
 | 
				
			||||||
 | 
								PDCHL1FEC *pdch = parent();
 | 
				
			||||||
 | 
								short *qbits = mchCS14Dec.qbits;
 | 
				
			||||||
 | 
								BitVector cshead(mchCS14Dec.mC.head(12).sliced());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								RLCBlockReservation *res = mchParent->getReservation(bsn);
 | 
				
			||||||
 | 
								int thisUsf = pdch->getUsf(bsn-2);
 | 
				
			||||||
 | 
								// If we miss a reservation or usf, print it:
 | 
				
			||||||
 | 
								int missedRes = avg>0.4 && !result && (res||thisUsf);
 | 
				
			||||||
 | 
								if (missedRes || (GPRSDebug & (result?4:256))) {
 | 
				
			||||||
 | 
									std::ostringstream ss;
 | 
				
			||||||
 | 
									char buf[30];
 | 
				
			||||||
 | 
								 	ss <<"writeLowSideRx "<<parent()
 | 
				
			||||||
 | 
										<<(result?" === good" : "=== bad")
 | 
				
			||||||
 | 
										<< (res?" res:" : "") <<(res ? res->str() : "")
 | 
				
			||||||
 | 
										//<<LOGVAR(cshead)
 | 
				
			||||||
 | 
										//<<LOGVAR2("cs",(int)mchCS14Dec.getCS())
 | 
				
			||||||
 | 
										<<LOGVAR(cc)
 | 
				
			||||||
 | 
										<<LOGVAR2("revusf",decodeUSF(mchCS14Dec.mC))
 | 
				
			||||||
 | 
										<<LOGVAR(burst_fn)<<LOGVAR(bsn) 
 | 
				
			||||||
 | 
										<<LOGVAR2("RSSI",inBurst.RSSI()) <<LOGVAR2("TE",inBurst.timingError())
 | 
				
			||||||
 | 
										// But lets print out the USFs bracketing this on either side.
 | 
				
			||||||
 | 
										<<getAnsweringUsfText(buf,bsn)
 | 
				
			||||||
 | 
										//<<" AnsweringUsf="<<pdch->getUsf(bsn-2)<<" "<<pdch->getUsf(bsn-1)
 | 
				
			||||||
 | 
										//<<" ["<<pdch->getUsf(bsn)<<"] "<<pdch->getUsf(bsn+1)<<" "<<pdch->getUsf(bsn+2)
 | 
				
			||||||
 | 
										<<" qbits="<<qbits[0]<<qbits[1]<<qbits[2]<<qbits[3]
 | 
				
			||||||
 | 
												   <<qbits[4]<<qbits[5]<<qbits[6]<<qbits[7]
 | 
				
			||||||
 | 
										<<LOGVAR(low)<<LOGVAR(avg)
 | 
				
			||||||
 | 
										;
 | 
				
			||||||
 | 
									if (missedRes) {
 | 
				
			||||||
 | 
										for (int i = 0; i < 4; i++) {
 | 
				
			||||||
 | 
											// There was an unanswered reservation or usf.
 | 
				
			||||||
 | 
											avg = mchCS14Dec.mI[i].getEnergy(&low);
 | 
				
			||||||
 | 
											GPRSLOG(1) << "energy["<<i<<"]:"<<LOGVAR(avg)<<LOGVAR(low)<<" "
 | 
				
			||||||
 | 
												<<mchCS14Dec.mI[i];
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									GLOG(DEBUG)<<ss.str();
 | 
				
			||||||
 | 
									// Make sure we see a decoder failure if it reoccurs.
 | 
				
			||||||
 | 
									if (missedRes) std::cout <<ss.str() <<"\n";
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} // if GPRSDebug
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (result) {
 | 
				
			||||||
 | 
								// Check clock skew for debugging purposes.
 | 
				
			||||||
 | 
								static int cnt = 0;
 | 
				
			||||||
 | 
								if (bsn >= gBSNNext-1) {
 | 
				
			||||||
 | 
									if (cnt++ % 32 == 0) {
 | 
				
			||||||
 | 
										GLOG(ERR) << "Incoming burst at frame:"<<burst_fn
 | 
				
			||||||
 | 
											<<" is not sufficiently ahead of clock:"<<gBSNNext.FN();
 | 
				
			||||||
 | 
										if (GPRSDebug) {
 | 
				
			||||||
 | 
										std::cout << "Incoming burst at frame:"<<burst_fn
 | 
				
			||||||
 | 
											<<" is not sufficiently ahead of clock:"<<gBSNNext.FN()<<"\n";
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								countGoodFrame();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// The four frame radio block has been decoded and is in mD.
 | 
				
			||||||
 | 
								if (gConfig.getBool("Control.GSMTAP.GPRS")) {
 | 
				
			||||||
 | 
									// Send to GSMTAP.  Untested.
 | 
				
			||||||
 | 
									gWriteGSMTAP(ARFCN(),TN(),gBSNNext.FN(), //GSM::TDMA_PACCH,
 | 
				
			||||||
 | 
											frame2GsmTapType(*result),
 | 
				
			||||||
 | 
											false,	// not SACCH
 | 
				
			||||||
 | 
											true,	// this is an uplink.
 | 
				
			||||||
 | 
											*result);	// The data.
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								mchUplinkData.write(new RLCRawBlock(bsn,*result,inBurst.RSSI(),inBurst.timingError(),cc));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								countBadFrame();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// We dont have a full 4 bursts yet, and we rarely care about these
 | 
				
			||||||
 | 
							// intermediate results, but here is a way to see them:
 | 
				
			||||||
 | 
							GPRSLOG(64) <<"writeLowSideRx "<<parent()<<LOGVAR(burstfn)<<LOGVAR(B) 
 | 
				
			||||||
 | 
								<<" RSSI=" <<inBurst.RSSI() << " timing=" << inBurst.timingError();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is an entry point from other directories.
 | 
				
			||||||
 | 
					// Just bind the inburst with the associated channel and call the routine above.
 | 
				
			||||||
 | 
					void GPRSWriteLowSideRx(const GSM::RxBurst& inBurst, PDCHL1FEC*pdch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pdch->uplink()->writeLowSideRx(inBurst);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Transfer one RLCBlock, which is four frames, to the radio layer.
 | 
				
			||||||
 | 
					// Copied from XXCHL1Encoder::transmit()
 | 
				
			||||||
 | 
					// void PDCHL1Downlink::transmit(mI)
 | 
				
			||||||
 | 
					// See XCCHL1Encoder:transmit(mI)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Code duplicated almost verbatim from L1Encoder.
 | 
				
			||||||
 | 
					// Inability to share code embedded in a method is one of the problems
 | 
				
			||||||
 | 
					// with object oriented programming.
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					void PDCHL1Downlink::mchResync()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// If the encoder's clock is far from the current BTS clock,
 | 
				
			||||||
 | 
						// get it caught up to something reasonable.
 | 
				
			||||||
 | 
						Time now = gBTS.time();
 | 
				
			||||||
 | 
						int32_t delta = mchNextWriteTime-now;
 | 
				
			||||||
 | 
						GPRSLOG(8) << "PDCHL1Downlink" <<LOGVAR(mchNextWriteTime)
 | 
				
			||||||
 | 
								<<LOGVAR(now)<< LOGVAR(delta);
 | 
				
			||||||
 | 
						if ((delta<0) || (delta>(51*26))) {
 | 
				
			||||||
 | 
							mchNextWriteTime = now;
 | 
				
			||||||
 | 
							//mchNextWriteTime.TN(now.TN());	// unneeded?
 | 
				
			||||||
 | 
							mchNextWriteTime.rollForward(mchMapping.frameMapping(mchTotalBursts),mchMapping.repeatLength());
 | 
				
			||||||
 | 
							GPRSLOG(2) <<"PDCHL1Downlink RESYNC" << LOGVAR(mchNextWriteTime) << LOGVAR(now);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Dispatch an RLC block on this downlink.
 | 
				
			||||||
 | 
					// This must run once for every Radio Block (4 TDMA frames or so) sent.
 | 
				
			||||||
 | 
					// It should be kept only as far enough ahead of the physical layer so that it never stalls.
 | 
				
			||||||
 | 
					// Based on: TCHFACCHL1Encoder::dispatch()
 | 
				
			||||||
 | 
					void PDCHL1Downlink::dlService()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Get right with the system clock.
 | 
				
			||||||
 | 
						// NO: mchResync();
 | 
				
			||||||
 | 
						static int debugCntTotal = 0, debugCntDummy = 0;
 | 
				
			||||||
 | 
						debugCntTotal++;
 | 
				
			||||||
 | 
						if ((GPRSDebug&512) || debugCntTotal % 1024 == 0) {
 | 
				
			||||||
 | 
							GPRSLOG(2) << "dlService sent total="<<debugCntTotal<<" dummy="<<debugCntDummy <<this->parent();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If gFixIdleFrame only send blocks on the even BSNs,
 | 
				
			||||||
 | 
						// and send idle frames on the odd BSNs, to make SURE that the
 | 
				
			||||||
 | 
						// RRBP and USF fields are cleared.
 | 
				
			||||||
 | 
						// This means that only the odd uplink BSNs will be used for uplink data.
 | 
				
			||||||
 | 
						// I also modified makeReservationInt to make the RRBP uplink reservations
 | 
				
			||||||
 | 
						// only on even frames, since they will be clear of data, but
 | 
				
			||||||
 | 
						// that is overkill for debugging.
 | 
				
			||||||
 | 
						// For reservations all we care is that the downlink RRBP field
 | 
				
			||||||
 | 
						// occurs only on even frames -
 | 
				
			||||||
 | 
						// the associated uplink block will be 3-7 blocks downtime from that,
 | 
				
			||||||
 | 
						// so could land on either even or odd frames.
 | 
				
			||||||
 | 
						if (gFixIdleFrame && (gBSNNext & 1)) { return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// I think we have to check active because the radio can get turned on/off
 | 
				
			||||||
 | 
						// completely without our knowledge.
 | 
				
			||||||
 | 
						// If this happens, the TBFs will expire, so we should probably destroy
 | 
				
			||||||
 | 
						// the entire GPRS machinery and start over when we get turned back on.
 | 
				
			||||||
 | 
						//if (! active()) {
 | 
				
			||||||
 | 
						//	mNextWriteTime += 52;	// Wait for a PCH multiframe.
 | 
				
			||||||
 | 
						//	gBTS.clock().wait(mNextWriteTime);
 | 
				
			||||||
 | 
						//	return;
 | 
				
			||||||
 | 
						//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Look for a data block to send.
 | 
				
			||||||
 | 
						// We did not queue these up in advance because the data that the engine
 | 
				
			||||||
 | 
						// wants to send may change every time it receives an ack/nack message.
 | 
				
			||||||
 | 
						//TBFList_t &list = gL2MAC.macTBFs;
 | 
				
			||||||
 | 
						//TBFList_t::iterator itr = list.begin(), e = list.end();
 | 
				
			||||||
 | 
						//for ( ; itr != e; itr++) {
 | 
				
			||||||
 | 
							//TBF *tbf = *itr;
 | 
				
			||||||
 | 
						TBF *tbf;
 | 
				
			||||||
 | 
						TBFList_t::iterator itr;
 | 
				
			||||||
 | 
						for (RListIterator<TBF*> itrl(gL2MAC.macTBFs); itrl.next(tbf,itr); ) {
 | 
				
			||||||
 | 
							if (!tbf->canUseDownlink(this)) {
 | 
				
			||||||
 | 
								GPRSLOG(4) <<"dlService"<<tbf<<" state "<<tbf->mtGetState()
 | 
				
			||||||
 | 
									<<" reqch:"<<tbf->mtMS->msPacch
 | 
				
			||||||
 | 
									<< " can not use downlink:"<<this->parent();
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							TBFState::type oldstate = tbf->mtGetState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (tbf->mtServiceDownlink(this)) {
 | 
				
			||||||
 | 
								GPRSLOG(2) <<"dlService"<<tbf<<LOGVAR(oldstate)<<" state="<<tbf->mtGetState()
 | 
				
			||||||
 | 
									<<" reqch:"<<tbf->mtMS->msPacch
 | 
				
			||||||
 | 
									<<" using ch:"<<this->parent();
 | 
				
			||||||
 | 
								// Move this tbf to end of the list so we may service someone else next time.
 | 
				
			||||||
 | 
								// TODO: If the tbf is using extended dynamic uplink, all ganged
 | 
				
			||||||
 | 
								// uplink channels are reserved at once, and so we are not sharing
 | 
				
			||||||
 | 
								// the other ganged uplinks with other TBFs that want to use them unless
 | 
				
			||||||
 | 
								// those TBFs also share this channel.
 | 
				
			||||||
 | 
								gL2MAC.macTBFs.erase(itr);
 | 
				
			||||||
 | 
								gL2MAC.macTBFs.push_back(tbf);
 | 
				
			||||||
 | 
								if (gFixIdleFrame) { bugFixIdleFrame(); }
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If nothing else, send a dummy message.
 | 
				
			||||||
 | 
						// We have to allocate it because we allocate all messages.
 | 
				
			||||||
 | 
						// Note that this message will have the MAC header fields USF and RRBP set by send1MsgFrame.
 | 
				
			||||||
 | 
						RLCMsgPacketDownlinkDummyControlBlock *dummymsg = new RLCMsgPacketDownlinkDummyControlBlock();
 | 
				
			||||||
 | 
						send1MsgFrame(NULL,dummymsg,0,MsgTransNone,NULL);
 | 
				
			||||||
 | 
						debugCntDummy++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (gFixIdleFrame) { bugFixIdleFrame(); }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is just a pass-through to TFIList.
 | 
				
			||||||
 | 
					void PDCHCommon::setTFITBF(int tfi, RLCDir::type dir, TBF *tbf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mchParent->mchTFIs->setTFITBF(tfi,dir,tbf);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TBF *PDCHCommon::getTFITBF(int tfi, RLCDirType dir)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						TBF *tbf = mchParent->mchTFIs->getTFITBF(dir,tfi);
 | 
				
			||||||
 | 
						if (tbf == NULL) {
 | 
				
			||||||
 | 
							// Somehow we lost track of this tbf.  Maybe the timers are set wrong,
 | 
				
			||||||
 | 
							// and we dumped it before the MS gave up on it.
 | 
				
			||||||
 | 
							GLOG(ERR) << "GPRS Radio Block Data Block with unrecognized tfi: " << tfi << " dir:"<<dir;
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return tbf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return the RF Power Control alpha and gamma value.
 | 
				
			||||||
 | 
					// ---------- From GSM05.08 10.2.1 ----------
 | 
				
			||||||
 | 
					// MS RF output power Pch is:
 | 
				
			||||||
 | 
					// Pch = min(Gamma0 - GammaCh - Alpha * (C + 48), PMAX)
 | 
				
			||||||
 | 
					// Alpha is a system param or optionally sent to MS in RLC messages (we dont.)
 | 
				
			||||||
 | 
					// C represents CCCH power measured inside the MS.
 | 
				
			||||||
 | 
					// Gamma0 = 39 dBm for GSM400, GSM900, GSM850, or 36 dBm for DCS1800 and PCS1900
 | 
				
			||||||
 | 
					// PMAX is MS_TXPWR_MAX_CCH if no PBCCH exists (our case.)
 | 
				
			||||||
 | 
					// ------------------------------------------
 | 
				
			||||||
 | 
					// The alpha value represents how much the MS should turn down its output
 | 
				
			||||||
 | 
					// power in response to its own measurement of CCCH input power.
 | 
				
			||||||
 | 
					// This alpha value is sent in the Packet Uplink/Downlink Assignment messages.
 | 
				
			||||||
 | 
					// The gamma value is subtracted from the max power.
 | 
				
			||||||
 | 
					// Basically, the BTS can take complete control of power by setting alpha to 0
 | 
				
			||||||
 | 
					// and using gamma, or let the MS take complete control by setting gamma to 0 and alpha to 1,
 | 
				
			||||||
 | 
					// or anything in between.
 | 
				
			||||||
 | 
					// TODO: Once we start talking to the MS, it will be RACHing to us on a regular basis,
 | 
				
			||||||
 | 
					// and will also be reporting lost bursts, so we could be fiddling with the power parameters 
 | 
				
			||||||
 | 
					// on a per-MS basis.  However, this function returns the default gamma alpha
 | 
				
			||||||
 | 
					// to be used for the initial single-block downlink assignment when we dont
 | 
				
			||||||
 | 
					// know what MS we are talking to yet.
 | 
				
			||||||
 | 
					// BEGINCONFIG
 | 
				
			||||||
 | 
					// 'GPRS.MS.Power.Alpha',10,1,0,'MS power control parameter; see GSM 05.08 10.2.1'
 | 
				
			||||||
 | 
					// 'GPRS.MS.Power.Gamma',31,1,0,'MS power control parameter; see GSM 05.08 10.2.1'
 | 
				
			||||||
 | 
					// ENDCONFIG
 | 
				
			||||||
 | 
					int GetPowerAlpha()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// God only knows what this should be.
 | 
				
			||||||
 | 
						int alpha = gConfig.getNum("GPRS.MS.Power.Alpha");
 | 
				
			||||||
 | 
						// The value runs 0..10 representing increments of 10%
 | 
				
			||||||
 | 
						return RN_BOUND(alpha,0,10);		// Bound to allowed values.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int GetPowerGamma()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// 0 means full power and let the MS control power.
 | 
				
			||||||
 | 
						int gamma = gConfig.getNum("GPRS.MS.Power.Gamma");
 | 
				
			||||||
 | 
						return RN_BOUND(gamma,0,31);		// Bound to allowed values, 5 bits.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int GetTimingAdvance(float timingError)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int initialTA = (int)(timingError + 0.5F);
 | 
				
			||||||
 | 
						initialTA = RN_BOUND(initialTA,0,62);	// why 62, not 63?
 | 
				
			||||||
 | 
						return initialTA;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
						// Turn on this PDCHL1
 | 
				
			||||||
 | 
						// Unused function: This is how you would reprogram TRXManager.
 | 
				
			||||||
 | 
						void PDCHL1FEC::snarf()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							assert(! encoder()->active());
 | 
				
			||||||
 | 
							assert(! decoder()->active());
 | 
				
			||||||
 | 
							// TODO: Wait until an appropriate time, lock everything in sight before doing this.
 | 
				
			||||||
 | 
							ARFCNManager*radio = encoder()->getRadio();
 | 
				
			||||||
 | 
							// Connect to the radio now.
 | 
				
			||||||
 | 
							mPDTCH.downstream(radio);
 | 
				
			||||||
 | 
							mPTCCH.downstream(radio);
 | 
				
			||||||
 | 
							mPDIdle.downstream(radio);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void PDCHL1FEC::unsnarf()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							assert(mlogchan);
 | 
				
			||||||
 | 
							ARFCNManager*radio = encoder()->getRadio();
 | 
				
			||||||
 | 
							// TODO: Wait until an appropriate time, lock everything in sight before doing this.
 | 
				
			||||||
 | 
							mlogchan->downstream(radio);
 | 
				
			||||||
 | 
							// TODO: This is not thread safe.  It would not work either,
 | 
				
			||||||
 | 
							// because the TRXManager function asserts that nobody got the channel earlier.
 | 
				
			||||||
 | 
							// Can we hook the channel inside the logical channel?
 | 
				
			||||||
 | 
							gBTS.addTCH(mlogchan);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										284
									
								
								GPRS/FEC.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								GPRS/FEC.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,284 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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 GPRS L1 radio channels, from GSM 05.02 and 05.03. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GPRSL1FEC_H
 | 
				
			||||||
 | 
					#define GPRSL1FEC_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <GSMCommon.h>
 | 
				
			||||||
 | 
					#include <GSMConfig.h>		// for gBTS
 | 
				
			||||||
 | 
					#include <GSML1FEC.h>
 | 
				
			||||||
 | 
					#include <GSMTransfer.h>	// for TxBurst
 | 
				
			||||||
 | 
					#include <GSMLogicalChannel.h> // for TCHFACCHLogicalChannel
 | 
				
			||||||
 | 
					#include "MAC.h"
 | 
				
			||||||
 | 
					using namespace GSM;
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
					class TBF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM05.03 sec 5.1.4 re GPRS CS-4 says: 16 bit parity with generator: D16 + D12 + D5 + 1,
 | 
				
			||||||
 | 
					static const unsigned long sCS4Generator = (1<<16) + (1<<12) + (1<<5) + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PDCHL1FEC;
 | 
				
			||||||
 | 
					class PDCHCommon
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						PDCHL1FEC *mchParent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PDCHCommon(PDCHL1FEC*wParent) { mchParent = wParent; }
 | 
				
			||||||
 | 
						ARFCNManager *getRadio();
 | 
				
			||||||
 | 
						unsigned TN();
 | 
				
			||||||
 | 
						unsigned ARFCN();
 | 
				
			||||||
 | 
						unsigned CN();
 | 
				
			||||||
 | 
						L1Decoder *getDecoder() const;
 | 
				
			||||||
 | 
						float FER() const;
 | 
				
			||||||
 | 
						void countGoodFrame();
 | 
				
			||||||
 | 
						void countBadFrame();
 | 
				
			||||||
 | 
						PDCHL1Uplink *uplink();
 | 
				
			||||||
 | 
						PDCHL1Downlink *downlink();
 | 
				
			||||||
 | 
						PDCHL1FEC *parent();	// If called from mchParent, returns itself.
 | 
				
			||||||
 | 
						TBF *getTFITBF(int tfi, RLCDirType dir);
 | 
				
			||||||
 | 
						void setTFITBF(int tfi, RLCDirType dir, TBF *TBF);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						const char *getAnsweringUsfText(char *buf, RLCBSN_t bsn); // Printable USFs around BSN for debugging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						char *shortId() {	// Return a short printable id for this channel.
 | 
				
			||||||
 | 
							static char buf[20];
 | 
				
			||||||
 | 
							sprintf(buf,"PDCH#%u:%u",getRadio()->ARFCN(),TN());
 | 
				
			||||||
 | 
							return buf;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For gprs channels, should we allocate them using getTCH(),
 | 
				
			||||||
 | 
					// which returns a TCHFACCHLogicalChannel which we have no use for,
 | 
				
			||||||
 | 
					// or should we get dedicated GPRS channels directly from TRXManager,
 | 
				
			||||||
 | 
					// which currently does not allow this.
 | 
				
			||||||
 | 
					// Answer: We are going to make the logical channels tri-state (inactive, RRactive, GPRSactive),
 | 
				
			||||||
 | 
					// and use getTCH to get them.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// There is one of these classes for each GPRS Data Channel, PDTCH.
 | 
				
			||||||
 | 
					// Downstream it attaches to a single Physical channel in L1FEC via mchEncoder and mchDecoder.
 | 
				
			||||||
 | 
					// TODO: I did this wrong.  This should be for a single ARFCN, but multiple
 | 
				
			||||||
 | 
					// upstream/downstream timeslots.
 | 
				
			||||||
 | 
					class PDCHL1FEC :
 | 
				
			||||||
 | 
						public L1UplinkReservation,
 | 
				
			||||||
 | 
						public PDCHCommon,
 | 
				
			||||||
 | 
						public USFList
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						PDCHL1Downlink *mchDownlink;
 | 
				
			||||||
 | 
						PDCHL1Uplink *mchUplink;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L1FEC *mchOldFec;		// The GSM TCH channel that this GPRS channel took over;
 | 
				
			||||||
 | 
											// it has the channel parameters.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Temporary: GPRS will not use anything in this LogicalChannel class, and we dont want
 | 
				
			||||||
 | 
						// the extra class hanging around, but currently the only way to dynamically
 | 
				
			||||||
 | 
						// allocate physical channels is via the associated logical channel.
 | 
				
			||||||
 | 
						TCHFACCHLogicalChannel *mchLogChan;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// The TFIs are a 5 bit handle for TBFs.  The USFs are a 3 bit handle for uplink TBFs.
 | 
				
			||||||
 | 
						TFIList *mchTFIs;// Points to the global TFIList.  Someday will point to the per-ARFCN TFIList.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void debug_test();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PDCHL1FEC(TCHFACCHLogicalChannel *wlogchan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Release the radio channel.
 | 
				
			||||||
 | 
						// GSM will start using it immediately.  TODO: Do we want to set a timer
 | 
				
			||||||
 | 
						// so it is not reused immediately?
 | 
				
			||||||
 | 
						~PDCHL1FEC();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Attach this GPRS channel to the specified GSM channel.
 | 
				
			||||||
 | 
						//void mchOpen(TCHFACCHLogicalChannel *wlogchan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void mchStart();
 | 
				
			||||||
 | 
						void mchStop();
 | 
				
			||||||
 | 
						void mchDump(std::ostream&os,bool verbose);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Return a description of PDTCH, which is the only one we care about.
 | 
				
			||||||
 | 
						// (We dont care about the associated SDCCH, whose frame is used in GPRS for
 | 
				
			||||||
 | 
						// continuous timing advance.)
 | 
				
			||||||
 | 
						// The packet channel description is the same as channel description except:
 | 
				
			||||||
 | 
						// From GSM04.08 10.5.25 table 10.5.2.25a table 10.5.58, and I quote:
 | 
				
			||||||
 | 
						// "The Channel type field (5 bit) shall be ignored by the receiver and
 | 
				
			||||||
 | 
						// all bits treated as spare. For backward compatibility
 | 
				
			||||||
 | 
						// reasons, the sender shall set the spare bits to binary '00001'."
 | 
				
			||||||
 | 
						// This doesnt matter in the slightest, because the typeAndOffset would
 | 
				
			||||||
 | 
						// have been TCHF_0 whose enum value is 1 anyway.
 | 
				
			||||||
 | 
						L3ChannelDescription packetChannelDescription()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							L1FEC *lf = mchOldFec;
 | 
				
			||||||
 | 
							return L3ChannelDescription((TypeAndOffset) 1, lf->TN(), lf->TSC(), lf->ARFCN());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, PDCHL1FEC *ch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For CS-1 decoding, just uses SharedL1Decoder.
 | 
				
			||||||
 | 
					// For CS-4 decoding: Uses the SharedL1Decoder through deinterleaving into mC.
 | 
				
			||||||
 | 
					class GprsDecoder : public SharedL1Decoder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Parity mBlockCoder_CS4;
 | 
				
			||||||
 | 
						BitVector mDP_CS4;
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						BitVector mD_CS4;
 | 
				
			||||||
 | 
						short qbits[8];
 | 
				
			||||||
 | 
						ChannelCodingType getCS();	// Determine CS from the qbits.
 | 
				
			||||||
 | 
						BitVector *getResult();
 | 
				
			||||||
 | 
						GprsDecoder() :
 | 
				
			||||||
 | 
							mBlockCoder_CS4(sCS4Generator,16,431+16),
 | 
				
			||||||
 | 
							mDP_CS4(431+16),
 | 
				
			||||||
 | 
							mD_CS4(mDP_CS4.head(424))
 | 
				
			||||||
 | 
							{}
 | 
				
			||||||
 | 
						bool decodeCS4();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CS-4 has 431 input data bits, which are always 424 real data bits (53 bytes)
 | 
				
			||||||
 | 
					// plus 7 unused bits that are set to 0, to make 431 data bits.
 | 
				
			||||||
 | 
					// The first 3 bits are usf encoded to 12 bits, to yield 440 bits.
 | 
				
			||||||
 | 
					// Then 16 bit parity bits yields 456 bits.
 | 
				
			||||||
 | 
					class GprsEncoder : public SharedL1Encoder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Parity mBlockCoder_CS4;
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// Uses SharedL1Encoder::mC for result vector
 | 
				
			||||||
 | 
						// Uses SharedL1Encoder::mI for the 4-way interleaved result vector.
 | 
				
			||||||
 | 
						BitVector mP_CS4;	// alias for parity part of mC
 | 
				
			||||||
 | 
						BitVector mU_CS4;	// alias for usf part of mC
 | 
				
			||||||
 | 
						BitVector mD_CS4;	// assembly area for parity.
 | 
				
			||||||
 | 
						GprsEncoder() :
 | 
				
			||||||
 | 
							SharedL1Encoder(),
 | 
				
			||||||
 | 
							mBlockCoder_CS4(sCS4Generator,16,431+16),
 | 
				
			||||||
 | 
							mP_CS4(mC.segment(440,16)),
 | 
				
			||||||
 | 
							mU_CS4(mC.segment(0,12)),
 | 
				
			||||||
 | 
							mD_CS4(mC.segment(12-3,431))
 | 
				
			||||||
 | 
							{}
 | 
				
			||||||
 | 
						void encodeCS4(const BitVector&src);
 | 
				
			||||||
 | 
						void encodeCS1(const BitVector &src);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PDCHL1Uplink : public PDCHCommon
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					#if GPRS_ENCODER
 | 
				
			||||||
 | 
						//SharedL1Decoder mchCS1Dec;
 | 
				
			||||||
 | 
						GprsDecoder mchCS14Dec;
 | 
				
			||||||
 | 
					#else	// This case does not compile yet.
 | 
				
			||||||
 | 
						L1Decoder mchCS1Dec;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						static const RLCDirType mchDir = RLCDir::Up;
 | 
				
			||||||
 | 
						// The uplink queue:
 | 
				
			||||||
 | 
						// There will typically only be one guy on here, and we could probably dispense
 | 
				
			||||||
 | 
						// with the queue, but it is safer to do it this way to avoid thread problems.
 | 
				
			||||||
 | 
						// InterthreadQueue template adds "*" so it is really a queue of BitVector*
 | 
				
			||||||
 | 
						InterthreadQueue<RLCRawBlock> mchUplinkData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PDCHL1Uplink(PDCHL1FEC *wParent) : PDCHCommon(wParent) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~PDCHL1Uplink() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeLowSideRx(const RxBurst &inBurst);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO: This needs to be per-MS.
 | 
				
			||||||
 | 
						// void setPhy(float wRSSI, float TimingError) {
 | 
				
			||||||
 | 
							// This function is inapplicable to packet channels, which have multiple
 | 
				
			||||||
 | 
							// MS listening to the same channel.
 | 
				
			||||||
 | 
							//assert(0);
 | 
				
			||||||
 | 
						//}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// One of these for each PDCH (physical channel), attached to L1FEC.
 | 
				
			||||||
 | 
					// Accepts Radio Blocks from anybody.
 | 
				
			||||||
 | 
					// Based on SACCHL1Encoder and TCHFACCHL1Encoder
 | 
				
			||||||
 | 
					// This does on-demand sending of RLCBlocks down to the physical channel.
 | 
				
			||||||
 | 
					// We wait until the last minute so we can encode uplink assignments in the blocks
 | 
				
			||||||
 | 
					// as close as possible to the present time.
 | 
				
			||||||
 | 
					// TODO: When we support different encodings we may have to base this on L1Encoder directly
 | 
				
			||||||
 | 
					// and copy a bunch of routines from XCCHL1Encoder?
 | 
				
			||||||
 | 
					static const int qCS1[8] = { 1,1,1,1,1,1,1,1 };
 | 
				
			||||||
 | 
					static const int qCS2[8] = { 1,1,0,0,1,0,0,0 }; // GSM05.03 sec 5.1.2.5
 | 
				
			||||||
 | 
					static const int qCS3[8] = { 0,0,1,0,0,0,0,1 }; // GSM05.03 sec 5.1.3.5
 | 
				
			||||||
 | 
					static const int qCS4[8] = { 0,0,0,1,0,1,1,0 }; // GSM0503 sec5.1.4.5; magically identifies CS-4.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PDCHL1Downlink : public PDCHCommon
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					#if GPRS_ENCODER
 | 
				
			||||||
 | 
						GprsEncoder mchEnc;
 | 
				
			||||||
 | 
						//GSM::SharedL1Encoder mchCS1Enc;
 | 
				
			||||||
 | 
						//GSM::SharedL1Encoder mchCS4Enc;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						GSM::L1Encoder mchCS1Enc;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						TxBurst mchBurst;					///< a preformatted burst template
 | 
				
			||||||
 | 
						//TxBurst mchFillerBurst;			// unused ///< the filler burst for this channel
 | 
				
			||||||
 | 
						int mchTotalBursts;
 | 
				
			||||||
 | 
						//GSM::Time mchNextWriteTime, mchPrevWriteTime;
 | 
				
			||||||
 | 
						const TDMAMapping& mchMapping;
 | 
				
			||||||
 | 
						BitVector mchIdleFrame;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The mDownlinkData is used only for control messages, which can stack up.
 | 
				
			||||||
 | 
						//InterthreadQueue<RLCDownlinkMessage> mchDownlinkMsgQ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						static const RLCDirType mchDir = RLCDir::Up;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void initBursts(L1FEC*);
 | 
				
			||||||
 | 
						PDCHL1Downlink(PDCHL1FEC *wParent) :
 | 
				
			||||||
 | 
							PDCHCommon(wParent),
 | 
				
			||||||
 | 
							//mchCS1Enc(ChannelCodingCS1),
 | 
				
			||||||
 | 
					#if GPRS_ENCODER
 | 
				
			||||||
 | 
							//mchCS4Enc(ChannelCodingCS4),
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							mchTotalBursts(0),
 | 
				
			||||||
 | 
							mchMapping(wParent->mchOldFec->encoder()->mapping()),
 | 
				
			||||||
 | 
							mchIdleFrame((size_t)0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						 	initBursts(wParent->mchOldFec);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~PDCHL1Downlink() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Enqueue a downlink message.  We dont use this for downlink data - those
 | 
				
			||||||
 | 
						// are sent by calling the RLCEngine when this queue is empty.
 | 
				
			||||||
 | 
						//void enqueueMsg(RLCDownlinkMessage *);
 | 
				
			||||||
 | 
						// The PDCH must feed the radio on time.  This is the routine that does it.
 | 
				
			||||||
 | 
						void dlService();
 | 
				
			||||||
 | 
						void transmit(RLCBSN_t bsn, BitVector *mI, const int *qbits, int transceiverflags);
 | 
				
			||||||
 | 
						//void rollForward();
 | 
				
			||||||
 | 
						//void mchResync();
 | 
				
			||||||
 | 
						int findNeedyUSF();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Send the L2Frame down to the radio now.
 | 
				
			||||||
 | 
						void send1Frame(BitVector& frame,ChannelCodingType encoding, bool idle);
 | 
				
			||||||
 | 
						bool send1DataFrame(RLCDownEngine *tbfdown, RLCDownlinkDataBlock *block, int makeres,MsgTransactionType mttype,unsigned *pcounter);
 | 
				
			||||||
 | 
						bool send1MsgFrame(TBF *tbf,RLCDownlinkMessage *msg, int makeres, MsgTransactionType mttype,unsigned *pcounter);
 | 
				
			||||||
 | 
						void sendIdleFrame(RLCBSN_t bsn);
 | 
				
			||||||
 | 
						void bugFixIdleFrame();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern bool chCompareFunc(PDCHCommon*ch1, PDCHCommon*ch2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}; // namespace GPRS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										696
									
								
								GPRS/GPRSCLI.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										696
									
								
								GPRS/GPRSCLI.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,696 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "GPRSInternal.h"
 | 
				
			||||||
 | 
					#include "TBF.h"
 | 
				
			||||||
 | 
					#include "RLCEngine.h"
 | 
				
			||||||
 | 
					#include "MAC.h"
 | 
				
			||||||
 | 
					#include "FEC.h"
 | 
				
			||||||
 | 
					#include "RLCMessages.h"
 | 
				
			||||||
 | 
					#include "Interthread.h"
 | 
				
			||||||
 | 
					#include "BSSG.h"
 | 
				
			||||||
 | 
					#include "LLC.h"
 | 
				
			||||||
 | 
					#define strmatch(what,pat) (0==strncmp(what,pat,strlen(pat)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace BSSG;
 | 
				
			||||||
 | 
					using namespace SGSN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define BAD_NUM_ARGS 1	// See CLI/CLI.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RN_CMD_OPTION(opt) (argi<argc && 0==strcmp(argv[argi],opt) ? ++argi : 0)
 | 
				
			||||||
 | 
					#define RN_CMD_ARG (argi<argc ? argv[argi++] : NULL)
 | 
				
			||||||
 | 
					//#define RN_CMD_OPTION(o) (argc>1 && 0==strncmp(argv[1],o,strlen(o)) ? argc--,argv++,1 : 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprsMem(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						gMemStats.text(os);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void printChans(bool verbose, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						PDCHL1FEC *pch;
 | 
				
			||||||
 | 
						RN_MAC_FOR_ALL_PDCH(pch) { pch->mchDump(os,verbose); }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//static int gprsChans(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					//{
 | 
				
			||||||
 | 
					//	bool verbose=0;
 | 
				
			||||||
 | 
					//	while (argi < argc) {
 | 
				
			||||||
 | 
					//		if (strmatch(argv[argi],"-v")) { verbose = 1; argi++; continue; }
 | 
				
			||||||
 | 
					//		os << "oops! unrecognized arg:" << argv[argi] << "\n";
 | 
				
			||||||
 | 
					//		return 0;
 | 
				
			||||||
 | 
					//	}
 | 
				
			||||||
 | 
					//	printChans(verbose,os);
 | 
				
			||||||
 | 
					//	return 0;
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprsList(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						bool xflag=0, aflag=0, listms=0, listtbf=0, listch=0;
 | 
				
			||||||
 | 
						int options = 0;
 | 
				
			||||||
 | 
						int id = -1;
 | 
				
			||||||
 | 
						while (argi < argc) {
 | 
				
			||||||
 | 
							if (strmatch(argv[argi],"ch")) { listch = 1; argi++; continue; }
 | 
				
			||||||
 | 
							if (strmatch(argv[argi],"tbf")) { listtbf = 1; argi++; continue; }
 | 
				
			||||||
 | 
							if (strmatch(argv[argi],"ms")) { listms = 1; argi++; continue; }
 | 
				
			||||||
 | 
							if (strmatch(argv[argi],"-v")) { options |= printVerbose; argi++; continue; }
 | 
				
			||||||
 | 
							if (strmatch(argv[argi],"-c")) { options |= printCaps; argi++; continue; }
 | 
				
			||||||
 | 
							if (strmatch(argv[argi],"-x")) { xflag = 1; argi++; continue; }
 | 
				
			||||||
 | 
							if (strmatch(argv[argi],"-a")) { aflag = 1; argi++; continue; }
 | 
				
			||||||
 | 
							if (isdigit(argv[argi][0])) {
 | 
				
			||||||
 | 
								if (id >= 0) goto oops;  // already found a number
 | 
				
			||||||
 | 
								id = atoi(argv[argi]);
 | 
				
			||||||
 | 
								argi++;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							oops:
 | 
				
			||||||
 | 
							os << "oops! unrecognized arg:" << argv[argi] << "\n";
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool all = !(listch|listtbf|listms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (all|listms) {
 | 
				
			||||||
 | 
							MSInfo *ms;
 | 
				
			||||||
 | 
							for (RListIterator<MSInfo*> itr(xflag ? gL2MAC.macExpiredMSs : gL2MAC.macMSs); itr.next(ms); ) {
 | 
				
			||||||
 | 
								if (id>=0 && (int)ms->msDebugId != id) continue;
 | 
				
			||||||
 | 
								if (aflag || ! ms->msDeprecated) { ms->msDump(os,(PrintOptions)options); }
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (all|listtbf) {
 | 
				
			||||||
 | 
							TBF *tbf;
 | 
				
			||||||
 | 
							//RN_MAC_FOR_ALL_TBF(tbf) { os << tbf->tbfDump(verbose); }
 | 
				
			||||||
 | 
							for (RListIterator<TBF*> itr(xflag ? gL2MAC.macExpiredTBFs : gL2MAC.macTBFs); itr.next(tbf); ) {
 | 
				
			||||||
 | 
								// If the id matches a MS, print the TBFs associated with that MS.
 | 
				
			||||||
 | 
								if (id>=0 && (int)tbf->mtDebugId != id && (int)tbf->mtMS->msDebugId != id) continue;
 | 
				
			||||||
 | 
								os << tbf->tbfDump(options&printVerbose) << endl;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (all|listch) {
 | 
				
			||||||
 | 
							printChans(options&printVerbose,os);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprsFree(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char *what = RN_CMD_ARG;
 | 
				
			||||||
 | 
						char *idstr = RN_CMD_ARG;
 | 
				
			||||||
 | 
						if (!idstr) return BAD_NUM_ARGS;
 | 
				
			||||||
 | 
						int id = atoi(idstr);
 | 
				
			||||||
 | 
						MSInfo *ms;  TBF *tbf;
 | 
				
			||||||
 | 
						if (strmatch(what,"ms")) {
 | 
				
			||||||
 | 
							RN_MAC_FOR_ALL_MS(ms) {
 | 
				
			||||||
 | 
								if (ms->msDebugId == (unsigned)id) {
 | 
				
			||||||
 | 
									os << "Deleting " <<ms <<"\n";
 | 
				
			||||||
 | 
									ms->msDelete(1);
 | 
				
			||||||
 | 
									return 0;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							os << "MS# "<<id <<" not found.\n";
 | 
				
			||||||
 | 
						} else if (strmatch(what,"tbf")) {
 | 
				
			||||||
 | 
							RN_MAC_FOR_ALL_TBF(tbf) {
 | 
				
			||||||
 | 
								if (tbf->mtDebugId == (unsigned)id) {
 | 
				
			||||||
 | 
									os << "Deleting " <<tbf <<"\n";
 | 
				
			||||||
 | 
									tbf->mtDelete(1);
 | 
				
			||||||
 | 
									return 0;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							os << "TBF# "<<id <<" not found.\n";
 | 
				
			||||||
 | 
						} else if (strmatch(what,"ch")) {
 | 
				
			||||||
 | 
							// They dont have an id.  Just delete the nth one.
 | 
				
			||||||
 | 
							PDCHL1FEC*pch;
 | 
				
			||||||
 | 
							RN_MAC_FOR_ALL_PDCH(pch) {
 | 
				
			||||||
 | 
								if (id-- == 0) {
 | 
				
			||||||
 | 
									os << "Deleting " <<pch <<"\n";
 | 
				
			||||||
 | 
									delete pch;
 | 
				
			||||||
 | 
									return 0;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							os << "Channel not found; use 0, 1, 2, etc for the Nth channel in gprs list ch\n";
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							os << "gprs free: unrecognized argument: " << what << "\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprsFreeExpired(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						MSInfo *ms;
 | 
				
			||||||
 | 
						for (RListIterator<MSInfo*> itr(gL2MAC.macExpiredMSs); itr.next(ms); ) {
 | 
				
			||||||
 | 
							itr.erase();
 | 
				
			||||||
 | 
							delete ms;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						TBF *tbf;
 | 
				
			||||||
 | 
						for (RListIterator<TBF*> itr(gL2MAC.macExpiredTBFs); itr.next(tbf); ) {
 | 
				
			||||||
 | 
							itr.erase();
 | 
				
			||||||
 | 
							delete tbf;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprsStats(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!GPRSConfig::IsEnabled()) {
 | 
				
			||||||
 | 
							os << "GPRS is not enabled.  See 'GPRS.Enable' option.\n";
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						GSM::Time now = gBTS.time();
 | 
				
			||||||
 | 
						os << "GSM FN=" << now.FN() << " GPRS BSN=" << gBSNNext << "\n"; 
 | 
				
			||||||
 | 
						os << "Current number of"
 | 
				
			||||||
 | 
							<< " PDCH=" << gL2MAC.macPDCHs.size()
 | 
				
			||||||
 | 
							<< " MS=" << gL2MAC.macMSs.size()
 | 
				
			||||||
 | 
							<< " TBF=" << gL2MAC.macTBFs.size()
 | 
				
			||||||
 | 
							<< "\n";
 | 
				
			||||||
 | 
						os << "Total number of"
 | 
				
			||||||
 | 
							<< " PDCH=" << Stats.countPDCH
 | 
				
			||||||
 | 
							<< " MS=" << Stats.countMSInfo
 | 
				
			||||||
 | 
							<< " TBF=" << Stats.countTBF
 | 
				
			||||||
 | 
							<< " RACH=" << Stats.countRach
 | 
				
			||||||
 | 
							<< "\n";
 | 
				
			||||||
 | 
						os << "Downlink utilization=" << gL2MAC.macDownlinkUtilization << "\n";
 | 
				
			||||||
 | 
						os << LOGVAR2("ServiceLoopTime",Stats.macServiceLoopTime) << "\n";
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0	// pinghttp test code not linked in yet.
 | 
				
			||||||
 | 
					static int gprsPingHttp(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (argi >= argc) { os << "syntax: gprs pinghttp address\n"; return 1; }
 | 
				
			||||||
 | 
						//char *addr = argv[argi++];
 | 
				
			||||||
 | 
						os << "pinghttp unimplemented\n";
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Start the service and allocate a channel.
 | 
				
			||||||
 | 
					// This is redundant - can call rach.
 | 
				
			||||||
 | 
					static int gprsStart(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Start the thread, if not running.
 | 
				
			||||||
 | 
						char *modearg = RN_CMD_ARG;
 | 
				
			||||||
 | 
						if (modearg) {
 | 
				
			||||||
 | 
							gL2MAC.macSingleStepMode = strmatch(modearg,"s");
 | 
				
			||||||
 | 
							if (!gL2MAC.macSingleStepMode) { os << "Unrecognized arg: "<<modearg<<"\n"; return 0; }
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							gL2MAC.macSingleStepMode = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Reinit the config in case single step mode is changing.
 | 
				
			||||||
 | 
						gL2MAC.macConfigInit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (gL2MAC.macRunning) {
 | 
				
			||||||
 | 
							os << "gprs service thread already running.\n";
 | 
				
			||||||
 | 
						} else if (gL2MAC.macStart()) {
 | 
				
			||||||
 | 
							os << "started gprs service "
 | 
				
			||||||
 | 
									<<(gL2MAC.macSingleStepMode ? "single step mode" : "thread") << "\n";
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							os << "failed to start gprs service.\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The macAddChannel below is failing.  Try waiting a while.
 | 
				
			||||||
 | 
						// Update: it fails between the time OpenBTS first starts up and the transceiver times out,
 | 
				
			||||||
 | 
						// about a 5 second window after start up.  This may only happen after a crash.
 | 
				
			||||||
 | 
						// Update again: I think on startup the channels come up with the recyclable timers
 | 
				
			||||||
 | 
						// running and we cannot allocate a channel until they expire.
 | 
				
			||||||
 | 
						sleep(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Allocate a channel, if none already allocated.
 | 
				
			||||||
 | 
						if (! gL2MAC.macActiveChannels()) { gL2MAC.macAddChannel(); }
 | 
				
			||||||
 | 
						if (! gL2MAC.macActiveChannels()) {
 | 
				
			||||||
 | 
							os << "failed to allocate channel for gprs.\n";
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							PDCHL1FEC *pdch = gL2MAC.macPickChannel();
 | 
				
			||||||
 | 
							assert(pdch);
 | 
				
			||||||
 | 
							os << "allocated channel for gprs: " << pdch << "\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprsStop(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						bool cflag = RN_CMD_OPTION("-c");
 | 
				
			||||||
 | 
						gL2MAC.macStop(cflag);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprsTestRach(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						GSM::Time now = gBTS.time();
 | 
				
			||||||
 | 
						unsigned RA = 15 << 5;
 | 
				
			||||||
 | 
						GPRSProcessRACH(RA,now,-20,0.5);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
						int gprs_llc_fcs(uint8_t *data, unsigned int len);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprsTest(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
						LLCParity parity;
 | 
				
			||||||
 | 
						ByteVector bv;
 | 
				
			||||||
 | 
						for (int i = 0; i <= 5; i++) {
 | 
				
			||||||
 | 
							switch (i) {
 | 
				
			||||||
 | 
							case 0: bv = ByteVector(3); bv.fill(0); bv.setByte(2,1); break;
 | 
				
			||||||
 | 
							case 1: bv = ByteVector(3); bv.fill(0); bv.setByte(2,2); break;
 | 
				
			||||||
 | 
							case 2: bv = ByteVector(3); bv.fill(0); bv.setByte(0,0x80); break;
 | 
				
			||||||
 | 
							case 3: bv = ByteVector(3); bv.fill(0); bv.setByte(0,0x40); break;
 | 
				
			||||||
 | 
							case 4: bv = ByteVector(3); bv.fill(0); bv.setByte(0,0); break;
 | 
				
			||||||
 | 
							case 5: bv = ByteVector("Now is the time for all good men"); break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							unsigned crc1 = gprs_llc_fcs(bv.begin(),bv.size());
 | 
				
			||||||
 | 
							unsigned crc2 = parity.computeFCS(bv);
 | 
				
			||||||
 | 
							printf("size=%d, byte=0x%x crc=0x%x, crcnew=0x%x\n",bv.size(),bv.getByte(0),crc1,crc2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//GPRSLOG(1) << "An unterminated string";
 | 
				
			||||||
 | 
						//GPRSLOG(1) << "Another unterminated string";
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Try printing out all the RLC messages.
 | 
				
			||||||
 | 
					// For the small ones, we will just print them out to see if that functionality works.
 | 
				
			||||||
 | 
					// For most of the big ones, we will actually call the function that generates these messages.
 | 
				
			||||||
 | 
					static int gprsTestMsg(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!gL2MAC.macActiveChannels()) {
 | 
				
			||||||
 | 
							os << "Oops!  You must allocate GPRS channels before running this test; try grps start.\n";
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Time gsmfn = gBTS.clock().FN();
 | 
				
			||||||
 | 
						os << "Test Messages, Current time:"<<LOGVAR(gsmfn)<<LOGVAR(gBSNNext)<<"\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create a dummy RLCRawBLock for the uplink messages to parse.
 | 
				
			||||||
 | 
						BitVector bvdummy(52*8);
 | 
				
			||||||
 | 
						bvdummy.zero();
 | 
				
			||||||
 | 
						RLCRawBlock rb(99,bvdummy,0,0,ChannelCodingCS1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// NOTE NOTE NOTE!  This debug code is running in a different thread
 | 
				
			||||||
 | 
						// than the MAC service loop.  If you try to run the code below while
 | 
				
			||||||
 | 
						// the MAC service loop is running, it will just crash.
 | 
				
			||||||
 | 
						gL2MAC.macStop(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create an MS, and some TBFs.
 | 
				
			||||||
 | 
						PDCHL1FEC *pdch = gL2MAC.macPickChannel();	// needed for immediate assignment msg.
 | 
				
			||||||
 | 
						MSInfo *ms = new MSInfo(123456);	// number is the TLLI.
 | 
				
			||||||
 | 
						RLCDownEngine *downengine = new RLCDownEngine(ms);
 | 
				
			||||||
 | 
						TBF *downtbf = downengine->getTBF();
 | 
				
			||||||
 | 
						RLCUpEngine *upengine = new RLCUpEngine(ms,0);
 | 
				
			||||||
 | 
						TBF *uptbf = upengine->getTBF();
 | 
				
			||||||
 | 
						uptbf->mtAttach();		// Assigns USF and TFI for the tbf, so we can see it in the messages.
 | 
				
			||||||
 | 
						downtbf->mtAttach();
 | 
				
			||||||
 | 
						os << "uplink TBF for messages is:\n";
 | 
				
			||||||
 | 
						os << uptbf->tbfDump(1);
 | 
				
			||||||
 | 
						os << "downlink TBF for messages is:\n";
 | 
				
			||||||
 | 
						os << downtbf->tbfDump(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os << "struct RLCMsgPacketUplinkDummyControlBlock : public RLCUplinkMessage\n";
 | 
				
			||||||
 | 
						struct RLCMsgPacketUplinkDummyControlBlock msgupdummy(&rb);
 | 
				
			||||||
 | 
						msgupdummy.text(os);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os << "\n\nstruct RLCMsgPacketDownlinkAckNack : public RLCUplinkMessage\n";
 | 
				
			||||||
 | 
						RLCMsgPacketDownlinkAckNack msgdownacknack(&rb);
 | 
				
			||||||
 | 
						msgdownacknack.text(os);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os << "\n\nstruct RLCMsgPacketControlAcknowledgement : public RLCUplinkMessage\n";
 | 
				
			||||||
 | 
						RLCMsgPacketControlAcknowledgement msgcontrolack(&rb);
 | 
				
			||||||
 | 
						msgcontrolack.text(os);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os << "\n\nstruct RLCMsgPacketResourceRequest : public RLCUplinkMessage\n";
 | 
				
			||||||
 | 
						RLCMsgPacketResourceRequest resreq(&rb);
 | 
				
			||||||
 | 
						resreq.text(os);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os << "\n\nclass RLCMsgPacketAccessReject : public RLCDownlinkMessage\n";
 | 
				
			||||||
 | 
						RLCMsgPacketAccessReject msgreject(downtbf);
 | 
				
			||||||
 | 
						msgreject.text(os);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os << "\n\nclass RLCMsgPacketTBFRelease : public RLCDownlinkMessage\n";
 | 
				
			||||||
 | 
						RLCMsgPacketTBFRelease msgtbfrel(downtbf);
 | 
				
			||||||
 | 
						msgtbfrel.text(os);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os << "\n\nclass RLCMsgPacketUplinkAckNack : public RLCDownlinkMessage\n";
 | 
				
			||||||
 | 
						RLCMsgPacketUplinkAckNack *msgupacknack = upengine->engineUpAckNack();
 | 
				
			||||||
 | 
						msgupacknack->text(os);
 | 
				
			||||||
 | 
						delete msgupacknack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os << "\n\nstruct RLCMsgPacketDownlinkDummyControlBlock : public RLCDownlinkMessage\n";
 | 
				
			||||||
 | 
						RLCMsgPacketDownlinkDummyControlBlock msgdowndummy;
 | 
				
			||||||
 | 
						msgdowndummy.text(os);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os << "\n\nL3ImmediateAssignment for Single Block Packet Assignment\n";
 | 
				
			||||||
 | 
						//ms->msMode = RROperatingMode::PacketIdle;
 | 
				
			||||||
 | 
						sendAssignment(pdch,downtbf, &os);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os << "\n\nclass RLCMsgPacketDownlinkAssignment : public RLCDownlinkMessage\n";
 | 
				
			||||||
 | 
						//ms->msMode = RROperatingMode::PacketTransfer;
 | 
				
			||||||
 | 
						ms->msT3193.set();
 | 
				
			||||||
 | 
						// To force the MS to send the message on PACH we can set T3191
 | 
				
			||||||
 | 
						sendAssignment(pdch,downtbf, &os);
 | 
				
			||||||
 | 
						ms->msT3193.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os << "\n\nclass RLCMsgPacketUplinkAssignment : public RLCDownlinkMessage\n";
 | 
				
			||||||
 | 
						sendAssignment(pdch,uptbf, &os);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprsTestBSN(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						RLCBSN_t bsn = 0;
 | 
				
			||||||
 | 
						int fn = 0;
 | 
				
			||||||
 | 
						for (fn = 0; fn < 100; fn++) {
 | 
				
			||||||
 | 
							bsn = FrameNumber2BSN(fn);
 | 
				
			||||||
 | 
							int fn2 = BSN2FrameNumber(bsn);
 | 
				
			||||||
 | 
							os << LOGVAR(fn) <<LOGVAR(bsn) <<LOGVAR(fn2) <<"\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create an uplink block as would be sent by an MS.
 | 
				
			||||||
 | 
					// The payload in sequential blocks will be ascending 16-bit words.
 | 
				
			||||||
 | 
					// Ie, payload of first block is 0,1,2,3,4,5,6,7,8,9
 | 
				
			||||||
 | 
					// and of second block is 10,11,12,13,14,15,16,17,18,19, etc.
 | 
				
			||||||
 | 
					// Makes it easy to check if the final assembled PDU is correct.
 | 
				
			||||||
 | 
					// TODO: We are not testing a partial final block.
 | 
				
			||||||
 | 
					static RLCRawBlock *fakeablock(int bsn, int tfi, int final)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Uplink always uses CS1.
 | 
				
			||||||
 | 
						// The payload size is 20 bytes, but this is where it comes from.
 | 
				
			||||||
 | 
						// We are going to assume it is even so we can just fill the payload
 | 
				
			||||||
 | 
						// with sequential integers.
 | 
				
			||||||
 | 
						int payloadsize = RLCPayloadSizeInBytes[ChannelCodingCS1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create the uplink block header structure, which we can write into the bitvector.
 | 
				
			||||||
 | 
						RLCUplinkDataBlockHeader bh;
 | 
				
			||||||
 | 
						bh.mmac.mPayloadType = MACPayloadType::RLCData;
 | 
				
			||||||
 | 
						bh.mmac.mCountDownValue = final ? 0 : 15;	// countdown, just use 15 until final block.
 | 
				
			||||||
 | 
						bh.mmac.mSI = 0;	// stall indicator from MS.
 | 
				
			||||||
 | 
						bh.mmac.mR = 0;		// retry bit from MS.
 | 
				
			||||||
 | 
						bh.mSpare = 0;		// Doesnt matter.
 | 
				
			||||||
 | 
						bh.mPI = 0;			// We dont use this.
 | 
				
			||||||
 | 
						bh.mTFI = tfi;
 | 
				
			||||||
 | 
						bh.mTI = 0;			// We dont use this.
 | 
				
			||||||
 | 
						// Octet 2: (starts at bit 16)
 | 
				
			||||||
 | 
						bh.mBSN = bsn;
 | 
				
			||||||
 | 
						bh.mE = 1;	// Extension bit: 1 means whole block is data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create a bitvector with an image of the header above followed by the
 | 
				
			||||||
 | 
						// data, which will just be sequential integers starting at bsn.
 | 
				
			||||||
 | 
						// Size is 1 byte MAC header + 2 bytes RLC header + payload
 | 
				
			||||||
 | 
						BitVector vec(8*(1 + 2 + payloadsize));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create a MsgCommon BitVector writer:
 | 
				
			||||||
 | 
						MsgCommonWrite mcw(vec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Write the header.
 | 
				
			||||||
 | 
						bh.mmac.writeMACHeader(mcw);
 | 
				
			||||||
 | 
						bh.writeRLCHeader(mcw);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Write the payload.
 | 
				
			||||||
 | 
						int numwords = payloadsize/2;
 | 
				
			||||||
 | 
						uint16_t dataword = bsn * numwords;	// Starting payload word for this bsn.
 | 
				
			||||||
 | 
						for ( ; numwords > 0; numwords--) {
 | 
				
			||||||
 | 
							mcw.writeField(dataword++,16); // Write as a 16 bit word.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The BitVector now looks like something the MS would send us.
 | 
				
			||||||
 | 
						// Go through the steps GPRS code uses to parse the incoming BitVector:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The GSM radio queues us an RLCRawBlock:
 | 
				
			||||||
 | 
						RLCRawBlock *rawblock = new RLCRawBlock(bsn,vec,0,0,ChannelCodingCS1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rawblock;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if INTERNAL_SGSN==0
 | 
				
			||||||
 | 
					static int gprsTestUl(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						bool randomize = RN_CMD_OPTION("-r");
 | 
				
			||||||
 | 
						int32_t mytlli = 6789;
 | 
				
			||||||
 | 
						MSInfo *ms = new MSInfo(mytlli);
 | 
				
			||||||
 | 
						RLCUpEngine *upengine = new RLCUpEngine(ms,0);
 | 
				
			||||||
 | 
						TBF *uptbf = upengine->getTBF();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						InterthreadQueue<NSMsg> testQ;
 | 
				
			||||||
 | 
						gBSSG.mbsTestQ = &testQ;	// Put uplink blocks on our own queue.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int payloadsize = RLCPayloadSizeInBytes[ChannelCodingCS1]; // 20 bytes / block.
 | 
				
			||||||
 | 
						int numbytes = 200;			// Max PDU size is 1500; we will test 20*20 == 400 to start.
 | 
				
			||||||
 | 
						int numblocks = numbytes / payloadsize;
 | 
				
			||||||
 | 
						// The window size is only 64, so we would normlly have to wait for the ack before proceeding.
 | 
				
			||||||
 | 
						// If we single stepped the MAC service loop while doing this, the dlservice routine
 | 
				
			||||||
 | 
						// would do that.
 | 
				
			||||||
 | 
						int bsn;
 | 
				
			||||||
 | 
						int TFI = 1; // Use a fake tfi for this test.
 | 
				
			||||||
 | 
						for (int j = 0; j < numblocks; j++) {
 | 
				
			||||||
 | 
							if (randomize && j < numblocks-16) {
 | 
				
			||||||
 | 
								// Goof up the order to see if blocks are reassembled in proper order.
 | 
				
			||||||
 | 
								// I left the last few blocks alone to simplify.
 | 
				
			||||||
 | 
								bsn = (j & 0xffff0) + ~(j & 0xf);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								bsn = j;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							int final = bsn == numblocks-1;
 | 
				
			||||||
 | 
							RLCRawBlock *rawblock = fakeablock(bsn,TFI,final);
 | 
				
			||||||
 | 
							// Raw uplink blocks are dequeued by processRLCUplinkDataBlock which
 | 
				
			||||||
 | 
							// sends them to the uplink engine thusly:
 | 
				
			||||||
 | 
							RLCUplinkDataBlock *rb = new RLCUplinkDataBlock(rawblock);
 | 
				
			||||||
 | 
							//rb->text(os);
 | 
				
			||||||
 | 
							// TODO: call processRLCUplinkDataBlock to test tfis
 | 
				
			||||||
 | 
							delete rawblock;
 | 
				
			||||||
 | 
							uptbf->engineRecvDataBlock(rb,0);
 | 
				
			||||||
 | 
							// The final block has E bit set, which makes the RLCUpEngine call sendPDU(),
 | 
				
			||||||
 | 
							// which sends the blocks to the BSSG, which puts them on our own queue.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						gBSSG.mbsTestQ = NULL;	// Restore BSSG to normal use.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Examine results on the testq.Get the block from the BSSG transmit queue.
 | 
				
			||||||
 | 
						if (testQ.size() != 1) {
 | 
				
			||||||
 | 
							os << "Unexpected BSSG Queue size="<<testQ.size()<<"\n";
 | 
				
			||||||
 | 
							return 0;	// We might be leaving some memory unfreed.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						// ulmsg is a BSSGMsgULUnitData.
 | 
				
			||||||
 | 
						NSMsg *msg = testQ.read();
 | 
				
			||||||
 | 
						BSSGMsgULUnitData *ulmsg = (BSSGMsgULUnitData*)msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check some things in the header.
 | 
				
			||||||
 | 
						int expected = mytlli;
 | 
				
			||||||
 | 
						if (ulmsg->getTLLI() != expected) {
 | 
				
			||||||
 | 
							os << "ULUnitData msg wrong tlli=" << ulmsg->getTLLI() <<LOGVAR(expected)<<"\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						BSSGMsgULUnitDataHeader *ulhdr = ulmsg->getHeader();
 | 
				
			||||||
 | 
						expected = BSPDUType::UL_UNITDATA;
 | 
				
			||||||
 | 
						if (ulhdr->mbuPDUType != expected) {
 | 
				
			||||||
 | 
							os << "ULUnitData msg wrong PDUType=" << ulhdr->mbuPDUType <<LOGVAR(expected)<<"\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int hdrsize = BSSGMsgULUnitData::HeaderLength;
 | 
				
			||||||
 | 
						if (0) {
 | 
				
			||||||
 | 
							// This test is incorrect: the output byte vector is always allocated max size
 | 
				
			||||||
 | 
							// so it does not have to be grown.
 | 
				
			||||||
 | 
							int msgsize = ulmsg->size();
 | 
				
			||||||
 | 
							expected = numbytes+hdrsize;
 | 
				
			||||||
 | 
							if (msgsize != expected) {
 | 
				
			||||||
 | 
								os << "BSSG UL UnitData msg wrong size" << LOGVAR(msgsize) << LOGVAR(expected)<<"\n";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check the data:
 | 
				
			||||||
 | 
						expected = 0;
 | 
				
			||||||
 | 
						int offset;
 | 
				
			||||||
 | 
						int bads = 0;
 | 
				
			||||||
 | 
						for (offset = 0; offset < numbytes; offset += sizeof(short), expected++) {
 | 
				
			||||||
 | 
							int got = ulmsg->getUInt16(hdrsize+offset);
 | 
				
			||||||
 | 
							if (got != expected) {
 | 
				
			||||||
 | 
								os << "BSSG data wrong at "<<LOGVAR(offset)<<LOGVAR(got)<<LOGVAR(expected)<<"\n";
 | 
				
			||||||
 | 
								if (++bads > 10) break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ulmsg->text(os);
 | 
				
			||||||
 | 
						/***
 | 
				
			||||||
 | 
						os << "Data from beginning was:\n";
 | 
				
			||||||
 | 
						todo: dump the ulmsg directly.  Use << this for ByteVector.
 | 
				
			||||||
 | 
						offset = 0;
 | 
				
			||||||
 | 
						for (int l = 0; l < 10; l++) {
 | 
				
			||||||
 | 
							os << offset << ":";
 | 
				
			||||||
 | 
							for (int i = 0; i < 10; i++, offset+=2) { os <<" " <<ulmsg->getUInt16(offset); }
 | 
				
			||||||
 | 
							os << "\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						***/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						delete ulmsg;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprsDebug(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (argi < argc) {
 | 
				
			||||||
 | 
							int newval = strtol(argv[argi++],NULL,0); // strtol allows hex
 | 
				
			||||||
 | 
							gConfig.set("GPRS.Debug",newval);
 | 
				
			||||||
 | 
							GPRSSetDebug(newval);
 | 
				
			||||||
 | 
						} else if (! GPRSDebug) {
 | 
				
			||||||
 | 
							//GPRSSetDebug(3);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						char buf[100]; sprintf(buf,"GPRSDebug=0x%x\n",GPRSDebug);
 | 
				
			||||||
 | 
						os << buf;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprsSet(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char *what = RN_CMD_ARG;
 | 
				
			||||||
 | 
						if (!what) { return BAD_NUM_ARGS; }
 | 
				
			||||||
 | 
						char *val = RN_CMD_ARG;		// may be null.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (strmatch(what,"clock") || strmatch(what,"sync")) {
 | 
				
			||||||
 | 
							if (val) { gFixSyncUseClock = atoi(val); }
 | 
				
			||||||
 | 
							os << LOGVAR(gFixSyncUseClock) << "\n";
 | 
				
			||||||
 | 
						} else if (strmatch(what,"console")) {
 | 
				
			||||||
 | 
							if (val) { gLogToConsole = atoi(val); }
 | 
				
			||||||
 | 
							os << LOGVAR(gLogToConsole) << "\n";
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							os << "gprs set: unrecognized argument: " << what << "\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprsStep(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!gL2MAC.macSingleStepMode) {
 | 
				
			||||||
 | 
							os << "error: MAC is not in single step mode\n";
 | 
				
			||||||
 | 
							return 0;	// disaster would ensue if we accidently started another serviceloop.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// We single step it ignoring the global clock, which
 | 
				
			||||||
 | 
						// might result in messages from the channel service routines.
 | 
				
			||||||
 | 
						++gBSNNext;
 | 
				
			||||||
 | 
						gL2MAC.macServiceLoop();
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int gprsConsole(int argc, char **argv, int argi, ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						gLogToConsole = !gLogToConsole;		// Default: toggle.
 | 
				
			||||||
 | 
						if (argi < argc) { gLogToConsole = atoi(argv[argi++]); }
 | 
				
			||||||
 | 
						os << "LogToConsole=" << gLogToConsole << "\n";
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct GprsSubCmds {
 | 
				
			||||||
 | 
						const char *name;
 | 
				
			||||||
 | 
						int (*subcmd)(int argc, char **argv, int argi,std::ostream&os);
 | 
				
			||||||
 | 
						const char *syntax;
 | 
				
			||||||
 | 
					} gprsSubCmds[] = {
 | 
				
			||||||
 | 
						{ "list",gprsList,	"list [ms|tbf|ch] [-v] [-x] [-c] [id]  # list active objects of specified type;\n\t\t -v => verbose; -c => include MS Capabilities -x => list expired rather than active" },
 | 
				
			||||||
 | 
						{ "stat",gprsStats, "stat  # Show GPRS statistics" },
 | 
				
			||||||
 | 
						{ "free",gprsFree, "free ms|tbf|ch id   # Delete something" },
 | 
				
			||||||
 | 
						{ "freex",gprsFreeExpired, "freex	# free expired ms and tbf structs" },
 | 
				
			||||||
 | 
						{ "debug",gprsDebug,	"debug [level]  # Set debug level; 0 turns off" },
 | 
				
			||||||
 | 
						{ "start",gprsStart,	"start [step]   # Start gprs, optionally in single-step-mode;\n\t\t- can also start by 'gprs rach'" },
 | 
				
			||||||
 | 
						{ "stop",gprsStop,	"stop [-c]  # stop gprs thread and if -c release channels" },
 | 
				
			||||||
 | 
						{ "step",gprsStep,	"step    # single step the MAC service loop (requires 'start step')." },
 | 
				
			||||||
 | 
						{ "set",gprsSet,	"set name [val]   # print and optionally set a variable - see source for names" },
 | 
				
			||||||
 | 
						{ "rach",gprsTestRach,	"rach   # Simulate a RACH, which starts gprs service" },
 | 
				
			||||||
 | 
						{ "testmsg",gprsTestMsg,	"testmsg   # Test message functions" },
 | 
				
			||||||
 | 
						{ "testbsn",gprsTestBSN,	"testbsn   # Test bsn<->frame number functions" },
 | 
				
			||||||
 | 
					#if INTERNAL_SGSN==0
 | 
				
			||||||
 | 
						{ "testul",gprsTestUl,	"testul [-r]   # Send a test PDU through the RLCEngine; -r => randomize order " },
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						{ "console",gprsConsole, "console [0|1]  # Send messages to console as well as /var/log/OpenBTS.log;\n\t\t (default=1 for debugging)" },
 | 
				
			||||||
 | 
						{ "mem",gprsMem,	"mem   # Memory leak detector - print numbers of structs in use" },
 | 
				
			||||||
 | 
						{ "test",gprsTest,	"test   # Temporary test" },
 | 
				
			||||||
 | 
						// Dont have the source code for pinghttp linked in yet.
 | 
				
			||||||
 | 
						//{ "pinghttp",gprsPingHttp,"pinghttp address  # Send an http request to address (dont use google.com)" },
 | 
				
			||||||
 | 
						// The "help" command is handled internally by gprsCLI.
 | 
				
			||||||
 | 
						{ NULL,NULL }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void help(std::ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << "gprs sub-commands to control GPRS radio mode.  Syntax: gprs subcommand <options...>\n";
 | 
				
			||||||
 | 
						os << "subcommands are:\n";
 | 
				
			||||||
 | 
						struct GprsSubCmds *gscp;
 | 
				
			||||||
 | 
						for (gscp = gprsSubCmds; gscp->name; gscp++) {
 | 
				
			||||||
 | 
							os << "\t" << gscp->syntax;
 | 
				
			||||||
 | 
							//if (gcp->arg) os << " " << gcp->arg;
 | 
				
			||||||
 | 
							os << "\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						os << "Notes:\n";
 | 
				
			||||||
 | 
						os << "  Downlink utilization averaged over 5 seconds; 1.0 means full utilization;\n";
 | 
				
			||||||
 | 
						os << "  2.0 means downlink requests exceeds available bandwidth by 2x, etc.\n";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Set defaults for gprs debugging.
 | 
				
			||||||
 | 
					/*******
 | 
				
			||||||
 | 
					static void debugdefaults()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static int inited = 0;
 | 
				
			||||||
 | 
						if (!inited) {
 | 
				
			||||||
 | 
							inited = 1;
 | 
				
			||||||
 | 
							GPRSDebug = 3;
 | 
				
			||||||
 | 
							gLogToConsole = 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					*******/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Should return: SUCCESS (0), BAD_NUM_ARGS(1), BAD_VALUE(2), FAILURE (5)
 | 
				
			||||||
 | 
					// but sadly, these are defined in CLI.cpp, so I guess we just return 0.
 | 
				
			||||||
 | 
					// Note: argv includes command name so argc==1 implies no args.
 | 
				
			||||||
 | 
					int gprsCLI(int argc, char **argv, std::ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						//debugdefaults();
 | 
				
			||||||
 | 
						ScopedLock lock(gL2MAC.macLock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (argc <= 1) { help(os); return 1; }
 | 
				
			||||||
 | 
						int argi = 1;	// The number of arguments consumed so far; argv[0] was "gprs"
 | 
				
			||||||
 | 
						char *subcmd = argv[argi++];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct GprsSubCmds *gscp;
 | 
				
			||||||
 | 
						int status = 0;	// maybe success
 | 
				
			||||||
 | 
						for (gscp = gprsSubCmds; gscp->name; gscp++) {
 | 
				
			||||||
 | 
							if (0 == strcasecmp(subcmd,gscp->name)) {
 | 
				
			||||||
 | 
								status = gscp->subcmd(argc,argv,argi,os);
 | 
				
			||||||
 | 
								if (status == BAD_NUM_ARGS) {
 | 
				
			||||||
 | 
									os << "wrong number of arguments\n";
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return status;
 | 
				
			||||||
 | 
								//if (gscp->arg == NULL || (argi < argc && 0 == strcasecmp(gscp->arg,argv[argi+1]))) {
 | 
				
			||||||
 | 
									//gscp->subcmd(argc,argv,argi + (gscp->arg?1:0),os);
 | 
				
			||||||
 | 
								//}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (strcasecmp(subcmd,"help")) {
 | 
				
			||||||
 | 
							os << "gprs: unrecognized sub-command: "<<subcmd<<"\n";
 | 
				
			||||||
 | 
							status = 2;	// bad command
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						help(os);
 | 
				
			||||||
 | 
						return status;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};	// namespace
 | 
				
			||||||
							
								
								
									
										103
									
								
								GPRS/GPRSExport.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								GPRS/GPRSExport.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (pat) This is the GPRS exported include for use by clients in other directories.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GPRSEXPORT_H
 | 
				
			||||||
 | 
					#define GPRSEXPORT_H
 | 
				
			||||||
 | 
					#include <ostream>
 | 
				
			||||||
 | 
					// The user of this file must include these first, to avoid circular .h files:
 | 
				
			||||||
 | 
					//#include "GSMConfig.h"		// For Time
 | 
				
			||||||
 | 
					//#include "GSMCommon.h"		// For ChannelType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// You must not include anything from the GSM directory to avoid circular calls
 | 
				
			||||||
 | 
					// that read files out of order, but we need transparent pointers to these classes,
 | 
				
			||||||
 | 
					// so they must be defined first.
 | 
				
			||||||
 | 
					namespace GSM {
 | 
				
			||||||
 | 
						class RxBurst;
 | 
				
			||||||
 | 
						class L3RRMessage;
 | 
				
			||||||
 | 
						class CCCHLogicalChannel;
 | 
				
			||||||
 | 
						class L3RequestReference;
 | 
				
			||||||
 | 
						class Time;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct GPRSConfig {
 | 
				
			||||||
 | 
						static unsigned GetRAColour();
 | 
				
			||||||
 | 
						static bool IsEnabled();
 | 
				
			||||||
 | 
						static bool sgsnIsInternal();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum ChannelCodingType {	// Compression/Coding schemes CS-1 to CS-4 coded as 0-3
 | 
				
			||||||
 | 
						ChannelCodingCS1,
 | 
				
			||||||
 | 
						ChannelCodingCS2,
 | 
				
			||||||
 | 
						ChannelCodingCS3,
 | 
				
			||||||
 | 
						ChannelCodingCS4,
 | 
				
			||||||
 | 
						ChannelCodingMax = ChannelCodingCS4,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// See notes at GPRSCellOptions_t::GPRSCellOptions_t()
 | 
				
			||||||
 | 
					struct GPRSCellOptions_t {
 | 
				
			||||||
 | 
						unsigned mNMO;
 | 
				
			||||||
 | 
					    unsigned mT3168Code;        // range 0..7
 | 
				
			||||||
 | 
					    unsigned mT3192Code;        // range 0..7
 | 
				
			||||||
 | 
					    unsigned mDRX_TIMER_MAX;
 | 
				
			||||||
 | 
					    unsigned mACCESS_BURST_TYPE;
 | 
				
			||||||
 | 
					    unsigned mCONTROL_ACK_TYPE;
 | 
				
			||||||
 | 
					    unsigned mBS_CV_MAX;
 | 
				
			||||||
 | 
						bool mNW_EXT_UTBF;	// Extended uplink TBF 44.060 9.3.1b and 9.3.1.3
 | 
				
			||||||
 | 
						GPRSCellOptions_t();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern const int GPRSUSFEncoding[8];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern GPRSCellOptions_t &GPRSGetCellOptions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The following are not in a class because we dont want to include the entire GPRS class hierarchy.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The function by which bursts are delivered to GPRS.
 | 
				
			||||||
 | 
					class PDCHL1FEC;
 | 
				
			||||||
 | 
					extern void GPRSWriteLowSideRx(const GSM::RxBurst&, PDCHL1FEC*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The function by which RACH messages are delivered to GPRS.
 | 
				
			||||||
 | 
					extern void GPRSProcessRACH(unsigned RA, const GSM::Time &when, float RSSI, float timingError);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern int GetPowerAlpha();
 | 
				
			||||||
 | 
					extern int GetPowerGamma();
 | 
				
			||||||
 | 
					extern unsigned GPRSDebug;
 | 
				
			||||||
 | 
					extern void GPRSSetDebug(int value);
 | 
				
			||||||
 | 
					extern void GPRSNotifyGsmActivity(const char *imsi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Hook into CLI/CLI.cpp:Parser class for GPRS sub-command.
 | 
				
			||||||
 | 
					int gprsCLI(int,char**,std::ostream&);
 | 
				
			||||||
 | 
					int configGprsChannelsMin();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gprsStart();	// External entry point to start gprs service.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}; // namespace GPRS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GPRSLOG is no longer used outside the GPRS directory.
 | 
				
			||||||
 | 
					/****
 | 
				
			||||||
 | 
					 * #ifndef GPRSLOG
 | 
				
			||||||
 | 
					 * #include "Logger.h"
 | 
				
			||||||
 | 
					 * #define GPRSLOG(level) if (GPRS::GPRSDebug & (level)) \
 | 
				
			||||||
 | 
					 *	Log(LOG_DEBUG).get() <<"GPRS,"<<(level)<<":"
 | 
				
			||||||
 | 
					 * #endif
 | 
				
			||||||
 | 
					***/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										129
									
								
								GPRS/GPRSInternal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								GPRS/GPRSInternal.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,129 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GPRSINTERNAL_H
 | 
				
			||||||
 | 
					#define GPRSINTERNAL_H
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include "GPRSRLC.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
						// FEC.h:
 | 
				
			||||||
 | 
						class PDCHL1FEC;
 | 
				
			||||||
 | 
						//class PTCCHL1Uplink;
 | 
				
			||||||
 | 
						//class PDIdleL1Uplink;
 | 
				
			||||||
 | 
						class PDCHL1Uplink;
 | 
				
			||||||
 | 
						class PDCHL1Downlink;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// MAC.h:
 | 
				
			||||||
 | 
						class RLCBSN_t;
 | 
				
			||||||
 | 
						class MSInfo;
 | 
				
			||||||
 | 
						class L2MAC;
 | 
				
			||||||
 | 
						class L1UplinkReservation;
 | 
				
			||||||
 | 
						class L1USFTable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GPRSL2RLCEngine.h:
 | 
				
			||||||
 | 
						class RLCEngine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//TBF.h:
 | 
				
			||||||
 | 
						class TBF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// RLCEngine.h:
 | 
				
			||||||
 | 
						class RLCUpEngine;
 | 
				
			||||||
 | 
						class RLCDownEngine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// RLCHdr.h:
 | 
				
			||||||
 | 
						class RLCDownlinkBlock;
 | 
				
			||||||
 | 
						class RLCUplinkDataBlock;
 | 
				
			||||||
 | 
						class RLCDownlinkDataBlock;
 | 
				
			||||||
 | 
						struct MACDownlinkHeader;
 | 
				
			||||||
 | 
						struct MACUplinkHeader;
 | 
				
			||||||
 | 
						struct RLCDownlinkControlBlockHeader;
 | 
				
			||||||
 | 
						struct RLCUplinkControlBlockHeader;
 | 
				
			||||||
 | 
						struct RLCSubBlockHeader;
 | 
				
			||||||
 | 
						struct RLCSubBlockTLLI;
 | 
				
			||||||
 | 
						struct RLCDownlinkDataBlockHeader;
 | 
				
			||||||
 | 
						struct RLCUplinkDataBlockHeader;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// RLCMessages.h:
 | 
				
			||||||
 | 
						class RLCMessage;
 | 
				
			||||||
 | 
						class RLCDownlinkMessage;
 | 
				
			||||||
 | 
						class RLCUplinkMessage;
 | 
				
			||||||
 | 
						struct RLCMsgPacketControlAcknowledgement;
 | 
				
			||||||
 | 
						struct RLCMsgElementPacketAckNackDescription;
 | 
				
			||||||
 | 
						struct RLCMsgElementChannelRequestDescription;
 | 
				
			||||||
 | 
						struct RLCMsgElementRACapabilityValuePart;
 | 
				
			||||||
 | 
						struct RLCMsgPacketDownlinkAckNack;
 | 
				
			||||||
 | 
						struct RLCMsgPacketResourceRequest;
 | 
				
			||||||
 | 
						struct RLCMsgPacketUplinkDummyControlBlock;
 | 
				
			||||||
 | 
						struct RLCMsgPacketResourceRequest;
 | 
				
			||||||
 | 
						//struct RLCMsgPacketMobileTBFStatus;
 | 
				
			||||||
 | 
						class RLCMsgPacketUplinkAssignment;
 | 
				
			||||||
 | 
						class RLCMsgPacketDownlinkAssignment;
 | 
				
			||||||
 | 
						class RLCMsgPacketAccessReject;
 | 
				
			||||||
 | 
						class RLCMsgPacketTBFRelease;
 | 
				
			||||||
 | 
						class RLCMsgPacketUplinkAckNack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// LLC.h:
 | 
				
			||||||
 | 
						class LLCFrame;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//GPRSL2RLCElements.h:class RLCElement
 | 
				
			||||||
 | 
						//GPRSL2RLCElements.h:class RLCAckNackDescription : public RLCElement
 | 
				
			||||||
 | 
						//GPRSL2RLCElements.h:class RLCChannelQualityReport : public RLCElement
 | 
				
			||||||
 | 
						//GPRSL2RLCElements.h:class RLCPacketTimingAdvance : public RLCElement
 | 
				
			||||||
 | 
						//GPRSL2RLCElements.h:class RLCPacketPowerControlParameters : public RLCElement
 | 
				
			||||||
 | 
						//GPRSL2RLCElements.h:class RLCChannelRequestDescription : public RLCElement
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GPRSL2RLCMessages.h:
 | 
				
			||||||
 | 
						//class RLCDownlinkMessage;
 | 
				
			||||||
 | 
						//class RLCPacketDownlinkAckNack;
 | 
				
			||||||
 | 
						//class RLCPacketUplinkAckNack;
 | 
				
			||||||
 | 
						//class RLCPacketDownlinkControlBlock;
 | 
				
			||||||
 | 
						//class RLCPacketUplinkControlBlock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int GetTimingAdvance(float timingError);
 | 
				
			||||||
 | 
						int GetPowerAlpha();
 | 
				
			||||||
 | 
						int GetPowerGamma();
 | 
				
			||||||
 | 
						extern unsigned GPRSDebug;
 | 
				
			||||||
 | 
						extern unsigned gGprsWatch;
 | 
				
			||||||
 | 
						extern std::string fmtfloat2(float num);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Defines.h"
 | 
				
			||||||
 | 
					#include "GSMConfig.h"		// For Time
 | 
				
			||||||
 | 
					#include "GSMCommon.h"		// For ChannelType
 | 
				
			||||||
 | 
					#include "GPRSExport.h"
 | 
				
			||||||
 | 
					#include "Utils.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Logger.h"
 | 
				
			||||||
 | 
					// Redefine GPRSLOG to include the current RLC BSN when called in this directory.
 | 
				
			||||||
 | 
					#ifdef GPRSLOG
 | 
				
			||||||
 | 
					#undef GPRSLOG
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					// 6-18-2012: If someone sets Log.Level to DEBUG, show everything.
 | 
				
			||||||
 | 
					#define GPRSLOG(level) if (GPRS::GPRSDebug & (level) || IS_LOG_LEVEL(DEBUG)) \
 | 
				
			||||||
 | 
						_LOG(DEBUG) <<"GPRS"<<(level)<<","<<GPRS::gBSNNext<<":"
 | 
				
			||||||
 | 
					#define LOGWATCHF(...) if (GPRS::gGprsWatch&1) printf(__VA_ARGS__); GPRSLOG(1)<<"watch:"<<format(__VA_ARGS__);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// If gprs debugging is on, print these messages regardless of Log.Level.
 | 
				
			||||||
 | 
					#define GLOG(wLevel) if (GPRSDebug || IS_LOG_LEVEL(wLevel)) _LOG(wLevel) << " "<<timestr()<<","<<GPRS::gBSNNext<<":"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Like assert() but dont core dump unless we are testing.
 | 
				
			||||||
 | 
					#define devassert(code) {if (GPRS::GPRSDebug||IS_LOG_LEVEL(DEBUG)) {assert(code);} else if (!(code)) {LOG(ERR)<<"assertion failed:"<< #code;}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										161
									
								
								GPRS/GPRSRLC.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								GPRS/GPRSRLC.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,161 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GPRSRLC_H
 | 
				
			||||||
 | 
					#define GPRSRLC_H
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					//#include <stdio.h>
 | 
				
			||||||
 | 
					#include <Timeval.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
					class RLCBSN_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// There are roughly 48 RLC blocks/second.
 | 
				
			||||||
 | 
					const unsigned RLCBlocksPerSecond = 48;
 | 
				
			||||||
 | 
					// TODO: Get this exact.
 | 
				
			||||||
 | 
					const double RLCBlockTime = 1.0 / RLCBlocksPerSecond;		// In seconds
 | 
				
			||||||
 | 
					const unsigned RLCBlockTimeMsecs = 1000.0 / RLCBlocksPerSecond;		// In mseconds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern RLCBSN_t FrameNumber2BSN(int fn);		// convert frame -> BSN
 | 
				
			||||||
 | 
					extern int BSN2FrameNumber(RLCBSN_t bsn);	// convert BSN -> frame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern RLCBSN_t gBSNNext;		// The next Block Sequence Number that will be send on the downlink.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RLCDir
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// Order matters: The first bit of a GlobalTFI is 0 for up, 1 for down, matching this.
 | 
				
			||||||
 | 
						// Either is used as a 'dont-care' dir in function parameters.
 | 
				
			||||||
 | 
						enum type { Up, Down, Either };
 | 
				
			||||||
 | 
						static const char *name(int val)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							switch ((type)val) {
 | 
				
			||||||
 | 
								case Up: return "RLCDir::Up";
 | 
				
			||||||
 | 
								case Down: return "RLCDir::Down";
 | 
				
			||||||
 | 
								case Either: return "RLCDir::Either";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return "unknown";	// Makes gcc happy.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#define RLCDirType RLCDir::type
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const RLCDir::type &mode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern unsigned RLCBlockSize[4];
 | 
				
			||||||
 | 
					extern const unsigned RLCBlockSizeBytesMax;
 | 
				
			||||||
 | 
					extern int deltaBSN(int bsn1,int bsn2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RLCBSN_t { // Type of radio block sequence numbers.  -1 means invalid.
 | 
				
			||||||
 | 
						int32_t mValue;
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// Number of Radio Blocks in a hyperframe: there are 12 blocks every 52 frames.
 | 
				
			||||||
 | 
						// Hyperframe = 2048UL * 26UL * 51UL;
 | 
				
			||||||
 | 
						static const unsigned BSNPeriodicity = 2048UL * 26UL * 51UL * 12UL / 52UL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Note: C++ default operator=() is ok.
 | 
				
			||||||
 | 
						RLCBSN_t() { mValue = -1; }
 | 
				
			||||||
 | 
						RLCBSN_t(int wValue) : mValue(wValue) {}
 | 
				
			||||||
 | 
						operator int() const { return mValue; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void normalize() {
 | 
				
			||||||
 | 
							mValue = mValue % BSNPeriodicity;
 | 
				
			||||||
 | 
							if (mValue<0) { mValue += BSNPeriodicity; }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Return v1 - v2, accounting for wraparound, assuming the values are
 | 
				
			||||||
 | 
						// less than half a hyperframe apart.
 | 
				
			||||||
 | 
						int BSNdelta(RLCBSN_t v2);
 | 
				
			||||||
 | 
						int BSNcompare(RLCBSN_t v2);
 | 
				
			||||||
 | 
						bool operator<(RLCBSN_t v2) { return BSNcompare(v2) < 0; }
 | 
				
			||||||
 | 
						bool operator<=(RLCBSN_t v2) { return BSNcompare(v2) <= 0; }
 | 
				
			||||||
 | 
						bool operator>(RLCBSN_t v2) { return BSNcompare(v2) > 0; }
 | 
				
			||||||
 | 
						bool operator>=(RLCBSN_t v2) { return BSNcompare(v2) >= 0; }
 | 
				
			||||||
 | 
						RLCBSN_t operator+(RLCBSN_t v2) {
 | 
				
			||||||
 | 
							RLCBSN_t result(this->mValue + v2.mValue); result.normalize(); return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						RLCBSN_t operator-(RLCBSN_t v2) {
 | 
				
			||||||
 | 
							RLCBSN_t result(this->mValue - v2.mValue); result.normalize(); return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						RLCBSN_t operator+(int32_t v2) {
 | 
				
			||||||
 | 
							RLCBSN_t result(this->mValue + v2); result.normalize(); return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						RLCBSN_t operator-(int32_t v2) {
 | 
				
			||||||
 | 
							RLCBSN_t result(this->mValue - v2); result.normalize(); return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						RLCBSN_t& operator++() { mValue++; normalize(); return *this; }	// prefix
 | 
				
			||||||
 | 
						void operator++(int) { mValue++; normalize(); }	// postfix
 | 
				
			||||||
 | 
						bool valid() const { return mValue >= 0; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//static const int invalid = -1;	// An invalid value for an RLCBSN_t.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Return the bsn that is msecs in the future from a base bsn.
 | 
				
			||||||
 | 
						// Used to conveniently specify timeouts in terms of something we track, namely, gBSNNext.
 | 
				
			||||||
 | 
						RLCBSN_t addTime(int msecs)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// 20msecs is one BSN.
 | 
				
			||||||
 | 
							int future = (msecs + RLCBlockTimeMsecs/2) / RLCBlockTimeMsecs;
 | 
				
			||||||
 | 
							return *this + future;	// The operator+ normalizes it.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Convert to GSM Frame Number.
 | 
				
			||||||
 | 
						unsigned FN() { return (unsigned) BSN2FrameNumber(mValue); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A timer based on BSNs.
 | 
				
			||||||
 | 
					// Must not extend greater than BSNPeriodicity/ into the future.
 | 
				
			||||||
 | 
					class GprsTimer {
 | 
				
			||||||
 | 
						Timeval mWhen;
 | 
				
			||||||
 | 
						bool mValid;
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					#if 1	// TODO: Switch this to use the BSN based timers below, but needs testing.
 | 
				
			||||||
 | 
						GprsTimer() : mValid(false) {}
 | 
				
			||||||
 | 
						bool valid() const { return mValid; }
 | 
				
			||||||
 | 
						void setInvalid() { mValid = false; }
 | 
				
			||||||
 | 
						// Two functions for a countdown timer:
 | 
				
			||||||
 | 
						void setFuture(int msecs) { mValid = true; mWhen.future(msecs); }
 | 
				
			||||||
 | 
						bool expired() const { return mValid && mWhen.passed(); }
 | 
				
			||||||
 | 
						// Two functions for a countup timer:
 | 
				
			||||||
 | 
						void setNow() { mValid = true; mWhen.now(); }
 | 
				
			||||||
 | 
						int elapsed() const { return mValid ? mWhen.elapsed() : 0; }
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						RLCBSN_t mWhen;	// Inits to not valid.
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						bool valid() { return mWhen.valid(); }
 | 
				
			||||||
 | 
						void setInvalid() { mValid = false; }
 | 
				
			||||||
 | 
						// setFuture and expired function as a countdown timer.
 | 
				
			||||||
 | 
						void setFuture(int msecs) {
 | 
				
			||||||
 | 
							mWhen = gBSNNext.addTime(msecs);
 | 
				
			||||||
 | 
							GPRSLOG(1) << format("*** setFuture %d bsnnext=%d when=%d\n",msecs,(int)gBSNNext,(int)mWhen);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool expired() {
 | 
				
			||||||
 | 
							GPRSLOG(1) << format("*** expired valid=%d bsnnext=%d when=%d togo=%d\n",
 | 
				
			||||||
 | 
								valid(),(int)gBSNNext,(int)mWhen,(mWhen-gBSNNext)*RLCBlockTimeMsecs );
 | 
				
			||||||
 | 
							return valid() && gBSNNext > mWhen;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// setNow and elapsed function as a countup timer.
 | 
				
			||||||
 | 
						void setNow() { mWhen = gBSNNext; }
 | 
				
			||||||
 | 
						// Elapsed time from a now() in msecs.
 | 
				
			||||||
 | 
						int elapsed() {
 | 
				
			||||||
 | 
							return valid() ? (int)(gBSNNext - mWhen) * RLCBlockTimeMsecs : 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						long remaining() const { return -elapsed(); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										69
									
								
								GPRS/GPRSTDMA.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								GPRS/GPRSTDMA.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					/**@file GPRS TDMA parameters. */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GPRSTDMA_H
 | 
				
			||||||
 | 
					#define GPRSTDMA_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "GSMCommon.h"
 | 
				
			||||||
 | 
					#include "GSMTDMA.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
					// (pat) We wont use this to program the radio right now, and probably never.
 | 
				
			||||||
 | 
					// However, it is needed to init a LogicalChannel.  The info needs to duplicate
 | 
				
			||||||
 | 
					// that for an RR TCH, which is what we are really using.
 | 
				
			||||||
 | 
					// Currently we will hook to the existing RR logical channels to get RLC blocks.
 | 
				
			||||||
 | 
					// In the future, we probably would not use this either, we would probably modify
 | 
				
			||||||
 | 
					// TRXManager to send the entire channel stream directly to us, and then direct
 | 
				
			||||||
 | 
					// the packets internally; not use the ARFCNManager::mDemuxTable,
 | 
				
			||||||
 | 
					// which is what this TDMA_MAPPING is primarily for.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** A macro to save some typing when we set up TDMA maps. */
 | 
				
			||||||
 | 
					// This is copied from ../GSM/GSMTDMA.cpp
 | 
				
			||||||
 | 
					#define MAKE_TDMA_MAPPING(NAME,TYPEANDOFFSET,DOWNLINK,UPLINK,ALLOWEDSLOTS,C0ONLY,REPEAT) \
 | 
				
			||||||
 | 
					 	const GSM::TDMAMapping g##NAME##Mapping(TYPEANDOFFSET,DOWNLINK,UPLINK,ALLOWEDSLOTS,C0ONLY, \
 | 
				
			||||||
 | 
					 		REPEAT,sizeof(NAME##Frames)/sizeof(unsigned),NAME##Frames)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** PDCH TDMA from GSM 03.64 6.1.2, GSM 05.02 Clause 7 Table 6 of 9. */
 | 
				
			||||||
 | 
					// (pat) This was the orignal; does not look correct to me:
 | 
				
			||||||
 | 
					// const unsigned PDCHFrames[] = {0,1,2,3, 4,5,6,7, 8,9,10,11, 13,14,15,16, 16,17,18,19,
 | 
				
			||||||
 | 
					//	20,21,22,23, 25,26,27,28, 29,30,31,32, 33,34,35,36, 38,39,40,41, 42,43,44,45, 46,47,48,49};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (pat) This is first line (PDTCH/F PACCH/F) of GSM05.02 clause 7 table  6 of 9
 | 
				
			||||||
 | 
					// Note that we skip over frames 12, 25, 38 and 51, which are used for other purposes.
 | 
				
			||||||
 | 
					// TODO: I dont know if we are going to handle the frame mapping this way,
 | 
				
			||||||
 | 
					// or let the GPRS code handle all the frames, including the PTCCH (timing advance) slots.
 | 
				
			||||||
 | 
					const unsigned PDTCHFFrames[] = {0,1,2,3, 4,5,6,7, 8,9,10,11, 13,14,15,16, 17,18,19,20,
 | 
				
			||||||
 | 
						21,22,23,24, 26,27,28,29, 30,31,32,33, 34,35,36,37, 39,40,41,42, 43,44,45,46, 47,48,49,50 };
 | 
				
			||||||
 | 
					const unsigned PTCCHFrames[] = { 12, 38 };
 | 
				
			||||||
 | 
					const unsigned PDIdleFrames[] = { 25, 51 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PDCH is the name of the packet data channel, comprised of PDTCH, PTCCH, and 2 idle frames.
 | 
				
			||||||
 | 
					MAKE_TDMA_MAPPING(PDTCHF,GSM::TDMA_PDTCHF,true,true,0xff,false,52); // Makes gPDTCHFMapping
 | 
				
			||||||
 | 
					MAKE_TDMA_MAPPING(PTCCH,GSM::TDMA_PTCCH,true,false,0xff,false,52);
 | 
				
			||||||
 | 
					MAKE_TDMA_MAPPING(PDIdle,GSM::TDMA_PDIDLE,true,false,0xff,false,52);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const GSM::MappingPair gPDTCHPair(gPDTCHFMapping,gPDTCHFMapping);
 | 
				
			||||||
 | 
					const GSM::MappingPair gPTCCHPair(gPTCCHMapping);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}; // namespace GPRS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										2570
									
								
								GPRS/MAC.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2570
									
								
								GPRS/MAC.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										522
									
								
								GPRS/MAC.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										522
									
								
								GPRS/MAC.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,522 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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 Common-use GSM declarations, most from the GSM 04.xx and 05.xx series. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GPRSL2MAC_H
 | 
				
			||||||
 | 
					#define GPRSL2MAC_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "MemoryLeak.h"
 | 
				
			||||||
 | 
					#include "GPRSRLC.h"
 | 
				
			||||||
 | 
					#include "GSML1FEC.h"
 | 
				
			||||||
 | 
					#include "GSMTDMA.h"
 | 
				
			||||||
 | 
					#include "GSMTransfer.h"	// For GSM::L2Frame
 | 
				
			||||||
 | 
					#include "Interthread.h"
 | 
				
			||||||
 | 
					//#include "GSMCommon.h"	// For ChannelType
 | 
				
			||||||
 | 
					#include "GSML3RRElements.h"	// For RequestReference
 | 
				
			||||||
 | 
					#include "TBF.h"
 | 
				
			||||||
 | 
					#include "RList.h"
 | 
				
			||||||
 | 
					#include "Utils.h"
 | 
				
			||||||
 | 
					#include <list>
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
					extern void mac_debug();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (pat) About BSN (radio block sequence numbers):
 | 
				
			||||||
 | 
					// We need a period for our internal BSNs, and it is somewhat arbitrary.
 | 
				
			||||||
 | 
					// For timing, we need a cyclic period of at least 8 52-multiframes.  (GSM03.64 Figure 19)
 | 
				
			||||||
 | 
					// There are specified timeouts of up to 5 seconds over which it would be convenient
 | 
				
			||||||
 | 
					// if radio block numbers were non-repeating, which would be about 1000 frames, 250 radio blocks.
 | 
				
			||||||
 | 
					// But there is no reason not to just use the hyperframe, so we will.
 | 
				
			||||||
 | 
					// A hyperframe is 2048 * 26 * 51 frames; (2048*26*51 * 12/52) = 626688 Radio Blocks.
 | 
				
			||||||
 | 
					// A 52 multiframe takes 240ms, so each radio block averages 20ms.
 | 
				
			||||||
 | 
					// btw, if the block sequence numbers went on forever, stored in an int, it would last 497 days.
 | 
				
			||||||
 | 
					// There are 52 frames for 12 blocks.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// In GSM the downlink block numbers trail the uplink block numbers slightly.
 | 
				
			||||||
 | 
					// I observed the incoming blocks are this far behind gBSNNext.
 | 
				
			||||||
 | 
					// If you are expecting an answer at time N,
 | 
				
			||||||
 | 
					// look for it when gBSNext == N+BSNLagTime.
 | 
				
			||||||
 | 
					const int BSNLagTime = 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern bool gFixTFIBug;
 | 
				
			||||||
 | 
					extern bool gFixSyncUseClock; // For debugging: Use wall time for service loop synchronization
 | 
				
			||||||
 | 
												// instead of GSM frames.  TODO: Get rid of this.
 | 
				
			||||||
 | 
					extern int gFixIdleFrame;	// Works around this bug - see code.
 | 
				
			||||||
 | 
					extern int gFixDRX;		// Works around DRX mode bug.  The value is the number of assignments
 | 
				
			||||||
 | 
										// that are unanswered before we assume the MS is in DRX mode.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Set to 1 to request a poll in the ImmediateAssignment on CCCH.
 | 
				
			||||||
 | 
					// We still have to wait for the poll time anyway, so this is here
 | 
				
			||||||
 | 
					// only for debugging.
 | 
				
			||||||
 | 
					extern bool gFixIAUsePoll;
 | 
				
			||||||
 | 
					extern bool gFixConvertForeignTLLI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef RList<PDCHL1FEC*> PDCHL1FECList_t;
 | 
				
			||||||
 | 
					typedef RList<MSInfo*> MSInfoList_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Stats_t {
 | 
				
			||||||
 | 
						Statistic<double> macServiceLoopTime;
 | 
				
			||||||
 | 
						UInt_z countPDCH;
 | 
				
			||||||
 | 
						UInt_z countMSInfo;
 | 
				
			||||||
 | 
						UInt_z countTBF;
 | 
				
			||||||
 | 
						UInt_z countRach;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					extern struct Stats_t Stats;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For now, there is only one pool of TFIs shared by all channels.
 | 
				
			||||||
 | 
					// It should be per-ARFCN, but I expect there to only be one.
 | 
				
			||||||
 | 
					// Sharing TFIs between channels on the ARFCN makes multislot tfi allocation easy.
 | 
				
			||||||
 | 
					// There are 32 uplink and 32 downlink TFIs, which should be enough for some time to come.
 | 
				
			||||||
 | 
					// If this gets congested, can be split up into one pool of TFIs per uplink/downlink channel,
 | 
				
			||||||
 | 
					// but then you have to be careful when allocating multislot tfis that the
 | 
				
			||||||
 | 
					// tfi is unique across all channels.
 | 
				
			||||||
 | 
					// TODO: When we allocate multislot tfis, the tfi must be unique in all slots.
 | 
				
			||||||
 | 
					// The easiest way to do that is to have a single tfilist for the entire system.
 | 
				
			||||||
 | 
					class TBF;
 | 
				
			||||||
 | 
					struct TFIList {
 | 
				
			||||||
 | 
						TBF *mTFIs[2][32];	// One list for uplink, one list for downlink.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 12-22-2011: It looked like the Blackberry abandoned an uplink TBF when
 | 
				
			||||||
 | 
						// a downlink TBF was established using the same TFI.   This seems like a horrible
 | 
				
			||||||
 | 
						// bug in the MS, but to work around it, I added the gFixTFIBug which uses
 | 
				
			||||||
 | 
						// a single TFI space for both uplink and downlink.  This eliminated
 | 
				
			||||||
 | 
						// a bunch of msStop calls with cause T3101, so I think this is a genuine
 | 
				
			||||||
 | 
						// problem with MSs and we need this fix in permanently.
 | 
				
			||||||
 | 
						// Update: The TFI is reserved during the time after a downlink ends, so the MS
 | 
				
			||||||
 | 
						// may have been justifiably upset about seeing it reissued for an uplink too soon.
 | 
				
			||||||
 | 
						RLCDir::type fixdir(RLCDir::type dir) {
 | 
				
			||||||
 | 
							return gFixTFIBug ? RLCDir::Up : dir;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TFIList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TBF *getTFITBF(RLCDirType dir, int tfi) { return mTFIs[fixdir(dir)][tfi]; }
 | 
				
			||||||
 | 
						void setTFITBF(int tfi,RLCDirType dir,TBF *tbf) { mTFIs[fixdir(dir)][tfi] = tbf; }
 | 
				
			||||||
 | 
						int findFreeTFI(RLCDirType dir);
 | 
				
			||||||
 | 
						void tfiDump(std::ostream&os);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					extern struct TFIList *gTFIs;
 | 
				
			||||||
 | 
					#if MAC_IMPLEMENTATION
 | 
				
			||||||
 | 
					TFIList::TFIList() { for (int i=0;i<32;i++) { mTFIs[0][i] = mTFIs[1][i] = NULL; } }
 | 
				
			||||||
 | 
					int TFIList::findFreeTFI(RLCDirType dir)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static int lasttfi = 0;
 | 
				
			||||||
 | 
						dir = fixdir(dir);
 | 
				
			||||||
 | 
						// TODO: After the TBF the tfi may only be reused by the same MS for some
 | 
				
			||||||
 | 
						// period of time.  See "TBF release" section.
 | 
				
			||||||
 | 
						// Temporary work around is to round-robin the tfis.
 | 
				
			||||||
 | 
						for (int i = 0; i < 32; i++) {
 | 
				
			||||||
 | 
							lasttfi++;
 | 
				
			||||||
 | 
							if (lasttfi == 32) { lasttfi = 0; }
 | 
				
			||||||
 | 
							if (!mTFIs[dir][lasttfi]) { return lasttfi; }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
						// DEBUG: start at 1 instead of 0
 | 
				
			||||||
 | 
						for (int tfi = 1; tfi < 32; tfi++) {
 | 
				
			||||||
 | 
							// DEBUG: try incrementing tfi to avoid duplication errors:
 | 
				
			||||||
 | 
							if (tfi == lasttfi) { continue; }
 | 
				
			||||||
 | 
							if (!mTFIs[dir][tfi]) {
 | 
				
			||||||
 | 
								lasttfi = tfi;
 | 
				
			||||||
 | 
								return tfi;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						return -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TFIList::tfiDump(std::ostream&os)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int dir, tfi;
 | 
				
			||||||
 | 
						for (dir = 0; dir <= (gFixTFIBug ? 0 : 1); dir++) {
 | 
				
			||||||
 | 
							os << "TFI=(";
 | 
				
			||||||
 | 
							if (!gFixTFIBug) {
 | 
				
			||||||
 | 
								os << RLCDir::name(dir) << ":";
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for (tfi = 0; tfi < 32; tfi++) {
 | 
				
			||||||
 | 
								TBF *tbf = mTFIs[dir][tfi];
 | 
				
			||||||
 | 
								if (tbf) { os << " " << tfi << "=>" << tbf; }
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						os << ")\n";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The USF associates radio blocks with an MS.
 | 
				
			||||||
 | 
					// It is placed in the downlink block header to indicate that the next uplink block
 | 
				
			||||||
 | 
					// is allocated to the MS assigned that USF.
 | 
				
			||||||
 | 
					// There may be multiple simultaneous uplink TBFs from the same MS; all will use the same USF.
 | 
				
			||||||
 | 
					// The MS does that if a higher priority or throughput TBF comes in while one is in progress.
 | 
				
			||||||
 | 
					// The USF is only 3 bits, and 0x7 is reserved (to indicate PRACH) on PCCCH channels.
 | 
				
			||||||
 | 
					// We are not using PCCCH, but I am going to avoid 0x7 anyway.
 | 
				
			||||||
 | 
					// Additionally, for RACH initiated single block uplink assignments, we need a USF
 | 
				
			||||||
 | 
					// that is not in use by any MS, for which we will reserve USF=0.
 | 
				
			||||||
 | 
					// so really only 6 USFs (1-6) are available per uplink channel, which should be plenty.
 | 
				
			||||||
 | 
					// Note that there are alot more TFIs than USFs, because TFIs are per-TBF,
 | 
				
			||||||
 | 
					// while USFs are per-MS.
 | 
				
			||||||
 | 
					// The USFList information applies to an uplink channel, but it is used primarily by
 | 
				
			||||||
 | 
					// the downlink channel to set the USF in the MAC header of each downlink block.
 | 
				
			||||||
 | 
					// Note that there is no pointer to the TBFs (could be multiple ones) from this USF struct,
 | 
				
			||||||
 | 
					// because arriving uplink blocks are associated with their TBFs using the TFI, not the USF.
 | 
				
			||||||
 | 
					// The USFs are numbered 0..7.
 | 
				
			||||||
 | 
					const int USFMIN = 1;
 | 
				
			||||||
 | 
					const int USFMAX = 6;
 | 
				
			||||||
 | 
					const int USFTotal = 8;
 | 
				
			||||||
 | 
					#define USF_VALID(usf) ((usf) >= USFMIN && (usf) <= USFMAX)
 | 
				
			||||||
 | 
					class USFList
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						//PDCHL1FEC *mlchan;			// The channel these USFs are being used on.
 | 
				
			||||||
 | 
						// We use the usf as the index, so mlUSFs[0] is unused.
 | 
				
			||||||
 | 
						struct UsfInfo {
 | 
				
			||||||
 | 
							MSInfo *muMS; 				// The MS assigned this USF on this channel.
 | 
				
			||||||
 | 
							GprsTimer muDeadTime;		// When a TBF dies reserve the USF for this ms until this expires.
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						UsfInfo mlUSFs[USFTotal]; 		// Some slots in this array are unused.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//int mRandomUSF;			// Used to pick one of the USFs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// When the channel is running, we save the usf that is sent on each downlink block,
 | 
				
			||||||
 | 
						// so that we can correlate the uplink responses independently of their content.
 | 
				
			||||||
 | 
						// We save them in an array indexed by bsn, length only has to cover the difference
 | 
				
			||||||
 | 
						// between uplink and downlink BSNs plus some slop, so 32 is way overkill.
 | 
				
			||||||
 | 
						unsigned char sRememberUsf[32];	// The usf transmitted in the downlink block.
 | 
				
			||||||
 | 
						unsigned sRememberUsfBsn[32];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						USFList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Return the usf that was specified on the downlink burst, given the bsn from the uplink burst.
 | 
				
			||||||
 | 
						int getUsf(unsigned upbsn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Remember the usf transmitted on specified downlink burst.  OK to pass 0 for usf.
 | 
				
			||||||
 | 
						void setUsf(unsigned downusf, unsigned downbsn);		// Save usf for current downlink burst.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Which MS is using this USF?
 | 
				
			||||||
 | 
						MSInfo *getUSFMS(int usf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int allocateUSF(MSInfo *ms);
 | 
				
			||||||
 | 
						int freeUSF(MSInfo *ms,bool wReserve);
 | 
				
			||||||
 | 
						//int getRandomUSF();
 | 
				
			||||||
 | 
						void usfDump(std::ostream&os);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct RachInfo
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned mRA;
 | 
				
			||||||
 | 
						const GSM::Time mWhen;
 | 
				
			||||||
 | 
						RadData mRadData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Gotta love this language.
 | 
				
			||||||
 | 
						RachInfo(unsigned wRA, const GSM::Time &wWhen, RadData wRD)
 | 
				
			||||||
 | 
							: mRA(wRA), mWhen(wWhen), mRadData(wRD)
 | 
				
			||||||
 | 
							{ RN_MEMCHKNEW(RachInfo) }
 | 
				
			||||||
 | 
						~RachInfo() { RN_MEMCHKDEL(RachInfo) }
 | 
				
			||||||
 | 
						void serviceRach();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// There is only one of these.
 | 
				
			||||||
 | 
					// It holds the lists used to find all the other stuff.
 | 
				
			||||||
 | 
					class L2MAC
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Thread macThread;
 | 
				
			||||||
 | 
						// The entire MAC runs in a single thread.
 | 
				
			||||||
 | 
						// This Mutex is used at startup to make sure we only start one.
 | 
				
			||||||
 | 
						// Also used to lock the serviceloop so the CLI does not modify MS or TBF lists simultaneously.
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						mutable Mutex macLock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The RACH bursts come in unsychronized to the rest of the GPRS code.
 | 
				
			||||||
 | 
						// The primary purpose of this queue is just to allow the MAC service loop
 | 
				
			||||||
 | 
						// to handle the RACH in its single thread by saving the RACH until the MAC service
 | 
				
			||||||
 | 
						// loop gets around to it.  If GPRS is running, we dont really expect multiple
 | 
				
			||||||
 | 
						// simultaneous RACHs to queue up here because we service the RACH queue on each loop.
 | 
				
			||||||
 | 
						// However, if a RACH comes in while GPRS service is stopped and all channels
 | 
				
			||||||
 | 
						// are in use for RR connections, the as-yet-unserviced RACHs may queue up here.
 | 
				
			||||||
 | 
						// When GPRS service resumes, we should disard RACHs that are too old.
 | 
				
			||||||
 | 
						// Note that there could be multiple RACH for the same MS, a case we cannot detect.
 | 
				
			||||||
 | 
						InterthreadQueue<RachInfo> macRachQ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// We are doing a linear search through these lists, but there should only be a few of them.
 | 
				
			||||||
 | 
						PDCHL1FECList_t macPDCHs;	// channels assigned to GPRS.
 | 
				
			||||||
 | 
						PDCHL1FECList_t macPacchs;	// The subset of macPDCHs that we assign as PACCH.
 | 
				
			||||||
 | 
						//Mutex macTbfListLock;
 | 
				
			||||||
 | 
						TBFList_t macTBFs;	// active TBFs.
 | 
				
			||||||
 | 
						MSInfoList_t macMSs;	// The MS we know about.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// For debugging, we keep expired TBF and MS around for post-mortem examination:
 | 
				
			||||||
 | 
						TBFList_t macExpiredTBFs;
 | 
				
			||||||
 | 
						MSInfoList_t macExpiredMSs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RN_MAC_FOR_ALL_PDCH(ch) RN_FOR_ALL(PDCHL1FECList_t,gL2MAC.macPDCHs,ch)
 | 
				
			||||||
 | 
					#define RN_MAC_FOR_ALL_PACCH(ch) RN_FOR_ALL(PDCHL1FECList_t,gL2MAC.macPacchs,ch)
 | 
				
			||||||
 | 
					#define RN_MAC_FOR_ALL_MS(ms) for (RListIterator<MSInfo*> itr(gL2MAC.macMSs); itr.next(ms); )
 | 
				
			||||||
 | 
					#define RN_MAC_FOR_ALL_TBF(tbf) for (RListIterator<TBF*> itr(gL2MAC.macTBFs); itr.next(tbf); ) 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L2MAC()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							gTFIs = new TFIList();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						~L2MAC() { delete gTFIs; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						unsigned macN3101Max;
 | 
				
			||||||
 | 
						unsigned macN3103Max;
 | 
				
			||||||
 | 
						unsigned macN3105Max;
 | 
				
			||||||
 | 
						unsigned macT3169Value;
 | 
				
			||||||
 | 
						unsigned macT3191Value;
 | 
				
			||||||
 | 
						unsigned macT3193Value;
 | 
				
			||||||
 | 
						unsigned macT3168Value;
 | 
				
			||||||
 | 
						unsigned macT3195Value;
 | 
				
			||||||
 | 
						unsigned macMSIdleMax;
 | 
				
			||||||
 | 
						unsigned macChIdleMax;	
 | 
				
			||||||
 | 
						unsigned macChCongestionMax;
 | 
				
			||||||
 | 
						unsigned macDownlinkPersist;
 | 
				
			||||||
 | 
						unsigned macDownlinkKeepAlive;
 | 
				
			||||||
 | 
						unsigned macUplinkPersist;
 | 
				
			||||||
 | 
						unsigned macUplinkKeepAlive;
 | 
				
			||||||
 | 
						float macChCongestionThreshold;
 | 
				
			||||||
 | 
						Float_z macDownlinkUtilization;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Bool_z macRunning;		// The macServiceLoop is running.
 | 
				
			||||||
 | 
						time_t macStartTime;
 | 
				
			||||||
 | 
						Bool_z macStopFlag;		// Set this to terminate the service thread.
 | 
				
			||||||
 | 
						Bool_z macSingleStepMode;	// For debugging.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						MSInfo *macFindMSByTlli(uint32_t tlli, int create = 0);
 | 
				
			||||||
 | 
						void macAddMS(MSInfo *ms);
 | 
				
			||||||
 | 
						void macForgetMS(MSInfo *ms,bool forever);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// When deleting tbfs, macForgetTBF could be called on a tbf already removed
 | 
				
			||||||
 | 
						// from the list, which is ok.
 | 
				
			||||||
 | 
						void macAddTBF(TBF *tbf);
 | 
				
			||||||
 | 
						void macForgetTBF(TBF *tbf,bool forever);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void macServiceLoop();
 | 
				
			||||||
 | 
						PDCHL1FEC *macPickChannel();	// pick the least busy channel;
 | 
				
			||||||
 | 
						PDCHL1FEC *macFindChannel(unsigned arfcn, unsigned tn);	// find specified channel, or null
 | 
				
			||||||
 | 
						unsigned macFindChannels(unsigned arfcn);
 | 
				
			||||||
 | 
						bool macAddChannel();		// Add a GSM RR channel to GPRS use.
 | 
				
			||||||
 | 
						bool macFreeChannel();		// Restore a GPRS channel back to GSM RR use.
 | 
				
			||||||
 | 
						void macForgetCh(PDCHL1FEC*ch);
 | 
				
			||||||
 | 
						void macConfigInit();
 | 
				
			||||||
 | 
						bool macStart();	// Fire it up.
 | 
				
			||||||
 | 
						void macStop(bool channelstoo);		// Try to kill it.
 | 
				
			||||||
 | 
						int macActiveChannels();		// Is GPRS running, ie, are there channels allocated?
 | 
				
			||||||
 | 
						int macActiveChannelsC(unsigned cn);		// Number of channels on specified 0-based ARFCN
 | 
				
			||||||
 | 
						float macComputeUtilization();
 | 
				
			||||||
 | 
						void macCheckChannels();
 | 
				
			||||||
 | 
						void macServiceRachQ();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					extern L2MAC gL2MAC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//const int TFIInvalid = -1;
 | 
				
			||||||
 | 
					//const int TFIUnknown = -2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The master clock is not exactly synced up with the radio clock.
 | 
				
			||||||
 | 
					// It is corrected at intervals.  This means there is a variable delay
 | 
				
			||||||
 | 
					// between the time we send a message to the MS and when we can expect an answer.
 | 
				
			||||||
 | 
					// It does not affect RRBP reservations, which are relative, but it affects
 | 
				
			||||||
 | 
					// L3 messages sent on CCCH, which must specify an exact time for the response.
 | 
				
			||||||
 | 
					// I'm not sure what to do about this.
 | 
				
			||||||
 | 
					// I am just adding some ExtraDelay, and if reservations are unanswered,
 | 
				
			||||||
 | 
					// increasing this value until they start getting answered.
 | 
				
			||||||
 | 
					// We could probably set this back to 0 when we observe the time() run backwards,
 | 
				
			||||||
 | 
					// which means the clock is synced back up.
 | 
				
			||||||
 | 
					extern int ExtraClockDelay;	// in blocks.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This specifies a Radio Block reservation in an uplink channel.
 | 
				
			||||||
 | 
					// Reservations are used for:
 | 
				
			||||||
 | 
					//	o response to CCCH Immediate Assignment One BLock initiated by MS on RACH.
 | 
				
			||||||
 | 
					//		In this case we dont even know what MS the message was coming from, so if
 | 
				
			||||||
 | 
					//		it does not arrive, nothing we can do but wait for the MS to try again later.
 | 
				
			||||||
 | 
					//  o For an Uplink TBF: The RLCUpEngine sends AckNack every N blocks.
 | 
				
			||||||
 | 
					//		We could require a response, but I dont think we will unless it gets stalled.
 | 
				
			||||||
 | 
					//	o For a stalled Uplink TBF: The RLCUpEngine sends an AckNack with an RRBP
 | 
				
			||||||
 | 
					//		reservation.  The MS may respond with any type of message.
 | 
				
			||||||
 | 
					//		If that response does not arrive, the RLCUpEngine network immediately sends
 | 
				
			||||||
 | 
					//		another AckNack.  Serviced when Uplink serviced.
 | 
				
			||||||
 | 
					//	o For a completed Uplink TBF: network sends a final acknack with RRBP reservation,
 | 
				
			||||||
 | 
					//		which must be repeated until received.
 | 
				
			||||||
 | 
					//		Could be handled by the engine or MsgTransaction.
 | 
				
			||||||
 | 
					//	o For a Downlink TBF: send an RRBP reservation every N blocks, which we expect
 | 
				
			||||||
 | 
					//		the MS to use to send us an AckNack, or some other message.  If we dont get
 | 
				
			||||||
 | 
					//		a response, send another RRBP reservation immediately.
 | 
				
			||||||
 | 
					//	o For a stalled Downlink TBF: resend the oldest block with another RRBP reservation.
 | 
				
			||||||
 | 
					//	Note: a completed Downlink TBF can be destroyed immediately, since we received
 | 
				
			||||||
 | 
					//	the final ack nack.
 | 
				
			||||||
 | 
					// The following are handled by the MsgTransaction class:
 | 
				
			||||||
 | 
					//  o response to Packet Polling Request message.
 | 
				
			||||||
 | 
					//		If the message does not arrive, we may want to try again.
 | 
				
			||||||
 | 
					//	o RRBP responses in downlink TBF data blocks or control blocks.
 | 
				
			||||||
 | 
					//		If these dont arrive from the MS, it doesnt matter.
 | 
				
			||||||
 | 
					//		The response type is actually unknown: it could be a Packet Downlink Ack/Nack,
 | 
				
			||||||
 | 
					//		or anything else the MS wants to send.  The uplink message will have all the required
 | 
				
			||||||
 | 
					//		info so we dont have to save anything in the RLCBlockReservation;
 | 
				
			||||||
 | 
					//	o Packet Control Acknowledgement responses.  Could come from:
 | 
				
			||||||
 | 
					//		- Packet uplink/downlink assignment message, which may require network to resend.
 | 
				
			||||||
 | 
					//		- Packet TBF release message, which requires network to resend.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//  o WRONG: For an Uplink TBF: The RLCUpEngine sends AckNack after N blocks and provides an RRBP
 | 
				
			||||||
 | 
					//		uplink response.  The MS may respond with any type of message.
 | 
				
			||||||
 | 
					//		If that response does not arrive, the RLCUpEngine network immediately sends
 | 
				
			||||||
 | 
					//		another AckNack.
 | 
				
			||||||
 | 
					struct RLCBlockReservation : public Text2Str {
 | 
				
			||||||
 | 
						enum type {
 | 
				
			||||||
 | 
							None,
 | 
				
			||||||
 | 
							ForRACH,
 | 
				
			||||||
 | 
							ForPoll, // For the poll response to a downlink immediate assignment when
 | 
				
			||||||
 | 
									// the MS is in packet idle mode.  see sendAssignment()
 | 
				
			||||||
 | 
							ForRRBP	// This has many subtypes of type MsgTransactionType
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						type mrType;		// Primary type
 | 
				
			||||||
 | 
						MsgTransactionType mrSubType;	// Subtype, only valid if mrType is ForRRBP
 | 
				
			||||||
 | 
						RLCBSN_t mrBSN;		// The block number that has been reserved.
 | 
				
			||||||
 | 
						TBF *mrTBF;			// tbf if applicable.  (Not known for MS initiated RACH.)
 | 
				
			||||||
 | 
						// TODO: Is it stupid to save mrRadData?  We will get new data when the MS responds.
 | 
				
			||||||
 | 
						RadData mrRadData;		// Saved from a RACH to be put in MS when it responds.
 | 
				
			||||||
 | 
						static const char *name(RLCBlockReservation::type type);
 | 
				
			||||||
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, RLCBlockReservation::type &type);
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, RLCBlockReservation &res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if MAC_IMPLEMENTATION
 | 
				
			||||||
 | 
					const char *RLCBlockReservation::name(RLCBlockReservation::type mode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (mode) {
 | 
				
			||||||
 | 
							CASENAME(None)
 | 
				
			||||||
 | 
							CASENAME(ForRACH)
 | 
				
			||||||
 | 
							CASENAME(ForPoll)
 | 
				
			||||||
 | 
							CASENAME(ForRRBP)
 | 
				
			||||||
 | 
							default: return "unrecognized";	// Not reached, but makes gcc happy.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, RLCBlockReservation::type &type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << RLCBlockReservation::name(type);
 | 
				
			||||||
 | 
						return os;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void RLCBlockReservation::text(std::ostream &os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << "res=(";
 | 
				
			||||||
 | 
						os << LOGVAR2("bsn",mrBSN) << " " << name(mrType);
 | 
				
			||||||
 | 
						if (mrTBF) { os << " " << mrTBF; }
 | 
				
			||||||
 | 
						os <<")";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, RLCBlockReservation &res)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						res.text(os);
 | 
				
			||||||
 | 
						return os;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The uplink reservation system.
 | 
				
			||||||
 | 
					// It is used by both uplink and downlink parts.
 | 
				
			||||||
 | 
					// I put it in its own class to avoid clutter elsewhere.
 | 
				
			||||||
 | 
					// Note that reservations are kept around after the current time passes,
 | 
				
			||||||
 | 
					// so that uplink acknowledgements can be paired with the message to which they belong.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// In order to efficiently utilize the uplink resource, we need to make reservations
 | 
				
			||||||
 | 
					// of future uplink RLCBlocks for various purposes.
 | 
				
			||||||
 | 
					// There is a problem in that the RRBP field is limited to identifying
 | 
				
			||||||
 | 
					// blocks 3-6 blocks in the future.  The obvious solution would be to reserve
 | 
				
			||||||
 | 
					// them first, but that will not work because the control messages occur asynchronously
 | 
				
			||||||
 | 
					// with respect to the data streams and (should) have higher priority.
 | 
				
			||||||
 | 
					// My K.I.S.S. system to efficiently use these resources is to reserve even numbered
 | 
				
			||||||
 | 
					// uplink blocks for RRBP responses, which are typically Ack/Nack blocks,
 | 
				
			||||||
 | 
					// but could actually be any type of block, and to reserve odd numbered uplink blocks
 | 
				
			||||||
 | 
					// for all other control blocks, which include Single-Block accesses initiated
 | 
				
			||||||
 | 
					// by RACH (in which case, there is no TFI or TLLI) and all other control block
 | 
				
			||||||
 | 
					// responses to network initiated messages.
 | 
				
			||||||
 | 
					// A minimum sized downlink response is 2 blocks long, so this method tends to fully
 | 
				
			||||||
 | 
					// utilize the channel and still limit latency (as opposed to alternative schemes
 | 
				
			||||||
 | 
					// that would assign fixed slots for various messages, or lump all the blocks together
 | 
				
			||||||
 | 
					// which would mean that RRBP responses would sometimes not have a reservation available.)
 | 
				
			||||||
 | 
					// Note that a single-block downlink resend as a result of an Ack/Nack message with
 | 
				
			||||||
 | 
					// a single Nack may be only one block long, so it is still possible to run
 | 
				
			||||||
 | 
					// out of odd numbered uplink blocks for RRBP responses.
 | 
				
			||||||
 | 
					// When downlink blocks are finally queued for transmission, any unreserved uplink
 | 
				
			||||||
 | 
					// blocks are utilized for current uplink data transfers using dynamic allocation using USF.
 | 
				
			||||||
 | 
					// Using this method, the reservations are also monotonically increasing in each
 | 
				
			||||||
 | 
					// domain (RRBP and non-RRBP) which makes it easy.
 | 
				
			||||||
 | 
					class L1UplinkReservation
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						private:
 | 
				
			||||||
 | 
						// TODO: mLock no longer needed because RACH processing synchronous now.
 | 
				
			||||||
 | 
						Mutex mLock;	// The reservation controller can be called from GSM threads, so protect it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// There should only be a few reservations at a time, probably limited to
 | 
				
			||||||
 | 
						// around one per actively attached MS.
 | 
				
			||||||
 | 
						// Update 8-8-2012: well, the MS can be in two sub-modes of transmit mode simultaneously,
 | 
				
			||||||
 | 
						// specifically, persistent keep-alive and reassignment, so I am upgrading this
 | 
				
			||||||
 | 
						// with the sub-type.
 | 
				
			||||||
 | 
						// There are timeouts of up to 5 seconds (250 blocks), so we should keep history that long.
 | 
				
			||||||
 | 
						// The maximum reservation in advance is probably from AGCH, which can stack up
 | 
				
			||||||
 | 
						// waiting for CCCH downlink spots up to a maximum (defined by a config option)
 | 
				
			||||||
 | 
						// in AccessGrantResponder(), but currently 1.5 seconds.
 | 
				
			||||||
 | 
						// There is no penalty for making this array larger, so just go ahead and overkill it.
 | 
				
			||||||
 | 
						static const int mReservationSize = (2*500);
 | 
				
			||||||
 | 
						RLCBlockReservation mReservations[mReservationSize];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L1UplinkReservation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private:
 | 
				
			||||||
 | 
						RLCBSN_t makeReservationInt(RLCBlockReservation::type restype, RLCBSN_t afterBSN,
 | 
				
			||||||
 | 
							TBF *tbf, RadData *rd, int *prrbp, MsgTransactionType mttype);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						RLCBSN_t makeCCCHReservation(GSM::CCCHLogicalChannel *AGCH,
 | 
				
			||||||
 | 
							RLCBlockReservation::type type, TBF *tbf, RadData *rd, bool forDRX, MsgTransactionType mttype);
 | 
				
			||||||
 | 
						RLCBSN_t makeRRBPReservation(TBF *tbf, int *prrbp, MsgTransactionType ttype);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get the reservation for the specified block timeslot.
 | 
				
			||||||
 | 
						// Return true if found, and return TFI in *TFI.
 | 
				
			||||||
 | 
						// bsn can be in the past or future.
 | 
				
			||||||
 | 
						RLCBlockReservation *getReservation(RLCBSN_t bsn);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						void clearReservation(RLCBSN_t bsn, TBF *tbf);
 | 
				
			||||||
 | 
						RLCBlockReservation::type recvReservation(RLCBSN_t bsn, TBF**restbf, RadData *prd,PDCHL1FEC *ch);
 | 
				
			||||||
 | 
						void dumpReservations(std::ostream&os);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern bool setMACFields(MACDownlinkHeader *block, PDCHL1FEC *pdch, TBF *tbf, int makeres,MsgTransactionType mttype,unsigned *pcounter);
 | 
				
			||||||
 | 
					extern int configGetNumQ(const char *name, int defaultvalue);
 | 
				
			||||||
 | 
					extern int configGprsMultislotMaxUplink();
 | 
				
			||||||
 | 
					extern int configGprsMultislotMaxDownlink();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The USF is assigned in each downlink block to indicate if the uplink
 | 
				
			||||||
 | 
					// block in the same frame position is available for uplink data,
 | 
				
			||||||
 | 
					// or reserved for some other purpose.
 | 
				
			||||||
 | 
					// There are only 7 USFs available, so we have to share.
 | 
				
			||||||
 | 
					// TODO: MOVE TO UPLINK RESERVATION:
 | 
				
			||||||
 | 
					//class L1USFTable
 | 
				
			||||||
 | 
					//{
 | 
				
			||||||
 | 
					//	TBF *mUSFTable[8];
 | 
				
			||||||
 | 
					//	public:
 | 
				
			||||||
 | 
					//	void setUSF(RLCBSN_t bsn, TBF* tbf) { mUSFTable[bsn % mUSFTableSize] = tbf; }
 | 
				
			||||||
 | 
					//	TBF *getTBFByUSF(RLCBSN_t bsn) { return mUSFTable[bsn % mUSFTableSize]; }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//	L1USFTable() { for (int i = mUSFTableSize-1; i>= 0; i--) { mUSFTable[i] = 0; } }
 | 
				
			||||||
 | 
					//};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};	// namespace GPRS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										1127
									
								
								GPRS/MSInfo.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1127
									
								
								GPRS/MSInfo.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										601
									
								
								GPRS/MSInfo.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										601
									
								
								GPRS/MSInfo.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,601 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#ifndef MSINFO_H
 | 
				
			||||||
 | 
					#define MSINFO_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <Interthread.h>
 | 
				
			||||||
 | 
					//#include <list>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "GPRSInternal.h"
 | 
				
			||||||
 | 
					#include "GPRSRLC.h"
 | 
				
			||||||
 | 
					//#include "RLCHdr.h"
 | 
				
			||||||
 | 
					#include "RList.h"
 | 
				
			||||||
 | 
					//#include "BSSG.h"
 | 
				
			||||||
 | 
					#include "Utils.h"
 | 
				
			||||||
 | 
					#include "SgsnExport.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CASENAME(x) case x: return #x;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
					struct RadData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef RList<PDCHL1Downlink*> PDCHL1DownlinkList_t;
 | 
				
			||||||
 | 
					typedef RList<PDCHL1Uplink*> PDCHL1UplinkList_t;
 | 
				
			||||||
 | 
					typedef RList<TBF*> TBFList_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A way to describe a collection of tbf states.
 | 
				
			||||||
 | 
					// See TBF.h isActive() and isTransmitting().
 | 
				
			||||||
 | 
					enum TbfMacroState {
 | 
				
			||||||
 | 
						TbfMAny,		// any state
 | 
				
			||||||
 | 
						TbfMActive,		// any active state
 | 
				
			||||||
 | 
						TbfMTransmitting	// any transmitting state.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This should be in TBF.h but classes TBF and MSInfo are circularly referential.
 | 
				
			||||||
 | 
					enum TbfCancelMode {
 | 
				
			||||||
 | 
						TbfRetryInapplicable,	// Tbf retry is inapplicable to an uplink tbf.
 | 
				
			||||||
 | 
						TbfNoRetry,				// Kill the tbf forever
 | 
				
			||||||
 | 
						TbfRetryAfterRelease,	// Retry tbf after sending a TbfRelease message.
 | 
				
			||||||
 | 
												// If the tbf release message fails, fall back to RetryAfterTimeout.
 | 
				
			||||||
 | 
						TbfRetryAfterWait		// Retry tbf after timeout.
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum MultislotSymmetry {
 | 
				
			||||||
 | 
						MultislotSymmetric,	// Use only symmetric multislot assignment, eg 2-down/2-up.
 | 
				
			||||||
 | 
						MultislotFull		// Use full multislot assignment, which may be assymetric.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM03.64 sec 6.2
 | 
				
			||||||
 | 
					// Note: The RROperatingMode is a state of the MS and known only at the layer2 MAC level.
 | 
				
			||||||
 | 
					// The GSM spec says essentially that the RROperatingMode is simply whether
 | 
				
			||||||
 | 
					// the MS thinks there is an active TBF running, which is not particularly useful to us.
 | 
				
			||||||
 | 
					// What we need to track is whether the MS is listening to
 | 
				
			||||||
 | 
					// CCCH (PacketIdle mode) or PDCH (PacketTransfer mode.)  Generally when all TBFs are
 | 
				
			||||||
 | 
					// finished the MS goes back to PacketIdle mode, but after a downlink transaction
 | 
				
			||||||
 | 
					// we keep it camped on PDCH for a time determined by the T3192 timer, whose value
 | 
				
			||||||
 | 
					// we broadcast in the System Information messages.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Note: For T3192 timeout determination, dont forget to add the time delay
 | 
				
			||||||
 | 
					// of the outgoing message queue, but that should be short.
 | 
				
			||||||
 | 
					// But messages enqueued on CCCH may have long delays.
 | 
				
			||||||
 | 
					// 
 | 
				
			||||||
 | 
					// This mode has almost nothing to do with Mobility Management Status, which is up in Layer3.
 | 
				
			||||||
 | 
					// When the docs talk about "GPRS attached" they usually mean Mobility Mangement State, not this.
 | 
				
			||||||
 | 
					// An MS in GMM state "Ready" (meaning the MS and SGSN have negotiated an SGSN supplied
 | 
				
			||||||
 | 
					// TLLI for the MS instead of using the random TLLI the MS uses for its first uplink message)
 | 
				
			||||||
 | 
					// will usually be in PacketIdle mode unless there is an ongoing TBF transaction.
 | 
				
			||||||
 | 
					// For a non dual-transfer-mode MS, the MS must relinquish (or suspend) its GMM "Ready" state
 | 
				
			||||||
 | 
					// to make a voice call, in which case it would no longer be in PacketIdle mode,
 | 
				
			||||||
 | 
					// but that has almost no meaning at the MAC level.  If the voice call ends and the MS wants
 | 
				
			||||||
 | 
					// to use GPRS again, it will send us another RACH and we dont need to know that
 | 
				
			||||||
 | 
					// a voice call was made in the meanwhile.
 | 
				
			||||||
 | 
					class RROperatingMode {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						enum type {
 | 
				
			||||||
 | 
							PacketIdle,
 | 
				
			||||||
 | 
							PacketTransfer,
 | 
				
			||||||
 | 
							//DualTransfer,
 | 
				
			||||||
 | 
							// This mode was unnecessary:
 | 
				
			||||||
 | 
							//Camped		// This is our own mode, not an official RROperatingMode.
 | 
				
			||||||
 | 
									// It marks the time MS is camped on Packet Channel between PacketTransfer
 | 
				
			||||||
 | 
									// and PacketIdle, when the T3192 timer is running.
 | 
				
			||||||
 | 
									// If a new transfer does not commence before T3192 expires,
 | 
				
			||||||
 | 
									// MS is in PacketIdle mode.
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						static const char *name(RROperatingMode::type mode);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const RROperatingMode::type &mode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// When we lose contact with the MS or something bad happens, we stop talking to it.
 | 
				
			||||||
 | 
					// This says why.  Reserve the first 100 numbers so the MsgTransactionType can be used
 | 
				
			||||||
 | 
					// as a stop cause when the corresponding MsgTransactionType counter expires.
 | 
				
			||||||
 | 
					class MSStopCause {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						enum type {
 | 
				
			||||||
 | 
							AssignCounter = 1,
 | 
				
			||||||
 | 
							// These were the values in release 3.0:
 | 
				
			||||||
 | 
							//ShutDown = 2,
 | 
				
			||||||
 | 
							//Stuck = 3,
 | 
				
			||||||
 | 
							//Reassign = 4,	// Reassignment failed.
 | 
				
			||||||
 | 
							ShutDown = 102,
 | 
				
			||||||
 | 
							Stuck = 103,
 | 
				
			||||||
 | 
							//Reassign = 104,	// Reassignment failed.
 | 
				
			||||||
 | 
							Rach = 105,		// Running TBF killed by a RACH.
 | 
				
			||||||
 | 
							ReleaseCounter = 106,
 | 
				
			||||||
 | 
							ReassignCounter = 107,
 | 
				
			||||||
 | 
							NonResponsive = 108,	// MS does not talk to us any more.
 | 
				
			||||||
 | 
							Goof = 109,
 | 
				
			||||||
 | 
							N3101 = 3101,
 | 
				
			||||||
 | 
							N3103 = 3103,
 | 
				
			||||||
 | 
							N3105 = 3105,
 | 
				
			||||||
 | 
							T3191 = 3191,
 | 
				
			||||||
 | 
							T3168 = 3168,
 | 
				
			||||||
 | 
							CauseUnknown = 9999 	// Used for unrecoverable internal inconsistency.
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						//int mValue;
 | 
				
			||||||
 | 
						//MSStopCause(/*enum MsgTransactionType*/ int wMsgTransType) : mValue(wMsgTransType) {}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SignalQuality {
 | 
				
			||||||
 | 
						// TODO: Get the Channel Quality Report from packet downlink ack/nack GSM04.60 11.2.6
 | 
				
			||||||
 | 
						Statistic<float> msTimingError;
 | 
				
			||||||
 | 
						Statistic<int> msRSSI;		// Dont bother saving RSSI as a float
 | 
				
			||||||
 | 
						Statistic<int> msChannelCoding;
 | 
				
			||||||
 | 
						Statistic<int> msCValue;
 | 
				
			||||||
 | 
						Statistic<int> msILevel;
 | 
				
			||||||
 | 
						Statistic<int> msRXQual;
 | 
				
			||||||
 | 
						Statistic<int> msSigVar;
 | 
				
			||||||
 | 
						void setRadData(RadData &rd);
 | 
				
			||||||
 | 
						void setRadData(float wRSSI,float wTimingError);
 | 
				
			||||||
 | 
						void dumpSignalQuality(std::ostream &os) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct StatTotalHits {
 | 
				
			||||||
 | 
						// Use ints instead of unsigned in case some statistic is buggy and runs backwards.
 | 
				
			||||||
 | 
						Int_z mTotal;
 | 
				
			||||||
 | 
						Int_z mGood;
 | 
				
			||||||
 | 
						void clear() { mGood = mTotal = 0; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// keep the history of some success rate over the last few seconds for reporting purposes.
 | 
				
			||||||
 | 
					class StatHits {
 | 
				
			||||||
 | 
						// Keep totals for each of the last NumHist 48-block-multiframes, which is approx one second.
 | 
				
			||||||
 | 
						public: static const int cNumHist = 20;
 | 
				
			||||||
 | 
						private:
 | 
				
			||||||
 | 
						StatTotalHits mTotal; 				// Totals for all time.
 | 
				
			||||||
 | 
						StatTotalHits mHistory[cNumHist];	// Recent history.
 | 
				
			||||||
 | 
						UInt_z mWhen[cNumHist];	// When the historical data in this history bucket was collected.
 | 
				
			||||||
 | 
						// Return index in arrays above, which is the current 48-block-multiframe
 | 
				
			||||||
 | 
						unsigned histind();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						void addTotal() {
 | 
				
			||||||
 | 
							mTotal.mTotal++;
 | 
				
			||||||
 | 
							mHistory[histind()].mTotal++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void addGood() {	// increment only good count, not total.
 | 
				
			||||||
 | 
							mTotal.mGood++;
 | 
				
			||||||
 | 
							mHistory[histind()].mGood++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void addMiss() { addTotal(); }
 | 
				
			||||||
 | 
						void addHit() {		// increment good and total.
 | 
				
			||||||
 | 
							unsigned i = histind();
 | 
				
			||||||
 | 
							mTotal.mTotal++; mTotal.mGood++;
 | 
				
			||||||
 | 
							mHistory[i].mTotal++; mHistory[i].mGood++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void getStats(float *pER, int *pTotal, float *pWorstER, int *pWorstTotal);
 | 
				
			||||||
 | 
						void textRecent(std::ostream &os); 	// Print the average for the last N seconds and worst second.
 | 
				
			||||||
 | 
						void textTotal(std::ostream&os);	// Print totals.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// More MS statistics.  Separate from MSInfo just because MSInfo is so large.
 | 
				
			||||||
 | 
					struct MSStat {
 | 
				
			||||||
 | 
						// This is a measure of the instantaneous traffic, used to pick the least busy channel.
 | 
				
			||||||
 | 
						// It is incremented every time a block is sent/received, and decayed on a regular time schedule.
 | 
				
			||||||
 | 
						UInt_z msTrafficMetric;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						StatHits msCountCcchReservations;
 | 
				
			||||||
 | 
						StatHits msCountRbbpReservations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						StatHits msCountBlocks;	// Counts both uplink and downlink.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UInt_z msConnectTime;
 | 
				
			||||||
 | 
						void msAddConnectTime(unsigned msecs) { msConnectTime += msecs; }
 | 
				
			||||||
 | 
						UInt_z msCountTbfs, msCountTbfFail, msCountTbfNoConnect;
 | 
				
			||||||
 | 
						UInt_z msBytesUp, msBytesDown;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//UInt_z msCountCcchReservations;
 | 
				
			||||||
 | 
						//UInt_z msCountCcchReservationReplies;
 | 
				
			||||||
 | 
						//UInt_z msCountRbbpReservations;
 | 
				
			||||||
 | 
						//UInt_z msCountRbbpReservationReplies;
 | 
				
			||||||
 | 
						//void service() {
 | 
				
			||||||
 | 
						//	// There are approx 48 blocks per second.
 | 
				
			||||||
 | 
						//	if (gBSNNext % 48 != 0) { return; }
 | 
				
			||||||
 | 
						//	unsigned ind = (gBSNNext / 48) % numHist;
 | 
				
			||||||
 | 
						//	msHistoryCcchReservations.sethits(int,msCountCcchReservations,msCountCcchReplies);
 | 
				
			||||||
 | 
						//	msHistoryRbbpReservations.sethits(int,msCountRbbpReservations,msCountRbbpReplies);
 | 
				
			||||||
 | 
						//	fivesecavg.countCcchReservations = msCountCcchReservations.
 | 
				
			||||||
 | 
						//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// We use talkUp/talkDown to determine when the MS is non-responsive:
 | 
				
			||||||
 | 
						GprsTimer msTalkUpTime;		// When the MS last talked to us.
 | 
				
			||||||
 | 
						GprsTimer msTalkDownTime;	// When we last talked to the MS.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Called at every uplink/downlink communication from MS.
 | 
				
			||||||
 | 
						// These are not strictly statistics because they are also used to kill a non-responsive MS.
 | 
				
			||||||
 | 
						// These timers differ from the persistent mode timers in that those
 | 
				
			||||||
 | 
						// count only data, and these count anything.
 | 
				
			||||||
 | 
						void talkedUp(bool doubleCount=false) { msTalkUpTime.setNow(); if (!doubleCount) {msTrafficMetric++;} }
 | 
				
			||||||
 | 
						void talkedDown() { msTalkDownTime.setNow(); msTrafficMetric++; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Dump all except traffic metric.
 | 
				
			||||||
 | 
						void msStatDump(const char *indent,std::ostream &os);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MS Info a.k.a. Radio Context.  There is one of these for each TLLI (not per-MS, per-TLLI)
 | 
				
			||||||
 | 
					// GSM04.08 4.7.1.4 talks about GPRS attach, P-TMSI, and TLLI.
 | 
				
			||||||
 | 
					// An MS, for our purposes in L2, is defined by its TLLI.  No TLLI, no MSInfo struct.
 | 
				
			||||||
 | 
					// The TLLI identifies the MS in all transactions except the initial RACH.
 | 
				
			||||||
 | 
					// The RACH creates an anonymous packet uplink assignment for the MS, still identified
 | 
				
			||||||
 | 
					// only by the RACH time, to transmit one block, which will be a Packet Resource Request
 | 
				
			||||||
 | 
					// containing the all important TLLI.  However, knowing the TLLI does not identify
 | 
				
			||||||
 | 
					// a unique MS; the MS may (and usually does) use several of them.
 | 
				
			||||||
 | 
					// Note that the MS can pick a "random" TLLI for itself when it does its first
 | 
				
			||||||
 | 
					// GPRS-attach, but the SGSN issues a new "local" TLLI based on P-TMSI on AttachAccept.
 | 
				
			||||||
 | 
					// The TLLI is specific to the sgsn, so if you switched sgsns, you would have a new TLLI,
 | 
				
			||||||
 | 
					// however, the MS remembers its old TLLIs and will try calling in with them,
 | 
				
			||||||
 | 
					// converted to foreign TLLIs.
 | 
				
			||||||
 | 
					// 
 | 
				
			||||||
 | 
					// The spec is entirely botched up about whether the critical information needed
 | 
				
			||||||
 | 
					// to communicate with the MS is in L3 (the SGSN) or L2 (the BTS.)
 | 
				
			||||||
 | 
					// The SGSN stores the MS capabilities (RACap and DRX mode), which is ok but unnecessary,
 | 
				
			||||||
 | 
					// since the MS retransmits them in every RAUpdate.
 | 
				
			||||||
 | 
					// (The SGSN copies would be forwarded to the new cell during a cell change though.)
 | 
				
			||||||
 | 
					// The problem is that we need to remember the radio parameters for the MS
 | 
				
			||||||
 | 
					// (RSSI and TimingError), and a whole bunch of ad-hoc timers running in the MS
 | 
				
			||||||
 | 
					// here in L2, where we identify MS using TLLI,
 | 
				
			||||||
 | 
					// but the mapping of TLLI to MS (as known by IMSI) is in L3.  Whoops!
 | 
				
			||||||
 | 
					// Also note that during a TLLI reassignment procedure using BSSG, the SGSN commands
 | 
				
			||||||
 | 
					// the BTS to switch TLLIs *after* it has received the Attach Complete Message,
 | 
				
			||||||
 | 
					// which has already arrived [possibly but not always] using the new TLLI,
 | 
				
			||||||
 | 
					// so here at L2 the MSInfo for the new TLLI already exists.
 | 
				
			||||||
 | 
					// We are supposed to combine the two RadioContexts (MSInfos) when we get
 | 
				
			||||||
 | 
					// the TLLI reassignment, and then recognize either TLLI.
 | 
				
			||||||
 | 
					// The RSSI and TimingError are updated separately per-TLLI (ie, per-MSInfo struct)
 | 
				
			||||||
 | 
					// because the MS initiates each conversation.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The entire communication system between the MS and the SGSN can best
 | 
				
			||||||
 | 
					// be described in terms of two different state-universes, corresponding
 | 
				
			||||||
 | 
					// to the GPRS-Registration state: Registered (GPRS-Attached) or not.
 | 
				
			||||||
 | 
					// In the pre-gprs-attach state the MS may call in with several TLLIs,
 | 
				
			||||||
 | 
					// and we dont know how to correlate them to an actual MS.
 | 
				
			||||||
 | 
					// In this case it is quite easy to lose communication with the MS, because
 | 
				
			||||||
 | 
					// when an incoming RACH+PacketResourceRequest is answered, a new PACCH
 | 
				
			||||||
 | 
					// may be assigned at random, and it may conflict with a previous assignment
 | 
				
			||||||
 | 
					// that is on-going or in-flight on AGCH.
 | 
				
			||||||
 | 
					// Also in this state I think there is simply no way to know for sure the state of the
 | 
				
			||||||
 | 
					// per-MS timers, which are needed to know how to send the Immediate Assignment.
 | 
				
			||||||
 | 
					// TODO: Maybe we should use a single PACCH timeslot for all unregistered TLLIs,
 | 
				
			||||||
 | 
					// which implies communicationg the registration state from the SGSN to L2.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// In the Registered-state we know that the new and old TLLI are the same physical MS,
 | 
				
			||||||
 | 
					// and communication is more secure.  We can assign a new PACCH for the MS.
 | 
				
			||||||
 | 
					// By Registered we mean that both the SGSN and the MS agree on the
 | 
				
			||||||
 | 
					// Registration state and the P-TMSI, which agreement is handshaked in both
 | 
				
			||||||
 | 
					// directions (3 messages) and consumated by the AttachComplete message sent by MS to SGSN.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TLLI_LOCAL_BIT 0x40000000
 | 
				
			||||||
 | 
					#define TLLI_MASK_LOCAL(tlli) ((tlli) & ~ TLLI_LOCAL_BIT)
 | 
				
			||||||
 | 
					static __inline__ uint32_t tlliEq(uint32_t tlli1, uint32_t tlli2) {
 | 
				
			||||||
 | 
						// Temporarily provide a way to disable this in case it does not work:
 | 
				
			||||||
 | 
						if (gConfig.getBool("GPRS.LocalTLLI.Enable")) {
 | 
				
			||||||
 | 
							return TLLI_MASK_LOCAL(tlli1) == TLLI_MASK_LOCAL(tlli2);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return tlli1 == tlli2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// vvv OLD COMMENT:
 | 
				
			||||||
 | 
					// Formerly I changed the TLLI in the MSInfo struct to an oldTLLI on the command of the SGSN,
 | 
				
			||||||
 | 
					// but that is incorrect - if the MS later sends another RACH using the oldTLLI,
 | 
				
			||||||
 | 
					// we need to respond with that oldTLLI, not the newTLLI, although possibly we
 | 
				
			||||||
 | 
					// should just ignore it in that case.
 | 
				
			||||||
 | 
					// The RACH creates an anonymous packet uplink assignment for the MS, still identified
 | 
				
			||||||
 | 
					// only by the RACH time, to transmit one block, which will be a Packet Resource Request
 | 
				
			||||||
 | 
					// with a TLLI.  If that TLLI maps to an old TLLI, it is because we have already
 | 
				
			||||||
 | 
					// succeeded with the Attach Complete, and therefore it is safe to use the new TLLI.
 | 
				
			||||||
 | 
					// Therefore, an MSInfo structure has one and only one TLLI, and the sole purpose of
 | 
				
			||||||
 | 
					// TLLI is as a layer-2 transport identifier for the MS.
 | 
				
			||||||
 | 
					// ^^^ OLD COMMENT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The MSInfo struct needs to hang around as long as the MS is in packet-transfer mode,
 | 
				
			||||||
 | 
					// which means as long as it has TBFs, or the MS is in the T3192 period when it is camped
 | 
				
			||||||
 | 
					// on the PACCH channel instead of the CCCH channel.
 | 
				
			||||||
 | 
					// If we lose a connection with an MS, we keep the dead TBF around too until we
 | 
				
			||||||
 | 
					// are sure it is no longer in use (config option, default 5 seconds),
 | 
				
			||||||
 | 
					// so we dont need the MSInfo to survive after that.  Doesn't hurt to keep it around, either.
 | 
				
			||||||
 | 
					// An unused MSInfo eventually decays and is destroyed; this delay must be longer
 | 
				
			||||||
 | 
					// than the expected use of the MSInfo by the SGSN - at least several seconds.
 | 
				
			||||||
 | 
					// The SGSN is what remembers GPRS-attached MS, and will send us both a TLLI and the
 | 
				
			||||||
 | 
					// MS capabilities (ie, multislot) in any transactions so that we can recreate the MSInfo at need.
 | 
				
			||||||
 | 
					class MSInfo : public SGSN::MSUEAdapter, public SignalQuality, public MSStat
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						unsigned msDebugId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Use of TLLI is in GSM04.08 4.7.1.5: P-TMSI handling.
 | 
				
			||||||
 | 
						// From GSM03.03 sec 2.6: Structure of TLLI:
 | 
				
			||||||
 | 
						// local TLLI is built by MS that has a valid P-TMSI:
 | 
				
			||||||
 | 
						//		top 2 bits 11, lower 30 bits are low 30 bits of P-TMSI.
 | 
				
			||||||
 | 
						// foreign TLLI is built by an MS that has a valid P-TMSI from elswhere:
 | 
				
			||||||
 | 
						//		top 2 bits 10, lower 30 bits from P-TMSI;
 | 
				
			||||||
 | 
						// random TLLI is built by an MS with no P-TMSI:
 | 
				
			||||||
 | 
						//		top top 5 bits 01111, lower 27 bits random.
 | 
				
			||||||
 | 
						// auxiliary TLLI is built by SGSN:
 | 
				
			||||||
 | 
						//		top 5 bits 01110, lower 27 bits at SGSN discretion.
 | 
				
			||||||
 | 
						// GSM03.03 sec 2.4: Structure of TMSI 
 | 
				
			||||||
 | 
						// 	The 32-bit TMSI has only local significance (within VLR or SGSN) and
 | 
				
			||||||
 | 
						// 	is created at manufacturer discretion, however, for SGSN top
 | 
				
			||||||
 | 
						//	2 bits must be 11, and it may not be all 1s, which value is reserved
 | 
				
			||||||
 | 
						//	to mean invalid.  We also reserve value 0 to mean unset.
 | 
				
			||||||
 | 
						//	TMSI is stored in hex notation in 4 octets, and always ciphered.
 | 
				
			||||||
 | 
						// GPRS-L2 doesn't care about any of the above, the SGSN knows those things.
 | 
				
			||||||
 | 
						// In GPRS-L2 we just use whatever TLLI we are told.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// An MSInfo structure is created as a result of an MS communication with a TLLI.
 | 
				
			||||||
 | 
						// No TLLI, no MSInfo structure.  These things can time out and die whenever
 | 
				
			||||||
 | 
						// they want - their lifetime only needs to be as long as the RSSI and TimingError is valid.
 | 
				
			||||||
 | 
						// They will be recreated if the MS RAChes us again.
 | 
				
			||||||
 | 
						// In the spec they can also be created by a page from the SGSN, but not implemented here.
 | 
				
			||||||
 | 
						// The MSInfo struct corresponds to an SgsnInfo in the SGSN, but because
 | 
				
			||||||
 | 
						// either can be deleted any time we dont keep pointers between them, we always
 | 
				
			||||||
 | 
						// look them up by TLLI for communication to/from the SGSN.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// This is as per the spec:
 | 
				
			||||||
 | 
						// The msTlli is the TLLI we (the L2 layer) always use to communicate with the MS.
 | 
				
			||||||
 | 
						// It is initialzied from the TLLI the MS used to communicate with us.
 | 
				
			||||||
 | 
						// It is only changed if we get a 'change tlli' command from the SGSN,
 | 
				
			||||||
 | 
						// which happens only after a successfully completed attach procedure,
 | 
				
			||||||
 | 
						// (which is a fully acknowledged procedure with 3 way handshake)
 | 
				
			||||||
 | 
						// at which time msTlli becomes msOldTlli, and we must subsequently recognize
 | 
				
			||||||
 | 
						// either msTlli (the new sgsn-assigned one) or msOldTlli (the original one)
 | 
				
			||||||
 | 
						// for uplink communication, but use only msTlli for downlink communication.
 | 
				
			||||||
 | 
						// 
 | 
				
			||||||
 | 
						// This is not as per the spec:
 | 
				
			||||||
 | 
						// After the attach is successful, we use only the assigned TLLI,
 | 
				
			||||||
 | 
						// only one MSInfo structure, and everything is copascetic.
 | 
				
			||||||
 | 
						// However, prior to a successful attach, I have seen the MS just use
 | 
				
			||||||
 | 
						// several different TLLIs in uplink messages one right after the other,
 | 
				
			||||||
 | 
						// maybe trying to find one that already works?
 | 
				
			||||||
 | 
						// So many of these MSInfo refer to the same phone.
 | 
				
			||||||
 | 
						// The spec deals with this by letting these things time out and die,
 | 
				
			||||||
 | 
						// however, this results in conflicting in-flight assignments on AGCH
 | 
				
			||||||
 | 
						// for different TLLIs that, in fact, refer to the same phone.
 | 
				
			||||||
 | 
						// The spec does not provide any way for the L2 layer (us) to find that out.
 | 
				
			||||||
 | 
						// So I introduced the AltTlli, which points to another MSInfo struct that
 | 
				
			||||||
 | 
						// refers the same phone.  It MUST NOT be used for communication with the MS,
 | 
				
			||||||
 | 
						// however, it can be used to avoid launching conflicting assignments.
 | 
				
			||||||
 | 
						// The Sgsn sends us AltTlli as the AliasTlli in the DownlinkQPdu.
 | 
				
			||||||
 | 
						UInt32_z msTlli;		// Identifies the MS, and is the TLLI used for downlink communication.
 | 
				
			||||||
 | 
						UInt32_z msOldTlli;		// Also identifies the MS, and must be recognized in uplink communication.
 | 
				
			||||||
 | 
						UInt32_z msAltTlli;		// Used to 'point' to another MSInfo struct that refers to the same MS,
 | 
				
			||||||
 | 
												// as reported to us by the SGSN, which knows these things.
 | 
				
			||||||
 | 
												// We save the TLLI instead of using a pointer because the other MSInfo
 | 
				
			||||||
 | 
												// could disappear at any time.
 | 
				
			||||||
 | 
						Bool_z msDeprecated;	// This MS has been replaced by some other, which is another way
 | 
				
			||||||
 | 
												// of saying that the active MS's oldTlli points to this one.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If the MS is in packet-idle state, there should be no TBFs.
 | 
				
			||||||
 | 
						TBFList_t msTBFs;	// TBFs for this MS, both uplink and downlink.
 | 
				
			||||||
 | 
					#define RN_MS_FOR_ALL_TBF(ms,tbf) for (RListIterator<TBF*> itr(ms->msTBFs); itr.next(tbf); ) 
 | 
				
			||||||
 | 
						Int_z msUSFs[8];		// USF in each timeslot.
 | 
				
			||||||
 | 
						// These are used by the RLCEngine to know when the MS has been granted a USF, ie, a chance to respond.
 | 
				
			||||||
 | 
						Int_z msNumDataUSFGrants;	// Total number of USF grants; reset when last TBF detached.
 | 
				
			||||||
 | 
						Int_z msAckNackUSFGrant;	// The msNumDataUSFGrants value of the last acknack.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Note: The uplink/downlink channels must all be in the same ARFCN that the MS is
 | 
				
			||||||
 | 
						// camped on, and for multislot, follow strict timeslot adjacency rules.
 | 
				
			||||||
 | 
						// The spec says that it is possible for different simultaneous TBFs for the same MS
 | 
				
			||||||
 | 
						// to use different channel assignments, for exmaple, if the MS is sending a low-data-rate
 | 
				
			||||||
 | 
						// TBF1 on a single channel and then wants to send a high-data-rate TBF2, it will interrupt
 | 
				
			||||||
 | 
						// TBF2, may request a multislot allocation, and do TBF2 first.
 | 
				
			||||||
 | 
						// Or another example, if TBF2 comes in and the TBF1 channel is on is congested, 
 | 
				
			||||||
 | 
						// the MAC can pick a different channel for TBF2.
 | 
				
			||||||
 | 
						// However, we are not going to support that.  The channels will be assigned to the MS
 | 
				
			||||||
 | 
						// permanently, and all TBFs will use the same ones, which is why these lists are
 | 
				
			||||||
 | 
						// here instead of in the TBF, where they really belong.
 | 
				
			||||||
 | 
						// We may, however, someday change the channel assignments dynamically based on the
 | 
				
			||||||
 | 
						// relative utilization of up and down links, for example, change from 4 down 1 up
 | 
				
			||||||
 | 
						// to 4 up 1 down, etc., but if we do that we will have to wait until there are
 | 
				
			||||||
 | 
						// no active TBFs, or reconfigure the existing TBFs.  Having all the channels
 | 
				
			||||||
 | 
						// here in the MSInfo instead of scattered in different TBFs will make
 | 
				
			||||||
 | 
						// such reconfiguration easier.
 | 
				
			||||||
 | 
						PDCHL1UplinkList_t msPCHUps;	// uplink channels assigned to the MS; usually just one.
 | 
				
			||||||
 | 
						PDCHL1DownlinkList_t msPCHDowns;	// downlink channels assigned to the MS; usually just one.
 | 
				
			||||||
 | 
						bool msCanUseUplinkTn(unsigned tn);
 | 
				
			||||||
 | 
						bool msCanUseDownlinkTn(unsigned tn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This is the channel the MS is listening to for messages.
 | 
				
			||||||
 | 
						// It is set before the msPCH assignments above.
 | 
				
			||||||
 | 
						// How did the MS come to be listening to this channel, you wonder?
 | 
				
			||||||
 | 
						// When a RACH comes in to the BTS, we do not know what MS it belongs to, so we pick
 | 
				
			||||||
 | 
						// the least busy GPRS channel and tell the MS to send its request on that channel.
 | 
				
			||||||
 | 
						// From then on the MS listens to that channel until we tell it differently
 | 
				
			||||||
 | 
						// in a channel assignment, which should be the next thing we send to it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO: Is the below correct?  Doesnt the MS always need to monitor PAACH which must
 | 
				
			||||||
 | 
						// be one of its assigned channels?
 | 
				
			||||||
 | 
						// It is possible for the msPCH assignments to not include the msPacch in several cases:
 | 
				
			||||||
 | 
						// 1. If we deliberately give the MS a different channel assignment
 | 
				
			||||||
 | 
						// for an uplink/downlink transfer, maybe because GPRS is underutilized and
 | 
				
			||||||
 | 
						// we decided to close the channel.  (Channel closing not implemented in first draft.)
 | 
				
			||||||
 | 
						// 2. Maybe we decide on a different set of channels to satisfy this particular
 | 
				
			||||||
 | 
						// MSs multislot requirements.
 | 
				
			||||||
 | 
						// 3. If a previously attached MS starts a new RACH request, which will assign
 | 
				
			||||||
 | 
						// a new PCH at random (because we dont know what MS it is yet) and for some
 | 
				
			||||||
 | 
						// reason we havent cleared the msPCH channels, maybe because our timeouts are out of phase.
 | 
				
			||||||
 | 
						// In this weird case a BSSG downlink command might try to talk to the MS on
 | 
				
			||||||
 | 
						// the old channels, which might even possibly work if the new assignment and the
 | 
				
			||||||
 | 
						// old happen to be the same.  The special cases are complicated.
 | 
				
			||||||
 | 
						// Note that if we used one-phase uplink access, this case 2 would extend in time out
 | 
				
			||||||
 | 
						// into the uplink transfer, but we wont do that.
 | 
				
			||||||
 | 
						PDCHL1FEC *msPacch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//RROperatingMode::type msMode; // Our belief about the state of MS: packet-idle, packet-transfer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UInt_z msIdleCounter;		// Counts how long MS is without TBFs; eventually we delete it.
 | 
				
			||||||
 | 
						UInt_z msStalled;			// If MS is blocked, this is why, for error reporting.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//Bool_z msUplinkRequest;		// Request from phone to establish uplink was delayed due to existing uplink tbf.
 | 
				
			||||||
 | 
						//RLCMsgChannelRequestDescriptionIE msUplinkRequestCRD;  // The CRD for delayed request above.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GSM04.18 11.1.2:
 | 
				
			||||||
 | 
						// T3141 - Started at Immediate Assignment, stopped when MS starts TBF.
 | 
				
			||||||
 | 
						// We dont need it because we poll instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Note: Using Z100Timer is overkill for our single-threaded application;
 | 
				
			||||||
 | 
						// could just use RLCBlockTime counters instead.
 | 
				
			||||||
 | 
						// Counters and Timers defined in GSM04.60 sec 13.
 | 
				
			||||||
 | 
						// We dont calculate N3105 and N3101 exactly the way the spec says,
 | 
				
			||||||
 | 
						// but doesnt matter if they are off by 1 or 2.
 | 
				
			||||||
 | 
						// NOTE: The blackberry sometimes waits 3 block periods before it starts
 | 
				
			||||||
 | 
						// answering USFs, so N3101 better be bigger than that.
 | 
				
			||||||
 | 
						UInt_z msN3101;			// Number of unacknowledged USF grants (off by one.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GSM04.60 sec 13:
 | 
				
			||||||
 | 
						// Note: The MS may take advantage of this time period by keeping the TBF open
 | 
				
			||||||
 | 
						// after a PDU finishes, and not sending anything for a long time, then
 | 
				
			||||||
 | 
						// sending sending additional PDUs in the same TBF later, but before the timer expires.
 | 
				
			||||||
 | 
						GSM::Z100Timer msT3191;		// Waiting for acknowledgement of final TBF data block.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GSM04.60 sec 13:
 | 
				
			||||||
 | 
						GSM::Z100Timer msT3193;		// After downlink TBF finished, MS camps on PDCH this long.
 | 
				
			||||||
 | 
													// MS runs same timer but called T3192.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GSM04.60 sec 13:
 | 
				
			||||||
 | 
						GSM::Z100Timer msT3168;		// MS camped on PDCH waiting for uplink assignment.
 | 
				
			||||||
 | 
													// This timer is defined to be in the MS, not the BTS,
 | 
				
			||||||
 | 
													// and we do not really need to track it as long as we
 | 
				
			||||||
 | 
													// are sure we send the downlink assignment message
 | 
				
			||||||
 | 
													// before this timer expires.  The timer value is in
 | 
				
			||||||
 | 
													// the sql and broadcast in the beacon.
 | 
				
			||||||
 | 
													// However, I am using the timer as a way of tracking
 | 
				
			||||||
 | 
													// whether the assignment is for a RACH, rather
 | 
				
			||||||
 | 
													// than setting some other variable.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GSM04.60 sec 13:
 | 
				
			||||||
 | 
						//GSM::Z100Timer msT3169;		// Final timeout for dead tbf.
 | 
				
			||||||
 | 
						//GSM::Z100Timer msT3195;		// Final timeout for dead tbf.
 | 
				
			||||||
 | 
						//GSM::Z100Timer msTxxxx;		// Combined T3169, T3191, T3195 - timeout for
 | 
				
			||||||
 | 
									// resource release after abnormal condition, during which time USF and TFI may not be reused.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// When this MS was last granted a USF.
 | 
				
			||||||
 | 
						// We use this when multiple MS are in contention for the uplink to make it fair.
 | 
				
			||||||
 | 
						RLCBSN_t msLastUsfGrant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Called when a USF is granted for this MS.
 | 
				
			||||||
 | 
						// If penalize, if the MS does not answer we kill of the tbf.
 | 
				
			||||||
 | 
						void msCountUSFGrant(bool penalize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Incoming downlink data queue.
 | 
				
			||||||
 | 
						// This queue is not between separate threads for BSSG,
 | 
				
			||||||
 | 
						// and it is no longer for the internal sgsn either.
 | 
				
			||||||
 | 
						//InterthreadQueue<BSSG::BSSGMsgDLUnitData> msDownlinkQueue;
 | 
				
			||||||
 | 
						InterthreadQueue2<SGSN::GprsSgsnDownlinkPdu,SingleLinkList<> > msDownlinkQueue;
 | 
				
			||||||
 | 
						Statistic<unsigned> msDownlinkQStat;
 | 
				
			||||||
 | 
						Statistic<double> msDownlinkQDelay;
 | 
				
			||||||
 | 
						Timeval msDownlinkQOldest;			// The timeval from the last guy in the queue.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Can this TBF use the specified uplink?
 | 
				
			||||||
 | 
						bool canUseUplink(PDCHL1Uplink*up) {
 | 
				
			||||||
 | 
							return msPCHUps.find(up);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Can this TBF use the specified downlink?
 | 
				
			||||||
 | 
						bool canUseDownlink(PDCHL1Downlink*down) {
 | 
				
			||||||
 | 
							return msPCHDowns.find(down);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Return the downlink channels as a bitmask for PacketDownlinkAssignment msg.
 | 
				
			||||||
 | 
						unsigned char msGetDownlinkTimeslots(MultislotSymmetry sym);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//PDCHL1Downlink *msPrimaryDownlink() { return msPCHDowns.front(); }
 | 
				
			||||||
 | 
						bool msAssignChannels(); 	// Get channel(s) for this MS.
 | 
				
			||||||
 | 
						private:
 | 
				
			||||||
 | 
							bool msAddCh(unsigned chmask, const char *tnlist);
 | 
				
			||||||
 | 
							bool msTrySlots(unsigned chmask,int down,int up);
 | 
				
			||||||
 | 
							bool msAssignChannels2(int maxdown, int maxup, int sum);
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						void msDeassignChannels();	// Release all channels for this MS.
 | 
				
			||||||
 | 
						void msReassignChannels();	// Not implemented specially yet.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//int msLastUplinkMsgBSN;	// When did we last hear from this MS?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						MSInfo(uint32_t tlli);
 | 
				
			||||||
 | 
						// Use msDelete instead of calling ~MSinfo() directly.
 | 
				
			||||||
 | 
						void msDelete(bool forever=0);	// If forever, do not move to expired list, just kill it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void msAddTBF(TBF *tbf) {
 | 
				
			||||||
 | 
							devassert(! msTBFs.find(tbf));
 | 
				
			||||||
 | 
							msTBFs.push_back(tbf);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void msForgetTBF(TBF *tbf) {
 | 
				
			||||||
 | 
							devassert(msTBFs.find(tbf));
 | 
				
			||||||
 | 
							msTBFs.remove(tbf);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Called when a TBF goes dead.  If it was the last active uplink TBF, surrender our USFs.
 | 
				
			||||||
 | 
						void msCleanUSFs();
 | 
				
			||||||
 | 
						void msFailUSFs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned msGetDownlinkQueuedBytes();
 | 
				
			||||||
 | 
						//TBF * msGetDownlinkActiveTBF();
 | 
				
			||||||
 | 
						private:
 | 
				
			||||||
 | 
						int msCountTBF1(RLCDirType dir, enum TbfMacroState tbfstate, TBF**ptbf=0) const;
 | 
				
			||||||
 | 
						int msCountTBF2(RLCDirType dir, enum TbfMacroState tbfstate, TBF**ptbf=0);
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						int msCountActiveTBF(RLCDirType dir, TBF**ptbf=0);
 | 
				
			||||||
 | 
						int msCountTransmittingTBF(RLCDirType dir, TBF**ptbf=0);
 | 
				
			||||||
 | 
						void msService();
 | 
				
			||||||
 | 
						void msStop(RLCDir::type dir, MSStopCause::type cause, TbfCancelMode cmode, int unsigned howlong);
 | 
				
			||||||
 | 
						MSStopCause::type msStopCause;
 | 
				
			||||||
 | 
						//void msRestart();
 | 
				
			||||||
 | 
						ChannelCodingType msGetChannelCoding(RLCDirType wdir) const;
 | 
				
			||||||
 | 
						int msGetTA() const { return GetTimingAdvance(msTimingError.getCurrent()); }
 | 
				
			||||||
 | 
						// All MS use the same power params at the moment.
 | 
				
			||||||
 | 
						int msGetAlpha() const { return GetPowerAlpha(); }
 | 
				
			||||||
 | 
						int msGetGamma() const { return GetPowerGamma(); }
 | 
				
			||||||
 | 
						void msDump(std::ostream&os, SGSN::PrintOptions options);
 | 
				
			||||||
 | 
						void msDumpCommon(std::ostream&os) const;
 | 
				
			||||||
 | 
						void msDumpChannels(std::ostream&os) const;
 | 
				
			||||||
 | 
						RROperatingMode::type getRROperatingMode();
 | 
				
			||||||
 | 
						string id() const;
 | 
				
			||||||
 | 
						void msAliasTlli(uint32_t newTlli);
 | 
				
			||||||
 | 
						void msChangeTlli(uint32_t newTlli);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//void msSetUplinkRequest(RLCMsgChannelRequestDescriptionIE &wCRD) {
 | 
				
			||||||
 | 
						//	msUplinkRequest = true;
 | 
				
			||||||
 | 
						//	msUplinkRequestCRD = wCRD;
 | 
				
			||||||
 | 
						//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// These are the functions required by the MSUEAdapter:
 | 
				
			||||||
 | 
						uint32_t msGetHandle() { return msTlli; }
 | 
				
			||||||
 | 
						string msid() const { return id(); }
 | 
				
			||||||
 | 
						//void msWriteHighSide(ByteVector &dlpdu, uint32_t tlli, const char *descr) {
 | 
				
			||||||
 | 
							//msDownlinkQueue.write(new GprsSgsnDownlinkPdu(dlpdu,tlli,descr));
 | 
				
			||||||
 | 
						//}
 | 
				
			||||||
 | 
						void msDeactivateRabs(unsigned rabMask) {}	// no-op in GPRS.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool msIsSuspended();				// Is the MS in suspended mode?
 | 
				
			||||||
 | 
						bool msIsRegistered();				// Is the MS GPRS registered?
 | 
				
			||||||
 | 
						bool isExtendedDynamic() { return msPCHUps.size() > msPCHDowns.size(); }
 | 
				
			||||||
 | 
						bool msCanUseExtendedUplink();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					extern unsigned gMSDebugId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const MSInfo*ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MSInfo *bssgMSChangeTLLI(unsigned oldTLLI,unsigned newTLLI);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};	// namespace
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										63
									
								
								GPRS/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								GPRS/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright 2008 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This software is distributed under the terms of the GNU Public License.
 | 
				
			||||||
 | 
					# See the COPYING file in the main directory for details.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					# it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					# the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					# (at your option) any later version.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					# GNU General Public License for more details.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					include $(top_srcdir)/Makefile.common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#AM_CXXFLAGS = -O0 -g
 | 
				
			||||||
 | 
					CXXFLAGS = -g -O0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					noinst_LTLIBRARIES = libGPRS.la
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libGPRS_la_SOURCES = \
 | 
				
			||||||
 | 
						MSInfo.cpp \
 | 
				
			||||||
 | 
						RLCEngine.cpp \
 | 
				
			||||||
 | 
						TBF.cpp \
 | 
				
			||||||
 | 
						MAC.cpp \
 | 
				
			||||||
 | 
						FEC.cpp \
 | 
				
			||||||
 | 
						RLCEngine.cpp \
 | 
				
			||||||
 | 
						RLCMessages.cpp \
 | 
				
			||||||
 | 
						ByteVector.cpp \
 | 
				
			||||||
 | 
						GPRSCLI.cpp \
 | 
				
			||||||
 | 
						RLC.cpp \
 | 
				
			||||||
 | 
						MsgBase.cpp
 | 
				
			||||||
 | 
					#BSSGMessages.cpp
 | 
				
			||||||
 | 
					#BSSG.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					noinst_HEADERS = \
 | 
				
			||||||
 | 
						ByteVector.h \
 | 
				
			||||||
 | 
						FEC.h \
 | 
				
			||||||
 | 
						GPRSExport.h \
 | 
				
			||||||
 | 
						GPRSInternal.h \
 | 
				
			||||||
 | 
						GPRSTDMA.h \
 | 
				
			||||||
 | 
						MAC.h \
 | 
				
			||||||
 | 
						MsgBase.h \
 | 
				
			||||||
 | 
						GPRSRLC.h \
 | 
				
			||||||
 | 
						RLCEngine.h \
 | 
				
			||||||
 | 
						RLCHdr.h \
 | 
				
			||||||
 | 
						RLCMessages.h \
 | 
				
			||||||
 | 
						RList.h \
 | 
				
			||||||
 | 
						ScalarTypes.h \
 | 
				
			||||||
 | 
						TBF.h \
 | 
				
			||||||
 | 
						MSInfo.h
 | 
				
			||||||
 | 
					#	BSSG.h
 | 
				
			||||||
 | 
					#	BSSGMessages.h
 | 
				
			||||||
							
								
								
									
										108
									
								
								GPRS/MsgBase.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								GPRS/MsgBase.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "MsgBase.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MsgCommonWrite::_define_vtable() {}
 | 
				
			||||||
 | 
					void MsgCommonLength::_define_vtable() {}
 | 
				
			||||||
 | 
					void MsgCommonText::_define_vtable() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Copied from same functions in L3Frame:
 | 
				
			||||||
 | 
					static const unsigned fillPattern[8] = {0,0,1,0,1,0,1,1};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MsgCommonWrite::writeField(const ItemWithValueAndWidth&item,const char*)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mResult.writeField(wp,item.getValue(),item.getWidth());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MsgCommonWrite::writeField(uint64_t value, unsigned len, const char *, Type2Str)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mResult.writeField(wp,value,len);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MsgCommonWrite::writeOptFieldLH(uint64_t value, unsigned len, int present, const char *)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (present) { writeH(); writeField(value,len); } else { writeL(); }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// pat added: write an Optional Field controlled by an initial 0/1 field.
 | 
				
			||||||
 | 
					void MsgCommonWrite::writeOptField01(uint64_t value, unsigned len, int present, const char*)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (present) { write1(); writeField(value,len); } else { write0(); }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MsgCommonWrite::writeH()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned fillBit = fillPattern[wp%8];	// wp is in MsgCommon
 | 
				
			||||||
 | 
						writeField(!fillBit,1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MsgCommonWrite::writeL()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned fillBit = fillPattern[wp%8];	// wp is in MsgCommon
 | 
				
			||||||
 | 
						writeField(fillBit,1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void MsgCommonWrite::writeBitMap(bool*bitmap,unsigned bitmaplen, const char*name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (unsigned i=0; i<bitmaplen; i++) {
 | 
				
			||||||
 | 
							writeField(bitmap[i],1,name);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					static void truncateredundant(char *str, int len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char *end = str + len - 1, *cp = end;
 | 
				
			||||||
 | 
						// Chop off trailing chars that are replicated.
 | 
				
			||||||
 | 
						int lastch = *cp;
 | 
				
			||||||
 | 
						for (; cp > str; cp--) {
 | 
				
			||||||
 | 
							if (*cp != lastch) {
 | 
				
			||||||
 | 
								if (cp < end-6) { strcpy(cp+2,"..."); }
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TOHEX(v) ((v) + ((v) < 10 ? '0' : ('a'-10)))
 | 
				
			||||||
 | 
					void MsgCommonText::writeBitMap(bool*bitmap,unsigned bitmaplen, const char*name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char txtbits[bitmaplen+6], *tp = txtbits;
 | 
				
			||||||
 | 
						unsigned i, accum = 0;
 | 
				
			||||||
 | 
						for (i=0; i<bitmaplen; i++) {
 | 
				
			||||||
 | 
							accum = (accum<<1) + (bitmap[i] ? 1 : 0);
 | 
				
			||||||
 | 
							if (((i+1) & 3) == 0) {
 | 
				
			||||||
 | 
								*tp++ = TOHEX(accum);
 | 
				
			||||||
 | 
								accum = 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						//if (i & 3) { *tp++ = TOHEX(accum); }  Our bitmap is always evenly % 4, so dont bother.
 | 
				
			||||||
 | 
						*tp = 0;
 | 
				
			||||||
 | 
						//truncateredundant(txtbits,bitmaplen);
 | 
				
			||||||
 | 
						mos << " " << name << "=(" << txtbits << ")";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This could fail multi-threaded, but it is only used for debug output.
 | 
				
			||||||
 | 
					const char *tohex(int val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static char buf[20];
 | 
				
			||||||
 | 
						sprintf(buf,"0x%x",val);
 | 
				
			||||||
 | 
						return buf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										216
									
								
								GPRS/MsgBase.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								GPRS/MsgBase.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,216 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#ifndef MSGBASE_H
 | 
				
			||||||
 | 
					#define MSGBASE_H 1
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <stdlib.h>		// For size_t
 | 
				
			||||||
 | 
					#include "Defines.h"
 | 
				
			||||||
 | 
					#include "BitVector.h"
 | 
				
			||||||
 | 
					#include "ScalarTypes.h"
 | 
				
			||||||
 | 
					typedef const char *Type2Str(int);
 | 
				
			||||||
 | 
					const char *tohex(int);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is a package to help write downlink messages and message elements.
 | 
				
			||||||
 | 
					// (Note: A message element class is a helper class that writes part of a message,
 | 
				
			||||||
 | 
					// but is not a final class.)
 | 
				
			||||||
 | 
					// Also see Field<> and Field_z<> types.
 | 
				
			||||||
 | 
					// You write a single function writeCommon(), and this package uses it to instantiate the
 | 
				
			||||||
 | 
					// functions from MsgBase in your message class, namely:
 | 
				
			||||||
 | 
					//		void writeBody(BitVector &dest, size_t &wp)
 | 
				
			||||||
 | 
					//		void writeBody(BitVector &dest)
 | 
				
			||||||
 | 
					//		int length()
 | 
				
			||||||
 | 
					//		void text(std::ostream&os)
 | 
				
			||||||
 | 
					// To use:
 | 
				
			||||||
 | 
					// Step 1: For both message classes and message element classes:
 | 
				
			||||||
 | 
					// 		- Create a single function named: writeCommon(MsgCommon& dest)
 | 
				
			||||||
 | 
					//		which writes out the message using the functions defined in MsgCommon,
 | 
				
			||||||
 | 
					// 		or the macros: WRITE_ITEM, WRITE_FIELD, etc.
 | 
				
			||||||
 | 
					//		The primary output function is writeField()
 | 
				
			||||||
 | 
					// Step 2: Your message classes only (not message element classes)
 | 
				
			||||||
 | 
					//		need to define the functions above.  You can do this by inheriting from MsgBody,
 | 
				
			||||||
 | 
					//		or just writing these yourself.
 | 
				
			||||||
 | 
					//		For examples see class MsgBody or class RLCDownlinkMessage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MsgCommon
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						size_t wp;
 | 
				
			||||||
 | 
						MsgCommon() : wp(0) {}
 | 
				
			||||||
 | 
						MsgCommon(size_t wwp) : wp(wwp) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ignore this.  g++ emits the vtable and typeid in the translation unit where
 | 
				
			||||||
 | 
						// the first virtual method (meeting certain qualifications) is defined, so we use this
 | 
				
			||||||
 | 
						// dummy method to control that and put the vtables in MsgBase.cpp.
 | 
				
			||||||
 | 
						// Do not change the order of these functions here or you will get inscrutable link errors.
 | 
				
			||||||
 | 
						// What a foo bar language.
 | 
				
			||||||
 | 
						virtual void _define_vtable() {}		// Must be the first function.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The primary function to write bitfields.
 | 
				
			||||||
 | 
					    virtual void writeField(
 | 
				
			||||||
 | 
							uint64_t value, 		// The value to be output to the BitVector by write().
 | 
				
			||||||
 | 
							unsigned len, 			// length in bits of value.
 | 
				
			||||||
 | 
							const char *name=0, 	// name to be output by text() function; if not supplied,
 | 
				
			||||||
 | 
													// then this var does not appear in the text()
 | 
				
			||||||
 | 
							Type2Str cvt=0) = 0;	// optional function to translate value to a string in text().
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This is used primarily to write variables of type Field or Field_z,
 | 
				
			||||||
 | 
						// for which the width is defined in the type declaration.
 | 
				
			||||||
 | 
					    virtual void writeField(const ItemWithValueAndWidth&item, const char *name = 0) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Same as above, but an optional field whose presence is controlled by present.
 | 
				
			||||||
 | 
					    virtual void writeOptFieldLH(	// For fields whose presence is indicated by H, absence by L.
 | 
				
			||||||
 | 
					        uint64_t value, unsigned len, int present, const char*name = 0) = 0;
 | 
				
			||||||
 | 
						// An alternative idiom to writeOptField01() is:
 | 
				
			||||||
 | 
						// 		if (dst.write01(present)) writeField(value,len,name);
 | 
				
			||||||
 | 
					    virtual void writeOptField01(	// For fields whose presence is indicated by 1, absence by 0.
 | 
				
			||||||
 | 
					        uint64_t value, unsigned len, int present, const char*name = 0) = 0;
 | 
				
			||||||
 | 
						virtual void writeH() {}
 | 
				
			||||||
 | 
					    virtual void writeL() {}
 | 
				
			||||||
 | 
					    virtual void write0() {}
 | 
				
			||||||
 | 
					    virtual void write1() {}
 | 
				
			||||||
 | 
						virtual bool write01(bool present) {return present;}
 | 
				
			||||||
 | 
						virtual void writeBitMap(bool*value,unsigned bitmaplen, const char*name) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// getStream() returns the ostream for the text() function, or NULL for
 | 
				
			||||||
 | 
						// length() or write() functions.  You can use it to make your function
 | 
				
			||||||
 | 
						// do something special for text().
 | 
				
			||||||
 | 
						virtual std::ostream* getStream() { return NULL; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define WRITE_ITEM(name) writeField(name,#name)
 | 
				
			||||||
 | 
					#define WRITE_OPT_ITEM01(name,opt) writeOptField01(name,name.getWidth(),opt,#name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define WRITE_FIELD(name,width) writeField(name,width,#name)
 | 
				
			||||||
 | 
					#define WRITE_OPT_FIELD01(name,width,opt) writeOptField01(name,width,opt,#name)
 | 
				
			||||||
 | 
					#define WRITE_OPT_FIELDLH(name,width,opt) writeOptFieldLH(name,width,opt,#name)
 | 
				
			||||||
 | 
					class MsgCommonWrite : public MsgCommon {
 | 
				
			||||||
 | 
						BitVector& mResult;
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						void _define_vtable();
 | 
				
			||||||
 | 
						MsgCommonWrite(BitVector& wResult) : mResult(wResult)  {}
 | 
				
			||||||
 | 
						MsgCommonWrite(BitVector& wResult, size_t &wp) : MsgCommon(wp), mResult(wResult)  {}
 | 
				
			||||||
 | 
						void writeField(uint64_t value, unsigned len, const char * name=0, Type2Str =0);
 | 
				
			||||||
 | 
					    void writeField(const ItemWithValueAndWidth&item, const char *name = 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void write0() { writeField(0,1); }
 | 
				
			||||||
 | 
						void write1() { writeField(1,1); }
 | 
				
			||||||
 | 
						bool write01(bool present) { writeField(present,1); return present; }
 | 
				
			||||||
 | 
						void writeH();
 | 
				
			||||||
 | 
						void writeL();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Write an Optional Field controlled by an initial L/H field.
 | 
				
			||||||
 | 
						void writeOptFieldLH(uint64_t value, unsigned len, int present, const char * name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Write an Optional Field controlled by an initial 0/1 field.
 | 
				
			||||||
 | 
						void writeOptField01(uint64_t value, unsigned len, int present, const char * name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Write a bitmap.
 | 
				
			||||||
 | 
						void writeBitMap(bool*value,unsigned bitmaplen, const char*name);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MsgCommonLength : public MsgCommon {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						void _define_vtable();
 | 
				
			||||||
 | 
					    void writeH() { wp++; }
 | 
				
			||||||
 | 
					    void writeL() { wp++; }
 | 
				
			||||||
 | 
					    void write0() { wp++; }
 | 
				
			||||||
 | 
					    void write1() { wp++; }
 | 
				
			||||||
 | 
						bool write01(bool present) { wp++; return present;}
 | 
				
			||||||
 | 
					    void writeField(uint64_t, unsigned len, const char* =0, Type2Str =0) { wp+=len; }
 | 
				
			||||||
 | 
					    //virtual void writeField(const ItemWithValueAndWidth&item, const char *name= 0) { wp = item.getWidth(); }
 | 
				
			||||||
 | 
					    virtual void writeField(const ItemWithValueAndWidth&item, const char *) { wp = item.getWidth(); }
 | 
				
			||||||
 | 
					    void writeOptFieldLH(uint64_t, unsigned len, int present, const char*) {
 | 
				
			||||||
 | 
							wp++; if (present) wp += len;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					    void writeOptField01(uint64_t, unsigned len, int present, const char*) {
 | 
				
			||||||
 | 
							wp++; if (present) wp += len;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void writeBitMap(bool*,unsigned bitmaplen, const char*) { wp+=bitmaplen; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MsgCommonText : public MsgCommon {
 | 
				
			||||||
 | 
					    std::ostream& mos;
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
						void _define_vtable();
 | 
				
			||||||
 | 
					    MsgCommonText(std::ostream &os) : mos(os) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void writeField(const ItemWithValueAndWidth&item, const char *name = 0) {
 | 
				
			||||||
 | 
					        if (name) { mos << " " << name << "=(" << item.getValue() << ")"; }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					    void writeField(uint64_t value, unsigned, const char*name=0, Type2Str cvt=0) {
 | 
				
			||||||
 | 
					        if (name) {
 | 
				
			||||||
 | 
								mos << " " << name << "=";
 | 
				
			||||||
 | 
								if (cvt) { mos << cvt(value); } else { mos << value; }
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void writeOptFieldLH(uint64_t value, unsigned, int present, const char*name = 0) {
 | 
				
			||||||
 | 
					        if (name && present) { mos << " " << name << "=(" << value << ")"; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void writeOptField01(uint64_t value, unsigned, int present, const char*name = 0) {
 | 
				
			||||||
 | 
					        if (name && present) { mos << " " << name << "=(" << value << ")"; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
						void writeBitMap(bool*value,unsigned bitmaplen, const char*name);
 | 
				
			||||||
 | 
						std::ostream* getStream() { return &mos; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is the base class for the message, that defines the functions that use MsgCommon.
 | 
				
			||||||
 | 
					/***
 | 
				
			||||||
 | 
					class MsgBody {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						virtual void writeCommon(MsgCommon &dest) const = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeOptional01(MsgCommon &dest, bool control) const {
 | 
				
			||||||
 | 
							if (control) {
 | 
				
			||||||
 | 
								dest.write1();
 | 
				
			||||||
 | 
								writeCommon(dest);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								dest.write0();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeBody(BitVector &vdst, size_t &wwp) const {
 | 
				
			||||||
 | 
							MsgCommonWrite dest(vdst,wwp);
 | 
				
			||||||
 | 
							writeCommon(dest);
 | 
				
			||||||
 | 
							wwp = dest.wp;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void writeBody(BitVector &vdst) const {
 | 
				
			||||||
 | 
							MsgCommonWrite dest(vdst);
 | 
				
			||||||
 | 
							writeCommon(dest);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						int lengthBodyBits() const {
 | 
				
			||||||
 | 
							MsgCommonLength dest;
 | 
				
			||||||
 | 
							writeCommon(dest);
 | 
				
			||||||
 | 
							return dest.wp;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void textBody(std::ostream&os) const {
 | 
				
			||||||
 | 
							MsgCommonText dest(os);
 | 
				
			||||||
 | 
							writeCommon(dest);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					***/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***
 | 
				
			||||||
 | 
					#define INHERIT_MSG_BASE \
 | 
				
			||||||
 | 
						void write(BitVector &dest, size_t &wp) { MsgBase::write(dest,wp); } \
 | 
				
			||||||
 | 
						void write(BitVector &dest) { MsgBase::write(dest); } \
 | 
				
			||||||
 | 
						int length() { return MsgBase::length(); } \
 | 
				
			||||||
 | 
						void text(std::ostream&os) const { MsgBase::text(os); }
 | 
				
			||||||
 | 
					***/
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										83
									
								
								GPRS/RLC.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								GPRS/RLC.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "GPRSRLC.h"
 | 
				
			||||||
 | 
					#include "GSMCommon.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int deltaBSN(int bsn1,int bsn2)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static const int halfModulus = RLCBSN_t::BSNPeriodicity/2;
 | 
				
			||||||
 | 
						int delta = bsn1 - bsn2;
 | 
				
			||||||
 | 
						if (delta>=halfModulus) delta -= RLCBSN_t::BSNPeriodicity;
 | 
				
			||||||
 | 
						else if (delta<-halfModulus) delta += RLCBSN_t::BSNPeriodicity;
 | 
				
			||||||
 | 
						return delta;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Based on GSM::FNDelta
 | 
				
			||||||
 | 
					// We assume the values are within a half periodicity of each other.
 | 
				
			||||||
 | 
					int RLCBSN_t::BSNdelta(RLCBSN_t v2)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						RLCBSN_t v1 = *this;
 | 
				
			||||||
 | 
						//int delta = v1.mValue - v2.mValue;
 | 
				
			||||||
 | 
						//if (delta>=halfModulus) delta -= BSNPeriodicity;
 | 
				
			||||||
 | 
						//else if (delta<-halfModulus) delta += BSNPeriodicity;
 | 
				
			||||||
 | 
						//return RLCBSN_t(delta);
 | 
				
			||||||
 | 
						return RLCBSN_t(deltaBSN(v1.mValue,v2.mValue));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return 1 if v1 > v2; return -1 if v1 < v2, using modulo BSNPeriodicity.
 | 
				
			||||||
 | 
					int RLCBSN_t::BSNcompare(RLCBSN_t v2)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int delta = BSNdelta(v2);
 | 
				
			||||||
 | 
						if (delta>0) return 1;
 | 
				
			||||||
 | 
						if (delta<0) return -1;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (pat) Return the block radio number for a frame number.
 | 
				
			||||||
 | 
					RLCBSN_t FrameNumber2BSN(int fn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// The RLC blocks use a 52-multiframe, but each 13-multiframe is identical:
 | 
				
			||||||
 | 
						// the first 12 frames are 3 RLC blocks, and the last frame is for timing or idle.
 | 
				
			||||||
 | 
						int mfn = (fn / 13);			// how many 13-multiframes
 | 
				
			||||||
 | 
						int rem = (fn - (mfn*13));	// how many blocks within the last multiframe.
 | 
				
			||||||
 | 
						RLCBSN_t result = mfn * 3 + ((rem==12) ? 2 : (rem/4));
 | 
				
			||||||
 | 
						result.normalize();
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return the Block Sequence Number for a frame number.
 | 
				
			||||||
 | 
					// There are 12 radio blocks per 52 frames,
 | 
				
			||||||
 | 
					int BSN2FrameNumber(RLCBSN_t absn)	// absolute block sequence number.
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// One extra frame is inserted after every 3 radio blocks,
 | 
				
			||||||
 | 
						// so 3 radio blocks take 13 frames.
 | 
				
			||||||
 | 
						int bsn = absn;	// Convert to int so we do math on int, not RLCBSN_t
 | 
				
			||||||
 | 
						int result = ((int)bsn / 3) * 13 + ((int)bsn % 3) * 4;
 | 
				
			||||||
 | 
						assert(result >= 0 && (unsigned) result <= GSM::gHyperframe);
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const RLCDir::type &dir)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << RLCDir::name(dir);
 | 
				
			||||||
 | 
						return os;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										1330
									
								
								GPRS/RLCEngine.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1330
									
								
								GPRS/RLCEngine.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										260
									
								
								GPRS/RLCEngine.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								GPRS/RLCEngine.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,260 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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 GPRS RLC-MAC state machine, GSM 04.60. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**@file GPRS RLC-MAC state machine, GSM 04.60. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GPRSL2RLCENGINE_H
 | 
				
			||||||
 | 
					#define GPRSL2RLCENGINE_H
 | 
				
			||||||
 | 
					#define INTERNAL_SGSN 1		// Use internal SGSN, no support for BSSG
 | 
				
			||||||
 | 
					#define UPLINK_PERSIST 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <Interthread.h>
 | 
				
			||||||
 | 
					#include <list>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "GPRSInternal.h"
 | 
				
			||||||
 | 
					#if INTERNAL_SGSN==0
 | 
				
			||||||
 | 
					#include "BSSGMessages.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#include "TBF.h"
 | 
				
			||||||
 | 
					#define FAST_TBF 1			// Use aggregated downlink TBFs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PDTCHL1FEC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Maximum LLC PDU size is 1560 bytes. (GSM04.60 sec9.1.12)  Bytes over are discarded in RLC.
 | 
				
			||||||
 | 
					// In unacknowledged mode, LLC-PDUs delivered in the order received, with 0-bits for missing blocks.
 | 
				
			||||||
 | 
					// The minimum payload size (using CS-1) is 20 bytes (see RLCPayloadSize)
 | 
				
			||||||
 | 
					// Therefore, a single PDU may take 78 blocks.
 | 
				
			||||||
 | 
					const int RLC_PDU_MAX_LEN = 1560;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//typedef std::list<BitVector*> LLCSegmentList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RLCEngineBase
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// WS => number of blocks sent simultaneously, awaiting ack, before blocking.
 | 
				
			||||||
 | 
						// SNS => must be double WS, by definition.
 | 
				
			||||||
 | 
						static const unsigned mWS = 64;		// Window Size
 | 
				
			||||||
 | 
						static const unsigned mSNS = 128;		// Sequence Number Space
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**@name SN arithmetic */
 | 
				
			||||||
 | 
						int deltaSN(unsigned sn1, unsigned sn2) const;
 | 
				
			||||||
 | 
						int deltaSNS(unsigned sn1, unsigned sn2) const;
 | 
				
			||||||
 | 
						bool deltaEQ(unsigned sn1, unsigned sn2) const;
 | 
				
			||||||
 | 
						unsigned addSN(int sn1, int sn2) const;	// Allow negative numbers.
 | 
				
			||||||
 | 
						void incSN(unsigned &psn);
 | 
				
			||||||
 | 
						virtual void engineDump(std::ostream &os) const = 0;
 | 
				
			||||||
 | 
						string engineDumpStr() const { std::ostringstream os; engineDump(os); return os.str(); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum RlcUpState {
 | 
				
			||||||
 | 
						RlcUpTransmit,
 | 
				
			||||||
 | 
						RlcUpQuiescent,
 | 
				
			||||||
 | 
						RlcUpPersistFinal,
 | 
				
			||||||
 | 
						RlcUpFinished,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (pat) This is an RLC endpoint as per GSM 04.60 9.1
 | 
				
			||||||
 | 
					// It sits in layer 2.
 | 
				
			||||||
 | 
					class RLCUpEngine : public TBF, public RLCEngineBase
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
						/**@name RLC state variables, from GSM 04.60 9. */
 | 
				
			||||||
 | 
						// We depend on the MS not to send us blocks with BSN > VQ+mWS, and inform
 | 
				
			||||||
 | 
						// the MS of our concept of VQ in the acknack we send.
 | 
				
			||||||
 | 
						// However, the only way we know the acknack is received may be that the MS
 | 
				
			||||||
 | 
						// sends more blocks.
 | 
				
			||||||
 | 
						struct {
 | 
				
			||||||
 | 
							unsigned VR;		///< highest BSN received + 1 modulo wSNS;
 | 
				
			||||||
 | 
												// 0 <= VR <= wSNS-1;
 | 
				
			||||||
 | 
							unsigned VQ;		///< lowest BSN not yet received (window base)
 | 
				
			||||||
 | 
												// In acknowledged mode, receive window is
 | 
				
			||||||
 | 
												// defined by: VQ <= BSN <= VQ + mWS
 | 
				
			||||||
 | 
							bool VN[mSNS];		///< receive status of previous RLC data blocks
 | 
				
			||||||
 | 
							RLCUplinkDataBlock *RxQ[mSNS];	///< assembly queue for inbound RLC data blocks
 | 
				
			||||||
 | 
							//unsigned RBSN;				///< BSN of incoming blocks.
 | 
				
			||||||
 | 
						} mSt;
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if INTERNAL_SGSN
 | 
				
			||||||
 | 
						ByteVector *mUpPDU;	// The PDU being assembled.
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						BSSG::BSSGMsgULUnitData *mUpPDU;	// The PDU being assembled.
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						Bool_z mIncompletePDU;	// Special case: If set, the PDU did not finish in this TBF;
 | 
				
			||||||
 | 
												// MS needs another TBF to send the rest of it.
 | 
				
			||||||
 | 
												// Dont think we need this if using unlimited dynamic mode uplink.
 | 
				
			||||||
 | 
						Bool_z mUpStalled;			// MS is stalled, copied from each uplink block header.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static const unsigned mNumUpPerAckNack = 10;
 | 
				
			||||||
 | 
						UInt_z mNumUpBlocksSinceAckNack;		// Number of blocks sent since the last acknack
 | 
				
			||||||
 | 
						UInt_z mTotalBlocksReceived;
 | 
				
			||||||
 | 
						UInt_z mUniqueBlocksReceived;
 | 
				
			||||||
 | 
						int mBytesPending;
 | 
				
			||||||
 | 
					#if UPLINK_PERSIST
 | 
				
			||||||
 | 
						RLCBSN_t mDataPersistFinalEndBSN;	// When DataPersistFinal mode started.
 | 
				
			||||||
 | 
						bool mUpPersistentMode;
 | 
				
			||||||
 | 
						GprsTimer mtUpKeepAliveTimer;	// Time to next keep alive.
 | 
				
			||||||
 | 
						GprsTimer mtUpPersistTimer; 	// How long TBF persists while idle.
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						// The MS keeps a running total of USF slots granted to the MS.
 | 
				
			||||||
 | 
						// The difference between the current value and the starting value divided
 | 
				
			||||||
 | 
						// by the number of unique blocks received is a measure of the loss ratio,
 | 
				
			||||||
 | 
						// although that breaks down if there are multiple simultaneous uplink TBFs.
 | 
				
			||||||
 | 
						unsigned mStartUsfGrants;
 | 
				
			||||||
 | 
						//unsigned mNumServiceSlotsSkipped;	// Reality check disaster recovery.
 | 
				
			||||||
 | 
						RlcUpState mtUpState;			// Set after all blocks have been received.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						RLCUpEngine(MSInfo *wms,int wOctetCount);
 | 
				
			||||||
 | 
						~RLCUpEngine();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void addUpPDU(BitVector&);
 | 
				
			||||||
 | 
						void sendPDU();
 | 
				
			||||||
 | 
						bool stalled() const { return mUpStalled; }
 | 
				
			||||||
 | 
					#if UPLINK_PERSIST
 | 
				
			||||||
 | 
						bool ulPersistentMode();
 | 
				
			||||||
 | 
						bool sendNonFinalAckNack(PDCHL1Downlink *down);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void engineRecvDataBlock(RLCUplinkDataBlock* block, int tn);
 | 
				
			||||||
 | 
						void engineUpAdvanceWindow();
 | 
				
			||||||
 | 
						bool engineService(PDCHL1Downlink *down);	// Thats right: we pass the downlink.
 | 
				
			||||||
 | 
						float engineDesiredUtilization();
 | 
				
			||||||
 | 
						void engineDump(std::ostream &os) const;
 | 
				
			||||||
 | 
						void engineGetStats(unsigned *pSlotsTotal, unsigned *pSlotsUsed, unsigned *pGrants) const {
 | 
				
			||||||
 | 
							*pSlotsTotal = mTotalBlocksReceived;
 | 
				
			||||||
 | 
							*pSlotsUsed = mUniqueBlocksReceived;
 | 
				
			||||||
 | 
							*pGrants = mtMS->msNumDataUSFGrants - mStartUsfGrants;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						int engineGetBytesPending() { return mBytesPending; }
 | 
				
			||||||
 | 
						RLCMsgPacketUplinkAckNack * engineUpAckNack();
 | 
				
			||||||
 | 
						TBF *getTBF() { return dynamic_cast<TBF*>(this);}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RLCDownEngine : public TBF, public RLCEngineBase
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**@name RLC state variables, from GSM 04.60 9. */
 | 
				
			||||||
 | 
						//@{
 | 
				
			||||||
 | 
						struct {
 | 
				
			||||||
 | 
							unsigned VS;		///< BSN of next RLC data block for tx
 | 
				
			||||||
 | 
											// After receipt of acknack message, VS is set back to VA.
 | 
				
			||||||
 | 
							unsigned VA;		///< BSN of oldest un-acked RLC data block
 | 
				
			||||||
 | 
							bool VB[mSNS];		///< ack status of pending RLC data blocks (true = acked)
 | 
				
			||||||
 | 
							RLCDownlinkDataBlock *TxQ[mSNS];	///< unacked RLC data blocks saved for re-tx
 | 
				
			||||||
 | 
							//int sendTime[mSNS];	///<RLCBSN when block was first sent.
 | 
				
			||||||
 | 
							// VCS is used for multi-block Control Messages, so does not apply to us.
 | 
				
			||||||
 | 
							// bool VCS;			///< 0 or 1 indicating state for multi-block RLC control messages.
 | 
				
			||||||
 | 
							unsigned TxQNum;		// One greater than last block in queue.  It wraps around.
 | 
				
			||||||
 | 
						} mSt;
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
 | 
						// This is additional state:
 | 
				
			||||||
 | 
						UInt_z mPrevAckSsn;		// The SSN of the previous downlinkAckNack.
 | 
				
			||||||
 | 
						UInt_z mResendSsn;		// Resend blocks with SN less than this.
 | 
				
			||||||
 | 
						UInt_z mPrevAckBlockCount;	// Saves value of mTotalBlocksSent from last downlinkAckNack.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Bool_z mDownStalled;	// Yes, set when the downlink is stalled.
 | 
				
			||||||
 | 
						Bool_z mDownFinished;	// Set when we have sent the block with the FBI indicator.
 | 
				
			||||||
 | 
						Bool_z mAllAcked;		// Have all the blocks been acked?
 | 
				
			||||||
 | 
						UInt_z mNumDownBlocksSinceAckNack;
 | 
				
			||||||
 | 
						unsigned mNumDownPerAckNack;
 | 
				
			||||||
 | 
						UInt_z mTotalBlocksSent;		// Total sent; some of them may have been lost in transmission
 | 
				
			||||||
 | 
						UInt_z mTotalDataBlocksSent;	// Number of blocks in the TBF.
 | 
				
			||||||
 | 
						UInt_z mUniqueDataBlocksSent;	// Number of blocks in the TBF.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if INTERNAL_SGSN==0
 | 
				
			||||||
 | 
						// BSSG no longer used:
 | 
				
			||||||
 | 
						BSSG::BSSGMsgDLUnitData  *mBSSGDlMsg;	// The downlink message that created this TBF.
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
													// We save it because the RLCEngine has a pointer into it,
 | 
				
			||||||
 | 
													// and delete it when we delete the TBF.
 | 
				
			||||||
 | 
						ByteVector mDownPDU;	// The PDU part of mBSSGDlMsg.  Automatic destruction.
 | 
				
			||||||
 | 
						SGSN::GprsSgsnDownlinkPdu *mDownlinkPdu;	// This is the most recent sdu sent in the TBF.
 | 
				
			||||||
 | 
													// We keep it around so we can retry the TBF if necessary.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Moved to class TBF:
 | 
				
			||||||
 | 
						GprsTimer mtDownKeepAliveTimer;	// Time to next keep alive.
 | 
				
			||||||
 | 
						GprsTimer mtDownPersistTimer; 	// How long TBF persists while idle.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RLCDownEngine(MSInfo *wms) :
 | 
				
			||||||
 | 
							TBF(wms,RLCDir::Down),
 | 
				
			||||||
 | 
					#if INTERNAL_SGSN==0
 | 
				
			||||||
 | 
							mBSSGDlMsg(0),	// No longer used.
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							mDownlinkPdu(0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							memset(&mSt,0,sizeof(mSt));
 | 
				
			||||||
 | 
							mNumDownPerAckNack = gConfig.getNum("GPRS.TBF.Downlink.Poll1");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						~RLCDownEngine();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Is the downlink engine stalled, waiting for acknack messages?
 | 
				
			||||||
 | 
						bool stalled() const { return mDownStalled; }
 | 
				
			||||||
 | 
						//bool finished() { return mSt.VS >= mSt.TxQNum; }
 | 
				
			||||||
 | 
						unsigned engineDownPDUSize() const { return mDownPDU.size(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RLCDownlinkDataBlock *getBlock(unsigned vs,int tn);
 | 
				
			||||||
 | 
						void engineWriteHighSide(SGSN::GprsSgsnDownlinkPdu *dlmsg);	// TODO: get rid of this.
 | 
				
			||||||
 | 
						bool dlPersistentMode();	// Are we using persistent TBF mode?
 | 
				
			||||||
 | 
						bool dataAvail();		// Is more downlink data available?
 | 
				
			||||||
 | 
						RLCDownlinkDataBlock* engineFillBlock(unsigned bsn,int tn);
 | 
				
			||||||
 | 
						void engineRecvAckNack(const RLCMsgPacketDownlinkAckNack*);
 | 
				
			||||||
 | 
						bool engineService(PDCHL1Downlink *down);
 | 
				
			||||||
 | 
						float engineDesiredUtilization();
 | 
				
			||||||
 | 
						void engineDump(std::ostream &os) const;
 | 
				
			||||||
 | 
						void engineGetStats(unsigned *pSlotsTotal, unsigned *pSlotsUsed, unsigned *pGrants) const {
 | 
				
			||||||
 | 
							// We dont care about the control blocks sent, only the data.
 | 
				
			||||||
 | 
							//*pSlotsTotal = mTotalBlocksSent;	// Number of blocks we have sent.
 | 
				
			||||||
 | 
							*pSlotsTotal = mTotalDataBlocksSent;	// Number of blocks we have sent.
 | 
				
			||||||
 | 
							*pSlotsUsed = mUniqueDataBlocksSent;	// Number of blocks MS has acknowledged.
 | 
				
			||||||
 | 
							*pGrants = 0;	// Inapplicable to downlink.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						int engineGetBytesPending() { return 0; }	// TODO, but it is a negligible amount
 | 
				
			||||||
 | 
						//bool isLastUABlock();
 | 
				
			||||||
 | 
						//unsigned blocksToGo();
 | 
				
			||||||
 | 
						bool resendNeeded(int bsn);
 | 
				
			||||||
 | 
						void advanceVS();
 | 
				
			||||||
 | 
						void advanceVA();
 | 
				
			||||||
 | 
						TBF *getTBF() { return dynamic_cast<TBF*>(this);}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This incorporates both an up and down engine, but only one of them is used.
 | 
				
			||||||
 | 
					//class RLCEngine :
 | 
				
			||||||
 | 
					//	public RLCUpEngine,
 | 
				
			||||||
 | 
					//	public RLCDownEngine
 | 
				
			||||||
 | 
					//{
 | 
				
			||||||
 | 
					//	public:
 | 
				
			||||||
 | 
					//	RLCEngine(TBF *);
 | 
				
			||||||
 | 
					//	~RLCEngine();
 | 
				
			||||||
 | 
					//};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}; // namespace GPRS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										481
									
								
								GPRS/RLCHdr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										481
									
								
								GPRS/RLCHdr.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,481 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef RLCHDR_H
 | 
				
			||||||
 | 
					#define RLCHDR_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include "GPRSRLC.h"
 | 
				
			||||||
 | 
					#include "ScalarTypes.h"
 | 
				
			||||||
 | 
					#include "Defines.h"
 | 
				
			||||||
 | 
					//#include <iostream>
 | 
				
			||||||
 | 
					#include <BitVector.h>
 | 
				
			||||||
 | 
					#include "ByteVector.h"
 | 
				
			||||||
 | 
					#include "MsgBase.h"
 | 
				
			||||||
 | 
					#include "GPRSInternal.h"
 | 
				
			||||||
 | 
					#include "MemoryLeak.h"
 | 
				
			||||||
 | 
					#define CASENAME(x) case x: return #x;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
					extern unsigned RLCPayloadSizeInBytes[4];
 | 
				
			||||||
 | 
					extern unsigned RLCBlockSizeInBits[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MACPayloadType // It is two bits.  GSM04.60sec10.4.7
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						enum type {
 | 
				
			||||||
 | 
							RLCData=0,
 | 
				
			||||||
 | 
							RLCControl=1, 	// The RLC/MAC block does NOT include the optional Octets.
 | 
				
			||||||
 | 
							RLCControlExt=2, 	// The RLC/MAC block DOES include the optional Octets.
 | 
				
			||||||
 | 
														// Used only in downlink direction.
 | 
				
			||||||
 | 
							// Value 3 is reserved.
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						static const char *name(int val) {
 | 
				
			||||||
 | 
							switch ((type)val) {
 | 
				
			||||||
 | 
								CASENAME(RLCData)
 | 
				
			||||||
 | 
								CASENAME(RLCControl)
 | 
				
			||||||
 | 
								CASENAME(RLCControlExt)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return "unrecognized MACPayloadType";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RLC/MAC control message may be sent in an RLC/MAC control block, which is always
 | 
				
			||||||
 | 
					// encoded with CS-1, so length is 176 bits (22 octets).
 | 
				
			||||||
 | 
					// Some MAC messages are also sent on PBCCH, PCCCH or PACCH.
 | 
				
			||||||
 | 
					// Note that no fields in any MAC or RLC header exceed one byte,
 | 
				
			||||||
 | 
					// so there are no network ordering problems, and we can simply use C structs
 | 
				
			||||||
 | 
					// to define the bit packing.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (pat) Data blocks defined in GSM04.60 sec 10.3.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct MACDownlinkHeader 	// 8 bits.  See GSM04.60sec10.3.1
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// From GSM 04.60 10.4:
 | 
				
			||||||
 | 
						// RRBP - expected frame delay for ACK
 | 
				
			||||||
 | 
						// SP - If 1 we expect an ack and RRBP means something.
 | 
				
			||||||
 | 
						// USF - User state flag for shared channels.
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					 	//MACPayloadType::type mPayloadType:2;
 | 
				
			||||||
 | 
						Field_z<2> mPayloadType;
 | 
				
			||||||
 | 
						Field_z<2> mRRBP; 	// RRBP: Relative Reserved Block Period.  See GSM04.60sec10.4.5
 | 
				
			||||||
 | 
											// It specifies 3,4,5 or 6 block delay for ACK/NACK (to give the
 | 
				
			||||||
 | 
											// MS time to decode the block.)
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						Field_z<1> mSP; 		// Supplementary/Polling Bit - indicates whether RRBP is valid.
 | 
				
			||||||
 | 
						Field_z<3> mUSF; 	// For uplink dynamic allocation method.
 | 
				
			||||||
 | 
								// indicates owner of the next uplink radio block in the same timeslot.
 | 
				
			||||||
 | 
								// Except on PCCCH, where a value of 0x111 indicates next uplink radio
 | 
				
			||||||
 | 
								// block reserved for PRACH.
 | 
				
			||||||
 | 
						unsigned lengthBits() { return 8; }	// Size of the MAC header in bits.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeMACHeader(MsgCommon& dest) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void init(MACPayloadType::type wPayloadType) { mPayloadType = wPayloadType; }
 | 
				
			||||||
 | 
						void setRRBP(int rrbp) { mRRBP = rrbp; mSP = 1; }
 | 
				
			||||||
 | 
						bool isControlMsg() { return mPayloadType != MACPayloadType::RLCData; }
 | 
				
			||||||
 | 
						bool isMacUnused() { return mSP == 0 && mUSF == 0; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#if RLCHDR_IMPLEMENTATION
 | 
				
			||||||
 | 
						void MACDownlinkHeader::writeMACHeader(MsgCommon& dest) const {
 | 
				
			||||||
 | 
							//dest.WRITE_ITEM(mPayloadType);
 | 
				
			||||||
 | 
							dest.writeField(mPayloadType,2,"PayLoadType",MACPayloadType::name);
 | 
				
			||||||
 | 
							dest.WRITE_ITEM(mRRBP);
 | 
				
			||||||
 | 
							dest.WRITE_ITEM(mSP);
 | 
				
			||||||
 | 
							dest.WRITE_ITEM(mUSF);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct MACUplinkHeader 	// 8 bits.  See GSM04.60sec10.3.2
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// Note: countdown value and SI are used only for uplink data blocks;
 | 
				
			||||||
 | 
						// for uplink control blocks, CountdownValue and SI are unused, always 0.
 | 
				
			||||||
 | 
						// Octet 1 (and only):
 | 
				
			||||||
 | 
						MACPayloadType::type mPayloadType:2;
 | 
				
			||||||
 | 
						Field<4> mCountDownValue; 	// GSM04.60 9.3.1.
 | 
				
			||||||
 | 
														// 15 until close to the end, then countdown.  Last block 0.
 | 
				
			||||||
 | 
														// Once MS starts countdown, it wont interrupt the
 | 
				
			||||||
 | 
														// transfer for higher priority TBFs.
 | 
				
			||||||
 | 
														// Update: MSs dont do the countdown, they send 15 until
 | 
				
			||||||
 | 
														// the next-to-last block, then send 0.
 | 
				
			||||||
 | 
						Field<1> mSI; // SI: Stall Indicator
 | 
				
			||||||
 | 
						Field<1> mR; 	// Retry bit, sent by MS if it had to try more than once to get this through.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned lengthBits() { return 8; }	// Size of the MAC header in bits.
 | 
				
			||||||
 | 
						int parseMAC(const BitVector&src);
 | 
				
			||||||
 | 
						// Since this is an uplink header, we only need to write it for testing purposes, eg, text().
 | 
				
			||||||
 | 
						void writeMACHeader(MsgCommon&dst) const;
 | 
				
			||||||
 | 
						void text(std::ostream&os) const;
 | 
				
			||||||
 | 
						bool isFinal() { return mCountDownValue == 0; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#if RLCHDR_IMPLEMENTATION
 | 
				
			||||||
 | 
						// It is one byte.
 | 
				
			||||||
 | 
						int MACUplinkHeader::parseMAC(const BitVector&src) {
 | 
				
			||||||
 | 
							size_t rp = 0;
 | 
				
			||||||
 | 
							mPayloadType = (MACPayloadType::type) src.readField(rp,2);
 | 
				
			||||||
 | 
							mCountDownValue = src.readField(rp,4);
 | 
				
			||||||
 | 
							mSI = src.readField(rp,1);
 | 
				
			||||||
 | 
							mR = src.readField(rp,1);
 | 
				
			||||||
 | 
							return 8;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Since this is an uplink header, we only need to write it for testing purposes, eg, text().
 | 
				
			||||||
 | 
						void MACUplinkHeader::writeMACHeader(MsgCommon&dst) const {
 | 
				
			||||||
 | 
							// This is special cased so we get the name of the payload type in the text.
 | 
				
			||||||
 | 
							dst.writeField(mPayloadType,2,"PayLoadType",MACPayloadType::name);
 | 
				
			||||||
 | 
							/***
 | 
				
			||||||
 | 
							std::ostream *os = dst.getStream();	// returned os is non-null only if caller is text().
 | 
				
			||||||
 | 
							if (os) {
 | 
				
			||||||
 | 
								*os << "mPayloadType=(" << MACPayloadType::name(mPayloadType) << ")";
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								dst.writeField(mPayloadType,2);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							***/
 | 
				
			||||||
 | 
							dst.WRITE_ITEM(mCountDownValue);
 | 
				
			||||||
 | 
							dst.WRITE_ITEM(mSI);
 | 
				
			||||||
 | 
							dst.WRITE_ITEM(mR);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void MACUplinkHeader::text(std::ostream&os) const {
 | 
				
			||||||
 | 
							MsgCommonText dst(os);
 | 
				
			||||||
 | 
							writeMACHeader(dst);
 | 
				
			||||||
 | 
							//os << "mPayloadType=(" << MACPayloadType::name(mPayloadType) << ")";
 | 
				
			||||||
 | 
							//os << RN_WRITE_TEXT(mCountDownValue);
 | 
				
			||||||
 | 
							//os << RN_WRITE_TEXT(mSI);
 | 
				
			||||||
 | 
							//os << RN_WRITE_TEXT(mR);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct RadData {
 | 
				
			||||||
 | 
						bool mValid;
 | 
				
			||||||
 | 
						float mRSSI;
 | 
				
			||||||
 | 
						float mTimingError;
 | 
				
			||||||
 | 
						RadData() { mValid = false; }
 | 
				
			||||||
 | 
						RadData(float wRSSI, float wTimingError) : mValid(true),mRSSI(wRSSI),mTimingError(wTimingError) {}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// An incoming block straight from the decoder.
 | 
				
			||||||
 | 
					struct RLCRawBlock {
 | 
				
			||||||
 | 
						RLCBSN_t mBSN;	// The BSN corresponding to the GSM FN of the first received burst.
 | 
				
			||||||
 | 
						RadData mRD;
 | 
				
			||||||
 | 
						BitVector mData;
 | 
				
			||||||
 | 
						MACUplinkHeader mmac;
 | 
				
			||||||
 | 
						ChannelCodingType mUpCC;
 | 
				
			||||||
 | 
						RLCRawBlock(int wfn, const BitVector &wData,float wRSSI, float wTimgingError,ChannelCodingType cc);
 | 
				
			||||||
 | 
						~RLCRawBlock() { RN_MEMCHKDEL(RLCRawBlock) }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#if RLCHDR_IMPLEMENTATION
 | 
				
			||||||
 | 
						RLCRawBlock::RLCRawBlock(int wbsn, const BitVector &wData,
 | 
				
			||||||
 | 
							float wRSSI, float wTimingError, ChannelCodingType cc)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							RN_MEMCHKNEW(RLCRawBlock)
 | 
				
			||||||
 | 
							mData.clone(wData);	// Explicit clone.
 | 
				
			||||||
 | 
							mBSN = wbsn;
 | 
				
			||||||
 | 
							mmac.parseMAC(mData); 	// Pull the MAC header out of the BitVector.
 | 
				
			||||||
 | 
							mUpCC = cc;
 | 
				
			||||||
 | 
							assert(mData.isOwner());
 | 
				
			||||||
 | 
							mRD = RadData(wRSSI,wTimingError);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// There may be multiple RLC_sub_blocks for multiple PDUs in one RLC/MAC block.
 | 
				
			||||||
 | 
					struct RLCSubBlockHeader
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static int makeoctet(unsigned length, unsigned mbit, unsigned ebit) {
 | 
				
			||||||
 | 
							return (length << 2) | (mbit << 1) | ebit;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						//	unsigned mLengthIndicator:6;	// length of PDU with block, but see GSM04.60sec10.4.14
 | 
				
			||||||
 | 
						// If the LLC PDU does not fit in a single RLC block, then there is no
 | 
				
			||||||
 | 
						// length indicator byte for the full RLC blocks, and the rest of the RLC block is PDU data.
 | 
				
			||||||
 | 
						// (Works because the E bit in the RLC header tells us if there is a length indicator.)
 | 
				
			||||||
 | 
						// Otherwise (ie, for the final RLC segment of each PDU), there is a length indicator,
 | 
				
			||||||
 | 
						// and there may be additional PDUs tacked on after the end of this PDU, specified by M bit.
 | 
				
			||||||
 | 
						// The description of length indicator in 10.4.14 is confusing.
 | 
				
			||||||
 | 
						// In English: you need the length-indicator byte if:
 | 
				
			||||||
 | 
						// (a) The PDU does not fill the RLC block, indicated by the LI field, or
 | 
				
			||||||
 | 
						// (b) This is the last segment of a PDU and there are more PDUs following
 | 
				
			||||||
 | 
						//     in the same TBF, indicated by M=1.
 | 
				
			||||||
 | 
						// The "singular" case mentioned in 10.4.14 occurs when you need the
 | 
				
			||||||
 | 
						// length-indicator for (b) but not for (a).  In this singular case only,
 | 
				
			||||||
 | 
						// you set the LI (length) field to 0, god only knows why, and presumably M=0,
 | 
				
			||||||
 | 
						// in the next-to-last segment, then presumably you put the last byte of
 | 
				
			||||||
 | 
						// the PDU into the next RLC block with a LI=1 and M=1.
 | 
				
			||||||
 | 
						// If the PDU is incomplete (for uplink blocks only, which presumably means
 | 
				
			||||||
 | 
						// the allocation ran out before the PDU data) then the final RLC block
 | 
				
			||||||
 | 
						// would be full so you normally wouldn't need an LI Byte, but to mark this fact
 | 
				
			||||||
 | 
						// you must reduce the last segment size by one to make room for an LI Byte with LI=0,
 | 
				
			||||||
 | 
						// and presumably M=0, which is distinguishable from the "singular case" above
 | 
				
			||||||
 | 
						// by the CountDown field reaching 0.
 | 
				
			||||||
 | 
						//	unsigned mM:1;			// More bit; if set there is another sub-block after this one.
 | 
				
			||||||
 | 
						//	unsigned mE:1;			// Extension bit, see GSM04.60 table 10.4.13.1.
 | 
				
			||||||
 | 
						// With M bit, indicates if there is more PDU data in this RLC block.
 | 
				
			||||||
 | 
						// M+E = 0+0: reserved;
 | 
				
			||||||
 | 
						// M+E = 0+1: no more data after the current LLC PDU segment.
 | 
				
			||||||
 | 
						// M+E = 1+0: new LLC PDU after current LLC PDU, with extension octet.
 | 
				
			||||||
 | 
						// M+E = 1+1: new LLC PDU after current LLC PDU, fills rest of RLC block.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// We are not using this, because we are not doing contention resolution required
 | 
				
			||||||
 | 
					// for single phase uplink.
 | 
				
			||||||
 | 
					struct RLCSubBlockTLLI
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						RLCSubBlockHeader hdr;	// 1 byte.
 | 
				
			||||||
 | 
						unsigned char mTLLI[4];	// 4 bytes containing a TLLI.
 | 
				
			||||||
 | 
						struct {
 | 
				
			||||||
 | 
							unsigned mPFI:7;
 | 
				
			||||||
 | 
							unsigned mE:1;
 | 
				
			||||||
 | 
						} b5;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct RLCDownlinkDataBlockHeader // GSM04.60sec10.2.1
 | 
				
			||||||
 | 
					 	: public MACDownlinkHeader		// 1 byte MAC header
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// From GSM 04.60 10.4:
 | 
				
			||||||
 | 
						// Octet 1:
 | 
				
			||||||
 | 
						// Use of Power Reduction field described in 5.08 10.2.2.  We dont need it.
 | 
				
			||||||
 | 
						Field_z<2> mPR; 	// Power Reduction.  0 means no power reduction, 1,2 mean some, 3 means 'not usable.'
 | 
				
			||||||
 | 
						Field_z<5> mTFI; 	// TFI - temp flow ID that this block is in
 | 
				
			||||||
 | 
						Field_z<1> mFBI;	// Final Block Indicator - last block of this TBF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Octet 2:
 | 
				
			||||||
 | 
						Field_z<7> mBSN; 		// Block Sequence Number, modulo 128
 | 
				
			||||||
 | 
						// If E bit is one, followed directly by RLC data.
 | 
				
			||||||
 | 
						// If E bit is zero, followed by zero or more RLC_Data_Sub_Block.
 | 
				
			||||||
 | 
						Field_z<1> mE;			// End of extensions bit.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Note: unused RLC data field filled with 0x2b as per 04.60 10.4.16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RLCDownlinkDataBlockHeader() {
 | 
				
			||||||
 | 
							MACDownlinkHeader::init(MACPayloadType::RLCData);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void writeRLCHeader(MsgCommon& dest) const;
 | 
				
			||||||
 | 
						void write(BitVector&dst) const;
 | 
				
			||||||
 | 
						void text(std::ostream&os) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//void setTFI(unsigned wTFI) { b1.mTFI = wTFI; }
 | 
				
			||||||
 | 
						//void setFBI(bool wFBI) { b1.mFBI = wFBI; }
 | 
				
			||||||
 | 
						//void setBSN(unsigned wBSN) { b2.mBSN = wBSN; }
 | 
				
			||||||
 | 
						//void setE(bool wE) { b2.mE = wE; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#if RLCHDR_IMPLEMENTATION
 | 
				
			||||||
 | 
						void RLCDownlinkDataBlockHeader::writeRLCHeader(MsgCommon& dest) const {
 | 
				
			||||||
 | 
							dest.WRITE_ITEM(mPR);
 | 
				
			||||||
 | 
							dest.WRITE_ITEM(mTFI);
 | 
				
			||||||
 | 
							dest.WRITE_ITEM(mFBI);
 | 
				
			||||||
 | 
							dest.WRITE_ITEM(mBSN);
 | 
				
			||||||
 | 
							dest.WRITE_ITEM(mE);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void RLCDownlinkDataBlockHeader::write(BitVector&dst) const {
 | 
				
			||||||
 | 
							MsgCommonWrite mcw(dst);
 | 
				
			||||||
 | 
							MACDownlinkHeader::writeMACHeader(mcw);
 | 
				
			||||||
 | 
							RLCDownlinkDataBlockHeader::writeRLCHeader(mcw);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void RLCDownlinkDataBlockHeader::text(std::ostream&os) const {
 | 
				
			||||||
 | 
							MsgCommonText dst(os);
 | 
				
			||||||
 | 
							writeMACHeader(dst);
 | 
				
			||||||
 | 
							writeRLCHeader(dst);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM04.60sec10.2.2
 | 
				
			||||||
 | 
					struct RLCUplinkDataBlockHeader
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Octet 0 is the MAC header.
 | 
				
			||||||
 | 
						MACUplinkHeader mmac;
 | 
				
			||||||
 | 
						// Octet 1: RLC Header (starts at bit 8)
 | 
				
			||||||
 | 
						Field<1> mSpare;
 | 
				
			||||||
 | 
						Field<1> mPI;	// PFI Indicator bit; If set, optional PFI is present in data.
 | 
				
			||||||
 | 
										// PFI identifies a Packet Flow Context defined GSM24.008,
 | 
				
			||||||
 | 
										// and mentioned in GSP04.60 table 11.2.6.2
 | 
				
			||||||
 | 
										// Range will not support these.
 | 
				
			||||||
 | 
						Field<5> mTFI; 	// TFI - temp flow ID that this block is in
 | 
				
			||||||
 | 
						Field<1> mTI;	// TLLI indicator.  If set, optional TLLI is present in dataa.
 | 
				
			||||||
 | 
										// TLLI must be send by MS during a one phase access, because
 | 
				
			||||||
 | 
										// the network does not know TLLI.  It is not needed for two phase access.
 | 
				
			||||||
 | 
						// Octet 2:
 | 
				
			||||||
 | 
						Field<7> mBSN;	// Block Sequence Number, modulo 128
 | 
				
			||||||
 | 
						Field<1> mE;	// Extension bit: 0 indicates next word is length indicator,
 | 
				
			||||||
 | 
										// 1 means whole block is data.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t parseRLCHeader(const RLCRawBlock *src);
 | 
				
			||||||
 | 
						// Since this is an uplink header, we only need to write it for testing purposes.
 | 
				
			||||||
 | 
						void writeRLCHeader(MsgCommon&dst) const;
 | 
				
			||||||
 | 
						void write(BitVector&dst) const;	// Only needed for testing.
 | 
				
			||||||
 | 
						void text(std::ostream&os) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#if RLCHDR_IMPLEMENTATION
 | 
				
			||||||
 | 
						size_t RLCUplinkDataBlockHeader::parseRLCHeader(const RLCRawBlock *src) {
 | 
				
			||||||
 | 
							mmac = src->mmac;
 | 
				
			||||||
 | 
							size_t rp = mmac.lengthBits();
 | 
				
			||||||
 | 
							rp++;	// skip spare bit.
 | 
				
			||||||
 | 
							mPI = src->mData.readField(rp,1);
 | 
				
			||||||
 | 
							mTFI = src->mData.readField(rp,5);
 | 
				
			||||||
 | 
							mTI = src->mData.readField(rp,1);
 | 
				
			||||||
 | 
							mBSN = src->mData.readField(rp,7);
 | 
				
			||||||
 | 
							mE = src->mData.readField(rp,1);
 | 
				
			||||||
 | 
							return rp;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Since this is an uplink header, we only need to write it for testing purposes.
 | 
				
			||||||
 | 
						void RLCUplinkDataBlockHeader::writeRLCHeader(MsgCommon&dst) const {
 | 
				
			||||||
 | 
							dst.WRITE_ITEM(mSpare);
 | 
				
			||||||
 | 
							dst.WRITE_ITEM(mPI);
 | 
				
			||||||
 | 
							dst.WRITE_ITEM(mTFI);
 | 
				
			||||||
 | 
							dst.WRITE_ITEM(mTI);
 | 
				
			||||||
 | 
							dst.WRITE_ITEM(mBSN);
 | 
				
			||||||
 | 
							dst.WRITE_ITEM(mE);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void RLCUplinkDataBlockHeader::write(BitVector&dst) const {	// Only needed for testing.
 | 
				
			||||||
 | 
							MsgCommonWrite mcw(dst);
 | 
				
			||||||
 | 
							mmac.writeMACHeader(mcw);
 | 
				
			||||||
 | 
							writeRLCHeader(mcw);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void RLCUplinkDataBlockHeader::text(std::ostream&os) const {
 | 
				
			||||||
 | 
							MsgCommonText dst(os);
 | 
				
			||||||
 | 
							mmac.writeMACHeader(dst);
 | 
				
			||||||
 | 
							writeRLCHeader(dst);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RLCUplinkDataSegment : public BitVector {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						RLCUplinkDataSegment(const BitVector&wPayload) :
 | 
				
			||||||
 | 
							BitVector(NULL,(char*)wPayload.begin(),(char*)wPayload.end()) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// access to potentially multiple data fields
 | 
				
			||||||
 | 
						// GSM04.60 sec 10.4.13 and 10.4.14
 | 
				
			||||||
 | 
						size_t LIByteLI(size_t lp=0) const { return peekField(lp,6); }
 | 
				
			||||||
 | 
						bool LIByteM(size_t lp=0) const { return peekField(lp+6,1); }
 | 
				
			||||||
 | 
						bool LIByteE(size_t lp=0) const { return peekField(lp+7,1); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RLCUplinkDataBlock
 | 
				
			||||||
 | 
						: public RLCUplinkDataBlockHeader
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						BitVector mData;
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						static const unsigned mHeaderSizeBits = 24;
 | 
				
			||||||
 | 
						ChannelCodingType mUpCC;	// We dont use this - debugging info only.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Convert a BitVector into an RLC data block.
 | 
				
			||||||
 | 
						// We simply take ownership of the BitVector memory.
 | 
				
			||||||
 | 
						// The default destructor will destroy the BitVector when delete is called on us.
 | 
				
			||||||
 | 
						RLCUplinkDataBlock(RLCRawBlock* wSrc);
 | 
				
			||||||
 | 
						~RLCUplinkDataBlock() { RN_MEMCHKDEL(RLCUplinkDataBlock) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Return subset of the BitVector that is payload.
 | 
				
			||||||
 | 
						BitVector getPayload() { return mData.tail(mHeaderSizeBits); }
 | 
				
			||||||
 | 
						void text(std::ostream&os);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#if RLCHDR_IMPLEMENTATION
 | 
				
			||||||
 | 
						RLCUplinkDataBlock::RLCUplinkDataBlock(RLCRawBlock* wSrc)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							RN_MEMCHKNEW(RLCUplinkDataBlock)
 | 
				
			||||||
 | 
							mData = wSrc->mData;
 | 
				
			||||||
 | 
							size_t tmp = parseRLCHeader(wSrc);
 | 
				
			||||||
 | 
							mUpCC = wSrc->mUpCC;
 | 
				
			||||||
 | 
							assert(tmp == mHeaderSizeBits);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void RLCUplinkDataBlock::text(std::ostream&os) {
 | 
				
			||||||
 | 
							os << "RLCUplinkDataBlock=(";
 | 
				
			||||||
 | 
							RLCUplinkDataBlockHeader::text(os);
 | 
				
			||||||
 | 
							os << "\npayload:";
 | 
				
			||||||
 | 
							// Write out the data as bytes.
 | 
				
			||||||
 | 
							BitVector payload(getPayload());
 | 
				
			||||||
 | 
							payload.hex(os);
 | 
				
			||||||
 | 
							/***
 | 
				
			||||||
 | 
							int i, size=payload.size(); char buf[10];
 | 
				
			||||||
 | 
							for (i=0; i < size-8; i+=8) {
 | 
				
			||||||
 | 
								sprintf(buf," %02x",(int)payload.peekField(i,8));
 | 
				
			||||||
 | 
								os << buf;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							***/
 | 
				
			||||||
 | 
							os << ")";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.60 10.2.1 */
 | 
				
			||||||
 | 
					class RLCDownlinkDataBlock
 | 
				
			||||||
 | 
						: public RLCDownlinkDataBlockHeader, public Text2Str
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// The mPayload does not own any allocated storage; it points into the mPDU of the TBF.
 | 
				
			||||||
 | 
						// We are not currently putting multiple PDUs in a TBF, so this is ok.
 | 
				
			||||||
 | 
						ByteVector mPayload;	// max size is 52, may be smaller.
 | 
				
			||||||
 | 
						bool mIdle;				// If true, block contains only a keepalive.
 | 
				
			||||||
 | 
						ChannelCodingType mChannelCoding;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int getPayloadSize() const {	// In bytes.
 | 
				
			||||||
 | 
							return RLCPayloadSizeInBytes[mChannelCoding];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int headerSizeBytes() { return 3; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RLCDownlinkDataBlock(ChannelCodingType wCC) : mIdle(0), mChannelCoding(wCC) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Convert the Downlink Data Block into a BitVector.
 | 
				
			||||||
 | 
						// We do this right before sending it down to the encoder.
 | 
				
			||||||
 | 
						BitVector getBitVector() const;
 | 
				
			||||||
 | 
						void text(std::ostream&os, bool includePayload) const;
 | 
				
			||||||
 | 
						void text(std::ostream&os) const { text(os,true); }	// Default value doesnt work. Gotta love that C++.
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						//	/** Construct the block and write data into it. */
 | 
				
			||||||
 | 
						//	DownlinkRLCDataBlock(
 | 
				
			||||||
 | 
						//		unsigned RRBP, bool SP, unsigned USF,
 | 
				
			||||||
 | 
						//		bool PR, unsigned TFI, bool FBI,
 | 
				
			||||||
 | 
						//		unsigned BSN,
 | 
				
			||||||
 | 
						//		const BitVector& data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#if RLCHDR_IMPLEMENTATION
 | 
				
			||||||
 | 
						BitVector RLCDownlinkDataBlock::getBitVector() const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Add 3 bytes for mac and rlc headers.
 | 
				
			||||||
 | 
							BitVector result(8 *(3+mPayload.size()));
 | 
				
			||||||
 | 
							RLCDownlinkDataBlockHeader::write(result);
 | 
				
			||||||
 | 
							BitVector resultpayload(result.tail(3*8));
 | 
				
			||||||
 | 
							resultpayload.unpack(mPayload.begin()); // unpack mPayload into resultpayload
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void RLCDownlinkDataBlock::text(std::ostream&os, bool includePayload) const {
 | 
				
			||||||
 | 
							os << "RLCDownlinkDataBlock=(";
 | 
				
			||||||
 | 
							RLCDownlinkDataBlockHeader::text(os);
 | 
				
			||||||
 | 
							os << LOGVAR2("CCoding",mChannelCoding) <<LOGVAR2("idle",mIdle);
 | 
				
			||||||
 | 
							if (includePayload) {
 | 
				
			||||||
 | 
								os << "\npayload:" << mPayload;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							/***
 | 
				
			||||||
 | 
							int i, size=mPayload.size(); char buf[10];
 | 
				
			||||||
 | 
							for (i=0; i < size-8; i+=8) {
 | 
				
			||||||
 | 
								sprintf(buf," %02x",(int)mPayload.peekField(i,8));
 | 
				
			||||||
 | 
								os << buf;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							***/
 | 
				
			||||||
 | 
							os << ")";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}; // namespace GPRS
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										197
									
								
								GPRS/RLCMessages.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								GPRS/RLCMessages.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,197 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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 GPRS L2 RLC Messages, from GSM 04.60 Section 11 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//#include <iostream>
 | 
				
			||||||
 | 
					#include "Defines.h"
 | 
				
			||||||
 | 
					//#include "GSMCommon.h"
 | 
				
			||||||
 | 
					#include <BitVector.h>
 | 
				
			||||||
 | 
					#include <Logger.h>
 | 
				
			||||||
 | 
					#define RLCHDR_IMPLEMENTATION 1
 | 
				
			||||||
 | 
					#include "RLCHdr.h"
 | 
				
			||||||
 | 
					#include "TBF.h"
 | 
				
			||||||
 | 
					#define RLCMESSAGES_IMPLEMENTATION 1
 | 
				
			||||||
 | 
					#include "RLCMessages.h"
 | 
				
			||||||
 | 
					#include "MAC.h"
 | 
				
			||||||
 | 
					#include "FEC.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *RLCUplinkMessage::name(MessageType type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static char buf[50];
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
							CASENAME(PacketCellChangeFailure)
 | 
				
			||||||
 | 
							CASENAME(PacketControlAcknowledgement)
 | 
				
			||||||
 | 
							CASENAME(PacketDownlinkAckNack)
 | 
				
			||||||
 | 
							CASENAME(PacketUplinkDummyControlBlock)
 | 
				
			||||||
 | 
							CASENAME(PacketMeasurementReport)
 | 
				
			||||||
 | 
							CASENAME(PacketEnhancedMeasurementReport)
 | 
				
			||||||
 | 
							CASENAME(PacketResourceRequest)
 | 
				
			||||||
 | 
							CASENAME(PacketMobileTBFStatus)
 | 
				
			||||||
 | 
							CASENAME(PacketPSIStatus)
 | 
				
			||||||
 | 
							CASENAME(EGPRSPacketDownlinkAckNack)
 | 
				
			||||||
 | 
							CASENAME(PacketPause)
 | 
				
			||||||
 | 
							CASENAME(AdditionalMSRadioAccessCapabilities)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								sprintf(buf,"RLCUplinkMessageType %d (unknown)",(int)type);
 | 
				
			||||||
 | 
								return buf;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *RLCDownlinkMessage::name(MessageType type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static char buf[50];
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
							CASENAME(PacketAccessReject )
 | 
				
			||||||
 | 
							CASENAME(PacketCellChangeOrder)
 | 
				
			||||||
 | 
							CASENAME(PacketDownlinkAssignment)
 | 
				
			||||||
 | 
							CASENAME(PacketMeasurementOrder )
 | 
				
			||||||
 | 
							CASENAME(PacketPagingRequest )
 | 
				
			||||||
 | 
							CASENAME(PacketPDCHRelease)
 | 
				
			||||||
 | 
							CASENAME(PacketPollingRequest)
 | 
				
			||||||
 | 
							CASENAME(PacketPowerControlTimingAdvance)
 | 
				
			||||||
 | 
							CASENAME(PacketPRACHParameters)
 | 
				
			||||||
 | 
							CASENAME(PacketQueueingNotification)
 | 
				
			||||||
 | 
							CASENAME(PacketTimeslotReconfigure)
 | 
				
			||||||
 | 
							CASENAME(PacketTBFRelease)
 | 
				
			||||||
 | 
							CASENAME(PacketUplinkAckNack)
 | 
				
			||||||
 | 
							CASENAME(PacketUplinkAssignment)
 | 
				
			||||||
 | 
							CASENAME(PacketDownlinkDummyControlBlock)
 | 
				
			||||||
 | 
							CASENAME(PSI1)
 | 
				
			||||||
 | 
							CASENAME(PSI2)
 | 
				
			||||||
 | 
							CASENAME(PSI3)
 | 
				
			||||||
 | 
							CASENAME(PSI3bis)
 | 
				
			||||||
 | 
							CASENAME(PSI4)
 | 
				
			||||||
 | 
							CASENAME(PSI5)
 | 
				
			||||||
 | 
							CASENAME(PSI6)
 | 
				
			||||||
 | 
							CASENAME(PSI7)
 | 
				
			||||||
 | 
							CASENAME(PSI8)
 | 
				
			||||||
 | 
							CASENAME(PSI13)
 | 
				
			||||||
 | 
							CASENAME(PSI14)
 | 
				
			||||||
 | 
							CASENAME(PSI3ter)
 | 
				
			||||||
 | 
							CASENAME(PSI3quater)
 | 
				
			||||||
 | 
							CASENAME(PSI15)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								sprintf(buf,"RLCDownlinkMessageType %d (unknown)",(int)type);
 | 
				
			||||||
 | 
								return buf;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MSInfo *RLCMsgPacketResourceRequest::getMS(PDCHL1FEC *chan, bool create)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// If MS is identified by a TLLI, the msg is not specifically associated with a TBF.
 | 
				
			||||||
 | 
						if (mTLLIPresent) {
 | 
				
			||||||
 | 
							MSInfo *ms = gL2MAC.macFindMSByTlli(mTLLI, create);
 | 
				
			||||||
 | 
							return ms;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							RLCDirType dir = mGTFI.mIsDownlinkTFI ? RLCDir::Down : RLCDir::Up;
 | 
				
			||||||
 | 
							TBF *tbf = chan->getTFITBF(mGTFI.mGTFI,dir);
 | 
				
			||||||
 | 
							if (tbf) return tbf->mtMS;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RLCMsgPacketUplinkAssignmentDynamicAllocationElt::setFrom(TBF *tbf,MultislotSymmetry sym)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						MSInfo *ms = tbf->mtMS;
 | 
				
			||||||
 | 
						setAlpha(ms->msGetAlpha());
 | 
				
			||||||
 | 
						PDCHL1Uplink *up;
 | 
				
			||||||
 | 
						RN_FOR_ALL(PDCHL1UplinkList_t,ms->msPCHUps,up) {
 | 
				
			||||||
 | 
							int tn = up->TN();
 | 
				
			||||||
 | 
							setUSF(tn,ms->msUSFs[tn]);
 | 
				
			||||||
 | 
							setGamma(tn,ms->msGetGamma());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (ms->isExtendedDynamic()) {
 | 
				
			||||||
 | 
							mExtendedDynamicAllocation = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//if (sym == MultislotSymmetric && isExtendedDynamic()) {
 | 
				
			||||||
 | 
						//	// This sounds odd, but we need to use the downlink timeslots to program
 | 
				
			||||||
 | 
						//	// the uplink timeslots so that they will be symmetric.
 | 
				
			||||||
 | 
						//	// If they are assymetric, the smaller array is always valid in both directions.
 | 
				
			||||||
 | 
						//	PDCHL1Downlink *down;
 | 
				
			||||||
 | 
						//	RN_FOR_ALL(PDCHL1DownlinkList_t,ms->msPCHDowns,down) {
 | 
				
			||||||
 | 
						//		int tn = down->TN();
 | 
				
			||||||
 | 
						//		setUSF(tn,ms->msUSFs[tn]);
 | 
				
			||||||
 | 
						//		setGamma(tn,ms->msGetGamma());
 | 
				
			||||||
 | 
						//	}
 | 
				
			||||||
 | 
						//} else {
 | 
				
			||||||
 | 
						//	PDCHL1Uplink *up;
 | 
				
			||||||
 | 
						//	RN_FOR_ALL(PDCHL1UplinkList_t,ms->msPCHUps,up) {
 | 
				
			||||||
 | 
						//		int tn = up->TN();
 | 
				
			||||||
 | 
						//		setUSF(tn,ms->msUSFs[tn]);
 | 
				
			||||||
 | 
						//		setGamma(tn,ms->msGetGamma());
 | 
				
			||||||
 | 
						//	}
 | 
				
			||||||
 | 
						//}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RLCMsgPowerControlParametersIE::setFrom(TBF *tbf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						MSInfo *ms = tbf->mtMS;
 | 
				
			||||||
 | 
						setAlpha(ms->msGetAlpha());
 | 
				
			||||||
 | 
						PDCHL1Downlink *down;
 | 
				
			||||||
 | 
						RN_FOR_ALL(PDCHL1DownlinkList_t,ms->msPCHDowns,down) {
 | 
				
			||||||
 | 
							int tn = down->TN();
 | 
				
			||||||
 | 
							setGamma(tn,ms->msGetGamma());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.60 11.2 */
 | 
				
			||||||
 | 
					RLCUplinkMessage* RLCUplinkMessageParse(RLCRawBlock *src)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						RLCUplinkMessage *result = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned mMessageType = src->mData.peekField(8,6);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Kyle reported that OpenBTS crashes parsing the RLCMsgMSRACapabilityValuePartIE.
 | 
				
			||||||
 | 
						// If you look at the message that is in, if the RACap is trashed, the message is unusable.
 | 
				
			||||||
 | 
						// So lets catch errors way up at this level, and ignore them on error.
 | 
				
			||||||
 | 
						// In case of error, we are probably permanently losing the memory associated with the messsage, oh well.
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							switch (mMessageType) {
 | 
				
			||||||
 | 
								case RLCUplinkMessage::PacketControlAcknowledgement:
 | 
				
			||||||
 | 
									result = new RLCMsgPacketControlAcknowledgement(src);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case RLCUplinkMessage::PacketDownlinkAckNack:
 | 
				
			||||||
 | 
									// Thats right: DownlinkAckNack is an uplink message.
 | 
				
			||||||
 | 
									result = new RLCMsgPacketDownlinkAckNack(src);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case RLCUplinkMessage::PacketUplinkDummyControlBlock:
 | 
				
			||||||
 | 
									result = new RLCMsgPacketUplinkDummyControlBlock(src);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case RLCUplinkMessage::PacketResourceRequest:
 | 
				
			||||||
 | 
									result = new RLCMsgPacketResourceRequest(src);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								// case PacketMobileTBFStatus:
 | 
				
			||||||
 | 
								// case AdditionalMSRadioAccessCapabilities:
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									GLOG(INFO) << "unsupported RLC uplink message, type=" << mMessageType;
 | 
				
			||||||
 | 
									//result = new RLCMsgPacketUplinkDummyControlBlock(src);
 | 
				
			||||||
 | 
									return NULL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
 | 
						} catch (ByteVectorError) {
 | 
				
			||||||
 | 
							GLOG(ERR) << "Parse Error: Premature end of message, type="
 | 
				
			||||||
 | 
								<<mMessageType<<"="<<RLCUplinkMessage::name((RLCUplinkMessage::MessageType)mMessageType);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};	// namespace GPRS
 | 
				
			||||||
							
								
								
									
										1547
									
								
								GPRS/RLCMessages.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1547
									
								
								GPRS/RLCMessages.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										214
									
								
								GPRS/RList.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								GPRS/RList.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,214 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef RLIST_H
 | 
				
			||||||
 | 
					#define RLIST_H
 | 
				
			||||||
 | 
					#include <list>
 | 
				
			||||||
 | 
					#include "GPRSInternal.h"	// For devassert
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A list with random access too.
 | 
				
			||||||
 | 
					template<class T>
 | 
				
			||||||
 | 
					class RList : public std::list<T> {
 | 
				
			||||||
 | 
						typedef typename std::list<T>::iterator itr_t;
 | 
				
			||||||
 | 
						typedef typename std::list<T> type_t;
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						T operator[](unsigned ind) {
 | 
				
			||||||
 | 
							unsigned i = 0;
 | 
				
			||||||
 | 
							for (itr_t itr = type_t::begin(); itr != type_t::end(); itr++) {
 | 
				
			||||||
 | 
								if (i++ == ind) return *itr;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return 0;	// Since we use 0 to mean unknown, this class can not be used
 | 
				
			||||||
 | 
										// if this is a valid value of T.  Could throw an error instead,
 | 
				
			||||||
 | 
										// and complicate things for the caller.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool find(T element) {
 | 
				
			||||||
 | 
							for (itr_t itr = type_t::begin(); itr != type_t::end(); itr++) {
 | 
				
			||||||
 | 
								if (*itr == element) {
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//bool find_safely(T element) { return find(element); }
 | 
				
			||||||
 | 
						//void push_back_safely(T&val) { assert(! find(val)); std::list<T>::push_back(val); }
 | 
				
			||||||
 | 
						//void remove_safely(T&val) { std::list<T>::remove(val); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Like RList but add an internal Mutex so it can auto-lock for multi-threads using RlistIteratorThreadSafe.
 | 
				
			||||||
 | 
					template<class T>
 | 
				
			||||||
 | 
					class RListThreadSafe : RList<T> {
 | 
				
			||||||
 | 
						Mutex mListLock;
 | 
				
			||||||
 | 
						bool find_safely(T element) {
 | 
				
			||||||
 | 
							ScopedLock lock(mListLock);
 | 
				
			||||||
 | 
							return find(element);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void push_back_safely(T&val) {
 | 
				
			||||||
 | 
							ScopedLock lock(mListLock);
 | 
				
			||||||
 | 
							assert(! find(val));
 | 
				
			||||||
 | 
							std::list<T>::push_back(val);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void remove_safely(T&val) {
 | 
				
			||||||
 | 
							ScopedLock lock(mListLock);
 | 
				
			||||||
 | 
							std::list<T>::remove(val);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Like ScopedLock but creates a scoped iterator especially useful in a for statement.
 | 
				
			||||||
 | 
					// Use like this: given a list and a mutex to protect access to the list:
 | 
				
			||||||
 | 
					// class T; list<T> mylist; Mutex mymutex; for (ScopedIterator<T,list<T> >(mylist,mymutex); next(var);)
 | 
				
			||||||
 | 
					template<class T, class ListType = RList<T> >
 | 
				
			||||||
 | 
					class ScopedIterator  {
 | 
				
			||||||
 | 
						ListType &mPList;
 | 
				
			||||||
 | 
						Mutex& mPMutex;
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						typename ListType::iterator mNextItr, mEndp;
 | 
				
			||||||
 | 
						void siInit() {
 | 
				
			||||||
 | 
							mPMutex.lock();
 | 
				
			||||||
 | 
							mNextItr = mPList.begin();
 | 
				
			||||||
 | 
							mEndp = mPList.end();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ScopedIterator(ListType &wPList, Mutex &wPMutex) : mPList(wPList), mPMutex(wPMutex) { siInit(); }
 | 
				
			||||||
 | 
						// Yes you can use this in a const method function and yes we are changing the Mutex and yes it is ok so use a const_cast.
 | 
				
			||||||
 | 
						// ScopedIterator(ListType const &wPList) : mPList(const_cast<ListType&>(wPList)), mPMutex(mPList.mListLock) { siInit(); }
 | 
				
			||||||
 | 
						~ScopedIterator() { mPMutex.unlock(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This is meant to be used as the test statement in a for or while loop.
 | 
				
			||||||
 | 
						// We always point the iterator at the next element, so that 
 | 
				
			||||||
 | 
						// deletion of the current element by the caller is permitted.
 | 
				
			||||||
 | 
						// We also store the end element when the iteration starts, so new
 | 
				
			||||||
 | 
						// elements pushed onto the back of the list during the iteration are ignored.
 | 
				
			||||||
 | 
						bool next(T &var) {
 | 
				
			||||||
 | 
							if (mNextItr == mEndp) return false;
 | 
				
			||||||
 | 
							var = *mNextItr++;
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// An iterator to be used in for loops.
 | 
				
			||||||
 | 
					template<class T>
 | 
				
			||||||
 | 
					class RListIterator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						typedef RList<T> ListType;
 | 
				
			||||||
 | 
						ListType &mPList;
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						typename ListType::iterator mItr, mNextItr, mEndp;
 | 
				
			||||||
 | 
						bool mFinished;
 | 
				
			||||||
 | 
						void siInit() {
 | 
				
			||||||
 | 
							mNextItr = mPList.begin();
 | 
				
			||||||
 | 
							mItr = mEndp = mPList.end();
 | 
				
			||||||
 | 
							mFinished = (mNextItr == mEndp);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						RListIterator(ListType &wPList) : mPList(wPList) { siInit(); }
 | 
				
			||||||
 | 
						RListIterator(ListType const &wPList) : mPList(const_cast<ListType&>(wPList)) { siInit(); }
 | 
				
			||||||
 | 
						bool next(T &var) {
 | 
				
			||||||
 | 
							if (mFinished) { return false; }
 | 
				
			||||||
 | 
							mItr = mNextItr;
 | 
				
			||||||
 | 
							var = *mNextItr++;
 | 
				
			||||||
 | 
							mFinished = (mNextItr == mEndp);	// We check now in case caller deletes end().
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Erase the current element.
 | 
				
			||||||
 | 
						void erase() {
 | 
				
			||||||
 | 
							devassert(mItr != mEndp);
 | 
				
			||||||
 | 
							mPList.erase(mItr);
 | 
				
			||||||
 | 
							mItr = mEndp;		// To indicate we have already erased it.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool next(T &var, typename ListType::iterator &itr) {	// Return a regular old iterator to the user.
 | 
				
			||||||
 | 
							bool result = next(var);
 | 
				
			||||||
 | 
							itr = mItr;
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A ScopedIterator wrapper specifically for RList, to be used in for loops.
 | 
				
			||||||
 | 
					// The RList has the mutex built-in.
 | 
				
			||||||
 | 
					template<class T>
 | 
				
			||||||
 | 
					class RListIteratorThreadSafe : public ScopedIterator<T>
 | 
				
			||||||
 | 
					{	public:
 | 
				
			||||||
 | 
						typedef RList<T> ListType;
 | 
				
			||||||
 | 
						RListIteratorThreadSafe(ListType &wPList) : ScopedIterator<T>(wPList,wPList.mListLock) {}
 | 
				
			||||||
 | 
						// Yes you can use this in a const method and yes the Mutex is non-const but yes it is ok so use a const_cast.
 | 
				
			||||||
 | 
						RListIteratorThreadSafe(ListType const &wPList) : ScopedIterator<T>(const_cast<ListType&>(wPList),const_cast<ListType&>(wPList).mListLock) {}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Assumes the list is an RList which has an internal mutex.
 | 
				
			||||||
 | 
					#define RN_RLIST_FOR_ALL_THREAD_SAFE(type,list,var) \
 | 
				
			||||||
 | 
						for (RListIteratorThreadSafe<type> itr(list); itr.next(var); ) 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					// This macro requires the caller to advance itr if the var is deleted.
 | 
				
			||||||
 | 
					//#define RN_FOR_ALL_WITH_ITR(type,list,var,itr) \
 | 
				
			||||||
 | 
					//	for (type::iterator itr = list.begin(); \
 | 
				
			||||||
 | 
					//		itr == list.end() ? 0 : ((var=*itr),1); \
 | 
				
			||||||
 | 
					//		itr++)
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This macro allows deletion of the current var from the list being iterated,
 | 
				
			||||||
 | 
					// because itr is advanced to the next position at the beginning of the loop,
 | 
				
			||||||
 | 
					// and list iterators are defined as keeping their position even if elements are deleted.
 | 
				
			||||||
 | 
					#define RN_FOR_ALL(type,list,var) \
 | 
				
			||||||
 | 
						for (type::iterator itr = (list).begin(); \
 | 
				
			||||||
 | 
							itr == (list).end() ? 0 : ((var=*itr++),1);)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Geez, the language sure botched this...
 | 
				
			||||||
 | 
					#define RN_FOR_ALL_CONST(type,list,var) \
 | 
				
			||||||
 | 
						for (type::const_iterator itr = (list).begin(); \
 | 
				
			||||||
 | 
							itr == (list).end() ? 0 : ((var=*itr++),1);)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* not used
 | 
				
			||||||
 | 
					#define RN_FOR_ALL_REVERSED(type,list,var) \
 | 
				
			||||||
 | 
						for (type::reverse_iterator var##_itr = (list).rbegin(); \
 | 
				
			||||||
 | 
							var##_itr == (list).rend() ? 0 : ((var=*var##_itr),1); \
 | 
				
			||||||
 | 
							var##_itr++)
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					// For your edification, these are the old functions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Does the list contain the element?  Return TRUE if so.
 | 
				
			||||||
 | 
					template<class T>
 | 
				
			||||||
 | 
					bool findlistrev(std::list<T> list, T element, typename std::list<T>::reverse_iterator *result = 0) {
 | 
				
			||||||
 | 
						for (typename std::list<T>::reverse_iterator itr = list.rbegin(); itr != list.rend(); itr++) {
 | 
				
			||||||
 | 
							if (*itr == element) {
 | 
				
			||||||
 | 
								if (result) *result = itr;
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
						// This did not work?
 | 
				
			||||||
 | 
						// return std::find(msPCHDowns.begin(),msPCHDowns.end(),(const PDCHL1Downlink*)down) != msPCHDowns.end();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<class T>
 | 
				
			||||||
 | 
					bool findlist(std::list<T> list, T element, typename std::list<T>::iterator *result = 0) {
 | 
				
			||||||
 | 
						for (typename std::list<T>::iterator itr = list.begin(); itr != list.end(); itr++) {
 | 
				
			||||||
 | 
							if (*itr == element) {
 | 
				
			||||||
 | 
								if (result) *result = itr;
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
						// This did not work?
 | 
				
			||||||
 | 
						// return std::find(msPCHDowns.begin(),msPCHDowns.end(),(const PDCHL1Downlink*)down) != msPCHDowns.end();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										136
									
								
								GPRS/ScalarTypes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								GPRS/ScalarTypes.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef SCALARTYPES_H
 | 
				
			||||||
 | 
					#define SCALARTYPES_H
 | 
				
			||||||
 | 
					#include <iostream>	// For size_t
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					//#include "GSMCommon.h"	// Was included for Z100Timer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// We dont bother to define *= /= etc.; you'll have to convert: a*=b; to: a=a*b;
 | 
				
			||||||
 | 
					#define _INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
 | 
				
			||||||
 | 
						Classname() : value(Init) {} \
 | 
				
			||||||
 | 
						Classname(Basetype wvalue) { value = wvalue; } /* Can set from basetype. */ \
 | 
				
			||||||
 | 
						operator Basetype(void) const { return value; }		/* Converts from basetype. */ \
 | 
				
			||||||
 | 
						Basetype operator=(Basetype wvalue) { return value = wvalue; } \
 | 
				
			||||||
 | 
						Basetype* operator&() { return &value; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define _INITIALIZED_SCALAR_ARITH_FUNCS(Basetype) \
 | 
				
			||||||
 | 
						Basetype operator++() { return ++value; } \
 | 
				
			||||||
 | 
						Basetype operator++(int) { return value++; } \
 | 
				
			||||||
 | 
						Basetype operator--() { return --value; } \
 | 
				
			||||||
 | 
						Basetype operator--(int) { return value--; } \
 | 
				
			||||||
 | 
						Basetype operator+=(Basetype wvalue) { return value = value + wvalue; } \
 | 
				
			||||||
 | 
						Basetype operator-=(Basetype wvalue) { return value = value - wvalue; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define _INITIALIZED_SCALAR_FUNCS(Classname,Basetype,Init) \
 | 
				
			||||||
 | 
						_INITIALIZED_SCALAR_BASE_FUNCS(Classname,Basetype,Init) \
 | 
				
			||||||
 | 
						_INITIALIZED_SCALAR_ARITH_FUNCS(Basetype)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define _DECLARE_SCALAR_TYPE(Classname_i,Classname_z,Basetype) \
 | 
				
			||||||
 | 
						template <Basetype Init> \
 | 
				
			||||||
 | 
						struct Classname_i { \
 | 
				
			||||||
 | 
							Basetype value; \
 | 
				
			||||||
 | 
							_INITIALIZED_SCALAR_FUNCS(Classname_i,Basetype,Init) \
 | 
				
			||||||
 | 
						}; \
 | 
				
			||||||
 | 
						typedef Classname_i<0> Classname_z;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Usage:
 | 
				
			||||||
 | 
					// Where 'classname' is one of the types listed below, then:
 | 
				
			||||||
 | 
					// 		classname_z specifies a zero initialized type;
 | 
				
			||||||
 | 
					// 		classname_i<value> initializes the type to the specified value.
 | 
				
			||||||
 | 
					// We also define Float_z.
 | 
				
			||||||
 | 
					_DECLARE_SCALAR_TYPE(Int_i, 	Int_z,  	int)
 | 
				
			||||||
 | 
					_DECLARE_SCALAR_TYPE(Char_i,	Char_z, 	signed char)
 | 
				
			||||||
 | 
					_DECLARE_SCALAR_TYPE(Int16_i,	Int16_z,	int16_t)
 | 
				
			||||||
 | 
					_DECLARE_SCALAR_TYPE(Int32_i,	Int32_z,	int32_t)
 | 
				
			||||||
 | 
					_DECLARE_SCALAR_TYPE(UInt_i,  	UInt_z,  	unsigned)
 | 
				
			||||||
 | 
					_DECLARE_SCALAR_TYPE(UChar_i,  	UChar_z,  	unsigned char)
 | 
				
			||||||
 | 
					_DECLARE_SCALAR_TYPE(UInt16_i,	UInt16_z,	uint16_t)
 | 
				
			||||||
 | 
					_DECLARE_SCALAR_TYPE(UInt32_i,	UInt32_z,	uint32_t)
 | 
				
			||||||
 | 
					_DECLARE_SCALAR_TYPE(Size_t_i,	Size_t_z,	size_t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Bool is special because it cannot accept some arithmetic funcs
 | 
				
			||||||
 | 
					//_DECLARE_SCALAR_TYPE(Bool_i,  	Bool_z, 	bool)
 | 
				
			||||||
 | 
					template <bool Init>
 | 
				
			||||||
 | 
					struct Bool_i {
 | 
				
			||||||
 | 
						bool value;
 | 
				
			||||||
 | 
						_INITIALIZED_SCALAR_BASE_FUNCS(Bool_i,bool,Init)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					typedef Bool_i<0> Bool_z;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// float is special, because C++ does not permit the template initalization:
 | 
				
			||||||
 | 
					struct Float_z {
 | 
				
			||||||
 | 
						float value;
 | 
				
			||||||
 | 
						_INITIALIZED_SCALAR_FUNCS(Float_z,float,0)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					struct Double_z {
 | 
				
			||||||
 | 
						double value;
 | 
				
			||||||
 | 
						_INITIALIZED_SCALAR_FUNCS(Double_z,double,0)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ItemWithValueAndWidth {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						virtual unsigned getValue() const = 0;
 | 
				
			||||||
 | 
						virtual unsigned getWidth() const = 0;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A Range Networks Field with a specified width.
 | 
				
			||||||
 | 
					// See RLCMessages.h for examples.
 | 
				
			||||||
 | 
					template <int Width=32, unsigned Init=0>
 | 
				
			||||||
 | 
					class Field_i : public ItemWithValueAndWidth
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						unsigned value;
 | 
				
			||||||
 | 
						_INITIALIZED_SCALAR_FUNCS(Field_i,unsigned,Init)
 | 
				
			||||||
 | 
						unsigned getWidth() const { return Width; }
 | 
				
			||||||
 | 
						unsigned getValue() const { return value; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Synonym for Field_i, but no way to do it.
 | 
				
			||||||
 | 
					template <int Width, unsigned Init=0>
 | 
				
			||||||
 | 
					class Field_z : public ItemWithValueAndWidth
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						unsigned value;
 | 
				
			||||||
 | 
						_INITIALIZED_SCALAR_FUNCS(Field_z,unsigned,Init)
 | 
				
			||||||
 | 
						unsigned getWidth() const { return Width; }
 | 
				
			||||||
 | 
						unsigned getValue() const { return value; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is an uninitialized field.
 | 
				
			||||||
 | 
					template <int Width=32, unsigned Init=0>
 | 
				
			||||||
 | 
					class Field : public ItemWithValueAndWidth
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						unsigned value;
 | 
				
			||||||
 | 
						_INITIALIZED_SCALAR_FUNCS(Field,unsigned,Init)
 | 
				
			||||||
 | 
						unsigned getWidth() const { return Width; }
 | 
				
			||||||
 | 
						unsigned getValue() const { return value; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A Z100Timer with an initial value specified.
 | 
				
			||||||
 | 
					//template <int Init>
 | 
				
			||||||
 | 
					//class Z100Timer_i : public GSM::Z100Timer {
 | 
				
			||||||
 | 
					//	public:
 | 
				
			||||||
 | 
					//	Z100Timer_i() : GSM::Z100Timer(Init) {}
 | 
				
			||||||
 | 
					//};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										1445
									
								
								GPRS/TBF.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1445
									
								
								GPRS/TBF.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										522
									
								
								GPRS/TBF.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										522
									
								
								GPRS/TBF.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,522 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
 | 
					* All Rights Reserved.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef TBF_H
 | 
				
			||||||
 | 
					#define TBF_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//#include <Interthread.h>
 | 
				
			||||||
 | 
					//#include <list>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "GPRSInternal.h"
 | 
				
			||||||
 | 
					#include "GPRSRLC.h"
 | 
				
			||||||
 | 
					#include "RLCHdr.h"
 | 
				
			||||||
 | 
					//#include "RList.h"
 | 
				
			||||||
 | 
					//#include "BSSG.h"
 | 
				
			||||||
 | 
					#include "Utils.h"
 | 
				
			||||||
 | 
					#include "MSInfo.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GPRS {
 | 
				
			||||||
 | 
					class MSInfo;
 | 
				
			||||||
 | 
					struct RLCMsgChannelRequestDescriptionIE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TBF - Temporary Block Flow.
 | 
				
			||||||
 | 
					// This class is responsible for doing the work of moving data to/from the MS,
 | 
				
			||||||
 | 
					// and all the directly involved messages and acknowledgements.
 | 
				
			||||||
 | 
					// The TBF class is the main class, but always includes a MsgTransaction class,
 | 
				
			||||||
 | 
					// and is itself encapsulated in either an RLCEngineUp or RLCEngineDown class.
 | 
				
			||||||
 | 
					// These did not need to be separate classes, it was just a convenient way to
 | 
				
			||||||
 | 
					// encapsulate the different functionalities clearly.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A major design decision was how to share the channel resource.
 | 
				
			||||||
 | 
					// We share the channels because an MS with a bad connection may require
 | 
				
			||||||
 | 
					// multiple block resends, and may have up to 5 second timeouts mulitple times.
 | 
				
			||||||
 | 
					// We allow only one downlink and one uplink TBF per MS.
 | 
				
			||||||
 | 
					// But all TBFs of all connected MS, both uplink and downlink, run simultaneously
 | 
				
			||||||
 | 
					// and are serviced in round-robin fashion, so short TBFs dont get hung behind hogs,
 | 
				
			||||||
 | 
					// and the system should degrade gracefully under load.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Rather than using queues for messages and data blocks, these classes generate
 | 
				
			||||||
 | 
					// all the messages and data blocks on demand.  At each RLC Block time, and for
 | 
				
			||||||
 | 
					// each GPRS channel, we look for a message or data block to send on that channel
 | 
				
			||||||
 | 
					// by calling a TBF service routine the downlink PDCH service routine.
 | 
				
			||||||
 | 
					// If the TBF has something to send on the channel at that time, it does so,
 | 
				
			||||||
 | 
					// and notifies the calling service routine that it should stop looking for a block to send.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Why do we use on-demand instead of queues?  Several reasons:
 | 
				
			||||||
 | 
					// o The data to be sent may be influenced by uplink blocks up until the time it is sent.
 | 
				
			||||||
 | 
					//   For example, we may need to resend a previous block, based on an intervening
 | 
				
			||||||
 | 
					//   acknack message from the MS.
 | 
				
			||||||
 | 
					// o If there is a block to be sent that needs an RRBP reservation and can't get it (because
 | 
				
			||||||
 | 
					//   the RRBP has an extremely limited reservation range and all may be in use), it surrenders
 | 
				
			||||||
 | 
					//   its service time to some TBF that can use the downlink without a reservation.
 | 
				
			||||||
 | 
					// o Assignment messages dont even know if they are going out on the PDCH or on the
 | 
				
			||||||
 | 
					//   CCCH queue until their transmit time arrives, at which time they consult the T3192/T3193
 | 
				
			||||||
 | 
					//   timers to find out.
 | 
				
			||||||
 | 
					// By generating all data and messages blocks on demand, we also ensure maximum utilization
 | 
				
			||||||
 | 
					// of the downlink resource regardless of the load.
 | 
				
			||||||
 | 
					// Note that TBFs may be traveling on multiple channels for multislot MS.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The TBF defines a little state machine, which marks the progress of the TBF.
 | 
				
			||||||
 | 
					// This has nothing to do with the RACH responder, which happens before a TBF ever gets started.
 | 
				
			||||||
 | 
					// The RACH repsponder grants only single-block uplink reservations, which we promptly
 | 
				
			||||||
 | 
					// forget about (except reserving that timeslot), and service only if a message
 | 
				
			||||||
 | 
					// actually arrives in the granted uplink block, at which time a TBF is created.
 | 
				
			||||||
 | 
					// And if we dont get the message, nobody ever knows;
 | 
				
			||||||
 | 
					// it is the responsibility of the MS to run a timer and try RACHing again.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Uplink for MS in PacketIdle:
 | 
				
			||||||
 | 
					// 		o MS sends RACH
 | 
				
			||||||
 | 
					//		o BTS sends ImmediateAssignment of single block on CCCH.
 | 
				
			||||||
 | 
					//			no timers started; if MS does not receive it, oh well...
 | 
				
			||||||
 | 
					//		o MS sends PacketResourceRequest on PDCH, then listens to PDCH for period T3168.
 | 
				
			||||||
 | 
					//			note that MS ignores downlink assignment during this period.
 | 
				
			||||||
 | 
					// 		label1:
 | 
				
			||||||
 | 
					//		o BTS allocates uplink TBF in state DataReadyToConnect, waits for internal resources.
 | 
				
			||||||
 | 
					//		label2:
 | 
				
			||||||
 | 
					//		o BTS sends PacketUplinkAssignment with poll for ControlAcknowledgment,
 | 
				
			||||||
 | 
					//		TBF moves to state DataWaiting1.  Note there is no timer - the ControlAcknowledgement
 | 
				
			||||||
 | 
					//		is scheduled for a particular RLCBSN.
 | 
				
			||||||
 | 
					//		TODO: If T3168 expired before reaching this state, dont bother.
 | 
				
			||||||
 | 
					//		o MS sends ControlAcknowledgement, TBF moves to DataTransmit state.
 | 
				
			||||||
 | 
					//		or: MS does not send response - if T3168 or retries not expired goto label2
 | 
				
			||||||
 | 
					//		o MS starts sending TBF data.
 | 
				
			||||||
 | 
					// Uplink for MS in PacketTransfer mode.
 | 
				
			||||||
 | 
					//		o MS sends PacketResourceRequest on PDCH. It can use an RRBP poll for this purpose.
 | 
				
			||||||
 | 
					//		I still havent figured out if we can let the MS do this during T3192 wait.
 | 
				
			||||||
 | 
					//		Unclear if MS starts T3168, but we certainly dont.
 | 
				
			||||||
 | 
					//		BTS allows multiple simultaneous uplinks, so just goto label1;
 | 
				
			||||||
 | 
					//		Not sure about special case where we are still waiting for response
 | 
				
			||||||
 | 
					//		from first PacketUplinkAssignment.
 | 
				
			||||||
 | 
					// Downlink when MS in PacketIdle:
 | 
				
			||||||
 | 
					//		This can only happen if we have heard from the MS recently so we
 | 
				
			||||||
 | 
					//		already have a TLLI to identify it.  It occurs when the SGSN sends a downlink TBF
 | 
				
			||||||
 | 
					//		but the MS has timed out and is back in PacketIdle mode, listening to CCCH.
 | 
				
			||||||
 | 
					//		This is distinct from a Paging Request which is initiated by the SGSN
 | 
				
			||||||
 | 
					//		when it is not sure if the MS is listening to this BTS or not.
 | 
				
			||||||
 | 
					//		label3:
 | 
				
			||||||
 | 
					//		o BTS sends ImmediateAssignment downlink message with poll.
 | 
				
			||||||
 | 
					//			we dont need to start timer T3141 because we poll.
 | 
				
			||||||
 | 
					//		o MS sends ControlAcknowledgement, TBF moves to DataTransmit state.
 | 
				
			||||||
 | 
					//		or: MS does not send response - if retries not expired goto label3
 | 
				
			||||||
 | 
					//		o BTS starts sending TBF data; TBF in DataTransmit state.
 | 
				
			||||||
 | 
					// Downlink for MS in PackeTransfer mode or T3192 wait period.
 | 
				
			||||||
 | 
					//		Can happen if MS is doing an uplink TBF or if MS camped on PDCH during
 | 
				
			||||||
 | 
					//		T3192 period after downlink completes.
 | 
				
			||||||
 | 
					//		label4:
 | 
				
			||||||
 | 
					//		o BTS sends PacketDownlinkAssignment message with RRBP poll.
 | 
				
			||||||
 | 
					//		o MS sends ControlAcknowledgement or other message in response to poll,
 | 
				
			||||||
 | 
					//		TBF moves to DataTransmit state.
 | 
				
			||||||
 | 
					//		or: MS does not send response - if retries not expired goto label4
 | 
				
			||||||
 | 
					//		
 | 
				
			||||||
 | 
					class TBFState
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						enum type {
 | 
				
			||||||
 | 
							Unused, // 0 Reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							DataReadyToConnect,
 | 
				
			||||||
 | 
								// Waiting to call tbf->mtAttach...() successfully.
 | 
				
			||||||
 | 
								// It is waiting on resources like TFI or USF.
 | 
				
			||||||
 | 
								// We (currently) only allow one downlink TBF per MS, although the MS can send multiple
 | 
				
			||||||
 | 
								// simultaneous uplink TBFs, and nothing we can do about that.
 | 
				
			||||||
 | 
								// When it connects (gets the resources reserved),
 | 
				
			||||||
 | 
								// it calls MsgTransaction->sendMsg, which enqueues the message
 | 
				
			||||||
 | 
								// (for either PACCH or CCCH), increments mSendTrys.
 | 
				
			||||||
 | 
								// TBF state changes to DataWaiting1.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// If the MS is in packet-idle mode, need to send the message on CCCH,
 | 
				
			||||||
 | 
								// otherwise on PACCH.  See MSMode.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							DataWaiting1,
 | 
				
			||||||
 | 
								// Waiting on MsgTransaction for MS to respond to uplink/downlink message,
 | 
				
			||||||
 | 
								// Reply is in the form of RRBP granted PacketControlAcknowledgement.
 | 
				
			||||||
 | 
								// If it is a multislot assignment that went on CCCH, we need yet
 | 
				
			||||||
 | 
								// another MsgTransaction to do the timeslot reconfigure, so go to DataWaiting2,
 | 
				
			||||||
 | 
								// otherwise go directly to DataTransmit.
 | 
				
			||||||
 | 
								// (Because the CCCH Immediate Assignment supports only single-slot.)
 | 
				
			||||||
 | 
								// Otherwise go directly to DataWaiting2.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							DataWaiting2,
 | 
				
			||||||
 | 
								// Send the Packet Timing Advance required for downlink immediate assignment on CCCH.
 | 
				
			||||||
 | 
								// Multislot TODO
 | 
				
			||||||
 | 
								// Send the Multislot assignment.
 | 
				
			||||||
 | 
								// Reply is in the form of RRBP granted PacketControlAcknowledgement.
 | 
				
			||||||
 | 
								// On reply, go to state DataTransmit.
 | 
				
			||||||
 | 
								// if gBSNNext <= mtExpectedAckBSN:
 | 
				
			||||||
 | 
								//		We are waiting for the MS to send the response.  Do nothing.
 | 
				
			||||||
 | 
								// else:
 | 
				
			||||||
 | 
								//		if the MS did not set mAckYet (by sending us a message,
 | 
				
			||||||
 | 
								//			probably Packet Control Acknowledgment):
 | 
				
			||||||
 | 
								//			if too many mSendTrys, give up, goto stateDead.
 | 
				
			||||||
 | 
								//			else resend the message and stay in this state.
 | 
				
			||||||
 | 
								//		else the MS did set mAck:
 | 
				
			||||||
 | 
								//			If the message is:
 | 
				
			||||||
 | 
								//			Packet TBF release, kill this TBF.
 | 
				
			||||||
 | 
								//			FinalAckNack: All uplink data successful, kill this TBF.
 | 
				
			||||||
 | 
								//			Otherwise it is data up/down: activate the TBF, goto stateActive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							DataReassign,
 | 
				
			||||||
 | 
								// Not currently used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							DataTransmit,
 | 
				
			||||||
 | 
								// MS and BTS are in PacketTransfer Mode
 | 
				
			||||||
 | 
								// A data transfer TBF is trying to do its thing using the RLCEngine.
 | 
				
			||||||
 | 
								// If it is a packet uplink assignment, set some timer, and we start setting USF
 | 
				
			||||||
 | 
								// in downlink blocks to allow the MS to send us uplink blocks.
 | 
				
			||||||
 | 
								// If it is a packet downlink assignment, start some timer and the serviceloop
 | 
				
			||||||
 | 
								// will start sending downlink blocks when there is nothing else to do.
 | 
				
			||||||
 | 
								// If it is packet TBF release, destroy this TBF.
 | 
				
			||||||
 | 
								// Periodically the RLCEngine will send AckNack blocks in unacknowledged mode.
 | 
				
			||||||
 | 
								// (Only the final AckNack message is sent in acknowledged mode.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							DataFinal,
 | 
				
			||||||
 | 
								// Only used for uplink TBFs now.
 | 
				
			||||||
 | 
								// We have received all the uplink data, but we are waiting
 | 
				
			||||||
 | 
								// for the MS to respond to the uplinkacknack message.
 | 
				
			||||||
 | 
								// It wont stop sending data until it gets it, so we make sure.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							TbfRelease,
 | 
				
			||||||
 | 
								// TBF is undergoing PacketTBFRelease procedure as the result of an abnormal termination.
 | 
				
			||||||
 | 
								// We send the PacketTBFRelease message and then wait in this state until we get the acknowledgement.
 | 
				
			||||||
 | 
								// When the get the response we will retry the TBF.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Finished,
 | 
				
			||||||
 | 
								// TBF is completely finished, but we keep it around until
 | 
				
			||||||
 | 
								// all its reservations expire before detaching.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Dead,
 | 
				
			||||||
 | 
								// Similar to Finished, but this TBF is dead because
 | 
				
			||||||
 | 
								// we lost contact with the MS or some timer expired.
 | 
				
			||||||
 | 
								// It is *not* detached yet, ie, it hangs on to its resources.
 | 
				
			||||||
 | 
								// We cannot reuse the resources for 5 (config parameter) seconds.
 | 
				
			||||||
 | 
								// We could also act preemptively to send TBF destruction messages, which if answered
 | 
				
			||||||
 | 
								// would allow us to get rid of the TBF, not that we care that much.
 | 
				
			||||||
 | 
							Deleting
 | 
				
			||||||
 | 
								// This state is entered via mtDetach().
 | 
				
			||||||
 | 
								// This resources for this TBF have been (or are in the midst of being) released.
 | 
				
			||||||
 | 
								// We use this ephemeral state to make deletion simpler.
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						static const char *name(int value);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const TBFState::type &type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if TBF_IMPLEMENTATION
 | 
				
			||||||
 | 
					const char *TBFState::name(int value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch ((type)value) {
 | 
				
			||||||
 | 
						case Unused: return "TBFState::Unused";
 | 
				
			||||||
 | 
						case DataReadyToConnect: return "TBFState::DataReadyToConnect";
 | 
				
			||||||
 | 
						case DataWaiting1: return "TBFState::DataWaiting1";
 | 
				
			||||||
 | 
						case DataWaiting2: return "TBFState::DataWaiting2";
 | 
				
			||||||
 | 
						case DataReassign: return "TBFState::DataReassign";
 | 
				
			||||||
 | 
						case DataTransmit: return "TBFState::DataTransmit";
 | 
				
			||||||
 | 
						//case DataStalled: return "TBFState:DataStalled";
 | 
				
			||||||
 | 
						case TbfRelease: return "TBFState::TbfRelease";
 | 
				
			||||||
 | 
						case Finished: return "TBFState::Finished";
 | 
				
			||||||
 | 
						case DataFinal: return "TBFState::DataFinal";
 | 
				
			||||||
 | 
						case Dead: return "TBFState::Dead";
 | 
				
			||||||
 | 
						case Deleting: return "TBFState::Deleting";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "TBFState undefined!";	// Makes gcc happy
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const TBFState::type &type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << TBFState::name(type);
 | 
				
			||||||
 | 
						return os;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// These are the message transaction types.
 | 
				
			||||||
 | 
					// Each TBFState only uses one type of message transaction, so we could use
 | 
				
			||||||
 | 
					// the TBFState as the message transaction type, but the code is clearly
 | 
				
			||||||
 | 
					// if the message types are seprate from the TBF states.
 | 
				
			||||||
 | 
					// When we change state there may be outstanding messages that belong to the previous state,
 | 
				
			||||||
 | 
					// especially on error conditions.
 | 
				
			||||||
 | 
					// Most of these states are used only by uplink or downlink tbfs but not both;
 | 
				
			||||||
 | 
					// the inapplicable states are never used.
 | 
				
			||||||
 | 
					enum MsgTransactionType {
 | 
				
			||||||
 | 
						MsgTransNone,		// Means nothing pending.
 | 
				
			||||||
 | 
						MsgTransAssign1,	// For ack to first assignment msg (on ccch or pacch.)
 | 
				
			||||||
 | 
						MsgTransAssign2,	// For ack to optional second assignment msg (always on pacch.)
 | 
				
			||||||
 | 
						MsgTransReassign,	// For ack to reassignment if required.
 | 
				
			||||||
 | 
						MsgTransDataFinal,	// For ack to final transmitted block. Used for both up and downlink.
 | 
				
			||||||
 | 
											// In downlink, the block with the FBI indicator. N3103 in uplink, N3105 in downlink
 | 
				
			||||||
 | 
						MsgTransTransmit,	// For acknack message during DataTransmit mode. Used for both uplink and downlink
 | 
				
			||||||
 | 
											// In downlink N3105, in uplink for the non-final-ack.
 | 
				
			||||||
 | 
						// There are two different transaction states for Assign messages because we may send two:
 | 
				
			||||||
 | 
						// if the first is on CCCH and we want multislot mode, we have to send a second
 | 
				
			||||||
 | 
						// assignment on pacch.
 | 
				
			||||||
 | 
						MsgTransTA,			// For ack to Timing Advance msg.
 | 
				
			||||||
 | 
						MsgTransTbfRelease,	// For ack to TbfRelease msg.
 | 
				
			||||||
 | 
						MsgTransMax			// Not a transaction - indicates number of Transaction Types.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This facility is used only for messages belonging to a TBF, which includes RRBP reservations
 | 
				
			||||||
 | 
					// and the Poll reservation for an assignment message sent on AGCH.
 | 
				
			||||||
 | 
					// It is not used for RACH polls, since we dont know who they belong to, and would be
 | 
				
			||||||
 | 
					// meaningless anyway because they do not correspond to a TBF somewhere changing states.
 | 
				
			||||||
 | 
					// We only track one outstanding reservation at a time for each TBF.
 | 
				
			||||||
 | 
					// Generally we send a message and then wait for a PacketControlAcknowledgement.
 | 
				
			||||||
 | 
					class MsgTransaction
 | 
				
			||||||
 | 
					{	private:
 | 
				
			||||||
 | 
						BitSet mtMsgAckBits; 		// Type of acks received.
 | 
				
			||||||
 | 
						BitSet mtMsgExpectedBits;	// The types messages we are waiting for.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						RLCBSN_t mtExpectedAckBSN[MsgTransMax];	// When we expect to get the acknowledgement from the MS.
 | 
				
			||||||
 | 
						// This is supposed to be in the MS, but I moved it to the TBF because
 | 
				
			||||||
 | 
						// some TBFs become non-responsive individually while others are still moving,
 | 
				
			||||||
 | 
						// so we dont want to kill off the MS if a single TBF dies.
 | 
				
			||||||
 | 
						UInt_z mtN3105;		// Counts RRBP data reservations that MS ignores.
 | 
				
			||||||
 | 
						UInt_z mtN3103;		// Counts final downlinkacknacks RRBP that MS ignores.
 | 
				
			||||||
 | 
						UInt_z mtAssignCounter;	// Count Assignment Messages.
 | 
				
			||||||
 | 
						UInt_z mtReassignCounter;	// Count reassigment messages.
 | 
				
			||||||
 | 
						UInt_z mtCcchAssignCounter;	// Number of assignments sent on CCCH.
 | 
				
			||||||
 | 
						UInt_z mtTbfReleaseCounter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Wait for the next message.
 | 
				
			||||||
 | 
						void mtMsgSetWait(MsgTransactionType mttype) {
 | 
				
			||||||
 | 
							devassert(mtExpectedAckBSN[mttype].valid());
 | 
				
			||||||
 | 
							mtMsgAckBits.clearBit(mttype);
 | 
				
			||||||
 | 
							mtMsgExpectedBits.setBit(mttype);
 | 
				
			||||||
 | 
							GPRSLOG(4) << "mtMsgSetWait"<<LOGVAR(mttype)<<LOGVAR(mtMsgExpectedBits);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void text(std::ostream &os) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set the BSN when the TBF is expecting a message, but note that the
 | 
				
			||||||
 | 
						// MS may send uplink data blocks before this time.
 | 
				
			||||||
 | 
						void mtSetAckExpected(RLCBSN_t when, MsgTransactionType mttype) {
 | 
				
			||||||
 | 
							GPRSLOG(4) << "mtSetAckExpected"<<LOGVAR(when)<<LOGVAR(mttype);
 | 
				
			||||||
 | 
							mtExpectedAckBSN[mttype] = when;
 | 
				
			||||||
 | 
							mtMsgSetWait(mttype);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Called to indicate that a message for this TBF arrived.
 | 
				
			||||||
 | 
						void mtRecvAck(MsgTransactionType mttype) {
 | 
				
			||||||
 | 
							GPRSLOG(4) << "mtRecvAck"<<LOGVAR(mttype)<<LOGVAR(mtMsgExpectedBits);
 | 
				
			||||||
 | 
							mtMsgAckBits.setBit(mttype);
 | 
				
			||||||
 | 
							mtMsgExpectedBits.clearBit(mttype);
 | 
				
			||||||
 | 
							mtN3105 = 0;	// Not all of these are for mtN3105, but doesnt hurt to always reset it.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool mtGotAck(MsgTransactionType mttype, bool clear) {
 | 
				
			||||||
 | 
							bool result = mtMsgAckBits.isSet(mttype);
 | 
				
			||||||
 | 
							GPRSLOG(4) << "mtGotAck "<<(result?"yes":"no")<<LOGVAR(mttype)<<LOGVAR(mtMsgExpectedBits);
 | 
				
			||||||
 | 
							if (result && clear) {
 | 
				
			||||||
 | 
								mtMsgExpectedBits.clearBit(mttype);
 | 
				
			||||||
 | 
								mtExpectedAckBSN[mttype] = -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return result;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Is any reservation currently outstanding?
 | 
				
			||||||
 | 
						bool mtMsgPending();
 | 
				
			||||||
 | 
						bool mtMsgPending(MsgTransactionType mttype);	// Is this msg still outstanding?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//MsgTransaction() { }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// We will allocate a TBF both for data uplink/downlink transfers, and for single
 | 
				
			||||||
 | 
					// messages to the MS which require an ACK, even though strictly speaking that is not a TBF.
 | 
				
			||||||
 | 
					// A TBF is not used for a single block packet [uplink] access.
 | 
				
			||||||
 | 
					class TBF :
 | 
				
			||||||
 | 
						public MsgTransaction		// Sends messages reliably.
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						private:
 | 
				
			||||||
 | 
						TBFState::type mtState;		// State of this TBF.  Dont set this directly, call setState().
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						unsigned mtDebugId;
 | 
				
			||||||
 | 
						MSInfo *mtMS;
 | 
				
			||||||
 | 
						RLCDirType mtDir;
 | 
				
			||||||
 | 
						Int_i<-1> mtTFI;				// Assigned TFI, or -1.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// There is some question about whether we need ACKs (PacketControlAcknowledgement) at all.
 | 
				
			||||||
 | 
						// For packet downlink, without the ACK we would not notice until the MS
 | 
				
			||||||
 | 
						// did not respond to RRBPs long enough that we figured it out.
 | 
				
			||||||
 | 
						// For packet uplink, without the ACK we could look at responses from this MS,
 | 
				
			||||||
 | 
						// and if they are bad for awhile, restart this, but we cant really be sure
 | 
				
			||||||
 | 
						// that the old TFI is released first.
 | 
				
			||||||
 | 
						// So the ACKs make the state machine much safer in both cases.
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Bool_z mtUnAckMode;			// a.k.a. RLCMode.  If 1, send in unacknowledged mode.
 | 
				
			||||||
 | 
													// Note: as of 6-2012 unacknowledged mode is not implemented.
 | 
				
			||||||
 | 
						Bool_z mtAttached;			// Flag for mtAttach()/mtDetach() status.
 | 
				
			||||||
 | 
						Bool_z mtAssignmentOnCCCH;	// Set if assignment was sent on CCCH.
 | 
				
			||||||
 | 
						Bool_z mtPerformReassign;	// Reissue an uplink TBF assignment to 'change priority' geesh.
 | 
				
			||||||
 | 
						//Bool_z mtIsRetry;			// Is this our second attempt to send this TBF?
 | 
				
			||||||
 | 
						Bool_z mtTASent;			// For debugging, true if we sent an extra Timing Adv message.
 | 
				
			||||||
 | 
						GprsTimer mtDeadTime;		// When a dead TBF can finally release resources.
 | 
				
			||||||
 | 
						MSStopCause::type mtCause;	// Why the TBF died.
 | 
				
			||||||
 | 
						//Float_z mtLowRSSI;			// Save the lowest RSSI seen for reporting purposes.
 | 
				
			||||||
 | 
						uint32_t mtTlli;			// The tlli of an uplink TBF.  It is != mtMS->msTlli only
 | 
				
			||||||
 | 
													// in the special case of a second AttachRequest occuring
 | 
				
			||||||
 | 
													// after the TLLI reassignment procedure.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Persistence timers, used for both uplink and downlink.
 | 
				
			||||||
 | 
						//GprsTimer mtKeepAliveTimer;	// Time to next keep alive.
 | 
				
			||||||
 | 
						GprsTimer mtPersistTimer;	// How long TBF persists while idle.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::string mtDescription;	// For error reporting, what was in this TBF?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Timeval mtStartTime;		// For reporting.  default init is to current time.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Statistics for flow control, needed only in downlink direction.
 | 
				
			||||||
 | 
						//TODO: RLCBSN_t mtStartTime;	// When we started sending it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TBF(MSInfo *wms, RLCDirType wdir);
 | 
				
			||||||
 | 
						// The virtual keyword tells C++ to call the derived destructors too.
 | 
				
			||||||
 | 
						// Otherwise it may not.  It is foo bar.
 | 
				
			||||||
 | 
						virtual ~TBF();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Can this TBF use the specified downlink?
 | 
				
			||||||
 | 
						// It depends on the MS allocated channels.
 | 
				
			||||||
 | 
						bool canUseDownlink(PDCHL1Downlink*down) { return mtMS->canUseDownlink(down); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Can this TBF use the specified uplink?
 | 
				
			||||||
 | 
						bool canUseUplink(PDCHL1Uplink*up) { return mtMS->canUseUplink(up); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void mtSetState(TBFState::type wstate);
 | 
				
			||||||
 | 
						TBFState::type mtGetState() { return mtState; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// These are the states that count toward an MS RROperatingMode being
 | 
				
			||||||
 | 
						// in PacketTransfer mode instead of PacketIdle mode.
 | 
				
			||||||
 | 
						// We leave DataWaiting1 out.
 | 
				
			||||||
 | 
						// First of all, we call this when we are doing a sendAssignment
 | 
				
			||||||
 | 
						// and the tbf on whose behalf we are inquiring is in DataWaiting1,
 | 
				
			||||||
 | 
						// so we get stuck here.
 | 
				
			||||||
 | 
						// Second of all, we dont really know if the MS is listening
 | 
				
			||||||
 | 
						// or not in DataWaiting1 mode because we have not heard back from
 | 
				
			||||||
 | 
						// it after the sendAssignment.  Maybe we need yet another mode.
 | 
				
			||||||
 | 
						bool isTransmitting() {
 | 
				
			||||||
 | 
							switch (mtState) {
 | 
				
			||||||
 | 
								case TBFState::DataWaiting2:
 | 
				
			||||||
 | 
								case TBFState::DataTransmit:
 | 
				
			||||||
 | 
								case TBFState::DataReassign:
 | 
				
			||||||
 | 
								//case TBFState::DataStalled:
 | 
				
			||||||
 | 
								case TBFState::DataFinal:
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								default:
 | 
				
			||||||
 | 
									return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Used to determine if there is already a TBF running so we should not start another.
 | 
				
			||||||
 | 
						// It is very important to include DataReadyToConnect because those indicate
 | 
				
			||||||
 | 
						// that an assignment is already in progress, and we dont want to start another.
 | 
				
			||||||
 | 
						bool isActive() {	// The TBF is trying to do something.
 | 
				
			||||||
 | 
							switch (mtState) {
 | 
				
			||||||
 | 
								case TBFState::DataReadyToConnect:
 | 
				
			||||||
 | 
								case TBFState::DataWaiting1:
 | 
				
			||||||
 | 
								case TBFState::DataWaiting2:
 | 
				
			||||||
 | 
								case TBFState::DataTransmit:
 | 
				
			||||||
 | 
								case TBFState::DataReassign:
 | 
				
			||||||
 | 
								//case TBFState::DataStalled:
 | 
				
			||||||
 | 
								case TBFState::DataFinal:
 | 
				
			||||||
 | 
								case TBFState::TbfRelease:	// Counts until it is acknowledged as released.
 | 
				
			||||||
 | 
									return true;
 | 
				
			||||||
 | 
								case TBFState::Dead:
 | 
				
			||||||
 | 
								case TBFState::Finished:	// TBF may still wait for res, but we dont count it.
 | 
				
			||||||
 | 
								case TBFState::Deleting:
 | 
				
			||||||
 | 
								case TBFState::Unused:
 | 
				
			||||||
 | 
									return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false;	// Unreached, but makes gcc happy.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool mtServiceDownlink(PDCHL1Downlink *down);
 | 
				
			||||||
 | 
						bool mtSendTbfRelease(PDCHL1Downlink *down);
 | 
				
			||||||
 | 
						void mtServiceUnattached();
 | 
				
			||||||
 | 
						void mtCancel(MSStopCause::type cause, TbfCancelMode release);
 | 
				
			||||||
 | 
						void mtCancel(MsgTransactionType cause, TbfCancelMode release) {
 | 
				
			||||||
 | 
							// Canceled due to expiry of MsgTransactionType timer.
 | 
				
			||||||
 | 
							mtCancel((MSStopCause::type) cause, release);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void mtRetry();
 | 
				
			||||||
 | 
						void mtFinishSuccess();
 | 
				
			||||||
 | 
						std::string tbfDump(bool verbose) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// For downlink we specify the channelcoding in the qbits of every block,
 | 
				
			||||||
 | 
						// so we can change channelcoding dynamically between CS-1 and CS-4.
 | 
				
			||||||
 | 
						// For uplink, the BTS specifies the encoding the MS will use in both
 | 
				
			||||||
 | 
						// the uplink assignment and in every uplinkacknack message.
 | 
				
			||||||
 | 
						// The ChannelCodingMax is used for retries to throttle back to a more secure codec.
 | 
				
			||||||
 | 
						ChannelCodingType mtChannelCodingMax;	// The max channel coding (0-3) allowed for this TBF.
 | 
				
			||||||
 | 
						ChannelCodingType mtCCMin, mtCCMax;		// Saved for reporting purposes.
 | 
				
			||||||
 | 
						ChannelCodingType mtChannelCoding() const; 	// Return 0 - 3 for CS-1 or CS-4 for data transfer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Note that this TBF is a base class of either an RLCEngineUp or RLCEngineDown,
 | 
				
			||||||
 | 
						// depending on the TBF direction.
 | 
				
			||||||
 | 
						// These functions are defined in RLCEngineUp and RLCEngineDown:
 | 
				
			||||||
 | 
						virtual bool engineService(PDCHL1Downlink *down) = 0;
 | 
				
			||||||
 | 
						virtual unsigned engineDownPDUSize() const {return 0;}
 | 
				
			||||||
 | 
						virtual void engineRecvDataBlock(RLCUplinkDataBlock* block, int tn) {}
 | 
				
			||||||
 | 
						virtual void engineRecvAckNack(const RLCMsgPacketDownlinkAckNack *msg) {}
 | 
				
			||||||
 | 
						virtual float engineDesiredUtilization() = 0;
 | 
				
			||||||
 | 
						virtual void engineGetStats(unsigned *pSlotsTotal, unsigned *pSlotsUsed, unsigned *pGrants) const = 0;
 | 
				
			||||||
 | 
						virtual int engineGetBytesPending() = 0;
 | 
				
			||||||
 | 
						virtual void engineDump(std::ostream &os) const = 0;
 | 
				
			||||||
 | 
						virtual bool stalled() const = 0;
 | 
				
			||||||
 | 
						//RLCDownEngine const* getDownEngine() const;	// Cant use this to change anything in RLCDownEngine
 | 
				
			||||||
 | 
						RLCDownEngine * getDownEngine();
 | 
				
			||||||
 | 
						static TBF *newUpTBF(MSInfo *ms,RLCMsgChannelRequestDescriptionIE &mCRD, uint32_t tlli, bool onRach);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// In a multislot configuration one of the slots is the primary slots and is
 | 
				
			||||||
 | 
						// used exclusively for all messages, and all other channels are used only for data.
 | 
				
			||||||
 | 
						// The primary slot must have both uplink and downlink timeslots assigned,
 | 
				
			||||||
 | 
						// and is currently identical to msPacch.  It is also the first channel in the
 | 
				
			||||||
 | 
						// downlink list.
 | 
				
			||||||
 | 
						bool isPrimary(PDCHL1Downlink *down);
 | 
				
			||||||
 | 
						bool wantsMultislot();	// Does this tbf want to be multislot?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Attach to a channel.  Return true if we succeeded.
 | 
				
			||||||
 | 
						bool mtAttach();
 | 
				
			||||||
 | 
						bool mtNonResponsive();		// Is this TBF non responsive?
 | 
				
			||||||
 | 
						// Detach from the channel.  Release our resources.
 | 
				
			||||||
 | 
						void mtDetach();	// Internal use only - call mtCancel or mtFinishSuccess
 | 
				
			||||||
 | 
						void mtDeReattach();	// Internal use only - call mtCancel or mtFinishSuccess
 | 
				
			||||||
 | 
						// If forever, do not move to expired list, just kill it.
 | 
				
			||||||
 | 
						void mtDelete(bool forever=0);	// Internal use only - call mtCancel or mtFinishSuccess
 | 
				
			||||||
 | 
						//void setRadData(RadData &wRD);
 | 
				
			||||||
 | 
						//void talkedUp() { mtMS->talkedUp(); }
 | 
				
			||||||
 | 
						void talkedDown() { mtMS->talkedDown(); }
 | 
				
			||||||
 | 
						uint32_t mtGetTlli();
 | 
				
			||||||
 | 
						const char *tbfid(bool verbose);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private:
 | 
				
			||||||
 | 
						bool mtAllocateTFI();
 | 
				
			||||||
 | 
						bool mtAllocateUSF();
 | 
				
			||||||
 | 
						void mtFreeTFI();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					extern unsigned gTBFDebugId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const TBF*tbf);
 | 
				
			||||||
 | 
					#if TBF_IMPLEMENTATION
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const TBF*tbf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (tbf) {
 | 
				
			||||||
 | 
							os << " TBF#" << tbf->mtDebugId <<" ";
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							os << " TBF(null ptr) ";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return os;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern bool sendAssignment(PDCHL1FEC *pacch,TBF *tbf, std::ostream *os);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}	// namespace GPRS
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										138
									
								
								GPRS/makefile.pat
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								GPRS/makefile.pat
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
				
			|||||||
 | 
					HDR=BSSG.h BSSGMessages.h ByteVector.h FEC.h GPRSExport.h GPRSInternal.h \
 | 
				
			||||||
 | 
						GPRSTDMA.h MAC.h MsgBase.h GPRSRLC.h RLCEngine.h RLCHdr.h RLCMessages.h RList.h \
 | 
				
			||||||
 | 
						ScalarTypes.h TBF.h MSInfo.h
 | 
				
			||||||
 | 
					SRC1= ByteVector.cpp \
 | 
				
			||||||
 | 
						MSInfo.cpp TBF.cpp FEC.cpp RLCEngine.cpp RLC.cpp MAC.cpp \
 | 
				
			||||||
 | 
						BSSG.cpp BSSGMessages.cpp GPRSCLI.cpp \
 | 
				
			||||||
 | 
						RLCMessages.cpp RLCEngine.cpp MsgBase.cpp
 | 
				
			||||||
 | 
					# Compile the most recently modified ones first.
 | 
				
			||||||
 | 
					SRC=$(shell ls -t $(SRC1))
 | 
				
			||||||
 | 
					#CSRC= iputils.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INCLUDE= -I. -I.. -I../SGSNGGSN -I../CommonLibs -I../Control -I../GPRS -I../GSM -I../SIP -I../SMS -I../TRXManager -I../Globals -I../CLI -I../HLR -I../SR -I../sqlite3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ODIR=.libs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GPRSOBJ= $(SRC:%.cpp=$(ODIR)/%.o)
 | 
				
			||||||
 | 
					COBJ= $(CSRC:%.c=$(ODIR)/%.o)
 | 
				
			||||||
 | 
					OBJ= $(COBJ) $(GPRSOBJ)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					default: a
 | 
				
			||||||
 | 
					#default: Makefile.am a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 'all' is the target made by ../Makefile
 | 
				
			||||||
 | 
					all:
 | 
				
			||||||
 | 
						make -f Makefile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					more:
 | 
				
			||||||
 | 
						(clear && make lib && cd ../apps && make) 2>&1 | more
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					a: .ALWAYS
 | 
				
			||||||
 | 
						#make lib && (cd ..; make)
 | 
				
			||||||
 | 
						make -f Makefile && (cd ..; make)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					g: $(GGSNOBJ)
 | 
				
			||||||
 | 
					g2: miniggsn.o iputils.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# The at-sign makes it not echo the program, so you can do: make sql > gprs.sql
 | 
				
			||||||
 | 
					gprs.sql: .ALWAYS
 | 
				
			||||||
 | 
						@awk '/BEGINCONFIG/,/ENDCONFIG/ { \
 | 
				
			||||||
 | 
							if (/BEGINCONFIG/||/ENDCONFIG/) next; \
 | 
				
			||||||
 | 
							sub("^[^/]*//",""); \
 | 
				
			||||||
 | 
							commas=$$0; gsub("[^,]*","",commas); \
 | 
				
			||||||
 | 
							if (length(commas) < 4) print "syntax error in",FILENAME,":",$$0 >"/dev/tty"; \
 | 
				
			||||||
 | 
							print "INSERT INTO \"CONFIG\" VALUES(" $$0 ");" \
 | 
				
			||||||
 | 
							}' *.cpp > gprs.sql
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test1: test1.cpp Makefile libGPRS.a
 | 
				
			||||||
 | 
						g++ $(INCLUDE) -o test1 test1.cpp libGPRS.a ../CommonLibs/.libs/libcommon.a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					crc: crc24.c
 | 
				
			||||||
 | 
						gcc -o crc crc24.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test2: test1.cpp Makefile libGPRS.a
 | 
				
			||||||
 | 
						g++ $(INCLUDE) -o test1 test1.cpp libGPRS.a ../CommonLibs/.libs/libcommon.a ../GSM/.libs/libGSM.a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					testbv: ByteVector.cpp ByteVector.h makefile
 | 
				
			||||||
 | 
						g++ $(INCLUDE) -g -o testbv -DTEST=1 ByteVector.cpp ../CommonLibs/.libs/libcommon.a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lib: $(OBJ)
 | 
				
			||||||
 | 
						ar cru $(ODIR)/libGPRS.a $(OBJ)
 | 
				
			||||||
 | 
						touch libGPRS.la
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#.cpp.o:
 | 
				
			||||||
 | 
					$(ODIR)/%.o: %.cpp
 | 
				
			||||||
 | 
						-mkdir $(ODIR) 2>/dev/null
 | 
				
			||||||
 | 
						g++ -O0 -DHAVE_CONFIG_H $(INCLUDE)  -Wall -g -c -o $(ODIR)/$*.o $*.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$(ODIR)/%.o: %.c
 | 
				
			||||||
 | 
						-mkdir $(ODIR) 2>/dev/null
 | 
				
			||||||
 | 
						g++ -O0 -DHAVE_CONFIG_H $(INCLUDE) -Wall -g -c -o $(ODIR)/$*.o $*.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# g++ -DHAVE_CONFIG_H -I. -I. -I..  -I../CommonLibs -I../Control -I../GPRS -I../GSM -I../SIP -I../SMS -I../TRXManager -I../Globals -I../CLI -I../HLR -I../SR -I../sqlite3  -Wall -O3 -g -O2 -MT RadioResource.lo -MD -MP -MF ".deps/RadioResource.Tpo" -c -o RadioResource.lo RadioResource.cpp; \
 | 
				
			||||||
 | 
							        then mv -f ".deps/RadioResource.Tpo" ".deps/RadioResource.Plo"; else rm -f ".deps/RadioResource.Tpo"; exit 1; fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$(OBJ):$(HDR)
 | 
				
			||||||
 | 
					$(ODIR)/miniggsn.o $(ODIR)/iputils.o: miniggsn.h Ggsn.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					svnadd:
 | 
				
			||||||
 | 
						svn add $(HDR) $(SRC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					clean:
 | 
				
			||||||
 | 
						/bin/rm $(ODIR)/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					commit:
 | 
				
			||||||
 | 
						svn commit $(HDR) $(SRC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pinghttp: pinghttp.c
 | 
				
			||||||
 | 
						gcc -DSTANDALONE=1 -o pinghttp pinghttp.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Need a short name for DOS file system.
 | 
				
			||||||
 | 
					SMALLFILES= GPRS/*.[hc]* GSM/*.[hc]* CLI/*.[hc]* \
 | 
				
			||||||
 | 
						CommonLibs/*.[hc]* Control/*.[hc]* TRXManager/*.[hc]*
 | 
				
			||||||
 | 
					small:
 | 
				
			||||||
 | 
						cd .. && tar -czvf GPRS_backup_`date +%m-%d`.tgz $(SMALLFILES) \
 | 
				
			||||||
 | 
							--no-recursion
 | 
				
			||||||
 | 
					backup:
 | 
				
			||||||
 | 
						cd .. && tar -czvf GPRS_full_`date +%m-%d`.tgz */* \
 | 
				
			||||||
 | 
							--exclude .svn --exclude .deps --exclude .libs --exclude 'sqlite*' \
 | 
				
			||||||
 | 
							--exclude '*o' --exclude '*.asn' --exclude '*cache*' --exclude 'Trans*' \
 | 
				
			||||||
 | 
							--exclude OpenBTS --exclude *Test --exclude bk*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ctags tags: .ALWAYS
 | 
				
			||||||
 | 
						cd ..; sh PAT.ctags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ALWAYS:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Evidently the makefile autogenerator doesnt work, because David complains
 | 
				
			||||||
 | 
					# every time he tries to make this directory.  So lets just write out the
 | 
				
			||||||
 | 
					# # automake makefile generator file to try to make him happy.
 | 
				
			||||||
 | 
					# This is pretty dumb, making an auto-make makefile from a makefile.
 | 
				
			||||||
 | 
					# Rebuild it whenever this makefile changes:
 | 
				
			||||||
 | 
					Makefile.am: makefile
 | 
				
			||||||
 | 
						@: Start with the copyright:
 | 
				
			||||||
 | 
						@sed -n '1,/^$$/p' < ../Makefile.am > Makefile.am
 | 
				
			||||||
 | 
						@awk >> Makefile.am '\
 | 
				
			||||||
 | 
							BEGIN { \
 | 
				
			||||||
 | 
							print "include $$(top_srcdir)/Makefile.common\n"; \
 | 
				
			||||||
 | 
							print "AM_CPPFLAGS = $$(STD_DEFINES_AND_INCLUDES)\n"; \
 | 
				
			||||||
 | 
							print "#AM_CXXFLAGS = -O2 -g\n"; \
 | 
				
			||||||
 | 
							print "noinst_LTLIBRARIES = libGPRS.la\n"; \
 | 
				
			||||||
 | 
							src="$(SRC)"; gsub(" +"," \\\n\t",src); \
 | 
				
			||||||
 | 
							hdr="$(HDR)"; gsub(" +"," \\\n\t",hdr); \
 | 
				
			||||||
 | 
							print "\nlibGPRS_la_SOURCES = \\"; print "\t" src; \
 | 
				
			||||||
 | 
							print "\nnoinst_HEADERS = \\"; print "\t" hdr; \
 | 
				
			||||||
 | 
							}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#==============================================
 | 
				
			||||||
 | 
					# These are the lines that modified the existing file, but I decided to just overwrite:
 | 
				
			||||||
 | 
					# /libGPRS_la_SOURCES/,/^$$/ { next }
 | 
				
			||||||
 | 
					# /noinst_HEADERS/,/^$$/ { next }
 | 
				
			||||||
 | 
					#{print}
 | 
				
			||||||
							
								
								
									
										60
									
								
								GPRS/makefile.tests
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								GPRS/makefile.tests
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					HDR=BSSG.h BSSGMessages.h ByteVector.h FEC.h GPRSExport.h GPRSInternal.h \
 | 
				
			||||||
 | 
						GPRSTDMA.h MAC.h MsgBase.h RLCEngine.h RLCHdr.h RLCMessages.h RList.h \
 | 
				
			||||||
 | 
						ScalarTypes.h TBF.h Transfer.h Utils.h
 | 
				
			||||||
 | 
					#HDR= Utils.h BSSG.h BSSGMessages.h FEC.h GPRSExport.h GPRSInternal.h \
 | 
				
			||||||
 | 
					#	GPRSTDMA.h MAC.h RLCEngine.h RLCHdr.h RLCMessages.h TBF.h Transfer.h Utils.h BaseTypes.h
 | 
				
			||||||
 | 
					SRC= GPRSConfig.cpp TBF.cpp RLCMessages.cpp BSSG.cpp BSSGMessages.cpp ByteVector.cpp FEC.cpp MAC.cpp MsgBase.cpp \
 | 
				
			||||||
 | 
						RLCEngine.cpp Transfer.cpp Utils.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INCLUDE= -I. -I..  -I../CommonLibs -I../Control -I../GPRS -I../GSM -I../SIP -I../SMS -I../TRXManager -I../Globals -I../CLI -I../HLR -I../SR -I../sqlite3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ODIR=.libs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OBJ= $(SRC:%.cpp=$(ODIR)/%.o)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					all: lib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test1: test1.cpp Makefile libGPRS.a
 | 
				
			||||||
 | 
						g++ $(INCLUDE) -o test1 test1.cpp libGPRS.a ../CommonLibs/.libs/libcommon.a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test2: test1.cpp Makefile libGPRS.a
 | 
				
			||||||
 | 
						g++ $(INCLUDE) -o test1 test1.cpp libGPRS.a ../CommonLibs/.libs/libcommon.a ../GSM/.libs/libGSM.a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lib: $(OBJ)
 | 
				
			||||||
 | 
						ar cru $(ODIR)/libGPRS.a $(OBJ)
 | 
				
			||||||
 | 
						touch libGPRS.la
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#.cpp.o:
 | 
				
			||||||
 | 
					$(ODIR)/%.o: %.cpp
 | 
				
			||||||
 | 
						-mkdir o 2>/dev/null
 | 
				
			||||||
 | 
						g++ -DHAVE_CONFIG_H -I. -I. -I..  -I../CommonLibs -I../Control -I../GPRS -I../GSM -I../SIP -I../SMS -I../TRXManager -I../Globals -I../CLI -I../HLR -I../SR -I../sqlite3  -Wall -g -c -o $(ODIR)/$*.o $*.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# g++ -DHAVE_CONFIG_H -I. -I. -I..  -I../CommonLibs -I../Control -I../GPRS -I../GSM -I../SIP -I../SMS -I../TRXManager -I../Globals -I../CLI -I../HLR -I../SR -I../sqlite3  -Wall -O3 -g -O2 -MT RadioResource.lo -MD -MP -MF ".deps/RadioResource.Tpo" -c -o RadioResource.lo RadioResource.cpp; \
 | 
				
			||||||
 | 
							        then mv -f ".deps/RadioResource.Tpo" ".deps/RadioResource.Plo"; else rm -f ".deps/RadioResource.Tpo"; exit 1; fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$(OBJ):$(HDR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					svnadd:
 | 
				
			||||||
 | 
						svn add $(HDR) $(SRC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					clean:
 | 
				
			||||||
 | 
						/bin/rm $(ODIR)/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					commit:
 | 
				
			||||||
 | 
						svn commit $(HDR) $(SRC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ping: ping.o
 | 
				
			||||||
 | 
						gcc -o ping ping.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pinghttp: pinghttp.c iputils.c makefile.tests
 | 
				
			||||||
 | 
						gcc -DSTANDALONE=1 -o pinghttp $(CFLAGS) pinghttp.c iputils.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tags: .ALWAYS
 | 
				
			||||||
 | 
						cd ..; sh PAT.ctags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.ALWAYS:
 | 
				
			||||||
							
								
								
									
										151
									
								
								GPRS/notes.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								GPRS/notes.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
				
			|||||||
 | 
					7-13, uplink.persist=3000, 1 channel: speed 11.7 latency 1.8 
 | 
				
			||||||
 | 
						uplink.persist=3000 3-down/2-up: speed 46.2 latency 1.3
 | 
				
			||||||
 | 
						uplink.persist=3000 4-down/1-up: speed 41.5 latency 1.4
 | 
				
			||||||
 | 
						again uplink.persist=3000 4-down/1-up: speed 27.8.5 latency 1.1
 | 
				
			||||||
 | 
						again: uplink.persist=3000 4-down/1-up: speed 38.1 latency 1.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Fri Jul 13 04:52:16 PDT 2012
 | 
				
			||||||
 | 
					7-11: Multitech modem 4-up/1-down.  This is after fixing the downlink USF bug.
 | 
				
			||||||
 | 
						These are with T3192Code=2 (1500ms)
 | 
				
			||||||
 | 
						downlink speed from mobilespeedtest.com: 22Kbps
 | 
				
			||||||
 | 
						In these there is downlink traffic on the same shared PACCH:
 | 
				
			||||||
 | 
							uplink ftp: total 1023652 bytes in 324s = 25Kbps
 | 
				
			||||||
 | 
							Raw (from looking at a single tbf, 3062B in 0.9s) 27Kbps
 | 
				
			||||||
 | 
							U412 96.5-93 4624b = 11Kbps.  Yuck.
 | 
				
			||||||
 | 
							uplink ftp 50025 in 15s = 26.7Kbps.
 | 
				
			||||||
 | 
						for 3-up/2-down: uplink ftp 50025 in 30s = 13Kbps.
 | 
				
			||||||
 | 
						for 2-up/2-down: uplink ftp 50025 in 18s. second test: 18s = 22.2Kbps
 | 
				
			||||||
 | 
						I checked the log, and we are definitely getting blocks on all 4 channels.
 | 
				
			||||||
 | 
						For the 3/2 test that was slow, it looks like the phone is having to RACH in to
 | 
				
			||||||
 | 
						start nearly every uplink.  For 4/1 the downlinks are so slow that they are still
 | 
				
			||||||
 | 
						running when the uplink ends.
 | 
				
			||||||
 | 
						These are with T3192Code=0 (500ms)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					7-5: 4 channel multislot, T3192Code=0, got 32.8 Kbps.
 | 
				
			||||||
 | 
					7-5: 4 channel multislot, T3192Code=3, got 25, and cause=105 errors (TBF killed by RACH).
 | 
				
			||||||
 | 
						I suspect setting T3192 too high can force the MS to RACH for an uplink TBF.
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					7-4-2012:
 | 
				
			||||||
 | 
						Tried ftp.
 | 
				
			||||||
 | 
						download 6:40
 | 
				
			||||||
 | 
						upload: 20 seconds "to calculate the time"
 | 
				
			||||||
 | 
						then sent a 0 byte file.  Gotta love that.
 | 
				
			||||||
 | 
					6-27-2012:
 | 
				
			||||||
 | 
						With new channel allocator that guarantees 3-down/2-up assignments:
 | 
				
			||||||
 | 
						mobilespeedtest.com: 25Kbps, latency 2.2
 | 
				
			||||||
 | 
						Had to set GPRS.ChannelCodingControl.RSSI to -50 to get it though
 | 
				
			||||||
 | 
						Setting GPRS.Codecs.Uplink,Downlink to 4 got 25.6Kbps
 | 
				
			||||||
 | 
						Setting T3192Code to 3 gives 29.4Kbps
 | 
				
			||||||
 | 
					6-21-2012:
 | 
				
			||||||
 | 
						iphone 001690000000002
 | 
				
			||||||
 | 
						iphone speedtest.com 20.6KBps, latency 2.6s 100K in 39s.
 | 
				
			||||||
 | 
						The phone sent XID type 5 (set U and UI frame size) to 1520
 | 
				
			||||||
 | 
						and XID type 11 (L3 params): LLC:LLC XID xidtype=11 xidlen=3 value=256;  LLC:LLC Sending XID command:03FB1605F02F000100
 | 
				
			||||||
 | 
						After power cycling the bts,
 | 
				
			||||||
 | 
						this phone does not accept the DetachRequest or PdpContextReject ImplicitlyDetached thing.
 | 
				
			||||||
 | 
						if you manually request the operator again with the phone, it sends a RoutingAreaRequest with a new TLLI
 | 
				
			||||||
 | 
						and after receiving the RoutingAreaUpdateReject does an immediate new attach.
 | 
				
			||||||
 | 
						--
 | 
				
			||||||
 | 
						This phone lost connection temporarily in log: iphone/1 around 07:54:46.
 | 
				
			||||||
 | 
						An uplink TBF#127 was (correctly) cancelled, but I subsequently saw many 'received uplink data block after expiration'
 | 
				
			||||||
 | 
						How is that possible?
 | 
				
			||||||
 | 
						--
 | 
				
			||||||
 | 
						After power cycling the bts, the carrier selection reported ATT and T-MOBILE only; tried 3 times.
 | 
				
			||||||
 | 
						It did not do anything to gprs, so I think this is a normal registration failure.
 | 
				
			||||||
 | 
						Then when power cycled, it did an immediate gprs-attach.
 | 
				
			||||||
 | 
						When I did the "Carrier Selection" again, it showed only 1001, not ATT and T-MOBILE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Samsung GT-I5500 Baseband version: I5500NEJP1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Put gprs tests on the wiki.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					6-18-2012: Davids galaxy error log:
 | 
				
			||||||
 | 
						res 303714: TBF#170 uplinkacknack - received
 | 
				
			||||||
 | 
						res 303715: TBF#171 downlinkassignment - unanswered
 | 
				
			||||||
 | 
						res 303735: TBF#171 immediate downlinkassignment - received
 | 
				
			||||||
 | 
						res 305577 TBF#177 sendass PACCH - unans (at 583)
 | 
				
			||||||
 | 
						res 305585 TBF#177 sendass PACCH - unans (at 591)
 | 
				
			||||||
 | 
						res 305593 TBF#177 sendass PACCH - unans (at 599)
 | 
				
			||||||
 | 
						at 5598 res 305601 TBF#177 sendass PACCH - unans (at 607)
 | 
				
			||||||
 | 
						at 5606 res 305618 TBF#177 sendass imm on TN1. 617 PDCH#51:1 has no usf.
 | 
				
			||||||
 | 
						at 5623 res 305636 TBF#177 packetuplinkacknack
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					6-13-2012:
 | 
				
			||||||
 | 
						After RLC modification to send only negatively acknowledged blocks,
 | 
				
			||||||
 | 
						now at 22.2Kbps, latency=2.0, 100K in 36.1s.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					6-11-2012:
 | 
				
			||||||
 | 
						After fixing nstuck bug, setting nstuck to 1000, fixing the negatively acknowledged resend bugs:
 | 
				
			||||||
 | 
						15.6Kbps, 19.8Kbps, 19.3Kbps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					With 2-down/2-up I typically get 19Kbps.
 | 
				
			||||||
 | 
					Tried setting Poll1 to 30, got 11-13Kbps on two different tests
 | 
				
			||||||
 | 
					with 8 total cause=3 errors.
 | 
				
			||||||
 | 
					Restarted with Poll1 at 10 (default) got 19Kbps with one cause=3.
 | 
				
			||||||
 | 
					then 10Kbps with one cause=3.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Restarted, removing the the needy USF fix entirely,
 | 
				
			||||||
 | 
					setting Multislot.Max.Downlink/Uplink =1/1 in sql and turning on GPRS.WATCH.
 | 
				
			||||||
 | 
						18.5Kbps
 | 
				
			||||||
 | 
					Turned off GPRS.WATCH 14.7Kbps with 2x3105 retries
 | 
				
			||||||
 | 
					Turned on GPRS.WATCH 19.7Kbps, did not see any retries.
 | 
				
			||||||
 | 
					Turned off GPRS.WATCH 17.5, no retries, then 16.7 with 1x3105
 | 
				
			||||||
 | 
					Turned on GPRS.WATCH 19.3Kbps, 16.3Kbps
 | 
				
			||||||
 | 
					Turned off GPRS.WATCH 12.6Kpbs, 17.6Kbps (no retries), 17.2Kbps.
 | 
				
			||||||
 | 
					I think it may depend on how long you wait between re-running the test,
 | 
				
			||||||
 | 
					ie, maybe there are still old tcp packets coming that bash the current test.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					6-8-2012 GPRS-Beta-4 3-down/2-up with needy USF turned off:
 | 
				
			||||||
 | 
						Your speed: 18.4
 | 
				
			||||||
 | 
						Your latency: 1.8s
 | 
				
			||||||
 | 
						Transferred 100KB in 43.5
 | 
				
			||||||
 | 
					Changing GPRS.USFMode (needy USF) didnt change a thing.
 | 
				
			||||||
 | 
					with 2-down/2-up: 13.9Kbps, 2.2s, 100KB in 57.3s, and it had alot of retries.
 | 
				
			||||||
 | 
					again: 13.5Kbps, 2.3s, 100KB in 60s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					6-7-2012 with 3-down/2-up working multislot:
 | 
				
			||||||
 | 
						Your speed: 19.8Kbps
 | 
				
			||||||
 | 
						Your latency: 1.8s
 | 
				
			||||||
 | 
						Transferred 100KB in 40.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					6-3-2012 with 2-down/2-up working multislot:
 | 
				
			||||||
 | 
						Your speed: 20.78Kbps
 | 
				
			||||||
 | 
						Your latency: 1.8s
 | 
				
			||||||
 | 
						Transferred 100KB in 38.5s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mobilespeedtest.com:
 | 
				
			||||||
 | 
					6-3-2012 with 2-down/1-up buggy multislot:
 | 
				
			||||||
 | 
						Your speed: 18.29Kbps
 | 
				
			||||||
 | 
						Your latency: 2.86
 | 
				
			||||||
 | 
						Transferred 100KB in 43.74s
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					5-22-2012, with continuous tbf, but the speed test stops for maybe 5 seconds right in the middle -why?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Your speed: 7.08Kbps
 | 
				
			||||||
 | 
						Your latency: 2.702
 | 
				
			||||||
 | 
						Transferred 100KB in 113s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						google.com -  8 secs
 | 
				
			||||||
 | 
						pats home page (www.menu1.org/pat) - 15 secs
 | 
				
			||||||
 | 
						cnn.com - 35-40 secs  (note that this page is not invariant.)
 | 
				
			||||||
 | 
						wikipedia dinosaur  1:52 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					After bug fix, but it still hung half way through, but the hang looked correct (saw fbi) so whats up?
 | 
				
			||||||
 | 
					Your speed: 8.08Kbps
 | 
				
			||||||
 | 
					Your latency: 2.6
 | 
				
			||||||
 | 
					Transferred 100KB in 99s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					After setting RLCMsgPacketDownlinkAssignment->mControlAck
 | 
				
			||||||
 | 
					Your speed: 11.17Kbps
 | 
				
			||||||
 | 
					Your latency: 2.8
 | 
				
			||||||
 | 
					Transferred 100KB in 71
 | 
				
			||||||
 | 
					wikipedia dinosaur 1:40
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					After fixing the mSP bug:
 | 
				
			||||||
 | 
						wikipedia dinosaur 1:35
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					============
 | 
				
			||||||
							
								
								
									
										788
									
								
								GPRS/pat.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										788
									
								
								GPRS/pat.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,788 @@
 | 
				
			|||||||
 | 
					Kyles Blackberry is Blackberry 9700
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Speed tests:
 | 
				
			||||||
 | 
						From mobileSpeedTest.com:
 | 
				
			||||||
 | 
						Original:
 | 
				
			||||||
 | 
							Your speed: 5.166 Kbps
 | 
				
			||||||
 | 
							Your latency: 3.057 seconds
 | 
				
			||||||
 | 
							Transferred 100KB in 154.86 seconds.
 | 
				
			||||||
 | 
						With ganged TBFs, TBF wrap-around disabled, GPRS.Counters.N3105=3, GPRS.TBF.Retry=4,
 | 
				
			||||||
 | 
						GPRS.SinglePduMode=0.  The thing is still getting alot of 3105 errors.
 | 
				
			||||||
 | 
						And in general it gets cause=1 too, although not during this test, but before.
 | 
				
			||||||
 | 
							Your speed: 6.984 Kbps
 | 
				
			||||||
 | 
							Your latency: 2.09 seconds
 | 
				
			||||||
 | 
							Transferred 100KB in 114.55 seconds.
 | 
				
			||||||
 | 
						Another try:
 | 
				
			||||||
 | 
							Your speed: 7.539 Kbps
 | 
				
			||||||
 | 
							Your latency: 2.583 seconds
 | 
				
			||||||
 | 
							Transferred 100KB in 106.11 seconds.
 | 
				
			||||||
 | 
						Then I tried allowing TBF wrap-around and it went down!
 | 
				
			||||||
 | 
							Your speed: 6.383 Kbps
 | 
				
			||||||
 | 
							Your latency: 2.34 seconds
 | 
				
			||||||
 | 
							Transferred 100KB in 125.34 seconds.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Got 11 retries during the test,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note: GSM04.08 (L3 Procedures) and GSM04.18 (L3 messages) replaced by 44.18 + 24.08
 | 
				
			||||||
 | 
						4.08 has a state machine picture in 4.1.2, and GMM states a little after.
 | 
				
			||||||
 | 
						24.007 7.1.1 has lots of state machines including same state machine picture,
 | 
				
			||||||
 | 
							but I dont think they are useful.
 | 
				
			||||||
 | 
						3.64 6.2 has the DTM state machine picture.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						23.060 6.1 has a description of GMM states (PMM_whatever) for 3G-SGSN.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						24.007 11.2.4 - Lists IEI formats so you can tell how to skip an unrecognized IEI.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						3.03 2.6 - how to encode TLLI.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Measurement Reports:
 | 
				
			||||||
 | 
						45.008 10.1.4: in Network Control Order 2 (the one I have been using) if the 
 | 
				
			||||||
 | 
						MS detects a downlink signalling failure or random access failure
 | 
				
			||||||
 | 
						(as defined in 44.018/44.060) the MS will perform autonomous cell reselection.
 | 
				
			||||||
 | 
						This may have been what was happening when the MS would stop listening
 | 
				
			||||||
 | 
						to the BTS for 2 second straight.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RA Update - for GPRS.
 | 
				
			||||||
 | 
					LA Update - for CS calls.
 | 
				
			||||||
 | 
					Combined RA/LA update permitted at SGSN.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DRX mode and paging groups covered in GSM05.02.
 | 
				
			||||||
 | 
						MS tells DRX mode to SGSN in the GPRS Attach or RA Update messages.
 | 
				
			||||||
 | 
						Just what does the sgsn think it is going to do with it?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GPRS:
 | 
				
			||||||
 | 
						Jean Samuel - French guy funding Russians to develop GPRS.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The BCS Block Check Sequence shown in GSM03.64 6.6.4 figure 20
 | 
				
			||||||
 | 
					is just the 40 bit checksum added by transmit().
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PDP Context Activiation: Getting IP address: 24.008 6.1.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GSM 4.64 - LLC layer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note from man 7 tcp:
 | 
				
			||||||
 | 
						tcp_frto (integer; default: 0; since Linux 2.4.21/2.6)
 | 
				
			||||||
 | 
					              Enables  F-RTO,  an enhanced recovery algorithm for TCP retransmission time-
 | 
				
			||||||
 | 
					              outs (RTOs).  It is particularly beneficial in wireless  environments  where
 | 
				
			||||||
 | 
					              packet loss is typically due to random radio interference rather than inter-
 | 
				
			||||||
 | 
					              mediate router congestion.  See RFC 4138 for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              This file can have one of the following values:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              0  Disabled.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              1  The basic version F-RTO algorithm is enabled.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              2  Enable SACK-enhanced F-RTO if flow uses SACK.  The basic version  can  be
 | 
				
			||||||
 | 
					                 used  also  when  SACK  is  in use though in that case scenario(s) exists
 | 
				
			||||||
 | 
					                 where F-RTO interacts badly with the packet counting of the  SACK-enabled
 | 
				
			||||||
 | 
					                 TCP flow.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              Before  Linux  2.6.22,  this  parameter was a Boolean value, supporting just
 | 
				
			||||||
 | 
					              values 0 and 1 above.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RadioResource.cpp: AccessGrantResponder()
 | 
				
			||||||
 | 
					serviceLoop()
 | 
				
			||||||
 | 
					TCHFACCHL1Encoder::dispatch() - does TCH data pushing.
 | 
				
			||||||
 | 
					mDemuxTable in TRXManager.cpp, calls writeLowSideRx in a L1Decoder descendent class.
 | 
				
			||||||
 | 
					mGPRSFEC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Maximum LLC PDU size is 1560 bytes. (GSM04.60 sec9.1.12)  Bytes over are discarded in RLC.
 | 
				
			||||||
 | 
					In unacknowledged mode, LLC-PDUs delivered in the order received, with 0-bits for missing blocks.
 | 
				
			||||||
 | 
					The minimum payload size (using CS-1) is 20 bytes (see RLCPayloadSize)
 | 
				
			||||||
 | 
					Therefore, a single PDU may take 78 blocks.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There are 1-N downstream L1 physical channels.
 | 
				
			||||||
 | 
					Each is connected to an L1FEC.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					An N-PDU is on the network side of SGMS
 | 
				
			||||||
 | 
					A PDU (aka NS-PDU) is on the downstream side of SGMS, after SNDCP
 | 
				
			||||||
 | 
					A Segment is a part of a PDU for transmission in an RLC Radio Block.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All MAC control functions come from the SGMS via BSSGP.
 | 
				
			||||||
 | 
					The RLC/MAC is entirely oblivious of PDU content, just passes it to SGMS.
 | 
				
			||||||
 | 
					The RLC reports downlink packet loss information to SGMS as
 | 
				
			||||||
 | 
					a Bucket Leak Rate, per BSS (aka BVC on the BSSGP interface), and per MS.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Notes:
 | 
				
			||||||
 | 
					  Notes: The SAPMux class defines writeHighSide and writeLowSide
 | 
				
			||||||
 | 
					  An encoder class defines only writeHighSide
 | 
				
			||||||
 | 
					  A decoder class defines only writeLowSide.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  MAC MODE:
 | 
				
			||||||
 | 
						Dont understand.  For downlink allocation it is:
 | 
				
			||||||
 | 
							Dynamic allocation, Extended Dynamic allocation, (arent these inapplicable?)
 | 
				
			||||||
 | 
							Fixed allocation full duplex, Fixed allocation half duplex.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  TBF Mode:
 | 
				
			||||||
 | 
					    Packet Uplink Assignment, Packet Downlink Assignment, Immediate Assignment
 | 
				
			||||||
 | 
					    TFI goes with TBF.  TFI is unique only within a PDCH.
 | 
				
			||||||
 | 
					    For Multislot, TFI is unique in all PDCH of multislot.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  RLC Mode:
 | 
				
			||||||
 | 
						Acknowledged or Unacknowledged.  (See GSM04.60 11.2.7 Packet Downlink Assignment)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Upstream:
 | 
				
			||||||
 | 
					  BSSGSP
 | 
				
			||||||
 | 
					    Definitions:
 | 
				
			||||||
 | 
					      BVCI - BSSGP Virtual Connection ID, 0 = signaling, 1 = PTM (point-to-multipoint)
 | 
				
			||||||
 | 
						It corresponds to a cell, and can be used instead of routing area id
 | 
				
			||||||
 | 
						at operators discretion.
 | 
				
			||||||
 | 
						NSEI+BVCI
 | 
				
			||||||
 | 
					      NSE - Network Service Entity.  There is one or more NSE inside the BSS for signaling.
 | 
				
			||||||
 | 
					      NSEI - Network Service Entity Id.
 | 
				
			||||||
 | 
						I think these correspond to BSS.
 | 
				
			||||||
 | 
					      ---
 | 
				
			||||||
 | 
					      NS-VC - Virtual Connection
 | 
				
			||||||
 | 
					      ---
 | 
				
			||||||
 | 
					      LSP - Link Selector Parameter, something used only inside the BSS or SGSN,
 | 
				
			||||||
 | 
					      		and not transmitted, to uniquely identify NS-VC.
 | 
				
			||||||
 | 
							We wont use it.
 | 
				
			||||||
 | 
					      QoS Profile: specifies transmission mode (acknowledged, etc), bit rate, other stuff.
 | 
				
			||||||
 | 
					    Messages:
 | 
				
			||||||
 | 
					    DL-UNITDATA
 | 
				
			||||||
 | 
					      Includes PDU type (DL-UNITDATA), TLLI, QoS Profile, PDU Lifetime, PDU.
 | 
				
			||||||
 | 
					      optional: IMSI, oldTLLI, PFI (Packet Flow Identifier), etc.
 | 
				
			||||||
 | 
					      Does NOT include LSP, BVCI, NSEI
 | 
				
			||||||
 | 
					    UL-UNITDATA
 | 
				
			||||||
 | 
					      Includes PDU type (UL-UNITDATA), TLLI, BVCI, Cell Identifier, PDU.
 | 
				
			||||||
 | 
					      Does NOT include LSP, BVCI, NSEI.
 | 
				
			||||||
 | 
					    GMM-PAGING-PS/GMM-PAGING-CS (for packet or voice)
 | 
				
			||||||
 | 
					      Includes PDU type (PAGING-PS), QoS Profile, P-TMSI <or> IMSI.
 | 
				
			||||||
 | 
					      Note: If TLLI is specified and already exists within a Radio Context in BSS
 | 
				
			||||||
 | 
					      [because MS has communicated previously] it is used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      BVCI <or> Location Area <or> Routing Area <or> BSSArea Indication
 | 
				
			||||||
 | 
					      Optional: P-TMSI, BVCI, Location area, Routing area.
 | 
				
			||||||
 | 
					    GMM-RA-CAPABILITY, GMM-RA-CAPABILITY-UPDATE
 | 
				
			||||||
 | 
					      Astonishingly, the BSS asks the SGSN for this info.
 | 
				
			||||||
 | 
					    GMM-RADIO-STATUS
 | 
				
			||||||
 | 
					    GMM-SUSPEND
 | 
				
			||||||
 | 
					    GMM-RESUME
 | 
				
			||||||
 | 
					    NM-FLUSH
 | 
				
			||||||
 | 
					    NM-LLC-DISCARDED
 | 
				
			||||||
 | 
					    NM-FLOW-CONTROL-BVC
 | 
				
			||||||
 | 
					    NM-FLOW-CONTROL-MS
 | 
				
			||||||
 | 
					    NM-STATUS
 | 
				
			||||||
 | 
					    NM-BVC-BLOCK/NM-BVC-UNBLOCK
 | 
				
			||||||
 | 
					    NM-BVC-RESET
 | 
				
			||||||
 | 
					    NM-TRACE
 | 
				
			||||||
 | 
					    PFM-...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Downstream:
 | 
				
			||||||
 | 
					  RACH/AGCH for paging.
 | 
				
			||||||
 | 
					  PDUs via RLC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SGSN Uplink:
 | 
				
			||||||
 | 
					// 
 | 
				
			||||||
 | 
					// OpenBSC endpoint for NS layer packets: libgp/gprs_ns.c:read_nsip_msg
 | 
				
			||||||
 | 
					// (in OpenBSC, NSIP means NS over IP), which eventually calls:
 | 
				
			||||||
 | 
					// libgp/gprs_ns.c:gprs_ns_rcvmsg(), in which BSS is identified by three ways:
 | 
				
			||||||
 | 
					// 1.  First it tries using the IP address of the BSS to identify the BSS.
 | 
				
			||||||
 | 
					// 2.  If unrecognized, the NSEI specified in the NS_RESET command is used.
 | 
				
			||||||
 | 
					// 3.  If no NS_RESET ever received, sets NSEI to 0xfffe and proceeds; this works
 | 
				
			||||||
 | 
					//     if there is only one BSS, ever, as in our case.
 | 
				
			||||||
 | 
					// NS-UNITDATA packets (the only data type) are sent to gprs_ns_rx_unitdata(),
 | 
				
			||||||
 | 
					// which somehow calls sgsn_ns_cb() (a callback function)
 | 
				
			||||||
 | 
					// which calls libgp/bprs_bssgp.c:gprs_bssgp_rcvmsg(bci and nsei as params)
 | 
				
			||||||
 | 
					// which looks up the BTS using the NSEI, then uses:
 | 
				
			||||||
 | 
					// switch (BVCI) {
 | 
				
			||||||
 | 
					//	case BVCI_SIGNALLING: gprs_bssgp_rx_sign()
 | 
				
			||||||
 | 
					//	case PVCI_PTM: // throw an error
 | 
				
			||||||
 | 
					//	default: gprs_bssgp_rx_ptp().
 | 
				
			||||||
 | 
					// gprs_bssgp_rx_ptp() is the main BSSGP function, does this:
 | 
				
			||||||
 | 
					// switch (pdu_type):
 | 
				
			||||||
 | 
					//	case BSSGP_PDUT_UL_UNITDATA: bssgp_rx_ul_ud(),
 | 
				
			||||||
 | 
					//		which calls bssgp_rx_ul_ud(),  which calls gprs_llc_rcvmsg()
 | 
				
			||||||
 | 
					//	case BSSGP_PDUT_FLOW_CONTROL_BVC: bssgp_rx_fc_bvc();
 | 
				
			||||||
 | 
					//	default: // throw an error
 | 
				
			||||||
 | 
					// 
 | 
				
			||||||
 | 
					// gprs/gprs_llc.c:gprs_llc_rcvmsg() is the main entry point.
 | 
				
			||||||
 | 
					//	extracts TLLI, and forwards message based on SAPI to:
 | 
				
			||||||
 | 
					//	case GPRS_SAPI_GMM: gsm0408_gprs_rcvmsg()
 | 
				
			||||||
 | 
					//	case GPRS_SAPI_SNDCP3/5/9/11: sndcp_llunitdata_ind(),
 | 
				
			||||||
 | 
					//		which assembles NS-PDUs as per SNDCP, then sends the complete N-PDU to:
 | 
				
			||||||
 | 
					//		sgsn_rx_sndcp_ud_ind(), which looks up the MM context by RAI+TLLI,
 | 
				
			||||||
 | 
					// 		then looks up the PDP context by NSAPI+MM context,
 | 
				
			||||||
 | 
					//		counts bytes sent, then calls gtp_data_req(gsn, pdp->lib,npdu,npdu_len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SGSN Downlink:
 | 
				
			||||||
 | 
					Packets come in on a tunnel, may have different header sizes depending on version.
 | 
				
			||||||
 | 
					Gtp lib decapsulates them, gtp_decaps0(),gtp_decaps1(), gets the header,
 | 
				
			||||||
 | 
					which may indicate PDP creation, update, etc, or "GTP_PDU" type, which calls:
 | 
				
			||||||
 | 
					gtp/gtp.c: gtp_gtpu_ind() which calls (via callback table, to get out of gtp lib)
 | 
				
			||||||
 | 
					sgsn_libgtp.c: cb_data_ind(struct pdp_t*lib,void *packet,unsigned len)
 | 
				
			||||||
 | 
					The pdp has an MM context, which has the nsei+bvci+tlli.
 | 
				
			||||||
 | 
					If mm_state is GMM_REGISTERED_SUSPENDED, it calls gprs_bssgp_tx_paging(),
 | 
				
			||||||
 | 
					(and apparently drops the incoming NPDU on the floor)
 | 
				
			||||||
 | 
					else if mm_state is GMM_REGISTERED_NORMAL it just sends the packet to:
 | 
				
			||||||
 | 
					sndcp_unitdata_req()
 | 
				
			||||||
 | 
					Eventually it calls:
 | 
				
			||||||
 | 
					int gprs_bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx)
 | 
				
			||||||
 | 
						which calls: gprs_ns_sendmsg(bssgp_nsi, msg);
 | 
				
			||||||
 | 
						where bssgp_nsi is a global var.
 | 
				
			||||||
 | 
					int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg)
 | 
				
			||||||
 | 
						does: { ... nsvc = nsvc_by_nsei(nsi, msgb_nsei(msg)); }
 | 
				
			||||||
 | 
						then calls gprs_ns_tx(nsvc,msg)
 | 
				
			||||||
 | 
					which calls nsip_sendmsg(nsvc,msg)
 | 
				
			||||||
 | 
					which if the encapsulation is udp calls gprs_ns_tx(nsvc,msg) (else *_frgre_*something()
 | 
				
			||||||
 | 
					which calls nsip_sendmsg(nsvc,msg) which uses:
 | 
				
			||||||
 | 
						struct gprs_ns_inst *nsi = nsvc->nsi;
 | 
				
			||||||
 | 
					    struct sockaddr_in *daddr = &nsvc->ip.bts_addr;
 | 
				
			||||||
 | 
						rc = sendto(nsi->nsip.fd.fd, msg->data, msg->len, 0,
 | 
				
			||||||
 | 
								   (struct sockaddr *)daddr, sizeof(*daddr));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In osmocom/openbsc/openbsc/src/gprs
 | 
				
			||||||
 | 
					I modified the sgsn Makefile to remove -lgtp, and took out all the gtp references
 | 
				
			||||||
 | 
					execpt pdp stuff, so we can move that file to sgsn and stop linking with libgtp.
 | 
				
			||||||
 | 
					Notes: the cb_conf callback creates the pdp context;
 | 
				
			||||||
 | 
					to replace, I must call create_pdp_conf(), which calls gsm48_tx_gsm_act_pdp_acc() to
 | 
				
			||||||
 | 
					send an acknowledgment to the MS.
 | 
				
			||||||
 | 
					The eua is struct pdp_t is the IP address.  First byte is IETF, second 
 | 
				
			||||||
 | 
					GPRS Messages in GSM04.08  also GSM44.18
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					the opensgsn accepts flow control messages but ignores them
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Wikipediate/GPRS_Core_Network says:
 | 
				
			||||||
 | 
					GTP-U is used for user-data in spearated unnels for each PDP context.
 | 
				
			||||||
 | 
					GTP-C for control: setup of PDP contexts, etc.
 | 
				
			||||||
 | 
					GTP-C on UDP port 2123 and GTP-U port 2125
 | 
				
			||||||
 | 
					GTP version zero supports both on one generic header, can be used with UDP or TCP on port 3386.
 | 
				
			||||||
 | 
					But port 3386 is also dedicated to the charging service, specifically: "GSM/UMTS CDR logging protocol".
 | 
				
			||||||
 | 
					GSM 20.060 - Routing!  Finally!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sndcp:
 | 
				
			||||||
 | 
					/* Request transmission of a SN-PDU over specified LLC Entity + SAPI */
 | 
				
			||||||
 | 
					sndcp_unitdata_req()
 | 
				
			||||||
 | 
					Note that sndcp header compression is optional, so I suspect opensgsn doesnt bother, not sure.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GTP and GGSN:
 | 
				
			||||||
 | 
					See 29.060 for GTP, and 27.060 for an example.
 | 
				
			||||||
 | 
					29.060 7.3.2 create pdp context response:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PPP is not normally used on the GGSN
 | 
				
			||||||
 | 
					PPP support was added to allow an MS to use PPP to go all the way to a network endpoint.
 | 
				
			||||||
 | 
					See cisco document: http://www.cisco.com/en/US/docs/ios/12_3/12_3y/12_3yq/ggsn_5_2/configuration/guide/ggsnppp.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					And I quote:
 | 
				
			||||||
 | 
					If the MS requests a dynamic PDP address with the PDP Type IPv4, IPv6 or
 | 
				
			||||||
 | 
					IPv4v6 and a dynamic PDP address is allowed, then the End User Address
 | 
				
			||||||
 | 
					information element shall be included and the PDP Address field in the End
 | 
				
			||||||
 | 
					User Address information element shall contain the dynamic PDP Address(es)
 | 
				
			||||||
 | 
					allocated by the GGSN.  If the MS requests a static PDP address with
 | 
				
			||||||
 | 
					the PDP Type IPv4, IPv6 or IPv4v6, or a PDP address is specified with
 | 
				
			||||||
 | 
					PDP Type PPP, then the End User Address information element shall be
 | 
				
			||||||
 | 
					included and the PDP Address field shall not be included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ephemeral ports:
 | 
				
			||||||
 | 
					cat /proc/sys/net/ipv4/ip_local_port_range 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 /* actually send the N-PDU to the SGSN core code, which then
 | 
				
			||||||
 | 
					  * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */
 | 
				
			||||||
 | 
					return sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli, sne->nsapi, msg, npdu_len, npdu)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Wrap BSSGP packets in yet another layer per 44.018
 | 
				
			||||||
 | 
					// OpenBSC endpoint: libgp/gprs_ns.c:read_nsip_msg (NSIP == NS over IP), which eventually calls:
 | 
				
			||||||
 | 
					struct NSLayer {
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Talk to BSSGSP (or whomever)
 | 
				
			||||||
 | 
					struct BSSGPLayer {
 | 
				
			||||||
 | 
					  send and recv L2 messages.
 | 
				
			||||||
 | 
					  Talk to the socket.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MACB {
 | 
				
			||||||
 | 
					  // Just one of these.
 | 
				
			||||||
 | 
					  For each PCH:
 | 
				
			||||||
 | 
					  	TFI table - points to MACD for each TFI for both uplink and downlink.
 | 
				
			||||||
 | 
						// Which MS is using which Block locations currently.
 | 
				
			||||||
 | 
						// This could just be bit mask, which is set when TBF allocated
 | 
				
			||||||
 | 
						// and reset when TBF unallocated.  If you really need to know
 | 
				
			||||||
 | 
						// who owns a slot, you could run through the TFI table.
 | 
				
			||||||
 | 
						Uplink_Block_Assignment[12];
 | 
				
			||||||
 | 
						Downlink_Block_Assignment[12];
 | 
				
			||||||
 | 
					  IMSI to MACD table
 | 
				
			||||||
 | 
					  PTMSI to MACD table
 | 
				
			||||||
 | 
					  TLLI to MACD table
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Routine to assign blocks to MACDs.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GPRSChannel pickChannel() {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MACD {	// aka Radio Context.
 | 
				
			||||||
 | 
					  // One per IMSI or TLLI, which means one per MS.
 | 
				
			||||||
 | 
					  // We will keep these around until the MS detaches, or they get really old.
 | 
				
			||||||
 | 
					  TLLI mtlli;	// Not known when MS first attaches.
 | 
				
			||||||
 | 
					  IMSI mimsi;
 | 
				
			||||||
 | 
					  // State of MS: packet-idle, packet-transfer.
 | 
				
			||||||
 | 
					  // Points to in-process TBFs; there could be multiple ones because a single
 | 
				
			||||||
 | 
					  // block.
 | 
				
			||||||
 | 
					  // 1 or more PCH assigned to MS.
 | 
				
			||||||
 | 
					  // NO, the SGMS does this: Incoming message may be control or data, routed to UplinkTBF.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GPRSChannel {
 | 
				
			||||||
 | 
					  GPRSL12Uplink *uplink;
 | 
				
			||||||
 | 
					  GPRSL12Downlink *downlink;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GPRSL12Uplink {	// aka PCH
 | 
				
			||||||
 | 
					  	// downstream attaches to a single Physical channel in L1FEC.
 | 
				
			||||||
 | 
					  	// Incoming messages are routed to MACD based on TFI.
 | 
				
			||||||
 | 
						List mReservations;	// Radio blocks that have been reserved for some purpose,
 | 
				
			||||||
 | 
									// eg, single block grants requested by RACH
 | 
				
			||||||
 | 
						// Return an available RB on this uplink.
 | 
				
			||||||
 | 
						RBN reserveOneBlock() {
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GPRSL12Downlink {	// aka PCH
 | 
				
			||||||
 | 
						// One of these for each PCH (physical channel), attached to L1FEC.
 | 
				
			||||||
 | 
						// Accepts Radio Blocks from anybody.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AGCHResponder {
 | 
				
			||||||
 | 
						// This class queues GPRS responses that must be sent via AGCH.
 | 
				
			||||||
 | 
						// These are:
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GPRSRachManager - not needed, just use some functions.
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Receives Packet Channel Request on RACH.  (from Control:AccessGrantResponder())
 | 
				
			||||||
 | 
						// Routes to MACD.
 | 
				
			||||||
 | 
						// Note that SGMS knows nothing about this yet - the MS will use its newly
 | 
				
			||||||
 | 
						// allocated channel to send a PDU that goes to SGMS.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A TBF can be a single block packet access, or one or two phase multi-block access,
 | 
				
			||||||
 | 
					// although this class does not do the phases, it just handles a single TBF transaction,
 | 
				
			||||||
 | 
					// then disappars.
 | 
				
			||||||
 | 
					class DownlinkTBF {
 | 
				
			||||||
 | 
						// Contains the downlink RLCEngine.
 | 
				
			||||||
 | 
						PDU *data[0..n]		// 1 or more PDUs to send.
 | 
				
			||||||
 | 
						GPRSL12Downlink mpch[4];		// Up to 4 physical channel
 | 
				
			||||||
 | 
						int PDUPriority
 | 
				
			||||||
 | 
						MACD *my_ms;		// Used to get TLLI.
 | 
				
			||||||
 | 
						int TFI;		// 0..7, assigned when transaction starts.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For open-ended dynamic uplink, which uses USF, the MS will continue to monitor
 | 
				
			||||||
 | 
					// the PDCH for its USF value until it receives ... or until T3180 expires: 5 seconds.
 | 
				
			||||||
 | 
					// If MS finishes, it sends its final block then immediately enters packet-idle mode
 | 
				
			||||||
 | 
					// unless there is a downlink in progress.
 | 
				
			||||||
 | 
					class UplinkTBF {
 | 
				
			||||||
 | 
						// Contains the uplink RLCEngine.
 | 
				
			||||||
 | 
						// Assembles the PDUs and sends them to BSSGP.
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					=========================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IMMEDIATE ASSIGNMENT FORMAT: GSM04.08sec9.1.18
 | 
				
			||||||
 | 
					// Notes: We will not use:
 | 
				
			||||||
 | 
					//	Immediate_Assignment_Extended for RR only and addresses 2 mobiles simultaneously.
 | 
				
			||||||
 | 
					//	"RR Packet Uplink/Downlink Assignment" is part of the "Packet Assignment"
 | 
				
			||||||
 | 
					//	command that is sent on DCCH of an MS with an ongoing RR channel.
 | 
				
			||||||
 | 
					struct {
 | 
				
			||||||
 | 
					  uint l2_pseudo_length:8;
 | 
				
			||||||
 | 
					  uint RR_protocol_discriminator:4;	// Not sure, see 04.18sec10.2, GSM24.07
 | 
				
			||||||
 | 
					  uint skip_indicator:4;	// 0 == dont ignore this message.
 | 
				
			||||||
 | 
					  uint message_type:8;		// 0x3f == IMMEDIATE_ASSIGNMENT,
 | 
				
			||||||
 | 
					  uint page_mode:4;
 | 
				
			||||||
 | 
					  uint dedicated_mode_or_TBF:4;	// sec10.5.2.25b
 | 
				
			||||||
 | 
					    // bits are:
 | 
				
			||||||
 | 
					  	// unused:1;
 | 
				
			||||||
 | 
						// TMA:1; 0 == no meaning ??, 1 == first of two message TBF assignment.
 | 
				
			||||||
 | 
						// downlink:1; 0 == RR, 1 == TBF.
 | 
				
			||||||
 | 
						// TD:1; 0 == no meaning ??, 1 == there is something in the rest octets
 | 
				
			||||||
 | 
							// for a TBF, it is the Packet Uplink Assignment.
 | 
				
			||||||
 | 
					  uint channel_description:24;		// for RR, ignored for TBF.
 | 
				
			||||||
 | 
					  // GSM04.08sec10.5.2.25a
 | 
				
			||||||
 | 
					  <or> uint packet_channel_description:24;	// for TBF
 | 
				
			||||||
 | 
					      struct {
 | 
				
			||||||
 | 
						uint channel_type:5;		// unused, must be 1;
 | 
				
			||||||
 | 
						uint TN:3;			// Timeslot Number.
 | 
				
			||||||
 | 
						uint TSC:3;			// Training Sequence Code; GSN.05.02
 | 
				
			||||||
 | 
						uint union_encode_bits:2; 	// set to 0x00 to indicate no frequency hopping.
 | 
				
			||||||
 | 
						uint spare:1;			// set to 0
 | 
				
			||||||
 | 
						uint ARFCN:10;
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					  uint request_reference:24;		// Identifies the MS that sent the RACH.
 | 
				
			||||||
 | 
					      // use: L3RequestReference foo(RA,GSM::Time&when);
 | 
				
			||||||
 | 
					      struct {
 | 
				
			||||||
 | 
						uint RA:8;		// The 8-bit RACH message sent by the MS.
 | 
				
			||||||
 | 
						// The T? fields encode the FN module 42432 in which RACH was received.
 | 
				
			||||||
 | 
						// Note: FN modulo 42432 == (51X((T3-T2) mod 26) + T3 + 51*26*T1prime
 | 
				
			||||||
 | 
						uint T1prime:5;		// FN (div 1326) mod 32;
 | 
				
			||||||
 | 
						uint T3highpart:3	// FN mod 51, in 6 bits;
 | 
				
			||||||
 | 
						uint T3lowpart:3;
 | 
				
			||||||
 | 
						uint T2:5;		// FN mod 26.
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					  uint timing_advance:8;
 | 
				
			||||||
 | 
					  mobile allocation:8; 		// encoded as Format LV? length 1-9 bytes.
 | 
				
			||||||
 | 
					  				// length indicator set to 0 unless there is frequency hopping.
 | 
				
			||||||
 | 
					  starting_time:24;		// optional, encoded as Format TV with IEI = 0x7c;
 | 
				
			||||||
 | 
					  				// Sec 9.1.18.2 says: starting_time ignored for TBF.
 | 
				
			||||||
 | 
					  IA_rest_octets[]		// For RR, may be Frequency Params
 | 
				
			||||||
 | 
					  			// For TBF, may be Packet Uplink Assignment, Packet Downlink Assignment,
 | 
				
			||||||
 | 
								// or Second Part Packet Assignment sec10.5.2.16
 | 
				
			||||||
 | 
					  // Packet Uplink Assignment Message rest_octets:
 | 
				
			||||||
 | 
					  struct IA_Rest_Octets {	// 10.5.2.16
 | 
				
			||||||
 | 
					    union1:2;  // Must be HH for a TBF
 | 
				
			||||||
 | 
					    uint type1;	// 0 => Packet Uplink/downlink Assignment,
 | 
				
			||||||
 | 
					    		// 1 =>  Second Part Assignment.
 | 
				
			||||||
 | 
					    uint type2:	// 0 => packet_uplink_assignment, 1 => Packet_Downlink_Assignment,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Packet_Uplink_Assignment {
 | 
				
			||||||
 | 
					    uint union_altype:1:	// 0 => single_block_allocation, 1 => Fixed or Dynamic Allocation;
 | 
				
			||||||
 | 
					    		// (But note: polling indicates a Fixed Allocation for just one block.)
 | 
				
			||||||
 | 
					    if (union_altype == 1) {
 | 
				
			||||||
 | 
					      uint TFI_Assignment:5;
 | 
				
			||||||
 | 
					      uint Polling:1;	// 1 => MS shall send a Packet Control Acknowledgement in the
 | 
				
			||||||
 | 
					      			// uplink block specified by TBF Starting TIme.
 | 
				
			||||||
 | 
					      uint union_fixed_indicator:1;
 | 
				
			||||||
 | 
					      if (union_fixed_indicator == 0) {
 | 
				
			||||||
 | 
						// USF stuff, we dont need yet.
 | 
				
			||||||
 | 
					      } else {	// union_fixed_indicator == 1
 | 
				
			||||||
 | 
						Allocation_Bitmap_Length:5;
 | 
				
			||||||
 | 
						Allocation_Bitmap // variable sized.
 | 
				
			||||||
 | 
						union_p0_indicator:1;	// 1 =>  P0 present;
 | 
				
			||||||
 | 
						if (union_p0_indicator == 1) {
 | 
				
			||||||
 | 
						  P0:4; BTS_PWR_CTRL_MODE:1; PR_MODE:1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      Channel_Coding_Command:2;
 | 
				
			||||||
 | 
					      TLLI_Block_Channel_Coding:1;
 | 
				
			||||||
 | 
					      { 0 | 1 ALPHA:4; }
 | 
				
			||||||
 | 
					      GAMMA:5
 | 
				
			||||||
 | 
					      { 0 | 1 Timing_Advance_Index:4}
 | 
				
			||||||
 | 
					      { 0 | 1 TBF_Starting_time:16 }
 | 
				
			||||||
 | 
					    } else {	// union_altype == 0
 | 
				
			||||||
 | 
					      // Single Block Allocation
 | 
				
			||||||
 | 
					      { 0 | 1 ALPHA:4; }
 | 
				
			||||||
 | 
					      GAMMA:5
 | 
				
			||||||
 | 
					      uint Note1_bits:2;	// Must be 0x1;
 | 
				
			||||||
 | 
					      TBF_Starting_time:16;
 | 
				
			||||||
 | 
					      {L | H 
 | 
				
			||||||
 | 
						  P0:4; BTS_PWR_CTRL_MODE:1; PR_MODE:1;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					=========================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Mobile Originated Packet Transfer:
 | 
				
			||||||
 | 
					  Described in GSM03.64sec6.6.4.7, and GSM44.018sec3.
 | 
				
			||||||
 | 
					  Note: GSM44.018 talks about RR establishment, which is a voice call, in sec 3.2,3.3 and 3.4.
 | 
				
			||||||
 | 
					  Voice mode is "idle mode" or "dedicated mode" 
 | 
				
			||||||
 | 
					  GPRS is in "packet idle mode" or "packet transfer mode".
 | 
				
			||||||
 | 
					  Note that an MS in packet idle mode will receive pages on PACCH, but the Page may specify
 | 
				
			||||||
 | 
					  establishment of a GSM RR connection, ie a voice call.
 | 
				
			||||||
 | 
					  Section 3.5 (and only sec 3.5) talks about packet mode.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  PACCH = Packet Associated Control Channel.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RACH -> Packet Channel Request (requests one block, may request one or two phase mode)
 | 
				
			||||||
 | 
						  GSM04.18sec9.1.8 - Channel Request word encoding.
 | 
				
			||||||
 | 
						  GSM04.18sec3.3.1.1.1 Channel request procedure.  and GSM24.07? 
 | 
				
			||||||
 | 
						  This message contains only:
 | 
				
			||||||
 | 
						  	Establishment Cause (1 byte)
 | 
				
			||||||
 | 
							Random Reference (There is none in this case,
 | 
				
			||||||
 | 
							because Establishment Cause takes the whole byte.)
 | 
				
			||||||
 | 
						  (Mobile may send M+1 of these)
 | 
				
			||||||
 | 
						  Which phase will be requested documented in 44.018 3.5.2.1.2, but I dont think we care.
 | 
				
			||||||
 | 
						  Establishment cause is one or two phase "packet access".
 | 
				
			||||||
 | 
						  The request may specify:
 | 
				
			||||||
 | 
						    - for user data, unacknowledged mode, MS requests single block, and attempts two-phase;
 | 
				
			||||||
 | 
						    - for user data, acknowledged mode, MS requests either one-phase access
 | 
				
			||||||
 | 
						    	or a single block packet access.
 | 
				
			||||||
 | 
						    - for page response, etc, MS requests a one phase access.
 | 
				
			||||||
 | 
						    Note: Network may change one-phase request into single-block access, which forces
 | 
				
			||||||
 | 
						      the MS to perform two-phase access [if it wants to send multiple packets].
 | 
				
			||||||
 | 
						  fy, for GSM: After sending M+1, MS starts T3126, and if no answer, gives up.
 | 
				
			||||||
 | 
						  MS enters packet transfer mode, and listens to all of BCCH and CCCH in its timeslot.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  GPRS: After sending max number of Channel Request, MS starts T3146,
 | 
				
			||||||
 | 
						  after which is sends a Random Access Failure and looks for another cell.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Response may be any of:
 | 
				
			||||||
 | 
						AGCH <- IMMEDIATE ASSIGNMENT, which either contains Packet Uplink Assignment,
 | 
				
			||||||
 | 
							or bit in "Dedicated mode or TBF" element saying it is 2 part,
 | 
				
			||||||
 | 
							in which case the real IMMEDIATE ASSIGNMENT is sent within 2 multiframes.
 | 
				
			||||||
 | 
							The MS will then respond to IMMEDIATE ASSIGNMENT, (and then can try to do its
 | 
				
			||||||
 | 
							uplink using the packet channel.)
 | 
				
			||||||
 | 
						AGCH <- REJECT, which lets the MS look for another cell.
 | 
				
			||||||
 | 
						PAGCH <- Packet Queuing Notification. (optional, used if heavy traffic,
 | 
				
			||||||
 | 
							but only applicable on PCCCH, not CCCH.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Medium Access Modes:
 | 
				
			||||||
 | 
						  Fixed, Dynamic (uses USF), Extended Dynamic, or Exclusive Allocation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						For two-phase access, need an additional set of transfers before starting:
 | 
				
			||||||
 | 
						PACCH -> Packet Resource Request (optional, used for Fixed or Exclusive Allocation)
 | 
				
			||||||
 | 
						PACCH <- Packet Uplink Assignment (optional, used for Fixed or Exclusive Allocation)
 | 
				
			||||||
 | 
						    For Fixed Allocation, Packet Uplink Assignment specfies start frame, slot, and
 | 
				
			||||||
 | 
						    bitmap of blocks assigned to MS.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						For one-phase access:
 | 
				
			||||||
 | 
						I think one-phase access also works for a single unacknowledged uplink block?
 | 
				
			||||||
 | 
						For Dynamic Allocation, USF (Uplink State Flag) in download block tells MS
 | 
				
			||||||
 | 
						when they can use the upload blocks.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unacknowledged Mode Uplink Data Transfer:
 | 
				
			||||||
 | 
					    Fixed Allocation Uplink 4.60sec8.1.1.3:
 | 
				
			||||||
 | 
						Initiation by MS sending PACKET RESOURCE REQUEST, using Mobile Originated blah above,
 | 
				
			||||||
 | 
						or during downlink transfer, can also send PACKET DOWNLINK ACK/NAC with
 | 
				
			||||||
 | 
						essentially the same info.
 | 
				
			||||||
 | 
						Closed-ended TBF:
 | 
				
			||||||
 | 
						  PACKET RESOURCE REQUEST includes non-zero RLC_OCTET_COUNT, which is number
 | 
				
			||||||
 | 
						  of bytes + RLC block length but less LLC boundaries, which the network must add in.
 | 
				
			||||||
 | 
						  Network sends PACKET UPLINK ASSIGNMENT with enough blocks to handle it.
 | 
				
			||||||
 | 
						  Premature end is possible by FINAL_ALLOCATION indication in a fixed allocation
 | 
				
			||||||
 | 
						  assignment message.
 | 
				
			||||||
 | 
						Open-ended TBF:
 | 
				
			||||||
 | 
						  At the beginning of the uplink allocation the MS may request to continue the TBF
 | 
				
			||||||
 | 
						  by transmitting another PACKET RESOURCE REQUEST or PACKET DOWNLINK ACK/NACK,
 | 
				
			||||||
 | 
						  with the number RLC data otets ready to transmit in the RLC_OCTET_COUNT.
 | 
				
			||||||
 | 
						  [in the Channel Request Description IE.]
 | 
				
			||||||
 | 
						  Alternatively, can request open-ended TBF with first PACKET RESOURCE REQUEST
 | 
				
			||||||
 | 
						  RLC_OCTET_COUNT == 0.
 | 
				
			||||||
 | 
						  Network sends another PACKET UPLINK ASSIGNMENT with a new fixed allocation,
 | 
				
			||||||
 | 
						  or PACKET ACCESS REJECT.  PACKET UPLINK ASSIGNMENT may also set the FINAL_ALLOCATION bit
 | 
				
			||||||
 | 
						  to stop the open-ended transfer after this final assignment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PDTCH (assigned slot and block numbers) -> Data
 | 
				
			||||||
 | 
						...
 | 
				
			||||||
 | 
						PACCH <- Packet Uplink Ack/Nack (includes the successfully received block mask, but it is not used except to determine channel quality.)
 | 
				
			||||||
 | 
						No Packet Control Acknowledgment is sent..
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Notes Uplink Data Transfer, either acknolwedged or unacknowledged:
 | 
				
			||||||
 | 
						Network can send a downlink assignment or timeslot reconfig (needed for multislot)
 | 
				
			||||||
 | 
						while uplink is running.
 | 
				
			||||||
 | 
						If MS in half duplex mode it has to wait for uplink to end before
 | 
				
			||||||
 | 
						starting a downlink.
 | 
				
			||||||
 | 
						Note: Half Duplex Mode is a Medium Access Mode described in GSM04.60sec8.1.0,
 | 
				
			||||||
 | 
						specified in MAC_MODE parameter in downlink assignment, or
 | 
				
			||||||
 | 
						weirdly in uplink assignment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Holding the line open:
 | 
				
			||||||
 | 
						By this I mean, keeping the MS listening to PDCH so you dont have to send
 | 
				
			||||||
 | 
						another control block on CCCh.
 | 
				
			||||||
 | 
						Downlink: you cant hold the TBF open, because the RLC Data Block has a TFI bit
 | 
				
			||||||
 | 
						that indicates the end of the transfer.  I looked a little at sending 0 sized blocks,
 | 
				
			||||||
 | 
						but became confused.  However, after downlink, the MS stays on PDCH
 | 
				
			||||||
 | 
						until T3192 expires, for both Acknowledged (sec 9.3.2.6) and Unacknowledged mode, (sec 9.3.3.5.)
 | 
				
			||||||
 | 
						(Note: T3191 is something to do with how long the MS has to respond
 | 
				
			||||||
 | 
						to the final block sent downlink.)
 | 
				
			||||||
 | 
						Accodring to GSM04.60 table 13.1, you can continue to send down the final block
 | 
				
			||||||
 | 
						of the transaction over and over to keep T3192 reset.
 | 
				
			||||||
 | 
						This would work for acknowledged mode, because the network is just pretending
 | 
				
			||||||
 | 
						it did not receive the final block, but I am not sure how this would work in
 | 
				
			||||||
 | 
						unacknowledged mode, where each block is only sent once.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Uplink: See 04.60 sec 9.3.3.3.  After the final uplink block, network sends
 | 
				
			||||||
 | 
						Packet Uplink Ack/Nack (even for unacknowledged mode) with "Final Ack" == 1
 | 
				
			||||||
 | 
						and an RRBP field, which indicates an uplink slot the MS uses to send
 | 
				
			||||||
 | 
						a new PACKET RESOURCE REQUEST if it wants to send more, or PACKET CONTROL ACKNOLWEDGEMENT
 | 
				
			||||||
 | 
						and unless there is a downlink TBF in progress, immediately enters packet-idle mode.
 | 
				
			||||||
 | 
						If the Packet Control Acknowledgement is a RACH, there is also a way to encode
 | 
				
			||||||
 | 
						CTRL_ACK to specify the MS wants a new uplink TBF.  The Packet Control Acknowledgement
 | 
				
			||||||
 | 
						documentation for CTRL_ACK is full of special cases because you would only use
 | 
				
			||||||
 | 
						that if it is a RACH, otherwise the MS would send a Packet Resource Request.
 | 
				
			||||||
 | 
						Options:
 | 
				
			||||||
 | 
						o   Sec 7.1 lists all the ways an uplink TBF can be established, and there is no way
 | 
				
			||||||
 | 
							for the MS to do this during the T3192 period, when there is no TBF in either
 | 
				
			||||||
 | 
							direction but the MS is still waiting out T3192 for a new downlink TBF.
 | 
				
			||||||
 | 
							If you want to keep the MS camped on PDCH, they expect you to use PCCCH.
 | 
				
			||||||
 | 
						o	The network could delay sending the final uplinkAck/Nack up to 5 sec, but that might
 | 
				
			||||||
 | 
							prevent the MS from starting a new uplink until then.
 | 
				
			||||||
 | 
						o   The network could send an AckNack requesting retransmission just to keep the MS
 | 
				
			||||||
 | 
							on the line, but that risks having an outright failure reported by the MS to upper
 | 
				
			||||||
 | 
							layers if it cannot get the block through on the resend, and at the very least,
 | 
				
			||||||
 | 
							reporting incorrect QoS parameters.
 | 
				
			||||||
 | 
						o	Near the end of T3192 (after downlink ends) send an unsolicited single-block uplink.
 | 
				
			||||||
 | 
							This is undocumented, so it might not work.
 | 
				
			||||||
 | 
						o   This idea does not work (because MS must respond to an RRBP, which it will not get):
 | 
				
			||||||
 | 
							The network could send a new downlink message before T3192 expires,
 | 
				
			||||||
 | 
							and then just never send any downlink (let the timer expire) and send
 | 
				
			||||||
 | 
							Packet Polling Requests to the MS.  The 51-multiframe is a 1/4 sec.
 | 
				
			||||||
 | 
						o	Maybe network could send a dummy control block with an RRBP field.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						For Fixed uplink, the MS may request more uplink via:
 | 
				
			||||||
 | 
						bits in the "Packet Downlink Ack/Nack" block sent
 | 
				
			||||||
 | 
						upwards during an existing downlink, or in the first packet of an existing uplink.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Notes: The "GPRS Cell Options" information element (GSM04.60 table 12.4.2) is
 | 
				
			||||||
 | 
					sent in PSI1, PSI13, PSI14 (PACCH only), and SI13.
 | 
				
			||||||
 | 
					Note that both PSI1 and PSI13 can be sent on PAACH as well as PCCCH.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It specifies the values for T3168: range 0-0.5sec and T3192: range 0-1.5sec.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The RRBP field in acknowledged download mode specifies yet another
 | 
				
			||||||
 | 
					single block packet the MS can send upwards, for Ack/Nack or any other purpose
 | 
				
			||||||
 | 
					the MS desires.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The first block of a downlink must arrive within T3190, which is 5sec.
 | 
				
			||||||
 | 
					The MS will stay camped on the downlink until T3190 expires, which is 5sec.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Acknowledged Mode Uplink Data Transfer:
 | 
				
			||||||
 | 
					    (Note: TBF survives until all blocks acknowledged.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Fixed Allocation Transfer:
 | 
				
			||||||
 | 
						PDTCH (assigned slot and block numbers) -> Data
 | 
				
			||||||
 | 
						...
 | 
				
			||||||
 | 
						PACCH <- Packet Uplink Ack/Nack
 | 
				
			||||||
 | 
							(or unsolicited PACKET UPLINK ASSIGNMENT or TIMESLOT RECONFIG)
 | 
				
			||||||
 | 
						PACCH -> Packet Control Acknowledgment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Dynamic Allocation Transfer:
 | 
				
			||||||
 | 
						PDTCH -> Data
 | 
				
			||||||
 | 
						...
 | 
				
			||||||
 | 
						PACCH <- Packet Uplink Ack/Nack
 | 
				
			||||||
 | 
						PDTCH -> Data (new data or possibly retries)
 | 
				
			||||||
 | 
						..
 | 
				
			||||||
 | 
						PDTCH -> Last Data Block.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PACCH <- Packet Uplink Assignment
 | 
				
			||||||
 | 
						PACCH -> Packet Control Acknowledgment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Downlink Transfer:
 | 
				
			||||||
 | 
						PACKET DOWNLINK ASSIGNMENT or TIMESLOT RECONFIG contains TBF start time.
 | 
				
			||||||
 | 
						If a second downlink assignment or timeslot reconfig arrives while downlink
 | 
				
			||||||
 | 
						in progress, MS waits until TBF start time, then switches to new one.
 | 
				
			||||||
 | 
						If the downlink assignment does not include a TBF start time, the MS reacts
 | 
				
			||||||
 | 
						within reaction time specified in GSM05.10.
 | 
				
			||||||
 | 
						If the gap between blocks addressed to itself ever exceeds T3190, MS aborts
 | 
				
			||||||
 | 
						as per sec 8.7.1
 | 
				
			||||||
 | 
						Network sends PACKET TBF RELEASE to end downlink prematurely.
 | 
				
			||||||
 | 
						If no on-going uplink TBF, puts the MS in packet idle mode.
 | 
				
			||||||
 | 
						Note: It looks like you can keep the "line open" by letting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Network Originated Downlink: GSM4.08 sec3.5.3
 | 
				
			||||||
 | 
						CCCH <- IMMEDIATE ASSIGNMENT with a Packet Downlink Assignment,
 | 
				
			||||||
 | 
							IMMEDIATE ASSIGNMENT format in GSN04.18sec9.1.18
 | 
				
			||||||
 | 
							Note: The network must use this only when MS is in packet-idle mode.
 | 
				
			||||||
 | 
							The IMMEDIATE ASSIGNMENT contains:
 | 
				
			||||||
 | 
							  packet channel description, initial timing advance, and packet downlink assignment,
 | 
				
			||||||
 | 
							  which contains: TLLI, TFI, RLC mode, power control, polling bit,
 | 
				
			||||||
 | 
							  timing advance valid flag,
 | 
				
			||||||
 | 
							  and optionally: TAI (timing advance index) and/or TBF start time.
 | 
				
			||||||
 | 
						optional: MS -> PDCH  If polling bit is 1, MS waits for TBF start time and
 | 
				
			||||||
 | 
							sends PACKET CONTROL ACKNOWLEDGMENT first.
 | 
				
			||||||
 | 
							Note: in this case TBF start time applies both to download time and upload time.
 | 
				
			||||||
 | 
						Then network starts sending the packets.
 | 
				
			||||||
 | 
						Note: 4.08 sec 3.5.3.2 says you can also send a single RLC/MAC control message the same way,
 | 
				
			||||||
 | 
						but you dont have to specify TFI.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Network Originated Page:
 | 
				
			||||||
 | 
					    The GMM can use this to find out the Routing Area of the MS.
 | 
				
			||||||
 | 
					    For GPRS see 44.018sec3.3.2.1.1 "Paging initiation using paging subchannel on CCCH"
 | 
				
			||||||
 | 
					    	PCH of CCCH <- Paging Request, using P-TMSI or IMSI.
 | 
				
			||||||
 | 
						  CCCH Paging Request format in GSM04.18sec9.1.22 and following.
 | 
				
			||||||
 | 
							If IMSI, page request may be for RR (voice) or packet, depending on
 | 
				
			||||||
 | 
							"Packet Page Indication" field.
 | 
				
			||||||
 | 
						RACH -> Channel Request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Mobile then does a Mobile Originated Packet Transfer, and sends a packet paging response
 | 
				
			||||||
 | 
						(GSM03.64) to the network, which is an LLC frame and we dont need to know anything
 | 
				
			||||||
 | 
						more about it, because it is interpreted inside the SGSN.
 | 
				
			||||||
 | 
						Note: The SGSN sets the mobility management mode to "Ready", but the MS is usually
 | 
				
			||||||
 | 
						still in packet-idle-mode until the SGSN initiates a downlink packet transfer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CCCH [AGCH?] <- Packet Uplink Assignment (or Immediate Assignment)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						For two-phase access:
 | 
				
			||||||
 | 
						PACCH -> Packet Resource Request (optional, used for Fixed or Exclusive Allocation)
 | 
				
			||||||
 | 
						PACCH <- Packed Uplink Assignment (optional, used for Fixed or Exclusive Allocation)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PDTCH -> Packet Paging Response (LLC frame)
 | 
				
			||||||
 | 
						Now the MM (Mobility management) state of the MS is Ready.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    When MS is in Ready state:
 | 
				
			||||||
 | 
						If Packet uplink in progress:
 | 
				
			||||||
 | 
						  PACCH <- Packet Downlink Assignment Message, specifies PDCH to be used.
 | 
				
			||||||
 | 
						else:
 | 
				
			||||||
 | 
						  CCCH <- Immediate Assignment Message, specifies PDCH to be used.
 | 
				
			||||||
 | 
						  -> Packet Control Acknowledgement (if requested by network, used for timing advance)
 | 
				
			||||||
 | 
						PDCH <- Data, indicated by TFI
 | 
				
			||||||
 | 
						  All the modes same as for uplink.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					=========================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GSM MODE (old, I aborted this):
 | 
				
			||||||
 | 
						RACH -> Packet Channel Request (requests one block, may request one or two phase mode)
 | 
				
			||||||
 | 
						  (Mobile may send M+1 of these)
 | 
				
			||||||
 | 
						  Includes establishment cause which may be "answer to paging", or
 | 
				
			||||||
 | 
						  "procedures that can be completed with SDCCH"
 | 
				
			||||||
 | 
						  GPRS Channel Request Purposes are in 44.018 3.5.2.1.2
 | 
				
			||||||
 | 
						  GSM: After sending M+1, MS starts T3126, and if no answer, gives up.
 | 
				
			||||||
 | 
						  GPRS: After sending max number of Channel Request, MS starts T3146,
 | 
				
			||||||
 | 
						  after which is sends a Random Access Failure and looks for another cell.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Response may be any of:
 | 
				
			||||||
 | 
						PAGCH <- Packet Queuing Notification. (optional, used if heavy traffic,
 | 
				
			||||||
 | 
							only applicable on PCCCH, not CCCH)
 | 
				
			||||||
 | 
						AGCH <- Packet Uplink Assignment???
 | 
				
			||||||
 | 
						AGCH <- REJECT, which lets the MS look for another cell.
 | 
				
			||||||
 | 
						AGCH <- IMMEDIATE ASSIGNMENT, which either contains Packet Downlink Assignment,
 | 
				
			||||||
 | 
							or bit saying it is 2 part, in which case the real IMMEDIATE ASSIGNMENT
 | 
				
			||||||
 | 
							is sent within 2 multiframes.
 | 
				
			||||||
 | 
							The MS will then respond to IMMEDIATE ASSIGNMENT, and then can try to do its
 | 
				
			||||||
 | 
							uplink using the packet channel.
 | 
				
			||||||
 | 
							On CCCH, may send either IMMEDIATE ASSIGNMENT,must use Immediate Assignment.
 | 
				
			||||||
 | 
						Class A or B may receive <- Paging Request, which upon which the MS may abort the packet attempt.
 | 
				
			||||||
 | 
					Network Originated Transfer:
 | 
				
			||||||
 | 
					    If MS is in Standby state:
 | 
				
			||||||
 | 
					    	PCH <- Paging Request
 | 
				
			||||||
 | 
						RACH -> Channel Request
 | 
				
			||||||
 | 
						Mobile then does a Mobile Originated Packet Transfer, and sends a Packet Paging Response
 | 
				
			||||||
 | 
						to the network, which is an LLC frame.
 | 
				
			||||||
 | 
						CCCH [AGCH?] <- Packet Uplink Assignment (or Immediate Assignment)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						For two-phase access:
 | 
				
			||||||
 | 
						PACCH -> Packet Resource Request (optional, used for Fixed or Exclusive Allocation)
 | 
				
			||||||
 | 
						PACCH <- Packed Uplink Assignment (optional, used for Fixed or Exclusive Allocation)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PDTCH -> Packet Paging Response (LLC frame)
 | 
				
			||||||
 | 
						Now the MM (Mobility management) state of the MS is Ready.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    When MS is in Ready state:
 | 
				
			||||||
 | 
						If Packet uplink in progress:
 | 
				
			||||||
 | 
						  PACCH <- Packet Downlink Assignment Message, specifies PDCH to be used.
 | 
				
			||||||
 | 
						else:
 | 
				
			||||||
 | 
						  CCCH <- Immediate Assignment Message, specifies PDCH to be used.
 | 
				
			||||||
 | 
						-> Packet Control Acknowledgement (if requested by network, used for timing advance)
 | 
				
			||||||
 | 
						PDCH <- Data, indicated by TFI
 | 
				
			||||||
 | 
						  All the modes same as for uplink.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					============================================================================
 | 
				
			||||||
 | 
					Radio stuff:
 | 
				
			||||||
 | 
					GSMConfig.cpp:
 | 
				
			||||||
 | 
					TCHFACCHLogicalChannel *GSMConfig::getTCH()
 | 
				
			||||||
 | 
					getChan<TCHFACCHLogicalChannel>(mTCHPool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					L1Encoder, L2Decoder are ok
 | 
				
			||||||
 | 
					L1FEC is referenced by parent.
 | 
				
			||||||
							
								
								
									
										926
									
								
								GPRS/pinghttp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										926
									
								
								GPRS/pinghttp.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,926 @@
 | 
				
			|||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <getopt.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <signal.h>
 | 
				
			||||||
 | 
					#include <sys/fcntl.h>
 | 
				
			||||||
 | 
					#include <sys/stat.h>
 | 
				
			||||||
 | 
					#include <sys/socket.h>
 | 
				
			||||||
 | 
					#include <netinet/in.h>
 | 
				
			||||||
 | 
					#include <arpa/inet.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//#include <osmocom/core/talloc.h>
 | 
				
			||||||
 | 
					//#include <osmocom/core/select.h>
 | 
				
			||||||
 | 
					//#include <osmocom/core/rate_ctr.h>
 | 
				
			||||||
 | 
					//#include <openbsc/gsm_04_08_gprs.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//#include <openbsc/signal.h>
 | 
				
			||||||
 | 
					//#include <openbsc/debug.h>
 | 
				
			||||||
 | 
					//#include <openbsc/sgsn.h>
 | 
				
			||||||
 | 
					//#include <openbsc/gprs_llc.h>
 | 
				
			||||||
 | 
					//#include <openbsc/gprs_bssgp.h>
 | 
				
			||||||
 | 
					//#include <openbsc/gprs_sgsn.h>
 | 
				
			||||||
 | 
					//#include <openbsc/gprs_gmm.h>
 | 
				
			||||||
 | 
					//#include <openbsc/socket.h>	// pat added for make_sock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <netdb.h>			// pat added for gethostbyname
 | 
				
			||||||
 | 
					#include <netinet/ip.h>		// pat added for IPv4 iphdr
 | 
				
			||||||
 | 
					#include <netinet/tcp.h>		// pat added for tcphdr
 | 
				
			||||||
 | 
					#include <netinet/udp.h>		// pat added for udphdr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/if.h>			// pat added.
 | 
				
			||||||
 | 
					#include <linux/if_tun.h>			// pat added.
 | 
				
			||||||
 | 
					#include <sys/ioctl.h>			// pat added.
 | 
				
			||||||
 | 
					#include <assert.h>				// pat added
 | 
				
			||||||
 | 
					#include <stdarg.h>				// pat added
 | 
				
			||||||
 | 
					#include <time.h>				// pat added.
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#include <wait.h>
 | 
				
			||||||
 | 
					//#include "miniggsn.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef MG_CON_DEFINED
 | 
				
			||||||
 | 
					// MiniGGSN IP connections.  One of these structs for each PDP context.
 | 
				
			||||||
 | 
					// Each PDP context will be mapped to a new IP address.
 | 
				
			||||||
 | 
					// There should be a pointer to this struct in the sgsn_pdp_ctx,
 | 
				
			||||||
 | 
					// but I am trying to keep all changes local for now.
 | 
				
			||||||
 | 
					typedef struct mg_con_s {
 | 
				
			||||||
 | 
					    struct sgsn_pdp_ctx *mg_pctx;   // Points back to the PDP context using this connection.
 | 
				
			||||||
 | 
						uint32_t mg_ip;                 // The IP address used for this connection, in network order.
 | 
				
			||||||
 | 
						int inited;
 | 
				
			||||||
 | 
					} mg_con_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// From iputils.h:
 | 
				
			||||||
 | 
					char *ip_ntoa(int32_t ip, char *buf);
 | 
				
			||||||
 | 
					char *ip_sockaddr2a(void * /*struct sockaddr * */ sap,char *buf);
 | 
				
			||||||
 | 
					int ip_add_addr(char *ifname, int32_t ipaddr, int maskbits);
 | 
				
			||||||
 | 
					unsigned int ip_checksum(void *ptr, unsigned len, void *dummyhdr);
 | 
				
			||||||
 | 
					void ip_hdr_dump(unsigned char *packet, const char *msg);
 | 
				
			||||||
 | 
					int ip_tun_open(char *tname, char *addrstr);
 | 
				
			||||||
 | 
					void ip_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// These options are used only for standalone testing.
 | 
				
			||||||
 | 
					struct options_s {
 | 
				
			||||||
 | 
					    int bind;
 | 
				
			||||||
 | 
						int printall;	// Print entire tcp response.
 | 
				
			||||||
 | 
						int raw_bind;
 | 
				
			||||||
 | 
						char *from;
 | 
				
			||||||
 | 
						int v;
 | 
				
			||||||
 | 
						int use_tunnel;
 | 
				
			||||||
 | 
						int repeat;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					struct options_s options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if STANDALONE
 | 
				
			||||||
 | 
					char *tun_if_name = "mstun";
 | 
				
			||||||
 | 
					int tun_fd = -1;
 | 
				
			||||||
 | 
					struct options_s options;
 | 
				
			||||||
 | 
					static char *mg_base_ip_str = "192.168.99.1";	// This did not raw bind.
 | 
				
			||||||
 | 
					static char *mg_base_ip_route = "192.168.99.0/24";
 | 
				
			||||||
 | 
					#define MGERROR(...) {printf("error:"); printf(__VA_ARGS__);}
 | 
				
			||||||
 | 
					#define MGINFO(...) {printf(__VA_ARGS__);}
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					char *miniggsn_rcv_npdu(struct osmo_fd *bfd, int *error, int *plen) { return 0; }
 | 
				
			||||||
 | 
					int miniggsn_snd_npdu(struct sgsn_pdp_ctx *pctx,unsigned char *npdu, unsigned len) { return 0; }
 | 
				
			||||||
 | 
					int miniggsn_snd_npdu_by_mgc(mg_con_t *mgp,char *npdu, unsigned len) { return 0; }
 | 
				
			||||||
 | 
					void miniggsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx) {}
 | 
				
			||||||
 | 
					struct sgsn_pdp_ctx *miniggsn_create_pdp_conf(struct pdp_t *pdp,struct sgsn_pdp_ctx *pctx) { return 0;}
 | 
				
			||||||
 | 
					void miniggsn_init();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if STANDALONE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ip address is in network order;  return as dotted string.
 | 
				
			||||||
 | 
					char *ip_ntoa(int32_t ip, char *buf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static char sbuf[30];
 | 
				
			||||||
 | 
						if (buf == NULL) { buf = sbuf;}
 | 
				
			||||||
 | 
						ip = ntohl(ip);
 | 
				
			||||||
 | 
						sprintf(buf,"%d.%d.%d.%d",(ip>>24)&0xff,(ip>>16)&0xff,(ip>>8)&0xff,ip&0xff);
 | 
				
			||||||
 | 
						return buf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// IP standard checksum, see wikipedia "IPv4 Header"
 | 
				
			||||||
 | 
					// len is in bytes, will normally be 20 == sizeof(struct iphdr).
 | 
				
			||||||
 | 
					unsigned int ip_checksum(void *ptr, unsigned len, void *dummyhdr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						//if (len != 20) printf("WARNING: unexpected header length in ip_checksum\n");
 | 
				
			||||||
 | 
					    uint32_t i, sum = 0;
 | 
				
			||||||
 | 
					    uint16_t *pp = (uint16_t*)ptr;
 | 
				
			||||||
 | 
						while (len > 1) { sum += *pp++; len -= 2; }
 | 
				
			||||||
 | 
						if (len == 1) {
 | 
				
			||||||
 | 
							uint16_t foo = 0;
 | 
				
			||||||
 | 
							unsigned char *cp = (unsigned char*)&foo;
 | 
				
			||||||
 | 
							*cp = *(unsigned char *)pp;
 | 
				
			||||||
 | 
							sum += foo;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (dummyhdr) {	// For TCP and UDP the dummy header is 3 words = 6 shorts.
 | 
				
			||||||
 | 
							pp = (uint16_t*)dummyhdr;
 | 
				
			||||||
 | 
							for (i = 0; i < 6; i++) { sum += pp[i]; }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						//printf("intermediate sum=0x%x\n",sum);
 | 
				
			||||||
 | 
					    // Convert from 2s complement to 1s complement:
 | 
				
			||||||
 | 
						//sum = ((sum >> 16)&0xffff) + (sum & 0xffff); /* add hi 16 to low 16 */
 | 
				
			||||||
 | 
						sum = ((sum >> 16)) + (sum & 0xffff); /* add hi 16 to low 16 */
 | 
				
			||||||
 | 
						sum += (sum >> 16);         /* add carry */
 | 
				
			||||||
 | 
						//printf("intermediate sum16=0x%x result=0x%x\n",sum,~sum);
 | 
				
			||||||
 | 
					    return 0xffff & ~sum;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ip_hdr_dump(unsigned char *packet, const char *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct iptcp {	// This is only accurate if no ip options specified.
 | 
				
			||||||
 | 
							struct iphdr ip;
 | 
				
			||||||
 | 
							struct tcphdr tcp;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						struct iptcp *pp = (struct iptcp*)packet;
 | 
				
			||||||
 | 
						char nbuf[100];
 | 
				
			||||||
 | 
						printf("%s: ",msg);
 | 
				
			||||||
 | 
						printf("%d bytes protocol=%d saddr=%s daddr=%s version=%d ihl=%d tos=%d id=%d\n",
 | 
				
			||||||
 | 
							ntohs(pp->ip.tot_len),pp->ip.protocol,ip_ntoa(pp->ip.saddr,nbuf),ip_ntoa(pp->ip.daddr,NULL),
 | 
				
			||||||
 | 
							pp->ip.version,pp->ip.ihl,pp->ip.tos, ntohs(pp->ip.id));
 | 
				
			||||||
 | 
						printf("\tcheck=%d computed=%d frag=%d ttl=%d\n",
 | 
				
			||||||
 | 
							pp->ip.check,ip_checksum(packet,sizeof(struct iphdr),NULL),
 | 
				
			||||||
 | 
							ntohs(pp->ip.frag_off),pp->ip.ttl);
 | 
				
			||||||
 | 
						printf("\ttcp SYN=%d ACK=%d FIN=%d RES=%d sport=%u dport=%u\n",
 | 
				
			||||||
 | 
							pp->tcp.syn,pp->tcp.ack,pp->tcp.fin,pp->tcp.rst,
 | 
				
			||||||
 | 
							ntohs(pp->tcp.source),ntohs(pp->tcp.dest));
 | 
				
			||||||
 | 
						printf("\t\tseq=%u ackseq=%u window=%u check=%u\n",
 | 
				
			||||||
 | 
							ntohl(pp->tcp.seq),ntohl(pp->tcp.ack_seq),htons(pp->tcp.window),htons(pp->tcp.check));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					char *ip_sockaddr2a(void * /*struct sockaddr * */ sap,char *buf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static char sbuf[100];
 | 
				
			||||||
 | 
						if (buf == NULL) { buf = sbuf;}
 | 
				
			||||||
 | 
						struct sockaddr_in *to = (struct sockaddr_in*)sap;
 | 
				
			||||||
 | 
						sprintf(buf,"proto=%d%s port=%d ip=%s",
 | 
				
			||||||
 | 
							ntohs(to->sin_family),
 | 
				
			||||||
 | 
							to->sin_family == AF_INET ? "(AF_INET)" : "",
 | 
				
			||||||
 | 
							ntohs(to->sin_port),
 | 
				
			||||||
 | 
							ip_ntoa(to->sin_addr.s_addr,NULL));
 | 
				
			||||||
 | 
						return buf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// Run the command.
 | 
				
			||||||
 | 
					// This is not ip specific, but used to call linux "ip" and "route" commands.
 | 
				
			||||||
 | 
					// If commands starts with '|', capture stdout and return the file descriptor to read it.
 | 
				
			||||||
 | 
					int runcmd(const char *path, ...)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int pipefd[2];
 | 
				
			||||||
 | 
						int dopipe = 0;
 | 
				
			||||||
 | 
						if (*path == '|') {
 | 
				
			||||||
 | 
							path++;
 | 
				
			||||||
 | 
							dopipe = 1;
 | 
				
			||||||
 | 
							if (pipe(pipefd) == -1) {
 | 
				
			||||||
 | 
								MGERROR("could not create pipe: %s",strerror(errno));
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						int pid = fork();
 | 
				
			||||||
 | 
						if (pid == -1) {
 | 
				
			||||||
 | 
							MGERROR("could not fork: %s",strerror(errno));
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (pid) {	// This is the parent; wait for child to exit, then return childs status.
 | 
				
			||||||
 | 
							int status;
 | 
				
			||||||
 | 
							if (dopipe) {
 | 
				
			||||||
 | 
								close(pipefd[1]); // Close unused write end of pipe.
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							waitpid(pid,&status,0);
 | 
				
			||||||
 | 
							return dopipe ? pipefd[0] : status;	// Return read end of pipe, if piped.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// This is the child process.
 | 
				
			||||||
 | 
						if (dopipe) {
 | 
				
			||||||
 | 
							close(pipefd[0]); // Close unused read end of pipe.
 | 
				
			||||||
 | 
							dup2(pipefd[1],1);	// Capture stdout to pipe.
 | 
				
			||||||
 | 
							close(pipefd[1]);	// Close now redundant fd.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Gather args into argc,argv;
 | 
				
			||||||
 | 
						int argc = 0; char *argv[100];
 | 
				
			||||||
 | 
						va_list ap;
 | 
				
			||||||
 | 
						va_start(ap, path);
 | 
				
			||||||
 | 
						do {
 | 
				
			||||||
 | 
							argv[argc] = va_arg(ap,char*);
 | 
				
			||||||
 | 
						} while (argv[argc++]);
 | 
				
			||||||
 | 
						argv[argc] = NULL;
 | 
				
			||||||
 | 
						va_end(ap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Print them out.
 | 
				
			||||||
 | 
						// But dont print if piped, because it goes into the pipe!
 | 
				
			||||||
 | 
						if (! dopipe) {
 | 
				
			||||||
 | 
							char buf[208], *bp = buf, *ep = &buf[200];
 | 
				
			||||||
 | 
							int i;
 | 
				
			||||||
 | 
							for (i = 0; argv[i]; i++) {
 | 
				
			||||||
 | 
								int len = strlen(argv[i]);
 | 
				
			||||||
 | 
								if (bp + len > ep) { strcpy(bp,"..."); break; }
 | 
				
			||||||
 | 
								strcpy(bp,argv[i]);
 | 
				
			||||||
 | 
								bp += len;
 | 
				
			||||||
 | 
								*bp++ = ' ';
 | 
				
			||||||
 | 
								*bp = 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							buf[200] = 0;
 | 
				
			||||||
 | 
							MGINFO("%s",buf);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// exec them.
 | 
				
			||||||
 | 
						execv(path,argv);
 | 
				
			||||||
 | 
						exit(2);	// Just in case.
 | 
				
			||||||
 | 
						return 0;	// This is never used.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// Return an array of ip addresses, terminated by a -1 address.
 | 
				
			||||||
 | 
					uint32_t *ip_findmyaddr()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#define maxaddrs 5
 | 
				
			||||||
 | 
						static uint32_t addrs[maxaddrs+1];
 | 
				
			||||||
 | 
						int n = 0;
 | 
				
			||||||
 | 
						int fd = runcmd("|/bin/hostname","hostname","-I", NULL);
 | 
				
			||||||
 | 
						if (fd < 0) {
 | 
				
			||||||
 | 
							failed:
 | 
				
			||||||
 | 
							addrs[0] = (unsigned) -1;	// converts to all 1s
 | 
				
			||||||
 | 
							return addrs;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						//printf("ip_findmyaddr fd=%d\n",fd);
 | 
				
			||||||
 | 
						const int bufsize = 2000;
 | 
				
			||||||
 | 
						char buf[bufsize];
 | 
				
			||||||
 | 
						int size = read(fd,buf,bufsize);
 | 
				
			||||||
 | 
						if (size < 0) { goto failed; }
 | 
				
			||||||
 | 
						buf[bufsize-1] = 0;
 | 
				
			||||||
 | 
						if (size < bufsize) { buf[size] = 0; }
 | 
				
			||||||
 | 
						char *cp = buf;
 | 
				
			||||||
 | 
						//printf("ip_findmyaddr buf=%s\n",buf);
 | 
				
			||||||
 | 
						while (n < maxaddrs) {
 | 
				
			||||||
 | 
							char *ep = strchr(cp,'\n');
 | 
				
			||||||
 | 
							if (ep) *ep = 0;
 | 
				
			||||||
 | 
							if (strlen(cp)) {
 | 
				
			||||||
 | 
								uint32_t addr = inet_addr(cp);
 | 
				
			||||||
 | 
								//printf("ip_findmyaddr cp=%s = 0x%x\n",cp,addr);
 | 
				
			||||||
 | 
								if (addr != 0 && addr != (unsigned)-1) {
 | 
				
			||||||
 | 
									addrs[n++] = inet_addr(cp);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (!ep) {
 | 
				
			||||||
 | 
								addrs[n] = (unsigned) -1;	// terminate the list.
 | 
				
			||||||
 | 
								return addrs;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							cp = ep+1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						addrs[maxaddrs] = (unsigned) -1;	// converts to all 1s
 | 
				
			||||||
 | 
						return addrs;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					int ip_tun_open(char *tname, char *addrstr) { assert(0); }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						int tot_len;
 | 
				
			||||||
 | 
						struct iphdr *ip;		// Pointer to ip header in buf ( == buf).
 | 
				
			||||||
 | 
						struct tcphdr *tcp;		// Pointer to tcp header in buf, if it is tcp.
 | 
				
			||||||
 | 
						struct udphdr *udp;		// Pointer to udp header in buf, if it is udp.
 | 
				
			||||||
 | 
						char *payload;			// Pointer to data in buf.
 | 
				
			||||||
 | 
						char storage[0];		// Storage for packet data, if this packet_t was malloced.
 | 
				
			||||||
 | 
					} packet_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return -1 on error
 | 
				
			||||||
 | 
					static int ip_findaddr(char *spec, struct sockaddr *addr, char *hostname)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sockaddr_in *to = (struct sockaddr_in*)addr;
 | 
				
			||||||
 | 
						memset(addr, 0, sizeof(struct sockaddr));
 | 
				
			||||||
 | 
					    to->sin_family = AF_INET;
 | 
				
			||||||
 | 
						if (inet_aton(spec,&to->sin_addr)) {
 | 
				
			||||||
 | 
							if (hostname) strcpy(hostname,spec);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							//alternate: getaddrinfo()
 | 
				
			||||||
 | 
					        struct hostent *hp = gethostbyname(spec);	// func is deprecated.
 | 
				
			||||||
 | 
					        if (hp) {
 | 
				
			||||||
 | 
					            to->sin_family = hp->h_addrtype;
 | 
				
			||||||
 | 
					            memcpy(&to->sin_addr,hp->h_addr, hp->h_length);
 | 
				
			||||||
 | 
								if (hostname) strcpy(hostname,hp->h_name);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
								return -1;	// failure
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
						return 0;	// ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Add an ip header to the packet and return in malloced memory.
 | 
				
			||||||
 | 
					// ipsrc,ipdst,srcport,dstport in network order.
 | 
				
			||||||
 | 
					// payload may be NULL to just send the header with flags.
 | 
				
			||||||
 | 
					packet_t *
 | 
				
			||||||
 | 
					ip_add_hdr2(int protocol, uint32_t ipsrc, uint32_t ipdst,
 | 
				
			||||||
 | 
						char *payload, struct tcphdr *tcpin, unsigned ipid)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int hdrsize = sizeof(struct iphdr);
 | 
				
			||||||
 | 
						int paylen = payload ? strlen(payload) : 0;
 | 
				
			||||||
 | 
						struct tcphdr *tcp;
 | 
				
			||||||
 | 
						struct udphdr *udp;
 | 
				
			||||||
 | 
						switch (protocol) {
 | 
				
			||||||
 | 
							case IPPROTO_TCP: hdrsize += sizeof(struct tcphdr); break;
 | 
				
			||||||
 | 
							case IPPROTO_UDP: hdrsize += sizeof(struct udphdr); break;
 | 
				
			||||||
 | 
							default: break; // Assume must ip header.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						struct {	// dummy ip header, including just part of the IP header, added to tcp checksum.
 | 
				
			||||||
 | 
							uint32_t saddr, daddr;
 | 
				
			||||||
 | 
							uint8_t zeros;
 | 
				
			||||||
 | 
							uint8_t protocol;
 | 
				
			||||||
 | 
							uint16_t tcp_len; // This is in network order!
 | 
				
			||||||
 | 
						} tcpdummyhdr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						packet_t *result = malloc(sizeof(packet_t) + hdrsize + paylen + 3);
 | 
				
			||||||
 | 
						struct iphdr *packet_header = (struct iphdr*)result->storage;
 | 
				
			||||||
 | 
						result->ip = packet_header;
 | 
				
			||||||
 | 
						result->tcp = (struct tcphdr*)((char*)result->storage + sizeof(struct iphdr));
 | 
				
			||||||
 | 
						result->udp = (struct udphdr*)((char*)result->storage + sizeof(struct iphdr));
 | 
				
			||||||
 | 
						result->payload = result->storage + hdrsize;
 | 
				
			||||||
 | 
						result->tot_len = hdrsize + paylen;
 | 
				
			||||||
 | 
						//while (result->tot_len & 0x3) { result->storage[result->tot_len++] = 0; }
 | 
				
			||||||
 | 
						memset(packet_header,0,hdrsize);
 | 
				
			||||||
 | 
						if (payload) memcpy(result->payload,payload,paylen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						packet_header->ihl = 5;
 | 
				
			||||||
 | 
						packet_header->version = 4;
 | 
				
			||||||
 | 
						// tos, id, frag_off == 0
 | 
				
			||||||
 | 
						packet_header->ttl = 64;
 | 
				
			||||||
 | 
						packet_header->id = ipid;
 | 
				
			||||||
 | 
						packet_header->protocol = protocol;
 | 
				
			||||||
 | 
						packet_header->saddr = ipsrc;
 | 
				
			||||||
 | 
						packet_header->daddr = ipdst;
 | 
				
			||||||
 | 
						packet_header->tot_len = htons(result->tot_len);
 | 
				
			||||||
 | 
						packet_header->check = ip_checksum(packet_header,sizeof(*packet_header),NULL);
 | 
				
			||||||
 | 
						// Double check:
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						if (0 != ip_checksum(packet_header,sizeof(*packet_header),NULL)) {
 | 
				
			||||||
 | 
							printf("WARNING: computed checksum invalid: check=%d computed=%d\n",
 | 
				
			||||||
 | 
								packet_header->check,ip_checksum(packet_header,sizeof(*packet_header),NULL));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (protocol) {
 | 
				
			||||||
 | 
							case IPPROTO_UDP:
 | 
				
			||||||
 | 
								assert(0);	// unimplemented.
 | 
				
			||||||
 | 
								udp = result->udp;
 | 
				
			||||||
 | 
								udp->len = htons(paylen + sizeof(*udp));
 | 
				
			||||||
 | 
								//udp->source = srcport;
 | 
				
			||||||
 | 
								//udp->dest = dstport;
 | 
				
			||||||
 | 
								// Checksum includes udp header plus IP pseudo-header!
 | 
				
			||||||
 | 
								// But checksum is optional, so just set to 0.
 | 
				
			||||||
 | 
								udp->check = 0;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case IPPROTO_TCP:
 | 
				
			||||||
 | 
								tcp = result->tcp;
 | 
				
			||||||
 | 
								memcpy(tcp,tcpin,sizeof(struct tcphdr));
 | 
				
			||||||
 | 
								tcp->doff = 5;		// tcp header size in words.
 | 
				
			||||||
 | 
								tcp->window = htons(0x1111);
 | 
				
			||||||
 | 
								// create the dummy header.
 | 
				
			||||||
 | 
								tcpdummyhdr.saddr = packet_header->saddr;
 | 
				
			||||||
 | 
								tcpdummyhdr.daddr = packet_header->daddr;
 | 
				
			||||||
 | 
								tcpdummyhdr.zeros = 0;
 | 
				
			||||||
 | 
								tcpdummyhdr.protocol = protocol;
 | 
				
			||||||
 | 
								// This length excludes the length of the ip header.
 | 
				
			||||||
 | 
								unsigned tcplen = result->tot_len - sizeof(struct iphdr);
 | 
				
			||||||
 | 
								tcpdummyhdr.tcp_len = htons(tcplen);
 | 
				
			||||||
 | 
								tcp->check = ip_checksum(tcp,tcplen,&tcpdummyhdr);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
								// successful options were: <mss 1460,sackOK,timestamp 7124617 0,nop,wscale 7>
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void pinghttpPrintReply(char *result,int len,char*hostname,int recvcnt)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (len < 0) { printf("unexpected result len: %d\n",len); return; }
 | 
				
			||||||
 | 
						int plen = len;	// how much to print
 | 
				
			||||||
 | 
						if (0 == recvcnt) {
 | 
				
			||||||
 | 
							printf("pinghttp: %s replies %d bytes:\n--->",hostname,len);
 | 
				
			||||||
 | 
							if (!options.printall) {
 | 
				
			||||||
 | 
								// Prune the response a little.
 | 
				
			||||||
 | 
								// Just print the first line.
 | 
				
			||||||
 | 
								char *c1 = memchr(result,'\n',len);
 | 
				
			||||||
 | 
								if (c1 && c1-result < len) plen = c1-result;
 | 
				
			||||||
 | 
								printf("%.*s\n",plen,result);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (options.printall) {
 | 
				
			||||||
 | 
							printf("%.*s\n",plen,result);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int pinghttpsend(packet_t *packet,mg_con_t *mgp,int sfd,struct sockaddr *toaddr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						usleep(100000);
 | 
				
			||||||
 | 
						if (options.v) ip_hdr_dump(packet->storage,"sending:");
 | 
				
			||||||
 | 
						//struct iphdr *pip = (struct iphdr*)packet->s;
 | 
				
			||||||
 | 
						//printf("packet->len = %d tot_len=%d\n",packet->tot_len,ntohs(pip->tot_len));
 | 
				
			||||||
 | 
						if (options.use_tunnel) {
 | 
				
			||||||
 | 
							rc = write(tun_fd,packet->storage,packet->tot_len);
 | 
				
			||||||
 | 
						} else if (mgp) {
 | 
				
			||||||
 | 
					#ifndef STANDALONE
 | 
				
			||||||
 | 
							rc = miniggsn_snd_npdu_by_mgc(mgp,packet->storage,packet->tot_len);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// Note: when I left the non-raw socket connected above and fell through
 | 
				
			||||||
 | 
							// to this code, it looked like the raw socket may not receive on this port until
 | 
				
			||||||
 | 
							// the connected socket is closed, not sure.
 | 
				
			||||||
 | 
							rc = sendto(sfd,packet->storage,packet->tot_len,0,toaddr,sizeof(struct sockaddr));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							printf("sendto failed: %s\n",strerror(errno));
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Ping using http port 80, see if anything is there.
 | 
				
			||||||
 | 
					// It is not a real 'ping', which uses ICMP protocol.
 | 
				
			||||||
 | 
					// This implements a subset of the TCP handshaking protocol.
 | 
				
			||||||
 | 
					// We assume there is only data packet (the httpget message) and we assume
 | 
				
			||||||
 | 
					// all packets are delivered, ie, no retries.
 | 
				
			||||||
 | 
					// It runs the protocol all the way to the final closure (FIN) acknowledgements,
 | 
				
			||||||
 | 
					// to make sure the connection is completely closed.  Otherwise you can only
 | 
				
			||||||
 | 
					// run one of these tests to any host until that host times our connection out.
 | 
				
			||||||
 | 
					int pinghttp(char *whoto,char *whofrom,mg_con_t *mgp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sockaddr toaddr;
 | 
				
			||||||
 | 
						struct sockaddr_in *to = (struct sockaddr_in*) &toaddr;
 | 
				
			||||||
 | 
						uint16_t dstport = htons(80);	// Connect to HTTP port 80.
 | 
				
			||||||
 | 
						char hostname[300];
 | 
				
			||||||
 | 
						int one = 1;
 | 
				
			||||||
 | 
						//char nbuf[100];
 | 
				
			||||||
 | 
						const int rbufsize = 1500;
 | 
				
			||||||
 | 
						char rbuf[rbufsize+1];
 | 
				
			||||||
 | 
						int sfd = -1;		// socket fd
 | 
				
			||||||
 | 
						int raw_mode = mgp || options.use_tunnel || whofrom;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ip_findaddr(whoto,&toaddr,hostname)) {
 | 
				
			||||||
 | 
							printf("pinghttp: unknown to host %s\n", whoto);
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						to->sin_port = dstport;		
 | 
				
			||||||
 | 
						if (toaddr.sa_family == AF_INET) {
 | 
				
			||||||
 | 
							printf("pinghost %s AF_INET addr %s\n",hostname,inet_ntoa(to->sin_addr));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						char httpget[300];
 | 
				
			||||||
 | 
						//sprintf(httpget, "GET /index.html HTTP/1.1\r\nHost: %s\r\n\r\n",hostname);
 | 
				
			||||||
 | 
						sprintf(httpget, "GET /index.html HTTP/1.1\r\nHost: localhost\r\n\r\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//sprintf(httpget, "GET /index.html HTTP/1.1\r\nHost: %s\r\n\r\n\r\n",inet_ntoa(to->sin_addr));
 | 
				
			||||||
 | 
						//sprintf(httpget, "GET /index.html\r\n\r\n");
 | 
				
			||||||
 | 
						//sprintf(httpget, "GET /index.html HTTP/1.0\r\n\r\n\r\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (! raw_mode) {
 | 
				
			||||||
 | 
							// Simple pinghttp version uses a regular socket and connect.
 | 
				
			||||||
 | 
							// The kernel handles IP headers and TCP handshakes.
 | 
				
			||||||
 | 
							// Send the httpget message, print any reply.
 | 
				
			||||||
 | 
							sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 | 
				
			||||||
 | 
							if (sfd < 0) { printf("pinghttp: socket() failed: %s\n",strerror(errno)); return 2; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (connect(sfd,&toaddr,sizeof(struct sockaddr))) {
 | 
				
			||||||
 | 
								close(sfd);
 | 
				
			||||||
 | 
								printf("pinghttp: connect() to %s failed: %s\n",hostname,strerror(errno));
 | 
				
			||||||
 | 
								return 2;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (0) {
 | 
				
			||||||
 | 
								// Out of interest, get the srcport assigned by connect above.
 | 
				
			||||||
 | 
								struct sockaddr sockname; socklen_t socknamelen = sizeof(struct sockaddr);
 | 
				
			||||||
 | 
								getsockname(sfd,&sockname,&socknamelen);
 | 
				
			||||||
 | 
								// Add one to the port, just hope it is free.
 | 
				
			||||||
 | 
								//srcport = htons(ntohs(((struct sockaddr_in*)&sockname)->sin_port) + 10);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (options.v) printf("pinghttp to %s send %s\n",hostname,httpget); 
 | 
				
			||||||
 | 
							rc = send(sfd,httpget,strlen(httpget),0);
 | 
				
			||||||
 | 
							if (rc < 0) { printf("pinghttp: send failed: %s\n",strerror(errno)); return 2; }
 | 
				
			||||||
 | 
							rc = recv(sfd,rbuf,rbufsize,0);
 | 
				
			||||||
 | 
							if (rc <= 0 || rc > rbufsize) {
 | 
				
			||||||
 | 
								printf("pinghttp: recv failed rc=%d error:%s\n",rc,strerror(errno)); return 2;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							pinghttpPrintReply(rbuf,rc,hostname,0);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// We are going to use raw mode.
 | 
				
			||||||
 | 
						// We will add the IP headers to the packet, and handle TCP handshake ourselves.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint32_t dstip = to->sin_addr.s_addr;
 | 
				
			||||||
 | 
						uint32_t srcip = 0;
 | 
				
			||||||
 | 
						packet_t reply;
 | 
				
			||||||
 | 
						uint16_t srcport = htons(4000 + (time(NULL) & 0xfff)); //made up
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (whofrom) {
 | 
				
			||||||
 | 
							//srcip = inet_addr(whofrom);	// only allows IP numbers.
 | 
				
			||||||
 | 
							//if (srcip == INADDR_NONE) {
 | 
				
			||||||
 | 
							//	printf("pinghttp: Cannot find specified from address: %s\n",whofrom);
 | 
				
			||||||
 | 
							//	return 2;
 | 
				
			||||||
 | 
							//}
 | 
				
			||||||
 | 
							struct sockaddr fromaddr;
 | 
				
			||||||
 | 
							char fromhostname[300];
 | 
				
			||||||
 | 
							if (ip_findaddr(whofrom,&fromaddr,fromhostname)) {
 | 
				
			||||||
 | 
								printf("pinghttp: unknown from host %s\n", whofrom);
 | 
				
			||||||
 | 
								return 2;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							srcip = ((struct sockaddr_in*)&fromaddr)->sin_addr.s_addr;	// already swizzled.
 | 
				
			||||||
 | 
						} else if (mgp) {
 | 
				
			||||||
 | 
							srcip = mgp->mg_ip;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							assert(0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (whofrom && !(mgp || options.use_tunnel)) {
 | 
				
			||||||
 | 
							// open raw socket
 | 
				
			||||||
 | 
							// If it going through nat, we should be able to pick nearly any port,
 | 
				
			||||||
 | 
							// and the nat will change it if it conflicts.
 | 
				
			||||||
 | 
							// NOTE: When you use RAW sockets, the kernel tcp driver will become confused
 | 
				
			||||||
 | 
							// by this tcp traffic and insert packets into the stream to goof it up.
 | 
				
			||||||
 | 
							// To prevent that use:
 | 
				
			||||||
 | 
							// iptables -A OUTPUT -p tcp --tcp-flags RST RST -j DROP
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							sfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
 | 
				
			||||||
 | 
							if (sfd < 0) { printf("cannot open raw socket\n"); return 2;}
 | 
				
			||||||
 | 
							setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&one,sizeof(one));
 | 
				
			||||||
 | 
							setsockopt(sfd,IPPROTO_IP,IP_HDRINCL,&one,sizeof(one));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Binding is unnecessary to simply read/write the socket.
 | 
				
			||||||
 | 
							if (options.bind) {
 | 
				
			||||||
 | 
								struct sockaddr_in bindto;
 | 
				
			||||||
 | 
								memset(&bindto,0,sizeof(bindto));
 | 
				
			||||||
 | 
								bindto.sin_family = AF_INET;
 | 
				
			||||||
 | 
								bindto.sin_port = 0;
 | 
				
			||||||
 | 
								bindto.sin_addr.s_addr = srcip;
 | 
				
			||||||
 | 
								if (bind(sfd,(struct sockaddr*)&bindto,sizeof(bindto))) {
 | 
				
			||||||
 | 
									printf("bindto %s failed\n",ip_sockaddr2a(&bindto,NULL));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Try using bindtodevice on the receive rfd.
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
								char *dummy = "foo";
 | 
				
			||||||
 | 
								rfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
 | 
				
			||||||
 | 
								if (rfd < 0) { printf("cannot open raw socket\n"); return 2;}
 | 
				
			||||||
 | 
								setsockopt(rfd,SOL_SOCKET,SO_REUSEADDR,&one,sizeof(one));
 | 
				
			||||||
 | 
								setsockopt(rfd,IPPROTO_IP,IP_HDRINCL,&one,sizeof(one));
 | 
				
			||||||
 | 
								if (setsockopt(rfd,SOL_SOCKET,SO_BINDTODEVICE,dummy,strlen(dummy)+1)) {
 | 
				
			||||||
 | 
									printf("BINDTODEVICE failed\n");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						int rfd = sfd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// seqloc is our sequence number.  seqrem is the servers sequence number.
 | 
				
			||||||
 | 
						//unsigned seqloc = 2580167164u;		// got from a successful TCP session.
 | 
				
			||||||
 | 
						//unsigned seqloc = 1580160000u + (rand() & 0xffff);
 | 
				
			||||||
 | 
						unsigned startloc = time(NULL)*9;// + (rand() & 0xffff);
 | 
				
			||||||
 | 
						unsigned seqloc = startloc;
 | 
				
			||||||
 | 
						//unsigned seqloc_syn = 0;		// seqloc of SYN sent.
 | 
				
			||||||
 | 
						unsigned seqloc_fin = 0;		// seqloc of FIN sent.
 | 
				
			||||||
 | 
						unsigned seqrem = 0;
 | 
				
			||||||
 | 
						unsigned seg_ack = 0;
 | 
				
			||||||
 | 
						int sendid = 1;
 | 
				
			||||||
 | 
						unsigned ipid = 0xfff & (time(NULL) * 9);
 | 
				
			||||||
 | 
						struct tcphdr tcpb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// States from RFC 793 page 22
 | 
				
			||||||
 | 
						enum {
 | 
				
			||||||
 | 
							tcp_start,
 | 
				
			||||||
 | 
							tcp_syn_sent,
 | 
				
			||||||
 | 
							tcp_syn_received,
 | 
				
			||||||
 | 
							tcp_established,
 | 
				
			||||||
 | 
							tcp_fin_wait1,
 | 
				
			||||||
 | 
							tcp_fin_wait2,
 | 
				
			||||||
 | 
							tcp_close_wait,
 | 
				
			||||||
 | 
							tcp_closing,
 | 
				
			||||||
 | 
							tcp_last_ack,
 | 
				
			||||||
 | 
							tcp_time_wait,
 | 
				
			||||||
 | 
							tcp_closed,
 | 
				
			||||||
 | 
							tcp_data_finished,	// State added to initiate close connect on our side.
 | 
				
			||||||
 | 
						} state = tcp_start;
 | 
				
			||||||
 | 
						char *state_name[20];
 | 
				
			||||||
 | 
						state_name[tcp_start] = "tcp_start";
 | 
				
			||||||
 | 
						state_name[tcp_syn_sent] = "tcp_syn_sent";
 | 
				
			||||||
 | 
						state_name[tcp_syn_received] = "tcp_syn_received";
 | 
				
			||||||
 | 
						state_name[tcp_established] = "tcp_established";
 | 
				
			||||||
 | 
						state_name[tcp_fin_wait1] = "tcp_fin_wait1";
 | 
				
			||||||
 | 
						state_name[tcp_fin_wait2] = "tcp_fin_wait2";
 | 
				
			||||||
 | 
						state_name[tcp_close_wait] = "tcp_close_wait";
 | 
				
			||||||
 | 
						state_name[tcp_closing] = "tcp_closing";
 | 
				
			||||||
 | 
						state_name[tcp_last_ack] = "tcp_last_ack";
 | 
				
			||||||
 | 
						state_name[tcp_time_wait] = "tcp_time_wait";
 | 
				
			||||||
 | 
						state_name[tcp_closed] = "tcp_closed";
 | 
				
			||||||
 | 
						state_name[tcp_data_finished] = "tcp_data_finished";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reply.tcp = 0;	// unnecessary, makes gcc happy.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enum { SYN = 1, FIN = 2 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int tcpsend(int flags, char *payload) {
 | 
				
			||||||
 | 
							memset(&tcpb,0,sizeof(tcpb));
 | 
				
			||||||
 | 
							tcpb.source = srcport;
 | 
				
			||||||
 | 
							tcpb.dest = dstport;
 | 
				
			||||||
 | 
							tcpb.seq = htonl(seqloc);
 | 
				
			||||||
 | 
							tcpb.ack_seq = htonl(seqrem);	// probably not right.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							int next_seqloc = seqloc;
 | 
				
			||||||
 | 
							packet_t *packet;
 | 
				
			||||||
 | 
							if (flags & SYN) {
 | 
				
			||||||
 | 
								tcpb.syn = 1;
 | 
				
			||||||
 | 
								next_seqloc++;
 | 
				
			||||||
 | 
								//seqloc_syn = next_seqloc;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								tcpb.ack = 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (flags & FIN) {
 | 
				
			||||||
 | 
								tcpb.fin = 1;
 | 
				
			||||||
 | 
								next_seqloc++;
 | 
				
			||||||
 | 
								seqloc_fin = next_seqloc;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (payload) {
 | 
				
			||||||
 | 
								tcpb.psh = 1;
 | 
				
			||||||
 | 
								next_seqloc += strlen(payload);
 | 
				
			||||||
 | 
								// the packet id for you.  How kind of it.
 | 
				
			||||||
 | 
								if (options.use_tunnel && !sendid) {
 | 
				
			||||||
 | 
									sendid = 1;
 | 
				
			||||||
 | 
									ipid = (time(NULL) & 0xfff)  + (rand() & 0xfff);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							packet = ip_add_hdr2(IPPROTO_TCP, srcip, dstip, payload,&tcpb,htons(ipid));
 | 
				
			||||||
 | 
							if (pinghttpsend(packet,mgp,sfd,&toaddr)) return 1;
 | 
				
			||||||
 | 
							if (sendid) { ipid++; }
 | 
				
			||||||
 | 
							seqloc = next_seqloc;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						} // tcpsend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int tries = 0;
 | 
				
			||||||
 | 
						int recvcnt = 0;
 | 
				
			||||||
 | 
						while (tries++ < 20) {
 | 
				
			||||||
 | 
							int prevstate = state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (state) {
 | 
				
			||||||
 | 
							case tcp_start:
 | 
				
			||||||
 | 
								state = tcp_syn_sent;
 | 
				
			||||||
 | 
								tcpsend(SYN,NULL);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case tcp_syn_sent:
 | 
				
			||||||
 | 
								if (reply.tcp->syn) {
 | 
				
			||||||
 | 
									// OK, now gotta send back an ack without data first.
 | 
				
			||||||
 | 
									// We dont inc seqrem here - that is now done after reply below.
 | 
				
			||||||
 | 
									// seqrem++;		// First byte is number 1, not number 0.
 | 
				
			||||||
 | 
									if (reply.tcp->ack) {
 | 
				
			||||||
 | 
										// If you dont send this first naked ACK, the host responds with another SYN.
 | 
				
			||||||
 | 
										tcpsend(0,NULL);
 | 
				
			||||||
 | 
										// If you dont send the data now, host times out waiting for something to do.
 | 
				
			||||||
 | 
										state = tcp_established;
 | 
				
			||||||
 | 
										goto send_data;
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										// We have to wait for an ACK.
 | 
				
			||||||
 | 
										state = tcp_syn_received;
 | 
				
			||||||
 | 
										tcpsend(0,NULL);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// We were waiting for a SYN but did not get it.  Oops.
 | 
				
			||||||
 | 
									printf("in tcp state syn_sent expecting SYN but did not get it?\n");
 | 
				
			||||||
 | 
									// This happens if a previous connection is still open.
 | 
				
			||||||
 | 
									// go listen some more
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case tcp_syn_received:
 | 
				
			||||||
 | 
								if (reply.tcp->ack) {
 | 
				
			||||||
 | 
									//seqrem++;		// First byte is number 1, not number 0.
 | 
				
			||||||
 | 
									state = tcp_established;
 | 
				
			||||||
 | 
									tcpsend(0,httpget);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									printf("int tcp state syn_received expecting ACK but did not get it?\n");
 | 
				
			||||||
 | 
									// go listen some more
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case tcp_established:
 | 
				
			||||||
 | 
								if (reply.tcp->fin) {
 | 
				
			||||||
 | 
									state = tcp_close_wait;
 | 
				
			||||||
 | 
									tcpsend(FIN,NULL);
 | 
				
			||||||
 | 
									if (seg_ack <= startloc+1) {
 | 
				
			||||||
 | 
										printf("remote connection closed before we could send data\n");
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if (seg_ack <= startloc+1) {
 | 
				
			||||||
 | 
									send_data:
 | 
				
			||||||
 | 
									tcpsend(0,httpget); // Now send or resend the data;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// We only have one packet to send, so if we got it then we are done.
 | 
				
			||||||
 | 
									// Advance seqloc
 | 
				
			||||||
 | 
									//seqloc = seg_ack;  moved to after reply.
 | 
				
			||||||
 | 
									state = tcp_fin_wait1;
 | 
				
			||||||
 | 
									tcpsend(FIN,NULL);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case tcp_data_finished:
 | 
				
			||||||
 | 
								state = tcp_fin_wait1;
 | 
				
			||||||
 | 
								tcpsend(FIN,NULL);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case tcp_fin_wait1:
 | 
				
			||||||
 | 
								// We sent a FIN, now we have to wait for a FIN or ACK back.
 | 
				
			||||||
 | 
								if (reply.tcp->fin) {
 | 
				
			||||||
 | 
									state = tcp_closing;
 | 
				
			||||||
 | 
									tcpsend(0,NULL);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									assert(seqloc_fin);
 | 
				
			||||||
 | 
									if (seg_ack > seqloc_fin) {
 | 
				
			||||||
 | 
										state = tcp_fin_wait2;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								//printf("fin_wait1 end=%d next=%s\n",seg_ack>=seqloc_fin, state_name[state]);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case tcp_fin_wait2:
 | 
				
			||||||
 | 
								// We are waiting for a FIN.
 | 
				
			||||||
 | 
								if (reply.tcp->fin) {
 | 
				
			||||||
 | 
									state = tcp_time_wait;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									printf("tcp in fin_wait2 expected FIN but did not get it\n");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								tcpsend(0,NULL);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case tcp_close_wait:
 | 
				
			||||||
 | 
								state = tcp_last_ack;
 | 
				
			||||||
 | 
								tcpsend(FIN,NULL);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case tcp_last_ack:
 | 
				
			||||||
 | 
								if (reply.tcp->ack) {
 | 
				
			||||||
 | 
									state = tcp_closed;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case tcp_closing:
 | 
				
			||||||
 | 
								// Waiting for ACK of FIN, but who cares?  We are done.
 | 
				
			||||||
 | 
								if (reply.tcp->ack) {
 | 
				
			||||||
 | 
									state = tcp_time_wait;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								state = tcp_time_wait;
 | 
				
			||||||
 | 
								tcpsend(0,NULL);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								assert(0);	// unhandled state
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (state == tcp_time_wait || state == tcp_closed || state == tcp_closing) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							wait_again:
 | 
				
			||||||
 | 
							// Wait for a reply...
 | 
				
			||||||
 | 
							if (options.use_tunnel) {
 | 
				
			||||||
 | 
								// The read only returns what is available, not full rc.
 | 
				
			||||||
 | 
								rc = read(tun_fd,rbuf,rbufsize);
 | 
				
			||||||
 | 
							} else if (mgp) {
 | 
				
			||||||
 | 
					#ifndef STANDALONE
 | 
				
			||||||
 | 
								char *msg = miniggsn_rcv_npdu(&mgp->mg_tcpfd, &error, &plen);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
								rc = -1;
 | 
				
			||||||
 | 
								//if (msg == NULL) { rc = -1; }
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								struct sockaddr recvaddr; socklen_t recvaddrlen = sizeof(struct sockaddr);
 | 
				
			||||||
 | 
								// Note: For a connected socket, the recvaddr is given garbage.
 | 
				
			||||||
 | 
								rc = recvfrom(rfd,rbuf,rbufsize,0,&recvaddr,&recvaddrlen);
 | 
				
			||||||
 | 
								printf("Reply %d bytes from socket: %s\n",rc,ip_sockaddr2a(&recvaddr,NULL));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (rc < 0) { printf("pinghttp: recv error: %s\n",strerror(errno)); return 2; }
 | 
				
			||||||
 | 
							if (rc == 0) {
 | 
				
			||||||
 | 
								printf("received 0 length packet - means shutdown\n");
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Reply received.
 | 
				
			||||||
 | 
							// Both headers are variable size, ipdr size in ->ihl, tcp in ->doff.
 | 
				
			||||||
 | 
							reply.ip = (struct iphdr*)rbuf;
 | 
				
			||||||
 | 
							reply.tcp = (struct tcphdr*) (rbuf + 4 * reply.ip->ihl);
 | 
				
			||||||
 | 
							int hdrsize = 4*reply.ip->ihl + 4*reply.tcp->doff;
 | 
				
			||||||
 | 
							if (rc < hdrsize) {
 | 
				
			||||||
 | 
								printf("bytes received %d less than header size %d?\n",rc,hdrsize);
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (reply.tcp->dest != srcport) {
 | 
				
			||||||
 | 
								printf("Message to port %d (not port %d) ignored\n",ntohs(reply.tcp->dest),ntohs(srcport));
 | 
				
			||||||
 | 
								goto wait_again;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Process the reply:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							reply.payload = rbuf + hdrsize;
 | 
				
			||||||
 | 
							int payloadlen = rc - hdrsize;
 | 
				
			||||||
 | 
							int seg_len = payloadlen + (reply.tcp->syn ? 1 : 0) + (reply.tcp->fin ? 1 : 0);
 | 
				
			||||||
 | 
							seqrem = ntohl(reply.tcp->seq) + seg_len;
 | 
				
			||||||
 | 
							if (reply.tcp->ack) {
 | 
				
			||||||
 | 
								seg_ack = ntohl(reply.tcp->ack_seq);
 | 
				
			||||||
 | 
								seqloc = seg_ack;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (options.v) printf("state=%s received %d bytes %s%s next state=%s\n",
 | 
				
			||||||
 | 
								state_name[prevstate],rc-hdrsize,
 | 
				
			||||||
 | 
								reply.tcp->syn?"SYN":"",  reply.tcp->fin?" FIN":"",
 | 
				
			||||||
 | 
								state_name[state]);
 | 
				
			||||||
 | 
							if (options.v) ip_hdr_dump(rbuf,"received:");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (payloadlen) {
 | 
				
			||||||
 | 
								pinghttpPrintReply(reply.payload,payloadlen,hostname,recvcnt++);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} // whileloop
 | 
				
			||||||
 | 
						if (sfd >= 0) close(sfd);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if STANDALONE
 | 
				
			||||||
 | 
					struct options_s options = {0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char *argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int xflag = 0;	// Extended test.
 | 
				
			||||||
 | 
						//options.from = "192.168.99.2";
 | 
				
			||||||
 | 
						uint32_t *addrs = ip_findmyaddr();
 | 
				
			||||||
 | 
						char mebuf[30];
 | 
				
			||||||
 | 
						options.from = ip_ntoa(addrs[0],mebuf);	// Hope this is the right one.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if STANDALONE
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						ip_init();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						//miniggsn_init();
 | 
				
			||||||
 | 
						next_option:
 | 
				
			||||||
 | 
						argc--; argv++;
 | 
				
			||||||
 | 
						while (argc && argv[0][0] == '-') {
 | 
				
			||||||
 | 
							if (0==strcmp(*argv,"-v")) { options.v=1; goto next_option; }
 | 
				
			||||||
 | 
							if (0==strcmp(*argv,"-x")) { xflag=1; goto next_option; }
 | 
				
			||||||
 | 
							if (0==strcmp(*argv,"-r")) { options.repeat=1; goto next_option; }
 | 
				
			||||||
 | 
							if (0==strcmp(*argv,"-b")) { options.bind=1; goto next_option; }
 | 
				
			||||||
 | 
							if (0==strcmp(*argv,"-a")) { options.printall=1; goto next_option; }
 | 
				
			||||||
 | 
							if (0==strcmp(*argv,"-t")) { options.use_tunnel=1; goto next_option; }
 | 
				
			||||||
 | 
							if (0==strcmp(*argv,"-T")) { options.use_tunnel=1; argc--;argv++;
 | 
				
			||||||
 | 
								if (argc <= 0) { printf("expected arg to -t"); return 2; }
 | 
				
			||||||
 | 
								mg_base_ip_route = *argv;
 | 
				
			||||||
 | 
								goto next_option;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (0==strcmp(*argv,"-f")) {
 | 
				
			||||||
 | 
								argc--; argv++;
 | 
				
			||||||
 | 
								if (argc <= 0) { printf("expected arg to -f"); return 2; }
 | 
				
			||||||
 | 
								options.from = *argv;
 | 
				
			||||||
 | 
								goto next_option;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							printf("Unrecognized option: %s\n",*argv); exit(2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (argc <= 0) {
 | 
				
			||||||
 | 
							printf("syntax: pinghttp [-v] [-a] [-t] [-x] [-T tun_addr] [-f from_ip] hostname\n");
 | 
				
			||||||
 | 
							printf("	-v: verbose dump of http traffic in and out\n");
 | 
				
			||||||
 | 
							printf("	-a: print entire http response\n");
 | 
				
			||||||
 | 
							printf("	-t: use a tunnel (instead of a raw socket)\n");
 | 
				
			||||||
 | 
							printf("	-x: emulate miniggsn; only one of -t or -x may be specified\n");
 | 
				
			||||||
 | 
							printf("	-T <addr>: set the tunnel address, default -T %s\n",mg_base_ip_route);
 | 
				
			||||||
 | 
							printf("	-f <addr>: set the from address, default -f %s\n",mg_base_ip_str);
 | 
				
			||||||
 | 
							printf("		Note: if specified the -f address should be in address range specified by -T,\n");
 | 
				
			||||||
 | 
							printf("		for example: 192.168.99.2\n");
 | 
				
			||||||
 | 
							printf("	hostname: call the http server at this host.  Dont use google.com\n");
 | 
				
			||||||
 | 
							exit(2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (geteuid() != 0) {
 | 
				
			||||||
 | 
							printf("Warning: you should be root to run this!\n"); fflush(stdout);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (options.use_tunnel) {
 | 
				
			||||||
 | 
							if (!options.from) { printf("need -f addr\n"); exit(2); }
 | 
				
			||||||
 | 
							tun_fd = ip_tun_open(tun_if_name,mg_base_ip_route);
 | 
				
			||||||
 | 
							if (tun_fd < 0) {
 | 
				
			||||||
 | 
								printf("Could not open %s\n",tun_if_name);
 | 
				
			||||||
 | 
								exit(2);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int stat = 0;
 | 
				
			||||||
 | 
						if (xflag) {
 | 
				
			||||||
 | 
							// Emulate some MS allocating some IP addresses.
 | 
				
			||||||
 | 
							mg_con_t cons[4];	// Up to four simulated outgoing phone connections.
 | 
				
			||||||
 | 
							int32_t base_ip = inet_addr(mg_base_ip_str);
 | 
				
			||||||
 | 
							if (base_ip == INADDR_NONE) {
 | 
				
			||||||
 | 
								printf("miniggsn: Cannot grok specified base ip address: %s\n",mg_base_ip_str);
 | 
				
			||||||
 | 
								exit(2);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							printf("base_ip=0x%x=%s\n",base_ip,ip_ntoa(base_ip,NULL));
 | 
				
			||||||
 | 
							base_ip = ntohl(base_ip);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							int i;
 | 
				
			||||||
 | 
							for (i=0; i < 4; i++) {
 | 
				
			||||||
 | 
								memset(&cons[i],0,sizeof(mg_con_t));
 | 
				
			||||||
 | 
								cons[i].mg_ip = htonl(base_ip + 1 + i);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Try sending a raw ping packet.
 | 
				
			||||||
 | 
							stat = pinghttp(argv[0],NULL,&cons[0]);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							stat = pinghttp(argv[0],options.from,0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (stat == 0) { printf("http ping %s success\n",argv[0]); }
 | 
				
			||||||
 | 
						return stat;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										356
									
								
								GPRS/todo.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										356
									
								
								GPRS/todo.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,356 @@
 | 
				
			|||||||
 | 
					Memory Leak:
 | 
				
			||||||
 | 
					With GPRS running continuously:
 | 
				
			||||||
 | 
						Startup, around 7:46:50
 | 
				
			||||||
 | 
						4     0 30359 30357  20   0  40388  4396 wait_f Sl+  pts/0      0:01 /home/pat/RangeNetworks/src/range/software/private/openbts/trunk/apps/OpenBTSGprs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Tue Aug 14 09:01:29 PDT 2012:
 | 
				
			||||||
 | 
						4     0 24590 10656  20   0  46276  9796 wait_f Sl+  pts/0     16:08 /home/pat/RangeNetworks/src/range/software/private/openbts/trunk/apps/OpenBTSGprs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Tue Aug 14 09:01:40 PDT 2012:
 | 
				
			||||||
 | 
						4     0 24590 10656  20   0  46276  9796 wait_f Sl+  pts/0     16:13 /home/pat/RangeNetworks/src/range/software/private/openbts/trunk/apps/OpenBTSGprs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Tue Aug 14 11:49:04 PDT 2012:
 | 
				
			||||||
 | 
						4     0 24590 10656  20   0  56260 20064 wait_f Sl+  pts/0     53:05 /home/pat/RangeNetworks/src/range/software/private/openbts/trunk/apps/OpenBTSGprs
 | 
				
			||||||
 | 
					168 minutes, 9984 KB change = 1K/sec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Without GPRS running:
 | 
				
			||||||
 | 
						Tue Aug 14 13:47:14 PDT 2012
 | 
				
			||||||
 | 
						4     0 30359 30357  20   0  40388  4396 wait_f Sl+  pts/0      0:17 /home/pat/RangeNetworks/src/range/software/private/openbts/trunk/apps/OpenBTSGprs
 | 
				
			||||||
 | 
						Tue Aug 14 16:26:28 PDT 2012
 | 
				
			||||||
 | 
						4     0 30359 30357  20   0  41412  4400 wait_f Sl+  pts/0      4:45 /home/pat/RangeNetworks/src/range/software/private/openb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o IPHONE:
 | 
				
			||||||
 | 
						Screws up royally.
 | 
				
			||||||
 | 
						Unlike all the other phones, when you power cycle the bts or the iphone, it does not do a DCCHController registration,
 | 
				
			||||||
 | 
						but it will register for gprs.
 | 
				
			||||||
 | 
						This is with GPRS.Timer.T3212=0
 | 
				
			||||||
 | 
						Even with GPRS.Enable=0, it will usually refuse to register.
 | 
				
			||||||
 | 
						I tried changing the LAC code, still did not register, power cycled the phone, it finally did a DCCH registration.
 | 
				
			||||||
 | 
						Next I set GPRS.Enable=1 and T3212=60, phone never ever registered, even after power cycling.  What gives?
 | 
				
			||||||
 | 
						Tried changing the LAC code and rebooting BTS again...  manual selection still does not work.
 | 
				
			||||||
 | 
						Power cycled the phone...  Finally, it registered DCCH and then GPRS.  Worked fine.
 | 
				
			||||||
 | 
						Power cycled the bts, it got gprs service without registering on DCCH.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Add reporting statistic: IPs in use, available, timingout.
 | 
				
			||||||
 | 
					o Someone sometimes uses the TFI in the global tfi after the TBF has ended.
 | 
				
			||||||
 | 
					o Try changing the decay power params.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Add more statistics:
 | 
				
			||||||
 | 
						number of failed/successful tbfs.  Use a better word than 'failed'.
 | 
				
			||||||
 | 
						number of unanswered/answered RRBPs.
 | 
				
			||||||
 | 
						total tbf connect time, as well as bytes up/down.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o The FIXME in mtSetState regarding removing unneeded USF reservations.
 | 
				
			||||||
 | 
					o The SGSN.Timer.RAUpdate is a GprsTimer - the IE description (24.008 10.5.7.3) says there
 | 
				
			||||||
 | 
						is a special 'units' value to deactivate the timer - implement it. test it.
 | 
				
			||||||
 | 
						update: setting the RAUpdate timer to 0 worked for the Multitech modem - disabled RAUpdate.
 | 
				
			||||||
 | 
					o The timeslot reconfig message does not work, so I took it out.
 | 
				
			||||||
 | 
					o The Blackberry sends packets with a return address that does not match its assignment,
 | 
				
			||||||
 | 
						and we dont catch that error.
 | 
				
			||||||
 | 
					o The 3101 failure is sometimes due to a GPRS suspension request.
 | 
				
			||||||
 | 
					o Blackberry does not like RAupdate, maybe tmsi status is incorrect.
 | 
				
			||||||
 | 
					Received RAUpdateComplete  MS#3,TLLI=c0087003,8008e001 imsi=310260520943554
 | 
				
			||||||
 | 
					 08:31:26.6:SGSN:Received GMMStatus: 98=Message_type_not_compatible_with_the_protocol_state MS#3,TLLI=c0087003,8008e001 imsi=310260520943554
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o GPRS Resumption IE must be included in the GSM CHANNEL RELEASE message, whereever that is.
 | 
				
			||||||
 | 
						44.018 3.4.13.1.1, IE described 10.5.2.14c
 | 
				
			||||||
 | 
						GPRS Suspend is in 44.018 9.1.13b
 | 
				
			||||||
 | 
						Update: It is not critical because the MS is supposed to restart GPRS on its own initiative by sending
 | 
				
			||||||
 | 
						an RAUpdate.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Since the IP addresses are semi-static, we may want a 'new' flag to the shell script
 | 
				
			||||||
 | 
					to indicate if the PdpAllocate is a duplicate.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Why am I getting this from Sgsn.cpp, after enabling TLLI reassignment:
 | 
				
			||||||
 | 
						LOG(WARNING) << "no corresponding MS for TLLI " << mMsHandle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o macAddChannel/macFreeChannel are no longer useful.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o If an uplink fails (eg cause=3101) the downlink will probably fail soon.
 | 
				
			||||||
 | 
						Should wind it down so we dont lose so many TBFs when the downlink is cancelled.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o If the MS becomes non-responsive (eg, misses RRBP) stop sending downlink blocks ahead
 | 
				
			||||||
 | 
						until we hear from it.  This would avoid losing so many TBFs if it gets cancelled.
 | 
				
			||||||
 | 
						Currently I think we just keep sending ahead until we stall.
 | 
				
			||||||
 | 
					o If there are communication problems, try sending RLC blocks with an RRBP in CS-1.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o 6-19-2012, from talk with David:
 | 
				
			||||||
 | 
						- Someday we may want to set GPRS priorities in the HLR so some phones
 | 
				
			||||||
 | 
							have better GPRS service than others, but probably not limit the bandwidth
 | 
				
			||||||
 | 
							since if it is available and no one has higher priority, just use it.
 | 
				
			||||||
 | 
						- Allocate the first GPRS channel for unregistered phones in ARFCN 0.
 | 
				
			||||||
 | 
						- Allocate other gprs channels dynamically from the end, even
 | 
				
			||||||
 | 
							if they end up in ARFCN 1.
 | 
				
			||||||
 | 
						- He also suggested the channel allocator should walk through and look
 | 
				
			||||||
 | 
						for sets of adjacent channels first.
 | 
				
			||||||
 | 
					I think fixed: o Looks like multislot 3-down/1-up fails.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Use a single PACCH for all unregistered TLLIs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Get rid of engineWriteHighSide()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Extended downlink TBF - 44.60 9.3.1a: Send an LLC UI Dummy command to keep the line open.
 | 
				
			||||||
 | 
					That is because there is no way to specify that the entire RLC block is filler
 | 
				
			||||||
 | 
					(unlike UMTS where they fixed this); the start is assumed to be the first TBF.
 | 
				
			||||||
 | 
					Naturally, no 'dummy command' is defined in the LLC spec, but I think they maybe
 | 
				
			||||||
 | 
					they mean a NULL UI frame.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Implement DRX mode paging.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					== STATE MACHINE PROBLEMS ==
 | 
				
			||||||
 | 
						o For uplink TBF, the modem sends the first uplink data blocks
 | 
				
			||||||
 | 
						before it sends the RRBP reply; tbf is still in state DataWaiting1,
 | 
				
			||||||
 | 
						make sure this works, and also it should automatically move
 | 
				
			||||||
 | 
						the tbf to DataTransmit.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Need high and low priority engine service routines, so if there is nothing else
 | 
				
			||||||
 | 
						happening on the radio link, can resend old blocks again, as well as blocks
 | 
				
			||||||
 | 
						that have not been positively acked.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Multitech modem does not like a detachRequest:
 | 
				
			||||||
 | 
						58.5:SGSN:Received ActivatePDPContextRequest TransactionId=0 mNSapi=5 mLlcSapi=3 mRequestType=0 mPdpAddress=ByteVector(size=2 data: 01 21) mQoS=ByteVector(size=3 data: 00 00 00) mApName=ByteVector(size=9 data: 08 69 6e 74 65 72 6e 65 74) mPco=ByteVector(size=20 data: 80 80 21 10 01 08 00 10 81 06 00 00 00 00 83 06 00 00 00 00) MS#1,TLLI=c00ef001
 | 
				
			||||||
 | 
						58.5:SGSN:Sending ActivatePDPContextReject TransactionId=0 mCause=101=Message_not_compatible_with_the_protocol_state MS#1,TLLI=c00ef001 frame=ByteVector(size=3 data: 8a 43 65)
 | 
				
			||||||
 | 
						58.5:SGSN:Sending GMMStatus mCause=10=Implicitly_detached MS#1,TLLI=c00ef001 frame=ByteVector(size=3 data: 08 20 0a)
 | 
				
			||||||
 | 
						58.5:SGSN:Sending DetachRequest mDetachType=1 mForceToStandby=0 mGmmCause=10=Implicitly_detached MS#1,TLLI=c00ef001 frame=ByteVector(size=6 data: 08 05 01 25 00 0a)
 | 
				
			||||||
 | 
						59.9:SGSN:Received GMMStatus: 96=Invalid_mandatory_information MS#1,TLLI=c00ef001
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Check for thread problems in SGSN.  Maybe getMS calls need to be changed
 | 
				
			||||||
 | 
						to something that return a result instead of a pointer to an MSInfo that
 | 
				
			||||||
 | 
						might disappear.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o If the BSN runs backwards in an uplink, it is time to send an uplinkacknack.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tickets:
 | 
				
			||||||
 | 
					== RESERVED TFI BUG ==
 | 
				
			||||||
 | 
						o BUG: If you send an assignment on CCCH, you cannot reuse an existing TFI
 | 
				
			||||||
 | 
						for a while. 44.060 9.3.2.6
 | 
				
			||||||
 | 
						I worked around by round-robin allocating TFIs and it worked great.
 | 
				
			||||||
 | 
						o Implement T3191; part of this.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					== AGCH QUEUE REWORK ==
 | 
				
			||||||
 | 
						This encompasses ticket 69: Implement proper paging groups.
 | 
				
			||||||
 | 
						o Either add ability to cancel messages, or add a call-back to create messages on demand.
 | 
				
			||||||
 | 
						o Get rid of the extraneous AGCH delay.
 | 
				
			||||||
 | 
						o Neither the returned AGCH, nor the getNextMsgSendTime(), are monotonically increasing.
 | 
				
			||||||
 | 
							Example: A downlink assignment is sent on CCCH, but a subsequent rach causes
 | 
				
			||||||
 | 
							a new single-block uplink assignment at an earlier time.  The MS is now sitting on PACCH,
 | 
				
			||||||
 | 
							and does not respond to the downlink assignment on CCCH, which is then repeated on PACCH.
 | 
				
			||||||
 | 
							However, this downlink TBF fails: the blackberry will send the control ack for
 | 
				
			||||||
 | 
							the downlink assignment but then does not respond subsequently. 
 | 
				
			||||||
 | 
						o DRX mode workaround is really hacked in TBF.cpp.  Instead of sending CCCH in the
 | 
				
			||||||
 | 
						correct paging group, if the MS stops responding I send the same message
 | 
				
			||||||
 | 
						on all 6 (count-em) CCCH paging channels.
 | 
				
			||||||
 | 
						o Get the DRX param from the Attach Request message - it has a non-drx period timer
 | 
				
			||||||
 | 
						after transfer state.  25.008 10.5.5.6, but DRX mode described GSM05.02 and GSM3.64 sec 6.5.10.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					== RA-Update problems ==
 | 
				
			||||||
 | 
						o user data transmission is suspended during RAupdate,
 | 
				
			||||||
 | 
							so try setting the RAupdate timers to infinity.
 | 
				
			||||||
 | 
						- suspended at what level?  The message goes to L3, so do the TBFs really get suspended?
 | 
				
			||||||
 | 
						o Why is Blackberry sending new raupdate-requests about 100 secs in?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o During a downlink TBF, make sure we send updated timing advance to the MS - in what message?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Handle GPRS suspend, look at MobilityManagement.cpp:IMSIDetachController
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o They are changing the network color codes on the fly now, so make sure
 | 
				
			||||||
 | 
					  they get that integrated into GPRS after merging gprs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o When you change the GSM beacon (like turning gprs on/off) we may need to set a
 | 
				
			||||||
 | 
						classmark somewhere so phones update.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o I saw it send an l3immediateassignment while there was an uplink tbf in progress!
 | 
				
			||||||
 | 
						When? Is this still possible?
 | 
				
			||||||
 | 
						Yes.  The messages cross in transit.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					== TESTING ==
 | 
				
			||||||
 | 
					o Test nokia gprs attach with thai sim card. - dump is in RangeNetworks/ticket_ms_does_not_work
 | 
				
			||||||
 | 
					o Test nokia gprs attach with range sim card - did not work at all with old SGSN.
 | 
				
			||||||
 | 
					o Retest Samsung MS with correct OpenRegistration in sql and grab OpenBTS log.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Test the transceiver code from the Handover features branch, which
 | 
				
			||||||
 | 
						has the GPRS filler patch that David integrated, and also has some kind
 | 
				
			||||||
 | 
						of bug to log a problem where there are multiple transactions with the same
 | 
				
			||||||
 | 
						timestamp and ARFCN.  This could conceivably be one of the
 | 
				
			||||||
 | 
						bugs that I am seeing?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					== ENHANCEMENTS ==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Get rid of the extra polls in the state machines.
 | 
				
			||||||
 | 
					o RLC unacknowledged mode.
 | 
				
			||||||
 | 
					o Do CS-4 to CS-1 fallback more intelligently.
 | 
				
			||||||
 | 
						o After retry, fall back to CS-1 until timer expires.
 | 
				
			||||||
 | 
					o Add CS-3.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RLC Down Engine:
 | 
				
			||||||
 | 
						o RLCDownEngine: create blocks on the fly, so that we can change CS-1/CS-4 on the fly.
 | 
				
			||||||
 | 
						o Done. Also allow new PDUs to be tacked onto an existing TBF.
 | 
				
			||||||
 | 
						o Done. RLCDownEngine: allow BSN to wrap-around.
 | 
				
			||||||
 | 
						o 3GPP 44.060 has new info on what RLCEngine should send when data exhausted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Slow down the adaptive delay in TransceiverRAD1.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o recvReservation - harden to check the TBF recipient before setting its msg ack.
 | 
				
			||||||
 | 
						We probably dont want to accept any data blocks ever, but check users.
 | 
				
			||||||
 | 
						Update: The MS sends a data block sometimes, and it seems to be ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o The RadData is probably saved in the wrong place - it is not related to any particular channel,
 | 
				
			||||||
 | 
						so it should be in some global place.  Or maybe not, because the single-block assignment
 | 
				
			||||||
 | 
						is associated with a specific channel.
 | 
				
			||||||
 | 
						Also:
 | 
				
			||||||
 | 
						o Only change timing advance by 1 unit at a time.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Why aren't I seeing measurement reports?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Other TODO:
 | 
				
			||||||
 | 
						Dual Transfer Mode?
 | 
				
			||||||
 | 
						Implement RA Capabilities & QofS?  What would we do - the service is pathetic no matter what.
 | 
				
			||||||
 | 
						Paging not needed unless we implement dual transfer mode.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Catch 'stuck' rlc and fix.  - done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o After killing a tbf, send a PacketTbfRelease and either wait for response or 5 seconds. - done
 | 
				
			||||||
 | 
						Although is that necessary?  The new tbf just takes up where the old left off, which is ok.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Fixed: Got a core-dump; third mReservations is corrupted, 0 vptr:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o FIXED: I downloaded dinosaur, switched to tmobile, downloaded dinosaur, switched back to Range, downloaded dinosaur,
 | 
				
			||||||
 | 
					and I saw this:
 | 
				
			||||||
 | 
						It did a new AttachRequest
 | 
				
			||||||
 | 
						then ActivatePdpContext, goti message: SGSN Duplicate PdpContextRequest
 | 
				
			||||||
 | 
						then: sndcp: packet too old
 | 
				
			||||||
 | 
					vvvvvv
 | 
				
			||||||
 | 
						SGSNReceived ActivatePDPContextRequest TransactionId=0 mNSapi=5 mLlcSapi=3 mRequestType=0 mPdpAddress=ByteVector(size=2 data: 01 21) mQoS=ByteVector(size=3 data: 00 00 1f) mApName=ByteVector(size=15 data: 0a 62 6c 61 63 6b 62 65 72 72 79 03 6e 65 74) mPco=ByteVector(size=47 data: 80 80 21 0a 01 9d 00 0a 81 06 00 00 00 00 80 21 0a 01 9e 00 0a 83 06 00 00 00 00 c0 23 11 01 9f 00 11 03 72 69 6d 08 70 61 73 73 77 6f 72 64) MS#2,TLLI=c00ba001 imsi=31026052094355
 | 
				
			||||||
 | 
						924.5:SGSNDuplicate PdpContextRequest
 | 
				
			||||||
 | 
						924.5:SGSNSending ActivatePDPContextAccept TransactionId=0 mLlcSapi=3 mPdpAddress=ByteVector(size=6 data: 01 21 c0 a8 63 01) mQoS=ByteVector(size=12 data: 63 92 12 72 99 10 10 43 ff ff ff 00) mRadioPriority=2 mPco=ByteVector(size=47 data: 80 80 21 0a 02 8e 00 0a 81 06 4b 4b 4b 4b 80 21 0a 02 8f 00 0a 83 06 4b 4b 4c 4c c0 23 11 01 90 00 11 03 72 69 6d 08 70 61 73 73 77 6f 72 64) MS#2,TLLI=c00ba001 imsi=31026052094355 frame=ByteVector(size=10 data: 8a 42 03 0c 63 92 12 72 99 10)
 | 
				
			||||||
 | 
						926.3:LLCllcWriteLowSide sapi=3
 | 
				
			||||||
 | 
						926.3:LLCUI::llcProcess
 | 
				
			||||||
 | 
						926.3:SNDCPuplink packet pdunum=0 segnum=0 size=488 header=ByteVector(size=20 data: 65 00 00 00 45 00 01 e8 f4 0f 00 00 80 11 38 5b c0 a8 63 01)
 | 
				
			||||||
 | 
						926.3:SNDCPflush num=0 sp->mSegCount=1
 | 
				
			||||||
 | 
						926.3:SNDCPpdpWriteLowSide packetlen=488
 | 
				
			||||||
 | 
						926.3:ggsn: writing proto=udp 488 byte packet from 192.168.99.1 to 206.51.26.189 at  17:48:23.2
 | 
				
			||||||
 | 
						928.9:LLCllcWriteLowSide sapi=3
 | 
				
			||||||
 | 
						928.9:LLCUI::llcProcess
 | 
				
			||||||
 | 
						928.9:SNDCPuplink packet pdunum=0 segnum=0 size=48 header=ByteVector(size=20 data: 66 00 00 00 45 00 00 30 f4 10 00 00 80 06 b7 cf c0 a8 63 02)
 | 
				
			||||||
 | 
						928.9:LLCSNDCP packet too old, discarded (number=0,current=521)
 | 
				
			||||||
 | 
						930.8:LLCllcWriteLowSide sapi=3
 | 
				
			||||||
 | 
						930.8:LLCUI::llcProcess
 | 
				
			||||||
 | 
						930.8:SNDCPuplink packet pdunum=1 segnum=0 size=488 header=ByteVector(size=20 data: 65 00 00 01 45 00 01 e8 f4 11 00 00 80 11 38 59 c0 a8 63 01)
 | 
				
			||||||
 | 
						930.8:SNDCPflush num=1 sp->mSegCount=1
 | 
				
			||||||
 | 
						930.8:SNDCPpdpWriteLowSide packetlen=488
 | 
				
			||||||
 | 
					^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o done: Dump the MNC/MCC the phone roams in.  Only dump the phone caps once.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Extended uplink TBF mode 44.060 9.3.1b - allows uplink to ride out temporary inactive periods.
 | 
				
			||||||
 | 
					o The control-ack bit says whether we are establishing a new downlink TBF!!
 | 
				
			||||||
 | 
					Update: This does not work on the blackberry.  I resorted to sending a TbfRelease message.
 | 
				
			||||||
 | 
					o Fixed: A stalled uplink never completed.
 | 
				
			||||||
 | 
					o Done: Try removing extra unasked-for USF in findNeedy
 | 
				
			||||||
 | 
					o This was caused by the same bug: after it stalled no acknacks were ever sent again.
 | 
				
			||||||
 | 
						The 06-08 TBF#180 sends the same blocks eg: BSN=(1) multiple times.
 | 
				
			||||||
 | 
					o Done: Fix the "CHAP" message in sgsn.
 | 
				
			||||||
 | 
					o Done: Fix the "Unable to allocate channel" message.
 | 
				
			||||||
 | 
					o Fixed: The STUCK should not count it as stuck if we received some new blocks,
 | 
				
			||||||
 | 
						even if SSN did not change - if there is an outage there may be many blocks
 | 
				
			||||||
 | 
						between SSN-64 and SSN to send, so SSN may not change for quite a while.
 | 
				
			||||||
 | 
					o Fixed: mUSF was not inited to 0 on blocks that were resent.
 | 
				
			||||||
 | 
						Not sure exactly, but this possibly resulted in errors:
 | 
				
			||||||
 | 
						Radio Block Data Block with unrecognized tfi
 | 
				
			||||||
 | 
						Uplink Data Block with unknown tfi
 | 
				
			||||||
 | 
						ERROR: Received reservation in RLC data block
 | 
				
			||||||
 | 
						After this fix, I could no longer generate the above errors, nor did I
 | 
				
			||||||
 | 
						see the long pauses in downlink reception.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Done: implemented LLC Change TLLI procedure
 | 
				
			||||||
 | 
						I think the LlcEngine should be in the GmmInfo, or at least we need to implement
 | 
				
			||||||
 | 
						change TLLI procedures.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Same as above: The LLC spec says the state machine does not change when TLLI reassigned.
 | 
				
			||||||
 | 
						But I thought I saw somewhere else that it does.  Which is it?
 | 
				
			||||||
 | 
						Since we dont use ABM, it would only affect the unacknowledged sequence number.
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					o Probably fixed by adding GLOG:
 | 
				
			||||||
 | 
						Why does LOG(ERR)<< in TBF.cpp (example @@@fail) not work if you
 | 
				
			||||||
 | 
						use the default Log.Level, but works if you add:
 | 
				
			||||||
 | 
						INSERT INTO "CONFIG" VALUES('Log.Level.TBF.cpp','DEBUG',0,0,'debug');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Fixed by mAltTlli, but has it been tested?
 | 
				
			||||||
 | 
						When we change tlli, when we check for an existing TBF, we have to check
 | 
				
			||||||
 | 
					 	both MSInfo structs.  The SGSN knows the IMSI for every TLLI, so it should
 | 
				
			||||||
 | 
						 ship down the assigned TLLI on AttachComplete.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Maybe fixed by resetting mSP and mUSF:
 | 
				
			||||||
 | 
						Saw mSP being set in an outgoing data block but without a reservation??
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o I think I fixed this by adding the TbfRelease procedue:
 | 
				
			||||||
 | 
						BUG: The MS will send packet resource request using an expired TFI.
 | 
				
			||||||
 | 
						I think we need to keep the tfis reserved until the T3312, or until explicitly reused.
 | 
				
			||||||
 | 
						And if reused, must be for the same MS, or an in-flight packet resource request would be invalid.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o I think this is fixed by a combination of many bug fixes:
 | 
				
			||||||
 | 
						== cause=3105 error ==
 | 
				
			||||||
 | 
						Might be T3168 problem  GSM04.60 7.1.3.1 and TBF.cpp:sendAssignemnt()
 | 
				
			||||||
 | 
						o Try resending the assignment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Done: add ms imsi to ggsn.log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Done, for uplink initiated by downlink acknack:
 | 
				
			||||||
 | 
						== Multiple uplink TBF ==
 | 
				
			||||||
 | 
						o Fix the multiple uplink TBF stuff - these will be initiated using the RRBP
 | 
				
			||||||
 | 
						reservations, not come in on a RACH.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o Done: Accept a new request in the PacketDownlinkAckNack.  Here is one from the blackberry:
 | 
				
			||||||
 | 
						INFO GPRS,1,412353: TBF#91PacketDownlinkAckNack: PayLoadType=RLCControl mCountDownValue=(0) 
 | 
				
			||||||
 | 
						mSI=(0) mR=(0) mMessageType=2 mTFI=(1) mFinalAckIndication=(1) mSSN=(2) Bitmap=(0000000000000003)
 | 
				
			||||||
 | 
						mPeakThroughputClass=(4) mRadioPriority=(1) mRLCMode=(0) mLLCPDUType=(1) mRLCOctetCount=(1345)
 | 
				
			||||||
 | 
						mSt.TxQNum=2 mSt.VA=2 mSt.VS=1
 | 
				
			||||||
 | 
						INFO GPRS,1,412353:@@@ok TBF#91 mtMS= MS#2 TLLI=c0009001 usf=0 mtDir=RLCDir::Down mtState==TBFState:DataTransmit
 | 
				
			||||||
 | 
					     mtAttached=1 mtTFI=1 mtMsgAck=1 mtExpectedAckBSN=412350 size=54
 | 
				
			||||||
 | 
						      mtChannelCoding=3 mtUnAckMode=0 OnCCCH=1 mtAssignCounter=1 descr=user pdu 18:52:31.9
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o NO: The way I am doing it now is fine:
 | 
				
			||||||
 | 
						Need to assign a permanent PACCH.  Need to move the MS off it?
 | 
				
			||||||
 | 
						MS->BTS RACH
 | 
				
			||||||
 | 
						MS<-BTS single block
 | 
				
			||||||
 | 
						MS->BTS resource request
 | 
				
			||||||
 | 
						MS<-BTS uplink/downlink assignment sent to old channel may assign new ch.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					o mControlAck fixed in trunk, but not in GPRS3 yet.
 | 
				
			||||||
 | 
					o Done: 6-18: Re-merge the controlAck fix from the trunk.
 | 
				
			||||||
 | 
					o Done 6-23: Clean up processUplinkResourceRequest:
 | 
				
			||||||
 | 
						when a RACH comes in cancel the downlink TBF.
 | 
				
			||||||
 | 
						improve uplink handling as well.
 | 
				
			||||||
 | 
						Add msUplinkRequest for delayed uplink request.
 | 
				
			||||||
 | 
					o Done 6-23 Add a count of number of reassignments, and possibly other states, to prevent lockup.
 | 
				
			||||||
 | 
						Added GPRS.Timers.MS.NonResponsive
 | 
				
			||||||
 | 
					o Done 6-26: Allow uplink request in final uplink acknack using TBF_EST.
 | 
				
			||||||
 | 
					o Done 6-27: Add an over-riding non-responsive MS timer.
 | 
				
			||||||
 | 
					o Fixed 7-4: TBFs are not dying properly - why not?  Because the MS lost
 | 
				
			||||||
 | 
						the channel assignment and so Dead TBF was not serviced.
 | 
				
			||||||
 | 
					o Who cares: windows ftp says: 500 I won't open a connection to 192.168.99.1 (only to 24.20.208.209)
 | 
				
			||||||
 | 
						(The latter is comcast, probably my static IP.)
 | 
				
			||||||
 | 
					o The Pittsburgh logs have @@@ messages with no antecedent messages.  Why?
 | 
				
			||||||
 | 
						Because David was turning the debug level on and off on the console.
 | 
				
			||||||
 | 
					o Fixed! If the multitech modem is attached and bts is power cycled, cant get it back.
 | 
				
			||||||
 | 
						I send a DetachRequest and a GMMStatus, and the modem sends back invalid mandatory information.
 | 
				
			||||||
 | 
						Try sending each message separately to figure out which one it doesnt like.
 | 
				
			||||||
 | 
						Try taking out the Gmm "Cause".
 | 
				
			||||||
 | 
					o Done 8-2, Pittsburgh problem: retest the InterThreadQueue fix.
 | 
				
			||||||
 | 
					o Fixed: 8-2, Pittsburgh problem: If multiple modems share the channel, does not work.
 | 
				
			||||||
 | 
					Fixed o A 1-down/2-up config asserted in findNeedyUsf here: assert(ms->msCanUseUplinkTn(tn));
 | 
				
			||||||
 | 
					Done o Send power params in downlink assignment  Probably need to put them there first.
 | 
				
			||||||
 | 
					FIXED: o Possibly the L3ImmediateAssignment for downlink is not working.
 | 
				
			||||||
 | 
					Completely redone: o sendReassignment has to restart the persistent uplink TBF.
 | 
				
			||||||
 | 
					Worked around this: o multitech modem says it is not extended uplink capable?
 | 
				
			||||||
 | 
					DONE o Can increase 3101 now, and multiply it by the number of uplink channels.
 | 
				
			||||||
 | 
					Fixed o The initial assignment is being multislot, and then I am doing a reassignment?
 | 
				
			||||||
 | 
					DONE o Put the power reports (eg: CX) from the MS somewhere in the CLI so David can see it in the field.
 | 
				
			||||||
 | 
					FIXED: o The blackberry uses a local tlli in the packetcontrolacknowledgment, see /OpenBTS/8-10/*.log Causes TBF failure.
 | 
				
			||||||
 | 
					FIXED: o David complained about the ggsn.log.  Make sure everything in there is in the real log.
 | 
				
			||||||
 | 
						It is, but you have to set LOG.Level to INFO go see it.
 | 
				
			||||||
 | 
					OK o I tried setting GGSN.MS.IP.MaxCount to 4.  After issuing 3 IPs, it denied the fourth (off by one)
 | 
				
			||||||
 | 
						The iphone reports: Could not acviate cellular network, Samsung says No Connection.
 | 
				
			||||||
 | 
						I think that is right, because they are reserved for a few minutes.
 | 
				
			||||||
							
								
								
									
										22
									
								
								GSM/AppInfTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								GSM/AppInfTest.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <GSML3RRMessages.h>
 | 
				
			||||||
 | 
					#include <GSMTransfer.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Load configuration from a file.
 | 
				
			||||||
 | 
					ConfigurationTable gConfig("OpenBTS.config");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GSM::L3ApplicationInformation ai();
 | 
				
			||||||
 | 
					    static const char init_request_msbased_gps[4] = {'@', '\x01', 'x', '\xa8'}; // pre encoded PER for the following XER:
 | 
				
			||||||
 | 
					    static std::vector<char> request_msbased_gps(init_request_msbased_gps,
 | 
				
			||||||
 | 
					        init_request_msbased_gps + sizeof(init_request_msbased_gps));
 | 
				
			||||||
 | 
					    GSM::L3ApplicationInformation ai2(request_msbased_gps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    GSM::L3Frame f(ai2);
 | 
				
			||||||
 | 
					    std::cout << f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1,24 +1,14 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
					* Copyright 2008 Free Software Foundation, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,21 +1,14 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
					* Copyright 2008 Free Software Foundation, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,29 +1,22 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
					* Copyright 2008 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2011 Range Networks, Inc.
 | 
					* Copyright 2011, 2013 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <Globals.h>
 | 
				
			||||||
#include "GSMCommon.h"
 | 
					#include "GSMCommon.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace GSM;
 | 
					using namespace GSM;
 | 
				
			||||||
@@ -47,6 +40,9 @@ const char* GSM::CallStateString(GSM::CallState state)
 | 
				
			|||||||
		case ReleaseRequest: return "release-request";
 | 
							case ReleaseRequest: return "release-request";
 | 
				
			||||||
		case SMSDelivering: return "SMS-delivery";
 | 
							case SMSDelivering: return "SMS-delivery";
 | 
				
			||||||
		case SMSSubmitting: return "SMS-submission";
 | 
							case SMSSubmitting: return "SMS-submission";
 | 
				
			||||||
 | 
							case HandoverInbound: return "HANDOVER Inbound";
 | 
				
			||||||
 | 
							case HandoverProgress: return "HANDOVER Progress";
 | 
				
			||||||
 | 
							case HandoverOutbound: return "HANDOVER Outbound";
 | 
				
			||||||
		case BusyReject: return "Busy Reject";
 | 
							case BusyReject: return "Busy Reject";
 | 
				
			||||||
		default: return NULL;
 | 
							default: return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -56,7 +52,7 @@ ostream& GSM::operator<<(ostream& os, GSM::CallState state)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	const char* str = GSM::CallStateString(state);
 | 
						const char* str = GSM::CallStateString(state);
 | 
				
			||||||
	if (str) os << str;
 | 
						if (str) os << str;
 | 
				
			||||||
	else os << "?" << state << "?";
 | 
						else os << "?" << ((int)state) << "?";
 | 
				
			||||||
	return os;
 | 
						return os;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -131,17 +127,17 @@ unsigned GSM::uplinkFreqKHz(GSMBand band, unsigned ARFCN)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	switch (band) {
 | 
						switch (band) {
 | 
				
			||||||
		case GSM850:
 | 
							case GSM850:
 | 
				
			||||||
			assert((ARFCN<252)&&(ARFCN>129));
 | 
								assert((ARFCN>=128)&&(ARFCN<=251));
 | 
				
			||||||
			return 824200+200*(ARFCN-128);
 | 
								return 824200+200*(ARFCN-128);
 | 
				
			||||||
		case EGSM900:
 | 
							case EGSM900:
 | 
				
			||||||
			if (ARFCN<=124) return 890000+200*ARFCN;
 | 
								if (ARFCN<=124) return 890000+200*ARFCN;
 | 
				
			||||||
			assert((ARFCN>974)&&(ARFCN<1024));
 | 
								assert((ARFCN>=975)&&(ARFCN<=1023));
 | 
				
			||||||
			return 890000+200*(ARFCN-1024);
 | 
								return 890000+200*(ARFCN-1024);
 | 
				
			||||||
		case DCS1800:
 | 
							case DCS1800:
 | 
				
			||||||
			assert((ARFCN>511)&&(ARFCN<886));
 | 
								assert((ARFCN>=512)&&(ARFCN<=885));
 | 
				
			||||||
			return 1710200+200*(ARFCN-512);
 | 
								return 1710200+200*(ARFCN-512);
 | 
				
			||||||
		case PCS1900:
 | 
							case PCS1900:
 | 
				
			||||||
			assert((ARFCN>511)&&(ARFCN<811));
 | 
								assert((ARFCN>=512)&&(ARFCN<=810));
 | 
				
			||||||
			return 1850200+200*(ARFCN-512);
 | 
								return 1850200+200*(ARFCN-512);
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			assert(0);
 | 
								assert(0);
 | 
				
			||||||
@@ -241,6 +237,19 @@ int32_t Clock::FN() const
 | 
				
			|||||||
	return currentFN;
 | 
						return currentFN;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double Clock::systime(const GSM::Time& when) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
 | 
						const double slotMicroseconds = (48.0 / 13e6) * 156.25;
 | 
				
			||||||
 | 
						const double frameMicroseconds = slotMicroseconds * 8.0;
 | 
				
			||||||
 | 
						int32_t elapsedFrames = when.FN() - mBaseFN;
 | 
				
			||||||
 | 
						if (elapsedFrames<0) elapsedFrames += gHyperframe;
 | 
				
			||||||
 | 
						double elapsedUSec = elapsedFrames * frameMicroseconds + when.TN() * slotMicroseconds;
 | 
				
			||||||
 | 
						double baseSeconds = mBaseTime.sec() + mBaseTime.usec()*1e-6;
 | 
				
			||||||
 | 
						double st = baseSeconds + 1e-6*elapsedUSec;
 | 
				
			||||||
 | 
						return st;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Clock::wait(const Time& when) const
 | 
					void Clock::wait(const Time& when) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -320,7 +329,13 @@ ostream& GSM::operator<<(ostream& os, TypeAndOffset tao)
 | 
				
			|||||||
		case SDCCH_8_5: os << "SDCCH/8-5"; break;
 | 
							case SDCCH_8_5: os << "SDCCH/8-5"; break;
 | 
				
			||||||
		case SDCCH_8_6: os << "SDCCH/8-6"; break;
 | 
							case SDCCH_8_6: os << "SDCCH/8-6"; break;
 | 
				
			||||||
		case SDCCH_8_7: os << "SDCCH/8-7"; break;
 | 
							case SDCCH_8_7: os << "SDCCH/8-7"; break;
 | 
				
			||||||
		case TDMA_BEACON: os << "(beacon)"; break;
 | 
							case TDMA_BEACON: os << "BCH"; break;
 | 
				
			||||||
 | 
							case TDMA_BEACON_BCCH: os << "BCCH"; break;
 | 
				
			||||||
 | 
							case TDMA_BEACON_CCCH: os << "CCCH"; break;
 | 
				
			||||||
 | 
							case TDMA_PDCH: os << "PDCH"; break;
 | 
				
			||||||
 | 
							case TDMA_PACCH: os << "PACCH"; break;
 | 
				
			||||||
 | 
							case TDMA_PTCCH: os << "PTCCH"; break;
 | 
				
			||||||
 | 
							case TDMA_PDIDLE: os << "PDIDLE"; break;
 | 
				
			||||||
		default: os << "?" << (int)tao << "?";
 | 
							default: os << "?" << (int)tao << "?";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return os;
 | 
						return os;
 | 
				
			||||||
@@ -343,8 +358,14 @@ ostream& GSM::operator<<(ostream& os, ChannelType val)
 | 
				
			|||||||
		case AnyTCHType: os << "any TCH"; break;
 | 
							case AnyTCHType: os << "any TCH"; break;
 | 
				
			||||||
		case LoopbackFullType: os << "Loopback Full"; break;
 | 
							case LoopbackFullType: os << "Loopback Full"; break;
 | 
				
			||||||
		case LoopbackHalfType: os << "Loopback Half"; break;
 | 
							case LoopbackHalfType: os << "Loopback Half"; break;
 | 
				
			||||||
 | 
							case PDTCHCS1Type: os << "PDTCHCS1"; break;
 | 
				
			||||||
 | 
							case PDTCHCS2Type: os << "PDTCHCS2"; break;
 | 
				
			||||||
 | 
							case PDTCHCS3Type: os << "PDTCHCS3"; break;
 | 
				
			||||||
 | 
							case PDTCHCS4Type: os << "PDTCHCS4"; break;
 | 
				
			||||||
 | 
							case PSingleBlock1PhaseType: os << "GPRS_SingleBlock1Phase"; break;
 | 
				
			||||||
 | 
							case PSingleBlock2PhaseType: os << "GPRS_SingleBlock2Phase"; break;
 | 
				
			||||||
		case AnyDCCHType: os << "any DCCH"; break;
 | 
							case AnyDCCHType: os << "any DCCH"; break;
 | 
				
			||||||
		default: os << "?" << (int)val << "?";
 | 
							default: os << "?" << (int)val << "?"; break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return os;
 | 
						return os;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,19 @@
 | 
				
			|||||||
/**@file Common-use GSM declarations, most from the GSM 04.xx and 05.xx series. */
 | 
					/**@file Common-use GSM declarations, most from the GSM 04.xx and 05.xx series. */
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008-2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -28,6 +22,7 @@
 | 
				
			|||||||
#ifndef GSMCOMMON_H
 | 
					#ifndef GSMCOMMON_H
 | 
				
			||||||
#define GSMCOMMON_H
 | 
					#define GSMCOMMON_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Defines.h"
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <sys/time.h>
 | 
					#include <sys/time.h>
 | 
				
			||||||
#include <ostream>
 | 
					#include <ostream>
 | 
				
			||||||
@@ -38,8 +33,6 @@
 | 
				
			|||||||
#include <BitVector.h>
 | 
					#include <BitVector.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace GSM {
 | 
					namespace GSM {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**@namespace GSM This namespace covers L1 FEC, L2 and L3 message translation. */
 | 
					/**@namespace GSM This namespace covers L1 FEC, L2 and L3 message translation. */
 | 
				
			||||||
@@ -82,7 +75,6 @@ std::ostream& operator<<(std::ostream& os, CallState state);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** A base class for GSM exceptions. */
 | 
					/** A base class for GSM exceptions. */
 | 
				
			||||||
 | 
					 | 
				
			||||||
class GSMError {};
 | 
					class GSMError {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Duration ofa GSM frame, in microseconds. */
 | 
					/** Duration ofa GSM frame, in microseconds. */
 | 
				
			||||||
@@ -221,6 +213,7 @@ enum ChannelType {
 | 
				
			|||||||
	CCCHType,		///< common control, a combination of several sub-types
 | 
						CCCHType,		///< common control, a combination of several sub-types
 | 
				
			||||||
	RACHType,		///< random access
 | 
						RACHType,		///< random access
 | 
				
			||||||
	SACCHType,		///< slow associated control (acutally dedicated, but...)
 | 
						SACCHType,		///< slow associated control (acutally dedicated, but...)
 | 
				
			||||||
 | 
						CBCHType,		///< cell broadcast channel
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
	///@name Dedicated control channels (DCCHs).
 | 
						///@name Dedicated control channels (DCCHs).
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
@@ -232,6 +225,19 @@ enum ChannelType {
 | 
				
			|||||||
	TCHFType,		///< full-rate traffic
 | 
						TCHFType,		///< full-rate traffic
 | 
				
			||||||
	TCHHType,		///< half-rate traffic
 | 
						TCHHType,		///< half-rate traffic
 | 
				
			||||||
	AnyTCHType,		///< any TCH type
 | 
						AnyTCHType,		///< any TCH type
 | 
				
			||||||
 | 
						//@{
 | 
				
			||||||
 | 
						//@name Packet channels for GPRS.
 | 
				
			||||||
 | 
						PDTCHCS1Type,
 | 
				
			||||||
 | 
						PDTCHCS2Type,
 | 
				
			||||||
 | 
						PDTCHCS3Type,
 | 
				
			||||||
 | 
						PDTCHCS4Type,
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
 | 
						//@{
 | 
				
			||||||
 | 
						//@name Packet CHANNEL REQUEST responses
 | 
				
			||||||
 | 
						// These are used only as return value from decodeChannelNeeded(), and do not correspond
 | 
				
			||||||
 | 
						// to any logical channels.
 | 
				
			||||||
 | 
						PSingleBlock1PhaseType,
 | 
				
			||||||
 | 
						PSingleBlock2PhaseType,
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
	///@name Special internal channel types.
 | 
						///@name Special internal channel types.
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
@@ -240,6 +246,7 @@ enum ChannelType {
 | 
				
			|||||||
	AnyDCCHType,			///< any dedicated control channel
 | 
						AnyDCCHType,			///< any dedicated control channel
 | 
				
			||||||
	UndefinedCHType,		///< undefined
 | 
						UndefinedCHType,		///< undefined
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -274,7 +281,12 @@ enum TypeAndOffset {
 | 
				
			|||||||
	/// Some extra ones for our internal use.
 | 
						/// Some extra ones for our internal use.
 | 
				
			||||||
	TDMA_BEACON_BCCH=253,
 | 
						TDMA_BEACON_BCCH=253,
 | 
				
			||||||
	TDMA_BEACON_CCCH=252,
 | 
						TDMA_BEACON_CCCH=252,
 | 
				
			||||||
	TDMA_BEACON=255
 | 
						TDMA_BEACON=255,
 | 
				
			||||||
 | 
						//TDMA_PDTCHF,	// packet data traffic logical channel, full speed.
 | 
				
			||||||
 | 
						TDMA_PDCH,		// packet data channel, inclusive
 | 
				
			||||||
 | 
						TDMA_PACCH,		// packet control channel, shared with data but distinguished in MAC header.
 | 
				
			||||||
 | 
						TDMA_PTCCH,		// packet data timing advance logical channel
 | 
				
			||||||
 | 
						TDMA_PDIDLE		// Handles the packet channel idle frames.
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::ostream& operator<<(std::ostream& os, TypeAndOffset);
 | 
					std::ostream& operator<<(std::ostream& os, TypeAndOffset);
 | 
				
			||||||
@@ -297,7 +309,7 @@ enum L3PD {
 | 
				
			|||||||
	L3PDSS2PD=0x04,
 | 
						L3PDSS2PD=0x04,
 | 
				
			||||||
	L3MobilityManagementPD=0x05,
 | 
						L3MobilityManagementPD=0x05,
 | 
				
			||||||
	L3RadioResourcePD=0x06,
 | 
						L3RadioResourcePD=0x06,
 | 
				
			||||||
	L3MobilityManagementGPRSPD=0x08,
 | 
						L3GPRSMobilityManagementPD=0x08,
 | 
				
			||||||
	L3SMSPD=0x09,
 | 
						L3SMSPD=0x09,
 | 
				
			||||||
	L3GPRSSessionManagementPD=0x0a,
 | 
						L3GPRSSessionManagementPD=0x0a,
 | 
				
			||||||
	L3NonCallSSPD=0x0b,
 | 
						L3NonCallSSPD=0x0b,
 | 
				
			||||||
@@ -328,6 +340,7 @@ extern const unsigned RACHWaitSParam[];
 | 
				
			|||||||
/**@name Modulus operations for frame numbers. */
 | 
					/**@name Modulus operations for frame numbers. */
 | 
				
			||||||
//@{
 | 
					//@{
 | 
				
			||||||
/** The GSM hyperframe is largest time period in the GSM system, GSM 05.02 4.3.3. */
 | 
					/** The GSM hyperframe is largest time period in the GSM system, GSM 05.02 4.3.3. */
 | 
				
			||||||
 | 
					// It is 2715648
 | 
				
			||||||
const uint32_t gHyperframe = 2048UL * 26UL * 51UL;
 | 
					const uint32_t gHyperframe = 2048UL * 26UL * 51UL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Get a clock difference, within the modulus, v1-v2. */
 | 
					/** Get a clock difference, within the modulus, v1-v2. */
 | 
				
			||||||
@@ -547,6 +560,9 @@ class Clock {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	/** Block until the clock passes a given time. */
 | 
						/** Block until the clock passes a given time. */
 | 
				
			||||||
	void wait(const Time&) const;
 | 
						void wait(const Time&) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Return the system time associated with a given timestamp. */
 | 
				
			||||||
 | 
						double systime(const Time&) const;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,14 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,21 +16,27 @@
 | 
				
			|||||||
#include "GSMConfig.h"
 | 
					#include "GSMConfig.h"
 | 
				
			||||||
#include "GSMTransfer.h"
 | 
					#include "GSMTransfer.h"
 | 
				
			||||||
#include "GSMLogicalChannel.h"
 | 
					#include "GSMLogicalChannel.h"
 | 
				
			||||||
 | 
					#include "GPRSExport.h"
 | 
				
			||||||
#include <ControlCommon.h>
 | 
					#include <ControlCommon.h>
 | 
				
			||||||
#include <Logger.h>
 | 
					#include <Logger.h>
 | 
				
			||||||
 | 
					#include <NeighborTable.h>
 | 
				
			||||||
#include <Reporting.h>
 | 
					#include <Reporting.h>
 | 
				
			||||||
#include <Globals.h>
 | 
					#include <Globals.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace std;
 | 
					using namespace std;
 | 
				
			||||||
using namespace GSM;
 | 
					using namespace GSM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GSMConfig::GSMConfig()
 | 
					GSMConfig::GSMConfig()
 | 
				
			||||||
	:
 | 
						:mCBCH(NULL),
 | 
				
			||||||
	mSI5Frame(UNIT_DATA),mSI6Frame(UNIT_DATA),
 | 
						mSI5Frame(UNIT_DATA),mSI6Frame(UNIT_DATA),
 | 
				
			||||||
	mStartTime(::time(NULL))
 | 
						mSI1(NULL),mSI2(NULL),mSI3(NULL),mSI4(NULL),
 | 
				
			||||||
 | 
						mSI5(NULL),mSI6(NULL),
 | 
				
			||||||
 | 
						mStartTime(::time(NULL)),
 | 
				
			||||||
 | 
						mChangemark(0)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -56,6 +52,12 @@ void GSMConfig::start()
 | 
				
			|||||||
	mPowerManager.start();
 | 
						mPowerManager.start();
 | 
				
			||||||
	// Do not call this until the paging channels are installed.
 | 
						// Do not call this until the paging channels are installed.
 | 
				
			||||||
	mPager.start();
 | 
						mPager.start();
 | 
				
			||||||
 | 
						// If requested, start gprs to allocate channels at startup.
 | 
				
			||||||
 | 
						// Otherwise, channels are allocated on demand, if possible.
 | 
				
			||||||
 | 
						if (GPRS::configGprsChannelsMin() > 0) {
 | 
				
			||||||
 | 
							// Start gprs.
 | 
				
			||||||
 | 
							GPRS::gprsStart();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	// Do not call this until AGCHs are installed.
 | 
						// Do not call this until AGCHs are installed.
 | 
				
			||||||
	mAccessGrantThread.start(Control::AccessGrantServiceLoop,NULL);
 | 
						mAccessGrantThread.start(Control::AccessGrantServiceLoop,NULL);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -65,10 +67,13 @@ void GSMConfig::start()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void GSMConfig::regenerateBeacon()
 | 
					void GSMConfig::regenerateBeacon()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						// FIXME -- Need to implement BCCH_CHANGE_MARK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gReports.incr("OpenBTS.GSM.RR.BeaconRegenerated");
 | 
						gReports.incr("OpenBTS.GSM.RR.BeaconRegenerated");
 | 
				
			||||||
 | 
						mChangemark++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Update everything from the configuration.
 | 
						// Update everything from the configuration.
 | 
				
			||||||
	LOG(NOTICE) << "regenerating system information messages";
 | 
						LOG(NOTICE) << "regenerating system information messages, changemark " << mChangemark;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// BSIC components
 | 
						// BSIC components
 | 
				
			||||||
	mNCC = gConfig.getNum("GSM.Identity.BSIC.NCC");
 | 
						mNCC = gConfig.getNum("GSM.Identity.BSIC.NCC");
 | 
				
			||||||
@@ -79,61 +84,100 @@ void GSMConfig::regenerateBeacon()
 | 
				
			|||||||
	// MCC/MNC/LAC
 | 
						// MCC/MNC/LAC
 | 
				
			||||||
	mLAI = L3LocationAreaIdentity();
 | 
						mLAI = L3LocationAreaIdentity();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::vector<unsigned> neighbors = gNeighborTable.ARFCNList();
 | 
				
			||||||
 | 
						// if the neighbor list is emtpy, put ourselves on it
 | 
				
			||||||
 | 
						if (neighbors.size()==0) neighbors.push_back(gConfig.getNum("GSM.Radio.C0"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Now regenerate all of the system information messages.
 | 
						// Now regenerate all of the system information messages.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// SI1
 | 
						// SI1
 | 
				
			||||||
	L3SystemInformationType1 SI1;
 | 
						L3SystemInformationType1 *SI1 = new L3SystemInformationType1;
 | 
				
			||||||
	LOG(INFO) << SI1;
 | 
						if (mSI1) delete mSI1;
 | 
				
			||||||
 | 
						mSI1 = SI1;
 | 
				
			||||||
 | 
						LOG(INFO) << *SI1;
 | 
				
			||||||
	L3Frame SI1L3(UNIT_DATA);
 | 
						L3Frame SI1L3(UNIT_DATA);
 | 
				
			||||||
	SI1.write(SI1L3);
 | 
						SI1->write(SI1L3);
 | 
				
			||||||
	L2Header SI1Header(L2Length(SI1L3.L2Length()));
 | 
						L2Header SI1Header(L2Length(SI1L3.L2Length()));
 | 
				
			||||||
	mSI1Frame = L2Frame(SI1Header,SI1L3);
 | 
						mSI1Frame = L2Frame(SI1Header,SI1L3);
 | 
				
			||||||
	LOG(DEBUG) << "mSI1Frame " << mSI1Frame;
 | 
						LOG(DEBUG) << "mSI1Frame " << mSI1Frame;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// SI2
 | 
						// SI2
 | 
				
			||||||
	L3SystemInformationType2 SI2;
 | 
						L3SystemInformationType2 *SI2 = new L3SystemInformationType2(neighbors);
 | 
				
			||||||
	LOG(INFO) << SI2;
 | 
						if (mSI2) delete mSI2;
 | 
				
			||||||
 | 
						mSI2 = SI2;
 | 
				
			||||||
 | 
						LOG(INFO) << *SI2;
 | 
				
			||||||
	L3Frame SI2L3(UNIT_DATA);
 | 
						L3Frame SI2L3(UNIT_DATA);
 | 
				
			||||||
	SI2.write(SI2L3);
 | 
						SI2->write(SI2L3);
 | 
				
			||||||
	L2Header SI2Header(L2Length(SI2L3.L2Length()));
 | 
						L2Header SI2Header(L2Length(SI2L3.L2Length()));
 | 
				
			||||||
	mSI2Frame = L2Frame(SI2Header,SI2L3);
 | 
						mSI2Frame = L2Frame(SI2Header,SI2L3);
 | 
				
			||||||
	LOG(DEBUG) << "mSI2Frame " << mSI2Frame;
 | 
						LOG(DEBUG) << "mSI2Frame " << mSI2Frame;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// SI3
 | 
						// SI3
 | 
				
			||||||
	L3SystemInformationType3 SI3;
 | 
						L3SystemInformationType3 *SI3 = new L3SystemInformationType3;
 | 
				
			||||||
	LOG(INFO) << SI3;
 | 
						if (mSI3) delete mSI3;
 | 
				
			||||||
 | 
						mSI3 = SI3;
 | 
				
			||||||
 | 
						LOG(INFO) << *SI3;
 | 
				
			||||||
	L3Frame SI3L3(UNIT_DATA);
 | 
						L3Frame SI3L3(UNIT_DATA);
 | 
				
			||||||
	SI3.write(SI3L3);
 | 
						SI3->write(SI3L3);
 | 
				
			||||||
	L2Header SI3Header(L2Length(SI3L3.L2Length()));
 | 
						L2Header SI3Header(L2Length(SI3L3.L2Length()));
 | 
				
			||||||
	mSI3Frame = L2Frame(SI3Header,SI3L3);
 | 
						mSI3Frame = L2Frame(SI3Header,SI3L3,true);
 | 
				
			||||||
	LOG(DEBUG) << "mSI3Frame " << mSI3Frame;
 | 
						LOG(DEBUG) << "mSI3Frame " << mSI3Frame;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// SI4
 | 
						// SI4
 | 
				
			||||||
	L3SystemInformationType4 SI4;
 | 
						L3SystemInformationType4 *SI4 = new L3SystemInformationType4;
 | 
				
			||||||
 | 
						if (mSI4) delete mSI4;
 | 
				
			||||||
 | 
						mSI4 = SI4;
 | 
				
			||||||
 | 
						LOG(INFO) << *SI4;
 | 
				
			||||||
	LOG(INFO) << SI4;
 | 
						LOG(INFO) << SI4;
 | 
				
			||||||
	L3Frame SI4L3(UNIT_DATA);
 | 
						L3Frame SI4L3(UNIT_DATA);
 | 
				
			||||||
	SI4.write(SI4L3);
 | 
						SI4->write(SI4L3);
 | 
				
			||||||
 | 
						//printf("SI4 bodylength=%d l2len=%d\n",SI4.l2BodyLength(),SI4L3.L2Length());
 | 
				
			||||||
 | 
						//printf("SI4L3.size=%d\n",SI4L3.size());
 | 
				
			||||||
	L2Header SI4Header(L2Length(SI4L3.L2Length()));
 | 
						L2Header SI4Header(L2Length(SI4L3.L2Length()));
 | 
				
			||||||
	mSI4Frame = L2Frame(SI4Header,SI4L3);
 | 
						mSI4Frame = L2Frame(SI4Header,SI4L3,true);
 | 
				
			||||||
	LOG(DEBUG) << "mSI4Frame " << mSI4Frame;
 | 
						LOG(DEBUG) << "mSI4Frame " << mSI4Frame;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if GPRS_PAT | GPRS_TEST
 | 
				
			||||||
 | 
						// SI13. pat added 8-2011 to advertise GPRS support.
 | 
				
			||||||
 | 
						L3SystemInformationType13 *SI13 = new L3SystemInformationType13;
 | 
				
			||||||
 | 
						LOG(INFO) << *SI13;
 | 
				
			||||||
 | 
						L3Frame SI13L3(UNIT_DATA);
 | 
				
			||||||
 | 
						//printf("start=%d\n",SI13L3.size());
 | 
				
			||||||
 | 
						SI13->write(SI13L3);
 | 
				
			||||||
 | 
						//printf("end=%d\n",SI13L3.size());
 | 
				
			||||||
 | 
						//printf("SI13 bodylength=%d l2len=%d\n",SI13.l2BodyLength(),SI13L3.L2Length());
 | 
				
			||||||
 | 
						//printf("SI13L3.size=%d\n",SI13L3.size());
 | 
				
			||||||
 | 
						L2Header SI13Header(L2Length(SI13L3.L2Length()));
 | 
				
			||||||
 | 
						mSI13Frame = L2Frame(SI13Header,SI13L3,true);
 | 
				
			||||||
 | 
						LOG(DEBUG) << "mSI13Frame " << mSI13Frame;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// SI5
 | 
						// SI5
 | 
				
			||||||
	L3SystemInformationType5 SI5;
 | 
						regenerateSI5();
 | 
				
			||||||
	LOG(INFO) << SI5;
 | 
					 | 
				
			||||||
	SI5.write(mSI5Frame);
 | 
					 | 
				
			||||||
	LOG(DEBUG) << "mSI5Frame " << mSI5Frame;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// SI6
 | 
						// SI6
 | 
				
			||||||
	L3SystemInformationType6 SI6;
 | 
						L3SystemInformationType6 *SI6 = new L3SystemInformationType6;
 | 
				
			||||||
	LOG(INFO) << SI6;
 | 
						if (mSI6) delete mSI6;
 | 
				
			||||||
	SI6.write(mSI6Frame);
 | 
						mSI6 = SI6;
 | 
				
			||||||
 | 
						LOG(INFO) << *SI6;
 | 
				
			||||||
 | 
						SI6->write(mSI6Frame);
 | 
				
			||||||
	LOG(DEBUG) "mSI6Frame " << mSI6Frame;
 | 
						LOG(DEBUG) "mSI6Frame " << mSI6Frame;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GSMConfig::regenerateSI5()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::vector<unsigned> neighbors = gNeighborTable.ARFCNList();
 | 
				
			||||||
 | 
						// if the neighbor list is emtpy, put ourselves on it
 | 
				
			||||||
 | 
						if (neighbors.size()==0) neighbors.push_back(gConfig.getNum("GSM.Radio.C0"));
 | 
				
			||||||
 | 
						L3SystemInformationType5 *SI5 = new L3SystemInformationType5(neighbors);
 | 
				
			||||||
 | 
						if (mSI5) delete mSI5;
 | 
				
			||||||
 | 
						mSI5 = SI5;
 | 
				
			||||||
 | 
						LOG(INFO) << *SI5;
 | 
				
			||||||
 | 
						SI5->write(mSI5Frame);
 | 
				
			||||||
 | 
						LOG(DEBUG) << "mSI5Frame " << mSI5Frame;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CCCHLogicalChannel* GSMConfig::minimumLoad(CCCHList &chanList)
 | 
					CCCHLogicalChannel* GSMConfig::minimumLoad(CCCHList &chanList)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -158,43 +202,190 @@ CCCHLogicalChannel* GSMConfig::minimumLoad(CCCHList &chanList)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template <class ChanType> ChanType* getChan(vector<ChanType*>& chanList)
 | 
					template <class ChanType> ChanType* getChan(vector<ChanType*>& chanList, bool forGprs)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const unsigned sz = chanList.size();
 | 
						const unsigned sz = chanList.size();
 | 
				
			||||||
 | 
						LOG(DEBUG) << "sz=" << sz;
 | 
				
			||||||
	if (sz==0) return NULL;
 | 
						if (sz==0) return NULL;
 | 
				
			||||||
	// Start the search from a random point in the list.
 | 
						// (pat) Dont randomize for GPRS!  GPRS requires that channels are returned
 | 
				
			||||||
	//unsigned pos = random() % sz;
 | 
						// in order for the initial channels allocated on C0.
 | 
				
			||||||
	// HACK -- Try in-order allocation for debugging.
 | 
						// We shouldnt randomize at all because we want RR TCH to come from the
 | 
				
			||||||
	for (unsigned i=0; i<sz; i++) {
 | 
						// front of the list and GPRS from the back.
 | 
				
			||||||
		ChanType *chan = chanList[i];
 | 
						unsigned pos = 0;
 | 
				
			||||||
		//ChanType *chan = chanList[pos];
 | 
						const char *configRandomize = "GSM.Channels.Randomize";
 | 
				
			||||||
		if (chan->recyclable()) return chan;
 | 
						if (gConfig.defines(configRandomize)) {
 | 
				
			||||||
		//pos = (pos+1) % sz;
 | 
							if (forGprs) {
 | 
				
			||||||
 | 
								// If the parameter is 'required', the gConfig.remove fails, but dont print a zillion messages.
 | 
				
			||||||
 | 
								static bool once = true;
 | 
				
			||||||
 | 
								if (once) {
 | 
				
			||||||
 | 
									LOG(ALERT) << "Config parameter '" << configRandomize << "' is incompatible with GPRS, removed";
 | 
				
			||||||
 | 
									once = false;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								gConfig.remove(configRandomize);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								pos = random() % sz;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for (unsigned i=0; i<sz; i++, pos = (pos+1)%sz) {
 | 
				
			||||||
 | 
							LOG(DEBUG) << "pos=" << pos << " " << i << "/" << sz;
 | 
				
			||||||
 | 
							ChanType *chan = chanList[pos];
 | 
				
			||||||
 | 
							if (! chan->inUseByGPRS() && chan->recyclable()) return chan;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Are the two channels adjacent?
 | 
				
			||||||
 | 
					template <class ChanType>
 | 
				
			||||||
 | 
					bool testAdjacent(ChanType *ch1, ChanType *ch2)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (ch1->CN() == ch2->CN() && ch1->TN() == ch2->TN()-1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return the goodness of this possible match of gprs channels.
 | 
				
			||||||
 | 
					// Higher numbers are gooder.
 | 
				
			||||||
 | 
					template <class ChanType>
 | 
				
			||||||
 | 
					int testGoodness(vector<ChanType*>& chanList, int lo, int hi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int goodness = 0;
 | 
				
			||||||
 | 
						if (lo > 0) {
 | 
				
			||||||
 | 
							ChanType *ch1 = chanList[lo-1];	// ch1 is below to ch lo.
 | 
				
			||||||
 | 
							if (testAdjacent(ch1,chanList[lo])) {
 | 
				
			||||||
 | 
								// The best match is adjacent to other gprs channels.
 | 
				
			||||||
 | 
								if (ch1->inUseByGPRS()) { goodness += 2; }
 | 
				
			||||||
 | 
								// The next best is an empty adjacent channel.
 | 
				
			||||||
 | 
								else if (ch1->recyclable()) { goodness += 1; }
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (hi < (int)chanList.size()-1) {
 | 
				
			||||||
 | 
							ChanType *ch2 = chanList[hi+1];	// ch2 is above ch hi
 | 
				
			||||||
 | 
							if (testAdjacent(ch2,chanList[hi])) {
 | 
				
			||||||
 | 
								if (ch2->inUseByGPRS()) { goodness += 2; }
 | 
				
			||||||
 | 
								else if (ch2->recyclable()) { goodness += 1; }
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return goodness;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (pat) 6-20-2012: To increase the likelihood that GPRS channels will be adjacent,
 | 
				
			||||||
 | 
					// GSM RR channels will be allocated from the front of the channel list
 | 
				
			||||||
 | 
					// and GPRS from the end.
 | 
				
			||||||
 | 
					// This function allocates a group of channels for gprs.
 | 
				
			||||||
 | 
					// Look for the largest group of adjacent channels <= groupSize.
 | 
				
			||||||
 | 
					// Give preference to channels that are adjacent to channels already
 | 
				
			||||||
 | 
					// allocated for gprs, or to empty channels.
 | 
				
			||||||
 | 
					// Give second preference to groups near the end of the channel list.
 | 
				
			||||||
 | 
					// Return the allocated channels in the array pointed to by results and
 | 
				
			||||||
 | 
					// return number of channels found.
 | 
				
			||||||
 | 
					template <class ChanType>
 | 
				
			||||||
 | 
					static unsigned getChanGroup(vector<ChanType*>& chanList, ChanType **results)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const unsigned sz = chanList.size();
 | 
				
			||||||
 | 
						if (sz==0) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const bool backwards = true;	// Currently we always search backwards.
 | 
				
			||||||
 | 
						// To search forwards, dont forget to invert besti,bestn below
 | 
				
			||||||
 | 
						ChanType *prevFreeCh = NULL;	// unneeded initialization
 | 
				
			||||||
 | 
						int curN = 0;					// current number of adjacent free channels.
 | 
				
			||||||
 | 
						int bestI=0, bestN=0;			// best match
 | 
				
			||||||
 | 
						int bestGoodness = 0;			// goodness of best match
 | 
				
			||||||
 | 
						for (unsigned i=0; i<sz; i++) {
 | 
				
			||||||
 | 
							ChanType *chan = chanList[backwards ? sz-i-1 : i];
 | 
				
			||||||
 | 
							if (chan->inUseByGPRS()) { continue; }
 | 
				
			||||||
 | 
							if (! chan->recyclable()) { continue; }
 | 
				
			||||||
 | 
							if (bestN == 0) {
 | 
				
			||||||
 | 
								bestI = i;
 | 
				
			||||||
 | 
								curN = bestN = 1;
 | 
				
			||||||
 | 
								bestGoodness = testGoodness(chanList,bestI,bestN);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if (testAdjacent<ChanType>(chan,prevFreeCh)) {
 | 
				
			||||||
 | 
									curN++; 	// chan is adjacent to prevCh.
 | 
				
			||||||
 | 
									int curGoodness = testGoodness(chanList,i,curN);
 | 
				
			||||||
 | 
									if (curN > bestN || (curN == bestN && curGoodness > bestGoodness)) {
 | 
				
			||||||
 | 
										// Best so far, so remember it.
 | 
				
			||||||
 | 
										bestN = curN;
 | 
				
			||||||
 | 
										bestI = i;
 | 
				
			||||||
 | 
										bestGoodness = curGoodness;
 | 
				
			||||||
 | 
										// optional early termination test
 | 
				
			||||||
 | 
										//if (bestN >= groupSize && bestIsAdjacent) { goto finished; }
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									curN = 0;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							prevFreeCh = chan;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						//finished:
 | 
				
			||||||
 | 
						for (int j = 0; j < bestN; j++) {
 | 
				
			||||||
 | 
							results[j] = chanList[bestI+j];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return bestN;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Allocate a group of channels for gprs.
 | 
				
			||||||
 | 
					// See comments at getChanGroup.
 | 
				
			||||||
 | 
					int GSMConfig::getTCHGroup(int groupSize,TCHFACCHLogicalChannel **results)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
 | 
						int nfound = getChanGroup<TCHFACCHLogicalChannel>(mTCHPool,results);
 | 
				
			||||||
 | 
						for (int i = 0; i < nfound; i++) {
 | 
				
			||||||
 | 
							results[i]->debugGetL1()->setGPRS(true,NULL);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nfound;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SDCCHLogicalChannel *GSMConfig::getSDCCH()
 | 
					SDCCHLogicalChannel *GSMConfig::getSDCCH()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						LOG(DEBUG);
 | 
				
			||||||
	ScopedLock lock(mLock);
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
	SDCCHLogicalChannel *chan = getChan<SDCCHLogicalChannel>(mSDCCHPool);
 | 
						LOG(DEBUG);
 | 
				
			||||||
 | 
						SDCCHLogicalChannel *chan = getChan<SDCCHLogicalChannel>(mSDCCHPool,0);
 | 
				
			||||||
 | 
						LOG(DEBUG);
 | 
				
			||||||
	if (chan) chan->open();
 | 
						if (chan) chan->open();
 | 
				
			||||||
 | 
						LOG(DEBUG);
 | 
				
			||||||
	return chan;
 | 
						return chan;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TCHFACCHLogicalChannel *GSMConfig::getTCH()
 | 
					// (pat) By a very tortuous path, chan->open() calls L1Encoder::open() and L1Decoder::open(),
 | 
				
			||||||
 | 
					// which sets mActive in both and resets the timers.
 | 
				
			||||||
 | 
					TCHFACCHLogicalChannel *GSMConfig::getTCH(
 | 
				
			||||||
 | 
						bool forGPRS,	// If true, allocate the channel to gprs, else to RR use.
 | 
				
			||||||
 | 
						bool onlyCN0)	// If true, allocate only channels on the lowest ARFCN.
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						LOG(DEBUG);
 | 
				
			||||||
	ScopedLock lock(mLock);
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
	TCHFACCHLogicalChannel *chan = getChan<TCHFACCHLogicalChannel>(mTCHPool);
 | 
						//if (GPRS::GPRSDebug) {
 | 
				
			||||||
 | 
						//	const unsigned sz = mTCHPool.size();
 | 
				
			||||||
 | 
						//	char buf[300];  int n = 0;
 | 
				
			||||||
 | 
						//	for (unsigned i=0; i<sz; i++) {
 | 
				
			||||||
 | 
						//		TCHFACCHLogicalChannel *chan = mTCHPool[i];
 | 
				
			||||||
 | 
						//		n += sprintf(&buf[n],"ch=%d:%d,g=%d,r=%d ",chan->CN(),chan->TN(),
 | 
				
			||||||
 | 
						//				chan->inUseByGPRS(),chan->recyclable());
 | 
				
			||||||
 | 
						//	}
 | 
				
			||||||
 | 
						//	LOG(WARNING)<<"getTCH list:"<<buf;
 | 
				
			||||||
 | 
						//}
 | 
				
			||||||
 | 
						TCHFACCHLogicalChannel *chan = getChan<TCHFACCHLogicalChannel>(mTCHPool,forGPRS);
 | 
				
			||||||
 | 
						// (pat) We have to open it or set gprs mode before returning to avoid a race.
 | 
				
			||||||
	if (chan) {
 | 
						if (chan) {
 | 
				
			||||||
	    chan->open();
 | 
							// The channels are searched in order from low to high, so if the first channel
 | 
				
			||||||
	    gReports.incr("OpenBTS.GSM.RR.ChannelAssignment");
 | 
							// found is not on CN0, we have failed.
 | 
				
			||||||
 | 
							//LOG(DEBUG)<<"getTCH returns"<<LOGVAR2("chan->CN",chan->CN());
 | 
				
			||||||
 | 
							if (onlyCN0 && chan->CN()) { return NULL; }
 | 
				
			||||||
 | 
							if (forGPRS) {
 | 
				
			||||||
 | 
								// (pat) Reserves channel for GPRS, but does not start delivering bursts yet.
 | 
				
			||||||
 | 
								chan->debugGetL1()->setGPRS(true,NULL);
 | 
				
			||||||
 | 
								return chan;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							chan->open();	// (pat) LogicalChannel::open();  Opens mSACCH also.
 | 
				
			||||||
 | 
							gReports.incr("OpenBTS.GSM.RR.ChannelAssignment");
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							//LOG(DEBUG)<<"getTCH returns NULL";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						LOG(DEBUG);
 | 
				
			||||||
	return chan;
 | 
						return chan;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -204,6 +395,7 @@ template <class ChanType> size_t chanAvailable(const vector<ChanType*>& chanList
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	size_t count = 0;
 | 
						size_t count = 0;
 | 
				
			||||||
	for (unsigned i=0; i<chanList.size(); i++) {
 | 
						for (unsigned i=0; i<chanList.size(); i++) {
 | 
				
			||||||
 | 
							if (chanList[i]->inUseByGPRS()) { continue; }
 | 
				
			||||||
		if (chanList[i]->recyclable()) count++;
 | 
							if (chanList[i]->recyclable()) count++;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return count;
 | 
						return count;
 | 
				
			||||||
@@ -227,7 +419,7 @@ size_t GSMConfig::TCHAvailable() const
 | 
				
			|||||||
size_t GSMConfig::totalLoad(const CCCHList& chanList) const
 | 
					size_t GSMConfig::totalLoad(const CCCHList& chanList) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	size_t total = 0;
 | 
						size_t total = 0;
 | 
				
			||||||
	for (int i=0; i<chanList.size(); i++) {
 | 
						for (unsigned i=0; i<chanList.size(); i++) {
 | 
				
			||||||
		total += chanList[i]->load();
 | 
							total += chanList[i]->load();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return total;
 | 
						return total;
 | 
				
			||||||
@@ -235,17 +427,41 @@ size_t GSMConfig::totalLoad(const CCCHList& chanList) const
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template <class ChanType> unsigned countActive(const vector<ChanType*>& chanList)
 | 
					unsigned countActive(const SDCCHList& chanList)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned active = 0;
 | 
				
			||||||
 | 
						const unsigned sz = chanList.size();
 | 
				
			||||||
 | 
						for (unsigned i=0; i<sz; i++) {
 | 
				
			||||||
 | 
							if (!chanList[i]->recyclable()) active++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return active;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned countActive(const TCHList& chanList)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned active = 0;
 | 
						unsigned active = 0;
 | 
				
			||||||
	const unsigned sz = chanList.size();
 | 
						const unsigned sz = chanList.size();
 | 
				
			||||||
	// Start the search from a random point in the list.
 | 
						// Start the search from a random point in the list.
 | 
				
			||||||
	for (unsigned i=0; i<sz; i++) {
 | 
						for (unsigned i=0; i<sz; i++) {
 | 
				
			||||||
 | 
							if (chanList[i]->inUseByGPRS()) continue;
 | 
				
			||||||
		if (!chanList[i]->recyclable()) active++;
 | 
							if (!chanList[i]->recyclable()) active++;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return active;
 | 
						return active;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned countAvailable(const TCHList& chanList)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned available = 0;
 | 
				
			||||||
 | 
						const unsigned sz = chanList.size();
 | 
				
			||||||
 | 
						// Start the search from a random point in the list.
 | 
				
			||||||
 | 
						for (unsigned i=0; i<sz; i++) {
 | 
				
			||||||
 | 
							if (chanList[i]->inUseByGPRS()) continue;
 | 
				
			||||||
 | 
							available++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return available;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
unsigned GSMConfig::SDCCHActive() const
 | 
					unsigned GSMConfig::SDCCHActive() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -257,6 +473,13 @@ unsigned GSMConfig::TCHActive() const
 | 
				
			|||||||
	return countActive(mTCHPool);
 | 
						return countActive(mTCHPool);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned GSMConfig::TCHTotal() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return countAvailable(mTCHPool);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
unsigned GSMConfig::T3122() const
 | 
					unsigned GSMConfig::T3122() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -270,7 +493,7 @@ unsigned GSMConfig::growT3122()
 | 
				
			|||||||
	ScopedLock lock(mLock);
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
	unsigned retVal = mT3122;
 | 
						unsigned retVal = mT3122;
 | 
				
			||||||
	mT3122 += (random() % mT3122) / 2;
 | 
						mT3122 += (random() % mT3122) / 2;
 | 
				
			||||||
	if (mT3122>max) mT3122=max;
 | 
						if (mT3122>(int)max) mT3122=max;
 | 
				
			||||||
	return retVal;
 | 
						return retVal;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -281,7 +504,7 @@ unsigned GSMConfig::shrinkT3122()
 | 
				
			|||||||
	ScopedLock lock(mLock);
 | 
						ScopedLock lock(mLock);
 | 
				
			||||||
	unsigned retVal = mT3122;
 | 
						unsigned retVal = mT3122;
 | 
				
			||||||
	mT3122 -= (random() % mT3122) / 2;
 | 
						mT3122 -= (random() % mT3122) / 2;
 | 
				
			||||||
	if (mT3122<min) mT3122=min;
 | 
						if (mT3122<(int)min) mT3122=min;
 | 
				
			||||||
	return retVal;
 | 
						return retVal;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -294,7 +517,7 @@ void GSMConfig::createCombination0(TransceiverManager& TRX, unsigned TN)
 | 
				
			|||||||
	LOG_ASSERT(TN!=0);
 | 
						LOG_ASSERT(TN!=0);
 | 
				
			||||||
	LOG(NOTICE) << "Configuring dummy filling on C0T " << TN;
 | 
						LOG(NOTICE) << "Configuring dummy filling on C0T " << TN;
 | 
				
			||||||
	ARFCNManager *radio = TRX.ARFCN(0);
 | 
						ARFCNManager *radio = TRX.ARFCN(0);
 | 
				
			||||||
	radio->setSlot(TN,0);
 | 
						radio->setSlot(TN,0);	// (pat) 0 => Transciever.h enum ChannelCombination = FILL
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -303,14 +526,13 @@ void GSMConfig::createCombinationI(TransceiverManager& TRX, unsigned CN, unsigne
 | 
				
			|||||||
	LOG_ASSERT((CN!=0)||(TN!=0));
 | 
						LOG_ASSERT((CN!=0)||(TN!=0));
 | 
				
			||||||
	LOG(NOTICE) << "Configuring combination I on C" << CN << "T" << TN;
 | 
						LOG(NOTICE) << "Configuring combination I on C" << CN << "T" << TN;
 | 
				
			||||||
	ARFCNManager *radio = TRX.ARFCN(CN);
 | 
						ARFCNManager *radio = TRX.ARFCN(CN);
 | 
				
			||||||
	radio->setSlot(TN,1);
 | 
						radio->setSlot(TN,1);	// (pat) 1 => Transciever.h enum ChannelCombination = I
 | 
				
			||||||
	TCHFACCHLogicalChannel* chan = new TCHFACCHLogicalChannel(CN,TN,gTCHF_T[TN]);
 | 
						TCHFACCHLogicalChannel* chan = new TCHFACCHLogicalChannel(CN,TN,gTCHF_T[TN]);
 | 
				
			||||||
	chan->downstream(radio);
 | 
						chan->downstream(radio);
 | 
				
			||||||
	Thread* thread = new Thread;
 | 
						Thread* thread = new Thread;
 | 
				
			||||||
	thread->start((void*(*)(void*))Control::DCCHDispatcher,chan);
 | 
						thread->start((void*(*)(void*))Control::DCCHDispatcher,chan);
 | 
				
			||||||
	chan->open();
 | 
						chan->open();
 | 
				
			||||||
	gBTS.addTCH(chan);
 | 
						gBTS.addTCH(chan);
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -319,7 +541,7 @@ void GSMConfig::createCombinationVII(TransceiverManager& TRX, unsigned CN, unsig
 | 
				
			|||||||
	LOG_ASSERT((CN!=0)||(TN!=0));
 | 
						LOG_ASSERT((CN!=0)||(TN!=0));
 | 
				
			||||||
	LOG(NOTICE) << "Configuring combination VII on C" << CN << "T" << TN;
 | 
						LOG(NOTICE) << "Configuring combination VII on C" << CN << "T" << TN;
 | 
				
			||||||
	ARFCNManager *radio = TRX.ARFCN(CN);
 | 
						ARFCNManager *radio = TRX.ARFCN(CN);
 | 
				
			||||||
	radio->setSlot(TN,7);
 | 
						radio->setSlot(TN,7);	// (pat) 7 => Transciever.h enum ChannelCombination = VII
 | 
				
			||||||
	for (int i=0; i<8; i++) {
 | 
						for (int i=0; i<8; i++) {
 | 
				
			||||||
		SDCCHLogicalChannel* chan = new SDCCHLogicalChannel(CN,TN,gSDCCH8[i]);
 | 
							SDCCHLogicalChannel* chan = new SDCCHLogicalChannel(CN,TN,gSDCCH8[i]);
 | 
				
			||||||
		chan->downstream(radio);
 | 
							chan->downstream(radio);
 | 
				
			||||||
@@ -345,4 +567,95 @@ bool GSMConfig::hold() const
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if ENABLE_PAGING_CHANNELS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 5-27-2012 pat added:
 | 
				
			||||||
 | 
					// Routines for CCCH messages to add real paging channels.
 | 
				
			||||||
 | 
					// Added in the simplest possible way to avoid destabilizing anything.
 | 
				
			||||||
 | 
					// GPRS still needs a pretty major rewrite of the underlying CCCHLogicalChannel class
 | 
				
			||||||
 | 
					// to reduce the latency, but paging queues at least relieve the congestion on CCCH.
 | 
				
			||||||
 | 
					// In DRX [Discontinuous Reception] mode the MS listens only to a subset of CCCH based on its IMSI.
 | 
				
			||||||
 | 
					// This is a GPRS thing but dependent on the configuration of CCCH in our system.
 | 
				
			||||||
 | 
					// See: GSM 05.02 6.5.2: Determination of CCCH_GROUP and PAGING_GROUP for MS in idle mode.
 | 
				
			||||||
 | 
					void GSMConfig::crackPagingFromImsi(	
 | 
				
			||||||
 | 
						unsigned imsiMod1000	// The phones imsi mod 1000, so just atoi the last 3 digits.
 | 
				
			||||||
 | 
						unsigned &paging_block_index,	// Returns which of the paging ccchs to use.
 | 
				
			||||||
 | 
						unsigned &multiframe_index	// Returns which 51-multiframe to use.
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						L3ControlChannelDescription mCC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// BS_CCCH_SDCCH_COMB is defined in GSM 05.02 3.3.2.3;
 | 
				
			||||||
 | 
						int bs_cc_chans;			// The number of ccch timeslots per 51-multiframe.
 | 
				
			||||||
 | 
						bool bs_ccch_sdcch_comb;	// temp var indicates if sdcch is on same TS as ccch.
 | 
				
			||||||
 | 
						switch (mCC.mCCCH_CONF) {
 | 
				
			||||||
 | 
						case 0: bs_cc_chans=1; bs_ccch_sdcch_comb=false; break;
 | 
				
			||||||
 | 
						case 1: bs_cc_chans=1; bs_ccch_sdcch_comb=true; break;
 | 
				
			||||||
 | 
						case 2: bs_cc_chans=2; bs_ccch_sdcch_comb=false; break;
 | 
				
			||||||
 | 
						case 4: bs_cc_chans=3; bs_ccch_sdcch_comb=false; break;
 | 
				
			||||||
 | 
						case 6: bs_cc_chans=4; bs_ccch_sdcch_comb=false; break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							LOG(ERR) << "Invalid GSM.CCCH.CCCH-CONF value:"<<mCC.mCCCH_CONF <<" GPRS will fail until fixed";
 | 
				
			||||||
 | 
							return NULL;	// There will be no reliable GPRS service until you fix this.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// BS_PA_MFRMS is the number of 51-multiframes used for paging.
 | 
				
			||||||
 | 
						unsigned bs_pa_mfrms = mCC.getBS_PA_MFRMS();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Here are some example numbers:
 | 
				
			||||||
 | 
						// We currently use CCCH_CONF=1 so cc_chans=1, so agch_avail=3.
 | 
				
			||||||
 | 
						// Since BS_CC_CHANS=1, then CCCH_GROUP is always 0.
 | 
				
			||||||
 | 
						// For BS_PA_MFRMS=2, BS_AG_BLKS_RES=2:
 | 
				
			||||||
 | 
						//	N=2; tmp = imsi % 2; CCCH_GROUP = 0; PAGING_GROUP = imsi % 2;
 | 
				
			||||||
 | 
						// For BS_PA_MFRMS=2, BS_AG_BLKS_RES=1:
 | 
				
			||||||
 | 
						//	N=4; tmp = imsi % 4; PAGING_GROUP = imsi % 4;
 | 
				
			||||||
 | 
						// For BS_PA_MFRMS=2, BS_AG_BLKS_RES=0:
 | 
				
			||||||
 | 
						//	N=6; tmp = imsi % 6; PAGING_GROUP = imsi % 6;
 | 
				
			||||||
 | 
						// For BS_PA_MFRMS=3, BS_AG_BLKS_RES=0:
 | 
				
			||||||
 | 
						//	N=9; tmp = imsi % 9; PAGING_GROUP = imsi % 9;
 | 
				
			||||||
 | 
						// Paging block index = PAGING_GROUP % BS_PA_MFRMS
 | 
				
			||||||
 | 
						// Multiframe index = PAGING_GROUP / pch_avail;
 | 
				
			||||||
 | 
						// correct multiframe when: multiframe_index == (FN div 51) % BA_PA_MFRMS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// From GSM 05.02 Clause 7 table 5 (located after sec 6.5)
 | 
				
			||||||
 | 
						unsigned agch_avail = bs_ccch_sdcch_comb ? 3 : 8;
 | 
				
			||||||
 | 
						// If you hit this assertion, go fix L3ControlChannelDescription
 | 
				
			||||||
 | 
						// to make sure you leave some paging channels available.
 | 
				
			||||||
 | 
						assert(agch_avail > mPCC.mBS_AG_BLKS_RES);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GSM 05.02 6.5.2: N is number of paging blocks "available" on one CCCH.
 | 
				
			||||||
 | 
						// The "available" is in quotes and not specifically defined, but I believe
 | 
				
			||||||
 | 
						// they mean after subtracting out BS_AG_BLKS_RES, as per 6.5.1 paragraph v).
 | 
				
			||||||
 | 
						unsigned pch_avail = agch_avail - mPCC.mBS_AG_BLKS_RES;
 | 
				
			||||||
 | 
						unsigned Ntotal = pch_avail * bs_pa_mfrms;
 | 
				
			||||||
 | 
						unsigned tmp = (imsiMod1000 % (bs_cc_chans * Ntotal)) % Ntotal;
 | 
				
			||||||
 | 
						unsigned paging_group = tmp % Ntotal;
 | 
				
			||||||
 | 
						paging_block_index = paging_group / (Ntotal / bs_pa_mfrms);
 | 
				
			||||||
 | 
						// And I quote: The required 51-multiframe occurs when:
 | 
				
			||||||
 | 
						// PAGING_GROUP div (N div BS_PA_MFRMS) = (FN div 51) mod (BS_PA_MFRMS)
 | 
				
			||||||
 | 
						multiframe_index = paging_group / (Ntotal % bs_pa_mfrms); 
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GSMConfig::sendPCH(const L3RRMessage& msg,unsigned imsiMod1000)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned paging_block_index;	// which of the paging ccchs to use.
 | 
				
			||||||
 | 
						unsigned multiframe_index;	// which 51-multiframe to use.
 | 
				
			||||||
 | 
						crackPagingFromImsi(imsiMod1000,paging_block_index, multiframe_index);
 | 
				
			||||||
 | 
						assert(multiframe_index < sMax_BS_PA_MFRMS);
 | 
				
			||||||
 | 
						CCCHLogicalChannel* ch = getPCH(paging_block_index);
 | 
				
			||||||
 | 
						ch->mPagingQ[multiframe_index].write(new L3Frame((const L3Message&)msg,UNIT_DATA));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Time GSMConfig::getPchSendTime(imsiMod1000)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned paging_block_index;	// which of the paging ccchs to use.
 | 
				
			||||||
 | 
						unsigned multiframe_index;	// which 51-multiframe to use.
 | 
				
			||||||
 | 
						crackPagingFromImsi(imsiMod1000,paging_block_index, multiframe_index);
 | 
				
			||||||
 | 
						assert(multiframe_index < sMax_BS_PA_MFRMS);
 | 
				
			||||||
 | 
						CCCHLogicalChannel* ch = getPCH(paging_block_index);
 | 
				
			||||||
 | 
						return ch->getNextPchSendTime(multiframe_index);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// vim: ts=4 sw=4
 | 
					// vim: ts=4 sw=4
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,18 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008-2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008-2010 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
 | 
					* Copyright 2012 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -27,6 +21,7 @@
 | 
				
			|||||||
#ifndef GSMCONFIG_H
 | 
					#ifndef GSMCONFIG_H
 | 
				
			||||||
#define GSMCONFIG_H
 | 
					#define GSMCONFIG_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Defines.h"
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
#include <Interthread.h>
 | 
					#include <Interthread.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -43,9 +38,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace GSM {
 | 
					namespace GSM {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// From GSM 05.02 6.5.
 | 
				
			||||||
 | 
					const unsigned sMax_BS_PA_MFRMS = 9;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CCCHLogicalChannel;
 | 
					class CCCHLogicalChannel;
 | 
				
			||||||
class SDCCHLogicalChannel;
 | 
					class SDCCHLogicalChannel;
 | 
				
			||||||
 | 
					class CBCHLogicalChannel;
 | 
				
			||||||
class TCHFACCHLogicalChannel;
 | 
					class TCHFACCHLogicalChannel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CCCHList : public std::vector<CCCHLogicalChannel*> {};
 | 
					class CCCHList : public std::vector<CCCHLogicalChannel*> {};
 | 
				
			||||||
@@ -73,6 +72,7 @@ class GSMConfig {
 | 
				
			|||||||
	CCCHList mPCHPool;		///< paging CCCH subchannels
 | 
						CCCHList mPCHPool;		///< paging CCCH subchannels
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CBCHLogicalChannel* mCBCH;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**@name Allocatable channel pools. */
 | 
						/**@name Allocatable channel pools. */
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
@@ -86,7 +86,7 @@ class GSMConfig {
 | 
				
			|||||||
	unsigned mBCC;		///< basestation color code
 | 
						unsigned mBCC;		///< basestation color code
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GSMBand mBand;		///< BTS operating band
 | 
						GSMBand mBand;		///< BTS operating band, or 0 for custom band
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Clock mClock;		///< local copy of BTS master clock
 | 
						Clock mClock;		///< local copy of BTS master clock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -96,6 +96,7 @@ class GSMConfig {
 | 
				
			|||||||
	L2Frame mSI2Frame;
 | 
						L2Frame mSI2Frame;
 | 
				
			||||||
	L2Frame mSI3Frame;
 | 
						L2Frame mSI3Frame;
 | 
				
			||||||
	L2Frame mSI4Frame;
 | 
						L2Frame mSI4Frame;
 | 
				
			||||||
 | 
						L2Frame mSI13Frame;	// pat added for GPRS
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**@name Encoded L3 frames to be sent on the SACCH. */
 | 
						/**@name Encoded L3 frames to be sent on the SACCH. */
 | 
				
			||||||
@@ -104,6 +105,16 @@ class GSMConfig {
 | 
				
			|||||||
	L3Frame mSI6Frame;
 | 
						L3Frame mSI6Frame;
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**@name Copies of system information messages as they were most recently generated. */
 | 
				
			||||||
 | 
						//@{
 | 
				
			||||||
 | 
						L3SystemInformationType1* mSI1;
 | 
				
			||||||
 | 
						L3SystemInformationType2* mSI2;
 | 
				
			||||||
 | 
						L3SystemInformationType3* mSI3;
 | 
				
			||||||
 | 
						L3SystemInformationType4* mSI4;
 | 
				
			||||||
 | 
						L3SystemInformationType5* mSI5;
 | 
				
			||||||
 | 
						L3SystemInformationType6* mSI6;
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int mT3122;
 | 
						int mT3122;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	time_t mStartTime;
 | 
						time_t mStartTime;
 | 
				
			||||||
@@ -115,6 +126,12 @@ class GSMConfig {
 | 
				
			|||||||
	InterthreadQueue<Control::ChannelRequestRecord> mChannelRequestQueue;
 | 
						InterthreadQueue<Control::ChannelRequestRecord> mChannelRequestQueue;
 | 
				
			||||||
	Thread mAccessGrantThread;
 | 
						Thread mAccessGrantThread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned mChangemark;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void crackPagingFromImsi(unsigned imsiMod1000,unsigned &ccch_group,unsigned &paging_Index);;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
@@ -133,12 +150,22 @@ class GSMConfig {
 | 
				
			|||||||
	const L2Frame& SI2Frame() const { return mSI2Frame; }
 | 
						const L2Frame& SI2Frame() const { return mSI2Frame; }
 | 
				
			||||||
	const L2Frame& SI3Frame() const { return mSI3Frame; }
 | 
						const L2Frame& SI3Frame() const { return mSI3Frame; }
 | 
				
			||||||
	const L2Frame& SI4Frame() const { return mSI4Frame; }
 | 
						const L2Frame& SI4Frame() const { return mSI4Frame; }
 | 
				
			||||||
 | 
						const L2Frame& SI13Frame() const { return mSI13Frame; }	// pat added for GPRS
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
	/**@name Get references to L3 frames for SACCH SI messages. */
 | 
						/**@name Get references to L3 frames for SACCH SI messages. */
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
	const L3Frame& SI5Frame() const { return mSI5Frame; }
 | 
						const L3Frame& SI5Frame() const { return mSI5Frame; }
 | 
				
			||||||
	const L3Frame& SI6Frame() const { return mSI6Frame; }
 | 
						const L3Frame& SI6Frame() const { return mSI6Frame; }
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
						/**@name Get the messages themselves. */
 | 
				
			||||||
 | 
						//@{
 | 
				
			||||||
 | 
						const L3SystemInformationType1* SI1() const { return mSI1; }
 | 
				
			||||||
 | 
						const L3SystemInformationType2* SI2() const { return mSI2; }
 | 
				
			||||||
 | 
						const L3SystemInformationType3* SI3() const { return mSI3; }
 | 
				
			||||||
 | 
						const L3SystemInformationType4* SI4() const { return mSI4; }
 | 
				
			||||||
 | 
						const L3SystemInformationType5* SI5() const { return mSI5; }
 | 
				
			||||||
 | 
						const L3SystemInformationType6* SI6() const { return mSI6; }
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Get the current master clock value. */
 | 
						/** Get the current master clock value. */
 | 
				
			||||||
	Time time() const { return mClock.get(); }
 | 
						Time time() const { return mClock.get(); }
 | 
				
			||||||
@@ -151,6 +178,7 @@ class GSMConfig {
 | 
				
			|||||||
	unsigned NCC() const { return mNCC; }
 | 
						unsigned NCC() const { return mNCC; }
 | 
				
			||||||
	GSM::Clock& clock() { return mClock; }
 | 
						GSM::Clock& clock() { return mClock; }
 | 
				
			||||||
	const L3LocationAreaIdentity& LAI() const { return mLAI; }
 | 
						const L3LocationAreaIdentity& LAI() const { return mLAI; }
 | 
				
			||||||
 | 
						unsigned changemark() const { return mChangemark; }
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Return the BSIC, NCC:BCC. */
 | 
						/** Return the BSIC, NCC:BCC. */
 | 
				
			||||||
@@ -162,6 +190,12 @@ class GSMConfig {
 | 
				
			|||||||
	*/
 | 
						*/
 | 
				
			||||||
	void regenerateBeacon();
 | 
						void regenerateBeacon();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
							SI5 is generated separately because it may get random
 | 
				
			||||||
 | 
							neighbors added each time it's sent.
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
 | 
						void regenerateSI5();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
		Hold off on channel allocations; don't answer RACH.
 | 
							Hold off on channel allocations; don't answer RACH.
 | 
				
			||||||
		@param val true to hold, false to clear hold
 | 
							@param val true to hold, false to clear hold
 | 
				
			||||||
@@ -196,8 +230,28 @@ class GSMConfig {
 | 
				
			|||||||
	void addPCH(CCCHLogicalChannel* wCCCH) { mPCHPool.push_back(wCCCH); }
 | 
						void addPCH(CCCHLogicalChannel* wCCCH) { mPCHPool.push_back(wCCCH); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Return a minimum-load AGCH. */
 | 
						/** Return a minimum-load AGCH. */
 | 
				
			||||||
 | 
						// (pat) TODO: This strategy needs to change.
 | 
				
			||||||
 | 
						// There needs to be a common message queue for all CCCH timeslots from which the
 | 
				
			||||||
 | 
						// FEC can pull the next AGCH message if there is no paging message at that paging slot.
 | 
				
			||||||
 | 
						// And if someone besides pat works on this, note that gprs also wants
 | 
				
			||||||
 | 
						// to be able cancel messages after sending them in case conditions have changed,
 | 
				
			||||||
 | 
						// and also needs to know, a-priori, the exact frame number when the message
 | 
				
			||||||
 | 
						// is going to be sent, none of which works properly at the moment.
 | 
				
			||||||
	CCCHLogicalChannel* getAGCH() { return minimumLoad(mAGCHPool); }
 | 
						CCCHLogicalChannel* getAGCH() { return minimumLoad(mAGCHPool); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if ENABLE_PAGING_CHANNELS
 | 
				
			||||||
 | 
						///< (pat) Send a paging message for the specified imsi.
 | 
				
			||||||
 | 
						// This function should be used instead of getPCH(), etc. which should then be made private.
 | 
				
			||||||
 | 
						void sendPCH(const L3RRMessage& msg,unsigned imsiMod1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						///< (pat) Return the approximate time of the next PCH message for this imsi.
 | 
				
			||||||
 | 
						// This routine should be elided after DRX mode in GPRS is fixed.
 | 
				
			||||||
 | 
						Time getPchSendTime(imsiMod1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						///< (pat) Send a message on the avail AGCH.
 | 
				
			||||||
 | 
						void sendAGCH(const L3RRMessage& msg);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Return a minimum-load PCH. */
 | 
						/** Return a minimum-load PCH. */
 | 
				
			||||||
	CCCHLogicalChannel* getPCH() { return minimumLoad(mPCHPool); }
 | 
						CCCHLogicalChannel* getPCH() { return minimumLoad(mPCHPool); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -224,6 +278,17 @@ class GSMConfig {
 | 
				
			|||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**@ Manage the CBCH. */
 | 
				
			||||||
 | 
						//@{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** The add method is not mutex protected and should only be used during initialization. */
 | 
				
			||||||
 | 
						void addCBCH(CBCHLogicalChannel *wCBCH)
 | 
				
			||||||
 | 
							{ assert(mCBCH==NULL); mCBCH=wCBCH; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CBCHLogicalChannel* getCBCH() { return mCBCH; }
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**@name Manage SDCCH Pool. */
 | 
						/**@name Manage SDCCH Pool. */
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
	/** The add method is not mutex protected and should only be used during initialization. */
 | 
						/** The add method is not mutex protected and should only be used during initialization. */
 | 
				
			||||||
@@ -245,11 +310,12 @@ class GSMConfig {
 | 
				
			|||||||
	/** The add method is not mutex protected and should only be used during initialization. */
 | 
						/** The add method is not mutex protected and should only be used during initialization. */
 | 
				
			||||||
	void addTCH(TCHFACCHLogicalChannel *wTCH) { mTCHPool.push_back(wTCH); }
 | 
						void addTCH(TCHFACCHLogicalChannel *wTCH) { mTCHPool.push_back(wTCH); }
 | 
				
			||||||
	/** Return a pointer to a usable channel. */
 | 
						/** Return a pointer to a usable channel. */
 | 
				
			||||||
	TCHFACCHLogicalChannel *getTCH();
 | 
						TCHFACCHLogicalChannel *getTCH(bool forGPRS=false, bool onlyCN0=false);
 | 
				
			||||||
 | 
						int getTCHGroup(int groupSize,TCHFACCHLogicalChannel **results);
 | 
				
			||||||
	/** Return true if an TCH is available, but do not allocate it. */
 | 
						/** Return true if an TCH is available, but do not allocate it. */
 | 
				
			||||||
	size_t TCHAvailable() const;
 | 
						size_t TCHAvailable() const;
 | 
				
			||||||
	/** Return number of total TCH. */
 | 
						/** Return number of total TCH. */
 | 
				
			||||||
	unsigned TCHTotal() const { return mTCHPool.size(); }
 | 
						unsigned TCHTotal() const;
 | 
				
			||||||
	/** Return number of active TCH. */
 | 
						/** Return number of active TCH. */
 | 
				
			||||||
	unsigned TCHActive() const;
 | 
						unsigned TCHActive() const;
 | 
				
			||||||
	/** Just a reference to the TCH pool. */
 | 
						/** Just a reference to the TCH pool. */
 | 
				
			||||||
@@ -271,6 +337,9 @@ class GSMConfig {
 | 
				
			|||||||
	void createCombinationI(TransceiverManager &TRX, unsigned CN, unsigned TN);
 | 
						void createCombinationI(TransceiverManager &TRX, unsigned CN, unsigned TN);
 | 
				
			||||||
	/** Combination VII is 8 SDCCHs. */
 | 
						/** Combination VII is 8 SDCCHs. */
 | 
				
			||||||
	void createCombinationVII(TransceiverManager &TRX, unsigned CN, unsigned TN);
 | 
						void createCombinationVII(TransceiverManager &TRX, unsigned CN, unsigned TN);
 | 
				
			||||||
 | 
						/** Combination XIII is a GPRS PDTCH: PDTCH/F+PACCH/F+PTCCH/F */
 | 
				
			||||||
 | 
						// pat todo: This does not exist yet.
 | 
				
			||||||
 | 
						void createCombinationXIII(TransceiverManager &TRX, unsigned CN, unsigned TN);
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Return number of seconds since starting. */
 | 
						/** Return number of seconds since starting. */
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										725
									
								
								GSM/GSML1FEC.cpp
									
									
									
									
									
								
							
							
						
						
									
										725
									
								
								GSM/GSML1FEC.cpp
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										461
									
								
								GSM/GSML1FEC.h
									
									
									
									
									
								
							
							
						
						
									
										461
									
								
								GSM/GSML1FEC.h
									
									
									
									
									
								
							@@ -2,31 +2,25 @@
 | 
				
			|||||||
* Copyright 2008-2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008-2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					
 | 
				
			||||||
* See the COPYING 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					* information for this specific distribuion.
 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	This program is distributed in the hope that it will be useful,
 | 
					 | 
				
			||||||
	but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef GSML1FEC_H
 | 
					#ifndef GSML1FEC_H
 | 
				
			||||||
#define GSML1FEC_H
 | 
					#define GSML1FEC_H
 | 
				
			||||||
 | 
					#include "Defines.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "Threads.h"
 | 
					#include "Threads.h"
 | 
				
			||||||
#include <assert.h>
 | 
					#include <assert.h>
 | 
				
			||||||
@@ -36,14 +30,18 @@
 | 
				
			|||||||
#include "GSMTransfer.h"
 | 
					#include "GSMTransfer.h"
 | 
				
			||||||
#include "GSMTDMA.h"
 | 
					#include "GSMTDMA.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "a53.h"
 | 
				
			||||||
 | 
					#include "A51.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "GSM610Tables.h"
 | 
					#include "GSM610Tables.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <Globals.h>
 | 
					#include <Globals.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../GPRS/GPRSExport.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ARFCNManager;
 | 
					class ARFCNManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace GSM {
 | 
					namespace GSM {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -61,9 +59,6 @@ class SACCHL1Decoder;
 | 
				
			|||||||
class SACCHL1FEC;
 | 
					class SACCHL1FEC;
 | 
				
			||||||
class TrafficTranscoder;
 | 
					class TrafficTranscoder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
	Naming convention for bit vectors follows GSM 05.03 Section 2.2.
 | 
						Naming convention for bit vectors follows GSM 05.03 Section 2.2.
 | 
				
			||||||
	d[k]		data
 | 
						d[k]		data
 | 
				
			||||||
@@ -74,12 +69,19 @@ class TrafficTranscoder;
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum EncryptionType {
 | 
				
			||||||
 | 
						ENCRYPT_NO,
 | 
				
			||||||
 | 
						ENCRYPT_MAYBE,
 | 
				
			||||||
 | 
						ENCRYPT_YES
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
	Abstract class for L1 encoders.
 | 
						Abstract class for L1 encoders.
 | 
				
			||||||
	In most subclasses, writeHighSide() drives the processing.
 | 
						In most subclasses, writeHighSide() drives the processing.
 | 
				
			||||||
 | 
						(pat) base class for: XCCHL1Encoder, GeneratorL1Encoder
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
class L1Encoder {
 | 
					class L1Encoder {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -105,19 +107,31 @@ class L1Encoder {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	/**@ Internal state. */
 | 
						/**@ Internal state. */
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
 | 
						// (pat) The way this works is rollForward() sets mNextWriteTime to the next
 | 
				
			||||||
 | 
						// frame time specified in mMapping.  Each logical channel combination has a
 | 
				
			||||||
 | 
						// custom serviceloop function running in a separate thread to multiplex the downstream data,
 | 
				
			||||||
 | 
						// and send an appropriate frame to ARFCNManager::writeHighSideTx.
 | 
				
			||||||
 | 
						// This is totally unlike decoders, for which AFCNManager:receiveBurst uses
 | 
				
			||||||
 | 
						// the encoder mapping (which it has cached) to send incoming bursts directly
 | 
				
			||||||
 | 
						// to the mapped L1Decoder::writeLowSideRx() for each frame.
 | 
				
			||||||
	unsigned mTotalBursts;			///< total bursts sent since last open()
 | 
						unsigned mTotalBursts;			///< total bursts sent since last open()
 | 
				
			||||||
	GSM::Time mPrevWriteTime;		///< timestamp of pervious generated burst
 | 
						GSM::Time mPrevWriteTime;		///< timestamp of pervious generated burst
 | 
				
			||||||
	GSM::Time mNextWriteTime;		///< timestamp of next generated burst
 | 
						GSM::Time mNextWriteTime;		///< timestamp of next generated burst
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	volatile bool mRunning;			///< true while the service loop is running
 | 
						volatile bool mRunning;			///< true while the service loop is running
 | 
				
			||||||
	bool mActive;					///< true between open() and close()
 | 
						bool mActive;					///< true between open() and close()
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ViterbiR2O4 mVCoder;	///< nearly all GSM channels use the same convolutional code
 | 
						// (pat) Moved to classes that need the convolutional coder.
 | 
				
			||||||
 | 
						//ViterbiR2O4 mVCoder;	///< nearly all GSM channels use the same convolutional code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	char mDescriptiveString[100];
 | 
						char mDescriptiveString[100];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						EncryptionType mEncrypted;
 | 
				
			||||||
 | 
						int mEncryptionAlgorithm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
		The basic encoder constructor.
 | 
							The basic encoder constructor.
 | 
				
			||||||
		@param wCN carrier index.
 | 
							@param wCN carrier index.
 | 
				
			||||||
@@ -136,6 +150,10 @@ class L1Encoder {
 | 
				
			|||||||
		mDownstream=wDownstream;
 | 
							mDownstream=wDownstream;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ARFCNManager *getRadio() { return mDownstream; }
 | 
				
			||||||
 | 
						// Used by XCCHEncoder
 | 
				
			||||||
 | 
						void transmit(BitVector *mI, BitVector *mE, const int *qbits);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**@name Accessors. */
 | 
						/**@name Accessors. */
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
	const TDMAMapping& mapping() const { return mMapping; }
 | 
						const TDMAMapping& mapping() const { return mMapping; }
 | 
				
			||||||
@@ -155,6 +173,9 @@ class L1Encoder {
 | 
				
			|||||||
	/** Open the channel for a new transaction.  */
 | 
						/** Open the channel for a new transaction.  */
 | 
				
			||||||
	virtual void open();
 | 
						virtual void open();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Set mDownstream handover correlator mode. */
 | 
				
			||||||
 | 
						void handoverPending(bool flag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
		Returns true if the channel is in use by a transaction.
 | 
							Returns true if the channel is in use by a transaction.
 | 
				
			||||||
		For broadcast and unicast channels this is always true.
 | 
							For broadcast and unicast channels this is always true.
 | 
				
			||||||
@@ -174,6 +195,10 @@ class L1Encoder {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	const char* descriptiveString() const { return mDescriptiveString; }
 | 
						const char* descriptiveString() const { return mDescriptiveString; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L1FEC* parent() { return mParent; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GSM::Time getNextWriteTime() { resync(); return mNextWriteTime; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected:
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Roll write times forward to the next positions. */
 | 
						/** Roll write times forward to the next positions. */
 | 
				
			||||||
@@ -200,16 +225,18 @@ class L1Encoder {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
	An abstract class for L1 decoders.
 | 
						An abstract class for L1 decoders.
 | 
				
			||||||
	writeLowSide() drives the processing.
 | 
						writeLowSideRx() drives the processing.
 | 
				
			||||||
 | 
						// (pat) base class for: RACHL1Decoder, XCCHL1Decoder
 | 
				
			||||||
 | 
						// It would be more elegant to split this into two classes: a base class
 | 
				
			||||||
 | 
						// for both GPRS and RR, and the rest of this class that is RR specific.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
class L1Decoder {
 | 
					class L1Decoder {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected:
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// (pat) Not used for GPRS
 | 
				
			||||||
	SAPMux * mUpstream;
 | 
						SAPMux * mUpstream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**@name Mutex-controlled state information. */
 | 
						/**@name Mutex-controlled state information. */
 | 
				
			||||||
@@ -220,6 +247,7 @@ class L1Decoder {
 | 
				
			|||||||
	Z100Timer mT3101;					///< timer for new channels
 | 
						Z100Timer mT3101;					///< timer for new channels
 | 
				
			||||||
	Z100Timer mT3109;					///< timer for existing channels
 | 
						Z100Timer mT3109;					///< timer for existing channels
 | 
				
			||||||
	Z100Timer mT3111;					///< timer for reuse of a closed channel
 | 
						Z100Timer mT3111;					///< timer for reuse of a closed channel
 | 
				
			||||||
 | 
						Z100Timer mT3103;					///< timer for handover
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
	bool mActive;						///< true between open() and close()
 | 
						bool mActive;						///< true between open() and close()
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
@@ -230,6 +258,7 @@ class L1Decoder {
 | 
				
			|||||||
	volatile bool mRunning;						///< true if all required service threads are started
 | 
						volatile bool mRunning;						///< true if all required service threads are started
 | 
				
			||||||
	volatile float mFER;						///< current FER estimate
 | 
						volatile float mFER;						///< current FER estimate
 | 
				
			||||||
	static const int mFERMemory=20;				///< FER decay time, in frames
 | 
						static const int mFERMemory=20;				///< FER decay time, in frames
 | 
				
			||||||
 | 
						volatile bool mHandoverPending;				///< if true, we are decoding handover bursts
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**@name Parameters fixed by the constructor, not requiring mutex protection. */
 | 
						/**@name Parameters fixed by the constructor, not requiring mutex protection. */
 | 
				
			||||||
@@ -240,7 +269,13 @@ class L1Decoder {
 | 
				
			|||||||
	L1FEC* mParent;			///< a containing L1 processor, if any
 | 
						L1FEC* mParent;			///< a containing L1 processor, if any
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ViterbiR2O4 mVCoder;	///< nearly all GSM channels use the same convolutional code
 | 
						// (pat) Moved to classes that use the convolutional coder.
 | 
				
			||||||
 | 
						//ViterbiR2O4 mVCoder;	///< nearly all GSM channels use the same convolutional code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						EncryptionType mEncrypted;
 | 
				
			||||||
 | 
						int mEncryptionAlgorithm;
 | 
				
			||||||
 | 
						unsigned char mKc[8];
 | 
				
			||||||
 | 
						int mFN[8];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
@@ -254,11 +289,14 @@ class L1Decoder {
 | 
				
			|||||||
	L1Decoder(unsigned wCN, unsigned wTN, const TDMAMapping& wMapping, L1FEC* wParent)
 | 
						L1Decoder(unsigned wCN, unsigned wTN, const TDMAMapping& wMapping, L1FEC* wParent)
 | 
				
			||||||
			:mUpstream(NULL),
 | 
								:mUpstream(NULL),
 | 
				
			||||||
			mT3101(T3101ms),mT3109(T3109ms),mT3111(T3111ms),
 | 
								mT3101(T3101ms),mT3109(T3109ms),mT3111(T3111ms),
 | 
				
			||||||
 | 
								mT3103(gConfig.getNum("GSM.Timer.T3103")),
 | 
				
			||||||
			mActive(false),
 | 
								mActive(false),
 | 
				
			||||||
			mRunning(false),
 | 
								mRunning(false),
 | 
				
			||||||
			mFER(0.0F),
 | 
								mFER(0.0F),
 | 
				
			||||||
			mCN(wCN),mTN(wTN),
 | 
								mCN(wCN),mTN(wTN),
 | 
				
			||||||
			mMapping(wMapping),mParent(wParent)
 | 
								mMapping(wMapping),mParent(wParent),
 | 
				
			||||||
 | 
								mEncrypted(ENCRYPT_NO),
 | 
				
			||||||
 | 
								mEncryptionAlgorithm(0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		// Start T3101 so that the channel will
 | 
							// Start T3101 so that the channel will
 | 
				
			||||||
		// become recyclable soon.
 | 
							// become recyclable soon.
 | 
				
			||||||
@@ -291,7 +329,7 @@ class L1Decoder {
 | 
				
			|||||||
	bool recyclable() const;
 | 
						bool recyclable() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Connect the upstream SAPMux and L2.  */
 | 
						/** Connect the upstream SAPMux and L2.  */
 | 
				
			||||||
	void upstream(SAPMux * wUpstream)
 | 
						virtual void upstream(SAPMux * wUpstream)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		assert(mUpstream==NULL);	// Only call this once.
 | 
							assert(mUpstream==NULL);	// Only call this once.
 | 
				
			||||||
		mUpstream=wUpstream;
 | 
							mUpstream=wUpstream;
 | 
				
			||||||
@@ -304,7 +342,7 @@ class L1Decoder {
 | 
				
			|||||||
	const TDMAMapping& mapping() const { return mMapping; }
 | 
						const TDMAMapping& mapping() const { return mMapping; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Accept an RxBurst and process it into the deinterleaver. */
 | 
						/** Accept an RxBurst and process it into the deinterleaver. */
 | 
				
			||||||
	virtual void writeLowSide(const RxBurst&) = 0;
 | 
						virtual void writeLowSideRx(const RxBurst&) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**@name Components of the channel description. */
 | 
						/**@name Components of the channel description. */
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
@@ -313,11 +351,21 @@ class L1Decoder {
 | 
				
			|||||||
	TypeAndOffset typeAndOffset() const;    ///< this comes from mMapping
 | 
						TypeAndOffset typeAndOffset() const;    ///< this comes from mMapping
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Control the processing of handover access busts. */
 | 
				
			||||||
 | 
						void handoverPending(bool flag)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (flag) mT3103.set();
 | 
				
			||||||
 | 
							mHandoverPending=flag;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						L1FEC* parent() { return mParent; }	// pat thinks it is not used virtual.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** How much time left in T3101? */
 | 
				
			||||||
 | 
						long debug3101remaining() { return mT3101.remaining(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected:
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	virtual L1FEC* parent() { return mParent; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Return pointer to paired L1 encoder, if any. */
 | 
						/** Return pointer to paired L1 encoder, if any. */
 | 
				
			||||||
	virtual L1Encoder* sibling();
 | 
						virtual L1Encoder* sibling();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -327,9 +375,12 @@ class L1Decoder {
 | 
				
			|||||||
	/** Mark the decoder as started.  */
 | 
						/** Mark the decoder as started.  */
 | 
				
			||||||
	virtual void start() { mRunning=true; }
 | 
						virtual void start() { mRunning=true; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
	void countGoodFrame();
 | 
						void countGoodFrame();
 | 
				
			||||||
 | 
					 | 
				
			||||||
	void countBadFrame();
 | 
						void countBadFrame();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool decrypt_maybe(string wIMSI, int wA5Alg);
 | 
				
			||||||
 | 
						unsigned char *kc() { return mKc; }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -338,6 +389,96 @@ class L1Decoder {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
	The L1FEC encapsulates an encoder and decoder.
 | 
						The L1FEC encapsulates an encoder and decoder.
 | 
				
			||||||
 | 
						Notes by pat 8/2011:
 | 
				
			||||||
 | 
						A complete L2 <-> L1 handler includes a set of instances of classes L1FEC, L1Encoder, L1Decoder.
 | 
				
			||||||
 | 
						These are always wrapped by an instance of LogicalChannel, which defines the
 | 
				
			||||||
 | 
						complete L3 <-> L1 handler.  The L1<->L2 handling is quite different for different
 | 
				
			||||||
 | 
						logical channels, so all these classes are always over-ridden by more specific ones
 | 
				
			||||||
 | 
						for each logical channel.  The descendents of L1Encoder/L2Decoder classes
 | 
				
			||||||
 | 
						are not just encoders/decoders; together with the associated LogicalChannel class
 | 
				
			||||||
 | 
						they incorporate the complete upstream and downstream channel handler.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Initialization:
 | 
				
			||||||
 | 
						All these instances are immortal (unlike GPRS PDCHL1FEC, which is allocated/deallocated
 | 
				
			||||||
 | 
						on demand.)  The mEncoder and mDecoder below are set once
 | 
				
			||||||
 | 
						and never changed, to define the related set of L1FEC+L1Encoder+L2Decoder.
 | 
				
			||||||
 | 
						At startup, GSMConfig uses info from the tables in GPRSTDMA
 | 
				
			||||||
 | 
						to create a complete set of instances of all these classes for each logical channel,
 | 
				
			||||||
 | 
						in each physical channel to which they apply.  (The C0T0 beach gets a different
 | 
				
			||||||
 | 
						set of classes than TCH Traffic channels, but every LogicalChannel descendent has
 | 
				
			||||||
 | 
						its own distinct set of L1FEC+L1Encoder+L1Decoder descendents.)
 | 
				
			||||||
 | 
						Note that there is an L1FEC+L1Encoder+L1Decoder per logical channel, not per
 | 
				
			||||||
 | 
						physical channel; they all share the physical channel resource, as described below.
 | 
				
			||||||
 | 
						The downstream end is connected to ARFCNManager in TRXManager.cpp.
 | 
				
			||||||
 | 
						The upstream end goes various places, connected at runtime through SAPMux,
 | 
				
			||||||
 | 
						or for some classes (example: RACH), directly to low-level managers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						See also documentation in LogicalChannel::send().
 | 
				
			||||||
 | 
						L2 -> L1 data flow is as follows:
 | 
				
			||||||
 | 
							L2 calls SAPMux::writeHighSide(L2Frame),
 | 
				
			||||||
 | 
							which calls L1FEC::writeHighSide(L2Frame),
 | 
				
			||||||
 | 
							<or> L2 calls L1FEC::writeHighSide(L2Frame) directly,
 | 
				
			||||||
 | 
							which then calls (L1Encoder)mEncoder->writeHighSide(L2Frame)
 | 
				
			||||||
 | 
							This is overridden to provide the logical channel specific handling,
 | 
				
			||||||
 | 
							which is performed by descendents of L1Encoder.  The frames may be processed
 | 
				
			||||||
 | 
							at that point (for example, cause RR setup/teardown based on the frame primitive)
 | 
				
			||||||
 | 
							or be passed downstream, in which case they usually go through sendFrame() below,
 | 
				
			||||||
 | 
							which is over-ridden to provide the logical-channel specific encoding.
 | 
				
			||||||
 | 
							Eventually, downstream frames go to L1Encoder::writeHighSideTx, which
 | 
				
			||||||
 | 
							delivers them to the ARFCNManager.
 | 
				
			||||||
 | 
							They may be delivered directly or spend time in an InterThreadQueue,
 | 
				
			||||||
 | 
							which is processed by a serviceLoop, (which may reside either in the L1Encoder
 | 
				
			||||||
 | 
							or LogicalChannel descendent) to synchronize them to the BTS frame clock
 | 
				
			||||||
 | 
							(by using rollForward() to set mPrevTime, mNextTime, and then waitToSend() to block.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L1 -> L2 data flow is as follows:
 | 
				
			||||||
 | 
							In TRXManager, the mDemuxTable, which was initialized from the GSMTDMA frame data,
 | 
				
			||||||
 | 
							is consulted to pass the radio burst to the appropriate logical channel, using
 | 
				
			||||||
 | 
							L1FEC::writeLowSideRx(RxBurst) in the appropriate L1FEC descendent.
 | 
				
			||||||
 | 
							From there, anything can happen.  Four bursts need to be assembled and decoded.
 | 
				
			||||||
 | 
							For TCH, FACH and SACH, this happens in (L1Decoder descendent)::processBurst(),
 | 
				
			||||||
 | 
							which then calls countGoodFrame()+handleGoodFrame() or countBadFrame() if the
 | 
				
			||||||
 | 
							parity was wrong.  handleGoodFrame() does the L1 housekeeping (start/stop timers,
 | 
				
			||||||
 | 
							remember power/timing parameters) then passes the frame up using SAPMux->writeLowSide(),
 | 
				
			||||||
 | 
							which calls some descendent L2DL.
 | 
				
			||||||
 | 
							For RACH, writeLowSideRx decodes the burst and sends a message directly
 | 
				
			||||||
 | 
							to gBTS.channelRequest(), which enqueues them for eventual processing
 | 
				
			||||||
 | 
							by AccessGrantResponder().
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Routines:
 | 
				
			||||||
 | 
						The start() routine is usually called once to create a thread to start a serviceloop thread.
 | 
				
			||||||
 | 
						Radio bursts are then delivered to the class endpoints forever.
 | 
				
			||||||
 | 
						The channels are turned on/off by calling open()/close(), which sets the active flag
 | 
				
			||||||
 | 
						to determine whether they will process those bursts or drop them.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GPRS Support:
 | 
				
			||||||
 | 
						The "L2Frame" used ubiquitously in this code is a GSM-specific L2 frame.
 | 
				
			||||||
 | 
						Now we want to add GPRS support with a new frame structure.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						I split the XCCHL1encoder/XCCHL1decoder classes into separate parts for handling
 | 
				
			||||||
 | 
						the logical channel flow, which remained in the original classes, and the
 | 
				
			||||||
 | 
						actual data encoding/decoding, which moved to SharedL1Encoder/SharedL2Encoder.
 | 
				
			||||||
 | 
						The new SharedL1Encoder/SharedL2Encoder are shared with GPRS.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Almost all the other functions in the L1Encoder/L2Decoder are different for GPRS
 | 
				
			||||||
 | 
						because the channel is shared by multiple MS.  So GPRS has its own
 | 
				
			||||||
 | 
						set of classes: PDCHL1FEC, PDCHL1Uplink, PDCHL1Downlink.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Notice that the frame numbers used by GPRS for Radio Blocks are identical to the
 | 
				
			||||||
 | 
						data frame numbers for GSM RR TCH channels.  Similarly, the GPRS timing advance channels
 | 
				
			||||||
 | 
						use the same frame numbers as GPRS RR SACCH (although we dont use those yet.)
 | 
				
			||||||
 | 
						We will allocate the GPRS channels dynamically from the TCH pool using
 | 
				
			||||||
 | 
						getTCH to allocate an existing TCH LogicalChannel class, which wont otherwise
 | 
				
			||||||
 | 
						be used for GPRS, except to return to the pool when GPRS signs off the channel.
 | 
				
			||||||
 | 
						The L1Decoder/L1Encoder classes will now be three state: inactive, active for RR,
 | 
				
			||||||
 | 
						active for GPRS.  Uplink data will be diverted to GPRS code at the earliest point
 | 
				
			||||||
 | 
						possible, which is in XCCHL1Decoder::writeLowSideRx().
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Another option was to completely bypass this code, modifing TRXManager,
 | 
				
			||||||
 | 
						either by changing the mDemuxTable to send radio bursts directly to the GPRS code, or
 | 
				
			||||||
 | 
						adding a new hook to simply send the entire timeslot to GPRS.
 | 
				
			||||||
 | 
						We might still want to go back and do that at some point, possibly when
 | 
				
			||||||
 | 
						we implement continuous timing advance.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
class L1FEC {
 | 
					class L1FEC {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -347,31 +488,51 @@ class L1FEC {
 | 
				
			|||||||
	L1Decoder* mDecoder;
 | 
						L1Decoder* mDecoder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
						// The mGprsReserved variable prevents the GSM subsystem from using the channel.
 | 
				
			||||||
 | 
						// When the GPRS PDCHL1FEC is ready to receive bursts, it sets mGPRSFEC.
 | 
				
			||||||
 | 
						bool mGprsReserved;				// If set, channel reserved for GPRS.
 | 
				
			||||||
 | 
						GPRS::PDCHL1FEC *mGPRSFEC;	// If set, bursts are delivered to GPRS.
 | 
				
			||||||
 | 
										// Currently, this could go in TCHFACCHL1Decoder instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
		The L1FEC constructor is over-ridden for different channel types.
 | 
							The L1FEC constructor is over-ridden for different channel types.
 | 
				
			||||||
		But the default has no encoder or decoder.
 | 
							But the default has no encoder or decoder.
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	L1FEC():mEncoder(NULL),mDecoder(NULL) {}
 | 
						L1FEC():mEncoder(NULL),mDecoder(NULL)
 | 
				
			||||||
 | 
						, mGprsReserved(0)
 | 
				
			||||||
 | 
						, mGPRSFEC(0) 
 | 
				
			||||||
 | 
						{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** This is no-op because these channels should not be destroyed. */
 | 
						/** This is no-op because these channels should not be destroyed.
 | 
				
			||||||
 | 
							(pat) We may allocate/deallocate GPRS channels on demand,
 | 
				
			||||||
 | 
							stealing GSM channels, so above statement may become untrue.
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
	virtual ~L1FEC() {};
 | 
						virtual ~L1FEC() {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Send in an RxBurst for decoding. */
 | 
						/** Send in an RxBurst for decoding. */
 | 
				
			||||||
	void writeLowSide(const RxBurst& burst)
 | 
						// (pat) I dont think this is ever called.  Gotta love C++
 | 
				
			||||||
		{ assert(mDecoder); mDecoder->writeLowSide(burst); }
 | 
						void writeLowSideRx(const RxBurst& burst)
 | 
				
			||||||
 | 
							{ assert(mDecoder); mDecoder->writeLowSideRx(burst); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Send in an L2Frame for encoding and transmission. */
 | 
						/** Send in an L2Frame for encoding and transmission. */
 | 
				
			||||||
	void writeHighSide(const L2Frame& frame)
 | 
						// (pat) not used for GPRS.
 | 
				
			||||||
		{ assert(mEncoder); mEncoder->writeHighSide(frame); }
 | 
						virtual void writeHighSide(const L2Frame& frame)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							assert(mEncoder); mEncoder->writeHighSide(frame);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Attach L1 to a downstream radio. */
 | 
						/** Attach L1 to a downstream radio. */
 | 
				
			||||||
	void downstream(ARFCNManager*);
 | 
						void downstream(ARFCNManager*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Attach L1 to an upstream SAPI mux and L2. */
 | 
						/** Attach L1 to an upstream SAPI mux and L2. */
 | 
				
			||||||
	void upstream(SAPMux* mux)
 | 
						// (pat) not used for GPRS.
 | 
				
			||||||
 | 
						virtual void upstream(SAPMux* mux)
 | 
				
			||||||
		{ if (mDecoder) mDecoder->upstream(mux); }
 | 
							{ if (mDecoder) mDecoder->upstream(mux); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** set encoder and decoder handover pending mode. */
 | 
				
			||||||
 | 
						void handoverPending(bool flag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**@name Ganged actions. */
 | 
						/**@name Ganged actions. */
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
	void open();
 | 
						void open();
 | 
				
			||||||
@@ -379,34 +540,37 @@ class L1FEC {
 | 
				
			|||||||
	//@}
 | 
						//@}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**@name Pass-through actions. */
 | 
						/**@name Pass-through actions that concern the physical channel. */
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
	TypeAndOffset typeAndOffset() const
 | 
						TypeAndOffset typeAndOffset() const
 | 
				
			||||||
		{ assert(mEncoder); return mEncoder->typeAndOffset(); }
 | 
							{ assert(mEncoder); return mEncoder->typeAndOffset(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unsigned TN() const
 | 
						unsigned TN() const		// Timeslot number to use.
 | 
				
			||||||
		{ assert(mEncoder); return mEncoder->TN(); }
 | 
							{ assert(mEncoder); return mEncoder->TN(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unsigned CN() const
 | 
						unsigned CN() const		// Carrier index.
 | 
				
			||||||
		{ assert(mEncoder); return mEncoder->CN(); }
 | 
							{ assert(mEncoder); return mEncoder->CN(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unsigned TSC() const
 | 
						unsigned TSC() const	// Trainging sequence for this channel.
 | 
				
			||||||
		{ assert(mEncoder); return mEncoder->TSC(); }
 | 
							{ assert(mEncoder); return mEncoder->TSC(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unsigned ARFCN() const
 | 
						unsigned ARFCN() const	// Absolute Radio Frequence Channel Number.
 | 
				
			||||||
		{ assert(mEncoder); return mEncoder->ARFCN(); }
 | 
							{ assert(mEncoder); return mEncoder->ARFCN(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	float FER() const
 | 
						float FER() const		// Frame Error Rate
 | 
				
			||||||
		{ assert(mDecoder); return mDecoder->FER(); }
 | 
							{ assert(mDecoder); return mDecoder->FER(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool recyclable() const
 | 
						bool recyclable() const	// Can we reuse this channel yet?
 | 
				
			||||||
		{ assert(mDecoder); return mDecoder->recyclable(); }
 | 
							{ assert(mDecoder); return mDecoder->recyclable(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool active() const;
 | 
						bool active() const;	// Channel in use?  See L1Encoder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// (pat) This lovely function is unsed.
 | 
				
			||||||
 | 
						// TRXManager.cpp:installDecoder uses L1Decoder::mapping() directly.
 | 
				
			||||||
	const TDMAMapping& txMapping() const
 | 
						const TDMAMapping& txMapping() const
 | 
				
			||||||
		{ assert(mEncoder); return mEncoder->mapping(); }
 | 
							{ assert(mEncoder); return mEncoder->mapping(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// (pat) This function is unsed.
 | 
				
			||||||
	const TDMAMapping& rcvMapping() const
 | 
						const TDMAMapping& rcvMapping() const
 | 
				
			||||||
		{ assert(mDecoder); return mDecoder->mapping(); }
 | 
							{ assert(mDecoder); return mDecoder->mapping(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -415,6 +579,11 @@ class L1FEC {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//void setDecoder(L1Decoder*me) { mDecoder = me; }
 | 
				
			||||||
 | 
						//void setEncoder(L1Encoder*me) { mEncoder = me; }
 | 
				
			||||||
 | 
						ARFCNManager *getRadio() { return mEncoder->getRadio(); }
 | 
				
			||||||
 | 
						bool inUseByGPRS() { return mGprsReserved; }
 | 
				
			||||||
 | 
						void setGPRS(bool reserved, GPRS::PDCHL1FEC *pch) { mGprsReserved = reserved; mGPRSFEC = pch; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	L1Decoder* decoder() { return mDecoder; }
 | 
						L1Decoder* decoder() { return mDecoder; }
 | 
				
			||||||
	L1Encoder* encoder() { return mEncoder; }
 | 
						L1Encoder* encoder() { return mEncoder; }
 | 
				
			||||||
@@ -433,7 +602,7 @@ class TestL1FEC : public L1FEC {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void writeLowSide(const RxBurst&);
 | 
						void writeLowSideRx(const RxBurst&);
 | 
				
			||||||
	void writeHighSide(const L2Frame&);
 | 
						void writeHighSide(const L2Frame&);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void downstream(ARFCNManager *wDownstream) { mDownstream=wDownstream; }
 | 
						void downstream(ARFCNManager *wDownstream) { mDownstream=wDownstream; }
 | 
				
			||||||
@@ -442,12 +611,14 @@ class TestL1FEC : public L1FEC {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** L1 decoder for Random Access (RACH). */
 | 
					/** L1 decoder for Random Access (RACH). */
 | 
				
			||||||
class RACHL1Decoder : public L1Decoder {
 | 
					class RACHL1Decoder : public L1Decoder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private:
 | 
						private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**@name FEC state. */
 | 
						/**@name FEC state. */
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
 | 
						ViterbiR2O4 mVCoder;	///< nearly all GSM channels use the same convolutional code
 | 
				
			||||||
	Parity mParity;					///< block coder
 | 
						Parity mParity;					///< block coder
 | 
				
			||||||
	BitVector mU;					///< u[], as per GSM 05.03 2.2
 | 
						BitVector mU;					///< u[], as per GSM 05.03 2.2
 | 
				
			||||||
	BitVector mD;					///< d[], as per GSM 05.03 2.2
 | 
						BitVector mD;					///< d[], as per GSM 05.03 2.2
 | 
				
			||||||
@@ -456,6 +627,9 @@ class RACHL1Decoder : public L1Decoder {
 | 
				
			|||||||
	// The RACH channel uses an internal FIFO,
 | 
						// The RACH channel uses an internal FIFO,
 | 
				
			||||||
	// because the channel allocation process might block
 | 
						// because the channel allocation process might block
 | 
				
			||||||
	// and we don't want to block the radio receive thread.
 | 
						// and we don't want to block the radio receive thread.
 | 
				
			||||||
 | 
						// (pat) I dont think this is used.  I think TRXManager calls writeLowSideRx directly.
 | 
				
			||||||
 | 
						// The serviceLoop is still started, and watches mQ forever, hopefully
 | 
				
			||||||
 | 
						// waiting for a burst that never comes.
 | 
				
			||||||
	RxBurstFIFO	mQ;					///< a FIFO to decouple the rx thread
 | 
						RxBurstFIFO	mQ;					///< a FIFO to decouple the rx thread
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Thread mServiceThread;			///< a thread to process the FIFO
 | 
						Thread mServiceThread;			///< a thread to process the FIFO
 | 
				
			||||||
@@ -473,7 +647,7 @@ class RACHL1Decoder : public L1Decoder {
 | 
				
			|||||||
	void start();
 | 
						void start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Decode the burst and call the channel allocator. */
 | 
						/** Decode the burst and call the channel allocator. */
 | 
				
			||||||
	void writeLowSide(const RxBurst&);
 | 
						void writeLowSideRx(const RxBurst&);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** A loop to watch the FIFO. */
 | 
						/** A loop to watch the FIFO. */
 | 
				
			||||||
	void serviceLoop();
 | 
						void serviceLoop();
 | 
				
			||||||
@@ -486,14 +660,105 @@ void *RACHL1DecoderServiceLoopAdapter(RACHL1Decoder*);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Abstract L1 decoder for most control channels -- GSM 05.03 4.1 */
 | 
					 | 
				
			||||||
class XCCHL1Decoder : public L1Decoder {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is just an encoder, nothing else, shared by RR and GPRS.
 | 
				
			||||||
 | 
					// This is the encoder specified in GSM05.03 sec 4.1, used for SACCH and GPRS CS-1.
 | 
				
			||||||
 | 
					// Why isnt this derived directly from L1Encoder, you ask?
 | 
				
			||||||
 | 
					// First it was because GPRS has multiple encoders for different encoding schemes
 | 
				
			||||||
 | 
					// and they all use a single L1Encoder attached to the radio.
 | 
				
			||||||
 | 
					// Second, because the GSM L1Encoder is not just an encoder, it is the complete stack
 | 
				
			||||||
 | 
					// down to the radio, whereas this class is just an encoder only.
 | 
				
			||||||
 | 
					// First case above is now inapplicable because the additional GPRS encoders are now
 | 
				
			||||||
 | 
					// derived from this one.
 | 
				
			||||||
 | 
					class SharedL1Encoder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
						ViterbiR2O4 mVCoder;	///< nearly all GSM channels use the same convolutional code
 | 
				
			||||||
 | 
					    Parity mBlockCoder;
 | 
				
			||||||
 | 
					    BitVector mC;               ///< c[], as per GSM 05.03 2.2
 | 
				
			||||||
 | 
					    BitVector mU;               ///< u[], as per GSM 05.03 2.2
 | 
				
			||||||
 | 
					    //BitVector mDP;              ///< d[]:p[] (data & parity)
 | 
				
			||||||
 | 
					    BitVector mP;               ///< p[], as per GSM 05.03 2.2
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					    BitVector mD;               ///< d[], as per GSM 05.03 2.2		Incoming Data.
 | 
				
			||||||
 | 
					    BitVector mI[4];           ///< i[][], as per GSM 05.03 2.2	Outgoing Data.
 | 
				
			||||||
 | 
					    BitVector mE[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						  Encode u[] to c[].
 | 
				
			||||||
 | 
						  Includes LSB-MSB reversal within each octet.
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
 | 
						void encode41();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						  Interleave c[] to i[].
 | 
				
			||||||
 | 
						  GSM 05.03 4.1.4.
 | 
				
			||||||
 | 
						  It is not virtual.
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
 | 
						void interleave41();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SharedL1Encoder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//void encodeFrame41(const L2Frame &frame, int offset);
 | 
				
			||||||
 | 
						void encodeFrame41(const BitVector &frame, int offset, bool copy=true);
 | 
				
			||||||
 | 
						void initInterleave(int);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Shared by RR and GPRS
 | 
				
			||||||
 | 
					class SharedL1Decoder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
    protected:
 | 
					    protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**@name FEC state. */
 | 
					    /**@name FEC state. */
 | 
				
			||||||
    //@{
 | 
					    //@{
 | 
				
			||||||
 | 
						ViterbiR2O4 mVCoder;	///< nearly all GSM channels use the same convolutional code
 | 
				
			||||||
    Parity mBlockCoder;
 | 
					    Parity mBlockCoder;
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					    SoftVector mC;              ///< c[], as per GSM 05.03 2.2
 | 
				
			||||||
 | 
					    BitVector mU;               ///< u[], as per GSM 05.03 2.2
 | 
				
			||||||
 | 
					    BitVector mP;               ///< p[], as per GSM 05.03 2.2
 | 
				
			||||||
 | 
					    BitVector mDP;              ///< d[]:p[] (data & parity)
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					    BitVector mD;               ///< d[], as per GSM 05.03 2.2
 | 
				
			||||||
 | 
					    SoftVector mE[4];
 | 
				
			||||||
 | 
					    SoftVector mI[4];           ///< i[][], as per GSM 05.03 2.2
 | 
				
			||||||
 | 
						/**@name Handover Access Burst FEC state. */
 | 
				
			||||||
 | 
						//@{
 | 
				
			||||||
 | 
						Parity mHParity;			///< block coder for handover access bursts
 | 
				
			||||||
 | 
						BitVector mHU;				///< u[] for handover access, as per GSM 05.03 4.6
 | 
				
			||||||
 | 
						BitVector mHD;				///< d[] for handover access, as per GSM 05.03 4.6
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
 | 
					    //@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GSM::Time mReadTime;        ///< timestamp of the first burst
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SharedL1Decoder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void deinterleave();
 | 
				
			||||||
 | 
					    bool decode();
 | 
				
			||||||
 | 
						SoftVector *result() { return mI; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Abstract L1 decoder for most control channels -- GSM 05.03 4.1 */
 | 
				
			||||||
 | 
					class XCCHL1Decoder :
 | 
				
			||||||
 | 
						public SharedL1Decoder,
 | 
				
			||||||
 | 
						public L1Decoder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Moved to SharedL1Decoder
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
						/**@name FEC state. */
 | 
				
			||||||
 | 
						//@{
 | 
				
			||||||
 | 
						/**@name Normal Burst FEC state. */
 | 
				
			||||||
 | 
						//@{
 | 
				
			||||||
 | 
						Parity mBlockCoder;			///< block coder for normal bursts
 | 
				
			||||||
	SoftVector mI[4];			///< i[][], as per GSM 05.03 2.2
 | 
						SoftVector mI[4];			///< i[][], as per GSM 05.03 2.2
 | 
				
			||||||
	SoftVector mC;				///< c[], as per GSM 05.03 2.2
 | 
						SoftVector mC;				///< c[], as per GSM 05.03 2.2
 | 
				
			||||||
	BitVector mU;				///< u[], as per GSM 05.03 2.2
 | 
						BitVector mU;				///< u[], as per GSM 05.03 2.2
 | 
				
			||||||
@@ -501,15 +766,24 @@ class XCCHL1Decoder : public L1Decoder {
 | 
				
			|||||||
	BitVector mDP;				///< d[]:p[] (data & parity)
 | 
						BitVector mDP;				///< d[]:p[] (data & parity)
 | 
				
			||||||
	BitVector mD;				///< d[], as per GSM 05.03 2.2
 | 
						BitVector mD;				///< d[], as per GSM 05.03 2.2
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
						/**@name Handover Access Burst FEC state. */
 | 
				
			||||||
	GSM::Time mReadTime;		///< timestamp of the first burst
 | 
						//@{
 | 
				
			||||||
	unsigned mRSSIHistory[4];
 | 
						Parity mHParity;			///< block coder for handover access bursts
 | 
				
			||||||
 | 
						BitVector mHU;				///< u[] for handover access, as per GSM 05.03 4.6
 | 
				
			||||||
 | 
						BitVector mHD;				///< d[] for handover access, as per GSM 05.03 4.6
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
 | 
						//@}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	XCCHL1Decoder(unsigned wCN, unsigned wTN, const TDMAMapping& wMapping,
 | 
						XCCHL1Decoder(unsigned wCN, unsigned wTN, const TDMAMapping& wMapping,
 | 
				
			||||||
		L1FEC *wParent);
 | 
							L1FEC *wParent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void saveMi();
 | 
				
			||||||
 | 
						void restoreMi();
 | 
				
			||||||
 | 
						void decrypt();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected:
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Offset to the start of the L2 header. */
 | 
						/** Offset to the start of the L2 header. */
 | 
				
			||||||
@@ -519,7 +793,7 @@ class XCCHL1Decoder : public L1Decoder {
 | 
				
			|||||||
	virtual ChannelType channelType() const = 0;
 | 
						virtual ChannelType channelType() const = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Accept a timeslot for processing and drive data up the chain. */
 | 
						/** Accept a timeslot for processing and drive data up the chain. */
 | 
				
			||||||
	virtual void writeLowSide(const RxBurst&);
 | 
						virtual void writeLowSideRx(const RxBurst&);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	  Accept a new timeslot for processing and save it in i[].
 | 
						  Accept a new timeslot for processing and save it in i[].
 | 
				
			||||||
@@ -529,6 +803,8 @@ class XCCHL1Decoder : public L1Decoder {
 | 
				
			|||||||
	*/
 | 
						*/
 | 
				
			||||||
	virtual bool processBurst(const RxBurst&);
 | 
						virtual bool processBurst(const RxBurst&);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 | 
						// Moved to SharedL1Encoder.
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	  Deinterleave the i[] to c[].
 | 
						  Deinterleave the i[] to c[].
 | 
				
			||||||
	  This virtual method works for all block-interleaved channels (xCCHs).
 | 
						  This virtual method works for all block-interleaved channels (xCCHs).
 | 
				
			||||||
@@ -542,6 +818,7 @@ class XCCHL1Decoder : public L1Decoder {
 | 
				
			|||||||
	  @return True if frame passed parity check.
 | 
						  @return True if frame passed parity check.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	bool decode();
 | 
						bool decode();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	/** Finish off a properly-received L2Frame in mU and send it up to L2. */
 | 
						/** Finish off a properly-received L2Frame in mU and send it up to L2. */
 | 
				
			||||||
	virtual void handleGoodFrame();
 | 
						virtual void handleGoodFrame();
 | 
				
			||||||
@@ -567,8 +844,6 @@ class SDCCHL1Decoder : public XCCHL1Decoder {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
	L1 decoder for the SACCH.
 | 
						L1 decoder for the SACCH.
 | 
				
			||||||
	Like any other control channel, but with hooks for power/timing control.
 | 
						Like any other control channel, but with hooks for power/timing control.
 | 
				
			||||||
@@ -578,9 +853,9 @@ class SACCHL1Decoder : public XCCHL1Decoder {
 | 
				
			|||||||
	private:
 | 
						private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SACCHL1FEC *mSACCHParent;
 | 
						SACCHL1FEC *mSACCHParent;
 | 
				
			||||||
	unsigned mRSSICounter;
 | 
						volatile float mRSSI;			///< most recent RSSI, dB wrt full scale
 | 
				
			||||||
	volatile float mRSSI[4];			///< RSSI history , dB wrt full scale
 | 
						volatile float mTimingError;		///< Timing error history in symbols
 | 
				
			||||||
	volatile float mTimingError[4];		///< Timing error histoty in symbol
 | 
						volatile double mTimestamp;		///< system time of most recent received burst
 | 
				
			||||||
	volatile int mActualMSPower;		///< actual MS tx power in dBm
 | 
						volatile int mActualMSPower;		///< actual MS tx power in dBm
 | 
				
			||||||
	volatile int mActualMSTiming;		///< actual MS tx timing advance in symbols
 | 
						volatile int mActualMSTiming;		///< actual MS tx timing advance in symbols
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -593,10 +868,12 @@ class SACCHL1Decoder : public XCCHL1Decoder {
 | 
				
			|||||||
		SACCHL1FEC *wParent)
 | 
							SACCHL1FEC *wParent)
 | 
				
			||||||
		:XCCHL1Decoder(wCN,wTN,wMapping,(L1FEC*)wParent),
 | 
							:XCCHL1Decoder(wCN,wTN,wMapping,(L1FEC*)wParent),
 | 
				
			||||||
		mSACCHParent(wParent),
 | 
							mSACCHParent(wParent),
 | 
				
			||||||
		mRSSICounter(0)
 | 
							mRSSI(0.0F),
 | 
				
			||||||
	{
 | 
							mTimingError(0.0F),
 | 
				
			||||||
		for (int i=0; i<4; i++) mRSSI[i]=0.0F;
 | 
							mTimestamp(0.0),
 | 
				
			||||||
	}
 | 
							mActualMSPower(0),
 | 
				
			||||||
 | 
							mActualMSTiming(0)
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ChannelType channelType() const { return SACCHType; }
 | 
						ChannelType channelType() const { return SACCHType; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -612,18 +889,24 @@ class SACCHL1Decoder : public XCCHL1Decoder {
 | 
				
			|||||||
	bool processBurst(const RxBurst&);
 | 
						bool processBurst(const RxBurst&);
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	/** Set pyshical parameters for initialization. */
 | 
						/** Set pyshical parameters for initialization. */
 | 
				
			||||||
	void setPhy(float wRSSI, float wTimingError);
 | 
						void setPhy(float wRSSI, float wTimingError, double wTimestamp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void setPhy(const SACCHL1Decoder& other);
 | 
						void setPhy(const SACCHL1Decoder& other);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** RSSI of most recent received burst, in dB wrt full scale. */
 | 
						/** RSSI of most recent received burst, in dB wrt full scale. */
 | 
				
			||||||
	float RSSI() const;
 | 
						float RSSI() const { return mRSSI; }
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						/** Artificially push down RSSI to induce the handset to push more power. */
 | 
				
			||||||
 | 
						void RSSIBumpDown(float dB) { mRSSI -= dB; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
		Timing error of most recent received burst, symbol units.
 | 
							Timing error of most recent received burst, symbol units.
 | 
				
			||||||
		Positive is late; negative is early.
 | 
							Positive is late; negative is early.
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	float timingError() const;
 | 
						float timingError() const { return mTimingError; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Timestamp of most recent received burst. */
 | 
				
			||||||
 | 
						double timestamp() const { return mTimestamp; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected:
 | 
						protected:
 | 
				
			||||||
@@ -645,10 +928,15 @@ class SACCHL1Decoder : public XCCHL1Decoder {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** L1 encoder used for many control channels -- mostly from GSM 05.03 4.1 */
 | 
					/** L1 encoder used for many control channels -- mostly from GSM 05.03 4.1 */
 | 
				
			||||||
class XCCHL1Encoder : public L1Encoder {
 | 
					class XCCHL1Encoder :
 | 
				
			||||||
 | 
						public SharedL1Encoder,
 | 
				
			||||||
 | 
						public L1Encoder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected:
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Moved to SharedL1Encoder
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
	/**@name FEC signal processing state.  */
 | 
						/**@name FEC signal processing state.  */
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
	Parity mBlockCoder;			///< block coder for this channel
 | 
						Parity mBlockCoder;			///< block coder for this channel
 | 
				
			||||||
@@ -658,6 +946,7 @@ class XCCHL1Encoder : public L1Encoder {
 | 
				
			|||||||
	BitVector mD;				///< d[], as per GSM 05.03 2.2
 | 
						BitVector mD;				///< d[], as per GSM 05.03 2.2
 | 
				
			||||||
	BitVector mP;				///< p[], as per GSM 05.03 2.2
 | 
						BitVector mP;				///< p[], as per GSM 05.03 2.2
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -670,6 +959,7 @@ class XCCHL1Encoder : public L1Encoder {
 | 
				
			|||||||
	protected:
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Process pending incoming messages. */
 | 
						/** Process pending incoming messages. */
 | 
				
			||||||
 | 
						// (pat) Messages may be control primitives.  If it is data, it is passed to sendFrame()
 | 
				
			||||||
	virtual void writeHighSide(const L2Frame&);
 | 
						virtual void writeHighSide(const L2Frame&);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Offset from the start of mU to the start of the L2 frame. */
 | 
						/** Offset from the start of mU to the start of the L2 frame. */
 | 
				
			||||||
@@ -677,7 +967,9 @@ class XCCHL1Encoder : public L1Encoder {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	/** Send a single L2 frame.  */
 | 
						/** Send a single L2 frame.  */
 | 
				
			||||||
	virtual void sendFrame(const L2Frame&);
 | 
						virtual void sendFrame(const L2Frame&);
 | 
				
			||||||
 | 
						// Moved to SharedL1Encoder
 | 
				
			||||||
 | 
						//virtual void transmit(BitVector *mI);
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	  Encode u[] to c[].
 | 
						  Encode u[] to c[].
 | 
				
			||||||
	  Includes LSB-MSB reversal within each octet.
 | 
						  Includes LSB-MSB reversal within each octet.
 | 
				
			||||||
@@ -697,6 +989,7 @@ class XCCHL1Encoder : public L1Encoder {
 | 
				
			|||||||
	  GSM 05.03 4.1.5, 05.02 5.2.3.
 | 
						  GSM 05.03 4.1.5, 05.02 5.2.3.
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	virtual void transmit();
 | 
						virtual void transmit();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -710,6 +1003,9 @@ private:
 | 
				
			|||||||
	bool mPreviousFACCH;	///< A copy of the previous stealing flag state.
 | 
						bool mPreviousFACCH;	///< A copy of the previous stealing flag state.
 | 
				
			||||||
	size_t mOffset;			///< Current deinterleaving offset.
 | 
						size_t mOffset;			///< Current deinterleaving offset.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						BitVector mE[8];
 | 
				
			||||||
 | 
						// (pat) Yes, the mI here duplicates but overrides the same
 | 
				
			||||||
 | 
						// vector down in XCCHL1Encoder.
 | 
				
			||||||
	BitVector mI[8];			///< deinterleaving history, 8 blocks instead of 4
 | 
						BitVector mI[8];			///< deinterleaving history, 8 blocks instead of 4
 | 
				
			||||||
	BitVector mTCHU;				///< u[], but for traffic
 | 
						BitVector mTCHU;				///< u[], but for traffic
 | 
				
			||||||
	BitVector mTCHD;				///< d[], but for traffic
 | 
						BitVector mTCHD;				///< d[], but for traffic
 | 
				
			||||||
@@ -743,8 +1039,12 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GSM 05.03, 3.1.3
 | 
				
			||||||
 | 
						void interleave31(int blockOffset);
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
	/** Interleave c[] to i[].  GSM 05.03 4.1.4. */
 | 
						/** Interleave c[] to i[].  GSM 05.03 4.1.4. */
 | 
				
			||||||
	virtual void interleave(int blockOffset);
 | 
						virtual void interleave31(int blockOffset);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Encode a FACCH and enqueue it for transmission. */
 | 
						/** Encode a FACCH and enqueue it for transmission. */
 | 
				
			||||||
	void sendFrame(const L2Frame&);
 | 
						void sendFrame(const L2Frame&);
 | 
				
			||||||
@@ -773,6 +1073,7 @@ class TCHFACCHL1Decoder : public XCCHL1Decoder {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	protected:
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SoftVector mE[8];	///< deinterleaving history, 8 blocks instead of 4
 | 
				
			||||||
	SoftVector mI[8];	///< deinterleaving history, 8 blocks instead of 4
 | 
						SoftVector mI[8];	///< deinterleaving history, 8 blocks instead of 4
 | 
				
			||||||
	BitVector mTCHU;					///< u[] (uncoded) in the spec
 | 
						BitVector mTCHU;					///< u[] (uncoded) in the spec
 | 
				
			||||||
	BitVector mTCHD;					///< d[] (data) in the spec
 | 
						BitVector mTCHD;					///< d[] (data) in the spec
 | 
				
			||||||
@@ -780,8 +1081,8 @@ class TCHFACCHL1Decoder : public XCCHL1Decoder {
 | 
				
			|||||||
	BitVector mClass1A_d;				///< the class 1A part of d[]
 | 
						BitVector mClass1A_d;				///< the class 1A part of d[]
 | 
				
			||||||
	SoftVector mClass2_c;				///< the class 2 part of c[]
 | 
						SoftVector mClass2_c;				///< the class 2 part of c[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	VocoderFrame mVFrame;				///< unpacking buffer for vocoder frame
 | 
						VocoderFrame mVFrame;		///< unpacking buffer for current vocoder frame
 | 
				
			||||||
	unsigned char mPrevGoodFrame[33];	///< previous good frame.
 | 
						VocoderFrame mPrevGoodFrame;	///< previous good frame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Parity mTCHParity;
 | 
						Parity mTCHParity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -798,7 +1099,7 @@ class TCHFACCHL1Decoder : public XCCHL1Decoder {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** TCH/FACCH has a special-case writeLowSide. */
 | 
						/** TCH/FACCH has a special-case writeLowSide. */
 | 
				
			||||||
	void writeLowSide(const RxBurst& inBurst);
 | 
						void writeLowSideRx(const RxBurst& inBurst);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
		Unlike other DCCHs, TCH/FACCH process burst calls
 | 
							Unlike other DCCHs, TCH/FACCH process burst calls
 | 
				
			||||||
@@ -806,9 +1107,14 @@ class TCHFACCHL1Decoder : public XCCHL1Decoder {
 | 
				
			|||||||
	*/
 | 
						*/
 | 
				
			||||||
	bool processBurst( const RxBurst& );
 | 
						bool processBurst( const RxBurst& );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void saveMi();
 | 
				
			||||||
 | 
						void restoreMi();
 | 
				
			||||||
 | 
						void decrypt(int B);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	/** Deinterleave i[] to c[].  */
 | 
						/** Deinterleave i[] to c[].  */
 | 
				
			||||||
	void deinterleave(int blockOffset );
 | 
						void deinterleave(int blockOffset );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// (pat) Routine does not exist.
 | 
				
			||||||
	void replaceFACCH( int blockOffset );
 | 
						void replaceFACCH( int blockOffset );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
@@ -837,7 +1143,9 @@ class TCHFACCHL1Decoder : public XCCHL1Decoder {
 | 
				
			|||||||
	This is base class for output-only encoders.
 | 
						This is base class for output-only encoders.
 | 
				
			||||||
	These all have very thin L2/L3 and are driven by a clock instead of a FIFO.
 | 
						These all have very thin L2/L3 and are driven by a clock instead of a FIFO.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
class GeneratorL1Encoder : public L1Encoder {
 | 
					class GeneratorL1Encoder :
 | 
				
			||||||
 | 
						public L1Encoder
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private:
 | 
						private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -880,7 +1188,7 @@ void *GeneratorL1EncoderServiceLoopAdapter(GeneratorL1Encoder*);
 | 
				
			|||||||
class SCHL1Encoder : public GeneratorL1Encoder {
 | 
					class SCHL1Encoder : public GeneratorL1Encoder {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private:
 | 
						private:
 | 
				
			||||||
 | 
						ViterbiR2O4 mVCoder;	///< nearly all GSM channels use the same convolutional code
 | 
				
			||||||
	Parity mBlockCoder;			///< block parity coder
 | 
						Parity mBlockCoder;			///< block parity coder
 | 
				
			||||||
	BitVector mU;				///< u[], as per GSM 05.03 2.2
 | 
						BitVector mU;				///< u[], as per GSM 05.03 2.2
 | 
				
			||||||
	BitVector mE;				///< e[], as per GSM 05.03 2.2
 | 
						BitVector mE;				///< e[], as per GSM 05.03 2.2
 | 
				
			||||||
@@ -975,7 +1283,6 @@ class BCCHL1Encoder : public NDCCHL1Encoder {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
	L1 decoder for the SACCH.
 | 
						L1 decoder for the SACCH.
 | 
				
			||||||
	Like any other control channel, but with hooks for power/timing control.
 | 
						Like any other control channel, but with hooks for power/timing control.
 | 
				
			||||||
@@ -1171,10 +1478,12 @@ class SACCHL1FEC : public L1FEC {
 | 
				
			|||||||
	//@{
 | 
						//@{
 | 
				
			||||||
	float RSSI() const { return mSACCHDecoder->RSSI(); }
 | 
						float RSSI() const { return mSACCHDecoder->RSSI(); }
 | 
				
			||||||
	float timingError() const { return mSACCHDecoder->timingError(); }
 | 
						float timingError() const { return mSACCHDecoder->timingError(); }
 | 
				
			||||||
 | 
						double timestamp() const { return mSACCHDecoder->timestamp(); }
 | 
				
			||||||
	int actualMSPower() const { return mSACCHDecoder->actualMSPower(); }
 | 
						int actualMSPower() const { return mSACCHDecoder->actualMSPower(); }
 | 
				
			||||||
	int actualMSTiming() const { return mSACCHDecoder->actualMSTiming(); }
 | 
						int actualMSTiming() const { return mSACCHDecoder->actualMSTiming(); }
 | 
				
			||||||
	void setPhy(const SACCHL1FEC&);
 | 
						void setPhy(const SACCHL1FEC&);
 | 
				
			||||||
	virtual void setPhy(float RSSI, float timingError);
 | 
						virtual void setPhy(float RSSI, float timingError, double wTimestamp);
 | 
				
			||||||
 | 
						void RSSIBumpDown(int dB) { mSACCHDecoder->RSSIBumpDown(dB); }
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,16 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for
 | 
				
			||||||
 | 
					* licensing information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -43,6 +35,7 @@ implementation, although no code is copied directly.
 | 
				
			|||||||
#include "GSML2LAPDm.h"
 | 
					#include "GSML2LAPDm.h"
 | 
				
			||||||
#include "GSMSAPMux.h"
 | 
					#include "GSMSAPMux.h"
 | 
				
			||||||
#include <Logger.h>
 | 
					#include <Logger.h>
 | 
				
			||||||
 | 
					#include <GSML3RRMessages.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace std;
 | 
					using namespace std;
 | 
				
			||||||
using namespace GSM;
 | 
					using namespace GSM;
 | 
				
			||||||
@@ -72,7 +65,7 @@ void CCCHL2::writeHighSide(const GSM::L3Frame& l3)
 | 
				
			|||||||
	assert(mDownstream);
 | 
						assert(mDownstream);
 | 
				
			||||||
	assert(l3.primitive()==UNIT_DATA);
 | 
						assert(l3.primitive()==UNIT_DATA);
 | 
				
			||||||
	L2Header header(L2Length(l3.L2Length()));
 | 
						L2Header header(L2Length(l3.L2Length()));
 | 
				
			||||||
	mDownstream->writeHighSide(L2Frame(header,l3));
 | 
						mDownstream->writeHighSide(L2Frame(header,l3,true));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -94,6 +87,7 @@ L2LAPDm::L2LAPDm(unsigned wC, unsigned wSAPI)
 | 
				
			|||||||
	mIdleFrame.fillField(8*0,(mC<<1)|1,8);		// address
 | 
						mIdleFrame.fillField(8*0,(mC<<1)|1,8);		// address
 | 
				
			||||||
	mIdleFrame.fillField(8*1,3,8);				// control
 | 
						mIdleFrame.fillField(8*1,3,8);				// control
 | 
				
			||||||
	mIdleFrame.fillField(8*2,1,8);				// length
 | 
						mIdleFrame.fillField(8*2,1,8);				// length
 | 
				
			||||||
 | 
						if (gConfig.getBool("GSM.Cipher.ScrambleFiller")) mIdleFrame.randomizeFiller(8*4);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -102,7 +96,9 @@ void L2LAPDm::writeL1(const L2Frame& frame)
 | 
				
			|||||||
	OBJLOG(DEBUG) <<"L2LAPDm::writeL1 " << frame;
 | 
						OBJLOG(DEBUG) <<"L2LAPDm::writeL1 " << frame;
 | 
				
			||||||
	//assert(mDownstream);
 | 
						//assert(mDownstream);
 | 
				
			||||||
	if (!mDownstream) return;
 | 
						if (!mDownstream) return;
 | 
				
			||||||
	ScopedLock lock(mLock);
 | 
						// It is tempting not to lock this, but if we don't,
 | 
				
			||||||
 | 
						// the ::open operation can result in contention in L1.
 | 
				
			||||||
 | 
						ScopedLock lock(mL1Lock);
 | 
				
			||||||
	mDownstream->writeHighSide(frame);
 | 
						mDownstream->writeHighSide(frame);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -288,13 +284,16 @@ void L2LAPDm::open()
 | 
				
			|||||||
	OBJLOG(DEBUG);
 | 
						OBJLOG(DEBUG);
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		ScopedLock lock(mLock);
 | 
							ScopedLock lock(mLock);
 | 
				
			||||||
 | 
							OBJLOG(DEBUG);
 | 
				
			||||||
		if (!mRunning) {
 | 
							if (!mRunning) {
 | 
				
			||||||
 | 
								OBJLOG(DEBUG);
 | 
				
			||||||
			// We can't call this from the constructor,
 | 
								// We can't call this from the constructor,
 | 
				
			||||||
			// since N201 may not be defined yet.
 | 
								// since N201 may not be defined yet.
 | 
				
			||||||
			mMaxIPayloadBits = 8*N201(L2Control::IFormat);
 | 
								mMaxIPayloadBits = 8*N201(L2Control::IFormat);
 | 
				
			||||||
			mRunning = true;
 | 
								mRunning = true;
 | 
				
			||||||
			mUpstreamThread.start((void *(*)(void*))LAPDmServiceLoopAdapter,this);
 | 
								mUpstreamThread.start((void *(*)(void*))LAPDmServiceLoopAdapter,this);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							OBJLOG(DEBUG);
 | 
				
			||||||
		mL3Out.clear();
 | 
							mL3Out.clear();
 | 
				
			||||||
		mL1In.clear();
 | 
							mL1In.clear();
 | 
				
			||||||
		clearCounters();
 | 
							clearCounters();
 | 
				
			||||||
@@ -302,7 +301,9 @@ void L2LAPDm::open()
 | 
				
			|||||||
		mAckSignal.signal();
 | 
							mAckSignal.signal();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						OBJLOG(DEBUG);
 | 
				
			||||||
	if (mSAPI==0) sendIdle();
 | 
						if (mSAPI==0) sendIdle();
 | 
				
			||||||
 | 
						OBJLOG(DEBUG);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -484,6 +485,9 @@ void L2LAPDm::receiveFrame(const GSM::L2Frame& frame)
 | 
				
			|||||||
				case L2Control::UFormat: receiveUFrame(frame); break;
 | 
									case L2Control::UFormat: receiveUFrame(frame); break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
							case HANDOVER_ACCESS:
 | 
				
			||||||
 | 
								mL3Out.write(new L3Frame(HANDOVER_ACCESS));
 | 
				
			||||||
 | 
								break;                  
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			OBJLOG(ERR) << "unhandled primitive in L1->L2 " << frame;
 | 
								OBJLOG(ERR) << "unhandled primitive in L1->L2 " << frame;
 | 
				
			||||||
			assert(0);
 | 
								assert(0);
 | 
				
			||||||
@@ -577,8 +581,7 @@ void L2LAPDm::receiveUFrameSABM(const L2Frame& frame)
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			// Re-establishment procedure, GSM 04.06 5.6.3.
 | 
								// Re-establishment procedure, GSM 04.06 5.6.3.
 | 
				
			||||||
			// This basically resets the ack engine.
 | 
								// This basically resets the ack engine.
 | 
				
			||||||
			// We should not actually see this, as of rev 2.4.
 | 
								// The most common reason for this is failed handover.
 | 
				
			||||||
			OBJLOG(WARNING) << "reestablishment not really supported";
 | 
					 | 
				
			||||||
			sendUFrameUA(frame.PF());
 | 
								sendUFrameUA(frame.PF());
 | 
				
			||||||
			clearCounters();
 | 
								clearCounters();
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
@@ -908,8 +911,17 @@ void L2LAPDm::sendUFrameUI(const L3Frame& l3)
 | 
				
			|||||||
	L2Control control(L2Control::UFormat,1,0x00);
 | 
						L2Control control(L2Control::UFormat,1,0x00);
 | 
				
			||||||
	L2Length length(l3.L2Length());
 | 
						L2Length length(l3.L2Length());
 | 
				
			||||||
	L2Header header(address,control,length);
 | 
						L2Header header(address,control,length);
 | 
				
			||||||
	writeL1NoAck(L2Frame(header,l3));
 | 
						L2Frame l2f = L2Frame(header, l3);
 | 
				
			||||||
 | 
						// FIXME -
 | 
				
			||||||
 | 
						// The correct solution is to build an L2 frame in RadioResource.cpp and control the bits explicitly up there.
 | 
				
			||||||
 | 
						// But I don't know if the LogcialChannel class has a method for sending frames directly into L2.
 | 
				
			||||||
 | 
						if (l3.PD() == L3RadioResourcePD && l3.MTI() == L3RRMessage::PhysicalInformation) {
 | 
				
			||||||
 | 
							l2f.CR(true);
 | 
				
			||||||
 | 
							l2f.PF(false);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						writeL1NoAck(l2f);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -995,6 +1007,25 @@ bool L2LAPDm::stuckChannel(const L2Frame& frame)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CBCHL2::writeHighSide(const GSM::L3Frame& l3)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						OBJLOG(DEBUG) <<"CBCHL2 incoming L3 frame: " << l3;
 | 
				
			||||||
 | 
						assert(mDownstream);
 | 
				
			||||||
 | 
						assert(l3.primitive()==UNIT_DATA);
 | 
				
			||||||
 | 
						assert(l3.size()==88*8);
 | 
				
			||||||
 | 
						L2Frame outFrame(DATA);
 | 
				
			||||||
 | 
						// Chop the L3 frame into 4 L2 frames.
 | 
				
			||||||
 | 
						for (unsigned i=0; i<4; i++) {
 | 
				
			||||||
 | 
							outFrame.fillField(0,0x02,4);
 | 
				
			||||||
 | 
							outFrame.fillField(4,i,4);
 | 
				
			||||||
 | 
							const BitVector thisSeg = l3.segment(i*22*8,22*8);
 | 
				
			||||||
 | 
							thisSeg.copyToSegment(outFrame,8);
 | 
				
			||||||
 | 
							OBJLOG(DEBUG) << "CBCHL2 outgoing L2 frame: " << outFrame;
 | 
				
			||||||
 | 
							mDownstream->writeHighSide(outFrame);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// vim: ts=4 sw=4
 | 
					// vim: ts=4 sw=4
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,24 +2,16 @@
 | 
				
			|||||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
					* Copyright 2008 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2011 Range Networks, Inc.
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -135,13 +127,38 @@ class CCCHL2 : public L2DL {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	void writeLowSide(const GSM::L2Frame&) { assert(0); }
 | 
						void writeLowSide(const GSM::L2Frame&) { assert(0); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    L3Frame* readHighSide(unsigned /*timeout = 3600000*/) { assert(0); return NULL; }
 | 
						L3Frame* readHighSide(unsigned timeout=3600000) { assert(0); return NULL; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void writeHighSide(const GSM::L3Frame&);
 | 
						void writeHighSide(const GSM::L3Frame&);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
						A "thin" L2 for CBCH.
 | 
				
			||||||
 | 
						This is a downlink-only channel and does not use LAPDm.
 | 
				
			||||||
 | 
						See GSM 04.12 3.3.1.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					class CBCHL2 : public L2DL {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned N201(GSM::L2Control::ControlFormat format) const { assert(0); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned N200() const { return 0; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void open() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeLowSide(const GSM::L2Frame&) { assert(0); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3Frame* readHighSide(unsigned timeout=3600000) { assert(0); return NULL; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeHighSide(const GSM::L3Frame&);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -392,6 +409,7 @@ class L2LAPDm : public L2DL {
 | 
				
			|||||||
			- This need not be called when the channel is closed,
 | 
								- This need not be called when the channel is closed,
 | 
				
			||||||
				as L1 will generate its own filler pattern that is more
 | 
									as L1 will generate its own filler pattern that is more
 | 
				
			||||||
				appropriate in this condition.
 | 
									appropriate in this condition.
 | 
				
			||||||
 | 
								- This does not need to be called for the SACCH or FACCH.
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	virtual void sendIdle() { writeL1(mIdleFrame); }
 | 
						virtual void sendIdle() { writeL1(mIdleFrame); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,17 @@
 | 
				
			|||||||
/**@file @brief Call Control messages, GSM 04.08 9.3 */
 | 
					/**@file
 | 
				
			||||||
 | 
						@brief Call Control messages, GSM 04.08 9.3
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -71,30 +63,40 @@ void L3BearerCapability::text(ostream& os) const
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void L3BCDDigits::parse(const L3Frame& src, size_t &rp, size_t numOctets)
 | 
					void L3BCDDigits::parse(const L3Frame& src, size_t &rp, size_t numOctets, bool international)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned i=0;
 | 
						unsigned i=0;
 | 
				
			||||||
	size_t readOctets = 0;
 | 
						size_t readOctets = 0;
 | 
				
			||||||
 | 
						if (international) mDigits[i++] = '+';
 | 
				
			||||||
	while (readOctets < numOctets) {
 | 
						while (readOctets < numOctets) {
 | 
				
			||||||
		unsigned d2 = src.readField(rp,4);
 | 
							unsigned d2 = src.readField(rp,4);
 | 
				
			||||||
		unsigned d1 = src.readField(rp,4);
 | 
							unsigned d1 = src.readField(rp,4);
 | 
				
			||||||
		readOctets++;
 | 
							readOctets++;
 | 
				
			||||||
		mDigits[i++]=d1+'0';
 | 
							mDigits[i++] = d1 == 10 ? '*' : d1 == 11 ? '#' : d1+'0';
 | 
				
			||||||
		if (d2!=0x0f) mDigits[i++]=d2+'0';
 | 
							if (d2!=0x0f) mDigits[i++] = d2 == 10 ? '*' : d2 == 11 ? '#' : d2+'0';
 | 
				
			||||||
		if (i>maxDigits) L3_READ_ERROR;
 | 
							if (i>maxDigits) L3_READ_ERROR;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	mDigits[i++]='\0';
 | 
						mDigits[i++]='\0';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int encode(char c)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return c == '*' ? 10 : c == '#' ? 11 : c-'0';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void L3BCDDigits::write(L3Frame& dest, size_t &wp) const
 | 
					void L3BCDDigits::write(L3Frame& dest, size_t &wp) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned index = 0;
 | 
						unsigned index = 0;
 | 
				
			||||||
	unsigned numDigits = strlen(mDigits);
 | 
						unsigned numDigits = strlen(mDigits);
 | 
				
			||||||
 | 
						if (index < numDigits && mDigits[index] == '+') {
 | 
				
			||||||
 | 
							index++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	while (index < numDigits) {
 | 
						while (index < numDigits) {
 | 
				
			||||||
		if ((index+1) < numDigits) dest.writeField(wp,mDigits[index+1]-'0',4);
 | 
							if ((index+1) < numDigits) dest.writeField(wp,encode(mDigits[index+1]),4);
 | 
				
			||||||
		else dest.writeField(wp,0x0f,4);
 | 
							else dest.writeField(wp,0x0f,4);
 | 
				
			||||||
		dest.writeField(wp,mDigits[index]-'0',4);
 | 
							dest.writeField(wp,encode(mDigits[index]),4);
 | 
				
			||||||
		index += 2;
 | 
							index += 2;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -103,6 +105,7 @@ void L3BCDDigits::write(L3Frame& dest, size_t &wp) const
 | 
				
			|||||||
size_t L3BCDDigits::lengthV() const 
 | 
					size_t L3BCDDigits::lengthV() const 
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned sz = strlen(mDigits);
 | 
						unsigned sz = strlen(mDigits);
 | 
				
			||||||
 | 
						if (*mDigits == '+') sz--;
 | 
				
			||||||
	return (sz/2) + (sz%2);
 | 
						return (sz/2) + (sz%2);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -131,7 +134,7 @@ void L3CalledPartyBCDNumber::parseV( const L3Frame &src, size_t &rp, size_t expe
 | 
				
			|||||||
	if (src.readField(rp, 1) != 1) L3_READ_ERROR;	
 | 
						if (src.readField(rp, 1) != 1) L3_READ_ERROR;	
 | 
				
			||||||
	mType = (TypeOfNumber)src.readField(rp, 3);
 | 
						mType = (TypeOfNumber)src.readField(rp, 3);
 | 
				
			||||||
	mPlan = (NumberingPlan)src.readField(rp, 4);
 | 
						mPlan = (NumberingPlan)src.readField(rp, 4);
 | 
				
			||||||
	mDigits.parse(src,rp,expectedLength-1);
 | 
						mDigits.parse(src,rp,expectedLength-1, mType == InternationalNumber);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -154,7 +157,7 @@ void L3CallingPartyBCDNumber::writeV( L3Frame &dest, size_t &wp ) const
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	// If Octet3a is extended, then write 0 else 1.
 | 
						// If Octet3a is extended, then write 0 else 1.
 | 
				
			||||||
	dest.writeField(wp, (!mHaveOctet3a & 0x01), 1);
 | 
						dest.writeField(wp, (!mHaveOctet3a & 0x01), 1);
 | 
				
			||||||
	dest.writeField(wp, mType, 3);
 | 
						dest.writeField(wp, *digits() == '+' ? InternationalNumber : mType, 3);
 | 
				
			||||||
	dest.writeField(wp, mPlan, 4);
 | 
						dest.writeField(wp, mPlan, 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if(mHaveOctet3a){
 | 
						if(mHaveOctet3a){
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,24 +2,14 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -71,7 +61,7 @@ class L3BCDDigits {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	L3BCDDigits(const char* wDigits) { strncpy(mDigits,wDigits,sizeof(mDigits)-1); mDigits[sizeof(mDigits)-1]='\0'; }
 | 
						L3BCDDigits(const char* wDigits) { strncpy(mDigits,wDigits,sizeof(mDigits)-1); mDigits[sizeof(mDigits)-1]='\0'; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void parse(const L3Frame& src, size_t &rp, size_t numOctets);
 | 
						void parse(const L3Frame& src, size_t &rp, size_t numOctets, bool international = false);
 | 
				
			||||||
	void write(L3Frame& dest, size_t &wp) const;
 | 
						void write(L3Frame& dest, size_t &wp) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Return number of octets needed to encode the digits. */
 | 
						/** Return number of octets needed to encode the digits. */
 | 
				
			||||||
@@ -200,6 +190,9 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// FIXME -- This should include any supplied diagnostics.
 | 
				
			||||||
 | 
						// See ticket GSM 04.08 10.5.4.11 and ticket #1139.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Location mLocation;
 | 
						Location mLocation;
 | 
				
			||||||
	unsigned mCause;
 | 
						unsigned mCause;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,27 +1,19 @@
 | 
				
			|||||||
/** @file Call Control messags, GSM 04.08 9.3.  */
 | 
					/** @file Call Control messags, GSM 04.08 9.3.  */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009, 2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2011 Range Networks, Inc.
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -57,8 +49,6 @@ ostream& GSM::operator<<(ostream& os, L3CCMessage::MessageType val)
 | 
				
			|||||||
				os << "Release Complete"; break;		  
 | 
									os << "Release Complete"; break;		  
 | 
				
			||||||
		case L3CCMessage::Setup: 
 | 
							case L3CCMessage::Setup: 
 | 
				
			||||||
				os << "Setup"; break;
 | 
									os << "Setup"; break;
 | 
				
			||||||
		case L3CCMessage::EmergencySetup:
 | 
					 | 
				
			||||||
				os << "Emergency Setup"; break;
 | 
					 | 
				
			||||||
		case L3CCMessage::CCStatus: 
 | 
							case L3CCMessage::CCStatus: 
 | 
				
			||||||
				os << "Status"; break;
 | 
									os << "Status"; break;
 | 
				
			||||||
		case L3CCMessage::CallConfirmed: 
 | 
							case L3CCMessage::CallConfirmed: 
 | 
				
			||||||
@@ -92,7 +82,6 @@ L3CCMessage * GSM::L3CCFactory(L3CCMessage::MessageType MTI)
 | 
				
			|||||||
		case L3CCMessage::Connect: return new L3Connect();
 | 
							case L3CCMessage::Connect: return new L3Connect();
 | 
				
			||||||
		case L3CCMessage::Alerting: return new L3Alerting();
 | 
							case L3CCMessage::Alerting: return new L3Alerting();
 | 
				
			||||||
		case L3CCMessage::Setup: return new L3Setup();
 | 
							case L3CCMessage::Setup: return new L3Setup();
 | 
				
			||||||
		case L3CCMessage::EmergencySetup: return new L3EmergencySetup();
 | 
					 | 
				
			||||||
		case L3CCMessage::Disconnect: return new L3Disconnect();
 | 
							case L3CCMessage::Disconnect: return new L3Disconnect();
 | 
				
			||||||
		case L3CCMessage::CallProceeding: return new L3CallProceeding();
 | 
							case L3CCMessage::CallProceeding: return new L3CallProceeding();
 | 
				
			||||||
		case L3CCMessage::Release: return new L3Release();
 | 
							case L3CCMessage::Release: return new L3Release();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,24 +4,16 @@
 | 
				
			|||||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2011 Range Networks, Inc.
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -65,7 +57,6 @@ class L3CCMessage : public L3Message {
 | 
				
			|||||||
		CallProceeding=0x02,
 | 
							CallProceeding=0x02,
 | 
				
			||||||
		Connect=0x07,
 | 
							Connect=0x07,
 | 
				
			||||||
		Setup=0x05,
 | 
							Setup=0x05,
 | 
				
			||||||
		EmergencySetup=0x0e,
 | 
					 | 
				
			||||||
		ConnectAcknowledge=0x0f,
 | 
							ConnectAcknowledge=0x0f,
 | 
				
			||||||
		Progress=0x03,
 | 
							Progress=0x03,
 | 
				
			||||||
		//@}
 | 
							//@}
 | 
				
			||||||
@@ -179,6 +170,7 @@ public:
 | 
				
			|||||||
		mCause(wCause), 
 | 
							mCause(wCause), 
 | 
				
			||||||
		mCallState(wCallState)
 | 
							mCallState(wCallState)
 | 
				
			||||||
	{}
 | 
						{}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	const L3Cause& cause() const { return mCause; }
 | 
						const L3Cause& cause() const { return mCause; }
 | 
				
			||||||
	const L3CallState callState() const { return mCallState; }
 | 
						const L3CallState callState() const { return mCallState; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -295,29 +287,6 @@ public:
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
	GSM 04.08 9.3.8
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
class L3EmergencySetup : public L3CCMessage
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// We fill in IEs one at a time as we need them.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	L3EmergencySetup(unsigned wTI=7)
 | 
					 | 
				
			||||||
		:L3CCMessage(wTI)
 | 
					 | 
				
			||||||
	{ }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	int MTI() const { return EmergencySetup; }
 | 
					 | 
				
			||||||
	void parseBody( const L3Frame &src, size_t &rp ) {}
 | 
					 | 
				
			||||||
	size_t l2BodyLength() const { return 0; }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** GSM 04.08 9.3.3 */
 | 
					/** GSM 04.08 9.3.3 */
 | 
				
			||||||
class L3CallProceeding : public L3CCMessage {
 | 
					class L3CallProceeding : public L3CCMessage {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,24 +6,16 @@
 | 
				
			|||||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -342,9 +334,9 @@ void L3MobileStationClassmark3::text(ostream& os) const
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	os << "multiband=" << mMultiband;
 | 
						os << "multiband=" << mMultiband;
 | 
				
			||||||
	os << " A5/4=" << mA5_4;
 | 
						os << " A5/4=" << mA5_4;
 | 
				
			||||||
	os << " A5/5=" << mA5_4;
 | 
						os << " A5/5=" << mA5_5;
 | 
				
			||||||
	os << " A5/6=" << mA5_4;
 | 
						os << " A5/6=" << mA5_6;
 | 
				
			||||||
	os << " A5/7=" << mA5_4;
 | 
						os << " A5/7=" << mA5_7;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,24 +5,16 @@
 | 
				
			|||||||
* Copyright 2008-2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008-2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										375
									
								
								GSM/GSML3GPRSElements.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										375
									
								
								GSM/GSML3GPRSElements.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,375 @@
 | 
				
			|||||||
 | 
					/**@file @brief L3 Radio Resource messages related to GPRS */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2011 Kestrel Signal Processing, Inc.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <typeinfo>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "GSML3RRMessages.h"
 | 
				
			||||||
 | 
					#include "../GPRS/GPRSExport.h"
 | 
				
			||||||
 | 
					#include <Logger.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GSM {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM 04.60 sec 12.24
 | 
				
			||||||
 | 
					void L3GPRSCellOptions::writeBits(L3Frame& dest, size_t &wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						GPRS::GPRSCellOptions_t& gco = GPRS::GPRSGetCellOptions();
 | 
				
			||||||
 | 
						dest.writeField(wp,gco.mNMO,2);
 | 
				
			||||||
 | 
						dest.writeField(wp,gco.mT3168Code,3);
 | 
				
			||||||
 | 
						dest.writeField(wp,gco.mT3192Code,3);
 | 
				
			||||||
 | 
						dest.writeField(wp,gco.mDRX_TIMER_MAX,3);
 | 
				
			||||||
 | 
						dest.writeField(wp,gco.mACCESS_BURST_TYPE,1);
 | 
				
			||||||
 | 
						dest.writeField(wp,gco.mCONTROL_ACK_TYPE,1);
 | 
				
			||||||
 | 
						dest.writeField(wp,gco.mBS_CV_MAX,4);
 | 
				
			||||||
 | 
						dest.writeField(wp,0,1);	// optional PAN_ fields omitted.
 | 
				
			||||||
 | 
						LOG(INFO)<< "beacon"<<LOGVAR2("NW_EXT_UTBF",gco.mNW_EXT_UTBF);
 | 
				
			||||||
 | 
						if (!gco.mNW_EXT_UTBF) {
 | 
				
			||||||
 | 
							dest.writeField(wp,0,1);	// optional extension information omitted
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							dest.writeField(wp,1,1);	// extension information included.
 | 
				
			||||||
 | 
							unsigned extlen = 6;		// 6 bits of extension information.
 | 
				
			||||||
 | 
							dest.writeField(wp,extlen,6);	// length of extension.
 | 
				
			||||||
 | 
							// R99 extensions:
 | 
				
			||||||
 | 
							dest.writeField(wp,0,1);	// No EGPRS.
 | 
				
			||||||
 | 
							dest.writeField(wp,0,1);	// No PFC_FEATURE_MODE
 | 
				
			||||||
 | 
							dest.writeField(wp,0,1);	// No DTM_SUPPORT
 | 
				
			||||||
 | 
							dest.writeField(wp,0,1);	// No BSS_PAGING_COORDINATION
 | 
				
			||||||
 | 
							// Rel-4 extensions:
 | 
				
			||||||
 | 
							// I tried setting CCN to 1 to get the MS to indicate GERAN feature pack I support.
 | 
				
			||||||
 | 
							// CCN is network assisted cell change and is also part of GERAN feature pack I.
 | 
				
			||||||
 | 
							dest.writeField(wp,0,1);	// CCN_ACTIVE. CCN described 44.060 5.5.1.1a
 | 
				
			||||||
 | 
							dest.writeField(wp,gco.mNW_EXT_UTBF,1);	// Finally.
 | 
				
			||||||
 | 
							// Rel-6 extensions:
 | 
				
			||||||
 | 
							// We dont want any of these, but here they are as documentation.
 | 
				
			||||||
 | 
							//dest.writeField(wp,0,1);	// No MULTIPLE_TBF_CAPABILITY
 | 
				
			||||||
 | 
							//dest.writeField(wp,0,1);	// No EXT_UTBF_NODATA
 | 
				
			||||||
 | 
							//dest.writeField(wp,0,1);	// No DTM_ENHANCEMENTS_CAPABILITY
 | 
				
			||||||
 | 
							//dest.writeField(wp,0,1);	// No MBMS procedures
 | 
				
			||||||
 | 
							// End of Rel extensions, we are allowed to truncate here.
 | 
				
			||||||
 | 
							dest.writeField(wp,0,1);	// Required spare bit - this is very confusing in the spec.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t L3GPRSCellOptions::lengthBits() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						GPRS::GPRSCellOptions_t& gco = GPRS::GPRSGetCellOptions();
 | 
				
			||||||
 | 
						size_t result = 2+3+3+3+1+1+4+1+1;
 | 
				
			||||||
 | 
						if (gco.mNW_EXT_UTBF) {
 | 
				
			||||||
 | 
							result += 6 + 6 + 1;	// 6 bit len + 6 bits of extension + 1 spare bit.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3GPRSCellOptions::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						GPRS::GPRSCellOptions_t& gco = GPRS::GPRSGetCellOptions();
 | 
				
			||||||
 | 
						os << "NMO=" << gco.mNMO;
 | 
				
			||||||
 | 
						os << " T3168Code=" << gco.mT3168Code;
 | 
				
			||||||
 | 
						os << " T3192Code=" << gco.mT3192Code;
 | 
				
			||||||
 | 
						os << " DRX_TIMER_MAX=" << gco.mDRX_TIMER_MAX;
 | 
				
			||||||
 | 
						os << " ACCESS_BURST_TYPE=" << gco.mACCESS_BURST_TYPE;
 | 
				
			||||||
 | 
						os << " CONTROL_ACK_TYPE=" << gco.mCONTROL_ACK_TYPE;
 | 
				
			||||||
 | 
						os << " BS_CV_MAX=" << gco.mBS_CV_MAX;
 | 
				
			||||||
 | 
						os << LOGVAR2("NW_EXT_UTBF",gco.mNW_EXT_UTBF);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *L3IAPacketAssignment::IAPacketAssignmentTypeText(enum IAPacketAssignmentType type) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
						case PacketUplinkAssignUninitialized: return "";	// No rest octets neeeded for this.
 | 
				
			||||||
 | 
						case PacketUplinkAssignFixed: return "Fixed Packet Uplink Assignment";
 | 
				
			||||||
 | 
						case PacketUplinkAssignDynamic: return "Dynamic Packet Uplink Assignment";
 | 
				
			||||||
 | 
						case PacketUplinkAssignSingleBlock: return "Single Block Packet Uplink Assignment";
 | 
				
			||||||
 | 
						case PacketDownlinkAssign: return "Packet Downlink Assignment";
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							LOG(ERR) << "unrecognized packet assignment type code " << (int)type;
 | 
				
			||||||
 | 
							return "??Unknown Assignment Type??";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3IAPacketAssignment::setPacketUplinkAssignSingleBlock(unsigned TBFStartingTime)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mPacketAssignmentType = PacketUplinkAssignSingleBlock;
 | 
				
			||||||
 | 
						mTBFStartingTimePresent = true;
 | 
				
			||||||
 | 
						mTBFStartingTime = TBFStartingTime;
 | 
				
			||||||
 | 
						mChannelCodingCommand = 0;	// use CS-1; redundant
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3IAPacketAssignment::setPacketUplinkAssignDynamic(unsigned TFI, unsigned CSNum, unsigned USF)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mPacketAssignmentType = PacketUplinkAssignDynamic;
 | 
				
			||||||
 | 
						mTFIPresent = true;
 | 
				
			||||||
 | 
						mTFIAssignment = TFI;
 | 
				
			||||||
 | 
						mChannelCodingCommand = CSNum;	// CS-1, etc.
 | 
				
			||||||
 | 
						mUSFGranularity = 0;		// redundant.
 | 
				
			||||||
 | 
						mUSF = USF;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3IAPacketAssignment::setPacketDownlinkAssign(
 | 
				
			||||||
 | 
						unsigned wTLLI, unsigned wTFI,unsigned wCSNum,
 | 
				
			||||||
 | 
						unsigned wRLCMode,unsigned wTAValid)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mPacketAssignmentType = PacketDownlinkAssign;
 | 
				
			||||||
 | 
						mTLLI = wTLLI;
 | 
				
			||||||
 | 
						mTFIPresent = true;
 | 
				
			||||||
 | 
						mTFIAssignment = wTFI;
 | 
				
			||||||
 | 
						mChannelCodingCommand = wCSNum;	// CS-1, etc.
 | 
				
			||||||
 | 
						mRLCMode = wRLCMode;
 | 
				
			||||||
 | 
						mTAValid = wTAValid; // We provided a valid TimingAdvance.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3IAPacketAssignment::setPacketPollTime(unsigned wTBFStartingTime)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mPolling = true;
 | 
				
			||||||
 | 
						mTBFStartingTimePresent = true;
 | 
				
			||||||
 | 
						mTBFStartingTime = wTBFStartingTime;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3IAPacketAssignment::setPacketUplinkAssignFixed()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mPacketAssignmentType = PacketUplinkAssignFixed;
 | 
				
			||||||
 | 
						// unimplemented
 | 
				
			||||||
 | 
						assert(0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// We broadcast alpha in the SI13 message, but not gamma.
 | 
				
			||||||
 | 
					// This gives us a chance to over-ride the alpha,gamma for an individual MS,
 | 
				
			||||||
 | 
					// or based on RSSI from the RACH.
 | 
				
			||||||
 | 
					// Dont know if we will use this, but here it is anyway.
 | 
				
			||||||
 | 
					// If this is not called, the default value of gamma == 0 means MS broadcasts at full power,
 | 
				
			||||||
 | 
					// moderated only by the alpha broadcast on SI13.
 | 
				
			||||||
 | 
					void L3IAPacketAssignment::setPacketPowerOptions(unsigned wAlpha, unsigned wGamma)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mAlpha = wAlpha; mGamma = wGamma; mAlphaPresent = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TBF Starting Time GSM 04.08 10.5.2.38
 | 
				
			||||||
 | 
					static void writeStartTime(MsgCommon &dest, unsigned startframe)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::ostream*os = dest.getStream();	// non-NULL for text() function.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The names T1, T2, T3 are defined in table 10.5.79
 | 
				
			||||||
 | 
						unsigned T1 = (startframe/1326)%32;
 | 
				
			||||||
 | 
						unsigned T3 = startframe%51;
 | 
				
			||||||
 | 
						unsigned T2 = startframe%26;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Recompute original startframe:
 | 
				
			||||||
 | 
						// Note that T3-T2 may be negative:
 | 
				
			||||||
 | 
						int recomputed = 51 * (((int)T3-(int)T2) % 26) + T3 + 51 * 26 * T1;
 | 
				
			||||||
 | 
						unsigned startframemod = (startframe % 42432);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If we are writing text(), output both the original startframe and 
 | 
				
			||||||
 | 
						// the computed T1,T2,T3.
 | 
				
			||||||
 | 
						if (os) {
 | 
				
			||||||
 | 
							// The recomputed time may be a much smaller number than startframe.
 | 
				
			||||||
 | 
							*os << " TBFStartFrame=" <<startframe << "=(" << "T=" <<recomputed;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Note: The fields are written here Most-Significant-Bit first in each byte,
 | 
				
			||||||
 | 
						// then the bytes are reversed in the encoder before being sent to the radio.
 | 
				
			||||||
 | 
						// 		 	7      6      5      4      3     2      1      0
 | 
				
			||||||
 | 
						//	  [			T1[4:0]                 ][   T3[5:3]        ]  Octet 1
 | 
				
			||||||
 | 
						//	  [       T3[2:0]     ][            T2[4:0]             ]  Octet 2
 | 
				
			||||||
 | 
						dest.writeField(T1,5,"T1p");
 | 
				
			||||||
 | 
						dest.writeField(T3,6,"T3");	// Yes T3 comes before T2.
 | 
				
			||||||
 | 
						dest.writeField(T2,5,"T2");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This just doesnt work, despite the documentation.
 | 
				
			||||||
 | 
						if (os && recomputed != (int)startframemod) {
 | 
				
			||||||
 | 
							*os << " TBF Start Time miscalculation: "
 | 
				
			||||||
 | 
								<<LOGVAR(startframemod) <<"!=" <<LOGVAR(recomputed);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (os) { *os << ")"; }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (pat) The uplink assignment is always initiated by the MS using a RACH,
 | 
				
			||||||
 | 
					// so the MS is identified by the request reference, and this message
 | 
				
			||||||
 | 
					// does not contain a TLLI.
 | 
				
			||||||
 | 
					void L3IAPacketAssignment::writePacketUplinkAssignment(MsgCommon &dest) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// The IA Rest Octets start with some bits to indicate a Packet Uplink Assignment:
 | 
				
			||||||
 | 
						// GSM04.08 sec 10.5.2.16
 | 
				
			||||||
 | 
						dest.writeH();
 | 
				
			||||||
 | 
						dest.writeH();
 | 
				
			||||||
 | 
						dest.write0();
 | 
				
			||||||
 | 
						dest.write0();
 | 
				
			||||||
 | 
						if (mPacketAssignmentType == PacketUplinkAssignFixed ||
 | 
				
			||||||
 | 
							mPacketAssignmentType == PacketUplinkAssignDynamic) {
 | 
				
			||||||
 | 
							dest.write1();
 | 
				
			||||||
 | 
							dest.WRITE_FIELD(mTFIAssignment, 5);
 | 
				
			||||||
 | 
							dest.WRITE_FIELD(mPolling, 1);
 | 
				
			||||||
 | 
							switch (mPacketAssignmentType) {
 | 
				
			||||||
 | 
							case PacketUplinkAssignDynamic:
 | 
				
			||||||
 | 
								dest.write0();
 | 
				
			||||||
 | 
								dest.WRITE_FIELD(mUSF, 3);
 | 
				
			||||||
 | 
								dest.WRITE_FIELD(mUSFGranularity, 1);
 | 
				
			||||||
 | 
								dest.write0(); 	// No downlink power parameters present.
 | 
				
			||||||
 | 
								// mPowerOption.writePower(dest,0);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case PacketUplinkAssignFixed:
 | 
				
			||||||
 | 
								dest.write1();
 | 
				
			||||||
 | 
								dest.WRITE_FIELD(mAllocationBitmapLength, 5);
 | 
				
			||||||
 | 
								if (mAllocationBitmapLength) {
 | 
				
			||||||
 | 
									dest.WRITE_FIELD(mAllocationBitmap, mAllocationBitmapLength);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								dest.write0();	// No downlink power parameters present.
 | 
				
			||||||
 | 
								// mPowerOption.writePower(dest,0);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default: assert(0);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							dest.WRITE_FIELD(mChannelCodingCommand, 2);
 | 
				
			||||||
 | 
							dest.WRITE_FIELD(mTLLIBlockChannelCoding, 1);
 | 
				
			||||||
 | 
							if (dest.write01(mAlphaPresent)) { dest.WRITE_FIELD(mAlpha,4); }
 | 
				
			||||||
 | 
							dest.WRITE_FIELD(mGamma,5);
 | 
				
			||||||
 | 
							if (dest.write01(mTimingAdvanceIndexPresent)) {
 | 
				
			||||||
 | 
								dest.WRITE_FIELD(mTimingAdvanceIndex,4);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (dest.write01(mTBFStartingTimePresent)) {
 | 
				
			||||||
 | 
								writeStartTime(dest,mTBFStartingTime);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {	// single block assignment.
 | 
				
			||||||
 | 
							dest.write0();	// uplink assignment type designator
 | 
				
			||||||
 | 
							if (dest.write01(mAlphaPresent)) { dest.WRITE_FIELD(mAlpha,4); }
 | 
				
			||||||
 | 
							dest.WRITE_FIELD(mGamma,5);
 | 
				
			||||||
 | 
							dest.write0(); dest.write1();	// As per 10.5.2.16 Note 1.
 | 
				
			||||||
 | 
							assert(mTBFStartingTimePresent);	// required for single block uplink assignment.
 | 
				
			||||||
 | 
							writeStartTime(dest,mTBFStartingTime);
 | 
				
			||||||
 | 
							dest.writeL();	// No downlink power parameters present.
 | 
				
			||||||
 | 
							//mPowerOption.writePower(dest,1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (pat) The downlink assignment may be (normally is) initiated by the network,
 | 
				
			||||||
 | 
					// in which case the "request reference" in the Immediate Assignment message
 | 
				
			||||||
 | 
					// is set to an impossible value, and the MS is identified by the TLLI.
 | 
				
			||||||
 | 
					void L3IAPacketAssignment::writePacketDownlinkAssignment(MsgCommon &dest) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// The IA Rest Octets start with some bits to indicate a Packet Downlink Assignment:
 | 
				
			||||||
 | 
						// GSM04.08 sec 10.5.2.16
 | 
				
			||||||
 | 
						dest.writeH();
 | 
				
			||||||
 | 
						dest.writeH();
 | 
				
			||||||
 | 
						dest.write0();
 | 
				
			||||||
 | 
						dest.write1();
 | 
				
			||||||
 | 
						dest.writeField(mTLLI,32,"TLLI",tohex);
 | 
				
			||||||
 | 
						if (dest.write01(mTFIPresent)) {
 | 
				
			||||||
 | 
							dest.WRITE_FIELD(mTFIAssignment,5);
 | 
				
			||||||
 | 
							dest.WRITE_FIELD(mRLCMode,1);
 | 
				
			||||||
 | 
							dest.WRITE_OPT_FIELD01(mAlpha,4,mAlphaPresent);
 | 
				
			||||||
 | 
							dest.WRITE_FIELD(mGamma,5);
 | 
				
			||||||
 | 
							dest.WRITE_FIELD(mPolling, 1);
 | 
				
			||||||
 | 
							dest.WRITE_FIELD(mTAValid, 1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						dest.WRITE_OPT_FIELD01(mTimingAdvanceIndex,4,mTimingAdvanceIndexPresent);
 | 
				
			||||||
 | 
						if (dest.write01(mTBFStartingTimePresent)) {
 | 
				
			||||||
 | 
							writeStartTime(dest,mTBFStartingTime);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						dest.write0();	// No downlink power parameters present.
 | 
				
			||||||
 | 
						dest.writeL();	// No Egprs.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3IAPacketAssignment::writeIAPacketAssignment(MsgCommon &dest) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// (pat) These messages are the rest octets in an Immediate Assignment Message.
 | 
				
			||||||
 | 
						switch (mPacketAssignmentType) {
 | 
				
			||||||
 | 
						case PacketUplinkAssignUninitialized:
 | 
				
			||||||
 | 
							return;	// No rest octets neeeded for this.
 | 
				
			||||||
 | 
						case PacketUplinkAssignFixed:
 | 
				
			||||||
 | 
						case PacketUplinkAssignDynamic:
 | 
				
			||||||
 | 
						case PacketUplinkAssignSingleBlock:
 | 
				
			||||||
 | 
							return writePacketUplinkAssignment(dest);
 | 
				
			||||||
 | 
						case PacketDownlinkAssign:
 | 
				
			||||||
 | 
							return writePacketDownlinkAssignment(dest);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3IAPacketAssignment::writeBits(L3Frame &frame, size_t &wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						MsgCommonWrite tmp(frame,wp);
 | 
				
			||||||
 | 
						writeIAPacketAssignment(tmp);
 | 
				
			||||||
 | 
						wp = tmp.wp;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return the length of this puppy.
 | 
				
			||||||
 | 
					size_t L3IAPacketAssignment::lengthBits() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						MsgCommonLength dest;
 | 
				
			||||||
 | 
						writeIAPacketAssignment(dest);
 | 
				
			||||||
 | 
						return dest.wp;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Print a human readable version of this puppy.
 | 
				
			||||||
 | 
					void L3IAPacketAssignment::text(std::ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (mPacketAssignmentType != PacketUplinkAssignUninitialized) {
 | 
				
			||||||
 | 
							os << " " << IAPacketAssignmentTypeText(mPacketAssignmentType);
 | 
				
			||||||
 | 
							MsgCommonText dest(os);
 | 
				
			||||||
 | 
							writeIAPacketAssignment(dest);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0	// Currently unused
 | 
				
			||||||
 | 
					void L3GPRSPowerControlParameters::writeBits(L3Frame& dest, size_t &wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						dest.writeField(wp,mAlpha,4);
 | 
				
			||||||
 | 
						// We dont use any of these gamma fields at the moment:
 | 
				
			||||||
 | 
						dest.writeField(wp,0,1);	// GAMMA_TM0
 | 
				
			||||||
 | 
						dest.writeField(wp,0,1);	// GAMMA_TM1
 | 
				
			||||||
 | 
						dest.writeField(wp,0,1);	// GAMMA_TM2
 | 
				
			||||||
 | 
						dest.writeField(wp,0,1);	// GAMMA_TM3
 | 
				
			||||||
 | 
						dest.writeField(wp,0,1);	// GAMMA_TM4
 | 
				
			||||||
 | 
						dest.writeField(wp,0,1);	// GAMMA_TM5
 | 
				
			||||||
 | 
						dest.writeField(wp,0,1);	// GAMMA_TM6
 | 
				
			||||||
 | 
						dest.writeField(wp,0,1);	// GAMMA_TM7
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3GPRSPowerControlParameters::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << "Alpha=" << mAlpha;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					L3GPRSSI13PowerControlParameters::L3GPRSSI13PowerControlParameters()
 | 
				
			||||||
 | 
						: mAlpha(GPRS::GetPowerAlpha()),
 | 
				
			||||||
 | 
						mTAvgW(gConfig.getNum("GPRS.MS.Power.T_AVG_W")),
 | 
				
			||||||
 | 
						mTAvgT(gConfig.getNum("GPRS.MS.Power.T_AVG_T")),
 | 
				
			||||||
 | 
						mPCMeasChan(0),
 | 
				
			||||||
 | 
						mNAvgI(15)	// We dont use this so dont bother putting in sql.
 | 
				
			||||||
 | 
					{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3GPRSSI13PowerControlParameters::writeBits(L3Frame& dest, size_t &wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// TODO: use WRITE_ITEM from MsgBase.h 
 | 
				
			||||||
 | 
						dest.writeField(wp,mAlpha,4);
 | 
				
			||||||
 | 
						dest.writeField(wp,mTAvgW,5);
 | 
				
			||||||
 | 
						dest.writeField(wp,mTAvgT,5);
 | 
				
			||||||
 | 
						dest.writeField(wp,mPCMeasChan,1);
 | 
				
			||||||
 | 
						dest.writeField(wp,mNAvgI,4);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3GPRSSI13PowerControlParameters::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << "(" <<LOGVAR(mAlpha) <<LOGVAR(mTAvgW) <<LOGVAR(mTAvgT)
 | 
				
			||||||
 | 
							<<LOGVAR(mPCMeasChan) <<LOGVAR(mNAvgI) << ")";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										180
									
								
								GSM/GSML3GPRSElements.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								GSM/GSML3GPRSElements.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,180 @@
 | 
				
			|||||||
 | 
					/**@file @brief L3 Radio Resource messages related to GPRS */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2011 Kestrel Signal Processing, Inc.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GSML3GPRSELEMENTS_H
 | 
				
			||||||
 | 
					#define GSML3GPRSELEMENTS_H
 | 
				
			||||||
 | 
					#include "GSML3Message.h"
 | 
				
			||||||
 | 
					#include "../GPRS/MsgBase.h"
 | 
				
			||||||
 | 
					#include "../GPRS/GPRSExport.h"
 | 
				
			||||||
 | 
					#include "ScalarTypes.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GSM {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Defined in GSM 04.60 12.24 but used in GSM 04.08 10.5.2.37b - SI13 Rest Octets. */
 | 
				
			||||||
 | 
					class L3GPRSCellOptions : public GenericMessageElement
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3GPRSCellOptions() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t lengthBits() const;
 | 
				
			||||||
 | 
						void writeBits(L3Frame& dest, size_t &wp) const;
 | 
				
			||||||
 | 
						void text(std::ostream& os) const;
 | 
				
			||||||
 | 
						//void parseV( const L3Frame&, size_t&, size_t) { abort(); }
 | 
				
			||||||
 | 
						//void parseV(const L3Frame&, size_t&) { abort(); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM 04.08 10.5.2.16
 | 
				
			||||||
 | 
					// (pat) This message is sent to MS inside the rest octets of an Immediate Assignment message
 | 
				
			||||||
 | 
					// on CCCH in response to a CHANNEL REQUEST message sent by the MS on RACH.
 | 
				
			||||||
 | 
					// The Packet Uplink and Packet Downlink assignment messages are so similar
 | 
				
			||||||
 | 
					// they are combined in one structure.
 | 
				
			||||||
 | 
					// Note that there are also a Packet Uplink/Downlink Assignment messages (GSM04.60) that do
 | 
				
			||||||
 | 
					// the same thing as these messages, but with completely different format,
 | 
				
			||||||
 | 
					// and sent on PACCH not CCCH.
 | 
				
			||||||
 | 
					// TODO: Padding to end of message should be as for RR messages, not PDCH messages.
 | 
				
			||||||
 | 
					// This should be done by whomever sends this message.
 | 
				
			||||||
 | 
					struct L3IAPacketAssignment : GenericMessageElement
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
							// (note: the MS may choose to send a Packet Uplink Request instead.
 | 
				
			||||||
 | 
						// There are three types of packet uplink assignment, and one type of downlink assignment:
 | 
				
			||||||
 | 
						enum IAPacketAssignmentType {
 | 
				
			||||||
 | 
							PacketUplinkAssignUninitialized,
 | 
				
			||||||
 | 
							PacketUplinkAssignFixed,
 | 
				
			||||||
 | 
							PacketUplinkAssignDynamic,
 | 
				
			||||||
 | 
							PacketUplinkAssignSingleBlock,
 | 
				
			||||||
 | 
							PacketDownlinkAssign
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						const char *IAPacketAssignmentTypeText(enum IAPacketAssignmentType type) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enum IAPacketAssignmentType mPacketAssignmentType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Bool_z mTFIPresent;
 | 
				
			||||||
 | 
						Field_z<5> mTFIAssignment;
 | 
				
			||||||
 | 
						Field_z<1> mPolling;	// Set if MS is being polled for Packet Control Acknowledgement.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This part for Uplink Dynamic Allocation Mode [for packet uplink transfer]:
 | 
				
			||||||
 | 
						Field_z<3> mUSF;
 | 
				
			||||||
 | 
						Field_z<1> mUSFGranularity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This part for Uplink Fixed Allocation Mode [for packet uplink transfer]:
 | 
				
			||||||
 | 
						Field_z<5> mAllocationBitmapLength;
 | 
				
			||||||
 | 
						Field_z<32> mAllocationBitmap;		// variable sized, up to 32 bits
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// alpha, gamma for MS power control.  See GSM05.08
 | 
				
			||||||
 | 
						Field_z<4> mAlpha;		Bool_z mAlphaPresent;		// optional param
 | 
				
			||||||
 | 
						Field_z<5> mGamma;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Field_z<4> mTimingAdvanceIndex;  Bool_z mTimingAdvanceIndexPresent;	// optional param
 | 
				
			||||||
 | 
						// From GSM 04.08 10.5.2.16, and I quote:
 | 
				
			||||||
 | 
						// The TBF starting time is coded using the same coding as the V format
 | 
				
			||||||
 | 
						// of the type 3 information element Starting Time (10.5.2.38).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Field_z<16> mTBFStartingTime;    Bool_z mTBFStartingTimePresent;	// optional param
 | 
				
			||||||
 | 
						Field_z<2> mChannelCodingCommand;	// CS-1, CS-2, CS-3 or CS-4.
 | 
				
			||||||
 | 
						Field_z<1> mTLLIBlockChannelCoding;
 | 
				
			||||||
 | 
						// (pat) We wont use the downlink power control parameters (P0, etc), so dont even bother.
 | 
				
			||||||
 | 
						// L3AssignmentPowerOption mPowerOption;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// The following variables used only for Packet Downlink Assignment
 | 
				
			||||||
 | 
						Field_z<32> mTLLI;
 | 
				
			||||||
 | 
						Field_z<1> mRLCMode;
 | 
				
			||||||
 | 
						Field_z<1> mTAValid;	// Is the timingadvance in the main Immediate Assignment Message valid?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void setPacketUplinkAssignSingleBlock(unsigned TBFStartingTime);
 | 
				
			||||||
 | 
						void setPacketUplinkAssignDynamic(unsigned TFI, unsigned CSNum, unsigned USF);
 | 
				
			||||||
 | 
						void setPacketDownlinkAssign(
 | 
				
			||||||
 | 
							unsigned wTLLI, unsigned wTFI,unsigned wCSNum, unsigned wRLCMode,unsigned wTAValid);
 | 
				
			||||||
 | 
						void setPacketUplinkAssignFixed();
 | 
				
			||||||
 | 
						void setPacketPowerOptions(unsigned wAlpha, unsigned wGamma);
 | 
				
			||||||
 | 
						void setPacketPollTime(unsigned TBFStartingTime);
 | 
				
			||||||
 | 
						void writePacketUplinkAssignment(MsgCommon &dest) const;
 | 
				
			||||||
 | 
						void writePacketDownlinkAssignment(MsgCommon &dest) const; 
 | 
				
			||||||
 | 
						void writeIAPacketAssignment(MsgCommon &dest) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeBits(L3Frame &dest, size_t &wp) const;
 | 
				
			||||||
 | 
						size_t lengthBits() const;
 | 
				
			||||||
 | 
						void text(std::ostream& os) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3IAPacketAssignment() { mPacketAssignmentType = PacketUplinkAssignUninitialized; /*redundant*/ }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0 // This is currently unused, so lets indicate so.
 | 
				
			||||||
 | 
					/** GSM 04.60 12.13 */
 | 
				
			||||||
 | 
					// NOTE: These are the power control parameters for assignment in GSM 4.60,
 | 
				
			||||||
 | 
					// not the power control parameters for the SI13 rest octets
 | 
				
			||||||
 | 
					// This is NOT a L3ProtocolElement; it is not in TLV format or byte aligned.
 | 
				
			||||||
 | 
					class L3GPRSPowerControlParameters : public GenericMessageElement
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned mAlpha;	///< GSM 04.60 Table 12.9.2
 | 
				
			||||||
 | 
						// GSM04.60 12.13
 | 
				
			||||||
 | 
						// (pat) There are 8 gamma values, one for each channel.
 | 
				
			||||||
 | 
						// sec 12.13 says the presence/absense of gamma may be used to denote
 | 
				
			||||||
 | 
						// timeslot for "an uplink TBF", presumably in the absense of a TIMESLOT ALLOCATION IE,
 | 
				
			||||||
 | 
						// but I dont see which uplink TBF assignment would use that and the spec does not say.
 | 
				
			||||||
 | 
						// I dont see why you need gamma in the SI13 message at all; Gamma is assigned
 | 
				
			||||||
 | 
						// in the uplink/downlink assignment messages as a non-optional element.
 | 
				
			||||||
 | 
						// I am going to leave them out entirely for now.
 | 
				
			||||||
 | 
						// unsigned mGamma[8];
 | 
				
			||||||
 | 
						// bool mGammaPresent[8];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Init alpha to the defalt value.
 | 
				
			||||||
 | 
						L3GPRSPowerControlParameters() : mAlpha(GPRS::GetPowerAlpha()) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t lengthBits() const { return 4+8; }
 | 
				
			||||||
 | 
						void writeBits(L3Frame& dest, size_t &wp) const;
 | 
				
			||||||
 | 
						void text(std::ostream& os) const;
 | 
				
			||||||
 | 
						//void parseV( const L3Frame&, size_t&, size_t) { abort(); }
 | 
				
			||||||
 | 
						//void parseV(const L3Frame&, size_t&) { abort(); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 10.5.2.37b Power Control Parameters for SI13 Rest Octets */
 | 
				
			||||||
 | 
					// Info has moved to 44.060 12.13.
 | 
				
			||||||
 | 
					// NOTE: This is not the same as the Global Power Control Parameters
 | 
				
			||||||
 | 
					// in GSM 44.060 12.9a, which include a Pb element.
 | 
				
			||||||
 | 
					class L3GPRSSI13PowerControlParameters : public GenericMessageElement
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// See GSM 5.08 10.2.1
 | 
				
			||||||
 | 
						// (pat) The MS can regulate its own output power based on measurements it makes.
 | 
				
			||||||
 | 
						// See comments at GetPowerAlpha().  The alpha below is used for initial
 | 
				
			||||||
 | 
						// communication and may be over-ridden later when the MS starts talking to us.
 | 
				
			||||||
 | 
						// The other parameters are "forgetting factors" determining the window period for
 | 
				
			||||||
 | 
						// the MS measurements.  I dont think the values (other than alpha itself)
 | 
				
			||||||
 | 
						// are critical because they are clamped to sane values in the formulas in GSM05.08.
 | 
				
			||||||
 | 
						unsigned mAlpha;	///< Range 0..10 See GSM 04.60 Table 12.9.2
 | 
				
			||||||
 | 
						unsigned mTAvgW;	// The MS measurement 'forgetting factor' in Packet Idle Mode.
 | 
				
			||||||
 | 
						unsigned mTAvgT; 	// The MS measurement 'forgetting factor' in Packet Transfer Mode.
 | 
				
			||||||
 | 
						unsigned mPCMeasChan;		// Which channel Ms monitors: 0 => use BCCH, 1 => PDCH1
 | 
				
			||||||
 | 
						unsigned mNAvgI;	// 'Forgetting factor' for MS reporting to BTS.
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						L3GPRSSI13PowerControlParameters();
 | 
				
			||||||
 | 
						size_t lengthBits() const { return 4+5+5+1+4; }
 | 
				
			||||||
 | 
						void writeBits(L3Frame& dest, size_t &wp) const;
 | 
				
			||||||
 | 
						void text(std::ostream& os) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}; // namespace
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@@ -2,27 +2,19 @@
 | 
				
			|||||||
	@brief Elements for Mobility Management messages, GSM 04.08 9.2.
 | 
						@brief Elements for Mobility Management messages, GSM 04.08 9.2.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -48,7 +40,6 @@ ostream& GSM::operator<<(ostream& os, L3CMServiceType::TypeCode code)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	switch (code) {
 | 
						switch (code) {
 | 
				
			||||||
		case L3CMServiceType::MobileOriginatedCall: os << "MOC"; break;
 | 
							case L3CMServiceType::MobileOriginatedCall: os << "MOC"; break;
 | 
				
			||||||
		case L3CMServiceType::EmergencyCall: os << "Emergency"; break;
 | 
					 | 
				
			||||||
		case L3CMServiceType::ShortMessage: os << "SMS"; break;
 | 
							case L3CMServiceType::ShortMessage: os << "SMS"; break;
 | 
				
			||||||
		case L3CMServiceType::SupplementaryService: os << "SS"; break;
 | 
							case L3CMServiceType::SupplementaryService: os << "SS"; break;
 | 
				
			||||||
		case L3CMServiceType::VoiceCallGroup: os << "VGCS"; break;
 | 
							case L3CMServiceType::VoiceCallGroup: os << "VGCS"; break;
 | 
				
			||||||
@@ -57,6 +48,7 @@ ostream& GSM::operator<<(ostream& os, L3CMServiceType::TypeCode code)
 | 
				
			|||||||
		case L3CMServiceType::MobileTerminatedCall: os << "MTC"; break;
 | 
							case L3CMServiceType::MobileTerminatedCall: os << "MTC"; break;
 | 
				
			||||||
		case L3CMServiceType::MobileTerminatedShortMessage: os << "MTSMS"; break;
 | 
							case L3CMServiceType::MobileTerminatedShortMessage: os << "MTSMS"; break;
 | 
				
			||||||
		case L3CMServiceType::TestCall: os << "Test"; break;
 | 
							case L3CMServiceType::TestCall: os << "Test"; break;
 | 
				
			||||||
 | 
							case L3CMServiceType::FuzzCall: os << "Fuzz"; break;
 | 
				
			||||||
		default: os << "?" << (int)code << "?";
 | 
							default: os << "?" << (int)code << "?";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return os;
 | 
						return os;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,24 +4,16 @@
 | 
				
			|||||||
* Copyright 2008-2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008-2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -43,7 +35,6 @@ class L3CMServiceType : public L3ProtocolElement {
 | 
				
			|||||||
	enum TypeCode {
 | 
						enum TypeCode {
 | 
				
			||||||
		UndefinedType=0,
 | 
							UndefinedType=0,
 | 
				
			||||||
		MobileOriginatedCall=1,
 | 
							MobileOriginatedCall=1,
 | 
				
			||||||
		EmergencyCall=2,
 | 
					 | 
				
			||||||
		ShortMessage=4,							///< specifically, MO-SMS
 | 
							ShortMessage=4,							///< specifically, MO-SMS
 | 
				
			||||||
		SupplementaryService=8,
 | 
							SupplementaryService=8,
 | 
				
			||||||
		VoiceCallGroup=9,
 | 
							VoiceCallGroup=9,
 | 
				
			||||||
@@ -52,6 +43,8 @@ class L3CMServiceType : public L3ProtocolElement {
 | 
				
			|||||||
		MobileTerminatedCall=100,				///< non-standard code
 | 
							MobileTerminatedCall=100,				///< non-standard code
 | 
				
			||||||
		MobileTerminatedShortMessage=101,		///< non-standard code
 | 
							MobileTerminatedShortMessage=101,		///< non-standard code
 | 
				
			||||||
		TestCall=102,			///< non-standard code
 | 
							TestCall=102,			///< non-standard code
 | 
				
			||||||
 | 
							HandoverCall=103,		///< non-standard code
 | 
				
			||||||
 | 
							FuzzCall=104,			///< non-standard code
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
	private:
 | 
						private:
 | 
				
			||||||
@@ -126,7 +119,7 @@ public:
 | 
				
			|||||||
	/** Set the network name, taking the default from gConfig. */
 | 
						/** Set the network name, taking the default from gConfig. */
 | 
				
			||||||
	L3NetworkName(const char* wName,
 | 
						L3NetworkName(const char* wName,
 | 
				
			||||||
	              GSMAlphabet alphabet=ALPHABET_7BIT,
 | 
						              GSMAlphabet alphabet=ALPHABET_7BIT,
 | 
				
			||||||
	              int wCI=gConfig.defines("GSM.ShowCountry"))
 | 
						              int wCI=gConfig.getBool("GSM.ShowCountry"))
 | 
				
			||||||
		:L3ProtocolElement(), mAlphabet(alphabet), mCI(wCI)
 | 
							:L3ProtocolElement(), mAlphabet(alphabet), mCI(wCI)
 | 
				
			||||||
	{ strncpy(mName,wName,maxLen); mName[maxLen] = '\0'; }
 | 
						{ strncpy(mName,wName,maxLen); mName[maxLen] = '\0'; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,24 +6,16 @@
 | 
				
			|||||||
* Copyright 2008-2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008-2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -124,7 +116,8 @@ void L3MMMessage::text(ostream& os) const
 | 
				
			|||||||
void L3LocationUpdatingRequest::parseBody( const  L3Frame &src, size_t &rp )
 | 
					void L3LocationUpdatingRequest::parseBody( const  L3Frame &src, size_t &rp )
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
		// skip updating type
 | 
							// skip updating type
 | 
				
			||||||
		rp += 4;
 | 
							// (pat) Save this for debugging purposes.
 | 
				
			||||||
 | 
							mUpdateType = src.readField(rp,4);
 | 
				
			||||||
		// skip ciphering ket sequence number
 | 
							// skip ciphering ket sequence number
 | 
				
			||||||
		rp += 4;
 | 
							rp += 4;
 | 
				
			||||||
		mLAI.parseV(src,rp);
 | 
							mLAI.parseV(src,rp);
 | 
				
			||||||
@@ -136,6 +129,7 @@ void L3LocationUpdatingRequest::parseBody( const  L3Frame &src, size_t &rp )
 | 
				
			|||||||
void L3LocationUpdatingRequest::text(ostream& os) const
 | 
					void L3LocationUpdatingRequest::text(ostream& os) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	L3MMMessage::text(os);
 | 
						L3MMMessage::text(os);
 | 
				
			||||||
 | 
						os << "UpdateType=("<<mUpdateType<<")";
 | 
				
			||||||
	os << " LAI=("<<mLAI<<")";
 | 
						os << " LAI=("<<mLAI<<")";
 | 
				
			||||||
	os << " MobileIdentity=("<<mMobileIdentity<<")";
 | 
						os << " MobileIdentity=("<<mMobileIdentity<<")";
 | 
				
			||||||
	os << " classmark=(" << mClassmark << ")";
 | 
						os << " classmark=(" << mClassmark << ")";
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,24 +3,16 @@
 | 
				
			|||||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
					* Copyright 2008 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -110,6 +102,7 @@ L3MMMessage* parseL3MM(const L3Frame& source);
 | 
				
			|||||||
/** GSM 04.08 9.2.15 */
 | 
					/** GSM 04.08 9.2.15 */
 | 
				
			||||||
class L3LocationUpdatingRequest : public L3MMMessage
 | 
					class L3LocationUpdatingRequest : public L3MMMessage
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						unsigned mUpdateType;	// (pat) Added for debugging.
 | 
				
			||||||
	L3MobileStationClassmark1 mClassmark;
 | 
						L3MobileStationClassmark1 mClassmark;
 | 
				
			||||||
	L3MobileIdentity mMobileIdentity; // (LV) 1+len
 | 
						L3MobileIdentity mMobileIdentity; // (LV) 1+len
 | 
				
			||||||
	L3LocationAreaIdentity mLAI;
 | 
						L3LocationAreaIdentity mLAI;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,25 +1,14 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -52,6 +41,7 @@ void L3Message::parse(const L3Frame& source)
 | 
				
			|||||||
void L3Message::write(L3Frame& dest) const
 | 
					void L3Message::write(L3Frame& dest) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	size_t l3len = bitsNeeded();
 | 
						size_t l3len = bitsNeeded();
 | 
				
			||||||
 | 
						//printf("bitsneeded=%d\n",l3len);
 | 
				
			||||||
	if (dest.size()!=l3len) dest.resize(l3len);
 | 
						if (dest.size()!=l3len) dest.resize(l3len);
 | 
				
			||||||
	size_t wp = 0;
 | 
						size_t wp = 0;
 | 
				
			||||||
	// write the standard L3 header
 | 
						// write the standard L3 header
 | 
				
			||||||
@@ -133,10 +123,6 @@ ostream& GSM::operator<<(ostream& os, const L3Message& msg)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
GSM::L3Message* GSM::parseL3(const GSM::L3Frame& source)
 | 
					GSM::L3Message* GSM::parseL3(const GSM::L3Frame& source)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (source.size()==0) return NULL;
 | 
						if (source.size()==0) return NULL;
 | 
				
			||||||
@@ -258,6 +244,12 @@ ostream& GSM::operator<<(ostream& os, const L3ProtocolElement& elem)
 | 
				
			|||||||
	return os;
 | 
						return os;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ostream& GSM::operator<<(ostream& os, const GenericMessageElement& msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						msg.text(os);
 | 
				
			||||||
 | 
						return os;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,24 +2,14 @@
 | 
				
			|||||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -69,13 +59,14 @@ class L3Message {
 | 
				
			|||||||
	/**
 | 
						/**
 | 
				
			||||||
		Body length not including header but including rest octets.
 | 
							Body length not including header but including rest octets.
 | 
				
			||||||
		In subclasses with no rest octets, this returns l2BodyLength.
 | 
							In subclasses with no rest octets, this returns l2BodyLength.
 | 
				
			||||||
 | 
							(pat) in BYTES!!!
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	virtual size_t fullBodyLength() const =0;
 | 
						virtual size_t fullBodyLength() const =0;
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	/** Return the expected message length in bytes, including L3 header, but not including rest octets.  */
 | 
						/** Return the expected message length in bytes, including L3 header, but not including rest octets.  */
 | 
				
			||||||
	size_t L2Length() const { return l2BodyLength()+2; }
 | 
						size_t L2Length() const { return l2BodyLength()+2; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Length including header and rest octets. */
 | 
						/** Length ((pat) in BYTES!!) including header and rest octets. */
 | 
				
			||||||
	size_t FullLength() const { return fullBodyLength()+2; }
 | 
						size_t FullLength() const { return fullBodyLength()+2; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Return number of BITS needed to hold message and header.  */
 | 
						/** Return number of BITS needed to hold message and header.  */
 | 
				
			||||||
@@ -303,6 +294,20 @@ class L3ProtocolElement {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
std::ostream& operator<<(std::ostream& os, const L3ProtocolElement& elem);
 | 
					std::ostream& operator<<(std::ostream& os, const L3ProtocolElement& elem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Pat added:  A Non-Aligned Message Element that is not an L3ProtocolElement because
 | 
				
			||||||
 | 
					// it is not in TLV format, and is not byte or half-byte aligned,
 | 
				
			||||||
 | 
					// but is rather just a stream of bits, often used in the Message RestOctets.
 | 
				
			||||||
 | 
					class GenericMessageElement {
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						// We dont use these virtual functions except for text().
 | 
				
			||||||
 | 
						// They are basically here as documentation.
 | 
				
			||||||
 | 
						virtual size_t lengthBits() const = 0;
 | 
				
			||||||
 | 
						virtual void writeBits(L3Frame& dest, size_t &wp) const = 0;
 | 
				
			||||||
 | 
						virtual void text(std::ostream& os) const = 0;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::ostream& operator<<(std::ostream& os, const GenericMessageElement& elem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}; // GSM
 | 
					}; // GSM
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,35 +1,25 @@
 | 
				
			|||||||
/**@file
 | 
					/**@file @brief Radio Resource messages, GSM 04.08 9.1.  */
 | 
				
			||||||
	@brief Radio Resource messages, GSM 04.08 9.1.
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010, 2013 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <iterator> // for L3APDUData::text
 | 
					#include <iterator> // for L3APDUData::text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "GSML3RRElements.h"
 | 
					#include "GSML3RRElements.h"
 | 
				
			||||||
 | 
					#include "Defines.h"
 | 
				
			||||||
 | 
					#include "GSMConfig.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <Logger.h>
 | 
					#include <Logger.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -105,6 +95,20 @@ void L3CellSelectionParameters::text(ostream& os) const
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned L3ControlChannelDescription::getBS_PA_MFRMS()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned bs_pa_mfrms = mBS_PA_MFRMS + 2;
 | 
				
			||||||
 | 
						if (bs_pa_mfrms != RN_BOUND(bs_pa_mfrms,2,sMax_BS_PA_MFRMS)) {
 | 
				
			||||||
 | 
							static bool printed_msg = false;
 | 
				
			||||||
 | 
							if (!printed_msg) {
 | 
				
			||||||
 | 
								LOG(ERR) << "Invalid BS_PA_MFRMS value, must be 2.."<<sMax_BS_PA_MFRMS;
 | 
				
			||||||
 | 
								printed_msg = true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							bs_pa_mfrms = 2;	// If invalid, it is ok as long as we use the same value all the time.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return bs_pa_mfrms;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void L3ControlChannelDescription::writeV(L3Frame& dest, size_t &wp) const
 | 
					void L3ControlChannelDescription::writeV(L3Frame& dest, size_t &wp) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -180,7 +184,7 @@ void L3FrequencyList::writeV(L3Frame& dest, size_t &wp) const
 | 
				
			|||||||
	// bit map
 | 
						// bit map
 | 
				
			||||||
	unsigned delta = spread();
 | 
						unsigned delta = spread();
 | 
				
			||||||
	unsigned numBits = 8*lengthV() - 17;
 | 
						unsigned numBits = 8*lengthV() - 17;
 | 
				
			||||||
	if (numBits<delta) { LOG(ALERT) << "L3FrequencyList cannot encode full ARFCN set"; }
 | 
						if (numBits<delta) { LOG(ALERT) << "L3FrequencyList cannot encode full ARFCN set, base=" << baseARFCN << " delta=" << delta; }
 | 
				
			||||||
	for (unsigned i=0; i<numBits; i++) {
 | 
						for (unsigned i=0; i<numBits; i++) {
 | 
				
			||||||
		unsigned thisARFCN = baseARFCN + 1 + i;
 | 
							unsigned thisARFCN = baseARFCN + 1 + i;
 | 
				
			||||||
		if (contains(thisARFCN)) dest.writeField(wp,1,1);
 | 
							if (contains(thisARFCN)) dest.writeField(wp,1,1);
 | 
				
			||||||
@@ -316,6 +320,7 @@ void L3ChannelDescription::writeV( L3Frame &dest, size_t &wp ) const
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// HACK -- Hard code for non-hopping.
 | 
						// HACK -- Hard code for non-hopping.
 | 
				
			||||||
 | 
						// (pat) Same format used for Packet Channel Description 10.5.2.25a
 | 
				
			||||||
	assert(mHFlag==0);
 | 
						assert(mHFlag==0);
 | 
				
			||||||
	dest.writeField(wp,mTypeAndOffset,5);
 | 
						dest.writeField(wp,mTypeAndOffset,5);
 | 
				
			||||||
	dest.writeField(wp,mTN,3);
 | 
						dest.writeField(wp,mTN,3);
 | 
				
			||||||
@@ -345,7 +350,6 @@ void L3ChannelDescription::parseV(const L3Frame& src, size_t &rp)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void L3ChannelDescription::text(std::ostream& os) const
 | 
					void L3ChannelDescription::text(std::ostream& os) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					 | 
				
			||||||
	os << "typeAndOffset=" << mTypeAndOffset;
 | 
						os << "typeAndOffset=" << mTypeAndOffset;
 | 
				
			||||||
	os << " TN=" << mTN;
 | 
						os << " TN=" << mTN;
 | 
				
			||||||
	os << " TSC=" << mTSC;
 | 
						os << " TSC=" << mTSC;
 | 
				
			||||||
@@ -353,11 +357,11 @@ void L3ChannelDescription::text(std::ostream& os) const
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
void L3RequestReference::writeV( L3Frame &dest, size_t &wp ) const 
 | 
					void L3RequestReference::writeV( L3Frame &dest, size_t &wp ) const 
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Note: fields are written MSB first, then bytes are reversed later in the encoder.
 | 
				
			||||||
	// 					Request Reference Format.
 | 
						// 					Request Reference Format.
 | 
				
			||||||
	// 		 	7      6      5      4      3     2      1      0
 | 
						// 		 	7      6      5      4      3     2      1      0
 | 
				
			||||||
	//	  [      			RequestReference [7:0]   			]  Octet 2
 | 
						//	  [      			RequestReference [7:0]   			]  Octet 2
 | 
				
			||||||
@@ -373,7 +377,10 @@ void L3RequestReference::writeV( L3Frame &dest, size_t &wp ) const
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void L3RequestReference::text(ostream& os) const
 | 
					void L3RequestReference::text(ostream& os) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	os << "RA=" << mRA;	
 | 
						os << hex << "RA=0x" << mRA << dec;	
 | 
				
			||||||
 | 
						// pat added: This is the frame number recomputed from T1p, T2, T3:
 | 
				
			||||||
 | 
						unsigned recomputed = 51 * ((mT3-mT2) % 26) + mT3 + 51 * 26 * mT1p;
 | 
				
			||||||
 | 
						os << " T=" << recomputed;
 | 
				
			||||||
	os << " T1'=" << mT1p;
 | 
						os << " T1'=" << mT1p;
 | 
				
			||||||
	os << " T2=" << mT2;
 | 
						os << " T2=" << mT2;
 | 
				
			||||||
	os << " T3=" << mT3;	
 | 
						os << " T3=" << mT3;	
 | 
				
			||||||
@@ -600,21 +607,21 @@ void L3MeasurementResults::text(ostream& os) const
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
unsigned L3MeasurementResults::RXLEV_NCELL(unsigned * target) const
 | 
					unsigned L3MeasurementResults::RXLEV_NCELLs(unsigned * target) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	for (unsigned i=0; i<mNO_NCELL; i++) target[i] = mRXLEV_NCELL[i];
 | 
						for (unsigned i=0; i<mNO_NCELL; i++) target[i] = mRXLEV_NCELL[i];
 | 
				
			||||||
	return mNO_NCELL;
 | 
						return mNO_NCELL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
unsigned L3MeasurementResults::BCCH_FREQ_NCELL(unsigned * target) const
 | 
					unsigned L3MeasurementResults::BCCH_FREQ_NCELLs(unsigned * target) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	for (unsigned i=0; i<mNO_NCELL; i++) target[i] = mBCCH_FREQ_NCELL[i];
 | 
						for (unsigned i=0; i<mNO_NCELL; i++) target[i] = mBCCH_FREQ_NCELL[i];
 | 
				
			||||||
	return mNO_NCELL;
 | 
						return mNO_NCELL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
unsigned L3MeasurementResults::BSIC_NCELL(unsigned * target) const
 | 
					unsigned L3MeasurementResults::BSIC_NCELLs(unsigned * target) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	for (unsigned i=0; i<mNO_NCELL; i++) target[i] = mBSIC_NCELL[i];
 | 
						for (unsigned i=0; i<mNO_NCELL; i++) target[i] = mBSIC_NCELL[i];
 | 
				
			||||||
	return mNO_NCELL;
 | 
						return mNO_NCELL;
 | 
				
			||||||
@@ -642,74 +649,247 @@ float L3MeasurementResults::decodeQualToBER(unsigned qual) const
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
L3SI3RestOctets::L3SI3RestOctets()
 | 
					L3SI3RestOctets::L3SI3RestOctets()
 | 
				
			||||||
	:L3RestOctets(),
 | 
						:L3RestOctets(),
 | 
				
			||||||
 | 
						mHaveSI3RestOctets(false),
 | 
				
			||||||
	mHaveSelectionParameters(false),
 | 
						mHaveSelectionParameters(false),
 | 
				
			||||||
	mCBQ(0),mCELL_RESELECT_OFFSET(0),
 | 
						mCBQ(0),mCELL_RESELECT_OFFSET(0),
 | 
				
			||||||
	mTEMPORARY_OFFSET(0),
 | 
						mTEMPORARY_OFFSET(0),
 | 
				
			||||||
	mPENALTY_TIME(0)
 | 
						mPENALTY_TIME(0),
 | 
				
			||||||
 | 
						mRA_COLOUR(0),
 | 
				
			||||||
 | 
						mHaveGPRS(false)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						// (pat) 11-26-2011 If you enable this in the OpenBTS sql, the microtech
 | 
				
			||||||
 | 
						// modem stops registering.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// See GSM 04.08 10.5.2.34 and 05.08 9 Table 1.
 | 
						// See GSM 04.08 10.5.2.34 and 05.08 9 Table 1.
 | 
				
			||||||
	if (!gConfig.defines("GSM.SI3RO")) return;
 | 
						// 12-12: Pat reversed the logic of this so the default is to have the rest octets
 | 
				
			||||||
 | 
						// if any of the other SI3R0 things are defined, unless you specifically
 | 
				
			||||||
 | 
						// define GSM.SI3RO as 0:
 | 
				
			||||||
 | 
						if (gConfig.defines("GSM.SI3RO")) {
 | 
				
			||||||
 | 
							mHaveSI3RestOctets = gConfig.getNum("GSM.SI3RO");
 | 
				
			||||||
 | 
							if (!mHaveSI3RestOctets) return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Optional Cell Selection Parameters.
 | 
						// Optional Cell Selection Parameters.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// CELL_BAR_QUALIFY. 1 bit. Default value is 0.
 | 
						// CELL_BAR_QUALIFY. 1 bit. Default value is 0.
 | 
				
			||||||
	if (gConfig.defines("GSM.SI3RO.CBQ")) {
 | 
						if (gConfig.defines("GSM.SI3RO.CBQ")) {
 | 
				
			||||||
		mCBQ = gConfig.getNum("GSM.SI3RO.CBQ");
 | 
							mCBQ = gConfig.getNum("GSM.SI3RO.CBQ");
 | 
				
			||||||
 | 
							mHaveSI3RestOctets = true;
 | 
				
			||||||
		mHaveSelectionParameters = true;
 | 
							mHaveSelectionParameters = true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// CELL_RESELECT_OFFSET. 6 bits. Default value is 0.
 | 
						// CELL_RESELECT_OFFSET. 6 bits. Default value is 0.
 | 
				
			||||||
	// C2 offset in 2 dB steps
 | 
						// C2 offset in 2 dB steps
 | 
				
			||||||
	if (gConfig.defines("GSM.SI3RO.CRO")) {
 | 
						if (gConfig.defines("GSM.SI3RO.CRO")) {
 | 
				
			||||||
		mCELL_RESELECT_OFFSET = gConfig.getNum("GSM.SI3RO.CRO");
 | 
							mCELL_RESELECT_OFFSET = gConfig.getNum("GSM.SI3RO.CRO");
 | 
				
			||||||
 | 
							mHaveSI3RestOctets = true;
 | 
				
			||||||
		mHaveSelectionParameters = true;
 | 
							mHaveSelectionParameters = true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Another offset to C2 in 10 dB steps, applied during penalty time.
 | 
						// Another offset to C2 in 10 dB steps, applied during penalty time.
 | 
				
			||||||
	// 3 bits.  // Default is 0 dB but "7" means "infinity".
 | 
						// 3 bits.  // Default is 0 dB but "7" means "infinity".
 | 
				
			||||||
	if (gConfig.defines("GSM.SI3RO.TEMPORARY_OFFSET")) {
 | 
						if (gConfig.defines("GSM.SI3RO.TEMPORARY_OFFSET")) {
 | 
				
			||||||
		mTEMPORARY_OFFSET = gConfig.getNum("GSM.SI3RO.TEMPORARY_OFFSET");
 | 
							mTEMPORARY_OFFSET = gConfig.getNum("GSM.SI3RO.TEMPORARY_OFFSET");
 | 
				
			||||||
 | 
							mHaveSI3RestOctets = true;
 | 
				
			||||||
		mHaveSelectionParameters = true;
 | 
							mHaveSelectionParameters = true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// The time for which the temporary offset is applied, 20*(n+1).
 | 
						// The time for which the temporary offset is applied, 20*(n+1).
 | 
				
			||||||
	if (gConfig.defines("GSM.SI3RO.PENALTY_TIME")) {
 | 
						if (gConfig.defines("GSM.SI3RO.PENALTY_TIME")) {
 | 
				
			||||||
		mPENALTY_TIME = gConfig.getNum("GSM.SI3RO.PENALTY_TIME");
 | 
							mPENALTY_TIME = gConfig.getNum("GSM.SI3RO.PENALTY_TIME");
 | 
				
			||||||
 | 
							mHaveSI3RestOctets = true;
 | 
				
			||||||
		mHaveSelectionParameters = true;
 | 
							mHaveSelectionParameters = true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mHaveGPRS = GPRS::GPRSConfig::IsEnabled();
 | 
				
			||||||
 | 
						if (mHaveGPRS) {
 | 
				
			||||||
 | 
							mHaveSI3RestOctets = true;
 | 
				
			||||||
 | 
							mRA_COLOUR = gConfig.getNum("GPRS.RA_COLOUR");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
size_t L3SI3RestOctets::lengthV() const
 | 
					size_t L3SI3RestOctets::lengthV() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	size_t sumBits = 0;
 | 
						size_t sumBits = 0;
 | 
				
			||||||
 | 
						if (!mHaveSI3RestOctets) { return 0; }
 | 
				
			||||||
	if (mHaveSelectionParameters) sumBits += 1 + 1+6+3+5;
 | 
						if (mHaveSelectionParameters) sumBits += 1 + 1+6+3+5;
 | 
				
			||||||
 | 
						else sumBits += 1;
 | 
				
			||||||
 | 
						sumBits += 1 // L for Optional Power Offset
 | 
				
			||||||
 | 
								+ 1 // L for System Information 2ter Indicator
 | 
				
			||||||
 | 
								+ 1 // L for Early Classmark Sending Control
 | 
				
			||||||
 | 
								+ 1; // L for Scheduling if and where
 | 
				
			||||||
 | 
						if (mHaveGPRS) sumBits += 1 // H for GPRS Indicator
 | 
				
			||||||
 | 
								+ 4;// Size of GPRS Indicator field.
 | 
				
			||||||
 | 
						else sumBits += 1;
 | 
				
			||||||
	size_t octets = sumBits/8;
 | 
						size_t octets = sumBits/8;
 | 
				
			||||||
	if (sumBits%8) octets += 1;
 | 
						if (sumBits%8) octets += 1;
 | 
				
			||||||
	return octets;
 | 
						return octets;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM04.08 sec10.5.2.34
 | 
				
			||||||
void L3SI3RestOctets::writeV(L3Frame& dest, size_t &wp) const
 | 
					void L3SI3RestOctets::writeV(L3Frame& dest, size_t &wp) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						if (!mHaveSI3RestOctets) { return; }
 | 
				
			||||||
 | 
						size_t wpstart = wp;
 | 
				
			||||||
	if (mHaveSelectionParameters) {
 | 
						if (mHaveSelectionParameters) {
 | 
				
			||||||
		dest.writeH(wp);
 | 
							dest.writeH(wp);
 | 
				
			||||||
		dest.writeField(wp,mCBQ,1);
 | 
							dest.writeField(wp,mCBQ,1);
 | 
				
			||||||
		dest.writeField(wp,mCELL_RESELECT_OFFSET,6);
 | 
							dest.writeField(wp,mCELL_RESELECT_OFFSET,6);
 | 
				
			||||||
		dest.writeField(wp,mTEMPORARY_OFFSET,3);
 | 
							dest.writeField(wp,mTEMPORARY_OFFSET,3);
 | 
				
			||||||
		dest.writeField(wp,mPENALTY_TIME,5);
 | 
							dest.writeField(wp,mPENALTY_TIME,5);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							dest.writeL(wp);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						dest.writeL(wp); // L for Optional Power Offset
 | 
				
			||||||
 | 
						dest.writeL(wp); // L for System Information 2ter Indicator
 | 
				
			||||||
 | 
						dest.writeL(wp); // L for Early Classmark Sending Control
 | 
				
			||||||
 | 
						dest.writeL(wp); // L for Scheduling if and where (means no System Information Type 9.)
 | 
				
			||||||
 | 
						if (mHaveGPRS) {
 | 
				
			||||||
 | 
							dest.writeH(wp); // H for GPRS Indicator
 | 
				
			||||||
 | 
							// (pat) The GPRS Indicator.
 | 
				
			||||||
 | 
							dest.writeField(wp,mRA_COLOUR,3);
 | 
				
			||||||
 | 
							dest.writeField(wp,0,1);	// SI13 POSITION: 0 => BCCH Norm
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							dest.writeL(wp);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						while (wp & 7) { dest.writeL(wp); }	// spare padding to byte boundary.
 | 
				
			||||||
 | 
						assert(wp-wpstart == lengthV() * 8);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void L3SI3RestOctets::text(ostream& os) const
 | 
					void L3SI3RestOctets::text(ostream& os) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						if (!mHaveSI3RestOctets) { return; }
 | 
				
			||||||
	if (mHaveSelectionParameters) {
 | 
						if (mHaveSelectionParameters) {
 | 
				
			||||||
		os << "CBQ=" << mCBQ;
 | 
							os << "CBQ=" << mCBQ;
 | 
				
			||||||
		os << " CELL_RESELECT_OFFSET=" << mCELL_RESELECT_OFFSET;
 | 
							os << " CELL_RESELECT_OFFSET=" << mCELL_RESELECT_OFFSET;
 | 
				
			||||||
		os << " TEMPORARY_OFFSET=" << mTEMPORARY_OFFSET;
 | 
							os << " TEMPORARY_OFFSET=" << mTEMPORARY_OFFSET;
 | 
				
			||||||
		os << " PANALTY_TIME=" << mPENALTY_TIME;
 | 
							os << " PENALTY_TIME=" << mPENALTY_TIME;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (mHaveGPRS) {
 | 
				
			||||||
 | 
							os << " RA_COLOUR=" << mRA_COLOUR;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3HandoverReference::writeV(L3Frame& frame, size_t& wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						frame.writeField(wp,mValue,8);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3HandoverReference::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << "value=" << mValue;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t L3CipheringModeSetting::lengthV() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3CipheringModeSetting::writeV(L3Frame& frame, size_t& wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						frame.writeField(wp, mCiphering? mAlgorithm-1: 0, 3);
 | 
				
			||||||
 | 
						frame.writeField(wp, mCiphering, 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3CipheringModeSetting::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << "ciphering=" << mCiphering;
 | 
				
			||||||
 | 
						os << " algorithm=A5/" << mAlgorithm;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t L3CipheringModeResponse::lengthV() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3CipheringModeResponse::writeV(L3Frame& frame, size_t& wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						frame.writeField(wp, 0, 3);
 | 
				
			||||||
 | 
						frame.writeField(wp, mIncludeIMEISV, 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3CipheringModeResponse::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << "includeIMEISV=" << mIncludeIMEISV;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3SynchronizationIndication::writeV(L3Frame& frame, size_t& wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						frame.writeField(wp,0xD,4);
 | 
				
			||||||
 | 
						frame.writeField(wp,mNCI,1);
 | 
				
			||||||
 | 
						frame.writeField(wp,mROT,1);
 | 
				
			||||||
 | 
						frame.writeField(wp,mSI,2);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3SynchronizationIndication::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << "NCI=" << (int)mNCI << " ROT=" << (int)mROT << " SI=" << mSI;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3CellDescription::writeV(L3Frame& frame, size_t &wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						frame.writeField(wp, mARFCN>>8, 2);
 | 
				
			||||||
 | 
						frame.writeField(wp, mNCC, 3);
 | 
				
			||||||
 | 
						frame.writeField(wp, mBCC, 3);
 | 
				
			||||||
 | 
						frame.writeField(wp, mARFCN & 0x0ff, 8);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3CellDescription::text(std::ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << " ARFCN=" << mARFCN;	
 | 
				
			||||||
 | 
						os << " NCC=" << mNCC;
 | 
				
			||||||
 | 
						os << " BCC=" << mBCC;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3SI13RestOctets::writeV(L3Frame& dest, size_t &wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t wpstart = wp;
 | 
				
			||||||
 | 
						dest.writeH(wp);	// Indicates Rest Octets are present.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// FIXME -- Need to implement BCCH_CHANGE_MARK
 | 
				
			||||||
 | 
						// If implemented, BCCH_CHANGE_MARK would be a counter
 | 
				
			||||||
 | 
						// that is incremented when the BCCH content changes.
 | 
				
			||||||
 | 
						dest.writeField(wp,gBTS.changemark()%8,3);	// BCCH_CHANGE_MARK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dest.writeField(wp,0,4);	// SI13_CHANGE_FIELD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dest.writeField(wp,0,1);	// no changemark or mobile allocation
 | 
				
			||||||
 | 
									// (pat) The GPRS "mobile allocation" is optional and does
 | 
				
			||||||
 | 
									// not indicate that GPRS service is present or absent.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dest.writeField(wp,0,1);	// no PBCCH  (pat says: This is correct.)
 | 
				
			||||||
 | 
						dest.writeField(wp,mRAC,8);
 | 
				
			||||||
 | 
						dest.writeField(wp,mSPGC_CCCH_SUP,1);
 | 
				
			||||||
 | 
						dest.writeField(wp,mPRIORITY_ACCESS_THR,3);
 | 
				
			||||||
 | 
						dest.writeField(wp,mNETWORK_CONTROL_ORDER,2);
 | 
				
			||||||
 | 
						mCellOptions.writeBits(dest,wp);
 | 
				
			||||||
 | 
						mPowerControlParameters.writeBits(dest,wp);
 | 
				
			||||||
 | 
						while (wp & 7) { dest.writeL(wp); }	// spare padding to byte bondary.
 | 
				
			||||||
 | 
						assert(wp-wpstart == lengthV() * 8);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3SI13RestOctets::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						os << "RAC=" << mRAC;
 | 
				
			||||||
 | 
						os << " SPGC_CCCH_SUP=" << mSPGC_CCCH_SUP;
 | 
				
			||||||
 | 
						os << " PRIORITY_ACCESS_THR=" << mPRIORITY_ACCESS_THR;
 | 
				
			||||||
 | 
						os << " NETWORK_CONTROL_ORDER=" << mNETWORK_CONTROL_ORDER;
 | 
				
			||||||
 | 
						os << " cellOptions=(" << mCellOptions << ")";
 | 
				
			||||||
 | 
						os << " powerControlParameters=(" << mPowerControlParameters << ")";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// vim: ts=4 sw=4
 | 
					// vim: ts=4 sw=4
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,25 +2,18 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
 | 
					* Copyright 2011, 2012 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,6 +23,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
#include "GSML3Message.h"
 | 
					#include "GSML3Message.h"
 | 
				
			||||||
 | 
					#include "GSML3GPRSElements.h"
 | 
				
			||||||
#include <Globals.h>
 | 
					#include <Globals.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -55,7 +49,7 @@ class L3CellOptionsBCCH : public L3ProtocolElement {
 | 
				
			|||||||
		mPWRC=0;
 | 
							mPWRC=0;
 | 
				
			||||||
		mDTX=2;
 | 
							mDTX=2;
 | 
				
			||||||
		// Configuarable values.
 | 
							// Configuarable values.
 | 
				
			||||||
		mRADIO_LINK_TIMEOUT= gConfig.getNum("GSM.RADIO-LINK-TIMEOUT");
 | 
							mRADIO_LINK_TIMEOUT= gConfig.getNum("GSM.CellOptions.RADIO-LINK-TIMEOUT");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size_t lengthV() const { return 1; }
 | 
						size_t lengthV() const { return 1; }
 | 
				
			||||||
@@ -87,7 +81,7 @@ class L3CellOptionsSACCH : public L3ProtocolElement {
 | 
				
			|||||||
		mPWRC=0;
 | 
							mPWRC=0;
 | 
				
			||||||
		mDTX=2;
 | 
							mDTX=2;
 | 
				
			||||||
		// Configuarable values.
 | 
							// Configuarable values.
 | 
				
			||||||
		mRADIO_LINK_TIMEOUT=gConfig.getNum("GSM.RADIO-LINK-TIMEOUT");
 | 
							mRADIO_LINK_TIMEOUT=gConfig.getNum("GSM.CellOptions.RADIO-LINK-TIMEOUT");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size_t lengthV() const { return 1; }
 | 
						size_t lengthV() const { return 1; }
 | 
				
			||||||
@@ -144,10 +138,16 @@ class L3ControlChannelDescription : public L3ProtocolElement {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	private:
 | 
						private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// (pat) 5-27-2012: I put in 'real' paging channels and used them for GPRS,
 | 
				
			||||||
 | 
						// but someone still needs to modify the GSM stack to use them and test them there.
 | 
				
			||||||
 | 
						// Then we could change the parameters below to provide more paging channels.
 | 
				
			||||||
 | 
						// See class CCCHCombinedChannel.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unsigned mATT;				///< 1 -> IMSI attach/detach
 | 
						unsigned mATT;				///< 1 -> IMSI attach/detach
 | 
				
			||||||
	unsigned mBS_AG_BLKS_RES;	///< access grant channel reservation
 | 
						unsigned mBS_AG_BLKS_RES;	///< access grant channel reservation
 | 
				
			||||||
	unsigned mCCCH_CONF;			///< channel combination for CCCH
 | 
						unsigned mCCCH_CONF;			///< channel combination for CCCH
 | 
				
			||||||
	unsigned mBS_PA_MFRMS;		///< paging channel configuration
 | 
						unsigned mBS_PA_MFRMS;		///< paging channel configuration
 | 
				
			||||||
 | 
									// Note: This var is 0..7 representing BS_PA_MFRMS values 2..9.
 | 
				
			||||||
	unsigned mT3212;				///< periodic updating timeout
 | 
						unsigned mT3212;				///< periodic updating timeout
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
@@ -159,11 +159,14 @@ class L3ControlChannelDescription : public L3ProtocolElement {
 | 
				
			|||||||
		mBS_AG_BLKS_RES=2;			// reserve 2 CCCHs for access grant
 | 
							mBS_AG_BLKS_RES=2;			// reserve 2 CCCHs for access grant
 | 
				
			||||||
		mBS_PA_MFRMS=0;				// minimum PCH spacing
 | 
							mBS_PA_MFRMS=0;				// minimum PCH spacing
 | 
				
			||||||
		// Configurable values.
 | 
							// Configurable values.
 | 
				
			||||||
		mATT=(unsigned)gConfig.defines("Control.LUR.AttachDetach");
 | 
							mATT=(unsigned)gConfig.getBool("Control.LUR.AttachDetach");
 | 
				
			||||||
		mCCCH_CONF=gConfig.getNum("GSM.CCCH.CCCH-CONF");
 | 
							mCCCH_CONF=gConfig.getNum("GSM.CCCH.CCCH-CONF");
 | 
				
			||||||
		mT3212=gConfig.getNum("GSM.Timer.T3212")/6;
 | 
							mT3212=gConfig.getNum("GSM.Timer.T3212")/6;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// BS_PA_MFRMS is the number of 51-multiframes used for paging in the range 2..9.
 | 
				
			||||||
 | 
						unsigned getBS_PA_MFRMS();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size_t lengthV() const { return 3; }
 | 
						size_t lengthV() const { return 3; }
 | 
				
			||||||
	void writeV(L3Frame& dest, size_t &wp) const;
 | 
						void writeV(L3Frame& dest, size_t &wp) const;
 | 
				
			||||||
	void parseV(const L3Frame&, size_t&) { assert(0); }
 | 
						void parseV(const L3Frame&, size_t&) { assert(0); }
 | 
				
			||||||
@@ -257,8 +260,14 @@ class L3NeighborCellsDescription : public L3FrequencyList {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	L3NeighborCellsDescription()
 | 
						L3NeighborCellsDescription() {}
 | 
				
			||||||
		:L3FrequencyList(gConfig.getVector("GSM.CellSelection.Neighbors"))
 | 
					
 | 
				
			||||||
 | 
						//L3NeighborCellsDescription()
 | 
				
			||||||
 | 
						//	:L3FrequencyList(gConfig.getVector("GSM.CellSelection.Neighbors"))
 | 
				
			||||||
 | 
						//{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3NeighborCellsDescription(const std::vector<unsigned>& neighbors)
 | 
				
			||||||
 | 
							:L3FrequencyList(neighbors)
 | 
				
			||||||
	{}
 | 
						{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void writeV(L3Frame& dest, size_t &wp) const;
 | 
						void writeV(L3Frame& dest, size_t &wp) const;
 | 
				
			||||||
@@ -322,7 +331,7 @@ class L3RACHControlParameters : public L3ProtocolElement {
 | 
				
			|||||||
		// Configurable values.
 | 
							// Configurable values.
 | 
				
			||||||
		mMaxRetrans = gConfig.getNum("GSM.RACH.MaxRetrans");
 | 
							mMaxRetrans = gConfig.getNum("GSM.RACH.MaxRetrans");
 | 
				
			||||||
		mTxInteger = gConfig.getNum("GSM.RACH.TxInteger");
 | 
							mTxInteger = gConfig.getNum("GSM.RACH.TxInteger");
 | 
				
			||||||
		mAC = gConfig.getNum("GSM.RACH.AC");
 | 
							mAC = 0x0400; //NO EMERGENCY SERVICE - kurtis
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size_t lengthV() const { return 3; }
 | 
						size_t lengthV() const { return 3; }
 | 
				
			||||||
@@ -365,6 +374,7 @@ public:
 | 
				
			|||||||
/** DedicatedModeOrTBF, GSM 04.08 10.5.2.25b */
 | 
					/** DedicatedModeOrTBF, GSM 04.08 10.5.2.25b */
 | 
				
			||||||
class L3DedicatedModeOrTBF : public L3ProtocolElement {
 | 
					class L3DedicatedModeOrTBF : public L3ProtocolElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// (pat) This is poorly named: mDownlink must be TRUE for a TBF, even if it is an uplink tbf.
 | 
				
			||||||
	unsigned mDownlink;		///< Indicates the IA reset octets contain additional information.
 | 
						unsigned mDownlink;		///< Indicates the IA reset octets contain additional information.
 | 
				
			||||||
	unsigned mTMA;			///< This is part of a 2-message assignment.
 | 
						unsigned mTMA;			///< This is part of a 2-message assignment.
 | 
				
			||||||
	unsigned mDMOrTBF;		///< Dedicated link (circuit-switched) or temporary block flow (GPRS/pakcet).
 | 
						unsigned mDMOrTBF;		///< Dedicated link (circuit-switched) or temporary block flow (GPRS/pakcet).
 | 
				
			||||||
@@ -372,9 +382,9 @@ class L3DedicatedModeOrTBF : public L3ProtocolElement {
 | 
				
			|||||||
	
 | 
						
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	L3DedicatedModeOrTBF()
 | 
						L3DedicatedModeOrTBF(bool forTBF, bool wDownlink)
 | 
				
			||||||
		:L3ProtocolElement(),
 | 
							:L3ProtocolElement(),
 | 
				
			||||||
		mDownlink(0), mTMA(0), mDMOrTBF(0)
 | 
							mDownlink(wDownlink), mTMA(0), mDMOrTBF(forTBF)
 | 
				
			||||||
	{}
 | 
						{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size_t lengthV() const { return 1; }
 | 
						size_t lengthV() const { return 1; }
 | 
				
			||||||
@@ -387,7 +397,12 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** ChannelDescription, GSM 04.08 10.5.2.5 */
 | 
					/** ChannelDescription, GSM 04.18 10.5.2.5
 | 
				
			||||||
 | 
						(pat) The Packet Channel Description, GSM 04.18 10.5.2.25a, is the
 | 
				
			||||||
 | 
						same as the Channel Description except with mTypeAndOffset always 1,
 | 
				
			||||||
 | 
						and for the addition of indirect frequency hopping encoding,
 | 
				
			||||||
 | 
						which is irrelevant for us at the moment.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
class L3ChannelDescription : public L3ProtocolElement {
 | 
					class L3ChannelDescription : public L3ProtocolElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -416,7 +431,8 @@ public:
 | 
				
			|||||||
	/** Non-hopping initializer. */
 | 
						/** Non-hopping initializer. */
 | 
				
			||||||
	L3ChannelDescription(TypeAndOffset wTypeAndOffset, unsigned wTN,
 | 
						L3ChannelDescription(TypeAndOffset wTypeAndOffset, unsigned wTN,
 | 
				
			||||||
			unsigned wTSC, unsigned wARFCN)
 | 
								unsigned wTSC, unsigned wARFCN)
 | 
				
			||||||
		:mTypeAndOffset(wTypeAndOffset),mTN(wTN),
 | 
							:L3ProtocolElement(),
 | 
				
			||||||
 | 
							mTypeAndOffset(wTypeAndOffset),mTN(wTN),
 | 
				
			||||||
		mTSC(wTSC),
 | 
							mTSC(wTSC),
 | 
				
			||||||
		mHFlag(0),
 | 
							mHFlag(0),
 | 
				
			||||||
		mARFCN(wARFCN),
 | 
							mARFCN(wARFCN),
 | 
				
			||||||
@@ -436,8 +452,24 @@ public:
 | 
				
			|||||||
	void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
 | 
						void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
 | 
				
			||||||
	void text(std::ostream&) const;
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TypeAndOffset typeAndOffset() const { return mTypeAndOffset; }
 | 
				
			||||||
 | 
						unsigned TN() const { return mTN; }
 | 
				
			||||||
 | 
						unsigned TSC() const{ return mTSC; }
 | 
				
			||||||
 | 
						unsigned ARFCN() const { return mARFCN; }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 040.08 10.5.2.5a */
 | 
				
			||||||
 | 
					class L3ChannelDescription2 : public L3ChannelDescription {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						L3ChannelDescription2(TypeAndOffset wTypeAndOffset, unsigned wTN,
 | 
				
			||||||
 | 
							unsigned wTSC, unsigned wARFCN)
 | 
				
			||||||
 | 
							:L3ChannelDescription(wTypeAndOffset,wTN,wTSC,wARFCN)
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3ChannelDescription2() { }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -734,6 +766,10 @@ class L3MeasurementResults : public L3ProtocolElement {
 | 
				
			|||||||
	L3MeasurementResults()
 | 
						L3MeasurementResults()
 | 
				
			||||||
		:L3ProtocolElement(),
 | 
							:L3ProtocolElement(),
 | 
				
			||||||
		mMEAS_VALID(false),
 | 
							mMEAS_VALID(false),
 | 
				
			||||||
 | 
							mRXLEV_FULL_SERVING_CELL(0),
 | 
				
			||||||
 | 
							mRXLEV_SUB_SERVING_CELL(0),
 | 
				
			||||||
 | 
							mRXQUAL_FULL_SERVING_CELL(0),
 | 
				
			||||||
 | 
							mRXQUAL_SUB_SERVING_CELL(0),
 | 
				
			||||||
		mNO_NCELL(0)
 | 
							mNO_NCELL(0)
 | 
				
			||||||
	{ }
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -756,11 +792,11 @@ class L3MeasurementResults : public L3ProtocolElement {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	unsigned NO_NCELL() const { return mNO_NCELL; }
 | 
						unsigned NO_NCELL() const { return mNO_NCELL; }
 | 
				
			||||||
	unsigned RXLEV_NCELL(unsigned i) const { assert(i<mNO_NCELL); return mRXLEV_NCELL[i]; }
 | 
						unsigned RXLEV_NCELL(unsigned i) const { assert(i<mNO_NCELL); return mRXLEV_NCELL[i]; }
 | 
				
			||||||
	unsigned RXLEV_NCELL(unsigned *) const;
 | 
						unsigned RXLEV_NCELLs(unsigned *) const;
 | 
				
			||||||
	unsigned BCCH_FREQ_NCELL(unsigned i) const { assert(i<mNO_NCELL); return mBCCH_FREQ_NCELL[i]; }
 | 
						unsigned BCCH_FREQ_NCELL(unsigned i) const { assert(i<mNO_NCELL); return mBCCH_FREQ_NCELL[i]; }
 | 
				
			||||||
	unsigned BCCH_FREQ_NCELL(unsigned *) const;
 | 
						unsigned BCCH_FREQ_NCELLs(unsigned *) const;
 | 
				
			||||||
	unsigned BSIC_NCELL(unsigned i) const { assert(i<mNO_NCELL); return mBSIC_NCELL[i]; }
 | 
						unsigned BSIC_NCELL(unsigned i) const { assert(i<mNO_NCELL); return mBSIC_NCELL[i]; }
 | 
				
			||||||
	unsigned BSIC_NCELL(unsigned *) const;
 | 
						unsigned BSIC_NCELLs(unsigned *) const;
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**@ Real-unit conversions. */
 | 
						/**@ Real-unit conversions. */
 | 
				
			||||||
@@ -787,6 +823,139 @@ class L3MeasurementResults : public L3ProtocolElement {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 10.5.2.2 */
 | 
				
			||||||
 | 
					class L3CellDescription : public L3ProtocolElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned mARFCN;
 | 
				
			||||||
 | 
						unsigned mNCC;
 | 
				
			||||||
 | 
						unsigned mBCC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3CellDescription( unsigned wARFCN, unsigned wNCC, unsigned wBCC)
 | 
				
			||||||
 | 
							:L3ProtocolElement(),
 | 
				
			||||||
 | 
							mARFCN(wARFCN),
 | 
				
			||||||
 | 
							mNCC(wNCC),mBCC(wBCC)
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3CellDescription() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t lengthV() const { return 2; }
 | 
				
			||||||
 | 
						void writeV(L3Frame&, size_t&) const;
 | 
				
			||||||
 | 
						void parseV(const L3Frame&, size_t&) { assert(0); }
 | 
				
			||||||
 | 
						void parseV(const L3Frame&, size_t& , size_t) { assert(0); }
 | 
				
			||||||
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 10.5.2.15 */
 | 
				
			||||||
 | 
					class L3HandoverReference : public L3ProtocolElement
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned mValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3HandoverReference(unsigned wValue)
 | 
				
			||||||
 | 
							:L3ProtocolElement(),
 | 
				
			||||||
 | 
							mValue(wValue)
 | 
				
			||||||
 | 
						{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3HandoverReference() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t lengthV() const { return 1; }
 | 
				
			||||||
 | 
						void writeV(L3Frame &, size_t &wp ) const;
 | 
				
			||||||
 | 
						void parseV( const L3Frame&, size_t&, size_t) { abort(); }
 | 
				
			||||||
 | 
						void parseV(const L3Frame&, size_t&) { abort(); }
 | 
				
			||||||
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned value() const { return mValue; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 10.5.2.9 */
 | 
				
			||||||
 | 
					class L3CipheringModeSetting : public L3ProtocolElement
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool mCiphering;
 | 
				
			||||||
 | 
						int mAlgorithm;  // algorithm is A5/mAlgorithm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3CipheringModeSetting(bool wCiphering, int wAlgorithm)
 | 
				
			||||||
 | 
							:mCiphering(wCiphering), mAlgorithm(wAlgorithm)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// assert(wAlgorithm >= 1 && wAlgorithm <= 7);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t lengthV() const;
 | 
				
			||||||
 | 
						void writeV(L3Frame&, size_t& wp) const;
 | 
				
			||||||
 | 
						void parseV( const L3Frame&, size_t&, size_t) { abort(); }
 | 
				
			||||||
 | 
						void parseV(const L3Frame&, size_t&) { abort(); }
 | 
				
			||||||
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 10.5.2.10 */
 | 
				
			||||||
 | 
					class L3CipheringModeResponse : public L3ProtocolElement
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool mIncludeIMEISV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3CipheringModeResponse(bool wIncludeIMEISV)
 | 
				
			||||||
 | 
							:mIncludeIMEISV(wIncludeIMEISV)
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t lengthV() const;
 | 
				
			||||||
 | 
						void writeV(L3Frame&, size_t& wp) const;
 | 
				
			||||||
 | 
						void parseV( const L3Frame&, size_t&, size_t) { abort(); }
 | 
				
			||||||
 | 
						void parseV(const L3Frame&, size_t&) { abort(); }
 | 
				
			||||||
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 10.5.2.39 */
 | 
				
			||||||
 | 
					class L3SynchronizationIndication : public L3ProtocolElement
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool mNCI;
 | 
				
			||||||
 | 
						bool mROT;
 | 
				
			||||||
 | 
						int mSI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3SynchronizationIndication(bool wNCI, bool wROT, int wSI = 0)
 | 
				
			||||||
 | 
							:L3ProtocolElement(),
 | 
				
			||||||
 | 
							mNCI(wNCI),
 | 
				
			||||||
 | 
							mROT(wROT),
 | 
				
			||||||
 | 
							mSI(wSI & 3)
 | 
				
			||||||
 | 
						{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3SynchronizationIndication() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t lengthV() const { return 1; }
 | 
				
			||||||
 | 
						void writeV(L3Frame &, size_t &wp ) const;
 | 
				
			||||||
 | 
						void parseV( const L3Frame&, size_t&, size_t) { abort(); }
 | 
				
			||||||
 | 
						void parseV(const L3Frame&, size_t&) { abort(); }
 | 
				
			||||||
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned NCI() const { return mNCI; }
 | 
				
			||||||
 | 
						unsigned ROT() const { return mROT; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 10.5.2.28a */
 | 
				
			||||||
 | 
					class L3PowerCommandAndAccessType : public L3PowerCommand { };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** A special subclass for rest octets, just in case we need it later. */
 | 
					/** A special subclass for rest octets, just in case we need it later. */
 | 
				
			||||||
@@ -800,12 +969,15 @@ class L3SI3RestOctets : public L3RestOctets {
 | 
				
			|||||||
	private:
 | 
						private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// We do not yet support the full parameter set.
 | 
						// We do not yet support the full parameter set.
 | 
				
			||||||
 | 
						bool mHaveSI3RestOctets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool mHaveSelectionParameters;
 | 
						bool mHaveSelectionParameters;
 | 
				
			||||||
	bool mCBQ;
 | 
						bool mCBQ;
 | 
				
			||||||
	unsigned mCELL_RESELECT_OFFSET;
 | 
						unsigned mCELL_RESELECT_OFFSET;		// 6 bits
 | 
				
			||||||
	unsigned mTEMPORARY_OFFSET;
 | 
						unsigned mTEMPORARY_OFFSET;			// 3 bits
 | 
				
			||||||
	unsigned mPENALTY_TIME;
 | 
						unsigned mPENALTY_TIME;				// 5 bits
 | 
				
			||||||
 | 
						unsigned mRA_COLOUR;				// (pat) In GPRS_Indicator, 3 bits
 | 
				
			||||||
 | 
						bool mHaveGPRS;						// (pat)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -820,6 +992,127 @@ class L3SI3RestOctets : public L3RestOctets {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					/** GSM 04.60 12.24 */
 | 
				
			||||||
 | 
					// (pat) Someone kindly added this before I got here.
 | 
				
			||||||
 | 
					// This info is included in the SI13 rest octets.
 | 
				
			||||||
 | 
					class L3GPRSCellOptions : public L3ProtocolElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned mNMO;			// Network Mode of Operation See GSM 03.60 6.3.3.1
 | 
				
			||||||
 | 
						unsigned mT3168;		// range 0..7
 | 
				
			||||||
 | 
						unsigned mT3192;		// range 0..7
 | 
				
			||||||
 | 
						unsigned mDRX_TIMER_MAX;
 | 
				
			||||||
 | 
						unsigned mACCESS_BURST_TYPE;
 | 
				
			||||||
 | 
						unsigned mCONTROL_ACK_TYPE;
 | 
				
			||||||
 | 
						unsigned mBS_VC_MAX;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3GPRSCellOptions()
 | 
				
			||||||
 | 
							:L3ProtocolElement(),
 | 
				
			||||||
 | 
							mNMO(2),	// (pat) 2 == Network Mode of Operation III, which means
 | 
				
			||||||
 | 
										// GPRS attached MS uses Packet Paging channel
 | 
				
			||||||
 | 
										// if allocated (which it wont be), otherwise CCCH.
 | 
				
			||||||
 | 
							mT3168(gConfig.getNum("GPRS.CellOptions.T3168Code")),
 | 
				
			||||||
 | 
							mT3192(gConfig.getNum("GPRS.CellOptions.T3192Code")),
 | 
				
			||||||
 | 
							mDRX_TIMER_MAX(gConfig.getNum("GPRS.CellOptions.DRX_TIMER_MAX")),
 | 
				
			||||||
 | 
							mACCESS_BURST_TYPE(0),	// (pat) 0 == use 8 bit format of Packet Channel Request Message.
 | 
				
			||||||
 | 
							mCONTROL_ACK_TYPE(1),	// (pat) 1 == default format for Packet Control Acknowledgement
 | 
				
			||||||
 | 
													// is RLC/MAC block, ie, not special.
 | 
				
			||||||
 | 
							mBS_VC_MAX(gConfig.getNum("GPRS.CellOptions.BS_VC_MAX"))
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t lengthV() const { return 2+3+3+3+1+1+4+1+1; }
 | 
				
			||||||
 | 
						void writeV(L3Frame& dest, size_t &wp) const;
 | 
				
			||||||
 | 
						void parseV( const L3Frame&, size_t&, size_t) { abort(); }
 | 
				
			||||||
 | 
						void parseV(const L3Frame&, size_t&) { abort(); }
 | 
				
			||||||
 | 
						void text(std::ostream& os) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					/** GSM 04.60 12.13 */
 | 
				
			||||||
 | 
					class L3GPRSPowerControlParameters : public L3ProtocolElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned mALPHA;	///< GSM 04.60 Table 12.9.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3GPRSPowerControlParameters()
 | 
				
			||||||
 | 
							:L3ProtocolElement(),
 | 
				
			||||||
 | 
							mALPHA(gConfig.getNum("GPRS.PowerControl.ALPHA"))
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t lengthV() const { return 4+8; }
 | 
				
			||||||
 | 
						void writeV(L3Frame& dest, size_t &wp) const;
 | 
				
			||||||
 | 
						void parseV( const L3Frame&, size_t&, size_t) { abort(); }
 | 
				
			||||||
 | 
						void parseV(const L3Frame&, size_t&) { abort(); }
 | 
				
			||||||
 | 
						void text(std::ostream& os) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 10.5.2.37b */
 | 
				
			||||||
 | 
					class L3SI13RestOctets : public L3RestOctets {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned mRAC;				///< routing area code GSM03.03
 | 
				
			||||||
 | 
						bool mSPGC_CCCH_SUP;			///< indicates support of SPLIT_PG_CYCLE on CCCH
 | 
				
			||||||
 | 
						unsigned mPRIORITY_ACCESS_THR;
 | 
				
			||||||
 | 
						unsigned mNETWORK_CONTROL_ORDER;	///< network reselection behavior
 | 
				
			||||||
 | 
						const L3GPRSCellOptions mCellOptions;
 | 
				
			||||||
 | 
						const L3GPRSSI13PowerControlParameters mPowerControlParameters;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3SI13RestOctets()
 | 
				
			||||||
 | 
							:L3RestOctets(),
 | 
				
			||||||
 | 
							mRAC(gConfig.getNum("GPRS.RAC")),	// (pat) aka Routing Area Code.
 | 
				
			||||||
 | 
							mSPGC_CCCH_SUP(false),
 | 
				
			||||||
 | 
							// See GSM04.08 table 10.5.76: Value 6 means any priority packet access allowed.
 | 
				
			||||||
 | 
							mPRIORITY_ACCESS_THR(gConfig.getNum("GPRS.PRIORITY-ACCESS-THR")),
 | 
				
			||||||
 | 
							// (pat) GSM05.08 sec 10.1.4: This controls whether the MS
 | 
				
			||||||
 | 
							// does cell reselection or the network, and appears to apply
 | 
				
			||||||
 | 
							// only to GPRS mode.  Value NC2 = 2 means the network
 | 
				
			||||||
 | 
							// performs cell reselection, but it has a side-effect that
 | 
				
			||||||
 | 
							// the MS continually sends Measurement reports, and these
 | 
				
			||||||
 | 
							// clog up the RACH channel so badly that the MS cannot send
 | 
				
			||||||
 | 
							// enough uplink messages to make the SGSN happy.
 | 
				
			||||||
 | 
							// The reporting interval values are as follows,
 | 
				
			||||||
 | 
							// defined in GSM04.60 11.2.23 table 11.2.23.2
 | 
				
			||||||
 | 
							// NC_REPORTING_PERIOD_I - used in packet idle mode.
 | 
				
			||||||
 | 
							// NC_REPORTING_PERIOD_T - used in packet transfer mode.
 | 
				
			||||||
 | 
							// They can be in PSI5, SI2quarter (but I dont see them there),
 | 
				
			||||||
 | 
							// or by a PACKET MEASUREMENT ORDER msg.
 | 
				
			||||||
 | 
							// I am going to set this to 0 for now instead of 2.
 | 
				
			||||||
 | 
							// Update: Lets set it back to see if the MS keeps sending measurement reports when it is non-responsive.
 | 
				
			||||||
 | 
							// Update: If the MS is requested to make measurement reports it
 | 
				
			||||||
 | 
							// reduces the multislot capability.  Measurements described 45.008
 | 
				
			||||||
 | 
							mNETWORK_CONTROL_ORDER(gConfig.getNum("GPRS.NC.NetworkControlOrder")),
 | 
				
			||||||
 | 
							mPowerControlParameters()	// redundant explicit call
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t lengthBits() const
 | 
				
			||||||
 | 
						{ return 1+3+4+1+1+8+1+3+2+mCellOptions.lengthBits()+mPowerControlParameters.lengthBits(); }
 | 
				
			||||||
 | 
						size_t lengthV() const
 | 
				
			||||||
 | 
						{ return (lengthBits() + 7) / 8; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeV(L3Frame& dest, size_t &wp) const;
 | 
				
			||||||
 | 
						void parseV( const L3Frame&, size_t&, size_t) { abort(); }
 | 
				
			||||||
 | 
						void parseV(const L3Frame&, size_t&) { abort(); }
 | 
				
			||||||
 | 
						void text(std::ostream& os) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // GSM
 | 
					} // GSM
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,27 +2,19 @@
 | 
				
			|||||||
  @brief GSM Radio Resorce messages, from GSM 04.08 9.1.
 | 
					  @brief GSM Radio Resorce messages, from GSM 04.08 9.1.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2011 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2011, 2012 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -56,9 +48,6 @@ void L3Message::parseBody(const L3Frame&, size_t&)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ostream& GSM::operator<<(ostream& os, L3RRMessage::MessageType val)
 | 
					ostream& GSM::operator<<(ostream& os, L3RRMessage::MessageType val)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	switch (val) {
 | 
						switch (val) {
 | 
				
			||||||
@@ -126,6 +115,18 @@ ostream& GSM::operator<<(ostream& os, L3RRMessage::MessageType val)
 | 
				
			|||||||
			os << "RR Status"; break;
 | 
								os << "RR Status"; break;
 | 
				
			||||||
		case L3RRMessage::ApplicationInformation:
 | 
							case L3RRMessage::ApplicationInformation:
 | 
				
			||||||
			os << "Application Information"; break;
 | 
								os << "Application Information"; break;
 | 
				
			||||||
 | 
							case L3RRMessage::HandoverCommand:
 | 
				
			||||||
 | 
								os << "Handover Command"; break;
 | 
				
			||||||
 | 
							case L3RRMessage::HandoverComplete:
 | 
				
			||||||
 | 
								os << "Handover Complete"; break;
 | 
				
			||||||
 | 
							case L3RRMessage::HandoverFailure:
 | 
				
			||||||
 | 
								os << "Handover Failure"; break;
 | 
				
			||||||
 | 
							case L3RRMessage::CipheringModeCommand:
 | 
				
			||||||
 | 
								os << "Ciphering Mode Command"; break;
 | 
				
			||||||
 | 
							case L3RRMessage::CipheringModeComplete:
 | 
				
			||||||
 | 
								os << "Ciphering Mode Complete"; break;
 | 
				
			||||||
 | 
							case L3RRMessage::PhysicalInformation:
 | 
				
			||||||
 | 
								os << "Physical Information"; break;
 | 
				
			||||||
		default: os << hex << "0x" << (int)val << dec;
 | 
							default: os << hex << "0x" << (int)val << dec;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return os;
 | 
						return os;
 | 
				
			||||||
@@ -152,6 +153,9 @@ L3RRMessage* GSM::L3RRFactory(L3RRMessage::MessageType MTI)
 | 
				
			|||||||
		case L3RRMessage::ClassmarkEnquiry: return new L3ClassmarkEnquiry();
 | 
							case L3RRMessage::ClassmarkEnquiry: return new L3ClassmarkEnquiry();
 | 
				
			||||||
		case L3RRMessage::MeasurementReport: return new L3MeasurementReport();
 | 
							case L3RRMessage::MeasurementReport: return new L3MeasurementReport();
 | 
				
			||||||
		case L3RRMessage::ApplicationInformation: return new L3ApplicationInformation();
 | 
							case L3RRMessage::ApplicationInformation: return new L3ApplicationInformation();
 | 
				
			||||||
 | 
							case L3RRMessage::HandoverComplete: return new L3HandoverComplete();
 | 
				
			||||||
 | 
							case L3RRMessage::HandoverFailure: return new L3HandoverFailure();
 | 
				
			||||||
 | 
							case L3RRMessage::CipheringModeComplete: return new L3CipheringModeComplete();
 | 
				
			||||||
        // Partial support just to get along with some phones.
 | 
					        // Partial support just to get along with some phones.
 | 
				
			||||||
        case L3RRMessage::GPRSSuspensionRequest: return new L3GPRSSuspensionRequest();
 | 
					        case L3RRMessage::GPRSSuspensionRequest: return new L3GPRSSuspensionRequest();
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
@@ -215,7 +219,8 @@ void L3PagingRequestType1::writeBody(L3Frame& dest, size_t &wp) const
 | 
				
			|||||||
	assert(sz<=2);
 | 
						assert(sz<=2);
 | 
				
			||||||
	// Remember to reverse orders of 1/2-octet fields.
 | 
						// Remember to reverse orders of 1/2-octet fields.
 | 
				
			||||||
	// Because GSM transmits LSB-first within each byte.
 | 
						// Because GSM transmits LSB-first within each byte.
 | 
				
			||||||
	// channel needed codes
 | 
						// (pat) No, it is because the fields are written MSB first here,
 | 
				
			||||||
 | 
						// and then later byte-reversed in the encoder before being sent to radio LSB first.
 | 
				
			||||||
	dest.writeField(wp,channelNeededCode(mChannelsNeeded[1]),2);
 | 
						dest.writeField(wp,channelNeededCode(mChannelsNeeded[1]),2);
 | 
				
			||||||
	dest.writeField(wp,channelNeededCode(mChannelsNeeded[0]),2);
 | 
						dest.writeField(wp,channelNeededCode(mChannelsNeeded[0]),2);
 | 
				
			||||||
	// "normal paging", GSM 04.08 Table 10.5.63
 | 
						// "normal paging", GSM 04.08 Table 10.5.63
 | 
				
			||||||
@@ -320,6 +325,7 @@ void L3SystemInformationType3::writeBody(L3Frame& dest, size_t &wp) const
 | 
				
			|||||||
	- RACH Control Parameters 10.5.2.29 M V 3 
 | 
						- RACH Control Parameters 10.5.2.29 M V 3 
 | 
				
			||||||
	- Rest Octets 10.5.2.34 O CSN.1
 | 
						- Rest Octets 10.5.2.34 O CSN.1
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
						size_t wpstart = wp;
 | 
				
			||||||
	LOG(DEBUG) << dest;
 | 
						LOG(DEBUG) << dest;
 | 
				
			||||||
	mCI.writeV(dest,wp);
 | 
						mCI.writeV(dest,wp);
 | 
				
			||||||
	LOG(DEBUG) << dest;
 | 
						LOG(DEBUG) << dest;
 | 
				
			||||||
@@ -333,8 +339,9 @@ void L3SystemInformationType3::writeBody(L3Frame& dest, size_t &wp) const
 | 
				
			|||||||
	LOG(DEBUG) << dest;
 | 
						LOG(DEBUG) << dest;
 | 
				
			||||||
	mRACHControlParameters.writeV(dest,wp);
 | 
						mRACHControlParameters.writeV(dest,wp);
 | 
				
			||||||
	LOG(DEBUG) << dest;
 | 
						LOG(DEBUG) << dest;
 | 
				
			||||||
	if (mHaveRestOctets) mRestOctets.writeV(dest,wp);
 | 
						/*if (mHaveRestOctets)*/ mRestOctets.writeV(dest,wp);
 | 
				
			||||||
	LOG(DEBUG) << dest;
 | 
						LOG(DEBUG) << dest;
 | 
				
			||||||
 | 
						assert(wp-wpstart == fullBodyLength() * 8);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -347,25 +354,75 @@ void L3SystemInformationType3::text(ostream& os) const
 | 
				
			|||||||
	os << " cellOptions=(" << mCellOptions << ")";
 | 
						os << " cellOptions=(" << mCellOptions << ")";
 | 
				
			||||||
	os << " cellSelectionParameters=(" << mCellSelectionParameters << ")";
 | 
						os << " cellSelectionParameters=(" << mCellSelectionParameters << ")";
 | 
				
			||||||
	os << " RACHControlParameters=(" << mRACHControlParameters << ")";
 | 
						os << " RACHControlParameters=(" << mRACHControlParameters << ")";
 | 
				
			||||||
	if (mHaveRestOctets) os << " SI3RO=(" << mRestOctets << ")";
 | 
						/*if (mHaveRestOctets)*/ os << " SI3RO=(" << mRestOctets << ")";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					L3SIType4RestOctets::L3SIType4RestOctets()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#if GPRS_PAT|GPRS_TESTSI4
 | 
				
			||||||
 | 
						mRA_COLOUR = gConfig.getNum("GPRS.RA_COLOUR");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM04.08 sec 10.5.2.35
 | 
				
			||||||
 | 
					void L3SIType4RestOctets::writeV(L3Frame &dest, size_t &wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#if GPRS_PAT|GPRS_TESTSI4
 | 
				
			||||||
 | 
						dest.writeL(wp); // SI4 Rest Octets_O -> Optional selection parameters
 | 
				
			||||||
 | 
						dest.writeL(wp); // SI4 Rest Octets_O -> Optional Power offset
 | 
				
			||||||
 | 
						if (GPRS::GPRSConfig::IsEnabled()) {
 | 
				
			||||||
 | 
							dest.writeH(wp); // SI4 Rest Octets_O -> GPRS Indicator (present)
 | 
				
			||||||
 | 
							dest.writeField(wp,mRA_COLOUR,3);
 | 
				
			||||||
 | 
							dest.write0(wp);	// SI13 message is sent on BCCH Norm schedule.
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							dest.writeL(wp); // SI4 Rest Octets_O -> GPRS Indicator (absent)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						dest.writeL(wp);	// Indicates 'Break Indicator' branch of message.
 | 
				
			||||||
 | 
						dest.writeL(wp);	// Break Indicator == L means no extra info sent in SI Type 7 and 8.
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t L3SIType4RestOctets::lengthBits() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#if GPRS_PAT|GPRS_TESTSI4
 | 
				
			||||||
 | 
						return 1 + 1 + (GPRS::GPRSConfig::IsEnabled() ? 5 : 1) + 1 + 1;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3SIType4RestOctets::writeText(std::ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#if GPRS_PAT|GPRS_TESTSI4
 | 
				
			||||||
 | 
						if (GPRS::GPRSConfig::IsEnabled()) {
 | 
				
			||||||
 | 
							os << "GPRS enabled; RA_COLOUR=(" << mRA_COLOUR << ")";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
L3SystemInformationType4::L3SystemInformationType4()
 | 
					L3SystemInformationType4::L3SystemInformationType4()
 | 
				
			||||||
	:L3RRMessageNRO()
 | 
						:L3RRMessageRO(),
 | 
				
			||||||
 | 
						mHaveCBCH(gConfig.getStr("Control.SMSCB.Table").length() != 0),
 | 
				
			||||||
 | 
						mCBCHChannelDescription(SDCCH_4_2,0,gConfig.getNum("GSM.Identity.BSIC.BCC"),gConfig.getNum("GSM.Radio.C0"))
 | 
				
			||||||
{ }
 | 
					{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
size_t L3SystemInformationType4::l2BodyLength() const
 | 
					size_t L3SystemInformationType4::l2BodyLength() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	size_t len = mLAI.lengthV();
 | 
						size_t len = mLAI.lengthV();
 | 
				
			||||||
	len += mCellSelectionParameters.lengthV();
 | 
						len += mCellSelectionParameters.lengthV();
 | 
				
			||||||
	len += mRACHControlParameters.lengthV();
 | 
						len += mRACHControlParameters.lengthV();
 | 
				
			||||||
 | 
						if (mHaveCBCH) len += mCBCHChannelDescription.lengthTV();
 | 
				
			||||||
	return len;
 | 
						return len;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t L3SystemInformationType4::restOctetsLength() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return mType4RestOctets.lengthV();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void L3SystemInformationType4::writeBody(L3Frame& dest, size_t &wp) const
 | 
					void L3SystemInformationType4::writeBody(L3Frame& dest, size_t &wp) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -375,9 +432,16 @@ void L3SystemInformationType4::writeBody(L3Frame& dest, size_t &wp) const
 | 
				
			|||||||
	- Cell Selection Parameters 10.5.2.4 M V 2 
 | 
						- Cell Selection Parameters 10.5.2.4 M V 2 
 | 
				
			||||||
	- RACH Control Parameters 10.5.2.29 M V 3 
 | 
						- RACH Control Parameters 10.5.2.29 M V 3 
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
						size_t wpstart = wp;
 | 
				
			||||||
	mLAI.writeV(dest,wp);
 | 
						mLAI.writeV(dest,wp);
 | 
				
			||||||
	mCellSelectionParameters.writeV(dest,wp);
 | 
						mCellSelectionParameters.writeV(dest,wp);
 | 
				
			||||||
	mRACHControlParameters.writeV(dest,wp);
 | 
						mRACHControlParameters.writeV(dest,wp);
 | 
				
			||||||
 | 
						if (mHaveCBCH) {
 | 
				
			||||||
 | 
							mCBCHChannelDescription.writeTV(0x64,dest,wp);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mType4RestOctets.writeV(dest,wp);
 | 
				
			||||||
 | 
						while (wp & 7) { dest.writeL(wp); } // Zero to byte boundary.
 | 
				
			||||||
 | 
						assert(wp-wpstart == fullBodyLength() * 8);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -387,8 +451,11 @@ void L3SystemInformationType4::text(ostream& os) const
 | 
				
			|||||||
	os << "LAI=(" << mLAI << ")";
 | 
						os << "LAI=(" << mLAI << ")";
 | 
				
			||||||
	os << " cellSelectionParameters=(" << mCellSelectionParameters << ")";
 | 
						os << " cellSelectionParameters=(" << mCellSelectionParameters << ")";
 | 
				
			||||||
	os << " RACHControlParameters=(" << mRACHControlParameters << ")";
 | 
						os << " RACHControlParameters=(" << mRACHControlParameters << ")";
 | 
				
			||||||
 | 
						if (mHaveCBCH) {
 | 
				
			||||||
 | 
							os << "CBCHChannelDescription=(" << mCBCHChannelDescription << ")";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mType4RestOctets.writeText(os);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -399,6 +466,12 @@ void L3SystemInformationType5::writeBody(L3Frame& dest, size_t &wp) const
 | 
				
			|||||||
	- BCCH Frequency List 10.5.2.22 M V 16 
 | 
						- BCCH Frequency List 10.5.2.22 M V 16 
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
	mBCCHFrequencyList.writeV(dest,wp);
 | 
						mBCCHFrequencyList.writeV(dest,wp);
 | 
				
			||||||
 | 
						wp -= 111;
 | 
				
			||||||
 | 
						int p = gConfig.getFloat("GSM.Cipher.RandomNeighbor") * (float)0xFFFFFF;
 | 
				
			||||||
 | 
						for (unsigned i = 1; i <= 111; i++) {
 | 
				
			||||||
 | 
							int b = ((random() & 0xFFFFFF) < p) | dest.peekField(wp, 1);
 | 
				
			||||||
 | 
							dest.writeField(wp, b, 1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -436,9 +509,9 @@ void L3SystemInformationType6::text(ostream& os) const
 | 
				
			|||||||
	os << " NCCPermitted=(" << mNCCPermitted << ")";
 | 
						os << " NCCPermitted=(" << mNCCPermitted << ")";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
void L3ImmediateAssignment::writeBody( L3Frame &dest, size_t &wp ) const
 | 
					void L3ImmediateAssignment::writeBody( L3Frame &dest, size_t &wp ) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						size_t wpstart = wp;
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	- Page Mode 10.5.2.26 M V 1/2 
 | 
						- Page Mode 10.5.2.26 M V 1/2 
 | 
				
			||||||
	- Dedicated mode or TBF 10.5.2.25b M V 1/2 
 | 
						- Dedicated mode or TBF 10.5.2.25b M V 1/2 
 | 
				
			||||||
@@ -448,14 +521,19 @@ void L3ImmediateAssignment::writeBody( L3Frame &dest, size_t &wp ) const
 | 
				
			|||||||
	(ignoring optional elements)
 | 
						(ignoring optional elements)
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	// reverse order of 1/2-octet fields
 | 
						// reverse order of 1/2-octet fields
 | 
				
			||||||
 | 
						// (pat) Because we are writing MSB first here.
 | 
				
			||||||
	mDedicatedModeOrTBF.writeV(dest, wp);
 | 
						mDedicatedModeOrTBF.writeV(dest, wp);
 | 
				
			||||||
	mPageMode.writeV(dest, wp);
 | 
						mPageMode.writeV(dest, wp);
 | 
				
			||||||
	mChannelDescription.writeV(dest, wp);
 | 
						mChannelDescription.writeV(dest, wp);	// From L3ChannelDescription
 | 
				
			||||||
	mRequestReference.writeV(dest, wp);
 | 
						mRequestReference.writeV(dest, wp);
 | 
				
			||||||
	mTimingAdvance.writeV(dest, wp);
 | 
						mTimingAdvance.writeV(dest, wp);
 | 
				
			||||||
	// No mobile allocation in non-hopping systems.
 | 
						// No mobile allocation in non-hopping systems.
 | 
				
			||||||
	// A zero-length LV.  Just write L=0.
 | 
						// A zero-length LV.  Just write L=0.  (pat) LV, etc. defined in GSM04.07 sec 11.2.1.1
 | 
				
			||||||
	dest.writeField(wp,0,8);
 | 
						dest.writeField(wp,0,8);
 | 
				
			||||||
 | 
						//assert(wp-wpstart == l2BodyLength() * 8);
 | 
				
			||||||
 | 
						// Note: optional starting Time not implemented.
 | 
				
			||||||
 | 
						mIARestOctets.writeBits(dest,wp);
 | 
				
			||||||
 | 
						assert(wp-wpstart == fullBodyLength() * 8);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -466,6 +544,7 @@ void L3ImmediateAssignment::text(ostream& os) const
 | 
				
			|||||||
	os << " ChannelDescription=("<<mChannelDescription<<")";
 | 
						os << " ChannelDescription=("<<mChannelDescription<<")";
 | 
				
			||||||
	os << " RequestReference=("<<mRequestReference<<")";
 | 
						os << " RequestReference=("<<mRequestReference<<")";
 | 
				
			||||||
	os << " TimingAdvance="<<mTimingAdvance;
 | 
						os << " TimingAdvance="<<mTimingAdvance;
 | 
				
			||||||
 | 
						mIARestOctets.text(os); 
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -652,6 +731,7 @@ void L3ApplicationInformation::writeBody( L3Frame &dest, size_t &wp ) const
 | 
				
			|||||||
- APDU Data 10.5.2.50 M LV N
 | 
					- APDU Data 10.5.2.50 M LV N
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
	// reverse order of 1/2-octet fields
 | 
						// reverse order of 1/2-octet fields
 | 
				
			||||||
 | 
						// (pat) Because we are writing MSB first here.
 | 
				
			||||||
	static size_t start = wp;
 | 
						static size_t start = wp;
 | 
				
			||||||
	LOG(DEBUG) << "L3ApplicationInformation: written " << wp - start << " bits";
 | 
						LOG(DEBUG) << "L3ApplicationInformation: written " << wp - start << " bits";
 | 
				
			||||||
	mFlags.writeV(dest, wp);
 | 
						mFlags.writeV(dest, wp);
 | 
				
			||||||
@@ -674,6 +754,7 @@ void L3ApplicationInformation::text(ostream& os) const
 | 
				
			|||||||
void L3ApplicationInformation::parseBody(const L3Frame& src, size_t &rp)
 | 
					void L3ApplicationInformation::parseBody(const L3Frame& src, size_t &rp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// reverse order of 1/2-octet fields
 | 
						// reverse order of 1/2-octet fields
 | 
				
			||||||
 | 
						// (pat) Because we are writing MSB first here.
 | 
				
			||||||
	mFlags.parseV(src, rp);
 | 
						mFlags.parseV(src, rp);
 | 
				
			||||||
	mID.parseV(src, rp);
 | 
						mID.parseV(src, rp);
 | 
				
			||||||
	mData.parseLV(src, rp);
 | 
						mData.parseLV(src, rp);
 | 
				
			||||||
@@ -685,10 +766,34 @@ size_t L3ApplicationInformation::l2BodyLength() const
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 3GPP 44.018 9.1.13b
 | 
				
			||||||
 | 
					// (pat 3-2012) Added parsing.
 | 
				
			||||||
void L3GPRSSuspensionRequest::parseBody(const L3Frame &src, size_t& rp)
 | 
					void L3GPRSSuspensionRequest::parseBody(const L3Frame &src, size_t& rp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// We don't really parse this yet.
 | 
						// The TLLI is what we most need out of this message to identify the MS.
 | 
				
			||||||
	return;
 | 
						// Note that TLLI is not just a simple number; encoding defined in 23.003.
 | 
				
			||||||
 | 
						// We dont worry about it here; the SGSN handles that.
 | 
				
			||||||
 | 
						mTLLI = src.readField(rp,4*8);
 | 
				
			||||||
 | 
						// 3GPP 24.008 10.5.5.15 Routing Area Identification.
 | 
				
			||||||
 | 
						// Similar to L3LocationAreaIdentity but includes RAC too.
 | 
				
			||||||
 | 
						// We dont really care about it now, and when we do, all we will care
 | 
				
			||||||
 | 
						// is if it matches our own or not.
 | 
				
			||||||
 | 
						// Just squirrel away the 6 bytes as a ByteVector.
 | 
				
			||||||
 | 
						// This is an immediate object whose memory will be deleted automatically.
 | 
				
			||||||
 | 
						mRaId = ByteVector(src.segment(rp,6*8));
 | 
				
			||||||
 | 
						rp += 6*8;	// And skip over it.
 | 
				
			||||||
 | 
						mSuspensionCause = src.readField(rp,1*8);	// 10.5.2.47
 | 
				
			||||||
 | 
						// Optional service support, IEI=0x01.
 | 
				
			||||||
 | 
						// It is for MBMS and we dont really care about it, but get it anyway.
 | 
				
			||||||
 | 
						if (rp>=src.size()+2*8 && 0x01 == src.peekField(rp,8)) {
 | 
				
			||||||
 | 
							rp+=8;	// Skip over the IEI type
 | 
				
			||||||
 | 
							mServiceSupport = src.readField(rp,8);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void L3GPRSSuspensionRequest::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						L3RRMessage::text(os);
 | 
				
			||||||
 | 
						os <<LOGVAR(mTLLI)<<LOGVAR(mRaId)<<LOGVAR(mSuspensionCause)<<LOGVAR(mServiceSupport);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -713,4 +818,106 @@ void L3ClassmarkChange::text(ostream& os) const
 | 
				
			|||||||
		os << " +classmark=(" << mAdditionalClassmark << ")";
 | 
							os << " +classmark=(" << mAdditionalClassmark << ")";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t L3HandoverCommand::l2BodyLength() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t sum =
 | 
				
			||||||
 | 
							mCellDescription.lengthV() +
 | 
				
			||||||
 | 
							mChannelDescriptionAfter.lengthV() +
 | 
				
			||||||
 | 
							mHandoverReference.lengthV() +
 | 
				
			||||||
 | 
							mPowerCommandAccessType.lengthV() +
 | 
				
			||||||
 | 
							mSynchronizationIndication.lengthV();
 | 
				
			||||||
 | 
						return sum;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3HandoverCommand::writeBody(L3Frame& frame, size_t& wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mCellDescription.writeV(frame,wp);
 | 
				
			||||||
 | 
						mChannelDescriptionAfter.writeV(frame,wp);
 | 
				
			||||||
 | 
						mHandoverReference.writeV(frame,wp);
 | 
				
			||||||
 | 
						mPowerCommandAccessType.writeV(frame,wp);
 | 
				
			||||||
 | 
						mSynchronizationIndication.writeV(frame,wp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3HandoverCommand::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						L3RRMessage::text(os);
 | 
				
			||||||
 | 
						os << "cell=(" << mCellDescription << ")";
 | 
				
			||||||
 | 
						os << " channelr=(" << mChannelDescriptionAfter << ")";
 | 
				
			||||||
 | 
						os << " ref=" << mHandoverReference;
 | 
				
			||||||
 | 
						os << " powerAndAccess=(" << mPowerCommandAccessType << ")";
 | 
				
			||||||
 | 
						os << " synchronization=(" << mSynchronizationIndication << ")";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3HandoverComplete::parseBody(const L3Frame& frame, size_t& rp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mCause.parseV(frame,rp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3HandoverComplete::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						L3RRMessage::text(os);
 | 
				
			||||||
 | 
						os << "cause=" << mCause;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3HandoverFailure::parseBody(const L3Frame& frame, size_t& rp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mCause.parseV(frame,rp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3HandoverFailure::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						L3RRMessage::text(os);
 | 
				
			||||||
 | 
						os << "cause=" << mCause;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int L3CipheringModeCommand::MTI() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return CipheringModeCommand;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3CipheringModeCommand::writeBody(L3Frame& frame, size_t& wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// reverse order of 1/2-octet fields
 | 
				
			||||||
 | 
						mCipheringResponse.writeV(frame,wp);
 | 
				
			||||||
 | 
						mCipheringModeSetting.writeV(frame,wp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3CipheringModeCommand::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						L3RRMessage::text(os);
 | 
				
			||||||
 | 
						os << "ciphering mode setting=(" << mCipheringModeSetting << ")";
 | 
				
			||||||
 | 
						os << " ciphering response=(" << mCipheringResponse << ")";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int L3CipheringModeComplete::MTI() const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return CipheringModeComplete;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3CipheringModeComplete::parseBody(const L3Frame& frame, size_t& rp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// mobile equipment identity optional
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3CipheringModeComplete::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						L3RRMessage::text(os);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3PhysicalInformation::writeBody(L3Frame& frame, size_t& wp) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mTA.writeV(frame,wp);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void L3PhysicalInformation::text(ostream& os) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						L3RRMessage::text(os);
 | 
				
			||||||
 | 
						os << "TA=" << mTA;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// vim: ts=4 sw=4
 | 
					// vim: ts=4 sw=4
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,24 +4,14 @@
 | 
				
			|||||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2011 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2011 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -35,6 +25,7 @@
 | 
				
			|||||||
#include "GSML3Message.h"
 | 
					#include "GSML3Message.h"
 | 
				
			||||||
#include "GSML3CommonElements.h"
 | 
					#include "GSML3CommonElements.h"
 | 
				
			||||||
#include "GSML3RRElements.h"
 | 
					#include "GSML3RRElements.h"
 | 
				
			||||||
 | 
					#include <ByteVector.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace GSM {
 | 
					namespace GSM {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -42,6 +33,20 @@ namespace GSM {
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
	This a virtual class for L3 messages in the Radio Resource protocol.
 | 
						This a virtual class for L3 messages in the Radio Resource protocol.
 | 
				
			||||||
	These messages are defined in GSM 04.08 9.1.
 | 
						These messages are defined in GSM 04.08 9.1.
 | 
				
			||||||
 | 
						(pat) The GSM 04.08 and 04.18 specs list all these messages together, but say
 | 
				
			||||||
 | 
						nothing about their transport mechanisms, which are wildly different and described elsewhere.
 | 
				
			||||||
 | 
						For example, GPRS Mobility Management messages are handled inside the SGSN,
 | 
				
			||||||
 | 
						which wraps them in an LLC protocol and sends them to the RLC/MAC layer
 | 
				
			||||||
 | 
						(via BSSG and NS protocols, in which these messages reside temporarily)
 | 
				
			||||||
 | 
						which wraps them in an RLC Header, then wraps them with a MAC header,
 | 
				
			||||||
 | 
						before delivering to the radio transmit, which munges them even further.
 | 
				
			||||||
 | 
						And by the way, if the MS is in DTM (Dual Transfer Mode) the MAC layer
 | 
				
			||||||
 | 
						may (or should?) unwrap the L3 Message from its LLC wrapper and send it on a
 | 
				
			||||||
 | 
						dedicated RR message channel instead of sending it via GPRS PDCH.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Note that the message numbers are reused several times in the tables
 | 
				
			||||||
 | 
						for different purposes, for example, MessageTypes for Mobility Management reuse
 | 
				
			||||||
 | 
						numbers in Message Types for Radio Rsource Management.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
class L3RRMessage : public L3Message {
 | 
					class L3RRMessage : public L3Message {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -64,7 +69,7 @@ class L3RRMessage : public L3Message {
 | 
				
			|||||||
		SystemInformationType7=0x1f,
 | 
							SystemInformationType7=0x1f,
 | 
				
			||||||
		SystemInformationType8=0x18,
 | 
							SystemInformationType8=0x18,
 | 
				
			||||||
		SystemInformationType9=0x04,
 | 
							SystemInformationType9=0x04,
 | 
				
			||||||
		SystemInformationType13=0x00,
 | 
							SystemInformationType13=0x00,	// (pat) yes, this is correct.
 | 
				
			||||||
		SystemInformationType16=0x3d,
 | 
							SystemInformationType16=0x3d,
 | 
				
			||||||
		SystemInformationType17=0x3e,
 | 
							SystemInformationType17=0x3e,
 | 
				
			||||||
		//@}
 | 
							//@}
 | 
				
			||||||
@@ -89,10 +94,14 @@ class L3RRMessage : public L3Message {
 | 
				
			|||||||
		///@name Handover
 | 
							///@name Handover
 | 
				
			||||||
		//@{
 | 
							//@{
 | 
				
			||||||
		HandoverCommand=0x2b,
 | 
							HandoverCommand=0x2b,
 | 
				
			||||||
 | 
							HandoverComplete=0x2c,
 | 
				
			||||||
 | 
							HandoverFailure=0x28,
 | 
				
			||||||
 | 
							PhysicalInformation=0x2d,
 | 
				
			||||||
		//@}
 | 
							//@}
 | 
				
			||||||
		///@name ciphering
 | 
							///@name ciphering
 | 
				
			||||||
		//@{
 | 
							//@{
 | 
				
			||||||
		CipheringModeCommand=0x35,
 | 
							CipheringModeCommand=0x35,
 | 
				
			||||||
 | 
							CipheringModeComplete=0x32,
 | 
				
			||||||
		//@}
 | 
							//@}
 | 
				
			||||||
		///@name miscellaneous
 | 
							///@name miscellaneous
 | 
				
			||||||
		//@{
 | 
							//@{
 | 
				
			||||||
@@ -108,12 +117,14 @@ class L3RRMessage : public L3Message {
 | 
				
			|||||||
		//@{
 | 
							//@{
 | 
				
			||||||
		SynchronizationChannelInformation=0x100,
 | 
							SynchronizationChannelInformation=0x100,
 | 
				
			||||||
		ChannelRequest=0x101,
 | 
							ChannelRequest=0x101,
 | 
				
			||||||
 | 
							HandoverAccess=0x102,
 | 
				
			||||||
		//@}
 | 
							//@}
 | 
				
			||||||
		///@name application information - used for RRLP
 | 
							///@name application information - used for RRLP
 | 
				
			||||||
		//@{
 | 
							//@{
 | 
				
			||||||
		ApplicationInformation=0x38,
 | 
							ApplicationInformation=0x38,
 | 
				
			||||||
		//@}
 | 
							//@}
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
						static const char *name(MessageType mt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	L3RRMessage():L3Message() { } 
 | 
						L3RRMessage():L3Message() { } 
 | 
				
			||||||
@@ -129,6 +140,9 @@ std::ostream& operator<<(std::ostream& os, L3RRMessage::MessageType);
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Subclass for L3 RR Messages with no rest octets.  */
 | 
					/** Subclass for L3 RR Messages with no rest octets.  */
 | 
				
			||||||
 | 
					// (pat) L3RRMessagesRO subclass must define:
 | 
				
			||||||
 | 
					//  l2BodyLength - length of body only, and there are no rest octets.
 | 
				
			||||||
 | 
					// 	writeBody() - writes the body.
 | 
				
			||||||
class L3RRMessageNRO : public L3RRMessage {
 | 
					class L3RRMessageNRO : public L3RRMessage {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
@@ -140,6 +154,10 @@ class L3RRMessageNRO : public L3RRMessage {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Subclass for L3 RR messages with rest octets */
 | 
					/** Subclass for L3 RR messages with rest octets */
 | 
				
			||||||
 | 
					// (pat) L3RRMessagesRO subclass must define:
 | 
				
			||||||
 | 
					//  l2BodyLength - length of body only, in bytes
 | 
				
			||||||
 | 
					//  restOctetsLength - length of rest octets only, in bytes.
 | 
				
			||||||
 | 
					// 	writeBody() - writes the body AND the rest octets.
 | 
				
			||||||
class L3RRMessageRO : public L3RRMessage {
 | 
					class L3RRMessageRO : public L3RRMessage {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
@@ -293,7 +311,13 @@ class L3SystemInformationType2 : public L3RRMessageNRO {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	L3SystemInformationType2():L3RRMessageNRO() {}
 | 
						//L3SystemInformationType2():L3RRMessageNRO() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3SystemInformationType2(const std::vector<unsigned>& wARFCNs)
 | 
				
			||||||
 | 
							:L3RRMessageNRO(),
 | 
				
			||||||
 | 
							mBCCHFrequencyList(wARFCNs)
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void BCCHFrequencyList(const L3NeighborCellsDescription& wBCCHFrequencyList)
 | 
						void BCCHFrequencyList(const L3NeighborCellsDescription& wBCCHFrequencyList)
 | 
				
			||||||
		{ mBCCHFrequencyList = wBCCHFrequencyList; }
 | 
							{ mBCCHFrequencyList = wBCCHFrequencyList; }
 | 
				
			||||||
@@ -335,14 +359,16 @@ class L3SystemInformationType3 : public L3RRMessageRO {
 | 
				
			|||||||
	L3CellSelectionParameters mCellSelectionParameters;
 | 
						L3CellSelectionParameters mCellSelectionParameters;
 | 
				
			||||||
	L3RACHControlParameters mRACHControlParameters;
 | 
						L3RACHControlParameters mRACHControlParameters;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool mHaveRestOctets;
 | 
						// (pat) GSM04.08 table 9.32 says RestOctets are mandatory, so why are they optional here?
 | 
				
			||||||
 | 
						// I moved this control into the rest octets and changed the logic.
 | 
				
			||||||
 | 
						//bool mHaveRestOctets;
 | 
				
			||||||
 | 
						// (pat) The rest of the Type3 setup information is in L3SI3RestOctets:
 | 
				
			||||||
	L3SI3RestOctets mRestOctets;
 | 
						L3SI3RestOctets mRestOctets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	L3SystemInformationType3()
 | 
						L3SystemInformationType3()
 | 
				
			||||||
		:L3RRMessageRO(),
 | 
							:L3RRMessageRO()
 | 
				
			||||||
		mHaveRestOctets(gConfig.defines("GSM.SI3RO"))
 | 
					 | 
				
			||||||
	{ }
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void CI(const L3CellIdentity& wCI) { mCI = wCI; }
 | 
						void CI(const L3CellIdentity& wCI) { mCI = wCI; }
 | 
				
			||||||
@@ -372,6 +398,15 @@ class L3SystemInformationType3 : public L3RRMessageRO {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct L3SIType4RestOctets
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned mRA_COLOUR;
 | 
				
			||||||
 | 
						L3SIType4RestOctets();
 | 
				
			||||||
 | 
						void writeV(L3Frame &dest, size_t &wp) const;
 | 
				
			||||||
 | 
						void writeText(std::ostream& os) const;
 | 
				
			||||||
 | 
						size_t lengthBits() const;
 | 
				
			||||||
 | 
						size_t lengthV() const { return (lengthBits()+7)/8; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -379,14 +414,19 @@ class L3SystemInformationType3 : public L3RRMessageRO {
 | 
				
			|||||||
	- Location Area Identification 10.5.1.3 M V 5 
 | 
						- Location Area Identification 10.5.1.3 M V 5 
 | 
				
			||||||
	- Cell Selection Parameters 10.5.2.4 M V 2 
 | 
						- Cell Selection Parameters 10.5.2.4 M V 2 
 | 
				
			||||||
	- RACH Control Parameters 10.5.2.29 M V 3 
 | 
						- RACH Control Parameters 10.5.2.29 M V 3 
 | 
				
			||||||
 | 
						pats note: We included the GPRS Indicator in the rest octets for SI3, so I am assuming
 | 
				
			||||||
 | 
						we do not also have to included it in SI4.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
class L3SystemInformationType4 : public L3RRMessageNRO {
 | 
					class L3SystemInformationType4 : public L3RRMessageRO {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private:
 | 
						private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	L3LocationAreaIdentity mLAI;
 | 
						L3LocationAreaIdentity mLAI;
 | 
				
			||||||
	L3CellSelectionParameters mCellSelectionParameters;
 | 
						L3CellSelectionParameters mCellSelectionParameters;
 | 
				
			||||||
	L3RACHControlParameters mRACHControlParameters;
 | 
						L3RACHControlParameters mRACHControlParameters;
 | 
				
			||||||
 | 
						bool mHaveCBCH;
 | 
				
			||||||
 | 
						L3ChannelDescription mCBCHChannelDescription;
 | 
				
			||||||
 | 
						L3SIType4RestOctets mType4RestOctets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -403,7 +443,7 @@ class L3SystemInformationType4 : public L3RRMessageNRO {
 | 
				
			|||||||
	int MTI() const { return (int)SystemInformationType4; }
 | 
						int MTI() const { return (int)SystemInformationType4; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size_t l2BodyLength() const;
 | 
						size_t l2BodyLength() const;
 | 
				
			||||||
 | 
						size_t restOctetsLength() const;
 | 
				
			||||||
	void writeBody(L3Frame &dest, size_t &wp) const;
 | 
						void writeBody(L3Frame &dest, size_t &wp) const;
 | 
				
			||||||
	void text(std::ostream&) const;
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@@ -423,7 +463,12 @@ class L3SystemInformationType5 : public L3RRMessageNRO {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	L3SystemInformationType5():L3RRMessageNRO() { }
 | 
						//L3SystemInformationType5():L3RRMessageNRO() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3SystemInformationType5(const std::vector<unsigned>& wARFCNs)
 | 
				
			||||||
 | 
							:L3RRMessageNRO(),
 | 
				
			||||||
 | 
							mBCCHFrequencyList(wARFCNs)
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void BCCHFrequencyList(const L3NeighborCellsDescription& wBCCHFrequencyList)
 | 
						void BCCHFrequencyList(const L3NeighborCellsDescription& wBCCHFrequencyList)
 | 
				
			||||||
		{ mBCCHFrequencyList = wBCCHFrequencyList; }
 | 
							{ mBCCHFrequencyList = wBCCHFrequencyList; }
 | 
				
			||||||
@@ -477,38 +522,114 @@ class L3SystemInformationType6 : public L3RRMessageNRO {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GSM 04.08 10.5.2.16
 | 
				
			||||||
 | 
					struct L3IARestOctets : public GenericMessageElement
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Someday this may include frequence parameters, but now all it can be is Packet Assignment.
 | 
				
			||||||
 | 
						struct L3IAPacketAssignment mPacketAssignment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t lengthBits() const {
 | 
				
			||||||
 | 
							return mPacketAssignment.lengthBits();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void writeBits(L3Frame &dest, size_t &wp) const {
 | 
				
			||||||
 | 
							mPacketAssignment.writeBits(dest, wp);
 | 
				
			||||||
 | 
							while (wp & 7) { dest.writeL(wp); }	// fill out to byte boundary.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void text(std::ostream& os) const {
 | 
				
			||||||
 | 
							mPacketAssignment.text(os);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Immediate Assignment, GSM 04.08 9.1.18 */
 | 
					/** Immediate Assignment, GSM 04.08 9.1.18 */
 | 
				
			||||||
class L3ImmediateAssignment : public L3RRMessageNRO {
 | 
					// (pat) The elements below correspond directly to the parts of the Immediate Assignment
 | 
				
			||||||
 | 
					// message content as defined in 9.1.18, table 9.18.
 | 
				
			||||||
 | 
					// (pat) some deobfuscation.
 | 
				
			||||||
 | 
					//	// Example from Control::AccessGrantResponder()
 | 
				
			||||||
 | 
					//	L3ImmediateAssignment assign(	// Initialization of assign message
 | 
				
			||||||
 | 
					//		L3RequestReference(
 | 
				
			||||||
 | 
					//			unsigned RA,	// => L3RequestReference::mRA
 | 
				
			||||||
 | 
					//			GSM::Time &when // => L3RequestReference::mT1p,mT2,mT3
 | 
				
			||||||
 | 
					//		),	// {/*empty body*/}
 | 
				
			||||||
 | 
					//		LogicalChannel *LCH->channelDescription(),
 | 
				
			||||||
 | 
					//			// returns L3ChannelDescription(
 | 
				
			||||||
 | 
					//			//	LCH->mL1.typeAndOffset(), // => L3ChannelDescription::mTypeAndOffset;
 | 
				
			||||||
 | 
					//			//	LCH->mL1.TN(),		// => L3ChannelDescription::mTN
 | 
				
			||||||
 | 
					//			//	LCH->mL1.TSC(), 	// => L3ChannelDescription::mTSC
 | 
				
			||||||
 | 
					//			//	LCH->mL1.ARFCN())	// => L3ChannelDescription::mARFCN;
 | 
				
			||||||
 | 
					//			//	{ L3ChannelDescription::mHFlag = 0, ::mMAIO = 0, ::mHSN = 0 }
 | 
				
			||||||
 | 
					//		L3TimingAdvance(
 | 
				
			||||||
 | 
					//			int initialTA	// => L3TimingAdvance::mTimingAdvance
 | 
				
			||||||
 | 
					//			// L3TimingAdvance : L3ProtocolElement() [virtual class, no constructor]
 | 
				
			||||||
 | 
					//		)
 | 
				
			||||||
 | 
					//	);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class L3ImmediateAssignment : public L3RRMessageRO
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	L3PageMode mPageMode;
 | 
						L3PageMode mPageMode;
 | 
				
			||||||
	L3DedicatedModeOrTBF mDedicatedModeOrTBF;
 | 
						L3DedicatedModeOrTBF mDedicatedModeOrTBF;
 | 
				
			||||||
	L3RequestReference mRequestReference;
 | 
						L3RequestReference mRequestReference;
 | 
				
			||||||
 | 
						// (pat) Note that either "Channel Description" or "Packet Channel Description"
 | 
				
			||||||
 | 
						// appears in the message.  They are identical except for some new options
 | 
				
			||||||
 | 
						// added to Packet Channel Description.
 | 
				
			||||||
	L3ChannelDescription mChannelDescription;  
 | 
						L3ChannelDescription mChannelDescription;  
 | 
				
			||||||
	L3TimingAdvance mTimingAdvance;
 | 
						L3TimingAdvance mTimingAdvance;
 | 
				
			||||||
 | 
						// Used for Packet uplink/downlink assignment messages, which, when transmitted
 | 
				
			||||||
 | 
						// on CCCH, appear in the rest octets of the Immediate Assignment message.
 | 
				
			||||||
 | 
						struct L3IARestOctets mIARestOctets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
 | 
						size_t restOctetsLength() const { return (mIARestOctets.lengthBits()+7)/8; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	L3ImmediateAssignment(
 | 
						L3ImmediateAssignment(
 | 
				
			||||||
				const L3RequestReference& wRequestReference,
 | 
									const L3RequestReference& wRequestReference,
 | 
				
			||||||
				const L3ChannelDescription& wChannelDescription,
 | 
									const L3ChannelDescription& wChannelDescription,
 | 
				
			||||||
				const L3TimingAdvance& wTimingAdvance = L3TimingAdvance(0))
 | 
									const L3TimingAdvance& wTimingAdvance = L3TimingAdvance(0),
 | 
				
			||||||
		:L3RRMessageNRO(),
 | 
									const bool forTBF = false, bool wDownlink = false)
 | 
				
			||||||
 | 
							:L3RRMessageRO(),
 | 
				
			||||||
 | 
							mPageMode(0),
 | 
				
			||||||
 | 
							mDedicatedModeOrTBF(forTBF,wDownlink),
 | 
				
			||||||
		mRequestReference(wRequestReference),
 | 
							mRequestReference(wRequestReference),
 | 
				
			||||||
		mChannelDescription(wChannelDescription),
 | 
							mChannelDescription(wChannelDescription),
 | 
				
			||||||
		mTimingAdvance(wTimingAdvance)
 | 
							mTimingAdvance(wTimingAdvance)
 | 
				
			||||||
	{}
 | 
						{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int MTI() const { return (int)ImmediateAssignment; }
 | 
						int MTI() const { return (int)ImmediateAssignment; }
 | 
				
			||||||
	size_t l2BodyLength() const { return 9; }
 | 
						size_t l2BodyLength() const {
 | 
				
			||||||
 | 
							// 1/2: page mode
 | 
				
			||||||
 | 
							// 1/2: Dedicated mode or TBF
 | 
				
			||||||
 | 
							// 3: channel description or packet channel description 
 | 
				
			||||||
 | 
							// 3: request reference
 | 
				
			||||||
 | 
							// 1: timing advance
 | 
				
			||||||
 | 
							// 1-9: Mobile Allocation (not implemented, so just 1 byte)
 | 
				
			||||||
 | 
							// 0: starting time, not implemented
 | 
				
			||||||
 | 
							// = 9 total bytes.
 | 
				
			||||||
 | 
							return 9;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// (pat) Return the PacketAssignment part of the message for the client to fill in.
 | 
				
			||||||
 | 
						// I did it this way to cause the least change to the preexisting L3ImmediateAssignment class.
 | 
				
			||||||
 | 
						struct L3IAPacketAssignment *packetAssign() { return &mIARestOctets.mPacketAssignment; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// (pat) writeBody called via:
 | 
				
			||||||
 | 
						// CCCHLogicalChannel:send(L3RRMessage msg)
 | 
				
			||||||
 | 
						// calls L3FrameFIFO mq.L3FrameFIFO::write(new L3Frame(L3Message msg,UNIT_DATA));
 | 
				
			||||||
 | 
						// L3Frame::L3Frame(L3Message&,Primitive) : BitVector(msg.bitsNeeded)
 | 
				
			||||||
 | 
						//                      mPrimitive(wPrimitive),
 | 
				
			||||||
 | 
						//                      mL2Length(wPrimitive) { L3Message msg.write(); }
 | 
				
			||||||
 | 
						// L3Message::write() calls writeBody()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void writeBody(L3Frame &dest, size_t &wp) const;
 | 
						void writeBody(L3Frame &dest, size_t &wp) const;
 | 
				
			||||||
	void text(std::ostream&) const;
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -549,12 +670,17 @@ class L3ChannelRelease : public L3RRMessageNRO {
 | 
				
			|||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	L3RRCause mRRCause;
 | 
						L3RRCause mRRCause;
 | 
				
			||||||
 | 
						// 3GPP 44.018 10.5.2.14c GPRS Resumption.
 | 
				
			||||||
 | 
						// It is one bit to specify to the MS whether GPRS services should resume.
 | 
				
			||||||
 | 
						// Kinda important.
 | 
				
			||||||
 | 
						bool mGprsResumptionPresent;
 | 
				
			||||||
 | 
						bool mGprsResumptionBit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	/** The default cause is 0x0, "normal event". */
 | 
						/** The default cause is 0x0, "normal event". */
 | 
				
			||||||
	L3ChannelRelease(const L3RRCause& cause = L3RRCause(0x0))
 | 
						L3ChannelRelease(const L3RRCause& cause = L3RRCause(0x0))
 | 
				
			||||||
		:L3RRMessageNRO(),mRRCause(cause)
 | 
							:L3RRMessageNRO(),mRRCause(cause),mGprsResumptionPresent(0)
 | 
				
			||||||
	{}
 | 
						{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int MTI() const { return (int) ChannelRelease; }
 | 
						int MTI() const { return (int) ChannelRelease; }
 | 
				
			||||||
@@ -819,16 +945,37 @@ class L3ApplicationInformation : public L3RRMessageNRO {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** GSM 04.08 9.1.13b */
 | 
					/** GSM 04.08 (or 04.18) 9.1.13b */
 | 
				
			||||||
class L3GPRSSuspensionRequest : public L3RRMessageNRO {
 | 
					// 44.018 3.4.25 GPRS Suspension procedure.
 | 
				
			||||||
 | 
					// 44.018 3.4.13 RR Connection releasee procedure - GPRS resumption if includes:
 | 
				
			||||||
 | 
					// 10.5.2.14c GPRS Resumption IEI
 | 
				
			||||||
 | 
					class L3GPRSSuspensionRequest : public L3RRMessageNRO
 | 
				
			||||||
 | 
					{	public:
 | 
				
			||||||
 | 
						// The TLLI is what we most need out of this message to identify the MS.
 | 
				
			||||||
 | 
						// Note that TLLI is not just a simple number; encoding defined in 23.003.
 | 
				
			||||||
 | 
						// We dont worry about it here; the SGSN handles that.
 | 
				
			||||||
 | 
						uint32_t mTLLI;
 | 
				
			||||||
 | 
						// 3GPP 24.008 10.5.5.15 Routing Area Identification.
 | 
				
			||||||
 | 
						// Similar to L3LocationAreaIdentity but includes RAC too.
 | 
				
			||||||
 | 
						// We dont really care about it now, and when we do, all we will care
 | 
				
			||||||
 | 
						// is if it matches our own or not, so just squirrel away the 6 bytes as a ByteVector.
 | 
				
			||||||
 | 
						// This is an immediate object whose memory will be deleted automatically.
 | 
				
			||||||
 | 
						ByteVector mRaId;
 | 
				
			||||||
 | 
						// Suspension cause 44.018 10.5.2.47.
 | 
				
			||||||
 | 
						// Can be 0: mobile originated call, 1: Location area update, 2: SMS, or others
 | 
				
			||||||
 | 
						uint8_t mSuspensionCause;
 | 
				
			||||||
 | 
						// Optional service support, IEI=0x01.
 | 
				
			||||||
 | 
						// It is for MBMS and we dont really care about it, but get it anyway.
 | 
				
			||||||
 | 
						uint8_t mServiceSupport;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						// Must init only the optional elements:
 | 
				
			||||||
 | 
						L3GPRSSuspensionRequest() : mServiceSupport(0) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int MTI() const { return (int) GPRSSuspensionRequest; }
 | 
						int MTI() const { return (int) GPRSSuspensionRequest; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size_t l2BodyLength() const { return 11; }
 | 
						size_t l2BodyLength() const { return 11; }	// This is uplink only so not relevant.
 | 
				
			||||||
 | 
					 | 
				
			||||||
	void parseBody(const L3Frame&, size_t&);
 | 
						void parseBody(const L3Frame&, size_t&);
 | 
				
			||||||
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -865,9 +1012,209 @@ class L3ClassmarkChange : public L3RRMessageNRO {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 9.1.43a */
 | 
				
			||||||
 | 
					// (pat) Someone kindly added this before I got here...
 | 
				
			||||||
 | 
					// SI13 includes GPRS Cell Options in its rest octets,
 | 
				
			||||||
 | 
					// so we broadcast it on BCCH if GPRS is supported.
 | 
				
			||||||
 | 
					class L3SystemInformationType13 : public L3RRMessageRO {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3SI13RestOctets mRestOctets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3SystemInformationType13()
 | 
				
			||||||
 | 
							:L3RRMessageRO()
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int MTI() const { return (int)SystemInformationType13; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t l2BodyLength() const { return 0; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t restOctetsLength() const { return mRestOctets.lengthV(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void writeBody(L3Frame &dest, size_t &wp) const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							size_t wpstart = wp;
 | 
				
			||||||
 | 
							mRestOctets.writeV(dest,wp);
 | 
				
			||||||
 | 
							assert(wp-wpstart == fullBodyLength() * 8);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void text(std::ostream& os) const
 | 
				
			||||||
 | 
							{ L3RRMessage::text(os); os << mRestOctets; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class L3HandoverCommand : public L3RRMessageNRO {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3CellDescription mCellDescription;
 | 
				
			||||||
 | 
						L3ChannelDescription2 mChannelDescriptionAfter;
 | 
				
			||||||
 | 
						L3HandoverReference mHandoverReference;
 | 
				
			||||||
 | 
						L3PowerCommandAndAccessType mPowerCommandAccessType;
 | 
				
			||||||
 | 
						L3SynchronizationIndication mSynchronizationIndication;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3HandoverCommand(const L3CellDescription& wCellDescription,
 | 
				
			||||||
 | 
							const L3ChannelDescription2 wChannelDescriptionAfter,
 | 
				
			||||||
 | 
							const L3HandoverReference& wHandoverReference,
 | 
				
			||||||
 | 
							const L3PowerCommandAndAccessType& wPowerCommandAccessType,
 | 
				
			||||||
 | 
							const L3SynchronizationIndication& wSynchronizationIndication)
 | 
				
			||||||
 | 
							:L3RRMessageNRO(),
 | 
				
			||||||
 | 
							mCellDescription(wCellDescription),
 | 
				
			||||||
 | 
							mChannelDescriptionAfter(wChannelDescriptionAfter),
 | 
				
			||||||
 | 
							mHandoverReference(wHandoverReference),
 | 
				
			||||||
 | 
							mPowerCommandAccessType(wPowerCommandAccessType),
 | 
				
			||||||
 | 
							mSynchronizationIndication(wSynchronizationIndication)
 | 
				
			||||||
 | 
							{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int MTI() const { return (int) HandoverCommand; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t l2BodyLength() const;
 | 
				
			||||||
 | 
						void writeBody(L3Frame&, size_t&) const;
 | 
				
			||||||
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 9.1.16 */
 | 
				
			||||||
 | 
					class L3HandoverComplete : public L3RRMessageNRO {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3RRCause mCause;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int MTI() const { return (int) HandoverComplete; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t l2BodyLength() const { return mCause.lengthV(); }
 | 
				
			||||||
 | 
						void parseBody(const L3Frame&, size_t&);
 | 
				
			||||||
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const L3RRCause& cause() const { return mCause; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 9.1.17 */
 | 
				
			||||||
 | 
					class L3HandoverFailure : public L3RRMessageNRO {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3RRCause mCause;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int MTI() const { return (int) HandoverFailure; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t l2BodyLength() const { return mCause.lengthV(); }
 | 
				
			||||||
 | 
						void parseBody(const L3Frame&, size_t&);
 | 
				
			||||||
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const L3RRCause& cause() const { return mCause; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 9.1.9 */
 | 
				
			||||||
 | 
					class L3CipheringModeCommand : public L3RRMessageNRO {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3CipheringModeSetting mCipheringModeSetting;
 | 
				
			||||||
 | 
						L3CipheringModeResponse mCipheringResponse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3CipheringModeCommand(L3CipheringModeSetting wCipheringModeSetting, L3CipheringModeResponse wCipheringResponse)
 | 
				
			||||||
 | 
							: mCipheringModeSetting(wCipheringModeSetting),
 | 
				
			||||||
 | 
							mCipheringResponse(wCipheringResponse)
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int MTI() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t l2BodyLength() const { return 1; }
 | 
				
			||||||
 | 
						void writeBody(L3Frame&, size_t&) const;
 | 
				
			||||||
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 9.1.10 */
 | 
				
			||||||
 | 
					class L3CipheringModeComplete : public L3RRMessageNRO {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int MTI() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t l2BodyLength() const { return 0; }
 | 
				
			||||||
 | 
						void parseBody(const L3Frame&, size_t&);
 | 
				
			||||||
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** GSM 04.08 9.1.28 */
 | 
				
			||||||
 | 
					class L3PhysicalInformation : public L3RRMessageNRO {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3TimingAdvance mTA;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3PhysicalInformation(const L3TimingAdvance& wTA)
 | 
				
			||||||
 | 
							:L3RRMessageNRO(),
 | 
				
			||||||
 | 
							mTA(wTA)
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int MTI() const { return (int) PhysicalInformation; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t l2BodyLength() const { return mTA.lengthV(); }
 | 
				
			||||||
 | 
						void writeBody(L3Frame&, size_t&) const;
 | 
				
			||||||
 | 
						void text(std::ostream&) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
						GSM 04.08 9.1.14
 | 
				
			||||||
 | 
						This messages has no parse or write methods, but is used to
 | 
				
			||||||
 | 
						transfer information from L1 to the control layer.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					class L3HandoverAccess : public L3RRMessageNRO {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned mReference;
 | 
				
			||||||
 | 
						float mTimingError;
 | 
				
			||||||
 | 
						float mRSSI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						L3HandoverAccess(unsigned wReference, unsigned wTimingError, float wRSSI)
 | 
				
			||||||
 | 
							:L3RRMessageNRO(),
 | 
				
			||||||
 | 
							mReference(wReference),mTimingError(wTimingError),mRSSI(wRSSI)
 | 
				
			||||||
 | 
						{ }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int MTI() const { return (int) HandoverAccess; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t l2BodyLength() const { return 1; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned reference() const { return mReference; }
 | 
				
			||||||
 | 
						float timingError() const { return mTimingError; }
 | 
				
			||||||
 | 
						float RSSI() const { return mRSSI; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // GSM
 | 
					} // GSM
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
// vim: ts=4 sw=4
 | 
					// vim: ts=4 sw=4
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,28 +1,20 @@
 | 
				
			|||||||
/**@file Logical Channel.  */
 | 
					/**@file Logical Channel.  */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
* Copyright 2011 Range Networks, Inc.
 | 
					* Copyright 2011 Range Networks, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -31,15 +23,16 @@
 | 
				
			|||||||
#include "GSML3RRElements.h"
 | 
					#include "GSML3RRElements.h"
 | 
				
			||||||
#include "GSML3Message.h"
 | 
					#include "GSML3Message.h"
 | 
				
			||||||
#include "GSML3RRMessages.h"
 | 
					#include "GSML3RRMessages.h"
 | 
				
			||||||
 | 
					#include "GSMSMSCBL3Messages.h"
 | 
				
			||||||
#include "GSMLogicalChannel.h"
 | 
					#include "GSMLogicalChannel.h"
 | 
				
			||||||
#include "GSMConfig.h"
 | 
					#include "GSMConfig.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <TransactionTable.h>
 | 
					#include <TransactionTable.h>
 | 
				
			||||||
#include <SMSControl.h>
 | 
					#include <SMSControl.h>
 | 
				
			||||||
#include <ControlCommon.h>
 | 
					#include <ControlCommon.h>
 | 
				
			||||||
 | 
					#include "GPRSExport.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <Logger.h>
 | 
					#include <Logger.h>
 | 
				
			||||||
#undef WARNING
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace std;
 | 
					using namespace std;
 | 
				
			||||||
using namespace GSM;
 | 
					using namespace GSM;
 | 
				
			||||||
@@ -49,10 +42,14 @@ using namespace GSM;
 | 
				
			|||||||
void LogicalChannel::open()
 | 
					void LogicalChannel::open()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	LOG(INFO);
 | 
						LOG(INFO);
 | 
				
			||||||
 | 
						LOG(DEBUG);
 | 
				
			||||||
	if (mSACCH) mSACCH->open();
 | 
						if (mSACCH) mSACCH->open();
 | 
				
			||||||
	if (mL1) mL1->open();
 | 
						LOG(DEBUG);
 | 
				
			||||||
 | 
						if (mL1) mL1->open();		// (pat) L1FEC::open()
 | 
				
			||||||
 | 
						LOG(DEBUG);
 | 
				
			||||||
	for (int s=0; s<4; s++) {
 | 
						for (int s=0; s<4; s++) {
 | 
				
			||||||
		if (mL2[s]) mL2[s]->open();
 | 
							if (mL2[s]) mL2[s]->open();
 | 
				
			||||||
 | 
							LOG(DEBUG) << "SAPI=" << s << " open complete";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Empty any stray transactions in the FIFO from the SIP layer.
 | 
						// Empty any stray transactions in the FIFO from the SIP layer.
 | 
				
			||||||
	while (true) {
 | 
						while (true) {
 | 
				
			||||||
@@ -61,9 +58,11 @@ void LogicalChannel::open()
 | 
				
			|||||||
		LOG(WARNING) << "flushing stray transaction " << *trans;
 | 
							LOG(WARNING) << "flushing stray transaction " << *trans;
 | 
				
			||||||
		// FIXME -- Shouldn't we be deleting these?
 | 
							// FIXME -- Shouldn't we be deleting these?
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						LOG(DEBUG);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (pat) This is connecting layer2, not layer1.
 | 
				
			||||||
void LogicalChannel::connect()
 | 
					void LogicalChannel::connect()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	mMux.downstream(mL1);
 | 
						mMux.downstream(mL1);
 | 
				
			||||||
@@ -75,9 +74,11 @@ void LogicalChannel::connect()
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (pat) This is only called during initialization, using the createCombination*() functions.
 | 
				
			||||||
 | 
					// The L1FEC->downstream hooks the radio to this logical channel, permanently.
 | 
				
			||||||
void LogicalChannel::downstream(ARFCNManager* radio)
 | 
					void LogicalChannel::downstream(ARFCNManager* radio)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	assert(mL1);
 | 
						assert(mL1);	// This is L1FEC
 | 
				
			||||||
	mL1->downstream(radio);
 | 
						mL1->downstream(radio);
 | 
				
			||||||
	if (mSACCH) mSACCH->downstream(radio);
 | 
						if (mSACCH) mSACCH->downstream(radio);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -115,23 +116,68 @@ void CCCHLogicalChannel::open()
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (pat) BUG TODO: TO WHOM IT MAY CONCERN:
 | 
				
			||||||
 | 
					// I am not sure this routine works properly.  If there is no CCCH message (an L3Frame)
 | 
				
			||||||
 | 
					// in the queue immediately after the previous frame is sent, an idle frame is inserted.
 | 
				
			||||||
 | 
					// If a subsequent valid CCCH message (paging response or MS initiated RR call or packet
 | 
				
			||||||
 | 
					// uplink request) arrives it will be blocked until the idle frame is sent.
 | 
				
			||||||
 | 
					// Probably doesnt matter for RR establishment, but for packets, the extra 1/4 sec
 | 
				
			||||||
 | 
					// delay (length of a 51-multiframe) is going to hurt.
 | 
				
			||||||
 | 
					// Note that a GPRS Immediate Assignment message must know when this CCCH gets sent.
 | 
				
			||||||
 | 
					// Right now, it has to guess.
 | 
				
			||||||
 | 
					// pats TODO: Send the transceiver an idle frame rather than doing it here.
 | 
				
			||||||
 | 
					// This should be architecturally changed to a pull-system instead of push.
 | 
				
			||||||
 | 
					// Among other things, that would let us prioritize the responses
 | 
				
			||||||
 | 
					// (eg, emergency calls go first) and let the packet Immediate Assignment message be
 | 
				
			||||||
 | 
					// created right before being sent, when we are certain when the
 | 
				
			||||||
 | 
					// Immediate Assignment is being sent.
 | 
				
			||||||
void CCCHLogicalChannel::serviceLoop()
 | 
					void CCCHLogicalChannel::serviceLoop()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// build the idle frame
 | 
						// build the idle frame
 | 
				
			||||||
	static const L3PagingRequestType1 filler;
 | 
						static const L3PagingRequestType1 filler;
 | 
				
			||||||
	static const L3Frame idleFrame(filler,UNIT_DATA);
 | 
						static const L3Frame idleFrame(filler,UNIT_DATA);
 | 
				
			||||||
 | 
					#if ENABLE_PAGING_CHANNELS
 | 
				
			||||||
 | 
						L3ControlChannelDescription mCC;
 | 
				
			||||||
 | 
						unsigned bs_pa_mfrms = mCC.getBS_PA_MFRMS();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	// prime the first idle frame
 | 
						// prime the first idle frame
 | 
				
			||||||
	LogicalChannel::send(idleFrame);
 | 
						LogicalChannel::send(idleFrame);
 | 
				
			||||||
	// run the loop
 | 
						// run the loop
 | 
				
			||||||
	while (true) {
 | 
						while (true) {
 | 
				
			||||||
		L3Frame* frame = mQ.read();
 | 
							L3Frame* frame = NULL;
 | 
				
			||||||
 | 
					#if ENABLE_PAGING_CHANNELS
 | 
				
			||||||
 | 
							// Check for paging message for this specific paging slot first,
 | 
				
			||||||
 | 
							// and if none, send any message in the mQ.
 | 
				
			||||||
 | 
							// The multiframe paging logic is from GSM 05.02 6.5.3.
 | 
				
			||||||
 | 
							// See documentation at crackPagingFromImsi() which is used to
 | 
				
			||||||
 | 
							// get the messages into the proper mPagingQ.
 | 
				
			||||||
 | 
							GSM::Time next = getNextWriteTime();
 | 
				
			||||||
 | 
							unsigned multiframe_index = (next.FN() / 51) % bs_pa_mfrms;
 | 
				
			||||||
 | 
							frame = mPagingQ[multiframe_index].read();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							if (frame == NULL) {
 | 
				
			||||||
 | 
								frame = mQ.read();	// (pat) This is a blocking read; mQ is an InterThreadQueue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if (frame) {
 | 
							if (frame) {
 | 
				
			||||||
 | 
								// (pat) This tortuously calls XCCCHL1Encoder::transmit (see my documentation
 | 
				
			||||||
 | 
								// at LogicalChannel::send), which blocks until L1Encoder::mPrevWriteTime.
 | 
				
			||||||
 | 
								// Note: The q size is 0 while we are blocked here, so if we are trying
 | 
				
			||||||
 | 
								// to determine the next write time by adding the qsize, we are way off.
 | 
				
			||||||
 | 
								// Thats why there is an mWaitingToSend flag.
 | 
				
			||||||
 | 
								mWaitingToSend = true;	// Waiting to send this block at mNextWriteTime.
 | 
				
			||||||
			LogicalChannel::send(*frame);
 | 
								LogicalChannel::send(*frame);
 | 
				
			||||||
 | 
								mWaitingToSend = false;
 | 
				
			||||||
			OBJLOG(DEBUG) << "CCCHLogicalChannel::serviceLoop sending " << *frame;
 | 
								OBJLOG(DEBUG) << "CCCHLogicalChannel::serviceLoop sending " << *frame;
 | 
				
			||||||
			delete frame;
 | 
								delete frame;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (mQ.size()==0) {
 | 
							if (mQ.size()==0) {
 | 
				
			||||||
 | 
								// (pat) The radio continues to send the last frame forever,
 | 
				
			||||||
 | 
								// so we only send one idle frame here.
 | 
				
			||||||
 | 
								// Unfortunately, this slows the response.
 | 
				
			||||||
 | 
								// TODO: Send a static idle frame to the Transciever and rewrite this.
 | 
				
			||||||
 | 
								mWaitingToSend = true;	// Waiting to send an idle frame at mNextWriteTime.
 | 
				
			||||||
			LogicalChannel::send(idleFrame);
 | 
								LogicalChannel::send(idleFrame);
 | 
				
			||||||
 | 
								mWaitingToSend = false;
 | 
				
			||||||
			OBJLOG(DEBUG) << "CCCHLogicalChannel::serviceLoop sending idle frame";
 | 
								OBJLOG(DEBUG) << "CCCHLogicalChannel::serviceLoop sending idle frame";
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -144,6 +190,77 @@ void *GSM::CCCHLogicalChannelServiceLoopAdapter(CCCHLogicalChannel* chan)
 | 
				
			|||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if ENABLE_PAGING_CHANNELS
 | 
				
			||||||
 | 
					// (pat) This routine is going to be entirely replaced with one that works better for gprs.
 | 
				
			||||||
 | 
					// In the meantime, just return a number that is large enough to cover
 | 
				
			||||||
 | 
					// the worst case, which assumes that the messages in mQ also
 | 
				
			||||||
 | 
					// must go out on the paging timeslot.
 | 
				
			||||||
 | 
					Time GSM::CCCHLogicalChannel::getNextPchSendTime(unsigned multiframe_index)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						L3ControlChannelDescription mCC;
 | 
				
			||||||
 | 
						// Paging is distributed over this many multi-frames.
 | 
				
			||||||
 | 
						unsigned bs_pa_mfrms = mCC.getBS_PA_MFRMS();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GSM::Time next = getNextWriteTime();
 | 
				
			||||||
 | 
						unsigned next_multiframe_index = (next.FN() / 51) % bs_pa_mfrms;
 | 
				
			||||||
 | 
						assert(bs_pa_mfrms > 1);
 | 
				
			||||||
 | 
						assert(multiframe_index < bs_pa_mfrms);
 | 
				
			||||||
 | 
						assert(next_multiframe_index < bs_pa_mfrms);
 | 
				
			||||||
 | 
						int achload = mQ.size();
 | 
				
			||||||
 | 
						if (mWaitingToSend) { achload++; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Total wait time is time needed to empty queue, plus the time until the first
 | 
				
			||||||
 | 
						// paging opportunity, plus 2 times the number of guys waiting in the paging queue,
 | 
				
			||||||
 | 
						// but it is all nonsense because if a new agch comes in,
 | 
				
			||||||
 | 
						// it will displace the paging message because the q is sent first.
 | 
				
			||||||
 | 
						// This just needs to be totally redone, and the best way is not to figure out
 | 
				
			||||||
 | 
						// when the message will be sent at all, but rather use a call-back to gprs
 | 
				
			||||||
 | 
						// just before the message is finally sent.
 | 
				
			||||||
 | 
						int multiframesToWait = 0;
 | 
				
			||||||
 | 
						if (achload) {
 | 
				
			||||||
 | 
							multiframesToWait = bs_pa_mfrms - 1;	// Assume worst case.
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// If there is nothing else waiting, we can estimate better:
 | 
				
			||||||
 | 
							while (next_multiframe_index != multiframe_index) {
 | 
				
			||||||
 | 
								multiframe_index = (multiframe_index+1) % bs_pa_mfrms;
 | 
				
			||||||
 | 
								multiframesToWait++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						int total = achload + multiframesToWait + bs_pa_mfrms * mPagingQ[multiframe_index].size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int fnresult = (next.FN() + total * 51) % gHyperframe;
 | 
				
			||||||
 | 
						GSM::Time result(fnresult);
 | 
				
			||||||
 | 
						LOG(DEBUG) << "CCCHLogicalChannel::getNextSend="<< next.FN()
 | 
				
			||||||
 | 
							<<" load="<<achload<<LOGVAR(mWaitingToSend) <<" now="<<gBTS.time().FN()<<LOGVAR(fnresult);
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Time GSM::CCCHLogicalChannel::getNextMsgSendTime() {
 | 
				
			||||||
 | 
						// Get the current frame.
 | 
				
			||||||
 | 
						// DAB GPRS - This should call L1->resync() first, otherwise, in an idle system,
 | 
				
			||||||
 | 
						// DAB GPRS - you can get times well into the past..
 | 
				
			||||||
 | 
						// (pat) Above is done in the underlying getNextWriteTime()
 | 
				
			||||||
 | 
						// Pats note: This may return the current frame number if it is ready to send now.
 | 
				
			||||||
 | 
						// 3-18-2012: FIXME: This result is not monotonically increasing!!
 | 
				
			||||||
 | 
						// That is screwing up GPRS sendAssignment.
 | 
				
			||||||
 | 
						GSM::Time next = getNextWriteTime();
 | 
				
			||||||
 | 
						int achload = load();
 | 
				
			||||||
 | 
						if (mWaitingToSend) { achload++; }
 | 
				
			||||||
 | 
						//old: GSM::Time result = next + (achload+3) * 51;	// add one to be safe.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// (pat) TODO: We are adding a whole 51-multframe for each additional
 | 
				
			||||||
 | 
						// CCCH message, which may not be correct.
 | 
				
			||||||
 | 
						// Note: We dont need to carefully make sure the frame
 | 
				
			||||||
 | 
						// numbers are valid (eg, by rollForward), because this code is used by GPRS
 | 
				
			||||||
 | 
						// which is going to convert it to an RLC block time anyway.
 | 
				
			||||||
 | 
						int fnresult = (next.FN() + achload * 51) % gHyperframe;
 | 
				
			||||||
 | 
						GSM::Time result(fnresult);
 | 
				
			||||||
 | 
						LOG(DEBUG) << "CCCHLogicalChannel::getNextSend="<< next.FN()
 | 
				
			||||||
 | 
							<<" load="<<achload<<LOGVAR(mWaitingToSend) <<" now="<<gBTS.time().FN()<<LOGVAR(fnresult);
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
L3ChannelDescription LogicalChannel::channelDescription() const
 | 
					L3ChannelDescription LogicalChannel::channelDescription() const
 | 
				
			||||||
@@ -177,7 +294,7 @@ SDCCHLogicalChannel::SDCCHLogicalChannel(
 | 
				
			|||||||
	SAP3L2->master(SAP0L2);
 | 
						SAP3L2->master(SAP0L2);
 | 
				
			||||||
	mL2[0] = SAP0L2;
 | 
						mL2[0] = SAP0L2;
 | 
				
			||||||
	mL2[3] = SAP3L2;
 | 
						mL2[3] = SAP3L2;
 | 
				
			||||||
	mSACCH = new SACCHLogicalChannel(wCN,wTN,wMapping.SACCH());
 | 
						mSACCH = new SACCHLogicalChannel(wCN,wTN,wMapping.SACCH(),this);
 | 
				
			||||||
	connect();
 | 
						connect();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -188,8 +305,10 @@ SDCCHLogicalChannel::SDCCHLogicalChannel(
 | 
				
			|||||||
SACCHLogicalChannel::SACCHLogicalChannel(
 | 
					SACCHLogicalChannel::SACCHLogicalChannel(
 | 
				
			||||||
		unsigned wCN,
 | 
							unsigned wCN,
 | 
				
			||||||
		unsigned wTN,
 | 
							unsigned wTN,
 | 
				
			||||||
		const MappingPair& wMapping)
 | 
							const MappingPair& wMapping,
 | 
				
			||||||
		: mRunning(false)
 | 
							const LogicalChannel *wHost)
 | 
				
			||||||
 | 
							: mRunning(false),
 | 
				
			||||||
 | 
							mHost(wHost)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	mSACCHL1 = new SACCHL1FEC(wCN,wTN,wMapping);
 | 
						mSACCHL1 = new SACCHL1FEC(wCN,wTN,wMapping);
 | 
				
			||||||
	mL1 = mSACCHL1;
 | 
						mL1 = mSACCHL1;
 | 
				
			||||||
@@ -232,15 +351,17 @@ L3Message* processSACCHMessage(L3Frame *l3frame)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SACCHLogicalChannel::serviceLoop()
 | 
					void SACCHLogicalChannel::serviceLoop()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// run the loop
 | 
						// run the loop
 | 
				
			||||||
	unsigned count = 0;
 | 
						unsigned count = 0;
 | 
				
			||||||
	while (true) {
 | 
						while (true) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Throttle back if not active.
 | 
							// Throttle back if not active.
 | 
				
			||||||
		if (!active()) {
 | 
							if (!active()) {
 | 
				
			||||||
			OBJLOG(DEBUG) << "SACCH sleeping";
 | 
								//OBJLOG(DEBUG) << "SACCH sleeping";
 | 
				
			||||||
			sleepFrames(51);
 | 
								sleepFrames(51);
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -249,8 +370,12 @@ void SACCHLogicalChannel::serviceLoop()
 | 
				
			|||||||
		// otherwise sleep and continue;
 | 
							// otherwise sleep and continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Send alternating SI5/SI6.
 | 
							// Send alternating SI5/SI6.
 | 
				
			||||||
 | 
							// These L3Frames were created with the UNIT_DATA primivitive.
 | 
				
			||||||
		OBJLOG(DEBUG) << "sending SI5/6 on SACCH";
 | 
							OBJLOG(DEBUG) << "sending SI5/6 on SACCH";
 | 
				
			||||||
		if (count%2) LogicalChannel::send(gBTS.SI5Frame());
 | 
							if (count%2) {
 | 
				
			||||||
 | 
								gBTS.regenerateSI5();
 | 
				
			||||||
 | 
								LogicalChannel::send(gBTS.SI5Frame());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		else LogicalChannel::send(gBTS.SI6Frame());
 | 
							else LogicalChannel::send(gBTS.SI6Frame());
 | 
				
			||||||
		count++;
 | 
							count++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -274,6 +399,8 @@ void SACCHLogicalChannel::serviceLoop()
 | 
				
			|||||||
					// Add the measurement results to the table
 | 
										// Add the measurement results to the table
 | 
				
			||||||
					// Note that the typeAndOffset of a SACCH match the host channel.
 | 
										// Note that the typeAndOffset of a SACCH match the host channel.
 | 
				
			||||||
					gPhysStatus.setPhysical(this, mMeasurementResults);
 | 
										gPhysStatus.setPhysical(this, mMeasurementResults);
 | 
				
			||||||
 | 
										// Check for handover requirement.
 | 
				
			||||||
 | 
										Control::HandoverDetermination(mMeasurementResults,this);
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					OBJLOG(NOTICE) << "SACCH SAP0 sent unaticipated message " << rrMessage;
 | 
										OBJLOG(NOTICE) << "SACCH SAP0 sent unaticipated message " << rrMessage;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -320,6 +447,10 @@ void SACCHLogicalChannel::serviceLoop()
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Did we get anything from the phone?
 | 
				
			||||||
 | 
								// If not, we may have lost contact.  Bump the RSSI to induce more power
 | 
				
			||||||
 | 
								if (nothing) RSSIBumpDown(gConfig.getNum("Control.SACCHTimeout.BumpDown"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Nothing happened?
 | 
								// Nothing happened?
 | 
				
			||||||
			if (nothing) break;
 | 
								if (nothing) break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -336,18 +467,22 @@ void *GSM::SACCHLogicalChannelServiceLoopAdapter(SACCHLogicalChannel* chan)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// These have to go into the .cpp file to prevent an illegal forward reference.
 | 
					// These have to go into the .cpp file to prevent an illegal forward reference.
 | 
				
			||||||
void LogicalChannel::setPhy(float wRSSI, float wTimingError)
 | 
					void LogicalChannel::setPhy(float wRSSI, float wTimingError, double wTimestamp)
 | 
				
			||||||
	{ assert(mSACCH); mSACCH->setPhy(wRSSI,wTimingError); }
 | 
						{ assert(mSACCH); mSACCH->setPhy(wRSSI,wTimingError,wTimestamp); }
 | 
				
			||||||
void LogicalChannel::setPhy(const LogicalChannel& other)
 | 
					void LogicalChannel::setPhy(const LogicalChannel& other)
 | 
				
			||||||
	{ assert(mSACCH); mSACCH->setPhy(*other.SACCH()); }
 | 
						{ assert(mSACCH); mSACCH->setPhy(*other.SACCH()); }
 | 
				
			||||||
float LogicalChannel::RSSI() const
 | 
					float LogicalChannel::RSSI() const
 | 
				
			||||||
	{ assert(mSACCH); return mSACCH->RSSI(); }
 | 
						{ assert(mSACCH); return mSACCH->RSSI(); }
 | 
				
			||||||
float LogicalChannel::timingError() const
 | 
					float LogicalChannel::timingError() const
 | 
				
			||||||
	{ assert(mSACCH); return mSACCH->timingError(); }
 | 
						{ assert(mSACCH); return mSACCH->timingError(); }
 | 
				
			||||||
 | 
					double LogicalChannel::timestamp() const
 | 
				
			||||||
 | 
						{ assert(mSACCH); return mSACCH->timestamp(); }
 | 
				
			||||||
int LogicalChannel::actualMSPower() const
 | 
					int LogicalChannel::actualMSPower() const
 | 
				
			||||||
	{ assert(mSACCH); return mSACCH->actualMSPower(); }
 | 
						{ assert(mSACCH); return mSACCH->actualMSPower(); }
 | 
				
			||||||
int LogicalChannel::actualMSTiming() const
 | 
					int LogicalChannel::actualMSTiming() const
 | 
				
			||||||
	{ assert(mSACCH); return mSACCH->actualMSTiming(); }
 | 
						{ assert(mSACCH); return mSACCH->actualMSTiming(); }
 | 
				
			||||||
 | 
					const L3MeasurementResults& LogicalChannel::measurementResults() const
 | 
				
			||||||
 | 
						{ assert(mSACCH); return mSACCH->measurementResults(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -362,13 +497,30 @@ TCHFACCHLogicalChannel::TCHFACCHLogicalChannel(
 | 
				
			|||||||
	// SAP1 and SAP2 are not used.
 | 
						// SAP1 and SAP2 are not used.
 | 
				
			||||||
	mL2[0] = new FACCHL2(1,0);
 | 
						mL2[0] = new FACCHL2(1,0);
 | 
				
			||||||
	mL2[3] = new FACCHL2(1,3);
 | 
						mL2[3] = new FACCHL2(1,3);
 | 
				
			||||||
	mSACCH = new SACCHLogicalChannel(wCN,wTN,wMapping.SACCH());
 | 
						mSACCH = new SACCHLogicalChannel(wCN,wTN,wMapping.SACCH(),this);
 | 
				
			||||||
	connect();
 | 
						connect();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CBCHLogicalChannel::CBCHLogicalChannel(const CompleteMapping& wMapping)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mL1 = new CBCHL1FEC(wMapping.LCH());
 | 
				
			||||||
 | 
						mL2[0] = new CBCHL2;
 | 
				
			||||||
 | 
						mSACCH = new SACCHLogicalChannel(0,0,wMapping.SACCH(),this);
 | 
				
			||||||
 | 
						connect();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CBCHLogicalChannel::send(const L3SMSCBMessage& msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						L3Frame frame(UNIT_DATA,88*8);
 | 
				
			||||||
 | 
						msg.write(frame);
 | 
				
			||||||
 | 
						LogicalChannel::send(frame);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool LogicalChannel::waitForPrimitive(Primitive primitive, unsigned timeout_ms)
 | 
					bool LogicalChannel::waitForPrimitive(Primitive primitive, unsigned timeout_ms)
 | 
				
			||||||
@@ -398,6 +550,20 @@ void LogicalChannel::waitForPrimitive(Primitive primitive)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					L3Frame* LogicalChannel::waitForEstablishOrHandover()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						while (true) {
 | 
				
			||||||
 | 
							L3Frame *req = recv();
 | 
				
			||||||
 | 
							if (req==NULL) continue;
 | 
				
			||||||
 | 
							if (req->primitive()==ESTABLISH) return req;
 | 
				
			||||||
 | 
							if (req->primitive()==HANDOVER_ACCESS) return req;
 | 
				
			||||||
 | 
							LOG(INFO) << "LogicalChannel: Ignored primitive:"<<req->primitive();
 | 
				
			||||||
 | 
							delete req;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NULL;	// to keep the compiler happy
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ostream& GSM::operator<<(ostream& os, const LogicalChannel& chan)
 | 
					ostream& GSM::operator<<(ostream& os, const LogicalChannel& chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,24 +4,16 @@
 | 
				
			|||||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
					* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses;
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* see the COPYING file in the main directory for licensing
 | 
				
			||||||
 | 
					* information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -61,6 +53,7 @@ typedef InterthreadQueue<Control::TransactionEntry> TransactionFIFO;
 | 
				
			|||||||
class SACCHLogicalChannel;
 | 
					class SACCHLogicalChannel;
 | 
				
			||||||
class L3Message;
 | 
					class L3Message;
 | 
				
			||||||
class L3RRMessage;
 | 
					class L3RRMessage;
 | 
				
			||||||
 | 
					class L3SMSCBMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -70,6 +63,8 @@ class L3RRMessage;
 | 
				
			|||||||
	The concept of the logical channel and the channel types are defined in GSM 04.03.
 | 
						The concept of the logical channel and the channel types are defined in GSM 04.03.
 | 
				
			||||||
	This is virtual class; specific channel types are subclasses.
 | 
						This is virtual class; specific channel types are subclasses.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					// (pat) It would be nice to break this into two classes: one that has the base functionality
 | 
				
			||||||
 | 
					// that GPRS will not use, and one with all the RR specific channel stuff.
 | 
				
			||||||
class LogicalChannel {
 | 
					class LogicalChannel {
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
protected:	
 | 
					protected:	
 | 
				
			||||||
@@ -118,12 +113,17 @@ public:
 | 
				
			|||||||
	/**@name Pass-throughs. */
 | 
						/**@name Pass-throughs. */
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Pat 5-27-2012: Let the LogicalChannel know the next scheduled write time.
 | 
				
			||||||
 | 
						GSM::Time getNextWriteTime() { return mL1->encoder()->getNextWriteTime(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Set L1 physical parameters from a RACH or pre-exsting channel. */
 | 
						/** Set L1 physical parameters from a RACH or pre-exsting channel. */
 | 
				
			||||||
	virtual void setPhy(float wRSSI, float wTimingError);
 | 
						virtual void setPhy(float wRSSI, float wTimingError, double wTimestamp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Set L1 physical parameters from an existing logical channel. */
 | 
						/* Set L1 physical parameters from an existing logical channel. */
 | 
				
			||||||
	virtual void setPhy(const LogicalChannel&);
 | 
						virtual void setPhy(const LogicalChannel&);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtual const L3MeasurementResults& measurementResults() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**@name L3 interfaces */
 | 
						/**@name L3 interfaces */
 | 
				
			||||||
	//@{
 | 
						//@{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -146,6 +146,48 @@ public:
 | 
				
			|||||||
	*/
 | 
						*/
 | 
				
			||||||
	virtual void send(const L3Frame& frame, unsigned SAPI=0)
 | 
						virtual void send(const L3Frame& frame, unsigned SAPI=0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							// (pat) Note that writeHighSide is overloaded per class hierarchy, and is also used
 | 
				
			||||||
 | 
							// for entirely unrelated classes, which are distinguishable (by humans,
 | 
				
			||||||
 | 
							// not by the compiler, which considers them unrelated functions)
 | 
				
			||||||
 | 
							// by arguments of L3Frame or L2Frame.
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							// For traffic channels:
 | 
				
			||||||
 | 
							// This function calls virtual L2DL::writeHighSide(L3Frame) which I think maps
 | 
				
			||||||
 | 
							// to L2LAPDm::writeHighSide() which interprets the primitive, and then
 | 
				
			||||||
 | 
							// sends traffic data through sendUFrameUI(L3Frame) which creates an L2Frame
 | 
				
			||||||
 | 
							// and sends it through several irrelevant functions to L2LAPDm::writeL1
 | 
				
			||||||
 | 
							// which calls (SAPMux)mDownstream->SAPMux::writeHighSide(L2Frame),
 | 
				
			||||||
 | 
							// which does nothing but call mL1->writeHighSide(L2Frame), which is a pass-through
 | 
				
			||||||
 | 
							// except that the SapMux uses mDownStream which is copied from mL1, so there is a
 | 
				
			||||||
 | 
							// chance to redirect it.  But wouldn't that be an error?
 | 
				
			||||||
 | 
							// Anyway, L1Encoder::writeHighSide is usually overridden.
 | 
				
			||||||
 | 
							// For TCH, it goes to XCCHL1Encoder::writeHighSide() which processes
 | 
				
			||||||
 | 
							// the L2Frame primitive, then sends traffic data to TCHFACCHL1Encoder::sendFrame(),
 | 
				
			||||||
 | 
							// which just enqueues the frame - it does not block.
 | 
				
			||||||
 | 
							// A thread runs GSM::TCHFACCHL1EncoderRoutine() which
 | 
				
			||||||
 | 
							// calls TCHFACCHL1Encoder::dispatch() which is synchronized with the gBTS clock,
 | 
				
			||||||
 | 
							// unsynchronized with the queue, because it must send data no matter what.
 | 
				
			||||||
 | 
							// Eventually it encodes the data and
 | 
				
			||||||
 | 
							// calls (ARFCNManager*)mDownStream->writeHighSideTx(), which writes to the socket.
 | 
				
			||||||
 | 
							//
 | 
				
			||||||
 | 
							// For CCCH channels:
 | 
				
			||||||
 | 
							// CCCHLogicalChannel::send(L3RRMessage) wraps the message in an L3Frame
 | 
				
			||||||
 | 
							// and enqueues the message on CCCHLogicalChannel::mQ.
 | 
				
			||||||
 | 
							// CCCHLogicalChannel::serviceLoop() pulls it out and sends it to
 | 
				
			||||||
 | 
							// LogicalChannel::send(L3Frame) [this function], which is virtual, but I dont think it
 | 
				
			||||||
 | 
							// is over-ridden, so message goes to L2DL::writeHighSide(L3Frame) which
 | 
				
			||||||
 | 
							// is over-ridden to CCCHL2::writeHighSide(L3Frame) which creates an L2Frame
 | 
				
			||||||
 | 
							// and calls (SAPMux)mDownstream->writeHighSide(L2Frame), which just
 | 
				
			||||||
 | 
							// calls (L1FEC)mDownStream->writeHighSide(L2Frame), which
 | 
				
			||||||
 | 
							// (because CCCHL1FEC is nearly empty) just
 | 
				
			||||||
 | 
							// calls (L1Encoder)mEncoder->writeHighSide(L2Frame), which maps
 | 
				
			||||||
 | 
							// to CCCHL1Encoder which maps to XCCHL1Encoder::writeHighSide(L2Frame),
 | 
				
			||||||
 | 
							// which processes the L2Frame primitive, and sends traffic data to
 | 
				
			||||||
 | 
							// XCCHL1Encoder::sendFrame(L2Frame), which encodes the frame and then calls
 | 
				
			||||||
 | 
							// XCCHL1Encoder::transmit(implicit mI arg with encoded burst) that
 | 
				
			||||||
 | 
							// finally blocks until L1Encoder::mPrevWriteTime occurs, then sets the
 | 
				
			||||||
 | 
							// burst time to L1Encoder::mNextWriteTime and
 | 
				
			||||||
 | 
							// calls (ARFCNManager*)mDownStream->writeHighSideTx() which writes to the socket.
 | 
				
			||||||
		assert(mL2[SAPI]);
 | 
							assert(mL2[SAPI]);
 | 
				
			||||||
		LOG(DEBUG) << "SAP"<< SAPI << " " << frame;
 | 
							LOG(DEBUG) << "SAP"<< SAPI << " " << frame;
 | 
				
			||||||
		mL2[SAPI]->writeHighSide(frame);
 | 
							mL2[SAPI]->writeHighSide(frame);
 | 
				
			||||||
@@ -180,6 +222,9 @@ public:
 | 
				
			|||||||
	*/
 | 
						*/
 | 
				
			||||||
	void waitForPrimitive(GSM::Primitive primitive);
 | 
						void waitForPrimitive(GSM::Primitive primitive);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Block until a HANDOVER_ACCESS or ESTABLISH arrives. */
 | 
				
			||||||
 | 
						L3Frame* waitForEstablishOrHandover();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
		Block on a channel until a given primitive arrives.
 | 
							Block on a channel until a given primitive arrives.
 | 
				
			||||||
		Any payload is discarded.  Block indefinitely, no timeout.
 | 
							Any payload is discarded.  Block indefinitely, no timeout.
 | 
				
			||||||
@@ -197,18 +242,20 @@ public:
 | 
				
			|||||||
	//@{
 | 
						//@{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Write a received radio burst into the "low" side of the channel. */
 | 
						/** Write a received radio burst into the "low" side of the channel. */
 | 
				
			||||||
	virtual void writeLowSide(const RxBurst& burst) { assert(mL1); mL1->writeLowSide(burst); }
 | 
						virtual void writeLowSide(const RxBurst& burst) { assert(mL1); mL1->writeLowSideRx(burst); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Return true if the channel is safely abandoned (closed or orphaned). */
 | 
						/** Return true if the channel is safely abandoned (closed or orphaned). */
 | 
				
			||||||
	bool recyclable() const { assert(mL1); return mL1->recyclable(); }
 | 
						virtual bool recyclable() const { assert(mL1); return mL1->recyclable(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Return true if the channel is active. */
 | 
						/** Return true if the channel is active. */
 | 
				
			||||||
	bool active() const { assert(mL1); return mL1->active(); }
 | 
						virtual bool active() const { assert(mL1); return mL1->active(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** The TDMA parameters for the transmit side. */
 | 
						/** The TDMA parameters for the transmit side. */
 | 
				
			||||||
 | 
						// (pat) This lovely function is unused.  Use L1Encoder::mapping()
 | 
				
			||||||
	const TDMAMapping& txMapping() const { assert(mL1); return mL1->txMapping(); }
 | 
						const TDMAMapping& txMapping() const { assert(mL1); return mL1->txMapping(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** The TDMAParameters for the receive side. */
 | 
						/** The TDMAParameters for the receive side. */
 | 
				
			||||||
 | 
						// (pat) This lovely function is unused.  Use L1Decoder::mapping()
 | 
				
			||||||
	const TDMAMapping& rcvMapping() const { assert(mL1); return mL1->rcvMapping(); }
 | 
						const TDMAMapping& rcvMapping() const { assert(mL1); return mL1->rcvMapping(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** GSM 04.08 10.5.2.5 type and offset code. */
 | 
						/** GSM 04.08 10.5.2.5 type and offset code. */
 | 
				
			||||||
@@ -229,10 +276,14 @@ public:
 | 
				
			|||||||
	virtual float RSSI() const;
 | 
						virtual float RSSI() const;
 | 
				
			||||||
	/** Uplink timing error. */
 | 
						/** Uplink timing error. */
 | 
				
			||||||
	virtual float timingError() const;
 | 
						virtual float timingError() const;
 | 
				
			||||||
 | 
						/** System timestamp of RSSI and TA */
 | 
				
			||||||
 | 
						virtual double timestamp() const;
 | 
				
			||||||
	/** Actual MS uplink power. */
 | 
						/** Actual MS uplink power. */
 | 
				
			||||||
	virtual int actualMSPower() const;
 | 
						virtual int actualMSPower() const;
 | 
				
			||||||
	/** Actual MS uplink timing advance. */
 | 
						/** Actual MS uplink timing advance. */
 | 
				
			||||||
	virtual int actualMSTiming() const;
 | 
						virtual int actualMSTiming() const;
 | 
				
			||||||
 | 
						/** Control whether to accept a handover. */
 | 
				
			||||||
 | 
						void handoverPending(bool flag) { assert(mL1); mL1->handoverPending(flag); }
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//@} // L1
 | 
						//@} // L1
 | 
				
			||||||
@@ -277,6 +328,10 @@ public:
 | 
				
			|||||||
	*/
 | 
						*/
 | 
				
			||||||
	virtual void connect();
 | 
						virtual void connect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
						bool inUseByGPRS() { return mL1->inUseByGPRS(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool decryptUplink_maybe(string wIMSI, int wA5Alg) { return mL1->decoder()->decrypt_maybe(wIMSI, wA5Alg); }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -357,13 +412,15 @@ class SACCHLogicalChannel : public LogicalChannel {
 | 
				
			|||||||
	/** MeasurementResults from the MS. They are caught in serviceLoop, accessed
 | 
						/** MeasurementResults from the MS. They are caught in serviceLoop, accessed
 | 
				
			||||||
	 for recording along with GPS and other data in MobilityManagement.cpp */
 | 
						 for recording along with GPS and other data in MobilityManagement.cpp */
 | 
				
			||||||
	L3MeasurementResults mMeasurementResults;
 | 
						L3MeasurementResults mMeasurementResults;
 | 
				
			||||||
 | 
						const LogicalChannel *mHost;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SACCHLogicalChannel(
 | 
						SACCHLogicalChannel(
 | 
				
			||||||
		unsigned wCN,
 | 
							unsigned wCN,
 | 
				
			||||||
		unsigned wTN,
 | 
							unsigned wTN,
 | 
				
			||||||
		const MappingPair& wMapping);
 | 
							const MappingPair& wMapping,
 | 
				
			||||||
 | 
							const LogicalChannel* wHost);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ChannelType type() const { return SACCHType; }
 | 
						ChannelType type() const { return SACCHType; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -375,10 +432,14 @@ class SACCHLogicalChannel : public LogicalChannel {
 | 
				
			|||||||
	//@{
 | 
						//@{
 | 
				
			||||||
	float RSSI() const { return mSACCHL1->RSSI(); }
 | 
						float RSSI() const { return mSACCHL1->RSSI(); }
 | 
				
			||||||
	float timingError() const { return mSACCHL1->timingError(); }
 | 
						float timingError() const { return mSACCHL1->timingError(); }
 | 
				
			||||||
 | 
						double timestamp() const { return mSACCHL1->timestamp(); }
 | 
				
			||||||
	int actualMSPower() const { return mSACCHL1->actualMSPower(); }
 | 
						int actualMSPower() const { return mSACCHL1->actualMSPower(); }
 | 
				
			||||||
	int actualMSTiming() const { return mSACCHL1->actualMSTiming(); }
 | 
						int actualMSTiming() const { return mSACCHL1->actualMSTiming(); }
 | 
				
			||||||
	void setPhy(float RSSI, float timingError) { mSACCHL1->setPhy(RSSI,timingError); }
 | 
						void setPhy(float RSSI, float timingError, double wTimestamp)
 | 
				
			||||||
 | 
							{ mSACCHL1->setPhy(RSSI,timingError,wTimestamp); }
 | 
				
			||||||
	void setPhy(const SACCHLogicalChannel& other) { mSACCHL1->setPhy(*other.mSACCHL1); }
 | 
						void setPhy(const SACCHLogicalChannel& other) { mSACCHL1->setPhy(*other.mSACCHL1); }
 | 
				
			||||||
 | 
						void RSSIBumpDown(int dB) { assert(mL1); mSACCHL1->RSSIBumpDown(dB); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**@name Channel and neighbour cells stats as reported from MS */
 | 
						/**@name Channel and neighbour cells stats as reported from MS */
 | 
				
			||||||
@@ -386,6 +447,12 @@ class SACCHLogicalChannel : public LogicalChannel {
 | 
				
			|||||||
	const L3MeasurementResults& measurementResults() const { return mMeasurementResults; }
 | 
						const L3MeasurementResults& measurementResults() const { return mMeasurementResults; }
 | 
				
			||||||
	//@}
 | 
						//@}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Get active state from the host DCCH. */
 | 
				
			||||||
 | 
						bool active() const { assert(mHost); return mHost->active(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Get recyclable state from the host DCCH. */
 | 
				
			||||||
 | 
						bool recyclable() const { assert(mHost); return mHost->recyclable(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected:
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Read and process a measurement report, called from the service loop. */
 | 
						/** Read and process a measurement report, called from the service loop. */
 | 
				
			||||||
@@ -400,9 +467,6 @@ class SACCHLogicalChannel : public LogicalChannel {
 | 
				
			|||||||
void *SACCHLogicalChannelServiceLoopAdapter(SACCHLogicalChannel*);
 | 
					void *SACCHLogicalChannelServiceLoopAdapter(SACCHLogicalChannel*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
	Common control channel.
 | 
						Common control channel.
 | 
				
			||||||
	The "uplink" component of the CCCH is the RACH.
 | 
						The "uplink" component of the CCCH is the RACH.
 | 
				
			||||||
@@ -410,10 +474,14 @@ void *SACCHLogicalChannelServiceLoopAdapter(SACCHLogicalChannel*);
 | 
				
			|||||||
	bi-directional control channel. Common control channels are physically
 | 
						bi-directional control channel. Common control channels are physically
 | 
				
			||||||
	sub-divided into the common control channel (CCCH), the packet common control
 | 
						sub-divided into the common control channel (CCCH), the packet common control
 | 
				
			||||||
	channel (PCCCH), and the Compact packet common control channel (CPCCCH)."
 | 
						channel (PCCCH), and the Compact packet common control channel (CPCCCH)."
 | 
				
			||||||
 | 
						(pat) To implement DRX and paging I added the CCCHCombinedChannel to which CCCH messages
 | 
				
			||||||
 | 
						should now be sent, and this class is now just a private attachment point whose primary
 | 
				
			||||||
 | 
						purpose is to house the serviceloop for a single CCCH.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
class CCCHLogicalChannel : public NDCCHLogicalChannel {
 | 
					class CCCHLogicalChannel : public NDCCHLogicalChannel {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	protected:
 | 
						protected:
 | 
				
			||||||
 | 
						friend class GSMConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
		Because the CCCH is written by multiple threads,
 | 
							Because the CCCH is written by multiple threads,
 | 
				
			||||||
@@ -423,7 +491,14 @@ class CCCHLogicalChannel : public NDCCHLogicalChannel {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	Thread mServiceThread;	///< a thread for the service loop
 | 
						Thread mServiceThread;	///< a thread for the service loop
 | 
				
			||||||
	L3FrameFIFO mQ;			///< because the CCCH is written by multiple threads
 | 
						L3FrameFIFO mQ;			///< because the CCCH is written by multiple threads
 | 
				
			||||||
 | 
					#if ENABLE_PAGING_CHANNELS
 | 
				
			||||||
 | 
						L3FrameFIFO mPagingQ[sMax_BS_PA_MFRMS];	///< A queue for each paging channel on this timeslot.
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	bool mRunning;			///< a flag to indication that the service loop is running
 | 
						bool mRunning;			///< a flag to indication that the service loop is running
 | 
				
			||||||
 | 
						bool mWaitingToSend;	// If this is set, there is another CCCH message
 | 
				
			||||||
 | 
												// waiting in the encoder serviceloop.
 | 
				
			||||||
 | 
												// This variable is not mutex locked and could
 | 
				
			||||||
 | 
												// be incorrect, but it is not critical.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -432,7 +507,11 @@ class CCCHLogicalChannel : public NDCCHLogicalChannel {
 | 
				
			|||||||
	void open();
 | 
						void open();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void send(const L3RRMessage& msg)
 | 
						void send(const L3RRMessage& msg)
 | 
				
			||||||
		{ mQ.write(new L3Frame((const L3Message&)msg,UNIT_DATA)); }
 | 
							{
 | 
				
			||||||
 | 
								// DEBUG:
 | 
				
			||||||
 | 
								//LOG(WARNING) << "CCCHLogicalChannel2::write q";
 | 
				
			||||||
 | 
								mQ.write(new L3Frame((const L3Message&)msg,UNIT_DATA));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void send(const L3Message&) { assert(0); }
 | 
						void send(const L3Message&) { assert(0); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -442,6 +521,27 @@ class CCCHLogicalChannel : public NDCCHLogicalChannel {
 | 
				
			|||||||
	/** Return the number of messages waiting for transmission. */
 | 
						/** Return the number of messages waiting for transmission. */
 | 
				
			||||||
	unsigned load() const { return mQ.size(); }
 | 
						unsigned load() const { return mQ.size(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// (pat) GPRS needs to know exactly when the CCCH message will be sent downstream,
 | 
				
			||||||
 | 
						// because it needs to allocate an upstream radio block after that time,
 | 
				
			||||||
 | 
						// and preferably as quickly as possible after that time.
 | 
				
			||||||
 | 
						// For now, I'm going to punt on this and return the worst case.
 | 
				
			||||||
 | 
						// TODO: This is the wrong way to do this.
 | 
				
			||||||
 | 
						// First, this calculation should not be here; it will be hard for anyone maintaining
 | 
				
			||||||
 | 
						// the code and making changes that would affect this calculation to find it here.
 | 
				
			||||||
 | 
						// Second, it depends on what kind of C0T0 beacon we have.
 | 
				
			||||||
 | 
						// We should wait until it is time to send the message, then create it.
 | 
				
			||||||
 | 
						// To do this, either the CCCHLogicalChannel::serviceLoop should be rewritten,
 | 
				
			||||||
 | 
						// or we should hook XCCHL1Encoder::sendFrame(L2Frame) to modify the message
 | 
				
			||||||
 | 
						// if it is a packet message.  Or more drastically, make the CCCHLogicalChannel::mQ
 | 
				
			||||||
 | 
						// queue hold internal messages not L3Frames, for example, for RACH a struct
 | 
				
			||||||
 | 
						// with the arrival time, RACH message, signal strength and timing advance,
 | 
				
			||||||
 | 
						// and delay generating the RRMessage until it is ready to send.
 | 
				
			||||||
 | 
						// 
 | 
				
			||||||
 | 
						// But for now, just punt and send a frame time far enough in the future that it
 | 
				
			||||||
 | 
						// is guaranteed to work:
 | 
				
			||||||
 | 
						// Note: Time wraps at gHyperFrame.
 | 
				
			||||||
 | 
						Time getNextMsgSendTime();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ChannelType type() const { return CCCHType; }
 | 
						ChannelType type() const { return CCCHType; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	friend void *CCCHLogicalChannelServiceLoopAdapter(CCCHLogicalChannel*);
 | 
						friend void *CCCHLogicalChannelServiceLoopAdapter(CCCHLogicalChannel*);
 | 
				
			||||||
@@ -492,6 +592,33 @@ class TCHFACCHLogicalChannel : public LogicalChannel {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
						Cell broadcast control channel (CBCH).
 | 
				
			||||||
 | 
						See GSM 04.12 3.3.1.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					class CBCHLogicalChannel : public NDCCHLogicalChannel {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
							The CBCH should be written be a single thread.
 | 
				
			||||||
 | 
							The input interface is *not* multi-thread safe.
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CBCHLogicalChannel(const CompleteMapping& wMapping);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void send(const L3SMSCBMessage& msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void send(const L3Message&) { assert(0); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ChannelType type() const { return CBCHType; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**@name Test channels, not actually used in GSM. */
 | 
					/**@name Test channels, not actually used in GSM. */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,14 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,14 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
					* Copyright 2008 Free Software Foundation, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This use of this software may be subject to additional restrictions.
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
* See the LEGAL file in the main directory for details.
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
	it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
	the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
	(at your option) any later version.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This program is distributed in the hope that it will be useful,
 | 
					    This program is distributed in the hope that it will be useful,
 | 
				
			||||||
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
				
			||||||
	GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -42,6 +32,7 @@ class L2DL;
 | 
				
			|||||||
	A "service access point" in GSM/ISDN is analogous to port number in IP.
 | 
						A "service access point" in GSM/ISDN is analogous to port number in IP.
 | 
				
			||||||
	GSM allows up to 4 SAPs, although only two are presently used.
 | 
						GSM allows up to 4 SAPs, although only two are presently used.
 | 
				
			||||||
	See GSM 04.05 5.2 for an introduction.
 | 
						See GSM 04.05 5.2 for an introduction.
 | 
				
			||||||
 | 
						(pat) SAPs exist at every level in the OSI model.  This should probably be called L2SAPMux.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
class SAPMux  {
 | 
					class SAPMux  {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user