Merge branch 'jolly'

Conflicts:
	src/gprs_bssgp_pcu.cpp
	src/gprs_rlcmac.cpp
	src/gprs_rlcmac_data.cpp
	src/gprs_rlcmac_sched.cpp
This commit is contained in:
Ivan Kluchnikov 2012-10-05 18:17:57 +04:00
commit 9eb552b239
19 changed files with 3411 additions and 698 deletions

339
COPYING Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 2 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, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

22
README Normal file
View File

@ -0,0 +1,22 @@
This is an implementation of Packet Control Unit (PCU) according to TS 04.60
The PCU is part of BSS, so it connects directly to SGSN.
== Current limitations ==
* No PFC support
* No fixed allocation support
* No extended dynamic allocation support
* No unacknowledged mode operation
* No PCCCH/PBCCH support
* Only single slot assignment on uplink direction
* No half-duplex class support
* No two-phase access support
* No handover support
* No measurement support
* No polling for control ack on assignment
* No TA loop
* No power loop
* No CS loop
* No EGPRS

View File

@ -33,7 +33,8 @@ libgprs_la_SOURCES = \
gprs_rlcmac_sched.cpp \
gsm_timer.cpp \
bitvector.cpp \
pcu_l1_if.cpp
pcu_l1_if.cpp \
pcu_vty.c
if ENABLE_SYSMOBTS
libgprs_la_SOURCES += \
@ -44,8 +45,10 @@ libgprs_la_SOURCES += \
endif
noinst_PROGRAMS = \
RLCMACTest \
pcu
RLCMACTest
bin_PROGRAMS = \
osmo-pcu
noinst_HEADERS = \
gprs_debug.h \
@ -56,7 +59,8 @@ noinst_HEADERS = \
pcuif_proto.h \
pcu_l1_if.h \
gsm_timer.h \
bitvector.h
bitvector.h \
pcu_vty.h
RLCMACTest_SOURCES = RLCMACTest.cpp
RLCMACTest_LDADD = \
@ -64,8 +68,8 @@ RLCMACTest_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
pcu_SOURCES = pcu_main.cpp
pcu_LDADD = \
osmo_pcu_SOURCES = pcu_main.cpp
osmo_pcu_LDADD = \
libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOCORE_LIBS) \

View File

@ -25,15 +25,21 @@ struct sgsn_instance *sgsn;
void *tall_bsc_ctx;
struct bssgp_bvc_ctx *bctx = NULL;
struct gprs_nsvc *nsvc = NULL;
static int bvc_sig_reset = 0, bvc_reset = 0, bvc_unblocked = 0;
extern uint16_t spoof_mcc, spoof_mnc;
struct osmo_timer_list bvc_timer;
static void bvc_timeout(void *_priv);
int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
{
struct bssgp_ud_hdr *budh;
int tfi;
int8_t tfi; /* must be signed */
uint32_t tlli;
int i, j;
uint8_t trx, ts;
uint8_t *data;
uint16_t len;
struct gprs_rlcmac_tbf *tbf;
@ -78,6 +84,54 @@ int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
}
imsi[j] = '\0';
}
/* parse ms radio access capability */
uint8_t ms_class = 0;
if (TLVP_PRESENT(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP))
{
bitvec *block;
uint8_t cap_len = TLVP_LEN(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
uint8_t *cap = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_MS_RADIO_ACCESS_CAP);
unsigned rp = 0;
block = bitvec_alloc(cap_len);
bitvec_unpack(block, cap);
bitvec_read_field(block, rp, 4); // Access Technology Type
bitvec_read_field(block, rp, 7); // Length of Access Capabilities
bitvec_read_field(block, rp, 3); // RF Power Capability
if (bitvec_read_field(block, rp, 1)) // A5 Bits Present
bitvec_read_field(block, rp, 7); // A5 Bits
bitvec_read_field(block, rp, 1); // ES IND
bitvec_read_field(block, rp, 1); // PS
bitvec_read_field(block, rp, 1); // VGCS
bitvec_read_field(block, rp, 1); // VBS
if (bitvec_read_field(block, rp, 1)) { // Multislot Cap Present
if (bitvec_read_field(block, rp, 1)) // HSCSD Present
bitvec_read_field(block, rp, 5); // Class
if (bitvec_read_field(block, rp, 1)) { // GPRS Present
ms_class = bitvec_read_field(block, rp, 5); // Class
bitvec_read_field(block, rp, 1); // Ext.
}
if (bitvec_read_field(block, rp, 1)) // SMS Present
bitvec_read_field(block, rp, 4); // SMS Value
bitvec_read_field(block, rp, 4); // SMS Value
}
}
/* get lifetime */
uint16_t delay_csec = 0xffff;
if (TLVP_PRESENT(tp, BSSGP_IE_PDU_LIFETIME))
{
uint8_t lt_len = TLVP_LEN(tp, BSSGP_IE_PDU_LIFETIME);
uint16_t *lt = (uint16_t *) TLVP_VAL(tp, BSSGP_IE_PDU_LIFETIME);
if (lt_len == 2)
delay_csec = ntohs(*lt);
else
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP invalid length of "
"PDU_LIFETIME IE\n");
} else
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP missing mandatory "
"PDU_LIFETIME IE\n");
LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len);
/* check for existing TBF */
@ -90,29 +144,83 @@ int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
tbf->llc_length = len;
memset(&tbf->dir.dl, 0, sizeof(tbf->dir.dl)); /* reset
rlc states */
gprs_rlcmac_trigger_downlink_assignment(tbf, 1, NULL);
tbf->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; /* keep
to flags */
tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH);
if (!tbf->ms_class && ms_class)
tbf->ms_class = ms_class;
tbf_update(tbf);
gprs_rlcmac_trigger_downlink_assignment(tbf, tbf, NULL);
} else {
/* the TBF exists, so we must write it in the queue */
struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
/* the TBF exists, so we must write it in the queue
* we prepend lifetime in front of PDU */
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct timeval *tv;
struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv),
"llc_pdu_queue");
if (!llc_msg)
return -ENOMEM;
tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));
if (bts->force_llc_lifetime)
delay_csec = bts->force_llc_lifetime;
/* keep timestap at 0 for infinite delay */
if (delay_csec != 0xffff) {
/* calculate timestamp of timeout */
gettimeofday(tv, NULL);
tv->tv_usec += (delay_csec % 100) * 10000;
tv->tv_sec += delay_csec / 100;
if (tv->tv_usec > 999999) {
tv->tv_usec -= 1000000;
tv->tv_sec++;
}
}
memcpy(msgb_put(llc_msg, len), data, len);
msgb_enqueue(&tbf->llc_queue, llc_msg);
/* set ms class for updating TBF */
if (!tbf->ms_class && ms_class)
tbf->ms_class = ms_class;
}
} else {
// Create new TBF
tfi = tfi_alloc(&trx, &ts);
uint8_t trx, ts, use_trx, first_ts, ta, ss;
struct gprs_rlcmac_tbf *old_tbf;
/* check for uplink data, so we copy our informations */
tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF);
if (tbf && tbf->dir.ul.contention_resolution_done
&& !tbf->dir.ul.final_ack_sent) {
use_trx = tbf->trx;
first_ts = tbf->first_ts;
ta = tbf->ta;
ss = 0;
old_tbf = tbf;
} else {
use_trx = -1;
first_ts = -1;
ta = 0; /* FIXME: initial TA */
ss = 1; /* PCH assignment only allows one timeslot */
old_tbf = NULL;
}
// Create new TBF (any TRX)
tfi = tfi_alloc(GPRS_RLCMAC_DL_TBF, &trx, &ts, use_trx, first_ts);
if (tfi < 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
/* FIXME: send reject */
return -EBUSY;
}
tbf = tbf_alloc(tfi, trx, ts);
tbf->direction = GPRS_RLCMAC_DL_TBF;
/* set number of downlink slots according to multislot class */
tbf = tbf_alloc(tbf, GPRS_RLCMAC_DL_TBF, tfi, trx, ts, ms_class,
ss);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
/* FIXME: send reject */
return -EBUSY;
}
tbf->tlli = tlli;
tbf->tlli_valid = 1;
tbf->ta = ta;
LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] START TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] START TFI: %d TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
/* new TBF, so put first frame */
memcpy(tbf->llc_frame, data, len);
@ -122,7 +230,7 @@ int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
* we don't use old_downlink, so the possible uplink is used
* to trigger downlink assignment. if there is no uplink,
* AGCH is used. */
gprs_rlcmac_trigger_downlink_assignment(tbf, 0, imsi);
gprs_rlcmac_trigger_downlink_assignment(tbf, old_tbf, imsi);
}
return 0;
@ -135,6 +243,9 @@ int gprs_bssgp_pcu_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, struct bssgp_
uint8_t pdu_type = bgph->pdu_type;
unsigned rc = 0;
if (!bctx)
return -EINVAL;
/* If traffic is received on a BVC that is marked as blocked, the
* received PDU shall not be accepted and a STATUS PDU (Cause value:
* BVC Blocked) shall be sent to the peer entity on the signalling BVC */
@ -192,6 +303,11 @@ int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp
break;
case BSSGP_PDUT_BVC_RESET_ACK:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_RESET_ACK\n");
if (!bvc_sig_reset)
bvc_sig_reset = 1;
else
bvc_reset = 1;
bvc_timeout(NULL);
break;
case BSSGP_PDUT_PAGING_PS:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_PS\n");
@ -213,6 +329,8 @@ int gprs_bssgp_pcu_rx_sign(struct msgb *msg, struct tlv_parsed *tp, struct bssgp
break;
case BSSGP_PDUT_BVC_UNBLOCK_ACK:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_UNBLOCK_ACK\n");
bvc_unblocked = 1;
bvc_timeout(NULL);
break;
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n");
@ -259,7 +377,9 @@ int gprs_bssgp_pcu_rcvmsg(struct msgb *msg)
/* look-up or create the BTS context for this BVC */
bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET_ACK)
if (!bctx
&& pdu_type != BSSGP_PDUT_BVC_RESET_ACK
&& pdu_type != BSSGP_PDUT_BVC_UNBLOCK_ACK)
{
LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
"type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
@ -335,14 +455,22 @@ static int nsvc_signal_cb(unsigned int subsys, unsigned int signal,
case S_NS_UNBLOCK:
if (!nsvc_unblocked) {
nsvc_unblocked = 1;
LOGP(DPCU, LOGL_NOTICE, "NS-VC is unblocked.\n");
bssgp_tx_bvc_reset(bctx, bctx->bvci,
BSSGP_CAUSE_PROTO_ERR_UNSPEC);
LOGP(DPCU, LOGL_NOTICE, "NS-VC %d is unblocked.\n",
nsvc->nsvci);
bvc_sig_reset = 0;
bvc_reset = 0;
bvc_unblocked = 0;
bvc_timeout(NULL);
}
break;
case S_NS_BLOCK:
if (nsvc_unblocked) {
nsvc_unblocked = 0;
if (osmo_timer_pending(&bvc_timer))
osmo_timer_del(&bvc_timer);
bvc_sig_reset = 0;
bvc_reset = 0;
bvc_unblocked = 0;
LOGP(DPCU, LOGL_NOTICE, "NS-VC is blocked.\n");
}
break;
@ -351,12 +479,63 @@ static int nsvc_signal_cb(unsigned int subsys, unsigned int signal,
return 0;
}
int gprs_bssgp_tx_fc_bvc(void)
{
if (!bctx) {
LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
return -EIO;
}
/* FIXME: use real values */
return bssgp_tx_fc_bvc(bctx, 1, 6553500, 819100, 50000, 50000,
NULL, NULL);
// return bssgp_tx_fc_bvc(bctx, 1, 84000, 25000, 48000, 45000,
// NULL, NULL);
}
static void bvc_timeout(void *_priv)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
if (!bvc_sig_reset) {
LOGP(DBSSGP, LOGL_INFO, "Sending reset on BVCI 0\n");
bssgp_tx_bvc_reset(bctx, 0, BSSGP_CAUSE_OML_INTERV);
osmo_timer_schedule(&bvc_timer, 1, 0);
return;
}
if (!bvc_reset) {
LOGP(DBSSGP, LOGL_INFO, "Sending reset on BVCI %d\n",
bctx->bvci);
bssgp_tx_bvc_reset(bctx, bctx->bvci, BSSGP_CAUSE_OML_INTERV);
osmo_timer_schedule(&bvc_timer, 1, 0);
return;
}
if (!bvc_unblocked) {
LOGP(DBSSGP, LOGL_INFO, "Sending unblock on BVCI %d\n",
bctx->bvci);
bssgp_tx_bvc_unblock(bctx);
osmo_timer_schedule(&bvc_timer, 1, 0);
return;
}
LOGP(DBSSGP, LOGL_DEBUG, "Sending flow control info on BVCI %d\n",
bctx->bvci);
gprs_bssgp_tx_fc_bvc();
osmo_timer_schedule(&bvc_timer, bts->fc_interval, 0);
}
/* create BSSGP/NS layer instances */
int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, uint16_t lac,
uint16_t rac, uint16_t cell_id)
{
struct sockaddr_in dest;
int rc;
mcc = ((mcc & 0xf00) >> 8) * 100 + ((mcc & 0x0f0) >> 4) * 10 + (mcc & 0x00f);
mnc = ((mnc & 0xf00) >> 8) * 100 + ((mnc & 0x0f0) >> 4) * 10 + (mnc & 0x00f);
cell_id = ntohs(cell_id);
if (bctx)
return 0; /* if already created, must return 0: no error */
@ -366,7 +545,13 @@ int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
return -EINVAL;
}
gprs_ns_nsip_listen(bssgp_nsi);
rc = gprs_ns_nsip_listen(bssgp_nsi);
if (rc < 0) {
LOGP(DBSSGP, LOGL_ERROR, "Failed to create socket\n");
gprs_ns_destroy(bssgp_nsi);
bssgp_nsi = NULL;
return -EINVAL;
}
dest.sin_family = AF_INET;
dest.sin_port = htons(sgsn_port);
@ -398,6 +583,9 @@ int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
// bssgp_tx_bvc_reset(bctx, bctx->bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC);
bvc_timer.cb = bvc_timeout;
return 0;
}
@ -406,6 +594,9 @@ void gprs_bssgp_destroy(void)
if (!bssgp_nsi)
return;
if (osmo_timer_pending(&bvc_timer))
osmo_timer_del(&bvc_timer);
osmo_signal_unregister_handler(SS_L_NS, nsvc_signal_cb, NULL);
nsvc = NULL;

View File

@ -29,17 +29,14 @@ extern "C" {
#include <osmocom/core/application.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_bssgp_bss.h>
#include <osmocom/gprs/gprs_msgb.h>
int bssgp_tx_bvc_reset(struct bssgp_bvc_ctx *bctx, uint16_t bvci, uint8_t cause);
int bssgp_tx_ul_ud(struct bssgp_bvc_ctx *bctx, uint32_t tlli, const uint8_t *qos_profile, struct msgb *llc_pdu);
struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
}
#include <gprs_debug.h>
#define QOS_PROFILE 0
#define QOS_PROFILE 4
#define BSSGP_HDR_LEN 53
#define NS_HDR_LEN 4
#define IE_LLC_PDU 14

View File

@ -40,6 +40,7 @@ static const struct log_info_cat default_categories[] = {
{"DRLCMACDL", "\033[1;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
{"DRLCMACBW", "\033[1;31m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_INFO, 1},
{"DBSSGP", "\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_INFO , 1},
{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1},
};

View File

@ -34,6 +34,7 @@ enum {
DRLCMACDL,
DRLCMACUL,
DRLCMACSCHED,
DRLCMACBW,
DBSSGP,
DPCU,
aDebug_LastEntry

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,7 @@
#ifndef GPRS_RLCMAC_H
#define GPRS_RLCMAC_H
#ifdef __cplusplus
#include <bitvector.h>
#include <gsm_rlcmac.h>
#include <gsm_timer.h>
@ -28,6 +29,7 @@ extern "C" {
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
}
#endif
/* This special feature will delay assignment of downlink TBF by one second,
* in case there is already a TBF.
@ -46,21 +48,28 @@ struct gprs_rlcmac_pdch {
uint8_t tsc; /* TSC of this slot */
uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
struct gprs_rlcmac_tbf *tbf[32]; /* array of TBF pointers, by TFI */
struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
struct llist_head paging_list; /* list of paging messages */
uint32_t last_rts_fn; /* store last frame number of RTS */
};
struct gprs_rlcmac_trx {
uint16_t arfcn;
struct gprs_rlcmac_pdch pdch[8];
struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
};
struct gprs_rlcmac_bts {
uint8_t fc_interval;
uint8_t cs1;
uint8_t cs2;
uint8_t cs3;
uint8_t cs4;
uint8_t initial_cs;
uint8_t initial_cs_dl, initial_cs_ul;
uint8_t force_cs; /* 0=use from BTS 1=use from VTY */
uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
uint8_t t3142;
uint8_t t3169;
uint8_t t3191;
@ -70,10 +79,16 @@ struct gprs_rlcmac_bts {
uint8_t n3103;
uint8_t n3105;
struct gprs_rlcmac_trx trx[8];
int (*alloc_algorithm)(struct gprs_rlcmac_tbf *old_tbf,
struct gprs_rlcmac_tbf *tbf, uint32_t cust);
uint32_t alloc_algorithm_curst; /* options to customize algorithm */
uint8_t force_two_phase;
uint8_t alpha, gamma;
};
extern struct gprs_rlcmac_bts *gprs_rlcmac_bts;
#ifdef __cplusplus
/*
* TBF instance
*/
@ -83,8 +98,8 @@ extern struct gprs_rlcmac_bts *gprs_rlcmac_bts;
#define RLC_MAX_WS 64 /* max window size */
#define RLC_MAX_LEN 54 /* CS-4 including spare bits */
#define Tassign_agch 0,500000/* wait for assignment, before transmitting DL */
#define Tassign_pacch 0,100000/* wait for assignment, before transmitting DL */
#define Tassign_agch 0,200000 /* waiting after IMM.ASS confirm */
#define Tassign_pacch 2,0 /* timeout for pacch assigment */
enum gprs_rlcmac_tbf_state {
GPRS_RLCMAC_NULL = 0, /* new created TBF */
@ -123,27 +138,45 @@ enum gprs_rlcmac_tbf_direction {
GPRS_RLCMAC_UL_TBF
};
#define GPRS_RLCMAC_FLAG_CCCH 0 /* assignment on CCCH */
#define GPRS_RLCMAC_FLAG_PACCH 1 /* assignment on PACCH */
#define GPRS_RLCMAC_FLAG_UL_DATA 2 /* uplink data received */
#define GPRS_RLCMAC_FLAG_DL_ACK 3 /* downlink acknowledge received */
#define GPRS_RLCMAC_FLAG_TO_UL_ACK 4
#define GPRS_RLCMAC_FLAG_TO_DL_ACK 5
#define GPRS_RLCMAC_FLAG_TO_UL_ASS 6
#define GPRS_RLCMAC_FLAG_TO_DL_ASS 7
#define GPRS_RLCMAC_FLAG_TO_MASK 0xf0 /* timeout bits */
struct gprs_rlcmac_tbf {
struct llist_head list;
enum gprs_rlcmac_tbf_state state;
uint32_t state_flags;
enum gprs_rlcmac_tbf_direction direction;
uint8_t tfi;
uint32_t tlli;
uint8_t tlli_valid;
uint8_t trx, ts, tsc;
struct gprs_rlcmac_pdch *pdch;
uint16_t arfcn, ta;
uint8_t trx;
uint16_t arfcn;
uint8_t tsc;
uint8_t first_ts; /* first TS used by TBF */
uint8_t first_common_ts; /* first TS that the phone can send and
reveive simultaniously */
uint8_t control_ts; /* timeslot control messages and polling */
uint8_t ms_class;
struct gprs_rlcmac_pdch *pdch[8]; /* list of PDCHs allocated to TBF */
uint16_t ta;
uint8_t llc_frame[LLC_MAX_LEN]; /* current DL or UL frame */
uint16_t llc_index; /* current write/read position of frame */
uint16_t llc_length; /* len of current DL LLC_frame, 0 == no frame */
llist_head llc_queue; /* queued LLC DL data */
struct llist_head llc_queue; /* queued LLC DL data */
enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state;
enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
enum gprs_rlcmac_tbf_poll_state poll_state;
uint32_t poll_fn;
uint32_t poll_fn; /* frame number to poll */
uint16_t ws; /* window size */
uint16_t sns; /* sequence number space */
@ -160,7 +193,8 @@ struct gprs_rlcmac_tbf {
uint16_t v_a; /* ack state */
char v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
int32_t tx_counter; /* count all transmitted blocks */
uint8_t n3105; /* N3105 counter */
char imsi[16]; /* store IMSI for PCH retransmission */
uint8_t wait_confirm; /* wait for CCCH IMM.ASS cnf */
} dl;
struct {
uint16_t bsn; /* block sequence number */
@ -169,12 +203,16 @@ struct gprs_rlcmac_tbf {
char v_n[RLC_MAX_SNS/2]; /* receive state array */
int32_t rx_counter; /* count all received blocks */
uint8_t n3103; /* N3103 counter */
uint8_t usf; /* USF */
uint8_t usf[8]; /* list USFs per PDCH (timeslot) */
uint8_t contention_resolution_done; /* set after done */
uint8_t final_ack_sent; /* set if we sent final ack */
} ul;
} dir;
uint8_t rlc_block[RLC_MAX_SNS/2][RLC_MAX_LEN]; /* block history */
uint8_t rlc_block_len[RLC_MAX_SNS/2]; /* block len of history */
uint8_t n3105; /* N3105 counter */
struct osmo_timer_list timer;
unsigned int T; /* Txxxx number */
unsigned int num_T_exp; /* number of consecutive T expirations */
@ -182,24 +220,73 @@ struct gprs_rlcmac_tbf {
struct osmo_gsm_timer_list gsm_timer;
unsigned int fT; /* fTxxxx number */
unsigned int num_fT_exp; /* number of consecutive fT expirations */
struct timeval bw_tv; /* timestamp for bandwidth calculation */
uint32_t bw_octets; /* number of octets transmitted since bw_tv */
uint8_t cs; /* current coding scheme */
};
extern struct llist_head gprs_rlcmac_tbfs;
extern struct llist_head gprs_rlcmac_ul_tbfs; /* list of uplink TBFs */
extern struct llist_head gprs_rlcmac_dl_tbfs; /* list of downlink TBFs */
extern struct llist_head gprs_rlcmac_sbas; /* list of single block allocs */
int tfi_alloc(uint8_t *_trx, uint8_t *_ts);
/*
* paging entry
*/
struct gprs_rlcmac_paging {
struct llist_head list;
uint8_t chan_needed;
uint8_t identity_lv[9];
};
struct gprs_rlcmac_tbf *tbf_alloc(uint8_t tfi, uint8_t trx, uint8_t ts);
/*
* single block allocation entry
*/
struct gprs_rlcmac_sba {
struct llist_head list;
uint8_t trx;
uint8_t ts;
uint32_t fn;
uint8_t ta;
};
struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, int direction);
/*
* coding scheme info
*/
struct gprs_rlcmac_cs {
uint8_t block_length;
uint8_t block_data;
uint8_t block_payload;
};
struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, int direction);
extern struct gprs_rlcmac_cs gprs_rlcmac_cs[];
struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn);
int sba_alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta);
int find_free_usf(uint8_t trx, uint8_t ts);
struct gprs_rlcmac_sba *sba_find(uint8_t trx, uint8_t ts, uint32_t fn);
int tfi_alloc(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, uint8_t *_ts,
int8_t use_trx, int8_t first_ts);
struct gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_tbf *old_tbf,
enum gprs_rlcmac_tbf_direction dir, uint8_t tfi, uint8_t trx,
uint8_t first_ts, uint8_t ms_class, uint8_t single_slot);
struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, uint8_t trx,
enum gprs_rlcmac_tbf_direction dir);
struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli,
enum gprs_rlcmac_tbf_direction dir);
struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
void tbf_free(struct gprs_rlcmac_tbf *tbf);
int tbf_update(struct gprs_rlcmac_tbf *tbf);
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
void tbf_new_state(struct gprs_rlcmac_tbf *tbf,
enum gprs_rlcmac_tbf_state state);
@ -216,21 +303,24 @@ enum gprs_rlcmac_block_type {
GPRS_RLCMAC_RESERVED = 0x3
};
int gprs_rlcmac_rcv_block(uint8_t *data, uint8_t len, uint32_t fn);
int gprs_rlcmac_rcv_block(uint8_t trx, uint8_t ts, uint8_t *data, uint8_t len,
uint32_t fn);
int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
uint32_t fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling,
uint32_t poll_fn);
uint32_t fn, uint8_t single_block, uint8_t alpha, uint8_t gamma);
void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli, uint8_t new_tfi,
uint8_t usf, uint16_t arfcn, uint8_t tn, uint8_t ta, uint8_t tsc,
uint8_t poll);
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t alpha,
uint8_t gamma);
void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
uint8_t old_downlink, uint8_t new_tfi, uint16_t arfcn,
uint8_t tn, uint8_t ta, uint8_t tsc, uint8_t poll);
uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll,
uint8_t alpha, uint8_t gamma);
void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
uint8_t final);
@ -243,7 +333,8 @@ int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf);
int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint32_t fn);
int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint8_t trx, uint8_t ts,
uint32_t fn);
struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
@ -251,16 +342,22 @@ struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
struct msgb *gprs_rlcmac_send_packet_downlink_assignment(
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
void gprs_rlcmac_trigger_downlink_assignment(gprs_rlcmac_tbf *tbf,
uint8_t old_downlink, char *imsi);
void gprs_rlcmac_trigger_downlink_assignment(struct gprs_rlcmac_tbf *tbf,
struct gprs_rlcmac_tbf *old_tbf, char *imsi);
int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
uint8_t ssn, uint8_t *rbb);
int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t *data, uint8_t len);
unsigned write_packet_paging_request(bitvec * dest);
unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
uint8_t *identity, uint8_t chan_needed);
int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts,
uint8_t *data, uint8_t len);
struct msgb *gprs_rlcmac_send_data_block_acknowledged(
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
uint32_t fn);
@ -268,4 +365,25 @@ struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
int gprs_rlcmac_imm_ass_cnf(uint8_t *data, uint32_t fn);
int gprs_rlcmac_add_paging(uint8_t chan_needed, uint8_t *identity_lv);
struct gprs_rlcmac_paging *gprs_rlcmac_dequeue_paging(
struct gprs_rlcmac_pdch *pdch);
struct msgb *gprs_rlcmac_send_packet_paging_request(
struct gprs_rlcmac_pdch *pdch);
extern "C" {
#endif
int alloc_algorithm_a(struct gprs_rlcmac_tbf *old_tbf,
struct gprs_rlcmac_tbf *tbf, uint32_t cust);
int alloc_algorithm_b(struct gprs_rlcmac_tbf *old_tbf,
struct gprs_rlcmac_tbf *tbf, uint32_t cust);
#ifdef __cplusplus
}
#endif
#endif // GPRS_RLCMAC_H

File diff suppressed because it is too large Load Diff

View File

@ -21,8 +21,189 @@
#include <gprs_rlcmac.h>
#include <pcu_l1_if.h>
extern struct llist_head block_queue;
uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
struct gprs_rlcmac_tbf **poll_tbf,
struct gprs_rlcmac_tbf **ul_ass_tbf,
struct gprs_rlcmac_tbf **dl_ass_tbf,
struct gprs_rlcmac_tbf **ul_ack_tbf)
{
struct gprs_rlcmac_tbf *tbf;
uint32_t poll_fn;
/* check special TBF for events */
poll_fn = fn + 4;
if ((block_nr % 3) == 2)
poll_fn ++;
poll_fn = poll_fn % 2715648;
llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
/* this trx, this ts */
if (tbf->trx != trx || tbf->control_ts != ts)
continue;
/* polling for next uplink block */
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
&& tbf->poll_fn == poll_fn)
*poll_tbf = tbf;
if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
*ul_ack_tbf = tbf;
if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
*dl_ass_tbf = tbf;
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
*ul_ass_tbf = tbf;
}
llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
/* this trx, this ts */
if (tbf->trx != trx || tbf->control_ts != ts)
continue;
/* polling for next uplink block */
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
&& tbf->poll_fn == poll_fn)
*poll_tbf = tbf;
if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
*dl_ass_tbf = tbf;
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
*ul_ass_tbf = tbf;
}
return poll_fn;
}
uint32_t sched_sba(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr)
{
uint32_t sba_fn;
struct gprs_rlcmac_sba *sba;
/* check special TBF for events */
sba_fn = fn + 4;
if ((block_nr % 3) == 2)
sba_fn ++;
sba_fn = sba_fn % 2715648;
sba = sba_find(trx, ts, sba_fn);
if (sba) {
llist_del(&sba->list);
talloc_free(sba);
return sba_fn;
}
return 0xffffffff;
}
uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
{
struct gprs_rlcmac_tbf *tbf;
uint8_t usf = 0x07;
uint8_t i, tfi;
/* select uplink ressource */
for (i = 0, tfi = pdch->next_ul_tfi; i < 32;
i++, tfi = (tfi + 1) & 31) {
tbf = pdch->ul_tbf[tfi];
/* no TBF for this tfi, go next */
if (!tbf)
continue;
/* no UL ressources needed, go next */
/* we don't need to give ressources in FINISHED state,
* because we have received all blocks and only poll
* for packet control ack. */
if (tbf->state != GPRS_RLCMAC_FLOW)
continue;
/* use this USF */
usf = tbf->dir.ul.usf[ts];
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
"TS=%d FN=%d block_nr=%d scheduling USF=%d for "
"required uplink ressource of UL TBF=%d\n", trx, ts, fn,
block_nr, usf, tfi);
/* next TBF to handle ressource is the next one */
pdch->next_ul_tfi = (tfi + 1) & 31;
break;
}
return usf;
}
struct msgb *sched_select_ctrl_msg(uint8_t trx, uint8_t ts, uint32_t fn,
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch,
struct gprs_rlcmac_tbf *ul_ass_tbf,
struct gprs_rlcmac_tbf *dl_ass_tbf,
struct gprs_rlcmac_tbf *ul_ack_tbf)
{
struct msgb *msg = NULL;
struct gprs_rlcmac_tbf *tbf = NULL;
/* schedule PACKET UPLINK ASSIGNMENT (1st priority) */
if (ul_ass_tbf) {
tbf = ul_ass_tbf;
msg = gprs_rlcmac_send_packet_uplink_assignment(tbf, fn);
}
/* schedule PACKET DOWNLINK ASSIGNMENT (2nd priotiry) */
if (!msg && dl_ass_tbf) {
tbf = dl_ass_tbf;
msg = gprs_rlcmac_send_packet_downlink_assignment(tbf, fn);
}
/* schedule PACKET UPLINK ACK (3rd priority) */
if (!msg && ul_ack_tbf) {
tbf = ul_ack_tbf;
msg = gprs_rlcmac_send_uplink_ack(tbf, fn);
}
/* any message */
if (msg) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
"message at RTS for %s TBF=%d (TRX=%d, TS=%d)\n",
(tbf->direction == GPRS_RLCMAC_UL_TBF)
? "UL" : "DL", tbf->tfi, trx, ts);
return msg;
}
/* schedule PACKET PAGING REQUEST */
if (!llist_empty(&pdch->paging_list))
msg = gprs_rlcmac_send_packet_paging_request(pdch);
if (msg) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling paging request "
"message at RTS for (TRX=%d, TS=%d)\n", trx, ts);
return msg;
}
return NULL;
}
struct msgb *sched_select_downlink(uint8_t trx, uint8_t ts, uint32_t fn,
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
{
struct msgb *msg = NULL;
struct gprs_rlcmac_tbf *tbf = NULL;
uint8_t i, tfi;
/* select downlink ressource */
for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
i++, tfi = (tfi + 1) & 31) {
tbf = pdch->dl_tbf[tfi];
/* no TBF for this tfi, go next */
if (!tbf)
continue;
/* no DL TBF, go next */
if (tbf->direction != GPRS_RLCMAC_DL_TBF)
continue;
/* no DL ressources needed, go next */
if (tbf->state != GPRS_RLCMAC_FLOW
&& tbf->state != GPRS_RLCMAC_FINISHED)
continue;
/* waiting for CCCH IMM.ASS confirm */
if (tbf->dir.dl.wait_confirm)
continue;
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data message at "
"RTS for DL TBF=%d (TRX=%d, TS=%d)\n", tfi, trx, ts);
/* next TBF to handle ressource is the next one */
pdch->next_dl_tfi = (tfi + 1) & 31;
/* generate DL data block */
msg = gprs_rlcmac_send_data_block_acknowledged(tbf, fn,
ts);
break;
}
return msg;
}
static uint8_t rlcmac_dl_idle[23] = {
0x47, /* control without optional header octets, no polling, USF=111 */
0x94, /* dummy downlink control message, paging mode 00 */
@ -31,16 +212,28 @@ static uint8_t rlcmac_dl_idle[23] = {
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
};
struct msgb *sched_dummy(void)
{
struct msgb *msg;
msg = msgb_alloc(23, "rlcmac_dl_idle");
if (!msg)
return NULL;
memcpy(msgb_put(msg, 23), rlcmac_dl_idle, 23);
return msg;
}
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_pdch *pdch;
struct gprs_rlcmac_tbf *tbf;
struct gprs_rlcmac_tbf *poll_tbf = NULL, *dl_ass_tbf = NULL,
*ul_ass_tbf = NULL, *ul_ack_tbf = NULL;
uint8_t usf = 0x7;
struct msgb *msg = NULL;
uint32_t poll_fn;
uint8_t i, tfi;
uint32_t poll_fn, sba_fn;
if (trx >= 8 || ts >= 8)
return -EINVAL;
@ -55,128 +248,47 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
/* store last frame number of RTS */
pdch->last_rts_fn = fn;
/* check uplink resource for polling */
poll_fn = fn + 4;
if ((block_nr % 3) == 2)
poll_fn ++;
poll_fn = poll_fn % 2715648;
for (tfi = 0; tfi < 32; tfi++) {
tbf = pdch->tbf[tfi];
/* no TBF for this tfi, go next */
if (!tbf)
continue;
/* no polling */
if (tbf->poll_state != GPRS_RLCMAC_POLL_SCHED)
continue;
/* polling for next uplink block */
if (tbf->poll_fn == poll_fn)
break;
}
/* found uplink where a block is polled */
if (tfi < 32) {
poll_fn = sched_poll(trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
&dl_ass_tbf, &ul_ack_tbf);
/* check uplink ressource for polling */
if (poll_tbf)
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
"TS=%d FN=%d block_nr=%d scheduling free USF for "
"polling at FN=%d of TFI=%d\n", trx, ts, fn, block_nr,
poll_fn, tfi);
"polling at FN=%d of %s TFI=%d\n", trx, ts, fn,
block_nr, poll_fn,
(poll_tbf->direction == GPRS_RLCMAC_UL_TBF)
? "UL" : "DL", poll_tbf->tfi);
/* use free USF */
/* else, we search for uplink resource */
} else {
/* select uplink resource */
for (i = 0, tfi = pdch->next_ul_tfi; i < 32;
i++, tfi = (tfi + 1) & 31) {
tbf = pdch->tbf[tfi];
/* no TBF for this tfi, go next */
if (!tbf)
continue;
/* no UL TBF, go next */
if (tbf->direction != GPRS_RLCMAC_UL_TBF)
continue;
/* no UL resources needed, go next */
/* we don't need to give resources in FINISHED state,
* because we have received all blocks and only poll
* for packet control ack. */
if (tbf->state != GPRS_RLCMAC_FLOW)
continue;
/* use this USF */
usf = tbf->dir.ul.usf;
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: "
"TRX=%d TS=%d FN=%d block_nr=%d scheduling "
"USF=%d for required uplink resource of "
"TBF=%d\n", trx, ts, fn, block_nr, usf, tfi);
/* next TBF to handle resource is the next one */
pdch->next_ul_tfi = (tfi + 1) & 31;
break;
}
}
/* else. check for sba */
else if ((sba_fn = sched_sba(trx, ts, fn, block_nr) != 0xffffffff))
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
"TS=%d FN=%d block_nr=%d scheduling free USF for "
"single block allocation at FN=%d\n", trx, ts, fn,
block_nr, sba_fn);
/* use free USF */
/* else, we search for uplink ressource */
else
usf = sched_select_uplink(trx, ts, fn, block_nr, pdch);
/* Prio 1: select control message */
for (tfi = 0; tfi < 32; tfi++) {
tbf = pdch->tbf[tfi];
/* no TBF for this tfi, go next */
if (!tbf)
continue;
/* schedule PACKET DOWNLINK ASSIGNMENT */
if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
msg = gprs_rlcmac_send_packet_downlink_assignment(tbf,
fn);
else
/* schedule PACKET UPLINK ASSIGNMENT */
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
msg = gprs_rlcmac_send_packet_uplink_assignment(tbf,
fn);
else
/* schedule PACKET UPLINK ACK */
if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
msg = gprs_rlcmac_send_uplink_ack(tbf, fn);
if (msg) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
"message at RTS for TBF=%d\n", tfi);
break;
}
}
msg = sched_select_ctrl_msg(trx, ts, fn, block_nr, pdch, ul_ass_tbf,
dl_ass_tbf, ul_ack_tbf);
/* Prio 2: select data message for downlink */
if (!msg) {
/* select downlink resource */
for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
i++, tfi = (tfi + 1) & 31) {
tbf = pdch->tbf[tfi];
/* no TBF for this tfi, go next */
if (!tbf)
continue;
/* no DL TBF, go next */
if (tbf->direction != GPRS_RLCMAC_DL_TBF)
continue;
/* no DL resources needed, go next */
if (tbf->state != GPRS_RLCMAC_FLOW
&& tbf->state != GPRS_RLCMAC_FINISHED)
continue;
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data "
"message at RTS for TBF=%d\n", tfi);
/* next TBF to handle resource is the next one */
pdch->next_dl_tfi = (tfi + 1) & 31;
/* generate DL data block */
msg = gprs_rlcmac_send_data_block_acknowledged(tbf, fn);
break;
}
}
if (!msg)
msg = sched_select_downlink(trx, ts, fn, block_nr, pdch);
/* Prio 3: send dummy contol message */
if (!msg) {
msg = msgb_alloc(23, "rlcmac_dl_idle");
if (!msg)
return -ENOMEM;
memcpy(msgb_put(msg, 23), rlcmac_dl_idle, 23);
}
if (!msg)
msg = sched_dummy();
if (!msg)
return -ENOMEM;
/* msg is now available */
/* set USF */
msg->data[0] = (msg->data[0] & 0xf8) | usf;
// printf("len=%d, date=%s\n", msg->len, osmo_hexdump(msg->data, msg->len));
/* send PDTCH/PACCH to L1 */
pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);

View File

@ -35,6 +35,8 @@ extern "C" {
#include <pcuif_proto.h>
}
extern void *tall_pcu_ctx;
struct femtol1_hdl {
struct gsm_time gsm_time;
uint32_t hLayer1; /* handle to the L1 instance in the DSP */
@ -142,7 +144,7 @@ int pcu_l1if_open()
int rc;
/* allocate new femtol1_handle */
fl1h = talloc_zero(NULL, struct femtol1_hdl);
fl1h = talloc_zero(tall_pcu_ctx, struct femtol1_hdl);
INIT_LLIST_HEAD(&fl1h->wlc_list);
l1fh->fl1h = fl1h;

View File

@ -37,6 +37,8 @@ extern "C" {
#include <gprs_bssgp_pcu.h>
#include <pcuif_proto.h>
extern void *tall_pcu_ctx;
// Variable for storage current FN.
int frame_number;
@ -180,8 +182,8 @@ static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
switch (data_ind->sapi) {
case PCU_IF_SAPI_PDTCH:
rc = gprs_rlcmac_rcv_block(data_ind->data, data_ind->len,
data_ind->fn);
rc = gprs_rlcmac_rcv_block(data_ind->trx_nr, data_ind->ts_nr,
data_ind->data, data_ind->len, data_ind->fn);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with "
@ -192,6 +194,26 @@ static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
return rc;
}
static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
{
int rc = 0;
LOGP(DL1IF, LOGL_DEBUG, "Data confirm received: sapi=%d fn=%d\n",
data_cnf->sapi, data_cnf->fn);
switch (data_cnf->sapi) {
case PCU_IF_SAPI_PCH:
rc = gprs_rlcmac_imm_ass_cnf(data_cnf->data, data_cnf->fn);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received PCU data confirm with "
"unsupported sapi %d\n", data_cnf->sapi);
rc = -EINVAL;
}
return rc;
}
static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
{
int rc = 0;
@ -245,14 +267,51 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
return rc;
}
int flush_pdch(struct gprs_rlcmac_pdch *pdch, uint8_t trx, uint8_t ts)
{
uint8_t tfi;
struct gprs_rlcmac_tbf *tbf;
struct gprs_rlcmac_paging *pag;
struct gprs_rlcmac_sba *sba, *sba2;
/* kick all TBF on slot */
for (tfi = 0; tfi < 32; tfi++) {
tbf = pdch->ul_tbf[tfi];
if (tbf)
tbf_free(tbf);
tbf = pdch->dl_tbf[tfi];
if (tbf)
tbf_free(tbf);
}
/* flush all pending paging messages */
while ((pag = gprs_rlcmac_dequeue_paging(pdch)))
talloc_free(pag);
llist_for_each_entry_safe(sba, sba2, &gprs_rlcmac_sbas, list) {
if (sba->trx == trx && sba->ts == ts) {
llist_del(&sba->list);
talloc_free(sba);
}
}
return 0;
}
static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_pdch *pdch;
int rc = 0;
int trx, ts, tfi;
struct gprs_rlcmac_tbf *tbf;
int trx, ts;
int i;
if (info_ind->version != PCU_IF_VERSION) {
fprintf(stderr, "PCU interface version number of BTS (%d) is "
"different (%d).\nPlease re-compile!\n",
info_ind->version, PCU_IF_VERSION);
exit(-1);
}
LOGP(DL1IF, LOGL_DEBUG, "Info indication received:\n");
if (!(info_ind->flags & PCU_IF_FLAG_ACTIVE)) {
@ -262,22 +321,20 @@ bssgp_failed:
for (trx = 0; trx < 8; trx++) {
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
for (ts = 0; ts < 8; ts++) {
for (tfi = 0; tfi < 32; tfi++) {
tbf = bts->trx[trx].pdch[ts].tbf[tfi];
if (tbf)
tbf_free(tbf);
}
if (bts->trx[trx].pdch[ts].enable)
flush_pdch(&bts->trx[trx].pdch[ts],
trx, ts);
}
}
gprs_bssgp_destroy();
return 0;
}
LOGP(DL1IF, LOGL_INFO, "BTS available\n");
LOGP(DL1IF, LOGL_DEBUG, " mcc=%d\n", info_ind->mcc);
LOGP(DL1IF, LOGL_DEBUG, " mnc=%d\n", info_ind->mnc);
LOGP(DL1IF, LOGL_DEBUG, " mcc=%x\n", info_ind->mcc);
LOGP(DL1IF, LOGL_DEBUG, " mnc=%x\n", info_ind->mnc);
LOGP(DL1IF, LOGL_DEBUG, " lac=%d\n", info_ind->lac);
LOGP(DL1IF, LOGL_DEBUG, " rac=%d\n", info_ind->rac);
LOGP(DL1IF, LOGL_DEBUG, " cell_id=%d\n", info_ind->cell_id);
LOGP(DL1IF, LOGL_DEBUG, " cell_id=%d\n", ntohs(info_ind->cell_id));
LOGP(DL1IF, LOGL_DEBUG, " nsei=%d\n", info_ind->nsei);
LOGP(DL1IF, LOGL_DEBUG, " nse_timer=%d %d %d %d %d %d %d\n",
info_ind->nse_timer[0], info_ind->nse_timer[1],
@ -346,32 +403,33 @@ bssgp_failed:
bts->n3103 = info_ind->n3103;
bts->n3105 = info_ind->n3105;
}
if (info_ind->initial_cs < 1 || info_ind->initial_cs > 4)
bts->initial_cs = 1;
else
bts->initial_cs = info_ind->initial_cs;
if (!bts->force_cs) {
if (info_ind->initial_cs < 1 || info_ind->initial_cs > 4)
bts->initial_cs_dl = 1;
else
bts->initial_cs_dl = info_ind->initial_cs;
bts->initial_cs_ul = bts->initial_cs_dl;
}
for (trx = 0; trx < 8; trx++) {
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
for (ts = 0; ts < 8; ts++) {
pdch = &bts->trx[trx].pdch[ts];
if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
/* FIXME: activate dynamically at RLCMAC */
if (!bts->trx[trx].pdch[ts].enable)
if (!pdch->enable) {
pcu_tx_act_req(trx, ts, 1);
bts->trx[trx].pdch[ts].enable = 1;
bts->trx[trx].pdch[ts].tsc =
info_ind->trx[trx].tsc[ts];
INIT_LLIST_HEAD(&pdch->paging_list);
pdch->enable = 1;
}
pdch->tsc = info_ind->trx[trx].tsc[ts];
LOGP(DL1IF, LOGL_INFO, "PDCH: trx=%d ts=%d\n",
trx, ts);
} else {
if (bts->trx[trx].pdch[ts].enable)
if (pdch->enable) {
pcu_tx_act_req(trx, ts, 0);
bts->trx[trx].pdch[ts].enable = 0;
/* kick all tbf FIXME: multislot */
for (tfi = 0; tfi < 32; tfi++) {
tbf = bts->trx[trx].pdch[ts].tbf[tfi];
if (tbf)
tbf_free(tbf);
pdch->enable = 0;
flush_pdch(pdch, trx, ts);
}
}
}
@ -382,8 +440,6 @@ bssgp_failed:
static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
int trx, ts, tfi;
struct gprs_rlcmac_tbf *tbf;
uint32_t elapsed;
uint8_t fn13 = time_ind->fn % 13;
@ -398,25 +454,33 @@ static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
set_current_fn(time_ind->fn);
/* check for poll timeout */
for (trx = 0; trx < 8; trx++) {
for (ts = 0; ts < 8; ts++) {
for (tfi = 0; tfi < 32; tfi++) {
tbf = bts->trx[trx].pdch[ts].tbf[tfi];
if (!tbf)
continue;
if (tbf->poll_state != GPRS_RLCMAC_POLL_SCHED)
continue;
elapsed = (frame_number - tbf->poll_fn)
% 2715648;
if (elapsed >= 20 && elapsed < 200)
gprs_rlcmac_poll_timeout(tbf);
}
llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
elapsed = (frame_number - tbf->poll_fn) % 2715648;
if (elapsed >= 20 && elapsed < 200)
gprs_rlcmac_poll_timeout(tbf);
}
}
llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
elapsed = (frame_number - tbf->poll_fn) % 2715648;
if (elapsed >= 20 && elapsed < 200)
gprs_rlcmac_poll_timeout(tbf);
}
}
return 0;
}
static int pcu_rx_pag_req(struct gsm_pcu_if_pag_req *pag_req)
{
LOGP(DL1IF, LOGL_DEBUG, "Paging request received: chan_needed=%d "
"length=%d\n", pag_req->chan_needed, pag_req->identity_lv[0]);
return gprs_rlcmac_add_paging(pag_req->chan_needed,
pag_req->identity_lv);
}
int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim)
{
int rc = 0;
@ -425,6 +489,9 @@ int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim)
case PCU_IF_MSG_DATA_IND:
rc = pcu_rx_data_ind(&pcu_prim->u.data_ind);
break;
case PCU_IF_MSG_DATA_CNF:
rc = pcu_rx_data_cnf(&pcu_prim->u.data_cnf);
break;
case PCU_IF_MSG_RTS_REQ:
rc = pcu_rx_rts_req(&pcu_prim->u.rts_req);
break;
@ -437,6 +504,9 @@ int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim)
case PCU_IF_MSG_TIME_IND:
rc = pcu_rx_time_ind(&pcu_prim->u.time_ind);
break;
case PCU_IF_MSG_PAG_REQ:
rc = pcu_rx_pag_req(&pcu_prim->u.pag_req);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received unknwon PCU msg type %d\n",
msg_type);

View File

@ -25,15 +25,28 @@
#include <gprs_debug.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
extern "C" {
#include "pcu_vty.h"
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
}
struct gprs_rlcmac_bts *gprs_rlcmac_bts;
extern struct gprs_nsvc *nsvc;
uint16_t spoof_mcc = 0, spoof_mnc = 0;
static int config_given = 0;
static const char *config_file = "osmo-pcu.cfg";
extern struct vty_app_info pcu_vty_info;
void *tall_pcu_ctx;
static int quit = 0;
static void print_help()
{
printf( "Some useful options:\n"
" -h --help this text\n"
" -c --config-file Specify the filename of the config "
"file\n"
" -m --mcc MCC use given MCC instead of value "
"provided by BTS\n"
" -n --mnc MNC use given MNC instead of value "
@ -48,12 +61,13 @@ static void handle_options(int argc, char **argv)
int option_idx = 0, c;
static const struct option long_options[] = {
{ "help", 0, 0, 'h' },
{ "config-file", 1, 0, 'c' },
{ "mcc", 1, 0, 'm' },
{ "mnc", 1, 0, 'n' },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "hm:n:",
c = getopt_long(argc, argv, "hc:m:n:",
long_options, &option_idx);
if (c == -1)
break;
@ -63,6 +77,10 @@ static void handle_options(int argc, char **argv)
print_help();
exit(0);
break;
case 'c':
config_file = strdup(optarg);
config_given = 1;
break;
case 'm':
spoof_mcc = atoi(optarg);
break;
@ -77,16 +95,54 @@ static void handle_options(int argc, char **argv)
}
}
void sighandler(int sigset)
{
if (sigset == SIGHUP || sigset == SIGPIPE)
return;
fprintf(stderr, "Signal %d received.\n", sigset);
switch (sigset) {
case SIGINT:
/* If another signal is received afterwards, the program
* is terminated without finishing shutdown process.
*/
signal(SIGINT, SIG_DFL);
signal(SIGHUP, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
signal(SIGABRT, SIG_DFL);
signal(SIGUSR1, SIG_DFL);
signal(SIGUSR2, SIG_DFL);
quit = 1;
break;
case SIGABRT:
/* in case of abort, we want to obtain a talloc report
* and then return to the caller, who will abort the process
*/
case SIGUSR1:
case SIGUSR2:
talloc_report_full(tall_pcu_ctx, stderr);
break;
}
}
int main(int argc, char *argv[])
{
struct gprs_rlcmac_bts *bts;
int rc;
bts = gprs_rlcmac_bts = talloc_zero(NULL, struct gprs_rlcmac_bts);
tall_pcu_ctx = talloc_named_const(NULL, 1, "Osmo-PCU context");
if (!tall_pcu_ctx)
return -ENOMEM;
bts = gprs_rlcmac_bts = talloc_zero(tall_pcu_ctx,
struct gprs_rlcmac_bts);
if (!gprs_rlcmac_bts)
return -ENOMEM;
gprs_rlcmac_bts->initial_cs = 1;
bts->initial_cs = 1;
bts->fc_interval = 1;
bts->initial_cs_dl = bts->initial_cs_ul = 1;
bts->cs1 = 1;
bts->t3142 = 20;
bts->t3169 = 5;
@ -96,22 +152,55 @@ int main(int argc, char *argv[])
bts->n3101 = 10;
bts->n3103 = 4;
bts->n3105 = 8;
bts->alpha = 10; /* a = 1.0 */
msgb_set_talloc_ctx(tall_pcu_ctx);
osmo_init_logging(&gprs_log_info);
vty_init(&pcu_vty_info);
pcu_vty_init(&gprs_log_info);
handle_options(argc, argv);
if ((!!spoof_mcc) + (!!spoof_mnc) == 1) {
fprintf(stderr, "--mcc and --mnc must be specified "
"together.\n");
exit(0);
}
rc = vty_read_config_file(config_file, NULL);
if (rc < 0 && config_given) {
fprintf(stderr, "Failed to parse the config file: '%s'\n",
config_file);
exit(1);
}
if (rc < 0)
fprintf(stderr, "No config file: '%s' Using default config.\n",
config_file);
rc = telnet_init(tall_pcu_ctx, NULL, 4240);
if (rc < 0) {
fprintf(stderr, "Error initializing telnet\n");
exit(1);
}
if (!bts->alloc_algorithm)
bts->alloc_algorithm = alloc_algorithm_b;
rc = pcu_l1if_open();
if (rc < 0)
return rc;
while (1)
{
signal(SIGINT, sighandler);
signal(SIGHUP, sighandler);
signal(SIGTERM, sighandler);
signal(SIGPIPE, sighandler);
signal(SIGABRT, sighandler);
signal(SIGUSR1, sighandler);
signal(SIGUSR2, sighandler);
while (!quit) {
osmo_gsm_timers_check();
osmo_gsm_timers_prepare();
osmo_gsm_timers_update();
@ -119,8 +208,14 @@ int main(int argc, char *argv[])
osmo_select_main(0);
}
telnet_exit();
pcu_l1if_close();
talloc_free(gprs_rlcmac_bts);
talloc_report_full(tall_pcu_ctx, stderr);
talloc_free(tall_pcu_ctx);
return 0;
}

313
src/pcu_vty.c Normal file
View File

@ -0,0 +1,313 @@
/* OsmoBTS VTY interface */
#include <stdint.h>
#include <osmocom/vty/logging.h>
#include <osmocom/core/linuxlist.h>
#include "pcu_vty.h"
#include "gprs_rlcmac.h"
enum node_type pcu_vty_go_parent(struct vty *vty)
{
switch (vty->node) {
#if 0
case TRX_NODE:
vty->node = PCU_NODE;
{
struct gsm_bts_trx *trx = vty->index;
vty->index = trx->bts;
}
break;
#endif
default:
vty->node = CONFIG_NODE;
}
return (enum node_type) vty->node;
}
int pcu_vty_is_config_node(struct vty *vty, int node)
{
switch (node) {
case PCU_NODE:
return 1;
default:
return 0;
}
}
static struct cmd_node pcu_node = {
(enum node_type) PCU_NODE,
"%s(pcu)#",
1,
};
gDEFUN(ournode_exit, ournode_exit_cmd, "exit",
"Exit current node, go down to provious node")
{
switch (vty->node) {
#if 0
case TRXV_NODE:
vty->node = PCU_NODE;
{
struct gsm_bts_trx *trx = vty->index;
vty->index = trx->bts;
}
break;
#endif
default:
break;
}
return CMD_SUCCESS;
}
gDEFUN(ournode_end, ournode_end_cmd, "end",
"End current mode and change to enable mode")
{
switch (vty->node) {
default:
vty_config_unlock(vty);
vty->node = ENABLE_NODE;
vty->index = NULL;
vty->index_sub = NULL;
break;
}
return CMD_SUCCESS;
}
static int config_write_pcu(struct vty *vty)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
vty_out(vty, "pcu%s", VTY_NEWLINE);
vty_out(vty, " flow-control-interval %d%s", bts->fc_interval,
VTY_NEWLINE);
if (bts->force_cs)
if (bts->initial_cs_ul == bts->initial_cs_dl)
vty_out(vty, " cs %d%s", bts->initial_cs_dl,
VTY_NEWLINE);
else
vty_out(vty, " cs %d %d%s", bts->initial_cs_dl,
bts->initial_cs_ul, VTY_NEWLINE);
if (bts->force_llc_lifetime == 0xffff)
vty_out(vty, " queue lifetime infinite%s", VTY_NEWLINE);
else if (bts->force_llc_lifetime)
vty_out(vty, " queue lifetime %d%s", bts->force_llc_lifetime,
VTY_NEWLINE);
if (bts->alloc_algorithm == alloc_algorithm_a)
vty_out(vty, " alloc-algorithm a%s", VTY_NEWLINE);
if (bts->alloc_algorithm == alloc_algorithm_b)
vty_out(vty, " alloc-algorithm b%s", VTY_NEWLINE);
if (bts->force_two_phase)
vty_out(vty, " two-phase-access%s", VTY_NEWLINE);
vty_out(vty, " alpha %d%s", bts->alpha, VTY_NEWLINE);
vty_out(vty, " gamma %d%s", bts->gamma * 2, VTY_NEWLINE);
}
/* per-BTS configuration */
DEFUN(cfg_pcu,
cfg_pcu_cmd,
"pcu",
"BTS specific configure")
{
vty->node = PCU_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_fc_interval,
cfg_pcu_fc_interval_cmd,
"flow-control-interval <1-10>",
"Interval between sending subsequent Flow Control PDUs\n"
"Interval time in seconds\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
bts->fc_interval = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_cs,
cfg_pcu_cs_cmd,
"cs <1-4> [<1-4>]",
"Set the Coding Scheme to be used, (overrides BTS config)\n"
"Initial CS used\nAlternative uplink CS")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
uint8_t cs = atoi(argv[0]);
bts->force_cs = 1;
bts->initial_cs_dl = cs;
if (argc > 1)
bts->initial_cs_ul = atoi(argv[1]);
else
bts->initial_cs_ul = cs;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_cs,
cfg_pcu_no_cs_cmd,
"no cs",
NO_STR "Don't force given Coding Scheme, (use BTS config)\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
bts->force_cs = 0;
return CMD_SUCCESS;
}
#define QUEUE_STR "Packet queue options\n"
#define LIFETIME_STR "Set lifetime limit of LLC frame in centi-seconds " \
"(overrides the value given by SGSN)\n"
DEFUN(cfg_pcu_queue_lifetime,
cfg_pcu_queue_lifetime_cmd,
"queue lifetime <1-65534>",
QUEUE_STR LIFETIME_STR "Lifetime in centi-seconds")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
uint8_t csec = atoi(argv[0]);
bts->force_llc_lifetime = csec;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_queue_lifetime_inf,
cfg_pcu_queue_lifetime_inf_cmd,
"queue lifetime infinite",
QUEUE_STR LIFETIME_STR "Infinite lifetime")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
bts->force_llc_lifetime = 0xffff;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_queue_lifetime,
cfg_pcu_no_queue_lifetime_cmd,
"no queue lifetime",
NO_STR QUEUE_STR "Disable lifetime limit of LLC frame (use value given "
"by SGSN)\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
bts->force_llc_lifetime = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_alloc,
cfg_pcu_alloc_cmd,
"alloc-algorithm (a|b)",
"Select slot allocation algorithm to use when assigning timeslots on "
"PACCH\nSingle slot is assigned only\nMultiple slots are assigned for "
"semi-duplex operation")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
switch (argv[0][0]) {
case 'a':
bts->alloc_algorithm = alloc_algorithm_a;
break;
case 'b':
bts->alloc_algorithm = alloc_algorithm_b;
break;
}
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_two_phase,
cfg_pcu_two_phase_cmd,
"two-phase-access",
"Force two phase access when MS requests single phase access\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
bts->force_two_phase = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_two_phase,
cfg_pcu_no_two_phase_cmd,
"no two-phase-access",
NO_STR "Only use two phase access when requested my MS\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
bts->force_two_phase = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_alpha,
cfg_pcu_alpha_cmd,
"alpha <0-10>",
"Alpha parameter for MS power control in units of 0.1 (see TS 05.08) "
"NOTE: Be sure to set Alpha value at System information 13 too.\n"
"Alpha in units of 0.1\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
bts->alpha = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_gamma,
cfg_pcu_gamma_cmd,
"gamma <0-62>",
"Gamma parameter for MS power control in units of dB (see TS 05.08)\n"
"Gamma in even unit of dBs\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
bts->gamma = atoi(argv[0]) / 2;
return CMD_SUCCESS;
}
static const char pcu_copyright[] =
"Copyright (C) 2012 by ...\r\n"
"License GNU GPL version 2 or later\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
struct vty_app_info pcu_vty_info = {
.name = "Osmo-PCU",
.version = PACKAGE_VERSION,
.copyright = pcu_copyright,
.go_parent_cb = pcu_vty_go_parent,
.is_config_node = pcu_vty_is_config_node,
};
int pcu_vty_init(const struct log_info *cat)
{
// install_element_ve(&show_pcu_cmd);
logging_vty_add_cmds(cat);
install_node(&pcu_node, config_write_pcu);
install_element(CONFIG_NODE, &cfg_pcu_cmd);
install_default(PCU_NODE);
install_element(PCU_NODE, &cfg_pcu_no_two_phase_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_cmd);
install_element(PCU_NODE, &cfg_pcu_no_cs_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_lifetime_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_lifetime_inf_cmd);
install_element(PCU_NODE, &cfg_pcu_no_queue_lifetime_cmd);
install_element(PCU_NODE, &cfg_pcu_alloc_cmd);
install_element(PCU_NODE, &cfg_pcu_two_phase_cmd);
install_element(PCU_NODE, &cfg_pcu_fc_interval_cmd);
install_element(PCU_NODE, &cfg_pcu_alpha_cmd);
install_element(PCU_NODE, &cfg_pcu_gamma_cmd);
install_element(PCU_NODE, &ournode_end_cmd);
return 0;
}

22
src/pcu_vty.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef _PCU_VTY_H
#define _PCU_VTY_H
#include <osmocom/vty/command.h>
#include <osmocom/vty/vty.h>
enum pcu_vty_node {
PCU_NODE = _LAST_OSMOVTY_NODE + 1,
};
extern struct cmd_element ournode_exit_cmd;
extern struct cmd_element ournode_end_cmd;
enum node_type pcu_vty_go_parent(struct vty *vty);
int pcu_vty_is_config_node(struct vty *vty, int node);
int pcu_vty_init(const struct log_info *cat);
extern struct vty_app_info pcu_vty_info;
#endif /* _PCU_VTY_H */

View File

@ -1,19 +1,23 @@
#ifndef _PCUIF_PROTO_H
#define _PCUIF_PROTO_H
#define PCU_IF_VERSION 0x04
/* msg_type */
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
#define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */
#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */
#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send data to given chan. */
#define PCU_IF_MSG_RACH_IND 0x22 /* receive rach */
#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */
#define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */
#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */
#define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */
#define PCU_IF_MSG_TIME_IND 0x52 /* gsm time indication */
#define PCU_IF_MSG_TIME_IND 0x52 /* GSM time indication */
#define PCU_IF_MSG_PAG_REQ 0x60 /* paging request */
/* sapi */
#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */
#define PCU_IF_SAPI_AGCH 0x02 /* assignment on CCCH */
#define PCU_IF_SAPI_PCH 0x03 /* paging request on CCCH */
#define PCU_IF_SAPI_AGCH 0x02 /* assignment on AGCH */
#define PCU_IF_SAPI_PCH 0x03 /* paging/assignment on PCH */
#define PCU_IF_SAPI_BCCH 0x04 /* SI on BCCH */
#define PCU_IF_SAPI_PDTCH 0x05 /* packet data/control/ccch block */
#define PCU_IF_SAPI_PRACH 0x06 /* packet random access channel */
@ -70,11 +74,14 @@ struct gsm_pcu_if_info_trx {
uint8_t pdch_mask; /* PDCH channels per TS */
uint8_t spare;
uint8_t tsc[8]; /* TSC per channel */
uint32_t hlayer1;
} __attribute__ ((packed));
struct gsm_pcu_if_info_ind {
uint32_t version;
uint32_t flags;
struct gsm_pcu_if_info_trx trx[8]; /* TRX infos per BTS */
uint8_t bsic;
/* RAI */
uint16_t mcc, mnc, lac, rac;
/* NSE */
@ -117,6 +124,12 @@ struct gsm_pcu_if_time_ind {
uint32_t fn;
} __attribute__ ((packed));
struct gsm_pcu_if_pag_req {
uint8_t sapi;
uint8_t chan_needed;
uint8_t identity_lv[9];
} __attribute__ ((packed));
struct gsm_pcu_if {
/* context based information */
uint8_t msg_type; /* message type */
@ -125,12 +138,14 @@ struct gsm_pcu_if {
union {
struct gsm_pcu_if_data data_req;
struct gsm_pcu_if_data data_cnf;
struct gsm_pcu_if_data data_ind;
struct gsm_pcu_if_rts_req rts_req;
struct gsm_pcu_if_rach_ind rach_ind;
struct gsm_pcu_if_info_ind info_ind;
struct gsm_pcu_if_act_req act_req;
struct gsm_pcu_if_time_ind time_ind;
struct gsm_pcu_if_pag_req pag_req;
} u;
} __attribute__ ((packed));

View File

@ -37,6 +37,7 @@ extern "C" {
#include <gprs_bssgp_pcu.h>
#include <pcuif_proto.h>
extern void *tall_pcu_ctx;
/*
* SYSMO-PCU socket functions
@ -72,14 +73,15 @@ int pcu_sock_send(struct msgb *msg)
return 0;
}
static void pcu_sock_close(struct pcu_sock_state *state)
static void pcu_sock_close(struct pcu_sock_state *state, int lost)
{
struct osmo_fd *bfd = &state->conn_bfd;
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_tbf *tbf;
uint8_t trx, ts, tfi;
LOGP(DL1IF, LOGL_NOTICE, "PCU socket has LOST connection\n");
LOGP(DL1IF, LOGL_NOTICE, "PCU socket has %s connection\n",
(lost) ? "LOST" : "closed");
close(bfd->fd);
bfd->fd = -1;
@ -93,20 +95,24 @@ static void pcu_sock_close(struct pcu_sock_state *state)
/* disable all slots, kick all TBFs */
for (trx = 0; trx < 8; trx++) {
for (ts = 0; ts < 8; ts++) {
for (ts = 0; ts < 8; ts++)
bts->trx[trx].pdch[ts].enable = 0;
for (tfi = 0; tfi < 32; tfi++) {
tbf = bts->trx[trx].pdch[ts].tbf[tfi];
if (tbf)
tbf_free(tbf);
}
for (tfi = 0; tfi < 32; tfi++) {
tbf = bts->trx[trx].ul_tbf[tfi];
if (tbf)
tbf_free(tbf);
tbf = bts->trx[trx].dl_tbf[tfi];
if (tbf)
tbf_free(tbf);
}
}
gprs_bssgp_destroy();
state->timer.cb = pcu_sock_timeout;
osmo_timer_schedule(&state->timer, 5, 0);
if (lost) {
state->timer.cb = pcu_sock_timeout;
osmo_timer_schedule(&state->timer, 5, 0);
}
}
static int pcu_sock_read(struct osmo_fd *bfd)
@ -142,7 +148,7 @@ static int pcu_sock_read(struct osmo_fd *bfd)
close:
msgb_free(msg);
pcu_sock_close(state);
pcu_sock_close(state, 1);
return -1;
}
@ -189,7 +195,7 @@ dontsend:
return 0;
close:
pcu_sock_close(state);
pcu_sock_close(state, 1);
return -1;
}
@ -219,7 +225,7 @@ int pcu_l1if_open(void)
state = pcu_sock_state;
if (!state) {
state = talloc_zero(NULL, struct pcu_sock_state);
state = talloc_zero(tall_pcu_ctx, struct pcu_sock_state);
if (!state)
return -ENOMEM;
INIT_LLIST_HEAD(&state->upqueue);
@ -253,6 +259,7 @@ int pcu_l1if_open(void)
if (rc != 0) {
LOGP(DL1IF, LOGL_ERROR, "Failed to Connect the PCU-SYSMO "
"socket, delaying... '%s'\n", local.sun_path);
pcu_sock_state = state;
close(bfd->fd);
bfd->fd = -1;
state->timer.cb = pcu_sock_timeout;
@ -292,7 +299,7 @@ void pcu_l1if_close(void)
bfd = &state->conn_bfd;
if (bfd->fd > 0)
pcu_sock_close(state);
pcu_sock_close(state, 0);
talloc_free(state);
pcu_sock_state = NULL;
}

View File

@ -94,11 +94,27 @@ Handling of LLC Frame of downlink TBF and LLC Queue of downlink TBF:
If a new LLC PDU is attached to LLC Frame during WAIT RELEASE state, the
state is changed to FLOW (downlink flow is assigned to MS).
Handling of LLC Frame on uplink TBF:
Received uplink blocks are appended to LLC Frame of TBF. If the PDU is
complete, it is forwarded to the upper layer.
Control TS:
On uplink or downlink assignment, it is required to have one timeslot that
can be used to receive and transmit. This timeslot is used at uplink TBF
to acknowledge and to assign other TBF. This timeslot is used at downlink
TBF to poll acknowledgement and to assign other TBF.
The first common TS (first_common_ts) is calculated when channels are
allocated. After creation of TBF or after assignment to different TS layout,
the first common TS is used for control TS.
The first common TS (and so control TS) must not need to be allocated to
MS as uplink TBF. (E.g. in case of non-available USF for this slot)
Polling:
In order to poll uplink control block from MS, a special poll state and
frame number is stored at TBF. The scheduler reads that value and will not
@ -109,3 +125,37 @@ Polling:
- The received frame is bad (BFI).
- The GSM indicates that the block should have been already received.
Because polling requires uplink response from MS, the polling must be
performed at control TS.
Data structures of TBFs and PDCHs:
There is a global structure for BTS.
The BTS structure has 8 TRX structures.
Each TRX structure has 8 PDCH structures, one for each timeslot.
There are two linked lists of TBF instances:
- uplink TBFs
- downlink TBFs
Each TBF instance also has:
- a direction
- a TFI of range 0..31
- an array of 8 PDCH structures, one for each assigned timeslot
- in case of uplink TBF: an array of 8 USFs, one for each assigned timeslot
Each PDCH structure also has:
- an array of 32 uplink TBFs that are assigned to this PDCH
- an array of 32 downlink TBFs that are assigned to this PDCH
On creation of a new TBF, it links to all assigned PDCHs.
Each PDCH links to that TBF. The TBF is added to the list of TBFs.
In case of uplink TBF: The allocated USFs are stored for each timeslot.
On release of a TBF, the link to this PDCH is removed from all assigned
PDCHs. The TBF is removed from the list of TBFs. The TBF is destroyed.