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:
commit
9eb552b239
|
@ -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.
|
|
@ -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
|
|
@ -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) \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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},
|
||||
};
|
||||
|
|
|
@ -34,6 +34,7 @@ enum {
|
|||
DRLCMACDL,
|
||||
DRLCMACUL,
|
||||
DRLCMACSCHED,
|
||||
DRLCMACBW,
|
||||
DBSSGP,
|
||||
DPCU,
|
||||
aDebug_LastEntry
|
||||
|
|
1419
src/gprs_rlcmac.cpp
1419
src/gprs_rlcmac.cpp
File diff suppressed because it is too large
Load Diff
|
@ -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
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
107
src/pcu_main.cpp
107
src/pcu_main.cpp
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
||||
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
50
src/tbf.txt
50
src/tbf.txt
|
@ -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.
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue